func deleteDeployment(ctx context.Context) error {
	log.Infoln("Attempting to delete deployment in", checkNamespace, "namespace.")
	deletePolicy := metav1.DeletePropagationForeground
	graceSeconds := int64(1)
	deleteOpts := metav1.DeleteOptions{
		GracePeriodSeconds: &graceSeconds,
		PropagationPolicy:  &deletePolicy,
	}
	return client.AppsV1().Deployments(checkNamespace).Delete(ctx, checkDeploymentName, deleteOpts)
}
deleteDeployment issues a foreground delete for the check test deployment name.
package main
import (
	"context"
	"errors"
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	log "github.com/sirupsen/logrus"
	v1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	resource "k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	watchpkg "k8s.io/apimachinery/pkg/watch"
)
const (
	defaultLabelKey        = "deployment-timestamp"
	defaultLabelValueBase  = "unix-"
	defaultMinReadySeconds = 5
	defaultMaxSurge       = 2
	defaultMaxUnavailable = 2
	defaultImagePullPolicy = "IfNotPresent"
	defaultMillicoreRequest = 15                
	defaultMillicoreLimit   = 75                
	defaultMemoryRequest    = 20 * 1024 * 1024  
	defaultMemoryLimit      = 75 * 1024 * 1024  
	defaultProbeFailureThreshold    = 5   
	defaultProbeSuccessThreshold    = 1   
	defaultProbeInitialDelaySeconds = 2   
	defaultProbeTimeoutSeconds      = 2   
	defaultProbePeriodSeconds       = 15  
)
func createDeploymentConfig(image string) *v1.Deployment {
	deployment := &v1.Deployment{}
	checkImage := image
	log.Infoln("Creating deployment resource with", checkDeploymentReplicas, "replica(s) in", checkNamespace, "namespace using image ["+checkImage+"] with environment variables:", additionalEnvVars)
	containers := make([]corev1.Container, 0)
	if len(checkImage) == 0 {
		err := errors.New("check image url for container is empty: " + checkImage)
		log.Warnln(err.Error())
		return deployment
	}
	var container corev1.Container
	container = createContainerConfig(checkImage)
	containers = append(containers, container)
	if len(checkDeploymentNodeSelectors) == 0 {
		checkDeploymentNodeSelectors = nil
	}
	graceSeconds := int64(1)
	podSpec := corev1.PodSpec{
		Containers:                    containers,
		NodeSelector:                  checkDeploymentNodeSelectors,
		RestartPolicy:                 corev1.RestartPolicyAlways,
		TerminationGracePeriodSeconds: &graceSeconds,
		ServiceAccountName:            checkServiceAccount,
		Tolerations:                   checkDeploymentTolerations,
	}
	labels := make(map[string]string, 0)
	labels[defaultLabelKey] = defaultLabelValueBase + strconv.Itoa(int(now.Unix()))
	labels["source"] = "kuberhealthy"
	podTemplateSpec := corev1.PodTemplateSpec{
		Spec: podSpec,
	}
	podTemplateSpec.ObjectMeta.Labels = labels
	podTemplateSpec.ObjectMeta.Name = checkDeploymentName
	podTemplateSpec.ObjectMeta.Namespace = checkNamespace
	labelSelector := metav1.LabelSelector{
		MatchLabels: labels,
	}
	maxSurge := math.Ceil(float64(checkDeploymentReplicas) / float64(2))
	maxUnavailable := math.Ceil(float64(checkDeploymentReplicas) / float64(2))
	rollingUpdateSpec := v1.RollingUpdateDeployment{
		MaxUnavailable: &intstr.IntOrString{
			IntVal: int32(maxUnavailable),
			StrVal: strconv.Itoa(int(maxUnavailable)),
		},
		MaxSurge: &intstr.IntOrString{
			IntVal: int32(maxSurge),
			StrVal: strconv.Itoa(int(maxSurge)),
		},
	}
	deployStrategy := v1.DeploymentStrategy{
		Type:          v1.RollingUpdateDeploymentStrategyType,
		RollingUpdate: &rollingUpdateSpec,
	}
	replicas := int32(checkDeploymentReplicas)
	deploySpec := v1.DeploymentSpec{
		Strategy:        deployStrategy,
		MinReadySeconds: defaultMinReadySeconds,
		Replicas:        &replicas,
		Selector:        &labelSelector,
		Template:        podTemplateSpec,
	}
	deployment.ObjectMeta.Name = checkDeploymentName
	deployment.ObjectMeta.Namespace = checkNamespace
	deployment.Spec = deploySpec
	return deployment
}
type DeploymentResult struct {
	Deployment *v1.Deployment
	Err        error
}
func createDeployment(ctx context.Context, deploymentConfig *v1.Deployment) chan DeploymentResult {
	createChan := make(chan DeploymentResult)
	go func() {
		log.Infoln("Creating deployment in cluster with name:", deploymentConfig.Name)
		defer close(createChan)
		result := DeploymentResult{}
		deployment, err := client.AppsV1().Deployments(checkNamespace).Create(ctx, deploymentConfig, metav1.CreateOptions{})
		if err != nil {
			log.Infoln("Failed to create a deployment in the cluster:", err)
			result.Err = err
			createChan <- result
			return
		}
		if deployment == nil {
			err = errors.New("got a nil deployment result: ")
			log.Errorln("Failed to create a deployment in the cluster: %w", err)
			result.Err = err
			createChan <- result
			return
		}
		for {
			log.Infoln("Watching for deployment to exist.")
			watch, err := client.AppsV1().Deployments(checkNamespace).Watch(ctx, metav1.ListOptions{
				Watch:         true,
				FieldSelector: "metadata.name=" + deployment.Name,
			})
			if err != nil {
				result.Err = err
				createChan <- result
				return
			}
			if watch == nil {
				continue
			}
			for event := range watch.ResultChan() {  
				d, ok := event.Object.(*v1.Deployment)
				if !ok {  
					log.Infoln("Got a watch event for a non-deployment object -- ignoring.")
					continue
				}
				log.Debugln("Received an event watching for deployment changes:", d.Name, "got event", event.Type)
				if deploymentAvailable(d) {
					result.Deployment = d
					createChan <- result
					return
				}
				select {
				case <-ctx.Done():  
					log.Errorln("Context expired while waiting for deployment to create.")
					err = cleanUp(ctx)  
					if err != nil {
						result.Err = errors.New("failed to clean up properly: " + err.Error())
					}
					createChan <- result
					return
				default:
				}
			}
			watch.Stop()
		}
	}()
	return createChan
}
func createContainerConfig(imageURL string) corev1.Container {
	log.Infoln("Creating container using image ["+imageURL+"] with environment variables:", additionalEnvVars)
	basicPort := corev1.ContainerPort{
		ContainerPort: checkContainerPort,
	}
	containerPorts := []corev1.ContainerPort{basicPort}
	requests := make(map[corev1.ResourceName]resource.Quantity, 0)
	requests[corev1.ResourceCPU] = *resource.NewMilliQuantity(int64(millicoreRequest), resource.DecimalSI)
	requests[corev1.ResourceMemory] = *resource.NewQuantity(int64(memoryRequest), resource.BinarySI)
	limits := make(map[corev1.ResourceName]resource.Quantity, 0)
	limits[corev1.ResourceCPU] = *resource.NewMilliQuantity(int64(millicoreLimit), resource.DecimalSI)
	limits[corev1.ResourceMemory] = *resource.NewQuantity(int64(memoryLimit), resource.BinarySI)
	resources := corev1.ResourceRequirements{
		Requests: requests,
		Limits:   limits,
	}
	envs := make([]corev1.EnvVar, 0)
	for k, v := range additionalEnvVars {
		ev := corev1.EnvVar{
			Name:  k,
			Value: v,
		}
		envs = append(envs, ev)
	}
	tcpSocket := corev1.TCPSocketAction{
		Port: intstr.IntOrString{
			IntVal: checkContainerPort,
			StrVal: strconv.Itoa(int(checkContainerPort)),
		},
	}
	handler := corev1.Handler{
		TCPSocket: &tcpSocket,
	}
	liveProbe := corev1.Probe{
		Handler:             handler,
		InitialDelaySeconds: defaultProbeInitialDelaySeconds,
		TimeoutSeconds:      defaultProbeTimeoutSeconds,
		PeriodSeconds:       defaultProbePeriodSeconds,
		SuccessThreshold:    defaultProbeSuccessThreshold,
		FailureThreshold:    defaultProbeFailureThreshold,
	}
	readyProbe := corev1.Probe{
		Handler:             handler,
		InitialDelaySeconds: defaultProbeInitialDelaySeconds,
		TimeoutSeconds:      defaultProbeTimeoutSeconds,
		PeriodSeconds:       defaultProbePeriodSeconds,
		SuccessThreshold:    defaultProbeSuccessThreshold,
		FailureThreshold:    defaultProbeFailureThreshold,
	}
	c := corev1.Container{
		Name:            defaultCheckContainerName,
		Image:           imageURL,
		ImagePullPolicy: defaultImagePullPolicy,
		Ports:           containerPorts,
		Resources:       resources,
		Env:             envs,
		LivenessProbe:   &liveProbe,
		ReadinessProbe:  &readyProbe,
	}
	return c
}
func deleteDeploymentAndWait(ctx context.Context) error {
	deleteChan := make(chan error)
	go func() {
		defer close(deleteChan)
		log.Debugln("Checking if deployment has been deleted.")
		for {
			select {
			case <-ctx.Done():
				deleteChan <- fmt.Errorf("timed out while waiting for deployment to delete")
			default:
				log.Debugln("Delete deployment and wait has not yet timed out.")
			}
			log.Debugln("Waiting 5 seconds before trying again.")
			time.Sleep(time.Second * 5)
			deploymentList, err := client.AppsV1().Deployments(checkNamespace).List(ctx, metav1.ListOptions{
				FieldSelector: "metadata.name=" + checkDeploymentName,
			})
			if err != nil {
				log.Errorln("Error listing deployments:", err.Error())
				continue
			}
			var deploymentExists bool
			for _, deploy := range deploymentList.Items {
				if deploy.GetName() == checkDeploymentName {
					deploymentExists = true
					err = deleteDeployment(ctx)
					if err != nil {
						log.Errorln("Error when running a delete on deployment", checkDeploymentName+":", err.Error())
					}
					break
				}
			}
			if !deploymentExists {
				deleteChan <- nil
				break
			}
		}
	}()
	err := deleteDeployment(ctx)
	if err != nil {
		log.Infoln("Could not delete deployment:", checkDeploymentName)
	}
	return <-deleteChan
}