...

Source file src/k8s.io/kubectl/pkg/cmd/create/create_job.go

Documentation: k8s.io/kubectl/pkg/cmd/create

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package create
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/spf13/cobra"
    24  
    25  	batchv1 "k8s.io/api/batch/v1"
    26  	corev1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/cli-runtime/pkg/genericclioptions"
    30  	"k8s.io/cli-runtime/pkg/genericiooptions"
    31  	"k8s.io/cli-runtime/pkg/resource"
    32  	batchv1client "k8s.io/client-go/kubernetes/typed/batch/v1"
    33  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    34  	"k8s.io/kubectl/pkg/scheme"
    35  	"k8s.io/kubectl/pkg/util"
    36  	"k8s.io/kubectl/pkg/util/i18n"
    37  	"k8s.io/kubectl/pkg/util/templates"
    38  )
    39  
    40  var (
    41  	jobLong = templates.LongDesc(i18n.T(`
    42  		Create a job with the specified name.`))
    43  
    44  	jobExample = templates.Examples(i18n.T(`
    45  		# Create a job
    46  		kubectl create job my-job --image=busybox
    47  
    48  		# Create a job with a command
    49  		kubectl create job my-job --image=busybox -- date
    50  
    51  		# Create a job from a cron job named "a-cronjob"
    52  		kubectl create job test-job --from=cronjob/a-cronjob`))
    53  )
    54  
    55  // CreateJobOptions is the command line options for 'create job'
    56  type CreateJobOptions struct {
    57  	PrintFlags *genericclioptions.PrintFlags
    58  
    59  	PrintObj func(obj runtime.Object) error
    60  
    61  	Name    string
    62  	Image   string
    63  	From    string
    64  	Command []string
    65  
    66  	Namespace           string
    67  	EnforceNamespace    bool
    68  	Client              batchv1client.BatchV1Interface
    69  	DryRunStrategy      cmdutil.DryRunStrategy
    70  	ValidationDirective string
    71  	Builder             *resource.Builder
    72  	FieldManager        string
    73  	CreateAnnotation    bool
    74  
    75  	genericiooptions.IOStreams
    76  }
    77  
    78  // NewCreateJobOptions initializes and returns new CreateJobOptions instance
    79  func NewCreateJobOptions(ioStreams genericiooptions.IOStreams) *CreateJobOptions {
    80  	return &CreateJobOptions{
    81  		PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
    82  		IOStreams:  ioStreams,
    83  	}
    84  }
    85  
    86  // NewCmdCreateJob is a command to ease creating Jobs from CronJobs.
    87  func NewCmdCreateJob(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
    88  	o := NewCreateJobOptions(ioStreams)
    89  	cmd := &cobra.Command{
    90  		Use:                   "job NAME --image=image [--from=cronjob/name] -- [COMMAND] [args...]",
    91  		DisableFlagsInUseLine: true,
    92  		Short:                 i18n.T("Create a job with the specified name"),
    93  		Long:                  jobLong,
    94  		Example:               jobExample,
    95  		Run: func(cmd *cobra.Command, args []string) {
    96  			cmdutil.CheckErr(o.Complete(f, cmd, args))
    97  			cmdutil.CheckErr(o.Validate())
    98  			cmdutil.CheckErr(o.Run())
    99  		},
   100  	}
   101  
   102  	o.PrintFlags.AddFlags(cmd)
   103  
   104  	cmdutil.AddApplyAnnotationFlags(cmd)
   105  	cmdutil.AddValidateFlags(cmd)
   106  	cmdutil.AddDryRunFlag(cmd)
   107  	cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.")
   108  	cmd.Flags().StringVar(&o.From, "from", o.From, "The name of the resource to create a Job from (only cronjob is supported).")
   109  	cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
   110  	return cmd
   111  }
   112  
   113  // Complete completes all the required options
   114  func (o *CreateJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
   115  	name, err := NameFromCommandArgs(cmd, args)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	o.Name = name
   120  	if len(args) > 1 {
   121  		o.Command = args[1:]
   122  	}
   123  
   124  	clientConfig, err := f.ToRESTConfig()
   125  	if err != nil {
   126  		return err
   127  	}
   128  	o.Client, err = batchv1client.NewForConfig(clientConfig)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
   134  
   135  	o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
   136  	if err != nil {
   137  		return err
   138  	}
   139  	o.Builder = f.NewBuilder()
   140  
   141  	o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
   142  	if err != nil {
   143  		return err
   144  	}
   145  	cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
   146  	printer, err := o.PrintFlags.ToPrinter()
   147  	if err != nil {
   148  		return err
   149  	}
   150  	o.PrintObj = func(obj runtime.Object) error {
   151  		return printer.PrintObj(obj, o.Out)
   152  	}
   153  
   154  	o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	return nil
   160  }
   161  
   162  // Validate makes sure provided values and valid Job options
   163  func (o *CreateJobOptions) Validate() error {
   164  	if (len(o.Image) == 0 && len(o.From) == 0) || (len(o.Image) != 0 && len(o.From) != 0) {
   165  		return fmt.Errorf("either --image or --from must be specified")
   166  	}
   167  	if o.Command != nil && len(o.Command) != 0 && len(o.From) != 0 {
   168  		return fmt.Errorf("cannot specify --from and command")
   169  	}
   170  	return nil
   171  }
   172  
   173  // Run performs the execution of 'create job' sub command
   174  func (o *CreateJobOptions) Run() error {
   175  	var job *batchv1.Job
   176  	if len(o.Image) > 0 {
   177  		job = o.createJob()
   178  	} else {
   179  		infos, err := o.Builder.
   180  			WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
   181  			NamespaceParam(o.Namespace).DefaultNamespace().
   182  			ResourceTypeOrNameArgs(false, o.From).
   183  			Flatten().
   184  			Latest().
   185  			Do().
   186  			Infos()
   187  		if err != nil {
   188  			return err
   189  		}
   190  		if len(infos) != 1 {
   191  			return fmt.Errorf("from must be an existing cronjob")
   192  		}
   193  
   194  		switch obj := infos[0].Object.(type) {
   195  		case *batchv1.CronJob:
   196  			job = o.createJobFromCronJob(obj)
   197  		default:
   198  			return fmt.Errorf("unknown object type %T", obj)
   199  		}
   200  	}
   201  
   202  	if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, job, scheme.DefaultJSONEncoder()); err != nil {
   203  		return err
   204  	}
   205  
   206  	if o.DryRunStrategy != cmdutil.DryRunClient {
   207  		createOptions := metav1.CreateOptions{}
   208  		if o.FieldManager != "" {
   209  			createOptions.FieldManager = o.FieldManager
   210  		}
   211  		createOptions.FieldValidation = o.ValidationDirective
   212  		if o.DryRunStrategy == cmdutil.DryRunServer {
   213  			createOptions.DryRun = []string{metav1.DryRunAll}
   214  		}
   215  		var err error
   216  		job, err = o.Client.Jobs(o.Namespace).Create(context.TODO(), job, createOptions)
   217  		if err != nil {
   218  			return fmt.Errorf("failed to create job: %v", err)
   219  		}
   220  	}
   221  
   222  	return o.PrintObj(job)
   223  }
   224  
   225  func (o *CreateJobOptions) createJob() *batchv1.Job {
   226  	job := &batchv1.Job{
   227  		// this is ok because we know exactly how we want to be serialized
   228  		TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"},
   229  		ObjectMeta: metav1.ObjectMeta{
   230  			Name: o.Name,
   231  		},
   232  		Spec: batchv1.JobSpec{
   233  			Template: corev1.PodTemplateSpec{
   234  				Spec: corev1.PodSpec{
   235  					Containers: []corev1.Container{
   236  						{
   237  							Name:    o.Name,
   238  							Image:   o.Image,
   239  							Command: o.Command,
   240  						},
   241  					},
   242  					RestartPolicy: corev1.RestartPolicyNever,
   243  				},
   244  			},
   245  		},
   246  	}
   247  	if o.EnforceNamespace {
   248  		job.Namespace = o.Namespace
   249  	}
   250  	return job
   251  }
   252  
   253  func (o *CreateJobOptions) createJobFromCronJob(cronJob *batchv1.CronJob) *batchv1.Job {
   254  	annotations := make(map[string]string)
   255  	annotations["cronjob.kubernetes.io/instantiate"] = "manual"
   256  	for k, v := range cronJob.Spec.JobTemplate.Annotations {
   257  		annotations[k] = v
   258  	}
   259  
   260  	job := &batchv1.Job{
   261  		// this is ok because we know exactly how we want to be serialized
   262  		TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"},
   263  		ObjectMeta: metav1.ObjectMeta{
   264  			Name:            o.Name,
   265  			Annotations:     annotations,
   266  			Labels:          cronJob.Spec.JobTemplate.Labels,
   267  			OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(cronJob, batchv1.SchemeGroupVersion.WithKind("CronJob"))},
   268  		},
   269  		Spec: cronJob.Spec.JobTemplate.Spec,
   270  	}
   271  	if o.EnforceNamespace {
   272  		job.Namespace = o.Namespace
   273  	}
   274  	return job
   275  }
   276  

View as plain text