Skip to content

Commit

Permalink
util/yaml: Added package which helps working with yamls
Browse files Browse the repository at this point in the history
It allows to get/set some basic info like kind or name to k8s objects
stored as yamls
  • Loading branch information
zimnx committed Nov 16, 2020
1 parent 5908083 commit ac30775
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 0 deletions.
53 changes: 53 additions & 0 deletions pkg/util/yaml/yaml.go
@@ -0,0 +1,53 @@
// Copyright (C) 2017 ScyllaDB

package yaml

import (
"bufio"
"io"

"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
apiyaml "k8s.io/apimachinery/pkg/util/yaml"
"sigs.k8s.io/yaml"
)

// ToUnstructured takes a YAML and converts it to a list of Unstructured objects
func ToUnstructured(rawyaml io.Reader) ([]unstructured.Unstructured, error) {
var objs []unstructured.Unstructured

reader := apiyaml.NewYAMLReader(bufio.NewReader(rawyaml))
count := 1
for {
// Read one YAML document at a time, until io.EOF is returned
b, err := reader.Read()
if err != nil {
if err == io.EOF {
break
}
return nil, errors.Wrapf(err, "failed to read yaml")
}
if len(b) == 0 {
break
}

var m map[string]interface{}
if err := yaml.Unmarshal(b, &m); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal the %d yaml document: %q", count, string(b))
}

var u unstructured.Unstructured
u.SetUnstructuredContent(m)

// Ignore empty objects.
// Empty objects are generated if there are weird things in manifest files like e.g. two --- in a row without a yaml doc in the middle
if u.Object == nil {
continue
}

objs = append(objs, u)
count++
}

return objs, nil
}
139 changes: 139 additions & 0 deletions pkg/util/yaml/yaml_test.go
@@ -0,0 +1,139 @@
// Copyright (C) 2017 ScyllaDB

package yaml

import (
"bytes"
"strings"
"testing"
)

func TestToUnstructured(t *testing.T) {
type args struct {
rawyaml []byte
}
tests := []struct {
name string
args args
wantObjsCount int
err string
}{
{
name: "single object",
args: args{
rawyaml: []byte("apiVersion: v1\n" +
"kind: ConfigMap\n"),
},
wantObjsCount: 1,
},
{
name: "multiple objects are detected",
args: args{
rawyaml: []byte("apiVersion: v1\n" +
"kind: ConfigMap\n" +
"---\n" +
"apiVersion: v1\n" +
"kind: Secret\n"),
},
wantObjsCount: 2,
},
{
name: "empty object are dropped",
args: args{
rawyaml: []byte("---\n" + //empty objects before
"---\n" +
"---\n" +
"apiVersion: v1\n" +
"kind: ConfigMap\n" +
"---\n" + // empty objects in the middle
"---\n" +
"---\n" +
"apiVersion: v1\n" +
"kind: Secret\n" +
"---\n" + //empty objects after
"---\n" +
"---\n"),
},
wantObjsCount: 2,
},
{
name: "--- in the middle of objects are ignored",
args: args{
[]byte("apiVersion: v1\n" +
"kind: ConfigMap\n" +
"data: \n" +
" key: |\n" +
" ··Several lines of text,\n" +
" ··with some --- \n" +
" ---\n" +
" ··in the middle\n" +
"---\n" +
"apiVersion: v1\n" +
"kind: Secret\n"),
},
wantObjsCount: 2,
},
{
name: "returns error for invalid yaml",
args: args{
rawyaml: []byte("apiVersion: v1\n" +
"kind: ConfigMap\n" +
"---\n" +
"apiVersion: v1\n" +
"foobar\n" +
"kind: Secret\n"),
},
err: "failed to unmarshal the 2 yaml document",
},
{
name: "returns error for invalid yaml",
args: args{
rawyaml: []byte("apiVersion: v1\n" +
"kind: ConfigMap\n" +
"---\n" +
"apiVersion: v1\n" +
"kind: Pod\n" +
"---\n" +
"apiVersion: v1\n" +
"kind: Deployment\n" +
"---\n" +
"apiVersion: v1\n" +
"foobar\n" +
"kind: ConfigMap\n"),
},
err: "failed to unmarshal the 4 yaml document",
},
{
name: "returns error for invalid yaml",
args: args{
rawyaml: []byte("apiVersion: v1\n" +
"foobar\n" +
"kind: ConfigMap\n" +
"---\n" +
"apiVersion: v1\n" +
"kind: Secret\n"),
},
err: "failed to unmarshal the 1 yaml document",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ToUnstructured(bytes.NewReader(tt.args.rawyaml))
if tt.err != "" {
if err == nil {
t.Fatal("expected error, got nil")
}
if !strings.Contains(err.Error(), tt.err) {
t.Errorf("expected err %q got %q", tt.err, err)
}
} else {
if err != nil {
t.Errorf("expected nil error, got %s", err)
}
if len(got) != tt.wantObjsCount {
t.Errorf("expected %d object, got %d", tt.wantObjsCount, len(got))
}
}
})
}
}

0 comments on commit ac30775

Please sign in to comment.