Skip to content

Commit

Permalink
feat: [ASSMT-651]: show migrated entities summary (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielGz authored Nov 12, 2024
1 parent 77d97bf commit 23fe214
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 40 deletions.
11 changes: 5 additions & 6 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,11 @@ func migrateSpinnakerApplication() error {
return nil
}

payload := map[string][]map[string]interface{}{"pipelines": pipelines}

stages, _ := getSupportedStages()
if stages != nil {
checkUnsupportedStages(payload, stages)
dryRun := migrationReq.DryRun
payload := map[string]interface{}{
"pipelines": pipelines, // Expecting pipelines as []map[string]interface{}
"dryRun": dryRun, // dryRun as a bool
}
_, err = createSpinnakerPipelines(payload)
_, err = createSpinnakerPipelines(payload, dryRun)
return err
}
12 changes: 11 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,11 @@ func main() {
Usage: "x509 key location for authenticating with spinnaker",
Destination: &migrationReq.Key,
}),
altsrc.NewBoolFlag(&cli.BoolFlag{
Name: "dryRun",
Usage: "perform a dry run without side effects",
Destination: &migrationReq.DryRun,
}),
}
app := &cli.App{
Name: "harness-upgrade",
Expand Down Expand Up @@ -587,6 +592,11 @@ func main() {
Name: "pipelines",
Usage: "Import pipelines into an existing project by providing the `appId` & `pipelineIds`",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "dryRun",
Usage: "dry run",
Destination: &migrationReq.DryRun,
},
&cli.BoolFlag{
Name: "all",
Usage: "all pipelines",
Expand Down Expand Up @@ -619,7 +629,7 @@ func main() {
},
&cli.BoolFlag{
Name: "insecure",
Usage: "Weteher to validate the TLS certificate or not",
Usage: "Whether to validate the TLS certificate or not",
Destination: &migrationReq.AllowInsecureReq,
},
&cli.StringFlag{
Expand Down
88 changes: 61 additions & 27 deletions pipelines.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,19 +126,17 @@ func migrateSpinnakerPipelines() error {
if err != nil {
return err
}

payload := map[string][]map[string]interface{}{"pipelines": pipelines}

stages, _ := getSupportedStages()
if stages != nil {
checkUnsupportedStages(payload, stages)
dryRun := migrationReq.DryRun
payload := map[string]interface{}{
"pipelines": pipelines, // Expecting pipelines as []map[string]interface{}
"dryRun": dryRun, // dryRun as a bool
}

_, err = createSpinnakerPipelines(payload)
_, err = createSpinnakerPipelines(payload, dryRun)
return err
}

func checkUnsupportedStages(payload map[string][]map[string]interface{}, supportedStages []string) {
func checkUnsupportedStages(payload map[string]interface{}, supportedStages []string) {
unsupportedStagesMap := make(map[string][]string)

// Convert supported stages to lowercase for case-insensitive comparison
Expand All @@ -151,7 +149,7 @@ func checkUnsupportedStages(payload map[string][]map[string]interface{}, support
re := regexp.MustCompile(`[^a-zA-Z0-9]+`)

// Iterate over each pipeline
for _, pipeline := range payload["pipelines"] {
for _, pipeline := range payload["pipelines"].([]map[string]interface{}) {
unsupportedStages := []string{}

// Get stages from the pipeline
Expand Down Expand Up @@ -180,14 +178,19 @@ func checkUnsupportedStages(payload map[string][]map[string]interface{}, support
}
}

// Log unsupported stages
if len(unsupportedStagesMap) > 0 {
for pipelineID, stages := range unsupportedStagesMap {
log.Warnf("Pipeline ID: %s, Unsupported Stages: %v", pipelineID, stages)
// Construct a single log message for each pipeline
message := fmt.Sprintf("\n Pipeline with id: %s\n has unsupported Stages:\n", pipelineID)
for _, stage := range stages {
message += fmt.Sprintf(" - %s\n", stage)
}
log.Warn(message)
}
} else {
log.Info("All stages in all pipelines are supported.")
}

}

func fetchDependentPipelines(pipelines []map[string]interface{}, err error, authMethod string) ([]map[string]interface{}, error) {
Expand Down Expand Up @@ -472,16 +475,19 @@ func getSupportedStages() ([]string, error) {
return stages, nil
}

func createSpinnakerPipelines(pipelines interface{}) (reqId string, err error) {
func createSpinnakerPipelines(pipelines map[string]interface{}, dryRun bool) (reqId string, err error) {
queryParams := map[string]string{
ProjectIdentifier: migrationReq.ProjectIdentifier,
OrgIdentifier: migrationReq.OrgIdentifier,
AccountIdentifier: migrationReq.Account,
}
err = CheckProjectExistsAndCreate()
if err != nil {
return "", err
if !dryRun && migrationReq.Environment != Dev {
err = CheckProjectExistsAndCreate()
if err != nil {
return "", err
}
}

j, err := json.MarshalIndent(pipelines, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal pipelines JSON: %v", err)
Expand All @@ -497,16 +503,9 @@ func createSpinnakerPipelines(pipelines interface{}) (reqId string, err error) {
if err != nil {
return "", fmt.Errorf("failed to get resource: %v", err)
}
if resource.Errors != nil && len(resource.Errors) > 0 {
// Convert the data to JSON
jsonData, err := json.MarshalIndent(resource.Errors, "", " ")
if err != nil {
return "", fmt.Errorf("failed to marshal resource errors JSON: %v", err)
}
// Convert bytes to string and print
jsonString := string(jsonData)
log.Warnf(jsonString)
return "", fmt.Errorf("failed to create pipeline : %v", migrationReq.PipelineName)
hasErrors := resource.Errors != nil && len(resource.Errors) > 0
if hasErrors {
printAllErrors(pipelines, resource.Errors)
}
if len(resource.RequestId) != 0 {
reqId = resource.RequestId
Expand All @@ -520,7 +519,42 @@ func createSpinnakerPipelines(pipelines interface{}) (reqId string, err error) {
jsonString := string(jsonData)
log.Warnf("Entity not migrated : %s", jsonString)
}
reconcilePipeline(resp, queryParams)
log.Info("Spinnaker migration completed")
// Pretty print successfullyMigratedDetails
if len(resource.SuccessfullyMigratedDetails) > 0 {
printCreatedEntities(resource.SuccessfullyMigratedDetails)
} else {
return "", fmt.Errorf("spinnaker migration failed")
}
if !dryRun && migrationReq.Environment != Dev {
reconcilePipeline(resp, queryParams)
log.Info("Spinnaker migration completed")
} else {
log.Infof("Note: This was a dry run of the spinnaker migration")
}

return reqId, nil
}
func printAllErrors(pipelines map[string]interface{}, errors []UpgradeError) {
stages, _ := getSupportedStages()
if stages != nil {
checkUnsupportedStages(pipelines, stages)
}
printResourceErrors(errors)
}

func printResourceErrors(errors []UpgradeError) error {
for _, err := range errors {
if !strings.Contains(err.Message, "SpinnakerStageType") {
log.Warnf(fmt.Sprintf(" . %s\n", err.Message))
}
}
return nil
}

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",
ngDetail.EntityType, ngDetail.Identifier, ngDetail.OrgIdentifier, ngDetail.ProjectIdentifier)
}
}
25 changes: 19 additions & 6 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,25 @@ type MigrationStats struct {
}

type Resource struct {
RequestId string `json:"requestId"`
Stats map[string]MigrationStats `json:"stats"`
Errors []UpgradeError `json:"errors"`
Status string `json:"status"`
ResponsePayload interface{} `json:"responsePayload"`
SkipDetails []NGSkipDetail `json:"skipDetails"`
RequestId string `json:"requestId"`
Stats map[string]MigrationStats `json:"stats"`
Errors []UpgradeError `json:"errors"`
Status string `json:"status"`
ResponsePayload interface{} `json:"responsePayload"`
SkipDetails []NGSkipDetail `json:"skipDetails"`
SuccessfullyMigratedDetails []SuccessfullyMigratedDetail `json:"successfullyMigratedDetails"`
}

type SuccessfullyMigratedDetail struct {
CgEntityDetail interface{} `json:"cgEntityDetail"` // Assuming cgEntityDetail can be nil or of a specific type
NgEntityDetail NgEntityDetail `json:"ngEntityDetail"`
}

type NgEntityDetail struct {
EntityType string `json:"entityType"`
Identifier string `json:"identifier"`
OrgIdentifier string `json:"orgIdentifier"`
ProjectIdentifier string `json:"projectIdentifier"`
}

type ResponseBody struct {
Expand Down

0 comments on commit 23fe214

Please sign in to comment.