1
16
17 package create
18
19 import (
20 "context"
21 "fmt"
22 "io"
23 "net/url"
24 "runtime"
25 "strings"
26
27 "github.com/spf13/cobra"
28 "k8s.io/klog/v2"
29
30 "k8s.io/apimachinery/pkg/api/meta"
31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
33 kruntime "k8s.io/apimachinery/pkg/runtime"
34 "k8s.io/apimachinery/pkg/runtime/schema"
35 "k8s.io/cli-runtime/pkg/genericclioptions"
36 "k8s.io/cli-runtime/pkg/genericiooptions"
37 "k8s.io/cli-runtime/pkg/printers"
38 "k8s.io/cli-runtime/pkg/resource"
39 "k8s.io/client-go/dynamic"
40 cmdutil "k8s.io/kubectl/pkg/cmd/util"
41 "k8s.io/kubectl/pkg/cmd/util/editor"
42 "k8s.io/kubectl/pkg/generate"
43 "k8s.io/kubectl/pkg/rawhttp"
44 "k8s.io/kubectl/pkg/scheme"
45 "k8s.io/kubectl/pkg/util"
46 "k8s.io/kubectl/pkg/util/i18n"
47 "k8s.io/kubectl/pkg/util/templates"
48 )
49
50
51 type CreateOptions struct {
52 PrintFlags *genericclioptions.PrintFlags
53 RecordFlags *genericclioptions.RecordFlags
54
55 DryRunStrategy cmdutil.DryRunStrategy
56
57 ValidationDirective string
58
59 fieldManager string
60
61 FilenameOptions resource.FilenameOptions
62 Selector string
63 EditBeforeCreate bool
64 Raw string
65
66 Recorder genericclioptions.Recorder
67 PrintObj func(obj kruntime.Object) error
68
69 genericiooptions.IOStreams
70 }
71
72 var (
73 createLong = templates.LongDesc(i18n.T(`
74 Create a resource from a file or from stdin.
75
76 JSON and YAML formats are accepted.`))
77
78 createExample = templates.Examples(i18n.T(`
79 # Create a pod using the data in pod.json
80 kubectl create -f ./pod.json
81
82 # Create a pod based on the JSON passed into stdin
83 cat pod.json | kubectl create -f -
84
85 # Edit the data in registry.yaml in JSON then create the resource using the edited data
86 kubectl create -f registry.yaml --edit -o json`))
87 )
88
89
90 func NewCreateOptions(ioStreams genericiooptions.IOStreams) *CreateOptions {
91 return &CreateOptions{
92 PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
93 RecordFlags: genericclioptions.NewRecordFlags(),
94
95 Recorder: genericclioptions.NoopRecorder{},
96
97 IOStreams: ioStreams,
98 }
99 }
100
101
102 func NewCmdCreate(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
103 o := NewCreateOptions(ioStreams)
104
105 cmd := &cobra.Command{
106 Use: "create -f FILENAME",
107 DisableFlagsInUseLine: true,
108 Short: i18n.T("Create a resource from a file or from stdin"),
109 Long: createLong,
110 Example: createExample,
111 Run: func(cmd *cobra.Command, args []string) {
112 if cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) {
113 ioStreams.ErrOut.Write([]byte("Error: must specify one of -f and -k\n\n"))
114 defaultRunFunc := cmdutil.DefaultSubCommandRun(ioStreams.ErrOut)
115 defaultRunFunc(cmd, args)
116 return
117 }
118 cmdutil.CheckErr(o.Complete(f, cmd, args))
119 cmdutil.CheckErr(o.Validate())
120 cmdutil.CheckErr(o.RunCreate(f, cmd))
121 },
122 }
123
124
125 o.RecordFlags.AddFlags(cmd)
126
127 usage := "to use to create the resource"
128 cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
129 cmdutil.AddValidateFlags(cmd)
130 cmd.Flags().BoolVar(&o.EditBeforeCreate, "edit", o.EditBeforeCreate, "Edit the API resource before creating")
131 cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows",
132 "Only relevant if --edit=true. Defaults to the line ending native to your platform.")
133 cmdutil.AddApplyAnnotationFlags(cmd)
134 cmdutil.AddDryRunFlag(cmd)
135 cmdutil.AddLabelSelectorFlagVar(cmd, &o.Selector)
136 cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to POST to the server. Uses the transport specified by the kubeconfig file.")
137 cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-create")
138
139 o.PrintFlags.AddFlags(cmd)
140
141
142 cmd.AddCommand(NewCmdCreateNamespace(f, ioStreams))
143 cmd.AddCommand(NewCmdCreateQuota(f, ioStreams))
144 cmd.AddCommand(NewCmdCreateSecret(f, ioStreams))
145 cmd.AddCommand(NewCmdCreateConfigMap(f, ioStreams))
146 cmd.AddCommand(NewCmdCreateServiceAccount(f, ioStreams))
147 cmd.AddCommand(NewCmdCreateService(f, ioStreams))
148 cmd.AddCommand(NewCmdCreateDeployment(f, ioStreams))
149 cmd.AddCommand(NewCmdCreateClusterRole(f, ioStreams))
150 cmd.AddCommand(NewCmdCreateClusterRoleBinding(f, ioStreams))
151 cmd.AddCommand(NewCmdCreateRole(f, ioStreams))
152 cmd.AddCommand(NewCmdCreateRoleBinding(f, ioStreams))
153 cmd.AddCommand(NewCmdCreatePodDisruptionBudget(f, ioStreams))
154 cmd.AddCommand(NewCmdCreatePriorityClass(f, ioStreams))
155 cmd.AddCommand(NewCmdCreateJob(f, ioStreams))
156 cmd.AddCommand(NewCmdCreateCronJob(f, ioStreams))
157 cmd.AddCommand(NewCmdCreateIngress(f, ioStreams))
158 cmd.AddCommand(NewCmdCreateToken(f, ioStreams))
159 return cmd
160 }
161
162
163 func (o *CreateOptions) Validate() error {
164 if len(o.Raw) > 0 {
165 if o.EditBeforeCreate {
166 return fmt.Errorf("--raw and --edit are mutually exclusive")
167 }
168 if len(o.FilenameOptions.Filenames) != 1 {
169 return fmt.Errorf("--raw can only use a single local file or stdin")
170 }
171 if strings.Index(o.FilenameOptions.Filenames[0], "http://") == 0 || strings.Index(o.FilenameOptions.Filenames[0], "https://") == 0 {
172 return fmt.Errorf("--raw cannot read from a url")
173 }
174 if o.FilenameOptions.Recursive {
175 return fmt.Errorf("--raw and --recursive are mutually exclusive")
176 }
177 if len(o.Selector) > 0 {
178 return fmt.Errorf("--raw and --selector (-l) are mutually exclusive")
179 }
180 if o.PrintFlags.OutputFormat != nil && len(*o.PrintFlags.OutputFormat) > 0 {
181 return fmt.Errorf("--raw and --output are mutually exclusive")
182 }
183 if _, err := url.ParseRequestURI(o.Raw); err != nil {
184 return fmt.Errorf("--raw must be a valid URL path: %v", err)
185 }
186 }
187
188 return nil
189 }
190
191
192 func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
193 if len(args) != 0 {
194 return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
195 }
196 var err error
197 o.RecordFlags.Complete(cmd)
198 o.Recorder, err = o.RecordFlags.ToRecorder()
199 if err != nil {
200 return err
201 }
202
203 o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
204 if err != nil {
205 return err
206 }
207 cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
208
209 o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
210 if err != nil {
211 return err
212 }
213
214 printer, err := o.PrintFlags.ToPrinter()
215 if err != nil {
216 return err
217 }
218
219 o.PrintObj = func(obj kruntime.Object) error {
220 return printer.PrintObj(obj, o.Out)
221 }
222
223 return nil
224 }
225
226
227 func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error {
228
229
230 if len(o.Raw) > 0 {
231 restClient, err := f.RESTClient()
232 if err != nil {
233 return err
234 }
235 return rawhttp.RawPost(restClient, o.IOStreams, o.Raw, o.FilenameOptions.Filenames[0])
236 }
237
238 if o.EditBeforeCreate {
239 return RunEditOnCreate(f, o.PrintFlags, o.RecordFlags, o.IOStreams, cmd, &o.FilenameOptions, o.fieldManager)
240 }
241
242 schema, err := f.Validator(o.ValidationDirective)
243 if err != nil {
244 return err
245 }
246
247 cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
248 if err != nil {
249 return err
250 }
251
252 r := f.NewBuilder().
253 Unstructured().
254 Schema(schema).
255 ContinueOnError().
256 NamespaceParam(cmdNamespace).DefaultNamespace().
257 FilenameParam(enforceNamespace, &o.FilenameOptions).
258 LabelSelectorParam(o.Selector).
259 Flatten().
260 Do()
261 err = r.Err()
262 if err != nil {
263 return err
264 }
265
266 count := 0
267 err = r.Visit(func(info *resource.Info, err error) error {
268 if err != nil {
269 return err
270 }
271 if err := util.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info.Object, scheme.DefaultJSONEncoder()); err != nil {
272 return cmdutil.AddSourceToErr("creating", info.Source, err)
273 }
274
275 if err := o.Recorder.Record(info.Object); err != nil {
276 klog.V(4).Infof("error recording current command: %v", err)
277 }
278
279 if o.DryRunStrategy != cmdutil.DryRunClient {
280 obj, err := resource.
281 NewHelper(info.Client, info.Mapping).
282 DryRun(o.DryRunStrategy == cmdutil.DryRunServer).
283 WithFieldManager(o.fieldManager).
284 WithFieldValidation(o.ValidationDirective).
285 Create(info.Namespace, true, info.Object)
286 if err != nil {
287 return cmdutil.AddSourceToErr("creating", info.Source, err)
288 }
289 info.Refresh(obj, true)
290 }
291
292 count++
293
294 return o.PrintObj(info.Object)
295 })
296 if err != nil {
297 return err
298 }
299 if count == 0 {
300 return fmt.Errorf("no objects passed to create")
301 }
302 return nil
303 }
304
305
306 func RunEditOnCreate(f cmdutil.Factory, printFlags *genericclioptions.PrintFlags, recordFlags *genericclioptions.RecordFlags, ioStreams genericiooptions.IOStreams, cmd *cobra.Command, options *resource.FilenameOptions, fieldManager string) error {
307 editOptions := editor.NewEditOptions(editor.EditBeforeCreateMode, ioStreams)
308 editOptions.FilenameOptions = *options
309 validationDirective, err := cmdutil.GetValidationDirective(cmd)
310 if err != nil {
311 return err
312 }
313 editOptions.ValidateOptions = cmdutil.ValidateOptions{
314 ValidationDirective: string(validationDirective),
315 }
316 editOptions.PrintFlags = printFlags
317 editOptions.ApplyAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
318 editOptions.RecordFlags = recordFlags
319 editOptions.FieldManager = "kubectl-create"
320
321 err = editOptions.Complete(f, []string{}, cmd)
322 if err != nil {
323 return err
324 }
325 return editOptions.Run()
326 }
327
328
329 func NameFromCommandArgs(cmd *cobra.Command, args []string) (string, error) {
330 argsLen := cmd.ArgsLenAtDash()
331
332 if argsLen == -1 {
333 argsLen = len(args)
334 }
335 if argsLen != 1 {
336 return "", cmdutil.UsageErrorf(cmd, "exactly one NAME is required, got %d", argsLen)
337 }
338 return args[0], nil
339 }
340
341
342 type CreateSubcommandOptions struct {
343
344 PrintFlags *genericclioptions.PrintFlags
345
346 Name string
347
348 StructuredGenerator generate.StructuredGenerator
349 DryRunStrategy cmdutil.DryRunStrategy
350 CreateAnnotation bool
351 FieldManager string
352 ValidationDirective string
353
354 Namespace string
355 EnforceNamespace bool
356
357 Mapper meta.RESTMapper
358 DynamicClient dynamic.Interface
359
360 PrintObj printers.ResourcePrinterFunc
361
362 genericiooptions.IOStreams
363 }
364
365
366 func NewCreateSubcommandOptions(ioStreams genericiooptions.IOStreams) *CreateSubcommandOptions {
367 return &CreateSubcommandOptions{
368 PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
369 IOStreams: ioStreams,
370 }
371 }
372
373
374 func (o *CreateSubcommandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string, generator generate.StructuredGenerator) error {
375 name, err := NameFromCommandArgs(cmd, args)
376 if err != nil {
377 return err
378 }
379
380 o.Name = name
381 o.StructuredGenerator = generator
382 o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
383 if err != nil {
384 return err
385 }
386 o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
387
388 cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
389 printer, err := o.PrintFlags.ToPrinter()
390 if err != nil {
391 return err
392 }
393
394 o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
395 if err != nil {
396 return err
397 }
398
399 o.PrintObj = func(obj kruntime.Object, out io.Writer) error {
400 return printer.PrintObj(obj, out)
401 }
402
403 o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
404 if err != nil {
405 return err
406 }
407
408 o.DynamicClient, err = f.DynamicClient()
409 if err != nil {
410 return err
411 }
412
413 o.Mapper, err = f.ToRESTMapper()
414 if err != nil {
415 return err
416 }
417
418 return nil
419 }
420
421
422 func (o *CreateSubcommandOptions) Run() error {
423 obj, err := o.StructuredGenerator.StructuredGenerate()
424 if err != nil {
425 return err
426 }
427 if err := util.CreateOrUpdateAnnotation(o.CreateAnnotation, obj, scheme.DefaultJSONEncoder()); err != nil {
428 return err
429 }
430 if o.DryRunStrategy != cmdutil.DryRunClient {
431
432 gvks, _, err := scheme.Scheme.ObjectKinds(obj)
433 if err != nil {
434 return err
435 }
436 gvk := gvks[0]
437 mapping, err := o.Mapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version)
438 if err != nil {
439 return err
440 }
441
442 asUnstructured := &unstructured.Unstructured{}
443 if err := scheme.Scheme.Convert(obj, asUnstructured, nil); err != nil {
444 return err
445 }
446 if mapping.Scope.Name() == meta.RESTScopeNameRoot {
447 o.Namespace = ""
448 }
449 createOptions := metav1.CreateOptions{}
450 if o.FieldManager != "" {
451 createOptions.FieldManager = o.FieldManager
452 }
453 createOptions.FieldValidation = o.ValidationDirective
454
455 if o.DryRunStrategy == cmdutil.DryRunServer {
456 createOptions.DryRun = []string{metav1.DryRunAll}
457 }
458 actualObject, err := o.DynamicClient.Resource(mapping.Resource).Namespace(o.Namespace).Create(context.TODO(), asUnstructured, createOptions)
459 if err != nil {
460 return err
461 }
462
463
464 obj = actualObject
465 } else {
466 if meta, err := meta.Accessor(obj); err == nil && o.EnforceNamespace {
467 meta.SetNamespace(o.Namespace)
468 }
469 }
470
471 return o.PrintObj(obj, o.Out)
472 }
473
View as plain text