mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 10:53:34 +00:00
Fresh dep ensure
This commit is contained in:
61
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete/BUILD
generated
vendored
Normal file
61
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete/BUILD
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"delete.go",
|
||||
"delete_flags.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/delete",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/kubectl/cmd/util:go_default_library",
|
||||
"//pkg/kubectl/cmd/wait:go_default_library",
|
||||
"//pkg/kubectl/util/i18n:go_default_library",
|
||||
"//pkg/kubectl/util/templates:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/printers:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/dynamic:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["delete_test.go"],
|
||||
data = [
|
||||
"//test/e2e/testing-manifests:all-srcs",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/kubectl/cmd/testing:go_default_library",
|
||||
"//pkg/kubectl/scheme:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library",
|
||||
"//staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest/fake:go_default_library",
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
342
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete/delete.go
generated
vendored
Normal file
342
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete/delete.go
generated
vendored
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
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 delete
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
|
||||
"k8s.io/client-go/dynamic"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
kubectlwait "k8s.io/kubernetes/pkg/kubectl/cmd/wait"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/templates"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
cmdutil.CheckErr(o.Complete(f, args, cmd))
|
||||
cmdutil.CheckErr(o.Validate())
|
||||
cmdutil.CheckErr(o.RunDelete())
|
||||
},
|
||||
SuggestFor: []string{"rm"},
|
||||
}
|
||||
|
||||
deleteFlags.AddFlags(cmd)
|
||||
|
||||
cmdutil.AddIncludeUninitializedFlag(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *DeleteOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error {
|
||||
cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
|
||||
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. Users may provide --force to bypass this conversion.
|
||||
o.GracePeriod = 1
|
||||
}
|
||||
|
||||
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() error {
|
||||
if o.Output != "" && o.Output != "name" {
|
||||
return fmt.Errorf("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 {
|
||||
return o.DeleteResult(o.Result)
|
||||
}
|
||||
|
||||
func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
|
||||
found := 0
|
||||
if o.IgnoreNotFound {
|
||||
r = r.IgnoreErrors(errors.IsNotFound)
|
||||
}
|
||||
deletedInfos := []*resource.Info{}
|
||||
uidMap := kubectlwait.UIDMap{}
|
||||
err := r.Visit(func(info *resource.Info, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deletedInfos = append(deletedInfos, info)
|
||||
found++
|
||||
|
||||
options := &metav1.DeleteOptions{}
|
||||
if o.GracePeriod >= 0 {
|
||||
options = metav1.NewDeleteOptions(int64(o.GracePeriod))
|
||||
}
|
||||
policy := metav1.DeletePropagationBackground
|
||||
if !o.Cascade {
|
||||
policy = metav1.DeletePropagationOrphan
|
||||
}
|
||||
options.PropagationPolicy = &policy
|
||||
|
||||
response, err := o.deleteResource(info, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resourceLocation := kubectlwait.ResourceLocation{
|
||||
GroupResource: info.Mapping.Resource.GroupResource(),
|
||||
Namespace: info.Namespace,
|
||||
Name: info.Name,
|
||||
}
|
||||
if status, ok := response.(*metav1.Status); ok && status.Details != nil {
|
||||
uidMap[resourceLocation] = status.Details.UID
|
||||
return nil
|
||||
}
|
||||
responseMetadata, err := meta.Accessor(response)
|
||||
if err != nil {
|
||||
// we don't have UID, but we didn't fail the delete, next best thing is just skipping the UID
|
||||
klog.V(1).Info(err)
|
||||
return nil
|
||||
}
|
||||
uidMap[resourceLocation] = responseMetadata.GetUID()
|
||||
|
||||
return nil
|
||||
})
|
||||
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: genericclioptions.ResourceFinderForResult(resource.InfoListVisitor(deletedInfos)),
|
||||
UIDMap: uidMap,
|
||||
DynamicClient: o.DynamicClient,
|
||||
Timeout: effectiveTimeout,
|
||||
|
||||
Printer: printers.NewDiscardingPrinter(),
|
||||
ConditionFn: kubectlwait.IsDeleted,
|
||||
IOStreams: o.IOStreams,
|
||||
}
|
||||
err = waitOptions.RunWait()
|
||||
if errors.IsForbidden(err) || errors.IsMethodNotSupported(err) {
|
||||
// if we're forbidden from waiting, we shouldn't fail.
|
||||
// if the resource doesn't support a verb we need, we shouldn't fail.
|
||||
klog.V(1).Info(err)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, cmdutil.AddSourceToErr("deleting", info.Source, err)
|
||||
}
|
||||
|
||||
o.PrintObj(info)
|
||||
return deleteResponse, 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)
|
||||
}
|
193
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete/delete_flags.go
generated
vendored
Normal file
193
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete/delete_flags.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
Copyright 2018 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 delete
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/client-go/dynamic"
|
||||
)
|
||||
|
||||
// PrintFlags composes common printer flag structs
|
||||
// used for commands requiring deletion logic.
|
||||
type DeleteFlags struct {
|
||||
FileNameFlags *genericclioptions.FileNameFlags
|
||||
LabelSelector *string
|
||||
FieldSelector *string
|
||||
|
||||
All *bool
|
||||
Cascade *bool
|
||||
Force *bool
|
||||
GracePeriod *int
|
||||
IgnoreNotFound *bool
|
||||
Now *bool
|
||||
Timeout *time.Duration
|
||||
Wait *bool
|
||||
Output *string
|
||||
}
|
||||
|
||||
func (f *DeleteFlags) ToOptions(dynamicClient dynamic.Interface, streams genericclioptions.IOStreams) *DeleteOptions {
|
||||
options := &DeleteOptions{
|
||||
DynamicClient: dynamicClient,
|
||||
IOStreams: streams,
|
||||
}
|
||||
|
||||
// add filename options
|
||||
if f.FileNameFlags != nil {
|
||||
options.FilenameOptions = f.FileNameFlags.ToOptions()
|
||||
}
|
||||
if f.LabelSelector != nil {
|
||||
options.LabelSelector = *f.LabelSelector
|
||||
}
|
||||
if f.FieldSelector != nil {
|
||||
options.FieldSelector = *f.FieldSelector
|
||||
}
|
||||
|
||||
// add output format
|
||||
if f.Output != nil {
|
||||
options.Output = *f.Output
|
||||
}
|
||||
|
||||
if f.All != nil {
|
||||
options.DeleteAll = *f.All
|
||||
}
|
||||
if f.Cascade != nil {
|
||||
options.Cascade = *f.Cascade
|
||||
}
|
||||
if f.Force != nil {
|
||||
options.ForceDeletion = *f.Force
|
||||
}
|
||||
if f.GracePeriod != nil {
|
||||
options.GracePeriod = *f.GracePeriod
|
||||
}
|
||||
if f.IgnoreNotFound != nil {
|
||||
options.IgnoreNotFound = *f.IgnoreNotFound
|
||||
}
|
||||
if f.Now != nil {
|
||||
options.DeleteNow = *f.Now
|
||||
}
|
||||
if f.Timeout != nil {
|
||||
options.Timeout = *f.Timeout
|
||||
}
|
||||
if f.Wait != nil {
|
||||
options.WaitForDeletion = *f.Wait
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
func (f *DeleteFlags) AddFlags(cmd *cobra.Command) {
|
||||
f.FileNameFlags.AddFlags(cmd.Flags())
|
||||
if f.LabelSelector != nil {
|
||||
cmd.Flags().StringVarP(f.LabelSelector, "selector", "l", *f.LabelSelector, "Selector (label query) to filter on, not including uninitialized ones.")
|
||||
}
|
||||
if f.FieldSelector != nil {
|
||||
cmd.Flags().StringVarP(f.FieldSelector, "field-selector", "", *f.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.")
|
||||
}
|
||||
if f.All != nil {
|
||||
cmd.Flags().BoolVar(f.All, "all", *f.All, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.")
|
||||
}
|
||||
if f.Force != nil {
|
||||
cmd.Flags().BoolVar(f.Force, "force", *f.Force, "Only used when grace-period=0. 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 {
|
||||
cmd.Flags().BoolVar(f.Cascade, "cascade", *f.Cascade, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.")
|
||||
}
|
||||
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).")
|
||||
}
|
||||
if f.GracePeriod != nil {
|
||||
cmd.Flags().IntVar(f.GracePeriod, "grace-period", *f.GracePeriod, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true (force deletion).")
|
||||
}
|
||||
if f.Timeout != nil {
|
||||
cmd.Flags().DurationVar(f.Timeout, "timeout", *f.Timeout, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
|
||||
}
|
||||
if f.IgnoreNotFound != nil {
|
||||
cmd.Flags().BoolVar(f.IgnoreNotFound, "ignore-not-found", *f.IgnoreNotFound, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.")
|
||||
}
|
||||
if f.Wait != nil {
|
||||
cmd.Flags().BoolVar(f.Wait, "wait", *f.Wait, "If true, wait for resources to be gone before returning. This waits for finalizers.")
|
||||
}
|
||||
if f.Output != nil {
|
||||
cmd.Flags().StringVarP(f.Output, "output", "o", *f.Output, "Output mode. Use \"-o name\" for shorter output (resource/name).")
|
||||
}
|
||||
}
|
||||
|
||||
// NewDeleteCommandFlags provides default flags and values for use with the "delete" command
|
||||
func NewDeleteCommandFlags(usage string) *DeleteFlags {
|
||||
cascade := true
|
||||
gracePeriod := -1
|
||||
|
||||
// setup command defaults
|
||||
all := false
|
||||
force := false
|
||||
ignoreNotFound := false
|
||||
now := false
|
||||
output := ""
|
||||
labelSelector := ""
|
||||
fieldSelector := ""
|
||||
timeout := time.Duration(0)
|
||||
wait := true
|
||||
|
||||
filenames := []string{}
|
||||
recursive := false
|
||||
|
||||
return &DeleteFlags{
|
||||
FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive},
|
||||
LabelSelector: &labelSelector,
|
||||
FieldSelector: &fieldSelector,
|
||||
|
||||
Cascade: &cascade,
|
||||
GracePeriod: &gracePeriod,
|
||||
|
||||
All: &all,
|
||||
Force: &force,
|
||||
IgnoreNotFound: &ignoreNotFound,
|
||||
Now: &now,
|
||||
Timeout: &timeout,
|
||||
Wait: &wait,
|
||||
Output: &output,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDeleteFlags provides default flags and values for use in commands outside of "delete"
|
||||
func NewDeleteFlags(usage string) *DeleteFlags {
|
||||
cascade := true
|
||||
gracePeriod := -1
|
||||
|
||||
force := false
|
||||
timeout := time.Duration(0)
|
||||
wait := false
|
||||
|
||||
filenames := []string{}
|
||||
recursive := false
|
||||
|
||||
return &DeleteFlags{
|
||||
FileNameFlags: &genericclioptions.FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive},
|
||||
|
||||
Cascade: &cascade,
|
||||
GracePeriod: &gracePeriod,
|
||||
|
||||
// add non-defaults
|
||||
Force: &force,
|
||||
Timeout: &timeout,
|
||||
Wait: &wait,
|
||||
}
|
||||
}
|
713
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete/delete_test.go
generated
vendored
Normal file
713
vendor/k8s.io/kubernetes/pkg/kubectl/cmd/delete/delete_test.go
generated
vendored
Normal file
@ -0,0 +1,713 @@
|
||||
/*
|
||||
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 delete
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
|
||||
"k8s.io/client-go/rest/fake"
|
||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
)
|
||||
|
||||
func fakecmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
|
||||
DisableFlagsInUseLine: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func TestDeleteObjectByTuple(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
_, _, rc := cmdtesting.TestData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
|
||||
// replication controller with cascade off
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &rc.Items[0])}, nil
|
||||
|
||||
// secret with cascade on, but no client-side reaper
|
||||
case p == "/namespaces/test/secrets/mysecret" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &rc.Items[0])}, nil
|
||||
|
||||
default:
|
||||
// Ensures no GET is performed when deleting by name
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"replicationcontrollers/redis-master-controller"})
|
||||
if buf.String() != "replicationcontroller/redis-master-controller\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
|
||||
// Test cascading delete of object without client-side reaper doesn't make GET requests
|
||||
streams, _, buf, _ = genericclioptions.NewTestIOStreams()
|
||||
cmd = NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"secrets/mysecret"})
|
||||
if buf.String() != "secret/mysecret\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func hasExpectedPropagationPolicy(body io.ReadCloser, policy *metav1.DeletionPropagation) bool {
|
||||
if body == nil || policy == nil {
|
||||
return body == nil && policy == nil
|
||||
}
|
||||
var parsedBody metav1.DeleteOptions
|
||||
rawBody, _ := ioutil.ReadAll(body)
|
||||
json.Unmarshal(rawBody, &parsedBody)
|
||||
if parsedBody.PropagationPolicy == nil {
|
||||
return false
|
||||
}
|
||||
return *policy == *parsedBody.PropagationPolicy
|
||||
}
|
||||
|
||||
// Tests that DeleteOptions.OrphanDependents is appropriately set while deleting objects.
|
||||
func TestOrphanDependentsInDeleteObject(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
_, _, rc := cmdtesting.TestData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
var policy *metav1.DeletionPropagation
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m, b := req.URL.Path, req.Method, req.Body; {
|
||||
|
||||
case p == "/namespaces/test/secrets/mysecret" && m == "DELETE" && hasExpectedPropagationPolicy(b, policy):
|
||||
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &rc.Items[0])}, nil
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
// DeleteOptions.PropagationPolicy should be Background, when cascade is true (default).
|
||||
backgroundPolicy := metav1.DeletePropagationBackground
|
||||
policy = &backgroundPolicy
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
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 cascade is false.
|
||||
orphanPolicy := metav1.DeletePropagationOrphan
|
||||
policy = &orphanPolicy
|
||||
streams, _, buf, _ = genericclioptions.NewTestIOStreams()
|
||||
cmd = NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"secrets/mysecret"})
|
||||
if buf.String() != "secret/mysecret\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteNamedObject(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
_, _, rc := cmdtesting.TestData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
|
||||
// replication controller with cascade off
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master-controller" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &rc.Items[0])}, nil
|
||||
|
||||
// secret with cascade on, but no client-side reaper
|
||||
case p == "/namespaces/test/secrets/mysecret" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &rc.Items[0])}, nil
|
||||
|
||||
default:
|
||||
// Ensures no GET is performed when deleting by name
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"replicationcontrollers", "redis-master-controller"})
|
||||
if buf.String() != "replicationcontroller/redis-master-controller\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
|
||||
// Test cascading delete of object without client-side reaper doesn't make GET requests
|
||||
streams, _, buf, _ = genericclioptions.NewTestIOStreams()
|
||||
cmd = NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"secrets", "mysecret"})
|
||||
if buf.String() != "secret/mysecret\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteObject(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
_, _, rc := cmdtesting.TestData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &rc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("filename", "../../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
// uses the name from the file, not the response
|
||||
if buf.String() != "replicationcontroller/redis-master\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteObjectGraceZero(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
pods, _, _ := cmdtesting.TestData()
|
||||
|
||||
count := 0
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
t.Logf("got request %s %s", req.Method, req.URL.Path)
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/pods/nginx" && m == "GET":
|
||||
count++
|
||||
switch count {
|
||||
case 1, 2, 3:
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &pods.Items[0])}, nil
|
||||
default:
|
||||
return &http.Response{StatusCode: 404, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &metav1.Status{})}, nil
|
||||
}
|
||||
case p == "/api/v1/namespaces/test" && m == "GET":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &corev1.Namespace{})}, nil
|
||||
case p == "/namespaces/test/pods/nginx" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &pods.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
streams, _, buf, errBuf := genericclioptions.NewTestIOStreams()
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Flags().Set("grace-period", "0")
|
||||
cmd.Run(cmd, []string{"pods/nginx"})
|
||||
|
||||
// uses the name from the file, not the response
|
||||
if buf.String() != "pod/nginx\n" {
|
||||
t.Errorf("unexpected output: %s\n---\n%s", buf.String(), errBuf.String())
|
||||
}
|
||||
if count != 0 {
|
||||
t.Errorf("unexpected calls to GET: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteObjectNotFound(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 404, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.StringBody("")}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
options := &DeleteOptions{
|
||||
FilenameOptions: resource.FilenameOptions{
|
||||
Filenames: []string{"../../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml"},
|
||||
},
|
||||
GracePeriod: -1,
|
||||
Cascade: false,
|
||||
Output: "name",
|
||||
IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
|
||||
}
|
||||
err := options.Complete(tf, []string{}, fakecmd())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = options.RunDelete()
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Errorf("unexpected error: expected NotFound, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteObjectIgnoreNotFound(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 404, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.StringBody("")}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("filename", "../../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("ignore-not-found", "true")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
if buf.String() != "" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAllNotFound(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
_, svc, _ := cmdtesting.TestData()
|
||||
// Add an item to the list which will result in a 404 on delete
|
||||
svc.Items = append(svc.Items, corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
|
||||
notFoundError := &errors.NewNotFound(corev1.Resource("services"), "foo").ErrStatus
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/services" && m == "GET":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, svc)}, nil
|
||||
case p == "/namespaces/test/services/foo" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 404, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, notFoundError)}, nil
|
||||
case p == "/namespaces/test/services/baz" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
// Make sure we can explicitly choose to fail on NotFound errors, even with --all
|
||||
options := &DeleteOptions{
|
||||
FilenameOptions: resource.FilenameOptions{},
|
||||
GracePeriod: -1,
|
||||
Cascade: false,
|
||||
DeleteAll: true,
|
||||
IgnoreNotFound: false,
|
||||
Output: "name",
|
||||
IOStreams: genericclioptions.NewTestIOStreamsDiscard(),
|
||||
}
|
||||
err := options.Complete(tf, []string{"services"}, fakecmd())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = options.RunDelete()
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Errorf("unexpected error: expected NotFound, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAllIgnoreNotFound(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
_, svc, _ := cmdtesting.TestData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
// Add an item to the list which will result in a 404 on delete
|
||||
svc.Items = append(svc.Items, corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
|
||||
notFoundError := &errors.NewNotFound(corev1.Resource("services"), "foo").ErrStatus
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/services" && m == "GET":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, svc)}, nil
|
||||
case p == "/namespaces/test/services/foo" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 404, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, notFoundError)}, nil
|
||||
case p == "/namespaces/test/services/baz" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("all", "true")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"services"})
|
||||
|
||||
if buf.String() != "service/baz\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteMultipleObject(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
_, svc, rc := cmdtesting.TestData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &rc.Items[0])}, nil
|
||||
case p == "/namespaces/test/services/frontend" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("filename", "../../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml")
|
||||
cmd.Flags().Set("filename", "../../../../test/e2e/testing-manifests/guestbook/frontend-service.yaml")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
if buf.String() != "replicationcontroller/redis-master\nservice/frontend\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
_, svc, _ := cmdtesting.TestData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/redis-master" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 404, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.StringBody("")}, nil
|
||||
case p == "/namespaces/test/services/frontend" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
|
||||
options := &DeleteOptions{
|
||||
FilenameOptions: resource.FilenameOptions{
|
||||
Filenames: []string{"../../../../test/e2e/testing-manifests/guestbook/legacy/redis-master-controller.yaml", "../../../../test/e2e/testing-manifests/guestbook/frontend-service.yaml"},
|
||||
},
|
||||
GracePeriod: -1,
|
||||
Cascade: false,
|
||||
Output: "name",
|
||||
IOStreams: streams,
|
||||
}
|
||||
err := options.Complete(tf, []string{}, fakecmd())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
err = options.RunDelete()
|
||||
if err == nil || !errors.IsNotFound(err) {
|
||||
t.Errorf("unexpected error: expected NotFound, got %v", err)
|
||||
}
|
||||
|
||||
if buf.String() != "service/frontend\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteMultipleResourcesWithTheSameName(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
_, svc, rc := cmdtesting.TestData()
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/replicationcontrollers/baz" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &rc.Items[0])}, nil
|
||||
case p == "/namespaces/test/replicationcontrollers/foo" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &rc.Items[0])}, nil
|
||||
case p == "/namespaces/test/services/baz" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &svc.Items[0])}, nil
|
||||
case p == "/namespaces/test/services/foo" && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
// Ensures no GET is performed when deleting by name
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("namespace", "test")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"replicationcontrollers,services", "baz", "foo"})
|
||||
if buf.String() != "replicationcontroller/baz\nreplicationcontroller/foo\nservice/baz\nservice/foo\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteDirectory(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
_, _, rc := cmdtesting.TestData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case strings.HasPrefix(p, "/namespaces/test/replicationcontrollers/") && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &rc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("filename", "../../../../test/e2e/testing-manifests/guestbook/legacy")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{})
|
||||
|
||||
if buf.String() != "replicationcontroller/frontend\nreplicationcontroller/redis-master\nreplicationcontroller/redis-slave\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteMultipleSelector(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
pods, svc, _ := cmdtesting.TestData()
|
||||
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
||||
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
|
||||
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/namespaces/test/pods" && m == "GET":
|
||||
if req.URL.Query().Get(metav1.LabelSelectorQueryParam("v1")) != "a=b" {
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, pods)}, nil
|
||||
case p == "/namespaces/test/services" && m == "GET":
|
||||
if req.URL.Query().Get(metav1.LabelSelectorQueryParam("v1")) != "a=b" {
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, svc)}, nil
|
||||
case strings.HasPrefix(p, "/namespaces/test/pods/") && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &pods.Items[0])}, nil
|
||||
case strings.HasPrefix(p, "/namespaces/test/services/") && m == "DELETE":
|
||||
return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &svc.Items[0])}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
|
||||
cmd := NewCmdDelete(tf, streams)
|
||||
cmd.Flags().Set("selector", "a=b")
|
||||
cmd.Flags().Set("cascade", "false")
|
||||
cmd.Flags().Set("output", "name")
|
||||
cmd.Run(cmd, []string{"pods,services"})
|
||||
|
||||
if buf.String() != "pod/foo\npod/bar\nservice/baz\n" {
|
||||
t.Errorf("unexpected output: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceErrors(t *testing.T) {
|
||||
cmdtesting.InitTestErrorHandler(t)
|
||||
testCases := map[string]struct {
|
||||
args []string
|
||||
errFn func(error) bool
|
||||
}{
|
||||
"no args": {
|
||||
args: []string{},
|
||||
errFn: func(err error) bool { return strings.Contains(err.Error(), "You must provide one or more resources") },
|
||||
},
|
||||
"resources but no selectors": {
|
||||
args: []string{"pods"},
|
||||
errFn: func(err error) bool {
|
||||
return strings.Contains(err.Error(), "resource(s) were provided, but no name, label selector, or --all flag specified")
|
||||
},
|
||||
},
|
||||
"multiple resources but no selectors": {
|
||||
args: []string{"pods,deployments"},
|
||||
errFn: func(err error) bool {
|
||||
return strings.Contains(err.Error(), "resource(s) were provided, but no name, label selector, or --all flag specified")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for k, testCase := range testCases {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
tf := cmdtesting.NewTestFactory().WithNamespace("test")
|
||||
defer tf.Cleanup()
|
||||
|
||||
tf.ClientConfigVal = cmdtesting.DefaultClientConfig()
|
||||
|
||||
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||
options := &DeleteOptions{
|
||||
FilenameOptions: resource.FilenameOptions{},
|
||||
GracePeriod: -1,
|
||||
Cascade: false,
|
||||
Output: "name",
|
||||
IOStreams: streams,
|
||||
}
|
||||
err := options.Complete(tf, testCase.args, fakecmd())
|
||||
if !testCase.errFn(err) {
|
||||
t.Errorf("%s: unexpected error: %v", k, err)
|
||||
return
|
||||
}
|
||||
|
||||
if buf.Len() > 0 {
|
||||
t.Errorf("buffer should be empty: %s", string(buf.Bytes()))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user