Skip to content

Commit

Permalink
Merge pull request #450 from akgalwas/migrator-refactor
Browse files Browse the repository at this point in the history
Migrator refactor
  • Loading branch information
m00g3n authored Oct 29, 2024
2 parents c0273a2 + 620309d commit 2423ea3
Show file tree
Hide file tree
Showing 13 changed files with 816 additions and 723 deletions.
133 changes: 133 additions & 0 deletions hack/runtime-migrator/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package main

import (
"encoding/json"
"fmt"
"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"
"github.com/kyma-project/infrastructure-manager/pkg/gardener"
"github.com/kyma-project/infrastructure-manager/pkg/gardener/kubeconfig"
"github.com/pkg/errors"
"log"
"log/slog"
"os"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"time"
)

const (
contextTimeout = 5 * time.Minute
expirationTime = 60 * time.Minute
runtimeIDAnnotation = "kcp.provisioner.kyma-project.io/runtime-id"
)

func main() {
slog.Info("Starting runtime-migrator")
cfg := config.NewConfig()

opts := zap.Options{
Development: true,
}
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)
if err != nil {
slog.Error(fmt.Sprintf("Failed to create kubeconfig provider: %v", err))
os.Exit(1)
}

kcpClient, err := config.CreateKcpClient(&cfg)
if err != nil {
slog.Error("Failed to create kcp client: %v ", err)
os.Exit(1)
}

gardenerShootClient, err := setupGardenerShootClient(cfg.GardenerKubeconfigPath, gardenerNamespace)
if err != nil {
slog.Error("Failed to setup Gardener shoot client: %v", err)
os.Exit(1)
}

slog.Info("Migrating runtimes")
migrator, err := NewMigration(cfg, converterConfig, kubeconfigProvider, kcpClient, gardenerShootClient)
if err != nil {
slog.Error("Failed to create migrator: %v", err)
os.Exit(1)
}

err = migrator.Do(getRuntimeIDsFromStdin(cfg))
if err != nil {
slog.Error(fmt.Sprintf("Failed to migrate runtimes: %v", err))
os.Exit(1)
}
}

func setupKubernetesKubeconfigProvider(kubeconfigPath string, namespace string, expirationTime time.Duration) (kubeconfig.Provider, error) {
restConfig, err := gardener.NewRestConfigFromFile(kubeconfigPath)
if err != nil {
return kubeconfig.Provider{}, err
}

gardenerClientSet, err := gardener_types.NewForConfig(restConfig)
if err != nil {
return kubeconfig.Provider{}, err
}

gardenerClient, err := client.New(restConfig, client.Options{})
if err != nil {
return kubeconfig.Provider{}, err
}

shootClient := gardenerClientSet.Shoots(namespace)
dynamicKubeconfigAPI := gardenerClient.SubResource("adminkubeconfig")

err = v1beta1.AddToScheme(gardenerClient.Scheme())
if err != nil {
return kubeconfig.Provider{}, errors.Wrap(err, "failed to register Gardener schema")
}

return kubeconfig.NewKubeconfigProvider(shootClient,
dynamicKubeconfigAPI,
namespace,
int64(expirationTime.Seconds())), nil
}

func getRuntimeIDsFromStdin(cfg config.Config) []string {
var runtimeIDs []string
if cfg.InputType == config.InputTypeJSON {
decoder := json.NewDecoder(os.Stdin)
slog.Info("Reading runtimeIds from stdin")
if err := decoder.Decode(&runtimeIDs); err != nil {
log.Printf("Could not load list of RuntimeIds - %s", err)
}
}
return runtimeIDs
}

func setupGardenerShootClient(kubeconfigPath, gardenerNamespace string) (gardener_types.ShootInterface, error) {
restConfig, err := gardener.NewRestConfigFromFile(kubeconfigPath)
if err != nil {
return nil, err
}

gardenerClientSet, err := gardener_types.NewForConfig(restConfig)
if err != nil {
return nil, err
}

shootClient := gardenerClientSet.Shoots(gardenerNamespace)

return shootClient, nil
}
157 changes: 157 additions & 0 deletions hack/runtime-migrator/cmd/migration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package main

import (
"context"
"fmt"
"github.com/gardener/gardener/pkg/apis/core/v1beta1"
gardener_types "github.com/gardener/gardener/pkg/client/core/clientset/versioned/typed/core/v1beta1"
runtimev1 "github.com/kyma-project/infrastructure-manager/api/v1"
config2 "github.com/kyma-project/infrastructure-manager/hack/runtime-migrator-app/internal/config"
"github.com/kyma-project/infrastructure-manager/hack/runtime-migrator-app/internal/migration"
"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"
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) {

outputWriter, err := migration.NewOutputWriter(migratorConfig.OutputPath)
if err != nil {
return Migration{}, err
}

return Migration{
runtimeMigrator: runtime.NewMigrator(migratorConfig, converterConfig, kubeconfigProvider, kcpClient),
runtimeVerifier: runtime.NewVerifier(converterConfig, migratorConfig.OutputPath),
kcpClient: kcpClient,
shootClient: shootClient,
outputWriter: outputWriter,
isDryRun: migratorConfig.IsDryRun,
}, 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)
defer cancel()

shootList, err := m.shootClient.List(migratorContext, v1.ListOptions{})
if err != nil {
return err
}

results := migration.NewMigratorResults(m.outputWriter.NewResultsDir)

reportError := func(runtimeID, shootName string, msg string, err error) {
var errorMsg string

if err != nil {
errorMsg = fmt.Sprintf("%s: %v", msg, err)
} else {
errorMsg = fmt.Sprintf(msg)
}

results.ErrorOccurred(runtimeID, shootName, errorMsg)
slog.Error(errorMsg, "runtimeID", runtimeID)
}

reportValidationError := func(runtimeID, shootName string, msg string) {
results.ValidationFailed(runtimeID, shootName)
slog.Warn(msg, "runtimeID", runtimeID)
}

reportSuccess := func(runtimeID, shootName string, msg string) {
results.OperationSucceeded(runtimeID, shootName)
slog.Info(msg, "runtimeID", runtimeID)
}

for _, runtimeID := range runtimeIDs {
slog.Info(fmt.Sprintf("Migrating runtime with ID: %s", runtimeID))

shoot := findShoot(runtimeID, shootList)
if shoot == nil {
reportError(runtimeID, "", "Failed to find shoot", nil)

continue
}

runtime, err := m.runtimeMigrator.Do(migratorContext, *shoot)
if err != nil {
reportError(runtimeID, shoot.Name, "Failed to migrate runtime", err)

continue
}

err = m.outputWriter.SaveRuntimeCR(runtime)
if err != nil {
reportError(runtimeID, shoot.Name, "Failed to save runtime CR", err)

continue
}

shootComparisonResult, err := m.runtimeVerifier.Do(runtime, *shoot)
if err != nil {
reportError(runtimeID, shoot.Name, "Failed to verify runtime", err)

continue
}

if !shootComparisonResult.IsEqual() {
err = m.outputWriter.SaveComparisonResult(shootComparisonResult)
if err != nil {
reportError(runtimeID, shoot.Name, "Failed to save comparison result", err)
} else {
reportValidationError(runtimeID, shoot.Name, "Runtime CR can cause unwanted update in Gardener. Please verify the runtime CR.")
}

continue
}

if !m.isDryRun {
err = m.applyRuntimeCR(runtime)
if err != nil {
reportError(runtimeID, shoot.Name, "Failed to apply Runtime CR", err)
}

continue
}

reportSuccess(runtimeID, shoot.Name, "Runtime processed successfully")
}

resultsFile, err := m.outputWriter.SaveMigrationResults(results)
if err != nil {
return err
}

slog.Info(fmt.Sprintf("Migration completed. Successfully migrated runtimes: %d, Failed migrations: %d, Differences detected: %d", results.Succeeded, results.Failed, results.DifferenceDetected))
slog.Info(fmt.Sprintf("Migration results saved in: %s", resultsFile))

return nil
}

func findShoot(runtimeID string, shootList *v1beta1.ShootList) *v1beta1.Shoot {
for _, shoot := range shootList.Items {
if shoot.Annotations[runtimeIDAnnotation] == runtimeID {
return &shoot
}
}
return nil
}

func (m Migration) applyRuntimeCR(runtime runtimev1.Runtime) error {
// TODO: This method covers create scenario only, we should implement update as well
return m.kcpClient.Create(context.Background(), &runtime)
}
7 changes: 5 additions & 2 deletions hack/runtime-migrator/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
k8s.io/api v0.31.1
k8s.io/apimachinery v0.31.1
k8s.io/client-go v0.31.1
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6
sigs.k8s.io/controller-runtime v0.19.0
sigs.k8s.io/yaml v1.4.0
)
Expand All @@ -23,6 +24,7 @@ require (
github.com/gardener/gardener-extension-provider-gcp v1.39.0 // indirect
github.com/gardener/gardener-extension-provider-openstack v1.42.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
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
Expand All @@ -42,6 +44,8 @@ require (
github.com/onsi/gomega v1.34.2 // indirect
github.com/spf13/pflag v1.0.5 // indirect
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/net v0.30.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/sys v0.26.0 // indirect
Expand All @@ -55,12 +59,11 @@ require (
k8s.io/apiextensions-apiserver v0.31.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a // indirect
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
)

replace (
github.com/kyma-project/infrastructure-manager => ../../
github.com/kyma-project/infrastructure-manager/hack/shoot-comparator => ../shoot-comparator
)
)
6 changes: 2 additions & 4 deletions hack/runtime-migrator/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kyma-project/infrastructure-manager v0.0.0-20241023155010-55a6abeb1690 h1:fE1U3PFxlJo5vF2wfltfflRc9QvPMC9MTysrtCmYWQA=
github.com/kyma-project/infrastructure-manager v0.0.0-20241023155010-55a6abeb1690/go.mod h1:9rM6mFtC3i/vufP+rXNu2mIJazjnqVXFRUUwEP4/fmA=
github.com/kyma-project/infrastructure-manager/hack/shoot-comparator v0.0.0-20241023155010-55a6abeb1690 h1:OxOvbNmewE4ZZXFhEGC8UqlVgJvAEYWB/FYaPY7R7Q8=
github.com/kyma-project/infrastructure-manager/hack/shoot-comparator v0.0.0-20241023155010-55a6abeb1690/go.mod h1:4bf+z9t4M8+5incqcRpmoq6lv4+tRJC8reLtP1pYO+c=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
Expand Down Expand Up @@ -102,6 +98,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
Expand Down
Loading

0 comments on commit 2423ea3

Please sign in to comment.