From 0b90c175b2678c5804b4219544401b992b95f1de Mon Sep 17 00:00:00 2001 From: shreddedbacon Date: Tue, 5 May 2026 16:22:22 +1000 Subject: [PATCH] feat: support for organization deploy keys --- api/lagoon/v1beta2/lagoonbuild_types.go | 1 + api/lagoon/v1beta2/zz_generated.deepcopy.go | 5 ++ .../crd/bases/crd.lagoon.sh_lagoonbuilds.yaml | 3 + internal/controllers/v1beta2/build_helpers.go | 60 +++++++++++++++++++ 4 files changed, 69 insertions(+) diff --git a/api/lagoon/v1beta2/lagoonbuild_types.go b/api/lagoon/v1beta2/lagoonbuild_types.go index a585e178..1034b65d 100644 --- a/api/lagoon/v1beta2/lagoonbuild_types.go +++ b/api/lagoon/v1beta2/lagoonbuild_types.go @@ -129,6 +129,7 @@ type Project struct { ProjectSecret string `json:"projectSecret"` SubFolder string `json:"subfolder,omitempty"` Key []byte `json:"key"` + OrganizationKey []byte `json:"organizationKey,omitempty"` Monitoring Monitoring `json:"monitoring"` Variables LagoonVariables `json:"variables"` Registry string `json:"registry,omitempty"` diff --git a/api/lagoon/v1beta2/zz_generated.deepcopy.go b/api/lagoon/v1beta2/zz_generated.deepcopy.go index 67a871f4..f826b706 100644 --- a/api/lagoon/v1beta2/zz_generated.deepcopy.go +++ b/api/lagoon/v1beta2/zz_generated.deepcopy.go @@ -508,6 +508,11 @@ func (in *Project) DeepCopyInto(out *Project) { *out = make([]byte, len(*in)) copy(*out, *in) } + if in.OrganizationKey != nil { + in, out := &in.OrganizationKey, &out.OrganizationKey + *out = make([]byte, len(*in)) + copy(*out, *in) + } out.Monitoring = in.Monitoring in.Variables.DeepCopyInto(&out.Variables) if in.EnvironmentIdling != nil { diff --git a/config/crd/bases/crd.lagoon.sh_lagoonbuilds.yaml b/config/crd/bases/crd.lagoon.sh_lagoonbuilds.yaml index 504ea1c9..72e5c681 100644 --- a/config/crd/bases/crd.lagoon.sh_lagoonbuilds.yaml +++ b/config/crd/bases/crd.lagoon.sh_lagoonbuilds.yaml @@ -223,6 +223,9 @@ spec: name: type: string type: object + organizationKey: + format: byte + type: string productionEnvironment: type: string projectIdling: diff --git a/internal/controllers/v1beta2/build_helpers.go b/internal/controllers/v1beta2/build_helpers.go index 4f1038f2..62c347eb 100644 --- a/internal/controllers/v1beta2/build_helpers.go +++ b/internal/controllers/v1beta2/build_helpers.go @@ -111,6 +111,40 @@ func (r *LagoonBuildReconciler) getCreateOrUpdateSSHKeySecret(ctx context.Contex return nil } +// getCreateOrUpdateOrganizationKeySecret will create or update the ssh key. +func (r *LagoonBuildReconciler) getCreateOrUpdateOrganizationKeySecret(ctx context.Context, + sshKey *corev1.Secret, + spec lagooncrd.LagoonBuildSpec, + ns string) error { + sshKey.ObjectMeta = metav1.ObjectMeta{ + Name: "lagoon-organization-key", + Namespace: ns, + } + sshKey.Type = "kubernetes.io/ssh-auth" + sshKey.Data = map[string][]byte{ + "ssh-privatekey": spec.Project.OrganizationKey, + } + err := r.Get(ctx, types.NamespacedName{ + Namespace: ns, + Name: "lagoon-organization-key", + }, sshKey) + if err != nil { + if err := r.Create(ctx, sshKey); err != nil { + return fmt.Errorf("there was an error creating the lagoon-sshkey. Error was: %v", err) + } + } + // if the keys are different, then load in the new key from the spec + if !bytes.Equal(sshKey.Data["ssh-privatekey"], spec.Project.OrganizationKey) { + sshKey.Data = map[string][]byte{ + "ssh-privatekey": spec.Project.OrganizationKey, + } + if err := r.Update(ctx, sshKey); err != nil { + return fmt.Errorf("there was an error updating the lagoon-sshkey. Error was: %v", err) + } + } + return nil +} + // processBuild will actually process the build. func (r *LagoonBuildReconciler) processBuild(ctx context.Context, opLog logr.Logger, lagoonBuild lagooncrd.LagoonBuild) error { // we run these steps again just to be sure that it gets updated/created if it hasn't already @@ -124,6 +158,16 @@ func (r *LagoonBuildReconciler) processBuild(ctx context.Context, opLog logr.Log if err != nil { return err } + if lagoonBuild.Spec.Project.OrganizationKey != nil { + if r.EnableDebug { + opLog.Info(fmt.Sprintf("Checking `lagoon-organization-key` Secret exists: %s", lagoonBuild.Name)) + } + sshKey2 := &corev1.Secret{} + err := r.getCreateOrUpdateOrganizationKeySecret(ctx, sshKey2, lagoonBuild.Spec, lagoonBuild.Namespace) + if err != nil { + return err + } + } // create the `lagoon-deployer` ServiceAccount if r.EnableDebug { @@ -604,6 +648,22 @@ func (r *LagoonBuildReconciler) processBuild(ctx context.Context, opLog logr.Log MountPath: "/var/run/secrets/lagoon/ssh", }, } + if lagoonBuild.Spec.Project.OrganizationKey != nil { + volumes = append(volumes, corev1.Volume{ + Name: "lagoon-organization-key", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "lagoon-organization-key", + DefaultMode: helpers.Int32Ptr(420), + }, + }, + }) + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: "lagoon-organization-key", + ReadOnly: true, + MountPath: "/var/run/secrets/lagoon/organization", + }) + } // if the existing token exists, mount it if serviceaccountTokenSecret != "" {