mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 10:51:29 +00:00
Merge pull request #88292 from julianvmodesto/add-dry-run
Add --dry-run=server|client|none to more kubectl commands
This commit is contained in:
commit
1591590030
@ -41,6 +41,7 @@ go_test(
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/resource:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest/fake:go_default_library",
|
||||
"//staging/src/k8s.io/kubectl/pkg/cmd/testing:go_default_library",
|
||||
"//staging/src/k8s.io/kubectl/pkg/cmd/util:go_default_library",
|
||||
"//staging/src/k8s.io/kubectl/pkg/scheme:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
],
|
||||
|
@ -115,6 +115,9 @@ type DeleteOptions struct {
|
||||
GracePeriod int
|
||||
Timeout time.Duration
|
||||
|
||||
DryRunStrategy cmdutil.DryRunStrategy
|
||||
DryRunVerifier *resource.DryRunVerifier
|
||||
|
||||
Output string
|
||||
|
||||
DynamicClient dynamic.Interface
|
||||
@ -143,6 +146,7 @@ func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra
|
||||
}
|
||||
|
||||
deleteFlags.AddFlags(cmd)
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
@ -173,6 +177,20 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Co
|
||||
o.GracePeriod = 1
|
||||
}
|
||||
|
||||
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dynamicClient, err := f.DynamicClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
discoveryClient, err := f.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.DryRunVerifier = resource.NewDryRunVerifier(dynamicClient, discoveryClient)
|
||||
|
||||
if len(o.Raw) == 0 {
|
||||
r := f.NewBuilder().
|
||||
Unstructured().
|
||||
@ -291,6 +309,18 @@ func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
|
||||
fmt.Fprintf(o.ErrOut, "warning: deleting cluster-scoped resources, not scoped to the provided namespace\n")
|
||||
warnClusterScope = false
|
||||
}
|
||||
|
||||
if o.DryRunStrategy == cmdutil.DryRunClient {
|
||||
if !o.Quiet {
|
||||
o.PrintObj(info)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if o.DryRunStrategy == cmdutil.DryRunServer {
|
||||
if err := o.DryRunVerifier.HasSupport(info.Mapping.GroupVersionKind); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
response, err := o.deleteResource(info, options)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -330,6 +360,11 @@ func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we are dry-running, then we don't want to wait
|
||||
if o.DryRunStrategy != cmdutil.DryRunNone {
|
||||
return nil
|
||||
}
|
||||
|
||||
effectiveTimeout := o.Timeout
|
||||
if effectiveTimeout == 0 {
|
||||
// if we requested to wait forever, set it to a week.
|
||||
@ -356,7 +391,10 @@ func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
|
||||
}
|
||||
|
||||
func (o *DeleteOptions) deleteResource(info *resource.Info, deleteOptions *metav1.DeleteOptions) (runtime.Object, error) {
|
||||
deleteResponse, err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, deleteOptions)
|
||||
deleteResponse, err := resource.
|
||||
NewHelper(info.Client, info.Mapping).
|
||||
DryRun(o.DryRunStrategy == cmdutil.DryRunServer).
|
||||
DeleteWithOptions(info.Namespace, info.Name, deleteOptions)
|
||||
if err != nil {
|
||||
return nil, cmdutil.AddSourceToErr("deleting", info.Source, err)
|
||||
}
|
||||
@ -381,6 +419,13 @@ func (o *DeleteOptions) PrintObj(info *resource.Info) {
|
||||
operation = "force deleted"
|
||||
}
|
||||
|
||||
switch o.DryRunStrategy {
|
||||
case cmdutil.DryRunClient:
|
||||
operation = fmt.Sprintf("%s (dry run)", operation)
|
||||
case cmdutil.DryRunServer:
|
||||
operation = fmt.Sprintf("%s (server dry run)", operation)
|
||||
}
|
||||
|
||||
if o.Output == "name" {
|
||||
// -o name: prints resource/name
|
||||
fmt.Fprintf(o.Out, "%s/%s\n", kindString, info.Name)
|
||||
|
@ -33,6 +33,7 @@ import (
|
||||
"k8s.io/cli-runtime/pkg/resource"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/scheme"
|
||||
)
|
||||
|
||||
@ -42,6 +43,7 @@ func fakecmd() *cobra.Command {
|
||||
DisableFlagsInUseLine: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {},
|
||||
}
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,9 @@ type ReplaceOptions struct {
|
||||
DeleteFlags *delete.DeleteFlags
|
||||
DeleteOptions *delete.DeleteOptions
|
||||
|
||||
DryRunStrategy cmdutil.DryRunStrategy
|
||||
DryRunVerifier *resource.DryRunVerifier
|
||||
|
||||
PrintObj func(obj runtime.Object) error
|
||||
|
||||
createAnnotation bool
|
||||
@ -123,6 +126,7 @@ func NewCmdReplace(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobr
|
||||
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
|
||||
cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to PUT to the server. Uses the transport specified by the kubeconfig file.")
|
||||
|
||||
@ -141,6 +145,21 @@ func (o *ReplaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
|
||||
o.validate = cmdutil.GetFlagBool(cmd, "validate")
|
||||
o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
|
||||
|
||||
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dynamicClient, err := f.DynamicClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
discoveryClient, err := f.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.DryRunVerifier = resource.NewDryRunVerifier(dynamicClient, discoveryClient)
|
||||
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
|
||||
|
||||
printer, err := o.PrintFlags.ToPrinter()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -149,10 +168,6 @@ func (o *ReplaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
|
||||
return printer.PrintObj(obj, o.Out)
|
||||
}
|
||||
|
||||
dynamicClient, err := f.DynamicClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deleteOpts := o.DeleteFlags.ToOptions(dynamicClient, o.IOStreams)
|
||||
|
||||
//Replace will create a resource if it doesn't exist already, so ignore not found error
|
||||
@ -264,8 +279,20 @@ func (o *ReplaceOptions) Run(f cmdutil.Factory) error {
|
||||
klog.V(4).Infof("error recording current command: %v", err)
|
||||
}
|
||||
|
||||
if o.DryRunStrategy == cmdutil.DryRunClient {
|
||||
return o.PrintObj(info.Object)
|
||||
}
|
||||
if o.DryRunStrategy == cmdutil.DryRunServer {
|
||||
if err := o.DryRunVerifier.HasSupport(info.Mapping.GroupVersionKind); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the object with the annotation applied.
|
||||
obj, err := resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, true, info.Object)
|
||||
obj, err := resource.
|
||||
NewHelper(info.Client, info.Mapping).
|
||||
DryRun(o.DryRunStrategy == cmdutil.DryRunServer).
|
||||
Replace(info.Namespace, info.Name, true, info.Object)
|
||||
if err != nil {
|
||||
return cmdutil.AddSourceToErr("replacing", info.Source, err)
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ go_library(
|
||||
deps = [
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
|
@ -24,8 +24,9 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||
@ -44,6 +45,9 @@ type TaintOptions struct {
|
||||
PrintFlags *genericclioptions.PrintFlags
|
||||
ToPrinter func(string) (printers.ResourcePrinter, error)
|
||||
|
||||
DryRunStrategy cmdutil.DryRunStrategy
|
||||
DryRunVerifier *resource.DryRunVerifier
|
||||
|
||||
resources []string
|
||||
taintsToAdd []v1.Taint
|
||||
taintsToRemove []v1.Taint
|
||||
@ -109,6 +113,7 @@ func NewCmdTaint(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.
|
||||
}
|
||||
|
||||
options.PrintFlags.AddFlags(cmd)
|
||||
cmdutil.AddDryRunFlag(cmd)
|
||||
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmd.Flags().StringVarP(&options.selector, "selector", "l", options.selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
|
||||
@ -124,6 +129,21 @@ func (o *TaintOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st
|
||||
return err
|
||||
}
|
||||
|
||||
o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dynamicClient, err := f.DynamicClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
discoveryClient, err := f.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.DryRunVerifier = resource.NewDryRunVerifier(dynamicClient, discoveryClient)
|
||||
cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
|
||||
|
||||
// retrieves resource and taint args from args
|
||||
// also checks args to verify that all resources are specified before taints
|
||||
taintArgs := []string{}
|
||||
@ -261,12 +281,54 @@ func (o TaintOptions) RunTaint() error {
|
||||
klog.V(2).Infof("couldn't compute patch: %v", err)
|
||||
}
|
||||
|
||||
printer, err := o.ToPrinter(operation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if o.DryRunStrategy == cmdutil.DryRunClient {
|
||||
if createdPatch {
|
||||
typedObj, err := scheme.Scheme.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nodeObj, ok := typedObj.(*v1.Node)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T", typedObj)
|
||||
}
|
||||
|
||||
originalObjJS, err := json.Marshal(nodeObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
originalPatchedObjJS, err := strategicpatch.StrategicMergePatch(originalObjJS, patchBytes, nodeObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
targetObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, originalPatchedObjJS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(targetObj, o.Out)
|
||||
}
|
||||
return printer.PrintObj(obj, o.Out)
|
||||
}
|
||||
|
||||
mapping := info.ResourceMapping()
|
||||
if o.DryRunStrategy == cmdutil.DryRunServer {
|
||||
if err := o.DryRunVerifier.HasSupport(mapping.GroupVersionKind); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
client, err := o.ClientForMapping(mapping)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helper := resource.NewHelper(client, mapping)
|
||||
helper := resource.
|
||||
NewHelper(client, mapping).
|
||||
DryRun(o.DryRunStrategy == cmdutil.DryRunServer)
|
||||
|
||||
var outputObj runtime.Object
|
||||
if createdPatch {
|
||||
@ -278,10 +340,6 @@ func (o TaintOptions) RunTaint() error {
|
||||
return err
|
||||
}
|
||||
|
||||
printer, err := o.ToPrinter(operation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return printer.PrintObj(outputObj, o.Out)
|
||||
})
|
||||
}
|
||||
|
@ -583,6 +583,34 @@ run_pod_tests() {
|
||||
}
|
||||
}
|
||||
__EOF__
|
||||
kube::test::get_object_assert "node node-v1-test" "{{range.items}}{{if .metadata.annotations.a}}found{{end}}{{end}}:" ':'
|
||||
|
||||
# Dry-run command
|
||||
kubectl replace --dry-run=server -f - "${kube_flags[@]}" << __EOF__
|
||||
{
|
||||
"kind": "Node",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "node-v1-test",
|
||||
"annotations": {"a":"b"},
|
||||
"resourceVersion": "0"
|
||||
}
|
||||
}
|
||||
__EOF__
|
||||
kubectl replace --dry-run=client -f - "${kube_flags[@]}" << __EOF__
|
||||
{
|
||||
"kind": "Node",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {
|
||||
"name": "node-v1-test",
|
||||
"annotations": {"a":"b"},
|
||||
"resourceVersion": "0"
|
||||
}
|
||||
}
|
||||
__EOF__
|
||||
kube::test::get_object_assert "node node-v1-test" "{{range.items}}{{if .metadata.annotations.a}}found{{end}}{{end}}:" ':'
|
||||
|
||||
# Command
|
||||
kubectl replace -f - "${kube_flags[@]}" << __EOF__
|
||||
{
|
||||
"kind": "Node",
|
||||
|
@ -32,6 +32,15 @@ run_kubectl_delete_allnamespaces_tests() {
|
||||
kubectl create configmap "two" --namespace="${ns_two}"
|
||||
kubectl label configmap "one" --namespace="${ns_one}" deletetest=true
|
||||
kubectl label configmap "two" --namespace="${ns_two}" deletetest=true
|
||||
|
||||
# dry-run
|
||||
kubectl delete configmap --dry-run=client -l deletetest=true --all-namespaces
|
||||
kubectl delete configmap --dry-run=server -l deletetest=true --all-namespaces
|
||||
kubectl config set-context "${CONTEXT}" --namespace="${ns_one}"
|
||||
kube::test::get_object_assert configmap "{{range.items}}{{${id_field:?}}}:{{end}}" 'one:'
|
||||
kubectl config set-context "${CONTEXT}" --namespace="${ns_two}"
|
||||
kube::test::get_object_assert configmap "{{range.items}}{{${id_field:?}}}:{{end}}" 'two:'
|
||||
|
||||
kubectl delete configmap -l deletetest=true --all-namespaces
|
||||
|
||||
# no configmaps should be in either of those namespaces
|
||||
|
@ -75,6 +75,10 @@ __EOF__
|
||||
# taint/untaint
|
||||
# Pre-condition: node doesn't have dedicated=foo:PreferNoSchedule taint
|
||||
kube::test::get_object_assert "nodes 127.0.0.1" '{{range .spec.taints}}{{if eq .key \"dedicated\"}}{{.key}}={{.value}}:{{.effect}}{{end}}{{end}}' "" # expect no output
|
||||
# Dry-run
|
||||
kubectl taint node 127.0.0.1 --dry-run=client dedicated=foo:PreferNoSchedule
|
||||
kubectl taint node 127.0.0.1 --dry-run=server dedicated=foo:PreferNoSchedule
|
||||
kube::test::get_object_assert "nodes 127.0.0.1" '{{range .spec.taints}}{{if eq .key \"dedicated\"}}{{.key}}={{.value}}:{{.effect}}{{end}}{{end}}' "" # expect no output
|
||||
# taint can add a taint (<key>=<value>:<effect>)
|
||||
kubectl taint node 127.0.0.1 dedicated=foo:PreferNoSchedule
|
||||
kube::test::get_object_assert "nodes 127.0.0.1" '{{range .spec.taints}}{{if eq .key \"dedicated\"}}{{.key}}={{.value}}:{{.effect}}{{end}}{{end}}' "dedicated=foo:PreferNoSchedule"
|
||||
@ -83,6 +87,10 @@ __EOF__
|
||||
# taint can add a taint (<key>:<effect>)
|
||||
kubectl taint node 127.0.0.1 dedicated:PreferNoSchedule
|
||||
kube::test::get_object_assert "nodes 127.0.0.1" '{{range .spec.taints}}{{if eq .key \"dedicated\"}}{{.key}}={{.value}}:{{.effect}}{{end}}{{end}}' "dedicated=<no value>:PreferNoSchedule"
|
||||
# Dry-run remove a taint
|
||||
kubectl taint node 127.0.0.1 --dry-run=client dedicated-
|
||||
kubectl taint node 127.0.0.1 --dry-run=server dedicated-
|
||||
kube::test::get_object_assert "nodes 127.0.0.1" '{{range .spec.taints}}{{if eq .key \"dedicated\"}}{{.key}}={{.value}}:{{.effect}}{{end}}{{end}}' "dedicated=<no value>:PreferNoSchedule"
|
||||
# taint can remove a taint
|
||||
kubectl taint node 127.0.0.1 dedicated-
|
||||
# Post-condition: node doesn't have dedicated=foo:PreferNoSchedule taint
|
||||
|
Loading…
Reference in New Issue
Block a user