...

Source file src/sigs.k8s.io/cli-utils/pkg/testutil/object.go

Documentation: sigs.k8s.io/cli-utils/pkg/testutil

     1  // Copyright 2020 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  //
     4  // The testutil package houses utility function for testing.
     5  
     6  package testutil
     7  
     8  import (
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    13  	"k8s.io/apimachinery/pkg/runtime"
    14  	"k8s.io/kubectl/pkg/scheme"
    15  	"sigs.k8s.io/cli-utils/pkg/object"
    16  	"sigs.k8s.io/cli-utils/pkg/object/dependson"
    17  )
    18  
    19  // OwningInventoryKey is the annotation key indicating the inventory owning an object.
    20  // This is a copy of inventory.OwningInventoryKey to avoid dependency cycle.
    21  const OwningInventoryKey = "config.k8s.io/owning-inventory"
    22  
    23  var codec = scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
    24  
    25  // Unstructured translates the passed object config string into an
    26  // object in Unstructured format. The mutators modify the config
    27  // yaml before returning the object.
    28  func Unstructured(t *testing.T, manifest string, mutators ...Mutator) *unstructured.Unstructured {
    29  	u := &unstructured.Unstructured{}
    30  	err := runtime.DecodeInto(codec, []byte(manifest), u)
    31  	if !assert.NoError(t, err) {
    32  		t.FailNow()
    33  	}
    34  	return Mutate(u, mutators...)
    35  }
    36  
    37  // Mutate executes the specified Mutators on the specified object.
    38  func Mutate(u *unstructured.Unstructured, mutators ...Mutator) *unstructured.Unstructured {
    39  	for _, m := range mutators {
    40  		m.Mutate(u)
    41  	}
    42  	return u
    43  }
    44  
    45  // Mutator inteface defines a function to update an object
    46  // while translating it unto Unstructured format from yaml config.
    47  type Mutator interface {
    48  	Mutate(u *unstructured.Unstructured)
    49  }
    50  
    51  // ToIdentifier translates object yaml config into ObjMetadata.
    52  func ToIdentifier(t *testing.T, manifest string) object.ObjMetadata {
    53  	obj := Unstructured(t, manifest)
    54  	return object.ObjMetadata{
    55  		GroupKind: obj.GetObjectKind().GroupVersionKind().GroupKind(),
    56  		Name:      obj.GetName(),
    57  		Namespace: obj.GetNamespace(), // If cluster-scoped, empty namespace string
    58  	}
    59  }
    60  
    61  // AddOwningInv returns a Mutator which adds the passed inv string
    62  // as the owning inventory annotation.
    63  func AddOwningInv(t *testing.T, inv string) Mutator {
    64  	return addOwningInvMutator{
    65  		t:   t,
    66  		inv: inv,
    67  	}
    68  }
    69  
    70  // owningInvMutator encapsulates the fields necessary to modify
    71  // an object by adding the owning inventory annotation. This
    72  // structure implements the Mutator interface.
    73  type addOwningInvMutator struct {
    74  	t   *testing.T
    75  	inv string
    76  }
    77  
    78  // Mutate updates the passed object by adding the owning
    79  // inventory annotation. Needed to implement the Mutator interface.
    80  func (a addOwningInvMutator) Mutate(u *unstructured.Unstructured) {
    81  	annos, found, err := unstructured.NestedStringMap(u.Object, "metadata", "annotations")
    82  	if !assert.NoError(a.t, err) {
    83  		a.t.FailNow()
    84  	}
    85  	if !found {
    86  		annos = make(map[string]string)
    87  	}
    88  	annos[OwningInventoryKey] = a.inv
    89  	err = unstructured.SetNestedStringMap(u.Object, annos, "metadata", "annotations")
    90  	if !assert.NoError(a.t, err) {
    91  		a.t.FailNow()
    92  	}
    93  }
    94  
    95  // DeleteOwningInv returns a Mutator which deletes the passed inv string
    96  // from the owning inventory annotation.
    97  func DeleteOwningInv(t *testing.T, inv string) Mutator {
    98  	return deleteOwningInvMutator{
    99  		t:   t,
   100  		inv: inv,
   101  	}
   102  }
   103  
   104  // deleteOwningInvMutator encapsulates the fields necessary to modify
   105  // an object by deleting the owning inventory annotation. This
   106  // structure implements the Mutator interface.
   107  type deleteOwningInvMutator struct {
   108  	t   *testing.T
   109  	inv string
   110  }
   111  
   112  // Mutate updates the passed object by deleting the owning
   113  // inventory annotation. Needed to implement the Mutator interface.
   114  func (a deleteOwningInvMutator) Mutate(u *unstructured.Unstructured) {
   115  	annos, found, err := unstructured.NestedStringMap(u.Object, "metadata", "annotations")
   116  	if !assert.NoError(a.t, err) {
   117  		a.t.FailNow()
   118  	}
   119  	if !found {
   120  		annos = make(map[string]string)
   121  	}
   122  	if !assert.Equal(a.t, a.inv, annos[OwningInventoryKey]) {
   123  		a.t.FailNow()
   124  	}
   125  	delete(annos, OwningInventoryKey)
   126  	if len(annos) > 0 {
   127  		err = unstructured.SetNestedStringMap(u.Object, annos, "metadata", "annotations")
   128  		if !assert.NoError(a.t, err) {
   129  			a.t.FailNow()
   130  		}
   131  	} else {
   132  		unstructured.RemoveNestedField(u.Object, "metadata", "annotations")
   133  	}
   134  }
   135  
   136  // AddDependsOn returns a testutil.Mutator which adds the passed objects as a
   137  // depends-on annotation to the object which is mutated. Multiple objects
   138  // passed in means multiple depends on objects in the annotation separated
   139  // by a comma.
   140  func AddDependsOn(t *testing.T, deps ...object.ObjMetadata) Mutator {
   141  	return dependsOnMutator{
   142  		t:    t,
   143  		deps: dependson.DependencySet(deps),
   144  	}
   145  }
   146  
   147  // dependsOnMutator encapsulates fields for adding depends-on annotation
   148  // to a test object. Implements the Mutator interface.
   149  type dependsOnMutator struct {
   150  	t    *testing.T
   151  	deps dependson.DependencySet
   152  }
   153  
   154  // Mutate writes a depends-on annotation on the supplied object. The value of
   155  // the annotation is a set of dependencies referencing the dependsOnMutator's
   156  // depObjs.
   157  func (d dependsOnMutator) Mutate(u *unstructured.Unstructured) {
   158  	err := dependson.WriteAnnotation(u, d.deps)
   159  	if !assert.NoError(d.t, err) {
   160  		d.t.FailNow()
   161  	}
   162  }
   163  

View as plain text