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 }