Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete namespace cli command #424

Merged
merged 14 commits into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/cloud-platform_environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
59 changes: 59 additions & 0 deletions doc/cloud-platform_environment_destroy.md
Original file line number Diff line number Diff line change
@@ -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 <namespace>

```

### 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

83 changes: 73 additions & 10 deletions pkg/commands/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func addEnvironmentCmd(topLevel *cobra.Command) {
environmentApplyCmd,
environmentBumpModuleCmd,
environmentCreateCmd,
environmentDestroyCmd,
environmentDivergenceCmd,
environmentEcrCmd,
environmentPlanCmd,
Expand Down Expand Up @@ -63,28 +64,44 @@ 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")
poornima-krishnasamy marked this conversation as resolved.
Show resolved Hide resolved

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")

// 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 {
log.Fatal(err)
}
}

var environmentCmd = &cobra.Command{
Expand Down Expand Up @@ -214,6 +231,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 <namespace>
`),
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`,
Expand Down
59 changes: 59 additions & 0 deletions pkg/environment/applier.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Loading
Loading