...

Source file src/k8s.io/cli-runtime/pkg/resource/builder_test.go

Documentation: k8s.io/cli-runtime/pkg/resource

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package resource
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"net/http"
    25  	"net/http/httptest"
    26  	"os"
    27  	"path/filepath"
    28  	"reflect"
    29  	"strings"
    30  	"testing"
    31  
    32  	"sigs.k8s.io/yaml"
    33  
    34  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    35  	"k8s.io/apimachinery/pkg/api/meta"
    36  	"k8s.io/apimachinery/pkg/api/meta/testrestmapper"
    37  	"k8s.io/apimachinery/pkg/api/resource"
    38  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    39  	"k8s.io/apimachinery/pkg/runtime"
    40  	"k8s.io/apimachinery/pkg/runtime/schema"
    41  	"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
    42  	"k8s.io/apimachinery/pkg/util/dump"
    43  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    44  	"k8s.io/apimachinery/pkg/watch"
    45  	"k8s.io/client-go/rest"
    46  	"k8s.io/client-go/rest/fake"
    47  	restclientwatch "k8s.io/client-go/rest/watch"
    48  	"k8s.io/client-go/restmapper"
    49  	// TODO we need to remove this linkage and create our own scheme
    50  	v1 "k8s.io/api/core/v1"
    51  	"k8s.io/client-go/kubernetes/scheme"
    52  )
    53  
    54  var (
    55  	corev1GV    = schema.GroupVersion{Version: "v1"}
    56  	corev1Codec = scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(corev1GV), scheme.Codecs.UniversalDecoder(corev1GV), corev1GV, corev1GV)
    57  )
    58  
    59  func stringBody(body string) io.ReadCloser {
    60  	return io.NopCloser(bytes.NewReader([]byte(body)))
    61  }
    62  
    63  func watchBody(events ...watch.Event) string {
    64  	buf := &bytes.Buffer{}
    65  	codec := corev1Codec
    66  	enc := restclientwatch.NewEncoder(streaming.NewEncoder(buf, codec), codec)
    67  	for _, e := range events {
    68  		enc.Encode(&e)
    69  	}
    70  	return buf.String()
    71  }
    72  
    73  func fakeClient() FakeClientFunc {
    74  	return func(version schema.GroupVersion) (RESTClient, error) {
    75  		return &fake.RESTClient{}, nil
    76  	}
    77  }
    78  
    79  func fakeClientWith(testName string, t *testing.T, data map[string]string) FakeClientFunc {
    80  	return func(version schema.GroupVersion) (RESTClient, error) {
    81  		return &fake.RESTClient{
    82  			GroupVersion:         corev1GV,
    83  			NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
    84  			Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
    85  				p := req.URL.Path
    86  				q := req.URL.RawQuery
    87  				if len(q) != 0 {
    88  					p = p + "?" + q
    89  				}
    90  				body, ok := data[p]
    91  				if !ok {
    92  					t.Fatalf("%s: unexpected request: %s (%s)\n%#v", testName, p, req.URL, req)
    93  				}
    94  				header := http.Header{}
    95  				header.Set("Content-Type", runtime.ContentTypeJSON)
    96  				return &http.Response{
    97  					StatusCode: http.StatusOK,
    98  					Header:     header,
    99  					Body:       stringBody(body),
   100  				}, nil
   101  			}),
   102  		}, nil
   103  	}
   104  }
   105  
   106  func testData() (*v1.PodList, *v1.ServiceList) {
   107  	pods := &v1.PodList{
   108  		ListMeta: metav1.ListMeta{
   109  			ResourceVersion: "15",
   110  		},
   111  		Items: []v1.Pod{
   112  			{
   113  				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
   114  				Spec:       V1DeepEqualSafePodSpec(),
   115  			},
   116  			{
   117  				ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
   118  				Spec:       V1DeepEqualSafePodSpec(),
   119  			},
   120  		},
   121  	}
   122  	svc := &v1.ServiceList{
   123  		ListMeta: metav1.ListMeta{
   124  			ResourceVersion: "16",
   125  		},
   126  		Items: []v1.Service{
   127  			{
   128  				ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
   129  				Spec: v1.ServiceSpec{
   130  					Type:            "ClusterIP",
   131  					SessionAffinity: "None",
   132  				},
   133  			},
   134  		},
   135  	}
   136  	return pods, svc
   137  }
   138  
   139  func streamTestData() (io.Reader, *v1.PodList, *v1.ServiceList) {
   140  	pods, svc := testData()
   141  	r, w := io.Pipe()
   142  	go func() {
   143  		defer w.Close()
   144  		w.Write([]byte(runtime.EncodeOrDie(corev1Codec, pods)))
   145  		w.Write([]byte(runtime.EncodeOrDie(corev1Codec, svc)))
   146  	}()
   147  	return r, pods, svc
   148  }
   149  
   150  func subresourceTestData(name string) *v1.Pod {
   151  	return &v1.Pod{
   152  		ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "test", ResourceVersion: "10"},
   153  		Spec:       V1DeepEqualSafePodSpec(),
   154  		Status:     V1DeepEqualSafePodStatus(),
   155  	}
   156  }
   157  
   158  func JSONToYAMLOrDie(in []byte) []byte {
   159  	data, err := yaml.JSONToYAML(in)
   160  	if err != nil {
   161  		panic(err)
   162  	}
   163  	return data
   164  }
   165  
   166  func streamYAMLTestData() (io.Reader, *v1.PodList, *v1.ServiceList) {
   167  	pods, svc := testData()
   168  	r, w := io.Pipe()
   169  	go func() {
   170  		defer w.Close()
   171  		w.Write(JSONToYAMLOrDie([]byte(runtime.EncodeOrDie(corev1Codec, pods))))
   172  		w.Write([]byte("\n---\n"))
   173  		w.Write(JSONToYAMLOrDie([]byte(runtime.EncodeOrDie(corev1Codec, svc))))
   174  	}()
   175  	return r, pods, svc
   176  }
   177  
   178  func streamTestObject(obj runtime.Object) io.Reader {
   179  	r, w := io.Pipe()
   180  	go func() {
   181  		defer w.Close()
   182  		w.Write([]byte(runtime.EncodeOrDie(corev1Codec, obj)))
   183  	}()
   184  	return r
   185  }
   186  
   187  type testVisitor struct {
   188  	InjectErr error
   189  	Infos     []*Info
   190  }
   191  
   192  func (v *testVisitor) Handle(info *Info, err error) error {
   193  	if err != nil {
   194  		return err
   195  	}
   196  	v.Infos = append(v.Infos, info)
   197  	return v.InjectErr
   198  }
   199  
   200  func (v *testVisitor) Objects() []runtime.Object {
   201  	objects := []runtime.Object{}
   202  	for i := range v.Infos {
   203  		objects = append(objects, v.Infos[i].Object)
   204  	}
   205  	return objects
   206  }
   207  
   208  var aPod string = `
   209  {
   210      "kind": "Pod",
   211  		"apiVersion": "` + corev1GV.String() + `",
   212      "metadata": {
   213          "name": "busybox{id}",
   214          "labels": {
   215              "name": "busybox{id}"
   216          }
   217      },
   218      "spec": {
   219          "containers": [
   220              {
   221                  "name": "busybox",
   222                  "image": "busybox",
   223                  "command": [
   224                      "sleep",
   225                      "3600"
   226                  ],
   227                  "imagePullPolicy": "IfNotPresent"
   228              }
   229          ],
   230          "restartPolicy": "Always"
   231      }
   232  }
   233  `
   234  var aPodBadAnnotations string = `
   235  {
   236      "kind": "Pod",
   237      "apiVersion": "` + corev1GV.String() + `",
   238      "metadata": {
   239          "name": "busybox{id}",
   240          "labels": {
   241              "name": "busybox{id}"
   242          },
   243          "annotations": {
   244              "name": 0
   245          }
   246      },
   247      "spec": {
   248          "containers": [
   249              {
   250                  "name": "busybox",
   251                  "image": "busybox",
   252                  "command": [
   253                      "sleep",
   254                      "3600"
   255                  ],
   256                  "imagePullPolicy": "IfNotPresent"
   257              }
   258          ],
   259          "restartPolicy": "Always"
   260      }
   261  }
   262  `
   263  
   264  var aRC string = `
   265  {
   266      "kind": "ReplicationController",
   267  		"apiVersion": "` + corev1GV.String() + `",
   268      "metadata": {
   269          "name": "busybox{id}",
   270          "labels": {
   271              "app": "busybox"
   272          }
   273      },
   274      "spec": {
   275          "replicas": 1,
   276          "template": {
   277              "metadata": {
   278                  "name": "busybox{id}",
   279                  "labels": {
   280                      "app": "busybox{id}"
   281                  }
   282              },
   283              "spec": {
   284                  "containers": [
   285                      {
   286                          "name": "busybox",
   287                          "image": "busybox",
   288                          "command": [
   289                              "sleep",
   290                              "3600"
   291                          ],
   292                          "imagePullPolicy": "IfNotPresent"
   293                      }
   294                  ],
   295                  "restartPolicy": "Always"
   296              }
   297          }
   298      }
   299  }
   300  `
   301  
   302  func newDefaultBuilder() *Builder {
   303  	return newDefaultBuilderWith(fakeClient())
   304  }
   305  
   306  func newDefaultBuilderWith(fakeClientFn FakeClientFunc) *Builder {
   307  	return NewFakeBuilder(
   308  		fakeClientFn,
   309  		func() (meta.RESTMapper, error) {
   310  			return testrestmapper.TestOnlyStaticRESTMapper(scheme.Scheme), nil
   311  		},
   312  		func() (restmapper.CategoryExpander, error) {
   313  			return FakeCategoryExpander, nil
   314  		}).
   315  		WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...)
   316  }
   317  
   318  func newUnstructuredDefaultBuilder() *Builder {
   319  	return newUnstructuredDefaultBuilderWith(fakeClient())
   320  }
   321  
   322  func newUnstructuredDefaultBuilderWith(fakeClientFn FakeClientFunc) *Builder {
   323  	return NewFakeBuilder(
   324  		fakeClientFn,
   325  		func() (meta.RESTMapper, error) {
   326  			return testrestmapper.TestOnlyStaticRESTMapper(scheme.Scheme), nil
   327  		},
   328  		func() (restmapper.CategoryExpander, error) {
   329  			return FakeCategoryExpander, nil
   330  		}).
   331  		Unstructured()
   332  }
   333  
   334  type errorRestMapper struct {
   335  	meta.RESTMapper
   336  	err error
   337  }
   338  
   339  func (l *errorRestMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
   340  	return nil, l.err
   341  }
   342  
   343  func (l *errorRestMapper) Reset() {
   344  	meta.MaybeResetRESTMapper(l.RESTMapper)
   345  }
   346  
   347  func newDefaultBuilderWithMapperError(fakeClientFn FakeClientFunc, err error) *Builder {
   348  	return NewFakeBuilder(
   349  		fakeClientFn,
   350  		func() (meta.RESTMapper, error) {
   351  			return &errorRestMapper{
   352  				RESTMapper: testrestmapper.TestOnlyStaticRESTMapper(scheme.Scheme),
   353  				err:        err,
   354  			}, nil
   355  		},
   356  		func() (restmapper.CategoryExpander, error) {
   357  			return FakeCategoryExpander, nil
   358  		}).
   359  		WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...)
   360  }
   361  
   362  func TestPathBuilderAndVersionedObjectNotDefaulted(t *testing.T) {
   363  	b := newDefaultBuilder().
   364  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{"../../artifacts/kitten-rc.yaml"}})
   365  
   366  	test := &testVisitor{}
   367  	singleItemImplied := false
   368  
   369  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
   370  	if err != nil || !singleItemImplied || len(test.Infos) != 1 {
   371  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
   372  	}
   373  
   374  	info := test.Infos[0]
   375  	if info.Name != "update-demo-kitten" || info.Namespace != "" || info.Object == nil {
   376  		t.Errorf("unexpected info: %#v", info)
   377  	}
   378  
   379  	obj := info.Object
   380  	version, ok := obj.(*v1.ReplicationController)
   381  	// versioned object does not have defaulting applied
   382  	if obj == nil || !ok || version.Spec.Replicas != nil {
   383  		t.Errorf("unexpected versioned object: %#v", obj)
   384  	}
   385  }
   386  
   387  func TestNodeBuilder(t *testing.T) {
   388  	node := &v1.Node{
   389  		ObjectMeta: metav1.ObjectMeta{Name: "node1", Namespace: "should-not-have", ResourceVersion: "10"},
   390  		Spec:       v1.NodeSpec{},
   391  		Status: v1.NodeStatus{
   392  			Capacity: v1.ResourceList{
   393  				v1.ResourceCPU:    resource.MustParse("1000m"),
   394  				v1.ResourceMemory: resource.MustParse("1Mi"),
   395  			},
   396  		},
   397  	}
   398  	r, w := io.Pipe()
   399  	go func() {
   400  		defer w.Close()
   401  		w.Write([]byte(runtime.EncodeOrDie(corev1Codec, node)))
   402  	}()
   403  
   404  	b := newDefaultBuilder().NamespaceParam("test").Stream(r, "STDIN")
   405  
   406  	test := &testVisitor{}
   407  
   408  	err := b.Do().Visit(test.Handle)
   409  	if err != nil || len(test.Infos) != 1 {
   410  		t.Fatalf("unexpected response: %v %#v", err, test.Infos)
   411  	}
   412  	info := test.Infos[0]
   413  	if info.Name != "node1" || info.Namespace != "" || info.Object == nil {
   414  		t.Errorf("unexpected info: %#v", info)
   415  	}
   416  }
   417  
   418  func createTestDir(t *testing.T, path string) {
   419  	if err := os.MkdirAll(path, 0750); err != nil {
   420  		t.Fatalf("error creating test dir: %v", err)
   421  	}
   422  }
   423  
   424  func writeTestFile(t *testing.T, path string, contents string) {
   425  	if err := os.WriteFile(path, []byte(contents), 0644); err != nil {
   426  		t.Fatalf("error creating test file %#v", err)
   427  	}
   428  }
   429  
   430  func TestFilenameOptionsValidate(t *testing.T) {
   431  	testcases := []struct {
   432  		filenames []string
   433  		kustomize string
   434  		recursive bool
   435  		errExp    bool
   436  		msgExp    string
   437  	}{
   438  		{
   439  			filenames: []string{"file"},
   440  			kustomize: "dir",
   441  			errExp:    true,
   442  			msgExp:    "only one of -f or -k can be specified",
   443  		},
   444  		{
   445  			kustomize: "dir",
   446  			recursive: true,
   447  			errExp:    true,
   448  			msgExp:    "the -k flag can't be used with -f or -R",
   449  		},
   450  		{
   451  			filenames: []string{"file"},
   452  			errExp:    false,
   453  		},
   454  		{
   455  			filenames: []string{"dir"},
   456  			recursive: true,
   457  			errExp:    false,
   458  		},
   459  		{
   460  			kustomize: "dir",
   461  			errExp:    false,
   462  		},
   463  	}
   464  	for _, testcase := range testcases {
   465  		o := &FilenameOptions{
   466  			Kustomize: testcase.kustomize,
   467  			Filenames: testcase.filenames,
   468  			Recursive: testcase.recursive,
   469  		}
   470  		errs := o.validate()
   471  		if testcase.errExp {
   472  			if len(errs) == 0 {
   473  				t.Fatalf("expected error not happened")
   474  			}
   475  			if errs[0].Error() != testcase.msgExp {
   476  				t.Fatalf("expected %s, but got %#v", testcase.msgExp, errs[0])
   477  			}
   478  		} else {
   479  			if len(errs) > 0 {
   480  				t.Fatalf("Unexpected error %#v", errs)
   481  			}
   482  		}
   483  	}
   484  }
   485  
   486  func TestPathBuilderWithMultiple(t *testing.T) {
   487  	// create test dirs
   488  	tmpDir, err := os.MkdirTemp("", "recursive_test_multiple")
   489  	if err != nil {
   490  		t.Fatalf("error creating temp dir: %v", err)
   491  	}
   492  
   493  	createTestDir(t, fmt.Sprintf("%s/%s", tmpDir, "recursive/pod/pod_1"))
   494  	createTestDir(t, fmt.Sprintf("%s/%s", tmpDir, "recursive/rc/rc_1"))
   495  	createTestDir(t, fmt.Sprintf("%s/%s", tmpDir, "inode/hardlink"))
   496  	defer os.RemoveAll(tmpDir)
   497  
   498  	// create test files
   499  	writeTestFile(t, fmt.Sprintf("%s/recursive/pod/busybox.json", tmpDir), strings.Replace(aPod, "{id}", "0", -1))
   500  	writeTestFile(t, fmt.Sprintf("%s/recursive/pod/pod_1/busybox.json", tmpDir), strings.Replace(aPod, "{id}", "1", -1))
   501  	writeTestFile(t, fmt.Sprintf("%s/recursive/rc/busybox.json", tmpDir), strings.Replace(aRC, "{id}", "0", -1))
   502  	writeTestFile(t, fmt.Sprintf("%s/recursive/rc/rc_1/busybox.json", tmpDir), strings.Replace(aRC, "{id}", "1", -1))
   503  	writeTestFile(t, fmt.Sprintf("%s/inode/hardlink/busybox.json", tmpDir), strings.Replace(aPod, "{id}", "0", -1))
   504  	if err := os.Link(fmt.Sprintf("%s/inode/hardlink/busybox.json", tmpDir), fmt.Sprintf("%s/inode/hardlink/busybox-link.json", tmpDir)); err != nil {
   505  		t.Fatalf("error creating test file: %v", err)
   506  	}
   507  
   508  	tests := []struct {
   509  		name          string
   510  		object        runtime.Object
   511  		recursive     bool
   512  		directory     string
   513  		expectedNames []string
   514  	}{
   515  		{"pod", &v1.Pod{}, false, "../../artifacts/pod.yaml", []string{"nginx"}},
   516  		{"recursive-pod", &v1.Pod{}, true, fmt.Sprintf("%s/recursive/pod", tmpDir), []string{"busybox0", "busybox1"}},
   517  		{"rc", &v1.ReplicationController{}, false, "../../artifacts/redis-master-controller.yaml", []string{"redis-master"}},
   518  		{"recursive-rc", &v1.ReplicationController{}, true, fmt.Sprintf("%s/recursive/rc", tmpDir), []string{"busybox0", "busybox1"}},
   519  		{"hardlink", &v1.Pod{}, false, fmt.Sprintf("%s/inode/hardlink/busybox-link.json", tmpDir), []string{"busybox0"}},
   520  		{"hardlink", &v1.Pod{}, true, fmt.Sprintf("%s/inode/hardlink/busybox-link.json", tmpDir), []string{"busybox0"}},
   521  	}
   522  
   523  	for _, tt := range tests {
   524  		t.Run(tt.name, func(t *testing.T) {
   525  			b := newDefaultBuilder().
   526  				FilenameParam(false, &FilenameOptions{Recursive: tt.recursive, Filenames: []string{tt.directory}}).
   527  				NamespaceParam("test").DefaultNamespace()
   528  
   529  			testVisitor := &testVisitor{}
   530  			singleItemImplied := false
   531  
   532  			err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(testVisitor.Handle)
   533  			if err != nil {
   534  				t.Fatalf("unexpected response: %v %t %#v %s", err, singleItemImplied, testVisitor.Infos, tt.name)
   535  			}
   536  
   537  			info := testVisitor.Infos
   538  
   539  			for i, v := range info {
   540  				switch tt.object.(type) {
   541  				case *v1.Pod:
   542  					if _, ok := v.Object.(*v1.Pod); !ok || v.Name != tt.expectedNames[i] || v.Namespace != "test" {
   543  						t.Errorf("unexpected info: %v", dump.Pretty(v.Object))
   544  					}
   545  				case *v1.ReplicationController:
   546  					if _, ok := v.Object.(*v1.ReplicationController); !ok || v.Name != tt.expectedNames[i] || v.Namespace != "test" {
   547  						t.Errorf("unexpected info: %v", dump.Pretty(v.Object))
   548  					}
   549  				}
   550  			}
   551  		})
   552  	}
   553  }
   554  
   555  func TestPathBuilderWithMultipleInvalid(t *testing.T) {
   556  	// create test dirs
   557  	tmpDir, err := os.MkdirTemp("", "recursive_test_multiple_invalid")
   558  	if err != nil {
   559  		t.Fatalf("error creating temp dir: %v", err)
   560  	}
   561  
   562  	createTestDir(t, fmt.Sprintf("%s/%s", tmpDir, "inode/symlink/pod"))
   563  	defer os.RemoveAll(tmpDir)
   564  
   565  	// create test files
   566  	writeTestFile(t, fmt.Sprintf("%s/inode/symlink/pod/busybox.json", tmpDir), strings.Replace(aPod, "{id}", "0", -1))
   567  	if err := os.Symlink(fmt.Sprintf("%s/inode/symlink/pod", tmpDir), fmt.Sprintf("%s/inode/symlink/pod-link", tmpDir)); err != nil {
   568  		t.Fatalf("error creating test file: %v", err)
   569  	}
   570  	if err := os.Symlink(fmt.Sprintf("%s/inode/symlink/loop", tmpDir), fmt.Sprintf("%s/inode/symlink/loop", tmpDir)); err != nil {
   571  		t.Fatalf("error creating test file: %v", err)
   572  	}
   573  
   574  	tests := []struct {
   575  		name      string
   576  		recursive bool
   577  		directory string
   578  	}{
   579  		{"symlink", false, fmt.Sprintf("%s/inode/symlink/pod-link", tmpDir)},
   580  		{"symlink", true, fmt.Sprintf("%s/inode/symlink/pod-link", tmpDir)},
   581  		{"loop", false, fmt.Sprintf("%s/inode/symlink/loop", tmpDir)},
   582  		{"loop", true, fmt.Sprintf("%s/inode/symlink/loop", tmpDir)},
   583  	}
   584  
   585  	for _, tt := range tests {
   586  		t.Run(tt.name, func(t *testing.T) {
   587  			b := newDefaultBuilder().
   588  				FilenameParam(false, &FilenameOptions{Recursive: tt.recursive, Filenames: []string{tt.directory}}).
   589  				NamespaceParam("test").DefaultNamespace()
   590  
   591  			testVisitor := &testVisitor{}
   592  			singleItemImplied := false
   593  
   594  			err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(testVisitor.Handle)
   595  			if err == nil {
   596  				t.Fatalf("unexpected response: %v %t %#v %s", err, singleItemImplied, testVisitor.Infos, tt.name)
   597  			}
   598  		})
   599  	}
   600  }
   601  
   602  func TestDirectoryBuilder(t *testing.T) {
   603  	b := newDefaultBuilder().
   604  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{"../../artifacts/guestbook"}}).
   605  		NamespaceParam("test").DefaultNamespace()
   606  
   607  	test := &testVisitor{}
   608  	singleItemImplied := false
   609  
   610  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
   611  	if err != nil || singleItemImplied || len(test.Infos) < 3 {
   612  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
   613  	}
   614  
   615  	found := false
   616  	for _, info := range test.Infos {
   617  		if info.Name == "redis-master" && info.Namespace == "test" && info.Object != nil {
   618  			found = true
   619  			break
   620  		}
   621  	}
   622  	if !found {
   623  		t.Errorf("unexpected responses: %#v", test.Infos)
   624  	}
   625  }
   626  
   627  func TestFilePatternBuilderWhenFileLiteralExists(t *testing.T) {
   628  	const pathPattern = "../../artifacts/oddly-named-file[x].yaml"
   629  	b := newDefaultBuilder().
   630  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{pathPattern}}).
   631  		NamespaceParam("test").DefaultNamespace()
   632  
   633  	test := &testVisitor{}
   634  	singleItemImplied := false
   635  
   636  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
   637  	if err != nil || !singleItemImplied || len(test.Infos) != 1 {
   638  		t.Fatalf("unexpected result: %v %t %#v", err, singleItemImplied, test.Infos)
   639  	}
   640  	if !strings.Contains(test.Infos[0].Source, "oddly-named-file[x]") {
   641  		t.Errorf("unexpected file name: %#v", test.Infos[0].Source)
   642  	}
   643  }
   644  
   645  func TestFilePatternBuilder(t *testing.T) {
   646  	b := newDefaultBuilder().
   647  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{"../../artifacts/guestbook/redis-*.yaml"}}).
   648  		NamespaceParam("test").DefaultNamespace()
   649  
   650  	test := &testVisitor{}
   651  	singleItemImplied := false
   652  
   653  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
   654  	if err != nil || singleItemImplied || len(test.Infos) < 3 {
   655  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
   656  	}
   657  
   658  	for _, info := range test.Infos {
   659  		if strings.Index(info.Name, "redis-") != 0 {
   660  			t.Errorf("unexpected response: %#v", info.Name)
   661  		}
   662  	}
   663  }
   664  
   665  func TestErrorFilePatternBuilder(t *testing.T) {
   666  	testCases := map[string]struct {
   667  		input        string
   668  		expectedErr  string
   669  		inputInError bool
   670  	}{
   671  		"invalid pattern": {
   672  			input:        "[a-z*.yaml",
   673  			expectedErr:  "syntax error in pattern",
   674  			inputInError: true,
   675  		},
   676  		"file does not exist": {
   677  			input:        "../../artifacts/guestbook/notexist.yaml",
   678  			expectedErr:  "does not exist",
   679  			inputInError: true,
   680  		},
   681  		"directory does not exist and valid glob": {
   682  			input:        "../../artifacts/_does_not_exist_/*.yaml",
   683  			expectedErr:  "does not exist",
   684  			inputInError: true,
   685  		},
   686  	}
   687  	for name, tc := range testCases {
   688  		t.Run(name, func(t *testing.T) {
   689  			b := newDefaultBuilder().
   690  				FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{tc.input}}).
   691  				NamespaceParam("test").DefaultNamespace()
   692  
   693  			test := &testVisitor{}
   694  			singleItemImplied := false
   695  
   696  			err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
   697  			if err == nil || len(test.Infos) != 0 || !strings.Contains(err.Error(), tc.expectedErr) ||
   698  				(tc.inputInError && !strings.Contains(err.Error(), tc.input)) {
   699  				t.Errorf("unexpected response: %v %#v", err, test.Infos)
   700  			}
   701  		})
   702  	}
   703  }
   704  
   705  func setupKustomizeDirectory() (string, error) {
   706  	path, err := os.MkdirTemp("", "")
   707  	if err != nil {
   708  		return "", err
   709  	}
   710  
   711  	contents := map[string]string{
   712  		"configmap.yaml": `
   713  apiVersion: v1
   714  kind: ConfigMap
   715  metadata:
   716    name: the-map
   717  data:
   718    altGreeting: "Good Morning!"
   719    enableRisky: "false"
   720  `,
   721  		"deployment.yaml": `
   722  apiVersion: apps/v1
   723  kind: Deployment
   724  metadata:
   725    name: the-deployment
   726  spec:
   727    replicas: 3
   728    template:
   729      metadata:
   730        labels:
   731          deployment: hello
   732      spec:
   733        containers:
   734        - name: the-container
   735          image: monopole/hello:1
   736          command: ["/hello",
   737                    "--port=8080",
   738                    "--enableRiskyFeature=$(ENABLE_RISKY)"]
   739          ports:
   740          - containerPort: 8080
   741          env:
   742          - name: ALT_GREETING
   743            valueFrom:
   744              configMapKeyRef:
   745                name: the-map
   746                key: altGreeting
   747          - name: ENABLE_RISKY
   748            valueFrom:
   749              configMapKeyRef:
   750                name: the-map
   751                key: enableRisky
   752  `,
   753  		"service.yaml": `
   754  kind: Service
   755  apiVersion: v1
   756  metadata:
   757    name: the-service
   758  spec:
   759    selector:
   760      deployment: hello
   761    type: LoadBalancer
   762    ports:
   763    - protocol: TCP
   764      port: 8666
   765      targetPort: 8080
   766  `,
   767  		"kustomization.yaml": `
   768  nameprefix: test-
   769  resources:
   770  - deployment.yaml
   771  - service.yaml
   772  - configmap.yaml
   773  `,
   774  	}
   775  
   776  	for filename, content := range contents {
   777  		err = os.WriteFile(filepath.Join(path, filename), []byte(content), 0660)
   778  		if err != nil {
   779  			return "", err
   780  		}
   781  	}
   782  	return path, nil
   783  }
   784  
   785  func TestKustomizeDirectoryBuilder(t *testing.T) {
   786  	dir, err := setupKustomizeDirectory()
   787  	if err != nil {
   788  		t.Fatalf("unexpected error %v", err)
   789  	}
   790  	defer os.RemoveAll(dir)
   791  
   792  	tests := []struct {
   793  		directory     string
   794  		expectErr     bool
   795  		errMsg        string
   796  		number        int
   797  		expectedNames []string
   798  	}{
   799  		{
   800  			directory: "../../artifacts/guestbook",
   801  			expectErr: true,
   802  			errMsg:    "unable to find one of 'kustomization.yaml', 'kustomization.yml' or 'Kustomization'",
   803  		},
   804  		{
   805  			directory:     dir,
   806  			expectErr:     false,
   807  			expectedNames: []string{"test-the-map", "test-the-deployment", "test-the-service"},
   808  		},
   809  		{
   810  			directory: filepath.Join(dir, "kustomization.yaml"),
   811  			expectErr: true,
   812  			errMsg:    "must build at directory",
   813  		},
   814  		{
   815  			directory: "../../artifacts/kustomization/should-not-load.yaml",
   816  			expectErr: true,
   817  			errMsg:    "must build at directory",
   818  		},
   819  	}
   820  	for _, tt := range tests {
   821  		b := newDefaultBuilder().
   822  			FilenameParam(false, &FilenameOptions{Kustomize: tt.directory}).
   823  			NamespaceParam("test").DefaultNamespace()
   824  		test := &testVisitor{}
   825  		err := b.Do().Visit(test.Handle)
   826  		if tt.expectErr {
   827  			if err == nil {
   828  				t.Fatalf("expected error unhappened")
   829  			}
   830  			if !strings.Contains(err.Error(), tt.errMsg) {
   831  				t.Fatalf("expected %s but got %s", tt.errMsg, err.Error())
   832  			}
   833  		} else {
   834  			if err != nil || len(test.Infos) < tt.number {
   835  				t.Fatalf("unexpected response: %v %#v", err, test.Infos)
   836  			}
   837  			contained := func(name string) bool {
   838  				for _, info := range test.Infos {
   839  					if info.Name == name && info.Namespace == "test" && info.Object != nil {
   840  						return true
   841  					}
   842  				}
   843  				return false
   844  			}
   845  
   846  			allFound := true
   847  			for _, name := range tt.expectedNames {
   848  				if !contained(name) {
   849  					allFound = false
   850  				}
   851  			}
   852  			if !allFound {
   853  				t.Errorf("unexpected responses: %#v", test.Infos)
   854  			}
   855  		}
   856  	}
   857  }
   858  
   859  func TestNamespaceOverride(t *testing.T) {
   860  	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   861  		w.WriteHeader(http.StatusOK)
   862  		w.Write([]byte(runtime.EncodeOrDie(corev1Codec, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "test"}})))
   863  	}))
   864  	defer s.Close()
   865  
   866  	b := newDefaultBuilder().
   867  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{s.URL}}).
   868  		NamespaceParam("test")
   869  
   870  	test := &testVisitor{}
   871  
   872  	err := b.Do().Visit(test.Handle)
   873  	if err != nil || len(test.Infos) != 1 && test.Infos[0].Namespace != "foo" {
   874  		t.Fatalf("unexpected response: %v %#v", err, test.Infos)
   875  	}
   876  
   877  	b = newDefaultBuilder().
   878  		FilenameParam(true, &FilenameOptions{Recursive: false, Filenames: []string{s.URL}}).
   879  		NamespaceParam("test")
   880  
   881  	test = &testVisitor{}
   882  
   883  	err = b.Do().Visit(test.Handle)
   884  	if err == nil {
   885  		t.Fatalf("expected namespace error. got: %#v", test.Infos)
   886  	}
   887  }
   888  
   889  func TestURLBuilder(t *testing.T) {
   890  	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   891  		w.WriteHeader(http.StatusOK)
   892  		w.Write([]byte(runtime.EncodeOrDie(corev1Codec, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "test"}})))
   893  		w.Write([]byte(runtime.EncodeOrDie(corev1Codec, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "test1"}})))
   894  	}))
   895  	defer s.Close()
   896  
   897  	b := newDefaultBuilder().
   898  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{s.URL}}).
   899  		NamespaceParam("foo")
   900  
   901  	test := &testVisitor{}
   902  
   903  	err := b.Do().Visit(test.Handle)
   904  	if err != nil || len(test.Infos) != 2 {
   905  		t.Fatalf("unexpected response: %v %#v", err, test.Infos)
   906  	}
   907  	info := test.Infos[0]
   908  	if info.Name != "test" || info.Namespace != "foo" || info.Object == nil {
   909  		t.Errorf("unexpected info: %#v", info)
   910  	}
   911  
   912  	info = test.Infos[1]
   913  	if info.Name != "test1" || info.Namespace != "foo" || info.Object == nil {
   914  		t.Errorf("unexpected info: %#v", info)
   915  	}
   916  
   917  }
   918  
   919  func TestURLBuilderRequireNamespace(t *testing.T) {
   920  	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   921  		w.WriteHeader(http.StatusOK)
   922  		w.Write([]byte(runtime.EncodeOrDie(corev1Codec, &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "test"}})))
   923  	}))
   924  	defer s.Close()
   925  
   926  	b := newDefaultBuilder().
   927  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{s.URL}}).
   928  		NamespaceParam("test").RequireNamespace()
   929  
   930  	test := &testVisitor{}
   931  	singleItemImplied := false
   932  
   933  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
   934  	if err == nil || !singleItemImplied || len(test.Infos) != 0 {
   935  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
   936  	}
   937  }
   938  
   939  func TestReplaceAliases(t *testing.T) {
   940  	tests := []struct {
   941  		name     string
   942  		arg      string
   943  		expected string
   944  	}{
   945  		{
   946  			name:     "no-replacement",
   947  			arg:      "service",
   948  			expected: "service",
   949  		},
   950  		{
   951  			name:     "all-replacement",
   952  			arg:      "all",
   953  			expected: "pods,replicationcontrollers,services,statefulsets.apps,horizontalpodautoscalers.autoscaling,jobs.batch,cronjobs.batch,daemonsets.extensions,deployments.extensions,replicasets.extensions",
   954  		},
   955  		{
   956  			name:     "alias-in-comma-separated-arg",
   957  			arg:      "all,secrets",
   958  			expected: "pods,replicationcontrollers,services,statefulsets.apps,horizontalpodautoscalers.autoscaling,jobs.batch,cronjobs.batch,daemonsets.extensions,deployments.extensions,replicasets.extensions,secrets",
   959  		},
   960  	}
   961  
   962  	b := newDefaultBuilder()
   963  
   964  	for _, test := range tests {
   965  		replaced := b.ReplaceAliases(test.arg)
   966  		if replaced != test.expected {
   967  			t.Errorf("%s: unexpected argument: expected %s, got %s", test.name, test.expected, replaced)
   968  		}
   969  	}
   970  }
   971  
   972  func TestResourceByName(t *testing.T) {
   973  	pods, _ := testData()
   974  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
   975  		"/namespaces/test/pods/foo": runtime.EncodeOrDie(corev1Codec, &pods.Items[0]),
   976  	})).NamespaceParam("test")
   977  
   978  	test := &testVisitor{}
   979  	singleItemImplied := false
   980  
   981  	if b.Do().Err() == nil {
   982  		t.Errorf("unexpected non-error")
   983  	}
   984  
   985  	b.ResourceTypeOrNameArgs(true, "pods", "foo")
   986  
   987  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
   988  	if err != nil || !singleItemImplied || len(test.Infos) != 1 {
   989  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
   990  	}
   991  	if !apiequality.Semantic.DeepEqual(&pods.Items[0], test.Objects()[0]) {
   992  		t.Errorf("unexpected object: %#v", test.Objects()[0])
   993  	}
   994  
   995  	mapping, err := b.Do().ResourceMapping()
   996  	if err != nil {
   997  		t.Fatalf("unexpected error: %v", err)
   998  	}
   999  	if mapping.Resource != (schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}) {
  1000  		t.Errorf("unexpected resource mapping: %#v", mapping)
  1001  	}
  1002  }
  1003  func TestSubresourceByName(t *testing.T) {
  1004  	pod := subresourceTestData("foo")
  1005  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1006  		"/namespaces/test/pods/foo/status": runtime.EncodeOrDie(corev1Codec, pod),
  1007  	})).NamespaceParam("test")
  1008  
  1009  	test := &testVisitor{}
  1010  	singleItemImplied := false
  1011  
  1012  	if b.Do().Err() == nil {
  1013  		t.Errorf("unexpected non-error")
  1014  	}
  1015  
  1016  	b.ResourceTypeOrNameArgs(true, "pods", "foo").Subresource("status")
  1017  
  1018  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1019  	if err != nil || !singleItemImplied || len(test.Infos) != 1 {
  1020  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
  1021  	}
  1022  	if !apiequality.Semantic.DeepEqual(pod, test.Objects()[0]) {
  1023  		t.Errorf("unexpected object: %#v", test.Objects()[0])
  1024  	}
  1025  
  1026  	mapping, err := b.Do().ResourceMapping()
  1027  	if err != nil {
  1028  		t.Fatalf("unexpected error: %v", err)
  1029  	}
  1030  	if mapping.Resource != (schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}) {
  1031  		t.Errorf("unexpected resource mapping: %#v", mapping)
  1032  	}
  1033  }
  1034  
  1035  func TestRestMappingErrors(t *testing.T) {
  1036  	pods, _ := testData()
  1037  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1038  		"/namespaces/test/pods/foo": runtime.EncodeOrDie(corev1Codec, &pods.Items[0]),
  1039  	})).NamespaceParam("test")
  1040  
  1041  	if b.Do().Err() == nil {
  1042  		t.Errorf("unexpected non-error")
  1043  	}
  1044  
  1045  	test := &testVisitor{}
  1046  	singleItemImplied := false
  1047  
  1048  	b.ResourceTypeOrNameArgs(true, "foo", "bar")
  1049  
  1050  	// ensure that requesting a resource we _know_ not to exist results in an expected *meta.NoKindMatchError
  1051  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1052  	if err != nil {
  1053  		if !strings.Contains(err.Error(), "server doesn't have a resource type \"foo\"") {
  1054  			t.Fatalf("unexpected error: %v", err)
  1055  		}
  1056  	}
  1057  
  1058  	expectedErr := fmt.Errorf("expected error")
  1059  	b = newDefaultBuilderWithMapperError(fakeClientWith("", t, map[string]string{
  1060  		"/namespaces/test/pods/foo": runtime.EncodeOrDie(corev1Codec, &pods.Items[0]),
  1061  	}), expectedErr).NamespaceParam("test")
  1062  
  1063  	if b.Do().Err() == nil {
  1064  		t.Errorf("unexpected non-error")
  1065  	}
  1066  
  1067  	// ensure we request a resource we know not to exist. This way, we
  1068  	// end up taking the codepath we want to test in the resource builder
  1069  	b.ResourceTypeOrNameArgs(true, "foo", "bar")
  1070  
  1071  	// ensure that receiving an error for any reason other than a non-existent resource is returned as-is
  1072  	err = b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1073  	if err != nil && !strings.Contains(err.Error(), expectedErr.Error()) {
  1074  		t.Fatalf("unexpected error: %v", err)
  1075  	}
  1076  }
  1077  
  1078  func TestMultipleResourceByTheSameName(t *testing.T) {
  1079  	pods, svcs := testData()
  1080  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1081  		"/namespaces/test/pods/foo":     runtime.EncodeOrDie(corev1Codec, &pods.Items[0]),
  1082  		"/namespaces/test/pods/baz":     runtime.EncodeOrDie(corev1Codec, &pods.Items[1]),
  1083  		"/namespaces/test/services/foo": runtime.EncodeOrDie(corev1Codec, &svcs.Items[0]),
  1084  		"/namespaces/test/services/baz": runtime.EncodeOrDie(corev1Codec, &svcs.Items[0]),
  1085  	})).
  1086  		NamespaceParam("test")
  1087  
  1088  	test := &testVisitor{}
  1089  	singleItemImplied := false
  1090  
  1091  	if b.Do().Err() == nil {
  1092  		t.Errorf("unexpected non-error")
  1093  	}
  1094  
  1095  	b.ResourceTypeOrNameArgs(true, "pods,services", "foo", "baz")
  1096  
  1097  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1098  	if err != nil || singleItemImplied || len(test.Infos) != 4 {
  1099  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
  1100  	}
  1101  	if !apiequality.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &svcs.Items[0], &svcs.Items[0]}, test.Objects()) {
  1102  		t.Errorf("unexpected visited objects: %#v", test.Objects())
  1103  	}
  1104  
  1105  	if _, err := b.Do().ResourceMapping(); err == nil {
  1106  		t.Errorf("unexpected non-error")
  1107  	}
  1108  }
  1109  
  1110  func TestRequestModifier(t *testing.T) {
  1111  	for _, tc := range []struct {
  1112  		name string
  1113  		f    func(t *testing.T, got **rest.Request) *Builder
  1114  	}{
  1115  		{
  1116  			name: "simple",
  1117  			f: func(t *testing.T, got **rest.Request) *Builder {
  1118  				return newDefaultBuilderWith(fakeClientWith(t.Name(), t, nil)).
  1119  					NamespaceParam("foo").
  1120  					TransformRequests(func(req *rest.Request) {
  1121  						*got = req
  1122  					}).
  1123  					ResourceNames("", "services/baz").
  1124  					RequireObject(false)
  1125  			},
  1126  		},
  1127  		{
  1128  			name: "flatten",
  1129  			f: func(t *testing.T, got **rest.Request) *Builder {
  1130  				pods, _ := testData()
  1131  				return newDefaultBuilderWith(fakeClientWith(t.Name(), t, map[string]string{
  1132  					"/namespaces/foo/pods": runtime.EncodeOrDie(corev1Codec, pods),
  1133  				})).
  1134  					NamespaceParam("foo").
  1135  					TransformRequests(func(req *rest.Request) {
  1136  						*got = req
  1137  					}).
  1138  					ResourceTypeOrNameArgs(true, "pods").
  1139  					Flatten()
  1140  			},
  1141  		},
  1142  	} {
  1143  		t.Run(tc.name, func(t *testing.T) {
  1144  			var got *rest.Request
  1145  			b := tc.f(t, &got)
  1146  			i, err := b.Do().Infos()
  1147  			if err != nil {
  1148  				t.Fatal(err)
  1149  			}
  1150  			req := i[0].Client.Get()
  1151  			if got != req {
  1152  				t.Fatalf("request was not received by modifier: %#v", req)
  1153  			}
  1154  		})
  1155  	}
  1156  }
  1157  
  1158  func TestResourceNames(t *testing.T) {
  1159  	pods, svc := testData()
  1160  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1161  		"/namespaces/test/pods/foo":     runtime.EncodeOrDie(corev1Codec, &pods.Items[0]),
  1162  		"/namespaces/test/services/baz": runtime.EncodeOrDie(corev1Codec, &svc.Items[0]),
  1163  	})).NamespaceParam("test")
  1164  
  1165  	test := &testVisitor{}
  1166  
  1167  	if b.Do().Err() == nil {
  1168  		t.Errorf("unexpected non-error")
  1169  	}
  1170  
  1171  	b.ResourceNames("pods", "foo", "services/baz")
  1172  
  1173  	err := b.Do().Visit(test.Handle)
  1174  	if err != nil || len(test.Infos) != 2 {
  1175  		t.Fatalf("unexpected response: %v %#v", err, test.Infos)
  1176  	}
  1177  	if !apiequality.Semantic.DeepEqual(&pods.Items[0], test.Objects()[0]) {
  1178  		t.Errorf("unexpected object: \n%#v, expected: \n%#v", test.Objects()[0], &pods.Items[0])
  1179  	}
  1180  	if !apiequality.Semantic.DeepEqual(&svc.Items[0], test.Objects()[1]) {
  1181  		t.Errorf("unexpected object: \n%#v, expected: \n%#v", test.Objects()[1], &svc.Items[0])
  1182  	}
  1183  }
  1184  
  1185  func TestResourceNamesWithoutResource(t *testing.T) {
  1186  	pods, svc := testData()
  1187  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1188  		"/namespaces/test/pods/foo":     runtime.EncodeOrDie(corev1Codec, &pods.Items[0]),
  1189  		"/namespaces/test/services/baz": runtime.EncodeOrDie(corev1Codec, &svc.Items[0]),
  1190  	})).NamespaceParam("test")
  1191  
  1192  	test := &testVisitor{}
  1193  
  1194  	if b.Do().Err() == nil {
  1195  		t.Errorf("unexpected non-error")
  1196  	}
  1197  
  1198  	b.ResourceNames("", "foo", "services/baz")
  1199  
  1200  	err := b.Do().Visit(test.Handle)
  1201  	if err == nil || !strings.Contains(err.Error(), "must be RESOURCE/NAME") {
  1202  		t.Fatalf("unexpected response: %v", err)
  1203  	}
  1204  }
  1205  
  1206  func TestResourceByNameWithoutRequireObject(t *testing.T) {
  1207  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{})).NamespaceParam("test")
  1208  
  1209  	test := &testVisitor{}
  1210  	singleItemImplied := false
  1211  
  1212  	if b.Do().Err() == nil {
  1213  		t.Errorf("unexpected non-error")
  1214  	}
  1215  
  1216  	b.ResourceTypeOrNameArgs(true, "pods", "foo").RequireObject(false)
  1217  
  1218  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1219  	if err != nil || !singleItemImplied || len(test.Infos) != 1 {
  1220  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
  1221  	}
  1222  	if test.Infos[0].Name != "foo" {
  1223  		t.Errorf("unexpected name: %#v", test.Infos[0].Name)
  1224  	}
  1225  	if test.Infos[0].Object != nil {
  1226  		t.Errorf("unexpected object: %#v", test.Infos[0].Object)
  1227  	}
  1228  
  1229  	mapping, err := b.Do().ResourceMapping()
  1230  	if err != nil {
  1231  		t.Fatalf("unexpected error: %v", err)
  1232  	}
  1233  	if mapping.GroupVersionKind.Kind != "Pod" || mapping.Resource != (schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}) {
  1234  		t.Errorf("unexpected resource mapping: %#v", mapping)
  1235  	}
  1236  }
  1237  
  1238  func TestResourceByNameAndEmptySelector(t *testing.T) {
  1239  	pods, _ := testData()
  1240  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1241  		"/namespaces/test/pods/foo": runtime.EncodeOrDie(corev1Codec, &pods.Items[0]),
  1242  	})).
  1243  		NamespaceParam("test").
  1244  		LabelSelectorParam("").
  1245  		ResourceTypeOrNameArgs(true, "pods", "foo")
  1246  
  1247  	singleItemImplied := false
  1248  	infos, err := b.Do().IntoSingleItemImplied(&singleItemImplied).Infos()
  1249  	if err != nil || !singleItemImplied || len(infos) != 1 {
  1250  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, infos)
  1251  	}
  1252  	if !apiequality.Semantic.DeepEqual(&pods.Items[0], infos[0].Object) {
  1253  		t.Errorf("unexpected object: %#v", infos[0])
  1254  	}
  1255  
  1256  	mapping, err := b.Do().ResourceMapping()
  1257  	if err != nil {
  1258  		t.Fatalf("unexpected error: %v", err)
  1259  	}
  1260  	if mapping.Resource != (schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}) {
  1261  		t.Errorf("unexpected resource mapping: %#v", mapping)
  1262  	}
  1263  }
  1264  
  1265  func TestLabelSelector(t *testing.T) {
  1266  	pods, svc := testData()
  1267  	labelKey := metav1.LabelSelectorQueryParam(corev1GV.String())
  1268  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1269  		"/namespaces/test/pods?" + labelKey + "=a%3Db":     runtime.EncodeOrDie(corev1Codec, pods),
  1270  		"/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(corev1Codec, svc),
  1271  	})).
  1272  		LabelSelectorParam("a=b").
  1273  		NamespaceParam("test").
  1274  		Flatten()
  1275  
  1276  	test := &testVisitor{}
  1277  	singleItemImplied := false
  1278  
  1279  	if b.Do().Err() == nil {
  1280  		t.Errorf("unexpected non-error")
  1281  	}
  1282  
  1283  	b.ResourceTypeOrNameArgs(true, "pods,service")
  1284  
  1285  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1286  	if err != nil || singleItemImplied || len(test.Infos) != 3 {
  1287  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
  1288  	}
  1289  	if !apiequality.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &svc.Items[0]}, test.Objects()) {
  1290  		t.Errorf("unexpected visited objects: %#v", test.Objects())
  1291  	}
  1292  
  1293  	if _, err := b.Do().ResourceMapping(); err == nil {
  1294  		t.Errorf("unexpected non-error")
  1295  	}
  1296  }
  1297  
  1298  func TestLabelSelectorRequiresKnownTypes(t *testing.T) {
  1299  	b := newDefaultBuilder().
  1300  		LabelSelectorParam("a=b").
  1301  		NamespaceParam("test").
  1302  		ResourceTypes("unknown")
  1303  
  1304  	if b.Do().Err() == nil {
  1305  		t.Errorf("unexpected non-error")
  1306  	}
  1307  }
  1308  
  1309  func TestFieldSelector(t *testing.T) {
  1310  	pods, svc := testData()
  1311  	fieldKey := metav1.FieldSelectorQueryParam(corev1GV.String())
  1312  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1313  		"/namespaces/test/pods?" + fieldKey + "=a%3Db":     runtime.EncodeOrDie(corev1Codec, pods),
  1314  		"/namespaces/test/services?" + fieldKey + "=a%3Db": runtime.EncodeOrDie(corev1Codec, svc),
  1315  	})).
  1316  		FieldSelectorParam("a=b").
  1317  		NamespaceParam("test").
  1318  		Flatten()
  1319  
  1320  	test := &testVisitor{}
  1321  	singleItemImplied := false
  1322  
  1323  	if b.Do().Err() == nil {
  1324  		t.Errorf("unexpected non-error")
  1325  	}
  1326  
  1327  	b.ResourceTypeOrNameArgs(true, "pods,service")
  1328  
  1329  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1330  	if err != nil || singleItemImplied || len(test.Infos) != 3 {
  1331  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
  1332  	}
  1333  	if !apiequality.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &svc.Items[0]}, test.Objects()) {
  1334  		t.Errorf("unexpected visited objects: %#v", test.Objects())
  1335  	}
  1336  
  1337  	if _, err := b.Do().ResourceMapping(); err == nil {
  1338  		t.Errorf("unexpected non-error")
  1339  	}
  1340  }
  1341  
  1342  func TestFieldSelectorRequiresKnownTypes(t *testing.T) {
  1343  	b := newDefaultBuilder().
  1344  		FieldSelectorParam("a=b").
  1345  		NamespaceParam("test").
  1346  		ResourceTypes("unknown")
  1347  
  1348  	if b.Do().Err() == nil {
  1349  		t.Errorf("unexpected non-error")
  1350  	}
  1351  }
  1352  func TestNoSelectorUnknowResourceType(t *testing.T) {
  1353  	b := newDefaultBuilder().
  1354  		NamespaceParam("test").
  1355  		ResourceTypeOrNameArgs(false, "unknown")
  1356  
  1357  	err := b.Do().Err()
  1358  	if err != nil {
  1359  		if !strings.Contains(err.Error(), "server doesn't have a resource type \"unknown\"") {
  1360  			t.Fatalf("unexpected error: %v", err)
  1361  		}
  1362  	}
  1363  }
  1364  func TestSingleResourceType(t *testing.T) {
  1365  	b := newDefaultBuilder().
  1366  		LabelSelectorParam("a=b").
  1367  		SingleResourceType().
  1368  		ResourceTypeOrNameArgs(true, "pods,services")
  1369  
  1370  	if b.Do().Err() == nil {
  1371  		t.Errorf("unexpected non-error")
  1372  	}
  1373  }
  1374  
  1375  func TestResourceTuple(t *testing.T) {
  1376  	expectNoErr := func(err error) bool { return err == nil }
  1377  	expectErr := func(err error) bool { return err != nil }
  1378  	testCases := map[string]struct {
  1379  		args        []string
  1380  		subresource string
  1381  		errFn       func(error) bool
  1382  	}{
  1383  		"valid": {
  1384  			args:  []string{"pods/foo"},
  1385  			errFn: expectNoErr,
  1386  		},
  1387  		"valid multiple with name indirection": {
  1388  			args:  []string{"pods/foo", "pod/bar"},
  1389  			errFn: expectNoErr,
  1390  		},
  1391  		"valid multiple with namespaced and non-namespaced types": {
  1392  			args:  []string{"nodes/foo", "pod/bar"},
  1393  			errFn: expectNoErr,
  1394  		},
  1395  		"mixed arg types": {
  1396  			args:  []string{"pods/foo", "bar"},
  1397  			errFn: expectErr,
  1398  		},
  1399  		/*"missing resource": {
  1400  			args:  []string{"pods/foo2"},
  1401  			errFn: expectNoErr, // not an error because resources are lazily visited
  1402  		},*/
  1403  		"comma in resource": {
  1404  			args:  []string{",pods/foo"},
  1405  			errFn: expectErr,
  1406  		},
  1407  		"multiple types in resource": {
  1408  			args:  []string{"pods,services/foo"},
  1409  			errFn: expectErr,
  1410  		},
  1411  		"unknown resource type": {
  1412  			args:  []string{"unknown/foo"},
  1413  			errFn: expectErr,
  1414  		},
  1415  		"leading slash": {
  1416  			args:  []string{"/bar"},
  1417  			errFn: expectErr,
  1418  		},
  1419  		"trailing slash": {
  1420  			args:  []string{"bar/"},
  1421  			errFn: expectErr,
  1422  		},
  1423  		"valid status subresource": {
  1424  			args:        []string{"pods/foo"},
  1425  			subresource: "status",
  1426  			errFn:       expectNoErr,
  1427  		},
  1428  		"valid status subresource for multiple with name indirection": {
  1429  			args:        []string{"pods/foo", "pod/bar"},
  1430  			subresource: "status",
  1431  			errFn:       expectNoErr,
  1432  		},
  1433  	}
  1434  	for k, tt := range testCases {
  1435  		t.Run("using default namespace", func(t *testing.T) {
  1436  			for _, requireObject := range []bool{true, false} {
  1437  				expectedRequests := map[string]string{}
  1438  				if requireObject {
  1439  					pods, _ := testData()
  1440  					expectedRequests = map[string]string{
  1441  						"/namespaces/test/pods/foo":        runtime.EncodeOrDie(corev1Codec, &pods.Items[0]),
  1442  						"/namespaces/test/pods/bar":        runtime.EncodeOrDie(corev1Codec, &pods.Items[0]),
  1443  						"/namespaces/test/pods/foo/status": runtime.EncodeOrDie(corev1Codec, subresourceTestData("foo")),
  1444  						"/namespaces/test/pods/bar/status": runtime.EncodeOrDie(corev1Codec, subresourceTestData("bar")),
  1445  						"/nodes/foo":                       runtime.EncodeOrDie(corev1Codec, &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}),
  1446  					}
  1447  				}
  1448  				b := newDefaultBuilderWith(fakeClientWith(k, t, expectedRequests)).
  1449  					NamespaceParam("test").DefaultNamespace().
  1450  					ResourceTypeOrNameArgs(true, tt.args...).
  1451  					RequireObject(requireObject).
  1452  					Subresource(tt.subresource)
  1453  
  1454  				r := b.Do()
  1455  
  1456  				if !tt.errFn(r.Err()) {
  1457  					t.Errorf("%s: unexpected error: %v", k, r.Err())
  1458  				}
  1459  				if r.Err() != nil {
  1460  					continue
  1461  				}
  1462  				switch {
  1463  				case (r.singleItemImplied && len(tt.args) != 1),
  1464  					(!r.singleItemImplied && len(tt.args) == 1):
  1465  					t.Errorf("%s: result had unexpected singleItemImplied value", k)
  1466  				}
  1467  				info, err := r.Infos()
  1468  				if err != nil {
  1469  					// test error
  1470  					continue
  1471  				}
  1472  				if len(info) != len(tt.args) {
  1473  					t.Errorf("%s: unexpected number of infos returned: %#v", k, info)
  1474  				}
  1475  			}
  1476  		})
  1477  	}
  1478  }
  1479  
  1480  func TestStream(t *testing.T) {
  1481  	r, pods, rc := streamTestData()
  1482  	b := newDefaultBuilder().
  1483  		NamespaceParam("test").Stream(r, "STDIN").Flatten()
  1484  
  1485  	test := &testVisitor{}
  1486  	singleItemImplied := false
  1487  
  1488  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1489  	if err != nil || singleItemImplied || len(test.Infos) != 3 {
  1490  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
  1491  	}
  1492  	if !apiequality.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &rc.Items[0]}, test.Objects()) {
  1493  		t.Errorf("unexpected visited objects: %#v", test.Objects())
  1494  	}
  1495  }
  1496  
  1497  func TestYAMLStream(t *testing.T) {
  1498  	r, pods, rc := streamYAMLTestData()
  1499  	b := newDefaultBuilder().
  1500  		NamespaceParam("test").Stream(r, "STDIN").Flatten()
  1501  
  1502  	test := &testVisitor{}
  1503  	singleItemImplied := false
  1504  
  1505  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1506  	if err != nil || singleItemImplied || len(test.Infos) != 3 {
  1507  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
  1508  	}
  1509  	if !apiequality.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &rc.Items[0]}, test.Objects()) {
  1510  		t.Errorf("unexpected visited objects: %#v", test.Objects())
  1511  	}
  1512  }
  1513  
  1514  func TestMultipleObject(t *testing.T) {
  1515  	r, pods, svc := streamTestData()
  1516  	obj, err := newDefaultBuilder().
  1517  		NamespaceParam("test").Stream(r, "STDIN").Flatten().
  1518  		Do().Object()
  1519  
  1520  	if err != nil {
  1521  		t.Fatalf("unexpected error: %v", err)
  1522  	}
  1523  
  1524  	expected := &v1.List{
  1525  		Items: []runtime.RawExtension{
  1526  			{Object: &pods.Items[0]},
  1527  			{Object: &pods.Items[1]},
  1528  			{Object: &svc.Items[0]},
  1529  		},
  1530  	}
  1531  	if !apiequality.Semantic.DeepDerivative(expected, obj) {
  1532  		t.Errorf("unexpected visited objects: %#v", obj)
  1533  	}
  1534  }
  1535  
  1536  func TestContinueOnErrorVisitor(t *testing.T) {
  1537  	r, _, _ := streamTestData()
  1538  	req := newDefaultBuilder().
  1539  		ContinueOnError().
  1540  		NamespaceParam("test").Stream(r, "STDIN").Flatten().
  1541  		Do()
  1542  	count := 0
  1543  	testErr := fmt.Errorf("test error")
  1544  	err := req.Visit(func(_ *Info, _ error) error {
  1545  		count++
  1546  		if count > 1 {
  1547  			return testErr
  1548  		}
  1549  		return nil
  1550  	})
  1551  	if err == nil {
  1552  		t.Fatalf("unexpected error: %v", err)
  1553  	}
  1554  	if count != 3 {
  1555  		t.Fatalf("did not visit all infos: %d", count)
  1556  	}
  1557  	agg, ok := err.(utilerrors.Aggregate)
  1558  	if !ok {
  1559  		t.Fatalf("unexpected error: %v", err)
  1560  	}
  1561  	if len(agg.Errors()) != 2 || agg.Errors()[0] != testErr || agg.Errors()[1] != testErr {
  1562  		t.Fatalf("unexpected error: %v", err)
  1563  	}
  1564  }
  1565  
  1566  func TestSingleItemImpliedObject(t *testing.T) {
  1567  	obj, err := newDefaultBuilder().
  1568  		NamespaceParam("test").DefaultNamespace().
  1569  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{"../../artifacts/guestbook/redis-master-controller.yaml"}}).
  1570  		Flatten().
  1571  		Do().Object()
  1572  
  1573  	if err != nil {
  1574  		t.Fatalf("unexpected error: %v", err)
  1575  	}
  1576  
  1577  	rc, ok := obj.(*v1.ReplicationController)
  1578  	if !ok {
  1579  		t.Fatalf("unexpected object: %#v", obj)
  1580  	}
  1581  	if rc.Name != "redis-master" || rc.Namespace != "test" {
  1582  		t.Errorf("unexpected controller: %#v", rc)
  1583  	}
  1584  }
  1585  
  1586  func TestSingleItemImpliedObjectNoExtension(t *testing.T) {
  1587  	obj, err := newDefaultBuilder().
  1588  		NamespaceParam("test").DefaultNamespace().
  1589  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{"../../artifacts/pod.yaml"}}).
  1590  		Flatten().
  1591  		Do().Object()
  1592  
  1593  	if err != nil {
  1594  		t.Fatalf("unexpected error: %v", err)
  1595  	}
  1596  
  1597  	pod, ok := obj.(*v1.Pod)
  1598  	if !ok {
  1599  		t.Fatalf("unexpected object: %#v", obj)
  1600  	}
  1601  	if pod.Name != "nginx" || pod.Namespace != "test" {
  1602  		t.Errorf("unexpected pod: %#v", pod)
  1603  	}
  1604  }
  1605  
  1606  func TestSingleItemImpliedRootScopedObject(t *testing.T) {
  1607  	node := &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "test"}}
  1608  	r := streamTestObject(node)
  1609  	infos, err := newDefaultBuilder().
  1610  		NamespaceParam("test").DefaultNamespace().
  1611  		Stream(r, "STDIN").
  1612  		Flatten().
  1613  		Do().Infos()
  1614  
  1615  	if err != nil || len(infos) != 1 {
  1616  		t.Fatalf("unexpected error: %v", err)
  1617  	}
  1618  
  1619  	if infos[0].Namespace != "" {
  1620  		t.Errorf("namespace should be empty: %#v", infos[0])
  1621  	}
  1622  	n, ok := infos[0].Object.(*v1.Node)
  1623  	if !ok {
  1624  		t.Fatalf("unexpected object: %#v", infos[0].Object)
  1625  	}
  1626  	if n.Name != "test" || n.Namespace != "" {
  1627  		t.Errorf("unexpected object: %#v", n)
  1628  	}
  1629  }
  1630  
  1631  func TestListObject(t *testing.T) {
  1632  	pods, _ := testData()
  1633  	labelKey := metav1.LabelSelectorQueryParam(corev1GV.String())
  1634  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1635  		"/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(corev1Codec, pods),
  1636  	})).
  1637  		LabelSelectorParam("a=b").
  1638  		NamespaceParam("test").
  1639  		ResourceTypeOrNameArgs(true, "pods").
  1640  		Flatten()
  1641  
  1642  	obj, err := b.Do().Object()
  1643  	if err != nil {
  1644  		t.Fatalf("unexpected error: %v", err)
  1645  	}
  1646  
  1647  	list, ok := obj.(*v1.List)
  1648  	if !ok {
  1649  		t.Fatalf("unexpected object: %#v", obj)
  1650  	}
  1651  	if list.ResourceVersion != pods.ResourceVersion || len(list.Items) != 2 {
  1652  		t.Errorf("unexpected list: %#v", list)
  1653  	}
  1654  
  1655  	mapping, err := b.Do().ResourceMapping()
  1656  	if err != nil {
  1657  		t.Fatalf("unexpected error: %v", err)
  1658  	}
  1659  	if mapping.Resource != (schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}) {
  1660  		t.Errorf("unexpected resource mapping: %#v", mapping)
  1661  	}
  1662  }
  1663  
  1664  func TestListObjectWithDifferentVersions(t *testing.T) {
  1665  	pods, svc := testData()
  1666  	labelKey := metav1.LabelSelectorQueryParam(corev1GV.String())
  1667  	obj, err := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1668  		"/namespaces/test/pods?" + labelKey + "=a%3Db":     runtime.EncodeOrDie(corev1Codec, pods),
  1669  		"/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(corev1Codec, svc),
  1670  	})).
  1671  		LabelSelectorParam("a=b").
  1672  		NamespaceParam("test").
  1673  		ResourceTypeOrNameArgs(true, "pods,services").
  1674  		Flatten().
  1675  		Do().Object()
  1676  
  1677  	if err != nil {
  1678  		t.Fatalf("unexpected error: %v", err)
  1679  	}
  1680  
  1681  	list, ok := obj.(*v1.List)
  1682  	if !ok {
  1683  		t.Fatalf("unexpected object: %#v", obj)
  1684  	}
  1685  	// resource version differs between type lists, so it's not possible to get a single version.
  1686  	if list.ResourceVersion != "" || len(list.Items) != 3 {
  1687  		t.Errorf("unexpected list: %#v", list)
  1688  	}
  1689  }
  1690  
  1691  func TestListObjectSubresource(t *testing.T) {
  1692  	pods, _ := testData()
  1693  	labelKey := metav1.LabelSelectorQueryParam(corev1GV.String())
  1694  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1695  		"/namespaces/test/pods?" + labelKey: runtime.EncodeOrDie(corev1Codec, pods),
  1696  	})).
  1697  		NamespaceParam("test").
  1698  		ResourceTypeOrNameArgs(true, "pods").
  1699  		Subresource("status").
  1700  		Flatten()
  1701  
  1702  	_, err := b.Do().Object()
  1703  	if err == nil || !strings.Contains(err.Error(), "subresource cannot be used when bulk resources are specified") {
  1704  		t.Fatalf("unexpected response: %v", err)
  1705  	}
  1706  }
  1707  
  1708  func TestWatch(t *testing.T) {
  1709  	_, svc := testData()
  1710  	w, err := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1711  		"/namespaces/test/services?fieldSelector=metadata.name%3Dredis-master&resourceVersion=12&watch=true": watchBody(watch.Event{
  1712  			Type:   watch.Added,
  1713  			Object: &svc.Items[0],
  1714  		}),
  1715  	})).
  1716  		NamespaceParam("test").DefaultNamespace().
  1717  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{"../../artifacts/guestbook/redis-master-service.yaml"}}).Flatten().
  1718  		Do().Watch("12")
  1719  
  1720  	if err != nil {
  1721  		t.Fatalf("unexpected error: %v", err)
  1722  	}
  1723  
  1724  	defer w.Stop()
  1725  	ch := w.ResultChan()
  1726  	select {
  1727  	case obj := <-ch:
  1728  		if obj.Type != watch.Added {
  1729  			t.Fatalf("unexpected watch event %#v", obj)
  1730  		}
  1731  		service, ok := obj.Object.(*v1.Service)
  1732  		if !ok {
  1733  			t.Fatalf("unexpected object: %#v", obj)
  1734  		}
  1735  		if service.Name != "baz" || service.ResourceVersion != "12" {
  1736  			t.Errorf("unexpected service: %#v", service)
  1737  		}
  1738  	}
  1739  }
  1740  
  1741  func TestWatchMultipleError(t *testing.T) {
  1742  	_, err := newDefaultBuilder().
  1743  		NamespaceParam("test").DefaultNamespace().
  1744  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{"../../artifacts/guestbook/redis-master-controller.yaml"}}).Flatten().
  1745  		FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{"../../artifacts/guestbook/redis-master-controller.yaml"}}).Flatten().
  1746  		Do().Watch("")
  1747  
  1748  	if err == nil {
  1749  		t.Fatalf("unexpected non-error")
  1750  	}
  1751  }
  1752  
  1753  func TestLatest(t *testing.T) {
  1754  	r, _, _ := streamTestData()
  1755  	newPod := &v1.Pod{
  1756  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "13"},
  1757  	}
  1758  	newPod2 := &v1.Pod{
  1759  		ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "14"},
  1760  	}
  1761  	newSvc := &v1.Service{
  1762  		ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "15"},
  1763  	}
  1764  
  1765  	b := newDefaultBuilderWith(fakeClientWith("", t, map[string]string{
  1766  		"/namespaces/test/pods/foo":     runtime.EncodeOrDie(corev1Codec, newPod),
  1767  		"/namespaces/test/pods/bar":     runtime.EncodeOrDie(corev1Codec, newPod2),
  1768  		"/namespaces/test/services/baz": runtime.EncodeOrDie(corev1Codec, newSvc),
  1769  	})).
  1770  		NamespaceParam("other").Stream(r, "STDIN").Flatten().Latest()
  1771  
  1772  	test := &testVisitor{}
  1773  	singleItemImplied := false
  1774  
  1775  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1776  	if err != nil || singleItemImplied || len(test.Infos) != 3 {
  1777  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
  1778  	}
  1779  	if !apiequality.Semantic.DeepDerivative([]runtime.Object{newPod, newPod2, newSvc}, test.Objects()) {
  1780  		t.Errorf("unexpected visited objects: %#v", test.Objects())
  1781  	}
  1782  }
  1783  
  1784  func TestReceiveMultipleErrors(t *testing.T) {
  1785  	pods, svc := testData()
  1786  
  1787  	r, w := io.Pipe()
  1788  	go func() {
  1789  		defer w.Close()
  1790  		w.Write([]byte(`{}`))
  1791  		w.Write([]byte(runtime.EncodeOrDie(corev1Codec, &pods.Items[0])))
  1792  	}()
  1793  
  1794  	r2, w2 := io.Pipe()
  1795  	go func() {
  1796  		defer w2.Close()
  1797  		w2.Write([]byte(`{}`))
  1798  		w2.Write([]byte(runtime.EncodeOrDie(corev1Codec, &svc.Items[0])))
  1799  	}()
  1800  
  1801  	b := newDefaultBuilder().
  1802  		Stream(r, "1").Stream(r2, "2").
  1803  		ContinueOnError()
  1804  
  1805  	test := &testVisitor{}
  1806  	singleItemImplied := false
  1807  
  1808  	err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle)
  1809  	if err == nil || singleItemImplied || len(test.Infos) != 2 {
  1810  		t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos)
  1811  	}
  1812  
  1813  	errs, ok := err.(utilerrors.Aggregate)
  1814  	if !ok {
  1815  		t.Fatalf("unexpected error: %v", reflect.TypeOf(err))
  1816  	}
  1817  	if len(errs.Errors()) != 2 {
  1818  		t.Errorf("unexpected errors %v", errs)
  1819  	}
  1820  }
  1821  
  1822  func TestHasNames(t *testing.T) {
  1823  	basename := filepath.Base(os.Args[0])
  1824  	tests := []struct {
  1825  		name            string
  1826  		args            []string
  1827  		expectedHasName bool
  1828  		expectedError   error
  1829  	}{
  1830  		{
  1831  			name:            "test1",
  1832  			args:            []string{""},
  1833  			expectedHasName: false,
  1834  			expectedError:   nil,
  1835  		},
  1836  		{
  1837  			name:            "test2",
  1838  			args:            []string{"rc"},
  1839  			expectedHasName: false,
  1840  			expectedError:   nil,
  1841  		},
  1842  		{
  1843  			name:            "test3",
  1844  			args:            []string{"rc,pod,svc"},
  1845  			expectedHasName: false,
  1846  			expectedError:   nil,
  1847  		},
  1848  		{
  1849  			name:            "test4",
  1850  			args:            []string{"rc/foo"},
  1851  			expectedHasName: true,
  1852  			expectedError:   nil,
  1853  		},
  1854  		{
  1855  			name:            "test5",
  1856  			args:            []string{"rc", "foo"},
  1857  			expectedHasName: true,
  1858  			expectedError:   nil,
  1859  		},
  1860  		{
  1861  			name:            "test6",
  1862  			args:            []string{"rc,pod,svc", "foo"},
  1863  			expectedHasName: true,
  1864  			expectedError:   nil,
  1865  		},
  1866  		{
  1867  			name:            "test7",
  1868  			args:            []string{"rc/foo", "rc/bar", "rc/zee"},
  1869  			expectedHasName: true,
  1870  			expectedError:   nil,
  1871  		},
  1872  		{
  1873  			name:            "test8",
  1874  			args:            []string{"rc/foo", "bar"},
  1875  			expectedHasName: false,
  1876  			expectedError:   fmt.Errorf("there is no need to specify a resource type as a separate argument when passing arguments in resource/name form (e.g. '" + basename + " get resource/<resource_name>' instead of '" + basename + " get resource resource/<resource_name>'"),
  1877  		},
  1878  	}
  1879  	for _, tt := range tests {
  1880  		t.Run(tt.name, func(t *testing.T) {
  1881  			hasNames, err := HasNames(tt.args)
  1882  			if !reflect.DeepEqual(tt.expectedError, err) {
  1883  				t.Errorf("expected HasName to error:\n%s\tgot:\n%s", tt.expectedError, err)
  1884  			}
  1885  			if hasNames != tt.expectedHasName {
  1886  				t.Errorf("expected HasName to return %v for %s", tt.expectedHasName, tt.args)
  1887  			}
  1888  		})
  1889  	}
  1890  }
  1891  
  1892  func TestUnstructured(t *testing.T) {
  1893  	// create test dirs
  1894  	tmpDir, err := os.MkdirTemp(os.TempDir(), "unstructured_test")
  1895  	if err != nil {
  1896  		t.Fatalf("error creating temp dir: %v", err)
  1897  	}
  1898  	defer os.RemoveAll(tmpDir)
  1899  
  1900  	// create test files
  1901  	writeTestFile(t, fmt.Sprintf("%s/pod.json", tmpDir), aPod)
  1902  	writeTestFile(t, fmt.Sprintf("%s/badpod.json", tmpDir), aPodBadAnnotations)
  1903  
  1904  	tests := []struct {
  1905  		name          string
  1906  		file          string
  1907  		expectedError string
  1908  	}{
  1909  		{
  1910  			name:          "pod",
  1911  			file:          "pod.json",
  1912  			expectedError: "",
  1913  		},
  1914  		{
  1915  			name:          "badpod",
  1916  			file:          "badpod.json",
  1917  			expectedError: "ObjectMeta.",
  1918  		},
  1919  	}
  1920  
  1921  	for _, tc := range tests {
  1922  		t.Run(tc.name, func(t *testing.T) {
  1923  			result := newUnstructuredDefaultBuilder().
  1924  				ContinueOnError().
  1925  				FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{fmt.Sprintf("%s/%s", tmpDir, tc.file)}}).
  1926  				Flatten().
  1927  				Do()
  1928  
  1929  			err := result.Err()
  1930  			if err == nil {
  1931  				_, err = result.Infos()
  1932  			}
  1933  
  1934  			if len(tc.expectedError) == 0 {
  1935  				if err != nil {
  1936  					t.Errorf("unexpected error: %v", err)
  1937  				}
  1938  			} else {
  1939  				if err == nil {
  1940  					t.Errorf("expected error, got none")
  1941  				} else if !strings.Contains(err.Error(), tc.expectedError) {
  1942  					t.Errorf("expected error with '%s', got: %v", tc.expectedError, err)
  1943  				}
  1944  			}
  1945  
  1946  		})
  1947  	}
  1948  }
  1949  
  1950  func TestStdinMultiUseError(t *testing.T) {
  1951  	if got, want := newUnstructuredDefaultBuilder().Stdin().StdinInUse().Do().Err(), StdinMultiUseError; !errors.Is(got, want) {
  1952  		t.Errorf("got: %q, want: %q", got, want)
  1953  	}
  1954  	if got, want := newUnstructuredDefaultBuilder().StdinInUse().Stdin().Do().Err(), StdinMultiUseError; !errors.Is(got, want) {
  1955  		t.Errorf("got: %q, want: %q", got, want)
  1956  	}
  1957  }
  1958  

View as plain text