From ad6dfa0370d7c95a7ebba4eb81c6de0f018098d1 Mon Sep 17 00:00:00 2001 From: Avesh Agarwal Date: Thu, 25 Feb 2016 15:43:48 -0500 Subject: [PATCH] Fix kubectl create to create all resources in a url. https://github.com/kubernetes/kubernetes/issues/18751 . --- hack/test-cmd.sh | 23 +++++++++++++++++++++++ pkg/kubectl/resource/builder.go | 5 ++--- pkg/kubectl/resource/builder_test.go | 16 +++++++++++----- pkg/kubectl/resource/visitor.go | 21 +++++---------------- 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 83a2dd56516..2aff078d872 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -1499,6 +1499,29 @@ __EOF__ kubectl delete -f "${file}" "${kube_flags[@]}" done + ############################# + # Multiple Resources via URL# + ############################# + + # Pre-condition: no service (other than default kubernetes services) or replication controller exists + kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:' + kube::test::get_object_assert rc "{{range.items}}{{$id_field}}:{{end}}" '' + + # Command + kubectl create -f https://raw.githubusercontent.com/kubernetes/kubernetes/master/hack/testdata/multi-resource-yaml.yaml "${kube_flags[@]}" + + # Post-condition: service(mock) and rc(mock) exist + kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:mock:' + kube::test::get_object_assert rc "{{range.items}}{{$id_field}}:{{end}}" 'mock:' + + # Clean up + kubectl delete -f https://raw.githubusercontent.com/kubernetes/kubernetes/master/hack/testdata/multi-resource-yaml.yaml "${kube_flags[@]}" + + # Post-condition: no service (other than default kubernetes services) or replication controller exists + kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:' + kube::test::get_object_assert rc "{{range.items}}{{$id_field}}:{{end}}" '' + + ###################### # Persistent Volumes # ###################### diff --git a/pkg/kubectl/resource/builder.go b/pkg/kubectl/resource/builder.go index 3025a4cbbfa..90000ad5385 100644 --- a/pkg/kubectl/resource/builder.go +++ b/pkg/kubectl/resource/builder.go @@ -126,9 +126,8 @@ func (b *Builder) FilenameParam(enforceNamespace bool, paths ...string) *Builder func (b *Builder) URL(urls ...*url.URL) *Builder { for _, u := range urls { b.paths = append(b.paths, &URLVisitor{ - Mapper: b.mapper, - URL: u, - Schema: b.schema, + URL: u, + StreamVisitor: NewStreamVisitor(nil, b.mapper, u.String(), b.schema), }) } return b diff --git a/pkg/kubectl/resource/builder_test.go b/pkg/kubectl/resource/builder_test.go index 1fad662b63f..2d784906238 100644 --- a/pkg/kubectl/resource/builder_test.go +++ b/pkg/kubectl/resource/builder_test.go @@ -316,25 +316,31 @@ func TestURLBuilder(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &api.Pod{ObjectMeta: api.ObjectMeta{Namespace: "foo", Name: "test"}}))) + w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &api.Pod{ObjectMeta: api.ObjectMeta{Namespace: "foo", Name: "test1"}}))) })) // TODO: Uncomment when fix #19254 // defer s.Close() b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, s.URL). - NamespaceParam("test") + NamespaceParam("foo") test := &testVisitor{} - singular := false - err := b.Do().IntoSingular(&singular).Visit(test.Handle) - if err != nil || !singular || len(test.Infos) != 1 { - t.Fatalf("unexpected response: %v %t %#v", err, singular, test.Infos) + err := b.Do().Visit(test.Handle) + if err != nil || len(test.Infos) != 2 { + t.Fatalf("unexpected response: %v %#v", err, test.Infos) } info := test.Infos[0] if info.Name != "test" || info.Namespace != "foo" || info.Object == nil { t.Errorf("unexpected info: %#v", info) } + + info = test.Infos[1] + if info.Name != "test1" || info.Namespace != "foo" || info.Object == nil { + t.Errorf("unexpected info: %#v", info) + } + } func TestURLBuilderRequireNamespace(t *testing.T) { diff --git a/pkg/kubectl/resource/visitor.go b/pkg/kubectl/resource/visitor.go index a187919a414..10c8425d975 100644 --- a/pkg/kubectl/resource/visitor.go +++ b/pkg/kubectl/resource/visitor.go @@ -20,7 +20,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "net/http" "net/url" "os" @@ -219,9 +218,8 @@ func ValidateSchema(data []byte, schema validation.Schema) error { // URLVisitor downloads the contents of a URL, and if successful, returns // an info object representing the downloaded object. type URLVisitor struct { - *Mapper - URL *url.URL - Schema validation.Schema + URL *url.URL + *StreamVisitor } func (v *URLVisitor) Visit(fn VisitorFunc) error { @@ -233,18 +231,9 @@ func (v *URLVisitor) Visit(fn VisitorFunc) error { if res.StatusCode != 200 { return fmt.Errorf("unable to read URL %q, server reported %d %s", v.URL, res.StatusCode, res.Status) } - data, err := ioutil.ReadAll(res.Body) - if err != nil { - return fmt.Errorf("unable to read URL %q: %v\n", v.URL, err) - } - if err := ValidateSchema(data, v.Schema); err != nil { - return fmt.Errorf("error validating %q: %v", v.URL, err) - } - info, err := v.Mapper.InfoForData(data, v.URL.String()) - if err != nil { - return err - } - return fn(info, nil) + + v.StreamVisitor.Reader = res.Body + return v.StreamVisitor.Visit(fn) } // DecoratedVisitor will invoke the decorators in order prior to invoking the visitor function