From 61e88cbc412a0a77e4eba3dc03b888cc18ea647e Mon Sep 17 00:00:00 2001 From: ryane Date: Fri, 23 Feb 2018 20:40:25 -0500 Subject: [PATCH] don't wrap helm init -o/--dry-run output in List Manifests in yaml are separated by `---`. In json, manifests are separated by newlines (see https://github.com/kubernetes/kubernetes/blob/master/hack/testdata/multi-resource-json.json) --- cmd/helm/init.go | 56 ++++++++++++++++++++++------------- cmd/helm/init_test.go | 51 +++++++++++++++---------------- cmd/helm/installer/install.go | 26 ++++++++-------- 3 files changed, 72 insertions(+), 61 deletions(-) diff --git a/cmd/helm/init.go b/cmd/helm/init.go index 91441ab53..ef39450d7 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -176,46 +176,62 @@ func (i *initCmd) run() error { i.opts.ServiceAccount = i.serviceAccount i.opts.MaxHistory = i.maxHistory + writeYAMLManifests := func(manifests []string) error { + w := i.out + for _, manifest := range manifests { + if _, err := fmt.Fprintln(w, "---"); err != nil { + return err + } + + if _, err := fmt.Fprintln(w, manifest); err != nil { + return err + } + } + + // YAML ending document boundary marker + _, err := fmt.Fprintln(w, "...") + return err + } if len(i.opts.Output) > 0 { - var body []byte + var manifests []string var err error - if body, err = installer.TillerManifests(&i.opts); err != nil { + if manifests, err = installer.TillerManifests(&i.opts); err != nil { return err } switch i.opts.Output.String() { case "json": - var out bytes.Buffer - jsonb, err := yaml.ToJSON(body) - if err != nil { - return err - } - buf := bytes.NewBuffer(jsonb) - if err := json.Indent(&out, buf.Bytes(), "", " "); err != nil { - return err - } - if _, err = i.out.Write(out.Bytes()); err != nil { - return err + for _, manifest := range manifests { + var out bytes.Buffer + jsonb, err := yaml.ToJSON([]byte(manifest)) + if err != nil { + return err + } + buf := bytes.NewBuffer(jsonb) + if err := json.Indent(&out, buf.Bytes(), "", " "); err != nil { + return err + } + if _, err = i.out.Write(out.Bytes()); err != nil { + return err + } + fmt.Fprint(i.out, "\n") } return nil case "yaml": - if _, err = i.out.Write(body); err != nil { - return err - } - return nil + return writeYAMLManifests(manifests) default: return fmt.Errorf("unknown output format: %q", i.opts.Output) } } if settings.Debug { - var body []byte + var manifests []string var err error // write Tiller manifests - if body, err = installer.TillerManifests(&i.opts); err != nil { + if manifests, err = installer.TillerManifests(&i.opts); err != nil { return err } - if _, err = i.out.Write(body); err != nil { + if err = writeYAMLManifests(manifests); err != nil { return err } } diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go index 269b0b09a..8ffdca57f 100644 --- a/cmd/helm/init_test.go +++ b/cmd/helm/init_test.go @@ -18,6 +18,7 @@ package main import ( "bytes" + "io" "io/ioutil" "os" "path/filepath" @@ -32,11 +33,10 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + yamlutil "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/kubernetes/fake" testcore "k8s.io/client-go/testing" - "encoding/json" - "k8s.io/helm/cmd/helm/installer" "k8s.io/helm/pkg/helm/helmpath" ) @@ -167,13 +167,15 @@ func TestInitCmd_dryRun(t *testing.T) { t.Errorf("expected no server calls, got %d", got) } - list := &metav1.List{} - if err := yaml.Unmarshal(buf.Bytes(), &list); err != nil { - t.Errorf("Expected parseable List, got %q\n\t%s", buf.String(), err) + docs := bytes.Split(buf.Bytes(), []byte("\n---")) + if got, want := len(docs), 2; got != want { + t.Fatalf("Expected document count of %d, got %d", want, got) } - - if got, want := len(list.Items), 2; got != want { - t.Fatalf("Expected resource count of %d, got %d", want, got) + for _, doc := range docs { + var y map[string]interface{} + if err := yaml.Unmarshal(doc, &y); err != nil { + t.Errorf("Expected parseable YAML, got %q\n\t%s", doc, err) + } } } @@ -304,7 +306,7 @@ func TestInitCmd_tlsOptions(t *testing.T) { } } -// TestInitCmd_output tests that init -o formats are unmarshal-able +// TestInitCmd_output tests that init -o can be decoded func TestInitCmd_output(t *testing.T) { // This is purely defensive in this case. home, err := ioutil.TempDir("", "helm_home") @@ -318,26 +320,14 @@ func TestInitCmd_output(t *testing.T) { settings.Debug = dbg }() fc := fake.NewSimpleClientset() - tests := []struct { - expectF func([]byte, interface{}) error - expectName string - }{ - { - json.Unmarshal, - "json", - }, - { - yaml.Unmarshal, - "yaml", - }, - } + tests := []string{"json", "yaml"} for _, s := range tests { var buf bytes.Buffer cmd := &initCmd{ out: &buf, home: helmpath.Home(home), kubeClient: fc, - opts: installer.Options{Output: installer.OutputFormat(s.expectName)}, + opts: installer.Options{Output: installer.OutputFormat(s)}, namespace: v1.NamespaceDefault, } if err := cmd.run(); err != nil { @@ -346,10 +336,17 @@ func TestInitCmd_output(t *testing.T) { if got := len(fc.Actions()); got != 0 { t.Errorf("expected no server calls, got %d", got) } - d := &v1beta1.Deployment{} - if err = s.expectF(buf.Bytes(), &d); err != nil { - t.Errorf("error unmarshalling init %s output %s %s", s.expectName, err, buf.String()) + + var obj interface{} + decoder := yamlutil.NewYAMLOrJSONDecoder(&buf, 4096) + for { + err := decoder.Decode(&obj) + if err != nil { + if err == io.EOF { + break + } + t.Errorf("error decoding init %s output %s %s", s, err, buf.String()) + } } } - } diff --git a/cmd/helm/installer/install.go b/cmd/helm/installer/install.go index 720a18522..35092d474 100644 --- a/cmd/helm/installer/install.go +++ b/cmd/helm/installer/install.go @@ -27,7 +27,6 @@ import ( "k8s.io/api/core/v1" "k8s.io/api/extensions/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" - meta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" @@ -146,10 +145,10 @@ func Service(namespace string) *v1.Service { } // TillerManifests gets the Deployment, Service, and Secret (if tls-enabled) manifests -func TillerManifests(opts *Options) ([]byte, error) { +func TillerManifests(opts *Options) ([]string, error) { dep, err := Deployment(opts) if err != nil { - return []byte{}, err + return []string{}, err } svc := Service(opts.Namespace) @@ -159,22 +158,21 @@ func TillerManifests(opts *Options) ([]byte, error) { if opts.EnableTLS { secret, err := Secret(opts) if err != nil { - return []byte{}, err + return []string{}, err } objs = append(objs, secret) } - list := &metav1.List{ - TypeMeta: metav1.TypeMeta{ - Kind: "List", - APIVersion: "v1", - }, - } - if err := meta.SetList(list, objs); err != nil { - return []byte{}, err + manifests := make([]string, len(objs)) + for i, obj := range objs { + o, err := yaml.Marshal(obj) + if err != nil { + return []string{}, err + } + manifests[i] = string(o) } - buf, err := yaml.Marshal(list) - return buf, err + + return manifests, err } func generateLabels(labels map[string]string) map[string]string {