2019-01-12 04:58:27 +00:00
/ *
Copyright 2014 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package create
import (
2020-03-26 21:07:15 +00:00
"context"
2019-01-12 04:58:27 +00:00
"fmt"
"io"
"net/url"
"runtime"
"strings"
"github.com/spf13/cobra"
2020-08-10 17:43:49 +00:00
"k8s.io/klog/v2"
2019-01-12 04:58:27 +00:00
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
kruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/genericclioptions"
2019-04-07 17:07:55 +00:00
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/cli-runtime/pkg/resource"
2019-01-12 04:58:27 +00:00
"k8s.io/client-go/dynamic"
2019-09-27 21:51:53 +00:00
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/cmd/util/editor"
"k8s.io/kubectl/pkg/generate"
"k8s.io/kubectl/pkg/rawhttp"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
2019-01-12 04:58:27 +00:00
)
2019-04-07 17:07:55 +00:00
// CreateOptions is the commandline options for 'create' sub command
2019-01-12 04:58:27 +00:00
type CreateOptions struct {
PrintFlags * genericclioptions . PrintFlags
RecordFlags * genericclioptions . RecordFlags
2020-03-26 21:07:15 +00:00
DryRunStrategy cmdutil . DryRunStrategy
DryRunVerifier * resource . DryRunVerifier
2019-01-12 04:58:27 +00:00
2020-08-10 17:43:49 +00:00
fieldManager string
2019-01-12 04:58:27 +00:00
FilenameOptions resource . FilenameOptions
Selector string
EditBeforeCreate bool
Raw string
Recorder genericclioptions . Recorder
PrintObj func ( obj kruntime . Object ) error
genericclioptions . IOStreams
}
var (
createLong = templates . LongDesc ( i18n . T ( `
Create a resource from a file or from stdin .
JSON and YAML formats are accepted . ` ) )
createExample = templates . Examples ( i18n . T ( `
# Create a pod using the data in pod . json .
kubectl create - f . / pod . json
# Create a pod based on the JSON passed into stdin .
cat pod . json | kubectl create - f -
# Edit the data in docker - registry . yaml in JSON then create the resource using the edited data .
kubectl create - f docker - registry . yaml -- edit - o json ` ) )
)
2019-04-07 17:07:55 +00:00
// NewCreateOptions returns an initialized CreateOptions instance
2019-01-12 04:58:27 +00:00
func NewCreateOptions ( ioStreams genericclioptions . IOStreams ) * CreateOptions {
return & CreateOptions {
PrintFlags : genericclioptions . NewPrintFlags ( "created" ) . WithTypeSetter ( scheme . Scheme ) ,
RecordFlags : genericclioptions . NewRecordFlags ( ) ,
Recorder : genericclioptions . NoopRecorder { } ,
IOStreams : ioStreams ,
}
}
2019-04-07 17:07:55 +00:00
// NewCmdCreate returns new initialized instance of create sub command
2019-01-12 04:58:27 +00:00
func NewCmdCreate ( f cmdutil . Factory , ioStreams genericclioptions . IOStreams ) * cobra . Command {
o := NewCreateOptions ( ioStreams )
cmd := & cobra . Command {
Use : "create -f FILENAME" ,
DisableFlagsInUseLine : true ,
Short : i18n . T ( "Create a resource from a file or from stdin." ) ,
Long : createLong ,
Example : createExample ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
2019-08-30 18:33:25 +00:00
if cmdutil . IsFilenameSliceEmpty ( o . FilenameOptions . Filenames , o . FilenameOptions . Kustomize ) {
ioStreams . ErrOut . Write ( [ ] byte ( "Error: must specify one of -f and -k\n\n" ) )
2019-01-12 04:58:27 +00:00
defaultRunFunc := cmdutil . DefaultSubCommandRun ( ioStreams . ErrOut )
defaultRunFunc ( cmd , args )
return
}
cmdutil . CheckErr ( o . Complete ( f , cmd ) )
cmdutil . CheckErr ( o . ValidateArgs ( cmd , args ) )
cmdutil . CheckErr ( o . RunCreate ( f , cmd ) )
} ,
}
// bind flag structs
o . RecordFlags . AddFlags ( cmd )
usage := "to use to create the resource"
cmdutil . AddFilenameOptionFlags ( cmd , & o . FilenameOptions , usage )
cmdutil . AddValidateFlags ( cmd )
cmd . Flags ( ) . BoolVar ( & o . EditBeforeCreate , "edit" , o . EditBeforeCreate , "Edit the API resource before creating" )
cmd . Flags ( ) . Bool ( "windows-line-endings" , runtime . GOOS == "windows" ,
"Only relevant if --edit=true. Defaults to the line ending native to your platform." )
cmdutil . AddApplyAnnotationFlags ( cmd )
cmdutil . AddDryRunFlag ( cmd )
cmd . Flags ( ) . StringVarP ( & o . Selector , "selector" , "l" , o . Selector , "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)" )
cmd . Flags ( ) . StringVar ( & o . Raw , "raw" , o . Raw , "Raw URI to POST to the server. Uses the transport specified by the kubeconfig file." )
2020-08-10 17:43:49 +00:00
cmdutil . AddFieldManagerFlagVar ( cmd , & o . fieldManager , "kubectl-create" )
2019-01-12 04:58:27 +00:00
o . PrintFlags . AddFlags ( cmd )
// create subcommands
cmd . AddCommand ( NewCmdCreateNamespace ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateQuota ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateSecret ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateConfigMap ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateServiceAccount ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateService ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateDeployment ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateClusterRole ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateClusterRoleBinding ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateRole ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateRoleBinding ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreatePodDisruptionBudget ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreatePriorityClass ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCreateJob ( f , ioStreams ) )
2019-04-07 17:07:55 +00:00
cmd . AddCommand ( NewCmdCreateCronJob ( f , ioStreams ) )
2020-12-01 01:06:26 +00:00
cmd . AddCommand ( NewCmdCreateIngress ( f , ioStreams ) )
2019-01-12 04:58:27 +00:00
return cmd
}
2019-04-07 17:07:55 +00:00
// ValidateArgs makes sure there is no discrepency in command options
2019-01-12 04:58:27 +00:00
func ( o * CreateOptions ) ValidateArgs ( cmd * cobra . Command , args [ ] string ) error {
if len ( args ) != 0 {
return cmdutil . UsageErrorf ( cmd , "Unexpected args: %v" , args )
}
if len ( o . Raw ) > 0 {
if o . EditBeforeCreate {
return cmdutil . UsageErrorf ( cmd , "--raw and --edit are mutually exclusive" )
}
if len ( o . FilenameOptions . Filenames ) != 1 {
return cmdutil . UsageErrorf ( cmd , "--raw can only use a single local file or stdin" )
}
if strings . Index ( o . FilenameOptions . Filenames [ 0 ] , "http://" ) == 0 || strings . Index ( o . FilenameOptions . Filenames [ 0 ] , "https://" ) == 0 {
return cmdutil . UsageErrorf ( cmd , "--raw cannot read from a url" )
}
if o . FilenameOptions . Recursive {
return cmdutil . UsageErrorf ( cmd , "--raw and --recursive are mutually exclusive" )
}
if len ( o . Selector ) > 0 {
return cmdutil . UsageErrorf ( cmd , "--raw and --selector (-l) are mutually exclusive" )
}
if len ( cmdutil . GetFlagString ( cmd , "output" ) ) > 0 {
return cmdutil . UsageErrorf ( cmd , "--raw and --output are mutually exclusive" )
}
if _ , err := url . ParseRequestURI ( o . Raw ) ; err != nil {
return cmdutil . UsageErrorf ( cmd , "--raw must be a valid URL path: %v" , err )
}
}
return nil
}
2019-04-07 17:07:55 +00:00
// Complete completes all the required options
2019-01-12 04:58:27 +00:00
func ( o * CreateOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command ) error {
var err error
o . RecordFlags . Complete ( cmd )
o . Recorder , err = o . RecordFlags . ToRecorder ( )
if err != nil {
return err
}
2020-03-26 21:07:15 +00:00
o . DryRunStrategy , err = cmdutil . GetDryRunStrategy ( cmd )
if err != nil {
return err
}
cmdutil . PrintFlagsWithDryRunStrategy ( o . PrintFlags , o . DryRunStrategy )
dynamicClient , err := f . DynamicClient ( )
if err != nil {
return err
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
discoveryClient , err := f . ToDiscoveryClient ( )
if err != nil {
return err
}
o . DryRunVerifier = resource . NewDryRunVerifier ( dynamicClient , discoveryClient )
2019-01-12 04:58:27 +00:00
printer , err := o . PrintFlags . ToPrinter ( )
if err != nil {
return err
}
o . PrintObj = func ( obj kruntime . Object ) error {
return printer . PrintObj ( obj , o . Out )
}
return nil
}
2019-04-07 17:07:55 +00:00
// RunCreate performs the creation
2019-01-12 04:58:27 +00:00
func ( o * CreateOptions ) RunCreate ( f cmdutil . Factory , cmd * cobra . Command ) error {
// raw only makes sense for a single file resource multiple objects aren't likely to do what you want.
// the validator enforces this, so
if len ( o . Raw ) > 0 {
2019-09-27 21:51:53 +00:00
restClient , err := f . RESTClient ( )
if err != nil {
return err
}
return rawhttp . RawPost ( restClient , o . IOStreams , o . Raw , o . FilenameOptions . Filenames [ 0 ] )
2019-01-12 04:58:27 +00:00
}
if o . EditBeforeCreate {
2020-08-10 17:43:49 +00:00
return RunEditOnCreate ( f , o . PrintFlags , o . RecordFlags , o . IOStreams , cmd , & o . FilenameOptions , o . fieldManager )
2019-01-12 04:58:27 +00:00
}
schema , err := f . Validator ( cmdutil . GetFlagBool ( cmd , "validate" ) )
if err != nil {
return err
}
cmdNamespace , enforceNamespace , err := f . ToRawKubeConfigLoader ( ) . Namespace ( )
if err != nil {
return err
}
r := f . NewBuilder ( ) .
Unstructured ( ) .
Schema ( schema ) .
ContinueOnError ( ) .
NamespaceParam ( cmdNamespace ) . DefaultNamespace ( ) .
FilenameParam ( enforceNamespace , & o . FilenameOptions ) .
LabelSelectorParam ( o . Selector ) .
Flatten ( ) .
Do ( )
err = r . Err ( )
if err != nil {
return err
}
count := 0
err = r . Visit ( func ( info * resource . Info , err error ) error {
if err != nil {
return err
}
2019-09-27 21:51:53 +00:00
if err := util . CreateOrUpdateAnnotation ( cmdutil . GetFlagBool ( cmd , cmdutil . ApplyAnnotationsFlag ) , info . Object , scheme . DefaultJSONEncoder ( ) ) ; err != nil {
2019-01-12 04:58:27 +00:00
return cmdutil . AddSourceToErr ( "creating" , info . Source , err )
}
if err := o . Recorder . Record ( info . Object ) ; err != nil {
klog . V ( 4 ) . Infof ( "error recording current command: %v" , err )
}
2020-03-26 21:07:15 +00:00
if o . DryRunStrategy != cmdutil . DryRunClient {
if o . DryRunStrategy == cmdutil . DryRunServer {
if err := o . DryRunVerifier . HasSupport ( info . Mapping . GroupVersionKind ) ; err != nil {
return cmdutil . AddSourceToErr ( "creating" , info . Source , err )
}
}
obj , err := resource .
NewHelper ( info . Client , info . Mapping ) .
DryRun ( o . DryRunStrategy == cmdutil . DryRunServer ) .
2020-08-10 17:43:49 +00:00
WithFieldManager ( o . fieldManager ) .
2020-03-26 21:07:15 +00:00
Create ( info . Namespace , true , info . Object )
if err != nil {
2019-01-12 04:58:27 +00:00
return cmdutil . AddSourceToErr ( "creating" , info . Source , err )
}
2020-03-26 21:07:15 +00:00
info . Refresh ( obj , true )
2019-01-12 04:58:27 +00:00
}
count ++
return o . PrintObj ( info . Object )
} )
if err != nil {
return err
}
if count == 0 {
return fmt . Errorf ( "no objects passed to create" )
}
return nil
}
2019-04-07 17:07:55 +00:00
// RunEditOnCreate performs edit on creation
2020-08-10 17:43:49 +00:00
func RunEditOnCreate ( f cmdutil . Factory , printFlags * genericclioptions . PrintFlags , recordFlags * genericclioptions . RecordFlags , ioStreams genericclioptions . IOStreams , cmd * cobra . Command , options * resource . FilenameOptions , fieldManager string ) error {
2019-01-12 04:58:27 +00:00
editOptions := editor . NewEditOptions ( editor . EditBeforeCreateMode , ioStreams )
editOptions . FilenameOptions = * options
editOptions . ValidateOptions = cmdutil . ValidateOptions {
EnableValidation : cmdutil . GetFlagBool ( cmd , "validate" ) ,
}
editOptions . PrintFlags = printFlags
editOptions . ApplyAnnotation = cmdutil . GetFlagBool ( cmd , cmdutil . ApplyAnnotationsFlag )
editOptions . RecordFlags = recordFlags
2020-08-10 17:43:49 +00:00
editOptions . FieldManager = "kubectl-create"
2019-01-12 04:58:27 +00:00
err := editOptions . Complete ( f , [ ] string { } , cmd )
if err != nil {
return err
}
return editOptions . Run ( )
}
// NameFromCommandArgs is a utility function for commands that assume the first argument is a resource name
func NameFromCommandArgs ( cmd * cobra . Command , args [ ] string ) ( string , error ) {
argsLen := cmd . ArgsLenAtDash ( )
// ArgsLenAtDash returns -1 when -- was not specified
if argsLen == - 1 {
argsLen = len ( args )
}
if argsLen != 1 {
return "" , cmdutil . UsageErrorf ( cmd , "exactly one NAME is required, got %d" , argsLen )
}
return args [ 0 ] , nil
}
// CreateSubcommandOptions is an options struct to support create subcommands
type CreateSubcommandOptions struct {
// PrintFlags holds options necessary for obtaining a printer
PrintFlags * genericclioptions . PrintFlags
// Name of resource being created
Name string
// StructuredGenerator is the resource generator for the object being created
StructuredGenerator generate . StructuredGenerator
2020-03-26 21:07:15 +00:00
DryRunStrategy cmdutil . DryRunStrategy
DryRunVerifier * resource . DryRunVerifier
CreateAnnotation bool
2020-08-10 17:43:49 +00:00
FieldManager string
2019-01-12 04:58:27 +00:00
Namespace string
EnforceNamespace bool
Mapper meta . RESTMapper
DynamicClient dynamic . Interface
PrintObj printers . ResourcePrinterFunc
genericclioptions . IOStreams
}
2019-04-07 17:07:55 +00:00
// NewCreateSubcommandOptions returns initialized CreateSubcommandOptions
2019-01-12 04:58:27 +00:00
func NewCreateSubcommandOptions ( ioStreams genericclioptions . IOStreams ) * CreateSubcommandOptions {
return & CreateSubcommandOptions {
PrintFlags : genericclioptions . NewPrintFlags ( "created" ) . WithTypeSetter ( scheme . Scheme ) ,
IOStreams : ioStreams ,
}
}
2019-04-07 17:07:55 +00:00
// Complete completes all the required options
2019-01-12 04:58:27 +00:00
func ( o * CreateSubcommandOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command , args [ ] string , generator generate . StructuredGenerator ) error {
name , err := NameFromCommandArgs ( cmd , args )
if err != nil {
return err
}
o . Name = name
o . StructuredGenerator = generator
2020-03-26 21:07:15 +00:00
o . DryRunStrategy , err = cmdutil . GetDryRunStrategy ( cmd )
if err != nil {
return err
}
dynamicClient , err := f . DynamicClient ( )
if err != nil {
return err
}
discoveryClient , err := f . ToDiscoveryClient ( )
if err != nil {
return err
}
o . DryRunVerifier = resource . NewDryRunVerifier ( dynamicClient , discoveryClient )
2019-01-12 04:58:27 +00:00
o . CreateAnnotation = cmdutil . GetFlagBool ( cmd , cmdutil . ApplyAnnotationsFlag )
2020-03-26 21:07:15 +00:00
cmdutil . PrintFlagsWithDryRunStrategy ( o . PrintFlags , o . DryRunStrategy )
2019-01-12 04:58:27 +00:00
printer , err := o . PrintFlags . ToPrinter ( )
if err != nil {
return err
}
o . PrintObj = func ( obj kruntime . Object , out io . Writer ) error {
return printer . PrintObj ( obj , out )
}
o . Namespace , o . EnforceNamespace , err = f . ToRawKubeConfigLoader ( ) . Namespace ( )
if err != nil {
return err
}
o . DynamicClient , err = f . DynamicClient ( )
if err != nil {
return err
}
o . Mapper , err = f . ToRESTMapper ( )
if err != nil {
return err
}
return nil
}
2019-04-07 17:07:55 +00:00
// Run executes a create subcommand using the specified options
2019-01-12 04:58:27 +00:00
func ( o * CreateSubcommandOptions ) Run ( ) error {
obj , err := o . StructuredGenerator . StructuredGenerate ( )
if err != nil {
return err
}
2020-08-10 17:43:49 +00:00
if err := util . CreateOrUpdateAnnotation ( o . CreateAnnotation , obj , scheme . DefaultJSONEncoder ( ) ) ; err != nil {
return err
}
2020-03-26 21:07:15 +00:00
if o . DryRunStrategy != cmdutil . DryRunClient {
2019-01-12 04:58:27 +00:00
// create subcommands have compiled knowledge of things they create, so type them directly
gvks , _ , err := scheme . Scheme . ObjectKinds ( obj )
if err != nil {
return err
}
gvk := gvks [ 0 ]
mapping , err := o . Mapper . RESTMapping ( schema . GroupKind { Group : gvk . Group , Kind : gvk . Kind } , gvk . Version )
if err != nil {
return err
}
asUnstructured := & unstructured . Unstructured { }
if err := scheme . Scheme . Convert ( obj , asUnstructured , nil ) ; err != nil {
return err
}
if mapping . Scope . Name ( ) == meta . RESTScopeNameRoot {
o . Namespace = ""
}
2020-03-26 21:07:15 +00:00
createOptions := metav1 . CreateOptions { }
2020-08-10 17:43:49 +00:00
if o . FieldManager != "" {
createOptions . FieldManager = o . FieldManager
}
2020-03-26 21:07:15 +00:00
if o . DryRunStrategy == cmdutil . DryRunServer {
if err := o . DryRunVerifier . HasSupport ( mapping . GroupVersionKind ) ; err != nil {
return err
}
createOptions . DryRun = [ ] string { metav1 . DryRunAll }
}
actualObject , err := o . DynamicClient . Resource ( mapping . Resource ) . Namespace ( o . Namespace ) . Create ( context . TODO ( ) , asUnstructured , createOptions )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
// ensure we pass a versioned object to the printer
obj = actualObject
} else {
if meta , err := meta . Accessor ( obj ) ; err == nil && o . EnforceNamespace {
meta . SetNamespace ( o . Namespace )
}
}
return o . PrintObj ( obj , o . Out )
}