mirror of https://github.com/k3s-io/k3s
226 lines
4.7 KiB
Go
226 lines
4.7 KiB
Go
|
package yaml
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/ghodss/yaml"
|
||
|
"github.com/pkg/errors"
|
||
|
"github.com/rancher/wrangler/pkg/data/convert"
|
||
|
"github.com/rancher/wrangler/pkg/gvk"
|
||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||
|
"k8s.io/apimachinery/pkg/runtime"
|
||
|
yamlDecoder "k8s.io/apimachinery/pkg/util/yaml"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
cleanPrefix = []string{
|
||
|
"kubectl.kubernetes.io/",
|
||
|
}
|
||
|
cleanContains = []string{
|
||
|
"cattle.io/",
|
||
|
}
|
||
|
)
|
||
|
|
||
|
func Unmarshal(data []byte, v interface{}) error {
|
||
|
return yamlDecoder.NewYAMLToJSONDecoder(bytes.NewBuffer(data)).Decode(v)
|
||
|
}
|
||
|
|
||
|
func ToObjects(in io.Reader) ([]runtime.Object, error) {
|
||
|
var result []runtime.Object
|
||
|
reader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(in, 4096))
|
||
|
for {
|
||
|
raw, err := reader.Read()
|
||
|
if err == io.EOF {
|
||
|
break
|
||
|
}
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
obj, err := toObjects(raw)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
result = append(result, obj...)
|
||
|
}
|
||
|
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
func toObjects(bytes []byte) ([]runtime.Object, error) {
|
||
|
bytes, err := yamlDecoder.ToJSON(bytes)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
check := map[string]interface{}{}
|
||
|
if err := json.Unmarshal(bytes, &check); err != nil || len(check) == 0 {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
obj, _, err := unstructured.UnstructuredJSONScheme.Decode(bytes, nil, nil)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if l, ok := obj.(*unstructured.UnstructuredList); ok {
|
||
|
var result []runtime.Object
|
||
|
for _, obj := range l.Items {
|
||
|
copy := obj
|
||
|
result = append(result, ©)
|
||
|
}
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
return []runtime.Object{obj}, nil
|
||
|
}
|
||
|
|
||
|
// Export will attempt to clean up the objects a bit before
|
||
|
// rendering to yaml so that they can easily be imported into another
|
||
|
// cluster
|
||
|
func Export(objects ...runtime.Object) ([]byte, error) {
|
||
|
if len(objects) == 0 {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
buffer := &bytes.Buffer{}
|
||
|
for i, obj := range objects {
|
||
|
if i > 0 {
|
||
|
buffer.WriteString("\n---\n")
|
||
|
}
|
||
|
|
||
|
obj, err := CleanObjectForExport(obj)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
bytes, err := yaml.Marshal(obj)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrapf(err, "failed to encode %s", obj.GetObjectKind().GroupVersionKind())
|
||
|
}
|
||
|
buffer.Write(bytes)
|
||
|
}
|
||
|
|
||
|
return buffer.Bytes(), nil
|
||
|
}
|
||
|
|
||
|
func CleanObjectForExport(obj runtime.Object) (runtime.Object, error) {
|
||
|
obj = obj.DeepCopyObject()
|
||
|
if obj.GetObjectKind().GroupVersionKind().Kind == "" {
|
||
|
if gvk, err := gvk.Get(obj); err == nil {
|
||
|
obj.GetObjectKind().SetGroupVersionKind(gvk)
|
||
|
} else if err != nil {
|
||
|
return nil, errors.Wrapf(err, "kind and/or apiVersion is not set on input object: %v", obj)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
data, err := convert.EncodeToMap(obj)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
unstr := &unstructured.Unstructured{
|
||
|
Object: data,
|
||
|
}
|
||
|
|
||
|
metadata := map[string]interface{}{}
|
||
|
|
||
|
if name := unstr.GetName(); len(name) > 0 {
|
||
|
metadata["name"] = name
|
||
|
} else if generated := unstr.GetGenerateName(); len(generated) > 0 {
|
||
|
metadata["generateName"] = generated
|
||
|
} else {
|
||
|
return nil, fmt.Errorf("either name or generateName must be set on obj: %v", obj)
|
||
|
}
|
||
|
|
||
|
if unstr.GetNamespace() != "" {
|
||
|
metadata["namespace"] = unstr.GetNamespace()
|
||
|
}
|
||
|
if annotations := unstr.GetAnnotations(); len(annotations) > 0 {
|
||
|
cleanMap(annotations)
|
||
|
if len(annotations) > 0 {
|
||
|
metadata["annotations"] = annotations
|
||
|
} else {
|
||
|
delete(metadata, "annotations")
|
||
|
}
|
||
|
}
|
||
|
if labels := unstr.GetLabels(); len(labels) > 0 {
|
||
|
cleanMap(labels)
|
||
|
if len(labels) > 0 {
|
||
|
metadata["labels"] = labels
|
||
|
} else {
|
||
|
delete(metadata, "labels")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if spec, ok := data["spec"]; ok {
|
||
|
if spec == nil {
|
||
|
delete(data, "spec")
|
||
|
} else if m, ok := spec.(map[string]interface{}); ok && len(m) == 0 {
|
||
|
delete(data, "spec")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
data["metadata"] = metadata
|
||
|
delete(data, "status")
|
||
|
|
||
|
return unstr, nil
|
||
|
}
|
||
|
|
||
|
func CleanAnnotationsForExport(annotations map[string]string) map[string]string {
|
||
|
result := make(map[string]string, len(annotations))
|
||
|
|
||
|
outer:
|
||
|
for k := range annotations {
|
||
|
for _, prefix := range cleanPrefix {
|
||
|
if strings.HasPrefix(k, prefix) {
|
||
|
continue outer
|
||
|
}
|
||
|
}
|
||
|
for _, contains := range cleanContains {
|
||
|
if strings.Contains(k, contains) {
|
||
|
continue outer
|
||
|
}
|
||
|
}
|
||
|
result[k] = annotations[k]
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
func cleanMap(annoLabels map[string]string) {
|
||
|
for k := range annoLabels {
|
||
|
for _, prefix := range cleanPrefix {
|
||
|
if strings.HasPrefix(k, prefix) {
|
||
|
delete(annoLabels, k)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func ToBytes(objects []runtime.Object) ([]byte, error) {
|
||
|
if len(objects) == 0 {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
buffer := &bytes.Buffer{}
|
||
|
for i, obj := range objects {
|
||
|
if i > 0 {
|
||
|
buffer.WriteString("\n---\n")
|
||
|
}
|
||
|
|
||
|
bytes, err := yaml.Marshal(obj)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrapf(err, "failed to encode %s", obj.GetObjectKind().GroupVersionKind())
|
||
|
}
|
||
|
buffer.Write(bytes)
|
||
|
}
|
||
|
|
||
|
return buffer.Bytes(), nil
|
||
|
}
|