postdeploy-hooks/k8s-cleanup/main.go (78 lines of code) (raw):
package main
import (
"context"
"flag"
"fmt"
"os"
"regexp"
"cloud.google.com/go/storage"
)
var (
namespace = flag.String("namespace", "", "Namespace(s) to filter on when finding resources to delete. "+
"For multiple namespaces, separate them with a comma. For example --namespace=foo,bar. By default "+
"resources will be deleted across all namespaces.")
resourceType = flag.String("resource-type",
"service,cronjob.batch,job.batch,deployment.apps,statefulset.apps,pod,configmap,secret,horizontalpodautoscaler.autoscaling",
"Comma separated list of resource type(s) to filter on when finding "+
"resources to delete. See default list above of resources that will"+
"be deleted. To have ALL resources deleted pass in \"all\". "+
"You can also qualify the resource type by an API group if you want"+
"to specify resources only in a specific API group. For example --resource-type=deployments.apps")
)
// gkeClusterRegex represents the regex that a GKE cluster resource name needs to match.
var gkeClusterRegex = regexp.MustCompile("^projects/([^/]+)/locations/([^/]+)/clusters/([^/]+)$")
const (
// The name of the postdeploy hook cleanup sample, this is passed back to
// Cloud Deploy as metadata in the deploy results.
cleanupSampleName = "clouddeploy-k8s-cleanup-sample"
postdeployHookMetadataKey = "postdeploy-hook-source"
)
func main() {
flag.Parse()
// Print the value of the command-line flags to aid debugging.
fmt.Printf("Value of resource-type command-line flag: %s\n", *resourceType)
fmt.Printf("Value of namespace command-line flag: %s \n", *namespace)
if err := do(); err != nil {
fmt.Printf("err: %v\n", err)
os.Exit(1)
}
fmt.Println("Done!")
os.Exit(0)
}
func do() error {
// Step 1. Run gcloud get-credentials to set up the cluster credentials.
gkeCluster := os.Getenv("GKE_CLUSTER")
if err := gcloudClusterCredentials(gkeCluster); err != nil {
return err
}
// Step 2. Get a list of resources to delete.
kubectlExec := CreateCommandExecutor("kubectl")
oldResources, err := kubectlExec.resourcesToDelete(*namespace, *resourceType)
if err != nil {
return err
}
// Step 3. Delete the resources.
if err := kubectlExec.deleteResources(oldResources); err != nil {
return err
}
// Step 4. Upload metadata.
ctx := context.Background()
deployHookResult := &postdeployHookResult{
Metadata: map[string]string{
postdeployHookMetadataKey: cleanupSampleName,
},
}
gcsClient, err := storage.NewClient(ctx)
if err != nil {
return fmt.Errorf("unable to create cloud storage client: %v", err)
}
if err := uploadResult(ctx, gcsClient, deployHookResult); err != nil {
return err
}
return nil
}
// gcloudClusterCredentials runs `gcloud container clusters get-crendetials` to set up
// the cluster credentials.
func gcloudClusterCredentials(gkeCluster string) error {
gcloudExec := CreateCommandExecutor("gcloud")
m := gkeClusterRegex.FindStringSubmatch(gkeCluster)
if len(m) == 0 {
return fmt.Errorf("invalid GKE cluster name: %s", gkeCluster)
}
args := []string{"container", "clusters", "get-credentials", m[3], fmt.Sprintf("--region=%s", m[2]), fmt.Sprintf("--project=%s", m[1])}
_, err := gcloudExec.execCommand(args)
if err != nil {
return fmt.Errorf("unable to set up cluster credentials: %w", err)
}
return nil
}