...

Source file src/edge-infra.dev/pkg/k8s/unstructured/converter.go

Documentation: edge-infra.dev/pkg/k8s/unstructured

     1  package unstructured
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"strings"
     7  
     8  	"github.com/pkg/errors"
     9  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    10  	"k8s.io/apimachinery/pkg/runtime"
    11  	"k8s.io/apimachinery/pkg/runtime/schema"
    12  
    13  	"sigs.k8s.io/controller-runtime/pkg/client"
    14  )
    15  
    16  const YAMLFileDelimiter = "\n---\n"
    17  
    18  // Aliases for upstream unstructured package types.
    19  type Unstructured = unstructured.Unstructured
    20  
    21  var (
    22  	// ErrUnstructuredFieldNotFound error returned when field not found in unstructured obj.
    23  	ErrUnstructuredFieldNotFound = errors.New("field not found")
    24  )
    25  
    26  // New creates an empty unstructured object with the provided type meta
    27  func New(gv schema.GroupVersion, kind, namespace, name string) *unstructured.Unstructured {
    28  	u := &unstructured.Unstructured{}
    29  	u.SetNamespace(namespace)
    30  	u.SetName(name)
    31  	u.SetGroupVersionKind(schema.GroupVersionKind{
    32  		Group:   gv.Group,
    33  		Kind:    kind,
    34  		Version: gv.Version,
    35  	})
    36  	return u
    37  }
    38  
    39  // FromUnstructured converts from Unstructured to a concrete K8s type
    40  func FromUnstructured(u *unstructured.Unstructured, obj interface{}) (err error) {
    41  	err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, obj)
    42  	return err
    43  }
    44  
    45  // ToUnstructured converts a generic K8s object into Unstructured. Typically
    46  // useful for working with libraries that accept []*unstructured.Unstructured
    47  func ToUnstructured(obj client.Object) (*unstructured.Unstructured, error) {
    48  	return FromRuntime(obj)
    49  }
    50  
    51  func FromRuntime(obj runtime.Object) (*unstructured.Unstructured, error) {
    52  	// If the incoming object is already unstructured, perform a deep copy first
    53  	// otherwise DefaultUnstructuredConverter ends up returning the inner map without
    54  	// making a copy.
    55  	if _, ok := obj.(runtime.Unstructured); ok {
    56  		obj = obj.DeepCopyObject()
    57  	}
    58  	rawMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	return &unstructured.Unstructured{Object: rawMap}, nil
    63  }
    64  
    65  // ToUnstructuredArray converts one or more K8s objects into an array of Unstructured
    66  // objects. Useful for working with libraries that accept []*unstructured.Unstructured
    67  func ToUnstructuredArray(objs ...client.Object) ([]*unstructured.Unstructured, error) {
    68  	var uu []*unstructured.Unstructured
    69  	for _, o := range objs {
    70  		u, err := ToUnstructured(o)
    71  		if err != nil {
    72  			return nil, err
    73  		}
    74  		uu = append(uu, u)
    75  	}
    76  
    77  	return uu, nil
    78  }
    79  
    80  // ToClientObjArray converts one ore more unstructured objects into an array
    81  // of client.Object by casting.
    82  func ToClientObjArray(objs ...*unstructured.Unstructured) []client.Object {
    83  	clobjs := make([]client.Object, len(objs))
    84  	for i, obj := range objs {
    85  		clobjs[i] = obj
    86  	}
    87  	return clobjs
    88  }
    89  
    90  // ToUnstructuredList converts a K8s object to Unstructured and then adds it to
    91  // an UnstructuredList before returning it
    92  func ToUnstructuredList(obj client.Object) (*unstructured.UnstructuredList, error) {
    93  	un, err := ToUnstructured(obj)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	unl := &unstructured.UnstructuredList{Items: []unstructured.Unstructured{*un}}
    98  	unl.SetAPIVersion("v1")
    99  	unl.SetKind("List")
   100  	return unl, nil
   101  }
   102  
   103  // AddToUnstructuredListBytes takes in an UnstructuredList represented as []bytes
   104  // and appends the provided client.Object to it.  The result is seralized back
   105  // into []byte and returned.
   106  func AddToUnstructuredListBytes(resources []byte, obj client.Object) ([]byte, error) {
   107  	if len(resources) == 0 {
   108  		unl, err := ToUnstructuredList(obj)
   109  		if err != nil {
   110  			return nil, err
   111  		}
   112  		return unl.MarshalJSON()
   113  	}
   114  	unl := &unstructured.UnstructuredList{}
   115  	if err := unl.UnmarshalJSON(resources); err != nil {
   116  		return nil, err
   117  	}
   118  	un, err := ToUnstructured(obj)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	unl.Items = append(unl.Items, *un)
   123  	return unl.MarshalJSON()
   124  }
   125  
   126  // StringsToUnstructuredList converts an array of strings into an UnstructuredList
   127  func StringsToUnstructuredList(objs []string) (*unstructured.UnstructuredList, error) {
   128  	resp := &unstructured.UnstructuredList{}
   129  	for _, obj := range objs {
   130  		resource := &unstructured.Unstructured{}
   131  		if err := resource.UnmarshalJSON([]byte(obj)); err != nil {
   132  			return resp, err
   133  		}
   134  		resp.Items = append(resp.Items, *resource)
   135  	}
   136  	return resp, nil
   137  }
   138  
   139  // UnstructuredListToStrings converts an UnstructuredList into an array of strings
   140  // where each element is an item from the UnstructuredList
   141  //
   142  //nolint:revive // UnstructuredListToStrings is more clear than ListToStrings
   143  func UnstructuredListToStrings(unl *unstructured.UnstructuredList) ([]string, error) {
   144  	if unl == nil {
   145  		return []string{}, nil
   146  	}
   147  	var result []string
   148  	for _, item := range unl.Items {
   149  		data, err := item.MarshalJSON()
   150  		if err != nil {
   151  			return result, err
   152  		}
   153  		result = append(result, string(data))
   154  	}
   155  	return result, nil
   156  }
   157  
   158  // UnmarshalField is a wrapper around JSON and Unstructured objects to decode and copy a specific field
   159  // value into an object.
   160  func UnmarshalField(u *unstructured.Unstructured, v interface{}, fields ...string) error {
   161  	value, found, err := unstructured.NestedFieldNoCopy(u.Object, fields...)
   162  	if err != nil {
   163  		return errors.Wrapf(err, "failed to retrieve field %q from %q", strings.Join(fields, "."), u.GroupVersionKind())
   164  	}
   165  	if !found || value == nil {
   166  		return ErrUnstructuredFieldNotFound
   167  	}
   168  	valueBytes, err := json.Marshal(value)
   169  	if err != nil {
   170  		return errors.Wrapf(err, "failed to json-encode field %q value from %q", strings.Join(fields, "."), u.GroupVersionKind())
   171  	}
   172  	if err := json.Unmarshal(valueBytes, v); err != nil {
   173  		return errors.Wrapf(err, "failed to json-decode field %q value from %q", strings.Join(fields, "."), u.GroupVersionKind())
   174  	}
   175  	return nil
   176  }
   177  
   178  // ToJSON converts unstructured objects to multi doc json bytes
   179  func ToJSON(uobjs []*unstructured.Unstructured) ([]byte, error) {
   180  	jsonDoc := make([]byte, 0)
   181  	for _, doc := range uobjs {
   182  		jsonData, err := doc.MarshalJSON()
   183  		if err != nil {
   184  			return nil, err
   185  		}
   186  		jsonDoc = append(jsonDoc, jsonData...)
   187  	}
   188  	return jsonDoc, nil
   189  }
   190  
   191  // FromJSON converts a json multi doc bytes to unstructured object list
   192  func FromJSON(jsonData []byte) ([]*unstructured.Unstructured, error) {
   193  	manifests := []*unstructured.Unstructured{}
   194  	decoder := json.NewDecoder(bytes.NewBuffer(jsonData))
   195  	for {
   196  		var rawObj runtime.RawExtension
   197  		if err := decoder.Decode(&rawObj); err != nil {
   198  			break
   199  		}
   200  		obj := &unstructured.Unstructured{}
   201  		if rawObj.Size() == 0 {
   202  			continue
   203  		}
   204  		if err := obj.UnmarshalJSON(rawObj.Raw); err != nil {
   205  			return nil, err
   206  		}
   207  		manifests = append(manifests, obj)
   208  	}
   209  	return manifests, nil
   210  }
   211  

View as plain text