From 470e23e770223fb9ddb147b3d431c7727f342255 Mon Sep 17 00:00:00 2001 From: Maciej Szulik Date: Fri, 12 Feb 2021 17:04:50 +0100 Subject: [PATCH] kubectl create job support both v1beta1 and v1 cronjob --- .../kubectl/pkg/cmd/create/create_job.go | 54 ++++++++++---- .../kubectl/pkg/cmd/create/create_job_test.go | 70 ++++++++++++++++++- 2 files changed, 110 insertions(+), 14 deletions(-) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/create/create_job.go b/staging/src/k8s.io/kubectl/pkg/cmd/create/create_job.go index 207edf7da44..41ed9d09a56 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/create/create_job.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/create/create_job.go @@ -177,7 +177,7 @@ func (o *CreateJobOptions) Run() error { job = o.createJob() } else { infos, err := o.Builder. - Unstructured(). + WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). NamespaceParam(o.Namespace).DefaultNamespace(). ResourceTypeOrNameArgs(false, o.From). Flatten(). @@ -191,16 +191,14 @@ func (o *CreateJobOptions) Run() error { return fmt.Errorf("from must be an existing cronjob") } - uncastVersionedObj, err := scheme.Scheme.ConvertToVersion(infos[0].Object, batchv1beta1.SchemeGroupVersion) - if err != nil { - return fmt.Errorf("from must be an existing cronjob: %v", err) + switch obj := infos[0].Object.(type) { + case *batchv1.CronJob: + job = o.createJobFromCronJob(obj) + case *batchv1beta1.CronJob: + job = o.createJobFromCronJobV1Beta1(obj) + default: + return fmt.Errorf("unknown object type %T", obj) } - cronJob, ok := uncastVersionedObj.(*batchv1beta1.CronJob) - if !ok { - return fmt.Errorf("from must be an existing cronjob") - } - - job = o.createJobFromCronJob(cronJob) } if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, job, scheme.DefaultJSONEncoder()); err != nil { @@ -256,7 +254,39 @@ func (o *CreateJobOptions) createJob() *batchv1.Job { return job } -func (o *CreateJobOptions) createJobFromCronJob(cronJob *batchv1beta1.CronJob) *batchv1.Job { +func (o *CreateJobOptions) createJobFromCronJobV1Beta1(cronJob *batchv1beta1.CronJob) *batchv1.Job { + annotations := make(map[string]string) + annotations["cronjob.kubernetes.io/instantiate"] = "manual" + for k, v := range cronJob.Spec.JobTemplate.Annotations { + annotations[k] = v + } + + job := &batchv1.Job{ + // this is ok because we know exactly how we want to be serialized + TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"}, + ObjectMeta: metav1.ObjectMeta{ + Name: o.Name, + Annotations: annotations, + Labels: cronJob.Spec.JobTemplate.Labels, + OwnerReferences: []metav1.OwnerReference{ + { + // TODO (soltysh): switch this to v1 in v1.22, when n-1 skew will be fulfilled + APIVersion: batchv1beta1.SchemeGroupVersion.String(), + Kind: "CronJob", + Name: cronJob.GetName(), + UID: cronJob.GetUID(), + }, + }, + }, + Spec: cronJob.Spec.JobTemplate.Spec, + } + if o.EnforceNamespace { + job.Namespace = o.Namespace + } + return job +} + +func (o *CreateJobOptions) createJobFromCronJob(cronJob *batchv1.CronJob) *batchv1.Job { annotations := make(map[string]string) annotations["cronjob.kubernetes.io/instantiate"] = "manual" for k, v := range cronJob.Spec.JobTemplate.Annotations { @@ -273,7 +303,7 @@ func (o *CreateJobOptions) createJobFromCronJob(cronJob *batchv1beta1.CronJob) * OwnerReferences: []metav1.OwnerReference{ { APIVersion: batchv1beta1.SchemeGroupVersion.String(), - Kind: cronJob.Kind, + Kind: "CronJob", Name: cronJob.GetName(), UID: cronJob.GetUID(), }, diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/create/create_job_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/create/create_job_test.go index 8dca106e1ec..b5160fbd26a 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/create/create_job_test.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/create/create_job_test.go @@ -135,7 +135,7 @@ func TestCreateJob(t *testing.T) { } } -func TestCreateJobFromCronJob(t *testing.T) { +func TestCreateJobFromCronJobV1Beta1(t *testing.T) { jobName := "test-job" cronJob := &batchv1beta1.CronJob{ Spec: batchv1beta1.CronJobSpec{ @@ -167,7 +167,73 @@ func TestCreateJobFromCronJob(t *testing.T) { OwnerReferences: []metav1.OwnerReference{ { APIVersion: batchv1beta1.SchemeGroupVersion.String(), - Kind: cronJob.Kind, + Kind: "CronJob", + Name: cronJob.GetName(), + UID: cronJob.GetUID(), + }, + }, + }, + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Image: "test-image"}, + }, + RestartPolicy: corev1.RestartPolicyNever, + }, + }, + }, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + o := &CreateJobOptions{ + Name: jobName, + } + job := o.createJobFromCronJobV1Beta1(tc.from) + + if !apiequality.Semantic.DeepEqual(job, tc.expected) { + t.Errorf("expected:\n%#v\ngot:\n%#v", tc.expected, job) + } + }) + } +} + +func TestCreateJobFromCronJob(t *testing.T) { + jobName := "test-job" + cronJob := &batchv1.CronJob{ + Spec: batchv1.CronJobSpec{ + JobTemplate: batchv1.JobTemplateSpec{ + Spec: batchv1.JobSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Image: "test-image"}, + }, + RestartPolicy: corev1.RestartPolicyNever, + }, + }, + }, + }, + }, + } + tests := map[string]struct { + from *batchv1.CronJob + expected *batchv1.Job + }{ + "from CronJob": { + from: cronJob, + expected: &batchv1.Job{ + TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"}, + ObjectMeta: metav1.ObjectMeta{ + Name: jobName, + Annotations: map[string]string{"cronjob.kubernetes.io/instantiate": "manual"}, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: batchv1beta1.SchemeGroupVersion.String(), + Kind: "CronJob", Name: cronJob.GetName(), UID: cronJob.GetUID(), },