...

Source file src/sigs.k8s.io/cli-utils/test/e2e/customprovider/provider.go

Documentation: sigs.k8s.io/cli-utils/test/e2e/customprovider

     1  // Copyright 2020 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package customprovider
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strings"
    10  
    11  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    12  	"k8s.io/apimachinery/pkg/api/meta"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    15  	"k8s.io/apimachinery/pkg/runtime/schema"
    16  	"k8s.io/client-go/dynamic"
    17  	"k8s.io/kubectl/pkg/cmd/util"
    18  	"sigs.k8s.io/cli-utils/pkg/apis/actuation"
    19  	"sigs.k8s.io/cli-utils/pkg/common"
    20  	"sigs.k8s.io/cli-utils/pkg/inventory"
    21  	"sigs.k8s.io/cli-utils/pkg/object"
    22  )
    23  
    24  var InventoryCRD = []byte(strings.TrimSpace(`
    25  apiVersion: apiextensions.k8s.io/v1
    26  kind: CustomResourceDefinition
    27  metadata:
    28    name: inventories.cli-utils.example.io
    29  spec:
    30    conversion:
    31      strategy: None
    32    group: cli-utils.example.io
    33    names:
    34      kind: Inventory
    35      listKind: InventoryList
    36      plural: inventories
    37      singular: inventory
    38    scope: Namespaced
    39    versions:
    40    - name: v1alpha1
    41      schema:
    42        openAPIV3Schema:
    43          description: Example for cli-utils e2e tests
    44          properties:
    45            spec:
    46              properties:
    47                objects:
    48                  items:
    49                    properties:
    50                      group:
    51                        type: string
    52                      kind:
    53                        type: string
    54                      name:
    55                        type: string
    56                      namespace:
    57                        type: string
    58                    required:
    59                    - group
    60                    - kind
    61                    - name
    62                    - namespace
    63                    type: object
    64                  type: array
    65              type: object
    66            status:
    67              properties:
    68                objects:
    69                  items:
    70                    properties:
    71                      group:
    72                        type: string
    73                      kind:
    74                        type: string
    75                      name:
    76                        type: string
    77                      namespace:
    78                        type: string
    79                      strategy:
    80                        type: string
    81                      actuation:
    82                        type: string
    83                      reconcile:
    84                        type: string
    85                    required:
    86                    - group
    87                    - kind
    88                    - name
    89                    - namespace
    90                    - strategy
    91                    - actuation
    92                    - reconcile
    93                    type: object
    94                  type: array
    95              type: object
    96          type: object
    97      served: true
    98      storage: true
    99      subresources:
   100        status: {}
   101  `))
   102  
   103  var InventoryGVK = schema.GroupVersionKind{
   104  	Group:   "cli-utils.example.io",
   105  	Version: "v1alpha1",
   106  	Kind:    "Inventory",
   107  }
   108  
   109  var _ inventory.ClientFactory = CustomClientFactory{}
   110  
   111  type CustomClientFactory struct {
   112  }
   113  
   114  func (CustomClientFactory) NewClient(factory util.Factory) (inventory.Client, error) {
   115  	return inventory.NewClient(factory,
   116  		WrapInventoryObj, invToUnstructuredFunc, inventory.StatusPolicyAll, inventory.ConfigMapGVK)
   117  }
   118  
   119  func invToUnstructuredFunc(inv inventory.Info) *unstructured.Unstructured {
   120  	switch invInfo := inv.(type) {
   121  	case *InventoryCustomType:
   122  		return invInfo.inv
   123  	default:
   124  		return nil
   125  	}
   126  }
   127  
   128  func WrapInventoryObj(obj *unstructured.Unstructured) inventory.Storage {
   129  	return &InventoryCustomType{inv: obj}
   130  }
   131  
   132  func WrapInventoryInfoObj(obj *unstructured.Unstructured) inventory.Info {
   133  	return &InventoryCustomType{inv: obj}
   134  }
   135  
   136  var _ inventory.Storage = &InventoryCustomType{}
   137  var _ inventory.Info = &InventoryCustomType{}
   138  
   139  type InventoryCustomType struct {
   140  	inv *unstructured.Unstructured
   141  }
   142  
   143  func (i InventoryCustomType) Namespace() string {
   144  	return i.inv.GetNamespace()
   145  }
   146  
   147  func (i InventoryCustomType) Name() string {
   148  	return i.inv.GetName()
   149  }
   150  
   151  func (i InventoryCustomType) Strategy() inventory.Strategy {
   152  	return inventory.NameStrategy
   153  }
   154  
   155  func (i InventoryCustomType) ID() string {
   156  	labels := i.inv.GetLabels()
   157  	id, found := labels[common.InventoryLabel]
   158  	if !found {
   159  		return ""
   160  	}
   161  	return id
   162  }
   163  
   164  func (i InventoryCustomType) Load() (object.ObjMetadataSet, error) {
   165  	var inv object.ObjMetadataSet
   166  	s, found, err := unstructured.NestedSlice(i.inv.Object, "spec", "objects")
   167  	if err != nil {
   168  		return inv, err
   169  	}
   170  	if !found {
   171  		return inv, nil
   172  	}
   173  	for _, item := range s {
   174  		m := item.(map[string]interface{})
   175  		namespace, _, _ := unstructured.NestedString(m, "namespace")
   176  		name, _, _ := unstructured.NestedString(m, "name")
   177  		group, _, _ := unstructured.NestedString(m, "group")
   178  		kind, _, _ := unstructured.NestedString(m, "kind")
   179  		id := object.ObjMetadata{
   180  			Namespace: namespace,
   181  			Name:      name,
   182  			GroupKind: schema.GroupKind{
   183  				Group: group,
   184  				Kind:  kind,
   185  			},
   186  		}
   187  		inv = append(inv, id)
   188  	}
   189  	return inv, nil
   190  }
   191  
   192  func (i InventoryCustomType) Store(objs object.ObjMetadataSet, status []actuation.ObjectStatus) error {
   193  	var specObjs []interface{}
   194  	for _, obj := range objs {
   195  		specObjs = append(specObjs, map[string]interface{}{
   196  			"group":     obj.GroupKind.Group,
   197  			"kind":      obj.GroupKind.Kind,
   198  			"namespace": obj.Namespace,
   199  			"name":      obj.Name,
   200  		})
   201  	}
   202  	var statusObjs []interface{}
   203  	for _, objStatus := range status {
   204  		statusObjs = append(statusObjs, map[string]interface{}{
   205  			"group":     objStatus.Group,
   206  			"kind":      objStatus.Kind,
   207  			"namespace": objStatus.Namespace,
   208  			"name":      objStatus.Name,
   209  			"strategy":  objStatus.Strategy.String(),
   210  			"actuation": objStatus.Actuation.String(),
   211  			"reconcile": objStatus.Reconcile.String(),
   212  		})
   213  	}
   214  	if len(specObjs) > 0 {
   215  		err := unstructured.SetNestedSlice(i.inv.Object, specObjs, "spec", "objects")
   216  		if err != nil {
   217  			return err
   218  		}
   219  	} else {
   220  		unstructured.RemoveNestedField(i.inv.Object, "spec")
   221  	}
   222  	if len(statusObjs) > 0 {
   223  		err := unstructured.SetNestedSlice(i.inv.Object, statusObjs, "status", "objects")
   224  		if err != nil {
   225  			return err
   226  		}
   227  	} else {
   228  		unstructured.RemoveNestedField(i.inv.Object, "status")
   229  	}
   230  	return nil
   231  }
   232  
   233  func (i InventoryCustomType) GetObject() (*unstructured.Unstructured, error) {
   234  	return i.inv, nil
   235  }
   236  
   237  // Apply is an Inventory interface function implemented to apply the inventory
   238  // object.
   239  func (i InventoryCustomType) Apply(dc dynamic.Interface, mapper meta.RESTMapper, _ inventory.StatusPolicy) error {
   240  	invInfo, namespacedClient, err := i.getNamespacedClient(dc, mapper)
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	// Get cluster object, if exsists.
   246  	clusterObj, err := namespacedClient.Get(context.TODO(), invInfo.GetName(), metav1.GetOptions{})
   247  	if err != nil && !apierrors.IsNotFound(err) {
   248  		return err
   249  	}
   250  
   251  	var appliedObj *unstructured.Unstructured
   252  
   253  	if clusterObj == nil {
   254  		// Create cluster inventory object, if it does not exist on cluster.
   255  		appliedObj, err = namespacedClient.Create(context.TODO(), invInfo, metav1.CreateOptions{})
   256  	} else {
   257  		// Update the cluster inventory object instead.
   258  		appliedObj, err = namespacedClient.Update(context.TODO(), invInfo, metav1.UpdateOptions{})
   259  	}
   260  	if err != nil {
   261  		return err
   262  	}
   263  
   264  	// Update status.
   265  	invInfo.SetResourceVersion(appliedObj.GetResourceVersion())
   266  	_, err = namespacedClient.UpdateStatus(context.TODO(), invInfo, metav1.UpdateOptions{})
   267  	return err
   268  }
   269  
   270  func (i InventoryCustomType) ApplyWithPrune(dc dynamic.Interface, mapper meta.RESTMapper, _ inventory.StatusPolicy, _ object.ObjMetadataSet) error {
   271  	invInfo, namespacedClient, err := i.getNamespacedClient(dc, mapper)
   272  	if err != nil {
   273  		return err
   274  	}
   275  
   276  	// Update the cluster inventory object.
   277  	appliedObj, err := namespacedClient.Update(context.TODO(), invInfo, metav1.UpdateOptions{})
   278  	if err != nil {
   279  		return err
   280  	}
   281  
   282  	// Update status.
   283  	invInfo.SetResourceVersion(appliedObj.GetResourceVersion())
   284  	_, err = namespacedClient.UpdateStatus(context.TODO(), invInfo, metav1.UpdateOptions{})
   285  	return err
   286  }
   287  
   288  func (i InventoryCustomType) getNamespacedClient(dc dynamic.Interface, mapper meta.RESTMapper) (*unstructured.Unstructured, dynamic.ResourceInterface, error) {
   289  	invInfo, err := i.GetObject()
   290  	if err != nil {
   291  		return nil, nil, err
   292  	}
   293  	if invInfo == nil {
   294  		return nil, nil, fmt.Errorf("attempting to create a nil inventory object")
   295  	}
   296  
   297  	mapping, err := mapper.RESTMapping(invInfo.GroupVersionKind().GroupKind(), invInfo.GroupVersionKind().Version)
   298  	if err != nil {
   299  		return nil, nil, err
   300  	}
   301  
   302  	// Create client to interact with cluster.
   303  	namespacedClient := dc.Resource(mapping.Resource).Namespace(invInfo.GetNamespace())
   304  
   305  	return invInfo, namespacedClient, nil
   306  }
   307  

View as plain text