Skip to content

Commit

Permalink
Add minimum kube version to CSV & check it against server version
Browse files Browse the repository at this point in the history
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 <vdinh@redhat.com>
  • Loading branch information
dinhxuanvu committed Jan 11, 2019
1 parent 3408023 commit e2b3767
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 50 deletions.
32 changes: 31 additions & 1 deletion pkg/controller/operators/olm/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ func installStrategy(deploymentName string, permissions []install.StrategyDeploy
}

func csv(
name, namespace, replaces string,
name, namespace, minKubeVerson, replaces string,
installStrategy v1alpha1.NamedInstallStrategy,
owned, required []*v1beta1.CustomResourceDefinition,
phase v1alpha1.ClusterServiceVersionPhase,
Expand All @@ -479,6 +479,7 @@ func csv(
Namespace: namespace,
},
Spec: v1alpha1.ClusterServiceVersionSpec{
MinKubeVersion: minKubeVersion,
Replaces: replaces,
InstallStrategy: installStrategy,
InstallModes: []v1alpha1.InstallMode{
Expand Down Expand Up @@ -947,6 +948,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
csv("csv1",
namespace,
"0.0",
"",
installStrategy("csv1-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand All @@ -955,6 +957,7 @@ func TestTransitionCSV(t *testing.T) {
),
csv("csv2",
namespace,
"0.0",
"",
installStrategy("csv2-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -985,6 +988,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
withCertInfo(withAPIServices(csv("csv1",
namespace,
"0.0"
"",
installStrategy("a1", nil, nil),
[]*v1beta1.CustomResourceDefinition{},
Expand All @@ -993,6 +997,7 @@ func TestTransitionCSV(t *testing.T) {
), apis("a1.v1.a1Kind"), nil), metav1.NewTime(time.Now().Add(24*time.Hour)), metav1.NewTime(time.Now())),
withAPIServices(csv("csv2",
namespace,
"0.0",
"",
installStrategy("a1", nil, nil),
[]*v1beta1.CustomResourceDefinition{},
Expand Down Expand Up @@ -1128,6 +1133,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
csv("csv1",
namespace,
"0.0",
"",
installStrategy("csv1-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand All @@ -1151,6 +1157,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
withAPIServices(csv("csv1",
namespace,
"0.0",
"",
installStrategy("csv1-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{},
Expand Down Expand Up @@ -1218,6 +1225,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
withCertInfo(withAPIServices(csv("csv1",
namespace,
"0.0",
"",
installStrategy("a1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -1285,6 +1293,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
withCertInfo(withAPIServices(csv("csv1",
namespace,
"0.0",
"",
installStrategy("a1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -1352,6 +1361,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
withCertInfo(withAPIServices(csv("csv1",
namespace,
"0.0",
"",
installStrategy("a1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -1419,6 +1429,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
withCertInfo(withAPIServices(csv("csv1",
namespace,
"0.0",
"",
installStrategy("a1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -1486,6 +1497,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
withCertInfo(withAPIServices(csv("csv1",
namespace,
"0.0",
"",
installStrategy("a1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -1553,6 +1565,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
withCertInfo(withAPIServices(csv("csv1",
namespace,
"0.0",
"",
installStrategy("a1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -1620,6 +1633,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
withCertInfo(withAPIServices(csv("csv1",
namespace,
"0.0",
"",
installStrategy("a1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -1687,6 +1701,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
withCertInfo(withAPIServices(csv("csv1",
namespace,
"0.0",
"",
installStrategy("a1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -1921,6 +1936,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
csv("csv1",
namespace,
"0.0",
"",
installStrategy("csv1-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand All @@ -1929,6 +1945,7 @@ func TestTransitionCSV(t *testing.T) {
),
csv("csv2",
namespace,
"0.0",
"csv1",
installStrategy("csv2-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -1957,6 +1974,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
csv("csv1",
namespace,
"0.0",
"",
installStrategy("csv1-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand All @@ -1965,6 +1983,7 @@ func TestTransitionCSV(t *testing.T) {
),
csv("csv2",
namespace,
"0.0",
"csv1",
installStrategy("csv2-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -1994,6 +2013,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
csv("csv3",
namespace,
"0.0",
"csv2",
installStrategy("csv3-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand All @@ -2002,6 +2022,7 @@ func TestTransitionCSV(t *testing.T) {
),
csv("csv1",
namespace,
"0.0",
"",
installStrategy("csv1-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand All @@ -2010,6 +2031,7 @@ func TestTransitionCSV(t *testing.T) {
),
csv("csv2",
namespace,
"0.0",
"csv1",
installStrategy("csv2-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -2040,6 +2062,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
csv("csv3",
namespace,
"0.0",
"csv2",
installStrategy("csv3-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand All @@ -2048,6 +2071,7 @@ func TestTransitionCSV(t *testing.T) {
),
csv("csv1",
namespace,
"0.0",
"",
installStrategy("csv1-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand All @@ -2056,6 +2080,7 @@ func TestTransitionCSV(t *testing.T) {
),
csv("csv2",
namespace,
"0.0",
"csv1",
installStrategy("csv2-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -2086,6 +2111,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
csv("csv2",
namespace,
"0.0",
"csv1",
installStrategy("csv2-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand All @@ -2094,6 +2120,7 @@ func TestTransitionCSV(t *testing.T) {
),
csv("csv3",
namespace,
"0.0",
"csv2",
installStrategy("csv3-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -2123,6 +2150,7 @@ func TestTransitionCSV(t *testing.T) {
csvs: []runtime.Object{
csv("csv2",
namespace,
"0.0",
"csv1",
installStrategy("csv2-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand All @@ -2131,6 +2159,7 @@ func TestTransitionCSV(t *testing.T) {
),
csv("csv3",
namespace,
"0.0",
"csv2",
installStrategy("csv3-dep1", nil, nil),
[]*v1beta1.CustomResourceDefinition{crd("c1", "v1")},
Expand Down Expand Up @@ -2255,6 +2284,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},
Expand Down
109 changes: 60 additions & 49 deletions pkg/controller/operators/olm/requirements.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.RequirementStatusReasonPresentNotSatisfied
status.Message = "CSV missing minimum kube version specification"
met = false
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
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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()
Expand All @@ -353,8 +364,8 @@ 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")
}
Expand Down

0 comments on commit e2b3767

Please sign in to comment.