Skip to content

Commit 27317f6

Browse files
committed
fix: support creating v1beta CRDs to avoid data loss during conversion to v1. Due to data loss during client side conversions OLM will support two different paths for v1 and v1beta1 CRDs.
1 parent ed17b12 commit 27317f6

File tree

10 files changed

+367
-121
lines changed

10 files changed

+367
-121
lines changed

cmd/olm/cleanup_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ func TestCleanupOwnerReferences(t *testing.T) {
478478
require.Equal(t, tt.expected.err, cleanupOwnerReferences(c, crc))
479479

480480
listOpts := metav1.ListOptions{}
481-
csvs, err := crc.OperatorsV1alpha1().ClusterServiceVersions(metav1.NamespaceAll).List(listOpts)
481+
csvs, err := crc.OperatorsV1alpha1().ClusterServiceVersions(metav1.NamespaceAll).List(context.TODO(), listOpts)
482482
require.NoError(t, err)
483483
require.ElementsMatch(t, tt.expected.csvs, csvs.Items)
484484

pkg/controller/operators/catalog/operator.go

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
rbacv1 "k8s.io/api/rbac/v1"
1818
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
1919
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
20+
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
2021
"k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
2122
extinf "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions"
2223
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -1315,7 +1316,21 @@ func GetCRDV1VersionsMap(crd *apiextensionsv1.CustomResourceDefinition) map[stri
13151316
}
13161317

13171318
// Ensure all existing served versions are present in new CRD
1318-
func EnsureCRDVersions(oldCRD *apiextensionsv1.CustomResourceDefinition, newCRD *apiextensionsv1.CustomResourceDefinition) error {
1319+
func GetCRDV1Beta1VersionsMap(crd *apiextensionsv1beta1.CustomResourceDefinition) map[string]struct{} {
1320+
versionsMap := map[string]struct{}{}
1321+
1322+
for _, version := range crd.Spec.Versions {
1323+
versionsMap[version.Name] = struct{}{}
1324+
}
1325+
if crd.Spec.Version != "" {
1326+
versionsMap[crd.Spec.Version] = struct{}{}
1327+
}
1328+
1329+
return versionsMap
1330+
}
1331+
1332+
// Ensure all existing served versions are present in new CRD
1333+
func EnsureV1CRDVersions(oldCRD *apiextensionsv1.CustomResourceDefinition, newCRD *apiextensionsv1.CustomResourceDefinition) error {
13191334
newCRDVersions := GetCRDV1VersionsMap(newCRD)
13201335

13211336
for _, oldVersion := range oldCRD.Spec.Versions {
@@ -1329,6 +1344,27 @@ func EnsureCRDVersions(oldCRD *apiextensionsv1.CustomResourceDefinition, newCRD
13291344
return nil
13301345
}
13311346

1347+
// Ensure all existing served versions are present in new CRD
1348+
func EnsureV1Beta1CRDVersions(oldCRD *apiextensionsv1beta1.CustomResourceDefinition, newCRD *apiextensionsv1beta1.CustomResourceDefinition) error {
1349+
newCRDVersions := GetCRDV1Beta1VersionsMap(newCRD)
1350+
1351+
for _, oldVersion := range oldCRD.Spec.Versions {
1352+
if oldVersion.Served {
1353+
_, ok := newCRDVersions[oldVersion.Name]
1354+
if !ok {
1355+
return fmt.Errorf("New CRD (%s) must contain existing served versions (%s)", oldCRD.Name, oldVersion.Name)
1356+
}
1357+
}
1358+
}
1359+
if oldCRD.Spec.Version != "" {
1360+
_, ok := newCRDVersions[oldCRD.Spec.Version]
1361+
if !ok {
1362+
return fmt.Errorf("New CRD (%s) must contain existing version (%s)", oldCRD.Name, oldCRD.Spec.Version)
1363+
}
1364+
}
1365+
return nil
1366+
}
1367+
13321368
// Validate all existing served versions against new CRD's validation (if changed)
13331369
func validateV1CRDCompatibility(dynamicClient dynamic.Interface, oldCRD *apiextensionsv1.CustomResourceDefinition, newCRD *apiextensionsv1.CustomResourceDefinition) error {
13341370
logrus.Debugf("Comparing %#v to %#v", oldCRD.Spec.Versions, newCRD.Spec.Versions)
@@ -1364,6 +1400,38 @@ func validateV1CRDCompatibility(dynamicClient dynamic.Interface, oldCRD *apiexte
13641400
return nil
13651401
}
13661402

1403+
// Validate all existing served versions against new CRD's validation (if changed)
1404+
func validateV1Beta1CRDCompatibility(dynamicClient dynamic.Interface, oldCRD *apiextensionsv1beta1.CustomResourceDefinition, newCRD *apiextensionsv1beta1.CustomResourceDefinition) error {
1405+
logrus.Debugf("Comparing %#v to %#v", oldCRD.Spec.Validation, newCRD.Spec.Validation)
1406+
// If validation schema is unchanged, return right away
1407+
if reflect.DeepEqual(oldCRD.Spec.Validation, newCRD.Spec.Validation) {
1408+
return nil
1409+
}
1410+
convertedCRD := &apiextensions.CustomResourceDefinition{}
1411+
if err := apiextensionsv1beta1.Convert_v1beta1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(newCRD, convertedCRD, nil); err != nil {
1412+
return err
1413+
}
1414+
for _, version := range oldCRD.Spec.Versions {
1415+
if !version.Served {
1416+
gvr := schema.GroupVersionResource{Group: oldCRD.Spec.Group, Version: version.Name, Resource: oldCRD.Spec.Names.Plural}
1417+
err := validateExistingCRs(dynamicClient, gvr, convertedCRD)
1418+
if err != nil {
1419+
return err
1420+
}
1421+
}
1422+
}
1423+
1424+
if oldCRD.Spec.Version != "" {
1425+
gvr := schema.GroupVersionResource{Group: oldCRD.Spec.Group, Version: oldCRD.Spec.Version, Resource: oldCRD.Spec.Names.Plural}
1426+
err := validateExistingCRs(dynamicClient, gvr, convertedCRD)
1427+
if err != nil {
1428+
return err
1429+
}
1430+
}
1431+
logrus.Debugf("Successfully validated CRD %s\n", newCRD.Name)
1432+
return nil
1433+
}
1434+
13671435
func validateExistingCRs(dynamicClient dynamic.Interface, gvr schema.GroupVersionResource, newCRD *apiextensions.CustomResourceDefinition) error {
13681436
// make dynamic client
13691437
crList, err := dynamicClient.Resource(gvr).List(context.TODO(), metav1.ListOptions{})
@@ -1410,6 +1478,28 @@ func removeDeprecatedV1StoredVersions(oldCRD *apiextensionsv1.CustomResourceDefi
14101478
}
14111479
}
14121480

1481+
func removeDeprecatedV1Beta1StoredVersions(oldCRD *apiextensionsv1beta1.CustomResourceDefinition, newCRD *apiextensionsv1beta1.CustomResourceDefinition) []string {
1482+
// StoredVersions requires to have at least one version.
1483+
if len(oldCRD.Status.StoredVersions) <= 1 {
1484+
return nil
1485+
}
1486+
1487+
newStoredVersions := []string{}
1488+
newCRDVersions := GetCRDV1Beta1VersionsMap(newCRD)
1489+
for _, v := range oldCRD.Status.StoredVersions {
1490+
_, ok := newCRDVersions[v]
1491+
if ok {
1492+
newStoredVersions = append(newStoredVersions, v)
1493+
}
1494+
}
1495+
1496+
if len(newStoredVersions) < 1 {
1497+
return nil
1498+
} else {
1499+
return newStoredVersions
1500+
}
1501+
}
1502+
14131503
// ExecutePlan applies a planned InstallPlan to a namespace.
14141504
func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error {
14151505
if plan.Status.Phase != v1alpha1.InstallPlanPhaseInstalling {
@@ -1436,7 +1526,7 @@ func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error {
14361526
}
14371527

14381528
ensurer := newStepEnsurer(kubeclient, crclient, dynamicClient)
1439-
b := newBuilder(kubeclient, dynamicClient, o.csvProvidedAPIsIndexer)
1529+
b := newBuilder(kubeclient, dynamicClient, o.csvProvidedAPIsIndexer, o.logger)
14401530

14411531
for i, step := range plan.Status.Plan {
14421532
doStep := true

pkg/controller/operators/catalog/operator_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ func TestEnsureV1CRDVersions(t *testing.T) {
232232
}
233233

234234
for _, tt := range tests {
235-
err := EnsureCRDVersions(&tt.oldCRD, &tt.newCRD)
235+
err := EnsureV1CRDVersions(&tt.oldCRD, &tt.newCRD)
236236
if tt.expectedFailure {
237237
require.Error(t, err)
238238
}

0 commit comments

Comments
 (0)