Skip to content
This repository has been archived by the owner on Sep 15, 2022. It is now read-only.

Commit

Permalink
Sustain service broker registration (#76)
Browse files Browse the repository at this point in the history
sustain broker registration if service instances exists
  • Loading branch information
piotrmiskiewicz authored Jan 3, 2020
1 parent 6536f9b commit e941b81
Show file tree
Hide file tree
Showing 25 changed files with 1,183 additions and 118 deletions.
2 changes: 2 additions & 0 deletions Gopkg.lock

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

3 changes: 3 additions & 0 deletions charts/helm-broker/templates/rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ rules:
- apiGroups: ["servicecatalog.k8s.io"]
resources: ["servicebrokers", "clusterservicebrokers"]
verbs: ["create","delete","list","get","update", "watch"]
- apiGroups: ["servicecatalog.k8s.io"]
resources: ["serviceinstance", "serviceclass", "clusterserviceclass"]
verbs: ["list","get","update", "watch"]
- apiGroups: ["rafter.kyma-project.io"]
resources: ["clusterassetgroups", "assetgroups"]
verbs: ["get", "create", "update", "delete", "list", "watch"]
Expand Down
7 changes: 5 additions & 2 deletions internal/controller/addons_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ type ReconcileAddonsConfiguration struct {
}

// NewReconcileAddonsConfiguration returns a new reconcile.Reconciler
func NewReconcileAddonsConfiguration(mgr manager.Manager, addonGetterFactory addonGetterFactory, chartStorage chartStorage, addonStorage addonStorage, brokerFacade brokerFacade, docsProvider docsProvider, brokerSyncer brokerSyncer, templateService templateService, tmpDir string, log logrus.FieldLogger) reconcile.Reconciler {
func NewReconcileAddonsConfiguration(mgr manager.Manager, addonGetterFactory addonGetterFactory,
chartStorage chartStorage, addonStorage addonStorage, brokerFacade brokerFacade, docsProvider docsProvider,
brokerSyncer brokerSyncer, templateService templateService, tmpDir string, log logrus.FieldLogger) reconcile.Reconciler {
return &ReconcileAddonsConfiguration{
log: log.WithField("controller", "addons"),
Client: mgr.GetClient(),

common: newControllerCommon(mgr.GetClient(), addonGetterFactory, addonStorage, chartStorage, docsProvider, brokerSyncer, brokerFacade, templateService, path.Join(tmpDir, "addon-loader-dst"), log),
common: newControllerCommon(mgr.GetClient(), addonGetterFactory, addonStorage, chartStorage,
docsProvider, brokerSyncer, brokerFacade, templateService, path.Join(tmpDir, "addon-loader-dst"), log),
}
}

Expand Down
46 changes: 12 additions & 34 deletions internal/controller/addons_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ func TestReconcileAddonsConfiguration_AddAddonsProcess(t *testing.T) {
}
}
ts.brokerFacade.On("Exist").Return(false, nil).Once()
ts.brokerFacade.On("Create").Return(nil).Once()
ts.addonGetterFactory.On("NewGetter", fixAddonsCfg.Spec.Repositories[0].URL, path.Join(tmpDir, "addon-loader-dst")).Return(ts.addonGetter, nil).Once()
defer ts.assertExpectations()

// WHEN
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage, ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage,
ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())

// THEN
result, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Namespace: fixAddonsCfg.Namespace, Name: fixAddonsCfg.Name}})
Expand Down Expand Up @@ -105,7 +105,8 @@ func TestReconcileAddonsConfiguration_AddAddonsProcess_ErrorIfBrokerExist(t *tes
defer ts.assertExpectations()

// WHEN
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage, ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage,
ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())

// THEN
result, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Namespace: fixAddonsCfg.Namespace, Name: fixAddonsCfg.Name}})
Expand Down Expand Up @@ -139,12 +140,12 @@ func TestReconcileAddonsConfiguration_UpdateAddonsProcess(t *testing.T) {
}
}
ts.brokerFacade.On("Exist").Return(false, nil).Once()
ts.brokerFacade.On("Create").Return(nil).Once()
ts.addonGetterFactory.On("NewGetter", fixAddonsCfg.Spec.Repositories[0].URL, path.Join(tmpDir, "addon-loader-dst")).Return(ts.addonGetter, nil).Once()
defer ts.assertExpectations()

// WHEN
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage, ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage,
ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())

// THEN
result, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Namespace: fixAddonsCfg.Namespace, Name: fixAddonsCfg.Name}})
Expand Down Expand Up @@ -175,7 +176,8 @@ func TestReconcileAddonsConfiguration_UpdateAddonsProcess_ConflictingAddons(t *t
defer ts.assertExpectations()

// WHEN
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage, ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage,
ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())

// THEN
result, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Namespace: fixAddonsCfg.Namespace, Name: fixAddonsCfg.Name}})
Expand All @@ -195,11 +197,11 @@ func TestReconcileAddonsConfiguration_DeleteAddonsProcess(t *testing.T) {
ts := getTestSuite(t, fixAddonsCfg)
tmpDir := os.TempDir()

ts.brokerFacade.On("Delete").Return(nil).Once()
defer ts.assertExpectations()

// WHEN
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage, ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage,
ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())

// THEN
result, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Namespace: fixAddonsCfg.Namespace, Name: fixAddonsCfg.Name}})
Expand All @@ -219,11 +221,11 @@ func TestReconcileAddonsConfiguration_DeleteAddonsProcess_ReconcileOtherAddons(t
ts := getTestSuite(t, fixAddonsCfg, failedAddCfg)
tmpDir := os.TempDir()

ts.brokerFacade.On("Delete").Return(nil).Once()
defer ts.assertExpectations()

// WHEN
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage, ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage,
ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())

// THEN
result, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Namespace: fixAddonsCfg.Namespace, Name: fixAddonsCfg.Name}})
Expand All @@ -241,30 +243,6 @@ func TestReconcileAddonsConfiguration_DeleteAddonsProcess_ReconcileOtherAddons(t
assert.NotContains(t, res.Finalizers, v1alpha1.FinalizerAddonsConfiguration)
}

func TestReconcileAddonsConfiguration_DeleteAddonsProcess_Error(t *testing.T) {
// GIVEN
fixAddonsCfg := fixDeletedAddonsConfiguration()
ts := getTestSuite(t, fixAddonsCfg)
tmpDir := os.TempDir()

ts.brokerFacade.On("Delete").Return(errors.New("")).Once()
defer ts.assertExpectations()

// WHEN
reconciler := NewReconcileAddonsConfiguration(ts.mgr, ts.addonGetterFactory, ts.chartStorage, ts.addonStorage, ts.brokerFacade, ts.docsProvider, ts.brokerSyncer, ts.templateService, tmpDir, spy.NewLogDummy())

// THEN
result, err := reconciler.Reconcile(reconcile.Request{NamespacedName: types.NamespacedName{Namespace: fixAddonsCfg.Namespace, Name: fixAddonsCfg.Name}})
assert.Error(t, err)
assert.False(t, result.Requeue)
assert.Equal(t, result.RequeueAfter, time.Second*15)

res := v1alpha1.AddonsConfiguration{}
err = ts.mgr.GetClient().Get(context.Background(), types.NamespacedName{Namespace: fixAddonsCfg.Namespace, Name: fixAddonsCfg.Name}, &res)
assert.NoError(t, err)
assert.Contains(t, res.Finalizers, v1alpha1.FinalizerAddonsConfiguration)
}

func fixRepositories() []v1alpha1.StatusRepository {
return []v1alpha1.StatusRepository{
{
Expand Down
2 changes: 1 addition & 1 deletion internal/controller/broker/broker_facade.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const (
BrokerLabelValue = "true"
)

// Facade is responsible for creation k8s objects for namespaced broker
// Facade is responsible for creation k8s objects for namespaced broker. The Facade is not thread-safe.
type Facade struct {
client client.Client
systemNamespace string
Expand Down
111 changes: 111 additions & 0 deletions internal/controller/broker_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package controller

import (
"context"

"github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1"
"github.com/kyma-project/helm-broker/internal/controller/broker"
"github.com/kyma-project/helm-broker/pkg/apis/addons/v1alpha1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)

// BrokerController is a controller which reacts on changes for ServiceInstance, ServiceBroker and AddonsConfiguration.
// Only this controller should create/delete ServiceBroker.
type BrokerController struct {
instanceChecker instanceChecker
cli client.Client

namespacedBrokerFacade brokerFacade
}

var createDeletePredicate = predicate.Funcs{
CreateFunc: func(_ event.CreateEvent) bool { return true },
DeleteFunc: func(_ event.DeleteEvent) bool { return true },
UpdateFunc: func(_ event.UpdateEvent) bool { return false },
}

var eventHandler = &handler.EnqueueRequestsFromMapFunc{
ToRequests: handler.ToRequestsFunc(
func(mp handler.MapObject) []reconcile.Request {
return []reconcile.Request{{NamespacedName: types.NamespacedName{Namespace: mp.Meta.GetNamespace()}}}
},
)}

// NewBrokerController creates BrokerController instance.
func NewBrokerController(checker instanceChecker, cli client.Client, bFacade brokerFacade) *BrokerController {
return &BrokerController{
instanceChecker: checker,
cli: cli,
namespacedBrokerFacade: bFacade,
}
}

// Start starts the controller
func (sbc *BrokerController) Start(mgr manager.Manager) error {
// Create a new controller
c, err := controller.New("broker-controller", mgr, controller.Options{Reconciler: sbc})
if err != nil {
return err
}

// Watch for changes to ServiceInstance
err = c.Watch(&source.Kind{Type: &v1beta1.ServiceInstance{}}, eventHandler, createDeletePredicate)
if err != nil {
return err
}

err = c.Watch(&source.Kind{Type: &v1alpha1.AddonsConfiguration{}}, eventHandler, createDeletePredicate)
if err != nil {
return err
}

err = c.Watch(&source.Kind{Type: &v1beta1.ServiceBroker{}}, eventHandler, predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool { return e.Meta.GetName() == broker.NamespacedBrokerName },
DeleteFunc: func(e event.DeleteEvent) bool { return e.Meta.GetName() == broker.NamespacedBrokerName },
UpdateFunc: func(_ event.UpdateEvent) bool { return false },
})
if err != nil {
return err
}

return nil
}

// Reconcile checks if the (cluster) service broker must be removed
func (sbc *BrokerController) Reconcile(request reconcile.Request) (reconcile.Result, error) {
currentNamespace := request.Namespace

sbc.namespacedBrokerFacade.SetNamespace(request.Namespace)
sbExists, err := sbc.namespacedBrokerFacade.Exist()
if err != nil {
return reconcile.Result{}, err
}
// list addons configurations
acList := v1alpha1.AddonsConfigurationList{}
err = sbc.cli.List(context.TODO(), &client.ListOptions{Namespace: currentNamespace}, &acList)
if err != nil {
return reconcile.Result{}, err
}
anyNamespacedConfigExists := len(acList.Items) > 0

instanceByNamespacedExists, err := sbc.instanceChecker.AnyServiceInstanceExistsForNamespacedServiceBroker(currentNamespace)
if err != nil {
return reconcile.Result{}, err
}
if sbExists && !anyNamespacedConfigExists && !instanceByNamespacedExists {
sbc.namespacedBrokerFacade.Delete()
}
if !sbExists && (anyNamespacedConfigExists || instanceByNamespacedExists) {
sbc.namespacedBrokerFacade.Create()
}

return reconcile.Result{}, nil
}
Loading

0 comments on commit e941b81

Please sign in to comment.