mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			410 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			410 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2014 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package cmd
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/golang/glog"
 | 
						|
	"github.com/spf13/cobra"
 | 
						|
 | 
						|
	"k8s.io/apimachinery/pkg/api/errors"
 | 
						|
	"k8s.io/apimachinery/pkg/api/meta"
 | 
						|
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						|
	"k8s.io/apimachinery/pkg/util/wait"
 | 
						|
	"k8s.io/client-go/dynamic"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
 | 
						|
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
 | 
						|
	kubectlwait "k8s.io/kubernetes/pkg/kubectl/cmd/wait"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
 | 
						|
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	delete_long = templates.LongDesc(i18n.T(`
 | 
						|
		Delete resources by filenames, stdin, resources and names, or by resources and label selector.
 | 
						|
 | 
						|
		JSON and YAML formats are accepted. Only one type of the arguments may be specified: filenames,
 | 
						|
		resources and names, or resources and label selector.
 | 
						|
 | 
						|
		Some resources, such as pods, support graceful deletion. These resources define a default period
 | 
						|
		before they are forcibly terminated (the grace period) but you may override that value with
 | 
						|
		the --grace-period flag, or pass --now to set a grace-period of 1. Because these resources often
 | 
						|
		represent entities in the cluster, deletion may not be acknowledged immediately. If the node
 | 
						|
		hosting a pod is down or cannot reach the API server, termination may take significantly longer
 | 
						|
		than the grace period. To force delete a resource, you must pass a grace period of 0 and specify
 | 
						|
		the --force flag.
 | 
						|
 | 
						|
		IMPORTANT: Force deleting pods does not wait for confirmation that the pod's processes have been
 | 
						|
		terminated, which can leave those processes running until the node detects the deletion and
 | 
						|
		completes graceful deletion. If your processes use shared storage or talk to a remote API and
 | 
						|
		depend on the name of the pod to identify themselves, force deleting those pods may result in
 | 
						|
		multiple processes running on different machines using the same identification which may lead
 | 
						|
		to data corruption or inconsistency. Only force delete pods when you are sure the pod is
 | 
						|
		terminated, or if your application can tolerate multiple copies of the same pod running at once.
 | 
						|
		Also, if you force delete pods the scheduler may place new pods on those nodes before the node
 | 
						|
		has released those resources and causing those pods to be evicted immediately.
 | 
						|
 | 
						|
		Note that the delete command does NOT do resource version checks, so if someone submits an
 | 
						|
		update to a resource right when you submit a delete, their update will be lost along with the
 | 
						|
		rest of the resource.`))
 | 
						|
 | 
						|
	delete_example = templates.Examples(i18n.T(`
 | 
						|
		# Delete a pod using the type and name specified in pod.json.
 | 
						|
		kubectl delete -f ./pod.json
 | 
						|
 | 
						|
		# Delete a pod based on the type and name in the JSON passed into stdin.
 | 
						|
		cat pod.json | kubectl delete -f -
 | 
						|
 | 
						|
		# Delete pods and services with same names "baz" and "foo"
 | 
						|
		kubectl delete pod,service baz foo
 | 
						|
 | 
						|
		# Delete pods and services with label name=myLabel.
 | 
						|
		kubectl delete pods,services -l name=myLabel
 | 
						|
 | 
						|
		# Delete a pod with minimal delay
 | 
						|
		kubectl delete pod foo --now
 | 
						|
 | 
						|
		# Force delete a pod on a dead node
 | 
						|
		kubectl delete pod foo --grace-period=0 --force
 | 
						|
 | 
						|
		# Delete all pods
 | 
						|
		kubectl delete pods --all`))
 | 
						|
)
 | 
						|
 | 
						|
type DeleteOptions struct {
 | 
						|
	resource.FilenameOptions
 | 
						|
 | 
						|
	LabelSelector   string
 | 
						|
	FieldSelector   string
 | 
						|
	DeleteAll       bool
 | 
						|
	IgnoreNotFound  bool
 | 
						|
	Cascade         bool
 | 
						|
	DeleteNow       bool
 | 
						|
	ForceDeletion   bool
 | 
						|
	WaitForDeletion bool
 | 
						|
 | 
						|
	Reaper func(mapping *meta.RESTMapping) (kubectl.Reaper, error)
 | 
						|
 | 
						|
	GracePeriod int
 | 
						|
	Timeout     time.Duration
 | 
						|
 | 
						|
	Output string
 | 
						|
 | 
						|
	DynamicClient dynamic.Interface
 | 
						|
	Mapper        meta.RESTMapper
 | 
						|
	Result        *resource.Result
 | 
						|
 | 
						|
	genericclioptions.IOStreams
 | 
						|
}
 | 
						|
 | 
						|
func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
 | 
						|
	deleteFlags := NewDeleteCommandFlags("containing the resource to delete.")
 | 
						|
 | 
						|
	cmd := &cobra.Command{
 | 
						|
		Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
 | 
						|
		DisableFlagsInUseLine: true,
 | 
						|
		Short:   i18n.T("Delete resources by filenames, stdin, resources and names, or by resources and label selector"),
 | 
						|
		Long:    delete_long,
 | 
						|
		Example: delete_example,
 | 
						|
		Run: func(cmd *cobra.Command, args []string) {
 | 
						|
			o := deleteFlags.ToOptions(nil, streams)
 | 
						|
			if err := o.Complete(f, args, cmd); err != nil {
 | 
						|
				cmdutil.CheckErr(err)
 | 
						|
			}
 | 
						|
			if err := o.Validate(cmd); err != nil {
 | 
						|
				cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, err.Error()))
 | 
						|
			}
 | 
						|
			if err := o.RunDelete(); err != nil {
 | 
						|
				cmdutil.CheckErr(err)
 | 
						|
			}
 | 
						|
		},
 | 
						|
		SuggestFor: []string{"rm"},
 | 
						|
	}
 | 
						|
 | 
						|
	deleteFlags.AddFlags(cmd)
 | 
						|
 | 
						|
	cmd.Flags().Bool("wait", true, `If true, wait for resources to be gone before returning.  This waits for finalizers.`)
 | 
						|
 | 
						|
	cmdutil.AddIncludeUninitializedFlag(cmd)
 | 
						|
	return cmd
 | 
						|
}
 | 
						|
 | 
						|
func (o *DeleteOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error {
 | 
						|
	cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	if o.DeleteAll || len(o.LabelSelector) > 0 || len(o.FieldSelector) > 0 {
 | 
						|
		if f := cmd.Flags().Lookup("ignore-not-found"); f != nil && !f.Changed {
 | 
						|
			// If the user didn't explicitly set the option, default to ignoring NotFound errors when used with --all, -l, or --field-selector
 | 
						|
			o.IgnoreNotFound = true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if o.DeleteNow {
 | 
						|
		if o.GracePeriod != -1 {
 | 
						|
			return fmt.Errorf("--now and --grace-period cannot be specified together")
 | 
						|
		}
 | 
						|
		o.GracePeriod = 1
 | 
						|
	}
 | 
						|
	if o.GracePeriod == 0 && !o.ForceDeletion {
 | 
						|
		// To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0
 | 
						|
		// into --grace-period=1 and wait until the object is successfully deleted. Users may provide --force
 | 
						|
		// to bypass this wait.
 | 
						|
		o.WaitForDeletion = true
 | 
						|
		o.GracePeriod = 1
 | 
						|
	}
 | 
						|
	if b, err := cmd.Flags().GetBool("wait"); err == nil {
 | 
						|
		o.WaitForDeletion = b
 | 
						|
	}
 | 
						|
 | 
						|
	o.Reaper = f.Reaper
 | 
						|
 | 
						|
	includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
 | 
						|
	r := f.NewBuilder().
 | 
						|
		Unstructured().
 | 
						|
		ContinueOnError().
 | 
						|
		NamespaceParam(cmdNamespace).DefaultNamespace().
 | 
						|
		FilenameParam(enforceNamespace, &o.FilenameOptions).
 | 
						|
		LabelSelectorParam(o.LabelSelector).
 | 
						|
		FieldSelectorParam(o.FieldSelector).
 | 
						|
		IncludeUninitialized(includeUninitialized).
 | 
						|
		SelectAllParam(o.DeleteAll).
 | 
						|
		ResourceTypeOrNameArgs(false, args...).RequireObject(false).
 | 
						|
		Flatten().
 | 
						|
		Do()
 | 
						|
	err = r.Err()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	o.Result = r
 | 
						|
 | 
						|
	o.Mapper, err = f.ToRESTMapper()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	o.DynamicClient, err = f.DynamicClient()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (o *DeleteOptions) Validate(cmd *cobra.Command) error {
 | 
						|
	if o.Output != "" && o.Output != "name" {
 | 
						|
		return cmdutil.UsageErrorf(cmd, "Unexpected -o output mode: %v. We only support '-o name'.", o.Output)
 | 
						|
	}
 | 
						|
 | 
						|
	if o.DeleteAll && len(o.LabelSelector) > 0 {
 | 
						|
		return fmt.Errorf("cannot set --all and --selector at the same time")
 | 
						|
	}
 | 
						|
	if o.DeleteAll && len(o.FieldSelector) > 0 {
 | 
						|
		return fmt.Errorf("cannot set --all and --field-selector at the same time")
 | 
						|
	}
 | 
						|
 | 
						|
	switch {
 | 
						|
	case o.GracePeriod == 0 && o.ForceDeletion:
 | 
						|
		fmt.Fprintf(o.ErrOut, "warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.\n")
 | 
						|
	case o.ForceDeletion:
 | 
						|
		fmt.Fprintf(o.ErrOut, "warning: --force is ignored because --grace-period is not 0.\n")
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (o *DeleteOptions) RunDelete() error {
 | 
						|
	// By default use a reaper to delete all related resources.
 | 
						|
	if o.Cascade {
 | 
						|
		// TODO(juanvallejo): although o.Result can be accessed from the options
 | 
						|
		// it is also passed here so that callers of this method outside of the "delete"
 | 
						|
		// command do not have to tack it to the "delete" options as well.
 | 
						|
		// Find a cleaner way to approach this.
 | 
						|
		return o.ReapResult(o.Result, true, false)
 | 
						|
	}
 | 
						|
	return o.DeleteResult(o.Result)
 | 
						|
}
 | 
						|
 | 
						|
func (o *DeleteOptions) ReapResult(r *resource.Result, isDefaultDelete, quiet bool) error {
 | 
						|
	found := 0
 | 
						|
	if o.IgnoreNotFound {
 | 
						|
		r = r.IgnoreErrors(errors.IsNotFound)
 | 
						|
	}
 | 
						|
	err := r.Visit(func(info *resource.Info, err error) error {
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		found++
 | 
						|
		reaper, err := o.Reaper(info.Mapping)
 | 
						|
		if err != nil {
 | 
						|
			// If there is no reaper for this resources and the user didn't explicitly ask for stop.
 | 
						|
			if kubectl.IsNoSuchReaperError(err) && isDefaultDelete {
 | 
						|
				// No client side reaper found. Let the server do cascading deletion.
 | 
						|
				return o.cascadingDeleteResource(info)
 | 
						|
			}
 | 
						|
			return cmdutil.AddSourceToErr("reaping", info.Source, err)
 | 
						|
		}
 | 
						|
		var options *metav1.DeleteOptions
 | 
						|
		if o.GracePeriod >= 0 {
 | 
						|
			options = metav1.NewDeleteOptions(int64(o.GracePeriod))
 | 
						|
		}
 | 
						|
		if err := reaper.Stop(info.Namespace, info.Name, o.Timeout, options); err != nil {
 | 
						|
			return cmdutil.AddSourceToErr("stopping", info.Source, err)
 | 
						|
		}
 | 
						|
		if o.WaitForDeletion {
 | 
						|
			if err := waitForObjectDeletion(info, o.Timeout); err != nil {
 | 
						|
				return cmdutil.AddSourceToErr("stopping", info.Source, err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if !quiet {
 | 
						|
			o.PrintObj(info)
 | 
						|
		}
 | 
						|
		return nil
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if found == 0 {
 | 
						|
		fmt.Fprintf(o.Out, "No resources found\n")
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
 | 
						|
	found := 0
 | 
						|
	if o.IgnoreNotFound {
 | 
						|
		r = r.IgnoreErrors(errors.IsNotFound)
 | 
						|
	}
 | 
						|
	err := r.Visit(func(info *resource.Info, err error) error {
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		found++
 | 
						|
 | 
						|
		// if we're here, it means that cascade=false (not the default), so we should orphan as requested
 | 
						|
		options := &metav1.DeleteOptions{}
 | 
						|
		if o.GracePeriod >= 0 {
 | 
						|
			options = metav1.NewDeleteOptions(int64(o.GracePeriod))
 | 
						|
		}
 | 
						|
		policy := metav1.DeletePropagationOrphan
 | 
						|
		options.PropagationPolicy = &policy
 | 
						|
		return o.deleteResource(info, options)
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if found == 0 {
 | 
						|
		fmt.Fprintf(o.Out, "No resources found\n")
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	if !o.WaitForDeletion {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	// if we don't have a dynamic client, we don't want to wait.  Eventually when delete is cleaned up, this will likely
 | 
						|
	// drop out.
 | 
						|
	if o.DynamicClient == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	effectiveTimeout := o.Timeout
 | 
						|
	if effectiveTimeout == 0 {
 | 
						|
		// if we requested to wait forever, set it to a week.
 | 
						|
		effectiveTimeout = 168 * time.Hour
 | 
						|
	}
 | 
						|
	waitOptions := kubectlwait.WaitOptions{
 | 
						|
		ResourceFinder: kubectlwait.ResourceFinderForResult(o.Result),
 | 
						|
		DynamicClient:  o.DynamicClient,
 | 
						|
		Timeout:        effectiveTimeout,
 | 
						|
 | 
						|
		Printer:     printers.NewDiscardingPrinter(),
 | 
						|
		ConditionFn: kubectlwait.IsDeleted,
 | 
						|
		IOStreams:   o.IOStreams,
 | 
						|
	}
 | 
						|
	err = waitOptions.RunWait()
 | 
						|
	if errors.IsForbidden(err) {
 | 
						|
		// if we're forbidden from waiting, we shouldn't fail.
 | 
						|
		glog.V(1).Info(err)
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (o *DeleteOptions) cascadingDeleteResource(info *resource.Info) error {
 | 
						|
	policy := metav1.DeletePropagationForeground
 | 
						|
	return o.deleteResource(info, &metav1.DeleteOptions{PropagationPolicy: &policy})
 | 
						|
}
 | 
						|
 | 
						|
func (o *DeleteOptions) deleteResource(info *resource.Info, deleteOptions *metav1.DeleteOptions) error {
 | 
						|
	if err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, deleteOptions); err != nil {
 | 
						|
		return cmdutil.AddSourceToErr("deleting", info.Source, err)
 | 
						|
	}
 | 
						|
 | 
						|
	o.PrintObj(info)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// deletion printing is special because we do not have an object to print.
 | 
						|
// This mirrors name printer behavior
 | 
						|
func (o *DeleteOptions) PrintObj(info *resource.Info) {
 | 
						|
	operation := "deleted"
 | 
						|
	groupKind := info.Mapping.GroupVersionKind
 | 
						|
	kindString := fmt.Sprintf("%s.%s", strings.ToLower(groupKind.Kind), groupKind.Group)
 | 
						|
	if len(groupKind.Group) == 0 {
 | 
						|
		kindString = strings.ToLower(groupKind.Kind)
 | 
						|
	}
 | 
						|
 | 
						|
	if o.GracePeriod == 0 {
 | 
						|
		operation = "force deleted"
 | 
						|
	}
 | 
						|
 | 
						|
	if o.Output == "name" {
 | 
						|
		// -o name: prints resource/name
 | 
						|
		fmt.Fprintf(o.Out, "%s/%s\n", kindString, info.Name)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// understandable output by default
 | 
						|
	fmt.Fprintf(o.Out, "%s \"%s\" %s\n", kindString, info.Name, operation)
 | 
						|
}
 | 
						|
 | 
						|
// objectDeletionWaitInterval is the interval to wait between checks for deletion.
 | 
						|
var objectDeletionWaitInterval = time.Second
 | 
						|
 | 
						|
// waitForObjectDeletion refreshes the object, waiting until it is deleted, a timeout is reached, or
 | 
						|
// an error is encountered. It checks once a second.
 | 
						|
func waitForObjectDeletion(info *resource.Info, timeout time.Duration) error {
 | 
						|
	copied := *info
 | 
						|
	info = &copied
 | 
						|
	// TODO: refactor Reaper so that we can pass the "wait" option into it, and then check for UID change.
 | 
						|
	return wait.PollImmediate(objectDeletionWaitInterval, timeout, func() (bool, error) {
 | 
						|
		switch err := info.Get(); {
 | 
						|
		case err == nil:
 | 
						|
			return false, nil
 | 
						|
		case errors.IsNotFound(err):
 | 
						|
			return true, nil
 | 
						|
		default:
 | 
						|
			return false, err
 | 
						|
		}
 | 
						|
	})
 | 
						|
}
 |