diff --git a/doc/design/subscription-config.md b/doc/design/subscription-config.md index f0abe4959d..2422b14dbc 100644 --- a/doc/design/subscription-config.md +++ b/doc/design/subscription-config.md @@ -84,3 +84,52 @@ spec: - mountPath: /config name: config-volume ``` + +### Tolerations + +The `tolerations` field defines a list of [Tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) for the Pod created by OLM. + +> Note: Tolerations defined here will be appended to existing Tolerations, if not already present. + +#### Example + +Inject toleration to tolerate all taints. + +``` +kind: Subscription +metadata: + name: my-operator +spec: + package: etcd + channel: alpha + config: + tolerations: + - operator: "Exists" +``` + +### Resources + +The `resources` field defines [Resource Constraints](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#resource-requests-and-limits-of-pod-and-container) for all the containers in the Pod created by OLM. + +> Note: Resource Constraints defined here will overwrite existing resource constraints. + +#### Example + +Inject a request of 0.25 cpu and 64 MiB of memory, and a limit of 0.5 cpu and 128MiB of memory in each container. + +``` +kind: Subscription +metadata: + name: my-operator +spec: + package: etcd + channel: alpha + config: + resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "128Mi" + cpu: "500m" +``` diff --git a/pkg/controller/operators/olm/overrides/config.go b/pkg/controller/operators/olm/overrides/config.go index c6642a5902..5d77aed6de 100644 --- a/pkg/controller/operators/olm/overrides/config.go +++ b/pkg/controller/operators/olm/overrides/config.go @@ -16,7 +16,7 @@ type operatorConfig struct { logger *logrus.Logger } -func (o *operatorConfig) GetConfigOverrides(ownerCSV ownerutil.Owner) (envVarOverrides []corev1.EnvVar, volumeOverrides []corev1.Volume, volumeMountOverrides []corev1.VolumeMount, err error) { +func (o *operatorConfig) GetConfigOverrides(ownerCSV ownerutil.Owner) (envVarOverrides []corev1.EnvVar, volumeOverrides []corev1.Volume, volumeMountOverrides []corev1.VolumeMount, tolerationOverrides []corev1.Toleration, resourcesOverride corev1.ResourceRequirements, err error) { list, listErr := o.lister.OperatorsV1alpha1().SubscriptionLister().Subscriptions(ownerCSV.GetNamespace()).List(labels.Everything()) if listErr != nil { err = fmt.Errorf("failed to list subscription namespace=%s - %v", ownerCSV.GetNamespace(), listErr) @@ -32,6 +32,8 @@ func (o *operatorConfig) GetConfigOverrides(ownerCSV ownerutil.Owner) (envVarOve envVarOverrides = owner.Spec.Config.Env volumeOverrides = owner.Spec.Config.Volumes volumeMountOverrides = owner.Spec.Config.VolumeMounts + tolerationOverrides = owner.Spec.Config.Tolerations + resourcesOverride = owner.Spec.Config.Resources return } diff --git a/pkg/controller/operators/olm/overrides/initializer.go b/pkg/controller/operators/olm/overrides/initializer.go index 6ac25d4aa0..93452e555b 100644 --- a/pkg/controller/operators/olm/overrides/initializer.go +++ b/pkg/controller/operators/olm/overrides/initializer.go @@ -2,6 +2,7 @@ package overrides import ( "fmt" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil" @@ -43,7 +44,7 @@ func (d *DeploymentInitializer) initialize(ownerCSV ownerutil.Owner, deployment var envVarOverrides, proxyEnvVar, merged []corev1.EnvVar var err error - envVarOverrides, volumeOverrides, volumeMountOverrides, err := d.config.GetConfigOverrides(ownerCSV) + envVarOverrides, volumeOverrides, volumeMountOverrides, tolerationOverrides, resourcesOverride, err := d.config.GetConfigOverrides(ownerCSV) if err != nil { err = fmt.Errorf("failed to get subscription pod configuration - %v", err) return err @@ -78,6 +79,14 @@ func (d *DeploymentInitializer) initialize(ownerCSV ownerutil.Owner, deployment return fmt.Errorf("failed to inject volumeMounts(s) into deployment spec name=%s - %v", deployment.Name, err) } + if err = InjectTolerationsIntoDeployment(podSpec, tolerationOverrides); err != nil { + return fmt.Errorf("failed to inject toleration(s) into deployment spec name=%s - %v", deployment.Name, err) + } + + if err = InjectResourcesIntoDeployment(podSpec, resourcesOverride); err != nil { + return fmt.Errorf("failed to inject resources into deployment spec name=%s - %v", deployment.Name, err) + } + return nil } diff --git a/pkg/controller/operators/olm/overrides/inject.go b/pkg/controller/operators/olm/overrides/inject.go index 74fcb1df1b..7471de3934 100644 --- a/pkg/controller/operators/olm/overrides/inject.go +++ b/pkg/controller/operators/olm/overrides/inject.go @@ -2,6 +2,7 @@ package overrides import ( "errors" + "reflect" corev1 "k8s.io/api/core/v1" ) @@ -149,3 +150,59 @@ func findVolumeMount(volumeMounts []corev1.VolumeMount, name string) (foundVolum return } + +// InjectTolerationsIntoDeployment injects provided Tolerations +// into the given Pod Spec +// +// Tolerations will be appended to the existing once if it +// does not already exist +func InjectTolerationsIntoDeployment(podSpec *corev1.PodSpec, tolerations []corev1.Toleration) error { + if podSpec == nil { + return errors.New("no pod spec provided") + } + + podSpec.Tolerations = mergeTolerations(podSpec.Tolerations, tolerations) + return nil +} + +func mergeTolerations(podTolerations []corev1.Toleration, newTolerations []corev1.Toleration) (mergedTolerations []corev1.Toleration) { + mergedTolerations = podTolerations + for _, newToleration := range newTolerations { + _, found := findToleration(podTolerations, newToleration) + if !found { + mergedTolerations = append(mergedTolerations, newToleration) + } + } + + return +} + +func findToleration(tolerations []corev1.Toleration, toleration corev1.Toleration) (foundToleration *corev1.Toleration, found bool) { + for i := range tolerations { + if reflect.DeepEqual(toleration, tolerations[i]) { + found = true + foundToleration = &toleration + + break + } + } + + return +} + +// InjectResourcesIntoDeployment will inject provided Resources +// into given podSpec +// +// If podSpec already defines Resources, it will be overwritten +func InjectResourcesIntoDeployment(podSpec *corev1.PodSpec, resources corev1.ResourceRequirements) error { + if podSpec == nil { + return errors.New("no pod spec provided") + } + + for i := range podSpec.Containers { + container := &podSpec.Containers[i] + container.Resources = resources + } + + return nil +} diff --git a/pkg/controller/operators/olm/overrides/inject_test.go b/pkg/controller/operators/olm/overrides/inject_test.go index f71cc1e055..473f04b689 100644 --- a/pkg/controller/operators/olm/overrides/inject_test.go +++ b/pkg/controller/operators/olm/overrides/inject_test.go @@ -4,9 +4,9 @@ import ( "testing" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/overrides" - "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" ) var ( @@ -27,36 +27,55 @@ var ( defaultVolumeMounts = []corev1.VolumeMount{ corev1.VolumeMount{ - Name: "foo", + Name: "foo", MountPath: "/bar", }, } defaultVolumes = []corev1.Volume{ corev1.Volume{ - Name: "foo", + Name: "foo", VolumeSource: corev1.VolumeSource{}, }, } + + defaultTolerations = []corev1.Toleration{ + corev1.Toleration{ + Key: "my-toleration-key", + Effect: corev1.TaintEffectNoSchedule, + Value: "my-toleration-value", + Operator: corev1.TolerationOpEqual, + }, + } + + defaultResources = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + }, + } ) func TestInjectVolumeMountIntoDeployment(t *testing.T) { tests := []struct { - name string - podSpec *corev1.PodSpec - volumeMounts []corev1.VolumeMount - expected *corev1.PodSpec + name string + podSpec *corev1.PodSpec + volumeMounts []corev1.VolumeMount + expected *corev1.PodSpec }{ { // The container does not define a VolumeMount and is injected with an empty list of VolumeMounts. // Expected: The container's VolumeMount list remains empty. - name: "EmptyVolumeMounts", + name: "EmptyVolumeMounts", podSpec: &corev1.PodSpec{ Containers: []corev1.Container{ corev1.Container{}, }, }, - volumeMounts: []corev1.VolumeMount{}, + volumeMounts: []corev1.VolumeMount{}, expected: &corev1.PodSpec{ Containers: []corev1.Container{ corev1.Container{}, @@ -66,14 +85,14 @@ func TestInjectVolumeMountIntoDeployment(t *testing.T) { { // The container does not define a VolumeMount and is injected with a single VolumeMount. // Expected: The container contains the injected VolumeMount. - name: "WithContainerHasNoVolumeMounts", + name: "WithContainerHasNoVolumeMounts", podSpec: &corev1.PodSpec{ Containers: []corev1.Container{ corev1.Container{}, }, }, - volumeMounts: defaultVolumeMounts, - expected:&corev1.PodSpec{ + volumeMounts: defaultVolumeMounts, + expected: &corev1.PodSpec{ Containers: []corev1.Container{ corev1.Container{ VolumeMounts: defaultVolumeMounts, @@ -84,7 +103,7 @@ func TestInjectVolumeMountIntoDeployment(t *testing.T) { { // The container defines a single VolumeMount which is injected with an empty VolumeMount list. // Expected: The container's VolumeMount list is unchanged. - name: "WithContainerHasVolumeMountsEmptyDefaults", + name: "WithContainerHasVolumeMountsEmptyDefaults", podSpec: &corev1.PodSpec{ Containers: []corev1.Container{ corev1.Container{ @@ -92,7 +111,7 @@ func TestInjectVolumeMountIntoDeployment(t *testing.T) { }, }, }, - volumeMounts: []corev1.VolumeMount{}, + volumeMounts: []corev1.VolumeMount{}, expected: &corev1.PodSpec{ Containers: []corev1.Container{ corev1.Container{ @@ -110,7 +129,7 @@ func TestInjectVolumeMountIntoDeployment(t *testing.T) { corev1.Container{ VolumeMounts: []corev1.VolumeMount{ corev1.VolumeMount{ - Name: "bar", + Name: "bar", MountPath: "/foo", }, }, @@ -123,11 +142,11 @@ func TestInjectVolumeMountIntoDeployment(t *testing.T) { corev1.Container{ VolumeMounts: []corev1.VolumeMount{ corev1.VolumeMount{ - Name: "bar", + Name: "bar", MountPath: "/foo", }, corev1.VolumeMount{ - Name: "foo", + Name: "foo", MountPath: "/bar", }, }, @@ -145,7 +164,7 @@ func TestInjectVolumeMountIntoDeployment(t *testing.T) { corev1.Container{ VolumeMounts: []corev1.VolumeMount{ corev1.VolumeMount{ - Name: "foo", + Name: "foo", MountPath: "/barbar", }, }, @@ -158,7 +177,7 @@ func TestInjectVolumeMountIntoDeployment(t *testing.T) { corev1.Container{ VolumeMounts: []corev1.VolumeMount{ corev1.VolumeMount{ - Name: "foo", + Name: "foo", MountPath: "/bar", }, }, @@ -166,7 +185,7 @@ func TestInjectVolumeMountIntoDeployment(t *testing.T) { }, }, }, -} + } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -180,47 +199,43 @@ func TestInjectVolumeMountIntoDeployment(t *testing.T) { } } - func TestInjectVolumeIntoDeployment(t *testing.T) { tests := []struct { name string podSpec *corev1.PodSpec - volumes []corev1.Volume + volumes []corev1.Volume expected *corev1.PodSpec }{ { // The PodSpec defines no Volumes and is injected with an empty list. // Expected: The PodSpec's VolumeMount list remains empty. - name: "EmptyVolumeMounts", + name: "EmptyVolumeMounts", podSpec: &corev1.PodSpec{ - Containers: []corev1.Container{ - }, + Containers: []corev1.Container{}, }, - volumes: []corev1.Volume{}, + volumes: []corev1.Volume{}, expected: &corev1.PodSpec{ - Containers: []corev1.Container{ - }, + Containers: []corev1.Container{}, }, }, { // The PodSpec does not define any Volumes and is injected with a VolumeMount. // Expected: The PodSpec contains the Volume that was injected. name: "WithContainerHasNoVolumeMounts", - podSpec: &corev1.PodSpec{ - }, - volumes: defaultVolumes, - expected:&corev1.PodSpec{ + podSpec: &corev1.PodSpec{}, + volumes: defaultVolumes, + expected: &corev1.PodSpec{ Volumes: defaultVolumes, }, }, { // The PodSpec contains a single VolumeMount and is injected with an empty Volume list // Expected: The PodSpec's Volume list is unchanged. - name: "WithContainerHasVolumeMountsEmptyDefaults", + name: "WithContainerHasVolumeMountsEmptyDefaults", podSpec: &corev1.PodSpec{ Volumes: defaultVolumes, }, - volumes: []corev1.Volume{}, + volumes: []corev1.Volume{}, expected: &corev1.PodSpec{ Volumes: defaultVolumes, }, @@ -232,7 +247,7 @@ func TestInjectVolumeIntoDeployment(t *testing.T) { podSpec: &corev1.PodSpec{ Volumes: []corev1.Volume{ corev1.Volume{ - Name: "bar", + Name: "bar", VolumeSource: corev1.VolumeSource{}, }, }, @@ -240,37 +255,37 @@ func TestInjectVolumeIntoDeployment(t *testing.T) { volumes: defaultVolumes, expected: &corev1.PodSpec{ Volumes: []corev1.Volume{ - corev1.Volume{ - Name: "bar", - VolumeSource: corev1.VolumeSource{}, - }, - corev1.Volume{ - Name: "foo", - VolumeSource: corev1.VolumeSource{}, - }, - }, + corev1.Volume{ + Name: "bar", + VolumeSource: corev1.VolumeSource{}, + }, + corev1.Volume{ + Name: "foo", + VolumeSource: corev1.VolumeSource{}, }, }, + }, + }, { // The PodSpec defines a single Volume that is injected with a Volume that has a name conflict. // Expected: The existing Volume is overwritten. name: "WithContainerHasOverlappingVolumeMounts", podSpec: &corev1.PodSpec{ - Volumes: []corev1.Volume{ - corev1.Volume{ - Name: "foo", - }, - }, + Volumes: []corev1.Volume{ + corev1.Volume{ + Name: "foo", + }, + }, }, volumes: defaultVolumes, expected: &corev1.PodSpec{ - Volumes: []corev1.Volume{ - corev1.Volume{ - Name: "foo", - VolumeSource: corev1.VolumeSource{}, - }, - }, + Volumes: []corev1.Volume{ + corev1.Volume{ + Name: "foo", + VolumeSource: corev1.VolumeSource{}, }, + }, + }, }, } @@ -286,7 +301,6 @@ func TestInjectVolumeIntoDeployment(t *testing.T) { } } - func TestInjectEnvIntoDeployment(t *testing.T) { tests := []struct { name string @@ -499,3 +513,191 @@ func TestInjectEnvIntoDeployment(t *testing.T) { }) } } + +func TestInjectTolerationsIntoDeployment(t *testing.T) { + tests := []struct { + name string + podSpec *corev1.PodSpec + tolerations []corev1.Toleration + expected *corev1.PodSpec + }{ + { + // PodSpec has no tolerations and toleration config is empty + // Expected: Tolerations will be empty + name: "WithEmptyTolerations", + podSpec: &corev1.PodSpec{ + Tolerations: []corev1.Toleration{}, + }, + tolerations: []corev1.Toleration{}, + expected: &corev1.PodSpec{ + Tolerations: []corev1.Toleration{}, + }, + }, + { + // PodSpec has no tolerations and one toleration config given + // Expected: Toleration will be appended + name: "WithDeploymentHasNoTolerations", + podSpec: &corev1.PodSpec{ + Tolerations: []corev1.Toleration{}, + }, + tolerations: defaultTolerations, + expected: &corev1.PodSpec{ + Tolerations: defaultTolerations, + }, + }, + { + // PodSpec has one toleration and different toleration config given + // Expected: Toleration will be appended + name: "WithDeploymentHasOneNonOverlappingToleration", + podSpec: &corev1.PodSpec{ + Tolerations: []corev1.Toleration{ + corev1.Toleration{ + Key: "my-different-toleration-key", + Operator: corev1.TolerationOpExists, + }, + }, + }, + tolerations: defaultTolerations, + expected: &corev1.PodSpec{ + Tolerations: append([]corev1.Toleration{ + corev1.Toleration{ + Key: "my-different-toleration-key", + Operator: corev1.TolerationOpExists, + }, + }, defaultTolerations...), + }, + }, + { + // PodSpec has one toleration and same toleration config given + // Expected: Toleration will not be appended + name: "WithDeploymentHasOneOverlappingToleration", + podSpec: &corev1.PodSpec{ + Tolerations: defaultTolerations, + }, + tolerations: defaultTolerations, + expected: &corev1.PodSpec{ + Tolerations: defaultTolerations, + }, + }, + { + // PodSpec has one toleration and 2 toleration config given with 1 overlapping + // Expected: Non overlapping toleration will be appended + name: "WithDeploymentHasOverlappingAndNonOverlappingTolerations", + podSpec: &corev1.PodSpec{ + Tolerations: []corev1.Toleration{ + corev1.Toleration{ + Key: "my-different-toleration-key", + Operator: corev1.TolerationOpExists, + }, + }, + }, + tolerations: append([]corev1.Toleration{ + corev1.Toleration{ + Key: "my-different-toleration-key", + Operator: corev1.TolerationOpExists, + }, + }, defaultTolerations...), + expected: &corev1.PodSpec{ + Tolerations: append([]corev1.Toleration{ + corev1.Toleration{ + Key: "my-different-toleration-key", + Operator: corev1.TolerationOpExists, + }, + }, defaultTolerations...), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + overrides.InjectTolerationsIntoDeployment(tt.podSpec, tt.tolerations) + + podSpecWant := tt.expected + podSpecGot := tt.podSpec + + assert.Equal(t, podSpecWant, podSpecGot) + }) + } +} + +func TestInjectResourcesIntoDeployment(t *testing.T) { + tests := []struct { + name string + podSpec *corev1.PodSpec + resources corev1.ResourceRequirements + expected *corev1.PodSpec + }{ + { + // PodSpec has one container and empty resources + // Expected: Resources will remain empty + name: "WithEmptyResources", + podSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ + corev1.Container{ + Resources: corev1.ResourceRequirements{}, + }, + }, + }, + resources: corev1.ResourceRequirements{}, + expected: &corev1.PodSpec{ + Containers: []corev1.Container{ + corev1.Container{ + Resources: corev1.ResourceRequirements{}, + }, + }, + }, + }, + { + // PodSpec has one container with empty resources and one resource config given + // Expected: Resources will be appended + name: "WithDeploymentHasNoResources", + podSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ + corev1.Container{ + Resources: corev1.ResourceRequirements{}, + }, + }, + }, + resources: defaultResources, + expected: &corev1.PodSpec{ + Containers: []corev1.Container{ + corev1.Container{ + Resources: defaultResources, + }, + }, + }, + }, + { + // PodSpec has one container with one resource and one resource config given + // Expected: Resources will be overwritten + // Here, overriding with empty resources + name: "WithDeploymentHasResources", + podSpec: &corev1.PodSpec{ + Containers: []corev1.Container{ + corev1.Container{ + Resources: defaultResources, + }, + }, + }, + resources: corev1.ResourceRequirements{}, + expected: &corev1.PodSpec{ + Containers: []corev1.Container{ + corev1.Container{ + Resources: corev1.ResourceRequirements{}, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + overrides.InjectResourcesIntoDeployment(tt.podSpec, tt.resources) + + podSpecWant := tt.expected + podSpecGot := tt.podSpec + + assert.Equal(t, podSpecWant, podSpecGot) + }) + } +} diff --git a/test/e2e/subscription_e2e_test.go b/test/e2e/subscription_e2e_test.go index c3190fa716..fbe0303c5b 100644 --- a/test/e2e/subscription_e2e_test.go +++ b/test/e2e/subscription_e2e_test.go @@ -3,6 +3,7 @@ package e2e import ( "encoding/json" "fmt" + "reflect" "strings" "sync" "testing" @@ -24,6 +25,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/clientcmd" @@ -1327,11 +1329,30 @@ func TestCreateNewSubscriptionWithPodConfig(t *testing.T) { podVolumeMounts := []corev1.VolumeMount{ corev1.VolumeMount{Name: testVolumeName, MountPath: "/test"}, } + podTolerations := []corev1.Toleration{ + corev1.Toleration{ + Key: "my-toleration-key", + Value: "my-toleration-value", + Effect: corev1.TaintEffectNoSchedule, + Operator: corev1.TolerationOpEqual, + }, + } + podResources := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + }, + } podConfig := v1alpha1.SubscriptionConfig{ Env: podEnv, Volumes: podVolumes, VolumeMounts: podVolumeMounts, + Tolerations: podTolerations, + Resources: podResources, } permissions := deploymentPermissions(t) @@ -1358,7 +1379,7 @@ func TestCreateNewSubscriptionWithPodConfig(t *testing.T) { expected := podEnv expected = append(expected, proxyEnv...) - checkDeploymentWithPodConfiguration(t, kubeClient, csv, podConfig.Env, podConfig.Volumes, podConfig.VolumeMounts) + checkDeploymentWithPodConfiguration(t, kubeClient, csv, podConfig.Env, podConfig.Volumes, podConfig.VolumeMounts, podConfig.Tolerations, podConfig.Resources) } func TestCreateNewSubscriptionWithDependencies(t *testing.T) { @@ -1402,7 +1423,7 @@ func TestCreateNewSubscriptionWithDependencies(t *testing.T) { } -func checkDeploymentWithPodConfiguration(t *testing.T, client operatorclient.ClientInterface, csv *v1alpha1.ClusterServiceVersion, envVar []corev1.EnvVar, volumes []corev1.Volume, volumeMounts []corev1.VolumeMount) { +func checkDeploymentWithPodConfiguration(t *testing.T, client operatorclient.ClientInterface, csv *v1alpha1.ClusterServiceVersion, envVar []corev1.EnvVar, volumes []corev1.Volume, volumeMounts []corev1.VolumeMount, tolerations []corev1.Toleration, resources corev1.ResourceRequirements) { resolver := install.StrategyResolver{} strategy, err := resolver.UnmarshalStrategy(csv.Spec.InstallStrategy) @@ -1450,6 +1471,28 @@ func checkDeploymentWithPodConfiguration(t *testing.T, client operatorclient.Cli return } + findTolerations := func(tolerations []corev1.Toleration, toleration corev1.Toleration) (foundToleration *corev1.Toleration, found bool) { + for i := range tolerations { + if reflect.DeepEqual(toleration, tolerations[i]) { + found = true + foundToleration = &toleration + + break + } + } + + return + } + + findResources := func(existingResource corev1.ResourceRequirements, podResource corev1.ResourceRequirements) (foundResource *corev1.ResourceRequirements, found bool) { + if reflect.DeepEqual(existingResource, podResource) { + found = true + foundResource = &podResource + } + + return + } + check := func(container *corev1.Container) { for _, e := range envVar { existing, found := findEnvVar(container.Env, e.Name) @@ -1464,6 +1507,11 @@ func checkDeploymentWithPodConfiguration(t *testing.T, client operatorclient.Cli require.NotNil(t, existing) require.Equalf(t, v.MountPath, existing.MountPath, "VolumeMount MountPath does not match %s=%s", v.Name, v.MountPath) } + + existing, found := findResources(container.Resources, resources) + require.Truef(t, found, "Resources not injected. Resource=%v", resources) + require.NotNil(t, existing) + require.Equalf(t, *existing, resources, "Resource=%v does not match expected Resource=%v", existing, resources) } for _, deploymentSpec := range strategyDetailsDeployment.DeploymentSpecs { @@ -1475,6 +1523,12 @@ func checkDeploymentWithPodConfiguration(t *testing.T, client operatorclient.Cli require.NotNil(t, existing) require.Equalf(t, v.ConfigMap.LocalObjectReference.Name, existing.ConfigMap.LocalObjectReference.Name, "volume ConfigMap Names does not match %s=%s", v.Name, v.ConfigMap.LocalObjectReference.Name) } + for _, toleration := range tolerations { + existing, found := findTolerations(deployment.Spec.Template.Spec.Tolerations, toleration) + require.Truef(t, found, "Toleration not injected. Toleration=%v", toleration) + require.NotNil(t, existing) + require.Equalf(t, *existing, toleration, "Toleration=%v does not match expected Toleration=%v", existing, toleration) + } for i := range deployment.Spec.Template.Spec.Containers { check(&deployment.Spec.Template.Spec.Containers[i])