This is an automated email from the ASF dual-hosted git repository.

astefanutti pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git

commit 21e7ceb37da0262fb5ad4cf401cb52629ee839f3
Author: Pasquale Congiusti <pasquale.congiu...@gmail.com>
AuthorDate: Thu Mar 11 14:34:33 2021 +0100

    feat(operator): toleration install flag
    
    * Refactored toleration trait to create Tolerations in a util method
    * Refactored OperatorOrCollect method to ease readability
    * Added toleration configuration in OLM and non OLM installations
---
 pkg/cmd/install.go          |   8 +-
 pkg/cmd/install_test.go     |  10 +++
 pkg/install/operator.go     | 180 ++++++++++++++++++++++++++------------------
 pkg/trait/toleration.go     |  40 +---------
 pkg/util/kubernetes/util.go |  38 ++++++++++
 pkg/util/olm/operator.go    |  19 ++++-
 6 files changed, 180 insertions(+), 115 deletions(-)

diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index a116c50..a6b7468 100644
--- a/pkg/cmd/install.go
+++ b/pkg/cmd/install.go
@@ -125,6 +125,9 @@ func newCmdInstall(rootCmdOptions *RootCmdOptions) 
(*cobra.Command, *installCmdO
        cmd.Flags().Bool("monitoring", false, "To enable or disable the 
operator monitoring")
        cmd.Flags().Int("monitoring-port", 8080, "The port of the metrics 
endpoint")
 
+       // Pod settings
+       cmd.Flags().StringArrayP("toleration", "", nil, "Add a Toleration to 
the operator Pod")
+
        // save
        cmd.Flags().Bool("save", false, "Save the install parameters into the 
default kamel configuration file (kamel-config.yaml)")
 
@@ -169,6 +172,7 @@ type installCmdOptions struct {
        MonitoringPort          int32    `mapstructure:"monitoring-port"`
        Properties              []string `mapstructure:"properties"`
        TraitProfile            string   `mapstructure:"trait-profile"`
+       Tolerations             []string `mapstructure:"tolerations"`
        HTTPProxySecret         string   `mapstructure:"http-proxy-secret"`
 
        registry         v1.IntegrationPlatformRegistrySpec
@@ -199,7 +203,6 @@ func (o *installCmdOptions) install(cobraCmd 
*cobra.Command, _ []string) error {
                if olmAvailable, err = olm.IsAPIAvailable(o.Context, olmClient, 
o.Namespace); err != nil {
                        return errors.Wrap(err, "error while checking OLM 
availability. Run with '--olm=false' to skip this check")
                }
-
                if olmAvailable {
                        if installViaOLM, err = 
olm.HasPermissionToInstall(o.Context, olmClient, o.Namespace, o.Global, 
o.olmOptions); err != nil {
                                return errors.Wrap(err, "error while checking 
permissions to install operator via OLM. Run with '--olm=false' to skip this 
check")
@@ -216,7 +219,7 @@ func (o *installCmdOptions) install(cobraCmd 
*cobra.Command, _ []string) error {
                if installViaOLM {
                        fmt.Fprintln(cobraCmd.OutOrStdout(), "OLM is available 
in the cluster")
                        var installed bool
-                       if installed, err = olm.Install(o.Context, olmClient, 
o.Namespace, o.Global, o.olmOptions, collection); err != nil {
+                       if installed, err = olm.Install(o.Context, olmClient, 
o.Namespace, o.Global, o.olmOptions, collection, o.Tolerations); err != nil {
                                return err
                        }
                        if !installed {
@@ -267,6 +270,7 @@ func (o *installCmdOptions) install(cobraCmd 
*cobra.Command, _ []string) error {
                                        Enabled: o.Monitoring,
                                        Port:    o.MonitoringPort,
                                },
+                               Tolerations: o.Tolerations,
                        }
                        err = install.OperatorOrCollect(o.Context, c, cfg, 
collection, o.Force)
                        if err != nil {
diff --git a/pkg/cmd/install_test.go b/pkg/cmd/install_test.go
index 157430c..b26c3eb 100644
--- a/pkg/cmd/install_test.go
+++ b/pkg/cmd/install_test.go
@@ -371,3 +371,13 @@ func TestDecodeMavenSettings(t *testing.T) {
        _, err = decodeMavenSettings("secret")
        assert.NotNil(t, err)
 }
+
+func TestInstallTolerationFlag(t *testing.T) {
+       installCmdOptions, rootCmd, _ := initializeInstallCmdOptions(t)
+       _, err := test.ExecuteCommand(rootCmd, cmdInstall,
+               "--toleration", "key1=value1:NoSchedule",
+               "--toleration", "key2=value2:NoExecute")
+       assert.Nil(t, err)
+       assert.Equal(t, "key1=value1:NoSchedule", 
installCmdOptions.Tolerations[0])
+       assert.Equal(t, "key2=value2:NoExecute", 
installCmdOptions.Tolerations[1])
+}
diff --git a/pkg/install/operator.go b/pkg/install/operator.go
index 2a650f8..397047b 100644
--- a/pkg/install/operator.go
+++ b/pkg/install/operator.go
@@ -31,6 +31,7 @@ import (
        k8serrors "k8s.io/apimachinery/pkg/api/errors"
        "k8s.io/apimachinery/pkg/api/meta"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
        "k8s.io/apimachinery/pkg/util/intstr"
 
        ctrl "sigs.k8s.io/controller-runtime/pkg/client"
@@ -54,6 +55,7 @@ type OperatorConfiguration struct {
        ClusterType           string
        Health                OperatorHealthConfiguration
        Monitoring            OperatorMonitoringConfiguration
+       Tolerations           []string
 }
 
 // OperatorHealthConfiguration --
@@ -70,81 +72,15 @@ type OperatorMonitoringConfiguration struct {
 // OperatorOrCollect installs the operator resources or adds them to the 
collector if present
 func OperatorOrCollect(ctx context.Context, c client.Client, cfg 
OperatorConfiguration, collection *kubernetes.Collection, force bool) error {
        customizer := func(o ctrl.Object) ctrl.Object {
-               if cfg.CustomImage != "" {
-                       if d, ok := o.(*appsv1.Deployment); ok {
-                               if d.Labels["camel.apache.org/component"] == 
"operator" {
-                                       
d.Spec.Template.Spec.Containers[0].Image = cfg.CustomImage
-                               }
-                       }
-               }
-
-               if cfg.CustomImagePullPolicy != "" {
-                       if d, ok := o.(*appsv1.Deployment); ok {
-                               if d.Labels["camel.apache.org/component"] == 
"operator" {
-                                       
d.Spec.Template.Spec.Containers[0].ImagePullPolicy = 
corev1.PullPolicy(cfg.CustomImagePullPolicy)
-                               }
-                       }
-               }
-
-               if d, ok := o.(*appsv1.Deployment); ok {
-                       if d.Labels["camel.apache.org/component"] == "operator" 
{
-                               // Metrics endpoint port
-                               d.Spec.Template.Spec.Containers[0].Args = 
append(d.Spec.Template.Spec.Containers[0].Args,
-                                       fmt.Sprintf("--monitoring-port=%d", 
cfg.Monitoring.Port))
-                               
d.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort = cfg.Monitoring.Port
-                               // Health endpoint port
-                               d.Spec.Template.Spec.Containers[0].Args = 
append(d.Spec.Template.Spec.Containers[0].Args,
-                                       fmt.Sprintf("--health-port=%d", 
cfg.Health.Port))
-                               
d.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Port = 
intstr.FromInt(int(cfg.Health.Port))
-                       }
-               }
-
-               if cfg.Global {
-                       if d, ok := o.(*appsv1.Deployment); ok {
-                               if d.Labels["camel.apache.org/component"] == 
"operator" {
-                                       // Make the operator watch all 
namespaces
-                                       
envvar.SetVal(&d.Spec.Template.Spec.Containers[0].Env, "WATCH_NAMESPACE", "")
-                               }
-                       }
-
-                       // Turn Role & RoleBinding into their equivalent 
cluster types
-                       if r, ok := o.(*rbacv1.Role); ok {
-                               if strings.HasPrefix(r.Name, 
"camel-k-operator") {
-                                       o = &rbacv1.ClusterRole{
-                                               ObjectMeta: metav1.ObjectMeta{
-                                                       Namespace: 
cfg.Namespace,
-                                                       Name:      r.Name,
-                                                       Labels: 
map[string]string{
-                                                               "app": 
"camel-k",
-                                                       },
-                                               },
-                                               Rules: r.Rules,
-                                       }
-                               }
-                       }
-
-                       if rb, ok := o.(*rbacv1.RoleBinding); ok {
-                               if strings.HasPrefix(rb.Name, 
"camel-k-operator") {
-                                       rb.Subjects[0].Namespace = cfg.Namespace
-
-                                       o = &rbacv1.ClusterRoleBinding{
-                                               ObjectMeta: metav1.ObjectMeta{
-                                                       Namespace: 
cfg.Namespace,
-                                                       Name:      rb.Name,
-                                                       Labels: 
map[string]string{
-                                                               "app": 
"camel-k",
-                                                       },
-                                               },
-                                               Subjects: rb.Subjects,
-                                               RoleRef: rbacv1.RoleRef{
-                                                       APIGroup: 
rb.RoleRef.APIGroup,
-                                                       Kind:     "ClusterRole",
-                                                       Name:     
rb.RoleRef.Name,
-                                               },
-                                       }
-                               }
-                       }
+               operatorDeployment := operatorDeployment(o)
+               if operatorDeployment == nil {
+                       return o
                }
+               maybeSetCustomImage(cfg, operatorDeployment)
+               maybeSetCustomImagePullPolicy(cfg, operatorDeployment)
+               setPorts(cfg, operatorDeployment)
+               maybeSetGlobal(cfg, o)
+               maybeSetTolerations(cfg, operatorDeployment)
                return o
        }
 
@@ -237,6 +173,102 @@ func OperatorOrCollect(ctx context.Context, c 
client.Client, cfg OperatorConfigu
        return nil
 }
 
+func operatorDeployment(o runtime.Object) *appsv1.Deployment {
+       if d, ok := o.(*appsv1.Deployment); ok {
+               if d.Labels["camel.apache.org/component"] == "operator" {
+                       return d
+               }
+       }
+       return nil
+}
+
+func maybeSetCustomImage(cfg OperatorConfiguration, d *appsv1.Deployment) 
error {
+       if cfg.CustomImage != "" {
+               d.Spec.Template.Spec.Containers[0].Image = cfg.CustomImage
+       }
+       return nil
+}
+
+func maybeSetCustomImagePullPolicy(cfg OperatorConfiguration, d 
*appsv1.Deployment) error {
+       if cfg.CustomImagePullPolicy != "" {
+               d.Spec.Template.Spec.Containers[0].ImagePullPolicy = 
corev1.PullPolicy(cfg.CustomImagePullPolicy)
+       }
+       return nil
+}
+
+func maybeSetTolerations(cfg OperatorConfiguration, d *appsv1.Deployment) 
error {
+       if cfg.Tolerations != nil {
+               tolerations, err := kubernetes.GetTolerations(cfg.Tolerations)
+               if err != nil {
+                       return err
+               }
+               d.Spec.Template.Spec.Tolerations = tolerations
+       }
+       return nil
+}
+
+func maybeSetGlobal(cfg OperatorConfiguration, o runtime.Object) error {
+       if cfg.Global {
+               if d, ok := o.(*appsv1.Deployment); ok {
+                       if d.Labels["camel.apache.org/component"] == "operator" 
{
+                               // Make the operator watch all namespaces
+                               
envvar.SetVal(&d.Spec.Template.Spec.Containers[0].Env, "WATCH_NAMESPACE", "")
+                       }
+               }
+
+               // Turn Role & RoleBinding into their equivalent cluster types
+               if r, ok := o.(*rbacv1.Role); ok {
+                       if strings.HasPrefix(r.Name, "camel-k-operator") {
+                               o = &rbacv1.ClusterRole{
+                                       ObjectMeta: metav1.ObjectMeta{
+                                               Namespace: cfg.Namespace,
+                                               Name:      r.Name,
+                                               Labels: map[string]string{
+                                                       "app": "camel-k",
+                                               },
+                                       },
+                                       Rules: r.Rules,
+                               }
+                       }
+               }
+
+               if rb, ok := o.(*rbacv1.RoleBinding); ok {
+                       if strings.HasPrefix(rb.Name, "camel-k-operator") {
+                               rb.Subjects[0].Namespace = cfg.Namespace
+
+                               o = &rbacv1.ClusterRoleBinding{
+                                       ObjectMeta: metav1.ObjectMeta{
+                                               Namespace: cfg.Namespace,
+                                               Name:      rb.Name,
+                                               Labels: map[string]string{
+                                                       "app": "camel-k",
+                                               },
+                                       },
+                                       Subjects: rb.Subjects,
+                                       RoleRef: rbacv1.RoleRef{
+                                               APIGroup: rb.RoleRef.APIGroup,
+                                               Kind:     "ClusterRole",
+                                               Name:     rb.RoleRef.Name,
+                                       },
+                               }
+                       }
+               }
+       }
+       return nil
+}
+
+func setPorts(cfg OperatorConfiguration, d *appsv1.Deployment) error {
+       // Metrics endpoint port
+       d.Spec.Template.Spec.Containers[0].Args = 
append(d.Spec.Template.Spec.Containers[0].Args,
+               fmt.Sprintf("--monitoring-port=%d", cfg.Monitoring.Port))
+       d.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort = 
cfg.Monitoring.Port
+       // Health endpoint port
+       d.Spec.Template.Spec.Containers[0].Args = 
append(d.Spec.Template.Spec.Containers[0].Args,
+               fmt.Sprintf("--health-port=%d", cfg.Health.Port))
+       d.Spec.Template.Spec.Containers[0].LivenessProbe.HTTPGet.Port = 
intstr.FromInt(int(cfg.Health.Port))
+       return nil
+}
+
 func installOpenShiftClusterRoleBinding(ctx context.Context, c client.Client, 
collection *kubernetes.Collection, namespace string) error {
        var target *rbacv1.ClusterRoleBinding
        existing, err := c.RbacV1().ClusterRoleBindings().Get(ctx, 
"camel-k-operator-openshift", metav1.GetOptions{})
diff --git a/pkg/trait/toleration.go b/pkg/trait/toleration.go
index c62bb7a..9b97c6e 100644
--- a/pkg/trait/toleration.go
+++ b/pkg/trait/toleration.go
@@ -19,13 +19,12 @@ package trait
 
 import (
        "fmt"
-       "regexp"
-       "strconv"
 
        corev1 "k8s.io/api/core/v1"
 
        v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
        "github.com/apache/camel-k/pkg/util"
+       "github.com/apache/camel-k/pkg/util/kubernetes"
 )
 
 // This trait sets Tolerations over Integration pods. Tolerations allow (but 
do not require) the pods to schedule onto nodes with matching taints.
@@ -48,8 +47,6 @@ type tolerationTrait struct {
        Taints []string `property:"taints" json:"taints,omitempty"`
 }
 
-var validTaintRegexp = 
regexp.MustCompile(`^([\w\/_\-\.]+)(=)?([\w_\-\.]+)?:(NoSchedule|NoExecute|PreferNoSchedule):?(\d*)?$`)
-
 func newTolerationTrait() Trait {
        return &tolerationTrait{
                BaseTrait: NewBaseTrait("toleration", 1200),
@@ -69,7 +66,7 @@ func (t *tolerationTrait) Configure(e *Environment) (bool, 
error) {
 }
 
 func (t *tolerationTrait) Apply(e *Environment) (err error) {
-       tolerations, err := t.getTolerations()
+       tolerations, err := kubernetes.GetTolerations(t.Taints)
        if err != nil {
                return err
        }
@@ -84,36 +81,3 @@ func (t *tolerationTrait) Apply(e *Environment) (err error) {
        podSpec.Tolerations = append(podSpec.Tolerations, tolerations...)
        return nil
 }
-
-func (t *tolerationTrait) getTolerations() ([]corev1.Toleration, error) {
-       tolerations := make([]corev1.Toleration, 0)
-       for _, t := range t.Taints {
-               if !validTaintRegexp.MatchString(t) {
-                       return nil, fmt.Errorf("could not match taint %v", t)
-               }
-               toleration := corev1.Toleration{}
-               // Parse the regexp groups
-               groups := validTaintRegexp.FindStringSubmatch(t)
-               toleration.Key = groups[1]
-               if groups[2] != "" {
-                       toleration.Operator = corev1.TolerationOpEqual
-               } else {
-                       toleration.Operator = corev1.TolerationOpExists
-               }
-               if groups[3] != "" {
-                       toleration.Value = groups[3]
-               }
-               toleration.Effect = corev1.TaintEffect(groups[4])
-
-               if groups[5] != "" {
-                       tolerationSeconds, err := strconv.ParseInt(groups[5], 
10, 64)
-                       if err != nil {
-                               return nil, err
-                       }
-                       toleration.TolerationSeconds = &tolerationSeconds
-               }
-               tolerations = append(tolerations, toleration)
-       }
-
-       return tolerations, nil
-}
diff --git a/pkg/util/kubernetes/util.go b/pkg/util/kubernetes/util.go
index 0ce1517..26bba6a 100644
--- a/pkg/util/kubernetes/util.go
+++ b/pkg/util/kubernetes/util.go
@@ -20,6 +20,8 @@ package kubernetes
 import (
        "context"
        "fmt"
+       "regexp"
+       "strconv"
 
        corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -32,6 +34,8 @@ import (
        "github.com/apache/camel-k/pkg/util"
 )
 
+var validTaintRegexp = 
regexp.MustCompile(`^([\w\/_\-\.]+)(=)?([\w_\-\.]+)?:(NoSchedule|NoExecute|PreferNoSchedule):?(\d*)?$`)
+
 // ToJSON --
 func ToJSON(value runtime.Object) ([]byte, error) {
        return json.Marshal(value)
@@ -228,3 +232,37 @@ func ResolveValueSource(ctx context.Context, client 
k8sclient.Reader, namespace
 
        return "", nil
 }
+
+// GetTolerations build an array of Tolerations from an array of string
+func GetTolerations(taints []string) ([]corev1.Toleration, error) {
+       tolerations := make([]corev1.Toleration, 0)
+       for _, t := range taints {
+               if !validTaintRegexp.MatchString(t) {
+                       return nil, fmt.Errorf("could not match taint %v", t)
+               }
+               toleration := corev1.Toleration{}
+               // Parse the regexp groups
+               groups := validTaintRegexp.FindStringSubmatch(t)
+               toleration.Key = groups[1]
+               if groups[2] != "" {
+                       toleration.Operator = corev1.TolerationOpEqual
+               } else {
+                       toleration.Operator = corev1.TolerationOpExists
+               }
+               if groups[3] != "" {
+                       toleration.Value = groups[3]
+               }
+               toleration.Effect = corev1.TaintEffect(groups[4])
+
+               if groups[5] != "" {
+                       tolerationSeconds, err := strconv.ParseInt(groups[5], 
10, 64)
+                       if err != nil {
+                               return nil, err
+                       }
+                       toleration.TolerationSeconds = &tolerationSeconds
+               }
+               tolerations = append(tolerations, toleration)
+       }
+
+       return tolerations, nil
+}
diff --git a/pkg/util/olm/operator.go b/pkg/util/olm/operator.go
index a01a2f0..360cc15 100644
--- a/pkg/util/olm/operator.go
+++ b/pkg/util/olm/operator.go
@@ -138,7 +138,7 @@ func HasPermissionToInstall(ctx context.Context, client 
client.Client, namespace
 }
 
 // Install creates a subscription for the OLM package
-func Install(ctx context.Context, client client.Client, namespace string, 
global bool, options Options, collection *kubernetes.Collection) (bool, error) {
+func Install(ctx context.Context, client client.Client, namespace string, 
global bool, options Options, collection *kubernetes.Collection, tolerations 
[]string) (bool, error) {
        options = fillDefaults(options)
        if installed, err := IsOperatorInstalled(ctx, client, namespace, 
global, options); err != nil {
                return false, err
@@ -166,6 +166,12 @@ func Install(ctx context.Context, client client.Client, 
namespace string, global
                        InstallPlanApproval:    
operatorsv1alpha1.ApprovalAutomatic,
                },
        }
+       // Additional configuration
+       err := maybeSetTolerations(&sub, tolerations)
+       if err != nil {
+               return false, errors.Wrap(err, fmt.Sprintf("could not set 
tolerations"))
+       }
+
        if collection != nil {
                collection.Add(&sub)
        } else if err := client.Create(ctx, &sub); err != nil {
@@ -199,6 +205,17 @@ func Install(ctx context.Context, client client.Client, 
namespace string, global
        return true, nil
 }
 
+func maybeSetTolerations(sub *operatorsv1alpha1.Subscription, tolArray 
[]string) error {
+       if tolArray != nil {
+               tolerations, err := kubernetes.GetTolerations(tolArray)
+               if err != nil {
+                       return err
+               }
+               sub.Spec.Config.Tolerations = tolerations
+       }
+       return nil
+}
+
 // Uninstall removes CSV and subscription from the namespace
 func Uninstall(ctx context.Context, client client.Client, namespace string, 
global bool, options Options) error {
        sub, err := findSubscription(ctx, client, namespace, global, options)

Reply via email to