From 8c8b373f7471dcb9c010584967528126bc3f0ce9 Mon Sep 17 00:00:00 2001 From: Jeff Peeler Date: Wed, 9 Jan 2019 16:35:33 -0500 Subject: [PATCH 1/4] WIP: add new minimum version field for kube Check new CSV field against server version reported from discovery and fail requirement check if version is earlier than necessary. --- .../v1alpha1/clusterserviceversion_types.go | 2 + pkg/controller/operators/olm/requirements.go | 50 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go b/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go index 020b74a531d..313b26458c4 100644 --- a/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go +++ b/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go @@ -133,6 +133,7 @@ type ClusterServiceVersionSpec struct { CustomResourceDefinitions CustomResourceDefinitions `json:"customresourcedefinitions,omitempty"` APIServiceDefinitions APIServiceDefinitions `json:"apiservicedefinitions,omitempty"` NativeAPIs []metav1.GroupVersionKind `json:"nativeAPIs,omitempty"` + MinKubeVersion string `json:"minKubeVersion,omitempty"` DisplayName string `json:"displayName"` Description string `json:"description,omitempty"` Keywords []string `json:"keywords,omitempty"` @@ -478,3 +479,4 @@ func (csv ClusterServiceVersion) GetOwnedAPIServiceDescriptions() []APIServiceDe return descs } + diff --git a/pkg/controller/operators/olm/requirements.go b/pkg/controller/operators/olm/requirements.go index a4e7b982a13..e70bf657046 100644 --- a/pkg/controller/operators/olm/requirements.go +++ b/pkg/controller/operators/olm/requirements.go @@ -3,6 +3,7 @@ package olm import ( "encoding/json" "fmt" + "strconv" "github.com/sirupsen/logrus" @@ -11,11 +12,13 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/coreos/go-semver/semver" ) func (a *Operator) requirementStatus(strategyDetailsDeployment *install.StrategyDetailsDeployment, crdDescs []v1alpha1.CRDDescription, ownedAPIServiceDescs []v1alpha1.APIServiceDescription, requiredAPIServiceDescs []v1alpha1.APIServiceDescription, - requiredNativeAPIs []metav1.GroupVersionKind) (met bool, statuses []v1alpha1.RequirementStatus) { + requiredNativeAPIs []metav1.GroupVersionKind, + minKubeVersion string) (met bool, statuses []v1alpha1.RequirementStatus) { met = true // Check for CRDs @@ -177,6 +180,49 @@ func (a *Operator) requirementStatus(strategyDetailsDeployment *install.Strategy } } + status := v1alpha1.RequirementStatus{ + Group: "apiextensions.k8s.io", + Version: "v1beta1", + Kind: "CustomResourceDefinition", + // TODO: refactor? + Name: "TODO: csv name, needs refactoring to get name", + } + + serverVersionInfo, err := a.client.Discovery().ServerVersion() + if err != nil { + // TODO: this status may be wrong + status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied + status.Message = "Server version discovery error" + met = false + statuses = append(statuses, status) + } + + // copy necessary fields into comparable for semver + majorInt, err := strconv.ParseInt(serverVersionInfo.Major, 10, 64) + minorInt, err := strconv.ParseInt(serverVersionInfo.Minor, 10, 64) + + serverVersionComparable := semver.Version{ + Major: majorInt, + Minor: minorInt, + } + + csvVersionInfo, err := semver.NewVersion(minKubeVersion) + if err != nil { + // TODO: this status may be wrong + status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied + status.Message = "CSV version parsing error" + met = false + statuses = append(statuses, status) + } + + if csvVersionInfo.Compare(serverVersionComparable) < 0 { + // TODO: this status may be wrong + status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied + status.Message = "CSV version requirement not met" + met = false + statuses = append(statuses, status) + } + return } @@ -292,7 +338,7 @@ func (a *Operator) requirementAndPermissionStatus(csv *v1alpha1.ClusterServiceVe return false, nil, fmt.Errorf("could not cast install strategy as type %T", strategyDetailsDeployment) } - reqMet, reqStatuses := a.requirementStatus(strategyDetailsDeployment, csv.GetAllCRDDescriptions(), csv.GetOwnedAPIServiceDescriptions(), csv.GetRequiredAPIServiceDescriptions(), csv.Spec.NativeAPIs) + reqMet, reqStatuses := a.requirementStatus(strategyDetailsDeployment, csv.GetAllCRDDescriptions(), csv.GetOwnedAPIServiceDescriptions(), csv.GetRequiredAPIServiceDescriptions(), csv.Spec.NativeAPIs, csv.Spec.MinKubeVersion) rbacLister := a.lister.RbacV1() roleLister := rbacLister.RoleLister() From e65ff0f2b490a3d142250a324dd3d932ccc77f38 Mon Sep 17 00:00:00 2001 From: Vu Dinh Date: Thu, 10 Jan 2019 16:17:27 -0500 Subject: [PATCH 2/4] Add minimum kube version to CSV & check it against server version CSV must contain minimum kube version under spec.minKubeVersion. OLM will retrieve that info from CSV and check it against server version to determine its capability. If not compatible, OLM will not install operator(s). Signed-off-by: Vu Dinh --- Documentation/design/building-your-csv.md | 10 +- .../0000_30_02-clusterserviceversion.crd.yaml | 6 +- .../_packageserver.clusterserviceversion.yaml | 1 + deploy/chart/values.yaml | 1 + .../0000_30_02-clusterserviceversion.crd.yaml | 6 +- pkg/controller/operators/olm/operator_test.go | 93 +++++++++++---- pkg/controller/operators/olm/requirements.go | 111 ++++++++++-------- .../operators/olm/requirements_test.go | 8 ++ test/e2e/csv_e2e_test.go | 73 ++++++++++++ 9 files changed, 232 insertions(+), 77 deletions(-) diff --git a/Documentation/design/building-your-csv.md b/Documentation/design/building-your-csv.md index 6346b7f4c8b..646e03f8a8e 100644 --- a/Documentation/design/building-your-csv.md +++ b/Documentation/design/building-your-csv.md @@ -44,7 +44,7 @@ It’s common for your Operator to use multiple CRDs to link together concepts, **Name**: The full name of your CRD -The next two sections require more explanation. +The next two sections require more explanation. **Resources**: Your CRDs will own one or more types of Kubernetes objects. These are listed in the resources section to inform your end-users of the objects they might need to troubleshoot or how to connect to the application, such as the Service or Ingress rule that exposes a database. @@ -178,7 +178,7 @@ An APIService is uniquely identified by the group-version it provides and can be **Kind**: A kind that the APIService is expected to provide. -**DeploymentName**: +**DeploymentName**: Name of the deployment defined by your CSV that corresponds to your APIService (required for owned APIServices). During the CSV pending phase, the OLM Operator will search your CSV's InstallStrategy for a deployment spec with a matching name, and if not found, will not transition the CSV to the install ready phase. **Resources**: @@ -190,8 +190,8 @@ It’s recommended to only list out the objects that are important to a human, n Essentially the same as for owned CRDs. ### APIService Resource Creation -The Lifecycle Manage is responsible for creating or replacing the Service and APIService resources for each unique owned APIService. -* Service pod selectors are copied from the CSV deployment matching the APIServiceDescription's DeploymentName. +The Lifecycle Manage is responsible for creating or replacing the Service and APIService resources for each unique owned APIService. +* Service pod selectors are copied from the CSV deployment matching the APIServiceDescription's DeploymentName. * A new CA key/cert pair is generated for for each installation and the base64 encoded CA bundle is embedded in the respective APIService resource. ### APIService Serving Certs @@ -232,6 +232,8 @@ The metadata section contains general metadata around the name, version and othe **Description**: A markdown blob that describes the Operator. Important information to include: features, limitations and common use-cases for the Operator. If your Operator manages different types of installs, eg. standalone vs clustered, it is useful to give an overview of how each differs from each other, or which ones are supported for production use. +**MinKubeVersion**: A minimum version of Kubernetes that server is supposed to have so operator(s) can be deployed. + **Labels** (optional): Any key/value pairs used to organize and categorize this CSV object. **Selectors** (optional): A label selector to identify related resources. Set this to select on current labels applied to this CSV object (if applicable). diff --git a/deploy/chart/templates/0000_30_02-clusterserviceversion.crd.yaml b/deploy/chart/templates/0000_30_02-clusterserviceversion.crd.yaml index 22568d41aad..e3142c168bc 100644 --- a/deploy/chart/templates/0000_30_02-clusterserviceversion.crd.yaml +++ b/deploy/chart/templates/0000_30_02-clusterserviceversion.crd.yaml @@ -60,6 +60,10 @@ spec: type: string description: Human readable description of what the application does + minKubeVersion: + type: string + description: Minimum kubernetes version requirement on the server to deploy operator + keywords: type: array description: List of keywords which will be used to discover and categorize app types @@ -198,7 +202,7 @@ spec: description: Version of the API resource kind: type: string - description: Kind of the API resource + description: Kind of the API resource apiservicedefinitions: type: object properties: diff --git a/deploy/chart/templates/_packageserver.clusterserviceversion.yaml b/deploy/chart/templates/_packageserver.clusterserviceversion.yaml index dc63a7626b6..cadb8c277ca 100644 --- a/deploy/chart/templates/_packageserver.clusterserviceversion.yaml +++ b/deploy/chart/templates/_packageserver.clusterserviceversion.yaml @@ -7,6 +7,7 @@ spec: displayName: Package Server description: Represents an Operator package that is available from a given CatalogSource which will resolve to a ClusterServiceVersion. + minKubeVersion: {{ .Values.minKubeVersion }} keywords: ['packagemanifests', 'olm', 'packages'] maintainers: - name: Red Hat diff --git a/deploy/chart/values.yaml b/deploy/chart/values.yaml index a6c9f2e1167..68a2b0a8c27 100644 --- a/deploy/chart/values.yaml +++ b/deploy/chart/values.yaml @@ -2,6 +2,7 @@ rbacApiVersion: rbac.authorization.k8s.io namespace: operator-lifecycle-manager catalog_namespace: operator-lifecycle-manager operator_namespace: operators +minKubeVersion: 1.11 imagestream: false debug: false olm: diff --git a/manifests/0000_30_02-clusterserviceversion.crd.yaml b/manifests/0000_30_02-clusterserviceversion.crd.yaml index 55ec9629967..ff3e6dac0d7 100644 --- a/manifests/0000_30_02-clusterserviceversion.crd.yaml +++ b/manifests/0000_30_02-clusterserviceversion.crd.yaml @@ -62,6 +62,10 @@ spec: type: string description: Human readable description of what the application does + minKubeVersion: + type: string + description: Minimum kubernetes version requirement on the server to deploy operator + keywords: type: array description: List of keywords which will be used to discover and categorize app types @@ -200,7 +204,7 @@ spec: description: Version of the API resource kind: type: string - description: Kind of the API resource + description: Kind of the API resource apiservicedefinitions: type: object properties: diff --git a/pkg/controller/operators/olm/operator_test.go b/pkg/controller/operators/olm/operator_test.go index 697201359a4..1f804be4eb9 100644 --- a/pkg/controller/operators/olm/operator_test.go +++ b/pkg/controller/operators/olm/operator_test.go @@ -458,7 +458,7 @@ func installStrategy(deploymentName string, permissions []install.StrategyDeploy } func csv( - name, namespace, replaces string, + name, namespace, minKubeVersion, replaces string, installStrategy v1alpha1.NamedInstallStrategy, owned, required []*v1beta1.CustomResourceDefinition, phase v1alpha1.ClusterServiceVersionPhase, @@ -483,6 +483,7 @@ func csv( Namespace: namespace, }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: minKubeVersion, Replaces: replaces, InstallStrategy: installStrategy, InstallModes: []v1alpha1.InstallMode{ @@ -729,6 +730,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -750,6 +752,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -771,6 +774,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -795,6 +799,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, @@ -840,6 +845,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -865,6 +871,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -889,6 +896,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -914,6 +922,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -939,6 +948,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("b1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -966,6 +976,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -974,6 +985,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, + "0.0", "", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1005,6 +1017,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -1013,6 +1026,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), apis("a1.v1.a1Kind"), nil), metav1.NewTime(time.Now().Add(24*time.Hour)), metav1.NewTime(time.Now())), withAPIServices(csvWithAnnotations(csv("csv2", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -1080,6 +1094,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1104,6 +1119,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1128,6 +1144,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1152,6 +1169,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1176,6 +1194,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -1198,6 +1217,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1222,6 +1242,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1246,6 +1267,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1314,6 +1336,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1382,6 +1405,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1450,6 +1474,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1518,6 +1543,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1586,6 +1612,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1654,6 +1681,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1722,6 +1750,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1790,6 +1819,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withConditionReason(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1877,6 +1907,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withInstallModes(withConditionReason(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1992,6 +2023,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2016,6 +2048,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2043,6 +2076,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2064,6 +2098,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2072,6 +2107,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, + "0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2100,6 +2136,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2108,6 +2145,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, + "0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2137,6 +2175,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2145,6 +2184,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, + "0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2175,6 +2215,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv3", namespace, + "0.0", "csv2", installStrategy("csv3-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2183,6 +2224,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2191,6 +2233,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, + "0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2222,6 +2265,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv3", namespace, + "0.0", "csv2", installStrategy("csv3-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2230,6 +2274,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2238,6 +2283,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, + "0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2269,6 +2315,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv2", namespace, + "0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2277,6 +2324,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv3", namespace, + "0.0", "csv2", installStrategy("csv3-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2307,6 +2355,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv2", namespace, + "0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2315,6 +2364,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv3", namespace, + "0.0", "csv2", installStrategy("csv3-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2411,6 +2461,7 @@ func TestSyncOperatorGroups(t *testing.T) { crd := crd("c1.fake.api.group", "v1") operatorCSV := csv("csv1", operatorNamespace, + "0.0", "", installStrategy("csv1-dep1", permissions, nil), []*v1beta1.CustomResourceDefinition{crd}, @@ -3064,7 +3115,7 @@ func TestIsReplacing(t *testing.T) { }{ { name: "QueryErr", - in: csv("name", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("name", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: []runtime.Object{}, }, @@ -3072,30 +3123,30 @@ func TestIsReplacing(t *testing.T) { }, { name: "CSVInCluster/NotReplacing", - in: csv("csv1", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: []runtime.Object{ - csv("csv1", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, expected: nil, }, { name: "CSVInCluster/Replacing", - in: csv("csv2", namespace, "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: []runtime.Object{ - csv("csv1", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, - expected: csv("csv1", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + expected: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, { name: "CSVInCluster/ReplacingNotFound", - in: csv("csv2", namespace, "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: []runtime.Object{ - csv("csv3", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + csv("csv3", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, expected: nil, @@ -3128,28 +3179,28 @@ func TestIsBeingReplaced(t *testing.T) { }{ { name: "QueryErr", - in: csv("name", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("name", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), expected: nil, }, { name: "CSVInCluster/NotReplacing", - in: csv("csv1", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: map[string]*v1alpha1.ClusterServiceVersion{ - "csv2": csv("csv2", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + "csv2": csv("csv2", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, expected: nil, }, { name: "CSVInCluster/Replacing", - in: csv("csv1", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: map[string]*v1alpha1.ClusterServiceVersion{ - "csv2": csv("csv2", namespace, "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + "csv2": csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, - expected: csv("csv2", namespace, "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + expected: csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, } for _, tt := range tests { @@ -3176,28 +3227,28 @@ func TestCheckReplacement(t *testing.T) { }{ { name: "QueryErr", - in: csv("name", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("name", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), expected: nil, }, { name: "CSVInCluster/NotReplacing", - in: csv("csv1", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: map[string]*v1alpha1.ClusterServiceVersion{ - "csv2": csv("csv2", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + "csv2": csv("csv2", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, expected: nil, }, { name: "CSVInCluster/Replacing", - in: csv("csv1", namespace, "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: map[string]*v1alpha1.ClusterServiceVersion{ - "csv2": csv("csv2", namespace, "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + "csv2": csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, - expected: csv("csv2", namespace, "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + expected: csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, } for _, tt := range tests { diff --git a/pkg/controller/operators/olm/requirements.go b/pkg/controller/operators/olm/requirements.go index e70bf657046..99988b79088 100644 --- a/pkg/controller/operators/olm/requirements.go +++ b/pkg/controller/operators/olm/requirements.go @@ -7,18 +7,69 @@ import ( "github.com/sirupsen/logrus" + "github.com/coreos/go-semver/semver" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" olmErrors "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/errors" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/coreos/go-semver/semver" ) +func (a *Operator) minKubeVersionStatus(name string, minKubeVersion string) (met bool, statuses []v1alpha1.RequirementStatus) { + status := v1alpha1.RequirementStatus{ + Group: "operators.coreos.com", + Version: "v1alpha1", + Kind: "ClusterServiceVersion", + Name: name, + } + + if minKubeVersion == "" { + status.Status = v1alpha1.RequirementStatusReasonNotPresent + status.Message = "CSV missing minimum kube version specification" + met = true + statuses = append(statuses, status) + return + } + + // Retrieve server k8s version + serverVersionInfo, err := a.OpClient.KubernetesInterface().Discovery().ServerVersion() + if err != nil { + status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied + status.Message = "Server version discovery error" + met = false + statuses = append(statuses, status) + } + + // copy necessary fields into comparable for semver + majorInt, err := strconv.ParseInt(serverVersionInfo.Major, 10, 64) + minorInt, err := strconv.ParseInt(serverVersionInfo.Minor, 10, 64) + + serverVersionComparable := semver.Version{ + Major: majorInt, + Minor: minorInt, + } + + csvVersionInfo, err := semver.NewVersion(minKubeVersion) + if err != nil { + status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied + status.Message = "CSV version parsing error" + met = false + statuses = append(statuses, status) + } + + if csvVersionInfo.Compare(serverVersionComparable) < 0 { + status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied + status.Message = "CSV version requirement not met" + met = false + statuses = append(statuses, status) + } + + return +} + func (a *Operator) requirementStatus(strategyDetailsDeployment *install.StrategyDetailsDeployment, crdDescs []v1alpha1.CRDDescription, ownedAPIServiceDescs []v1alpha1.APIServiceDescription, requiredAPIServiceDescs []v1alpha1.APIServiceDescription, - requiredNativeAPIs []metav1.GroupVersionKind, - minKubeVersion string) (met bool, statuses []v1alpha1.RequirementStatus) { + requiredNativeAPIs []metav1.GroupVersionKind) (met bool, statuses []v1alpha1.RequirementStatus) { met = true // Check for CRDs @@ -180,49 +231,6 @@ func (a *Operator) requirementStatus(strategyDetailsDeployment *install.Strategy } } - status := v1alpha1.RequirementStatus{ - Group: "apiextensions.k8s.io", - Version: "v1beta1", - Kind: "CustomResourceDefinition", - // TODO: refactor? - Name: "TODO: csv name, needs refactoring to get name", - } - - serverVersionInfo, err := a.client.Discovery().ServerVersion() - if err != nil { - // TODO: this status may be wrong - status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied - status.Message = "Server version discovery error" - met = false - statuses = append(statuses, status) - } - - // copy necessary fields into comparable for semver - majorInt, err := strconv.ParseInt(serverVersionInfo.Major, 10, 64) - minorInt, err := strconv.ParseInt(serverVersionInfo.Minor, 10, 64) - - serverVersionComparable := semver.Version{ - Major: majorInt, - Minor: minorInt, - } - - csvVersionInfo, err := semver.NewVersion(minKubeVersion) - if err != nil { - // TODO: this status may be wrong - status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied - status.Message = "CSV version parsing error" - met = false - statuses = append(statuses, status) - } - - if csvVersionInfo.Compare(serverVersionComparable) < 0 { - // TODO: this status may be wrong - status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied - status.Message = "CSV version requirement not met" - met = false - statuses = append(statuses, status) - } - return } @@ -338,7 +346,10 @@ func (a *Operator) requirementAndPermissionStatus(csv *v1alpha1.ClusterServiceVe return false, nil, fmt.Errorf("could not cast install strategy as type %T", strategyDetailsDeployment) } - reqMet, reqStatuses := a.requirementStatus(strategyDetailsDeployment, csv.GetAllCRDDescriptions(), csv.GetOwnedAPIServiceDescriptions(), csv.GetRequiredAPIServiceDescriptions(), csv.Spec.NativeAPIs, csv.Spec.MinKubeVersion) + // Check kubernetes version requirement between CSV and server + minKubeMet, minKubeStatus := a.minKubeVersionStatus(csv.Spec.DisplayName, csv.Spec.MinKubeVersion) + reqMet, reqStatuses := a.requirementStatus(strategyDetailsDeployment, csv.GetAllCRDDescriptions(), csv.GetOwnedAPIServiceDescriptions(), csv.GetRequiredAPIServiceDescriptions(), csv.Spec.NativeAPIs) + allReqStatuses := append(minKubeStatus, reqStatuses...) rbacLister := a.lister.RbacV1() roleLister := rbacLister.RoleLister() @@ -353,10 +364,10 @@ func (a *Operator) requirementAndPermissionStatus(csv *v1alpha1.ClusterServiceVe } // Aggregate requirement and permissions statuses - statuses := append(reqStatuses, permStatuses...) - met := reqMet && permMet + statuses := append(allReqStatuses, permStatuses...) + met := minKubeMet && reqMet && permMet if !met { - a.Log.WithField("reqMet", reqMet).WithField("permMet", permMet).Debug("permissions not met") + a.Log.WithField("minKubeMet", minKubeMet).WithField("reqMet", reqMet).WithField("permMet", permMet).Debug("permissions/requirements not met") } return met, statuses, nil diff --git a/pkg/controller/operators/olm/requirements_test.go b/pkg/controller/operators/olm/requirements_test.go index 135d26cd3e0..e397926428f 100644 --- a/pkg/controller/operators/olm/requirements_test.go +++ b/pkg/controller/operators/olm/requirements_test.go @@ -38,6 +38,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "BadInstallStrategy", csv: csv("csv1", namespace, + "0.0", "", v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, nil, @@ -54,6 +55,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "AllPermissionsMet", csv: csv("csv1", namespace, + "0.0", "", installStrategy( "csv1-dep", @@ -184,6 +186,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "OnePermissionNotMet", csv: csv("csv1", namespace, + "0.0", "", installStrategy( "csv1-dep", @@ -314,6 +317,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "AllRequirementsMet", csv: csv("csv1", namespace, + "0.0", "", installStrategy( "csv1-dep", @@ -431,6 +435,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "RequirementNotMet/NonServedCRDVersion", csv: csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v2")}, @@ -457,6 +462,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "RequirementNotMet/NotEstablishedCRDVersion", csv: csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "version-not-found")}, @@ -483,6 +489,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "RequirementNotMet/NamesConflictedCRD", csv: csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v2")}, @@ -515,6 +522,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "RequirementNotMet/CRDResourceInactive", csv: csv("csv1", namespace, + "0.0", "", installStrategy("csv1-dep", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v2")}, diff --git a/test/e2e/csv_e2e_test.go b/test/e2e/csv_e2e_test.go index d4dce434db3..4461f0c194a 100644 --- a/test/e2e/csv_e2e_test.go +++ b/test/e2e/csv_e2e_test.go @@ -255,6 +255,68 @@ func waitForCSVToDelete(t *testing.T, c versioned.Interface, name string) error return err } +func TestCreateCSVWithUnmetRequirementsMinKubeVersion(t *testing.T) { + defer cleaner.NotifyTestComplete(t, true) + + c := newKubeClient(t) + crc := newCRClient(t) + + depName := genName("dep-") + csv := v1alpha1.ClusterServiceVersion{ + TypeMeta: metav1.TypeMeta{ + Kind: v1alpha1.ClusterServiceVersionKind, + APIVersion: v1alpha1.ClusterServiceVersionAPIVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: genName("csv"), + }, + Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "999.999", + InstallModes: []v1alpha1.InstallMode{ + { + Type: v1alpha1.InstallModeTypeOwnNamespace, + Supported: true, + }, + { + Type: v1alpha1.InstallModeTypeSingleNamespace, + Supported: true, + }, + { + Type: v1alpha1.InstallModeTypeMultiNamespace, + Supported: true, + }, + { + Type: v1alpha1.InstallModeTypeAllNamespaces, + Supported: true, + }, + }, + InstallStrategy: newNginxInstallStrategy(depName, nil, nil), + CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ + Owned: []v1alpha1.CRDDescription{ + { + DisplayName: "Not In Cluster", + Description: "A CRD that is not currently in the cluster", + Name: "not.in.cluster.com", + Version: "v1alpha1", + Kind: "NotInCluster", + }, + }, + }, + }, + } + + cleanupCSV, err := createCSV(t, c, crc, csv, testNamespace, false, false) + require.NoError(t, err) + defer cleanupCSV() + + _, err = fetchCSV(t, crc, csv.Name, testNamespace, csvPendingChecker) + require.NoError(t, err) + + // Shouldn't create deployment + _, err = c.GetDeployment(testNamespace, depName) + require.Error(t, err) +} + // TODO: same test but missing serviceaccount instead func TestCreateCSVWithUnmetRequirementsCRD(t *testing.T) { defer cleaner.NotifyTestComplete(t, true) @@ -272,6 +334,7 @@ func TestCreateCSVWithUnmetRequirementsCRD(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -444,6 +507,7 @@ func TestCreateCSVWithUnmetRequirementsAPIService(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -532,6 +596,7 @@ func TestCreateCSVWithUnmetPermissionsAPIService(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -594,6 +659,7 @@ func TestCreateCSVWithUnmetRequirementsNativeAPI(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -684,6 +750,7 @@ func TestCreateCSVRequirementsMetCRD(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -939,6 +1006,7 @@ func TestCreateCSVRequirementsMetAPIService(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1094,6 +1162,7 @@ func TestCreateCSVWithOwnedAPIService(t *testing.T) { csv := v1alpha1.ClusterServiceVersion{ Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1268,6 +1337,7 @@ func TestUpdateCSVSameDeploymentName(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1450,6 +1520,7 @@ func TestUpdateCSVDifferentDeploymentName(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1637,6 +1708,7 @@ func TestUpdateCSVMultipleIntermediates(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1830,6 +1902,7 @@ func TestUpdateCSVMultipleVersionCRD(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ + MinKubeVersion: "0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, From 319c991deca1a539ff1cfe2bf6969515c566b06c Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Fri, 11 Jan 2019 18:44:29 -0500 Subject: [PATCH 3/4] fix(olm): always return a status on minKubeVersion checks --- pkg/controller/operators/olm/operator_test.go | 142 +++++++++--------- pkg/controller/operators/olm/requirements.go | 27 ++-- 2 files changed, 88 insertions(+), 81 deletions(-) diff --git a/pkg/controller/operators/olm/operator_test.go b/pkg/controller/operators/olm/operator_test.go index 1f804be4eb9..f4491be1bc3 100644 --- a/pkg/controller/operators/olm/operator_test.go +++ b/pkg/controller/operators/olm/operator_test.go @@ -483,7 +483,7 @@ func csv( Namespace: namespace, }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: minKubeVersion, + MinKubeVersion: minKubeVersion, Replaces: replaces, InstallStrategy: installStrategy, InstallModes: []v1alpha1.InstallMode{ @@ -730,7 +730,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -752,7 +752,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -774,7 +774,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -799,7 +799,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, @@ -845,7 +845,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -871,7 +871,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -896,7 +896,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -922,7 +922,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -948,7 +948,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("b1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -976,7 +976,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -985,7 +985,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, - "0.0", + "0.0.0", "", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1017,7 +1017,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -1026,7 +1026,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), apis("a1.v1.a1Kind"), nil), metav1.NewTime(time.Now().Add(24*time.Hour)), metav1.NewTime(time.Now())), withAPIServices(csvWithAnnotations(csv("csv2", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -1094,7 +1094,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1119,7 +1119,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1144,7 +1144,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1169,7 +1169,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1194,7 +1194,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{}, @@ -1217,7 +1217,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1242,7 +1242,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1267,7 +1267,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1336,7 +1336,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1405,7 +1405,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1474,7 +1474,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1543,7 +1543,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1612,7 +1612,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1681,7 +1681,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1750,7 +1750,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withCertInfo(withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1819,7 +1819,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withConditionReason(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1907,7 +1907,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withInstallModes(withConditionReason(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2023,7 +2023,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2048,7 +2048,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2076,7 +2076,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAPIServices(csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2098,7 +2098,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2107,7 +2107,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, - "0.0", + "0.0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2136,7 +2136,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2145,7 +2145,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, - "0.0", + "0.0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2175,7 +2175,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2184,7 +2184,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, - "0.0", + "0.0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2215,7 +2215,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv3", namespace, - "0.0", + "0.0.0", "csv2", installStrategy("csv3-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2224,7 +2224,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2233,7 +2233,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, - "0.0", + "0.0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2265,7 +2265,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv3", namespace, - "0.0", + "0.0.0", "csv2", installStrategy("csv3-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2274,7 +2274,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2283,7 +2283,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv2", namespace, - "0.0", + "0.0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2315,7 +2315,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv2", namespace, - "0.0", + "0.0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2324,7 +2324,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv3", namespace, - "0.0", + "0.0.0", "csv2", installStrategy("csv3-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2355,7 +2355,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ csvWithAnnotations(csv("csv2", namespace, - "0.0", + "0.0.0", "csv1", installStrategy("csv2-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2364,7 +2364,7 @@ func TestTransitionCSV(t *testing.T) { ), defaultTemplateAnnotations), csvWithAnnotations(csv("csv3", namespace, - "0.0", + "0.0.0", "csv2", installStrategy("csv3-dep1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2461,7 +2461,7 @@ func TestSyncOperatorGroups(t *testing.T) { crd := crd("c1.fake.api.group", "v1") operatorCSV := csv("csv1", operatorNamespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep1", permissions, nil), []*v1beta1.CustomResourceDefinition{crd}, @@ -3115,7 +3115,7 @@ func TestIsReplacing(t *testing.T) { }{ { name: "QueryErr", - in: csv("name", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("name", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: []runtime.Object{}, }, @@ -3123,30 +3123,30 @@ func TestIsReplacing(t *testing.T) { }, { name: "CSVInCluster/NotReplacing", - in: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv1", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: []runtime.Object{ - csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + csv("csv1", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, expected: nil, }, { name: "CSVInCluster/Replacing", - in: csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv2", namespace, "0.0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: []runtime.Object{ - csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + csv("csv1", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, - expected: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + expected: csv("csv1", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, { name: "CSVInCluster/ReplacingNotFound", - in: csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv2", namespace, "0.0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: []runtime.Object{ - csv("csv3", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + csv("csv3", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, expected: nil, @@ -3179,28 +3179,28 @@ func TestIsBeingReplaced(t *testing.T) { }{ { name: "QueryErr", - in: csv("name", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("name", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), expected: nil, }, { name: "CSVInCluster/NotReplacing", - in: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv1", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: map[string]*v1alpha1.ClusterServiceVersion{ - "csv2": csv("csv2", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + "csv2": csv("csv2", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, expected: nil, }, { name: "CSVInCluster/Replacing", - in: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv1", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: map[string]*v1alpha1.ClusterServiceVersion{ - "csv2": csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + "csv2": csv("csv2", namespace, "0.0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, - expected: csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + expected: csv("csv2", namespace, "0.0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, } for _, tt := range tests { @@ -3227,28 +3227,28 @@ func TestCheckReplacement(t *testing.T) { }{ { name: "QueryErr", - in: csv("name", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("name", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), expected: nil, }, { name: "CSVInCluster/NotReplacing", - in: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv1", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: map[string]*v1alpha1.ClusterServiceVersion{ - "csv2": csv("csv2", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + "csv2": csv("csv2", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, expected: nil, }, { name: "CSVInCluster/Replacing", - in: csv("csv1", namespace, "0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + in: csv("csv1", namespace, "0.0.0", "", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), initial: initial{ csvs: map[string]*v1alpha1.ClusterServiceVersion{ - "csv2": csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + "csv2": csv("csv2", namespace, "0.0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, }, - expected: csv("csv2", namespace, "0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), + expected: csv("csv2", namespace, "0.0.0", "csv1", installStrategy("dep", nil, nil), nil, nil, v1alpha1.CSVPhaseSucceeded), }, } for _, tt := range tests { diff --git a/pkg/controller/operators/olm/requirements.go b/pkg/controller/operators/olm/requirements.go index 99988b79088..936ce8555ee 100644 --- a/pkg/controller/operators/olm/requirements.go +++ b/pkg/controller/operators/olm/requirements.go @@ -3,7 +3,7 @@ package olm import ( "encoding/json" "fmt" - "strconv" + "strings" "github.com/sirupsen/logrus" @@ -38,15 +38,16 @@ func (a *Operator) minKubeVersionStatus(name string, minKubeVersion string) (met status.Message = "Server version discovery error" met = false statuses = append(statuses, status) + return } - // copy necessary fields into comparable for semver - majorInt, err := strconv.ParseInt(serverVersionInfo.Major, 10, 64) - minorInt, err := strconv.ParseInt(serverVersionInfo.Minor, 10, 64) - - serverVersionComparable := semver.Version{ - Major: majorInt, - Minor: minorInt, + serverVersion, err := semver.NewVersion(strings.TrimPrefix(serverVersionInfo.String(), "v")) + if err != nil { + status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied + status.Message = "Server version parsing error" + met = false + statuses = append(statuses, status) + return } csvVersionInfo, err := semver.NewVersion(minKubeVersion) @@ -55,15 +56,21 @@ func (a *Operator) minKubeVersionStatus(name string, minKubeVersion string) (met status.Message = "CSV version parsing error" met = false statuses = append(statuses, status) + return } - if csvVersionInfo.Compare(serverVersionComparable) < 0 { + if csvVersionInfo.Compare(*serverVersion) < 0 { status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied status.Message = "CSV version requirement not met" met = false statuses = append(statuses, status) + return } + status.Status = v1alpha1.RequirementStatusReasonPresent + status.Message = fmt.Sprintf("CSV minKubeVersion (%s) less than server version (%s)", minKubeVersion, serverVersionInfo.String()) + met = true + statuses = append(statuses, status) return } @@ -347,7 +354,7 @@ func (a *Operator) requirementAndPermissionStatus(csv *v1alpha1.ClusterServiceVe } // Check kubernetes version requirement between CSV and server - minKubeMet, minKubeStatus := a.minKubeVersionStatus(csv.Spec.DisplayName, csv.Spec.MinKubeVersion) + minKubeMet, minKubeStatus := a.minKubeVersionStatus(csv.GetName(), csv.Spec.MinKubeVersion) reqMet, reqStatuses := a.requirementStatus(strategyDetailsDeployment, csv.GetAllCRDDescriptions(), csv.GetOwnedAPIServiceDescriptions(), csv.GetRequiredAPIServiceDescriptions(), csv.Spec.NativeAPIs) allReqStatuses := append(minKubeStatus, reqStatuses...) From 57ad90fb79d5a0692c5232fe66a6107b8fdda09d Mon Sep 17 00:00:00 2001 From: Vu Dinh Date: Fri, 11 Jan 2019 19:28:15 -0500 Subject: [PATCH 4/4] fix(olm): Provide correct format of kube version in CSV The format of kube version in CSV should be Major.Minor.Patch. For example, 1.1.1 is correct. Signed-off-by: Vu Dinh --- deploy/chart/values.yaml | 2 +- pkg/controller/operators/olm/operator_test.go | 12 ++++ pkg/controller/operators/olm/requirements.go | 6 +- .../operators/olm/requirements_test.go | 65 ++++++++++++++++--- test/e2e/csv_e2e_test.go | 24 +++---- test/e2e/subscription_e2e_test.go | 2 + 6 files changed, 87 insertions(+), 24 deletions(-) diff --git a/deploy/chart/values.yaml b/deploy/chart/values.yaml index 68a2b0a8c27..89ee35c03a1 100644 --- a/deploy/chart/values.yaml +++ b/deploy/chart/values.yaml @@ -2,7 +2,7 @@ rbacApiVersion: rbac.authorization.k8s.io namespace: operator-lifecycle-manager catalog_namespace: operator-lifecycle-manager operator_namespace: operators -minKubeVersion: 1.11 +minKubeVersion: 1.11.0 imagestream: false debug: false olm: diff --git a/pkg/controller/operators/olm/operator_test.go b/pkg/controller/operators/olm/operator_test.go index f4491be1bc3..7215fd5c206 100644 --- a/pkg/controller/operators/olm/operator_test.go +++ b/pkg/controller/operators/olm/operator_test.go @@ -1849,6 +1849,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withConditionReason(csvWithAnnotations(csv("csv1", namespace, + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1878,6 +1879,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withConditionReason(csvWithAnnotations(csv("csv1", namespace, + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1944,6 +1946,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withConditionReason(csvWithAnnotations(csv("csv1", namespace, + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -1975,6 +1978,7 @@ func TestTransitionCSV(t *testing.T) { csvs: []runtime.Object{ withConditionReason(csvWithAnnotations(csv("csv1", namespace, + "0.0.0", "", installStrategy("a1", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v1")}, @@ -2477,6 +2481,14 @@ func TestSyncOperatorGroups(t *testing.T) { operatorCSVFinal.Status.LastUpdateTime = timeNow() operatorCSVFinal.Status.LastTransitionTime = timeNow() operatorCSVFinal.Status.RequirementStatus = []v1alpha1.RequirementStatus{ + { + Group: "operators.coreos.com", + Version: "v1alpha1", + Kind: "ClusterServiceVersion", + Name: "csv1", + Status: v1alpha1.RequirementStatusReasonPresent, + Message: "CSV minKubeVersion (0.0.0) less than server version (v0.0.0-master+$Format:%h$)", + }, { Group: "apiextensions.k8s.io", Version: "v1beta1", diff --git a/pkg/controller/operators/olm/requirements.go b/pkg/controller/operators/olm/requirements.go index 936ce8555ee..307d27f8af5 100644 --- a/pkg/controller/operators/olm/requirements.go +++ b/pkg/controller/operators/olm/requirements.go @@ -41,7 +41,7 @@ func (a *Operator) minKubeVersionStatus(name string, minKubeVersion string) (met return } - serverVersion, err := semver.NewVersion(strings.TrimPrefix(serverVersionInfo.String(), "v")) + serverVersion, err := semver.NewVersion(strings.Split(strings.TrimPrefix(serverVersionInfo.String(), "v"), "-")[0]) if err != nil { status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied status.Message = "Server version parsing error" @@ -59,9 +59,9 @@ func (a *Operator) minKubeVersionStatus(name string, minKubeVersion string) (met return } - if csvVersionInfo.Compare(*serverVersion) < 0 { + if csvVersionInfo.Compare(*serverVersion) > 0 { status.Status = v1alpha1.RequirementStatusReasonPresentNotSatisfied - status.Message = "CSV version requirement not met" + status.Message = fmt.Sprintf("CSV version requirement not met: minKubeVersion (%s) > server version (%s)", minKubeVersion, serverVersion.String()) met = false statuses = append(statuses, status) return diff --git a/pkg/controller/operators/olm/requirements_test.go b/pkg/controller/operators/olm/requirements_test.go index e397926428f..69866cf39b4 100644 --- a/pkg/controller/operators/olm/requirements_test.go +++ b/pkg/controller/operators/olm/requirements_test.go @@ -38,7 +38,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "BadInstallStrategy", csv: csv("csv1", namespace, - "0.0", + "0.0.0", "", v1alpha1.NamedInstallStrategy{"deployment", json.RawMessage{}}, nil, @@ -55,7 +55,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "AllPermissionsMet", csv: csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy( "csv1-dep", @@ -179,6 +179,13 @@ func TestRequirementAndPermissionStatus(t *testing.T) { }, }, }, + {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { + Group: "operators.coreos.com", + Version: "v1alpha1", + Kind: "ClusterServiceVersion", + Name: "csv1", + Status: v1alpha1.RequirementStatusReasonPresent, + }, }, expectedError: nil, }, @@ -186,7 +193,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "OnePermissionNotMet", csv: csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy( "csv1-dep", @@ -310,6 +317,13 @@ func TestRequirementAndPermissionStatus(t *testing.T) { }, }, }, + {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { + Group: "operators.coreos.com", + Version: "v1alpha1", + Kind: "ClusterServiceVersion", + Name: "csv1", + Status: v1alpha1.RequirementStatusReasonPresent, + }, }, expectedError: nil, }, @@ -317,7 +331,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "AllRequirementsMet", csv: csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy( "csv1-dep", @@ -428,6 +442,13 @@ func TestRequirementAndPermissionStatus(t *testing.T) { Name: "c2group", Status: v1alpha1.RequirementStatusReasonPresent, }, + {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { + Group: "operators.coreos.com", + Version: "v1alpha1", + Kind: "ClusterServiceVersion", + Name: "csv1", + Status: v1alpha1.RequirementStatusReasonPresent, + }, }, expectedError: nil, }, @@ -435,7 +456,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "RequirementNotMet/NonServedCRDVersion", csv: csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v2")}, @@ -455,6 +476,13 @@ func TestRequirementAndPermissionStatus(t *testing.T) { Name: "c1group", Status: v1alpha1.RequirementStatusReasonNotPresent, }, + {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { + Group: "operators.coreos.com", + Version: "v1alpha1", + Kind: "ClusterServiceVersion", + Name: "csv1", + Status: v1alpha1.RequirementStatusReasonPresent, + }, }, expectedError: nil, }, @@ -462,7 +490,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "RequirementNotMet/NotEstablishedCRDVersion", csv: csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "version-not-found")}, @@ -482,6 +510,13 @@ func TestRequirementAndPermissionStatus(t *testing.T) { Name: "c1group", Status: v1alpha1.RequirementStatusReasonNotAvailable, }, + {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { + Group: "operators.coreos.com", + Version: "v1alpha1", + Kind: "ClusterServiceVersion", + Name: "csv1", + Status: v1alpha1.RequirementStatusReasonPresent, + }, }, expectedError: nil, }, @@ -489,7 +524,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "RequirementNotMet/NamesConflictedCRD", csv: csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v2")}, @@ -515,6 +550,13 @@ func TestRequirementAndPermissionStatus(t *testing.T) { Name: "c1group", Status: v1alpha1.RequirementStatusReasonNotAvailable, }, + {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { + Group: "operators.coreos.com", + Version: "v1alpha1", + Kind: "ClusterServiceVersion", + Name: "csv1", + Status: v1alpha1.RequirementStatusReasonPresent, + }, }, expectedError: nil, }, @@ -522,7 +564,7 @@ func TestRequirementAndPermissionStatus(t *testing.T) { description: "RequirementNotMet/CRDResourceInactive", csv: csv("csv1", namespace, - "0.0", + "0.0.0", "", installStrategy("csv1-dep", nil, nil), []*v1beta1.CustomResourceDefinition{crd("c1", "v2")}, @@ -548,6 +590,13 @@ func TestRequirementAndPermissionStatus(t *testing.T) { Name: "c1group", Status: v1alpha1.RequirementStatusReasonNotAvailable, }, + {"operators.coreos.com", "v1alpha1", "ClusterServiceVersion", "csv1"}: { + Group: "operators.coreos.com", + Version: "v1alpha1", + Kind: "ClusterServiceVersion", + Name: "csv1", + Status: v1alpha1.RequirementStatusReasonPresent, + }, }, expectedError: nil, }, diff --git a/test/e2e/csv_e2e_test.go b/test/e2e/csv_e2e_test.go index 4461f0c194a..908192ab99d 100644 --- a/test/e2e/csv_e2e_test.go +++ b/test/e2e/csv_e2e_test.go @@ -271,7 +271,7 @@ func TestCreateCSVWithUnmetRequirementsMinKubeVersion(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "999.999", + MinKubeVersion: "999.999.999", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -334,7 +334,7 @@ func TestCreateCSVWithUnmetRequirementsCRD(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -507,7 +507,7 @@ func TestCreateCSVWithUnmetRequirementsAPIService(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -596,7 +596,7 @@ func TestCreateCSVWithUnmetPermissionsAPIService(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -659,7 +659,7 @@ func TestCreateCSVWithUnmetRequirementsNativeAPI(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -750,7 +750,7 @@ func TestCreateCSVRequirementsMetCRD(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1006,7 +1006,7 @@ func TestCreateCSVRequirementsMetAPIService(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1162,7 +1162,7 @@ func TestCreateCSVWithOwnedAPIService(t *testing.T) { csv := v1alpha1.ClusterServiceVersion{ Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1337,7 +1337,7 @@ func TestUpdateCSVSameDeploymentName(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1520,7 +1520,7 @@ func TestUpdateCSVDifferentDeploymentName(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1708,7 +1708,7 @@ func TestUpdateCSVMultipleIntermediates(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -1902,7 +1902,7 @@ func TestUpdateCSVMultipleVersionCRD(t *testing.T) { Name: genName("csv"), }, Spec: v1alpha1.ClusterServiceVersionSpec{ - MinKubeVersion: "0.0", + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, diff --git a/test/e2e/subscription_e2e_test.go b/test/e2e/subscription_e2e_test.go index ed07a0e89cf..eb8f18f8717 100644 --- a/test/e2e/subscription_e2e_test.go +++ b/test/e2e/subscription_e2e_test.go @@ -75,6 +75,7 @@ var ( Spec: v1alpha1.ClusterServiceVersionSpec{ Replaces: "", Version: *semver.New("0.1.0"), + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -104,6 +105,7 @@ var ( Spec: v1alpha1.ClusterServiceVersionSpec{ Replaces: outdated, Version: *semver.New("0.2.0"), + MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace,