From c576025841e3dbb20030e8d63dfff326ab93c1db Mon Sep 17 00:00:00 2001 From: Daniel Gonzalez Date: Tue, 10 Dec 2024 10:50:40 -0600 Subject: [PATCH] support dry run and plan in spinnaker migration --- app.go | 2 ++ main.go | 11 +++++++++++ pipelines.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- types.go | 1 + 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/app.go b/app.go index 24e42e2..e89cee7 100644 --- a/app.go +++ b/app.go @@ -91,9 +91,11 @@ func migrateSpinnakerApplication() error { } dryRun := migrationReq.DryRun + plan := migrationReq.Plan payload := map[string]interface{}{ "pipelines": pipelines, // Expecting pipelines as []map[string]interface{} "dryRun": dryRun, // dryRun as a bool + "planOnly": plan, } _, err = createSpinnakerPipelines(payload, dryRun) return err diff --git a/main.go b/main.go index 43dc71e..5be1e08 100644 --- a/main.go +++ b/main.go @@ -44,6 +44,7 @@ var migrationReq = struct { UrlNG string `survey:"urlNG"` UrlCG string `survey:"urlCG"` DryRun bool `survey:"dryRun"` + Plan bool `survey:"plan"` FileExtensions string `survey:"fileExtensions"` CustomExpressionsFile string `survey:"customExpressionsFile"` CustomStringsFile string `survey:"customStringsFile"` @@ -392,6 +393,11 @@ func main() { Usage: "perform a dry run without side effects", Destination: &migrationReq.DryRun, }), + altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: "plan", + Usage: "gets the harness entities as yaml", + Destination: &migrationReq.DryRun, + }), } app := &cli.App{ Name: "harness-upgrade", @@ -597,6 +603,11 @@ func main() { Usage: "dry run", Destination: &migrationReq.DryRun, }, + &cli.BoolFlag{ + Name: "plan", + Usage: "shows the entities as yaml", + Destination: &migrationReq.Plan, + }, &cli.BoolFlag{ Name: "all", Usage: "all pipelines", diff --git a/pipelines.go b/pipelines.go index 31f7963..0382b21 100644 --- a/pipelines.go +++ b/pipelines.go @@ -1,10 +1,12 @@ package main import ( + "encoding/base64" "encoding/json" "errors" "fmt" "os" + "path/filepath" "regexp" "sort" "strconv" @@ -127,9 +129,11 @@ func migrateSpinnakerPipelines() error { return err } dryRun := migrationReq.DryRun + plan := migrationReq.Plan payload := map[string]interface{}{ "pipelines": pipelines, // Expecting pipelines as []map[string]interface{} "dryRun": dryRun, // dryRun as a bool + "planOnly": plan, } _, err = createSpinnakerPipelines(payload, dryRun) @@ -522,18 +526,58 @@ func createSpinnakerPipelines(pipelines map[string]interface{}, dryRun bool) (re // Pretty print successfullyMigratedDetails if len(resource.SuccessfullyMigratedDetails) > 0 { printCreatedEntities(resource.SuccessfullyMigratedDetails) + if migrationReq.Plan { + saveProjectFiles(resource.SuccessfullyMigratedDetails, migrationReq.ProjectIdentifier) + } } else { return "", fmt.Errorf("spinnaker migration failed") } if !dryRun && migrationReq.Environment != Dev { reconcilePipeline(resp, queryParams) + } + + if !dryRun { log.Info("Spinnaker migration completed") } else { - log.Infof("Note: This was a dry run of the spinnaker migration") + log.Info("Note: This was a dry run of the spinnaker migration") } return reqId, nil } + +func saveProjectFiles(details []SuccessfullyMigratedDetail, projectIdentifier string) { + // Create the project folder if it doesn't exist + projectFolder := filepath.Join(".", projectIdentifier) + if _, err := os.Stat(projectFolder); os.IsNotExist(err) { + err := os.MkdirAll(projectFolder, os.ModePerm) + if err != nil { + log.Fatalf("Error creating directory %s: %v", projectFolder, err) + } + log.Printf("Created directory: %s", projectFolder) + } + + for _, detail := range details { + if len(detail.AdditionalInfo) > 0 { + // Generate the filename with the full path under the project folder + filename := filepath.Join(projectFolder, fmt.Sprintf("%s_%s.yaml", detail.NgEntityDetail.EntityType, detail.NgEntityDetail.Identifier)) + + // Decode the Base64 string + decodedInfo, err := base64.StdEncoding.DecodeString(detail.AdditionalInfo) + if err != nil { + log.Printf("Error decoding AdditionalInfo for %s: %v", filename, err) + continue + } + // Save the decoded content to the file + err = os.WriteFile(filename, decodedInfo, 0644) + if err != nil { + log.Printf("Error saving file %s: %v", filename, err) + } else { + log.Printf("Successfully saved file: %s", filename) + } + } + } +} + func printAllErrors(pipelines map[string]interface{}, errors []UpgradeError) { stages, _ := getSupportedStages() if stages != nil { @@ -554,7 +598,7 @@ func printResourceErrors(errors []UpgradeError) error { func printCreatedEntities(resources []SuccessfullyMigratedDetail) { for _, detail := range resources { ngDetail := detail.NgEntityDetail - log.Printf("created entity:\n EntityType: %s\n Identifier: %s\n OrgIdentifier: %s\n ProjectIdentifier: %s\n", + log.Printf("created entity:\n EntityType: %s\n Identifier: %s\n OrgIdentifier: %s\n ProjectIdentifier: %s\n\n", ngDetail.EntityType, ngDetail.Identifier, ngDetail.OrgIdentifier, ngDetail.ProjectIdentifier) } } diff --git a/types.go b/types.go index 5ab528d..f2ab5aa 100644 --- a/types.go +++ b/types.go @@ -203,6 +203,7 @@ type Resource struct { type SuccessfullyMigratedDetail struct { CgEntityDetail interface{} `json:"cgEntityDetail"` // Assuming cgEntityDetail can be nil or of a specific type NgEntityDetail NgEntityDetail `json:"ngEntityDetail"` + AdditionalInfo string `json:"additionalInfo"` } type NgEntityDetail struct {