diff --git a/hack/runtime-migrator/cmd/main.go b/hack/runtime-migrator/cmd/main.go index 062d4223..4e98dc87 100644 --- a/hack/runtime-migrator/cmd/main.go +++ b/hack/runtime-migrator/cmd/main.go @@ -2,15 +2,23 @@ package main import ( "bufio" + "context" "encoding/json" "fmt" + "github.com/go-playground/validator/v10" + "github.com/kyma-project/infrastructure-manager/pkg/gardener/shoot/extender/auditlogs" + "io" + v12 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" "log/slog" "os" + "strings" "time" "github.com/gardener/gardener/pkg/apis/core/v1beta1" gardener_types "github.com/gardener/gardener/pkg/client/core/clientset/versioned/typed/core/v1beta1" "github.com/kyma-project/infrastructure-manager/hack/runtime-migrator-app/internal/config" + kimConfig "github.com/kyma-project/infrastructure-manager/pkg/config" "github.com/kyma-project/infrastructure-manager/pkg/gardener" "github.com/kyma-project/infrastructure-manager/pkg/gardener/kubeconfig" "github.com/pkg/errors" @@ -35,12 +43,6 @@ func main() { logger := zap.New(zap.UseFlagOptions(&opts)) logf.SetLogger(logger) - converterConfig, err := config.LoadConverterConfig(cfg) - if err != nil { - slog.Error(fmt.Sprintf("Unable to load converter config: %v", err)) - os.Exit(1) - } - gardenerNamespace := fmt.Sprintf("garden-%s", cfg.GardenerProjectName) kubeconfigProvider, err := setupKubernetesKubeconfigProvider(cfg.GardenerKubeconfigPath, gardenerNamespace, expirationTime) @@ -61,15 +63,27 @@ func main() { os.Exit(1) } + auditLogConfig, err := getAuditLogConfig(kcpClient) + if err != nil { + slog.Error("Failed to get audit log config: %v", slog.Any("error", err)) + os.Exit(1) + } + + converterConfig, err := getConverterConfig(kcpClient) + if err != nil { + slog.Error("Failed to get converter config: %v", slog.Any("error", err)) + os.Exit(1) + } + slog.Info("Migrating runtimes") - migrator, err := NewMigration(cfg, converterConfig, kubeconfigProvider, kcpClient, gardenerShootClient) + migrator, err := NewMigration(cfg, converterConfig, auditLogConfig, kubeconfigProvider, kcpClient, gardenerShootClient) if err != nil { slog.Error("Failed to create migrator: %v", slog.Any("error", err)) os.Exit(1) } - slog.Info("Reading runtimeIds from stdin") - runtimeIds, err := getRuntimeIDsFromStdin(cfg) + slog.Info("Reading runtimeIds from input file") + runtimeIds, err := getRuntimeIDsFromInputFile(cfg) if err != nil { slog.Error("Failed to read runtime Ids from input: %v", slog.Any("error", err)) os.Exit(1) @@ -112,19 +126,29 @@ func setupKubernetesKubeconfigProvider(kubeconfigPath string, namespace string, int64(expirationTime.Seconds())), nil } -func getRuntimeIDsFromStdin(cfg config.Config) ([]string, error) { +func getRuntimeIDsFromInputFile(cfg config.Config) ([]string, error) { var runtimeIDs []string var err error if cfg.InputType == config.InputTypeJSON { - decoder := json.NewDecoder(os.Stdin) + file, err := os.Open(cfg.InputFilePath) + if err != nil { + return nil, err + } + decoder := json.NewDecoder(file) err = decoder.Decode(&runtimeIDs) } else if cfg.InputType == config.InputTypeTxt { - scanner := bufio.NewScanner(os.Stdin) + file, err := os.Open(cfg.InputFilePath) + if err != nil { + return nil, err + } + scanner := bufio.NewScanner(file) for scanner.Scan() { runtimeIDs = append(runtimeIDs, scanner.Text()) } err = scanner.Err() + } else { + return nil, fmt.Errorf("invalid input type: %s", cfg.InputType) } return runtimeIDs, err } @@ -144,3 +168,63 @@ func setupGardenerShootClient(kubeconfigPath, gardenerNamespace string) (gardene return shootClient, nil } + +func getAuditLogConfig(kcpClient client.Client) (auditlogs.Configuration, error) { + var cm v12.ConfigMap + key := types.NamespacedName{ + Name: "audit-extension-config", + Namespace: "kcp-system", + } + + err := kcpClient.Get(context.Background(), key, &cm) + if err != nil { + return nil, err + } + + configBytes := []byte(cm.Data["config"]) + + var data auditlogs.Configuration + if err := json.Unmarshal(configBytes, &data); err != nil { + return nil, err + } + + validate := validator.New(validator.WithRequiredStructEnabled()) + + for _, nestedMap := range data { + for _, auditLogData := range nestedMap { + if err := validate.Struct(auditLogData); err != nil { + return nil, err + } + } + } + + return data, nil +} + +func getConverterConfig(kcpClient client.Client) (kimConfig.ConverterConfig, error) { + var cm v12.ConfigMap + key := types.NamespacedName{ + Name: "infrastructure-manager-converter-config", + Namespace: "kcp-system", + } + + err := kcpClient.Get(context.Background(), key, &cm) + if err != nil { + return kimConfig.ConverterConfig{}, err + } + + getReader := func() (io.Reader, error) { + data, found := cm.Data["converter_config.json"] + if !found { + return nil, fmt.Errorf("converter_config.json not found in ConfigMap") + } + return strings.NewReader(data), nil + } + + var cfg kimConfig.Config + if err = cfg.Load(getReader); err != nil { + return kimConfig.ConverterConfig{}, err + } + + return cfg.ConverterConfig, nil +} diff --git a/hack/runtime-migrator/cmd/migration.go b/hack/runtime-migrator/cmd/migration.go index 80827f84..7d23a283 100644 --- a/hack/runtime-migrator/cmd/migration.go +++ b/hack/runtime-migrator/cmd/migration.go @@ -11,12 +11,22 @@ import ( "github.com/kyma-project/infrastructure-manager/hack/runtime-migrator-app/internal/runtime" "github.com/kyma-project/infrastructure-manager/pkg/config" "github.com/kyma-project/infrastructure-manager/pkg/gardener/kubeconfig" + "github.com/kyma-project/infrastructure-manager/pkg/gardener/shoot/extender/auditlogs" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "log/slog" "sigs.k8s.io/controller-runtime/pkg/client" ) -func NewMigration(migratorConfig config2.Config, converterConfig config.ConverterConfig, kubeconfigProvider kubeconfig.Provider, kcpClient client.Client, shootClient gardener_types.ShootInterface) (Migration, error) { +type Migration struct { + runtimeMigrator runtime.Migrator + runtimeVerifier runtime.Verifier + kcpClient client.Client + shootClient gardener_types.ShootInterface + outputWriter migration.OutputWriter + isDryRun bool +} + +func NewMigration(migratorConfig config2.Config, converterConfig config.ConverterConfig, auditLogConfig auditlogs.Configuration, kubeconfigProvider kubeconfig.Provider, kcpClient client.Client, shootClient gardener_types.ShootInterface) (Migration, error) { outputWriter, err := migration.NewOutputWriter(migratorConfig.OutputPath) if err != nil { @@ -24,8 +34,8 @@ func NewMigration(migratorConfig config2.Config, converterConfig config.Converte } return Migration{ - runtimeMigrator: runtime.NewMigrator(migratorConfig, converterConfig, kubeconfigProvider, kcpClient), - runtimeVerifier: runtime.NewVerifier(converterConfig, migratorConfig.OutputPath), + runtimeMigrator: runtime.NewMigrator(migratorConfig, kubeconfigProvider), + runtimeVerifier: runtime.NewVerifier(converterConfig, auditLogConfig), kcpClient: kcpClient, shootClient: shootClient, outputWriter: outputWriter, @@ -33,15 +43,6 @@ func NewMigration(migratorConfig config2.Config, converterConfig config.Converte }, nil } -type Migration struct { - runtimeMigrator runtime.Migrator - runtimeVerifier runtime.Verifier - kcpClient client.Client - shootClient gardener_types.ShootInterface - outputWriter migration.OutputWriter - isDryRun bool -} - func (m Migration) Do(runtimeIDs []string) error { migratorContext, cancel := context.WithTimeout(context.Background(), contextTimeout) diff --git a/hack/runtime-migrator/go.mod b/hack/runtime-migrator/go.mod index c64083e1..1ae29a43 100644 --- a/hack/runtime-migrator/go.mod +++ b/hack/runtime-migrator/go.mod @@ -4,6 +4,7 @@ go 1.23.1 require ( github.com/gardener/gardener v1.106.1 + github.com/go-playground/validator/v10 v10.22.1 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/pkg/errors v0.9.1 @@ -21,6 +22,7 @@ require ( github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gardener/gardener-extension-provider-aws v1.57.1 // indirect github.com/gardener/gardener-extension-provider-gcp v1.39.0 // indirect github.com/gardener/gardener-extension-provider-openstack v1.42.1 // indirect @@ -30,6 +32,8 @@ require ( github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect 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/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 @@ -39,6 +43,7 @@ require ( github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -48,6 +53,7 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect + golang.org/x/crypto v0.28.0 // indirect golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sys v0.26.0 // indirect diff --git a/hack/runtime-migrator/go.sum b/hack/runtime-migrator/go.sum index e49734bf..3f593cd4 100644 --- a/hack/runtime-migrator/go.sum +++ b/hack/runtime-migrator/go.sum @@ -34,6 +34,8 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= diff --git a/hack/runtime-migrator/internal/config/config.go b/hack/runtime-migrator/internal/config/config.go index 3f82c7ec..e2a8da06 100644 --- a/hack/runtime-migrator/internal/config/config.go +++ b/hack/runtime-migrator/internal/config/config.go @@ -3,15 +3,11 @@ package config import ( "flag" "fmt" - "io" - "log" - "os" - v1 "github.com/kyma-project/infrastructure-manager/api/v1" - "github.com/kyma-project/infrastructure-manager/pkg/config" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/clientcmd" + "log" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -22,7 +18,7 @@ type Config struct { OutputPath string IsDryRun bool InputType string - ConverterConfigPath string + InputFilePath string } const ( @@ -49,7 +45,7 @@ func NewConfig() Config { flag.StringVar(&result.OutputPath, "output-path", "/tmp/", "Path where generated yamls will be saved. Directory has to exist.") flag.BoolVar(&result.IsDryRun, "dry-run", true, "Dry-run flag. Has to be set to 'false' otherwise it will not apply the Custom Resources on the KCP cluster.") flag.StringVar(&result.InputType, "input-type", InputTypeJSON, "Type of input to be used. Possible values: **all** (will migrate all gardener shoots), and **json** (will migrate only cluster whose runtimeIds were passed as an input, see the example hack/runtime-migrator/input/runtimeids_sample.json).") - flag.StringVar(&result.ConverterConfigPath, "converter-config-filepath", "/path/to/converter_config.json", "A file path to the Gardener Shoot converter configuration.") + flag.StringVar(&result.InputFilePath, "input-file-path", "/path/to/input/file", "Path to the input file containing RuntimeCRs to be migrated.") flag.Parse() @@ -105,15 +101,3 @@ func CreateKcpClient(cfg *Config) (client.Client, error) { return k8sClient, nil } - -func LoadConverterConfig(cfg Config) (config.ConverterConfig, error) { - getReader := func() (io.Reader, error) { - return os.Open(cfg.ConverterConfigPath) - } - var kimConfig config.Config - if err := kimConfig.Load(getReader); err != nil { - log.Print(err, "unable to load converter configuration") - return config.ConverterConfig{}, err - } - return kimConfig.ConverterConfig, nil -} diff --git a/hack/runtime-migrator/internal/runtime/migrator.go b/hack/runtime-migrator/internal/runtime/migrator.go index 367528bd..d63aa723 100644 --- a/hack/runtime-migrator/internal/runtime/migrator.go +++ b/hack/runtime-migrator/internal/runtime/migrator.go @@ -28,12 +28,10 @@ type Migrator struct { kcpClient client.Client } -func NewMigrator(cfg migrator.Config, converterConfig config.ConverterConfig, kubeconfigProvider kubeconfig.Provider, kcpClient client.Client) Migrator { +func NewMigrator(cfg migrator.Config, kubeconfigProvider kubeconfig.Provider) Migrator { return Migrator{ cfg: cfg, - converterConfig: converterConfig, kubeconfigProvider: kubeconfigProvider, - kcpClient: kcpClient, } } diff --git a/hack/runtime-migrator/internal/runtime/verifier.go b/hack/runtime-migrator/internal/runtime/verifier.go index 59e83a40..f5dc3e2a 100644 --- a/hack/runtime-migrator/internal/runtime/verifier.go +++ b/hack/runtime-migrator/internal/runtime/verifier.go @@ -6,17 +6,20 @@ import ( "github.com/kyma-project/infrastructure-manager/hack/shoot-comparator/pkg/shoot" "github.com/kyma-project/infrastructure-manager/pkg/config" gardener_shoot "github.com/kyma-project/infrastructure-manager/pkg/gardener/shoot" + "github.com/kyma-project/infrastructure-manager/pkg/gardener/shoot/extender/auditlogs" "k8s.io/utils/ptr" + "slices" ) type Verifier struct { converterConfig config.ConverterConfig - outputPath string + auditLogConfig auditlogs.Configuration } -func NewVerifier(converterConfig config.ConverterConfig, outputPath string) Verifier { +func NewVerifier(converterConfig config.ConverterConfig, auditLogConfig auditlogs.Configuration) Verifier { return Verifier{ converterConfig: converterConfig, + auditLogConfig: auditLogConfig, } } @@ -30,7 +33,11 @@ type ShootComparisonResult struct { type Difference string func (v Verifier) Do(runtimeToVerify v1.Runtime, shootToMatch v1beta1.Shoot) (ShootComparisonResult, error) { - converter := gardener_shoot.NewConverter(v.converterConfig) + converter, err := v.newConverter(shootToMatch) + if err != nil { + return ShootComparisonResult{}, err + } + shootFromConverter, err := converter.ToShoot(runtimeToVerify) if err != nil { return ShootComparisonResult{}, err @@ -49,6 +56,36 @@ func (v Verifier) Do(runtimeToVerify v1.Runtime, shootToMatch v1beta1.Shoot) (Sh }, nil } +func (v Verifier) newConverter(shootToMatch v1beta1.Shoot) (gardener_shoot.Converter, error) { + auditLogData, err := v.auditLogConfig.GetAuditLogData( + shootToMatch.Spec.Provider.Type, + shootToMatch.Spec.Region) + + if err != nil { + return gardener_shoot.Converter{}, err + } + + return gardener_shoot.NewConverterPatch(gardener_shoot.PatchOpts{ + ConverterConfig: v.converterConfig, + AuditLogData: auditLogData, + Zones: getZones(shootToMatch.Spec.Provider.Workers), + }), nil +} + +func getZones(workers []v1beta1.Worker) []string { + var zones []string + + for _, worker := range workers { + for _, zone := range worker.Zones { + if !slices.Contains(zones, zone) { + zones = append(zones, zone) + } + } + } + + return zones +} + func compare(originalShoot, convertedShoot v1beta1.Shoot) (*Difference, error) { matcher := shoot.NewMatcher(originalShoot) equal, err := matcher.Match(convertedShoot)