func DeleteCluster(cfg string, name string) (bool, error) {
	scheme := runtime.NewScheme()
	err := clusterv1.AddToScheme(scheme)
	if err != nil {
		return false, err
	}
	kindclient, err := clientcmd.BuildConfigFromFlags("", cfg)
	if err != nil {
		return false, err
	}
	c, err := client.New(kindclient, client.Options{
		Scheme: scheme,
	})
	if err != nil {
		return false, err
	}
	cluster := &clusterv1.Cluster{}
	if err := c.Get(context.TODO(), client.ObjectKey{Namespace: "default", Name: name}, cluster); err != nil {
		return false, err
	}
	_, err = waitForAWSInfra(kindclient, name)
	if err != nil {
		return false, err
	}
	if err = c.Delete(context.TODO(), cluster, &client.DeleteOptions{}); err != nil {
		return false, err
	}
	retry := time.Duration(10 * time.Second)
	timeout := time.Duration(time.Hour)
	err = WaitForDeletion(c, cluster, retry, timeout)
	if err != nil {
		return false, err
	}
	return true, nil
}
DeleteCluster deletes the given capi managed cluster
package capi
import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	osruntime "runtime"
	"strings"
	"time"
	"context"
	"encoding/base64"
	"encoding/json"
	"github.com/aws/aws-sdk-go/aws/session"
	cfn "github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/christianh814/gokp/cmd/kind"
	"github.com/christianh814/gokp/cmd/utils"
	"github.com/rwtodd/Go.Sed/sed"
	log "github.com/sirupsen/logrus"
	"sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/cloudformation/bootstrap"
	cloudformation "sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/cloudformation/service"
	creds "sigs.k8s.io/cluster-api-provider-aws/cmd/clusterawsadm/credentials"
	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
	capiclient "sigs.k8s.io/cluster-api/cmd/clusterctl/client"
	kcpv1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
	"sigs.k8s.io/controller-runtime/pkg/client"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/discovery"
	"k8s.io/client-go/discovery/cached/memory"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/restmapper"
	"k8s.io/client-go/tools/clientcmd"
)
var CNIurl string = "https://docs.projectcalico.org/v3.20/manifests/calico.yaml"
var decUnstructured = yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
func CreateAwsK8sInstance(kindkconfig string, clusterName *string, workdir string, awscreds map[string]string, capicfg string) (bool, error) {
	for k := range awscreds {
		os.Setenv(k, awscreds[k])
	}
	log.Info("Boostrapping Cloud Formation stack on AWS")
	template := bootstrap.NewTemplate()
	sess, err := session.NewSession()
	if err != nil {
		return false, err
	}
	cfnSvc := cloudformation.NewService(cfn.New(sess))
	err = cfnSvc.ReconcileBootstrapStack(template.Spec.StackName, *template.RenderCloudFormation())
	if err != nil {
		return false, err
	}
	awsCreds, err := creds.NewAWSCredentialFromDefaultChain(awscreds["AWS_REGION"])
	if err != nil {
		return false, err
	}
	b64creds, err := awsCreds.RenderBase64EncodedAWSDefaultProfile()
	os.Setenv("AWS_B64ENCODED_CREDENTIALS", b64creds)
	if err != nil {
		return false, err
	}
	log.Info("Initializing AWS provider")
	c, err := capiclient.New("")
	if err != nil {
		return false, err
	}
	_, err = c.Init(capiclient.InitOptions{
		Kubeconfig:              capiclient.Kubeconfig{Path: kindkconfig},
		InfrastructureProviders: []string{"aws"},
		LogUsageInstructions:    false,
	})
	if err != nil {
		return false, err
	}
	newClient, err := capiclient.New("")
	if err != nil {
		return false, err
	}
	var cpMachineCount int64 = 3
	var workerMachineCount int64 = 3
	cto := capiclient.GetClusterTemplateOptions{
		Kubeconfig:               capiclient.Kubeconfig{Path: kindkconfig},
		ClusterName:              *clusterName,
		ControlPlaneMachineCount: &cpMachineCount,
		WorkerMachineCount:       &workerMachineCount,
		KubernetesVersion:        "v1.22.2",
		TargetNamespace:          "default",
	}
	installYaml, err := newClient.GetClusterTemplate(cto)
	if err != nil {
		return false, err
	}
	installClusterYaml := workdir + "/" + "install-cluster.yaml"
	err = utils.WriteYamlOutput(installYaml, installClusterYaml)
	if err != nil {
		return false, err
	}
	log.Info("Preflight complete, installing cluster")
	clusterInstallConfig, err := clientcmd.BuildConfigFromFlags("", kindkconfig)
	if err != nil {
		return false, err
	}
	clientset, err := kubernetes.NewForConfig(clusterInstallConfig)
	if err != nil {
		return false, err
	}
	counter := 0
	for runs := 10; counter <= runs; counter++ {
		capaClient := clientset.AppsV1().Deployments("capa-system")
		if counter > runs {
			return false, errors.New("CAPI Controller took too long to roll out")
		}
		capaDeployment, err := capaClient.Get(context.TODO(), "capa-controller-manager", metav1.GetOptions{})
		if err != nil {
			return false, err
		}
		availableReplicas := capaDeployment.Status.AvailableReplicas
		if availableReplicas > int32(0) {
			time.Sleep(5 * time.Second)
			break
		}
		time.Sleep(5 * time.Second)
	}
	err = utils.SplitYamls(workdir+"/"+"capi-install-yamls-output", installClusterYaml, "---")
	if err != nil {
		return false, err
	}
	yamlFiles, err := filepath.Glob(workdir + "/" + "capi-install-yamls-output" + "/" + "*.yaml")
	if err != nil {
		return false, err
	}
	for _, yamlFile := range yamlFiles {
		err = DoSSA(context.TODO(), clusterInstallConfig, yamlFile)
		if err != nil {
			log.Warn("Unable to read YAML: ", err)
		}
	}
	_, err = waitForAWSInfra(clusterInstallConfig, *clusterName)
	if err != nil {
		return false, err
	}
	_, err = waitForCP(clusterInstallConfig, *clusterName)
	if err != nil {
		return false, err
	}
	log.Info("Control Plane Nodes are Online, saving Kubeconfig")
	clusterKubeconfig, err := c.GetKubeconfig(capiclient.GetKubeconfigOptions{
		Kubeconfig:          capiclient.Kubeconfig{Path: kindkconfig},
		WorkloadClusterName: *clusterName,
	})
	if err != nil {
		return false, err
	}
	clusterkcfg, err := os.Create(capicfg)
	if err != nil {
		return false, err
	}
	clusterkcfg.WriteString(clusterKubeconfig)
	clusterkcfg.Close()
	capiInstallConfig, err := clientcmd.BuildConfigFromFlags("", capicfg)
	if err != nil {
		return false, err
	}
	cniYaml := workdir + "/" + "cni.yaml"
	_, err = utils.DownloadFile(cniYaml, CNIurl)
	if err != nil {
		return false, err
	}
	err = utils.SplitYamls(workdir+"/"+"cni-output", cniYaml, "---")
	if err != nil {
		return false, err
	}
	cniyamlFiles, err := filepath.Glob(workdir + "/" + "cni-output" + "/" + "*.yaml")
	if err != nil {
		return false, err
	}
	for _, cniyamlFile := range cniyamlFiles {
		err = DoSSA(context.TODO(), capiInstallConfig, cniyamlFile)
		if err != nil {
			if !strings.Contains(err.Error(), "is missing in") {
				return false, err
			}
		}
	}
	log.Info("Waiting for worker nodes to come online")
	time.Sleep(time.Minute)
	_, err = waitForReadyNodes(capiInstallConfig)
	if err != nil {
		return false, err
	}
	os.Unsetenv("AWS_B64ENCODED_CREDENTIALS")
	for k := range awscreds {
		os.Unsetenv(k)
	}
	log.Info("Successfully created AWS Kubernetes Cluster")
	return true, nil
}
func CreateDevelK8sInstance(kindkconfig string, clusterName *string, workdir string, capicfg string) (bool, error) {
	log.Info("Initializing Docker provider")
	c, err := capiclient.New("")
	if err != nil {
		return false, err
	}
	_, err = c.Init(capiclient.InitOptions{
		Kubeconfig:              capiclient.Kubeconfig{Path: kindkconfig},
		InfrastructureProviders: []string{"docker"},
		LogUsageInstructions:    false,
	})
	if err != nil {
		return false, err
	}
	newClient, err := capiclient.New("")
	if err != nil {
		return false, err
	}
	var cpMachineCount int64 = 3
	var workerMachineCount int64 = 3
	cto := capiclient.GetClusterTemplateOptions{
		Kubeconfig:               capiclient.Kubeconfig{Path: kindkconfig},
		ClusterName:              *clusterName,
		ControlPlaneMachineCount: &cpMachineCount,
		WorkerMachineCount:       &workerMachineCount,
		KubernetesVersion:        "v1.22.1",
		TargetNamespace:          "default",
		ProviderRepositorySource: &capiclient.ProviderRepositorySourceOptions{Flavor: "development"},
	}
	installYaml, err := newClient.GetClusterTemplate(cto)
	if err != nil {
		return false, err
	}
	installClusterYaml := workdir + "/" + "install-cluster.yaml"
	err = utils.WriteYamlOutput(installYaml, installClusterYaml)
	if err != nil {
		return false, err
	}
	log.Info("Preflight complete, installing cluster")
	clusterInstallConfig, err := clientcmd.BuildConfigFromFlags("", kindkconfig)
	if err != nil {
		return false, err
	}
	clientset, err := kubernetes.NewForConfig(clusterInstallConfig)
	if err != nil {
		return false, err
	}
	counter := 0
	for runs := 10; counter <= runs; counter++ {
		capaClient := clientset.AppsV1().Deployments("capd-system")
		if counter > runs {
			return false, errors.New("CAPI Controller took too long to roll out")
		}
		capdDeployment, err := capaClient.Get(context.TODO(), "capd-controller-manager", metav1.GetOptions{})
		if err != nil {
			return false, err
		}
		availableReplicas := capdDeployment.Status.AvailableReplicas
		if availableReplicas > int32(0) {
			time.Sleep(5 * time.Second)
			break
		}
		time.Sleep(5 * time.Second)
	}
	err = utils.SplitYamls(workdir+"/"+"capi-install-yamls-output", installClusterYaml, "---")
	if err != nil {
		return false, err
	}
	yamlFiles, err := filepath.Glob(workdir + "/" + "capi-install-yamls-output" + "/" + "*.yaml")
	if err != nil {
		return false, err
	}
	for _, yamlFile := range yamlFiles {
		err = DoSSA(context.TODO(), clusterInstallConfig, yamlFile)
		if err != nil {
			log.Warn("Unable to read YAML: ", err)
		}
	}
	_, err = waitForAWSInfra(clusterInstallConfig, *clusterName)
	if err != nil {
		return false, err
	}
	_, err = waitForCP(clusterInstallConfig, *clusterName)
	if err != nil {
		return false, err
	}
	log.Info("Control Plane Nodes are Online, saving Kubeconfig")
	var clusterKubeconfig string
	if osruntime.GOOS == "darwin" {
		dirtyKK, err := kind.GetKindKubeconfig(*clusterName, false)
		if err != nil {
			return false, err
		}
		engine, err := sed.New(strings.NewReader(`s/0.0.0.0/127.0.0.1/g s/certificate-authority-data:.*/insecure-skip-tls-verify: true/g`))
		if err != nil {
			return false, err
		}
		clusterKubeconfig, err = engine.RunString(dirtyKK)
		if err != nil {
			return false, err
		}
	} else {
		clusterKubeconfig, err = c.GetKubeconfig(capiclient.GetKubeconfigOptions{
			Kubeconfig:          capiclient.Kubeconfig{Path: kindkconfig},
			WorkloadClusterName: *clusterName,
		})
		if err != nil {
			return false, err
		}
	}
	clusterkcfg, err := os.Create(capicfg)
	if err != nil {
		return false, err
	}
	clusterkcfg.WriteString(clusterKubeconfig)
	clusterkcfg.Close()
	capiInstallConfig, err := clientcmd.BuildConfigFromFlags("", capicfg)
	if err != nil {
		return false, err
	}
	cniYaml := workdir + "/" + "cni.yaml"
	_, err = utils.DownloadFile(cniYaml, CNIurl)
	if err != nil {
		return false, err
	}
	err = utils.SplitYamls(workdir+"/"+"cni-output", cniYaml, "---")
	if err != nil {
		return false, err
	}
	cniyamlFiles, err := filepath.Glob(workdir + "/" + "cni-output" + "/" + "*.yaml")
	if err != nil {
		return false, err
	}
	for _, cniyamlFile := range cniyamlFiles {
		err = DoSSA(context.TODO(), capiInstallConfig, cniyamlFile)
		if err != nil {
			if !strings.Contains(err.Error(), "is missing in") {
				return false, err
			}
		}
	}
	log.Info("Waiting for worker nodes to come online")
	time.Sleep(time.Minute)
	_, err = waitForReadyNodes(capiInstallConfig)
	if err != nil {
		return false, err
	}
	return true, nil
}
func DoSSA(ctx context.Context, cfg *rest.Config, yaml string) error {
	yml, err := ioutil.ReadFile(yaml)
	if err != nil {
		log.Fatal(err)
	}
	dc, err := discovery.NewDiscoveryClientForConfig(cfg)
	if err != nil {
		return err
	}
	mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(dc))
	dyn, err := dynamic.NewForConfig(cfg)
	if err != nil {
		return err
	}
	obj := &unstructured.Unstructured{}
	_, gvk, err := decUnstructured.Decode(yml, nil, obj)
	if err != nil {
		return err
	}
	mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
	if err != nil {
		return err
	}
	var dr dynamic.ResourceInterface
	if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
		dr = dyn.Resource(mapping.Resource).Namespace(obj.GetNamespace())
	} else {
		dr = dyn.Resource(mapping.Resource)
	}
	data, err := json.Marshal(obj)
	if err != nil {
		return err
	}
	_, err = dr.Patch(ctx, obj.GetName(), types.ApplyPatchType, data, metav1.PatchOptions{
		FieldManager: "gokp-bootstrapper",
	})
	return err
}
func waitForAWSInfra(restConfig *rest.Config, clustername string) (bool, error) {
	log.Info("Waiting for Infrastructure")
	scheme := runtime.NewScheme()
	err := clusterv1.AddToScheme(scheme)
	if err != nil {
		return false, err
	}
	c, err := client.New(restConfig, client.Options{
		Scheme: scheme,
	})
	if err != nil {
		return false, err
	}
	counter := 0
	for runs := 20; counter <= runs; counter++ {
		if counter > runs {
			return false, errors.New("aws infra did not come up after 40 minutes")
		}
		cluster := &clusterv1.Cluster{}
		if err := c.Get(context.TODO(), client.ObjectKey{Namespace: "default", Name: clustername}, cluster); err != nil {
			return false, err
		}
		if cluster.Status.Phase == "Provisioned" {
			break
		}
		time.Sleep(time.Minute)
	}
	return true, nil
}
func waitForCP(restConfig *rest.Config, clustername string) (bool, error) {
	log.Info("Waiting for the Control Plane to appear")
	cpname := clustername + "-control-plane"
	var expectedCPReplicas int32 = 3
	scheme := runtime.NewScheme()
	_ = kcpv1.AddToScheme(scheme)
	c, err := client.New(restConfig, client.Options{
		Scheme: scheme,
	})
	if err != nil {
		return false, err
	}
	counter := 0
	for runs := 20; counter <= runs; counter++ {
		if counter > runs {
			return false, errors.New("control-plane did not come up after 10 minutes")
		}
		kcp := &kcpv1.KubeadmControlPlane{}
		if err := c.Get(context.TODO(), client.ObjectKey{Namespace: "default", Name: cpname}, kcp); err != nil {
			return false, err
		}
		if kcp.Status.Replicas == expectedCPReplicas {
			break
		}
		time.Sleep(time.Minute)
	}
	return true, nil
}
func waitForReadyNodes(cfg *rest.Config) (bool, error) {
	nodesClientSet, err := kubernetes.NewForConfig(cfg)
	if err != nil {
		return false, err
	}
	nodesClient, err := nodesClientSet.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		return false, err
	}
	for _, node := range nodesClient.Items {
		for _, i := range node.Status.Conditions {
			if i.Reason == "KubeletReady" {
				c := 0
				for r := 100; c <= r; c++ {
					if c > r {
						return false, errors.New("nodes took too long to come up")
					}
					if i.Type == "Ready" {
						break
					}
					time.Sleep(10 * time.Second)
				}
			}
		}
	}
	return true, nil
}