Commit 967280b5 authored by Antoine Pelisse's avatar Antoine Pelisse

Add --server-dry-run flag to `kubectl apply`

parent 8aea6746
...@@ -40,12 +40,16 @@ function run_kube_apiserver() { ...@@ -40,12 +40,16 @@ function run_kube_apiserver() {
# Include RBAC (to exercise bootstrapping), and AlwaysAllow to allow all actions # Include RBAC (to exercise bootstrapping), and AlwaysAllow to allow all actions
AUTHORIZATION_MODE="RBAC,AlwaysAllow" AUTHORIZATION_MODE="RBAC,AlwaysAllow"
# Enable features
ENABLE_FEATURE_GATES="DryRun=true"
"${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \ "${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \
--insecure-bind-address="127.0.0.1" \ --insecure-bind-address="127.0.0.1" \
--bind-address="127.0.0.1" \ --bind-address="127.0.0.1" \
--insecure-port="${API_PORT}" \ --insecure-port="${API_PORT}" \
--authorization-mode="${AUTHORIZATION_MODE}" \ --authorization-mode="${AUTHORIZATION_MODE}" \
--secure-port="${SECURE_API_PORT}" \ --secure-port="${SECURE_API_PORT}" \
--feature-gates="${ENABLE_FEATURE_GATES}" \
--enable-admission-plugins="${ENABLE_ADMISSION_PLUGINS}" \ --enable-admission-plugins="${ENABLE_ADMISSION_PLUGINS}" \
--disable-admission-plugins="${DISABLE_ADMISSION_PLUGINS}" \ --disable-admission-plugins="${DISABLE_ADMISSION_PLUGINS}" \
--etcd-servers="http://${ETCD_HOST}:${ETCD_PORT}" \ --etcd-servers="http://${ETCD_HOST}:${ETCD_PORT}" \
......
...@@ -294,7 +294,7 @@ func (o AnnotateOptions) RunAnnotate() error { ...@@ -294,7 +294,7 @@ func (o AnnotateOptions) RunAnnotate() error {
helper := resource.NewHelper(client, mapping) helper := resource.NewHelper(client, mapping)
if createdPatch { if createdPatch {
outputObj, err = helper.Patch(namespace, name, types.MergePatchType, patchBytes) outputObj, err = helper.Patch(namespace, name, types.MergePatchType, patchBytes, nil)
} else { } else {
outputObj, err = helper.Replace(namespace, name, false, obj) outputObj, err = helper.Replace(namespace, name, false, obj)
} }
......
...@@ -66,6 +66,7 @@ type ApplyOptions struct { ...@@ -66,6 +66,7 @@ type ApplyOptions struct {
Selector string Selector string
DryRun bool DryRun bool
ServerDryRun bool
Prune bool Prune bool
PruneResources []pruneResource PruneResources []pruneResource
cmdBaseName string cmdBaseName string
...@@ -172,6 +173,7 @@ func NewCmdApply(baseName string, f cmdutil.Factory, ioStreams genericclioptions ...@@ -172,6 +173,7 @@ func NewCmdApply(baseName string, f cmdutil.Factory, ioStreams genericclioptions
cmd.Flags().BoolVar(&o.All, "all", o.All, "Select all resources in the namespace of the specified resource types.") cmd.Flags().BoolVar(&o.All, "all", o.All, "Select all resources in the namespace of the specified resource types.")
cmd.Flags().StringArrayVar(&o.PruneWhitelist, "prune-whitelist", o.PruneWhitelist, "Overwrite the default whitelist with <group/version/kind> for --prune") cmd.Flags().StringArrayVar(&o.PruneWhitelist, "prune-whitelist", o.PruneWhitelist, "Overwrite the default whitelist with <group/version/kind> for --prune")
cmd.Flags().BoolVar(&o.OpenApiPatch, "openapi-patch", o.OpenApiPatch, "If true, use openapi to calculate diff when the openapi presents and the resource can be found in the openapi spec. Otherwise, fall back to use baked-in types.") cmd.Flags().BoolVar(&o.OpenApiPatch, "openapi-patch", o.OpenApiPatch, "If true, use openapi to calculate diff when the openapi presents and the resource can be found in the openapi spec. Otherwise, fall back to use baked-in types.")
cmd.Flags().BoolVar(&o.ServerDryRun, "server-dry-run", o.ServerDryRun, "If true, request will be sent to server with dry-run flag, which means the modifications won't be persisted. This is an alpha feature and flag.")
cmdutil.AddDryRunFlag(cmd) cmdutil.AddDryRunFlag(cmd)
cmdutil.AddIncludeUninitializedFlag(cmd) cmdutil.AddIncludeUninitializedFlag(cmd)
...@@ -186,13 +188,19 @@ func NewCmdApply(baseName string, f cmdutil.Factory, ioStreams genericclioptions ...@@ -186,13 +188,19 @@ func NewCmdApply(baseName string, f cmdutil.Factory, ioStreams genericclioptions
func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.DryRun = cmdutil.GetDryRunFlag(cmd) o.DryRun = cmdutil.GetDryRunFlag(cmd)
if o.DryRun && o.ServerDryRun {
return fmt.Errorf("--dry-run and --server-dry-run can't be used together")
}
// allow for a success message operation to be specified at print time // allow for a success message operation to be specified at print time
o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) { o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
o.PrintFlags.NamePrintFlags.Operation = operation o.PrintFlags.NamePrintFlags.Operation = operation
if o.DryRun { if o.DryRun {
o.PrintFlags.Complete("%s (dry run)") o.PrintFlags.Complete("%s (dry run)")
} }
if o.ServerDryRun {
o.PrintFlags.Complete("%s (server dry run)")
}
return o.PrintFlags.ToPrinter() return o.PrintFlags.ToPrinter()
} }
...@@ -354,7 +362,11 @@ func (o *ApplyOptions) Run() error { ...@@ -354,7 +362,11 @@ func (o *ApplyOptions) Run() error {
if !o.DryRun { if !o.DryRun {
// Then create the resource and skip the three-way merge // Then create the resource and skip the three-way merge
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) options := metav1.CreateOptions{}
if o.ServerDryRun {
options.DryRun = []string{metav1.DryRunAll}
}
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, &options)
if err != nil { if err != nil {
return cmdutil.AddSourceToErr("creating", info.Source, err) return cmdutil.AddSourceToErr("creating", info.Source, err)
} }
...@@ -402,6 +414,7 @@ func (o *ApplyOptions) Run() error { ...@@ -402,6 +414,7 @@ func (o *ApplyOptions) Run() error {
cascade: o.DeleteOptions.Cascade, cascade: o.DeleteOptions.Cascade,
timeout: o.DeleteOptions.Timeout, timeout: o.DeleteOptions.Timeout,
gracePeriod: o.DeleteOptions.GracePeriod, gracePeriod: o.DeleteOptions.GracePeriod,
serverDryRun: o.ServerDryRun,
openapiSchema: openapiSchema, openapiSchema: openapiSchema,
} }
...@@ -483,9 +496,10 @@ func (o *ApplyOptions) Run() error { ...@@ -483,9 +496,10 @@ func (o *ApplyOptions) Run() error {
labelSelector: o.Selector, labelSelector: o.Selector,
visitedUids: visitedUids, visitedUids: visitedUids,
cascade: o.DeleteOptions.Cascade, cascade: o.DeleteOptions.Cascade,
dryRun: o.DryRun, dryRun: o.DryRun,
gracePeriod: o.DeleteOptions.GracePeriod, serverDryRun: o.ServerDryRun,
gracePeriod: o.DeleteOptions.GracePeriod,
toPrinter: o.ToPrinter, toPrinter: o.ToPrinter,
...@@ -572,9 +586,10 @@ type pruner struct { ...@@ -572,9 +586,10 @@ type pruner struct {
labelSelector string labelSelector string
fieldSelector string fieldSelector string
cascade bool cascade bool
dryRun bool serverDryRun bool
gracePeriod int dryRun bool
gracePeriod int
toPrinter func(string) (printers.ResourcePrinter, error) toPrinter func(string) (printers.ResourcePrinter, error)
...@@ -629,14 +644,17 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, includeUnini ...@@ -629,14 +644,17 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, includeUnini
} }
func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping) error { func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping) error {
return runDelete(namespace, name, mapping, p.dynamicClient, p.cascade, p.gracePeriod) return runDelete(namespace, name, mapping, p.dynamicClient, p.cascade, p.gracePeriod, p.serverDryRun)
} }
func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascade bool, gracePeriod int) error { func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascade bool, gracePeriod int, serverDryRun bool) error {
options := &metav1.DeleteOptions{} options := &metav1.DeleteOptions{}
if gracePeriod >= 0 { if gracePeriod >= 0 {
options = metav1.NewDeleteOptions(int64(gracePeriod)) options = metav1.NewDeleteOptions(int64(gracePeriod))
} }
if serverDryRun {
options.DryRun = []string{metav1.DryRunAll}
}
policy := metav1.DeletePropagationForeground policy := metav1.DeletePropagationForeground
if !cascade { if !cascade {
policy = metav1.DeletePropagationOrphan policy = metav1.DeletePropagationOrphan
...@@ -646,7 +664,7 @@ func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Inte ...@@ -646,7 +664,7 @@ func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Inte
} }
func (p *patcher) delete(namespace, name string) error { func (p *patcher) delete(namespace, name string) error {
return runDelete(namespace, name, p.mapping, p.dynamicClient, p.cascade, p.gracePeriod) return runDelete(namespace, name, p.mapping, p.dynamicClient, p.cascade, p.gracePeriod, p.serverDryRun)
} }
type patcher struct { type patcher struct {
...@@ -657,10 +675,11 @@ type patcher struct { ...@@ -657,10 +675,11 @@ type patcher struct {
overwrite bool overwrite bool
backOff clockwork.Clock backOff clockwork.Clock
force bool force bool
cascade bool cascade bool
timeout time.Duration timeout time.Duration
gracePeriod int gracePeriod int
serverDryRun bool
openapiSchema openapi.Resources openapiSchema openapi.Resources
} }
...@@ -736,7 +755,12 @@ func (p *patcher) patchSimple(obj runtime.Object, modified []byte, source, names ...@@ -736,7 +755,12 @@ func (p *patcher) patchSimple(obj runtime.Object, modified []byte, source, names
return patch, obj, nil return patch, obj, nil
} }
patchedObj, err := p.helper.Patch(namespace, name, patchType, patch) options := metav1.UpdateOptions{}
if p.serverDryRun {
options.DryRun = []string{metav1.DryRunAll}
}
patchedObj, err := p.helper.Patch(namespace, name, patchType, patch, &options)
return patch, patchedObj, err return patch, patchedObj, err
} }
...@@ -776,11 +800,15 @@ func (p *patcher) deleteAndCreate(original runtime.Object, modified []byte, name ...@@ -776,11 +800,15 @@ func (p *patcher) deleteAndCreate(original runtime.Object, modified []byte, name
if err != nil { if err != nil {
return modified, nil, err return modified, nil, err
} }
createdObject, err := p.helper.Create(namespace, true, versionedObject) options := metav1.CreateOptions{}
if p.serverDryRun {
options.DryRun = []string{metav1.DryRunAll}
}
createdObject, err := p.helper.Create(namespace, true, versionedObject, &options)
if err != nil { if err != nil {
// restore the original object if we fail to create the new one // restore the original object if we fail to create the new one
// but still propagate and advertise error to user // but still propagate and advertise error to user
recreated, recreateErr := p.helper.Create(namespace, true, original) recreated, recreateErr := p.helper.Create(namespace, true, original, &options)
if recreateErr != nil { if recreateErr != nil {
err = fmt.Errorf("An error occurred force-replacing the existing object with the newly provided one:\n\n%v.\n\nAdditionally, an error occurred attempting to restore the original object:\n\n%v\n", err, recreateErr) err = fmt.Errorf("An error occurred force-replacing the existing object with the newly provided one:\n\n%v.\n\nAdditionally, an error occurred attempting to restore the original object:\n\n%v\n", err, recreateErr)
} else { } else {
......
...@@ -200,7 +200,7 @@ func (o *SetLastAppliedOptions) RunSetLastApplied() error { ...@@ -200,7 +200,7 @@ func (o *SetLastAppliedOptions) RunSetLastApplied() error {
return err return err
} }
helper := resource.NewHelper(client, mapping) helper := resource.NewHelper(client, mapping)
finalObj, err = helper.Patch(o.namespace, info.Name, patch.PatchType, patch.Patch) finalObj, err = helper.Patch(o.namespace, info.Name, patch.PatchType, patch.Patch, nil)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -318,7 +318,7 @@ func RunEditOnCreate(f cmdutil.Factory, printFlags *genericclioptions.PrintFlags ...@@ -318,7 +318,7 @@ func RunEditOnCreate(f cmdutil.Factory, printFlags *genericclioptions.PrintFlags
// createAndRefresh creates an object from input info and refreshes info with that object // createAndRefresh creates an object from input info and refreshes info with that object
func createAndRefresh(info *resource.Info) error { func createAndRefresh(info *resource.Info) error {
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -760,7 +760,7 @@ func (o *DrainOptions) RunCordonOrUncordon(desired bool) error { ...@@ -760,7 +760,7 @@ func (o *DrainOptions) RunCordonOrUncordon(desired bool) error {
fmt.Printf("error: unable to %s node %q: %v", cordonOrUncordon, nodeInfo.Name, err) fmt.Printf("error: unable to %s node %q: %v", cordonOrUncordon, nodeInfo.Name, err)
continue continue
} }
_, err = helper.Patch(o.Namespace, nodeInfo.Name, types.StrategicMergePatchType, patchBytes) _, err = helper.Patch(o.Namespace, nodeInfo.Name, types.StrategicMergePatchType, patchBytes, nil)
if err != nil { if err != nil {
fmt.Printf("error: unable to %s node %q: %v", cordonOrUncordon, nodeInfo.Name, err) fmt.Printf("error: unable to %s node %q: %v", cordonOrUncordon, nodeInfo.Name, err)
continue continue
......
...@@ -304,7 +304,7 @@ func (o *LabelOptions) RunLabel() error { ...@@ -304,7 +304,7 @@ func (o *LabelOptions) RunLabel() error {
helper := resource.NewHelper(client, mapping) helper := resource.NewHelper(client, mapping)
if createdPatch { if createdPatch {
outputObj, err = helper.Patch(namespace, name, types.MergePatchType, patchBytes) outputObj, err = helper.Patch(namespace, name, types.MergePatchType, patchBytes, nil)
} else { } else {
outputObj, err = helper.Replace(namespace, name, false, obj) outputObj, err = helper.Replace(namespace, name, false, obj)
} }
......
...@@ -220,7 +220,7 @@ func (o *PatchOptions) RunPatch() error { ...@@ -220,7 +220,7 @@ func (o *PatchOptions) RunPatch() error {
} }
helper := resource.NewHelper(client, mapping) helper := resource.NewHelper(client, mapping)
patchedObj, err := helper.Patch(namespace, name, patchType, patchBytes) patchedObj, err := helper.Patch(namespace, name, patchType, patchBytes, nil)
if err != nil { if err != nil {
return err return err
} }
...@@ -231,7 +231,7 @@ func (o *PatchOptions) RunPatch() error { ...@@ -231,7 +231,7 @@ func (o *PatchOptions) RunPatch() error {
if mergePatch, err := o.Recorder.MakeRecordMergePatch(patchedObj); err != nil { if mergePatch, err := o.Recorder.MakeRecordMergePatch(patchedObj); err != nil {
glog.V(4).Infof("error recording current command: %v", err) glog.V(4).Infof("error recording current command: %v", err)
} else if len(mergePatch) > 0 { } else if len(mergePatch) > 0 {
if recordedObj, err := helper.Patch(info.Namespace, info.Name, types.MergePatchType, mergePatch); err != nil { if recordedObj, err := helper.Patch(info.Namespace, info.Name, types.MergePatchType, mergePatch, nil); err != nil {
glog.V(4).Infof("error recording reason: %v", err) glog.V(4).Infof("error recording reason: %v", err)
} else { } else {
patchedObj = recordedObj patchedObj = recordedObj
......
...@@ -317,7 +317,7 @@ func (o *ReplaceOptions) forceReplace() error { ...@@ -317,7 +317,7 @@ func (o *ReplaceOptions) forceReplace() error {
glog.V(4).Infof("error recording current command: %v", err) glog.V(4).Infof("error recording current command: %v", err)
} }
obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -174,7 +174,7 @@ func (o PauseConfig) RunPause() error { ...@@ -174,7 +174,7 @@ func (o PauseConfig) RunPause() error {
continue continue
} }
obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
if err != nil { if err != nil {
allErrs = append(allErrs, fmt.Errorf("failed to patch: %v", err)) allErrs = append(allErrs, fmt.Errorf("failed to patch: %v", err))
continue continue
......
...@@ -178,7 +178,7 @@ func (o ResumeOptions) RunResume() error { ...@@ -178,7 +178,7 @@ func (o ResumeOptions) RunResume() error {
continue continue
} }
obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
if err != nil { if err != nil {
allErrs = append(allErrs, fmt.Errorf("failed to patch: %v", err)) allErrs = append(allErrs, fmt.Errorf("failed to patch: %v", err))
continue continue
......
...@@ -667,7 +667,7 @@ func (o *RunOptions) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command ...@@ -667,7 +667,7 @@ func (o *RunOptions) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command
if err != nil { if err != nil {
return nil, err return nil, err
} }
actualObj, err = resource.NewHelper(client, mapping).Create(namespace, false, obj) actualObj, err = resource.NewHelper(client, mapping).Create(namespace, false, obj, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -244,7 +244,7 @@ func (o *ScaleOptions) RunScale() error { ...@@ -244,7 +244,7 @@ func (o *ScaleOptions) RunScale() error {
return err return err
} }
helper := resource.NewHelper(client, mapping) helper := resource.NewHelper(client, mapping)
if _, err := helper.Patch(info.Namespace, info.Name, types.MergePatchType, mergePatch); err != nil { if _, err := helper.Patch(info.Namespace, info.Name, types.MergePatchType, mergePatch, nil); err != nil {
glog.V(4).Infof("error recording reason: %v", err) glog.V(4).Infof("error recording reason: %v", err)
} }
} }
......
...@@ -487,7 +487,7 @@ func (o *EnvOptions) RunEnv() error { ...@@ -487,7 +487,7 @@ func (o *EnvOptions) RunEnv() error {
continue continue
} }
actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
if err != nil { if err != nil {
allErrs = append(allErrs, fmt.Errorf("failed to patch env update to pod template: %v\n", err)) allErrs = append(allErrs, fmt.Errorf("failed to patch env update to pod template: %v\n", err))
continue continue
......
...@@ -282,7 +282,7 @@ func (o *SetImageOptions) Run() error { ...@@ -282,7 +282,7 @@ func (o *SetImageOptions) Run() error {
} }
// patch the change // patch the change
actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
if err != nil { if err != nil {
allErrs = append(allErrs, fmt.Errorf("failed to patch image update to pod template: %v\n", err)) allErrs = append(allErrs, fmt.Errorf("failed to patch image update to pod template: %v\n", err))
continue continue
......
...@@ -276,7 +276,7 @@ func (o *SetResourcesOptions) Run() error { ...@@ -276,7 +276,7 @@ func (o *SetResourcesOptions) Run() error {
continue continue
} }
actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
if err != nil { if err != nil {
allErrs = append(allErrs, fmt.Errorf("failed to patch limit update to pod template %v\n", err)) allErrs = append(allErrs, fmt.Errorf("failed to patch limit update to pod template %v\n", err))
continue continue
......
...@@ -184,7 +184,7 @@ func (o *SetSelectorOptions) RunSelector() error { ...@@ -184,7 +184,7 @@ func (o *SetSelectorOptions) RunSelector() error {
return o.PrintObj(info.Object, o.Out) return o.PrintObj(info.Object, o.Out)
} }
actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -44,7 +44,7 @@ var ( ...@@ -44,7 +44,7 @@ var (
serviceaccountLong = templates.LongDesc(i18n.T(` serviceaccountLong = templates.LongDesc(i18n.T(`
Update ServiceAccount of pod template resources. Update ServiceAccount of pod template resources.
Possible resources (case insensitive) can be: Possible resources (case insensitive) can be:
` + serviceaccountResources)) ` + serviceaccountResources))
serviceaccountExample = templates.Examples(i18n.T(` serviceaccountExample = templates.Examples(i18n.T(`
...@@ -202,7 +202,7 @@ func (o *SetServiceAccountOptions) Run() error { ...@@ -202,7 +202,7 @@ func (o *SetServiceAccountOptions) Run() error {
} }
continue continue
} }
actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
if err != nil { if err != nil {
patchErrs = append(patchErrs, fmt.Errorf("failed to patch ServiceAccountName %v", err)) patchErrs = append(patchErrs, fmt.Errorf("failed to patch ServiceAccountName %v", err))
continue continue
......
...@@ -255,7 +255,7 @@ func (o *SubjectOptions) Run(fn updateSubjects) error { ...@@ -255,7 +255,7 @@ func (o *SubjectOptions) Run(fn updateSubjects) error {
continue continue
} }
actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
if err != nil { if err != nil {
allErrs = append(allErrs, fmt.Errorf("failed to patch subjects to rolebinding: %v\n", err)) allErrs = append(allErrs, fmt.Errorf("failed to patch subjects to rolebinding: %v\n", err))
continue continue
......
...@@ -274,7 +274,7 @@ func (o TaintOptions) RunTaint() error { ...@@ -274,7 +274,7 @@ func (o TaintOptions) RunTaint() error {
var outputObj runtime.Object var outputObj runtime.Object
if createdPatch { if createdPatch {
outputObj, err = helper.Patch(namespace, name, types.StrategicMergePatchType, patchBytes) outputObj, err = helper.Patch(namespace, name, types.StrategicMergePatchType, patchBytes, nil)
} else { } else {
outputObj, err = helper.Replace(namespace, name, false, obj) outputObj, err = helper.Replace(namespace, name, false, obj)
} }
......
...@@ -503,7 +503,7 @@ func (o *EditOptions) annotationPatch(update *resource.Info) error { ...@@ -503,7 +503,7 @@ func (o *EditOptions) annotationPatch(update *resource.Info) error {
return err return err
} }
helper := resource.NewHelper(client, mapping) helper := resource.NewHelper(client, mapping)
_, err = helper.Patch(o.CmdNamespace, update.Name, patchType, patch) _, err = helper.Patch(o.CmdNamespace, update.Name, patchType, patch, nil)
if err != nil { if err != nil {
return err return err
} }
...@@ -632,7 +632,7 @@ func (o *EditOptions) visitToPatch(originalInfos []*resource.Info, patchVisitor ...@@ -632,7 +632,7 @@ func (o *EditOptions) visitToPatch(originalInfos []*resource.Info, patchVisitor
fmt.Fprintf(o.Out, "Patch: %s\n", string(patch)) fmt.Fprintf(o.Out, "Patch: %s\n", string(patch))
} }
patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, patchType, patch) patched, err := resource.NewHelper(info.Client, info.Mapping).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))
return nil return nil
......
...@@ -108,13 +108,16 @@ func (m *Helper) DeleteWithOptions(namespace, name string, options *metav1.Delet ...@@ -108,13 +108,16 @@ func (m *Helper) DeleteWithOptions(namespace, name string, options *metav1.Delet
Get() Get()
} }
func (m *Helper) Create(namespace string, modify bool, obj runtime.Object) (runtime.Object, error) { func (m *Helper) Create(namespace string, modify bool, obj runtime.Object, options *metav1.CreateOptions) (runtime.Object, error) {
if options == nil {
options = &metav1.CreateOptions{}
}
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)
if err != nil { if err != nil {
// We don't know how to clear the version on this object, so send it to the server as is // We don't know how to clear the version on this object, so send it to the server as is
return m.createResource(m.RESTClient, m.Resource, namespace, obj) return m.createResource(m.RESTClient, m.Resource, namespace, obj, options)
} }
if version != "" { if version != "" {
if err := metadataAccessor.SetResourceVersion(obj, ""); err != nil { if err := metadataAccessor.SetResourceVersion(obj, ""); err != nil {
...@@ -123,17 +126,27 @@ func (m *Helper) Create(namespace string, modify bool, obj runtime.Object) (runt ...@@ -123,17 +126,27 @@ func (m *Helper) Create(namespace string, modify bool, obj runtime.Object) (runt
} }
} }
return m.createResource(m.RESTClient, m.Resource, namespace, obj) return m.createResource(m.RESTClient, m.Resource, namespace, obj, options)
} }
func (m *Helper) createResource(c RESTClient, resource, namespace string, obj runtime.Object) (runtime.Object, error) { func (m *Helper) createResource(c RESTClient, resource, namespace string, obj runtime.Object, options *metav1.CreateOptions) (runtime.Object, error) {
return c.Post().NamespaceIfScoped(namespace, m.NamespaceScoped).Resource(resource).Body(obj).Do().Get() return c.Post().
NamespaceIfScoped(namespace, m.NamespaceScoped).
Resource(resource).
VersionedParams(options, metav1.ParameterCodec).
Body(obj).
Do().
Get()
} }
func (m *Helper) Patch(namespace, name string, pt types.PatchType, data []byte) (runtime.Object, error) { func (m *Helper) Patch(namespace, name string, pt types.PatchType, data []byte, options *metav1.UpdateOptions) (runtime.Object, error) {
if options == nil {
options = &metav1.UpdateOptions{}
}
return m.RESTClient.Patch(pt). return m.RESTClient.Patch(pt).
NamespaceIfScoped(namespace, m.NamespaceScoped). NamespaceIfScoped(namespace, m.NamespaceScoped).
Resource(m.Resource). Resource(m.Resource).
Name(name). Name(name).
VersionedParams(options, metav1.ParameterCodec).
Body(data). Body(data).
Do(). Do().
Get() Get()
......
...@@ -228,7 +228,7 @@ func TestHelperCreate(t *testing.T) { ...@@ -228,7 +228,7 @@ func TestHelperCreate(t *testing.T) {
RESTClient: client, RESTClient: client,
NamespaceScoped: true, NamespaceScoped: true,
} }
_, err := modifier.Create("bar", tt.Modify, tt.Object) _, err := modifier.Create("bar", tt.Modify, tt.Object, nil)
if (err != nil) != tt.Err { if (err != nil) != tt.Err {
t.Errorf("%d: unexpected error: %t %v", i, tt.Err, err) t.Errorf("%d: unexpected error: %t %v", i, tt.Err, err)
} }
......
...@@ -656,7 +656,7 @@ func RetrieveLazy(info *Info, err error) error { ...@@ -656,7 +656,7 @@ func RetrieveLazy(info *Info, err error) error {
// CreateAndRefresh creates an object from input info and refreshes info with that object // CreateAndRefresh creates an object from input info and refreshes info with that object
func CreateAndRefresh(info *Info) error { func CreateAndRefresh(info *Info) error {
obj, err := NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) obj, err := NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object, nil)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -75,6 +75,23 @@ run_kubectl_apply_tests() { ...@@ -75,6 +75,23 @@ run_kubectl_apply_tests() {
# cleanup # cleanup
kubectl delete pods selector-test-pod kubectl delete pods selector-test-pod
## kubectl apply --server-dry-run
# Pre-Condition: no POD exists
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" ''
# apply dry-run
kubectl apply --server-dry-run -f hack/testdata/pod.yaml "${kube_flags[@]}"
# No pod exists
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" ''
# apply non dry-run creates the pod
kubectl apply -f hack/testdata/pod.yaml "${kube_flags[@]}"
# apply changes
kubectl apply --server-dry-run -f hack/testdata/pod-apply.yaml "${kube_flags[@]}"
# Post-Condition: label still has initial value
kube::test::get_object_assert 'pods test-pod' "{{${labels_field}.name}}" 'test-pod-label'
# clean-up
kubectl delete -f hack/testdata/pod.yaml "${kube_flags[@]}"
## kubectl apply --prune ## kubectl apply --prune
# Pre-Condition: no POD exists # Pre-Condition: no POD exists
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment