From 75f2652d16a8a0ec937a1ad94adaad2580dc9c74 Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Wed, 28 Jun 2023 19:16:46 +0100 Subject: [PATCH 01/14] First draft for destroy namespace --- pkg/commands/environment.go | 78 +++++++++++++++++--- pkg/environment/environmentApply.go | 107 ++++++++++++++++++++++++++++ pkg/github/client.go | 9 +++ pkg/github/client_iface.go | 1 + 4 files changed, 185 insertions(+), 10 deletions(-) diff --git a/pkg/commands/environment.go b/pkg/commands/environment.go index 6d196ffe..04b0acbf 100644 --- a/pkg/commands/environment.go +++ b/pkg/commands/environment.go @@ -33,6 +33,7 @@ func addEnvironmentCmd(topLevel *cobra.Command) { environmentApplyCmd, environmentBumpModuleCmd, environmentCreateCmd, + environmentDestroyCmd, environmentDivergenceCmd, environmentEcrCmd, environmentPlanCmd, @@ -63,28 +64,39 @@ func addEnvironmentCmd(topLevel *cobra.Command) { environmentApplyCmd.Flags().StringVar(&optFlags.ClusterCtx, "cluster", "", "folder name under namespaces/ inside cloud-platform-environments repo refering to full cluster name") environmentApplyCmd.PersistentFlags().BoolVar(&optFlags.RedactedEnv, "redact", true, "Redact the terraform output before printing") - // e.g. if this is the Pull rquest to perform the apply: https://github.com/ministryofjustice/cloud-platform-environments/pull/8370, the pr ID is 8370. - environmentPlanCmd.Flags().IntVar(&optFlags.PRNumber, "prNumber", 0, "Pull request ID or number to which you want to perform the plan") - environmentPlanCmd.Flags().StringVarP(&optFlags.Namespace, "namespace", "n", "", "Namespace which you want to perform the plan") - - // Re-use the environmental variable TF_VAR_github_token to call Github Client which is needed to perform terraform operations on each namespace - environmentPlanCmd.Flags().StringVar(&optFlags.GithubToken, "github-token", os.Getenv("TF_VAR_github_token"), "Personal access Token from Github ") - environmentPlanCmd.Flags().StringVar(&optFlags.KubecfgPath, "kubecfg", filepath.Join(homedir.HomeDir(), ".kube", "config"), "path to kubeconfig file") - environmentPlanCmd.Flags().StringVar(&optFlags.ClusterCtx, "cluster", "", "folder name under namespaces/ inside cloud-platform-environments repo refering to full cluster name") - environmentPlanCmd.PersistentFlags().BoolVar(&optFlags.RedactedEnv, "redact", true, "Redact the terraform output before printing") - environmentBumpModuleCmd.Flags().StringVarP(&module, "module", "m", "", "Module to upgrade the version") environmentBumpModuleCmd.Flags().StringVarP(&moduleVersion, "module-version", "v", "", "Semantic version to bump a module to") environmentCreateCmd.Flags().BoolVarP(&skipEnvCheck, "skip-env-check", "s", false, "Skip the environment check") environmentCreateCmd.Flags().StringVarP(&answersFile, "answers-file", "a", "", "Path to the answers file") + // e.g. if this is the Pull rquest to perform the apply: https://github.com/ministryofjustice/cloud-platform-environments/pull/8370, the pr ID is 8370. + environmentDestroyCmd.Flags().IntVar(&optFlags.PRNumber, "prNumber", 0, "Pull request ID or number to which you want to perform the destroy") + environmentDestroyCmd.Flags().StringVarP(&optFlags.Namespace, "namespace", "n", "", "Namespace which you want to perform the destroy") + + // Re-use the environmental variable TF_VAR_github_token to call Github Client which is needed to perform terraform operations on each namespace + environmentDestroyCmd.Flags().StringVar(&optFlags.GithubToken, "github-token", os.Getenv("TF_VAR_github_token"), "Personal access Token from Github ") + environmentDestroyCmd.Flags().StringVar(&optFlags.KubecfgPath, "kubecfg", filepath.Join(homedir.HomeDir(), ".kube", "config"), "path to kubeconfig file") + environmentDestroyCmd.Flags().StringVar(&optFlags.ClusterCtx, "cluster", "", "folder name under namespaces/ inside cloud-platform-environments repo refering to full cluster name") + environmentDestroyCmd.PersistentFlags().BoolVar(&optFlags.RedactedEnv, "redact", true, "Redact the terraform output before printing") + environmentDivergenceCmd.Flags().StringVarP(&clusterName, "cluster-name", "c", "live", "[optional] Cluster name") environmentDivergenceCmd.Flags().StringVarP(&githubToken, "github-token", "g", "", "[required] Github token") environmentDivergenceCmd.Flags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "[optional] Kubeconfig file path") if err := environmentDivergenceCmd.MarkFlagRequired("github-token"); err != nil { log.Fatal(err) } + + // e.g. if this is the Pull rquest to perform the apply: https://github.com/ministryofjustice/cloud-platform-environments/pull/8370, the pr ID is 8370. + environmentPlanCmd.Flags().IntVar(&optFlags.PRNumber, "prNumber", 0, "Pull request ID or number to which you want to perform the plan") + environmentPlanCmd.Flags().StringVarP(&optFlags.Namespace, "namespace", "n", "", "Namespace which you want to perform the plan") + + // Re-use the environmental variable TF_VAR_github_token to call Github Client which is needed to perform terraform operations on each namespace + environmentPlanCmd.Flags().StringVar(&optFlags.GithubToken, "github-token", os.Getenv("TF_VAR_github_token"), "Personal access Token from Github ") + environmentPlanCmd.Flags().StringVar(&optFlags.KubecfgPath, "kubecfg", filepath.Join(homedir.HomeDir(), ".kube", "config"), "path to kubeconfig file") + environmentPlanCmd.Flags().StringVar(&optFlags.ClusterCtx, "cluster", "", "folder name under namespaces/ inside cloud-platform-environments repo refering to full cluster name") + environmentPlanCmd.PersistentFlags().BoolVar(&optFlags.RedactedEnv, "redact", true, "Redact the terraform output before printing") + } var environmentCmd = &cobra.Command{ @@ -214,6 +226,52 @@ var environmentApplyCmd = &cobra.Command{ }, } +var environmentDestroyCmd = &cobra.Command{ + Use: "destroy", + Short: `Perform a terraform destroy and kubectl delete for a given namespace`, + Long: ` + Perform a kubectl destroy and a terraform delete for a given namespace using either -namespace flag or the + the namespace in the given PR Id/Number + + Along with the mandatory input flag, the below environments variables needs to be set + TF_VAR_cluster_name - e.g. "cp-1902-02" to get the vpc details for some modules like rds, es + TF_VAR_cluster_state_bucket - State where the cluster state is stored + TF_VAR_cluster_state_key - folder name/state key inside the state bucket where cluster state is stored + TF_VAR_github_owner - Github owner: ministryofjustice + TF_VAR_github_token - Personal access token with repo scope to push github action secrets + TF_VAR_kubernetes_cluster - Full name of the Cluster e.g. XXXXXX.gr7.eu-west2.eks.amazonaws.com + PINGDOM_API_TOKEN - API Token to access pingdom + PIPELINE_TERRAFORM_STATE_LOCK_TABLE - DynamoDB table where the state lock is stored + PIPELINE_STATE_BUCKET - State bucket where the environments state is stored e.g cloud-platform-terraform-state + PIPELINE_STATE_KEY_PREFIX - State key/ folder where the environments terraform state is stored e.g cloud-platform-environments + PIPELINE_STATE_REGION - State region of the bucket e.g. eu-west-1 + PIPELINE_CLUSTER - Cluster name/folder inside namespaces/ in cloud-platform-environments + PIPELINE_CLUSTER_STATE - Cluster name/folder inside the state bucket where the environments terraform state is stored + `, + Example: heredoc.Doc(` + $ cloud-platform environment destroy -n + `), + PreRun: upgradeIfNotLatest, + Run: func(cmd *cobra.Command, args []string) { + contextLogger := log.WithFields(log.Fields{"subcommand": "destroy"}) + + ghConfig := &github.GithubClientConfig{ + Repository: "cloud-platform-environments", + Owner: "ministryofjustice", + } + + applier := &environment.Apply{ + Options: &optFlags, + GithubClient: github.NewGithubClient(ghConfig, optFlags.GithubToken), + } + + err := applier.Destroy() + if err != nil { + contextLogger.Fatal(err) + } + }, +} + var environmentEcrCreateCmd = &cobra.Command{ Use: "create", Short: `Create "resources/ecr.tf" terraform file for an ECR`, diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index 16ff9293..8c343c06 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -2,7 +2,9 @@ package environment import ( "fmt" + "io/ioutil" "log" + "net/http" "os" "strings" @@ -143,6 +145,41 @@ func (a *Apply) Apply() error { return nil } +// Destroy is the entry point for performing a namespace destroy. +// It checks if the working directory is in cloud-platform-environments, checks if a PR number is given +// Given a PR number, the method get the list of namespaces that are deleted in that merger PR. Then does the terraform init and destroy +// of all the namespaces merged in the PR and do a kubectl delete of all the namespaces +func (a *Apply) Destroy() error { + fmt.Println("Destroying Namespaces in PR", a.Options.PRNumber) + if a.Options.PRNumber == 0 { + err := fmt.Errorf("a PR ID/Number is required to perform apply") + return err + } + isMerged, err := a.GithubClient.IsMerged(a.Options.PRNumber) + if err != nil { + return err + } + if isMerged { + changedNamespaces, err := a.nsWriteChangedFilesInPR(a.Options.ClusterCtx, a.Options.PRNumber) + fmt.Println("Namespaces changed in PR", changedNamespaces) + if err != nil { + return err + } + for _, namespace := range changedNamespaces { + a.Options.Namespace = namespace + if _, err = os.Stat(a.Options.Namespace); err != nil { + fmt.Println("Destroying Namespace:", namespace) + // err = a.destroyNamespace() + // if err != nil { + // return err + // } + } + } + } + + return nil +} + // ApplyAll is the entry point for performing a namespace apply on all namespaces. // It checks if the working directory is in cloud-platform-environments, prepare the folder chunks based on the // numRoutines the apply has to run, then loop over the list of namespaces in each chunk and does the kubectl apply @@ -396,3 +433,73 @@ func (a *Apply) nsChangedInPR(cluster string, prNumber int) ([]string, error) { return util.DeduplicateList(namespaceNames), nil } + +// nsWriteChangedFilesInPR get the list of changed files for a given PR. checks if the namespaces exists in the given cluster and +// write the list of changes to a file. +func (a *Apply) nsWriteChangedFilesInPR(cluster string, prNumber int) ([]string, error) { + + files, err := a.GithubClient.GetChangedFiles(prNumber) + if err != nil { + return nil, fmt.Errorf("failed to fetch list of changed files: %s", err) + } + + var namespaceNames []string + for _, file := range files { + // namespaces filepaths are assumed to come in + // the format: namespaces/.cloud-platform.service.justice.gov.uk/ + s := strings.Split(*file.Filename, "/") + //only get namespaces from the folder that belong to the given cluster and + // ignore changes outside namespace directories + if len(s) > 1 && s[1] == cluster { + namespaceNames = append(namespaceNames, s[2]) + } + + wd, _ := os.Getwd() + // make directory if it doesn't exist + fmt.Println("wd", wd, "cluster", cluster, "s[2]", s[2]) + if _, err := os.Stat(wd + "/namespaces/" + cluster + "/" + s[2]); os.IsNotExist(err) { + err := os.Mkdir(wd+"/namespaces/"+cluster+"/"+s[2], 0755) + if err != nil { + fmt.Println("Error creating directory", err) + } + err = os.Mkdir(wd+"/namespaces/"+cluster+"/"+s[2]+"/resources", 0755) + if err != nil { + fmt.Println("Error creating resources directory", err) + } + } + // Get the contents of the CommitFile from SHA + // https://developer.github.com/v3/repos/contents/#get-contents + + fmt.Println("file.GetRawURL()", file.GetRawURL()) + + rawUrl := file.GetRawURL() + + response, err := http.Get(rawUrl) + + if response.StatusCode != 200 { + fmt.Println(" * Get Github File Raw Response is not 200 OK:", *file.Filename) + panic("Response Status: " + response.Status) + } + + if err != nil { + fmt.Println(" * Get Github File Raw Error:", err) + panic(err) + } + + defer response.Body.Close() + + data, err := ioutil.ReadAll(response.Body) + + if err != nil { + fmt.Println(" * Read Data Error:", err) + panic(err) + } + + // Create List with changed files + if err := ioutil.WriteFile(*file.Filename, data, 0644); err != nil { + return nil, fmt.Errorf("failed to write file list: %s", err) + } + } + return util.DeduplicateList(namespaceNames), nil + +} diff --git a/pkg/github/client.go b/pkg/github/client.go index 97193b91..e66ba6dc 100644 --- a/pkg/github/client.go +++ b/pkg/github/client.go @@ -107,3 +107,12 @@ func (gh *GithubClient) IsMerged(prNumber int) (bool, error) { return merged, nil } + +func (gh *GithubClient) GetContents(path string) (*github.RepositoryContent, error) { + repoOpts := &github.RepositoryContentGetOptions{} + file, _, _, err := gh.V3.Repositories.GetContents(context.Background(), gh.Owner, gh.Repository, path, repoOpts) + if err != nil { + return &github.RepositoryContent{}, err + } + return file, nil +} diff --git a/pkg/github/client_iface.go b/pkg/github/client_iface.go index 510ece36..591ba7da 100644 --- a/pkg/github/client_iface.go +++ b/pkg/github/client_iface.go @@ -13,4 +13,5 @@ type GithubIface interface { ListMergedPRs(date util.Date, count int) ([]Nodes, error) GetChangedFiles(int) ([]*github.CommitFile, error) IsMerged(prNumber int) (bool, error) + GetContents(path string) (*github.RepositoryContent, error) } From a2e8ad4f102fe2ba37cf3c7a7f58eb130b318d3c Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Sun, 2 Jul 2023 19:00:41 +0100 Subject: [PATCH 02/14] Get deleted files and write to same namespace folder --- pkg/environment/environmentApply.go | 101 ++++++++++++++-------------- pkg/util/util.go | 23 +++++++ 2 files changed, 74 insertions(+), 50 deletions(-) diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index 8c343c06..23c1115f 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -4,7 +4,6 @@ import ( "fmt" "io/ioutil" "log" - "net/http" "os" "strings" @@ -160,7 +159,7 @@ func (a *Apply) Destroy() error { return err } if isMerged { - changedNamespaces, err := a.nsWriteChangedFilesInPR(a.Options.ClusterCtx, a.Options.PRNumber) + changedNamespaces, err := a.nsCreateRawChangedFilesInPR(a.Options.ClusterCtx, a.Options.PRNumber) fmt.Println("Namespaces changed in PR", changedNamespaces) if err != nil { return err @@ -434,17 +433,49 @@ func (a *Apply) nsChangedInPR(cluster string, prNumber int) ([]string, error) { return util.DeduplicateList(namespaceNames), nil } -// nsWriteChangedFilesInPR get the list of changed files for a given PR. checks if the namespaces exists in the given cluster and -// write the list of changes to a file. -func (a *Apply) nsWriteChangedFilesInPR(cluster string, prNumber int) ([]string, error) { - - files, err := a.GithubClient.GetChangedFiles(prNumber) +// nsCreateRawChangedFilesInPR get the list of changed files for a given PR. checks if the file is deleted and +// write the deleted file to the namespace folder +func (a *Apply) nsCreateRawChangedFilesInPR(cluster string, prNumber int) ([]string, error) { + files, err = a.GithubClient.GetChangedFiles(prNumber) if err != nil { return nil, fmt.Errorf("failed to fetch list of changed files: %s", err) } + // nsforDestroy creates a namespace for destroy + namespaces, err := a.nsforDestroy(files, cluster) + if err != nil { + return nil, fmt.Errorf("failed to get namespace for destroy from the PR: %s", err) + } + err = createNamespaceforDestroy(namespaces, cluster) + if err != nil { + return nil, fmt.Errorf("failed to create namespace for destroy: %s", err) + } + + // Get the contents of the CommitFile from RawURL + // https://developer.github.com/v3/repos/contents/#get-contents + + for _, file := range files { + data, err := util.GetGithubRawContents(file.GetRawURL()) + if err != nil { + return nil, fmt.Errorf("failed to get raw contents: %s", err) + } + // Create List with changed files + if err := ioutil.WriteFile(*file.Filename, data, 0644); err != nil { + return nil, fmt.Errorf("failed to write file list: %s", err) + } + } + return namespaces, nil + +} + +func (a *Apply) nsforDestroy(files []*github.CommitFile, cluster string) ([]string, error) { var namespaceNames []string for _, file := range files { + // check of the file is a deleted file + if *file.Status != "removed" { + return nil, fmt.Errorf("Some of files are not marked for deletion: file %s is not deleted", *file.Filename) + } + // namespaces filepaths are assumed to come in // the format: namespaces/.cloud-platform.service.justice.gov.uk/ s := strings.Split(*file.Filename, "/") @@ -453,53 +484,23 @@ func (a *Apply) nsWriteChangedFilesInPR(cluster string, prNumber int) ([]string, if len(s) > 1 && s[1] == cluster { namespaceNames = append(namespaceNames, s[2]) } + } + return util.DeduplicateList(namespaceNames), nil +} - wd, _ := os.Getwd() +func createNamespaceforDestroy(namespaces []string, cluster string) error { + wd, _ := os.Getwd() + for _, ns := range namespaces { // make directory if it doesn't exist - fmt.Println("wd", wd, "cluster", cluster, "s[2]", s[2]) - if _, err := os.Stat(wd + "/namespaces/" + cluster + "/" + s[2]); os.IsNotExist(err) { - err := os.Mkdir(wd+"/namespaces/"+cluster+"/"+s[2], 0755) + if _, err := os.Stat(wd + "/namespaces/" + cluster + "/" + ns); err != nil { + err := os.Mkdir(wd+"/namespaces/"+cluster+"/"+ns, 0755) + err = os.Mkdir(wd+"/namespaces/"+cluster+"/"+ns+"/resources", 0755) if err != nil { - fmt.Println("Error creating directory", err) + return fmt.Errorf("Error creating namespaces or resources directory: %s", err) } - err = os.Mkdir(wd+"/namespaces/"+cluster+"/"+s[2]+"/resources", 0755) - if err != nil { - fmt.Println("Error creating resources directory", err) - } - } - // Get the contents of the CommitFile from SHA - // https://developer.github.com/v3/repos/contents/#get-contents - - fmt.Println("file.GetRawURL()", file.GetRawURL()) - - rawUrl := file.GetRawURL() - - response, err := http.Get(rawUrl) - - if response.StatusCode != 200 { - fmt.Println(" * Get Github File Raw Response is not 200 OK:", *file.Filename) - panic("Response Status: " + response.Status) - } - - if err != nil { - fmt.Println(" * Get Github File Raw Error:", err) - panic(err) - } - - defer response.Body.Close() - - data, err := ioutil.ReadAll(response.Body) - - if err != nil { - fmt.Println(" * Read Data Error:", err) - panic(err) - } - - // Create List with changed files - if err := ioutil.WriteFile(*file.Filename, data, 0644); err != nil { - return nil, fmt.Errorf("failed to write file list: %s", err) + } else { + return fmt.Errorf("Error creating directory: %s", err) } } - return util.DeduplicateList(namespaceNames), nil - + return nil } diff --git a/pkg/util/util.go b/pkg/util/util.go index 4e7aaacb..c239a4f2 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -6,6 +6,8 @@ import ( "errors" "fmt" "io" + "io/ioutil" + "net/http" "os/exec" "strings" "time" @@ -171,3 +173,24 @@ func DeduplicateList(s []string) (list []string) { return } + +func GetGithubRawContents(rawUrl string) ([]byte, error) { + response, err := http.Get(rawUrl) + + if response.StatusCode != 200 { + return nil, fmt.Errorf("GetRawContents: Github File Raw Response is not 200 OK. Filename: %s, Response: %s", rawUrl, response.Status) + } + + if err != nil { + return nil, fmt.Errorf(" GetRawContents: Github File Raw Error: %s", err) + } + + defer response.Body.Close() + + data, err := ioutil.ReadAll(response.Body) + + if err != nil { + return nil, fmt.Errorf("GetRawContents: Read Data Error: %s", err) + } + return data, nil +} From d5189ad6612055ec1671ffc5e190f0ca7ca0c090 Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Mon, 3 Jul 2023 18:53:16 +0100 Subject: [PATCH 03/14] Add terraform destroy to the delete-namespace command --- pkg/environment/applier.go | 59 +++++++++++++++ pkg/environment/environmentApply.go | 94 ++++++++++++++++++++++-- pkg/environment/environmentApply_test.go | 78 +++++++++++++++++++- pkg/environment/mocks/Applier.go | 70 ++++++++++++++++-- pkg/github/client.go | 9 --- pkg/github/client_iface.go | 1 - 6 files changed, 284 insertions(+), 27 deletions(-) diff --git a/pkg/environment/applier.go b/pkg/environment/applier.go index 550227fe..535756ee 100644 --- a/pkg/environment/applier.go +++ b/pkg/environment/applier.go @@ -17,8 +17,10 @@ const TerraformVersion = "1.2.5" type Applier interface { Initialize() KubectlApply(namespace, directory string, dryRun bool) (string, error) + KubectlDelete(namespace, directory string, dryRun bool) (string, error) TerraformInitAndPlan(namespace string, directory string) (string, error) TerraformInitAndApply(namespace string, directory string) (string, error) + TerraformInitAndDestroy(namespace string, directory string) (string, error) TerraformDestroy(directory string) error } @@ -153,6 +155,47 @@ func (m *ApplierImpl) TerraformInitAndPlan(namespace, directory string) (string, return out.String(), nil } +func (m *ApplierImpl) TerraformInitAndDestroy(namespace, directory string) (string, error) { + var out bytes.Buffer + terraform, err := tfexec.NewTerraform(directory, m.terraformBinaryPath) + if err != nil { + return "", errors.New("unable to instantiate Terraform: " + err.Error()) + } + + terraform.SetStdout(&out) + terraform.SetStderr(&out) + + // Sometimes the error text would be useful in the command output that's + // displayed in the UI. For this reason, we append the error to the + // output before we return it. + errReturn := func(out bytes.Buffer, err error) (string, error) { + if err != nil { + return fmt.Sprintf("%s\n%s", out.String(), err.Error()), err + } + + return out.String(), nil + } + + key := m.config.PipelineStateKeyPrefix + m.config.PipelineClusterState + "/" + namespace + "/terraform.tfstate" + + err = terraform.Init(context.Background(), + tfexec.BackendConfig(fmt.Sprintf("bucket=%s", m.config.PipelineStateBucket)), + tfexec.BackendConfig(fmt.Sprintf("key=%s", key)), + tfexec.BackendConfig(fmt.Sprintf("dynamodb_table=%s", m.config.PipelineTerraformStateLockTable)), + tfexec.BackendConfig(fmt.Sprintf("region=%s", m.config.PipelineStateRegion))) + if err != nil { + return errReturn(out, err) + } + + // ignore if any changes or no changes. + err = terraform.Destroy(context.Background()) + if err != nil { + return "", errors.New("unable to do Terraform Destroy: " + err.Error()) + } + + return out.String(), nil +} + func (m *ApplierImpl) TerraformDestroy(directory string) error { terraform, err := tfexec.NewTerraform(directory, m.terraformBinaryPath) if err != nil { @@ -177,3 +220,19 @@ func (m *ApplierImpl) KubectlApply(namespace, directory string, dryRun bool) (st return string(stdout), err } + +func (m *ApplierImpl) KubectlDelete(namespace, directory string, dryRun bool) (string, error) { + var args []string + if dryRun { + args = []string{"kubectl", "-n", namespace, "delete", "--dry-run=client", "-f", directory} + } else { + args = []string{"kubectl", "-n", namespace, "delete", "-f", directory} + } + + stdout, err := exec.Command(args[0], args[1:]...).CombinedOutput() + if err != nil { + err = fmt.Errorf("error: %v", err) + } + + return string(stdout), err +} diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index 23c1115f..d3553371 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -7,6 +7,7 @@ import ( "os" "strings" + gogithub "github.com/google/go-github/github" "github.com/kelseyhightower/envconfig" "github.com/ministryofjustice/cloud-platform-cli/pkg/github" "github.com/ministryofjustice/cloud-platform-cli/pkg/util" @@ -168,10 +169,10 @@ func (a *Apply) Destroy() error { a.Options.Namespace = namespace if _, err = os.Stat(a.Options.Namespace); err != nil { fmt.Println("Destroying Namespace:", namespace) - // err = a.destroyNamespace() - // if err != nil { - // return err - // } + err = a.destroyNamespace() + if err != nil { + return err + } } } } @@ -255,6 +256,19 @@ func (a *Apply) applyKubectl() (string, error) { return outputKubectl, nil } +// deleteKubectl calls the applier -> deleteKubectl with dry-run disabled and return the output from applier +func (a *Apply) deleteKubectl() (string, error) { + log.Printf("Running kubectl delete for namespace: %v in directory %v", a.Options.Namespace, a.Dir) + + outputKubectl, err := a.Applier.KubectlDelete(a.Options.Namespace, a.Dir, false) + if err != nil { + err := fmt.Errorf("error running kubectl delete on namespace %s: %v \n %v", a.Options.Namespace, err, outputKubectl) + return "", err + } + + return outputKubectl, nil +} + // planTerraform calls applier -> TerraformInitAndPlan and prints the output from applier func (a *Apply) planTerraform() (string, error) { log.Printf("Running Terraform Plan for namespace: %v", a.Options.Namespace) @@ -283,6 +297,20 @@ func (a *Apply) applyTerraform() (string, error) { return outputTerraform, nil } +// applyTerraform calls applier -> TerraformInitAndApply and prints the output from applier +func (a *Apply) destroyTerraform() (string, error) { + log.Printf("Running Terraform Destroy for namespace: %v", a.Options.Namespace) + + tfFolder := a.Dir + "/resources" + + outputTerraform, err := a.Applier.TerraformInitAndDestroy(a.Options.Namespace, tfFolder) + if err != nil { + err := fmt.Errorf("error running terraform on namespace %s: %v \n %v", a.Options.Namespace, err, outputTerraform) + return "", err + } + return outputTerraform, nil +} + // planNamespace intiates a new Apply object with options and env variables, and calls the // applyKubectl with dry-run enabled and calls applier TerraformInitAndPlan and prints the output func (a *Apply) planNamespace() error { @@ -409,6 +437,53 @@ func (a *Apply) applyNamespace() error { return nil } +// destroyNamespace intiates a apply object with options and env variables, and calls the +// calls applier TerraformInitAndDestroy, applyKubectl with dry-run disabled and prints the output +func (a *Apply) destroyNamespace() error { + // secretBlocker is a file used to control the behaviour of a namespace that will have all + // secrets in a namespace rotated. This came out of the requirement to rotate IAM credentials + // post circle breach. + repoPath := "namespaces/" + a.Options.ClusterCtx + "/" + a.Options.Namespace + + if _, err := os.Stat(repoPath); os.IsNotExist(err) { + fmt.Printf("Namespace %s does not exist, skipping destroy\n", a.Options.Namespace) + return nil + } + + applier := NewApply(*a.Options) + + exists, err := util.IsFilePathExists(repoPath + "/resources") + if err == nil && exists { + // Set KUBE_CONFIG_PATH to the path of the kubeconfig file + // This is needed for terraform to be able to connect to the cluster + if err := os.Setenv("KUBE_CONFIG_PATH", a.Options.KubecfgPath); err != nil { + return err + } + outputTerraform, err := applier.destroyTerraform() + if err != nil { + return err + } + + fmt.Println("\nOutput of terraform:") + util.RedactedEnv(os.Stdout, outputTerraform, a.Options.RedactedEnv) + + if util.IsYamlFileExists(repoPath) { + outputKubectl, err := applier.deleteKubectl() + if err != nil { + return err + } + + fmt.Println("\nOutput of kubectl:", outputKubectl) + } else { + fmt.Printf("Namespace %s does not have yaml resources folder, skipping kubectl delete", a.Options.Namespace) + } + + } else { + fmt.Printf("Namespace %s does not have terraform resources folder, skipping terraform destroy", a.Options.Namespace) + } + return nil +} + // nsChangedInPR get the list of changed files for a given PR. checks if the namespaces exists in the given cluster // folder and return the list of namespaces. func (a *Apply) nsChangedInPR(cluster string, prNumber int) ([]string, error) { @@ -436,13 +511,13 @@ func (a *Apply) nsChangedInPR(cluster string, prNumber int) ([]string, error) { // nsCreateRawChangedFilesInPR get the list of changed files for a given PR. checks if the file is deleted and // write the deleted file to the namespace folder func (a *Apply) nsCreateRawChangedFilesInPR(cluster string, prNumber int) ([]string, error) { - files, err = a.GithubClient.GetChangedFiles(prNumber) + files, err := a.GithubClient.GetChangedFiles(prNumber) if err != nil { return nil, fmt.Errorf("failed to fetch list of changed files: %s", err) } // nsforDestroy creates a namespace for destroy - namespaces, err := a.nsforDestroy(files, cluster) + namespaces, err := nsforDestroy(files, cluster) if err != nil { return nil, fmt.Errorf("failed to get namespace for destroy from the PR: %s", err) } @@ -468,9 +543,10 @@ func (a *Apply) nsCreateRawChangedFilesInPR(cluster string, prNumber int) ([]str } -func (a *Apply) nsforDestroy(files []*github.CommitFile, cluster string) ([]string, error) { +func nsforDestroy(files []*gogithub.CommitFile, cluster string) ([]string, error) { var namespaceNames []string for _, file := range files { + fmt.Println(*file.Filename, *file.Status, *file.Deletions) // check of the file is a deleted file if *file.Status != "removed" { return nil, fmt.Errorf("Some of files are not marked for deletion: file %s is not deleted", *file.Filename) @@ -481,10 +557,12 @@ func (a *Apply) nsforDestroy(files []*github.CommitFile, cluster string) ([]stri s := strings.Split(*file.Filename, "/") //only get namespaces from the folder that belong to the given cluster and // ignore changes outside namespace directories + fmt.Println(s[1], s[2]) if len(s) > 1 && s[1] == cluster { namespaceNames = append(namespaceNames, s[2]) } } + fmt.Println(namespaceNames) return util.DeduplicateList(namespaceNames), nil } @@ -499,7 +577,7 @@ func createNamespaceforDestroy(namespaces []string, cluster string) error { return fmt.Errorf("Error creating namespaces or resources directory: %s", err) } } else { - return fmt.Errorf("Error creating directory: %s", err) + return fmt.Errorf("Error creating directory, namespace exists in the environments repo: %s", err) } } return nil diff --git a/pkg/environment/environmentApply_test.go b/pkg/environment/environmentApply_test.go index 910ef7d5..aeeb6f48 100644 --- a/pkg/environment/environmentApply_test.go +++ b/pkg/environment/environmentApply_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/google/go-github/github" + gogithub "github.com/google/go-github/github" "github.com/ministryofjustice/cloud-platform-cli/pkg/environment/mocks" ghMock "github.com/ministryofjustice/cloud-platform-cli/pkg/mocks/github" "github.com/stretchr/testify/assert" @@ -129,14 +130,14 @@ func TestApply_nsChangedInPR(t *testing.T) { } tests := []struct { name string - GetChangedFilesOutputs []*github.CommitFile + GetChangedFilesOutputs []*gogithub.CommitFile args args want []string wantErr bool }{ { name: "pr with one namespace", - GetChangedFilesOutputs: []*github.CommitFile{ + GetChangedFilesOutputs: []*gogithub.CommitFile{ { SHA: github.String("6dcb09b5b57875f334f61aebed695e2e4193db5e"), Filename: github.String("namespaces/testctx/ns1/file1.txt"), @@ -218,3 +219,76 @@ func Test_applySkipExists(t *testing.T) { defer os.RemoveAll("namespaces") } + +func Test_nsforDestroy(t *testing.T) { + type args struct { + files []*gogithub.CommitFile + cluster string + } + tests := []struct { + name string + args args + want []string + wantErr bool + }{ + { + "pr with one namespace", + args{ + []*gogithub.CommitFile{ + { + SHA: github.String("6dcb09b5b57875f334f61aebed695e2e4193db5e"), + Filename: github.String("namespaces/testctx/ns1/file1.txt"), + Additions: github.Int(0), + Deletions: github.Int(14), + Changes: github.Int(0), + Status: github.String("removed"), + }, + { + SHA: github.String("f61aebed695e2e4193db5e6dcb09b5b57875f334"), + Filename: github.String("namespaces/testctx/ns1/file2.txt"), + Additions: github.Int(0), + Deletions: github.Int(16), + Changes: github.Int(0), + Status: github.String("removed"), + }, + }, + "testctx", + }, + []string{"ns1"}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := nsforDestroy(tt.args.files, tt.args.cluster) + if (err != nil) != tt.wantErr { + t.Errorf("nsforDestroy() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("nsforDestroy() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_createNamespaceforDestroy(t *testing.T) { + type args struct { + namespaces []string + cluster string + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := createNamespaceforDestroy(tt.args.namespaces, tt.args.cluster); (err != nil) != tt.wantErr { + t.Errorf("createNamespaceforDestroy() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/pkg/environment/mocks/Applier.go b/pkg/environment/mocks/Applier.go index a339f34d..4a9d85b3 100644 --- a/pkg/environment/mocks/Applier.go +++ b/pkg/environment/mocks/Applier.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.30.16. DO NOT EDIT. package mocks @@ -19,13 +19,40 @@ func (_m *Applier) KubectlApply(namespace string, directory string, dryRun bool) ret := _m.Called(namespace, directory, dryRun) var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(string, string, bool) (string, error)); ok { + return rf(namespace, directory, dryRun) + } if rf, ok := ret.Get(0).(func(string, string, bool) string); ok { r0 = rf(namespace, directory, dryRun) } else { r0 = ret.Get(0).(string) } + if rf, ok := ret.Get(1).(func(string, string, bool) error); ok { + r1 = rf(namespace, directory, dryRun) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// KubectlDelete provides a mock function with given fields: namespace, directory, dryRun +func (_m *Applier) KubectlDelete(namespace string, directory string, dryRun bool) (string, error) { + ret := _m.Called(namespace, directory, dryRun) + + var r0 string var r1 error + if rf, ok := ret.Get(0).(func(string, string, bool) (string, error)); ok { + return rf(namespace, directory, dryRun) + } + if rf, ok := ret.Get(0).(func(string, string, bool) string); ok { + r0 = rf(namespace, directory, dryRun) + } else { + r0 = ret.Get(0).(string) + } + if rf, ok := ret.Get(1).(func(string, string, bool) error); ok { r1 = rf(namespace, directory, dryRun) } else { @@ -54,13 +81,40 @@ func (_m *Applier) TerraformInitAndApply(namespace string, directory string) (st ret := _m.Called(namespace, directory) var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(string, string) (string, error)); ok { + return rf(namespace, directory) + } if rf, ok := ret.Get(0).(func(string, string) string); ok { r0 = rf(namespace, directory) } else { r0 = ret.Get(0).(string) } + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(namespace, directory) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TerraformInitAndDestroy provides a mock function with given fields: namespace, directory +func (_m *Applier) TerraformInitAndDestroy(namespace string, directory string) (string, error) { + ret := _m.Called(namespace, directory) + + var r0 string var r1 error + if rf, ok := ret.Get(0).(func(string, string) (string, error)); ok { + return rf(namespace, directory) + } + if rf, ok := ret.Get(0).(func(string, string) string); ok { + r0 = rf(namespace, directory) + } else { + r0 = ret.Get(0).(string) + } + if rf, ok := ret.Get(1).(func(string, string) error); ok { r1 = rf(namespace, directory) } else { @@ -75,13 +129,16 @@ func (_m *Applier) TerraformInitAndPlan(namespace string, directory string) (str ret := _m.Called(namespace, directory) var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(string, string) (string, error)); ok { + return rf(namespace, directory) + } if rf, ok := ret.Get(0).(func(string, string) string); ok { r0 = rf(namespace, directory) } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func(string, string) error); ok { r1 = rf(namespace, directory) } else { @@ -91,13 +148,12 @@ func (_m *Applier) TerraformInitAndPlan(namespace string, directory string) (str return r0, r1 } -type mockConstructorTestingTNewApplier interface { +// NewApplier creates a new instance of Applier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewApplier(t interface { mock.TestingT Cleanup(func()) -} - -// NewApplier creates a new instance of Applier. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewApplier(t mockConstructorTestingTNewApplier) *Applier { +}) *Applier { mock := &Applier{} mock.Mock.Test(t) diff --git a/pkg/github/client.go b/pkg/github/client.go index e66ba6dc..97193b91 100644 --- a/pkg/github/client.go +++ b/pkg/github/client.go @@ -107,12 +107,3 @@ func (gh *GithubClient) IsMerged(prNumber int) (bool, error) { return merged, nil } - -func (gh *GithubClient) GetContents(path string) (*github.RepositoryContent, error) { - repoOpts := &github.RepositoryContentGetOptions{} - file, _, _, err := gh.V3.Repositories.GetContents(context.Background(), gh.Owner, gh.Repository, path, repoOpts) - if err != nil { - return &github.RepositoryContent{}, err - } - return file, nil -} diff --git a/pkg/github/client_iface.go b/pkg/github/client_iface.go index 591ba7da..510ece36 100644 --- a/pkg/github/client_iface.go +++ b/pkg/github/client_iface.go @@ -13,5 +13,4 @@ type GithubIface interface { ListMergedPRs(date util.Date, count int) ([]Nodes, error) GetChangedFiles(int) ([]*github.CommitFile, error) IsMerged(prNumber int) (bool, error) - GetContents(path string) (*github.RepositoryContent, error) } From 73071a43c72af546831223c1f05e4ce6d902350c Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Tue, 4 Jul 2023 11:25:24 +0100 Subject: [PATCH 04/14] Cleanup prints and set KUBE_CONFIG_PATH during command init --- pkg/commands/environment.go | 5 +++++ pkg/environment/environmentApply.go | 21 ++------------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/pkg/commands/environment.go b/pkg/commands/environment.go index 04b0acbf..b237c26b 100644 --- a/pkg/commands/environment.go +++ b/pkg/commands/environment.go @@ -97,6 +97,11 @@ func addEnvironmentCmd(topLevel *cobra.Command) { environmentPlanCmd.Flags().StringVar(&optFlags.ClusterCtx, "cluster", "", "folder name under namespaces/ inside cloud-platform-environments repo refering to full cluster name") environmentPlanCmd.PersistentFlags().BoolVar(&optFlags.RedactedEnv, "redact", true, "Redact the terraform output before printing") + // Set KUBE_CONFIG_PATH to the path of the kubeconfig file + // This is needed for terraform to be able to connect to the cluster + if err := os.Setenv("KUBE_CONFIG_PATH", optFlags.KubecfgPath); err != nil { + return err + } } var environmentCmd = &cobra.Command{ diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index d3553371..7baa7c97 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -148,11 +148,11 @@ func (a *Apply) Apply() error { // Destroy is the entry point for performing a namespace destroy. // It checks if the working directory is in cloud-platform-environments, checks if a PR number is given // Given a PR number, the method get the list of namespaces that are deleted in that merger PR. Then does the terraform init and destroy -// of all the namespaces merged in the PR and do a kubectl delete of all the namespaces +// of all the namespaces merged in the PR and do a kubectl delete of the list of namespaces that are deleted in the PR func (a *Apply) Destroy() error { fmt.Println("Destroying Namespaces in PR", a.Options.PRNumber) if a.Options.PRNumber == 0 { - err := fmt.Errorf("a PR ID/Number is required to perform apply") + err := fmt.Errorf("a PR ID/Number is required to perform destroy") return err } isMerged, err := a.GithubClient.IsMerged(a.Options.PRNumber) @@ -335,11 +335,6 @@ func (a *Apply) planNamespace() error { exists, err := util.IsFilePathExists(repoPath + "/resources") if err == nil && exists { - // Set KUBE_CONFIG_PATH to the path of the kubeconfig file - // This is needed for terraform to be able to connect to the cluster - if err := os.Setenv("KUBE_CONFIG_PATH", a.Options.KubecfgPath); err != nil { - return err - } outputTerraform, err := applier.planTerraform() if err != nil { return err @@ -419,11 +414,6 @@ func (a *Apply) applyNamespace() error { exists, err := util.IsFilePathExists(repoPath + "/resources") if err == nil && exists { - // Set KUBE_CONFIG_PATH to the path of the kubeconfig file - // This is needed for terraform to be able to connect to the cluster - if err := os.Setenv("KUBE_CONFIG_PATH", a.Options.KubecfgPath); err != nil { - return err - } outputTerraform, err := applier.applyTerraform() if err != nil { return err @@ -454,11 +444,6 @@ func (a *Apply) destroyNamespace() error { exists, err := util.IsFilePathExists(repoPath + "/resources") if err == nil && exists { - // Set KUBE_CONFIG_PATH to the path of the kubeconfig file - // This is needed for terraform to be able to connect to the cluster - if err := os.Setenv("KUBE_CONFIG_PATH", a.Options.KubecfgPath); err != nil { - return err - } outputTerraform, err := applier.destroyTerraform() if err != nil { return err @@ -546,7 +531,6 @@ func (a *Apply) nsCreateRawChangedFilesInPR(cluster string, prNumber int) ([]str func nsforDestroy(files []*gogithub.CommitFile, cluster string) ([]string, error) { var namespaceNames []string for _, file := range files { - fmt.Println(*file.Filename, *file.Status, *file.Deletions) // check of the file is a deleted file if *file.Status != "removed" { return nil, fmt.Errorf("Some of files are not marked for deletion: file %s is not deleted", *file.Filename) @@ -562,7 +546,6 @@ func nsforDestroy(files []*gogithub.CommitFile, cluster string) ([]string, error namespaceNames = append(namespaceNames, s[2]) } } - fmt.Println(namespaceNames) return util.DeduplicateList(namespaceNames), nil } From e090363afb5584d05f68d7cb05491b614ee30ac3 Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Tue, 4 Jul 2023 11:30:48 +0100 Subject: [PATCH 05/14] change ioutil to os --- pkg/commands/environment.go | 2 +- pkg/environment/environmentApply.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/commands/environment.go b/pkg/commands/environment.go index b237c26b..c5e9966c 100644 --- a/pkg/commands/environment.go +++ b/pkg/commands/environment.go @@ -100,7 +100,7 @@ func addEnvironmentCmd(topLevel *cobra.Command) { // Set KUBE_CONFIG_PATH to the path of the kubeconfig file // This is needed for terraform to be able to connect to the cluster if err := os.Setenv("KUBE_CONFIG_PATH", optFlags.KubecfgPath); err != nil { - return err + log.Fatal(err) } } diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index 7baa7c97..73300be9 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -2,7 +2,6 @@ package environment import ( "fmt" - "io/ioutil" "log" "os" "strings" @@ -520,7 +519,7 @@ func (a *Apply) nsCreateRawChangedFilesInPR(cluster string, prNumber int) ([]str return nil, fmt.Errorf("failed to get raw contents: %s", err) } // Create List with changed files - if err := ioutil.WriteFile(*file.Filename, data, 0644); err != nil { + if err := os.WriteFile(*file.Filename, data, 0644); err != nil { return nil, fmt.Errorf("failed to write file list: %s", err) } } From 945d80729d2c5ba7b5149501e8c59f94be6ea015 Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Wed, 5 Jul 2023 15:35:38 +0100 Subject: [PATCH 06/14] Merge nsChangedInPR with nsChangesDestroy --- pkg/environment/environmentApply.go | 46 +++--- pkg/environment/environmentApply_test.go | 171 ++++++++++------------- 2 files changed, 87 insertions(+), 130 deletions(-) diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index 73300be9..6dcdbdb0 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -88,7 +88,11 @@ func (a *Apply) Plan() error { } return nil } else { - changedNamespaces, err := a.nsChangedInPR(a.Options.ClusterCtx, a.Options.PRNumber) + files, err := a.GithubClient.GetChangedFiles(a.Options.PRNumber) + if err != nil { + return fmt.Errorf("failed to fetch list of changed files: %s in PR %v", err, a.Options.PRNumber) + } + changedNamespaces, err := nsChangedInPR(files, a.Options.ClusterCtx, false) if err != nil { return err } @@ -125,7 +129,12 @@ func (a *Apply) Apply() error { return err } if isMerged { - changedNamespaces, err := a.nsChangedInPR(a.Options.ClusterCtx, a.Options.PRNumber) + repos, err := a.GithubClient.GetChangedFiles(a.Options.PRNumber) + if err != nil { + return err + } + + changedNamespaces, err := nsChangedInPR(repos, a.Options.ClusterCtx, false) if err != nil { return err } @@ -468,30 +477,6 @@ func (a *Apply) destroyNamespace() error { return nil } -// nsChangedInPR get the list of changed files for a given PR. checks if the namespaces exists in the given cluster -// folder and return the list of namespaces. -func (a *Apply) nsChangedInPR(cluster string, prNumber int) ([]string, error) { - repos, err := a.GithubClient.GetChangedFiles(prNumber) - if err != nil { - return nil, err - } - - var namespaceNames []string - for _, repo := range repos { - // namespaces filepaths are assumed to come in - // the format: namespaces/.cloud-platform.service.justice.gov.uk/ - s := strings.Split(*repo.Filename, "/") - //only get namespaces from the folder that belong to the given cluster and - // ignore changes outside namespace directories - if len(s) > 1 && s[1] == cluster { - namespaceNames = append(namespaceNames, s[2]) - } - - } - - return util.DeduplicateList(namespaceNames), nil -} - // nsCreateRawChangedFilesInPR get the list of changed files for a given PR. checks if the file is deleted and // write the deleted file to the namespace folder func (a *Apply) nsCreateRawChangedFilesInPR(cluster string, prNumber int) ([]string, error) { @@ -501,7 +486,7 @@ func (a *Apply) nsCreateRawChangedFilesInPR(cluster string, prNumber int) ([]str } // nsforDestroy creates a namespace for destroy - namespaces, err := nsforDestroy(files, cluster) + namespaces, err := nsChangedInPR(files, a.Options.ClusterCtx, true) if err != nil { return nil, fmt.Errorf("failed to get namespace for destroy from the PR: %s", err) } @@ -527,11 +512,13 @@ func (a *Apply) nsCreateRawChangedFilesInPR(cluster string, prNumber int) ([]str } -func nsforDestroy(files []*gogithub.CommitFile, cluster string) ([]string, error) { +// nsChangedInPR get the list of changed files for a given PR. checks if the namespaces exists in the given cluster +// folder and return the list of namespaces. +func nsChangedInPR(files []*gogithub.CommitFile, cluster string, isDeleted bool) ([]string, error) { var namespaceNames []string for _, file := range files { // check of the file is a deleted file - if *file.Status != "removed" { + if isDeleted && *file.Status != "removed" { return nil, fmt.Errorf("Some of files are not marked for deletion: file %s is not deleted", *file.Filename) } @@ -540,7 +527,6 @@ func nsforDestroy(files []*gogithub.CommitFile, cluster string) ([]string, error s := strings.Split(*file.Filename, "/") //only get namespaces from the folder that belong to the given cluster and // ignore changes outside namespace directories - fmt.Println(s[1], s[2]) if len(s) > 1 && s[1] == cluster { namespaceNames = append(namespaceNames, s[2]) } diff --git a/pkg/environment/environmentApply_test.go b/pkg/environment/environmentApply_test.go index aeeb6f48..0f396b44 100644 --- a/pkg/environment/environmentApply_test.go +++ b/pkg/environment/environmentApply_test.go @@ -6,9 +6,7 @@ import ( "testing" "github.com/google/go-github/github" - gogithub "github.com/google/go-github/github" "github.com/ministryofjustice/cloud-platform-cli/pkg/environment/mocks" - ghMock "github.com/ministryofjustice/cloud-platform-cli/pkg/mocks/github" "github.com/stretchr/testify/assert" ) @@ -123,67 +121,6 @@ func TestApply_ApplyKubectl(t *testing.T) { } } -func TestApply_nsChangedInPR(t *testing.T) { - type args struct { - cluster string - prNumber int - } - tests := []struct { - name string - GetChangedFilesOutputs []*gogithub.CommitFile - args args - want []string - wantErr bool - }{ - { - name: "pr with one namespace", - GetChangedFilesOutputs: []*gogithub.CommitFile{ - { - SHA: github.String("6dcb09b5b57875f334f61aebed695e2e4193db5e"), - Filename: github.String("namespaces/testctx/ns1/file1.txt"), - Additions: github.Int(103), - Deletions: github.Int(21), - Changes: github.Int(124), - Status: github.String("added"), - Patch: github.String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), - }, - { - SHA: github.String("f61aebed695e2e4193db5e6dcb09b5b57875f334"), - Filename: github.String("namespaces/testctx/ns1/file2.txt"), - Additions: github.Int(5), - Deletions: github.Int(3), - Changes: github.Int(103), - Status: github.String("modified"), - Patch: github.String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), - }, - }, - args: args{ - cluster: "testctx", - prNumber: 8834, - }, - want: []string{"ns1"}, - wantErr: false, - }, - } - for _, tt := range tests { - ghClient := new(ghMock.GithubIface) - ghClient.On("GetChangedFiles", 8834).Return(tt.GetChangedFilesOutputs, nil) - t.Run(tt.name, func(t *testing.T) { - a := &Apply{ - GithubClient: ghClient, - } - got, err := a.nsChangedInPR(tt.args.cluster, tt.args.prNumber) - if (err != nil) != tt.wantErr { - t.Errorf("Apply.nsChangedInPR() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Apply.nsChangedInPR() = %v, want %v", got, tt.want) - } - }) - } -} - func TestSecretBlockerExists(t *testing.T) { tempDir := "namespaces/testCluster/testNamespace" tempFile := tempDir + "/SECRET_ROTATE_BLOCK" @@ -220,10 +157,32 @@ func Test_applySkipExists(t *testing.T) { } -func Test_nsforDestroy(t *testing.T) { +func Test_createNamespaceforDestroy(t *testing.T) { type args struct { - files []*gogithub.CommitFile - cluster string + namespaces []string + cluster string + } + tests := []struct { + name string + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := createNamespaceforDestroy(tt.args.namespaces, tt.args.cluster); (err != nil) != tt.wantErr { + t.Errorf("createNamespaceforDestroy() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_nsChangedInPR(t *testing.T) { + type args struct { + files []*github.CommitFile + cluster string + isDeleted bool } tests := []struct { name string @@ -232,62 +191,74 @@ func Test_nsforDestroy(t *testing.T) { wantErr bool }{ { - "pr with one namespace", - args{ - []*gogithub.CommitFile{ + name: "Pr for namespace change", + args: args{ + files: []*github.CommitFile{ { - SHA: github.String("6dcb09b5b57875f334f61aebed695e2e4193db5e"), + SHA: github.String("f61aebed695e2e4193db5euy686dcb09b5b57875f334"), Filename: github.String("namespaces/testctx/ns1/file1.txt"), + Additions: github.Int(5), + Deletions: github.Int(3), + Changes: github.Int(103), + Status: github.String("modified"), + Patch: github.String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), + }, + { + SHA: github.String("f61aebed695e2e4193db5e6dcb09b5b57875f334"), + Filename: github.String("namespaces/testctx/ns1/file2.txt"), + Additions: github.Int(5), + Deletions: github.Int(3), + Changes: github.Int(103), + Status: github.String("modified"), + Patch: github.String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), + }, + }, + cluster: "testctx", + isDeleted: false, + }, + want: []string{"ns1"}, + wantErr: false, + }, + { + name: "Pr for namespace change with deleted file", + args: args{ + files: []*github.CommitFile{ + { + SHA: github.String("f61aebed695e2e4193db5euy686dcb09b5b57875f334"), + Filename: github.String("namespaces/testctx/ns2/file1.txt"), Additions: github.Int(0), - Deletions: github.Int(14), + Deletions: github.Int(3), Changes: github.Int(0), Status: github.String("removed"), + Patch: github.String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), }, { SHA: github.String("f61aebed695e2e4193db5e6dcb09b5b57875f334"), - Filename: github.String("namespaces/testctx/ns1/file2.txt"), + Filename: github.String("namespaces/testctx/ns2/file2.txt"), Additions: github.Int(0), - Deletions: github.Int(16), + Deletions: github.Int(3), Changes: github.Int(0), Status: github.String("removed"), + Patch: github.String("@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"), }, }, - "testctx", + cluster: "testctx", + isDeleted: true, }, - []string{"ns1"}, - false, + want: []string{"ns2"}, + wantErr: false, }, } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := nsforDestroy(tt.args.files, tt.args.cluster) + got, err := nsChangedInPR(tt.args.files, tt.args.cluster, tt.args.isDeleted) if (err != nil) != tt.wantErr { - t.Errorf("nsforDestroy() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("nsChangedInPR() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("nsforDestroy() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_createNamespaceforDestroy(t *testing.T) { - type args struct { - namespaces []string - cluster string - } - tests := []struct { - name string - args args - wantErr bool - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if err := createNamespaceforDestroy(tt.args.namespaces, tt.args.cluster); (err != nil) != tt.wantErr { - t.Errorf("createNamespaceforDestroy() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("nsChangedInPR() = %v, want %v", got, tt.want) } }) } From b8b73a058b43c7c863e1aa78169cf2f8e4a18b1b Mon Sep 17 00:00:00 2001 From: poornima-krishnasamy Date: Wed, 5 Jul 2023 14:36:36 +0000 Subject: [PATCH 07/14] docs(cobra): update auto-generated documentation --- doc/cloud-platform_environment.md | 1 + doc/cloud-platform_environment_destroy.md | 59 +++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 doc/cloud-platform_environment_destroy.md diff --git a/doc/cloud-platform_environment.md b/doc/cloud-platform_environment.md index a32febc1..ede64700 100644 --- a/doc/cloud-platform_environment.md +++ b/doc/cloud-platform_environment.md @@ -20,6 +20,7 @@ Cloud Platform Environment actions * [cloud-platform environment apply](cloud-platform_environment_apply.md) - Perform a terraform apply and kubectl apply for a given namespace * [cloud-platform environment bump-module](cloud-platform_environment_bump-module.md) - Bump all specified module versions * [cloud-platform environment create](cloud-platform_environment_create.md) - Create an environment +* [cloud-platform environment destroy](cloud-platform_environment_destroy.md) - Perform a terraform destroy and kubectl delete for a given namespace * [cloud-platform environment divergence](cloud-platform_environment_divergence.md) - Check for divergence between the environments repository and the cluster * [cloud-platform environment ecr](cloud-platform_environment_ecr.md) - Add an ECR to a namespace * [cloud-platform environment plan](cloud-platform_environment_plan.md) - Perform a terraform plan and kubectl apply --dry-run=client for a given namespace using either -namespace flag or the diff --git a/doc/cloud-platform_environment_destroy.md b/doc/cloud-platform_environment_destroy.md new file mode 100644 index 00000000..880c8041 --- /dev/null +++ b/doc/cloud-platform_environment_destroy.md @@ -0,0 +1,59 @@ +## cloud-platform environment destroy + +Perform a terraform destroy and kubectl delete for a given namespace + +### Synopsis + + + Perform a kubectl destroy and a terraform delete for a given namespace using either -namespace flag or the + the namespace in the given PR Id/Number + + Along with the mandatory input flag, the below environments variables needs to be set + TF_VAR_cluster_name - e.g. "cp-1902-02" to get the vpc details for some modules like rds, es + TF_VAR_cluster_state_bucket - State where the cluster state is stored + TF_VAR_cluster_state_key - folder name/state key inside the state bucket where cluster state is stored + TF_VAR_github_owner - Github owner: ministryofjustice + TF_VAR_github_token - Personal access token with repo scope to push github action secrets + TF_VAR_kubernetes_cluster - Full name of the Cluster e.g. XXXXXX.gr7.eu-west2.eks.amazonaws.com + PINGDOM_API_TOKEN - API Token to access pingdom + PIPELINE_TERRAFORM_STATE_LOCK_TABLE - DynamoDB table where the state lock is stored + PIPELINE_STATE_BUCKET - State bucket where the environments state is stored e.g cloud-platform-terraform-state + PIPELINE_STATE_KEY_PREFIX - State key/ folder where the environments terraform state is stored e.g cloud-platform-environments + PIPELINE_STATE_REGION - State region of the bucket e.g. eu-west-1 + PIPELINE_CLUSTER - Cluster name/folder inside namespaces/ in cloud-platform-environments + PIPELINE_CLUSTER_STATE - Cluster name/folder inside the state bucket where the environments terraform state is stored + + +``` +cloud-platform environment destroy [flags] +``` + +### Examples + +``` +$ cloud-platform environment destroy -n + +``` + +### Options + +``` + --cluster string folder name under namespaces/ inside cloud-platform-environments repo refering to full cluster name + --github-token string Personal access Token from Github + -h, --help help for destroy + --kubecfg string path to kubeconfig file (default "/home/runner/.kube/config") + -n, --namespace string Namespace which you want to perform the destroy + --prNumber int Pull request ID or number to which you want to perform the destroy + --redact Redact the terraform output before printing (default true) +``` + +### Options inherited from parent commands + +``` + --skip-version-check don't check for updates +``` + +### SEE ALSO + +* [cloud-platform environment](cloud-platform_environment.md) - Cloud Platform Environment actions + From 449c5e61bf61e87fd13c0dede6fd746e418e96eb Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Wed, 5 Jul 2023 15:57:16 +0100 Subject: [PATCH 08/14] Use os instead of ioutil --- pkg/util/util.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/util/util.go b/pkg/util/util.go index c239a4f2..6b8c23bc 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net/http" "os/exec" "strings" @@ -187,7 +186,7 @@ func GetGithubRawContents(rawUrl string) ([]byte, error) { defer response.Body.Close() - data, err := ioutil.ReadAll(response.Body) + data, err := io.ReadAll(response.Body) if err != nil { return nil, fmt.Errorf("GetRawContents: Read Data Error: %s", err) From 18365e1e8fc1245e4b578de2cf60822176b270aa Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Wed, 5 Jul 2023 16:20:07 +0100 Subject: [PATCH 09/14] Capitalize error to fix lint --- pkg/environment/environmentApply.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index 6dcdbdb0..6e193332 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -519,7 +519,7 @@ func nsChangedInPR(files []*gogithub.CommitFile, cluster string, isDeleted bool) for _, file := range files { // check of the file is a deleted file if isDeleted && *file.Status != "removed" { - return nil, fmt.Errorf("Some of files are not marked for deletion: file %s is not deleted", *file.Filename) + return nil, fmt.Errorf("some of files are not marked for deletion: file %s is not deleted", *file.Filename) } // namespaces filepaths are assumed to come in @@ -540,12 +540,15 @@ func createNamespaceforDestroy(namespaces []string, cluster string) error { // make directory if it doesn't exist if _, err := os.Stat(wd + "/namespaces/" + cluster + "/" + ns); err != nil { err := os.Mkdir(wd+"/namespaces/"+cluster+"/"+ns, 0755) + if err != nil { + return fmt.Errorf("error creating namespaces directory: %s", err) + } err = os.Mkdir(wd+"/namespaces/"+cluster+"/"+ns+"/resources", 0755) if err != nil { - return fmt.Errorf("Error creating namespaces or resources directory: %s", err) + return fmt.Errorf("error creating resources directory: %s", err) } } else { - return fmt.Errorf("Error creating directory, namespace exists in the environments repo: %s", err) + return fmt.Errorf("error creating directory, namespace exists in the environments repo: %s", err) } } return nil From 50d2d20269fdce22efa462bdffcc3443d769bd5e Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Wed, 5 Jul 2023 17:59:06 +0100 Subject: [PATCH 10/14] Add applier unit tests for destroy --- pkg/environment/environmentApply_test.go | 130 ++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/pkg/environment/environmentApply_test.go b/pkg/environment/environmentApply_test.go index 0f396b44..0c96a90f 100644 --- a/pkg/environment/environmentApply_test.go +++ b/pkg/environment/environmentApply_test.go @@ -158,6 +158,13 @@ func Test_applySkipExists(t *testing.T) { } func Test_createNamespaceforDestroy(t *testing.T) { + repoPath := "namespaces/testCluster" + + err := os.MkdirAll(repoPath, os.ModePerm) + if err != nil { + t.Errorf("Failed to create repo path: %s", err) + } + type args struct { namespaces []string cluster string @@ -167,15 +174,24 @@ func Test_createNamespaceforDestroy(t *testing.T) { args args wantErr bool }{ - // TODO: Add test cases. + { + name: "Create namespace for destroy", + args: args{ + namespaces: []string{"testNamespace"}, + cluster: "testCluster", + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if err := createNamespaceforDestroy(tt.args.namespaces, tt.args.cluster); (err != nil) != tt.wantErr { t.Errorf("createNamespaceforDestroy() error = %v, wantErr %v", err, tt.wantErr) } + }) } + defer os.RemoveAll("namespaces") } func Test_nsChangedInPR(t *testing.T) { @@ -263,3 +279,115 @@ func Test_nsChangedInPR(t *testing.T) { }) } } + +func TestApply_destroyTerraform(t *testing.T) { + type fields struct { + Options *Options + RequiredEnvVars RequiredEnvVars + Applier Applier + Dir string + } + tests := []struct { + name string + fields fields + TerraformOutputs string + checkExpectations func(t *testing.T, terraform *mocks.Applier, outputs string, err error) + }{ + { + name: "Destroy foobar namespace", + fields: fields{ + Options: &Options{ + Namespace: "foobar", + KubecfgPath: "/root/.kube/config", + ClusterCtx: "testctx", + }, + RequiredEnvVars: RequiredEnvVars{ + clustername: "cluster01", + clusterstatebucket: "clusterstatebucket", + kubernetescluster: "kubernetescluster01", + githubowner: "githubowner", + githubtoken: "githubtoken", + pingdomapitoken: "pingdomApikey", + }, + Dir: "/root/foobar", + }, + TerraformOutputs: "foobar", + checkExpectations: func(t *testing.T, apply *mocks.Applier, outputs string, err error) { + apply.AssertCalled(t, "TerraformInitAndDestroy", "foobar", "/root/foobar/resources") + assert.Nil(t, err) + assert.Len(t, outputs, 6) + }, + }, + } + for i := range tests { + terraform := new(mocks.Applier) + tfFolder := tests[i].fields.Dir + "/resources" + terraform.On("TerraformInitAndDestroy", tests[i].fields.Options.Namespace, tfFolder).Return(tests[i].TerraformOutputs, nil) + a := Apply{ + RequiredEnvVars: tests[i].fields.RequiredEnvVars, + Applier: terraform, + Dir: tests[i].fields.Dir, + Options: tests[i].fields.Options, + } + outputs, err := a.destroyTerraform() + t.Run(tests[i].name, func(t *testing.T) { + tests[i].checkExpectations(t, terraform, outputs, err) + }) + } + +} + +func TestApply_deleteKubectl(t *testing.T) { + type fields struct { + Options *Options + RequiredEnvVars RequiredEnvVars + Applier Applier + Dir string + } + tests := []struct { + name string + fields fields + KubectlOutputs string + checkExpectations func(t *testing.T, kubectl *mocks.Applier, outputs string, err error) + }{ + { + name: "Delete foo namespace", + fields: fields{ + Options: &Options{ + Namespace: "foobar", + KubecfgPath: "/root/.kube/config", + ClusterCtx: "testctx", + }, + RequiredEnvVars: RequiredEnvVars{ + clustername: "cluster01", + clusterstatebucket: "clusterstatebucket", + kubernetescluster: "kubernetescluster01", + githubowner: "githubowner", + githubtoken: "githubtoken", + pingdomapitoken: "pingdomApikey", + }, + Dir: "/root/foo", + }, + KubectlOutputs: "/root/foo", + checkExpectations: func(t *testing.T, apply *mocks.Applier, outputs string, err error) { + apply.AssertCalled(t, "KubectlDelete", "foobar", "/root/foo", false) + assert.Nil(t, err) + assert.Len(t, outputs, 9) + }, + }, + } + for i := range tests { + kubectl := new(mocks.Applier) + kubectl.On("KubectlDelete", "foobar", tests[i].fields.Dir, false).Return(tests[i].KubectlOutputs, nil) + a := Apply{ + RequiredEnvVars: tests[i].fields.RequiredEnvVars, + Applier: kubectl, + Dir: tests[i].fields.Dir, + Options: tests[i].fields.Options, + } + outputs, err := a.deleteKubectl() + t.Run(tests[i].name, func(t *testing.T) { + tests[i].checkExpectations(t, kubectl, outputs, err) + }) + } +} From 2bd7c8308b5e9f0ce947e5aa2182a2f82e6422b2 Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Wed, 5 Jul 2023 18:12:26 +0100 Subject: [PATCH 11/14] Add util unit tests --- pkg/util/util_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index f23cc8cf..2a3c7b81 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -322,3 +322,31 @@ func TestGetDatePastMinute(t *testing.T) { }) } } + +func TestGetGithubRawContents(t *testing.T) { + type args struct { + rawUrl string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "get github raw contents", + args: args{ + rawUrl: "https://raw.githubusercontent.com/ministryofjustice/cloud-platform-cli/main/README.md", + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := GetGithubRawContents(tt.args.rawUrl) + if (err != nil) != tt.wantErr { + t.Errorf("GetGithubRawContents() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} From 4a71cd8accf50ae58cba61af2c829828ed14d481 Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Thu, 6 Jul 2023 13:23:35 +0100 Subject: [PATCH 12/14] Update comment --- pkg/environment/environmentApply.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index 6e193332..fa69000b 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -154,9 +154,9 @@ func (a *Apply) Apply() error { } // Destroy is the entry point for performing a namespace destroy. -// It checks if the working directory is in cloud-platform-environments, checks if a PR number is given -// Given a PR number, the method get the list of namespaces that are deleted in that merger PR. Then does the terraform init and destroy -// of all the namespaces merged in the PR and do a kubectl delete of the list of namespaces that are deleted in the PR +// It checks if the working directory is in cloud-platform-environments, checks if a PR number is given and merged +// The method get the list of namespaces that are deleted in that merger PR, and for all namespaces in the PR does the +// terraform init and destroy and do a kubectl delete func (a *Apply) Destroy() error { fmt.Println("Destroying Namespaces in PR", a.Options.PRNumber) if a.Options.PRNumber == 0 { From 81973cfb8866dcea321883297dee11a9b9bb3125 Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Fri, 7 Jul 2023 15:43:02 +0100 Subject: [PATCH 13/14] Remove folder check on plan --- pkg/environment/environmentApply.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index fa69000b..264a2638 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -325,11 +325,6 @@ func (a *Apply) planNamespace() error { applier := NewApply(*a.Options) repoPath := "namespaces/" + a.Options.ClusterCtx + "/" + a.Options.Namespace - if _, err := os.Stat(repoPath); os.IsNotExist(err) { - fmt.Printf("Namespace %s does not exist, skipping plan\n", a.Options.Namespace) - return nil - } - if util.IsYamlFileExists(repoPath) { outputKubectl, err := applier.planKubectl() if err != nil { From 86462aabb38a60e42c2e55bc63d9412d2fd7f18f Mon Sep 17 00:00:00 2001 From: Poornima Krishnasamy Date: Mon, 10 Jul 2023 11:43:46 +0100 Subject: [PATCH 14/14] Fix typo and removed unwanted comments --- pkg/environment/environmentApply.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkg/environment/environmentApply.go b/pkg/environment/environmentApply.go index 264a2638..5e6fb216 100644 --- a/pkg/environment/environmentApply.go +++ b/pkg/environment/environmentApply.go @@ -305,7 +305,7 @@ func (a *Apply) applyTerraform() (string, error) { return outputTerraform, nil } -// applyTerraform calls applier -> TerraformInitAndApply and prints the output from applier +// applyTerraform calls applier -> TerraformInitAndDestroy and prints the output from applier func (a *Apply) destroyTerraform() (string, error) { log.Printf("Running Terraform Destroy for namespace: %v", a.Options.Namespace) @@ -433,9 +433,6 @@ func (a *Apply) applyNamespace() error { // destroyNamespace intiates a apply object with options and env variables, and calls the // calls applier TerraformInitAndDestroy, applyKubectl with dry-run disabled and prints the output func (a *Apply) destroyNamespace() error { - // secretBlocker is a file used to control the behaviour of a namespace that will have all - // secrets in a namespace rotated. This came out of the requirement to rotate IAM credentials - // post circle breach. repoPath := "namespaces/" + a.Options.ClusterCtx + "/" + a.Options.Namespace if _, err := os.Stat(repoPath); os.IsNotExist(err) { @@ -480,7 +477,6 @@ func (a *Apply) nsCreateRawChangedFilesInPR(cluster string, prNumber int) ([]str return nil, fmt.Errorf("failed to fetch list of changed files: %s", err) } - // nsforDestroy creates a namespace for destroy namespaces, err := nsChangedInPR(files, a.Options.ClusterCtx, true) if err != nil { return nil, fmt.Errorf("failed to get namespace for destroy from the PR: %s", err)