Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
util/yaml: Added package which helps working with yamls
It allows to get/set some basic info like kind or name to k8s objects stored as yamls
- Loading branch information
Showing
2 changed files
with
192 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)) | ||
} | ||
} | ||
}) | ||
} | ||
} |