Skip to content

Commit

Permalink
fix(installplans): add ability to apply Services
Browse files Browse the repository at this point in the history
  • Loading branch information
njhale committed Jan 19, 2019
1 parent 4d6a1e5 commit 4aa7674
Show file tree
Hide file tree
Showing 16 changed files with 235 additions and 34 deletions.
7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ require (
github.com/coreos/go-semver v0.2.0
github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 // indirect
github.com/docker/distribution v2.7.0+incompatible // indirect
github.com/emicklei/go-restful v2.8.1+incompatible // indirect
github.com/evanphx/json-patch v4.1.0+incompatible // indirect
github.com/ghodss/yaml v1.0.0
github.com/go-openapi/spec v0.17.2
Expand Down Expand Up @@ -44,15 +45,15 @@ require (
google.golang.org/grpc v1.16.0
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
k8s.io/api v0.0.0-20181203235848-2dd39edadc55
k8s.io/api v0.0.0-20190118113203-912cbe2bfef3
k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc
k8s.io/apimachinery v0.0.0-20181203235515-3d8ee2261517
k8s.io/apimachinery v0.0.0-20190118094746-1525e4dadd2d
k8s.io/apiserver v0.0.0-20181026151315-13cfe3978170
k8s.io/client-go v8.0.0+incompatible
k8s.io/code-generator v0.0.0-20181203235156-f8cba74510f3
k8s.io/gengo v0.0.0-20181113154421-fd15ee9cc2f7 // indirect
k8s.io/klog v0.1.0 // indirect
k8s.io/kube-aggregator v0.0.0-20181204002017-122bac39d429
k8s.io/kube-openapi v0.0.0-20181031203759-72693cb1fadd
k8s.io/kubernetes v1.11.7-beta.0.0.20190112090204-23cf8fe78f62
k8s.io/kubernetes v1.11.7-beta.0.0.20190118094746-65ecaf067134
)
10 changes: 8 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3C
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/emicklei/go-restful v2.8.0+incompatible h1:wN8GCRDPGHguIynsnBartv5GUgGUg1LAU7+xnSn1j7Q=
github.com/emicklei/go-restful v2.8.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.8.1+incompatible h1:AyDqLHbJ1quqbWr/OWDw+PlIP8ZFoTmYrGYaxzrLbNg=
github.com/emicklei/go-restful v2.8.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6 h1:V94anc0ZG3Pa/cAMwP2m1aQW3+/FF8Qmw/GsFyTJAp4=
github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6/go.mod h1:qr0VowGBT4CS4Q8vFF8BSeKz34PuqKGxs/L0IAQA9DQ=
github.com/evanphx/json-patch v3.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
Expand Down Expand Up @@ -268,12 +270,16 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh
k8s.io/api v0.0.0-20180904230853-4e7be11eab3f/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/api v0.0.0-20181203235848-2dd39edadc55 h1:FmAMYGd999iHkN+swot+oART9AumJiAvH0idpIZ3Ozo=
k8s.io/api v0.0.0-20181203235848-2dd39edadc55/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/api v0.0.0-20190118113203-912cbe2bfef3 h1:lV0+KGoNkvZOt4zGT4H83hQrzWMt/US/LSz4z4+BQS4=
k8s.io/api v0.0.0-20190118113203-912cbe2bfef3/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apiextensions-apiserver v0.0.0-20180905004947-16750353bf97/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc h1:IOukeE9HtTwpLslbujLDfRpfFU6tsjq28yO0fjnl/hk=
k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
k8s.io/apimachinery v0.0.0-20180904193909-def12e63c512/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/apimachinery v0.0.0-20181203235515-3d8ee2261517 h1:p6GEgV1/cc7H0AT6XfjHwHNIypirOprIB09oKp2DQ/M=
k8s.io/apimachinery v0.0.0-20181203235515-3d8ee2261517/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/apimachinery v0.0.0-20190118094746-1525e4dadd2d h1:LfIlK5wCRzbVZ1WVwcabYwBTKZJehDSBWq9Xf1S5g+o=
k8s.io/apimachinery v0.0.0-20190118094746-1525e4dadd2d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/apiserver v0.0.0-20181026151315-13cfe3978170 h1:CqI85nZvPaV+7JFono0nAOGOx2brocqefcOhDPVhHKI=
k8s.io/apiserver v0.0.0-20181026151315-13cfe3978170/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w=
k8s.io/client-go v0.0.0-20180718001006-59698c7d9724/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
Expand All @@ -292,5 +298,5 @@ k8s.io/kube-aggregator v0.0.0-20181204002017-122bac39d429/go.mod h1:8sbzT4QQKDEm
k8s.io/kube-openapi v0.0.0-20181031203759-72693cb1fadd h1:ggv/Vfza0i5xuhUZyYyxcc25AmQvHY8Zi1C2m8WgBvA=
k8s.io/kube-openapi v0.0.0-20181031203759-72693cb1fadd/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
k8s.io/kubernetes v1.11.7-beta.0.0.20181219023948-b875d52ea96d/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/kubernetes v1.11.7-beta.0.0.20190112090204-23cf8fe78f62 h1:BCNTASEARMUY8An3j4C7XN+wGAT257QTWW7zVf3LsVI=
k8s.io/kubernetes v1.11.7-beta.0.0.20190112090204-23cf8fe78f62/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/kubernetes v1.11.7-beta.0.0.20190118094746-65ecaf067134 h1:ouH8QfIx4CAbCe1ZYEFyP6D2HOcYgqroGYjEgiiZeHc=
k8s.io/kubernetes v1.11.7-beta.0.0.20190118094746-65ecaf067134/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
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 @@ -43,6 +43,7 @@ const (
clusterRoleKind = "ClusterRole"
clusterRoleBindingKind = "ClusterRoleBinding"
serviceAccountKind = "ServiceAccount"
serviceKind = "Service"
roleKind = "Role"
roleBindingKind = "RoleBinding"
)
Expand Down Expand Up @@ -1031,6 +1032,41 @@ func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error {
// If no error occurred, mark the step as Created.
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
148 changes: 137 additions & 11 deletions pkg/controller/operators/catalog/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"testing"
"time"
"encoding/json"

"github.com/ghodss/yaml"
"github.com/sirupsen/logrus"
Expand All @@ -19,6 +20,8 @@ import (
k8sfake "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/cache"
apiregistrationfake "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/fake"
appsv1 "k8s.io/api/apps/v1"
rbacv1 "k8s.io/api/rbac/v1"

"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake"
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{
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,
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)
}
Loading

0 comments on commit 4aa7674

Please sign in to comment.