浏览代码

create ephemeral pods deletion cronjob

Mohammed Nafees 4 年之前
父节点
当前提交
67b987a696
共有 1 个文件被更改,包括 204 次插入2 次删除
  1. 204 2
      cli/cmd/run.go

+ 204 - 2
cli/cmd/run.go

@@ -14,7 +14,9 @@ import (
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/cli/cmd/utils"
 	"github.com/spf13/cobra"
+	batchv1 "k8s.io/api/batch/v1"
 	v1 "k8s.io/api/core/v1"
+	rbacv1 "k8s.io/api/rbac/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/fields"
 	"k8s.io/apimachinery/pkg/watch"
@@ -288,7 +290,10 @@ func executeRunEphemeral(config *PorterRunSharedConfig, namespace, name, contain
 		return err
 	}
 
-	newPod, err := createPodFromExisting(config, existing, args)
+	newPod, err := createEphemeralPodFromExisting(config, existing, args)
+	if err != nil {
+		return err
+	}
 	podName := newPod.ObjectMeta.Name
 
 	// delete the ephemeral pod no matter what
@@ -300,6 +305,11 @@ func executeRunEphemeral(config *PorterRunSharedConfig, namespace, name, contain
 		return handlePodAttachError(err, config, namespace, podName, container)
 	}
 
+	err = checkForPodDeletionCronJob(config)
+	if err != nil {
+		return err
+	}
+
 	// refresh pod info for latest status
 	newPod, err = config.Clientset.CoreV1().
 		Pods(newPod.Namespace).
@@ -367,6 +377,194 @@ func executeRunEphemeral(config *PorterRunSharedConfig, namespace, name, contain
 	return err
 }
 
+func checkForPodDeletionCronJob(config *PorterRunSharedConfig) error {
+	namespaces, err := config.Clientset.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{})
+	if err != nil {
+		return err
+	}
+
+	for _, namespace := range namespaces.Items {
+		cronJobs, err := config.Clientset.BatchV1().CronJobs(namespace.Name).List(
+			context.Background(), metav1.ListOptions{},
+		)
+		if err != nil {
+			return err
+		}
+
+		for _, cronJob := range cronJobs.Items {
+			if cronJob.Name == "porter-ephemeral-pod-deletion-cronjob" {
+				return nil
+			}
+		}
+	}
+
+	// try and create the cron job and all of the other required resources as necessary,
+	// starting with the service account, then role and then a role binding
+
+	err = checkForServiceAccount(config)
+	if err != nil {
+		return err
+	}
+
+	err = checkForRole(config)
+	if err != nil {
+		return err
+	}
+
+	err = checkForRoleBinding(config)
+	if err != nil {
+		return err
+	}
+
+	// create the cronjob
+
+	cronJob := &batchv1.CronJob{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "porter-ephemeral-pod-deletion-cronjob",
+		},
+		Spec: batchv1.CronJobSpec{
+			Schedule: "* 6 * * *",
+			JobTemplate: batchv1.JobTemplateSpec{
+				Spec: batchv1.JobSpec{
+					Template: v1.PodTemplateSpec{
+						Spec: v1.PodSpec{
+							ServiceAccountName: "porter-ephemeral-pod-deletion-service-account",
+							RestartPolicy:      v1.RestartPolicyNever,
+							Containers: []v1.Container{
+								{
+									Name:            "ephemeral-pods-manager",
+									Image:           "public.ecr.aws/o1j4x7p4/ephemeral-pods-manager:latest",
+									ImagePullPolicy: v1.PullAlways,
+									Args:            []string{"delete"},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+	_, err = config.Clientset.BatchV1().CronJobs(namespace).Create(
+		context.Background(), cronJob, metav1.CreateOptions{},
+	)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func checkForServiceAccount(config *PorterRunSharedConfig) error {
+	serviceAccounts, err := config.Clientset.CoreV1().ServiceAccounts(namespace).List(
+		context.Background(), metav1.ListOptions{},
+	)
+	if err != nil {
+		return err
+	}
+
+	for _, serviceAccount := range serviceAccounts.Items {
+		if serviceAccount.Name == "porter-ephemeral-pod-deletion-service-account" {
+			return nil
+		}
+	}
+
+	serviceAccount := &v1.ServiceAccount{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "porter-ephemeral-pod-deletion-service-account",
+		},
+	}
+	_, err = config.Clientset.CoreV1().ServiceAccounts(namespace).Create(
+		context.Background(), serviceAccount, metav1.CreateOptions{},
+	)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func checkForRole(config *PorterRunSharedConfig) error {
+	roles, err := config.Clientset.RbacV1().ClusterRoles().List(
+		context.Background(), metav1.ListOptions{},
+	)
+	if err != nil {
+		return err
+	}
+
+	for _, role := range roles.Items {
+		if role.Name == "porter-ephemeral-pod-deletion-cluster-role" {
+			return nil
+		}
+	}
+
+	role := &rbacv1.ClusterRole{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "porter-ephemeral-pod-deletion-cluster-role",
+		},
+		Rules: []rbacv1.PolicyRule{
+			{
+				APIGroups: []string{""},
+				Resources: []string{"pods"},
+				Verbs:     []string{"list", "delete"},
+			},
+			{
+				APIGroups: []string{""},
+				Resources: []string{"namespaces"},
+				Verbs:     []string{"list"},
+			},
+		},
+	}
+	_, err = config.Clientset.RbacV1().ClusterRoles().Create(
+		context.Background(), role, metav1.CreateOptions{},
+	)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func checkForRoleBinding(config *PorterRunSharedConfig) error {
+	bindings, err := config.Clientset.RbacV1().ClusterRoleBindings().List(
+		context.Background(), metav1.ListOptions{},
+	)
+	if err != nil {
+		return err
+	}
+
+	for _, binding := range bindings.Items {
+		if binding.Name == "porter-ephemeral-pod-deletion-cluster-rolebinding" {
+			return nil
+		}
+	}
+
+	binding := &rbacv1.ClusterRoleBinding{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: "porter-ephemeral-pod-deletion-cluster-rolebinding",
+		},
+		RoleRef: rbacv1.RoleRef{
+			APIGroup: "rbac.authorization.k8s.io",
+			Kind:     "ClusterRole",
+			Name:     "porter-ephemeral-pod-deletion-cluster-role",
+		},
+		Subjects: []rbacv1.Subject{
+			{
+				APIGroup: "rbac.authorization.k8s.io",
+				Kind:     "ServiceAccount",
+				Name:     "porter-ephemeral-pod-deletion-service-account",
+			},
+		},
+	}
+	_, err = config.Clientset.RbacV1().ClusterRoleBindings().Create(
+		context.Background(), binding, metav1.CreateOptions{},
+	)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 func waitForPod(config *PorterRunSharedConfig, pod *v1.Pod) error {
 	var (
 		w   watch.Interface
@@ -519,7 +717,7 @@ func deletePod(config *PorterRunSharedConfig, name, namespace string) error {
 	return nil
 }
 
-func createPodFromExisting(config *PorterRunSharedConfig, existing *v1.Pod, args []string) (*v1.Pod, error) {
+func createEphemeralPodFromExisting(config *PorterRunSharedConfig, existing *v1.Pod, args []string) (*v1.Pod, error) {
 	newPod := existing.DeepCopy()
 
 	// only copy the pod spec, overwrite metadata
@@ -540,6 +738,10 @@ func createPodFromExisting(config *PorterRunSharedConfig, existing *v1.Pod, args
 	cmdRoot := args[0]
 	cmdArgs := make([]string, 0)
 
+	// annotate with the ephemeral pod tag
+	newPod.Labels = make(map[string]string)
+	newPod.Labels["porter/ephemeral-pod"] = "true"
+
 	if len(args) > 1 {
 		cmdArgs = args[1:]
 	}