From f05c143f999311ccaabaa5dde374eb686005c7c1 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 020b74a531..313b26458c 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 a4e7b982a1..e70bf65704 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 4d42f2f80f2412bc8c14218a5105a687b2f68341 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 6346b7f4c8..646e03f8a8 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 22568d41aa..e3142c168b 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 dc63a7626b..cadb8c277c 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 a6c9f2e116..68a2b0a8c2 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 55ec962996..ff3e6dac0d 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 697201359a..1f804be4eb 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 e70bf65704..99988b7908 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 135d26cd3e..e397926428 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 d4dce434db..4461f0c194 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 0182a24056405d43c5a6b13eac2a3094797ac201 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 1f804be4eb..f4491be1bc 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 99988b7908..936ce8555e 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 5ab896d4d713da821da2d6e394dc947b0fe5dbc1 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 68a2b0a8c2..89ee35c03a 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 f4491be1bc..7215fd5c20 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 936ce8555e..307d27f8af 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 e397926428..69866cf39b 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 4461f0c194..908192ab99 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 ed07a0e89c..eb8f18f871 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,