github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/lib/crd/storage.go (about)

     1  package crd
     2  
     3  import (
     4  	"fmt"
     5  
     6  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
     7  	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
     8  	"k8s.io/apimachinery/pkg/runtime"
     9  )
    10  
    11  // SafeStorageVersionUpgrade determines whether the new CRD spec includes all the storage versions of the existing on-cluster CRD.
    12  // For each stored version in the status of the CRD on the cluster (there will be at least one) - each version must exist in the spec of the new CRD that is being installed.
    13  // See https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definition-versioning/#upgrade-existing-objects-to-a-new-stored-version.
    14  func SafeStorageVersionUpgrade(existingCRD runtime.Object, newCRD runtime.Object) (bool, error) {
    15  	newSpecVersions, existingStatusVersions := getStoredVersions(existingCRD, newCRD)
    16  	if newSpecVersions == nil {
    17  		return true, fmt.Errorf("could not find any versions in the new CRD")
    18  	}
    19  	if existingStatusVersions == nil {
    20  		// every on-cluster CRD should have at least one stored version in its status
    21  		// in the case where there are no existing stored versions, checking against new versions is not relevant
    22  		return true, nil
    23  	}
    24  
    25  	for name := range existingStatusVersions {
    26  		if _, ok := newSpecVersions[name]; !ok {
    27  			// a storage version in the status of the old CRD is not present in the spec of the new CRD
    28  			// potential data loss of CRs without a storage migration - throw error and block the CRD upgrade
    29  			return false, fmt.Errorf("new CRD removes version %s that is listed as a stored version on the existing CRD", name)
    30  		}
    31  	}
    32  
    33  	return true, nil
    34  }
    35  
    36  // getStoredVersions returns the storage versions listed in the status of the old on-cluster CRD
    37  // and all the versions listed in the spec of the new CRD.
    38  func getStoredVersions(oldCRD runtime.Object, newCRD runtime.Object) (newSpecVersions map[string]struct{}, existingStatusVersions map[string]struct{}) {
    39  	existingStatusVersions = make(map[string]struct{})
    40  	newSpecVersions = make(map[string]struct{})
    41  
    42  	// find old storage versions by inspect the status field of the existing on-cluster CRD
    43  	switch crd := oldCRD.(type) {
    44  	case *apiextensionsv1.CustomResourceDefinition:
    45  		for _, version := range crd.Status.StoredVersions {
    46  			existingStatusVersions[version] = struct{}{}
    47  		}
    48  	case *apiextensionsv1beta1.CustomResourceDefinition:
    49  		for _, version := range crd.Status.StoredVersions {
    50  			existingStatusVersions[version] = struct{}{}
    51  		}
    52  	}
    53  
    54  	switch crd := newCRD.(type) {
    55  	case *apiextensionsv1.CustomResourceDefinition:
    56  		for _, version := range crd.Spec.Versions {
    57  			newSpecVersions[version.Name] = struct{}{}
    58  		}
    59  	case *apiextensionsv1beta1.CustomResourceDefinition:
    60  		if crd.Spec.Version != "" {
    61  			newSpecVersions[crd.Spec.Version] = struct{}{}
    62  		}
    63  		for _, version := range crd.Spec.Versions {
    64  			newSpecVersions[version.Name] = struct{}{}
    65  		}
    66  	}
    67  
    68  	return newSpecVersions, existingStatusVersions
    69  }