create_job.go 6.7 KB

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