diff --git a/client.go b/client.go index 0867a70..70eec42 100644 --- a/client.go +++ b/client.go @@ -15,8 +15,9 @@ func Post(reqUrl string, auth string, body interface{}) (respBodyObj ResponseBod postBody, _ := json.Marshal(body) requestBody := bytes.NewBuffer(postBody) log.WithFields(log.Fields{ + "url": reqUrl, "body": string(postBody), - }).Debug("The request body") + }).Debug("The request details") req, err := http.NewRequest("POST", reqUrl, requestBody) if err != nil { return @@ -31,18 +32,42 @@ func Get(reqUrl string, auth string) (respBodyObj ResponseBody, err error) { if err != nil { return } + log.WithFields(log.Fields{ + "url": reqUrl, + }).Debug("The request details") req.Header.Set("Content-Type", "application/json") req.Header.Set(AuthHeaderKey(auth), auth) return handleResp(req) } -func Delete(reqUrl string, auth string) (respBodyObj ResponseBody, err error) { - req, err := http.NewRequest("DELETE", reqUrl, nil) +func Delete(reqUrl string, auth string, body interface{}) (respBodyObj ResponseBody, err error) { + var requestBody *bytes.Buffer + if body != nil { + postBody, _ := json.Marshal(body) + requestBody = bytes.NewBuffer(postBody) + log.WithFields(log.Fields{ + "url": reqUrl, + "body": string(postBody), + }).Debug("The request details") + } else { + log.WithFields(log.Fields{ + "url": reqUrl, + }).Debug("The request details") + } + var req *http.Request + if requestBody != nil { + req, err = http.NewRequest("DELETE", reqUrl, requestBody) + } else { + req, err = http.NewRequest("DELETE", reqUrl, nil) + } if err != nil { return } req.Header.Set("Content-Type", "application/json") req.Header.Set(AuthHeaderKey(auth), auth) + if requestBody == nil { + req.ContentLength = 0 + } return handleResp(req) } diff --git a/constants.go b/constants.go index 3429913..4755d80 100644 --- a/constants.go +++ b/constants.go @@ -28,3 +28,9 @@ const ( ) var scopes = []string{"project", "org", "account"} + +const ( + AccountIdentifier = "accountIdentifier" + OrgIdentifier = "orgIdentifier" + ProjectIdentifier = "projectIdentifier" +) diff --git a/docs/docs/advanced/utility-commands.md b/docs/docs/advanced/utility-commands.md index 56392c3..d3e932d 100644 --- a/docs/docs/advanced/utility-commands.md +++ b/docs/docs/advanced/utility-commands.md @@ -126,4 +126,93 @@ harness-upgrade --api-key SAT_API_KEY \ --env ENV \ --org ORG \ project --identifiers identifier1,identifier2 rm +``` + +## Pipelines Management + +### Remove pipelines +To remove pipelines, you can provide their names or identifiers. + +To remove pipelines by name use `--names` +```shell +harness-upgrade --api-key SAT_API_KEY \ + --account ACCOUNT_ID \ + --org ORG_ID \ + --project PROJECT_ID \ + --env ENV \ + pipelines --names name1,name2 rm ``` + +To remove pipelines by identifier use `--identifiers` +```shell +harness-upgrade --api-key SAT_API_KEY \ + --account ACCOUNT_ID \ + --org ORG_ID \ + --project PROJECT_ID \ + --env ENV \ + pipelines --identifiers identifier1,identifier2 rm +``` + +To remove all pipelines +```shell +harness-upgrade --api-key SAT_API_KEY \ + --account ACCOUNT_ID \ + --org ORG_ID \ + --project PROJECT_ID \ + --env ENV \ + pipelines --all rm +``` + +## Templates Management + +### Remove templates +To remove templates, you can provide their names or identifiers. + +To remove templates by name use `--names` +```shell +harness-upgrade --api-key SAT_API_KEY \ + --account ACCOUNT_ID \ + --org ORG_ID \ + --project PROJECT_ID \ + --env ENV \ + templates --names name1,name2 rm +``` + +To remove templates by identifier use `--identifiers` +```shell +harness-upgrade --api-key SAT_API_KEY \ + --account ACCOUNT_ID \ + --org ORG_ID \ + --project PROJECT_ID \ + --env ENV \ + templates --identifiers identifier1,identifier2 rm +``` + +To remove all templates +```shell +harness-upgrade --api-key SAT_API_KEY \ + --account ACCOUNT_ID \ + --org ORG_ID \ + --project PROJECT_ID \ + --env ENV \ + templates --all rm +``` + +If the templates are being referenced, the deletion may fail. Use the `--force` flag to force delete the templates: + +```shell +harness-upgrade --api-key SAT_API_KEY \ + --account ACCOUNT_ID \ + --org ORG_ID \ + --project PROJECT_ID \ + --env ENV \ + templates --all --force rm +``` + +:::info +The above commands are for removing project level templates. + +If you want to remove org level templates, do not pass the --project flag. + +If you want to remove global level templates, do not pass the --project and --org flags. +::: diff --git a/docs/docs/upgrade/migrate-app.md b/docs/docs/upgrade/migrate-app.md index 30aefdf..f293d82 100644 --- a/docs/docs/upgrade/migrate-app.md +++ b/docs/docs/upgrade/migrate-app.md @@ -110,7 +110,7 @@ harness-upgrade --api-key SAT_API_KEY \ --template-scope SCOPE \ --workflow-scope SCOPE \ --env ENV \ - workflows --all + pipelines --all import ``` ### Migrating specific pipelines @@ -126,7 +126,7 @@ harness-upgrade --api-key SAT_API_KEY \ --template-scope SCOPE \ --workflow-scope SCOPE \ --env ENV \ - pipelines --pipelines PIPELINE_IDS + pipelines --pipelines PIPELINE_IDS import ``` ## Migrating triggers diff --git a/entity.go b/entity.go index 74e4607..928c398 100644 --- a/entity.go +++ b/entity.go @@ -20,7 +20,7 @@ func CreateEntities(body RequestBody) { } func QueueCreateEntity(body RequestBody) (reqId string, err error) { - url := GetUrl(migrationReq.Environment, MIGRATOR, "save/async", migrationReq.Account) + url := GetUrl(migrationReq.Environment, MigratorService, "save/async", migrationReq.Account) resp, err := Post(url, migrationReq.Auth, body) if err != nil { log.Fatal("Failed to create the entities", err) @@ -41,9 +41,9 @@ func PollForCompletion(reqId string) { s.Start() for { time.Sleep(time.Second) - url := GetUrlWithQueryParams(migrationReq.Environment, MIGRATOR, "save/async-result", map[string]string{ - "accountIdentifier": migrationReq.Account, - "requestId": reqId, + url := GetUrlWithQueryParams(migrationReq.Environment, MigratorService, "save/async-result", map[string]string{ + AccountIdentifier: migrationReq.Account, + "requestId": reqId, }) resp, err := Get(url, migrationReq.Auth) if err != nil { diff --git a/helper.go b/helper.go index b926e99..6d46a02 100644 --- a/helper.go +++ b/helper.go @@ -21,26 +21,36 @@ const ( ) const ( - MIGRATOR string = "Migrator" - NG = "NextGen" + MigratorService string = "Migrator" + NextGenService = "NextGen" + TemplateService = "Template" + PipelineService = "Pipeline" ) var urlMap = map[string]map[string]string{ Prod: { - MIGRATOR: "https://app.harness.io/gateway/ng-migration", - NG: "https://app.harness.io/gateway/ng", + PipelineService: "https://app.harness.io/gateway/pipeline", + TemplateService: "https://app.harness.io/gateway/template", + MigratorService: "https://app.harness.io/gateway/ng-migration/api/ng-migration", + NextGenService: "https://app.harness.io/gateway/ng", }, QA: { - MIGRATOR: "https://qa.harness.io/gateway/ng-migration", - NG: "https://qa.harness.io/gateway/ng", + PipelineService: "https://qa.harness.io/gateway/pipeline", + TemplateService: "https://qa.harness.io/gateway/template", + MigratorService: "https://qa.harness.io/gateway/ng-migration/api/ng-migration", + NextGenService: "https://qa.harness.io/gateway/ng", }, Dev: { - MIGRATOR: "https://localhost:9080", - NG: "https://localhost:8181/ng", + PipelineService: "https://localhost:8181/pipeline", + TemplateService: "https://localhost:8181/template", + MigratorService: "https://localhost:9080/api/ng-migration", + NextGenService: "https://localhost:8181/ng", }, Prod3: { - MIGRATOR: "https://app3.harness.io/gateway/ng-migration", - NG: "https://app3.harness.io/gateway/ng", + PipelineService: "https://app3.harness.io/gateway/pipeline", + TemplateService: "https://app3.harness.io/gateway/template", + MigratorService: "https://app3.harness.io/gateway/ng-migration/api/ng-migration", + NextGenService: "https://app3.harness.io/gateway/ng", }, } @@ -87,11 +97,11 @@ func GetUrlWithQueryParams(environment string, service string, endpoint string, params = params + k + "=" + v + "&" } - return fmt.Sprintf("%s/api/ng-migration/%s?%s", GetBaseUrl(environment, service), endpoint, params) + return fmt.Sprintf("%s/%s?%s", GetBaseUrl(environment, service), endpoint, params) } func GetUrl(environment string, service string, path string, accountId string) string { - return fmt.Sprintf("%s/api/ng-migration/%s?accountIdentifier=%s", GetBaseUrl(environment, service), path, accountId) + return fmt.Sprintf("%s/%s?accountIdentifier=%s", GetBaseUrl(environment, service), path, accountId) } func getOrDefault(value string, defaultValue string) string { @@ -213,9 +223,9 @@ func Split(str string, sep string) (result []string) { } func listEntities(entity string) (data []BaseEntityDetail, err error) { - url := GetUrlWithQueryParams(migrationReq.Environment, MIGRATOR, entity, map[string]string{ - "accountIdentifier": migrationReq.Account, - "appId": migrationReq.AppId, + url := GetUrlWithQueryParams(migrationReq.Environment, MigratorService, entity, map[string]string{ + AccountIdentifier: migrationReq.Account, + "appId": migrationReq.AppId, }) resp, err := Get(url, migrationReq.Auth) if err != nil { @@ -246,10 +256,14 @@ func GetBaseUrl(environment string, service string) string { } var url string switch service { - case NG: + case PipelineService: + url = migrationReq.BaseUrl + "/pipeline" + case TemplateService: + url = migrationReq.BaseUrl + "/template" + case NextGenService: url = migrationReq.BaseUrl + "/ng" - case MIGRATOR: - url = migrationReq.BaseUrl + "/ng-migration" + case MigratorService: + url = migrationReq.BaseUrl + "/ng-migration/api/ng-migration" default: panic("Unknown service! Please contact Harness support") } diff --git a/main.go b/main.go index f5f3c00..f5b6525 100644 --- a/main.go +++ b/main.go @@ -51,6 +51,7 @@ var migrationReq = struct { TargetAccount string `survey:"targetAccount"` TargetAuthToken string `survey:"targetAuth"` BaseUrl string `survey:"baseUrl"` + Force bool `survey:"force"` }{} func getReqBody(entityType EntityType, filter Filter) RequestBody { @@ -397,17 +398,40 @@ func main() { Flags: []cli.Flag{ &cli.BoolFlag{ Name: "all", - Usage: "if all pipelines in the app need to be migrated", + Usage: "all pipelines", Destination: &migrationReq.All, }, altsrc.NewStringFlag(&cli.StringFlag{ Name: "pipelines", - Usage: "pipelines as comma separated values `pipeline1,pipeline2`", + Usage: "first gen pipeline ids as comma separated values `pipeline1,pipeline2`", Destination: &migrationReq.PipelineIds, }), + &cli.StringFlag{ + Name: "identifiers", + Usage: "`IDENTIFIERS` of the next gen pipelines", + Destination: &migrationReq.Identifiers, + }, + &cli.StringFlag{ + Name: "names", + Usage: "`NAMES` of the next gen pipeline", + Destination: &migrationReq.Names, + }, }, - Action: func(context *cli.Context) error { - return cliWrapper(migratePipelines, context) + Subcommands: []*cli.Command{ + { + Name: "rm", + Usage: "Remove nextgen pipelines", + Action: func(context *cli.Context) error { + return cliWrapper(BulkRemovePipelines, context) + }, + }, + { + Name: "import", + Usage: "import first gen pipelines to next gen", + Action: func(context *cli.Context) error { + return cliWrapper(migratePipelines, context) + }, + }, }, }, { @@ -565,6 +589,41 @@ func main() { }, }, }, + { + Name: "templates", + Usage: "Template specific commands.", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "identifiers", + Usage: "`IDENTIFIERS` of the template", + Destination: &migrationReq.Identifiers, + }, + &cli.StringFlag{ + Name: "names", + Usage: "`NAMES` of the template", + Destination: &migrationReq.Names, + }, + &cli.BoolFlag{ + Name: "force", + Usage: "to force delete template", + Destination: &migrationReq.Force, + }, + &cli.BoolFlag{ + Name: "all", + Usage: "if set will delete all templates", + Destination: &migrationReq.All, + }, + }, + Subcommands: []*cli.Command{ + { + Name: "rm", + Usage: "Remove templates", + Action: func(context *cli.Context) error { + return cliWrapper(BulkRemoveTemplates, context) + }, + }, + }, + }, }, Before: altsrc.InitInputSourceWithContext(globalFlags, altsrc.NewYamlSourceFromFlagFunc("load")), Flags: globalFlags, diff --git a/org.go b/org.go index 7c59bd8..469cd40 100644 --- a/org.go +++ b/org.go @@ -32,7 +32,7 @@ func createOrg(*cli.Context) error { } } - url := fmt.Sprintf("%s/api/organizations?accountIdentifier=%s", GetBaseUrl(migrationReq.Environment, NG), migrationReq.Account) + url := fmt.Sprintf("%s/api/organizations?accountIdentifier=%s", GetBaseUrl(migrationReq.Environment, NextGenService), migrationReq.Account) log.Info("Creating the org....") @@ -83,7 +83,7 @@ func bulkRemoveOrg(*cli.Context) error { identifiers = append(identifiers, id) } } - log.Debugf("Valid identifers for the given names are - %s", identifiers) + log.Debugf("Valid identifiers for the given names are - %s", identifiers) } for _, identifier := range identifiers { @@ -94,11 +94,11 @@ func bulkRemoveOrg(*cli.Context) error { } func deleteOrg(orgId string) { - url := fmt.Sprintf("%s/api/organizations/%s?accountIdentifier=%s", GetBaseUrl(migrationReq.Environment, NG), orgId, migrationReq.Account) + url := fmt.Sprintf("%s/api/organizations/%s?accountIdentifier=%s", GetBaseUrl(migrationReq.Environment, NextGenService), orgId, migrationReq.Account) log.Infof("Deleting the org with identifier %s", orgId) - _, err := Delete(url, migrationReq.Auth) + _, err := Delete(url, migrationReq.Auth, nil) if err == nil { log.Infof("Successfully deleted the org - %s", orgId) @@ -108,7 +108,7 @@ func deleteOrg(orgId string) { } func getOrganisations() []OrgDetails { - url := fmt.Sprintf("%s/api/aggregate/organizations?accountIdentifier=%s&pageSize=1000", GetBaseUrl(migrationReq.Environment, NG), migrationReq.Account) + url := fmt.Sprintf("%s/api/aggregate/organizations?accountIdentifier=%s&pageSize=1000", GetBaseUrl(migrationReq.Environment, NextGenService), migrationReq.Account) resp, err := Get(url, migrationReq.Auth) if err != nil || resp.Status != "SUCCESS" { log.Fatal("Failed to fetch organisations", err) diff --git a/pipelines.go b/pipelines.go index d458c48..b8a3cd4 100644 --- a/pipelines.go +++ b/pipelines.go @@ -1,8 +1,11 @@ package main import ( + "encoding/json" + "fmt" log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" + "strconv" ) func migratePipelines(*cli.Context) error { @@ -50,3 +53,105 @@ func migratePipelines(*cli.Context) error { return nil } + +func BulkRemovePipelines(*cli.Context) error { + promptConfirm := PromptEnvDetails() + promptConfirm = PromptOrgAndProject([]string{Project}) || promptConfirm + names := Split(migrationReq.Names, ",") + identifiers := Split(migrationReq.Identifiers, ",") + + if migrationReq.All { + identifiers = []string{} + pipelines := getPipelines(migrationReq.OrgIdentifier, migrationReq.ProjectIdentifier) + for _, pipeline := range pipelines { + identifiers = append(identifiers, pipeline.Identifier) + } + } + + if len(names) == 0 && len(identifiers) == 0 { + log.Fatal("No names or identifiers for the pipelines provided. Aborting") + } + if len(names) > 0 && len(identifiers) > 0 { + log.Fatal("Both names and identifiers for the pipelines provided. Aborting") + } + + n := len(identifiers) + if len(names) > 0 { + n = len(names) + } + if promptConfirm { + confirm := ConfirmInput("Are you sure you want to proceed with deletion of " + strconv.Itoa(n) + " pipelines?") + if !confirm { + log.Fatal("Aborting...") + } + } + + if len(names) > 0 { + pipelines := getPipelines(migrationReq.OrgIdentifier, migrationReq.ProjectIdentifier) + for _, name := range names { + id := findPipelineIdByName(pipelines, name) + if len(id) > 0 { + identifiers = append(identifiers, id) + } + } + log.Debugf("Valid identifiers for the given names are - %s", identifiers) + } + + for _, identifier := range identifiers { + deletePipeline(migrationReq.OrgIdentifier, migrationReq.ProjectIdentifier, identifier) + } + log.Info("Finished operation for all given pipelines") + return nil +} + +func deletePipeline(orgId string, projectId string, pipelineId string) { + queryParams := map[string]string{ + ProjectIdentifier: projectId, + OrgIdentifier: orgId, + AccountIdentifier: migrationReq.Account, + } + url := GetUrlWithQueryParams(migrationReq.Environment, PipelineService, fmt.Sprintf("api/pipelines/%s", pipelineId), queryParams) + + log.Infof("Deleting the pipeline with identifier %s", pipelineId) + + _, err := Delete(url, migrationReq.Auth, nil) + + if err == nil { + log.Infof("Successfully deleted the pipeline - %s", pipelineId) + } else { + log.Errorf("Failed to delete the pipeline - %s", pipelineId) + } +} + +func getPipelines(orgId string, projectId string) []PipelineDetails { + queryParams := map[string]string{ + ProjectIdentifier: projectId, + OrgIdentifier: orgId, + AccountIdentifier: migrationReq.Account, + "size": "1000", + } + url := GetUrlWithQueryParams(migrationReq.Environment, PipelineService, "api/pipelines/list", queryParams) + resp, err := Post(url, migrationReq.Auth, FilterRequestBody{FilterType: "PipelineSetup"}) + if err != nil || resp.Status != "SUCCESS" { + log.Fatal("Failed to fetch pipelines", err) + } + byteData, err := json.Marshal(resp.Data) + if err != nil { + log.Fatal("Failed to fetch pipelines", err) + } + var pipelineListBody PipelineListBody + err = json.Unmarshal(byteData, &pipelineListBody) + if err != nil { + log.Fatal("Failed to fetch pipelines", err) + } + return pipelineListBody.Pipelines +} + +func findPipelineIdByName(pipelines []PipelineDetails, name string) string { + for _, o := range pipelines { + if o.Name == name { + return o.Identifier + } + } + return "" +} diff --git a/projects.go b/projects.go index 2573674..ca16a2f 100644 --- a/projects.go +++ b/projects.go @@ -53,7 +53,7 @@ func createProject(*cli.Context) error { } func createAProject(orgIdentifier string, name string, identifier string) error { - url := fmt.Sprintf("%s/api/projects?accountIdentifier=%s&orgIdentifier=%s", GetBaseUrl(migrationReq.Environment, NG), migrationReq.Account, orgIdentifier) + url := fmt.Sprintf("%s/api/projects?accountIdentifier=%s&orgIdentifier=%s", GetBaseUrl(migrationReq.Environment, NextGenService), migrationReq.Account, orgIdentifier) _, err := Post(url, migrationReq.Auth, ProjectBody{ Project: ProjectDetails{ OrgIdentifier: orgIdentifier, @@ -97,8 +97,8 @@ func bulkCreateProject(*cli.Context) error { return CreateProjectsUsingCSV() } - url := GetUrlWithQueryParams(migrationReq.Environment, MIGRATOR, "projects/bulk", map[string]string{ - "accountIdentifier": migrationReq.Account, + url := GetUrlWithQueryParams(migrationReq.Environment, MigratorService, "projects/bulk", map[string]string{ + AccountIdentifier: migrationReq.Account, }) log.Info("Creating the projects....") @@ -206,7 +206,7 @@ func bulkRemoveProject(*cli.Context) error { identifiers = append(identifiers, id) } } - log.Debugf("Valid identifers for the given names are - %s", identifiers) + log.Debugf("Valid identifiers for the given names are - %s", identifiers) } for _, identifier := range identifiers { @@ -217,11 +217,11 @@ func bulkRemoveProject(*cli.Context) error { } func deleteProject(projectId string) { - url := fmt.Sprintf("%s/api/projects/%s?accountIdentifier=%s&orgIdentifier=%s", GetBaseUrl(migrationReq.Environment, NG), projectId, migrationReq.Account, migrationReq.OrgIdentifier) + url := fmt.Sprintf("%s/api/projects/%s?accountIdentifier=%s&orgIdentifier=%s", GetBaseUrl(migrationReq.Environment, NextGenService), projectId, migrationReq.Account, migrationReq.OrgIdentifier) log.Infof("Deleting the project with identifier %s", projectId) - _, err := Delete(url, migrationReq.Auth) + _, err := Delete(url, migrationReq.Auth, nil) if err == nil { log.Infof("Successfully deleted the project - %s", projectId) @@ -231,7 +231,7 @@ func deleteProject(projectId string) { } func getProjects() []ProjectDetails { - url := fmt.Sprintf("%s/api/projects?accountIdentifier=%s&orgIdentifier=%s&pageSize=1000", GetBaseUrl(migrationReq.Environment, NG), migrationReq.Account, migrationReq.OrgIdentifier) + url := fmt.Sprintf("%s/api/projects?accountIdentifier=%s&orgIdentifier=%s&pageSize=1000", GetBaseUrl(migrationReq.Environment, NextGenService), migrationReq.Account, migrationReq.OrgIdentifier) resp, err := Get(url, migrationReq.Auth) if err != nil || resp.Status != "SUCCESS" { log.Fatal("Failed to fetch projects", err) diff --git a/summary.go b/summary.go index c5b7076..0038692 100644 --- a/summary.go +++ b/summary.go @@ -14,7 +14,7 @@ import ( func GetAccountSummary(*cli.Context) error { _ = PromptEnvDetails() - url := GetUrl(migrationReq.Environment, MIGRATOR, "discover/summary/async", migrationReq.Account) + url := GetUrl(migrationReq.Environment, MigratorService, "discover/summary/async", migrationReq.Account) return handleSummary(url) } @@ -23,9 +23,9 @@ func GetAppSummary(*cli.Context) error { if len(migrationReq.AppId) == 0 { migrationReq.AppId = TextInput("Please provide the application ID - ") } - url := GetUrlWithQueryParams(migrationReq.Environment, MIGRATOR, "discover/summary/async", map[string]string{ - "accountIdentifier": migrationReq.Account, - "appId": migrationReq.AppId, + url := GetUrlWithQueryParams(migrationReq.Environment, MigratorService, "discover/summary/async", map[string]string{ + AccountIdentifier: migrationReq.Account, + "appId": migrationReq.AppId, }) return handleSummary(url) } @@ -47,9 +47,9 @@ func handleSummary(url string) error { s.Start() for { time.Sleep(time.Second) - url := GetUrlWithQueryParams(migrationReq.Environment, MIGRATOR, "discover/summary/async-result", map[string]string{ - "accountIdentifier": migrationReq.Account, - "requestId": reqId, + url := GetUrlWithQueryParams(migrationReq.Environment, MigratorService, "discover/summary/async-result", map[string]string{ + AccountIdentifier: migrationReq.Account, + "requestId": reqId, }) resp, err := Get(url, migrationReq.Auth) if err != nil { diff --git a/templates.go b/templates.go new file mode 100644 index 0000000..3e27735 --- /dev/null +++ b/templates.go @@ -0,0 +1,126 @@ +package main + +import ( + "encoding/json" + "fmt" + log "github.com/sirupsen/logrus" + "github.com/urfave/cli/v2" + "strconv" +) + +func BulkRemoveTemplates(*cli.Context) error { + promptConfirm := PromptEnvDetails() + names := Split(migrationReq.Names, ",") + identifiers := Split(migrationReq.Identifiers, ",") + + if migrationReq.All { + identifiers = []string{} + templates := getTemplates(migrationReq.OrgIdentifier, migrationReq.ProjectIdentifier, []string{}) + for _, template := range templates { + identifiers = append(identifiers, template.Identifier) + } + } + + if len(names) == 0 && len(identifiers) == 0 { + log.Fatal("No names or identifiers for the templates provided. Aborting") + } + if len(names) > 0 && len(identifiers) > 0 { + log.Fatal("Both names and identifiers for the templates provided. Aborting") + } + + n := len(identifiers) + if len(names) > 0 { + n = len(names) + } + if promptConfirm { + confirm := ConfirmInput("Are you sure you want to proceed with deletion of " + strconv.Itoa(n) + " templates?") + if !confirm { + log.Fatal("Aborting...") + } + } + + if len(names) > 0 { + templates := getTemplates(migrationReq.OrgIdentifier, migrationReq.ProjectIdentifier, []string{}) + for _, name := range names { + id := findTemplateIdByName(templates, name) + if len(id) > 0 { + identifiers = append(identifiers, id) + } + } + log.Debugf("Valid identifiers for the given names are - %s", identifiers) + } + + for _, identifier := range identifiers { + deleteTemplate(migrationReq.OrgIdentifier, migrationReq.ProjectIdentifier, identifier, migrationReq.Force) + } + log.Info("Finished operation for all given templates") + return nil +} + +func deleteTemplate(orgId string, projectId string, templateId string, force bool) { + templates := getTemplates(orgId, projectId, []string{templateId}) + var versions []string + for _, template := range templates { + versions = append(versions, template.VersionLabel) + } + queryParams := map[string]string{ + AccountIdentifier: migrationReq.Account, + "forceDelete": strconv.FormatBool(force), + } + if len(orgId) > 0 { + queryParams[OrgIdentifier] = orgId + } + if len(projectId) > 0 { + queryParams[ProjectIdentifier] = projectId + } + url := GetUrlWithQueryParams(migrationReq.Environment, TemplateService, fmt.Sprintf("api/templates/%s", templateId), queryParams) + + log.Infof("Deleting the template with identifier %s", templateId) + + _, err := Delete(url, migrationReq.Auth, TemplateDeleteBody{TemplateVersionLabels: versions}) + + if err == nil { + log.Infof("Successfully deleted the template - %s", templateId) + } else { + log.Errorf("Failed to delete the template - %s", templateId) + } +} + +func getTemplates(orgId string, projectId string, templateIdentifiers []string) []TemplateDetails { + queryParams := map[string]string{ + AccountIdentifier: migrationReq.Account, + "size": "1000", + "templateListType": "LastUpdated", + } + if len(orgId) > 0 { + queryParams[OrgIdentifier] = orgId + } + if len(projectId) > 0 { + queryParams[ProjectIdentifier] = projectId + } + url := GetUrlWithQueryParams(migrationReq.Environment, TemplateService, "api/templates/list-metadata", queryParams) + resp, err := Post(url, migrationReq.Auth, FilterRequestBody{FilterType: TemplateService, TemplateIdentifiers: templateIdentifiers}) + if err != nil || resp.Status != "SUCCESS" { + log.Fatal("Failed to fetch templates", err) + } + byteData, err := json.Marshal(resp.Data) + if err != nil { + log.Fatal("Failed to fetch templates", err) + } + var templateListBody TemplateListBody + err = json.Unmarshal(byteData, &templateListBody) + if err != nil { + log.Fatal("Failed to fetch templates", err) + } + + return templateListBody.Templates +} + +func findTemplateIdByName(templates []TemplateDetails, templateName string) string { + for _, o := range templates { + if o.Name == templateName { + return o.Identifier + } + } + return "" +} diff --git a/types.go b/types.go index 36dc2ed..a75e49f 100644 --- a/types.go +++ b/types.go @@ -87,6 +87,36 @@ type OrgBody struct { Org OrgDetails `json:"organization"` } +type TemplateListBody struct { + Templates []TemplateDetails `json:"content"` +} + +type TemplateDetails struct { + Identifier string `json:"identifier"` + Name string `json:"name"` + Description string `json:"description"` + VersionLabel string `json:"versionLabel"` +} + +type PipelineListBody struct { + Pipelines []PipelineDetails `json:"content"` +} + +type PipelineDetails struct { + Identifier string `json:"identifier"` + Name string `json:"name"` + Description string `json:"description"` +} + +type FilterRequestBody struct { + FilterType string `json:"filterType"` + TemplateIdentifiers []string `json:"templateIdentifiers"` +} + +type TemplateDeleteBody struct { + TemplateVersionLabels []string `json:"templateVersionLabels"` +} + type RequestBody struct { DestinationDetails DestinationDetails `json:"destinationDetails"` EntityType EntityType `json:"entityType"`