Skip to content

Commit

Permalink
Allow operator to install resources to WATCH_NAMESPACE instead of req…
Browse files Browse the repository at this point in the history
…uiring keda ns

Signed-off-by: Joel Smith <[email protected]>
  • Loading branch information
joelsmith committed Sep 7, 2023
1 parent 1f48720 commit f0f1950
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 38 deletions.
19 changes: 10 additions & 9 deletions controllers/keda/configmap_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,29 @@ import (
)

const (
configMapName = "keda-metrics-apiserver"
configMapNamespace = "keda"
configMapName = "keda-metrics-apiserver"
)

// ConfigMapReconciler reconciles a ConfigMap object
type ConfigMapReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
Log logr.Logger
Scheme *runtime.Scheme
installNamespace string
}

func (r *ConfigMapReconciler) SetupWithManager(mgr ctrl.Manager) error {
func (r *ConfigMapReconciler) SetupWithManager(mgr ctrl.Manager, installNamespace string) error {
r.installNamespace = installNamespace
// we are interested only in one particular ConfigMap and only to it's creation/updates
pred := predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
if e.Object.GetName() == configMapName && e.Object.GetNamespace() == configMapNamespace {
if e.Object.GetName() == configMapName && e.Object.GetNamespace() == installNamespace {
return true
}
return false
},
UpdateFunc: func(e event.UpdateEvent) bool {
if e.ObjectNew.GetName() == configMapName && e.ObjectNew.GetNamespace() == configMapNamespace {
if e.ObjectNew.GetName() == configMapName && e.ObjectNew.GetNamespace() == installNamespace {
return e.ObjectOld.GetResourceVersion() != e.ObjectNew.GetResourceVersion()
}
return false
Expand Down Expand Up @@ -100,7 +101,7 @@ func (r *ConfigMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
}

kedaController := &kedav1alpha1.KedaController{}
err = r.Client.Get(ctx, types.NamespacedName{Name: "keda", Namespace: "keda"}, kedaController)
err = r.Client.Get(ctx, types.NamespacedName{Name: "keda", Namespace: r.installNamespace}, kedaController)
if err != nil {
if errors.IsNotFound(err) {
// there isn't any keda KedaController CR created in namespace keda -> do nothing
Expand All @@ -121,7 +122,7 @@ func (r *ConfigMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
}
// ConfigMap.Data were changed -> let's restart KEDA Metrics Server
logger.Info("ConfigMap containing CA Bundle was changed -> let's restart KEDA Metrics Server")
if err := util.DeleteMetricsServerPod(ctx, logger, r.Client); err != nil {
if err := util.DeleteMetricsServerPod(ctx, r.installNamespace, logger, r.Client); err != nil {
r.Log.Error(err, "Unable to restart KEDA Metrics Server")
return ctrl.Result{}, err
}
Expand Down
43 changes: 30 additions & 13 deletions controllers/keda/kedacontroller_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,8 @@ import (

const (

// Allowed Name and Namespace of KedaController resource
kedaControllerResourceName = "keda"
kedaControllerResourceNamespace = "keda"

installationNamespace = "keda"
// Allowed Name of KedaController resource
kedaControllerResourceName = "keda"

caBundleConfigMapName = "keda-ocp-cabundle"
injectCABundleAnnotation = "service.beta.openshift.io/inject-cabundle"
Expand All @@ -83,9 +80,11 @@ type KedaControllerReconciler struct {
resourcesWebhooks mf.Manifest
resourcesMonitoring mf.Manifest
discoveryClient *discovery.DiscoveryClient
resourceNamespace string
}

func (r *KedaControllerReconciler) SetupWithManager(mgr ctrl.Manager, logger logr.Logger) error {
func (r *KedaControllerReconciler) SetupWithManager(mgr ctrl.Manager, kedaControllerResourceNamespace string, logger logr.Logger) error {
r.resourceNamespace = kedaControllerResourceNamespace
resourcesManifest, err := resources.GetResourcesManifest()
if err != nil {
return err
Expand Down Expand Up @@ -149,8 +148,8 @@ func (r *KedaControllerReconciler) Reconcile(ctx context.Context, req ctrl.Reque
return ctrl.Result{}, err
}

if !isInteresting(req) {
msg := fmt.Sprintf("The KedaController resource needs to be created in namespace %s with name %s, otherwise it will be ignored", kedaControllerResourceNamespace, kedaControllerResourceName)
if !isInteresting(req, r.resourceNamespace) {
msg := fmt.Sprintf("The KedaController resource needs to be created in namespace %s with name %s, otherwise it will be ignored", r.resourceNamespace, kedaControllerResourceName)
logger.Info(msg)
status := instance.Status.DeepCopy()
status.MarkIgnored(msg)
Expand Down Expand Up @@ -217,7 +216,7 @@ func (r *KedaControllerReconciler) Reconcile(ctx context.Context, req ctrl.Reque
return ctrl.Result{}, err
}

if err := r.installMonitoring(ctx, logger); err != nil {
if err := r.installMonitoring(ctx, logger, instance); err != nil {
status.MarkInstallFailed("Not able to install monitoring resources")
if statusErr := util.UpdateKedaControllerStatus(ctx, r.Client, instance, status); statusErr != nil {
err = fmt.Errorf("got error: %s and then another: %s", err, statusErr)
Expand All @@ -226,7 +225,7 @@ func (r *KedaControllerReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}

status.Version = version.Version
status.MarkInstallSucceeded(fmt.Sprintf("KEDA v%s is installed in namespace '%s'", version.Version, installationNamespace))
status.MarkInstallSucceeded(fmt.Sprintf("KEDA v%s is installed in namespace '%s'", version.Version, r.resourceNamespace))
if err := util.UpdateKedaControllerStatus(ctx, r.Client, instance, status); err != nil {
return ctrl.Result{}, err
}
Expand Down Expand Up @@ -324,7 +323,10 @@ func sortMetricsResources(resources *[]unstructured.Unstructured) []unstructured

func (r *KedaControllerReconciler) installSA(logger logr.Logger, instance *kedav1alpha1.KedaController) error {
logger.Info("Reconciling KEDA ServiceAccount")
transforms := []mf.Transformer{transform.InjectOwner(instance)}
transforms := []mf.Transformer{
transform.InjectOwner(instance),
transform.ReplaceAllNamespaces(instance.Namespace),
}

if len(instance.Spec.ServiceAccount.Annotations) > 0 {
transforms = append(transforms, transform.AddServiceAccountAnnotations(instance.Spec.ServiceAccount.Annotations, r.Scheme))
Expand Down Expand Up @@ -353,6 +355,7 @@ func (r *KedaControllerReconciler) installController(ctx context.Context, logger
logger.Info("Reconciling KEDA Controller deployment")
transforms := []mf.Transformer{
transform.InjectOwner(instance),
transform.ReplaceAllNamespaces(instance.Namespace),
transform.ReplaceWatchNamespace(instance.Spec.WatchNamespace, "keda-operator", r.Scheme, logger),
}

Expand Down Expand Up @@ -440,7 +443,7 @@ func (r *KedaControllerReconciler) installController(ctx context.Context, logger
}

// installMonitoring install the controller resources for the monitoring stack
func (r *KedaControllerReconciler) installMonitoring(ctx context.Context, logger logr.Logger) error {
func (r *KedaControllerReconciler) installMonitoring(ctx context.Context, logger logr.Logger, instance *kedav1alpha1.KedaController) error {
logger.Info("Reconciling monitoring resources")

// this works only if required CRDs are present
Expand All @@ -453,6 +456,18 @@ func (r *KedaControllerReconciler) installMonitoring(ctx context.Context, logger
return nil
}

transforms := []mf.Transformer{
transform.InjectOwner(instance),
transform.ReplaceAllNamespaces(instance.Namespace),
}

manifest, err := r.resourcesMonitoring.Transform(transforms...)
if err != nil {
logger.Error(err, "Unable to transform monitoring resource manifests")
return err
}
r.resourcesMonitoring = manifest

if err := r.resourcesMonitoring.Apply(); err != nil {
logger.Error(err, "Unable to install monitoring resources")
return err
Expand All @@ -466,6 +481,7 @@ func (r *KedaControllerReconciler) installMetricsServer(ctx context.Context, log

transforms := []mf.Transformer{
transform.InjectOwner(instance),
transform.ReplaceAllNamespaces(instance.Namespace),
}

// Use alternate image spec if env var set
Expand Down Expand Up @@ -741,6 +757,7 @@ func (r *KedaControllerReconciler) installAdmissionWebhooks(ctx context.Context,
logger.Info("Reconciling KEDA Admission Webhooks deployment")
transforms := []mf.Transformer{
transform.InjectOwner(instance),
transform.ReplaceAllNamespaces(instance.Namespace),
transform.ReplaceWatchNamespace(instance.Spec.WatchNamespace, "keda-admission-webhooks", r.Scheme, logger),
}

Expand Down Expand Up @@ -835,7 +852,7 @@ func (r *KedaControllerReconciler) installAdmissionWebhooks(ctx context.Context,

// Because it's effectively cluster-scoped, we only care about a
// single, named resource: keda in the specified namespace
func isInteresting(request reconcile.Request) bool {
func isInteresting(request reconcile.Request, kedaControllerResourceNamespace string) bool {
return request.Name == kedaControllerResourceName && request.Namespace == kedaControllerResourceNamespace
}

Expand Down
19 changes: 10 additions & 9 deletions controllers/keda/secret_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,29 @@ import (
)

const (
secretName = "keda-metrics-apiserver"
secretNamespace = "keda"
secretName = "keda-metrics-apiserver"
)

// SecretReconciler reconciles a Secret object
type SecretReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
Log logr.Logger
Scheme *runtime.Scheme
secretNamespace string
}

func (r *SecretReconciler) SetupWithManager(mgr ctrl.Manager) error {
func (r *SecretReconciler) SetupWithManager(mgr ctrl.Manager, secretNamespace string) error {
r.secretNamespace = secretNamespace
// we are interested only in one particular Secret and only to it's creation/updates
pred := predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
if e.Object.GetName() == secretName && e.Object.GetNamespace() == secretNamespace {
if e.Object.GetName() == secretName && e.Object.GetNamespace() == r.secretNamespace {
return true
}
return false
},
UpdateFunc: func(e event.UpdateEvent) bool {
if e.ObjectNew.GetName() == secretName && e.ObjectNew.GetNamespace() == secretNamespace {
if e.ObjectNew.GetName() == secretName && e.ObjectNew.GetNamespace() == r.secretNamespace {
return e.ObjectOld.GetResourceVersion() != e.ObjectNew.GetResourceVersion()
}
return false
Expand Down Expand Up @@ -100,7 +101,7 @@ func (r *SecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
}

kedaController := &kedav1alpha1.KedaController{}
err = r.Client.Get(ctx, types.NamespacedName{Name: "keda", Namespace: "keda"}, kedaController)
err = r.Client.Get(ctx, types.NamespacedName{Name: "keda", Namespace: r.secretNamespace}, kedaController)
if err != nil {
if errors.IsNotFound(err) {
// there isn't any keda KedaController CR created in namespace keda -> do nothing
Expand All @@ -121,7 +122,7 @@ func (r *SecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
}
// Secret.Data were changed -> let's restart KEDA Metrics Server
logger.Info("Secret containing Certificates was changed -> let's restart KEDA Metrics Server")
if err := util.DeleteMetricsServerPod(ctx, logger, r.Client); err != nil {
if err := util.DeleteMetricsServerPod(ctx, r.secretNamespace, logger, r.Client); err != nil {
logger.Error(err, "Unable to restart KEDA Metrics Server")
return ctrl.Result{}, err
}
Expand Down
2 changes: 1 addition & 1 deletion controllers/keda/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ var _ = BeforeSuite(func() {
Log: ctrl.Log.WithName("test").WithName("KedaController"),
Scheme: k8sManager.GetScheme(),
}
err = (kedaControllerReconciler).SetupWithManager(k8sManager, kedaControllerReconciler.Log)
err = (kedaControllerReconciler).SetupWithManager(k8sManager, "keda", kedaControllerReconciler.Log)
Expect(err).ToNot(HaveOccurred())

} else {
Expand Down
35 changes: 35 additions & 0 deletions controllers/keda/transform/transform.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package transform

import (
"reflect"
"strconv"
"strings"

Expand Down Expand Up @@ -39,11 +40,45 @@ func (p Prefix) String() string {
}

const (
defaultNamespace = "keda"
containerNameKedaOperator = "keda-operator"
containerNameMetricsServer = "keda-metrics-apiserver"
containerNameAdmissionWebhooks = "keda-admission-webhooks"
)

// ReplaceAllNamespaces returns a transformer which will traverse the unstructured content looking for map entries with
// (key, value) of ("namespace", "keda"), changing the value of any matches to namespace
func ReplaceAllNamespaces(namespace string) mf.Transformer {
// recursive function helper
var helper func(obj interface{})

helper = func(obj interface{}) {
if obj == nil || reflect.ValueOf(obj).Kind() == reflect.Pointer && reflect.ValueOf(obj).IsNil() {
return
}
if mapInstance, ok := obj.(map[string]interface{}); ok {
for k, v := range mapInstance {
if stringVal, ok := v.(string); k == "namespace" && ok && stringVal == defaultNamespace {
mapInstance[k] = namespace
} else {
helper(v)
}
}
} else if arrayInstance, ok := obj.([]interface{}); ok {
for _, item := range arrayInstance {
helper(item)
}
} else {
return
}
}

return func(u *unstructured.Unstructured) error {
helper(u.UnstructuredContent())
return nil
}
}

func ReplaceNamespace(name string, namespace string, scheme *runtime.Scheme, logger logr.Logger) mf.Transformer {
return func(u *unstructured.Unstructured) error {
if u.GetName() == name {
Expand Down
3 changes: 1 addition & 2 deletions controllers/keda/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
)

const (
metricsServerNamespace = "keda"
metricsServerPodLabelKey = "app"
metricsServerPodLabelValue = "keda-metrics-apiserver"
)
Expand All @@ -39,7 +38,7 @@ func CalculateSecretedDataCheckSum(m map[string][]byte) string {
return fmt.Sprintf("%x", md5.Sum([]byte(data)))
}

func DeleteMetricsServerPod(ctx context.Context, logger logr.Logger, cl client.Client) error {
func DeleteMetricsServerPod(ctx context.Context, metricsServerNamespace string, logger logr.Logger, cl client.Client) error {
selector := make(map[string]string)
selector[metricsServerPodLabelKey] = metricsServerPodLabelValue

Expand Down
9 changes: 5 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,15 @@ func main() {

ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))

installNamespace := getWatchNamespace()
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "olm-operator.keda.sh",
Namespace: getWatchNamespace(),
Namespace: installNamespace,
})
if err != nil {
setupLog.Error(err, "unable to start manager")
Expand All @@ -89,23 +90,23 @@ func main() {
if err = (&kedacontrollers.KedaControllerReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr, setupLog); err != nil {
}).SetupWithManager(mgr, installNamespace, setupLog); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "KedaController")
os.Exit(1)
}
if err = (&kedacontrollers.ConfigMapReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("ConfigMap"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
}).SetupWithManager(mgr, installNamespace); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ConfigMap")
os.Exit(1)
}
if err = (&kedacontrollers.SecretReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Secret"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
}).SetupWithManager(mgr, installNamespace); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Secret")
os.Exit(1)
}
Expand Down

0 comments on commit f0f1950

Please sign in to comment.