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

Implement watches for label selectors #1354

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 50 additions & 3 deletions controllers/binding/servicebinding_controller.go
Expand Up @@ -17,6 +17,9 @@ limitations under the License.
package binding

import (
ctx "context"
"fmt"

"github.com/go-logr/logr"
"github.com/redhat-developer/service-binding-operator/apis"
"github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1"
Expand All @@ -26,10 +29,12 @@ import (
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/builder"
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/context"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic"
authv1 "k8s.io/client-go/kubernetes/typed/authorization/v1"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

// ServiceBindingReconciler reconciles a ServiceBinding object
Expand All @@ -41,10 +46,27 @@ type ServiceBindingReconciler struct {
// +kubebuilder:rbac:groups=binding.operators.coreos.com,resources=servicebindings/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=binding.operators.coreos.com,resources=servicebindings/finalizers,verbs=update

func New(client client.Client, log logr.Logger, scheme *runtime.Scheme) *ServiceBindingReconciler {
func validateLabels(fromSB, fromResource map[string]string) bool {
fl := len(fromSB)
l := 0
for k, v := range fromResource {
for m, n := range fromSB {
fmt.Println(k, v, m, n)
if k == m && v == n {
l = l + 1
}
}
}
if fl == l {
return true
}
return false
}

func New(clnt client.Client, log logr.Logger, scheme *runtime.Scheme) *ServiceBindingReconciler {
r := &ServiceBindingReconciler{
BindingReconciler: controllers.BindingReconciler{
Client: client,
Client: clnt,
Log: log,
Scheme: scheme,
PipelineProvider: func(conf *rest.Config, lookup kubernetes.K8STypeLookup) (pipeline.Pipeline, error) {
Expand All @@ -61,6 +83,31 @@ func New(client client.Client, log logr.Logger, scheme *runtime.Scheme) *Service
ReconcilingObject: func() apis.Object { return &v1alpha1.ServiceBinding{} },
},
}

r.MapWorkloadToSB = func(a client.Object) []reconcile.Request {
sbList := &v1alpha1.ServiceBindingList{}
opts := &client.ListOptions{}
if err := r.List(ctx.Background(), sbList, opts); err != nil {
return []reconcile.Request{}
}
reply := make([]reconcile.Request, 0, len(sbList.Items))
for _, sb := range sbList.Items {
if sb.Spec.Application.Kind == a.GetObjectKind().GroupVersionKind().Kind &&
validateLabels(sb.Spec.Application.LabelSelector.MatchLabels, a.GetLabels()) {
reply = append(reply, reconcile.Request{NamespacedName: types.NamespacedName{
Namespace: sb.Namespace,
Name: sb.Name,
}})
}
}
return reply
}
r.ResourceToWatch = func(ctx ctx.Context, key client.ObjectKey) (string, string, string) {
sb := &v1alpha1.ServiceBinding{}
err := r.Get(ctx, key, sb)
if err != nil {
return sb.Spec.Application.Group, sb.Spec.Application.Version, sb.Spec.Application.Kind
}
return "", "", ""
}
return r
}
33 changes: 31 additions & 2 deletions controllers/common.go
Expand Up @@ -10,12 +10,18 @@ import (
"github.com/redhat-developer/service-binding-operator/apis"
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
)

// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list
Expand Down Expand Up @@ -43,9 +49,13 @@ type BindingReconciler struct {

pipeline pipeline.Pipeline

ctrl controller.Controller

PipelineProvider func(*rest.Config, kubernetes.K8STypeLookup) (pipeline.Pipeline, error)

ReconcilingObject func() apis.Object
MapWorkloadToSB func(a client.Object) []reconcile.Request
ResourceToWatch func(ctx context.Context, key client.ObjectKey) (string, string, string)
}

// SetupWithManager sets up the controller with the Manager.
Expand All @@ -56,13 +66,17 @@ func (r *BindingReconciler) SetupWithManager(mgr ctrl.Manager) error {
}
r.pipeline = pipeline
p := predicate.Or(predicate.GenerationChangedPredicate{}, predicate.AnnotationChangedPredicate{})
return ctrl.NewControllerManagedBy(mgr).
ctrl, err := ctrl.NewControllerManagedBy(mgr).
For(r.ReconcilingObject()).
WithEventFilter(p).
WithOptions(controller.Options{MaxConcurrentReconciles: MaxConcurrentReconciles}).
Complete(r)
Build(r)
r.ctrl = ctrl
return err
}

var watchMap map[string]string = map[string]string{}

// +kubebuilder:rbac:groups=authorization.k8s.io,resources=subjectaccessreviews,verbs=create
// +kubebuilder:rbac:groups=authorization.k8s.io,resources=selfsubjectaccessreviews,verbs=create
// +kubebuilder:rbac:groups=authentication.k8s.io,resources=tokenreviews,verbs=create
Expand All @@ -79,6 +93,21 @@ func (r *BindingReconciler) SetupWithManager(mgr ctrl.Manager) error {
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.7.0/pkg/reconcile
func (r *BindingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {

g, v, k := r.ResourceToWatch(ctx, req.NamespacedName)
resource := &unstructured.Unstructured{}
gvk := schema.GroupVersionKind{Group: g, Version: v, Kind: k}
resource.SetGroupVersionKind(gvk)
if _, ok := watchMap[gvk.String()]; !ok {
watchMap[gvk.String()] = ""
err := r.ctrl.Watch(
&source.Kind{Type: resource},
handler.EnqueueRequestsFromMapFunc(r.MapWorkloadToSB))
if err != nil {
log.Log.Error(err, "error watching", "resource", resource)
}
}

log := r.Log.WithValues("serviceBinding", req.NamespacedName)
serviceBinding := r.ReconcilingObject()

Expand Down
62 changes: 60 additions & 2 deletions controllers/spec/servicebinding_controller.go
Expand Up @@ -17,6 +17,10 @@ limitations under the License.
package spec

import (
ctx "context"
"fmt"
"strings"

"github.com/go-logr/logr"
"github.com/redhat-developer/service-binding-operator/apis"
specv1beta1 "github.com/redhat-developer/service-binding-operator/apis/spec/v1beta1"
Expand All @@ -26,10 +30,12 @@ import (
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/builder"
"github.com/redhat-developer/service-binding-operator/pkg/reconcile/pipeline/context"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/dynamic"
authv1 "k8s.io/client-go/kubernetes/typed/authorization/v1"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

// ServiceBindingReconciler reconciles a ServiceBinding object
Expand All @@ -41,10 +47,27 @@ type ServiceBindingReconciler struct {
// +kubebuilder:rbac:groups=servicebinding.io,resources=servicebindings/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=servicebinding.io,resources=servicebindings/finalizers,verbs=update

func New(client client.Client, log logr.Logger, scheme *runtime.Scheme) *ServiceBindingReconciler {
func validateLabels(fromSB, fromResource map[string]string) bool {
fl := len(fromSB)
l := 0
for k, v := range fromResource {
for m, n := range fromSB {
fmt.Println(k, v, m, n)
if k == m && v == n {
l = l + 1
}
}
}
if fl == l {
return true
}
return false
}

func New(clnt client.Client, log logr.Logger, scheme *runtime.Scheme) *ServiceBindingReconciler {
r := &ServiceBindingReconciler{
BindingReconciler: controllers.BindingReconciler{
Client: client,
Client: clnt,
Log: log,
Scheme: scheme,
PipelineProvider: func(conf *rest.Config, lookup kubernetes.K8STypeLookup) (pipeline.Pipeline, error) {
Expand All @@ -61,6 +84,41 @@ func New(client client.Client, log logr.Logger, scheme *runtime.Scheme) *Service
ReconcilingObject: func() apis.Object { return &specv1beta1.ServiceBinding{} },
},
}
r.MapWorkloadToSB = func(a client.Object) []reconcile.Request {
sbList := &specv1beta1.ServiceBindingList{}
opts := &client.ListOptions{}
if err := r.List(ctx.Background(), sbList, opts); err != nil {
return []reconcile.Request{}
}
reply := make([]reconcile.Request, 0, len(sbList.Items))
for _, sb := range sbList.Items {
if sb.Spec.Workload.Kind == a.GetObjectKind().GroupVersionKind().Kind &&
validateLabels(sb.Spec.Workload.Selector.MatchLabels, a.GetLabels()) {
reply = append(reply, reconcile.Request{NamespacedName: types.NamespacedName{
Namespace: sb.Namespace,
Name: sb.Name,
}})
}
}
return reply
}
r.ResourceToWatch = func(ctx ctx.Context, key client.ObjectKey) (string, string, string) {
sb := &specv1beta1.ServiceBinding{}
err := r.Get(ctx, key, sb)
if err != nil {
av := strings.Split(sb.Spec.Workload.APIVersion, "/")
var g, v, k string
if len(av) > 1 {
g = av[0]
v = av[1]
} else {
v = av[0]
}
k = sb.Spec.Workload.Kind
return g, v, k
}
return "", "", ""
}

return r
}