Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(installplans): add ability to apply Services #674

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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=
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 @@ -43,6 +43,7 @@ const (
clusterRoleKind = "ClusterRole"
clusterRoleBindingKind = "ClusterRoleBinding"
serviceAccountKind = "ServiceAccount"
serviceKind = "Service"
roleKind = "Role"
roleBindingKind = "RoleBinding"
)
Expand Down Expand Up @@ -1032,6 +1033,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)
}
Loading