add server-side validation support to kubectl

This commit is contained in:
Kevin Delgado 2022-03-09 14:52:32 +00:00
parent 083c3ac4e8
commit fe3772890f
39 changed files with 715 additions and 139 deletions

View File

@ -0,0 +1,24 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: invalid-nginx-deployment
labels:
app: nginx
spec:
replicas: 3
replicas: 4
foo: bar
baz: baq
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

View File

@ -124,7 +124,16 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) (err er
} }
o.validator = func() (validation.Schema, error) { o.validator = func() (validation.Schema, error) {
return f.Validator(cmdutil.GetFlagBool(cmd, "validate")) directive, err := cmdutil.GetValidationDirective(cmd)
if err != nil {
return nil, err
}
dynamicClient, err := f.DynamicClient()
if err != nil {
return nil, err
}
verifier := resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamFieldValidation)
return f.Validator(directive, verifier)
} }
// build the printer // build the printer

View File

@ -54,6 +54,10 @@ type Helper struct {
// FieldManager is the name associated with the actor or entity that is making // FieldManager is the name associated with the actor or entity that is making
// changes. // changes.
FieldManager string FieldManager string
// FieldValidation is the directive used to indicate how the server should perform
// field validation (Ignore, Warn, or Strict)
FieldValidation string
} }
// NewHelper creates a Helper from a ResourceMapping // NewHelper creates a Helper from a ResourceMapping
@ -79,6 +83,13 @@ func (m *Helper) WithFieldManager(fieldManager string) *Helper {
return m return m
} }
// WithFieldValidation sets the field validation option to indicate
// how the server should perform field validation (Ignore, Warn, or Strict).
func (m *Helper) WithFieldValidation(validationDirective string) *Helper {
m.FieldValidation = validationDirective
return m
}
// Subresource sets the helper to access (<resource>/[ns/<namespace>/]<name>/<subresource>) // Subresource sets the helper to access (<resource>/[ns/<namespace>/]<name>/<subresource>)
func (m *Helper) WithSubresource(subresource string) *Helper { func (m *Helper) WithSubresource(subresource string) *Helper {
m.Subresource = subresource m.Subresource = subresource
@ -206,6 +217,9 @@ func (m *Helper) CreateWithOptions(namespace string, modify bool, obj runtime.Ob
if m.FieldManager != "" { if m.FieldManager != "" {
options.FieldManager = m.FieldManager options.FieldManager = m.FieldManager
} }
if m.FieldValidation != "" {
options.FieldValidation = m.FieldValidation
}
if modify { if modify {
// Attempt to version the object based on client logic. // Attempt to version the object based on client logic.
version, err := metadataAccessor.ResourceVersion(obj) version, err := metadataAccessor.ResourceVersion(obj)
@ -242,6 +256,9 @@ func (m *Helper) Patch(namespace, name string, pt types.PatchType, data []byte,
if m.FieldManager != "" { if m.FieldManager != "" {
options.FieldManager = m.FieldManager options.FieldManager = m.FieldManager
} }
if m.FieldValidation != "" {
options.FieldValidation = m.FieldValidation
}
return m.RESTClient.Patch(pt). return m.RESTClient.Patch(pt).
NamespaceIfScoped(namespace, m.NamespaceScoped). NamespaceIfScoped(namespace, m.NamespaceScoped).
Resource(m.Resource). Resource(m.Resource).
@ -262,6 +279,9 @@ func (m *Helper) Replace(namespace, name string, overwrite bool, obj runtime.Obj
if m.FieldManager != "" { if m.FieldManager != "" {
options.FieldManager = m.FieldManager options.FieldManager = m.FieldManager
} }
if m.FieldValidation != "" {
options.FieldValidation = m.FieldValidation
}
// Attempt to version the object based on client logic. // Attempt to version the object based on client logic.
version, err := metadataAccessor.ResourceVersion(obj) version, err := metadataAccessor.ResourceVersion(obj)

View File

@ -62,6 +62,11 @@ type QueryParamVerifier struct {
queryParam VerifiableQueryParam queryParam VerifiableQueryParam
} }
// Verifier is the generic verifier interface used for testing QueryParamVerifier
type Verifier interface {
HasSupport(gvk schema.GroupVersionKind) error
}
// VerifiableQueryParam is a query parameter who's enablement on the // VerifiableQueryParam is a query parameter who's enablement on the
// apiserver can be determined by evaluating the OpenAPI for a specific // apiserver can be determined by evaluating the OpenAPI for a specific
// GVK. // GVK.
@ -72,6 +77,7 @@ const (
QueryParamFieldValidation VerifiableQueryParam = "fieldValidation" QueryParamFieldValidation VerifiableQueryParam = "fieldValidation"
) )
// HasSupport checks if the given gvk supports the query param configured on v
func (v *QueryParamVerifier) HasSupport(gvk schema.GroupVersionKind) error { func (v *QueryParamVerifier) HasSupport(gvk schema.GroupVersionKind) error {
oapi, err := v.openAPIGetter.OpenAPISchema() oapi, err := v.openAPIGetter.OpenAPISchema()
if err != nil { if err != nil {
@ -90,7 +96,7 @@ func (v *QueryParamVerifier) HasSupport(gvk schema.GroupVersionKind) error {
} }
} }
if !supports { if !supports {
return newParamUnsupportedError(gvk, v.queryParam) return NewParamUnsupportedError(gvk, v.queryParam)
} }
return nil return nil
} }
@ -100,7 +106,7 @@ type paramUnsupportedError struct {
param VerifiableQueryParam param VerifiableQueryParam
} }
func newParamUnsupportedError(gvk schema.GroupVersionKind, param VerifiableQueryParam) error { func NewParamUnsupportedError(gvk schema.GroupVersionKind, param VerifiableQueryParam) error {
return &paramUnsupportedError{ return &paramUnsupportedError{
gvk: gvk, gvk: gvk,
param: param, param: param,

View File

@ -80,25 +80,27 @@ type ApplyOptions struct {
DeleteOptions *delete.DeleteOptions DeleteOptions *delete.DeleteOptions
ServerSideApply bool ServerSideApply bool
ForceConflicts bool ForceConflicts bool
FieldManager string FieldManager string
Selector string Selector string
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
Prune bool FieldValidationVerifier *resource.QueryParamVerifier
PruneResources []prune.Resource Prune bool
cmdBaseName string PruneResources []prune.Resource
All bool cmdBaseName string
Overwrite bool All bool
OpenAPIPatch bool Overwrite bool
PruneWhitelist []string OpenAPIPatch bool
PruneWhitelist []string
Validator validation.Schema ValidationDirective string
Builder *resource.Builder Validator validation.Schema
Mapper meta.RESTMapper Builder *resource.Builder
DynamicClient dynamic.Interface Mapper meta.RESTMapper
OpenAPISchema openapi.Resources DynamicClient dynamic.Interface
OpenAPISchema openapi.Resources
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
@ -238,6 +240,7 @@ func (flags *ApplyFlags) ToOptions(cmd *cobra.Command, baseName string, args []s
} }
dryRunVerifier := resource.NewQueryParamVerifier(dynamicClient, flags.Factory.OpenAPIGetter(), resource.QueryParamDryRun) dryRunVerifier := resource.NewQueryParamVerifier(dynamicClient, flags.Factory.OpenAPIGetter(), resource.QueryParamDryRun)
fieldValidationVerifier := resource.NewQueryParamVerifier(dynamicClient, flags.Factory.OpenAPIGetter(), resource.QueryParamFieldValidation)
fieldManager := GetApplyFieldManagerFlag(cmd, serverSideApply) fieldManager := GetApplyFieldManagerFlag(cmd, serverSideApply)
// allow for a success message operation to be specified at print time // allow for a success message operation to be specified at print time
@ -264,7 +267,12 @@ func (flags *ApplyFlags) ToOptions(cmd *cobra.Command, baseName string, args []s
} }
openAPISchema, _ := flags.Factory.OpenAPISchema() openAPISchema, _ := flags.Factory.OpenAPISchema()
validator, err := flags.Factory.Validator(cmdutil.GetFlagBool(cmd, "validate"))
validationDirective, err := cmdutil.GetValidationDirective(cmd)
if err != nil {
return nil, err
}
validator, err := flags.Factory.Validator(validationDirective, fieldValidationVerifier)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -308,14 +316,15 @@ func (flags *ApplyFlags) ToOptions(cmd *cobra.Command, baseName string, args []s
OpenAPIPatch: flags.OpenAPIPatch, OpenAPIPatch: flags.OpenAPIPatch,
PruneWhitelist: flags.PruneWhitelist, PruneWhitelist: flags.PruneWhitelist,
Recorder: recorder, Recorder: recorder,
Namespace: namespace, Namespace: namespace,
EnforceNamespace: enforceNamespace, EnforceNamespace: enforceNamespace,
Validator: validator, Validator: validator,
Builder: builder, ValidationDirective: validationDirective,
Mapper: mapper, Builder: builder,
DynamicClient: dynamicClient, Mapper: mapper,
OpenAPISchema: openAPISchema, DynamicClient: dynamicClient,
OpenAPISchema: openAPISchema,
IOStreams: flags.IOStreams, IOStreams: flags.IOStreams,
@ -407,7 +416,6 @@ func (o *ApplyOptions) SetObjects(infos []*resource.Info) {
// Run executes the `apply` command. // Run executes the `apply` command.
func (o *ApplyOptions) Run() error { func (o *ApplyOptions) Run() error {
if o.PreProcessorFn != nil { if o.PreProcessorFn != nil {
klog.V(4).Infof("Running apply pre-processor function") klog.V(4).Infof("Running apply pre-processor function")
if err := o.PreProcessorFn(); err != nil { if err := o.PreProcessorFn(); err != nil {
@ -472,7 +480,8 @@ func (o *ApplyOptions) applyOneObject(info *resource.Info) error {
helper := resource.NewHelper(info.Client, info.Mapping). helper := resource.NewHelper(info.Client, info.Mapping).
DryRun(o.DryRunStrategy == cmdutil.DryRunServer). DryRun(o.DryRunStrategy == cmdutil.DryRunServer).
WithFieldManager(o.FieldManager) WithFieldManager(o.FieldManager).
WithFieldValidation(o.ValidationDirective)
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
// Ensure the APIServer supports server-side dry-run for the resource, // Ensure the APIServer supports server-side dry-run for the resource,

View File

@ -83,6 +83,7 @@ func NewCmdApplyEditLastApplied(f cmdutil.Factory, ioStreams genericclioptions.I
cmd.Flags().BoolVar(&o.WindowsLineEndings, "windows-line-endings", o.WindowsLineEndings, cmd.Flags().BoolVar(&o.WindowsLineEndings, "windows-line-endings", o.WindowsLineEndings,
"Defaults to the line ending native to your platform.") "Defaults to the line ending native to your platform.")
cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, FieldManagerClientSideApply) cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, FieldManagerClientSideApply)
cmdutil.AddValidateFlags(cmd)
return cmd return cmd
} }

View File

@ -51,8 +51,11 @@ type CreateOptions struct {
PrintFlags *genericclioptions.PrintFlags PrintFlags *genericclioptions.PrintFlags
RecordFlags *genericclioptions.RecordFlags RecordFlags *genericclioptions.RecordFlags
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
FieldValidationVerifier *resource.QueryParamVerifier
ValidationDirective string
fieldManager string fieldManager string
@ -208,6 +211,12 @@ func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
return err return err
} }
o.DryRunVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamDryRun) o.DryRunVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamDryRun)
o.FieldValidationVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamFieldValidation)
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
printer, err := o.PrintFlags.ToPrinter() printer, err := o.PrintFlags.ToPrinter()
if err != nil { if err != nil {
@ -236,7 +245,8 @@ func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error {
if o.EditBeforeCreate { if o.EditBeforeCreate {
return RunEditOnCreate(f, o.PrintFlags, o.RecordFlags, o.IOStreams, cmd, &o.FilenameOptions, o.fieldManager) return RunEditOnCreate(f, o.PrintFlags, o.RecordFlags, o.IOStreams, cmd, &o.FilenameOptions, o.fieldManager)
} }
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
schema, err := f.Validator(o.ValidationDirective, o.FieldValidationVerifier)
if err != nil { if err != nil {
return err return err
} }
@ -283,6 +293,7 @@ func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error {
NewHelper(info.Client, info.Mapping). NewHelper(info.Client, info.Mapping).
DryRun(o.DryRunStrategy == cmdutil.DryRunServer). DryRun(o.DryRunStrategy == cmdutil.DryRunServer).
WithFieldManager(o.fieldManager). WithFieldManager(o.fieldManager).
WithFieldValidation(o.ValidationDirective).
Create(info.Namespace, true, info.Object) Create(info.Namespace, true, info.Object)
if err != nil { if err != nil {
return cmdutil.AddSourceToErr("creating", info.Source, err) return cmdutil.AddSourceToErr("creating", info.Source, err)
@ -307,15 +318,19 @@ func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error {
func RunEditOnCreate(f cmdutil.Factory, printFlags *genericclioptions.PrintFlags, recordFlags *genericclioptions.RecordFlags, ioStreams genericclioptions.IOStreams, cmd *cobra.Command, options *resource.FilenameOptions, fieldManager string) error { func RunEditOnCreate(f cmdutil.Factory, printFlags *genericclioptions.PrintFlags, recordFlags *genericclioptions.RecordFlags, ioStreams genericclioptions.IOStreams, cmd *cobra.Command, options *resource.FilenameOptions, fieldManager string) error {
editOptions := editor.NewEditOptions(editor.EditBeforeCreateMode, ioStreams) editOptions := editor.NewEditOptions(editor.EditBeforeCreateMode, ioStreams)
editOptions.FilenameOptions = *options editOptions.FilenameOptions = *options
validationDirective, err := cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
editOptions.ValidateOptions = cmdutil.ValidateOptions{ editOptions.ValidateOptions = cmdutil.ValidateOptions{
EnableValidation: cmdutil.GetFlagBool(cmd, "validate"), ValidationDirective: string(validationDirective),
} }
editOptions.PrintFlags = printFlags editOptions.PrintFlags = printFlags
editOptions.ApplyAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag) editOptions.ApplyAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
editOptions.RecordFlags = recordFlags editOptions.RecordFlags = recordFlags
editOptions.FieldManager = "kubectl-create" editOptions.FieldManager = "kubectl-create"
err := editOptions.Complete(f, []string{}, cmd) err = editOptions.Complete(f, []string{}, cmd)
if err != nil { if err != nil {
return err return err
} }
@ -347,6 +362,7 @@ type CreateSubcommandOptions struct {
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
CreateAnnotation bool CreateAnnotation bool
FieldManager string FieldManager string
ValidationDirective string
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
@ -393,6 +409,11 @@ func (o *CreateSubcommandOptions) Complete(f cmdutil.Factory, cmd *cobra.Command
return err return err
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
o.PrintObj = func(obj kruntime.Object, out io.Writer) error { o.PrintObj = func(obj kruntime.Object, out io.Writer) error {
return printer.PrintObj(obj, out) return printer.PrintObj(obj, out)
} }
@ -447,6 +468,8 @@ func (o *CreateSubcommandOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(mapping.GroupVersionKind); err != nil { if err := o.DryRunVerifier.HasSupport(mapping.GroupVersionKind); err != nil {
return err return err

View File

@ -213,6 +213,7 @@ func (c *CreateClusterRoleOptions) RunCreateRole() error {
if c.FieldManager != "" { if c.FieldManager != "" {
createOptions.FieldManager = c.FieldManager createOptions.FieldManager = c.FieldManager
} }
createOptions.FieldValidation = c.ValidationDirective
if c.DryRunStrategy == cmdutil.DryRunServer { if c.DryRunStrategy == cmdutil.DryRunServer {
if err := c.DryRunVerifier.HasSupport(clusterRole.GroupVersionKind()); err != nil { if err := c.DryRunVerifier.HasSupport(clusterRole.GroupVersionKind()); err != nil {
return err return err

View File

@ -59,9 +59,10 @@ type ClusterRoleBindingOptions struct {
FieldManager string FieldManager string
CreateAnnotation bool CreateAnnotation bool
Client rbacclientv1.RbacV1Interface Client rbacclientv1.RbacV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -149,6 +150,10 @@ func (o *ClusterRoleBindingOptions) Complete(f cmdutil.Factory, cmd *cobra.Comma
o.PrintObj = func(obj runtime.Object) error { o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -169,6 +174,7 @@ func (o *ClusterRoleBindingOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(clusterRoleBinding.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(clusterRoleBinding.GroupVersionKind()); err != nil {
return err return err

View File

@ -96,9 +96,10 @@ type ConfigMapOptions struct {
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
Client corev1client.CoreV1Interface Client corev1client.CoreV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -194,6 +195,11 @@ func (o *ConfigMapOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -222,6 +228,7 @@ func (o *ConfigMapOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(configMap.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(configMap.GroupVersionKind()); err != nil {
return err return err

View File

@ -60,14 +60,15 @@ type CreateCronJobOptions struct {
Command []string Command []string
Restart string Restart string
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
Client batchv1client.BatchV1Interface Client batchv1client.BatchV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
Builder *resource.Builder ValidationDirective string
FieldManager string Builder *resource.Builder
CreateAnnotation bool FieldManager string
CreateAnnotation bool
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -160,6 +161,11 @@ func (o *CreateCronJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, a
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -175,6 +181,7 @@ func (o *CreateCronJobOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(cronJob.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(cronJob.GroupVersionKind()); err != nil {
return err return err

View File

@ -72,9 +72,10 @@ type CreateDeploymentOptions struct {
FieldManager string FieldManager string
CreateAnnotation bool CreateAnnotation bool
Client appsv1client.AppsV1Interface Client appsv1client.AppsV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -167,6 +168,11 @@ func (o *CreateDeploymentOptions) Complete(f cmdutil.Factory, cmd *cobra.Command
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -191,6 +197,7 @@ func (o *CreateDeploymentOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(deploy.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(deploy.GroupVersionKind()); err != nil {
return err return err

View File

@ -116,9 +116,10 @@ type CreateIngressOptions struct {
EnforceNamespace bool EnforceNamespace bool
CreateAnnotation bool CreateAnnotation bool
Client networkingv1client.NetworkingV1Interface Client networkingv1client.NetworkingV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
FieldManager string FieldManager string
@ -208,6 +209,11 @@ func (o *CreateIngressOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, a
o.PrintObj = func(obj runtime.Object) error { o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -254,6 +260,7 @@ func (o *CreateIngressOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(ingress.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(ingress.GroupVersionKind()); err != nil {
return err return err

View File

@ -62,14 +62,15 @@ type CreateJobOptions struct {
From string From string
Command []string Command []string
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
Client batchv1client.BatchV1Interface Client batchv1client.BatchV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
Builder *resource.Builder ValidationDirective string
FieldManager string Builder *resource.Builder
CreateAnnotation bool FieldManager string
CreateAnnotation bool
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -155,6 +156,11 @@ func (o *CreateJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -207,6 +213,7 @@ func (o *CreateJobOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(job.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(job.GroupVersionKind()); err != nil {
return err return err

View File

@ -52,10 +52,11 @@ type NamespaceOptions struct {
// Name of resource being created // Name of resource being created
Name string Name string
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
CreateAnnotation bool ValidationDirective string
FieldManager string CreateAnnotation bool
FieldManager string
Client *coreclient.CoreV1Client Client *coreclient.CoreV1Client
@ -140,6 +141,11 @@ func (o *NamespaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
o.PrintObj = func(obj runtime.Object) error { o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -155,6 +161,7 @@ func (o *NamespaceOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(namespace.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(namespace.GroupVersionKind()); err != nil {
return err return err

View File

@ -70,9 +70,10 @@ type PodDisruptionBudgetOpts struct {
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
Client *policyv1client.PolicyV1Client Client *policyv1client.PolicyV1Client
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resourcecli.QueryParamVerifier DryRunVerifier *resourcecli.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -165,6 +166,11 @@ func (o *PodDisruptionBudgetOpts) Complete(f cmdutil.Factory, cmd *cobra.Command
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -217,6 +223,7 @@ func (o *PodDisruptionBudgetOpts) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(podDisruptionBudget.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(podDisruptionBudget.GroupVersionKind()); err != nil {
return err return err

View File

@ -64,9 +64,10 @@ type PriorityClassOptions struct {
FieldManager string FieldManager string
CreateAnnotation bool CreateAnnotation bool
Client *schedulingv1client.SchedulingV1Client Client *schedulingv1client.SchedulingV1Client
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -149,6 +150,11 @@ func (o *PriorityClassOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, a
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -168,6 +174,7 @@ func (o *PriorityClassOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(priorityClass.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(priorityClass.GroupVersionKind()); err != nil {
return err return err

View File

@ -66,9 +66,10 @@ type QuotaOpts struct {
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
Client *coreclient.CoreV1Client Client *coreclient.CoreV1Client
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resourcecli.QueryParamVerifier DryRunVerifier *resourcecli.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -155,6 +156,11 @@ func (o *QuotaOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []strin
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -182,6 +188,7 @@ func (o *QuotaOpts) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(resourceQuota.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(resourceQuota.GroupVersionKind()); err != nil {
return err return err

View File

@ -136,16 +136,17 @@ type CreateRoleOptions struct {
Resources []ResourceOptions Resources []ResourceOptions
ResourceNames []string ResourceNames []string
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
OutputFormat string ValidationDirective string
Namespace string OutputFormat string
EnforceNamespace bool Namespace string
Client clientgorbacv1.RbacV1Interface EnforceNamespace bool
Mapper meta.RESTMapper Client clientgorbacv1.RbacV1Interface
PrintObj func(obj runtime.Object) error Mapper meta.RESTMapper
FieldManager string PrintObj func(obj runtime.Object) error
CreateAnnotation bool FieldManager string
CreateAnnotation bool
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -271,6 +272,11 @@ func (o *CreateRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil { if err != nil {
return err return err
@ -374,6 +380,7 @@ func (o *CreateRoleOptions) RunCreateRole() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(role.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(role.GroupVersionKind()); err != nil {
return err return err

View File

@ -61,9 +61,10 @@ type RoleBindingOptions struct {
FieldManager string FieldManager string
CreateAnnotation bool CreateAnnotation bool
Client rbacclientv1.RbacV1Interface Client rbacclientv1.RbacV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -149,6 +150,11 @@ func (o *RoleBindingOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg
o.PrintObj = func(obj runtime.Object) error { o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -178,6 +184,7 @@ func (o *RoleBindingOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(roleBinding.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(roleBinding.GroupVersionKind()); err != nil {
return err return err

View File

@ -112,9 +112,10 @@ type CreateSecretOptions struct {
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
Client corev1client.CoreV1Interface Client corev1client.CoreV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -212,6 +213,11 @@ func (o *CreateSecretOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, ar
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -242,6 +248,7 @@ func (o *CreateSecretOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
err := o.DryRunVerifier.HasSupport(secret.GroupVersionKind()) err := o.DryRunVerifier.HasSupport(secret.GroupVersionKind())
if err != nil { if err != nil {

View File

@ -108,9 +108,10 @@ type CreateSecretDockerRegistryOptions struct {
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
Client corev1client.CoreV1Interface Client corev1client.CoreV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -211,6 +212,11 @@ func (o *CreateSecretDockerRegistryOptions) Complete(f cmdutil.Factory, cmd *cob
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -241,6 +247,7 @@ func (o *CreateSecretDockerRegistryOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
err := o.DryRunVerifier.HasSupport(secretDockerRegistry.GroupVersionKind()) err := o.DryRunVerifier.HasSupport(secretDockerRegistry.GroupVersionKind())
if err != nil { if err != nil {

View File

@ -69,9 +69,10 @@ type CreateSecretTLSOptions struct {
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
Client corev1client.CoreV1Interface Client corev1client.CoreV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -168,6 +169,11 @@ func (o *CreateSecretTLSOptions) Complete(f cmdutil.Factory, cmd *cobra.Command,
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -198,6 +204,7 @@ func (o *CreateSecretTLSOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
err := o.DryRunVerifier.HasSupport(secretTLS.GroupVersionKind()) err := o.DryRunVerifier.HasSupport(secretTLS.GroupVersionKind())
if err != nil { if err != nil {

View File

@ -74,9 +74,10 @@ type ServiceOptions struct {
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
Client corev1client.CoreV1Interface Client corev1client.CoreV1Interface
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -131,6 +132,11 @@ func (o *ServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -214,6 +220,7 @@ func (o *ServiceOptions) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
createOptions.DryRun = []string{metav1.DryRunAll} createOptions.DryRun = []string{metav1.DryRunAll}
} }

View File

@ -51,11 +51,12 @@ type ServiceAccountOpts struct {
PrintFlags *genericclioptions.PrintFlags PrintFlags *genericclioptions.PrintFlags
PrintObj func(obj runtime.Object) error PrintObj func(obj runtime.Object) error
// Name of resource being created // Name of resource being created
Name string Name string
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
CreateAnnotation bool ValidationDirective string
FieldManager string CreateAnnotation bool
FieldManager string
Namespace string Namespace string
EnforceNamespace bool EnforceNamespace bool
@ -146,6 +147,11 @@ func (o *ServiceAccountOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, arg
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
return nil return nil
} }
@ -173,6 +179,7 @@ func (o *ServiceAccountOpts) Run() error {
if o.FieldManager != "" { if o.FieldManager != "" {
createOptions.FieldManager = o.FieldManager createOptions.FieldManager = o.FieldManager
} }
createOptions.FieldValidation = o.ValidationDirective
if o.DryRunStrategy == cmdutil.DryRunServer { if o.DryRunStrategy == cmdutil.DryRunServer {
if err := o.DryRunVerifier.HasSupport(serviceAccount.GroupVersionKind()); err != nil { if err := o.DryRunVerifier.HasSupport(serviceAccount.GroupVersionKind()); err != nil {
return err return err

View File

@ -72,8 +72,6 @@ var (
// NewCmdEdit creates the `edit` command // NewCmdEdit creates the `edit` command
func NewCmdEdit(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command { func NewCmdEdit(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := editor.NewEditOptions(editor.NormalEditMode, ioStreams) o := editor.NewEditOptions(editor.NormalEditMode, ioStreams)
o.ValidateOptions = cmdutil.ValidateOptions{EnableValidation: true}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "edit (RESOURCE/NAME | -f FILENAME)", Use: "edit (RESOURCE/NAME | -f FILENAME)",
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
@ -94,7 +92,7 @@ func NewCmdEdit(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra
usage := "to use to edit the resource" usage := "to use to edit the resource"
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage) cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmdutil.AddValidateOptionFlags(cmd, &o.ValidateOptions) cmdutil.AddValidateFlags(cmd)
cmd.Flags().BoolVarP(&o.OutputPatch, "output-patch", "", o.OutputPatch, "Output the patch if the resource is edited.") cmd.Flags().BoolVarP(&o.OutputPatch, "output-patch", "", o.OutputPatch, "Output the patch if the resource is edited.")
cmd.Flags().BoolVar(&o.WindowsLineEndings, "windows-line-endings", o.WindowsLineEndings, cmd.Flags().BoolVar(&o.WindowsLineEndings, "windows-line-endings", o.WindowsLineEndings,
"Defaults to the line ending native to your platform.") "Defaults to the line ending native to your platform.")

View File

@ -77,13 +77,14 @@ type ReplaceOptions struct {
DeleteFlags *delete.DeleteFlags DeleteFlags *delete.DeleteFlags
DeleteOptions *delete.DeleteOptions DeleteOptions *delete.DeleteOptions
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
FieldValidationVerifier *resource.QueryParamVerifier
validationDirective string
PrintObj func(obj runtime.Object) error PrintObj func(obj runtime.Object) error
createAnnotation bool createAnnotation bool
validate bool
Schema validation.Schema Schema validation.Schema
Builder func() *resource.Builder Builder func() *resource.Builder
@ -151,7 +152,10 @@ func (o *ReplaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
return err return err
} }
o.validate = cmdutil.GetFlagBool(cmd, "validate") o.validationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag) o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd) o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
@ -163,6 +167,7 @@ func (o *ReplaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
return err return err
} }
o.DryRunVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamDryRun) o.DryRunVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamDryRun)
o.FieldValidationVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamFieldValidation)
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy) cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
printer, err := o.PrintFlags.ToPrinter() printer, err := o.PrintFlags.ToPrinter()
@ -196,7 +201,7 @@ func (o *ReplaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
return err return err
} }
schema, err := f.Validator(o.validate) schema, err := f.Validator(o.validationDirective, o.FieldValidationVerifier)
if err != nil { if err != nil {
return err return err
} }
@ -306,6 +311,7 @@ func (o *ReplaceOptions) Run(f cmdutil.Factory) error {
NewHelper(info.Client, info.Mapping). NewHelper(info.Client, info.Mapping).
DryRun(o.DryRunStrategy == cmdutil.DryRunServer). DryRun(o.DryRunStrategy == cmdutil.DryRunServer).
WithFieldManager(o.fieldManager). WithFieldManager(o.fieldManager).
WithFieldValidation(o.validationDirective).
WithSubresource(o.Subresource). WithSubresource(o.Subresource).
Replace(info.Namespace, info.Name, true, info.Object) Replace(info.Namespace, info.Name, true, info.Object)
if err != nil { if err != nil {
@ -409,6 +415,7 @@ func (o *ReplaceOptions) forceReplace() error {
obj, err := resource.NewHelper(info.Client, info.Mapping). obj, err := resource.NewHelper(info.Client, info.Mapping).
WithFieldManager(o.fieldManager). WithFieldManager(o.fieldManager).
WithFieldValidation(o.validationDirective).
Create(info.Namespace, true, info.Object) Create(info.Namespace, true, info.Object)
if err != nil { if err != nil {
return err return err

View File

@ -47,8 +47,9 @@ type TaintOptions struct {
PrintFlags *genericclioptions.PrintFlags PrintFlags *genericclioptions.PrintFlags
ToPrinter func(string) (printers.ResourcePrinter, error) ToPrinter func(string) (printers.ResourcePrinter, error)
DryRunStrategy cmdutil.DryRunStrategy DryRunStrategy cmdutil.DryRunStrategy
DryRunVerifier *resource.QueryParamVerifier DryRunVerifier *resource.QueryParamVerifier
ValidationDirective string
resources []string resources []string
taintsToAdd []v1.Taint taintsToAdd []v1.Taint
@ -150,6 +151,11 @@ func (o *TaintOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st
o.DryRunVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamDryRun) o.DryRunVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamDryRun)
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy) cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
// retrieves resource and taint args from args // retrieves resource and taint args from args
// also checks args to verify that all resources are specified before taints // also checks args to verify that all resources are specified before taints
taintArgs := []string{} taintArgs := []string{}
@ -337,8 +343,9 @@ func (o TaintOptions) RunTaint() error {
} }
helper := resource. helper := resource.
NewHelper(client, mapping). NewHelper(client, mapping).
DryRun(o.DryRunStrategy == cmdutil.DryRunServer).
WithFieldManager(o.fieldManager). WithFieldManager(o.fieldManager).
DryRun(o.DryRunStrategy == cmdutil.DryRunServer) WithFieldValidation(o.ValidationDirective)
var outputObj runtime.Object var outputObj runtime.Object
if createdPatch { if createdPatch {

View File

@ -499,7 +499,7 @@ func (f *TestFactory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (r
} }
// Validator returns a validation schema // Validator returns a validation schema
func (f *TestFactory) Validator(validate bool) (validation.Schema, error) { func (f *TestFactory) Validator(validateDirective string, verifier *resource.QueryParamVerifier) (validation.Schema, error) {
return validation.NullSchema{}, nil return validation.NullSchema{}, nil
} }

View File

@ -68,6 +68,8 @@ type EditOptions struct {
WindowsLineEndings bool WindowsLineEndings bool
cmdutil.ValidateOptions cmdutil.ValidateOptions
ValidationDirective string
FieldValidationVerifier *resource.QueryParamVerifier
OriginalResult *resource.Result OriginalResult *resource.Result
@ -215,6 +217,17 @@ func (o *EditOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Comm
return o.PrintFlags.ToPrinter() return o.PrintFlags.ToPrinter()
} }
dynamicClient, err := f.DynamicClient()
if err != nil {
return err
}
o.FieldValidationVerifier = resource.NewQueryParamVerifier(dynamicClient, f.OpenAPIGetter(), resource.QueryParamFieldValidation)
o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
if err != nil {
return err
}
o.CmdNamespace = cmdNamespace o.CmdNamespace = cmdNamespace
o.f = f o.f = f
@ -309,7 +322,7 @@ func (o *EditOptions) Run() error {
klog.V(4).Infof("User edited:\n%s", string(edited)) klog.V(4).Infof("User edited:\n%s", string(edited))
// Apply validation // Apply validation
schema, err := o.f.Validator(o.EnableValidation) schema, err := o.f.Validator(o.ValidationDirective, o.FieldValidationVerifier)
if err != nil { if err != nil {
return preservedFile(err, file, o.ErrOut) return preservedFile(err, file, o.ErrOut)
} }
@ -571,7 +584,10 @@ func (o *EditOptions) annotationPatch(update *resource.Info) error {
if err != nil { if err != nil {
return err return err
} }
helper := resource.NewHelper(client, mapping).WithFieldManager(o.FieldManager).WithSubresource(o.Subresource) helper := resource.NewHelper(client, mapping).
WithFieldManager(o.FieldManager).
WithFieldValidation(o.ValidationDirective).
WithSubresource(o.Subresource)
_, err = helper.Patch(o.CmdNamespace, update.Name, patchType, patch, nil) _, err = helper.Patch(o.CmdNamespace, update.Name, patchType, patch, nil)
if err != nil { if err != nil {
return err return err
@ -709,7 +725,9 @@ func (o *EditOptions) visitToPatch(originalInfos []*resource.Info, patchVisitor
} }
patched, err := resource.NewHelper(info.Client, info.Mapping). patched, err := resource.NewHelper(info.Client, info.Mapping).
WithFieldManager(o.FieldManager).WithSubresource(o.Subresource). WithFieldManager(o.FieldManager).
WithFieldValidation(o.ValidationDirective).
WithSubresource(o.Subresource).
Patch(info.Namespace, info.Name, patchType, patch, nil) Patch(info.Namespace, info.Name, patchType, patch, nil)
if err != nil { if err != nil {
fmt.Fprintln(o.ErrOut, results.addError(err, info)) fmt.Fprintln(o.ErrOut, results.addError(err, info))
@ -729,6 +747,7 @@ func (o *EditOptions) visitToCreate(createVisitor resource.Visitor) error {
err := createVisitor.Visit(func(info *resource.Info, incomingErr error) error { err := createVisitor.Visit(func(info *resource.Info, incomingErr error) error {
obj, err := resource.NewHelper(info.Client, info.Mapping). obj, err := resource.NewHelper(info.Client, info.Mapping).
WithFieldManager(o.FieldManager). WithFieldManager(o.FieldManager).
WithFieldValidation(o.ValidationDirective).
Create(info.Namespace, true, info.Object) Create(info.Namespace, true, info.Object)
if err != nil { if err != nil {
return err return err

View File

@ -61,7 +61,7 @@ type Factory interface {
UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error)
// Returns a schema that can validate objects stored on disk. // Returns a schema that can validate objects stored on disk.
Validator(validate bool) (validation.Schema, error) Validator(validationDirective string, verifier *resource.QueryParamVerifier) (validation.Schema, error)
// OpenAPISchema returns the parsed openapi schema definition // OpenAPISchema returns the parsed openapi schema definition
OpenAPISchema() (openapi.Resources, error) OpenAPISchema() (openapi.Resources, error)
// OpenAPIGetter returns a getter for the openapi schema document // OpenAPIGetter returns a getter for the openapi schema document

View File

@ -24,6 +24,7 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource" "k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/discovery" "k8s.io/client-go/discovery"
@ -141,8 +142,14 @@ func (f *factoryImpl) UnstructuredClientForMapping(mapping *meta.RESTMapping) (r
return restclient.RESTClientFor(cfg) return restclient.RESTClientFor(cfg)
} }
func (f *factoryImpl) Validator(validate bool) (validation.Schema, error) { func (f *factoryImpl) Validator(validationDirective string, verifier *resource.QueryParamVerifier) (validation.Schema, error) {
if !validate { // client-side schema validation is only performed
// when the validationDirective is strict.
// If the directive is warn, we rely on the ParamVerifyingSchema
// to ignore the client-side validation and provide a warning
// to the user that attempting warn validation when SS validation
// is unsupported is inert.
if validationDirective == metav1.FieldValidationIgnore {
return validation.NullSchema{}, nil return validation.NullSchema{}, nil
} }
@ -151,10 +158,11 @@ func (f *factoryImpl) Validator(validate bool) (validation.Schema, error) {
return nil, err return nil, err
} }
return validation.ConjunctiveSchema{ schema := validation.ConjunctiveSchema{
openapivalidation.NewSchemaValidation(resources), openapivalidation.NewSchemaValidation(resources),
validation.NoDoubleKeySchema{}, validation.NoDoubleKeySchema{},
}, nil }
return validation.NewParamVerifyingSchema(schema, verifier, string(validationDirective)), nil
} }
// OpenAPISchema returns metadata and structural information about // OpenAPISchema returns metadata and structural information about

View File

@ -399,11 +399,14 @@ func GetPodRunningTimeoutFlag(cmd *cobra.Command) (time.Duration, error) {
} }
func AddValidateFlags(cmd *cobra.Command) { func AddValidateFlags(cmd *cobra.Command) {
cmd.Flags().Bool("validate", true, "If true, use a schema to validate the input before sending it") cmd.Flags().String(
} "validate",
"strict",
func AddValidateOptionFlags(cmd *cobra.Command, options *ValidateOptions) { `Must be one of: strict (or true), warn, ignore (or false).
cmd.Flags().BoolVar(&options.EnableValidation, "validate", options.EnableValidation, "If true, use a schema to validate the input before sending it") "true" or "strict" will use a schema to validate the input and fail the request if invalid. It will perform server side validation if ServerSideFieldValidation is enabled on the api-server, but will fall back to less reliable client-side validation if not.
"warn" will warn about unknown or duplicate fields without blocking the request if server-side field validation is enabled on the API server, and behave as "ignore" otherwise.
"false" or "ignore" will not perform any schema validation, silently dropping any unknown or duplicate fields.`,
)
} }
func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) { func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) {
@ -475,7 +478,7 @@ func AddSubresourceFlags(cmd *cobra.Command, subresource *string, usage string,
} }
type ValidateOptions struct { type ValidateOptions struct {
EnableValidation bool ValidationDirective string
} }
// Merge converts the passed in object to JSON, merges the fragment into it using an RFC7396 JSON Merge Patch, // Merge converts the passed in object to JSON, merges the fragment into it using an RFC7396 JSON Merge Patch,
@ -576,6 +579,30 @@ func GetFieldManagerFlag(cmd *cobra.Command) string {
return GetFlagString(cmd, "field-manager") return GetFlagString(cmd, "field-manager")
} }
func GetValidationDirective(cmd *cobra.Command) (string, error) {
var validateFlag = GetFlagString(cmd, "validate")
b, err := strconv.ParseBool(validateFlag)
if err != nil {
switch validateFlag {
case cmd.Flag("validate").NoOptDefVal:
return metav1.FieldValidationStrict, nil
case "strict":
return metav1.FieldValidationStrict, nil
case "warn":
return metav1.FieldValidationWarn, nil
case "ignore":
return metav1.FieldValidationIgnore, nil
default:
return metav1.FieldValidationStrict, fmt.Errorf(`invalid - validate option %q; must be one of: strict (or true), warn, ignore (or false)`, validateFlag)
}
}
// The flag was a boolean
if b {
return metav1.FieldValidationStrict, nil
}
return metav1.FieldValidationIgnore, nil
}
type DryRunStrategy int type DryRunStrategy int
const ( const (

View File

@ -17,6 +17,7 @@ limitations under the License.
package util package util
import ( import (
goerrors "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -27,6 +28,7 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
@ -467,3 +469,68 @@ func TestDifferenceFunc(t *testing.T) {
} }
} }
} }
func TestGetValidationDirective(t *testing.T) {
tests := []struct {
validateFlag string
expectedDirective string
expectedErr error
}{
{
expectedDirective: metav1.FieldValidationStrict,
},
{
validateFlag: "true",
expectedDirective: metav1.FieldValidationStrict,
},
{
validateFlag: "True",
expectedDirective: metav1.FieldValidationStrict,
},
{
validateFlag: "strict",
expectedDirective: metav1.FieldValidationStrict,
},
{
validateFlag: "warn",
expectedDirective: metav1.FieldValidationWarn,
},
{
validateFlag: "ignore",
expectedDirective: metav1.FieldValidationIgnore,
},
{
validateFlag: "false",
expectedDirective: metav1.FieldValidationIgnore,
},
{
validateFlag: "False",
expectedDirective: metav1.FieldValidationIgnore,
},
{
validateFlag: "foo",
expectedDirective: metav1.FieldValidationStrict,
expectedErr: goerrors.New(`invalid - validate option "foo"; must be one of: strict (or true), warn, ignore (or false)`),
},
}
for _, tc := range tests {
cmd := &cobra.Command{}
AddValidateFlags(cmd)
cmd.Flags().Set("validate", tc.validateFlag)
directive, err := GetValidationDirective(cmd)
if directive != tc.expectedDirective {
t.Errorf("validation directive, expected: %v, but got: %v", tc.expectedDirective, directive)
}
if tc.expectedErr != nil {
if err.Error() != tc.expectedErr.Error() {
t.Errorf("GetValidationDirective error, expected: %v, but got: %v", tc.expectedErr, err)
}
} else {
if err != nil {
t.Errorf("expecte no error, but got: %v", err)
}
}
}
}

View File

@ -43,12 +43,12 @@ func NewSchemaValidation(resources openapi.Resources) *SchemaValidation {
// ValidateBytes will validates the object against using the Resources // ValidateBytes will validates the object against using the Resources
// object. // object.
func (v *SchemaValidation) ValidateBytes(data []byte) error { func (v *SchemaValidation) ValidateBytes(data []byte) error {
obj, err := parse(data) obj, err := Parse(data)
if err != nil { if err != nil {
return err return err
} }
gvk, errs := getObjectKind(obj) gvk, errs := GetObjectKind(obj)
if errs != nil { if errs != nil {
return utilerrors.NewAggregate(errs) return utilerrors.NewAggregate(errs)
} }
@ -71,7 +71,7 @@ func (v *SchemaValidation) validateList(object interface{}) []error {
return []error{errors.New("invalid object to validate")} return []error{errors.New("invalid object to validate")}
} }
for _, item := range fields["items"].([]interface{}) { for _, item := range fields["items"].([]interface{}) {
if gvk, errs := getObjectKind(item); errs != nil { if gvk, errs := GetObjectKind(item); errs != nil {
allErrors = append(allErrors, errs...) allErrors = append(allErrors, errs...)
} else { } else {
allErrors = append(allErrors, v.validateResource(item, gvk)...) allErrors = append(allErrors, v.validateResource(item, gvk)...)
@ -90,7 +90,7 @@ func (v *SchemaValidation) validateResource(obj interface{}, gvk schema.GroupVer
return validation.ValidateModel(obj, resource, gvk.Kind) return validation.ValidateModel(obj, resource, gvk.Kind)
} }
func parse(data []byte) (interface{}, error) { func Parse(data []byte) (interface{}, error) {
var obj interface{} var obj interface{}
out, err := yaml.ToJSON(data) out, err := yaml.ToJSON(data)
if err != nil { if err != nil {
@ -102,7 +102,7 @@ func parse(data []byte) (interface{}, error) {
return obj, nil return obj, nil
} }
func getObjectKind(object interface{}) (schema.GroupVersionKind, []error) { func GetObjectKind(object interface{}) (schema.GroupVersionKind, []error) {
var listErrors []error var listErrors []error
fields, ok := object.(map[string]interface{}) fields, ok := object.(map[string]interface{})
if !ok || fields == nil { if !ok || fields == nil {

View File

@ -22,7 +22,11 @@ import (
"fmt" "fmt"
ejson "github.com/exponent-io/jsonpath" ejson "github.com/exponent-io/jsonpath"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/klog/v2"
schemavalidation "k8s.io/kubectl/pkg/util/openapi/validation"
) )
// Schema is an interface that knows how to validate an API object serialized to a byte array. // Schema is an interface that knows how to validate an API object serialized to a byte array.
@ -101,3 +105,50 @@ func (c ConjunctiveSchema) ValidateBytes(data []byte) error {
} }
return utilerrors.NewAggregate(list) return utilerrors.NewAggregate(list)
} }
func NewParamVerifyingSchema(s Schema, verifier resource.Verifier, directive string) Schema {
return &paramVerifyingSchema{
schema: s,
verifier: verifier,
directive: directive,
}
}
// paramVerifyingSchema only performs validation
// based on the fieldValidation query param
// being unsupported by the apiserver, because
// server-side validation will be performed instead
// of client-side validation.
type paramVerifyingSchema struct {
schema Schema
verifier resource.Verifier
directive string
}
// ValidateBytes validates bytes per a ParamVerifyingSchema
func (c *paramVerifyingSchema) ValidateBytes(data []byte) error {
obj, err := schemavalidation.Parse(data)
if err != nil {
return err
}
gvk, errs := schemavalidation.GetObjectKind(obj)
if errs != nil {
return utilerrors.NewAggregate(errs)
}
err = c.verifier.HasSupport(gvk)
if resource.IsParamUnsupportedError(err) {
switch c.directive {
case metav1.FieldValidationStrict:
return c.schema.ValidateBytes(data)
case metav1.FieldValidationWarn:
klog.Warningf("cannot perform warn validation if server-side field validation is unsupported, skipping validation")
default:
// can't be reached
klog.Warningf("unexpected field validation directive: %s, skipping validation", c.directive)
}
return nil
}
return err
}

View File

@ -19,6 +19,9 @@ package validation
import ( import (
"fmt" "fmt"
"testing" "testing"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/resource"
) )
func TestValidateDuplicateLabelsFailCases(t *testing.T) { func TestValidateDuplicateLabelsFailCases(t *testing.T) {
@ -139,3 +142,121 @@ func TestConjunctiveSchema(t *testing.T) {
}) })
} }
} }
type mockVerifier struct {
supported bool
}
func (v *mockVerifier) HasSupport(gvk schema.GroupVersionKind) error {
if !v.supported {
return resource.NewParamUnsupportedError(gvk, resource.QueryParamFieldValidation)
}
return nil
}
// TestParamVerifyingSchema tests that client-side schema validation
// should be bypassed (and therefore validation succeeds) in all cases
// except when the field validation is "Strict" and server-side validation is
// unsupported.
func TestParamVerifyingSchema(t *testing.T) {
bytes := []byte(`
{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "name",
"labels": {
"name": "redis-master"
}
},
"spec": {
"containers": [
{
"name": "master",
"image": "gcr.io/fake_project/fake_image:fake_tag",
"args": "this is a bad command"
}
]
}
}
`)
supportedVerifier := &mockVerifier{true}
unsupportedVerifier := &mockVerifier{false}
tests := []struct {
name string
supported bool
schema Schema
verifier resource.Verifier
directive string
shouldPass bool
}{
{
name: "supported, strict",
schema: NullSchema{},
verifier: supportedVerifier,
directive: "Strict",
shouldPass: true,
},
{
name: "supported, warn",
schema: NullSchema{},
verifier: supportedVerifier,
directive: "Warn",
shouldPass: true,
},
{
name: "unsupported, strict",
schema: NullSchema{},
verifier: unsupportedVerifier,
directive: "Strict",
shouldPass: true,
},
{
name: "unsupported, warn",
schema: NullSchema{},
verifier: unsupportedVerifier,
directive: "Warn",
shouldPass: true,
},
{
name: "supported, strict, invalid schema",
schema: AlwaysInvalidSchema{},
verifier: supportedVerifier,
directive: "Strict",
shouldPass: true,
},
{
name: "supported, warn, invalid schema",
schema: AlwaysInvalidSchema{},
verifier: supportedVerifier,
directive: "Warn",
shouldPass: true,
},
{
name: "unsupported, strict, invalid schema",
schema: AlwaysInvalidSchema{},
verifier: unsupportedVerifier,
directive: "Strict",
shouldPass: false,
},
{
name: "unsupported, warn, invalid schema",
schema: AlwaysInvalidSchema{},
verifier: unsupportedVerifier,
directive: "Warn",
shouldPass: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
schema := NewParamVerifyingSchema(tt.schema, tt.verifier, tt.directive)
err := schema.ValidateBytes(bytes)
if err != nil && tt.shouldPass {
t.Errorf("Unexpected error: %v in %s", err, tt.name)
}
if err == nil && !tt.shouldPass {
t.Errorf("Unexpected non-error: %s", tt.name)
}
})
}
}

View File

@ -153,3 +153,62 @@ run_kubectl_create_kustomization_directory_tests() {
set +o nounset set +o nounset
set +o errexit set +o errexit
} }
# Runs tests related to kubectl create --validate
run_kubectl_create_validate_tests() {
set -o nounset
set -o errexit
create_and_use_new_namespace
## test --validate=true
kube::log::status "Testing kubectl create --validate=true"
# create and verify
output_message=$(! kubectl create -f hack/testdata/invalid-deployment-unknown-and-duplicate-fields.yaml --validate=true 2>&1)
kube::test::if_has_string "${output_message}" 'error validating data'
## test --validate=false
kube::log::status "Testing kubectl create --validate=false"
# create and verify
output_message=$(kubectl create -f hack/testdata/invalid-deployment-unknown-and-duplicate-fields.yaml --validate=false)
kube::test::if_has_string "${output_message}" "deployment.apps/invalid-nginx-deployment created"
# cleanup
kubectl delete deployment invalid-nginx-deployment
## test --validate=strict
kube::log::status "Testing kubectl create --validate=strict"
# create and verify
output_message=$(! kubectl create -f hack/testdata/invalid-deployment-unknown-and-duplicate-fields.yaml --validate=strict 2>&1)
kube::test::if_has_string "${output_message}" 'error validating data'
## test --validate=warn
kube::log::status "Testing kubectl create --validate=warn"
# create and verify
output_message=$(kubectl create -f hack/testdata/invalid-deployment-unknown-and-duplicate-fields.yaml --validate=warn)
kube::test::if_has_string "${output_message}" "deployment.apps/invalid-nginx-deployment created"
# cleanup
kubectl delete deployment invalid-nginx-deployment
## test --validate=ignore
kube::log::status "Testing kubectl create --validate=ignore"
# create and verify
output_message=$(kubectl create -f hack/testdata/invalid-deployment-unknown-and-duplicate-fields.yaml --validate=ignore)
kube::test::if_has_string "${output_message}" "deployment.apps/invalid-nginx-deployment created"
# cleanup
kubectl delete deployment invalid-nginx-deployment
## test default is strict validation
kube::log::status "Testing kubectl create"
# create and verify
output_message=$(! kubectl create -f hack/testdata/invalid-deployment-unknown-and-duplicate-fields.yaml 2>&1)
kube::test::if_has_string "${output_message}" 'error validating data'
## test invalid validate value
kube::log::status "Testing kubectl create --validate=foo"
# create and verify
output_message=$(! kubectl create -f hack/testdata/invalid-deployment-unknown-and-duplicate-fields.yaml --validate=foo 2>&1)
kube::test::if_has_string "${output_message}" 'invalid - validate option "foo"'
set +o nounset
set +o errexit
}

View File

@ -573,6 +573,7 @@ runTests() {
fi fi
if kube::test::if_supports_resource "${deployments}"; then if kube::test::if_supports_resource "${deployments}"; then
record_command run_kubectl_create_kustomization_directory_tests record_command run_kubectl_create_kustomization_directory_tests
record_command run_kubectl_create_validate_tests
fi fi
###################### ######################