diff --git a/hack/runtime-migrator/cmd/crb-cleanup/Makefile b/hack/runtime-migrator/cmd/crb-cleanup/Makefile new file mode 100644 index 00000000..d9be5aa2 --- /dev/null +++ b/hack/runtime-migrator/cmd/crb-cleanup/Makefile @@ -0,0 +1,15 @@ +ENVTEST_K8S_VERSION = 1.31.0 +ENVTEST_VERSION ?= release-0.19 + +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +ENVTEST ?= $(LOCALBIN)/setup-envtest +.PHONY: envtest +envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. +$(ENVTEST): $(LOCALBIN) + test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest + +test: envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out -v \ No newline at end of file diff --git a/hack/runtime-migrator/cmd/crb-cleanup/README.md b/hack/runtime-migrator/cmd/crb-cleanup/README.md new file mode 100644 index 00000000..6405f674 --- /dev/null +++ b/hack/runtime-migrator/cmd/crb-cleanup/README.md @@ -0,0 +1,62 @@ +# CRB Cleanup script + +This script is used to clean up provisioner's ClusterRoleBindings (CRBs) after migration. +It looks for provisioner and kim CRBs, compares them, +and if all of provisioner's CRBs have a KIM equivalent - it removes the provisioner's ones. + +The cleanup script is run locally, with kubeconfig pointing to the cluster. + +## Configuration + +| flag | description | default | +| ------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | +| `-kubeconfig` | Path to the kubeconfig file | in-cluster config | +| `-provisionerLabel` | Label selector for provisioner CRBs | `kyma-project.io/deprecation=to-be-removed-soon,reconciler.kyma-project.io/managed-by=provisioner` | +| `-kimLabel` | Label selector for kim CRBs | `reconciler.kyma-project.io/managed-by=infrastructure-manager` | +| `-output` | Output dir for created logs. | _empty_ (acts like `./ `) | +| `-dry-run` | Don't perform any destructive actions | `true` | +| `-verbose` | Print detailed logs | `false` | +| `-force` | Delete provisioner CRBs even if they have no kim equivalent | `false` | + +> [!NOTE] +> if `-output` doesn't end with `/`, the name of the files will be prefixed with last segment. +> eg. `-output=./dev/log/cluster_a-` will create files like `./dev/log/cluster_a-missing.json`, `./dev/log/cluster_a-removed.json`, etc. + +> [!WARNING] +> without `-dry-run=false` the script won't delete anything, even with a `-force` flag + +## Usage + +To run cleanup script, execute: + +```bash +go run ./cmd/crb-cleanup -output=./dev/logs/my-cluster/ -kubeconfig=./dev/kubeconfig -dry-run=false +``` + +If there are missing CRBs, the script will print a list of them and exit with a zero status code. +Missing CRBs can be inspected as JSON in `./dev/logs/my-cluster/missing.json`. No CRBs will be removed. + +After inspecting the missing CRBs, you can re-run the script with the `-force` flag to delete them. + +If no CRBs are missing, the script will remove provisioner CRBs. +Removed CRBs can be inspected as JSON in `./dev/logs/my-cluster/removed.json`. + +If any errors occured during deletion (eg. permission error), the CRBs that failed will be listed in `./dev/logs/my-cluster/failures.json`. + +All of the log files will be created either way. + +### prefixing logs in `kcp taskrun` + +Create a script: + +```bash +#!/bin/bash +crb-cleanup -output=./logs/${RUNTIME_NAME}_ -dry-run=true +``` + +When run, the script will create files like `./logs/some_runtime_missing.json` + +### `-dry-run` mode + +When running the script without `-dry-run=false` flag, CRBs that _would_ be removed will be listed as JSON in `./dev/logs/my-cluster/removed.json`. +No destructive actions will be performed. diff --git a/hack/runtime-migrator/cmd/crb-cleanup/cleaner.go b/hack/runtime-migrator/cmd/crb-cleanup/cleaner.go new file mode 100644 index 00000000..8b3d3dc4 --- /dev/null +++ b/hack/runtime-migrator/cmd/crb-cleanup/cleaner.go @@ -0,0 +1,89 @@ +package main + +import ( + "context" + "log/slog" + + v1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type KubeDeleter interface { + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error +} + +type Compared struct { + provisioner []v1.ClusterRoleBinding + kim []v1.ClusterRoleBinding + missing []v1.ClusterRoleBinding + additional []v1.ClusterRoleBinding +} + +type Cleaner interface { + Clean(context.Context, []v1.ClusterRoleBinding) []Failure +} + +type CRBCleaner struct { + client KubeDeleter +} + +type Failure struct { + CRB v1.ClusterRoleBinding `json:"crb"` + Err error `json:"error"` +} + +// Clean deletes CRBs, returning list of deleting errors +func (c CRBCleaner) Clean(ctx context.Context, crbs []v1.ClusterRoleBinding) []Failure { + failures := make([]Failure, 0) + + for _, crb := range crbs { + slog.Debug("Removing CRB", "crb", crb.Name) + err := c.client.Delete(ctx, crb.Name, metav1.DeleteOptions{}) + if err != nil { + slog.Error("Error removing CRB", "crb", crb.Name) + failures = append(failures, Failure{ + CRB: crb, + Err: err, + }) + } + } + + return failures +} + +// Compare returns missing, additional and original CRBs +func Compare(ctx context.Context, provisioner []v1.ClusterRoleBinding, kim []v1.ClusterRoleBinding) Compared { + missing, additional := difference(provisioner, kim, CRBEquals) + + return Compared{ + provisioner: provisioner, + kim: kim, + missing: missing, + additional: additional, + } +} + +func NewCRBCleaner(client KubeDeleter) Cleaner { + return CRBCleaner{ + client: client, + } +} + +type DryCleaner struct { + filer Filer +} + +func (p DryCleaner) Clean(_ context.Context, crbs []v1.ClusterRoleBinding) []Failure { + slog.Debug("Removing CRBs", "crbs", crbs) + err := p.filer.Removed(crbs) + if err != nil { + slog.Error("Error saving removed CRBs", "error", err, "crbs", CRBNames(crbs)) + } + return nil +} + +func NewDryCleaner(filer Filer) Cleaner { + return DryCleaner{ + filer: filer, + } +} diff --git a/hack/runtime-migrator/cmd/crb-cleanup/config.go b/hack/runtime-migrator/cmd/crb-cleanup/config.go new file mode 100644 index 00000000..c9fccfff --- /dev/null +++ b/hack/runtime-migrator/cmd/crb-cleanup/config.go @@ -0,0 +1,51 @@ +package main + +import ( + "flag" + "log/slog" + "os" + "reflect" + "strings" +) + +type Config struct { + Kubeconfig string + DryRun bool + Verbose bool + Force bool + ProvisionerLabel string + KimLabel string + Output string +} + +func ParseConfig() Config { + cfg := Config{} + flag.StringVar(&cfg.Kubeconfig, "kubeconfig", "", "Kubeconfig file path") + flag.StringVar(&cfg.ProvisionerLabel, "provisionerLabel", LabelSelectorProvisioner, "Label marking provisioner's CRBs") + flag.StringVar(&cfg.KimLabel, "kimLabel", LabelSelectorKim, "Label marking kim's CRBs") + flag.StringVar(&cfg.Output, "output", "", "Output folder for created files. Can also contain file prefix, if it doesn't end with `/` (can be a folder, eg ./foo/)") + flag.BoolVar(&cfg.DryRun, "dry-run", true, "Don't remove CRBs, write what would be removed to ./removed.json") + flag.BoolVar(&cfg.Verbose, "verbose", false, "Increase the log level to debug (default: info)") + flag.BoolVar(&cfg.Force, "force", false, "Force remove CRBs without checking migration status") + flag.Parse() + if cfg.Kubeconfig == "" { + if k, ok := os.LookupEnv("KUBECONFIG"); ok { + cfg.Kubeconfig = k + } + } + slog.Info("Parsed config", Spread(cfg)...) + return cfg +} + +// Spread returns list of struct fields in the format ["field_name", "field_value", /* ... */] +func Spread(val interface{}) []interface{} { + v := reflect.ValueOf(val) + t := v.Type() + + var res []interface{} + for i := 0; i < v.NumField(); i++ { + res = append(res, strings.ToLower(t.Field(i).Name), v.Field(i).Interface()) + } + + return res +} diff --git a/hack/runtime-migrator/cmd/crb-cleanup/crb_cleanup_suite_test.go b/hack/runtime-migrator/cmd/crb-cleanup/crb_cleanup_suite_test.go new file mode 100644 index 00000000..f9e592a1 --- /dev/null +++ b/hack/runtime-migrator/cmd/crb-cleanup/crb_cleanup_suite_test.go @@ -0,0 +1,13 @@ +package main_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestCrbCleanup(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "CrbCleanup Suite") +} diff --git a/hack/runtime-migrator/cmd/crb-cleanup/fetcher.go b/hack/runtime-migrator/cmd/crb-cleanup/fetcher.go new file mode 100644 index 00000000..f188a161 --- /dev/null +++ b/hack/runtime-migrator/cmd/crb-cleanup/fetcher.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + + v1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type Fetcher interface { + FetchKim(context.Context) ([]v1.ClusterRoleBinding, error) + FetchProvisioner(context.Context) ([]v1.ClusterRoleBinding, error) +} + +type KubeLister interface { + List(ctx context.Context, opts metav1.ListOptions) (*v1.ClusterRoleBindingList, error) +} + +type CRBFetcher struct { + labelKim string + labelProvisioner string + client KubeLister +} + +func (f CRBFetcher) fetch(ctx context.Context, label string) ([]v1.ClusterRoleBinding, error) { + list, err := f.client.List(ctx, metav1.ListOptions{ + LabelSelector: label, + }) + + if err != nil { + return nil, err + } + + return list.Items, nil +} + +func (f CRBFetcher) FetchKim(ctx context.Context) ([]v1.ClusterRoleBinding, error) { + return f.fetch(ctx, f.labelKim) +} + +func (f CRBFetcher) FetchProvisioner(ctx context.Context) ([]v1.ClusterRoleBinding, error) { + return f.fetch(ctx, f.labelProvisioner) +} + +func NewCRBFetcher(client KubeLister, labelProvisioner, labelKim string) Fetcher { + return CRBFetcher{ + labelKim: labelKim, + labelProvisioner: labelProvisioner, + client: client, + } +} diff --git a/hack/runtime-migrator/cmd/crb-cleanup/main.go b/hack/runtime-migrator/cmd/crb-cleanup/main.go new file mode 100644 index 00000000..bca364d3 --- /dev/null +++ b/hack/runtime-migrator/cmd/crb-cleanup/main.go @@ -0,0 +1,83 @@ +package main + +import ( + "context" + "log/slog" + "os" +) + +const LabelSelectorProvisioner = "kyma-project.io/deprecation=to-be-removed-soon,reconciler.kyma-project.io/managed-by=provisioner" +const LabelSelectorKim = "reconciler.kyma-project.io/managed-by=infrastructure-manager" + +func main() { + cfg := ParseConfig() + + if cfg.Verbose { + slog.SetLogLoggerLevel(slog.LevelDebug) + } else { + slog.SetLogLoggerLevel(slog.LevelInfo) + } + + kubectl := setupKubectl(cfg.Kubeconfig) + client := kubectl.RbacV1().ClusterRoleBindings() + fetcher := NewCRBFetcher(client, cfg.ProvisionerLabel, cfg.KimLabel) + + filer := NewJSONFiler(cfg.Output) + + var cleaner Cleaner + if cfg.DryRun { + slog.Info("Running in dry-run mode") + cleaner = NewDryCleaner(filer) + } else { + cleaner = NewCRBCleaner(client) + } + + failures, err := ProcessCRBs(fetcher, cleaner, filer, cfg) + if err != nil { + slog.Error("Error processing CRBs", "error", err) + os.Exit(1) + } + err = filer.Failures(failures) + if err != nil { + slog.Error("Error marshaling list of failures", "error", err, "failures", failures) + os.Exit(1) + } + slog.Info("Completed without errors") +} + +// ProcessCRBs fetches provisioner's and kim's CRBs, compares them and cleans provisioner's CRBs +// It returns error on fetch errors +// It does nothing, if provisioner's CRBs are not found in kim's CRBs, unless force flag is set +// It returns list of failures on removal errors +func ProcessCRBs(fetcher Fetcher, cleaner Cleaner, filer Filer, cfg Config) ([]Failure, error) { + ctx := context.Background() + provisionerCRBs, err := fetcher.FetchProvisioner(ctx) + if err != nil { + slog.Error("Error fetching provisioner CRBs", "error", err) + return nil, err + } + + kimCRBs, err := fetcher.FetchKim(ctx) + if err != nil { + slog.Error("Error fetching kim CRBs", "error", err) + return nil, err + } + + compared := Compare(ctx, provisionerCRBs, kimCRBs) + + if len(compared.missing) != 0 { + slog.Warn("Provisioner CRBs not found in kim CRBs", CRBNames(compared.missing)) + if filer != nil { + err := filer.Missing(compared.missing) + if err != nil { + slog.Error("Error saving unmatched CRBs", "error", err) + } + } + if !cfg.Force { + slog.Info("Use -force to remove provisioner CRBs without match") + return nil, nil + } + } + + return cleaner.Clean(ctx, provisionerCRBs), nil +} diff --git a/hack/runtime-migrator/cmd/crb-cleanup/main_test.go b/hack/runtime-migrator/cmd/crb-cleanup/main_test.go new file mode 100644 index 00000000..3c3b0545 --- /dev/null +++ b/hack/runtime-migrator/cmd/crb-cleanup/main_test.go @@ -0,0 +1,136 @@ +package main + +import ( + "context" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/envtest" +) + +var _ = Describe("Envtest", func() { + ctx := context.Background() + testenv := &envtest.Environment{} + cfg, err := testenv.Start() + Expect(err).ToNot(HaveOccurred()) + + client, err := kubernetes.NewForConfig(cfg) + Expect(err).ToNot(HaveOccurred()) + crbClient := client.RbacV1().ClusterRoleBindings() + fetcher := NewCRBFetcher(crbClient, "old=true", "new=true") + cleaner := NewCRBCleaner(crbClient) + + BeforeEach(func() { + new, err := fetcher.FetchKim(ctx) + Expect(err).ToNot(HaveOccurred()) + + old, err := fetcher.FetchProvisioner(ctx) + Expect(err).ToNot(HaveOccurred()) + + cleaner.Clean(ctx, append(new, old...)) + }) + + It("removes old CRBs", func() { + By("Generate test data") + old, new := generateCRBs(5) + + for _, crb := range append(old, new...) { + _, err := crbClient.Create(ctx, crb, metav1.CreateOptions{}) + Expect(err).ToNot(HaveOccurred(), "Failed to create CRB %q", crb.Name) + } + + By("Processing CRBs") + failures, err := ProcessCRBs(fetcher, cleaner, nil, Config{ + DryRun: false, + Force: false, + }) + + Expect(err).ToNot(HaveOccurred()) + Expect(failures).To(BeEmpty()) + + Eventually(func() ([]rbacv1.ClusterRoleBinding, error) { + return fetcher.FetchProvisioner(ctx) + }).Should(BeEmpty()) + }) + + It("skips removal when mismatch is found", func() { + By("Generate test data") + old, new := generateCRBs(5) + + for _, crb := range append(old, new[3:]...) { + _, err := crbClient.Create(ctx, crb, metav1.CreateOptions{}) + Expect(err).ToNot(HaveOccurred(), "Failed to create CRB %q", crb.Name) + } + + By("Processing CRBs") + failures, err := ProcessCRBs(fetcher, cleaner, nil, Config{ + DryRun: false, + Force: false, + }) + + Expect(err).ToNot(HaveOccurred()) + Expect(failures).To(BeEmpty()) + Consistently(func() ([]rbacv1.ClusterRoleBinding, error) { + return fetcher.FetchProvisioner(ctx) + }).Should(HaveLen(5)) + }) + + It("removes despite mismatch, with -force", func() { + By("Generate test data") + old, new := generateCRBs(5) + + for _, crb := range append(old, new[3:]...) { + _, err := crbClient.Create(ctx, crb, metav1.CreateOptions{}) + Expect(err).ToNot(HaveOccurred(), "Failed to create CRB %q", crb.Name) + } + + By("Processing CRBs") + failures, err := ProcessCRBs(fetcher, cleaner, nil, Config{ + DryRun: false, + Force: true, + }) + + Expect(err).ToNot(HaveOccurred()) + Expect(failures).To(BeEmpty()) + Eventually(func() ([]rbacv1.ClusterRoleBinding, error) { + return fetcher.FetchProvisioner(ctx) + }).Should(BeEmpty()) + }) +}) + +func generateCRBs(count int) ([]*rbacv1.ClusterRoleBinding, []*rbacv1.ClusterRoleBinding) { + old, new := make([]*rbacv1.ClusterRoleBinding, count), make([]*rbacv1.ClusterRoleBinding, count) + for i := 0; i < count; i++ { + old[i] = ClusterRoleBinding(fmt.Sprintf("old%2d", i), fmt.Sprintf("user%d@sap.com", i), fmt.Sprintf("role%2d", i), "old", "true") + new[i] = ClusterRoleBinding(fmt.Sprintf("new%2d", i), fmt.Sprintf("user%d@sap.com", i), fmt.Sprintf("role%2d", i), "new", "true") + } + return old, new +} + +func ClusterRoleBinding(name, user, role string, labels ...string) *rbacv1.ClusterRoleBinding { + labelsMap := map[string]string{} + for i := 0; i < len(labels); i += 2 { + labelsMap[labels[i]] = labels[i+1] + } + return &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labelsMap, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "User", + Name: user, + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: role, + }, + } +} diff --git a/hack/runtime-migrator/cmd/crb-cleanup/utils.go b/hack/runtime-migrator/cmd/crb-cleanup/utils.go new file mode 100644 index 00000000..a747f7ef --- /dev/null +++ b/hack/runtime-migrator/cmd/crb-cleanup/utils.go @@ -0,0 +1,158 @@ +package main + +import ( + "encoding/json" + "io" + "log/slog" + "os" + "path" + "strconv" + + v1 "k8s.io/api/rbac/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" +) + +func difference[T any](a, b []T, eql func(*T, *T) bool) ([]T, []T) { + var missing []T + + // Find elements in `a` that are not in `b` + for _, itemA := range a { + found := false + for i, itemB := range b { + if eql(&itemA, &itemB) { + found = true + b = append(b[:i], b[i+1:]...) + break + } + } + if !found { + missing = append(missing, itemA) + } + } + + return missing, b +} + +// CRBEquals checks if crbA is included in crbB +func CRBEquals(crbA, crbB *v1.ClusterRoleBinding) bool { + if crbA.RoleRef != crbB.RoleRef { + return false + } + + subjectsMap := make(map[v1.Subject]bool) + for _, subject := range crbA.Subjects { + subjectsMap[subject] = true + } + + for _, subject := range crbB.Subjects { + if _, ok := subjectsMap[subject]; !ok { + return false + } + } + return true +} + +func setupKubectl(kubeconfig string) *kubernetes.Clientset { + slog.Info("Loading kubeconfig", "path", kubeconfig) + config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + slog.Error("Error building kubeconfig", "error", err) + os.Exit(1) + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + slog.Error("Error building clientset", "error", err) + os.Exit(1) + } + + return clientset +} + +func CRBNames(crbs []v1.ClusterRoleBinding) slog.Attr { + names := make([]any, len(crbs)) + for i := range crbs { + names[i] = slog.String(strconv.Itoa(i), crbs[i].Name) + } + + return slog.Group("crbs", names...) +} + +type Filer interface { + Missing(crbs []v1.ClusterRoleBinding) error + Removed(crbs []v1.ClusterRoleBinding) error + Failures(failures []Failure) error +} + +type nopFiler struct{} + +func (n nopFiler) Failures(failures []Failure) error { + return nil +} + +func (n nopFiler) Missing(crbs []v1.ClusterRoleBinding) error { + return nil +} + +func (n nopFiler) Removed(crbs []v1.ClusterRoleBinding) error { + return nil +} + +func NewNopFiler() Filer { + return nopFiler{} +} + +type JSONFiler struct { + prefix string + missing io.Writer + removed io.Writer + failures io.Writer +} + +// Failures implements Filer. +func (j JSONFiler) Failures(failures []Failure) error { + if failures == nil || len(failures) <= 0 { + return nil + } + return saveLogs(failures, j.prefix+"failures.json") +} + +// Missing implements Filer. +func (j JSONFiler) Missing(crbs []v1.ClusterRoleBinding) error { + if crbs == nil || len(crbs) <= 0 { + return nil + } + return saveLogs(crbs, j.prefix+"missing.json") +} + +// Removed implements Filer. +func (j JSONFiler) Removed(crbs []v1.ClusterRoleBinding) error { + if crbs == nil || len(crbs) <= 0 { + return nil + } + return saveLogs(crbs, j.prefix+"removed.json") +} + +func saveLogs[T any](data T, path_ string) error { + dir := path.Dir(path_) + err := os.MkdirAll(dir, os.ModePerm) + if err != nil { + return err + } + file, err := os.Create(path_) + if err != nil { + return err + } + defer file.Close() + return json.NewEncoder(file).Encode(data) +} + +func NewJSONFiler(prefix string) Filer { + return JSONFiler{ + prefix: prefix, + missing: nil, + removed: nil, + failures: nil, + } +} diff --git a/hack/runtime-migrator/cmd/crb-cleanup/utils_test.go b/hack/runtime-migrator/cmd/crb-cleanup/utils_test.go new file mode 100644 index 00000000..3399cff5 --- /dev/null +++ b/hack/runtime-migrator/cmd/crb-cleanup/utils_test.go @@ -0,0 +1,25 @@ +package main + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Utils", func() { + Describe("Array difference", func() { + It("returns all unique elements", func() { + arr1 := []int{1, 2, 3, 4, 5} + arr2 := []int{3, 4, 5, 6, 7} + missing := []int{1, 2} + additional := []int{6, 7} + + intEql := func(a, b *int) bool { + return *a == *b + } + actualMissing, actualAdditional := difference(arr1, arr2, intEql) + + Expect(actualMissing).To(Equal(missing)) + Expect(actualAdditional).To(Equal(additional)) + }) + }) +}) diff --git a/hack/runtime-migrator/go.mod b/hack/runtime-migrator/go.mod index 3cc852f6..80169d7a 100644 --- a/hack/runtime-migrator/go.mod +++ b/hack/runtime-migrator/go.mod @@ -8,6 +8,8 @@ require ( github.com/go-playground/validator/v10 v10.23.0 github.com/kyma-project/infrastructure-manager v0.0.0-20241023155010-55a6abeb1690 github.com/kyma-project/infrastructure-manager/hack/shoot-comparator v0.0.0-20241023155010-55a6abeb1690 + github.com/onsi/ginkgo/v2 v2.22.0 + github.com/onsi/gomega v1.36.1 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.10.0 k8s.io/api v0.32.1 @@ -39,11 +41,13 @@ require ( github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -55,7 +59,6 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/gomega v1.36.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect @@ -72,6 +75,7 @@ require ( golang.org/x/term v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect + golang.org/x/tools v0.28.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.35.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/hack/runtime-migrator/go.sum b/hack/runtime-migrator/go.sum index de60bc1f..c71c5422 100644 --- a/hack/runtime-migrator/go.sum +++ b/hack/runtime-migrator/go.sum @@ -145,8 +145,8 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.36.0 h1:Pb12RlruUtj4XUuPUqeEWc6j5DkVVVA49Uf6YLfC95Y= -github.com/onsi/gomega v1.36.0/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=