diff --git a/docs/api-reference/landscapekit-v1alpha1.md b/docs/api-reference/landscapekit-v1alpha1.md index 0e58c231..9b23792e 100644 --- a/docs/api-reference/landscapekit-v1alpha1.md +++ b/docs/api-reference/landscapekit-v1alpha1.md @@ -81,6 +81,23 @@ _Appears in:_ +#### MergeMode + +_Underlying type:_ _string_ + +MergeMode controls how operator overwrites are handled during three-way merge. + + + +_Appears in:_ +- [LandscapeKitConfiguration](#landscapekitconfiguration) + +| Field | Description | +| --- | --- | +| `Hint` | MergeModeHint annotates operator-overwritten values with a comment showing the current GLK default.
| +| `Silent` | MergeModeSilent retains operator overwrites without annotation.
| + + #### OCMComponent diff --git a/example/20-componentconfig-glk.yaml b/example/20-componentconfig-glk.yaml index 2eab32f8..77b010d7 100644 --- a/example/20-componentconfig-glk.yaml +++ b/example/20-componentconfig-glk.yaml @@ -19,3 +19,4 @@ kind: LandscapeKitConfiguration # - component-name # versionConfig: # defaultVersionsUpdateStrategy: ReleaseBranch +# mergeMode: Hint diff --git a/hack/tools/prettify/main.go b/hack/tools/prettify/main.go index dbd33c7a..1b278375 100644 --- a/hack/tools/prettify/main.go +++ b/hack/tools/prettify/main.go @@ -11,6 +11,7 @@ import ( flag "github.com/spf13/pflag" + configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" "github.com/gardener/gardener-landscape-kit/pkg/utils/meta" ) @@ -31,7 +32,7 @@ func main() { if err != nil { log.Fatalf("Error reading file: %s", err) } - prettified, err := meta.ThreeWayMergeManifest(nil, content, nil) + prettified, err := meta.ThreeWayMergeManifest(nil, content, nil, configv1alpha1.MergeModeSilent) if err != nil { log.Fatalf("Marshalling failed: %s", err) } diff --git a/pkg/apis/config/v1alpha1/defaults.go b/pkg/apis/config/v1alpha1/defaults.go index 9c3a743a..0e151fef 100644 --- a/pkg/apis/config/v1alpha1/defaults.go +++ b/pkg/apis/config/v1alpha1/defaults.go @@ -3,3 +3,17 @@ // SPDX-License-Identifier: Apache-2.0 package v1alpha1 + +// SetDefaults_LandscapeKitConfiguration sets default values for LandscapeKitConfiguration fields. +func SetDefaults_LandscapeKitConfiguration(obj *LandscapeKitConfiguration) { + if obj.VersionConfig == nil { + obj.VersionConfig = &VersionConfiguration{} + } + if obj.VersionConfig.DefaultVersionsUpdateStrategy == nil { + obj.VersionConfig.DefaultVersionsUpdateStrategy = new(DefaultVersionsUpdateStrategyDisabled) + } + + if obj.MergeMode == nil { + obj.MergeMode = new(MergeModeHint) + } +} diff --git a/pkg/apis/config/v1alpha1/types.go b/pkg/apis/config/v1alpha1/types.go index 7f893fa9..a6c4207d 100644 --- a/pkg/apis/config/v1alpha1/types.go +++ b/pkg/apis/config/v1alpha1/types.go @@ -26,6 +26,11 @@ type LandscapeKitConfiguration struct { // VersionConfig is the configuration for versioning. // +optional VersionConfig *VersionConfiguration `json:"versionConfig,omitempty"` + // MergeMode determines how merge conflicts are resolved: + // - "Hint" (default): New default values from GLK are added as comments after any customized values. + // - "Silent": Operator-customized values are retained, new default values are omitted. + // +optional + MergeMode *MergeMode `json:"mergeMode,omitempty"` } // ComponentsConfiguration contains configuration for components. @@ -119,3 +124,19 @@ type VersionConfiguration struct { // +optional DefaultVersionsUpdateStrategy *DefaultVersionsUpdateStrategy `json:"defaultVersionsUpdateStrategy,omitempty"` } + +// MergeMode controls how operator overwrites are handled during three-way merge. +type MergeMode string + +const ( + // MergeModeHint annotates operator-overwritten values with a comment showing the current GLK default. + MergeModeHint MergeMode = "Hint" + // MergeModeSilent retains operator overwrites without annotation. + MergeModeSilent MergeMode = "Silent" +) + +// AllowedMergeModes lists all allowed merge modes. +var AllowedMergeModes = []string{ + string(MergeModeHint), + string(MergeModeSilent), +} diff --git a/pkg/apis/config/v1alpha1/validation/validation.go b/pkg/apis/config/v1alpha1/validation/validation.go index 1144b815..704cee8d 100644 --- a/pkg/apis/config/v1alpha1/validation/validation.go +++ b/pkg/apis/config/v1alpha1/validation/validation.go @@ -36,6 +36,10 @@ func ValidateLandscapeKitConfiguration(conf *configv1alpha1.LandscapeKitConfigur allErrs = append(allErrs, ValidateVersionConfig(conf.VersionConfig, field.NewPath("versionConfig"))...) } + if conf.MergeMode != nil && !slices.Contains(configv1alpha1.AllowedMergeModes, string(*conf.MergeMode)) { + allErrs = append(allErrs, field.NotSupported(field.NewPath("mergeMode"), *conf.MergeMode, configv1alpha1.AllowedMergeModes)) + } + return allErrs } diff --git a/pkg/apis/config/v1alpha1/validation/validation_test.go b/pkg/apis/config/v1alpha1/validation/validation_test.go index 1d8614c5..a0dcb09e 100644 --- a/pkg/apis/config/v1alpha1/validation/validation_test.go +++ b/pkg/apis/config/v1alpha1/validation/validation_test.go @@ -294,6 +294,43 @@ var _ = Describe("Validation", func() { Expect(errList).To(BeEmpty()) }) }) + + Context("MergeMode Configuration", func() { + It("should pass with valid MergeMode values", func() { + for _, mode := range []v1alpha1.MergeMode{ + v1alpha1.MergeModeHint, + v1alpha1.MergeModeSilent, + } { + conf := &v1alpha1.LandscapeKitConfiguration{ + MergeMode: &mode, + } + errList := validation.ValidateLandscapeKitConfiguration(conf) + Expect(errList).To(BeEmpty(), fmt.Sprintf("MergeMode %q should be valid", mode)) + } + }) + + It("should pass when MergeMode is not set", func() { + conf := &v1alpha1.LandscapeKitConfiguration{} + errList := validation.ValidateLandscapeKitConfiguration(conf) + Expect(errList).To(BeEmpty()) + }) + + It("should fail with an invalid MergeMode value", func() { + invalid := v1alpha1.MergeMode("Invalid") + conf := &v1alpha1.LandscapeKitConfiguration{ + MergeMode: &invalid, + } + + errList := validation.ValidateLandscapeKitConfiguration(conf) + Expect(errList).To(ConsistOf( + PointTo(MatchFields(IgnoreExtras, Fields{ + "Type": Equal(field.ErrorTypeNotSupported), + "Field": Equal("mergeMode"), + "BadValue": Equal(invalid), + })), + )) + }) + }) }) }) diff --git a/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go index 5d2b1307..3ce65028 100644 --- a/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -112,6 +112,11 @@ func (in *LandscapeKitConfiguration) DeepCopyInto(out *LandscapeKitConfiguration *out = new(VersionConfiguration) (*in).DeepCopyInto(*out) } + if in.MergeMode != nil { + in, out := &in.MergeMode, &out.MergeMode + *out = new(MergeMode) + **out = **in + } return } diff --git a/pkg/apis/config/v1alpha1/zz_generated.defaults.go b/pkg/apis/config/v1alpha1/zz_generated.defaults.go index dce68e63..e134802a 100644 --- a/pkg/apis/config/v1alpha1/zz_generated.defaults.go +++ b/pkg/apis/config/v1alpha1/zz_generated.defaults.go @@ -17,5 +17,10 @@ import ( // Public to allow building arbitrary schemes. // All generated defaulters are covering - they call all nested defaulters. func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&LandscapeKitConfiguration{}, func(obj interface{}) { SetObjectDefaults_LandscapeKitConfiguration(obj.(*LandscapeKitConfiguration)) }) return nil } + +func SetObjectDefaults_LandscapeKitConfiguration(in *LandscapeKitConfiguration) { + SetDefaults_LandscapeKitConfiguration(in) +} diff --git a/pkg/cmd/resolve/plain/plain.go b/pkg/cmd/resolve/plain/plain.go index 1329d140..96ce7052 100644 --- a/pkg/cmd/resolve/plain/plain.go +++ b/pkg/cmd/resolve/plain/plain.go @@ -6,10 +6,8 @@ package plain import ( "context" - "errors" "fmt" - "os" - "path" + "strings" "github.com/spf13/afero" "github.com/spf13/cobra" @@ -22,6 +20,7 @@ import ( configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" "github.com/gardener/gardener-landscape-kit/pkg/cmd" utilscomponentvector "github.com/gardener/gardener-landscape-kit/pkg/utils/componentvector" + utilsfiles "github.com/gardener/gardener-landscape-kit/pkg/utils/files" ) var configDecoder runtime.Decoder @@ -111,7 +110,7 @@ func (o *Options) loadConfigFile(filename string) error { func run(_ context.Context, opts *Options) error { if opts.Config != nil && opts.Config.VersionConfig != nil { - if updateStrategy := opts.Config.VersionConfig.DefaultVersionsUpdateStrategy; updateStrategy != nil && *updateStrategy == configv1alpha1.DefaultVersionsUpdateStrategyReleaseBranch { + if *opts.Config.VersionConfig.DefaultVersionsUpdateStrategy == configv1alpha1.DefaultVersionsUpdateStrategyReleaseBranch { opts.Log.Info("Updating default component vector file from the release branch", "branch", utilscomponentvector.GetReleaseBranchName()) var err error // The componentvector.DefaultComponentsYAML is intentionally overridden, so that subsequently it can be used to extract the updated default component vector versions. @@ -122,21 +121,30 @@ func run(_ context.Context, opts *Options) error { } } - compVectorFile := path.Join(opts.TargetDirPath, utilscomponentvector.ComponentVectorFilename) - opts.Log.Info("Writing component vector file", "file", compVectorFile) - - var customBytes []byte - var err error - if customBytes, err = opts.fs.ReadFile(compVectorFile); err != nil { - if !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("failed to read component vector override file: %w", err) - } + var ( + err error + newDefaultCV utilscomponentvector.Interface + newDefaultBytes []byte + ) + newDefaultCV, err = utilscomponentvector.NewWithOverride(componentvector.DefaultComponentsYAML) + if err != nil { + return fmt.Errorf("failed to build default component vector: %w", err) } - - componentVector, err := utilscomponentvector.NewWithOverride(componentvector.DefaultComponentsYAML, customBytes) + newDefaultBytes, err = utilscomponentvector.NameVersionBytes(newDefaultCV) if err != nil { - return fmt.Errorf("failed to create component vector: %w", err) + return fmt.Errorf("failed to marshal default component vector: %w", err) } - return utilscomponentvector.WriteComponentVectorFile(opts.fs, opts.TargetDirPath, componentVector) + header := []byte(strings.Join([]string{ + "# This file is updated by the gardener-landscape-kit.", + "# If this file is present in the root of a gardener-landscape-kit-managed repository, the component versions will be used as overrides.", + "# If custom component versions should be used, it is recommended to modify the specified versions here and run the `generate` command afterwards.", + }, "\n") + "\n") + newDefaultBytes = append(header, newDefaultBytes...) + + if err := utilsfiles.WriteObjectsToFilesystem(map[string][]byte{utilscomponentvector.ComponentVectorFilename: newDefaultBytes}, opts.TargetDirPath, "", opts.fs, *opts.Config.MergeMode); err != nil { + return fmt.Errorf("failed to write updated component vector: %w", err) + } + + return nil } diff --git a/pkg/components/flux/component.go b/pkg/components/flux/component.go index 6a47c9aa..54b2e4f0 100644 --- a/pkg/components/flux/component.go +++ b/pkg/components/flux/component.go @@ -94,7 +94,7 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { delete(objects, "flux-system/gitignore") delete(objects, "flux-system/doc.go") - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), DirName, opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), DirName, opts.GetFilesystem(), opts.GetMergeMode()) } func writeGitignoreFile(options components.LandscapeOptions) error { diff --git a/pkg/components/flux/component_test.go b/pkg/components/flux/component_test.go index e0df97fd..562ced8d 100644 --- a/pkg/components/flux/component_test.go +++ b/pkg/components/flux/component_test.go @@ -54,6 +54,16 @@ var _ = Describe("Flux Component Generation", func() { fs = afero.Afero{Fs: afero.NewMemMapFs()} + config := &v1alpha1.LandscapeKitConfiguration{ + Git: &v1alpha1.GitRepository{ + URL: repoURL, + Paths: v1alpha1.PathConfiguration{ + Landscape: relativeLandscapePath, + }, + }, + } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(config) + var err error opts, err = components.NewLandscapeOptions( &generateoptions.Options{ @@ -61,14 +71,7 @@ var _ = Describe("Flux Component Generation", func() { Log: logr.Discard(), }, TargetDirPath: targetPath, - Config: &v1alpha1.LandscapeKitConfiguration{ - Git: &v1alpha1.GitRepository{ - URL: repoURL, - Paths: v1alpha1.PathConfiguration{ - Landscape: relativeLandscapePath, - }, - }, - }, + Config: config, }, fs, ) diff --git a/pkg/components/gardener-extensions/networking-calico/component.go b/pkg/components/gardener-extensions/networking-calico/component.go index c297146f..eeb3d194 100644 --- a/pkg/components/gardener-extensions/networking-calico/component.go +++ b/pkg/components/gardener-extensions/networking-calico/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/networking-calico/component_test.go b/pkg/components/gardener-extensions/networking-calico/component_test.go index 351e731c..e1b6b341 100644 --- a/pkg/components/gardener-extensions/networking-calico/component_test.go +++ b/pkg/components/gardener-extensions/networking-calico/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/networking-cilium/component.go b/pkg/components/gardener-extensions/networking-cilium/component.go index b934ce4b..e39d9699 100644 --- a/pkg/components/gardener-extensions/networking-cilium/component.go +++ b/pkg/components/gardener-extensions/networking-cilium/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/networking-cilium/component_test.go b/pkg/components/gardener-extensions/networking-cilium/component_test.go index 802045ef..5093442f 100644 --- a/pkg/components/gardener-extensions/networking-cilium/component_test.go +++ b/pkg/components/gardener-extensions/networking-cilium/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/os-gardenlinux/component.go b/pkg/components/gardener-extensions/os-gardenlinux/component.go index 2cc5eb0b..0ee9de54 100644 --- a/pkg/components/gardener-extensions/os-gardenlinux/component.go +++ b/pkg/components/gardener-extensions/os-gardenlinux/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/os-gardenlinux/component_test.go b/pkg/components/gardener-extensions/os-gardenlinux/component_test.go index 096175c5..c93dde59 100644 --- a/pkg/components/gardener-extensions/os-gardenlinux/component_test.go +++ b/pkg/components/gardener-extensions/os-gardenlinux/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/os-suse-chost/component.go b/pkg/components/gardener-extensions/os-suse-chost/component.go index 3e933845..2c7be6a1 100644 --- a/pkg/components/gardener-extensions/os-suse-chost/component.go +++ b/pkg/components/gardener-extensions/os-suse-chost/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/os-suse-chost/component_test.go b/pkg/components/gardener-extensions/os-suse-chost/component_test.go index fb3fa110..eb27155d 100644 --- a/pkg/components/gardener-extensions/os-suse-chost/component_test.go +++ b/pkg/components/gardener-extensions/os-suse-chost/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/provider-alicloud/component.go b/pkg/components/gardener-extensions/provider-alicloud/component.go index b703f156..3870483f 100644 --- a/pkg/components/gardener-extensions/provider-alicloud/component.go +++ b/pkg/components/gardener-extensions/provider-alicloud/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/provider-alicloud/component_test.go b/pkg/components/gardener-extensions/provider-alicloud/component_test.go index 499a1357..c94be716 100644 --- a/pkg/components/gardener-extensions/provider-alicloud/component_test.go +++ b/pkg/components/gardener-extensions/provider-alicloud/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/provider-aws/component.go b/pkg/components/gardener-extensions/provider-aws/component.go index 76e3408d..2d65d347 100644 --- a/pkg/components/gardener-extensions/provider-aws/component.go +++ b/pkg/components/gardener-extensions/provider-aws/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/provider-aws/component_test.go b/pkg/components/gardener-extensions/provider-aws/component_test.go index 17a395e2..678720d6 100644 --- a/pkg/components/gardener-extensions/provider-aws/component_test.go +++ b/pkg/components/gardener-extensions/provider-aws/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/provider-azure/component.go b/pkg/components/gardener-extensions/provider-azure/component.go index 6bbb7f71..2b75ca26 100644 --- a/pkg/components/gardener-extensions/provider-azure/component.go +++ b/pkg/components/gardener-extensions/provider-azure/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/provider-azure/component_test.go b/pkg/components/gardener-extensions/provider-azure/component_test.go index 481f991f..a65922f7 100644 --- a/pkg/components/gardener-extensions/provider-azure/component_test.go +++ b/pkg/components/gardener-extensions/provider-azure/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/provider-gcp/component.go b/pkg/components/gardener-extensions/provider-gcp/component.go index dbbd3271..c050ecf6 100644 --- a/pkg/components/gardener-extensions/provider-gcp/component.go +++ b/pkg/components/gardener-extensions/provider-gcp/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/provider-gcp/component_test.go b/pkg/components/gardener-extensions/provider-gcp/component_test.go index 7b932561..0b1dd0fd 100644 --- a/pkg/components/gardener-extensions/provider-gcp/component_test.go +++ b/pkg/components/gardener-extensions/provider-gcp/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/provider-openstack/component.go b/pkg/components/gardener-extensions/provider-openstack/component.go index 6ffc8e8f..071b468e 100644 --- a/pkg/components/gardener-extensions/provider-openstack/component.go +++ b/pkg/components/gardener-extensions/provider-openstack/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/provider-openstack/component_test.go b/pkg/components/gardener-extensions/provider-openstack/component_test.go index bad84c52..f4119b90 100644 --- a/pkg/components/gardener-extensions/provider-openstack/component_test.go +++ b/pkg/components/gardener-extensions/provider-openstack/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/runtime-gvisor/component.go b/pkg/components/gardener-extensions/runtime-gvisor/component.go index 3a6e0cb9..c095911f 100644 --- a/pkg/components/gardener-extensions/runtime-gvisor/component.go +++ b/pkg/components/gardener-extensions/runtime-gvisor/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/runtime-gvisor/component_test.go b/pkg/components/gardener-extensions/runtime-gvisor/component_test.go index 65aede92..9c73eccb 100644 --- a/pkg/components/gardener-extensions/runtime-gvisor/component_test.go +++ b/pkg/components/gardener-extensions/runtime-gvisor/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/shoot-cert-service/component.go b/pkg/components/gardener-extensions/shoot-cert-service/component.go index b8c1c6b3..03645180 100644 --- a/pkg/components/gardener-extensions/shoot-cert-service/component.go +++ b/pkg/components/gardener-extensions/shoot-cert-service/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/shoot-cert-service/component_test.go b/pkg/components/gardener-extensions/shoot-cert-service/component_test.go index d7d21bb2..35b75d90 100644 --- a/pkg/components/gardener-extensions/shoot-cert-service/component_test.go +++ b/pkg/components/gardener-extensions/shoot-cert-service/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/shoot-dns-service/component.go b/pkg/components/gardener-extensions/shoot-dns-service/component.go index b452074d..c0a87bd0 100644 --- a/pkg/components/gardener-extensions/shoot-dns-service/component.go +++ b/pkg/components/gardener-extensions/shoot-dns-service/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/shoot-dns-service/component_test.go b/pkg/components/gardener-extensions/shoot-dns-service/component_test.go index 9fb02d38..747c03d7 100644 --- a/pkg/components/gardener-extensions/shoot-dns-service/component_test.go +++ b/pkg/components/gardener-extensions/shoot-dns-service/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/shoot-networking-problemdetector/component.go b/pkg/components/gardener-extensions/shoot-networking-problemdetector/component.go index ab35bebd..9053be4d 100644 --- a/pkg/components/gardener-extensions/shoot-networking-problemdetector/component.go +++ b/pkg/components/gardener-extensions/shoot-networking-problemdetector/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/shoot-networking-problemdetector/component_test.go b/pkg/components/gardener-extensions/shoot-networking-problemdetector/component_test.go index 1a7e7f3c..7f4d2e5c 100644 --- a/pkg/components/gardener-extensions/shoot-networking-problemdetector/component_test.go +++ b/pkg/components/gardener-extensions/shoot-networking-problemdetector/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener-extensions/shoot-oidc-service/component.go b/pkg/components/gardener-extensions/shoot-oidc-service/component.go index 9e8d9dcf..9dbcd84e 100644 --- a/pkg/components/gardener-extensions/shoot-oidc-service/component.go +++ b/pkg/components/gardener-extensions/shoot-oidc-service/component.go @@ -78,7 +78,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -100,5 +100,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener-extensions/shoot-oidc-service/component_test.go b/pkg/components/gardener-extensions/shoot-oidc-service/component_test.go index 667aedcb..683bad3b 100644 --- a/pkg/components/gardener-extensions/shoot-oidc-service/component_test.go +++ b/pkg/components/gardener-extensions/shoot-oidc-service/component_test.go @@ -35,7 +35,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -68,6 +70,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener/garden/component.go b/pkg/components/gardener/garden/component.go index eefa0f2f..5cbc3c3d 100644 --- a/pkg/components/gardener/garden/component.go +++ b/pkg/components/gardener/garden/component.go @@ -71,7 +71,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -88,5 +88,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener/garden/component_test.go b/pkg/components/gardener/garden/component_test.go index 781adff3..455d17f2 100644 --- a/pkg/components/gardener/garden/component_test.go +++ b/pkg/components/gardener/garden/component_test.go @@ -30,7 +30,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -62,6 +64,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener/operator/component.go b/pkg/components/gardener/operator/component.go index 5873ca7e..0221ed8e 100644 --- a/pkg/components/gardener/operator/component.go +++ b/pkg/components/gardener/operator/component.go @@ -80,7 +80,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func getTemplateValues(opts components.Options) (map[string]any, error) { @@ -134,7 +134,7 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func getOCIImageReferenceFromComponentVector(name string, cv *utilscomponentvector.ComponentVector) (string, error) { diff --git a/pkg/components/gardener/operator/component_test.go b/pkg/components/gardener/operator/component_test.go index 5870a6dd..d080114a 100644 --- a/pkg/components/gardener/operator/component_test.go +++ b/pkg/components/gardener/operator/component_test.go @@ -37,7 +37,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -86,6 +88,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/gardener/virtual-garden-access/component.go b/pkg/components/gardener/virtual-garden-access/component.go index 513753c2..81dc4142 100644 --- a/pkg/components/gardener/virtual-garden-access/component.go +++ b/pkg/components/gardener/virtual-garden-access/component.go @@ -73,7 +73,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -90,5 +90,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/gardener/virtual-garden-access/component_test.go b/pkg/components/gardener/virtual-garden-access/component_test.go index 58c8856c..ed74d8f6 100644 --- a/pkg/components/gardener/virtual-garden-access/component_test.go +++ b/pkg/components/gardener/virtual-garden-access/component_test.go @@ -30,7 +30,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -70,6 +72,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/components/types.go b/pkg/components/types.go index ca38214b..fa23a42d 100644 --- a/pkg/components/types.go +++ b/pkg/components/types.go @@ -15,7 +15,7 @@ import ( "github.com/spf13/afero" "github.com/gardener/gardener-landscape-kit/componentvector" - "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" + configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" generateoptions "github.com/gardener/gardener-landscape-kit/pkg/cmd/generate/options" utilscomponentvector "github.com/gardener/gardener-landscape-kit/pkg/utils/componentvector" "github.com/gardener/gardener-landscape-kit/pkg/utils/files" @@ -36,6 +36,8 @@ type Options interface { GetFilesystem() afero.Afero // GetLogger returns the logger instance. GetLogger() logr.Logger + // GetMergeMode returns the configured mode to solve merge conflicts. + GetMergeMode() configv1alpha1.MergeMode } // LandscapeOptions is an interface for options passed to components for generating the landscape. @@ -43,7 +45,7 @@ type LandscapeOptions interface { Options // GetGitRepository returns the git repository information. - GetGitRepository() *v1alpha1.GitRepository + GetGitRepository() *configv1alpha1.GitRepository // GetRelativeBasePath returns the base directory that is relative to the target path. GetRelativeBasePath() string // GetRelativeLandscapePath returns the landscape directory that is relative to the target path. @@ -65,6 +67,7 @@ type options struct { targetPath string filesystem afero.Afero logger logr.Logger + mergeMode configv1alpha1.MergeMode } // GetComponentVector returns the component vector. @@ -87,6 +90,11 @@ func (o *options) GetLogger() logr.Logger { return o.logger } +// GetMergeMode returns the configured merge mode for three-way merges. +func (o *options) GetMergeMode() configv1alpha1.MergeMode { + return o.mergeMode +} + // NewOptions returns a new Options instance. func NewOptions(opts *generateoptions.Options, fs afero.Afero) (Options, error) { var customComponentVectors [][]byte @@ -113,11 +121,13 @@ func NewOptions(opts *generateoptions.Options, fs afero.Afero) (Options, error) if err != nil { return nil, fmt.Errorf("failed to create component vector: %w", err) } + return &options{ componentVector: componentVector, targetPath: path.Clean(opts.TargetDirPath), filesystem: fs, logger: opts.Log, + mergeMode: *opts.Config.MergeMode, }, nil } @@ -134,11 +144,11 @@ func readCustomComponentsFile(opts *generateoptions.Options, fs afero.Afero, fil type landscapeOptions struct { Options - gitRepository *v1alpha1.GitRepository + gitRepository *configv1alpha1.GitRepository } // GetGitRepository returns the git repository information. -func (l *landscapeOptions) GetGitRepository() *v1alpha1.GitRepository { +func (l *landscapeOptions) GetGitRepository() *configv1alpha1.GitRepository { return l.gitRepository } diff --git a/pkg/components/types_test.go b/pkg/components/types_test.go index cf468887..c08cd7d4 100644 --- a/pkg/components/types_test.go +++ b/pkg/components/types_test.go @@ -34,7 +34,9 @@ var _ = Describe("Types", func() { opts = &options.Options{ Options: &cmd.Options{}, TargetDirPath: "", + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(opts.Config) }) Describe("#GetTargetPath", func() { @@ -105,20 +107,9 @@ var _ = Describe("Types", func() { Expect(exists).To(BeFalse()) }) - It("should return an empty component vector when config is nil", func() { - opts.Config = nil - - componentOpts, err := components.NewOptions(opts, fs) - - Expect(err).NotTo(HaveOccurred()) - Expect(componentOpts.GetComponentVector()).NotTo(BeNil()) - - _, exists := componentOpts.GetComponentVector().FindComponentVersion("test-component") - Expect(exists).To(BeFalse()) - }) - - It("should return an empty component vector when VersionConfig is nil", func() { + It("should return an empty component vector when config is empty", func() { opts.Config = &v1alpha1.LandscapeKitConfiguration{} + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(opts.Config) componentOpts, err := components.NewOptions(opts, fs) @@ -199,7 +190,9 @@ var _ = Describe("Types", func() { Log: logger, }, TargetDirPath: "/path/to/target", + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(opts.Config) result, err := components.NewOptions(opts, fs) @@ -232,6 +225,7 @@ var _ = Describe("Types", func() { }, TargetDirPath: "", } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(opts.Config) }) Describe("#GetGitRepository", func() { @@ -285,6 +279,7 @@ var _ = Describe("Types", func() { }, }, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(opts.Config) result, err := components.NewLandscapeOptions(opts, fs) diff --git a/pkg/components/virtual-garden/garden-config/component.go b/pkg/components/virtual-garden/garden-config/component.go index 56227b00..1104f314 100644 --- a/pkg/components/virtual-garden/garden-config/component.go +++ b/pkg/components/virtual-garden/garden-config/component.go @@ -73,7 +73,7 @@ func writeBaseTemplateFiles(opts components.Options) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { @@ -90,5 +90,5 @@ func writeLandscapeTemplateFiles(opts components.LandscapeOptions) error { return err } - return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem()) + return files.WriteObjectsToFilesystem(objects, opts.GetTargetPath(), path.Join(components.DirName, ComponentDirectory), opts.GetFilesystem(), opts.GetMergeMode()) } diff --git a/pkg/components/virtual-garden/garden-config/component_test.go b/pkg/components/virtual-garden/garden-config/component_test.go index b17dca5e..5886dc9b 100644 --- a/pkg/components/virtual-garden/garden-config/component_test.go +++ b/pkg/components/virtual-garden/garden-config/component_test.go @@ -30,7 +30,9 @@ var _ = Describe("Component Generation", func() { generateOpts = &generateoptions.Options{ TargetDirPath: "/repo/baseDir", Options: cmdOpts, + Config: &v1alpha1.LandscapeKitConfiguration{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) Describe("#GenerateBase", func() { @@ -67,6 +69,7 @@ var _ = Describe("Component Generation", func() { generateOpts.Config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{Paths: v1alpha1.PathConfiguration{Landscape: "./landscapeDir", Base: "./baseDir"}}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) }) It("should generate only the flux kustomization into the landscape dir", func() { diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go index 396db1b1..d53e0ed7 100644 --- a/pkg/registry/registry_test.go +++ b/pkg/registry/registry_test.go @@ -35,10 +35,14 @@ var _ = Describe("Registry", func() { config = &v1alpha1.LandscapeKitConfiguration{ Git: &v1alpha1.GitRepository{}, } + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(config) var err error options, err = components.NewOptions( - &generateoptions.Options{Options: &cmd.Options{Log: logr.Discard()}}, + &generateoptions.Options{ + Options: &cmd.Options{Log: logr.Discard()}, + Config: config, + }, afero.Afero{Fs: afero.NewMemMapFs()}, ) Expect(err).NotTo(HaveOccurred()) diff --git a/pkg/utils/componentvector/componentvector.go b/pkg/utils/componentvector/componentvector.go index f8476c8f..9b24e6c9 100644 --- a/pkg/utils/componentvector/componentvector.go +++ b/pkg/utils/componentvector/componentvector.go @@ -7,18 +7,12 @@ package componentvector import ( "fmt" "maps" - "path/filepath" "reflect" "slices" - "strings" - "github.com/spf13/afero" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/utils/ptr" "sigs.k8s.io/yaml" - - "github.com/gardener/gardener-landscape-kit/componentvector" - "github.com/gardener/gardener-landscape-kit/pkg/utils/files" ) const ( @@ -220,82 +214,17 @@ func resourcesToUnstructuredMap(resources map[string]ResourceData) (map[string]a return unstructuredMap, nil } -const ( - defaultVersionCommentMarker = "# <-- gardener-landscape-kit version default" -) - -// stripDefaultVersionComments removes GLK-managed default-version comment lines from a components.yaml file. -// A line is considered GLK-managed when it contains the unique GLK marker suffix. -// Stripping them before the three-way merge ensures the canonical comment is always (re-)written on the next run, even when the user has edited the comment text. -func stripDefaultVersionComments(data []byte) []byte { - lines := strings.Split(string(data), "\n") - out := make([]string, 0, len(lines)) - for _, line := range lines { - if !strings.Contains(line, defaultVersionCommentMarker) { - out = append(out, line) - } - } - return []byte(strings.Join(out, "\n")) -} - -// WriteComponentVectorFile writes the component vector file effectively used to the target directory if applicable. -func WriteComponentVectorFile(fs afero.Afero, targetDirPath string, componentVector Interface) error { - var ( - comp = &Components{} - postGenerateDefaultVersionCommentFns []func(string) string - ) - cvDefault, err := NewWithOverride(componentvector.DefaultComponentsYAML) - if err != nil { - return fmt.Errorf("failed to build default component vector: %w", err) - } - for _, componentName := range componentVector.ComponentNames() { - componentVersion, _ := componentVector.FindComponentVersion(componentName) - comp.Components = append(comp.Components, &ComponentVector{ - Name: componentName, - Version: componentVersion, - }) - defaultVersion, found := cvDefault.FindComponentVersion(componentName) - if found && componentVersion != defaultVersion { - defaultVersionComment := "# version: " + defaultVersion + " " + defaultVersionCommentMarker - postGenerateDefaultVersionCommentFns = append(postGenerateDefaultVersionCommentFns, func(data string) string { - return strings.ReplaceAll(data, componentName+"\n", componentName+"\n"+defaultVersionComment+"\n") - }) - } +// NameVersionBytes marshals cv into a name+version-only Components YAML, stripping all other fields. +// This compact format is used for the .glk/defaults/ snapshot and as the three-way merge baseline in plain.go. +func NameVersionBytes(cv Interface) ([]byte, error) { + stripped := &Components{} + for _, name := range cv.ComponentNames() { + version, _ := cv.FindComponentVersion(name) + stripped.Components = append(stripped.Components, &ComponentVector{Name: name, Version: version}) } - data, err := yaml.Marshal(comp) + data, err := yaml.Marshal(stripped) if err != nil { - return fmt.Errorf("failed to marshal component vector: %w", err) - } - - header := []byte(strings.Join([]string{ - "# This file is updated by the gardener-landscape-kit.", - "# If this file is specified in the gardener-landscape-kit configuration file, the component versions will be used as overrides.", - "# If custom component versions should be used, it is recommended to modify the specified versions here and run the `generate` command afterwards.", - }, "\n") + "\n") - - // Before writing, strip any GLK-managed default-version comment lines from the on-disk file. - // This resets GLK-owned annotations so the canonical comment is always (re-)applied below, even when the user has edited or removed the comment line. - filePath := filepath.Join(targetDirPath, ComponentVectorFilename) - if existing, readErr := fs.ReadFile(filePath); readErr == nil { - if stripped := stripDefaultVersionComments(existing); string(stripped) != string(existing) { - if writeErr := fs.WriteFile(filePath, stripped, 0600); writeErr != nil { - return writeErr - } - } - } - - // Pass 1: write without default-version comments so the three-way merge operates on - // comment-free content. This establishes a clean baseline in the .glk/defaults/ snapshot. - dataWithoutComments := append(header, data...) - if err := files.WriteObjectsToFilesystem(map[string][]byte{ComponentVectorFilename: dataWithoutComments}, targetDirPath, "", fs); err != nil { - return err - } - - // Pass 2: inject default-version comments and write again. Because the .glk/defaults/ snapshot from Pass 1 has no comments, - // the comments are always treated as "new" by the three-way merge and are therefore reliably written into the output file. - for _, fn := range postGenerateDefaultVersionCommentFns { - data = []byte(fn(string(data))) + return nil, fmt.Errorf("failed to marshal component versions: %w", err) } - dataWithComments := append(header, data...) - return files.WriteObjectsToFilesystem(map[string][]byte{ComponentVectorFilename: dataWithComments}, targetDirPath, "", fs) + return data, nil } diff --git a/pkg/utils/componentvector/componentvector_test.go b/pkg/utils/componentvector/componentvector_test.go index 753d5566..88365472 100644 --- a/pkg/utils/componentvector/componentvector_test.go +++ b/pkg/utils/componentvector/componentvector_test.go @@ -5,14 +5,9 @@ package componentvector_test import ( - "strings" - . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/spf13/afero" - "sigs.k8s.io/yaml" - "github.com/gardener/gardener-landscape-kit/componentvector" . "github.com/gardener/gardener-landscape-kit/pkg/utils/componentvector" ) @@ -683,115 +678,4 @@ components: }) }) - Describe("#WriteComponentVectorFile", func() { - const outputDir = "/output" - - // componentNames parses the written components.yaml and returns the list of component names. - componentNames := func(fs afero.Afero) []string { - data, err := fs.ReadFile(outputDir + "/components.yaml") - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - var comps struct { - Components []struct { - Name string `json:"name"` - } `json:"components"` - } - ExpectWithOffset(1, yaml.Unmarshal(data, &comps)).NotTo(HaveOccurred()) - names := make([]string, 0, len(comps.Components)) - for _, c := range comps.Components { - names = append(names, c.Name) - } - return names - } - - cv := func(contents []byte) Interface { - cv, err := NewWithOverride(contents) - ExpectWithOffset(1, err).NotTo(HaveOccurred()) - return cv - } - - BeforeEach(func() { - componentvector.DefaultComponentsYAML = []byte(`components: -- name: github.com/gardener/gardener - sourceRepository: https://github.com/gardener/gardener - version: v1.137.1 -- name: github.com/gardener/other-component - sourceRepository: https://github.com/gardener/other-component - version: v2.0.0 -`) - }) - - It("should not produce duplicate entries when the user edits the injected default-version comment", func() { - fs := afero.Afero{Fs: afero.NewMemMapFs()} - - overrideCV := []byte(`components: -- name: github.com/gardener/gardener - sourceRepository: https://github.com/gardener/gardener - version: v1.99.0 -- name: github.com/gardener/other-component - sourceRepository: https://github.com/gardener/other-component - version: v2.0.0 -`) - - // Run 1: write from default CV so no comment is injected. - Expect(WriteComponentVectorFile(fs, outputDir, cv(componentvector.DefaultComponentsYAML))).To(Succeed()) - - // User changes the gardener version. - writtenFile := outputDir + "/components.yaml" - writtenData, err := fs.ReadFile(writtenFile) - Expect(err).NotTo(HaveOccurred()) - Expect(fs.WriteFile(writtenFile, - []byte(strings.ReplaceAll(string(writtenData), "version: v1.137.1", "version: v1.99.0")), - 0600)).To(Succeed()) - - // Run 2: the injected default-version comment appears. - Expect(WriteComponentVectorFile(fs, outputDir, cv(overrideCV))).To(Succeed()) - - writtenData, err = fs.ReadFile(writtenFile) - Expect(err).NotTo(HaveOccurred()) - Expect(string(writtenData)).To(ContainSubstring("# version: v1.137.1 # <-- gardener-landscape-kit version default")) - - // User edits the injected comment (e.g. adds a personal annotation). - writtenData, err = fs.ReadFile(writtenFile) - Expect(err).NotTo(HaveOccurred()) - Expect(fs.WriteFile(writtenFile, - []byte(strings.ReplaceAll(string(writtenData), - "# version: v1.137.1 # <-- gardener-landscape-kit version default", - "# version: v1.137.1 # <-- default (my annotation)")), - 0600)).To(Succeed()) - - // Run 3: must not duplicate gardener entry and amend the comment with a new default version comment. - Expect(WriteComponentVectorFile(fs, outputDir, cv(overrideCV))).To(Succeed()) - - Expect(componentNames(fs)).To(ConsistOf( - "github.com/gardener/gardener", - "github.com/gardener/other-component", - )) - - // The correct default-version comment must have been restored. - writtenData, err = fs.ReadFile(writtenFile) - Expect(err).NotTo(HaveOccurred()) - Expect(string(writtenData)).To(ContainSubstring("# <-- gardener-landscape-kit version default")) - Expect(string(writtenData)).To(ContainSubstring("my annotation")) - }) - - It("should not re-add entries that the user removed from the file", func() { - fs := afero.Afero{Fs: afero.NewMemMapFs()} - - // Run 1: write both entries. - Expect(WriteComponentVectorFile(fs, outputDir, cv(componentvector.DefaultComponentsYAML))).To(Succeed()) - - // User removes the other-component entry entirely. - writtenFile := outputDir + "/components.yaml" - writtenData, err := fs.ReadFile(writtenFile) - Expect(err).NotTo(HaveOccurred()) - idx := strings.Index(string(writtenData), "- name: github.com/gardener/other-component") - Expect(idx).To(BeNumerically(">", 0)) - Expect(fs.WriteFile(writtenFile, writtenData[:idx], 0600)).To(Succeed()) - - // Run 2: same vector — the removed entry must not come back. - Expect(WriteComponentVectorFile(fs, outputDir, cv(componentvector.DefaultComponentsYAML))).To(Succeed()) - - Expect(componentNames(fs)).To(ConsistOf("github.com/gardener/gardener")) - }) - }) }) diff --git a/pkg/utils/files/writer.go b/pkg/utils/files/writer.go index 26f76020..755345fa 100644 --- a/pkg/utils/files/writer.go +++ b/pkg/utils/files/writer.go @@ -13,6 +13,7 @@ import ( "github.com/spf13/afero" + configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" "github.com/gardener/gardener-landscape-kit/pkg/utils/meta" ) @@ -47,7 +48,7 @@ func isSecret(contents []byte) bool { // WriteObjectsToFilesystem writes the given objects to the filesystem at the specified rootDir and relativeFilePath. // If the manifest file already exists, it patches changes from the new default. // Additionally, it maintains a default version of the manifest in a separate directory for future diff checks. -func WriteObjectsToFilesystem(objects map[string][]byte, rootDir, relativeFilePath string, fs afero.Afero) error { +func WriteObjectsToFilesystem(objects map[string][]byte, rootDir, relativeFilePath string, fs afero.Afero, mode configv1alpha1.MergeMode) error { if err := fs.MkdirAll(path.Join(rootDir, relativeFilePath), 0700); err != nil { return err } @@ -81,7 +82,7 @@ func WriteObjectsToFilesystem(objects map[string][]byte, rootDir, relativeFilePa object = append([]byte(secretEncryptionDisclaimer), object...) } - output, err := meta.ThreeWayMergeManifest(oldDefaultYaml, object, currentYaml) + output, err := meta.ThreeWayMergeManifest(oldDefaultYaml, object, currentYaml, mode) if err != nil { return err } diff --git a/pkg/utils/files/writer_test.go b/pkg/utils/files/writer_test.go index 8d2a91bb..d171fbf8 100644 --- a/pkg/utils/files/writer_test.go +++ b/pkg/utils/files/writer_test.go @@ -15,7 +15,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" + configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" "github.com/gardener/gardener-landscape-kit/pkg/utils/files" + "github.com/gardener/gardener-landscape-kit/pkg/utils/meta" ) var _ = Describe("Writer", func() { @@ -53,7 +55,7 @@ var _ = Describe("Writer", func() { baseDir := "/path/to" path := "my/files" - Expect(files.WriteObjectsToFilesystem(objects, baseDir, path, fs)).To(Succeed()) + Expect(files.WriteObjectsToFilesystem(objects, baseDir, path, fs, configv1alpha1.MergeModeSilent)).To(Succeed()) contents, err := fs.ReadFile("/path/to/my/files/file.yaml") Expect(err).NotTo(HaveOccurred()) @@ -65,7 +67,7 @@ var _ = Describe("Writer", func() { }) It("should overwrite the manifest file if no meta file is present yet", func() { - Expect(files.WriteObjectsToFilesystem(map[string][]byte{"config.yaml": objYaml}, "/landscape", "manifest", fs)).To(Succeed()) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"config.yaml": objYaml}, "/landscape", "manifest", fs, configv1alpha1.MergeModeSilent)).To(Succeed()) content, err := fs.ReadFile("/landscape/.glk/defaults/manifest/config.yaml") Expect(err).ToNot(HaveOccurred()) @@ -77,7 +79,7 @@ var _ = Describe("Writer", func() { }) It("should patch only changed default values on subsequent generates and retain custom modifications", func() { - Expect(files.WriteObjectsToFilesystem(map[string][]byte{"config.yaml": objYaml}, "/landscape", "manifest", fs)).To(Succeed()) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"config.yaml": objYaml}, "/landscape", "manifest", fs, configv1alpha1.MergeModeSilent)).To(Succeed()) content, err := fs.ReadFile("/landscape/manifest/config.yaml") Expect(err).ToNot(HaveOccurred()) @@ -96,7 +98,7 @@ var _ = Describe("Writer", func() { objYaml, err = yaml.Marshal(obj) Expect(err).NotTo(HaveOccurred()) - Expect(files.WriteObjectsToFilesystem(map[string][]byte{"config.yaml": objYaml}, "/landscape", "manifest", fs)).To(Succeed()) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"config.yaml": objYaml}, "/landscape", "manifest", fs, configv1alpha1.MergeModeSilent)).To(Succeed()) content, err = fs.ReadFile("/landscape/.glk/defaults/manifest/config.yaml") Expect(err).ToNot(HaveOccurred()) @@ -112,7 +114,7 @@ var _ = Describe("Writer", func() { objYaml, err := yaml.Marshal(obj) Expect(err).NotTo(HaveOccurred()) - Expect(files.WriteObjectsToFilesystem(map[string][]byte{"secret.yaml": objYaml}, "/landscape", "manifest", fs)).To(Succeed()) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"secret.yaml": objYaml}, "/landscape", "manifest", fs, configv1alpha1.MergeModeSilent)).To(Succeed()) content, err := fs.ReadFile("/landscape/manifest/secret.yaml") Expect(err).ToNot(HaveOccurred()) @@ -129,7 +131,7 @@ spec: kind: Secret name: my-secret`) - Expect(files.WriteObjectsToFilesystem(map[string][]byte{"secret.yaml": objYaml}, "/landscape", "manifest", fs)).To(Succeed()) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"secret.yaml": objYaml}, "/landscape", "manifest", fs, configv1alpha1.MergeModeSilent)).To(Succeed()) content, err := fs.ReadFile("/landscape/manifest/secret.yaml") Expect(err).ToNot(HaveOccurred()) @@ -138,6 +140,233 @@ spec: Not(ContainSubstring(`# SECURITY ADVISORY`)), )) }) + + DescribeTable("should annotate operator-overwritten values only in Hint mode", + func(mode configv1alpha1.MergeMode, expectAnnotation bool) { + initial := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": initial}, "/landscape", "manifest", fs, mode)).To(Succeed()) + + // Operator pins to a custom version with a comment explaining why + Expect(fs.WriteFile("/landscape/manifest/test.yaml", []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.5 # pinned for production +`), 0600)).To(Succeed()) + + // GLK ships a new default with a newer version + updated := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.1.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": updated}, "/landscape", "manifest", fs, mode)).To(Succeed()) + + content, err := fs.ReadFile("/landscape/manifest/test.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(ContainSubstring("version: v1.0.5")) + Expect(string(content)).To(ContainSubstring("pinned for production")) + + if expectAnnotation { + Expect(string(content)).To(ContainSubstring(meta.GLKDefaultPrefix + "v1.1.0")) + + // Re-run with the same default — annotation persists because the user did not remove it. + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": updated}, "/landscape", "manifest", fs, mode)).To(Succeed()) + content2, err := fs.ReadFile("/landscape/manifest/test.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content2)).To(Equal(string(content))) + } else { + Expect(string(content)).NotTo(ContainSubstring(meta.GLKDefaultPrefix)) + } + }, + Entry("Silent", configv1alpha1.MergeModeSilent, false), + Entry("Hint", configv1alpha1.MergeModeHint, true), + ) + + Context("MergeMode Hint", func() { + It("should not re-add the annotation after the user removed it, until the default changes again", func() { + initial := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": initial}, "/landscape", "manifest", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + + // Operator pins to v1.0.5 + Expect(fs.WriteFile("/landscape/manifest/test.yaml", []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.5 +`), 0600)).To(Succeed()) + + // GLK ships v1.1.0 — annotation appears + v110 := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.1.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": v110}, "/landscape", "manifest", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + content, err := fs.ReadFile("/landscape/manifest/test.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(ContainSubstring(meta.GLKDefaultPrefix)) + + // User acknowledges the annotation and removes it manually + Expect(fs.WriteFile("/landscape/manifest/test.yaml", []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.5 +`), 0600)).To(Succeed()) + + // Re-run with the same default — annotation stays removed + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": v110}, "/landscape", "manifest", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + content, err = fs.ReadFile("/landscape/manifest/test.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(ContainSubstring("version: v1.0.5")) + Expect(string(content)).NotTo(ContainSubstring(meta.GLKDefaultPrefix)) + + // GLK ships v1.2.0 — annotation re-appears because the default changed + v120 := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.2.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": v120}, "/landscape", "manifest", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + content, err = fs.ReadFile("/landscape/manifest/test.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(ContainSubstring("version: v1.0.5")) + Expect(string(content)).To(ContainSubstring(meta.GLKDefaultPrefix + "v1.2.0")) + }) + + It("should replace the annotation when the GLK default changes again instead of accumulating", func() { + initial := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": initial}, "/landscape", "accum", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + + // Operator pins to v1.0.5 + Expect(fs.WriteFile("/landscape/accum/test.yaml", []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.5 # pinned for production +`), 0600)).To(Succeed()) + + // GLK ships v1.1.0 — annotation appears + v110 := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.1.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": v110}, "/landscape", "accum", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + content, err := fs.ReadFile("/landscape/accum/test.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(ContainSubstring(meta.GLKDefaultPrefix + "v1.1.0")) + + // GLK ships v1.2.0 — annotation is replaced, not accumulated + v120 := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.2.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": v120}, "/landscape", "accum", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + content, err = fs.ReadFile("/landscape/accum/test.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(ContainSubstring("pinned for production")) + Expect(string(content)).To(ContainSubstring(meta.GLKDefaultPrefix + "v1.2.0")) + Expect(string(content)).NotTo(ContainSubstring(meta.GLKDefaultPrefix + "v1.1.0")) + + // GLK ships v1.3.0 — again replaced, never more than one annotation + v130 := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.3.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": v130}, "/landscape", "accum", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + content, err = fs.ReadFile("/landscape/accum/test.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(ContainSubstring("pinned for production")) + Expect(string(content)).To(ContainSubstring(meta.GLKDefaultPrefix + "v1.3.0")) + Expect(string(content)).NotTo(ContainSubstring(meta.GLKDefaultPrefix + "v1.2.0")) + Expect(string(content)).NotTo(ContainSubstring(meta.GLKDefaultPrefix + "v1.1.0")) + }) + + It("should remove the annotation entirely when the GLK default reverts to the operator's value", func() { + initial := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": initial}, "/landscape", "revert", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + + // Operator pins to v1.0.5 + Expect(fs.WriteFile("/landscape/revert/test.yaml", []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.5 +`), 0600)).To(Succeed()) + + // GLK ships v1.1.0 — annotation appears + updated := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.1.0 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": updated}, "/landscape", "revert", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + content, err := fs.ReadFile("/landscape/revert/test.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(ContainSubstring(meta.GLKDefaultPrefix)) + + // GLK reverts to v1.0.5 — operator's value now matches the default, no annotation + reverted := []byte(`apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.5 +`) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": reverted}, "/landscape", "revert", fs, configv1alpha1.MergeModeHint)).To(Succeed()) + content, err = fs.ReadFile("/landscape/revert/test.yaml") + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).NotTo(ContainSubstring(meta.GLKDefaultPrefix)) + Expect(string(content)).NotTo(ContainSubstring("# Attention - new default:")) + }) + }) }) Describe("#RelativePathFromDirDepth", func() { diff --git a/pkg/utils/kustomization/kustomization.go b/pkg/utils/kustomization/kustomization.go index 01e1123b..6ea15ad6 100644 --- a/pkg/utils/kustomization/kustomization.go +++ b/pkg/utils/kustomization/kustomization.go @@ -16,6 +16,7 @@ import ( kustomize "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/yaml" + configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" "github.com/gardener/gardener-landscape-kit/pkg/components" "github.com/gardener/gardener-landscape-kit/pkg/utils/files" ) @@ -51,14 +52,14 @@ func NewKustomization(resources []string, patches []kustomize.Patch) *kustomize. // WriteKustomizationComponent writes the objects and a Kustomization file to the fs. // The Kustomization file references all other objects. // The objects map will be modified to include the Kustomization file. -func WriteKustomizationComponent(objects map[string][]byte, baseDir, componentDir string, fs afero.Afero) error { +func WriteKustomizationComponent(objects map[string][]byte, baseDir, componentDir string, fs afero.Afero, mode configv1alpha1.MergeMode) error { kustomization := NewKustomization(slices.Collect(maps.Keys(objects)), nil) content, err := yaml.Marshal(kustomization) if err != nil { return err } objects[KustomizationFileName] = content - return files.WriteObjectsToFilesystem(objects, baseDir, componentDir, fs) + return files.WriteObjectsToFilesystem(objects, baseDir, componentDir, fs, mode) } // WriteLandscapeComponentsKustomizations traverses through the generated components directory and adds @@ -68,10 +69,10 @@ func WriteLandscapeComponentsKustomizations(options components.Options) error { targetDir := options.GetTargetPath() componentsDir := filepath.Join(targetDir, components.DirName) - return fs.Walk(componentsDir, writeKustomizationsToFileTree(fs, targetDir)) + return fs.Walk(componentsDir, writeKustomizationsToFileTree(fs, targetDir, options.GetMergeMode())) } -func writeKustomizationsToFileTree(fs afero.Afero, targetDir string) func(dir string, info os.FileInfo, err error) error { +func writeKustomizationsToFileTree(fs afero.Afero, targetDir string, mode configv1alpha1.MergeMode) func(dir string, info os.FileInfo, err error) error { var completedPaths []string return func(dir string, info os.FileInfo, err error) error { @@ -115,11 +116,11 @@ func writeKustomizationsToFileTree(fs afero.Afero, targetDir string) func(dir st } relativePath, _ := strings.CutPrefix(dir, targetDir) - return writeKustomizationFile(fs, targetDir, relativePath, directories) + return writeKustomizationFile(fs, targetDir, relativePath, directories, mode) } } -func writeKustomizationFile(fs afero.Afero, landscapeDir, relativePath string, directories []string) error { +func writeKustomizationFile(fs afero.Afero, landscapeDir, relativePath string, directories []string, mode configv1alpha1.MergeMode) error { var ( err error objects = make(map[string][]byte) @@ -132,5 +133,5 @@ func writeKustomizationFile(fs afero.Afero, landscapeDir, relativePath string, d objects[KustomizationFileName] = append([]byte(autoGenerationNotice), objects[KustomizationFileName]...) - return files.WriteObjectsToFilesystem(objects, landscapeDir, relativePath, fs) + return files.WriteObjectsToFilesystem(objects, landscapeDir, relativePath, fs, mode) } diff --git a/pkg/utils/kustomization/kustomization_test.go b/pkg/utils/kustomization/kustomization_test.go index 909fe1f1..b7cf67a3 100644 --- a/pkg/utils/kustomization/kustomization_test.go +++ b/pkg/utils/kustomization/kustomization_test.go @@ -16,6 +16,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" + configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" "github.com/gardener/gardener-landscape-kit/pkg/cmd" generateoptions "github.com/gardener/gardener-landscape-kit/pkg/cmd/generate/options" "github.com/gardener/gardener-landscape-kit/pkg/components" @@ -59,7 +60,7 @@ var _ = Describe("Kustomization", func() { } ) - Expect(WriteKustomizationComponent(objects, landscapeDir, componentDir, fs)).To(Succeed()) + Expect(WriteKustomizationComponent(objects, landscapeDir, componentDir, fs, configv1alpha1.MergeModeSilent)).To(Succeed()) contents, err := fs.ReadFile(filepath.Join(landscapeDir, componentDir, "configmap.yaml")) Expect(err).NotTo(HaveOccurred()) @@ -80,11 +81,15 @@ var _ = Describe("Kustomization", func() { BeforeEach(func() { fs = afero.Afero{Fs: afero.NewMemMapFs()} + config := &configv1alpha1.LandscapeKitConfiguration{} + configv1alpha1.SetObjectDefaults_LandscapeKitConfiguration(config) + var err error opts, err = components.NewOptions(&generateoptions.Options{ Options: &cmd.Options{ Log: logr.Discard(), }, + Config: config, TargetDirPath: "/absolute/path/with/../to/repo/landscape", }, fs) Expect(err).NotTo(HaveOccurred()) diff --git a/pkg/utils/meta/diff.go b/pkg/utils/meta/diff.go index d5e8e100..7699ee74 100644 --- a/pkg/utils/meta/diff.go +++ b/pkg/utils/meta/diff.go @@ -13,6 +13,8 @@ import ( "github.com/elliotchance/orderedmap/v3" "go.yaml.in/yaml/v4" + + configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" ) // section represents a single section in a manifest file (either a manifest or a comment) @@ -41,7 +43,7 @@ type manifestDiff struct { // It performs a three-way merge between the old default template, the new default template, and the current user-modified version. // It preserves user modifications while applying updates from the new default template. // Contents from the current manifest are prioritized and sorted first. -func ThreeWayMergeManifest(oldDefaultYaml, newDefaultYaml, currentYaml []byte) ([]byte, error) { +func ThreeWayMergeManifest(oldDefaultYaml, newDefaultYaml, currentYaml []byte, mode configv1alpha1.MergeMode) ([]byte, error) { var ( output []byte @@ -61,7 +63,7 @@ func ThreeWayMergeManifest(oldDefaultYaml, newDefaultYaml, currentYaml []byte) ( current := sect.content newDefault, _ := diff.newDefault.Get(sect.key) oldDefault, _ := diff.oldDefault.Get(sect.key) - merged, err := threeWayMergeSection(oldDefault, newDefault, current) + merged, err := threeWayMergeSection(oldDefault, newDefault, current, mode) if err != nil { return nil, err } @@ -75,7 +77,7 @@ func ThreeWayMergeManifest(oldDefaultYaml, newDefaultYaml, currentYaml []byte) ( continue } // Applying threeWayMergeSection with only the new section content to ensure proper formatting (idempotency). - merged, err := threeWayMergeSection(nil, sect.content, nil) + merged, err := threeWayMergeSection(nil, sect.content, nil, mode) if err != nil { return nil, err } diff --git a/pkg/utils/meta/diff_test.go b/pkg/utils/meta/diff_test.go index 7d626a2f..755e5dda 100644 --- a/pkg/utils/meta/diff_test.go +++ b/pkg/utils/meta/diff_test.go @@ -15,6 +15,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" + configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" "github.com/gardener/gardener-landscape-kit/pkg/utils/meta" ) @@ -41,7 +42,7 @@ var _ = Describe("Meta Dir Config Diff", func() { objYaml, err := yaml.Marshal(obj) Expect(err).NotTo(HaveOccurred()) - newContents, err := meta.ThreeWayMergeManifest(nil, objYaml, nil) + newContents, err := meta.ThreeWayMergeManifest(nil, objYaml, nil, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) // Modify the manifest on disk @@ -57,7 +58,7 @@ var _ = Describe("Meta Dir Config Diff", func() { newObjYaml, err := yaml.Marshal(obj) Expect(err).NotTo(HaveOccurred()) - content, err = meta.ThreeWayMergeManifest(objYaml, newObjYaml, content) + content, err = meta.ThreeWayMergeManifest(objYaml, newObjYaml, content, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) expectedConfigMapOutputWithNewKey, err := testdata.ReadFile("testdata/expected_configmap_output_newkey.yaml") @@ -76,7 +77,7 @@ var _ = Describe("Meta Dir Config Diff", func() { manifestGenerated, err := testdata.ReadFile("testdata/manifest-4-expected-generated.yaml") Expect(err).NotTo(HaveOccurred()) - mergedManifest, err := meta.ThreeWayMergeManifest(manifestDefault, manifestDefaultNew, manifestEdited) + mergedManifest, err := meta.ThreeWayMergeManifest(manifestDefault, manifestDefaultNew, manifestEdited, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(mergedManifest)).To(Equal(string(manifestGenerated))) }) @@ -87,7 +88,7 @@ var _ = Describe("Meta Dir Config Diff", func() { expectedConfigMapOutputWithNewKey, err := testdata.ReadFile("testdata/expected_configmap_output_newkey.yaml") Expect(err).NotTo(HaveOccurred()) - content, err := meta.ThreeWayMergeManifest(nil, expectedConfigMapOutputWithNewKey, []byte(strings.ReplaceAll(string(expectedDefaultConfigMapOutput), "key: value", "key: newDefaultValue"))) + content, err := meta.ThreeWayMergeManifest(nil, expectedConfigMapOutputWithNewKey, []byte(strings.ReplaceAll(string(expectedDefaultConfigMapOutput), "key: value", "key: newDefaultValue")), configv1alpha1.MergeModeSilent) Expect(err).ToNot(HaveOccurred()) Expect(string(content)).To(Equal(strings.ReplaceAll(string(expectedConfigMapOutputWithNewKey), "key: value", "key: newDefaultValue") + "\n")) }) @@ -102,21 +103,21 @@ var _ = Describe("Meta Dir Config Diff", func() { multipleManifestsExpectedGenerated, err := testdata.ReadFile("testdata/multiple-manifests-4-expected-generated.yaml") Expect(err).NotTo(HaveOccurred()) - content, err := meta.ThreeWayMergeManifest(nil, multipleManifestsInitial, nil) + content, err := meta.ThreeWayMergeManifest(nil, multipleManifestsInitial, nil, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(content)).To(Equal(string(multipleManifestsInitial))) - content, err = meta.ThreeWayMergeManifest(multipleManifestsInitial, multipleManifestsInitial, multipleManifestsInitial) + content, err = meta.ThreeWayMergeManifest(multipleManifestsInitial, multipleManifestsInitial, multipleManifestsInitial, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(content)).To(Equal(string(multipleManifestsInitial))) // Editing the written manifest and updating the manifest with the same default content should not overwrite anything - content, err = meta.ThreeWayMergeManifest(multipleManifestsInitial, multipleManifestsInitial, multipleManifestsEdited) + content, err = meta.ThreeWayMergeManifest(multipleManifestsInitial, multipleManifestsInitial, multipleManifestsEdited, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(content)).To(Equal(string(multipleManifestsEdited))) // New default manifest changes should be applied, while custom edits should be retained. - content, err = meta.ThreeWayMergeManifest(multipleManifestsInitial, multipleManifestsNewDefault, multipleManifestsEdited) + content, err = meta.ThreeWayMergeManifest(multipleManifestsInitial, multipleManifestsNewDefault, multipleManifestsEdited, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(content)).To(Equal(string(multipleManifestsExpectedGenerated))) }) @@ -131,7 +132,7 @@ var _ = Describe("Meta Dir Config Diff", func() { expected, err := testdata.ReadFile("testdata/order-4-expected.yaml") Expect(err).NotTo(HaveOccurred()) - content, err := meta.ThreeWayMergeManifest(oldDefault, newDefault, current) + content, err := meta.ThreeWayMergeManifest(oldDefault, newDefault, current, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(content)).To(Equal(string(expected))) }) @@ -145,22 +146,22 @@ var _ = Describe("Meta Dir Config Diff", func() { invalidYaml = []byte(`keyWith: colonSuffix:`) ) - _, err = meta.ThreeWayMergeManifest(emptyYaml, invalidYaml, emptyYaml) + _, err = meta.ThreeWayMergeManifest(emptyYaml, invalidYaml, emptyYaml, configv1alpha1.MergeModeSilent) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("parsing newDefault file for manifest diff failed")) - _, err = meta.ThreeWayMergeManifest(invalidYaml, validYaml, validYaml) + _, err = meta.ThreeWayMergeManifest(invalidYaml, validYaml, validYaml, configv1alpha1.MergeModeSilent) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("parsing oldDefault file for manifest diff failed")) - _, err = meta.ThreeWayMergeManifest(validYaml, validYaml, invalidYaml) + _, err = meta.ThreeWayMergeManifest(validYaml, validYaml, invalidYaml, configv1alpha1.MergeModeSilent) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("parsing current file for manifest diff failed")) - _, err = meta.ThreeWayMergeManifest(validYaml, validYaml, validYaml) + _, err = meta.ThreeWayMergeManifest(validYaml, validYaml, validYaml, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) - _, err = meta.ThreeWayMergeManifest(emptyYaml, emptyYaml, emptyYaml) + _, err = meta.ThreeWayMergeManifest(emptyYaml, emptyYaml, emptyYaml, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) }) @@ -175,7 +176,7 @@ var _ = Describe("Meta Dir Config Diff", func() { expected, err := testdata.ReadFile("testdata/replaced-file-4-expected-generated.yaml") Expect(err).NotTo(HaveOccurred()) - content, err := meta.ThreeWayMergeManifest(oldDefault, newDefault, current) + content, err := meta.ThreeWayMergeManifest(oldDefault, newDefault, current, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(content)).To(Equal(string(expected))) }) @@ -190,7 +191,7 @@ var _ = Describe("Meta Dir Config Diff", func() { expected, err := testdata.ReadFile("testdata/replaced-file-2-new-default.yaml") Expect(err).NotTo(HaveOccurred()) - content, err := meta.ThreeWayMergeManifest(oldDefault, newDefault, current) + content, err := meta.ThreeWayMergeManifest(oldDefault, newDefault, current, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(content)).To(Equal(string(expected))) }) @@ -201,7 +202,7 @@ var _ = Describe("Meta Dir Config Diff", func() { // Two documents with the same structure should be treated as the same manifest across generations. nonK8sYaml := []byte("foo: bar\nbaz: qux\n") - content, err := meta.ThreeWayMergeManifest(nonK8sYaml, nonK8sYaml, nonK8sYaml) + content, err := meta.ThreeWayMergeManifest(nonK8sYaml, nonK8sYaml, nonK8sYaml, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(content)).To(Equal(string(nonK8sYaml))) @@ -210,7 +211,7 @@ var _ = Describe("Meta Dir Config Diff", func() { newDefault := []byte("foo: bar\nbaz: updated\n") expected := []byte("foo: user-value\nbaz: updated\n") - content, err = meta.ThreeWayMergeManifest(nonK8sYaml, newDefault, edited) + content, err = meta.ThreeWayMergeManifest(nonK8sYaml, newDefault, edited, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(content)).To(MatchYAML(string(expected))) }) @@ -225,9 +226,64 @@ var _ = Describe("Meta Dir Config Diff", func() { expected, err := testdata.ReadFile("testdata/replaced-file-6-different-name-merged.yaml") Expect(err).NotTo(HaveOccurred()) - content, err := meta.ThreeWayMergeManifest(oldDefault, newDefault, current) + content, err := meta.ThreeWayMergeManifest(oldDefault, newDefault, current, configv1alpha1.MergeModeSilent) Expect(err).NotTo(HaveOccurred()) Expect(string(content)).To(Equal(string(expected))) }) + + It("should retain user modifications in slices during a three-way-merge", func() { + oldDefault, err := testdata.ReadFile("testdata/merge-slice-1-default.yaml") + Expect(err).NotTo(HaveOccurred()) + newDefault, err := testdata.ReadFile("testdata/merge-slice-3-new-default.yaml") + Expect(err).NotTo(HaveOccurred()) + current, err := testdata.ReadFile("testdata/merge-slice-2-edited.yaml") + Expect(err).NotTo(HaveOccurred()) + expected, err := testdata.ReadFile("testdata/merge-slice-4-expected-generated.yaml") + Expect(err).NotTo(HaveOccurred()) + + content, err := meta.ThreeWayMergeManifest(oldDefault, newDefault, current, configv1alpha1.MergeModeSilent) + Expect(err).NotTo(HaveOccurred()) + Expect(string(content)).To(Equal(string(expected))) + }) + }) + + Describe("#ThreeWayMergeManifest - MergeModeHint", func() { + It("should annotate a scalar value that the operator overrode and GLK updated, but not when there is no conflict", func() { + oldDefault := []byte(` +apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.0 +`) + newDefault := []byte(` +apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.1.0 +`) + // Operator pinned to v1.0.5 — conflicts with GLK's new default v1.1.0 + current := []byte(` +apiVersion: v1 +kind: ConfigMap +metadata: + name: test +data: + version: v1.0.5 +`) + result, err := meta.ThreeWayMergeManifest(oldDefault, newDefault, current, configv1alpha1.MergeModeHint) + Expect(err).NotTo(HaveOccurred()) + Expect(string(result)).To(ContainSubstring("version: v1.0.5")) + Expect(string(result)).To(ContainSubstring("# Attention - new default: v1.1.0")) + + // No conflict: operator did not change the value → new default taken silently, no annotation + result, err = meta.ThreeWayMergeManifest(oldDefault, newDefault, oldDefault, configv1alpha1.MergeModeHint) + Expect(err).NotTo(HaveOccurred()) + Expect(string(result)).To(ContainSubstring("version: v1.1.0")) + Expect(string(result)).NotTo(ContainSubstring(meta.GLKDefaultPrefix)) + }) }) }) diff --git a/pkg/utils/meta/merge.go b/pkg/utils/meta/merge.go index 805d28a0..db04dc36 100644 --- a/pkg/utils/meta/merge.go +++ b/pkg/utils/meta/merge.go @@ -5,11 +5,56 @@ package meta import ( + "strings" + "go.yaml.in/yaml/v4" + + configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" +) + +const ( + // GLKDefaultPrefix is the comment prefix for GLK-managed default annotations. + // It is exported so callers can use it as the strip anchor when removing annotations. + GLKDefaultPrefix = "# Attention - new default: " ) +// stripGLKAnnotation removes a GLK-managed annotation from a single comment string. +// If the annotation is part of a multi-line comment, only the annotation line is removed. +// If the annotation is appended to a value's line comment (e.g. "# user note # Attention …"), the prefix and everything after it is stripped. +func stripGLKAnnotation(comment string) string { + if !strings.Contains(comment, GLKDefaultPrefix) { + return comment + } + lines := strings.Split(comment, "\n") + out := make([]string, 0, len(lines)) + for _, line := range lines { + before, _, found := strings.Cut(line, GLKDefaultPrefix) + if !found { + out = append(out, line) + continue + } + stripped := strings.TrimRight(before, " \t") + if stripped != "" { + out = append(out, stripped) + } + } + return strings.Join(out, "\n") +} + +// stripGLKAnnotations removes GLK-managed annotations from all comment fields of a node. +func stripGLKAnnotations(nodes ...*yaml.Node) { + for _, n := range nodes { + if n == nil { + continue + } + n.HeadComment = stripGLKAnnotation(n.HeadComment) + n.LineComment = stripGLKAnnotation(n.LineComment) + n.FootComment = stripGLKAnnotation(n.FootComment) + } +} + // threeWayMergeSection performs a three-way merge on a single YAML section -func threeWayMergeSection(oldDefaultYaml, newDefaultYaml, currentYaml []byte) ([]byte, error) { +func threeWayMergeSection(oldDefaultYaml, newDefaultYaml, currentYaml []byte, mode configv1alpha1.MergeMode) ([]byte, error) { // Parse all three versions var oldDefault, newDefault, current yaml.Node if err := yaml.Unmarshal(newDefaultYaml, &newDefault); err != nil { @@ -26,14 +71,14 @@ func threeWayMergeSection(oldDefaultYaml, newDefaultYaml, currentYaml []byte) ([ } } - return EncodeResult(threeWayMerge(&oldDefault, &newDefault, ¤t)) + return EncodeResult(threeWayMerge(&oldDefault, &newDefault, ¤t, mode)) } // threeWayMerge performs a three-way merge of YAML nodes // oldDefault: the previous default template // newDefault: the new default template // current: the user's current version (possibly modified) -func threeWayMerge(oldDefault, newDefault, current *yaml.Node) *yaml.Node { +func threeWayMerge(oldDefault, newDefault, current *yaml.Node, mode configv1alpha1.MergeMode) *yaml.Node { // Unwrap document nodes if oldDefault.Kind == yaml.DocumentNode { oldDefault = oldDefault.Content[0] @@ -44,7 +89,7 @@ func threeWayMerge(oldDefault, newDefault, current *yaml.Node) *yaml.Node { if current.Kind == yaml.DocumentNode { return &yaml.Node{ Kind: yaml.DocumentNode, - Content: []*yaml.Node{threeWayMerge(oldDefault, newDefault, current.Content[0])}, + Content: []*yaml.Node{threeWayMerge(oldDefault, newDefault, current.Content[0], mode)}, } } @@ -97,23 +142,40 @@ func threeWayMerge(oldDefault, newDefault, current *yaml.Node) *yaml.Node { if !oldExists { oldValue = &yaml.Node{Kind: yaml.MappingNode} } - resultValue = threeWayMerge(oldValue, newValueNode, currentValue) + resultValue = threeWayMerge(oldValue, newValueNode, currentValue, mode) case currentValue.Kind == yaml.SequenceNode && newValueNode.Kind == yaml.SequenceNode: if !oldExists { oldValue = &yaml.Node{Kind: yaml.SequenceNode} } - resultValue = threeWayMergeSequence(oldValue, newValueNode, currentValue) - case oldExists && !nodesEqual(oldValue, newValueNode, false): + resultValue = threeWayMergeSequence(oldValue, newValueNode, currentValue, mode) + case oldExists && !nodesEqual(oldValue, newValueNode, false) && nodesEqual(oldValue, currentValue, false): + // Default changed and current was not modified: take the new default. resultValue = &yaml.Node{ Kind: newValueNode.Kind, Value: newValueNode.Value, Style: newValueNode.Style, Tag: newValueNode.Tag, HeadComment: currentValue.HeadComment, LineComment: currentValue.LineComment, FootComment: currentValue.FootComment, Content: newValueNode.Content, } mergeNodeComments(oldValue, newValueNode, resultValue) + case oldExists && !nodesEqual(oldValue, newValueNode, false): + // Both default and current changed: keep current (user's value wins). + resultValue = currentValue + mergeNodeComments(oldValue, newValueNode, resultValue) + if mode == configv1alpha1.MergeModeHint { + if !nodesEqual(newValueNode, currentValue, false) { + annotateConflict(resultKeyNode, resultValue, newValueNode) + } else { + // Values converged — strip any lingering GLK annotation. + stripGLKAnnotations(resultKeyNode, resultValue) + } + } default: resultValue = currentValue if oldExists { mergeNodeComments(oldValue, newValueNode, resultValue) + if mode == configv1alpha1.MergeModeHint && nodesEqual(newValueNode, currentValue, false) { + // Values converged — strip any lingering GLK annotation. + stripGLKAnnotations(resultKeyNode, resultValue) + } } } } @@ -138,6 +200,43 @@ func threeWayMerge(oldDefault, newDefault, current *yaml.Node) *yaml.Node { return result } +// annotateConflict adds a GLK-managed annotation comment to resultValue (or resultKeyNode for complex nodes) indicating the current GLK default. +// This is used in MergeModeHint when an operator override conflicts with an updated GLK default, so the user is hinted about the divergence. +// +// For scalar nodes, the annotation is a line comment on the value node (same line as the value). +// For complex nodes (mappings/sequences), the annotation is a head comment on the key node (line above the key). +func annotateConflict(resultKeyNode, resultValue, newDefaultNode *yaml.Node) { + switch newDefaultNode.Kind { + case yaml.ScalarNode: + annotation := glkManagedLineComment(newDefaultNode.Value) + // Strip any pre-existing GLK annotation before appending the current one, so repeated runs replace rather than accumulate the comment. + stripped := stripGLKAnnotation(resultValue.LineComment) + if stripped != "" { + resultValue.LineComment = stripped + " " + annotation + } else { + resultValue.LineComment = annotation + } + default: + // Strip existing GLK annotation from head comment before re-annotating. + resultKeyNode.HeadComment = stripGLKAnnotation(resultKeyNode.HeadComment) + resultKeyNode.HeadComment = glkManagedHeadComment(resultKeyNode.HeadComment) + } +} + +// glkManagedLineComment returns a GLK-managed line comment for a scalar value conflict. +func glkManagedLineComment(newValue string) string { + return GLKDefaultPrefix + newValue +} + +// glkManagedHeadComment returns a GLK-managed head comment for a complex node conflict. +func glkManagedHeadComment(existingHead string) string { + annotation := GLKDefaultPrefix + "(complex node changed)" + if existingHead == "" { + return annotation + } + return existingHead + "\n" + annotation +} + // mergeComment performs a three-way merge on a single comment string. // If the default comment changed (old != new), the new default is applied — unless the user // has also modified the comment (current != old), in which case the user's comment is kept @@ -172,7 +271,7 @@ func mergeNodeComments(oldNode, newNode, resultNode *yaml.Node) { } // Order is preserved based on newDefault, with user additions appended at the end -func threeWayMergeSequence(oldDefault, newDefault, current *yaml.Node) *yaml.Node { +func threeWayMergeSequence(oldDefault, newDefault, current *yaml.Node, mode configv1alpha1.MergeMode) *yaml.Node { if nodesEqual(oldDefault, current, true) { return newDefault } @@ -229,7 +328,7 @@ func threeWayMergeSequence(oldDefault, newDefault, current *yaml.Node) *yaml.Nod if !existsInOld { oldItem = &yaml.Node{Kind: yaml.MappingNode} } - result.Content = append(result.Content, threeWayMerge(oldItem, newItem, currentItem)) + result.Content = append(result.Content, threeWayMerge(oldItem, newItem, currentItem, mode)) } // Append items from newDefault that are truly new (not in old) and not already in current. @@ -249,7 +348,19 @@ func threeWayMergeSequence(oldDefault, newDefault, current *yaml.Node) *yaml.Nod return result } - // No identity key: fall back to full-string set-based merge. + // No identity key found. + // When all three sequences have the same length and all items are mappings, + // match items by position and three-way merge each pair. This handles cases + // where list items are modified but their count and order stay the same + // (e.g., a single scalar field in a mapping item is changed). + if len(oldDefault.Content) == len(newDefault.Content) && len(newDefault.Content) == len(current.Content) && allMappings(oldDefault, newDefault, current) { + for i := range current.Content { + result.Content = append(result.Content, threeWayMerge(oldDefault.Content[i], newDefault.Content[i], current.Content[i], mode)) + } + return result + } + + // Fall back to full-string set-based merge. oldSet := make(map[string]bool) for _, item := range oldDefault.Content { oldSet[nodeToString(item)] = true @@ -341,3 +452,15 @@ func mappingValue(node *yaml.Node, key string) string { } return "" } + +// allMappings reports whether every item in each of the given sequences is a mapping node. +func allMappings(seqs ...*yaml.Node) bool { + for _, seq := range seqs { + for _, item := range seq.Content { + if item.Kind != yaml.MappingNode { + return false + } + } + } + return true +} diff --git a/pkg/utils/meta/preprocessing_test.go b/pkg/utils/meta/preprocessing_test.go index f879d0e2..8e1971ae 100644 --- a/pkg/utils/meta/preprocessing_test.go +++ b/pkg/utils/meta/preprocessing_test.go @@ -10,6 +10,7 @@ import ( "github.com/spf13/afero" "go.yaml.in/yaml/v4" + configv1alpha1 "github.com/gardener/gardener-landscape-kit/pkg/apis/config/v1alpha1" "github.com/gardener/gardener-landscape-kit/pkg/utils/files" "github.com/gardener/gardener-landscape-kit/pkg/utils/meta" ) @@ -68,7 +69,7 @@ var _ = Describe("YAML Preprocessing", func() { Expect(err).ToNot(HaveOccurred()) Expect(fs.WriteFile("/landscape/manifest/test.yaml", testFile, 0600)).To(Succeed()) - Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": {}}, "/landscape", "manifest", fs)).To(Succeed()) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": {}}, "/landscape", "manifest", fs, configv1alpha1.MergeModeSilent)).To(Succeed()) content, err := fs.ReadFile("/landscape/manifest/test.yaml") Expect(err).ToNot(HaveOccurred()) @@ -82,7 +83,7 @@ var _ = Describe("YAML Preprocessing", func() { Expect(err).ToNot(HaveOccurred()) Expect(fs.WriteFile("/landscape/manifest/test.yaml", testFile, 0600)).To(Succeed()) - Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": {}}, "/landscape", "manifest", fs)).To(Succeed()) + Expect(files.WriteObjectsToFilesystem(map[string][]byte{"test.yaml": {}}, "/landscape", "manifest", fs, configv1alpha1.MergeModeSilent)).To(Succeed()) content, err := fs.ReadFile("/landscape/manifest/test.yaml") Expect(err).ToNot(HaveOccurred()) diff --git a/pkg/utils/meta/testdata/merge-slice-1-default.yaml b/pkg/utils/meta/testdata/merge-slice-1-default.yaml new file mode 100644 index 00000000..da79a139 --- /dev/null +++ b/pkg/utils/meta/testdata/merge-slice-1-default.yaml @@ -0,0 +1,3 @@ +components: +- name: github.com/gardener/gardener + version: v1.139.0 diff --git a/pkg/utils/meta/testdata/merge-slice-2-edited.yaml b/pkg/utils/meta/testdata/merge-slice-2-edited.yaml new file mode 100644 index 00000000..db6a125c --- /dev/null +++ b/pkg/utils/meta/testdata/merge-slice-2-edited.yaml @@ -0,0 +1,3 @@ +components: +- name: github.com/gardener/gardener + version: v1.99.0 diff --git a/pkg/utils/meta/testdata/merge-slice-3-new-default.yaml b/pkg/utils/meta/testdata/merge-slice-3-new-default.yaml new file mode 100644 index 00000000..8542ceb7 --- /dev/null +++ b/pkg/utils/meta/testdata/merge-slice-3-new-default.yaml @@ -0,0 +1,3 @@ +components: +- name: github.com/gardener/gardener + version: v1.139.1 diff --git a/pkg/utils/meta/testdata/merge-slice-4-expected-generated.yaml b/pkg/utils/meta/testdata/merge-slice-4-expected-generated.yaml new file mode 100644 index 00000000..db6a125c --- /dev/null +++ b/pkg/utils/meta/testdata/merge-slice-4-expected-generated.yaml @@ -0,0 +1,3 @@ +components: +- name: github.com/gardener/gardener + version: v1.99.0 diff --git a/pkg/utils/test/kustomize.go b/pkg/utils/test/kustomize.go index 780b7a57..3f5eccf1 100644 --- a/pkg/utils/test/kustomize.go +++ b/pkg/utils/test/kustomize.go @@ -118,6 +118,7 @@ func KustomizeComponent( }, } ) + v1alpha1.SetObjectDefaults_LandscapeKitConfiguration(generateOpts.Config) baseOpts, err := components.NewOptions(generateOpts, fs) if err != nil {