Skip to content
Draft
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
10 changes: 7 additions & 3 deletions internal/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,12 @@ type Controller struct {
gardenerCertInformerFactory gardenerCertInformers.SharedInformerFactory
certManagerInformerFactory certManagerInformers.SharedInformerFactory
gardenerDNSInformerFactory gardenerDNSInformers.SharedInformerFactory
queues map[int]workqueue.TypedRateLimitingInterface[QueueItem]
eventBroadcaster events.EventBroadcaster
eventRecorder events.EventRecorder
// per-namespace secret informers
nsSecretInformers map[string]informers.SharedInformerFactory
nsSecretInformersMu sync.RWMutex
queues map[int]workqueue.TypedRateLimitingInterface[QueueItem]
eventBroadcaster events.EventBroadcaster
eventRecorder events.EventRecorder
}

var (
Expand Down Expand Up @@ -134,6 +137,7 @@ func NewController(client kubernetes.Interface, crdClient versioned.Interface, i
gardenerCertInformerFactory: gardenerCertInformerFactory,
certManagerInformerFactory: certManagerInformerFactory,
gardenerDNSInformerFactory: gardenerDNSInformerFactory,
nsSecretInformers: map[string]informers.SharedInformerFactory{},
queues: queues,
eventBroadcaster: eventBroadcaster,
eventRecorder: recorder,
Expand Down
63 changes: 53 additions & 10 deletions internal/controller/informers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ SPDX-License-Identifier: Apache-2.0
package controller

import (
"context"
"reflect"
"time"

"github.com/sap/cap-operator/pkg/apis/sme.sap.com/v1alpha1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
)
Expand Down Expand Up @@ -80,7 +83,6 @@ func (c *Controller) initializeInformers() {
c.registerDomainListeners()
c.registerClusterDomainListeners()
c.registerJobListeners()
c.registerSecretListeners()
c.registerGatewayListeners()
c.registerVirtualServiceListeners()
c.registerDestinationRuleListeners()
Expand All @@ -100,10 +102,6 @@ func (c *Controller) initializeInformers() {
}

func (c *Controller) getEventHandlerFuncsForResource(res int) cache.ResourceEventHandlerFuncs {
_, ok := QueueMapping[res]
if !ok {
return cache.ResourceEventHandlerFuncs{}
}
return cache.ResourceEventHandlerFuncs{
AddFunc: func(new any) {
c.enqueueModifiedResource(res, new, nil)
Expand Down Expand Up @@ -162,11 +160,6 @@ func (c *Controller) registerDestinationRuleListeners() {
AddEventHandler(c.getEventHandlerFuncsForResource(ResourceDestinationRule))
}

func (c *Controller) registerSecretListeners() {
c.kubeInformerFactory.Core().V1().Secrets().Informer().
AddEventHandler(c.getEventHandlerFuncsForResource(ResourceSecret))
}

func (c *Controller) registerGatewayListeners() {
c.istioInformerFactory.Networking().V1().Gateways().Informer().
AddEventHandler(c.getEventHandlerFuncsForResource(ResourceGateway))
Expand All @@ -187,6 +180,56 @@ func (c *Controller) registerGardenerDNSEntrytListeners() {
AddEventHandler(c.getEventHandlerFuncsForResource(ResourceDNSEntry))
}

// Creates and Start a namespace-scoped secret informer for the namespace if one does not already exist.
func (c *Controller) ensureSecretInformerForNamespace(namespace string, ctx context.Context) {
c.nsSecretInformersMu.Lock()
defer c.nsSecretInformersMu.Unlock()

if c.nsSecretInformers == nil {
c.nsSecretInformers = map[string]informers.SharedInformerFactory{}
}

if _, exists := c.nsSecretInformers[namespace]; exists {
return
}

factory := informers.NewSharedInformerFactoryWithOptions(
c.kubeClient,
30*time.Minute,
informers.WithNamespace(namespace),
)

// Register
factory.Core().V1().Secrets().Informer().AddEventHandler(c.secretEventHandlers())

factory.Start(ctx.Done())
factory.WaitForCacheSync(ctx.Done())

c.nsSecretInformers[namespace] = factory
klog.InfoS("started namespace-scoped secret informer", "namespace", namespace)
}

// returns the secret lister for the given namespace.
func (c *Controller) secretListerForNamespace(namespace string) corev1listers.SecretNamespaceLister {
c.nsSecretInformersMu.RLock()
var factory informers.SharedInformerFactory
if c.nsSecretInformers != nil {
factory = c.nsSecretInformers[namespace]
}
c.nsSecretInformersMu.RUnlock()

if factory != nil {
return factory.Core().V1().Secrets().Lister().Secrets(namespace)
}
return c.kubeInformerFactory.Core().V1().Secrets().Lister().Secrets(namespace)
}

func (c *Controller) secretEventHandlers() cache.ResourceEventHandlerFuncs {
// Ignore notifications for objects for now --> we primarily only want to list these secrets for now.
// This could be implemented to used to add potentional handlers for Credential Rotation based rollouts
return cache.ResourceEventHandlerFuncs{}
}

func (c *Controller) enqueueModifiedResource(sourceKey int, new, old any) {
newObj, newOk := getMetaObject(new)
oldObj, oldOk := getMetaObject(old)
Expand Down
3 changes: 3 additions & 0 deletions internal/controller/reconcile-capapplication.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ func (c *Controller) reconcileCAPApplication(ctx context.Context, item QueueItem
}
ca := cached.DeepCopy()

// ensure a namespace-scoped secret informer is running for this CA's namespace
c.ensureSecretInformerForNamespace(ca.Namespace, ctx)

// prepare annotations, labels, finalizers
if c.prepareCAPApplication(ca) {
if err = c.updateCAPApplication(ctx, ca); err == nil {
Expand Down
25 changes: 13 additions & 12 deletions internal/controller/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/klog/v2"
)

Expand Down Expand Up @@ -286,11 +286,11 @@ func (c *Controller) getCachedCAPApplicationVersions(ca *v1alpha1.CAPApplication

func (c *Controller) checkSecretsExist(serviceInfos []v1alpha1.ServiceInfo, namespace string) error {
var err error
secretLister := c.kubeInformerFactory.Core().V1().Secrets().Lister()
secretLister := c.secretListerForNamespace(namespace)

for _, service := range serviceInfos {
secretName := service.Secret
if _, err = secretLister.Secrets(namespace).Get(secretName); err != nil {
if _, err = secretLister.Get(secretName); err != nil {
break
}
}
Expand All @@ -300,11 +300,11 @@ func (c *Controller) checkSecretsExist(serviceInfos []v1alpha1.ServiceInfo, name
func (c *Controller) checkAndPreserveSecrets(serviceInfos []v1alpha1.ServiceInfo, namespace string) error {
var err error
var secret *corev1.Secret
secretLister := c.kubeInformerFactory.Core().V1().Secrets().Lister()
secretLister := c.secretListerForNamespace(namespace)

for _, service := range serviceInfos {
secretName := service.Secret
if secret, err = secretLister.Secrets(namespace).Get(secretName); err != nil {
if secret, err = secretLister.Get(secretName); err != nil {
break
}
// Add finalizer to preserve Secret from being deleted accidentally
Expand All @@ -320,12 +320,12 @@ func (c *Controller) checkAndPreserveSecrets(serviceInfos []v1alpha1.ServiceInfo
func (c *Controller) cleanupPreservedSecrets(serviceInfos []v1alpha1.ServiceInfo, namespace string) error {
var err error
var secret *corev1.Secret
secretLister := c.kubeInformerFactory.Core().V1().Secrets().Lister()
secretLister := c.secretListerForNamespace(namespace)

for _, service := range serviceInfos {
secretName := service.Secret
// Check if a secret exists
if secret, err = secretLister.Secrets(namespace).Get(secretName); err != nil && !k8sErrors.IsNotFound(err) {
if secret, err = secretLister.Get(secretName); err != nil && !k8sErrors.IsNotFound(err) {
break
}
// Remove finalizer from preserved Secret (if one exists) to allow it to be cleaned up if needed
Expand Down Expand Up @@ -386,10 +386,10 @@ func getConsumedServiceInfos(consumedServicesMap map[string]string, serviceInfos
return consumedServiceInfo
}

func generateVCAPEnv(ns string, serviceInfos []v1alpha1.ServiceInfo, kubeInformerFactory informers.SharedInformerFactory) ([]byte, error) {
func generateVCAPEnv(ns string, serviceInfos []v1alpha1.ServiceInfo, secretLister corev1listers.SecretNamespaceLister) ([]byte, error) {
envVCAPServices := map[string][]map[string]any{}
for _, serviceInfo := range serviceInfos {
entry, err := util.CreateVCAPEntryFromSecret(&serviceInfo, ns, nil, kubeInformerFactory)
entry, err := util.CreateVCAPEntryFromSecret(&serviceInfo, ns, nil, secretLister)
if err != nil {
return nil, err
}
Expand All @@ -406,9 +406,10 @@ func generateVCAPEnv(ns string, serviceInfos []v1alpha1.ServiceInfo, kubeInforme
return json.Marshal(envVCAPServices)
}

func (c Controller) createVCAPSecret(namePrefix string, ns string, ownerRef metav1.OwnerReference, serviceInfos []v1alpha1.ServiceInfo) (string, error) {
func (c *Controller) createVCAPSecret(namePrefix string, ns string, ownerRef metav1.OwnerReference, serviceInfos []v1alpha1.ServiceInfo) (string, error) {
// Generate VCAP_SERVICES env. variable
vcapEnv, err := generateVCAPEnv(ns, serviceInfos, c.kubeInformerFactory)
secretsLister := c.secretListerForNamespace(ns)
vcapEnv, err := generateVCAPEnv(ns, serviceInfos, secretsLister)
if err != nil {
return "", err
}
Expand All @@ -417,7 +418,7 @@ func (c Controller) createVCAPSecret(namePrefix string, ns string, ownerRef meta
LabelSecretOwnerHash: sha1Sum(ns, ownerRef.Name, namePrefix),
}

secretList, err := c.kubeInformerFactory.Core().V1().Secrets().Lister().Secrets(ns).List(labels.SelectorFromSet(secretLabels))
secretList, err := secretsLister.List(labels.SelectorFromSet(secretLabels))
if err != nil { // Some error occurred
return "", err
} else if len(secretList) > 0 { // Existing secret present
Expand Down
8 changes: 4 additions & 4 deletions internal/util/vcap-credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
"github.com/sap/cap-operator/pkg/apis/sme.sap.com/v1alpha1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
corev1listers "k8s.io/client-go/listers/core/v1"
)

// See Kubernetes-Service-Bindings/doc
Expand Down Expand Up @@ -83,11 +83,11 @@ func ReadServiceCredentialsFromSecret[T any](serviceInfo *v1alpha1.ServiceInfo,
return ParseJSON[T](serviceCredInfo)
}

func CreateVCAPEntryFromSecret(serviceInfo *v1alpha1.ServiceInfo, ns string, kubeClient kubernetes.Interface, kubeInformerFactory informers.SharedInformerFactory) (entry map[string]any, err error) {
func CreateVCAPEntryFromSecret(serviceInfo *v1alpha1.ServiceInfo, ns string, kubeClient kubernetes.Interface, secretsLister corev1listers.SecretNamespaceLister) (entry map[string]any, err error) {
var secret *corev1.Secret
// Get secret
if kubeInformerFactory != nil {
secret, err = kubeInformerFactory.Core().V1().Secrets().Lister().Secrets(ns).Get(serviceInfo.Secret)
if secretsLister != nil {
secret, err = secretsLister.Get(serviceInfo.Secret)
} else {
secret, err = kubeClient.CoreV1().Secrets(ns).Get(context.TODO(), serviceInfo.Secret, metav1.GetOptions{})
}
Expand Down
2 changes: 1 addition & 1 deletion internal/util/vcap-credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func testCreateVCAPEntryFromSecret(t *testing.T) {
for i := range cases {
t.Run(cases[i].name, func(t *testing.T) {
config := &cases[i]
entry, err := CreateVCAPEntryFromSecret(config.serviceInfo, config.namespace, c, kubeInformerFactory)
entry, err := CreateVCAPEntryFromSecret(config.serviceInfo, config.namespace, c, kubeInformerFactory.Core().V1().Secrets().Lister().Secrets(config.namespace))
if err != nil {
if !config.expectError {
t.Errorf("unexpected error in test case: %s", config.name)
Expand Down