diff --git a/hack/runtime-migrator/cmd/main.go b/hack/runtime-migrator/cmd/main.go index 23af4373..07eed123 100644 --- a/hack/runtime-migrator/cmd/main.go +++ b/hack/runtime-migrator/cmd/main.go @@ -54,13 +54,11 @@ func main() { slog.Info("Migrating runtimes") migrator := NewMigration(cfg, converterConfig, kubeconfigProvider, kcpClient, gardenerShootClient) - results, err := migrator.Do(getRuntimeIDsFromStdin(cfg)) + err = migrator.Do(getRuntimeIDsFromStdin(cfg)) if err != nil { slog.Error(fmt.Sprintf("Failed to migrate runtimes - %v", err)) os.Exit(1) } - - slog.Info(fmt.Sprintf("Migration completed. Successfully migrated runtimes: %d, Failed migrations: %d, Differences detected: %d", results.Succeeded, results.Failed, results.DifferenceDetected)) } func setupKubernetesKubeconfigProvider(kubeconfigPath string, namespace string, expirationTime time.Duration) (kubeconfig.Provider, error) { diff --git a/hack/runtime-migrator/cmd/migration.go b/hack/runtime-migrator/cmd/migration.go index 84aed2b3..e1117523 100644 --- a/hack/runtime-migrator/cmd/migration.go +++ b/hack/runtime-migrator/cmd/migration.go @@ -36,19 +36,19 @@ type Migration struct { shootClient gardener_types.ShootInterface } -func (m Migration) Do(runtimeIDs []string) (migration.MigrationResults, error) { +func (m Migration) Do(runtimeIDs []string) error { outputWriter, err := migration.NewOutputWriter(m.migrationConfig.OutputPath) if err != nil { - return migration.MigrationResults{}, err + return err } shootList, err := m.shootClient.List(context.Background(), v1.ListOptions{}) if err != nil { - return migration.MigrationResults{}, err + return err } - results := migration.NewMigratorResults(outputWriter.RuntimeDir) + results := migration.NewMigratorResults(outputWriter.NewResultsDir) for _, runtimeID := range runtimeIDs { slog.Info(fmt.Sprintf("Migrating runtime with ID: %s", runtimeID)) @@ -80,22 +80,13 @@ func (m Migration) Do(runtimeIDs []string) (migration.MigrationResults, error) { shootComparisonResult, err := m.runtimeVerifier.Do(runtime, *shoot) if err != nil { msg := "Failed to verify runtime" - results.ValidationFailed(runtimeID, shoot.Name) - slog.Error(msg, "runtimeID", runtimeID) - - continue - } - - err = outputWriter.SaveComparisonResult(shootComparisonResult) - if err != nil { - msg := "Failed to store comparison results" results.ErrorOccurred(runtimeID, shoot.Name, msg) - slog.Error(fmt.Sprintf("Failed to save comparison result: %v", err), "runtimeID", runtimeID) + slog.Error(msg, "runtimeID", runtimeID) continue } - if shootComparisonResult.Diff != nil && !m.migrationConfig.IsDryRun { + if shootComparisonResult.IsEqual() && !m.migrationConfig.IsDryRun { err = m.applyRuntimeCR(runtime) if err != nil { msg := "Failed to apply Runtime CR" @@ -106,18 +97,31 @@ func (m Migration) Do(runtimeIDs []string) (migration.MigrationResults, error) { continue } - results.OperationSucceeded(runtimeID, shoot.Name) + if shootComparisonResult.IsEqual() { + results.OperationSucceeded(runtimeID, shoot.Name) + } else { + err = outputWriter.SaveComparisonResult(shootComparisonResult) + if err != nil { + msg := "Failed to store comparison results" + results.ErrorOccurred(runtimeID, shoot.Name, msg) + slog.Error(fmt.Sprintf("Failed to save comparison result: %v", err), "runtimeID", runtimeID) + + continue + } + + results.ValidationFailed(runtimeID, shoot.Name) + } } resultsFile, err := outputWriter.SaveMigrationResults(results) if err != nil { - return results, err + 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 results, nil + return nil } func findShoot(runtimeID string, shootList *v1beta1.ShootList) *v1beta1.Shoot { diff --git a/hack/runtime-migrator/internal/migration/output.go b/hack/runtime-migrator/internal/migration/output.go index 2bfc0c50..282c74c6 100644 --- a/hack/runtime-migrator/internal/migration/output.go +++ b/hack/runtime-migrator/internal/migration/output.go @@ -47,7 +47,7 @@ func NewOutputWriter(outputDir string) (OutputWriter, error) { }, nil } -func (ow OutputWriter) SaveMigrationResults(results MigrationResults) (string, error) { +func (ow OutputWriter) SaveMigrationResults(results Results) (string, error) { resultFile, err := json.Marshal(results.Results) if err != nil { return "", err @@ -81,6 +81,62 @@ func writeSpecToFile(outputPath, runtimeID string, shootAsYaml []byte) error { } func (ow OutputWriter) SaveComparisonResult(comparisonResult runtime.ShootComparisonResult) error { + comparisonResultsForRuntimeDir := path.Join(ow.ComparisonResultsDir, comparisonResult.RuntimeID) + err := os.MkdirAll(comparisonResultsForRuntimeDir, 0644) + if err != nil { + return err + } + + if comparisonResult.Diff != nil { + err = writeResultsToDiffFiles(comparisonResult.OriginalShoot.Name, comparisonResult.Diff, comparisonResultsForRuntimeDir) + if err != nil { + return err + } + } + + err = saveShootToFile(path.Join(comparisonResultsForRuntimeDir, "original-shoot.yaml"), comparisonResult.OriginalShoot) + if err != nil { + return err + } + + return saveShootToFile(path.Join(comparisonResultsForRuntimeDir, "converted-shoot.yaml"), comparisonResult.ConvertedShoot) +} + +func saveShootToFile(filePath string, shoot interface{}) error { + shootAsYaml, err := yaml.Marshal(shoot) + if err != nil { + return err + } + + err = os.WriteFile(filePath, shootAsYaml, 0644) + if err != nil { + return err + } return nil } + +func writeResultsToDiffFiles(shootName string, difference *runtime.Difference, resultsDir string) error { + writeAndCloseFunc := func(filePath string, text string) error { + file, err := os.Create(filePath) + if err != nil { + return err + } + defer func() { + if file != nil { + err := file.Close() + if err != nil { + fmt.Printf("failed to close file: %v", err) + } + } + }() + + _, err = file.Write([]byte(text)) + + return err + } + + diffFile := path.Join(resultsDir, fmt.Sprintf("%s.diff", shootName)) + + return writeAndCloseFunc(diffFile, string(*difference)) +} diff --git a/hack/runtime-migrator/internal/migration/results.go b/hack/runtime-migrator/internal/migration/results.go index 2741cfd1..efb2ef54 100644 --- a/hack/runtime-migrator/internal/migration/results.go +++ b/hack/runtime-migrator/internal/migration/results.go @@ -5,9 +5,7 @@ import ( migrator "github.com/kyma-project/infrastructure-manager/hack/runtime-migrator-app/internal" ) -const runtimeCrFullPath = "%sshoot-dd%s.yaml" - -type MigrationResults struct { +type Results struct { Results []migrator.MigrationResult Succeeded int Failed int @@ -15,51 +13,56 @@ type MigrationResults struct { OutputDirectory string } -func NewMigratorResults(outputDirectory string) MigrationResults { - return MigrationResults{ +func NewMigratorResults(outputDirectory string) Results { + return Results{ Results: make([]migrator.MigrationResult, 0), OutputDirectory: outputDirectory, } } -func (mr MigrationResults) ErrorOccurred(runtimeID, shootName string, errorMsg string) { +func (mr *Results) ErrorOccurred(runtimeID, shootName string, errorMsg string) { result := migrator.MigrationResult{ - RuntimeID: runtimeID, - ShootName: shootName, - Status: migrator.StatusError, - ErrorMessage: errorMsg, - PathToCRYaml: mr.getYamlPath(shootName), + RuntimeID: runtimeID, + ShootName: shootName, + Status: migrator.StatusError, + ErrorMessage: errorMsg, + RuntimeCRFilePath: mr.getRuntimeCRPath(shootName), } mr.Failed++ mr.Results = append(mr.Results, result) } -func (mr MigrationResults) ValidationFailed(runtimeID, shootName string) { +func (mr *Results) ValidationFailed(runtimeID, shootName string) { result := migrator.MigrationResult{ - RuntimeID: runtimeID, - ShootName: shootName, - Status: migrator.StatusRuntimeCRCanCauseUnwantedUpdate, - ErrorMessage: "Runtime may cause unwanted update in Gardener. Please verify the runtime CR.", - PathToCRYaml: mr.getYamlPath(runtimeID), + RuntimeID: runtimeID, + ShootName: shootName, + Status: migrator.StatusRuntimeCRCanCauseUnwantedUpdate, + ErrorMessage: "Runtime may cause unwanted update in Gardener. Please verify the runtime CR.", + RuntimeCRFilePath: mr.getRuntimeCRPath(runtimeID), + ComparisonResultsDirPath: mr.getComparisonResultPath(runtimeID), } mr.DifferenceDetected++ mr.Results = append(mr.Results, result) } -func (mr MigrationResults) OperationSucceeded(runtimeID string, shootName string) { +func (mr *Results) OperationSucceeded(runtimeID string, shootName string) { result := migrator.MigrationResult{ - RuntimeID: runtimeID, - ShootName: shootName, - Status: migrator.StatusSuccess, - PathToCRYaml: mr.getYamlPath(runtimeID), + RuntimeID: runtimeID, + ShootName: shootName, + Status: migrator.StatusSuccess, + RuntimeCRFilePath: mr.getRuntimeCRPath(runtimeID), } mr.Succeeded++ mr.Results = append(mr.Results, result) } -func (mr MigrationResults) getYamlPath(runtimeID string) string { - return fmt.Sprintf(runtimeCrFullPath, mr.OutputDirectory, runtimeID) +func (mr *Results) getRuntimeCRPath(runtimeID string) string { + return fmt.Sprintf("%s/runtimes/%s", mr.OutputDirectory, runtimeID) +} + +func (mr *Results) getComparisonResultPath(runtimeID string) string { + return fmt.Sprintf("%s/comparison-results/%s", mr.OutputDirectory, runtimeID) } diff --git a/hack/runtime-migrator/internal/result.go b/hack/runtime-migrator/internal/result.go index cc62c554..d296ca65 100644 --- a/hack/runtime-migrator/internal/result.go +++ b/hack/runtime-migrator/internal/result.go @@ -4,7 +4,8 @@ type StatusType string const ( StatusSuccess StatusType = "Success" - StatusError StatusType = "Error" + StatusError StatusType = "GenerationError" + StatusCRApplyError StatusType = "CRApplyError" StatusAlreadyExists StatusType = "AlreadyExists" StatusRuntimeIDNotFound StatusType = "RuntimeIDNotFound" StatusFailedToCreateRuntimeCR StatusType = "FailedToCreateRuntimeCR" @@ -12,9 +13,10 @@ const ( ) type MigrationResult struct { - RuntimeID string `json:"runtimeId"` - ShootName string `json:"shootName"` - Status StatusType `json:"status"` - ErrorMessage string `json:"errorMessage,omitempty"` - PathToCRYaml string `json:"pathToCRYaml,omitempty"` + RuntimeID string `json:"runtimeId"` + ShootName string `json:"shootName"` + Status StatusType `json:"status"` + ErrorMessage string `json:"errorMessage,omitempty"` + RuntimeCRFilePath string `json:"runtimeCRFilePath,omitempty"` + ComparisonResultsDirPath string `json:"comparisonResultDirPath,omitempty"` } diff --git a/hack/runtime-migrator/internal/runtime/verifier.go b/hack/runtime-migrator/internal/runtime/verifier.go index cb54060e..59e83a40 100644 --- a/hack/runtime-migrator/internal/runtime/verifier.go +++ b/hack/runtime-migrator/internal/runtime/verifier.go @@ -21,6 +21,7 @@ func NewVerifier(converterConfig config.ConverterConfig, outputPath string) Veri } type ShootComparisonResult struct { + RuntimeID string OriginalShoot v1beta1.Shoot ConvertedShoot v1beta1.Shoot Diff *Difference @@ -41,6 +42,7 @@ func (v Verifier) Do(runtimeToVerify v1.Runtime, shootToMatch v1beta1.Shoot) (Sh } return ShootComparisonResult{ + RuntimeID: runtimeToVerify.Name, OriginalShoot: shootToMatch, ConvertedShoot: shootFromConverter, Diff: diff, @@ -61,3 +63,7 @@ func compare(originalShoot, convertedShoot v1beta1.Shoot) (*Difference, error) { return nil, nil } + +func (cr ShootComparisonResult) IsEqual() bool { + return cr.Diff == nil +} diff --git a/hack/runtime-migrator/main.go b/hack/runtime-migrator/main.go index 6912ffb2..f7b4445c 100644 --- a/hack/runtime-migrator/main.go +++ b/hack/runtime-migrator/main.go @@ -140,10 +140,10 @@ func main() { } results = append(results, migrator.MigrationResult{ - RuntimeID: shoot.Annotations[runtimeIDAnnotation], - ShootName: shoot.Name, - Status: migrator.StatusSuccess, - PathToCRYaml: fmt.Sprintf(runtimeCrFullPath, cfg.OutputPath, shoot.Name), + RuntimeID: shoot.Annotations[runtimeIDAnnotation], + ShootName: shoot.Name, + Status: migrator.StatusSuccess, + RuntimeCRFilePath: fmt.Sprintf(runtimeCrFullPath, cfg.OutputPath, shoot.Name), }) } encoder := json.NewEncoder(os.Stdout)