...

Source file src/edge-infra.dev/pkg/k8s/runtime/patch/utils.go

Documentation: edge-infra.dev/pkg/k8s/runtime/patch

     1  package patch
     2  
     3  import (
     4  	"strings"
     5  
     6  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
     7  	"k8s.io/apimachinery/pkg/runtime"
     8  )
     9  
    10  type patchType string
    11  
    12  func (p patchType) Key() string {
    13  	return strings.Split(string(p), ".")[0]
    14  }
    15  
    16  const (
    17  	specPatch   patchType = "spec"
    18  	statusPatch patchType = "status"
    19  )
    20  
    21  var (
    22  	preserveUnstructuredKeys = map[string]bool{
    23  		"kind":       true,
    24  		"apiVersion": true,
    25  		"metadata":   true,
    26  	}
    27  )
    28  
    29  func unstructuredHasStatus(u *unstructured.Unstructured) bool {
    30  	_, ok := u.Object["status"]
    31  	return ok
    32  }
    33  
    34  // ToUnstructured converts a runtime.Object into an Unstructured object.
    35  func ToUnstructured(obj runtime.Object) (*unstructured.Unstructured, error) {
    36  	// If the incoming object is already unstructured, perform a deep copy first
    37  	// otherwise DefaultUnstructuredConverter ends up returning the inner map without
    38  	// making a copy.
    39  	if _, ok := obj.(runtime.Unstructured); ok {
    40  		obj = obj.DeepCopyObject()
    41  	}
    42  	rawMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	return &unstructured.Unstructured{Object: rawMap}, nil
    47  }
    48  
    49  // unsafeUnstructuredCopy returns a shallow copy of the unstructured object given as input.
    50  // It copies the common fields such as `kind`, `apiVersion`, `metadata` and the patchType specified.
    51  //
    52  // It's not safe to modify any of the keys in the returned unstructured object, the result should be treated as read-only.
    53  func unsafeUnstructuredCopy(obj *unstructured.Unstructured, focus patchType, isConditionsSetter bool) *unstructured.Unstructured {
    54  	// Create the return focused-unstructured object with a preallocated map.
    55  	res := &unstructured.Unstructured{Object: make(map[string]interface{}, len(obj.Object))}
    56  
    57  	// Ranges over the keys of the unstructured object, think of this as the very top level of an object
    58  	// when submitting a yaml to kubectl or a client.
    59  	// These would be keys like `apiVersion`, `kind`, `metadata`, `spec`, `status`, etc.
    60  	for key := range obj.Object {
    61  		value := obj.Object[key]
    62  
    63  		// Perform a shallow copy only for the keys we're interested in, or the ones that should be always preserved.
    64  		if key == focus.Key() || preserveUnstructuredKeys[key] {
    65  			res.Object[key] = value
    66  		}
    67  
    68  		// If we've determined that we're able to interface with conditions.Setter interface,
    69  		// when dealing with the status patch, remove the status.conditions sub-field from the object.
    70  		if isConditionsSetter && focus == statusPatch {
    71  			// NOTE: Removing status.conditions changes the incoming object! This is safe because the condition patch
    72  			// doesn't use the unstructured fields, and it runs before any other patch.
    73  			//
    74  			// If we want to be 100% safe, we could make a copy of the incoming object before modifying it, although
    75  			// copies have a high cpu and high memory usage, therefore we intentionally choose to avoid extra copies
    76  			// given that the ordering of operations and safety is handled internally by the patch helper.
    77  			unstructured.RemoveNestedField(res.Object, "status", "conditions")
    78  		}
    79  	}
    80  
    81  	return res
    82  }
    83  

View as plain text