Skip to content

Commit 4aa7674

Browse files
committed
fix(installplans): add ability to apply Services
1 parent 4d6a1e5 commit 4aa7674

File tree

16 files changed

+235
-34
lines changed

16 files changed

+235
-34
lines changed

go.mod

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ require (
55
github.com/coreos/go-semver v0.2.0
66
github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 // indirect
77
github.com/docker/distribution v2.7.0+incompatible // indirect
8+
github.com/emicklei/go-restful v2.8.1+incompatible // indirect
89
github.com/evanphx/json-patch v4.1.0+incompatible // indirect
910
github.com/ghodss/yaml v1.0.0
1011
github.com/go-openapi/spec v0.17.2
@@ -44,15 +45,15 @@ require (
4445
google.golang.org/grpc v1.16.0
4546
gopkg.in/inf.v0 v0.9.1 // indirect
4647
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
47-
k8s.io/api v0.0.0-20181203235848-2dd39edadc55
48+
k8s.io/api v0.0.0-20190118113203-912cbe2bfef3
4849
k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc
49-
k8s.io/apimachinery v0.0.0-20181203235515-3d8ee2261517
50+
k8s.io/apimachinery v0.0.0-20190118094746-1525e4dadd2d
5051
k8s.io/apiserver v0.0.0-20181026151315-13cfe3978170
5152
k8s.io/client-go v8.0.0+incompatible
5253
k8s.io/code-generator v0.0.0-20181203235156-f8cba74510f3
5354
k8s.io/gengo v0.0.0-20181113154421-fd15ee9cc2f7 // indirect
5455
k8s.io/klog v0.1.0 // indirect
5556
k8s.io/kube-aggregator v0.0.0-20181204002017-122bac39d429
5657
k8s.io/kube-openapi v0.0.0-20181031203759-72693cb1fadd
57-
k8s.io/kubernetes v1.11.7-beta.0.0.20190112090204-23cf8fe78f62
58+
k8s.io/kubernetes v1.11.7-beta.0.0.20190118094746-65ecaf067134
5859
)

go.sum

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3C
4141
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
4242
github.com/emicklei/go-restful v2.8.0+incompatible h1:wN8GCRDPGHguIynsnBartv5GUgGUg1LAU7+xnSn1j7Q=
4343
github.com/emicklei/go-restful v2.8.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
44+
github.com/emicklei/go-restful v2.8.1+incompatible h1:AyDqLHbJ1quqbWr/OWDw+PlIP8ZFoTmYrGYaxzrLbNg=
45+
github.com/emicklei/go-restful v2.8.1+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
4446
github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6 h1:V94anc0ZG3Pa/cAMwP2m1aQW3+/FF8Qmw/GsFyTJAp4=
4547
github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6/go.mod h1:qr0VowGBT4CS4Q8vFF8BSeKz34PuqKGxs/L0IAQA9DQ=
4648
github.com/evanphx/json-patch v3.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
@@ -268,12 +270,16 @@ honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWh
268270
k8s.io/api v0.0.0-20180904230853-4e7be11eab3f/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
269271
k8s.io/api v0.0.0-20181203235848-2dd39edadc55 h1:FmAMYGd999iHkN+swot+oART9AumJiAvH0idpIZ3Ozo=
270272
k8s.io/api v0.0.0-20181203235848-2dd39edadc55/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
273+
k8s.io/api v0.0.0-20190118113203-912cbe2bfef3 h1:lV0+KGoNkvZOt4zGT4H83hQrzWMt/US/LSz4z4+BQS4=
274+
k8s.io/api v0.0.0-20190118113203-912cbe2bfef3/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
271275
k8s.io/apiextensions-apiserver v0.0.0-20180905004947-16750353bf97/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
272276
k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc h1:IOukeE9HtTwpLslbujLDfRpfFU6tsjq28yO0fjnl/hk=
273277
k8s.io/apiextensions-apiserver v0.0.0-20181204003618-e419c5771cdc/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
274278
k8s.io/apimachinery v0.0.0-20180904193909-def12e63c512/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
275279
k8s.io/apimachinery v0.0.0-20181203235515-3d8ee2261517 h1:p6GEgV1/cc7H0AT6XfjHwHNIypirOprIB09oKp2DQ/M=
276280
k8s.io/apimachinery v0.0.0-20181203235515-3d8ee2261517/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
281+
k8s.io/apimachinery v0.0.0-20190118094746-1525e4dadd2d h1:LfIlK5wCRzbVZ1WVwcabYwBTKZJehDSBWq9Xf1S5g+o=
282+
k8s.io/apimachinery v0.0.0-20190118094746-1525e4dadd2d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
277283
k8s.io/apiserver v0.0.0-20181026151315-13cfe3978170 h1:CqI85nZvPaV+7JFono0nAOGOx2brocqefcOhDPVhHKI=
278284
k8s.io/apiserver v0.0.0-20181026151315-13cfe3978170/go.mod h1:6bqaTSOSJavUIXUtfaR9Os9JtTCm8ZqH2SUl2S60C4w=
279285
k8s.io/client-go v0.0.0-20180718001006-59698c7d9724/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
@@ -292,5 +298,5 @@ k8s.io/kube-aggregator v0.0.0-20181204002017-122bac39d429/go.mod h1:8sbzT4QQKDEm
292298
k8s.io/kube-openapi v0.0.0-20181031203759-72693cb1fadd h1:ggv/Vfza0i5xuhUZyYyxcc25AmQvHY8Zi1C2m8WgBvA=
293299
k8s.io/kube-openapi v0.0.0-20181031203759-72693cb1fadd/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
294300
k8s.io/kubernetes v1.11.7-beta.0.0.20181219023948-b875d52ea96d/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
295-
k8s.io/kubernetes v1.11.7-beta.0.0.20190112090204-23cf8fe78f62 h1:BCNTASEARMUY8An3j4C7XN+wGAT257QTWW7zVf3LsVI=
296-
k8s.io/kubernetes v1.11.7-beta.0.0.20190112090204-23cf8fe78f62/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
301+
k8s.io/kubernetes v1.11.7-beta.0.0.20190118094746-65ecaf067134 h1:ouH8QfIx4CAbCe1ZYEFyP6D2HOcYgqroGYjEgiiZeHc=
302+
k8s.io/kubernetes v1.11.7-beta.0.0.20190118094746-65ecaf067134/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=

pkg/api/apis/operators/v1alpha1/installplan_types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import (
44
"errors"
55
"fmt"
66

7-
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators"
87
corev1 "k8s.io/api/core/v1"
98
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
10+
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators"
1011
)
1112

1213
const (

pkg/controller/operators/catalog/operator.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const (
4343
clusterRoleKind = "ClusterRole"
4444
clusterRoleBindingKind = "ClusterRoleBinding"
4545
serviceAccountKind = "ServiceAccount"
46+
serviceKind = "Service"
4647
roleKind = "Role"
4748
roleBindingKind = "RoleBinding"
4849
)
@@ -1031,6 +1032,41 @@ func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error {
10311032
// If no error occurred, mark the step as Created.
10321033
plan.Status.Plan[i].Status = v1alpha1.StepStatusCreated
10331034
}
1035+
1036+
case serviceKind:
1037+
// Marshal the manifest into a Service instance
1038+
var s corev1.Service
1039+
err := json.Unmarshal([]byte(step.Resource.Manifest), &s)
1040+
if err != nil {
1041+
return errorwrap.Wrapf(err, "error parsing step manifest: %s", step.Resource.Name)
1042+
}
1043+
1044+
// Update UIDs on all CSV OwnerReferences
1045+
updated, err := o.getUpdatedOwnerReferences(s.OwnerReferences, plan.Namespace)
1046+
if err != nil {
1047+
return errorwrap.Wrapf(err, "error generating ownerrefs for service: %s", s.GetName())
1048+
}
1049+
s.SetOwnerReferences(updated)
1050+
s.SetNamespace(namespace)
1051+
1052+
// Attempt to create the Service
1053+
_, err = o.OpClient.KubernetesInterface().CoreV1().Services(plan.Namespace).Create(&s)
1054+
if k8serrors.IsAlreadyExists(err) {
1055+
// If it already exists we need to patch the existing SA with the new OwnerReferences
1056+
s.SetNamespace(plan.Namespace)
1057+
_, err = o.OpClient.UpdateService(&s)
1058+
if err != nil {
1059+
return errorwrap.Wrapf(err, "error updating service: %s", s.GetName())
1060+
}
1061+
1062+
// Mark as present
1063+
plan.Status.Plan[i].Status = v1alpha1.StepStatusPresent
1064+
} else if err != nil {
1065+
return errorwrap.Wrapf(err, "error creating service: %s", s.GetName())
1066+
} else {
1067+
// If no error occurred, mark the step as Created
1068+
plan.Status.Plan[i].Status = v1alpha1.StepStatusCreated
1069+
}
10341070

10351071
default:
10361072
return v1alpha1.ErrInvalidInstallPlan

pkg/controller/operators/catalog/operator_test.go

Lines changed: 137 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"testing"
77
"time"
8+
"encoding/json"
89

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

2326
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
2427
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake"
@@ -113,6 +116,98 @@ func TestTransitionInstallPlan(t *testing.T) {
113116
}
114117
}
115118

119+
func TestExecutePlan(t *testing.T) {
120+
namespace := "ns"
121+
122+
tests := []struct{
123+
testName string
124+
in *v1alpha1.InstallPlan
125+
want []runtime.Object
126+
err error
127+
}{
128+
{
129+
testName: "NoSteps",
130+
in: installPlan("p", namespace, v1alpha1.InstallPlanPhaseInstalling),
131+
want: []runtime.Object{},
132+
err: nil,
133+
},
134+
{
135+
testName: "MultipleSteps",
136+
in: withSteps(installPlan("p", namespace, v1alpha1.InstallPlanPhaseInstalling, "csv"),
137+
[]*v1alpha1.Step{
138+
&v1alpha1.Step{
139+
Resource: v1alpha1.StepResource{
140+
CatalogSource: "catalog",
141+
CatalogSourceNamespace: namespace,
142+
Group: "",
143+
Version: "v1",
144+
Kind: "Service",
145+
Name: "service",
146+
Manifest: toManifest(service("service", namespace)),
147+
},
148+
Status: v1alpha1.StepStatusUnknown,
149+
},
150+
&v1alpha1.Step{
151+
Resource: v1alpha1.StepResource{
152+
CatalogSource: "catalog",
153+
CatalogSourceNamespace: namespace,
154+
Group: "operators.coreos.com",
155+
Version: "v1alpha1",
156+
Kind: "ClusterServiceVersion",
157+
Name: "csv",
158+
Manifest: toManifest(csv("csv", namespace, nil, nil)),
159+
},
160+
Status: v1alpha1.StepStatusUnknown,
161+
},
162+
},
163+
),
164+
want: []runtime.Object{service("service", namespace), csv("csv", namespace, nil, nil)},
165+
err: nil,
166+
},
167+
}
168+
169+
for _, tt := range tests {
170+
t.Run(tt.testName, func(t *testing.T) {
171+
stopCh := make(chan struct{})
172+
defer func() { stopCh <- struct{}{} }()
173+
op, _, err := NewFakeOperator([]runtime.Object{tt.in}, nil, nil, nil, namespace, stopCh)
174+
require.NoError(t, err)
175+
176+
err = op.ExecutePlan(tt.in)
177+
require.Equal(t, tt.err, err)
178+
179+
for _, obj := range tt.want {
180+
var err error
181+
var fetched runtime.Object
182+
switch o := obj.(type) {
183+
case *appsv1.Deployment:
184+
fetched, err = op.OpClient.GetDeployment(namespace, o.GetName())
185+
case *rbacv1.ClusterRole:
186+
fetched, err = op.OpClient.GetClusterRole(o.GetName())
187+
case *rbacv1.Role:
188+
fetched, err = op.OpClient.GetRole(namespace, o.GetName())
189+
case *rbacv1.ClusterRoleBinding:
190+
fetched, err = op.OpClient.GetClusterRoleBinding(o.GetName())
191+
case *rbacv1.RoleBinding:
192+
fetched, err = op.OpClient.GetRoleBinding(namespace, o.GetName())
193+
case *corev1.ServiceAccount:
194+
fetched, err = op.OpClient.GetServiceAccount(namespace, o.GetName())
195+
case *corev1.Service:
196+
fetched, err = op.OpClient.GetService(namespace, o.GetName())
197+
case *v1alpha1.ClusterServiceVersion:
198+
fetched, err = op.client.OperatorsV1alpha1().ClusterServiceVersions(namespace).Get(o.GetName(), metav1.GetOptions{})
199+
default:
200+
require.Failf(t, "couldn't find expected object", "%#v", obj)
201+
}
202+
203+
require.NoError(t, err, "couldn't fetch %s %v", namespace, obj)
204+
fmt.Printf("fetched: %v", fetched)
205+
require.EqualValues(t, obj, fetched)
206+
}
207+
})
208+
}
209+
}
210+
116211
func TestSyncCatalogSources(t *testing.T) {
117212
tests := []struct {
118213
testName string
@@ -284,21 +379,21 @@ func TestCompetingCRDOwnersExist(t *testing.T) {
284379
testNamespace := "default"
285380
tests := []struct {
286381
name string
287-
csv v1alpha1.ClusterServiceVersion
382+
csv *v1alpha1.ClusterServiceVersion
288383
existingCRDOwners map[string][]string
289384
expectedErr error
290385
expectedResult bool
291386
}{
292387
{
293388
name: "NoCompetingOwnersExist",
294-
csv: csv("turkey", []string{"feathers"}, nil),
389+
csv: csv("turkey", testNamespace, []string{"feathers"}, nil),
295390
existingCRDOwners: nil,
296391
expectedErr: nil,
297392
expectedResult: false,
298393
},
299394
{
300395
name: "OnlyCompetingWithSelf",
301-
csv: csv("turkey", []string{"feathers"}, nil),
396+
csv: csv("turkey", testNamespace, []string{"feathers"}, nil),
302397
existingCRDOwners: map[string][]string{
303398
"feathers": {"turkey"},
304399
},
@@ -307,7 +402,7 @@ func TestCompetingCRDOwnersExist(t *testing.T) {
307402
},
308403
{
309404
name: "CompetingOwnersExist",
310-
csv: csv("turkey", []string{"feathers"}, nil),
405+
csv: csv("turkey", testNamespace, []string{"feathers"}, nil),
311406
existingCRDOwners: map[string][]string{
312407
"feathers": {"seagull"},
313408
},
@@ -316,7 +411,7 @@ func TestCompetingCRDOwnersExist(t *testing.T) {
316411
},
317412
{
318413
name: "CompetingOwnerExistsOnSecondCRD",
319-
csv: csv("turkey", []string{"feathers", "beak"}, nil),
414+
csv: csv("turkey", testNamespace, []string{"feathers", "beak"}, nil),
320415
existingCRDOwners: map[string][]string{
321416
"milk": {"cow"},
322417
"beak": {"squid"},
@@ -326,7 +421,7 @@ func TestCompetingCRDOwnersExist(t *testing.T) {
326421
},
327422
{
328423
name: "MoreThanOneCompetingOwnerExists",
329-
csv: csv("turkey", []string{"feathers"}, nil),
424+
csv: csv("turkey", testNamespace, []string{"feathers"}, nil),
330425
existingCRDOwners: map[string][]string{
331426
"feathers": {"seagull", "turkey"},
332427
},
@@ -338,7 +433,7 @@ func TestCompetingCRDOwnersExist(t *testing.T) {
338433
t.Run(tt.name, func(t *testing.T) {
339434
t.Parallel()
340435

341-
competing, err := competingCRDOwnersExist(testNamespace, &tt.csv, tt.existingCRDOwners)
436+
competing, err := competingCRDOwnersExist(testNamespace, tt.csv, tt.existingCRDOwners)
342437

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

443-
func installPlan(names ...string) v1alpha1.InstallPlan {
444-
return v1alpha1.InstallPlan{
538+
func installPlan(name, namespace string, phase v1alpha1.InstallPlanPhase, names ...string) *v1alpha1.InstallPlan {
539+
return &v1alpha1.InstallPlan{
540+
ObjectMeta: metav1.ObjectMeta{
541+
Name: name,
542+
Namespace: namespace,
543+
},
445544
Spec: v1alpha1.InstallPlanSpec{
446545
ClusterServiceVersionNames: names,
447546
},
448547
Status: v1alpha1.InstallPlanStatus{
548+
Phase: phase,
449549
Plan: []*v1alpha1.Step{},
450550
},
451551
}
452552
}
453553

454-
func csv(name string, owned, required []string) v1alpha1.ClusterServiceVersion {
554+
func withSteps(plan *v1alpha1.InstallPlan, steps []*v1alpha1.Step) *v1alpha1.InstallPlan {
555+
plan.Status.Plan = steps
556+
return plan
557+
}
558+
559+
func csv(name, namespace string, owned, required []string) *v1alpha1.ClusterServiceVersion {
455560
requiredCRDDescs := make([]v1alpha1.CRDDescription, 0)
456561
for _, name := range required {
457562
requiredCRDDescs = append(requiredCRDDescs, v1alpha1.CRDDescription{Name: name, Version: "v1", Kind: name})
458563
}
564+
if len(requiredCRDDescs) == 0 {
565+
requiredCRDDescs = nil
566+
}
459567

460568
ownedCRDDescs := make([]v1alpha1.CRDDescription, 0)
461569
for _, name := range owned {
462570
ownedCRDDescs = append(ownedCRDDescs, v1alpha1.CRDDescription{Name: name, Version: "v1", Kind: name})
463571
}
572+
if len(ownedCRDDescs) == 0 {
573+
ownedCRDDescs = nil
574+
}
464575

465-
return v1alpha1.ClusterServiceVersion{
576+
return &v1alpha1.ClusterServiceVersion{
466577
ObjectMeta: metav1.ObjectMeta{
467578
Name: name,
579+
Namespace: namespace,
468580
},
469581
Spec: v1alpha1.ClusterServiceVersionSpec{
470582
CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{
@@ -489,3 +601,17 @@ func crd(name string) v1beta1.CustomResourceDefinition {
489601
},
490602
}
491603
}
604+
605+
func service(name, namespace string) *corev1.Service {
606+
return &corev1.Service{
607+
ObjectMeta: metav1.ObjectMeta{
608+
Name: name,
609+
Namespace: namespace,
610+
},
611+
}
612+
}
613+
614+
func toManifest(obj runtime.Object) string {
615+
raw, _ := json.Marshal(obj)
616+
return string(raw)
617+
}

0 commit comments

Comments
 (0)