Skip to content

Commit

Permalink
add 'spec.nativeAPIs' field to CSV and check that native APIs exist b…
Browse files Browse the repository at this point in the history
…efore install
  • Loading branch information
alecmerdler committed Nov 2, 2018
1 parent f3faa4c commit f0a2d5c
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 6 deletions.
20 changes: 19 additions & 1 deletion deploy/chart/templates/0000_30_02-clusterserviceversion.crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,25 @@ spec:
values:
type: array
description: set of values for the expression

nativeAPIs:
type: array
description: What resources are required by the Operator, but must be provided by the underlying cluster and not as an extension.
items:
type: object
required:
- group
- version
- kind
properties:
group:
type: string
description: Group of the API resource
version:
type: string
description: Version of the API resource
kind:
type: string
description: Kind of the API resource
apiservicedefinitions:
type: object
properties:
Expand Down
13 changes: 11 additions & 2 deletions pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,23 @@ type APIServiceDefinitions struct {
Required []APIServiceDescription `json:"required,omitempty"`
}

// ClusterServiceVersionSpec declarations tell the OLM how to install an operator
// that can manage apps for given version and AppType.
// NativeAPI describes the GVK of a Kubernetes resource that is required by an
// Operator, but must be provided by the underlying cluster and not an extension.
type NativeAPI struct {
Group string `json:"group"`
Version string `json:"version"`
Kind string `json:"kind"`
}

// ClusterServiceVersionSpec declarations tell OLM how to install an operator
// that can manage apps for a given version.
type ClusterServiceVersionSpec struct {
InstallStrategy NamedInstallStrategy `json:"install"`
Version semver.Version `json:"version,omitempty"`
Maturity string `json:"maturity,omitempty"`
CustomResourceDefinitions CustomResourceDefinitions `json:"customresourcedefinitions,omitempty"`
APIServiceDefinitions APIServiceDefinitions `json:"apiservicedefinitions,omitempty"`
NativeAPIs []NativeAPI `json:"nativeAPIs,omitempty"`
DisplayName string `json:"displayName"`
Description string `json:"description,omitempty"`
Keywords []string `json:"keywords,omitempty"`
Expand Down
21 changes: 21 additions & 0 deletions pkg/api/apis/operators/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion pkg/controller/operators/olm/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,12 @@ func installStrategy(deploymentName string, permissions []install.StrategyDeploy
}
}

func csv(name, namespace, replaces string, installStrategy v1alpha1.NamedInstallStrategy, owned, required []*v1beta1.CustomResourceDefinition, phase v1alpha1.ClusterServiceVersionPhase) *v1alpha1.ClusterServiceVersion {
func csv(
name, namespace, replaces string,
installStrategy v1alpha1.NamedInstallStrategy,
owned, required []*v1beta1.CustomResourceDefinition,
phase v1alpha1.ClusterServiceVersionPhase,
) *v1alpha1.ClusterServiceVersion {
requiredCRDDescs := make([]v1alpha1.CRDDescription, 0)
for _, crd := range required {
requiredCRDDescs = append(requiredCRDDescs, v1alpha1.CRDDescription{Name: crd.GetName(), Version: crd.Spec.Versions[0].Name, Kind: crd.Spec.Names.Kind})
Expand Down
26 changes: 24 additions & 2 deletions pkg/controller/operators/olm/requirements.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (
)

func (a *Operator) requirementStatus(strategyDetailsDeployment *install.StrategyDetailsDeployment, crdDescs []v1alpha1.CRDDescription,
ownedAPIServiceDescs []v1alpha1.APIServiceDescription, requiredAPIServiceDescs []v1alpha1.APIServiceDescription) (met bool, statuses []v1alpha1.RequirementStatus) {
ownedAPIServiceDescs []v1alpha1.APIServiceDescription, requiredAPIServiceDescs []v1alpha1.APIServiceDescription,
requiredNativeAPIs []v1alpha1.NativeAPI) (met bool, statuses []v1alpha1.RequirementStatus) {
met = true

// Check for CRDs
Expand Down Expand Up @@ -125,6 +126,27 @@ func (a *Operator) requirementStatus(strategyDetailsDeployment *install.Strategy
}
}

for _, r := range requiredNativeAPIs {
name := fmt.Sprintf("%s.%s", r.Version, r.Group)
status := v1alpha1.RequirementStatus{
Group: r.Group,
Version: r.Version,
Kind: r.Kind,
Name: name,
}

if err := a.isGVKRegistered(r.Group, r.Version, r.Kind); err != nil {
status.Status = v1alpha1.RequirementStatusReasonNotPresent
met = false
statuses = append(statuses, status)
continue
} else {
status.Status = v1alpha1.RequirementStatusReasonPresent
statuses = append(statuses, status)
continue
}
}

return
}

Expand Down Expand Up @@ -238,7 +260,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())
reqMet, reqStatuses := a.requirementStatus(strategyDetailsDeployment, csv.GetAllCRDDescriptions(), csv.GetOwnedAPIServiceDescriptions(), csv.GetRequiredAPIServiceDescriptions(), csv.Spec.NativeAPIs)

rbacLister := a.lister.RbacV1()
roleLister := rbacLister.RoleLister()
Expand Down
33 changes: 33 additions & 0 deletions test/e2e/csv_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,39 @@ func TestCreateCSVWithUnmetPermissionsAPIService(t *testing.T) {
require.Error(t, err)
}

func TestCreateCSVWithUnmetRequirementsNativeAPI(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{
InstallStrategy: newNginxInstallStrategy(depName, nil, nil),
NativeAPIs: []v1alpha1.NativeAPI{{Group: "kubenative.io", Version: "v1", Kind: "Native"}},
},
}

cleanupCSV, err := createCSV(t, c, crc, csv, testNamespace, false)
require.NoError(t, err)
defer cleanupCSV()

_, err = fetchCSV(t, crc, csv.Name, csvPendingChecker)
require.NoError(t, err)

// Shouldn't create deployment
_, err = c.GetDeployment(testNamespace, depName)
require.Error(t, err)
}

// TODO: same test but create serviceaccount instead
func TestCreateCSVRequirementsMetCRD(t *testing.T) {
defer cleaner.NotifyTestComplete(t, true)
Expand Down

0 comments on commit f0a2d5c

Please sign in to comment.