Repository: apache/camel-k
Pull request #2114 (2021-03-11 15:39:51)

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

<!-- Description -->
With this PR we introduce the possibility to setup `Toleration`s during installation phase. The same configuration applies to `OLM` and non `OLM` installation.

<!--
Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". 

You can (optionally) mark this PR with labels "kind/bug" or "kind/feature" to make sure
the text is added to the right section of the release notes. 
-->

**Release Note**
```release-note
feat(operator): toleration install flag
```


heiko-braun: Is this an install time setting or can it be changed after the fact?
squakez: > Is this an install time setting or can it be changed after the fact?

It will influence the operator `Deployment` spec adding a list of `Toleration`s. Once installed, if you want to change that you will need to do update the `Deployment` spec, probably manually, and reschedule the operator pods.
heiko-braun: Thanks for the clarification @squakez 
astefanutti: @squakez what would you think of applying the others customizers from the operator Deployment, like the monitoring port, to the CSV as well? I think you've almost done all the work with that PR, and it would avoid having some asymmetric options.
squakez: > @squakez what would you think of applying the others customizers from the operator Deployment, like the monitoring port, to the CSV as well? I think you've almost done all the work with that PR, and it would avoid having some asymmetric options.

Yeah, I have in mind to add `NodeSelector` as well. I can create an issue to keep track of all of them.
squakez: @astefanutti I think this is now good for merge. Thanks!

Patch:
diff --git a/pkg/cmd/install.go b/pkg/cmd/install.go
index a116c5097..a6b746897 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/install/operator.go b/pkg/install/operator.go
index 2a650f830..9798d1a64 100644
--- a/pkg/install/operator.go
+++ b/pkg/install/operator.go
@@ -54,6 +54,7 @@ type OperatorConfiguration struct {
 	ClusterType           string
 	Health                OperatorHealthConfiguration
 	Monitoring            OperatorMonitoringConfiguration
+	Tolerations           []string
 }
 
 // OperatorHealthConfiguration --
@@ -86,6 +87,18 @@ func OperatorOrCollect(ctx context.Context, c client.Client, cfg OperatorConfigu
 			}
 		}
 
+		if cfg.Tolerations != nil {
+			if d, ok := o.(*appsv1.Deployment); ok {
+				if d.Labels["camel.apache.org/component"] == "operator" {
+					tolerations, err := kubernetes.GetTolerations(cfg.Tolerations)
+					if err != nil {
+						fmt.Println("Warning: could not parse the configured tolerations!")
+					}
+					d.Spec.Template.Spec.Tolerations = tolerations
+				}
+			}
+		}
+
 		if d, ok := o.(*appsv1.Deployment); ok {
 			if d.Labels["camel.apache.org/component"] == "operator" {
 				// Metrics endpoint port
diff --git a/pkg/trait/toleration.go b/pkg/trait/toleration.go
index c62bb7aef..9b97c6e11 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/core_client.go b/pkg/util/kubernetes/core_client.go
new file mode 100644
index 000000000..822b209d7
--- /dev/null
+++ b/pkg/util/kubernetes/core_client.go
@@ -0,0 +1,212 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kubernetes
+
+import (
+	"context"
+	"fmt"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
+
+	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+	"github.com/apache/camel-k/pkg/client"
+)
+
+// GetIntegrationPlatform --
+func GetIntegrationPlatform(context context.Context, client k8sclient.Reader, name string, namespace string) (*v1.IntegrationPlatform, error) {
+	key := k8sclient.ObjectKey{
+		Name:      name,
+		Namespace: namespace,
+	}
+
+	answer := v1.NewIntegrationPlatform(namespace, name)
+
+	if err := client.Get(context, key, &answer); err != nil {
+		return nil, err
+	}
+
+	return &answer, nil
+}
+
+// GetIntegrationKit --
+func GetIntegrationKit(context context.Context, client k8sclient.Reader, name string, namespace string) (*v1.IntegrationKit, error) {
+	key := k8sclient.ObjectKey{
+		Name:      name,
+		Namespace: namespace,
+	}
+
+	answer := v1.NewIntegrationKit(namespace, name)
+
+	if err := client.Get(context, key, &answer); err != nil {
+		return nil, err
+	}
+
+	return &answer, nil
+}
+
+// GetIntegration --
+func GetIntegration(context context.Context, client k8sclient.Reader, name string, namespace string) (*v1.Integration, error) {
+	key := k8sclient.ObjectKey{
+		Name:      name,
+		Namespace: namespace,
+	}
+
+	answer := v1.NewIntegration(namespace, name)
+
+	if err := client.Get(context, key, &answer); err != nil {
+		return nil, err
+	}
+
+	return &answer, nil
+}
+
+// GetBuild --
+func GetBuild(context context.Context, client client.Client, name string, namespace string) (*v1.Build, error) {
+	key := k8sclient.ObjectKey{
+		Name:      name,
+		Namespace: namespace,
+	}
+
+	answer := v1.NewBuild(namespace, name)
+
+	if err := client.Get(context, key, &answer); err != nil {
+		return nil, err
+	}
+
+	return &answer, nil
+}
+
+// GetConfigMap --
+func GetConfigMap(context context.Context, client k8sclient.Reader, name string, namespace string) (*corev1.ConfigMap, error) {
+	key := k8sclient.ObjectKey{
+		Name:      name,
+		Namespace: namespace,
+	}
+
+	answer := corev1.ConfigMap{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "ConfigMap",
+			APIVersion: "v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+	}
+
+	if err := client.Get(context, key, &answer); err != nil {
+		return nil, err
+	}
+
+	return &answer, nil
+}
+
+// GetSecret --
+func GetSecret(context context.Context, client k8sclient.Reader, name string, namespace string) (*corev1.Secret, error) {
+	key := k8sclient.ObjectKey{
+		Name:      name,
+		Namespace: namespace,
+	}
+
+	answer := corev1.Secret{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Secret",
+			APIVersion: "v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+	}
+
+	if err := client.Get(context, key, &answer); err != nil {
+		return nil, err
+	}
+
+	return &answer, nil
+}
+
+// GetService --
+func GetService(context context.Context, client k8sclient.Reader, name string, namespace string) (*corev1.Service, error) {
+	key := k8sclient.ObjectKey{
+		Name:      name,
+		Namespace: namespace,
+	}
+
+	answer := corev1.Service{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Service",
+			APIVersion: "v1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      name,
+			Namespace: namespace,
+		},
+	}
+
+	if err := client.Get(context, key, &answer); err != nil {
+		return nil, err
+	}
+
+	return &answer, nil
+}
+
+// GetSecretRefValue returns the value of a secret in the supplied namespace --
+func GetSecretRefValue(ctx context.Context, client k8sclient.Reader, namespace string, selector *corev1.SecretKeySelector) (string, error) {
+	secret, err := GetSecret(ctx, client, selector.Name, namespace)
+	if err != nil {
+		return "", err
+	}
+
+	if data, ok := secret.Data[selector.Key]; ok {
+		return string(data), nil
+	}
+
+	return "", fmt.Errorf("key %s not found in secret %s", selector.Key, selector.Name)
+}
+
+// GetConfigMapRefValue returns the value of a configmap in the supplied namespace
+func GetConfigMapRefValue(ctx context.Context, client k8sclient.Reader, namespace string, selector *corev1.ConfigMapKeySelector) (string, error) {
+	cm, err := GetConfigMap(ctx, client, selector.Name, namespace)
+	if err != nil {
+		return "", err
+	}
+
+	if data, ok := cm.Data[selector.Key]; ok {
+		return data, nil
+	}
+
+	return "", fmt.Errorf("key %s not found in config map %s", selector.Key, selector.Name)
+}
+
+// ResolveValueSource --
+func ResolveValueSource(ctx context.Context, client k8sclient.Reader, namespace string, valueSource *v1.ValueSource) (string, error) {
+	if valueSource.ConfigMapKeyRef != nil && valueSource.SecretKeyRef != nil {
+		return "", fmt.Errorf("value source has bot config map and secret configured")
+	}
+	if valueSource.ConfigMapKeyRef != nil {
+		return GetConfigMapRefValue(ctx, client, namespace, valueSource.ConfigMapKeyRef)
+	}
+	if valueSource.SecretKeyRef != nil {
+		return GetSecretRefValue(ctx, client, namespace, valueSource.SecretKeyRef)
+	}
+
+	return "", nil
+}
diff --git a/pkg/util/kubernetes/core_factory.go b/pkg/util/kubernetes/core_factory.go
new file mode 100644
index 000000000..5ae189111
--- /dev/null
+++ b/pkg/util/kubernetes/core_factory.go
@@ -0,0 +1,62 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kubernetes
+
+import (
+	"fmt"
+	"regexp"
+	"strconv"
+
+	corev1 "k8s.io/api/core/v1"
+)
+
+var validTaintRegexp = regexp.MustCompile(`^([\w\/_\-\.]+)(=)?([\w_\-\.]+)?:(NoSchedule|NoExecute|PreferNoSchedule):?(\d*)?$`)
+
+// 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/kubernetes/util.go b/pkg/util/kubernetes/util.go
index 0ce15172f..23d340138 100644
--- a/pkg/util/kubernetes/util.go
+++ b/pkg/util/kubernetes/util.go
@@ -18,17 +18,9 @@ limitations under the License.
 package kubernetes
 
 import (
-	"context"
-	"fmt"
-
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/json"
-	k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
 
-	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
-	"github.com/apache/camel-k/pkg/client"
 	"github.com/apache/camel-k/pkg/util"
 )
 
@@ -46,185 +38,3 @@ func ToYAML(value runtime.Object) ([]byte, error) {
 
 	return util.JSONToYAML(data)
 }
-
-// GetConfigMap --
-func GetConfigMap(context context.Context, client k8sclient.Reader, name string, namespace string) (*corev1.ConfigMap, error) {
-	key := k8sclient.ObjectKey{
-		Name:      name,
-		Namespace: namespace,
-	}
-
-	answer := corev1.ConfigMap{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "ConfigMap",
-			APIVersion: "v1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-		},
-	}
-
-	if err := client.Get(context, key, &answer); err != nil {
-		return nil, err
-	}
-
-	return &answer, nil
-}
-
-// GetSecret --
-func GetSecret(context context.Context, client k8sclient.Reader, name string, namespace string) (*corev1.Secret, error) {
-	key := k8sclient.ObjectKey{
-		Name:      name,
-		Namespace: namespace,
-	}
-
-	answer := corev1.Secret{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "Secret",
-			APIVersion: "v1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-		},
-	}
-
-	if err := client.Get(context, key, &answer); err != nil {
-		return nil, err
-	}
-
-	return &answer, nil
-}
-
-// GetIntegrationPlatform --
-func GetIntegrationPlatform(context context.Context, client k8sclient.Reader, name string, namespace string) (*v1.IntegrationPlatform, error) {
-	key := k8sclient.ObjectKey{
-		Name:      name,
-		Namespace: namespace,
-	}
-
-	answer := v1.NewIntegrationPlatform(namespace, name)
-
-	if err := client.Get(context, key, &answer); err != nil {
-		return nil, err
-	}
-
-	return &answer, nil
-}
-
-// GetIntegrationKit --
-func GetIntegrationKit(context context.Context, client k8sclient.Reader, name string, namespace string) (*v1.IntegrationKit, error) {
-	key := k8sclient.ObjectKey{
-		Name:      name,
-		Namespace: namespace,
-	}
-
-	answer := v1.NewIntegrationKit(namespace, name)
-
-	if err := client.Get(context, key, &answer); err != nil {
-		return nil, err
-	}
-
-	return &answer, nil
-}
-
-// GetIntegration --
-func GetIntegration(context context.Context, client k8sclient.Reader, name string, namespace string) (*v1.Integration, error) {
-	key := k8sclient.ObjectKey{
-		Name:      name,
-		Namespace: namespace,
-	}
-
-	answer := v1.NewIntegration(namespace, name)
-
-	if err := client.Get(context, key, &answer); err != nil {
-		return nil, err
-	}
-
-	return &answer, nil
-}
-
-// GetBuild --
-func GetBuild(context context.Context, client client.Client, name string, namespace string) (*v1.Build, error) {
-	key := k8sclient.ObjectKey{
-		Name:      name,
-		Namespace: namespace,
-	}
-
-	answer := v1.NewBuild(namespace, name)
-
-	if err := client.Get(context, key, &answer); err != nil {
-		return nil, err
-	}
-
-	return &answer, nil
-}
-
-// GetService --
-func GetService(context context.Context, client k8sclient.Reader, name string, namespace string) (*corev1.Service, error) {
-	key := k8sclient.ObjectKey{
-		Name:      name,
-		Namespace: namespace,
-	}
-
-	answer := corev1.Service{
-		TypeMeta: metav1.TypeMeta{
-			Kind:       "Service",
-			APIVersion: "v1",
-		},
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-		},
-	}
-
-	if err := client.Get(context, key, &answer); err != nil {
-		return nil, err
-	}
-
-	return &answer, nil
-}
-
-// GetSecretRefValue returns the value of a secret in the supplied namespace --
-func GetSecretRefValue(ctx context.Context, client k8sclient.Reader, namespace string, selector *corev1.SecretKeySelector) (string, error) {
-	secret, err := GetSecret(ctx, client, selector.Name, namespace)
-	if err != nil {
-		return "", err
-	}
-
-	if data, ok := secret.Data[selector.Key]; ok {
-		return string(data), nil
-	}
-
-	return "", fmt.Errorf("key %s not found in secret %s", selector.Key, selector.Name)
-}
-
-// GetConfigMapRefValue returns the value of a configmap in the supplied namespace
-func GetConfigMapRefValue(ctx context.Context, client k8sclient.Reader, namespace string, selector *corev1.ConfigMapKeySelector) (string, error) {
-	cm, err := GetConfigMap(ctx, client, selector.Name, namespace)
-	if err != nil {
-		return "", err
-	}
-
-	if data, ok := cm.Data[selector.Key]; ok {
-		return data, nil
-	}
-
-	return "", fmt.Errorf("key %s not found in config map %s", selector.Key, selector.Name)
-}
-
-// ResolveValueSource --
-func ResolveValueSource(ctx context.Context, client k8sclient.Reader, namespace string, valueSource *v1.ValueSource) (string, error) {
-	if valueSource.ConfigMapKeyRef != nil && valueSource.SecretKeyRef != nil {
-		return "", fmt.Errorf("value source has bot config map and secret configured")
-	}
-	if valueSource.ConfigMapKeyRef != nil {
-		return GetConfigMapRefValue(ctx, client, namespace, valueSource.ConfigMapKeyRef)
-	}
-	if valueSource.SecretKeyRef != nil {
-		return GetSecretRefValue(ctx, client, namespace, valueSource.SecretKeyRef)
-	}
-
-	return "", nil
-}
diff --git a/pkg/util/olm/operator.go b/pkg/util/olm/operator.go
index a01a2f05b..360cc1509 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)


Test patch:
diff --git a/pkg/cmd/install_test.go b/pkg/cmd/install_test.go
index 157430cb0..b26c3ebd8 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])
+}