From 76b1e506f5a0957fe8e39bd4ea4720e4f98ef038 Mon Sep 17 00:00:00 2001 From: shreddedbacon Date: Wed, 15 Nov 2023 13:37:01 +1100 Subject: [PATCH] feat: add organization labels to namespace and build/task pods --- apis/lagoon/v1beta1/lagoonbuild_types.go | 6 ++++ apis/lagoon/v1beta1/lagoontask_types.go | 1 + apis/lagoon/v1beta1/zz_generated.deepcopy.go | 30 +++++++++++++++++ .../crd/bases/crd.lagoon.sh_lagoonbuilds.yaml | 7 ++++ .../crd/bases/crd.lagoon.sh_lagoontasks.yaml | 7 ++++ controller-test.sh | 32 +++++++++++++++++-- controllers/v1beta1/build_helpers.go | 16 ++++++++++ controllers/v1beta1/task_controller.go | 16 ++++++++++ test-resources/example-project2.yaml | 5 ++- 9 files changed, 116 insertions(+), 4 deletions(-) diff --git a/apis/lagoon/v1beta1/lagoonbuild_types.go b/apis/lagoon/v1beta1/lagoonbuild_types.go index 59642110..c901ef22 100644 --- a/apis/lagoon/v1beta1/lagoonbuild_types.go +++ b/apis/lagoon/v1beta1/lagoonbuild_types.go @@ -140,6 +140,12 @@ type Project struct { EnvironmentIdling *int `json:"environmentIdling,omitempty"` ProjectIdling *int `json:"projectIdling,omitempty"` StorageCalculator *int `json:"storageCalculator,omitempty"` + Organization *Organization `json:"organization,omitempty"` +} + +type Organization struct { + ID *uint `json:"id,omitempty"` + Name string `json:"name,omitempty"` } // Variables contains the project and environment variables from lagoon. diff --git a/apis/lagoon/v1beta1/lagoontask_types.go b/apis/lagoon/v1beta1/lagoontask_types.go index 1708f0ba..3231e530 100644 --- a/apis/lagoon/v1beta1/lagoontask_types.go +++ b/apis/lagoon/v1beta1/lagoontask_types.go @@ -124,6 +124,7 @@ type LagoonTaskProject struct { Name string `json:"name"` NamespacePattern string `json:"namespacePattern,omitempty"` Variables LagoonVariables `json:"variables,omitempty"` + Organization *Organization `json:"organization,omitempty"` } // LagoonTaskEnvironment defines the lagoon environment information. diff --git a/apis/lagoon/v1beta1/zz_generated.deepcopy.go b/apis/lagoon/v1beta1/zz_generated.deepcopy.go index c19a4074..842af614 100644 --- a/apis/lagoon/v1beta1/zz_generated.deepcopy.go +++ b/apis/lagoon/v1beta1/zz_generated.deepcopy.go @@ -466,6 +466,11 @@ func (in *LagoonTaskList) DeepCopyObject() runtime.Object { func (in *LagoonTaskProject) DeepCopyInto(out *LagoonTaskProject) { *out = *in in.Variables.DeepCopyInto(&out.Variables) + if in.Organization != nil { + in, out := &in.Organization, &out.Organization + *out = new(Organization) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LagoonTaskProject. @@ -571,6 +576,26 @@ func (in *Monitoring) DeepCopy() *Monitoring { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Organization) DeepCopyInto(out *Organization) { + *out = *in + if in.ID != nil { + in, out := &in.ID, &out.ID + *out = new(uint) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Organization. +func (in *Organization) DeepCopy() *Organization { + if in == nil { + return nil + } + out := new(Organization) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Project) DeepCopyInto(out *Project) { *out = *in @@ -606,6 +631,11 @@ func (in *Project) DeepCopyInto(out *Project) { *out = new(int) **out = **in } + if in.Organization != nil { + in, out := &in.Organization, &out.Organization + *out = new(Organization) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Project. diff --git a/config/crd/bases/crd.lagoon.sh_lagoonbuilds.yaml b/config/crd/bases/crd.lagoon.sh_lagoonbuilds.yaml index e3b5946d..bbc2dc2d 100644 --- a/config/crd/bases/crd.lagoon.sh_lagoonbuilds.yaml +++ b/config/crd/bases/crd.lagoon.sh_lagoonbuilds.yaml @@ -94,6 +94,13 @@ spec: type: string namespacePattern: type: string + organization: + properties: + id: + type: integer + name: + type: string + type: object productionEnvironment: type: string projectIdling: diff --git a/config/crd/bases/crd.lagoon.sh_lagoontasks.yaml b/config/crd/bases/crd.lagoon.sh_lagoontasks.yaml index 180051c3..f1394f91 100644 --- a/config/crd/bases/crd.lagoon.sh_lagoontasks.yaml +++ b/config/crd/bases/crd.lagoon.sh_lagoontasks.yaml @@ -109,6 +109,13 @@ spec: type: string namespacePattern: type: string + organization: + properties: + id: + type: integer + name: + type: string + type: object variables: description: Variables contains the project and environment variables from lagoon. diff --git a/controller-test.sh b/controller-test.sh index 415ce1bc..01c53401 100755 --- a/controller-test.sh +++ b/controller-test.sh @@ -17,6 +17,7 @@ CHECK_TIMEOUT=20 NS=nginx-example-main LBUILD=7m5zypx LBUILD2=8m5zypx +LBUILD3=9m5zypx HARBOR_VERSION=${HARBOR_VERSION:-1.6.4} @@ -201,6 +202,31 @@ kubectl -n $CONTROLLER_NAMESPACE patch lagoonbuilds.crd.lagoon.sh lagoon-build-$ sleep 10 check_lagoon_build lagoon-build-${LBUILD} +echo "==> Trigger a lagoon build using kubectl apply and check organization labels exist" +kubectl -n $CONTROLLER_NAMESPACE apply -f test-resources/example-project2.yaml +# patch the resource with the controller namespace +kubectl -n $CONTROLLER_NAMESPACE patch lagoonbuilds.crd.lagoon.sh lagoon-build-${LBUILD2} --type=merge --patch '{"metadata":{"labels":{"lagoon.sh/controller":"'$CONTROLLER_NAMESPACE'"}}}' +# patch the resource with a random label to bump the controller event filter +kubectl -n $CONTROLLER_NAMESPACE patch lagoonbuilds.crd.lagoon.sh lagoon-build-${LBUILD2} --type=merge --patch '{"metadata":{"labels":{"bump":"bump"}}}' +sleep 10 +check_lagoon_build lagoon-build-${LBUILD2} +if ! $(kubectl get namespace -l 'organization.lagoon.sh/name=test-org' --no-headers 2> /dev/null | grep -q ${NS}); then + echo "==> Build failed to set organization name label on namespace" + clean_task_test_resources + check_controller_log ${1} + tear_down + echo "============== FAILED ===============" + exit 1 +fi +if ! $(kubectl get namespace -l 'organization.lagoon.sh/id=123' --no-headers 2> /dev/null | grep -q ${NS}); then + echo "==> Build failed to set organization id label on namespace" + clean_task_test_resources + check_controller_log ${1} + tear_down + echo "============== FAILED ===============" + exit 1 +fi + echo "==> Trigger a Task using kubectl apply to test dynamic secret mounting" kubectl -n $NS apply -f test-resources/dynamic-secret-in-task-project1-secret.yaml @@ -235,7 +261,7 @@ echo ' "routing_key":"ci-local-controller-kubernetes:builddeploy", "payload":"{ \"metadata\": { - \"name\": \"lagoon-build-8m5zypx\" + \"name\": \"lagoon-build-9m5zypx\" }, \"spec\": { \"build\": { @@ -246,7 +272,7 @@ echo ' \"project\": { \"name\": \"nginx-example\", \"environment\": \"main\", - \"uiLink\": \"https:\/\/dashboard.amazeeio.cloud\/projects\/project\/project-environment\/deployments\/lagoon-build-8m5zypx\", + \"uiLink\": \"https:\/\/dashboard.amazeeio.cloud\/projects\/project\/project-environment\/deployments\/lagoon-build-9m5zypx\", \"routerPattern\": \"main-nginx-example\", \"environmentType\": \"production\", \"productionEnvironment\": \"main\", @@ -274,7 +300,7 @@ echo ' curl -s -u guest:guest -H "Accept: application/json" -H "Content-Type:application/json" -X POST -d @payload.json http://172.17.0.1:15672/api/exchanges/%2f/lagoon-tasks/publish echo "" sleep 10 -check_lagoon_build lagoon-build-${LBUILD2} +check_lagoon_build lagoon-build-${LBUILD3} echo "==> Check pod cleanup worked" CHECK_COUNTER=1 diff --git a/controllers/v1beta1/build_helpers.go b/controllers/v1beta1/build_helpers.go index 50703ccb..82dd916d 100644 --- a/controllers/v1beta1/build_helpers.go +++ b/controllers/v1beta1/build_helpers.go @@ -106,6 +106,14 @@ func (r *LagoonBuildReconciler) getOrCreateNamespace(ctx context.Context, namesp "lagoon.sh/environmentType": lagoonBuild.Spec.Project.EnvironmentType, "lagoon.sh/controller": r.ControllerNamespace, } + if lagoonBuild.Spec.Project.Organization != nil { + if lagoonBuild.Spec.Project.Organization.ID != nil { + nsLabels["organization.lagoon.sh/id"] = fmt.Sprintf("%d", *lagoonBuild.Spec.Project.Organization.ID) + } + if lagoonBuild.Spec.Project.Organization.Name != "" { + nsLabels["organization.lagoon.sh/name"] = lagoonBuild.Spec.Project.Organization.Name + } + } if lagoonBuild.Spec.Project.ID != nil { nsLabels["lagoon.sh/projectId"] = fmt.Sprintf("%d", *lagoonBuild.Spec.Project.ID) } @@ -820,6 +828,14 @@ func (r *LagoonBuildReconciler) processBuild(ctx context.Context, opLog logr.Log }, }, } + if lagoonBuild.Spec.Project.Organization != nil { + if lagoonBuild.Spec.Project.Organization.ID != nil { + newPod.ObjectMeta.Labels["organization.lagoon.sh/id"] = fmt.Sprintf("%d", *lagoonBuild.Spec.Project.Organization.ID) + } + if lagoonBuild.Spec.Project.Organization.Name != "" { + newPod.ObjectMeta.Labels["organization.lagoon.sh/name"] = lagoonBuild.Spec.Project.Organization.Name + } + } // set the pod security context, if defined to a non-default value if r.BuildPodRunAsUser != 0 || r.BuildPodRunAsGroup != 0 || diff --git a/controllers/v1beta1/task_controller.go b/controllers/v1beta1/task_controller.go index dae8387c..6442ac57 100644 --- a/controllers/v1beta1/task_controller.go +++ b/controllers/v1beta1/task_controller.go @@ -271,6 +271,14 @@ func (r *LagoonTaskReconciler) getTaskPodDeployment(ctx context.Context, lagoonT }, Spec: dep.Spec.Template.Spec, } + if lagoonTask.Spec.Project.Organization != nil { + if lagoonTask.Spec.Project.Organization.ID != nil { + taskPod.ObjectMeta.Labels["organization.lagoon.sh/id"] = fmt.Sprintf("%d", *lagoonTask.Spec.Project.Organization.ID) + } + if lagoonTask.Spec.Project.Organization.Name != "" { + taskPod.ObjectMeta.Labels["organization.lagoon.sh/name"] = lagoonTask.Spec.Project.Organization.Name + } + } return taskPod, nil } } @@ -564,6 +572,14 @@ func (r *LagoonTaskReconciler) createAdvancedTask(ctx context.Context, lagoonTas }, }, } + if lagoonTask.Spec.Project.Organization != nil { + if lagoonTask.Spec.Project.Organization.ID != nil { + newPod.ObjectMeta.Labels["organization.lagoon.sh/id"] = fmt.Sprintf("%d", *lagoonTask.Spec.Project.Organization.ID) + } + if lagoonTask.Spec.Project.Organization.Name != "" { + newPod.ObjectMeta.Labels["organization.lagoon.sh/name"] = lagoonTask.Spec.Project.Organization.Name + } + } if lagoonTask.Spec.AdvancedTask.DeployerToken { // start this with the serviceaccount so that it gets the token mounted into it newPod.Spec.ServiceAccountName = "lagoon-deployer" diff --git a/test-resources/example-project2.yaml b/test-resources/example-project2.yaml index 36eadd79..e95044a4 100644 --- a/test-resources/example-project2.yaml +++ b/test-resources/example-project2.yaml @@ -1,7 +1,7 @@ kind: LagoonBuild apiVersion: crd.lagoon.sh/v1beta1 metadata: - name: lagoon-build-9m5zypx + name: lagoon-build-8m5zypx spec: build: ci: 'true' #to make sure that readwritemany is changed to readwriteonce @@ -9,6 +9,9 @@ spec: gitReference: origin/main project: name: nginx-example + organization: + id: 123 + name: test-org environment: main uiLink: https://dashboard.amazeeio.cloud/projects/project/project-environment/deployments/lagoon-build-7m5zypx routerPattern: 'main-nginx-example'