repo_description: Complete container management platform
repo_topics: cattle containers docker kubernetes orchestration rancher
code: // Package crds is used for installing rancher CRDs.
  package crds
  
  import (
  	"context"
  	"embed"
  	"fmt"
  	"path/filepath"
  	"time"
  
  	"github.com/rancher/rancher/pkg/fleet"
  	"github.com/rancher/wrangler/v3/pkg/crd"
  	"github.com/rancher/wrangler/v3/pkg/yaml"
  	apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  	clientv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
  	"k8s.io/apimachinery/pkg/labels"
  	"k8s.io/apimachinery/pkg/selection"
  )
  
  const (
  	baseDir = "."
  
  	// readyDuration time to wait for CRDs to be ready.
  	readyDuration = time.Minute * 1
  
  	k8sManagedByKey = "app.kubernetes.io/managed-by"
  	managerValue    = "rancher"
  	crdKind         = "CustomResourceDefinition"
  )
  
  var (
  	//go:embed yaml
  	crdFS embed.FS
  
  	errDuplicate = fmt.Errorf("duplicate CRD")
  )
  
  // EnsureRequired will ensure all required CRDs needed by Rancher based on the currently enabled features are installed and up to date.
  func EnsureRequired(ctx context.Context, crdClient clientv1.CustomResourceDefinitionInterface) error {
  	return Ensure(ctx, crdClient, RequiredCRDs())
  }
  
  // Ensure will ensure all given CRD names are installed and up to date.
  // Ensure looks for matching crd names inside the embedded directory 'yaml'.
  func Ensure(ctx context.Context, crdClient clientv1.CustomResourceDefinitionInterface, crdNames []string) error {
  	toCreateCRDs, err := getCRDs(crdNames)
  	if err != nil {
  		return fmt.Errorf("failed to get CRDs: %w", err)
  	}
  
  	ownedByRancher, err := labels.NewRequirement(k8sManagedByKey, selection.Equals, []string{managerValue})
  	if err != nil {
  		return fmt.Errorf("failed to create crd label selector: %w", err)
  	}
  	selector := labels.NewSelector().Add(*ownedByRancher)
  
  	err = crd.BatchCreateCRDs(ctx, crdClient, selector, readyDuration, toCreateCRDs)
  	if err != nil {
  		return fmt.Errorf("failed to create CRDs: %w", err)
  	}
  
  	return nil
  }
  
  // getCRDs finds all embedded CRDs that match the given list of CRDs. If a CRD is specified and not found an error is returned.
  func getCRDs(crdNames []string) ([]*apiextv1.CustomResourceDefinition, error) {
  	allCRDs, err := crdsFromDir(baseDir)
  	if err != nil {
  		return nil, err
  	}
  
  	capiMap := toMap(CAPICRDs())
  	bootstrapFleetMap := toMap(bootstrapFleet())
  
  	retCRDs := []*apiextv1.CustomResourceDefinition{}
  
  	for _, crdName := range crdNames {
  		// only return CRDs that are specified and have been migrated to the new CRD generation flow
  		if !MigratedResources[crdName] {
  			continue
  		}
  
  		crd, found := allCRDs[crdName]
  		if !found {
  			return nil, fmt.Errorf("CRD yaml '%s' not found in embedded file system", crdName)
  		}
  
  		if crd.Labels == nil {
  			crd.Labels = map[string]string{}
  		}
  
  		// dynamically add labels to capi crds since we do not generate them.
  		if capiMap[crd.Name] {
  			crd.Labels["auth.cattle.io/cluster-indexed"] = "true"
  		}
  
  		// Add managed by label
  		if bootstrapFleetMap[crd.Name] {
  			// Ensure labels/annotations are set so that helm will manage this
  			crd.Labels[k8sManagedByKey] = "Helm"
  			if crd.Annotations == nil {
  				crd.Annotations = map[string]string{}
  			}
  			crd.Annotations["meta.helm.sh/release-name"] = fleet.CRDChartName
  			crd.Annotations["meta.helm.sh/release-namespace"] = fleet.ReleaseNamespace
  		} else {
  			// since this CRD is installed by rancher add the manged by label
  			crd.Labels[k8sManagedByKey] = managerValue
  		}
  
  		retCRDs = append(retCRDs, crd)
  	}
  
  	return retCRDs, nil
  }
  
  // crdsFromDir recursively traverses the embedded yaml directory and find all CRD yamls.
  func crdsFromDir(dirName string) (map[string]*apiextv1.CustomResourceDefinition, error) {
  	// read all entries in the embedded directory
  	crdFiles, err := crdFS.ReadDir(dirName)
  	if err != nil {
  		return nil, fmt.Errorf("failed to read embedded dir '%s': %w", dirName, err)
  	}
  
  	allCRDs := map[string]*apiextv1.CustomResourceDefinition{}
  	for _, dirEntry := range crdFiles {
  		fullPath := filepath.Join(dirName, dirEntry.Name())
  		if dirEntry.IsDir() {
  			// if the entry is the dir recurse into that folder to get all crds
  			subCRDs, err := crdsFromDir(fullPath)
  			if err != nil {
  				return nil, err
  			}
  			for k, v := range subCRDs {
  				if _, ok := allCRDs[k]; ok {
  					return nil, fmt.Errorf("%w for '%s", errDuplicate, k)
  				}
  				allCRDs[k] = v
  			}
  			continue
  		}
  
  		// read the file and convert it to a crd object
  		file, err := crdFS.Open(fullPath)
  		if err != nil {
  			return nil, fmt.Errorf("failed to open embedded file '%s': %w", fullPath, err)
  		}
  		crdObjs, err := yaml.UnmarshalWithJSONDecoder[*apiextv1.CustomResourceDefinition](file)
  		if err != nil {
  			return nil, fmt.Errorf("failed to convert embedded file '%s' to yaml: %w", fullPath, err)
  		}
  		for _, crdObj := range crdObjs {
  			if crdObj.Kind != crdKind {
  				// if the yaml is not a CRD return an error
  				return nil, fmt.Errorf("decoded object is not '%s' instead found Kind='%s'", crdKind, crdObj.Kind)
  			}
  			if _, ok := allCRDs[crdObj.Name]; ok {
  				return nil, fmt.Errorf("%w for '%s", errDuplicate, crdObj.Name)
  			}
  			allCRDs[crdObj.Name] = crdObj
  		}
  	}
  	return allCRDs, nil
  }
  
  // toMap converts a list of slice to a map.
  func toMap(strings []string) map[string]bool {
  	retMap := make(map[string]bool, len(strings))
  	for _, name := range strings {
  		retMap[name] = true
  	}
  	return retMap
  }
  