github.com/GoogleContainerTools/kpt@v1.0.0-beta.50.0.20240520170205-c25345ffcbee/pkg/live/inventoryrg.go (about)

     1  // Copyright 2020 The kpt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package live
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"reflect"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/GoogleContainerTools/kpt/pkg/status"
    25  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    26  	"k8s.io/apimachinery/pkg/api/meta"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	"k8s.io/cli-runtime/pkg/resource"
    32  	"k8s.io/client-go/dynamic"
    33  	"k8s.io/klog/v2"
    34  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    35  	"k8s.io/kubectl/pkg/util"
    36  	"sigs.k8s.io/cli-utils/pkg/apis/actuation"
    37  	"sigs.k8s.io/cli-utils/pkg/common"
    38  	"sigs.k8s.io/cli-utils/pkg/inventory"
    39  	"sigs.k8s.io/cli-utils/pkg/kstatus/polling"
    40  	pollevent "sigs.k8s.io/cli-utils/pkg/kstatus/polling/event"
    41  	kstatus "sigs.k8s.io/cli-utils/pkg/kstatus/status"
    42  	"sigs.k8s.io/cli-utils/pkg/object"
    43  	"sigs.k8s.io/kustomize/kyaml/yaml"
    44  )
    45  
    46  const (
    47  	applyRGTimeout      = 10 * time.Second
    48  	applyRGPollInterval = 2 * time.Second
    49  )
    50  
    51  // ResourceGroupGVK is the group/version/kind of the custom
    52  // resource used to store inventory.
    53  var ResourceGroupGVK = schema.GroupVersionKind{
    54  	Group:   "kpt.dev",
    55  	Version: "v1alpha1",
    56  	Kind:    "ResourceGroup",
    57  }
    58  
    59  // InventoryResourceGroup wraps a ResourceGroup resource and implements
    60  // the Inventory and InventoryInfo interface. This wrapper loads and stores the
    61  // object metadata (inventory) to and from the wrapped ResourceGroup.
    62  type InventoryResourceGroup struct {
    63  	inv       *unstructured.Unstructured
    64  	objMetas  []object.ObjMetadata
    65  	objStatus []actuation.ObjectStatus
    66  }
    67  
    68  func (icm *InventoryResourceGroup) Strategy() inventory.Strategy {
    69  	return inventory.NameStrategy
    70  }
    71  
    72  var _ inventory.Storage = &InventoryResourceGroup{}
    73  var _ inventory.Info = &InventoryResourceGroup{}
    74  
    75  // WrapInventoryObj takes a passed ResourceGroup (as a resource.Info),
    76  // wraps it with the InventoryResourceGroup and upcasts the wrapper as
    77  // an the Inventory interface.
    78  func WrapInventoryObj(obj *unstructured.Unstructured) inventory.Storage {
    79  	if obj != nil {
    80  		klog.V(4).Infof("wrapping Inventory obj: %s/%s\n", obj.GetNamespace(), obj.GetName())
    81  	}
    82  	return &InventoryResourceGroup{inv: obj}
    83  }
    84  
    85  func WrapInventoryInfoObj(obj *unstructured.Unstructured) inventory.Info {
    86  	if obj != nil {
    87  		klog.V(4).Infof("wrapping InventoryInfo obj: %s/%s\n", obj.GetNamespace(), obj.GetName())
    88  	}
    89  	return &InventoryResourceGroup{inv: obj}
    90  }
    91  
    92  func InvToUnstructuredFunc(inv inventory.Info) *unstructured.Unstructured {
    93  	switch invInfo := inv.(type) {
    94  	case *InventoryResourceGroup:
    95  		return invInfo.inv
    96  	default:
    97  		return nil
    98  	}
    99  }
   100  
   101  // Name(), Namespace(), and ID() are InventoryResourceGroup functions to
   102  // implement the InventoryInfo interface.
   103  func (icm *InventoryResourceGroup) Name() string {
   104  	return icm.inv.GetName()
   105  }
   106  
   107  func (icm *InventoryResourceGroup) Namespace() string {
   108  	return icm.inv.GetNamespace()
   109  }
   110  
   111  func (icm *InventoryResourceGroup) ID() string {
   112  	labels := icm.inv.GetLabels()
   113  	if val, found := labels[common.InventoryLabel]; found {
   114  		return val
   115  	}
   116  	return ""
   117  }
   118  
   119  // Load is an Inventory interface function returning the set of
   120  // object metadata from the wrapped ResourceGroup, or an error.
   121  func (icm *InventoryResourceGroup) Load() (object.ObjMetadataSet, error) {
   122  	objs := object.ObjMetadataSet{}
   123  	if icm.inv == nil {
   124  		return objs, fmt.Errorf("inventory info is nil")
   125  	}
   126  	klog.V(4).Infof("loading inventory...")
   127  	items, exists, err := unstructured.NestedSlice(icm.inv.Object, "spec", "resources")
   128  	if err != nil {
   129  		err := fmt.Errorf("error retrieving object metadata from inventory object")
   130  		return objs, err
   131  	}
   132  	if !exists {
   133  		klog.V(4).Infof("Inventory (spec.resources) does not exist")
   134  		return objs, nil
   135  	}
   136  	klog.V(4).Infof("loading %d inventory items", len(items))
   137  	for _, itemUncast := range items {
   138  		item := itemUncast.(map[string]interface{})
   139  		namespace, _, err := unstructured.NestedString(item, "namespace")
   140  		if err != nil {
   141  			return []object.ObjMetadata{}, err
   142  		}
   143  		name, _, err := unstructured.NestedString(item, "name")
   144  		if err != nil {
   145  			return []object.ObjMetadata{}, err
   146  		}
   147  		group, _, err := unstructured.NestedString(item, "group")
   148  		if err != nil {
   149  			return []object.ObjMetadata{}, err
   150  		}
   151  		kind, _, err := unstructured.NestedString(item, "kind")
   152  		if err != nil {
   153  			return []object.ObjMetadata{}, err
   154  		}
   155  		groupKind := schema.GroupKind{
   156  			Group: strings.TrimSpace(group),
   157  			Kind:  strings.TrimSpace(kind),
   158  		}
   159  		klog.V(4).Infof("creating obj metadata: %s/%s/%s", namespace, name, groupKind)
   160  		objMeta := object.ObjMetadata{
   161  			GroupKind: groupKind,
   162  			Name:      name,
   163  			Namespace: namespace,
   164  		}
   165  		objs = append(objs, objMeta)
   166  	}
   167  	return objs, nil
   168  }
   169  
   170  // Store is an Inventory interface function implemented to store
   171  // the object metadata in the wrapped ResourceGroup. Actual storing
   172  // happens in "GetObject".
   173  func (icm *InventoryResourceGroup) Store(objMetas object.ObjMetadataSet, status []actuation.ObjectStatus) error {
   174  	icm.objMetas = objMetas
   175  	icm.objStatus = status
   176  	return nil
   177  }
   178  
   179  // GetObject returns the wrapped object (ResourceGroup) as a resource.Info
   180  // or an error if one occurs.
   181  func (icm *InventoryResourceGroup) GetObject() (*unstructured.Unstructured, error) {
   182  	if icm.inv == nil {
   183  		return nil, fmt.Errorf("inventory info is nil")
   184  	}
   185  	objStatusMap := map[object.ObjMetadata]actuation.ObjectStatus{}
   186  	for _, s := range icm.objStatus {
   187  		objStatusMap[inventory.ObjMetadataFromObjectReference(s.ObjectReference)] = s
   188  	}
   189  	klog.V(4).Infof("getting inventory resource group")
   190  	// Create a slice of Resources as empty Interface
   191  	klog.V(4).Infof("Creating list of %d resources", len(icm.objMetas))
   192  	var objs []interface{}
   193  	for _, objMeta := range icm.objMetas {
   194  		klog.V(4).Infof("storing inventory obj refercence: %s/%s", objMeta.Namespace, objMeta.Name)
   195  		objs = append(objs, map[string]interface{}{
   196  			"group":     objMeta.GroupKind.Group,
   197  			"kind":      objMeta.GroupKind.Kind,
   198  			"namespace": objMeta.Namespace,
   199  			"name":      objMeta.Name,
   200  		})
   201  	}
   202  	klog.V(4).Infof("Creating list of %d resources status", len(icm.objMetas))
   203  	var objStatus []interface{}
   204  	for _, objMeta := range icm.objMetas {
   205  		status, found := objStatusMap[objMeta]
   206  		if found {
   207  			klog.V(4).Infof("storing inventory obj refercence and its status: %s/%s", objMeta.Namespace, objMeta.Name)
   208  			objStatus = append(objStatus, map[string]interface{}{
   209  				"group":     objMeta.GroupKind.Group,
   210  				"kind":      objMeta.GroupKind.Kind,
   211  				"namespace": objMeta.Namespace,
   212  				"name":      objMeta.Name,
   213  				"status":    "Unknown",
   214  				"strategy":  status.Strategy.String(),
   215  				"actuation": status.Actuation.String(),
   216  				"reconcile": status.Reconcile.String(),
   217  			})
   218  		}
   219  	}
   220  
   221  	// Create the inventory object by copying the template.
   222  	invCopy := icm.inv.DeepCopy()
   223  	// Adds or clears the inventory ObjMetadata to the ResourceGroup "spec.resources" section
   224  	if len(objs) == 0 {
   225  		klog.V(4).Infoln("clearing inventory resources")
   226  		unstructured.RemoveNestedField(invCopy.UnstructuredContent(),
   227  			"spec", "resources")
   228  		unstructured.RemoveNestedField(invCopy.UnstructuredContent(),
   229  			"status", "resourceStatuses")
   230  	} else {
   231  		klog.V(4).Infof("storing inventory (%d) resources", len(objs))
   232  		err := unstructured.SetNestedSlice(invCopy.UnstructuredContent(),
   233  			objs, "spec", "resources")
   234  		if err != nil {
   235  			return nil, err
   236  		}
   237  		err = unstructured.SetNestedSlice(invCopy.UnstructuredContent(),
   238  			objStatus, "status", "resourceStatuses")
   239  		if err != nil {
   240  			return nil, err
   241  		}
   242  		generation := invCopy.GetGeneration()
   243  		err = unstructured.SetNestedField(invCopy.UnstructuredContent(),
   244  			generation, "status", "observedGeneration")
   245  		if err != nil {
   246  			return nil, err
   247  		}
   248  	}
   249  	return invCopy, nil
   250  }
   251  
   252  // Apply is a Storage interface function implemented to apply the inventory
   253  // object.
   254  func (icm *InventoryResourceGroup) Apply(dc dynamic.Interface, mapper meta.RESTMapper, statusPolicy inventory.StatusPolicy) error {
   255  	invInfo, namespacedClient, err := icm.getNamespacedClient(dc, mapper)
   256  	if err != nil {
   257  		return err
   258  	}
   259  
   260  	// Get cluster object, if exsists.
   261  	clusterObj, err := namespacedClient.Get(context.TODO(), invInfo.GetName(), metav1.GetOptions{})
   262  	if err != nil && !apierrors.IsNotFound(err) {
   263  		return err
   264  	}
   265  
   266  	var appliedObj *unstructured.Unstructured
   267  
   268  	if clusterObj == nil {
   269  		// Create cluster inventory object, if it does not exist on cluster.
   270  		appliedObj, err = namespacedClient.Create(context.TODO(), invInfo, metav1.CreateOptions{})
   271  	} else {
   272  		// Update the cluster inventory object instead.
   273  		appliedObj, err = namespacedClient.Update(context.TODO(), invInfo, metav1.UpdateOptions{})
   274  	}
   275  	if err != nil {
   276  		return err
   277  	}
   278  
   279  	// Update status.
   280  	if statusPolicy == inventory.StatusPolicyAll {
   281  		invInfo.SetResourceVersion(appliedObj.GetResourceVersion())
   282  		_, err = namespacedClient.UpdateStatus(context.TODO(), invInfo, metav1.UpdateOptions{})
   283  	}
   284  
   285  	return err
   286  }
   287  
   288  func (icm *InventoryResourceGroup) ApplyWithPrune(dc dynamic.Interface, mapper meta.RESTMapper, statusPolicy inventory.StatusPolicy, _ object.ObjMetadataSet) error {
   289  	invInfo, namespacedClient, err := icm.getNamespacedClient(dc, mapper)
   290  	if err != nil {
   291  		return err
   292  	}
   293  
   294  	// Update the cluster inventory object.
   295  	// Since the ResourceGroup CRD specifies the status as a sub-resource, this
   296  	// will not update the status.
   297  	appliedObj, err := namespacedClient.Update(context.TODO(), invInfo, metav1.UpdateOptions{})
   298  	if err != nil {
   299  		return err
   300  	}
   301  
   302  	// Update status, if status policy allows it.
   303  	// To avoid losing modifications performed by mutating webhooks, copy the
   304  	// status from the desired state to the latest state after the previous update.
   305  	// This also ensures that the ResourceVersion matches the latest state, to
   306  	// avoid the update being rejected by the server.
   307  	if statusPolicy == inventory.StatusPolicyAll {
   308  		status, found, err := unstructured.NestedMap(invInfo.UnstructuredContent(), "status")
   309  		if err != nil {
   310  			return err
   311  		}
   312  		if found {
   313  			err = unstructured.SetNestedField(appliedObj.UnstructuredContent(), status, "status")
   314  			if err != nil {
   315  				return err
   316  			}
   317  			_, err = namespacedClient.UpdateStatus(context.TODO(), appliedObj, metav1.UpdateOptions{})
   318  			if err != nil {
   319  				return err
   320  			}
   321  		}
   322  	}
   323  
   324  	return nil
   325  }
   326  
   327  func (icm *InventoryResourceGroup) getNamespacedClient(dc dynamic.Interface, mapper meta.RESTMapper) (*unstructured.Unstructured, dynamic.ResourceInterface, error) {
   328  	invInfo, err := icm.GetObject()
   329  	if err != nil {
   330  		return nil, nil, err
   331  	}
   332  	if invInfo == nil {
   333  		return nil, nil, fmt.Errorf("attempting to create a nil inventory object")
   334  	}
   335  
   336  	mapping, err := mapper.RESTMapping(invInfo.GroupVersionKind().GroupKind(), invInfo.GroupVersionKind().Version)
   337  	if err != nil {
   338  		return nil, nil, err
   339  	}
   340  
   341  	// Create client to interact with cluster.
   342  	namespacedClient := dc.Resource(mapping.Resource).Namespace(invInfo.GetNamespace())
   343  
   344  	return invInfo, namespacedClient, nil
   345  }
   346  
   347  // IsResourceGroupInventory returns true if the passed object is
   348  // a ResourceGroup inventory object; false otherwise. If an error
   349  // occurs, then false is returned and the error.
   350  func IsResourceGroupInventory(obj *unstructured.Unstructured) (bool, error) {
   351  	if obj == nil {
   352  		return false, fmt.Errorf("inventory object is nil")
   353  	}
   354  	if !inventory.IsInventoryObject(obj) {
   355  		return false, nil
   356  	}
   357  	invGK := obj.GetObjectKind().GroupVersionKind().GroupKind()
   358  	if ResourceGroupGVK.GroupKind() != invGK {
   359  		return false, nil
   360  	}
   361  	return true, nil
   362  }
   363  
   364  // CustomResourceDefinition schema, without specific version. The default version
   365  // is returned when the RESTMapper returns a RESTMapping for this GroupKind.
   366  var crdGroupKind = schema.GroupKind{
   367  	Group: "apiextensions.k8s.io",
   368  	Kind:  "CustomResourceDefinition",
   369  }
   370  
   371  // ResourceGroupCRDApplied returns true if the inventory ResourceGroup
   372  // CRD is available from the current RESTMapper, or false otherwise.
   373  func ResourceGroupCRDApplied(factory cmdutil.Factory) bool {
   374  	mapper, err := factory.ToRESTMapper()
   375  	if err != nil {
   376  		klog.V(4).Infof("error retrieving RESTMapper when checking ResourceGroup CRD: %s\n", err)
   377  		return false
   378  	}
   379  	_, err = mapper.RESTMapping(ResourceGroupGVK.GroupKind())
   380  	if err != nil {
   381  		klog.V(7).Infof("error retrieving ResourceGroup RESTMapping: %s\n", err)
   382  		return false
   383  	}
   384  	return true
   385  }
   386  
   387  // ResourceGroupCRDMatched checks if the ResourceGroup CRD
   388  // in the cluster matches the CRD in the kpt binary.
   389  func ResourceGroupCRDMatched(factory cmdutil.Factory) bool {
   390  	mapper, err := factory.ToRESTMapper()
   391  	if err != nil {
   392  		klog.V(4).Infof("error retrieving RESTMapper when checking ResourceGroup CRD: %s\n", err)
   393  		return false
   394  	}
   395  	crd, err := rgCRD(mapper)
   396  	if err != nil {
   397  		klog.V(7).Infof("failed to get ResourceGroup CRD from string: %s", err)
   398  		return false
   399  	}
   400  
   401  	dc, err := factory.DynamicClient()
   402  	if err != nil {
   403  		klog.V(7).Infof("error getting the dynamic client: %s\n", err)
   404  		return false
   405  	}
   406  
   407  	mapping, err := mapper.RESTMapping(crdGroupKind)
   408  	if err != nil {
   409  		klog.V(7).Infof("Failed to get mapping of CRD type: %s", err)
   410  		return false
   411  	}
   412  
   413  	liveCRD, err := dc.Resource(mapping.Resource).Get(context.TODO(), "resourcegroups.kpt.dev", metav1.GetOptions{
   414  		TypeMeta: metav1.TypeMeta{
   415  			APIVersion: crd.GetAPIVersion(),
   416  			Kind:       "CustomResourceDefinition",
   417  		},
   418  	})
   419  	if err != nil {
   420  		klog.V(7).Infof("error getting the ResourceGroup CRD from cluster: %s\n", err)
   421  		return false
   422  	}
   423  
   424  	liveSpec, _, err := unstructured.NestedMap(liveCRD.Object, "spec")
   425  	if err != nil {
   426  		klog.V(7).Infof("error getting the ResourceGroup CRD spec from cluster: %s\n", err)
   427  		return false
   428  	}
   429  	latestspec, _, err := unstructured.NestedMap(crd.Object, "spec")
   430  	if err != nil {
   431  		klog.V(7).Infof("error getting the ResourceGroup CRD spec from string: %s\n", err)
   432  		return false
   433  	}
   434  	return reflect.DeepEqual(liveSpec, latestspec)
   435  }
   436  
   437  // ResourceGroupInstaller can install the ResourceGroup CRD into a cluster.
   438  type ResourceGroupInstaller struct {
   439  	Factory cmdutil.Factory
   440  }
   441  
   442  func (rgi *ResourceGroupInstaller) InstallRG(ctx context.Context) error {
   443  	poller, err := status.NewStatusPoller(rgi.Factory)
   444  	if err != nil {
   445  		return err
   446  	}
   447  
   448  	mapper, err := rgi.Factory.ToRESTMapper()
   449  	if err != nil {
   450  		return err
   451  	}
   452  
   453  	crd, err := rgCRD(mapper)
   454  	if err != nil {
   455  		return err
   456  	}
   457  
   458  	if err := rgi.applyRG(crd); err != nil {
   459  		if apierrors.IsAlreadyExists(err) {
   460  			return nil
   461  		}
   462  		return err
   463  	}
   464  
   465  	objs := object.UnstructuredSetToObjMetadataSet([]*unstructured.Unstructured{crd})
   466  	ctx, cancel := context.WithTimeout(ctx, applyRGTimeout)
   467  	return func() error {
   468  		defer cancel()
   469  		for e := range poller.Poll(ctx, objs, polling.PollOptions{PollInterval: applyRGPollInterval}) {
   470  			switch e.Type {
   471  			case pollevent.ErrorEvent:
   472  				return e.Error
   473  			case pollevent.ResourceUpdateEvent:
   474  				if e.Resource.Status == kstatus.CurrentStatus {
   475  					meta.MaybeResetRESTMapper(mapper)
   476  				}
   477  			}
   478  		}
   479  		return nil
   480  	}()
   481  }
   482  
   483  func (rgi *ResourceGroupInstaller) applyRG(crd runtime.Object) error {
   484  	mapper, err := rgi.Factory.ToRESTMapper()
   485  	if err != nil {
   486  		return err
   487  	}
   488  	mapping, err := mapper.RESTMapping(crdGroupKind)
   489  	if err != nil {
   490  		return err
   491  	}
   492  	client, err := rgi.Factory.UnstructuredClientForMapping(mapping)
   493  	if err != nil {
   494  		return err
   495  	}
   496  
   497  	// Set the "last-applied-annotation" so future applies work correctly.
   498  	if err := util.CreateApplyAnnotation(crd, unstructured.UnstructuredJSONScheme); err != nil {
   499  		return err
   500  	}
   501  	// Apply the CRD to the cluster and ignore already exists error.
   502  	var clearResourceVersion = false
   503  	var emptyNamespace = ""
   504  	helper := resource.NewHelper(client, mapping)
   505  	_, err = helper.Create(emptyNamespace, clearResourceVersion, crd)
   506  	return err
   507  }
   508  
   509  // rgCRD returns the ResourceGroup CRD in Unstructured format or an error.
   510  func rgCRD(mapper meta.RESTMapper) (*unstructured.Unstructured, error) {
   511  	mapping, err := mapper.RESTMapping(crdGroupKind)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  	// mapping contains the full GVK version, which is used to determine
   516  	// the version of the ResourceGroup CRD to create. We have defined the
   517  	// v1beta1 and v1 versions of the apiextensions group of the CRD.
   518  	version := mapping.GroupVersionKind.Version
   519  	klog.V(4).Infof("using apiextensions.k8s.io version: %s", version)
   520  	rgCRDStr, ok := resourceGroupCRDs[version]
   521  	if !ok {
   522  		klog.V(4).Infof("ResourceGroup CRD version %s not found", version)
   523  		return nil, err
   524  	}
   525  	crd, err := stringToUnstructured(rgCRDStr)
   526  	if err != nil {
   527  		return nil, err
   528  	}
   529  	return crd, nil
   530  }
   531  
   532  // stringToUnstructured transforms a single resource represented by
   533  // the passed string into a pointer to an "Unstructured" object,
   534  // or an error if one occurred.
   535  func stringToUnstructured(str string) (*unstructured.Unstructured, error) {
   536  	node, err := yaml.Parse(str)
   537  	if err != nil {
   538  		return nil, err
   539  	}
   540  	s, err := node.String()
   541  	if err != nil {
   542  		return nil, err
   543  	}
   544  	var m map[string]interface{}
   545  	if err := yaml.Unmarshal([]byte(s), &m); err != nil {
   546  		return nil, err
   547  	}
   548  	return &unstructured.Unstructured{Object: m}, nil
   549  }
   550  
   551  // resourceGroupCRDs maps the apiextensions version to the ResourceGroup
   552  // custom resource definition string.
   553  var resourceGroupCRDs = map[string]string{
   554  	"v1beta1": v1beta1RGCrd,
   555  	"v1":      v1RGCrd,
   556  }
   557  
   558  // ResourceGroup custom resource definition using v1beta1 version
   559  // of the apiextensions.k8s.io API group. APIServers version 1.15
   560  // or less will use this apiextensions group by default.
   561  var v1beta1RGCrd = `
   562  apiVersion: apiextensions.k8s.io/v1beta1
   563  kind: CustomResourceDefinition
   564  metadata:
   565    name: resourcegroups.kpt.dev
   566  spec:
   567    group: kpt.dev
   568    names:
   569      kind: ResourceGroup
   570      listKind: ResourceGroupList
   571      plural: resourcegroups
   572      singular: resourcegroup
   573    scope: Namespaced
   574    subresources:
   575      status: {}
   576    validation:
   577      openAPIV3Schema:
   578        description: ResourceGroup is the Schema for the resourcegroups API
   579        properties:
   580          apiVersion:
   581            description: 'APIVersion defines the versioned schema of this representation
   582              of an object. Servers should convert recognized schemas to the latest
   583              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
   584            type: string
   585          kind:
   586            description: 'Kind is a string value representing the REST resource this
   587              object represents. Servers may infer this from the endpoint the client
   588              submits requests to. Cannot be updated. In CamelCase.
   589              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
   590            type: string
   591          metadata:
   592            type: object
   593          spec:
   594            description: ResourceGroupSpec defines the desired state of ResourceGroup
   595            properties:
   596              descriptor:
   597                description: Descriptor regroups the information and metadata about
   598                  a resource group
   599                properties:
   600                  description:
   601                    description: Description is a brief description of a group of resources
   602                    type: string
   603                  links:
   604                    description: Links are a list of descriptive URLs intended to be
   605                      used to surface additional information
   606                    items:
   607                      properties:
   608                        description:
   609                          description: Description explains the purpose of the link
   610                          type: string
   611                        url:
   612                          description: Url is the URL of the link
   613                          type: string
   614                      required:
   615                      - description
   616                      - url
   617                      type: object
   618                    type: array
   619                  revision:
   620                    description: Revision is an optional revision for a group of resources
   621                    type: string
   622                  type:
   623                    description: Type can contain prefix, such as Application/WordPress
   624                      or Service/Spanner
   625                    type: string
   626                type: object
   627              resources:
   628                description: Resources contains a list of resources that form the resource group
   629                items:
   630                  description: ObjMetadata organizes and stores the identifying information
   631                    for an object. This struct (as a string) is stored in a grouping
   632                    object to keep track of sets of applied objects.
   633                  properties:
   634                    group:
   635                      type: string
   636                    kind:
   637                      type: string
   638                    name:
   639                      type: string
   640                    namespace:
   641                      type: string
   642                  required:
   643                  - group
   644                  - kind
   645                  - name
   646                  - namespace
   647                  type: object
   648                type: array
   649            type: object
   650          status:
   651            description: ResourceGroupStatus defines the observed state of ResourceGroup
   652            properties:
   653              conditions:
   654                description: Conditions lists the conditions of the current status for
   655                  the group
   656                items:
   657                  properties:
   658                    lastTransitionTime:
   659                      description: last time the condition transit from one status to
   660                        another
   661                      format: date-time
   662                      type: string
   663                    message:
   664                      description: human-readable message indicating details about last
   665                        transition
   666                      type: string
   667                    reason:
   668                      description: one-word CamelCase reason for the condition's last
   669                        transition
   670                      type: string
   671                    status:
   672                      description: Status of the condition
   673                      type: string
   674                    type:
   675                      description: Type of the condition
   676                      type: string
   677                  required:
   678                  - status
   679                  - type
   680                  type: object
   681                type: array
   682              observedGeneration:
   683                description: ObservedGeneration is the most recent generation observed.
   684                  It corresponds to the Object's generation, which is updated on mutation
   685                  by the API Server. Everytime the controller does a successful reconcile,
   686                  it sets ObservedGeneration to match ResourceGroup.metadata.generation.
   687                format: int64
   688                type: integer
   689              resourceStatuses:
   690                description: ResourceStatuses lists the status for each resource in
   691                  the group
   692                items:
   693                  description: ResourceStatus contains the status of a given resource
   694                    uniquely identified by its group, kind, name and namespace.
   695                  properties:
   696                    actuation:
   697                      description: actuation indicates whether actuation has been
   698                        performed yet and how it went.
   699                      type: string
   700                    conditions:
   701                      items:
   702                        properties:
   703                          lastTransitionTime:
   704                            description: last time the condition transit from one status
   705                              to another
   706                            format: date-time
   707                            type: string
   708                          message:
   709                            description: human-readable message indicating details about
   710                              last transition
   711                            type: string
   712                          reason:
   713                            description: one-word CamelCase reason for the condition's
   714                              last transition
   715                            type: string
   716                          status:
   717                            description: Status of the condition
   718                            type: string
   719                          type:
   720                            description: Type of the condition
   721                            type: string
   722                        required:
   723                        - status
   724                        - type
   725                        type: object
   726                      type: array
   727                    group:
   728                      type: string
   729                    kind:
   730                      type: string
   731                    name:
   732                      type: string
   733                    namespace:
   734                      type: string
   735                    reconcile:
   736                      description: reconcile indicates whether reconciliation has
   737                        been performed yet and how it went.
   738                      type: string
   739                    sourceHash:
   740                      type: string
   741                    status:
   742                      description: Status describes the status of a resource
   743                      type: string
   744                    strategy:
   745                      description: strategy indicates the method of actuation (apply
   746                        or delete) used or planned to be used.
   747                      type: string
   748                  required:
   749                  - group
   750                  - kind
   751                  - name
   752                  - namespace
   753                  - status
   754                  type: object
   755                type: array
   756            required:
   757            - observedGeneration
   758            type: object
   759        type: object
   760    version: v1alpha1
   761    versions:
   762    - name: v1alpha1
   763      served: true
   764      storage: true
   765  status:
   766    acceptedNames:
   767      kind: ""
   768      plural: ""
   769    conditions: []
   770    storedVersions: []
   771  `
   772  
   773  // ResourceGroup custom resource definition using v1 version
   774  // of the apiextensions.k8s.io API group. APIServers at 1.16
   775  // or greater will use this apiextensions group by default.
   776  var v1RGCrd = `
   777  apiVersion: apiextensions.k8s.io/v1
   778  kind: CustomResourceDefinition
   779  metadata:
   780    name: resourcegroups.kpt.dev
   781  spec:
   782    conversion:
   783      strategy: None
   784    group: kpt.dev
   785    names:
   786      kind: ResourceGroup
   787      listKind: ResourceGroupList
   788      plural: resourcegroups
   789      singular: resourcegroup
   790    scope: Namespaced
   791    versions:
   792    - name: v1alpha1
   793      schema:
   794        openAPIV3Schema:
   795          description: ResourceGroup is the Schema for the resourcegroups API
   796          properties:
   797            apiVersion:
   798              description: 'APIVersion defines the versioned schema of this representation
   799                of an object. Servers should convert recognized schemas to the latest
   800                internal value, and may reject unrecognized values.
   801                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
   802              type: string
   803            kind:
   804              description: 'Kind is a string value representing the REST resource this
   805                object represents. Servers may infer this from the endpoint the client
   806                submits requests to. Cannot be updated. In CamelCase.
   807                More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
   808              type: string
   809            metadata:
   810              type: object
   811            spec:
   812              description: ResourceGroupSpec defines the desired state of ResourceGroup
   813              properties:
   814                descriptor:
   815                  description: Descriptor regroups the information and metadata about
   816                    a resource group
   817                  properties:
   818                    description:
   819                      description: Description is a brief description of a group of
   820                        resources
   821                      type: string
   822                    links:
   823                      description: Links are a list of descriptive URLs intended to
   824                        be used to surface additional information
   825                      items:
   826                        properties:
   827                          description:
   828                            description: Description explains the purpose of the link
   829                            type: string
   830                          url:
   831                            description: Url is the URL of the link
   832                            type: string
   833                        required:
   834                        - description
   835                        - url
   836                        type: object
   837                      type: array
   838                    revision:
   839                      description: Revision is an optional revision for a group of resources
   840                      type: string
   841                    type:
   842                      description: Type can contain prefix, such as Application/WordPress
   843                        or Service/Spanner
   844                      type: string
   845                  type: object
   846                resources:
   847                  description: Resources contains a list of resources that form the
   848                    resource group
   849                  items:
   850                    description: ObjMetadata organizes and stores the identifying information
   851                      for an object. This struct (as a string) is stored in a grouping
   852                      object to keep track of sets of applied objects.
   853                    properties:
   854                      group:
   855                        type: string
   856                      kind:
   857                        type: string
   858                      name:
   859                        type: string
   860                      namespace:
   861                        type: string
   862                    required:
   863                    - group
   864                    - kind
   865                    - name
   866                    - namespace
   867                    type: object
   868                  type: array
   869              type: object
   870            status:
   871              description: ResourceGroupStatus defines the observed state of ResourceGroup
   872              properties:
   873                conditions:
   874                  description: Conditions lists the conditions of the current status
   875                    for the group
   876                  items:
   877                    properties:
   878                      lastTransitionTime:
   879                        description: last time the condition transit from one status
   880                          to another
   881                        format: date-time
   882                        type: string
   883                      message:
   884                        description: human-readable message indicating details about
   885                          last transition
   886                        type: string
   887                      reason:
   888                        description: one-word CamelCase reason for the condition's last
   889                          transition
   890                        type: string
   891                      status:
   892                        description: Status of the condition
   893                        type: string
   894                      type:
   895                        description: Type of the condition
   896                        type: string
   897                    required:
   898                    - status
   899                    - type
   900                    type: object
   901                  type: array
   902                observedGeneration:
   903                  description: ObservedGeneration is the most recent generation observed.
   904                    It corresponds to the Object's generation, which is updated on mutation
   905                    by the API Server. Everytime the controller does a successful reconcile,
   906                    it sets ObservedGeneration to match ResourceGroup.metadata.generation.
   907                  format: int64
   908                  type: integer
   909                resourceStatuses:
   910                  description: ResourceStatuses lists the status for each resource in
   911                    the group
   912                  items:
   913                    description: ResourceStatus contains the status of a given resource
   914                      uniquely identified by its group, kind, name and namespace.
   915                    properties:
   916                      actuation:
   917                        description: actuation indicates whether actuation has been
   918                          performed yet and how it went.
   919                        type: string
   920                      conditions:
   921                        items:
   922                          properties:
   923                            lastTransitionTime:
   924                              description: last time the condition transit from one
   925                                status to another
   926                              format: date-time
   927                              type: string
   928                            message:
   929                              description: human-readable message indicating details
   930                                about last transition
   931                              type: string
   932                            reason:
   933                              description: one-word CamelCase reason for the condition's
   934                                last transition
   935                              type: string
   936                            status:
   937                              description: Status of the condition
   938                              type: string
   939                            type:
   940                              description: Type of the condition
   941                              type: string
   942                          required:
   943                          - status
   944                          - type
   945                          type: object
   946                        type: array
   947                      group:
   948                        type: string
   949                      kind:
   950                        type: string
   951                      name:
   952                        type: string
   953                      namespace:
   954                        type: string
   955                      reconcile:
   956                        description: reconcile indicates whether reconciliation has
   957                          been performed yet and how it went.
   958                        type: string
   959                      sourceHash:
   960                        type: string
   961                      status:
   962                        description: Status describes the status of a resource
   963                        type: string
   964                      strategy:
   965                        description: strategy indicates the method of actuation (apply
   966                          or delete) used or planned to be used.
   967                        type: string
   968                    required:
   969                    - group
   970                    - kind
   971                    - name
   972                    - namespace
   973                    - status
   974                    type: object
   975                  type: array
   976              required:
   977              - observedGeneration
   978              type: object
   979          type: object
   980      served: true
   981      storage: true
   982      subresources:
   983        status: {}
   984  status:
   985    acceptedNames:
   986      kind: ""
   987      plural: ""
   988    conditions: []
   989    storedVersions: []
   990  `