diff --git a/hack/runtime-migrator/internal/runtime/verifier.go b/hack/runtime-migrator/internal/runtime/verifier.go index f5dc3e2a..87451a54 100644 --- a/hack/runtime-migrator/internal/runtime/verifier.go +++ b/hack/runtime-migrator/internal/runtime/verifier.go @@ -87,7 +87,7 @@ func getZones(workers []v1beta1.Worker) []string { } func compare(originalShoot, convertedShoot v1beta1.Shoot) (*Difference, error) { - matcher := shoot.NewMatcher(originalShoot) + matcher := shoot.NewMatcherForPatch(originalShoot) equal, err := matcher.Match(convertedShoot) if err != nil { return nil, err diff --git a/hack/shoot-comparator/internal/files/comparator.go b/hack/shoot-comparator/internal/files/comparator.go index 0cae51e2..b3c8b6a5 100644 --- a/hack/shoot-comparator/internal/files/comparator.go +++ b/hack/shoot-comparator/internal/files/comparator.go @@ -20,7 +20,7 @@ func CompareFiles(leftFile, rightFile string) (bool, string, error) { return false, "", err } - matcher := shoot.NewMatcher(leftObject) + matcher := shoot.NewMatcherForCreate(leftObject) success, err := matcher.Match(rightObject) if err != nil { diff --git a/hack/shoot-comparator/pkg/shoot/matcher.go b/hack/shoot-comparator/pkg/shoot/matcher.go index 3a7dd3a7..6bc61c0a 100644 --- a/hack/shoot-comparator/pkg/shoot/matcher.go +++ b/hack/shoot-comparator/pkg/shoot/matcher.go @@ -14,16 +14,24 @@ import ( ) type Matcher struct { - toMatch interface{} - fails []string + toMatch interface{} + fails []string + DNSMatcher types.GomegaMatcher } -func NewMatcher(i interface{}) types.GomegaMatcher { +func NewMatcherForCreate(i interface{}) types.GomegaMatcher { return &Matcher{ toMatch: i, } } +func NewMatcherForPatch(i interface{}) types.GomegaMatcher { + return &Matcher{ + toMatch: i, + DNSMatcher: gstruct.Ignore(), + } +} + func getShoot(i interface{}) (shoot v1beta1.Shoot, err error) { if i == nil { return v1beta1.Shoot{}, fmt.Errorf("invalid value nil") @@ -56,6 +64,10 @@ func (m *Matcher) Match(actual interface{}) (success bool, err error) { return false, err } + if m.DNSMatcher == nil { + m.DNSMatcher = newDNSMatcher(shootToMatch.Spec.DNS) + } + matchers := []propertyMatcher{ { GomegaMatcher: gomega.BeComparableTo(shootToMatch.Name), @@ -118,7 +130,7 @@ func (m *Matcher) Match(actual interface{}) (success bool, err error) { path: "spec/secretBindingName", }, { - GomegaMatcher: newDNSMatcher(shootToMatch.Spec.DNS), + GomegaMatcher: m.DNSMatcher, path: "spec/dns", actual: shootActual.Spec.DNS, }, diff --git a/hack/shoot-comparator/pkg/shoot/matcher_test.go b/hack/shoot-comparator/pkg/shoot/matcher_test.go index bf524373..86220456 100644 --- a/hack/shoot-comparator/pkg/shoot/matcher_test.go +++ b/hack/shoot-comparator/pkg/shoot/matcher_test.go @@ -45,13 +45,13 @@ func deepCp(s v1beta1.Shoot, opts ...deepCpOpts) v1beta1.Shoot { } func testInvalidArgs(actual, expected interface{}) { - matcher := NewMatcher(expected) + matcher := NewMatcherForCreate(expected) _, err := matcher.Match(actual) Expect(err).To(HaveOccurred()) } func testResults(actual, expected interface{}, expectedMatch bool) { - matcher := NewMatcher(expected) + matcher := NewMatcherForCreate(expected) actualMatch, err := matcher.Match(actual) Expect(err).ShouldNot(HaveOccurred(), err) Expect(actualMatch).Should(Equal(expectedMatch), matcher.FailureMessage(actual)) diff --git a/internal/controller/runtime/suite_test.go b/internal/controller/runtime/suite_test.go index 4392baea..cf1e044d 100644 --- a/internal/controller/runtime/suite_test.go +++ b/internal/controller/runtime/suite_test.go @@ -39,7 +39,6 @@ import ( v12 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - //nolint:revive "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" diff --git a/pkg/gardener/shoot/converter.go b/pkg/gardener/shoot/converter.go index 52f89d2b..f0ce449d 100644 --- a/pkg/gardener/shoot/converter.go +++ b/pkg/gardener/shoot/converter.go @@ -17,7 +17,7 @@ func baseExtenders(cfg config.ConverterConfig) []Extend { return []Extend{ extender2.ExtendWithAnnotations, extender2.ExtendWithLabels, - extender2.NewDNSExtender(cfg.DNS.SecretName, cfg.DNS.DomainPrefix, cfg.DNS.ProviderType), + extender2.ExtendWithSeedSelector, extender2.NewOidcExtender(cfg.Kubernetes.DefaultOperatorOidc), extender2.ExtendWithCloudProfile, extender2.ExtendWithNetworkFilter, @@ -64,6 +64,10 @@ func NewConverterCreate(opts CreateOpts) Converter { opts.MachineImage.DefaultVersion, )) + baseExtenders = append(baseExtenders, + extender2.NewDNSExtender(opts.DNS.SecretName, opts.DNS.DomainPrefix, opts.DNS.ProviderType), + ) + baseExtenders = append(baseExtenders, extender2.NewKubernetesExtender(opts.Kubernetes.DefaultVersion, "")) diff --git a/pkg/gardener/shoot/converter_test.go b/pkg/gardener/shoot/converter_test.go index 835f740c..afc00b40 100644 --- a/pkg/gardener/shoot/converter_test.go +++ b/pkg/gardener/shoot/converter_test.go @@ -80,6 +80,14 @@ func TestConverter(t *testing.T) { assert.Equal(t, "1.30", shoot.Spec.Kubernetes.Version) assert.Equal(t, "gardenlinux", shoot.Spec.Provider.Workers[0].Machine.Image.Name) assert.Equal(t, "1592.2.0", *shoot.Spec.Provider.Workers[0].Machine.Image.Version) + assert.Nil(t, shoot.Spec.DNS) + + extensionLen := len(shoot.Spec.Extensions) + require.Equalf(t, extensionLen, 3, "unexpected number of extensions: %d, expected: 3", extensionLen) + // consider switchin to NotElementsMatch, whem released https://github.com/Antonboom/testifylint/issues/99 + for _, extension := range shoot.Spec.Extensions { + assert.NotEqual(t, "shoot-dns-service", extension.Type, "unexpected immutable field extension: 'shoot-dns-service'") + } }) t.Run("Create shoot from Runtime for existing shoot and update versions", func(t *testing.T) { diff --git a/pkg/gardener/shoot/extender/auditlogs/auditlogs_configuration_test.go b/pkg/gardener/shoot/extender/auditlogs/auditlogs_configuration_test.go index 9d3b61e9..b9b1e466 100644 --- a/pkg/gardener/shoot/extender/auditlogs/auditlogs_configuration_test.go +++ b/pkg/gardener/shoot/extender/auditlogs/auditlogs_configuration_test.go @@ -72,5 +72,4 @@ func fixTestAuditlogData(id int) AuditLogData { ServiceURL: fmt.Sprintf("https://test.service.%d", id), SecretName: fmt.Sprintf("test-service-%d", id), } - } diff --git a/pkg/gardener/shoot/extender/provider_test.go b/pkg/gardener/shoot/extender/provider_test.go index 0d96a8a4..48ac307a 100644 --- a/pkg/gardener/shoot/extender/provider_test.go +++ b/pkg/gardener/shoot/extender/provider_test.go @@ -202,7 +202,7 @@ func TestProviderExtender(t *testing.T) { CurrentZonesConfig: []string{"eu-central-1a", "eu-central-1b", "eu-central-1c"}, TestForPatch: true, }, - //"Patch option different image name - override image name and version with current image name and version": {}, + // "Patch option different image name - override image name and version with current image name and version": {}, } { t.Run(tname, func(t *testing.T) { // given diff --git a/pkg/gardener/shoot/extender/seed_selector.go b/pkg/gardener/shoot/extender/seed_selector.go new file mode 100644 index 00000000..459a22c6 --- /dev/null +++ b/pkg/gardener/shoot/extender/seed_selector.go @@ -0,0 +1,26 @@ +package extender + +import ( + gardener "github.com/gardener/gardener/pkg/apis/core/v1beta1" + imv1 "github.com/kyma-project/infrastructure-manager/api/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + seedRegionSelectorLabel = "seed.gardener.cloud/region" +) + +// ExtendWithSeedSelector creates a new extender function that can enforce shoot seed location to be the same region as shoot +// When EnforceSeedLocation flag in set on RuntimeCR to true it adds a special seedSelector field with labelSelector set to match seed region with shoot region +func ExtendWithSeedSelector(runtime imv1.Runtime, shoot *gardener.Shoot) error { + if runtime.Spec.Shoot.EnforceSeedLocation != nil && *runtime.Spec.Shoot.EnforceSeedLocation && runtime.Spec.Shoot.Region != "" { + shoot.Spec.SeedSelector = &gardener.SeedSelector{ + LabelSelector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + seedRegionSelectorLabel: runtime.Spec.Shoot.Region, + }, + }, + } + } + return nil +} diff --git a/pkg/gardener/shoot/extender/seed_selector_test.go b/pkg/gardener/shoot/extender/seed_selector_test.go new file mode 100644 index 00000000..30e31996 --- /dev/null +++ b/pkg/gardener/shoot/extender/seed_selector_test.go @@ -0,0 +1,74 @@ +package extender + +import ( + "testing" + + imv1 "github.com/kyma-project/infrastructure-manager/api/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSeedSelectorExtender(t *testing.T) { + t.Run("Add and populate seed selector field if RuntimeCR has SeedInSameRegionFlag set to true", func(t *testing.T) { + // given + runtimeShoot := getRuntimeWithSeedInSameRegionFlag(true) + shoot := fixEmptyGardenerShoot("test", "dev") + + // when + err := ExtendWithSeedSelector(runtimeShoot, &shoot) + + // then + require.NoError(t, err) + assert.NotNil(t, shoot.Spec.SeedSelector) + assert.Equal(t, runtimeShoot.Spec.Shoot.Region, shoot.Spec.SeedSelector.LabelSelector.MatchLabels[seedRegionSelectorLabel]) + }) + + t.Run("Don't add seed selector field if RuntimeCR has SeedInSameRegionFlag set to false", func(t *testing.T) { + // given + runtimeShoot := getRuntimeWithSeedInSameRegionFlag(false) + shoot := fixEmptyGardenerShoot("test", "dev") + + // when + err := ExtendWithSeedSelector(runtimeShoot, &shoot) + + // then + require.NoError(t, err) + assert.Nil(t, shoot.Spec.SeedSelector) + }) + + t.Run("Don't add seed selector field if RuntimeCR has no SeedInSameRegionFlag set", func(t *testing.T) { + // given + runtimeShoot := getRuntimeWithoutSeedInSameRegionFlag() + shoot := fixEmptyGardenerShoot("test", "dev") + + // when + err := ExtendWithSeedSelector(runtimeShoot, &shoot) + + // then + require.NoError(t, err) + assert.Nil(t, shoot.Spec.SeedSelector) + }) +} + +func getRuntimeWithSeedInSameRegionFlag(enabled bool) imv1.Runtime { + return imv1.Runtime{ + Spec: imv1.RuntimeSpec{ + Shoot: imv1.RuntimeShoot{ + Name: "myshoot", + EnforceSeedLocation: &enabled, + Region: "far-far-away", + }, + }, + } +} + +func getRuntimeWithoutSeedInSameRegionFlag() imv1.Runtime { + return imv1.Runtime{ + Spec: imv1.RuntimeSpec{ + Shoot: imv1.RuntimeShoot{ + Name: "myshoot", + Region: "far-far-away", + }, + }, + } +}