2014-10-06 01:24:19 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
2014-10-06 01:24:19 +00:00
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 (
2014-10-27 02:21:31 +00:00
"fmt"
2018-02-21 01:14:21 +00:00
"strings"
2015-05-29 23:16:30 +00:00
"time"
2014-10-06 01:24:19 +00:00
2018-05-18 12:12:55 +00:00
"github.com/golang/glog"
2014-10-06 01:24:19 +00:00
"github.com/spf13/cobra"
2014-12-15 17:32:46 +00:00
2018-05-25 14:11:40 +00:00
appsv1 "k8s.io/api/apps/v1"
2017-01-13 17:48:50 +00:00
"k8s.io/apimachinery/pkg/api/errors"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/api/meta"
2017-01-24 15:38:21 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2018-05-25 14:11:40 +00:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/util/wait"
2018-05-18 12:12:55 +00:00
"k8s.io/client-go/dynamic"
2016-10-07 22:24:42 +00:00
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
2015-08-05 22:03:47 +00:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2018-05-18 12:12:55 +00:00
kubectlwait "k8s.io/kubernetes/pkg/kubectl/cmd/wait"
2018-05-08 13:02:34 +00:00
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
2018-05-22 12:46:51 +00:00
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/printers"
2018-05-10 12:20:27 +00:00
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
2017-07-07 04:04:11 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
2014-10-06 01:24:19 +00:00
)
2016-05-20 17:49:56 +00:00
var (
2017-03-15 03:49:10 +00:00
delete_long = templates . LongDesc ( i18n . T ( `
2016-05-20 17:49:56 +00:00
Delete resources by filenames , stdin , resources and names , or by resources and label selector .
2014-10-06 01:24:19 +00:00
2016-10-25 02:43:37 +00:00
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
2017-08-02 13:27:51 +00:00
than the grace period . To force delete a resource , you must pass a grace period of 0 and specify
2016-10-25 02:43:37 +00:00
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 .
2014-10-06 01:24:19 +00:00
2017-08-02 13:27:51 +00:00
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 . ` ) )
2016-10-07 22:24:42 +00:00
2017-03-15 03:49:10 +00:00
delete_example = templates . Examples ( i18n . T ( `
2016-05-20 17:49:56 +00:00
# Delete a pod using the type and name specified in pod . json .
kubectl delete - f . / pod . json
2014-10-06 01:24:19 +00:00
2016-05-20 17:49:56 +00:00
# Delete a pod based on the type and name in the JSON passed into stdin .
cat pod . json | kubectl delete - f -
2014-12-15 17:32:46 +00:00
2016-05-20 17:49:56 +00:00
# Delete pods and services with same names "baz" and "foo"
kubectl delete pod , service baz foo
2015-08-19 08:33:02 +00:00
2016-05-20 17:49:56 +00:00
# Delete pods and services with label name = myLabel .
kubectl delete pods , services - l name = myLabel
2015-02-03 17:59:21 +00:00
2016-10-25 02:43:37 +00:00
# Delete a pod with minimal delay
2016-05-20 17:49:56 +00:00
kubectl delete pod foo -- now
2016-04-01 19:27:06 +00:00
2016-10-25 02:43:37 +00:00
# Force delete a pod on a dead node
kubectl delete pod foo -- grace - period = 0 -- force
2016-05-20 17:49:56 +00:00
# Delete all pods
2017-03-15 03:49:10 +00:00
kubectl delete pods -- all ` ) )
2015-02-20 21:28:43 +00:00
)
2015-02-12 14:59:58 +00:00
2017-01-15 07:30:06 +00:00
type DeleteOptions struct {
resource . FilenameOptions
2018-03-02 16:33:02 +00:00
LabelSelector string
FieldSelector string
2017-01-15 07:30:06 +00:00
DeleteAll bool
IgnoreNotFound bool
Cascade bool
DeleteNow bool
ForceDeletion bool
WaitForDeletion bool
GracePeriod int
Timeout time . Duration
2018-04-18 18:29:20 +00:00
Output string
2017-01-15 07:30:06 +00:00
2018-05-18 12:12:55 +00:00
DynamicClient dynamic . Interface
Mapper meta . RESTMapper
Result * resource . Result
2017-01-15 07:30:06 +00:00
2018-05-08 13:02:34 +00:00
genericclioptions . IOStreams
2017-01-15 07:30:06 +00:00
}
2018-05-08 13:02:34 +00:00
func NewCmdDelete ( f cmdutil . Factory , streams genericclioptions . IOStreams ) * cobra . Command {
2018-04-14 00:28:28 +00:00
deleteFlags := NewDeleteCommandFlags ( "containing the resource to delete." )
2015-09-02 22:38:40 +00:00
2015-02-20 21:28:43 +00:00
cmd := & cobra . Command {
2017-10-11 06:26:02 +00:00
Use : "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])" ,
DisableFlagsInUseLine : true ,
2017-01-25 01:00:32 +00:00
Short : i18n . T ( "Delete resources by filenames, stdin, resources and names, or by resources and label selector" ) ,
2015-02-20 21:28:43 +00:00
Long : delete_long ,
Example : delete_example ,
2014-10-06 01:24:19 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2018-05-18 12:12:55 +00:00
o := deleteFlags . ToOptions ( nil , streams )
2018-05-17 15:27:44 +00:00
cmdutil . CheckErr ( o . Complete ( f , args , cmd ) )
cmdutil . CheckErr ( o . Validate ( cmd ) )
cmdutil . CheckErr ( o . RunDelete ( ) )
2014-10-06 01:24:19 +00:00
} ,
2016-05-05 00:18:52 +00:00
SuggestFor : [ ] string { "rm" } ,
2014-10-06 01:24:19 +00:00
}
2018-04-09 23:13:50 +00:00
2018-04-14 00:28:28 +00:00
deleteFlags . AddFlags ( cmd )
2018-05-18 12:12:55 +00:00
cmd . Flags ( ) . Bool ( "wait" , true , ` If true, wait for resources to be gone before returning. This waits for finalizers. ` )
2017-08-11 06:21:44 +00:00
cmdutil . AddIncludeUninitializedFlag ( cmd )
2014-10-06 01:24:19 +00:00
return cmd
}
2015-03-09 22:08:16 +00:00
2018-05-08 13:02:34 +00:00
func ( o * DeleteOptions ) Complete ( f cmdutil . Factory , args [ ] string , cmd * cobra . Command ) error {
2015-06-26 20:49:34 +00:00
cmdNamespace , enforceNamespace , err := f . DefaultNamespace ( )
2015-03-09 22:08:16 +00:00
if err != nil {
return err
}
2017-01-15 07:30:06 +00:00
2018-05-07 14:41:45 +00:00
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
}
2018-05-18 12:12:55 +00:00
if b , err := cmd . Flags ( ) . GetBool ( "wait" ) ; err == nil {
o . WaitForDeletion = b
}
2018-05-07 14:41:45 +00:00
2017-08-11 06:21:44 +00:00
includeUninitialized := cmdutil . ShouldIncludeUninitialized ( cmd , false )
2017-11-14 03:43:58 +00:00
r := f . NewBuilder ( ) .
2017-11-14 04:01:51 +00:00
Unstructured ( ) .
2015-03-09 22:08:16 +00:00
ContinueOnError ( ) .
NamespaceParam ( cmdNamespace ) . DefaultNamespace ( ) .
2017-01-15 07:30:06 +00:00
FilenameParam ( enforceNamespace , & o . FilenameOptions ) .
2018-03-02 16:33:02 +00:00
LabelSelectorParam ( o . LabelSelector ) .
FieldSelectorParam ( o . FieldSelector ) .
2017-08-11 06:21:44 +00:00
IncludeUninitialized ( includeUninitialized ) .
2017-01-15 07:30:06 +00:00
SelectAllParam ( o . DeleteAll ) .
2015-04-20 19:00:52 +00:00
ResourceTypeOrNameArgs ( false , args ... ) . RequireObject ( false ) .
2015-03-09 22:08:16 +00:00
Flatten ( ) .
Do ( )
err = r . Err ( )
if err != nil {
return err
}
2017-01-15 07:30:06 +00:00
o . Result = r
2018-04-27 15:38:34 +00:00
2018-05-16 14:54:42 +00:00
o . Mapper , err = f . ToRESTMapper ( )
2018-04-27 15:38:34 +00:00
if err != nil {
return err
}
2017-01-15 07:30:06 +00:00
2018-05-18 12:12:55 +00:00
o . DynamicClient , err = f . DynamicClient ( )
if err != nil {
return err
}
2017-01-15 07:30:06 +00:00
return nil
}
2017-03-17 13:55:46 +00:00
func ( o * DeleteOptions ) Validate ( cmd * cobra . Command ) error {
2018-05-02 18:49:49 +00:00
if o . Output != "" && o . Output != "name" {
return cmdutil . UsageErrorf ( cmd , "Unexpected -o output mode: %v. We only support '-o name'." , o . Output )
2018-05-01 19:48:46 +00:00
}
2018-03-02 16:33:02 +00:00
if o . DeleteAll && len ( o . LabelSelector ) > 0 {
2017-08-25 08:28:21 +00:00
return fmt . Errorf ( "cannot set --all and --selector at the same time" )
}
2018-03-02 16:33:02 +00:00
if o . DeleteAll && len ( o . FieldSelector ) > 0 {
return fmt . Errorf ( "cannot set --all and --field-selector at the same time" )
}
2018-05-07 14:41:45 +00:00
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 :
2018-03-20 02:48:38 +00:00
fmt . Fprintf ( o . ErrOut , "warning: --force is ignored because --grace-period is not 0.\n" )
2016-04-01 19:27:06 +00:00
}
2017-01-15 07:30:06 +00:00
return nil
}
2016-04-01 19:27:06 +00:00
2017-01-15 07:30:06 +00:00
func ( o * DeleteOptions ) RunDelete ( ) error {
2018-04-09 23:13:50 +00:00
return o . DeleteResult ( o . Result )
2015-04-23 04:15:15 +00:00
}
2018-04-09 23:13:50 +00:00
func ( o * DeleteOptions ) DeleteResult ( r * resource . Result ) error {
2015-04-23 04:15:15 +00:00
found := 0
2018-04-09 23:13:50 +00:00
if o . IgnoreNotFound {
2015-05-21 17:01:34 +00:00
r = r . IgnoreErrors ( errors . IsNotFound )
}
2015-06-15 02:48:56 +00:00
err := r . Visit ( func ( info * resource . Info , err error ) error {
if err != nil {
return err
}
2015-04-23 04:15:15 +00:00
found ++
2017-05-11 12:07:25 +00:00
2017-07-07 04:22:38 +00:00
options := & metav1 . DeleteOptions { }
2018-04-09 23:13:50 +00:00
if o . GracePeriod >= 0 {
options = metav1 . NewDeleteOptions ( int64 ( o . GracePeriod ) )
2017-07-07 04:22:38 +00:00
}
2018-05-17 15:27:44 +00:00
policy := metav1 . DeletePropagationForeground
if ! o . Cascade {
policy = metav1 . DeletePropagationOrphan
}
2018-02-14 04:32:57 +00:00
options . PropagationPolicy = & policy
2018-04-09 23:13:50 +00:00
return o . deleteResource ( info , options )
2015-04-23 04:15:15 +00:00
} )
if err != nil {
return err
}
if found == 0 {
2018-04-09 23:13:50 +00:00
fmt . Fprintf ( o . Out , "No resources found\n" )
2018-05-18 12:12:55 +00:00
return nil
2015-04-23 04:15:15 +00:00
}
2018-05-18 12:12:55 +00:00
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 {
2018-05-24 17:36:30 +00:00
ResourceFinder : genericclioptions . ResourceFinderForResult ( o . Result ) ,
2018-05-18 12:12:55 +00:00
DynamicClient : o . DynamicClient ,
Timeout : effectiveTimeout ,
2018-05-22 12:46:51 +00:00
Printer : printers . NewDiscardingPrinter ( ) ,
2018-05-18 12:12:55 +00:00
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
2015-04-23 04:15:15 +00:00
}
2018-04-09 23:13:50 +00:00
func ( o * DeleteOptions ) deleteResource ( info * resource . Info , deleteOptions * metav1 . DeleteOptions ) error {
2018-05-25 14:11:40 +00:00
// TODO: this should be removed as soon as DaemonSet controller properly handles object deletion
// see https://github.com/kubernetes/kubernetes/issues/64313 for details
mapping := info . ResourceMapping ( )
if mapping . Resource . GroupResource ( ) == ( schema . GroupResource { Group : "extensions" , Resource : "daemonsets" } ) ||
mapping . Resource . GroupResource ( ) == ( schema . GroupResource { Group : "apps" , Resource : "daemonsets" } ) {
if err := updateDaemonSet ( info . Namespace , info . Name , o . DynamicClient ) ; err != nil {
return err
}
}
2017-01-27 03:15:59 +00:00
if err := resource . NewHelper ( info . Client , info . Mapping ) . DeleteWithOptions ( info . Namespace , info . Name , deleteOptions ) ; err != nil {
2015-06-23 12:26:27 +00:00
return cmdutil . AddSourceToErr ( "deleting" , info . Source , err )
2015-03-09 22:08:16 +00:00
}
2018-02-21 01:14:21 +00:00
2018-04-09 23:13:50 +00:00
o . PrintObj ( info )
2015-03-09 22:08:16 +00:00
return nil
}
2016-11-22 03:22:24 +00:00
2018-05-25 14:11:40 +00:00
func updateDaemonSet ( namespace , name string , dynamicClient dynamic . Interface ) error {
dsClient := dynamicClient . Resource ( schema . GroupVersionResource { Group : "apps" , Version : "v1" , Resource : "daemonsets" } ) . Namespace ( namespace )
obj , err := dsClient . Get ( name , metav1 . GetOptions { } )
if err != nil {
return err
}
ds := & appsv1 . DaemonSet { }
if err := runtime . DefaultUnstructuredConverter . FromUnstructured ( obj . Object , ds ) ; err != nil {
return err
}
// We set the nodeSelector to a random label. This label is nearly guaranteed
// to not be set on any node so the DameonSetController will start deleting
// daemon pods. Once it's done deleting the daemon pods, it's safe to delete
// the DaemonSet.
ds . Spec . Template . Spec . NodeSelector = map [ string ] string {
string ( uuid . NewUUID ( ) ) : string ( uuid . NewUUID ( ) ) ,
}
// force update to avoid version conflict
ds . ResourceVersion = ""
out , err := runtime . DefaultUnstructuredConverter . ToUnstructured ( ds )
if err != nil {
return err
}
if _ , err = dsClient . Update ( & unstructured . Unstructured { Object : out } ) ; err != nil {
return err
}
// Wait for the daemon set controller to kill all the daemon pods.
if err := wait . Poll ( 1 * time . Second , 5 * time . Minute , func ( ) ( bool , error ) {
updatedObj , err := dsClient . Get ( name , metav1 . GetOptions { } )
if err != nil {
return false , nil
}
updatedDS := & appsv1 . DaemonSet { }
if err := runtime . DefaultUnstructuredConverter . FromUnstructured ( updatedObj . Object , ds ) ; err != nil {
return false , nil
}
return updatedDS . Status . CurrentNumberScheduled + updatedDS . Status . NumberMisscheduled == 0 , nil
} ) ; err != nil {
return err
}
return nil
}
2018-04-09 23:13:50 +00:00
// 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 ) {
2018-02-21 01:14:21 +00:00
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 )
}
2018-04-09 23:13:50 +00:00
if o . GracePeriod == 0 {
2018-03-20 02:48:38 +00:00
operation = "force deleted"
}
2018-04-09 23:13:50 +00:00
if o . Output == "name" {
2018-02-21 01:14:21 +00:00
// -o name: prints resource/name
2018-04-09 23:13:50 +00:00
fmt . Fprintf ( o . Out , "%s/%s\n" , kindString , info . Name )
2018-02-21 01:14:21 +00:00
return
}
// understandable output by default
2018-04-09 23:13:50 +00:00
fmt . Fprintf ( o . Out , "%s \"%s\" %s\n" , kindString , info . Name , operation )
2018-02-21 01:14:21 +00:00
}