Improve ignore-not-found behavior (#132542)
* Improve ignore-not-found behavior * ignore lint errcheck Kubernetes-commit: a7e8a505c25965c074f2b10b1bf40230eca48a08
This commit is contained in:
parent
f07a946956
commit
76e6818d8d
|
|
@ -182,7 +182,7 @@ func NewCmdGet(parent string, f cmdutil.Factory, streams genericiooptions.IOStre
|
||||||
cmd.Flags().BoolVarP(&o.Watch, "watch", "w", o.Watch, "After listing/getting the requested object, watch for changes.")
|
cmd.Flags().BoolVarP(&o.Watch, "watch", "w", o.Watch, "After listing/getting the requested object, watch for changes.")
|
||||||
cmd.Flags().BoolVar(&o.WatchOnly, "watch-only", o.WatchOnly, "Watch for changes to the requested object(s), without listing/getting first.")
|
cmd.Flags().BoolVar(&o.WatchOnly, "watch-only", o.WatchOnly, "Watch for changes to the requested object(s), without listing/getting first.")
|
||||||
cmd.Flags().BoolVar(&o.OutputWatchEvents, "output-watch-events", o.OutputWatchEvents, "Output watch event objects when --watch or --watch-only is used. Existing objects are output as initial ADDED events.")
|
cmd.Flags().BoolVar(&o.OutputWatchEvents, "output-watch-events", o.OutputWatchEvents, "Output watch event objects when --watch or --watch-only is used. Existing objects are output as initial ADDED events.")
|
||||||
cmd.Flags().BoolVar(&o.IgnoreNotFound, "ignore-not-found", o.IgnoreNotFound, "If the requested object does not exist the command will return exit code 0.")
|
cmd.Flags().BoolVar(&o.IgnoreNotFound, "ignore-not-found", o.IgnoreNotFound, "If set to true, suppresses NotFound error for specific objects that do not exist. Using this flag with commands that query for collections of resources has no effect when no resources are found.")
|
||||||
cmd.Flags().StringVar(&o.FieldSelector, "field-selector", o.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.")
|
cmd.Flags().StringVar(&o.FieldSelector, "field-selector", o.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.")
|
||||||
cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
|
cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
|
||||||
addServerPrintColumnFlags(cmd, o)
|
addServerPrintColumnFlags(cmd, o)
|
||||||
|
|
@ -623,6 +623,10 @@ func (o *GetOptions) watch(f cmdutil.Factory, args []string) error {
|
||||||
}
|
}
|
||||||
infos, err := r.Infos()
|
infos, err := r.Infos()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Ignore "NotFound" error when ignore-not-found is set to true
|
||||||
|
if apierrors.IsNotFound(err) && o.IgnoreNotFound {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if multipleGVKsRequested(infos) {
|
if multipleGVKsRequested(infos) {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -711,7 +712,7 @@ func TestGetEmptyTable(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetObjectIgnoreNotFound(t *testing.T) {
|
func TestGetNonExistObject(t *testing.T) {
|
||||||
cmdtesting.InitTestErrorHandler(t)
|
cmdtesting.InitTestErrorHandler(t)
|
||||||
|
|
||||||
ns := &corev1.NamespaceList{
|
ns := &corev1.NamespaceList{
|
||||||
|
|
@ -745,6 +746,63 @@ func TestGetObjectIgnoreNotFound(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmdutil.BehaviorOnFatal(func(str string, code int) {
|
||||||
|
expectedErr := "Error from server (NotFound): the server could not find the requested resource (get pods nonexistentpod)"
|
||||||
|
if str != expectedErr {
|
||||||
|
t.Errorf("unexpected error: %s\nexpected: %s", str, expectedErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get nonexistentpod fails with above error message
|
||||||
|
streams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOut(buf)
|
||||||
|
cmd.SetErr(buf)
|
||||||
|
cmd.Run(cmd, []string{"pods", "nonexistentpod"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetNonExistObjectIgnoreNotFound(t *testing.T) {
|
||||||
|
cmdtesting.InitTestErrorHandler(t)
|
||||||
|
|
||||||
|
ns := &corev1.NamespaceList{
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "1",
|
||||||
|
},
|
||||||
|
Items: []corev1.Namespace{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "testns", Namespace: "test", ResourceVersion: "11"},
|
||||||
|
Spec: corev1.NamespaceSpec{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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/nonexistentpod" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: http.StatusNotFound, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.StringBody("")}, nil
|
||||||
|
case p == "/api/v1/namespaces/test" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &ns.Items[0])}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdutil.BehaviorOnFatal(func(str string, code int) {
|
||||||
|
expectedErr := ""
|
||||||
|
if str != expectedErr {
|
||||||
|
t.Errorf("unexpected error: %s\nexpected: %s", str, expectedErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get nonexistentpod passes without error when setting ignore-not-found to true
|
||||||
streams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
streams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
||||||
cmd := NewCmdGet("kubectl", tf, streams)
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
cmd.SetOut(buf)
|
cmd.SetOut(buf)
|
||||||
|
|
@ -2126,6 +2184,93 @@ foo <unknown>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatchNonExistObject(t *testing.T) {
|
||||||
|
pods, _ := watchTestData()
|
||||||
|
|
||||||
|
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/nonexistentpod" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: http.StatusNotFound, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.StringBody("")}, nil
|
||||||
|
case p == "/api/v1/namespaces/test" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &pods[1])}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdutil.BehaviorOnFatal(func(str string, code int) {
|
||||||
|
expectedErr := "Error from server (NotFound): the server could not find the requested resource (get pods nonexistentpod)"
|
||||||
|
if str != expectedErr {
|
||||||
|
t.Errorf("unexpected error: %s\nexpected: %s", str, expectedErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get nonexistentpod fails with above error message
|
||||||
|
streams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOut(buf)
|
||||||
|
cmd.SetErr(buf)
|
||||||
|
cmd.Flags().Set("watch", "true") //nolint:errcheck
|
||||||
|
cmd.Flags().Set("output", "yaml") //nolint:errcheck
|
||||||
|
cmd.Run(cmd, []string{"pods", "nonexistentpod"})
|
||||||
|
|
||||||
|
if buf.String() != "" {
|
||||||
|
t.Errorf("unexpected output: %s", buf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWatchNonExistObjectIgnoreNotFound(t *testing.T) {
|
||||||
|
pods, _ := watchTestData()
|
||||||
|
|
||||||
|
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/nonexistentpod" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: http.StatusNotFound, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.StringBody("")}, nil
|
||||||
|
case p == "/api/v1/namespaces/test" && m == "GET":
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &pods[1])}, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdutil.BehaviorOnFatal(func(str string, code int) {
|
||||||
|
expectedErr := ""
|
||||||
|
if str != expectedErr {
|
||||||
|
t.Errorf("unexpected error: %s\nexpected: %s", str, expectedErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get nonexistentpod passes without error when setting ignore-not-found to true
|
||||||
|
streams, _, buf, _ := genericiooptions.NewTestIOStreams()
|
||||||
|
cmd := NewCmdGet("kubectl", tf, streams)
|
||||||
|
cmd.SetOut(buf)
|
||||||
|
cmd.SetErr(buf)
|
||||||
|
cmd.Flags().Set("ignore-not-found", "true") //nolint:errcheck
|
||||||
|
cmd.Flags().Set("watch", "true") //nolint:errcheck
|
||||||
|
cmd.Flags().Set("output", "yaml") //nolint:errcheck
|
||||||
|
cmd.Run(cmd, []string{"pods", "nonexistentpod"})
|
||||||
|
|
||||||
|
if buf.String() != "" {
|
||||||
|
t.Errorf("unexpected output: %s", buf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWatchStatus(t *testing.T) {
|
func TestWatchStatus(t *testing.T) {
|
||||||
pods, events := watchTestData()
|
pods, events := watchTestData()
|
||||||
events = append(events, watch.Event{Type: "ERROR", Object: &metav1.Status{Status: "Failure", Reason: "InternalServerError", Message: "Something happened"}})
|
events = append(events, watch.Event{Type: "ERROR", Object: &metav1.Status{Status: "Failure", Reason: "InternalServerError", Message: "Something happened"}})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue