diff --git a/kyaml/kio/byteio_writer.go b/kyaml/kio/byteio_writer.go index dc58e8238..c22b2b262 100644 --- a/kyaml/kio/byteio_writer.go +++ b/kyaml/kio/byteio_writer.go @@ -12,7 +12,7 @@ import ( "sigs.k8s.io/kustomize/kyaml/yaml" ) -// Writer writes ResourceNodes to bytes. +// ByteWriter writes ResourceNodes to bytes. type ByteWriter struct { // Writer is where ResourceNodes are encoded. Writer io.Writer @@ -55,24 +55,24 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error { } } - encoder := yaml.NewEncoder(w.Writer) - // keep - hasJSONPathExt := make([]bool, len(nodes)) - defer encoder.Close() - for i := range nodes { - // Check if the output file is a JSON file so we can force JSON encoding in that case. - // YAML flow style encoding may not be compatible because of newlines introduced by the - // YAML marshaller in long double quoted string values. These newlines are insignificant - // when interpreted as YAML but invalid when interpreted as JSON. - if path, _, _ := kioutil.GetFileAnnotations(nodes[i]); path != "" { + // Check if the output is a single JSON file so we can force JSON encoding in that case. + // YAML flow style encoding may not be compatible because of unquoted strings and newlines + // introduced by the YAML marshaller in long string values. These newlines are insignificant + // when interpreted as YAML but invalid when interpreted as JSON. + jsonEncodeSingleNode := false + if w.WrappingKind == "" && len(nodes) == 1 { + if path, _, _ := kioutil.GetFileAnnotations(nodes[0]); path != "" { filename := filepath.Base(path) for _, glob := range JSONMatch { if match, _ := filepath.Match(glob, filename); match { - hasJSONPathExt[i] = true + jsonEncodeSingleNode = true break } } } + } + + for i := range nodes { // clean resources by removing annotations set by the Reader if !w.KeepReaderAnnotations { _, err := nodes[i].Pipe(yaml.ClearAnnotation(kioutil.IndexAnnotation)) @@ -96,10 +96,18 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error { } } + if jsonEncodeSingleNode { + encoder := json.NewEncoder(w.Writer) + encoder.SetIndent("", " ") + return errors.Wrap(encoder.Encode(nodes[0])) + } + + encoder := yaml.NewEncoder(w.Writer) + defer encoder.Close() // don't wrap the elements if w.WrappingKind == "" { for i := range nodes { - if err := w.encode(encoder, hasJSONPathExt[i], nodes[i].Document()); err != nil { + if err := encoder.Encode(nodes[i].Document()); err != nil { return err } } @@ -133,27 +141,7 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error { for i := range nodes { items.Content = append(items.Content, nodes[i].YNode()) } - err := w.encode(encoder, false, doc) + err := encoder.Encode(doc) yaml.UndoSerializationHacksOnNodes(nodes) return err } - -// encode encodes the input document node to appropriate node format -func (w ByteWriter) encode(encoder *yaml.Encoder, forceJSON bool, doc *yaml.Node) error { - rNode := &yaml.RNode{} - rNode.SetYNode(doc) - useJSONEncoder := forceJSON - if !forceJSON { - str, err := rNode.String() - if err != nil { - return errors.Wrap(err) - } - useJSONEncoder = json.Valid([]byte(str)) - } - if useJSONEncoder { - je := json.NewEncoder(w.Writer) - je.SetIndent("", " ") - return errors.Wrap(je.Encode(rNode)) - } - return encoder.Encode(doc) -} diff --git a/kyaml/kio/byteio_writer_test.go b/kyaml/kio/byteio_writer_test.go index 35169a1f4..a3f0b4876 100644 --- a/kyaml/kio/byteio_writer_test.go +++ b/kyaml/kio/byteio_writer_test.go @@ -198,9 +198,9 @@ metadata: items: []string{ `{ "a": "a long string that would certainly see a newline introduced by the YAML marshaller abcd123", - "metadata": { - "annotations": { - "config.kubernetes.io/path": "test.json" + metadata: { + annotations: { + config.kubernetes.io/path: test.json } } }`, @@ -215,6 +215,65 @@ metadata: } }`, }, + + // + // Test Case + // + { + name: "encode_wrapped_json_as_yaml", + instance: ByteWriter{ + Sort: true, + WrappingKind: ResourceListKind, + WrappingAPIVersion: ResourceListAPIVersion, + }, + items: []string{ + `{ + "a": "b", + "metadata": { + "annotations": { + "config.kubernetes.io/path": "test.json" + } + } +}`, + }, + + expectedOutput: `apiVersion: config.kubernetes.io/v1alpha1 +kind: ResourceList +items: +- {"a": "b", "metadata": {"annotations": {"config.kubernetes.io/path": "test.json"}}} +`, + }, + + // + // Test Case + // + { + name: "encode_multi_doc_json_as_yaml", + items: []string{ + `{ + "a": "b", + "metadata": { + "annotations": { + "config.kubernetes.io/path": "test-1.json" + } + } +}`, + `{ + "c": "d", + "metadata": { + "annotations": { + "config.kubernetes.io/path": "test-2.json" + } + } +}`, + }, + + expectedOutput: ` +{"a": "b", "metadata": {"annotations": {"config.kubernetes.io/path": "test-1.json"}}} +--- +{"c": "d", "metadata": {"annotations": {"config.kubernetes.io/path": "test-2.json"}}} +`, + }, } for i := range testCases {