Skip to content

Commit

Permalink
First version of the Extension comparator
Browse files Browse the repository at this point in the history
  • Loading branch information
akgalwas committed Aug 7, 2024
1 parent 7ce1b5a commit 67ba2e0
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 39 deletions.
11 changes: 5 additions & 6 deletions hack/runtime-migrator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,7 @@ func createRuntime(ctx context.Context, shoot v1beta1.Shoot, cfg migrator.Config
Nodes: *shoot.Spec.Networking.Nodes,
Services: *shoot.Spec.Networking.Services,
},
ControlPlane: v1beta1.ControlPlane{
HighAvailability: getHighAvailability(shoot),
},
ControlPlane: getControlPlane(shoot),
},
Security: v1.Security{
Administrators: subjects,
Expand Down Expand Up @@ -276,18 +274,19 @@ func getShootList(ctx context.Context, cfg migrator.Config, gardenerNamespace st
return list
}

func getHighAvailability(shoot v1beta1.Shoot) *v1beta1.HighAvailability {
func getControlPlane(shoot v1beta1.Shoot) v1beta1.ControlPlane {
if shoot.Spec.ControlPlane != nil {
if shoot.Spec.ControlPlane.HighAvailability != nil {
return &v1beta1.HighAvailability{
return v1beta1.ControlPlane{HighAvailability: &v1beta1.HighAvailability{
FailureTolerance: v1beta1.FailureTolerance{
Type: shoot.Spec.ControlPlane.HighAvailability.FailureTolerance.Type,
},
},
}
}
}

return nil
return v1beta1.ControlPlane{}
}

func getAdministratorsList(ctx context.Context, provider kubeconfig.Provider, shootName string) []string {
Expand Down
5 changes: 3 additions & 2 deletions hack/shoot-comparator/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ require (
github.com/gardener/gardener v1.98.0
github.com/onsi/ginkgo/v2 v2.19.0
github.com/onsi/gomega v1.33.1
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.9.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/apimachinery v0.29.6
sigs.k8s.io/yaml v1.4.0
)

Expand All @@ -31,10 +34,8 @@ require (
golang.org/x/tools v0.22.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.29.6 // indirect
k8s.io/apiextensions-apiserver v0.29.6 // indirect
k8s.io/apimachinery v0.29.6 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
Expand Down
74 changes: 74 additions & 0 deletions hack/shoot-comparator/pkg/shoot/etensionmatcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package shoot

import (
"github.com/gardener/gardener/pkg/apis/core/v1beta1"
. "github.com/onsi/ginkgo/v2" //nolint:revive
. "github.com/onsi/gomega" //nolint:revive
)

var _ = Describe(":: extension matcher :: ", func() {
var empty []v1beta1.Extension

DescribeTable(
"checking invalid args :: ",
testExtensionInvalidArgs,
Entry("when actual is nil", nil, empty),
Entry("when expected is nil", "", nil),
)

//disabledTrue := true
disabledFalse := false

DescribeTable(
"checking results :: ",
testExtensionResults,
Entry(
"should match empty and zero values",
nil,
empty,
true,
),
Entry(
"should match identical tables",
newExtensionTable(newExtension("test", &disabledFalse)),
newExtensionTable(newExtension("test", &disabledFalse)),
true,
),
Entry(
"should detect extension missing",
newExtensionTable(newExtension("test1", &disabledFalse), newExtension("test2", &disabledFalse)),
newExtensionTable(newExtension("test", &disabledFalse)),
false,
),
Entry(
"should detect redundant extension",
newExtensionTable(newExtension("test", &disabledFalse)),
newExtensionTable(newExtension("test1", &disabledFalse), newExtension("test2", &disabledFalse)),
false,
),
)
})

func newExtension(name string, disabled *bool) v1beta1.Extension {
return v1beta1.Extension{
Type: name,
Disabled: disabled,
}
}

func newExtensionTable(extensions ...v1beta1.Extension) []v1beta1.Extension {
return extensions
}

func testExtensionInvalidArgs(actual, expected interface{}) {
matcher := NewExtensionMatcher(expected)
_, err := matcher.Match(actual)
Expect(err).To(HaveOccurred())
}

func testExtensionResults(actual, expected interface{}, expectedMatch bool) {
matcher := NewExtensionMatcher(expected)
actualMatch, err := matcher.Match(actual)
Expect(err).ShouldNot(HaveOccurred())
Expect(actualMatch).Should(Equal(expectedMatch), matcher.FailureMessage(actual))
}
95 changes: 95 additions & 0 deletions hack/shoot-comparator/pkg/shoot/extensionmatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package shoot

import (
"fmt"
"github.com/gardener/gardener/pkg/apis/core/v1beta1"
"github.com/onsi/gomega"
"github.com/onsi/gomega/types"
"github.com/pkg/errors"
"reflect"
"strings"
)

type ExtensionMatcher struct {
toMatch interface{}
fails []string
}

func NewExtensionMatcher(i interface{}) types.GomegaMatcher {
return &ExtensionMatcher{
toMatch: i,
}
}

func (m *ExtensionMatcher) Match(actual interface{}) (success bool, err error) {

if err != nil {
return false, err
}

if actual == nil && m.toMatch != nil {
return false, errors.New("actual is nil")
}

if actual != nil && m.toMatch == nil {
return false, errors.New("expected is nil")
}

if actual == nil && m.toMatch == nil {
return true, nil
}

aExtensions, err := getExtension(actual)
findExtension := func(extensions []v1beta1.Extension, extensionToFind string) v1beta1.Extension {
for _, extension := range extensions {
if extension.Type == extensionToFind {
return extension
}
}
return v1beta1.Extension{}
}

eExtensions, err := getExtension(m.toMatch)
if err != nil {
return false, err
}

for i, eExtension := range eExtensions {
aExtension := findExtension(aExtensions, eExtension.Type)
matcher := gomega.BeComparableTo(eExtension.Type)
ok, err := matcher.Match(aExtension.Type)
if err != nil {
return false, err
}

if !ok {
msg := fmt.Sprintf("spec/Extensions[%d]: %s", i, matcher.FailureMessage(aExtension))
m.fails = append(m.fails, msg)
}

}

return false, nil
}

func (m *ExtensionMatcher) NegatedFailureMessage(_ interface{}) string {
return "expected should not equal actual"
}

func (m *ExtensionMatcher) FailureMessage(_ interface{}) string {
return strings.Join(m.fails, "\n")
}

func getExtension(i interface{}) (shoot []v1beta1.Extension, err error) {
if i == nil {
return []v1beta1.Extension{}, fmt.Errorf("invalid value nil")
}

switch v := i.(type) {
case []v1beta1.Extension:
return v, nil

default:
return []v1beta1.Extension{}, fmt.Errorf(`%w: %s`, errInvalidType, reflect.TypeOf(v))
}
}
64 changes: 33 additions & 31 deletions hack/shoot-comparator/pkg/shoot/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,138 +67,140 @@ func (m *Matcher) Match(actual interface{}) (success bool, err error) {
// Note: we define separate matchers for each field to make input more readable
// Annotations are not matched as they are not relevant for the comparison ; both KIM, and Provisioner have different set of annotations
for _, matcher := range []matcher{
{
GomegaMatcher: gomega.Equal(eShoot.TypeMeta),
expected: aShoot.TypeMeta,
},
{
GomegaMatcher: gomega.Equal(eShoot.Name),
// We need to skip comparing type meta as Provisioner doesn't set it.
// It is simpler to skip it than to make fix in the Provisioner.
//{
// GomegaMatcher: gomega.BeComparableTo(eShoot.TypeMeta),
// expected: aShoot.TypeMeta,
//},
{
GomegaMatcher: gomega.BeComparableTo(eShoot.Name),
expected: aShoot.Name,
path: "metadata/name",
},
{
GomegaMatcher: gomega.Equal(eShoot.Namespace),
GomegaMatcher: gomega.BeComparableTo(eShoot.Namespace),
expected: aShoot.Namespace,
path: "metadata/namespace",
},
{
GomegaMatcher: gomega.Equal(eShoot.Labels),
GomegaMatcher: gomega.BeComparableTo(eShoot.Labels),
expected: aShoot.Labels,
path: "metadata/labels",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Addons),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Addons),
expected: aShoot.Spec.Addons,
path: "spec/Addons",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.CloudProfileName),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.CloudProfileName),
expected: aShoot.Spec.CloudProfileName,
path: "spec/CloudProfileName",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.DNS),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.DNS),
expected: aShoot.Spec.DNS,
path: "spec/DNS",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Extensions),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Extensions),
expected: aShoot.Spec.Extensions,
path: "spec/Extensions",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Hibernation),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Hibernation),
expected: aShoot.Spec.Hibernation,
path: "spec/Hibernation",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Kubernetes),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Kubernetes),
expected: aShoot.Spec.Kubernetes,
path: "spec/Kubernetes",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Networking),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Networking),
expected: aShoot.Spec.Networking,
path: "spec/Networking",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Maintenance),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Maintenance),
expected: aShoot.Spec.Maintenance,
path: "spec/Maintenance",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Monitoring),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Monitoring),
expected: aShoot.Spec.Monitoring,
path: "spec/Monitoring",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Provider),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Provider),
expected: aShoot.Spec.Provider,
path: "spec/Provider",
},

{
GomegaMatcher: gomega.Equal(eShoot.Spec.Purpose),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Purpose),
expected: aShoot.Spec.Purpose,
path: "spec/Purpose",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Region),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Region),
expected: aShoot.Spec.Region,
path: "spec/Region",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.SecretBindingName),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.SecretBindingName),
expected: aShoot.Spec.SecretBindingName,
path: "spec/SecretBindingName",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.SeedName),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.SeedName),
expected: aShoot.Spec.SeedName,
path: "spec/SeedName",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.SeedSelector),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.SeedSelector),
expected: aShoot.Spec.SeedSelector,
path: "spec/SeedSelector",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Resources),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Resources),
expected: aShoot.Spec.Resources,
path: "spec/Resources",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.Tolerations),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.Tolerations),
expected: aShoot.Spec.Tolerations,
path: "spec/Tolerations",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.ExposureClassName),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.ExposureClassName),
expected: aShoot.Spec.ExposureClassName,
path: "spec/ExposureClassName",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.SystemComponents),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.SystemComponents),
expected: aShoot.Spec.SystemComponents,
path: "spec/SystemComponents",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.ControlPlane),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.ControlPlane),
expected: aShoot.Spec.ControlPlane,
path: "spec/ControlPlane",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.SchedulerName),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.SchedulerName),
expected: aShoot.Spec.SchedulerName,
path: "spec/SchedulerName",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.CloudProfile),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.CloudProfile),
expected: aShoot.Spec.CloudProfile,
path: "spec/CloudProfile",
},
{
GomegaMatcher: gomega.Equal(eShoot.Spec.CredentialsBindingName),
GomegaMatcher: gomega.BeComparableTo(eShoot.Spec.CredentialsBindingName),
expected: aShoot.Spec.CredentialsBindingName,
path: "spec/CredentialsBindingName",
},
Expand Down

0 comments on commit 67ba2e0

Please sign in to comment.