Skip to content

Commit

Permalink
Merge pull request #674 from njhale/support-services
Browse files Browse the repository at this point in the history
fix(installplans): add ability to apply Services
  • Loading branch information
openshift-merge-robot authored Jan 21, 2019
2 parents f3b9375 + 5941c06 commit a36ed09
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -479,4 +479,3 @@ func (csv ClusterServiceVersion) GetOwnedAPIServiceDescriptions() []APIServiceDe

return descs
}

3 changes: 2 additions & 1 deletion pkg/api/apis/operators/v1alpha1/installplan_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"errors"
"fmt"

"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators"
)

const (
Expand Down
36 changes: 36 additions & 0 deletions pkg/controller/operators/catalog/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const (
clusterRoleKind = "ClusterRole"
clusterRoleBindingKind = "ClusterRoleBinding"
serviceAccountKind = "ServiceAccount"
serviceKind = "Service"
roleKind = "Role"
roleBindingKind = "RoleBinding"
)
Expand Down Expand Up @@ -1115,6 +1116,41 @@ func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error {
plan.Status.Plan[i].Status = v1alpha1.StepStatusCreated
}

case serviceKind:
// Marshal the manifest into a Service instance
var s corev1.Service
err := json.Unmarshal([]byte(step.Resource.Manifest), &s)
if err != nil {
return errorwrap.Wrapf(err, "error parsing step manifest: %s", step.Resource.Name)
}

// Update UIDs on all CSV OwnerReferences
updated, err := o.getUpdatedOwnerReferences(s.OwnerReferences, plan.Namespace)
if err != nil {
return errorwrap.Wrapf(err, "error generating ownerrefs for service: %s", s.GetName())
}
s.SetOwnerReferences(updated)
s.SetNamespace(namespace)

// Attempt to create the Service
_, err = o.OpClient.KubernetesInterface().CoreV1().Services(plan.Namespace).Create(&s)
if k8serrors.IsAlreadyExists(err) {
// If it already exists we need to patch the existing SA with the new OwnerReferences
s.SetNamespace(plan.Namespace)
_, err = o.OpClient.UpdateService(&s)
if err != nil {
return errorwrap.Wrapf(err, "error updating service: %s", s.GetName())
}

// Mark as present
plan.Status.Plan[i].Status = v1alpha1.StepStatusPresent
} else if err != nil {
return errorwrap.Wrapf(err, "error creating service: %s", s.GetName())
} else {
// If no error occurred, mark the step as Created
plan.Status.Plan[i].Status = v1alpha1.StepStatusCreated
}

default:
return v1alpha1.ErrInvalidInstallPlan
}
Expand Down
152 changes: 139 additions & 13 deletions pkg/controller/operators/catalog/operator_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package catalog

import (
"encoding/json"
"errors"
"fmt"
"testing"
Expand All @@ -9,7 +10,9 @@ import (
"github.com/ghodss/yaml"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apiextensionsfake "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -113,6 +116,98 @@ func TestTransitionInstallPlan(t *testing.T) {
}
}

func TestExecutePlan(t *testing.T) {
namespace := "ns"

tests := []struct {
testName string
in *v1alpha1.InstallPlan
want []runtime.Object
err error
}{
{
testName: "NoSteps",
in: installPlan("p", namespace, v1alpha1.InstallPlanPhaseInstalling),
want: []runtime.Object{},
err: nil,
},
{
testName: "MultipleSteps",
in: withSteps(installPlan("p", namespace, v1alpha1.InstallPlanPhaseInstalling, "csv"),
[]*v1alpha1.Step{
&v1alpha1.Step{
Resource: v1alpha1.StepResource{
CatalogSource: "catalog",
CatalogSourceNamespace: namespace,
Group: "",
Version: "v1",
Kind: "Service",
Name: "service",
Manifest: toManifest(service("service", namespace)),
},
Status: v1alpha1.StepStatusUnknown,
},
&v1alpha1.Step{
Resource: v1alpha1.StepResource{
CatalogSource: "catalog",
CatalogSourceNamespace: namespace,
Group: "operators.coreos.com",
Version: "v1alpha1",
Kind: "ClusterServiceVersion",
Name: "csv",
Manifest: toManifest(csv("csv", namespace, nil, nil)),
},
Status: v1alpha1.StepStatusUnknown,
},
},
),
want: []runtime.Object{service("service", namespace), csv("csv", namespace, nil, nil)},
err: nil,
},
}

for _, tt := range tests {
t.Run(tt.testName, func(t *testing.T) {
stopCh := make(chan struct{})
defer func() { stopCh <- struct{}{} }()
op, _, err := NewFakeOperator([]runtime.Object{tt.in}, nil, nil, nil, namespace, stopCh)
require.NoError(t, err)

err = op.ExecutePlan(tt.in)
require.Equal(t, tt.err, err)

for _, obj := range tt.want {
var err error
var fetched runtime.Object
switch o := obj.(type) {
case *appsv1.Deployment:
fetched, err = op.OpClient.GetDeployment(namespace, o.GetName())
case *rbacv1.ClusterRole:
fetched, err = op.OpClient.GetClusterRole(o.GetName())
case *rbacv1.Role:
fetched, err = op.OpClient.GetRole(namespace, o.GetName())
case *rbacv1.ClusterRoleBinding:
fetched, err = op.OpClient.GetClusterRoleBinding(o.GetName())
case *rbacv1.RoleBinding:
fetched, err = op.OpClient.GetRoleBinding(namespace, o.GetName())
case *corev1.ServiceAccount:
fetched, err = op.OpClient.GetServiceAccount(namespace, o.GetName())
case *corev1.Service:
fetched, err = op.OpClient.GetService(namespace, o.GetName())
case *v1alpha1.ClusterServiceVersion:
fetched, err = op.client.OperatorsV1alpha1().ClusterServiceVersions(namespace).Get(o.GetName(), metav1.GetOptions{})
default:
require.Failf(t, "couldn't find expected object", "%#v", obj)
}

require.NoError(t, err, "couldn't fetch %s %v", namespace, obj)
fmt.Printf("fetched: %v", fetched)
require.EqualValues(t, obj, fetched)
}
})
}
}

func TestSyncCatalogSources(t *testing.T) {
tests := []struct {
testName string
Expand Down Expand Up @@ -284,21 +379,21 @@ func TestCompetingCRDOwnersExist(t *testing.T) {
testNamespace := "default"
tests := []struct {
name string
csv v1alpha1.ClusterServiceVersion
csv *v1alpha1.ClusterServiceVersion
existingCRDOwners map[string][]string
expectedErr error
expectedResult bool
}{
{
name: "NoCompetingOwnersExist",
csv: csv("turkey", []string{"feathers"}, nil),
csv: csv("turkey", testNamespace, []string{"feathers"}, nil),
existingCRDOwners: nil,
expectedErr: nil,
expectedResult: false,
},
{
name: "OnlyCompetingWithSelf",
csv: csv("turkey", []string{"feathers"}, nil),
csv: csv("turkey", testNamespace, []string{"feathers"}, nil),
existingCRDOwners: map[string][]string{
"feathers": {"turkey"},
},
Expand All @@ -307,7 +402,7 @@ func TestCompetingCRDOwnersExist(t *testing.T) {
},
{
name: "CompetingOwnersExist",
csv: csv("turkey", []string{"feathers"}, nil),
csv: csv("turkey", testNamespace, []string{"feathers"}, nil),
existingCRDOwners: map[string][]string{
"feathers": {"seagull"},
},
Expand All @@ -316,7 +411,7 @@ func TestCompetingCRDOwnersExist(t *testing.T) {
},
{
name: "CompetingOwnerExistsOnSecondCRD",
csv: csv("turkey", []string{"feathers", "beak"}, nil),
csv: csv("turkey", testNamespace, []string{"feathers", "beak"}, nil),
existingCRDOwners: map[string][]string{
"milk": {"cow"},
"beak": {"squid"},
Expand All @@ -326,7 +421,7 @@ func TestCompetingCRDOwnersExist(t *testing.T) {
},
{
name: "MoreThanOneCompetingOwnerExists",
csv: csv("turkey", []string{"feathers"}, nil),
csv: csv("turkey", testNamespace, []string{"feathers"}, nil),
existingCRDOwners: map[string][]string{
"feathers": {"seagull", "turkey"},
},
Expand All @@ -338,7 +433,7 @@ func TestCompetingCRDOwnersExist(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

competing, err := competingCRDOwnersExist(testNamespace, &tt.csv, tt.existingCRDOwners)
competing, err := competingCRDOwnersExist(testNamespace, tt.csv, tt.existingCRDOwners)

// Assert the error is as expected
if tt.expectedErr == nil {
Expand Down Expand Up @@ -440,31 +535,48 @@ func NewFakeOperator(clientObjs []runtime.Object, k8sObjs []runtime.Object, extO
return op, hasSyncedCheckFns, nil
}

func installPlan(names ...string) v1alpha1.InstallPlan {
return v1alpha1.InstallPlan{
func installPlan(name, namespace string, phase v1alpha1.InstallPlanPhase, names ...string) *v1alpha1.InstallPlan {
return &v1alpha1.InstallPlan{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: v1alpha1.InstallPlanSpec{
ClusterServiceVersionNames: names,
},
Status: v1alpha1.InstallPlanStatus{
Plan: []*v1alpha1.Step{},
Phase: phase,
Plan: []*v1alpha1.Step{},
},
}
}

func csv(name string, owned, required []string) v1alpha1.ClusterServiceVersion {
func withSteps(plan *v1alpha1.InstallPlan, steps []*v1alpha1.Step) *v1alpha1.InstallPlan {
plan.Status.Plan = steps
return plan
}

func csv(name, namespace string, owned, required []string) *v1alpha1.ClusterServiceVersion {
requiredCRDDescs := make([]v1alpha1.CRDDescription, 0)
for _, name := range required {
requiredCRDDescs = append(requiredCRDDescs, v1alpha1.CRDDescription{Name: name, Version: "v1", Kind: name})
}
if len(requiredCRDDescs) == 0 {
requiredCRDDescs = nil
}

ownedCRDDescs := make([]v1alpha1.CRDDescription, 0)
for _, name := range owned {
ownedCRDDescs = append(ownedCRDDescs, v1alpha1.CRDDescription{Name: name, Version: "v1", Kind: name})
}
if len(ownedCRDDescs) == 0 {
ownedCRDDescs = nil
}

return v1alpha1.ClusterServiceVersion{
return &v1alpha1.ClusterServiceVersion{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Name: name,
Namespace: namespace,
},
Spec: v1alpha1.ClusterServiceVersionSpec{
CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{
Expand All @@ -489,3 +601,17 @@ func crd(name string) v1beta1.CustomResourceDefinition {
},
}
}

func service(name, namespace string) *corev1.Service {
return &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
}
}

func toManifest(obj runtime.Object) string {
raw, _ := json.Marshal(obj)
return string(raw)
}
21 changes: 21 additions & 0 deletions pkg/controller/registry/resolver/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

opregistry "github.com/operator-framework/operator-registry/pkg/registry"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
Expand Down Expand Up @@ -113,6 +114,26 @@ func TestNamespaceResolver(t *testing.T) {
},
},
},
{
name: "SingleNewSubscription/ResolveOne/AdditionalBundleObjects/Service",
clusterState: []runtime.Object{
newSub(namespace, "a", "alpha", catalog),
},
bundlesInCatalog: []*opregistry.Bundle{
withBundleObject(bundle("b.v1", "b", "beta", "", Provides1, nil, nil, nil), u(&corev1.Service{TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: ""}, ObjectMeta: metav1.ObjectMeta{Name: "test-service"}})),
bundle("a.v1", "a", "alpha", "", nil, Requires1, nil, nil),
},
out: out{
steps: [][]*v1alpha1.Step{
bundleSteps(bundle("a.v1", "a", "alpha", "", nil, Requires1, nil, nil), namespace, catalog),
bundleSteps(withBundleObject(bundle("b.v1", "b", "beta", "", Provides1, nil, nil, nil), u(&corev1.Service{TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: ""}, ObjectMeta: metav1.ObjectMeta{Name: "test-service"}})), namespace, catalog),
subSteps(namespace, "b.v1", "b", "beta", catalog),
},
subs: []*v1alpha1.Subscription{
updatedSub(namespace, "a.v1", "a", "alpha", catalog),
},
},
},
{
name: "SingleNewSubscription/DependencyMissing",
clusterState: []runtime.Object{
Expand Down
6 changes: 6 additions & 0 deletions pkg/lib/ownerutil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ func InferGroupVersionKind(obj runtime.Object) error {
}

switch obj.(type) {
case *corev1.Service:
objectKind.SetGroupVersionKind(schema.GroupVersionKind{
Group: "",
Version: "v1",
Kind: "Service",
})
case *corev1.ServiceAccount:
objectKind.SetGroupVersionKind(schema.GroupVersionKind{
Group: "",
Expand Down

0 comments on commit a36ed09

Please sign in to comment.