Skip to content

Commit 7215bd6

Browse files
authored
Merge pull request #1416 from exdx/feat/v1-crds
feat: support v1 CRD objects in OLM
2 parents f3f46c8 + d7f5cfe commit 7215bd6

File tree

23 files changed

+1074
-597
lines changed

23 files changed

+1074
-597
lines changed

pkg/controller/operators/catalog/operator.go

Lines changed: 51 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ import (
1616
corev1 "k8s.io/api/core/v1"
1717
rbacv1 "k8s.io/api/rbac/v1"
1818
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
19-
v1beta1ext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
19+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2020
"k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
2121
extinf "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions"
22-
k8serrors "k8s.io/apimachinery/pkg/api/errors"
2322
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2423
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2524
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -53,7 +52,6 @@ import (
5352
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/scoped"
5453
sharedtime "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/time"
5554
"github.com/operator-framework/operator-lifecycle-manager/pkg/metrics"
56-
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
5755
)
5856

5957
const (
@@ -62,6 +60,7 @@ const (
6260
clusterRoleKind = "ClusterRole"
6361
clusterRoleBindingKind = "ClusterRoleBinding"
6462
configMapKind = "ConfigMap"
63+
csvKind = "ClusterServiceVersion"
6564
serviceAccountKind = "ServiceAccount"
6665
serviceKind = "Service"
6766
roleKind = "Role"
@@ -323,8 +322,8 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
323322
}
324323

325324
// Register CustomResourceDefinition QueueInformer
326-
crdInformer := extinf.NewSharedInformerFactory(op.opClient.ApiextensionsV1beta1Interface(), resyncPeriod()).Apiextensions().V1beta1().CustomResourceDefinitions()
327-
op.lister.APIExtensionsV1beta1().RegisterCustomResourceDefinitionLister(crdInformer.Lister())
325+
crdInformer := extinf.NewSharedInformerFactory(op.opClient.ApiextensionsInterface(), resyncPeriod()).Apiextensions().V1().CustomResourceDefinitions()
326+
op.lister.APIExtensionsV1().RegisterCustomResourceDefinitionLister(crdInformer.Lister())
328327
crdQueueInformer, err := queueinformer.NewQueueInformer(
329328
ctx,
330329
queueinformer.WithLogger(op.logger),
@@ -1300,22 +1299,18 @@ func (o *Operator) ResolvePlan(plan *v1alpha1.InstallPlan) error {
13001299
return nil
13011300
}
13021301

1303-
func getCRDVersionsMap(crd *v1beta1ext.CustomResourceDefinition) map[string]struct{} {
1302+
func GetCRDV1VersionsMap(crd *apiextensionsv1.CustomResourceDefinition) map[string]struct{} {
13041303
versionsMap := map[string]struct{}{}
13051304

13061305
for _, version := range crd.Spec.Versions {
13071306
versionsMap[version.Name] = struct{}{}
13081307
}
1309-
if crd.Spec.Version != "" {
1310-
versionsMap[crd.Spec.Version] = struct{}{}
1311-
}
1312-
13131308
return versionsMap
13141309
}
13151310

13161311
// Ensure all existing served versions are present in new CRD
1317-
func ensureCRDVersions(oldCRD *v1beta1ext.CustomResourceDefinition, newCRD *v1beta1ext.CustomResourceDefinition) error {
1318-
newCRDVersions := getCRDVersionsMap(newCRD)
1312+
func EnsureCRDVersions(oldCRD *apiextensionsv1.CustomResourceDefinition, newCRD *apiextensionsv1.CustomResourceDefinition) error {
1313+
newCRDVersions := GetCRDV1VersionsMap(newCRD)
13191314

13201315
for _, oldVersion := range oldCRD.Spec.Versions {
13211316
if oldVersion.Served {
@@ -1325,49 +1320,47 @@ func ensureCRDVersions(oldCRD *v1beta1ext.CustomResourceDefinition, newCRD *v1be
13251320
}
13261321
}
13271322
}
1328-
if oldCRD.Spec.Version != "" {
1329-
_, ok := newCRDVersions[oldCRD.Spec.Version]
1330-
if !ok {
1331-
return fmt.Errorf("New CRD (%s) must contain existing version (%s)", oldCRD.Name, oldCRD.Spec.Version)
1332-
}
1333-
}
13341323
return nil
13351324
}
13361325

13371326
// Validate all existing served versions against new CRD's validation (if changed)
1338-
func (o *Operator) validateCustomResourceDefinition(oldCRD *v1beta1ext.CustomResourceDefinition, newCRD *v1beta1ext.CustomResourceDefinition) error {
1339-
o.logger.Debugf("Comparing %#v to %#v", oldCRD.Spec.Validation, newCRD.Spec.Validation)
1327+
func validateV1CRDCompatibility(dynamicClient dynamic.Interface, oldCRD *apiextensionsv1.CustomResourceDefinition, newCRD *apiextensionsv1.CustomResourceDefinition) error {
1328+
logrus.Debugf("Comparing %#v to %#v", oldCRD.Spec.Versions, newCRD.Spec.Versions)
1329+
13401330
// If validation schema is unchanged, return right away
1341-
if reflect.DeepEqual(oldCRD.Spec.Validation, newCRD.Spec.Validation) {
1342-
return nil
1331+
newestSchema := newCRD.Spec.Versions[len(newCRD.Spec.Versions)-1].Schema
1332+
for i, oldVersion := range oldCRD.Spec.Versions {
1333+
if !reflect.DeepEqual(oldVersion.Schema, newestSchema) {
1334+
break
1335+
}
1336+
if i == len(oldCRD.Spec.Versions)-1 {
1337+
// we are on the last iteration
1338+
// schema has not changed between versions at this point.
1339+
return nil
1340+
}
13431341
}
1342+
13441343
convertedCRD := &apiextensions.CustomResourceDefinition{}
1345-
if err := v1beta1ext.Convert_v1beta1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(newCRD, convertedCRD, nil); err != nil {
1344+
if err := apiextensionsv1.Convert_v1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(newCRD, convertedCRD, nil); err != nil {
13461345
return err
13471346
}
13481347
for _, version := range oldCRD.Spec.Versions {
13491348
if !version.Served {
13501349
gvr := schema.GroupVersionResource{Group: oldCRD.Spec.Group, Version: version.Name, Resource: oldCRD.Spec.Names.Plural}
1351-
err := o.validateExistingCRs(gvr, convertedCRD)
1350+
err := validateExistingCRs(dynamicClient, gvr, convertedCRD)
13521351
if err != nil {
13531352
return err
13541353
}
13551354
}
13561355
}
13571356

1358-
if oldCRD.Spec.Version != "" {
1359-
gvr := schema.GroupVersionResource{Group: oldCRD.Spec.Group, Version: oldCRD.Spec.Version, Resource: oldCRD.Spec.Names.Plural}
1360-
err := o.validateExistingCRs(gvr, convertedCRD)
1361-
if err != nil {
1362-
return err
1363-
}
1364-
}
1365-
o.logger.Debugf("Successfully validated CRD %s\n", newCRD.Name)
1357+
logrus.Debugf("Successfully validated CRD %s\n", newCRD.Name)
13661358
return nil
13671359
}
13681360

1369-
func (o *Operator) validateExistingCRs(gvr schema.GroupVersionResource, newCRD *apiextensions.CustomResourceDefinition) error {
1370-
crList, err := o.dynamicClient.Resource(gvr).List(metav1.ListOptions{})
1361+
func validateExistingCRs(dynamicClient dynamic.Interface, gvr schema.GroupVersionResource, newCRD *apiextensions.CustomResourceDefinition) error {
1362+
// make dynamic client
1363+
crList, err := dynamicClient.Resource(gvr).List(metav1.ListOptions{})
13711364
if err != nil {
13721365
return fmt.Errorf("error listing resources in GroupVersionResource %#v: %s", gvr, err)
13731366
}
@@ -1389,14 +1382,14 @@ func (o *Operator) validateExistingCRs(gvr schema.GroupVersionResource, newCRD *
13891382
// The function may not always succeed as storedVersions requires at least one
13901383
// version. If there is only stored version, it won't be removed until a new
13911384
// stored version is added.
1392-
func removeDeprecatedStoredVersions(oldCRD *v1beta1ext.CustomResourceDefinition, newCRD *v1beta1ext.CustomResourceDefinition) []string {
1385+
func removeDeprecatedV1StoredVersions(oldCRD *apiextensionsv1.CustomResourceDefinition, newCRD *apiextensionsv1.CustomResourceDefinition) []string {
13931386
// StoredVersions requires to have at least one version.
13941387
if len(oldCRD.Status.StoredVersions) <= 1 {
13951388
return nil
13961389
}
13971390

13981391
newStoredVersions := []string{}
1399-
newCRDVersions := getCRDVersionsMap(newCRD)
1392+
newCRDVersions := GetCRDV1VersionsMap(newCRD)
14001393
for _, v := range oldCRD.Status.StoredVersions {
14011394
_, ok := newCRDVersions[v]
14021395
if ok {
@@ -1437,113 +1430,35 @@ func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error {
14371430
}
14381431

14391432
ensurer := newStepEnsurer(kubeclient, crclient, dynamicClient)
1433+
b := newBuilder(kubeclient, dynamicClient, o.csvProvidedAPIsIndexer)
14401434

14411435
for i, step := range plan.Status.Plan {
1442-
switch step.Status {
1443-
case v1alpha1.StepStatusPresent, v1alpha1.StepStatusCreated:
1436+
doStep := true
1437+
s, err := b.create(step)
1438+
if err != nil {
1439+
if _, ok := err.(notSupportedStepperErr); ok {
1440+
// stepper not implemented for this type yet
1441+
// stepper currently only implemented for CRD types
1442+
doStep = false
1443+
} else {
1444+
return err
1445+
}
1446+
}
1447+
if doStep {
1448+
status, err := s.Status()
1449+
if err != nil {
1450+
return err
1451+
}
1452+
plan.Status.Plan[i].Status = status
14441453
continue
1445-
case v1alpha1.StepStatusWaitingForAPI:
1446-
switch step.Resource.Kind {
1447-
case crdKind:
1448-
crd, err := o.opClient.ApiextensionsV1beta1Interface().ApiextensionsV1beta1().CustomResourceDefinitions().Get(step.Resource.Name, metav1.GetOptions{})
1449-
if err != nil {
1450-
if k8serrors.IsNotFound(err) {
1451-
plan.Status.Plan[i].Status = v1alpha1.StepStatusNotPresent
1452-
} else {
1453-
return errorwrap.Wrapf(err, "error finding the %s CRD", crd.Name)
1454-
}
1455-
continue
1456-
}
1457-
1458-
established, namesAccepted := false, false
1459-
for _, cdt := range crd.Status.Conditions {
1460-
switch cdt.Type {
1461-
case v1beta1.Established:
1462-
if cdt.Status == v1beta1.ConditionTrue {
1463-
established = true
1464-
}
1465-
case v1beta1.NamesAccepted:
1466-
if cdt.Status == v1beta1.ConditionTrue {
1467-
namesAccepted = true
1468-
}
1469-
}
1470-
}
1454+
}
14711455

1472-
if established && namesAccepted {
1473-
plan.Status.Plan[i].Status = v1alpha1.StepStatusCreated
1474-
}
1475-
continue
1476-
}
1456+
switch step.Status {
1457+
case v1alpha1.StepStatusPresent, v1alpha1.StepStatusCreated, v1alpha1.StepStatusWaitingForAPI:
1458+
continue
14771459
case v1alpha1.StepStatusUnknown, v1alpha1.StepStatusNotPresent:
14781460
o.logger.WithFields(logrus.Fields{"kind": step.Resource.Kind, "name": step.Resource.Name}).Debug("execute resource")
14791461
switch step.Resource.Kind {
1480-
case crdKind:
1481-
// Marshal the manifest into a CRD instance.
1482-
var crd v1beta1ext.CustomResourceDefinition
1483-
err := json.Unmarshal([]byte(step.Resource.Manifest), &crd)
1484-
if err != nil {
1485-
return errorwrap.Wrapf(err, "error parsing step manifest: %s", step.Resource.Name)
1486-
}
1487-
1488-
// TODO: check that names are accepted
1489-
// Attempt to create the CRD.
1490-
_, err = o.opClient.ApiextensionsV1beta1Interface().ApiextensionsV1beta1().CustomResourceDefinitions().Create(&crd)
1491-
if k8serrors.IsAlreadyExists(err) {
1492-
currentCRD, _ := o.lister.APIExtensionsV1beta1().CustomResourceDefinitionLister().Get(crd.GetName())
1493-
// Compare 2 CRDs to see if it needs to be updatetd
1494-
if !(reflect.DeepEqual(crd.Spec.Version, currentCRD.Spec.Version) &&
1495-
reflect.DeepEqual(crd.Spec.Versions, currentCRD.Spec.Versions) &&
1496-
reflect.DeepEqual(crd.Spec.Validation, currentCRD.Spec.Validation)) {
1497-
// Verify CRD ownership, only attempt to update if
1498-
// CRD has only one owner
1499-
// Example: provided=database.coreos.com/v1alpha1/EtcdCluster
1500-
matchedCSV, err := index.CRDProviderNames(o.csvProvidedAPIsIndexer, crd)
1501-
if err != nil {
1502-
return errorwrap.Wrapf(err, "error find matched CSV: %s", step.Resource.Name)
1503-
}
1504-
crd.SetResourceVersion(currentCRD.GetResourceVersion())
1505-
if len(matchedCSV) == 1 {
1506-
o.logger.Debugf("Found one owner for CRD %v", crd)
1507-
} else if len(matchedCSV) > 1 {
1508-
o.logger.Debugf("Found multiple owners for CRD %v", crd)
1509-
1510-
err := ensureCRDVersions(currentCRD, &crd)
1511-
if err != nil {
1512-
return errorwrap.Wrapf(err, "error missing existing CRD version(s) in new CRD: %s", step.Resource.Name)
1513-
}
1514-
1515-
if err = o.validateCustomResourceDefinition(currentCRD, &crd); err != nil {
1516-
return errorwrap.Wrapf(err, "error validating existing CRs agains new CRD's schema: %s", step.Resource.Name)
1517-
}
1518-
}
1519-
// Remove deprecated version in CRD storedVersions
1520-
storeVersions := removeDeprecatedStoredVersions(currentCRD, &crd)
1521-
if storeVersions != nil {
1522-
currentCRD.Status.StoredVersions = storeVersions
1523-
resultCRD, err := o.opClient.ApiextensionsV1beta1Interface().ApiextensionsV1beta1().CustomResourceDefinitions().UpdateStatus(currentCRD)
1524-
if err != nil {
1525-
return errorwrap.Wrapf(err, "error updating CRD's status: %s", step.Resource.Name)
1526-
}
1527-
crd.SetResourceVersion(resultCRD.GetResourceVersion())
1528-
}
1529-
// Update CRD to new version
1530-
_, err = o.opClient.ApiextensionsV1beta1Interface().ApiextensionsV1beta1().CustomResourceDefinitions().Update(&crd)
1531-
if err != nil {
1532-
return errorwrap.Wrapf(err, "error updating CRD: %s", step.Resource.Name)
1533-
}
1534-
}
1535-
// If it already existed, mark the step as Present.
1536-
plan.Status.Plan[i].Status = v1alpha1.StepStatusPresent
1537-
continue
1538-
} else if err != nil {
1539-
// Unexpected error creating the CRD.
1540-
return err
1541-
} else {
1542-
// If no error occured, make sure to wait for the API to become available.
1543-
plan.Status.Plan[i].Status = v1alpha1.StepStatusWaitingForAPI
1544-
continue
1545-
}
1546-
15471462
case v1alpha1.ClusterServiceVersionKind:
15481463
// Marshal the manifest into a CSV instance.
15491464
var csv v1alpha1.ClusterServiceVersion

0 commit comments

Comments
 (0)