Support kubectl delete foreground

Kubernetes-commit: 383b5f676670d99dc1fba4254ba7f6a81a052ba3
This commit is contained in:
zhouya0 2020-07-23 18:45:59 +08:00 committed by Kubernetes Publisher
parent 18af7c6ad0
commit 8411619cbf
9 changed files with 155 additions and 90 deletions

View File

@ -244,7 +244,11 @@ func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
return err return err
} }
o.DeleteOptions = o.DeleteFlags.ToOptions(o.DynamicClient, o.IOStreams) o.DeleteOptions, err = o.DeleteFlags.ToOptions(o.DynamicClient, o.IOStreams)
if err != nil {
return err
}
err = o.DeleteOptions.FilenameOptions.RequireFilenameOrKustomize() err = o.DeleteOptions.FilenameOptions.RequireFilenameOrKustomize()
if err != nil { if err != nil {
return err return err

View File

@ -25,6 +25,7 @@ import (
"github.com/jonboulle/clockwork" "github.com/jonboulle/clockwork"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@ -58,7 +59,7 @@ type Patcher struct {
BackOff clockwork.Clock BackOff clockwork.Clock
Force bool Force bool
Cascade bool CascadingStrategy metav1.DeletionPropagation
Timeout time.Duration Timeout time.Duration
GracePeriod int GracePeriod int
@ -83,7 +84,7 @@ func newPatcher(o *ApplyOptions, info *resource.Info, helper *resource.Helper) (
Overwrite: o.Overwrite, Overwrite: o.Overwrite,
BackOff: clockwork.NewRealClock(), BackOff: clockwork.NewRealClock(),
Force: o.DeleteOptions.ForceDeletion, Force: o.DeleteOptions.ForceDeletion,
Cascade: o.DeleteOptions.Cascade, CascadingStrategy: o.DeleteOptions.CascadingStrategy,
Timeout: o.DeleteOptions.Timeout, Timeout: o.DeleteOptions.Timeout,
GracePeriod: o.DeleteOptions.GracePeriod, GracePeriod: o.DeleteOptions.GracePeriod,
OpenapiSchema: openapiSchema, OpenapiSchema: openapiSchema,
@ -92,7 +93,7 @@ func newPatcher(o *ApplyOptions, info *resource.Info, helper *resource.Helper) (
} }
func (p *Patcher) delete(namespace, name string) error { func (p *Patcher) delete(namespace, name string) error {
options := asDeleteOptions(p.Cascade, p.GracePeriod) options := asDeleteOptions(p.CascadingStrategy, p.GracePeriod)
_, err := p.Helper.DeleteWithOptions(namespace, name, &options) _, err := p.Helper.DeleteWithOptions(namespace, name, &options)
return err return err
} }

View File

@ -41,7 +41,7 @@ type pruner struct {
labelSelector string labelSelector string
fieldSelector string fieldSelector string
cascade bool cascadingStrategy metav1.DeletionPropagation
dryRunStrategy cmdutil.DryRunStrategy dryRunStrategy cmdutil.DryRunStrategy
gracePeriod int gracePeriod int
@ -59,7 +59,7 @@ func newPruner(o *ApplyOptions) pruner {
visitedUids: o.VisitedUids, visitedUids: o.VisitedUids,
visitedNamespaces: o.VisitedNamespaces, visitedNamespaces: o.VisitedNamespaces,
cascade: o.DeleteOptions.Cascade, cascadingStrategy: o.DeleteOptions.CascadingStrategy,
dryRunStrategy: o.DryRunStrategy, dryRunStrategy: o.DryRunStrategy,
gracePeriod: o.DeleteOptions.GracePeriod, gracePeriod: o.DeleteOptions.GracePeriod,
@ -139,27 +139,23 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping) error {
} }
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, p.dryRunStrategy == cmdutil.DryRunServer) return runDelete(namespace, name, mapping, p.dynamicClient, p.cascadingStrategy, p.gracePeriod, p.dryRunStrategy == cmdutil.DryRunServer)
} }
func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascade bool, gracePeriod int, serverDryRun bool) error { func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascadingStrategy metav1.DeletionPropagation, gracePeriod int, serverDryRun bool) error {
options := asDeleteOptions(cascade, gracePeriod) options := asDeleteOptions(cascadingStrategy, gracePeriod)
if serverDryRun { if serverDryRun {
options.DryRun = []string{metav1.DryRunAll} options.DryRun = []string{metav1.DryRunAll}
} }
return c.Resource(mapping.Resource).Namespace(namespace).Delete(context.TODO(), name, options) return c.Resource(mapping.Resource).Namespace(namespace).Delete(context.TODO(), name, options)
} }
func asDeleteOptions(cascade bool, gracePeriod int) metav1.DeleteOptions { func asDeleteOptions(cascadingStrategy metav1.DeletionPropagation, gracePeriod int) metav1.DeleteOptions {
options := metav1.DeleteOptions{} options := metav1.DeleteOptions{}
if gracePeriod >= 0 { if gracePeriod >= 0 {
options = *metav1.NewDeleteOptions(int64(gracePeriod)) options = *metav1.NewDeleteOptions(int64(gracePeriod))
} }
policy := metav1.DeletePropagationForeground options.PropagationPolicy = &cascadingStrategy
if !cascade {
policy = metav1.DeletePropagationOrphan
}
options.PropagationPolicy = &policy
return options return options
} }

View File

@ -102,8 +102,8 @@ type DeleteOptions struct {
FieldSelector string FieldSelector string
DeleteAll bool DeleteAll bool
DeleteAllNamespaces bool DeleteAllNamespaces bool
CascadingStrategy metav1.DeletionPropagation
IgnoreNotFound bool IgnoreNotFound bool
Cascade bool
DeleteNow bool DeleteNow bool
ForceDeletion bool ForceDeletion bool
WaitForDeletion bool WaitForDeletion bool
@ -136,7 +136,8 @@ func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra
Long: deleteLong, Long: deleteLong,
Example: deleteExample, Example: deleteExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
o := deleteFlags.ToOptions(nil, streams) o, err := deleteFlags.ToOptions(nil, streams)
cmdutil.CheckErr(err)
cmdutil.CheckErr(o.Complete(f, args, cmd)) cmdutil.CheckErr(o.Complete(f, args, cmd))
cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunDelete(f)) cmdutil.CheckErr(o.RunDelete(f))
@ -301,11 +302,7 @@ func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
if o.GracePeriod >= 0 { if o.GracePeriod >= 0 {
options = metav1.NewDeleteOptions(int64(o.GracePeriod)) options = metav1.NewDeleteOptions(int64(o.GracePeriod))
} }
policy := metav1.DeletePropagationBackground options.PropagationPolicy = &o.CascadingStrategy
if !o.Cascade {
policy = metav1.DeletePropagationOrphan
}
options.PropagationPolicy = &policy
if warnClusterScope && info.Mapping.Scope.Name() == meta.RESTScopeNameRoot { if warnClusterScope && info.Mapping.Scope.Name() == meta.RESTScopeNameRoot {
fmt.Fprintf(o.ErrOut, "warning: deleting cluster-scoped resources, not scoped to the provided namespace\n") fmt.Fprintf(o.ErrOut, "warning: deleting cluster-scoped resources, not scoped to the provided namespace\n")

View File

@ -17,12 +17,16 @@ limitations under the License.
package delete package delete
import ( import (
"fmt"
"strconv"
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/klog/v2"
) )
// DeleteFlags composes common printer flag structs // DeleteFlags composes common printer flag structs
@ -34,7 +38,7 @@ type DeleteFlags struct {
All *bool All *bool
AllNamespaces *bool AllNamespaces *bool
Cascade *bool CascadingStrategy *string
Force *bool Force *bool
GracePeriod *int GracePeriod *int
IgnoreNotFound *bool IgnoreNotFound *bool
@ -45,7 +49,7 @@ type DeleteFlags struct {
Raw *string Raw *string
} }
func (f *DeleteFlags) ToOptions(dynamicClient dynamic.Interface, streams genericclioptions.IOStreams) *DeleteOptions { func (f *DeleteFlags) ToOptions(dynamicClient dynamic.Interface, streams genericclioptions.IOStreams) (*DeleteOptions, error) {
options := &DeleteOptions{ options := &DeleteOptions{
DynamicClient: dynamicClient, DynamicClient: dynamicClient,
IOStreams: streams, IOStreams: streams,
@ -73,8 +77,12 @@ func (f *DeleteFlags) ToOptions(dynamicClient dynamic.Interface, streams generic
if f.AllNamespaces != nil { if f.AllNamespaces != nil {
options.DeleteAllNamespaces = *f.AllNamespaces options.DeleteAllNamespaces = *f.AllNamespaces
} }
if f.Cascade != nil { if f.CascadingStrategy != nil {
options.Cascade = *f.Cascade var err error
options.CascadingStrategy, err = getCascadingStrategy(*f.CascadingStrategy)
if err != nil {
return nil, err
}
} }
if f.Force != nil { if f.Force != nil {
options.ForceDeletion = *f.Force options.ForceDeletion = *f.Force
@ -98,7 +106,7 @@ func (f *DeleteFlags) ToOptions(dynamicClient dynamic.Interface, streams generic
options.Raw = *f.Raw options.Raw = *f.Raw
} }
return options return options, nil
} }
func (f *DeleteFlags) AddFlags(cmd *cobra.Command) { func (f *DeleteFlags) AddFlags(cmd *cobra.Command) {
@ -118,8 +126,13 @@ func (f *DeleteFlags) AddFlags(cmd *cobra.Command) {
if f.Force != nil { if f.Force != nil {
cmd.Flags().BoolVar(f.Force, "force", *f.Force, "If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation.") cmd.Flags().BoolVar(f.Force, "force", *f.Force, "If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation.")
} }
if f.Cascade != nil { if f.CascadingStrategy != nil {
cmd.Flags().BoolVar(f.Cascade, "cascade", *f.Cascade, "If true, run background cascade deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.") cmd.Flags().StringVar(
f.CascadingStrategy,
"cascade",
*f.CascadingStrategy,
`Must be "background", "orphan", or "foreground". Selects the deletion cascading strategy for the dependents (e.g. Pods created by a ReplicationController). Defaults to background.`)
cmd.Flags().Lookup("cascade").NoOptDefVal = "background"
} }
if f.Now != nil { if f.Now != nil {
cmd.Flags().BoolVar(f.Now, "now", *f.Now, "If true, resources are signaled for immediate shutdown (same as --grace-period=1).") cmd.Flags().BoolVar(f.Now, "now", *f.Now, "If true, resources are signaled for immediate shutdown (same as --grace-period=1).")
@ -146,7 +159,7 @@ func (f *DeleteFlags) AddFlags(cmd *cobra.Command) {
// NewDeleteCommandFlags provides default flags and values for use with the "delete" command // NewDeleteCommandFlags provides default flags and values for use with the "delete" command
func NewDeleteCommandFlags(usage string) *DeleteFlags { func NewDeleteCommandFlags(usage string) *DeleteFlags {
cascade := true cascadingStrategy := "background"
gracePeriod := -1 gracePeriod := -1
// setup command defaults // setup command defaults
@ -172,7 +185,7 @@ func NewDeleteCommandFlags(usage string) *DeleteFlags {
LabelSelector: &labelSelector, LabelSelector: &labelSelector,
FieldSelector: &fieldSelector, FieldSelector: &fieldSelector,
Cascade: &cascade, CascadingStrategy: &cascadingStrategy,
GracePeriod: &gracePeriod, GracePeriod: &gracePeriod,
All: &all, All: &all,
@ -189,7 +202,7 @@ func NewDeleteCommandFlags(usage string) *DeleteFlags {
// NewDeleteFlags provides default flags and values for use in commands outside of "delete" // NewDeleteFlags provides default flags and values for use in commands outside of "delete"
func NewDeleteFlags(usage string) *DeleteFlags { func NewDeleteFlags(usage string) *DeleteFlags {
cascade := true cascadingStrategy := "background"
gracePeriod := -1 gracePeriod := -1
force := false force := false
@ -203,7 +216,7 @@ func NewDeleteFlags(usage string) *DeleteFlags {
return &DeleteFlags{ return &DeleteFlags{
FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Kustomize: &kustomize, Recursive: &recursive}, FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Kustomize: &kustomize, Recursive: &recursive},
Cascade: &cascade, CascadingStrategy: &cascadingStrategy,
GracePeriod: &gracePeriod, GracePeriod: &gracePeriod,
// add non-defaults // add non-defaults
@ -212,3 +225,27 @@ func NewDeleteFlags(usage string) *DeleteFlags {
Wait: &wait, Wait: &wait,
} }
} }
func getCascadingStrategy(cascadingFlag string) (metav1.DeletionPropagation, error) {
b, err := strconv.ParseBool(cascadingFlag)
// The flag is not a boolean
if err != nil {
switch cascadingFlag {
case "orphan":
return metav1.DeletePropagationOrphan, nil
case "foreground":
return metav1.DeletePropagationForeground, nil
case "background":
return metav1.DeletePropagationBackground, nil
default:
return metav1.DeletePropagationBackground, fmt.Errorf(`Invalid cascade value (%v). Must be "background", "foreground", or "orphan".`, cascadingFlag)
}
}
// The flag was a boolean
if b {
klog.Warningf(`--cascade=%v is deprecated (boolean value) and can be replaced with --cascade=%s.`, cascadingFlag, "background")
return metav1.DeletePropagationBackground, nil
}
klog.Warningf(`--cascade=%v is deprecated (boolean value) and can be replaced with --cascade=%s.`, cascadingFlag, "orphan")
return metav1.DeletePropagationOrphan, nil
}

View File

@ -112,8 +112,8 @@ func hasExpectedPropagationPolicy(body io.ReadCloser, policy *metav1.DeletionPro
return *policy == *parsedBody.PropagationPolicy return *policy == *parsedBody.PropagationPolicy
} }
// Tests that DeleteOptions.OrphanDependents is appropriately set while deleting objects. // TestCascadingStrategy tests that DeleteOptions.DeletionPropagation is appropriately set while deleting objects.
func TestOrphanDependentsInDeleteObject(t *testing.T) { func TestCascadingStrategy(t *testing.T) {
cmdtesting.InitTestErrorHandler(t) cmdtesting.InitTestErrorHandler(t)
_, _, rc := cmdtesting.TestData() _, _, rc := cmdtesting.TestData()
@ -137,7 +137,7 @@ func TestOrphanDependentsInDeleteObject(t *testing.T) {
}), }),
} }
// DeleteOptions.PropagationPolicy should be Background, when cascade is true (default). // DeleteOptions.PropagationPolicy should be Background, when cascading strategy is empty (default).
backgroundPolicy := metav1.DeletePropagationBackground backgroundPolicy := metav1.DeletePropagationBackground
policy = &backgroundPolicy policy = &backgroundPolicy
streams, _, buf, _ := genericclioptions.NewTestIOStreams() streams, _, buf, _ := genericclioptions.NewTestIOStreams()
@ -149,13 +149,26 @@ func TestOrphanDependentsInDeleteObject(t *testing.T) {
t.Errorf("unexpected output: %s", buf.String()) t.Errorf("unexpected output: %s", buf.String())
} }
// Test that delete options should be set to orphan when cascade is false. // DeleteOptions.PropagationPolicy should be Foreground, when cascading strategy is foreground.
foregroundPolicy := metav1.DeletePropagationForeground
policy = &foregroundPolicy
streams, _, buf, _ = genericclioptions.NewTestIOStreams()
cmd = NewCmdDelete(tf, streams)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "foreground")
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{"secrets/mysecret"})
if buf.String() != "secret/mysecret\n" {
t.Errorf("unexpected output: %s", buf.String())
}
// Test that delete options should be set to orphan when cascading strategy is orphan.
orphanPolicy := metav1.DeletePropagationOrphan orphanPolicy := metav1.DeletePropagationOrphan
policy = &orphanPolicy policy = &orphanPolicy
streams, _, buf, _ = genericclioptions.NewTestIOStreams() streams, _, buf, _ = genericclioptions.NewTestIOStreams()
cmd = NewCmdDelete(tf, streams) cmd = NewCmdDelete(tf, streams)
cmd.Flags().Set("namespace", "test") cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false") cmd.Flags().Set("cascade", "orphan")
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{"secrets/mysecret"}) cmd.Run(cmd, []string{"secrets/mysecret"})
if buf.String() != "secret/mysecret\n" { if buf.String() != "secret/mysecret\n" {
@ -428,7 +441,7 @@ func TestDeleteObjectNotFound(t *testing.T) {
Filenames: []string{"../../../testdata/redis-master-controller.yaml"}, Filenames: []string{"../../../testdata/redis-master-controller.yaml"},
}, },
GracePeriod: -1, GracePeriod: -1,
Cascade: false, CascadingStrategy: metav1.DeletePropagationOrphan,
Output: "name", Output: "name",
IOStreams: genericclioptions.NewTestIOStreamsDiscard(), IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
} }
@ -506,7 +519,7 @@ func TestDeleteAllNotFound(t *testing.T) {
options := &DeleteOptions{ options := &DeleteOptions{
FilenameOptions: resource.FilenameOptions{}, FilenameOptions: resource.FilenameOptions{},
GracePeriod: -1, GracePeriod: -1,
Cascade: false, CascadingStrategy: metav1.DeletePropagationOrphan,
DeleteAll: true, DeleteAll: true,
IgnoreNotFound: false, IgnoreNotFound: false,
Output: "name", Output: "name",
@ -631,7 +644,7 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
Filenames: []string{"../../../testdata/redis-master-controller.yaml", "../../../testdata/frontend-service.yaml"}, Filenames: []string{"../../../testdata/redis-master-controller.yaml", "../../../testdata/frontend-service.yaml"},
}, },
GracePeriod: -1, GracePeriod: -1,
Cascade: false, CascadingStrategy: metav1.DeletePropagationOrphan,
Output: "name", Output: "name",
IOStreams: streams, IOStreams: streams,
} }
@ -803,7 +816,7 @@ func TestResourceErrors(t *testing.T) {
options := &DeleteOptions{ options := &DeleteOptions{
FilenameOptions: resource.FilenameOptions{}, FilenameOptions: resource.FilenameOptions{},
GracePeriod: -1, GracePeriod: -1,
Cascade: false, CascadingStrategy: metav1.DeletePropagationOrphan,
Output: "name", Output: "name",
IOStreams: streams, IOStreams: streams,
} }

View File

@ -171,7 +171,10 @@ func (o *ReplaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
deleteOpts := o.DeleteFlags.ToOptions(dynamicClient, o.IOStreams) deleteOpts, err := o.DeleteFlags.ToOptions(dynamicClient, o.IOStreams)
if err != nil {
return err
}
//Replace will create a resource if it doesn't exist already, so ignore not found error //Replace will create a resource if it doesn't exist already, so ignore not found error
deleteOpts.IgnoreNotFound = true deleteOpts.IgnoreNotFound = true

View File

@ -246,7 +246,11 @@ func (o *RunOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }
deleteOpts := o.DeleteFlags.ToOptions(dynamicClient, o.IOStreams) deleteOpts, err := o.DeleteFlags.ToOptions(dynamicClient, o.IOStreams)
if err != nil {
return err
}
deleteOpts.IgnoreNotFound = true deleteOpts.IgnoreNotFound = true
deleteOpts.WaitForDeletion = false deleteOpts.WaitForDeletion = false
deleteOpts.GracePeriod = -1 deleteOpts.GracePeriod = -1

View File

@ -199,9 +199,14 @@ func TestRunArgsFollowDashRules(t *testing.T) {
} }
deleteFlags := delete.NewDeleteFlags("to use to replace the resource.") deleteFlags := delete.NewDeleteFlags("to use to replace the resource.")
deleteOptions, err := deleteFlags.ToOptions(nil, genericclioptions.NewTestIOStreamsDiscard())
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
opts := &RunOptions{ opts := &RunOptions{
PrintFlags: printFlags, PrintFlags: printFlags,
DeleteOptions: deleteFlags.ToOptions(nil, genericclioptions.NewTestIOStreamsDiscard()), DeleteOptions: deleteOptions,
IOStreams: genericclioptions.NewTestIOStreamsDiscard(), IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
@ -371,9 +376,14 @@ func TestGenerateService(t *testing.T) {
ioStreams, _, buff, _ := genericclioptions.NewTestIOStreams() ioStreams, _, buff, _ := genericclioptions.NewTestIOStreams()
deleteFlags := delete.NewDeleteFlags("to use to replace the resource.") deleteFlags := delete.NewDeleteFlags("to use to replace the resource.")
deleteOptions, err := deleteFlags.ToOptions(nil, genericclioptions.NewTestIOStreamsDiscard())
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
opts := &RunOptions{ opts := &RunOptions{
PrintFlags: printFlags, PrintFlags: printFlags,
DeleteOptions: deleteFlags.ToOptions(nil, genericclioptions.NewTestIOStreamsDiscard()), DeleteOptions: deleteOptions,
IOStreams: ioStreams, IOStreams: ioStreams,