diff --git a/api/v1/kustomization_types.go b/api/v1/kustomization_types.go index 1f530647..ea6c92ae 100644 --- a/api/v1/kustomization_types.go +++ b/api/v1/kustomization_types.go @@ -99,6 +99,20 @@ type KustomizationSpec struct { // +optional HealthChecks []meta.NamespacedObjectKindReference `json:"healthChecks,omitempty"` + // NamePrefix will prefix the names of all managed resources. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=200 + // +kubebuilder:validation:Optional + // +optional + NamePrefix string `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"` + + // NameSuffix will suffix the names of all managed resources. + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=200 + // +kubebuilder:validation:Optional + // +optional + NameSuffix string `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"` + // Strategic merge and JSON patches, defined as inline YAML objects, // capable of targeting objects based on kind, label and annotation selectors. // +optional diff --git a/config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml b/config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml index 74a584f4..b069696d 100644 --- a/config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml +++ b/config/crd/bases/kustomize.toolkit.fluxcd.io_kustomizations.yaml @@ -221,6 +221,16 @@ spec: required: - secretRef type: object + namePrefix: + description: NamePrefix will prefix the names of all managed resources. + maxLength: 200 + minLength: 1 + type: string + nameSuffix: + description: NameSuffix will suffix the names of all managed resources. + maxLength: 200 + minLength: 1 + type: string patches: description: |- Strategic merge and JSON patches, defined as inline YAML objects, diff --git a/docs/api/v1/kustomize.md b/docs/api/v1/kustomize.md index d279ce3c..7a2281ad 100644 --- a/docs/api/v1/kustomize.md +++ b/docs/api/v1/kustomize.md @@ -222,6 +222,30 @@ bool +namePrefix
+ +string + + + +(Optional) +

NamePrefix will prefix the names of all managed resources.

+ + + + +nameSuffix
+ +string + + + +(Optional) +

NameSuffix will suffix the names of all managed resources.

+ + + + patches
@@ -706,6 +730,30 @@ bool +namePrefix
+ +string + + + +(Optional) +

NamePrefix will prefix the names of all managed resources.

+ + + + +nameSuffix
+ +string + + + +(Optional) +

NameSuffix will suffix the names of all managed resources.

+ + + + patches
diff --git a/docs/spec/v1/kustomizations.md b/docs/spec/v1/kustomizations.md index 51b2d0ff..620291e4 100644 --- a/docs/spec/v1/kustomizations.md +++ b/docs/spec/v1/kustomizations.md @@ -401,6 +401,22 @@ should be applied to all the Kustomization's resources. It has two optional fiel on an object. Any existing annotation will be overridden if it matches with a key in this map. +### Name Prefix and Suffix + +`.spec.namePrefix` and `.spec.nameSuffix` are optional fields used to specify a prefix and suffix +to be added to the names of all the resources in the Kustomization. + +```yaml +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: app +spec: + # ...omitted for brevity + namePrefix: "prefix-" + nameSuffix: "-suffix" +``` + ### Patches `.spec.patches` is an optional list used to specify [Kustomize `patches`](https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/patches/) diff --git a/go.mod b/go.mod index 4a8db046..77f331ef 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/fluxcd/pkg/apis/kustomize v1.4.0 github.com/fluxcd/pkg/apis/meta v1.4.0 github.com/fluxcd/pkg/http/fetch v0.10.0 - github.com/fluxcd/pkg/kustomize v1.9.0 + github.com/fluxcd/pkg/kustomize v1.10.0 github.com/fluxcd/pkg/runtime v0.46.0 github.com/fluxcd/pkg/ssa v0.38.0 github.com/fluxcd/pkg/tar v0.6.0 diff --git a/go.sum b/go.sum index 151b0160..d5485e1c 100644 --- a/go.sum +++ b/go.sum @@ -145,8 +145,8 @@ github.com/fluxcd/pkg/envsubst v1.0.0 h1:LD86BRNSCGJrvyrH2aX5/pit7RfbFpkzRXogwca github.com/fluxcd/pkg/envsubst v1.0.0/go.mod h1:VAcb4OxcRdsDix1TRtr/mtTqFGHmNQaOvXQO2REArFQ= github.com/fluxcd/pkg/http/fetch v0.10.0 h1:Uh1ZrPa4B4EDgi+NFrY7qP6g9vg1O6JHKg3+iJLtt1w= github.com/fluxcd/pkg/http/fetch v0.10.0/go.mod h1:zZOsAqn7iODap40PVq29mcCPEKjDodYvamEaoN6tV/Q= -github.com/fluxcd/pkg/kustomize v1.9.0 h1:bqS3mXiK1q5TpUtIO5I5b+v/0r96NGJBiearKGUhicA= -github.com/fluxcd/pkg/kustomize v1.9.0/go.mod h1:PBerk0KzZN/IXaGociVp4MSMvsUQB0jR1P2SqSdixz0= +github.com/fluxcd/pkg/kustomize v1.10.0 h1:5upGCY1wgC26chNBqKiaQjtDUm2qJQTDVFLvKNPQb4I= +github.com/fluxcd/pkg/kustomize v1.10.0/go.mod h1:PBerk0KzZN/IXaGociVp4MSMvsUQB0jR1P2SqSdixz0= github.com/fluxcd/pkg/runtime v0.46.0 h1:+pxFwTk8j8lZIS9Vyc8EJbgvmFp9JqeT6pfLo/0iP98= github.com/fluxcd/pkg/runtime v0.46.0/go.mod h1:d9BaIjqoHL71fYeZsssrt08UFONGN2WQRaJ/Ay2d1Cc= github.com/fluxcd/pkg/sourceignore v0.6.0 h1:kD6QXL/upPEX66UpR669yK1Bxr/GtjzmZiqBeYpunUQ= diff --git a/internal/controller/kustomization_controller.go b/internal/controller/kustomization_controller.go index 7022475d..0e313ed2 100644 --- a/internal/controller/kustomization_controller.go +++ b/internal/controller/kustomization_controller.go @@ -27,6 +27,7 @@ import ( "time" securejoin "github.com/cyphar/filepath-securejoin" + "github.com/fluxcd/pkg/ssa/normalize" ssautil "github.com/fluxcd/pkg/ssa/utils" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -293,6 +294,7 @@ func (r *KustomizationReconciler) reconcile( obj *kustomizev1.Kustomization, src sourcev1.Source, patcher *patch.SerialPatcher) error { + log := ctrl.LoggerFrom(ctx) // Update status with the reconciliation progress. revision := src.GetArtifact().Revision @@ -317,7 +319,11 @@ func (r *KustomizationReconciler) reconcile( return err } - defer os.RemoveAll(tmpDir) + defer func(path string) { + if err := os.RemoveAll(path); err != nil { + log.Error(err, "failed to remove tmp dir", "path", path) + } + }(tmpDir) // Download artifact and extract files to the tmp dir. if err = fetch.NewArchiveFetcherWithLogger( @@ -653,12 +659,12 @@ func (r *KustomizationReconciler) apply(ctx context.Context, objects []*unstructured.Unstructured) (bool, *ssa.ChangeSet, error) { log := ctrl.LoggerFrom(ctx) - if err := ssa.SetNativeKindsDefaults(objects); err != nil { + if err := normalize.UnstructuredList(objects); err != nil { return false, nil, err } - if meta := obj.Spec.CommonMetadata; meta != nil { - ssautil.SetCommonMetadata(objects, meta.Labels, meta.Annotations) + if cmeta := obj.Spec.CommonMetadata; cmeta != nil { + ssautil.SetCommonMetadata(objects, cmeta.Labels, cmeta.Annotations) } applyOpts := ssa.DefaultApplyOptions() diff --git a/internal/controller/kustomization_transformer_test.go b/internal/controller/kustomization_transformer_test.go index e158de43..1ff2d3b0 100644 --- a/internal/controller/kustomization_transformer_test.go +++ b/internal/controller/kustomization_transformer_test.go @@ -146,6 +146,91 @@ data: }) } +func TestKustomizationReconciler_NamePrefixSuffix(t *testing.T) { + g := NewWithT(t) + id := "np-" + randStringRunes(5) + revision := "v1.0.0" + resultK := &kustomizev1.Kustomization{} + + err := createNamespace(id) + g.Expect(err).NotTo(HaveOccurred(), "failed to create test namespace") + + err = createKubeConfigSecret(id) + g.Expect(err).NotTo(HaveOccurred(), "failed to create kubeconfig secret") + + manifests := func(name string) []testserver.File { + return []testserver.File{ + { + Name: "config.yaml", + Body: fmt.Sprintf(`--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: %[1]s + annotations: + tenant: test +data: + key: val +`, name), + }, + } + } + + artifact, err := testServer.ArtifactFromFiles(manifests(id)) + g.Expect(err).NotTo(HaveOccurred()) + + repositoryName := types.NamespacedName{ + Name: fmt.Sprintf("cm-%s", randStringRunes(5)), + Namespace: id, + } + + err = applyGitRepository(repositoryName, artifact, revision) + g.Expect(err).NotTo(HaveOccurred()) + + kustomizationKey := types.NamespacedName{ + Name: fmt.Sprintf("cm-%s", randStringRunes(5)), + Namespace: id, + } + kustomization := &kustomizev1.Kustomization{ + ObjectMeta: metav1.ObjectMeta{ + Name: kustomizationKey.Name, + Namespace: kustomizationKey.Namespace, + }, + Spec: kustomizev1.KustomizationSpec{ + Interval: metav1.Duration{Duration: 2 * time.Minute}, + Path: "./", + KubeConfig: &meta.KubeConfigReference{ + SecretRef: meta.SecretKeyReference{ + Name: "kubeconfig", + }, + }, + SourceRef: kustomizev1.CrossNamespaceSourceReference{ + Name: repositoryName.Name, + Namespace: repositoryName.Namespace, + Kind: sourcev1.GitRepositoryKind, + }, + NamePrefix: "prefix-", + NameSuffix: "-suffix", + TargetNamespace: id, + }, + } + + g.Expect(k8sClient.Create(context.Background(), kustomization)).To(Succeed()) + + t.Run("sets name prefix and suffix", func(t *testing.T) { + g := NewWithT(t) + g.Eventually(func() bool { + _ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK) + return isReconcileSuccess(resultK) + }, timeout, time.Second).Should(BeTrue()) + kstatusCheck.CheckErr(ctx, resultK) + + name := fmt.Sprintf("prefix-%s-suffix", id) + var cm corev1.ConfigMap + g.Expect(k8sClient.Get(context.Background(), client.ObjectKey{Name: name, Namespace: id}, &cm)).To(Succeed()) + }) +} + func TestKustomizationReconciler_KustomizeTransformer(t *testing.T) { g := NewWithT(t) id := "transformers-" + randStringRunes(5)