Skip to content

Commit

Permalink
fix decode multi yaml doc with comments
Browse files Browse the repository at this point in the history
  • Loading branch information
xuzhenglun committed Jun 24, 2022
1 parent fe5be94 commit ff50a23
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 43 deletions.
63 changes: 20 additions & 43 deletions pkg/patterns/declarative/pkg/manifest/objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ limitations under the License.
package manifest

import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"sort"
"strings"

Expand Down Expand Up @@ -336,60 +338,35 @@ func (o *Objects) Sort(score func(o *Object) int) {
func ParseObjects(ctx context.Context, manifest string) (*Objects, error) {
log := log.Log

var b bytes.Buffer
objects := &Objects{}
reader := k8syaml.NewYAMLReader(bufio.NewReader(strings.NewReader(manifest)))
for {
raw, err := reader.Read()
if err != nil {
if err == io.EOF {
return objects, nil
}

var yamls []string
for _, line := range strings.Split(manifest, "\n") {
if line == "---" {
// yaml separator
yamls = append(yamls, b.String())
b.Reset()
} else {
b.WriteString(line)
b.WriteString("\n")
return nil, fmt.Errorf("invalid YAML doc, %w", err)
}
}
yamls = append(yamls, b.String())

objects := &Objects{}

for _, yaml := range yamls {
// We need this so we don't error on a file that is commented out
// TODO: How does apimachinery avoid this problem?
hasContent := false
for _, line := range strings.Split(yaml, "\n") {
l := strings.TrimSpace(line)
if l != "" && !strings.HasPrefix(l, "#") {
hasContent = true
break
}
raw = bytes.TrimSpace(raw)
out := unstructured.Unstructured{}
if err := k8syaml.Unmarshal(raw, &out); err != nil {
log.WithValues("error", err).WithValues("yaml", raw).V(2).Info("Unable to parse into Unstructured, storing as blob")
objects.Blobs = append(objects.Blobs, append(bytes.TrimPrefix(raw, []byte("---\n")), '\n'))
}

if !hasContent {
if len(raw) == 0 || bytes.Equal(raw, []byte("null")) || len(out.Object) == 0 {
continue
}

r := bytes.NewReader([]byte(yaml))
decoder := k8syaml.NewYAMLOrJSONDecoder(r, 1024)

out := &unstructured.Unstructured{}
err := decoder.Decode(out)
o, err := NewObject(&out)
if err != nil {
log.WithValues("error", err).WithValues("yaml", yaml).V(2).Info("Unable to parse into Unstructured, storing as blob")
objects.Blobs = append(objects.Blobs, []byte(yaml))
} else {
// We don't reuse the manifest because it's probably yaml, and we want to use json
// json = yaml
o, err := NewObject(out)
if err != nil {
return nil, err
}
objects.Items = append(objects.Items, o)
return nil, err
}

objects.Items = append(objects.Items, o)
}

return objects, nil
}

func newObject(u *unstructured.Unstructured, json []byte) (*Object, error) {
Expand Down
44 changes: 44 additions & 0 deletions pkg/patterns/declarative/pkg/manifest/objects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,50 @@ configMapGenerator:
`,
},
},
{
name: "multi doc with comment",
inputManifest: `--- # first one
apiVersion: v1
kind: ServiceAccount
metadata:
name: foo-operator
namespace: kube-system
--- # empty doc
# comments only
--- # second one
apiVersion: v1
kind: ServiceAccount
metadata:
name: foo-operator
namespace: kube-system`,
expectedObject: []*Object{
{
object: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": map[string]interface{}{
"name": "foo-operator",
"namespace": "kube-system",
},
},
},
},
{
object: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": map[string]interface{}{
"name": "foo-operator",
"namespace": "kube-system",
},
},
},
},
},
expectedBlobs: []string{},
},
}

for _, tt := range tests {
Expand Down

0 comments on commit ff50a23

Please sign in to comment.