From 1a1de2c46126e87a83b0c9565603bd65396435b2 Mon Sep 17 00:00:00 2001 From: James Slagle Date: Mon, 28 Sep 2026 16:59:47 -0400 Subject: [PATCH] Configure Ansible EE resources and CPU-based forks Sets the default ansible forks to 5 if not otherwise set. Add NodeSet EE resource requests and limits and derive the default fork count from CPU allocation while preserving explicit environment overrides. Jira: https://redhat.atlassian.net/browse/OSPRH-18954 Assisted-by: OpenCode GPT-6 Sol Signed-off-by: James Slagle --- ...nstack.org_openstackdataplanenodesets.yaml | 61 +++++++++ api/dataplane/v1beta1/common.go | 2 + .../openstackdataplanenodeset_types.go | 6 + .../v1beta1/zz_generated.deepcopy.go | 2 + bindata/crds/crds.yaml | 61 +++++++++ ...nstack.org_openstackdataplanenodesets.yaml | 61 +++++++++ ...ane_v1beta1_openstackdataplanenodeset.yaml | 9 ++ ...aplane_performance_tuning_large_scale.adoc | 34 ++++- docs/assemblies/dataplane_resources.adoc | 5 + internal/dataplane/util/ansible_execution.go | 4 + internal/dataplane/util/ansible_forks.go | 64 +++++++++ internal/dataplane/util/ansible_forks_test.go | 126 ++++++++++++++++++ internal/dataplane/util/ansibleee.go | 3 + ...enstackdataplanenodeset_controller_test.go | 64 +++++++++ .../01-assert.yaml | 28 ++++ .../02-assert.yaml | 4 + .../02-assert.yaml | 4 + .../01-assert.yaml | 26 ++++ .../02-assert.yaml | 2 + .../04-assert.yaml | 2 + .../06-assert.yaml | 4 + .../07-assert.yaml | 2 + .../dataplane-deploy-tls-test/02-assert.yaml | 4 + .../dataplane-deploy-tls-test/03-assert.yaml | 4 + .../dataplane-extramounts/00-assert.yaml | 2 + .../dataplane-service-config/00-assert.yaml | 13 +- .../dataplane-service-config/00-create.yaml | 7 + .../01-assert-override.yaml | 28 ++++ .../01-create-override.yaml | 17 +++ .../00-assert.yaml | 2 + .../dataplane-service-failure/00-assert.yaml | 2 + 31 files changed, 649 insertions(+), 4 deletions(-) create mode 100644 internal/dataplane/util/ansible_forks.go create mode 100644 internal/dataplane/util/ansible_forks_test.go create mode 100644 test/kuttl/tests/dataplane-service-config/01-assert-override.yaml create mode 100644 test/kuttl/tests/dataplane-service-config/01-create-override.yaml diff --git a/api/bases/dataplane.openstack.org_openstackdataplanenodesets.yaml b/api/bases/dataplane.openstack.org_openstackdataplanenodesets.yaml index eceb44b504..c0c58f205d 100644 --- a/api/bases/dataplane.openstack.org_openstackdataplanenodesets.yaml +++ b/api/bases/dataplane.openstack.org_openstackdataplanenodesets.yaml @@ -59,6 +59,67 @@ spec: description: OpenStackDataPlaneNodeSetSpec defines the desired state of OpenStackDataPlaneNodeSet properties: + ansibleEEResources: + description: |- + AnsibleEEResources specifies CPU and memory requests and limits for the + main Ansible execution environment Job container. No resources are set by default. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object baremetalSetTemplate: description: BaremetalSetTemplate Template for BaremetalSet for the NodeSet diff --git a/api/dataplane/v1beta1/common.go b/api/dataplane/v1beta1/common.go index 79476f60a1..f66b365ff0 100644 --- a/api/dataplane/v1beta1/common.go +++ b/api/dataplane/v1beta1/common.go @@ -179,6 +179,8 @@ type AnsibleEESpec struct { ExtraMounts []storage.VolMounts `json:"extraMounts,omitempty"` // Env is a list containing the environment variables to pass to the pod Env []corev1.EnvVar `json:"env,omitempty"` + // AnsibleEEResources is applied to the main execution container. + AnsibleEEResources corev1.ResourceRequirements `json:"ansibleEEResources,omitempty"` // ExtraVars for ansible execution ExtraVars map[string]json.RawMessage `json:"extraVars,omitempty"` // DNSConfig for setting dnsservers diff --git a/api/dataplane/v1beta1/openstackdataplanenodeset_types.go b/api/dataplane/v1beta1/openstackdataplanenodeset_types.go index 5e0cc7b0db..36584a6656 100644 --- a/api/dataplane/v1beta1/openstackdataplanenodeset_types.go +++ b/api/dataplane/v1beta1/openstackdataplanenodeset_types.go @@ -55,6 +55,11 @@ type OpenStackDataPlaneNodeSetSpec struct { // +kubebuilder:validation:Optional Env []corev1.EnvVar `json:"env,omitempty"` + // AnsibleEEResources specifies CPU and memory requests and limits for the + // main Ansible execution environment Job container. No resources are set by default. + // +kubebuilder:validation:Optional + AnsibleEEResources corev1.ResourceRequirements `json:"ansibleEEResources,omitempty"` + // +kubebuilder:validation:Optional // NetworkAttachments is a list of NetworkAttachment resource names to pass to the ansibleee resource // which allows to connect the ansibleee runner to the given network @@ -216,6 +221,7 @@ func (instance OpenStackDataPlaneNodeSet) GetAnsibleEESpec() AnsibleEESpec { NetworkAttachments: instance.Spec.NetworkAttachments, ExtraMounts: instance.Spec.NodeTemplate.ExtraMounts, Env: instance.Spec.Env, + AnsibleEEResources: instance.Spec.AnsibleEEResources, ServiceAccountName: instance.Name, } } diff --git a/api/dataplane/v1beta1/zz_generated.deepcopy.go b/api/dataplane/v1beta1/zz_generated.deepcopy.go index 05c763fb03..43ce437438 100644 --- a/api/dataplane/v1beta1/zz_generated.deepcopy.go +++ b/api/dataplane/v1beta1/zz_generated.deepcopy.go @@ -48,6 +48,7 @@ func (in *AnsibleEESpec) DeepCopyInto(out *AnsibleEESpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + in.AnsibleEEResources.DeepCopyInto(&out.AnsibleEEResources) if in.ExtraVars != nil { in, out := &in.ExtraVars, &out.ExtraVars *out = make(map[string]json.RawMessage, len(*in)) @@ -601,6 +602,7 @@ func (in *OpenStackDataPlaneNodeSetSpec) DeepCopyInto(out *OpenStackDataPlaneNod (*in)[i].DeepCopyInto(&(*out)[i]) } } + in.AnsibleEEResources.DeepCopyInto(&out.AnsibleEEResources) if in.NetworkAttachments != nil { in, out := &in.NetworkAttachments, &out.NetworkAttachments *out = make([]string, len(*in)) diff --git a/bindata/crds/crds.yaml b/bindata/crds/crds.yaml index 3dc9abf52f..d16023c817 100644 --- a/bindata/crds/crds.yaml +++ b/bindata/crds/crds.yaml @@ -20994,6 +20994,67 @@ spec: description: OpenStackDataPlaneNodeSetSpec defines the desired state of OpenStackDataPlaneNodeSet properties: + ansibleEEResources: + description: |- + AnsibleEEResources specifies CPU and memory requests and limits for the + main Ansible execution environment Job container. No resources are set by default. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object baremetalSetTemplate: description: BaremetalSetTemplate Template for BaremetalSet for the NodeSet diff --git a/config/crd/bases/dataplane.openstack.org_openstackdataplanenodesets.yaml b/config/crd/bases/dataplane.openstack.org_openstackdataplanenodesets.yaml index eceb44b504..c0c58f205d 100644 --- a/config/crd/bases/dataplane.openstack.org_openstackdataplanenodesets.yaml +++ b/config/crd/bases/dataplane.openstack.org_openstackdataplanenodesets.yaml @@ -59,6 +59,67 @@ spec: description: OpenStackDataPlaneNodeSetSpec defines the desired state of OpenStackDataPlaneNodeSet properties: + ansibleEEResources: + description: |- + AnsibleEEResources specifies CPU and memory requests and limits for the + main Ansible execution environment Job container. No resources are set by default. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object baremetalSetTemplate: description: BaremetalSetTemplate Template for BaremetalSet for the NodeSet diff --git a/config/samples/dataplane_v1beta1_openstackdataplanenodeset.yaml b/config/samples/dataplane_v1beta1_openstackdataplanenodeset.yaml index c68a1d4f2e..9c2e26c6ab 100644 --- a/config/samples/dataplane_v1beta1_openstackdataplanenodeset.yaml +++ b/config/samples/dataplane_v1beta1_openstackdataplanenodeset.yaml @@ -4,6 +4,15 @@ metadata: name: openstack-edpm spec: tlsEnabled: true + # Optional: resources for the main Ansible EE Job container. + # 1500m CPU request (below the 2 CPU limit) gives 2 default forks. + ansibleEEResources: + requests: + cpu: 1500m + memory: 512Mi + limits: + cpu: "2" + memory: 2Gi env: - name: ANSIBLE_FORCE_COLOR value: "True" diff --git a/docs/assemblies/dataplane_performance_tuning_large_scale.adoc b/docs/assemblies/dataplane_performance_tuning_large_scale.adoc index 952477bebd..e19ebc6302 100644 --- a/docs/assemblies/dataplane_performance_tuning_large_scale.adoc +++ b/docs/assemblies/dataplane_performance_tuning_large_scale.adoc @@ -217,7 +217,12 @@ simultaneously. *Default behavior* (from edpm-ansible playbooks): * *Strategy*: `linear` (waits for all hosts to complete a task before moving to next task) -* *Forks*: Defaults to 5 (can be overridden with `ANSIBLE_FORKS`) +* *Forks*: The operator sets `ANSIBLE_FORKS` from the EE container CPU request, + capped by a lower CPU limit. With only one CPU setting it uses that setting; + with neither it sets 8. Fractional CPUs round up (250m -> 1, 1500m -> 2). + Memory settings do not affect fork count. This does not create implicit + requests or limits. Kubernetes still requires the CPU request to be no + greater than the CPU limit for a schedulable Job. == Ansible Performance Tuning @@ -239,7 +244,7 @@ spec: - name: ANSIBLE_FORCE_COLOR value: "True" - # Increase parallel execution (default: 5) + # Override CPU-derived parallelism when needed (fallback without CPU: 8) # Set based on your control plane resources - name: ANSIBLE_FORKS value: "50" @@ -290,7 +295,7 @@ The `ANSIBLE_FORKS` setting is the most impactful tuning parameter. *Considerations:* -* *Control plane resources*: More forks require more CPU/memory in the ansible-runner pod +* *EE Job resources*: More forks require more CPU/memory in the ansible-runner pod * *Network capacity*: More simultaneous SSH connections * *Target node capacity*: Nodes must handle concurrent configuration tasks @@ -301,6 +306,29 @@ The `ANSIBLE_FORKS` setting is the most impactful tuning parameter. * Large deployments (50-100 nodes): `ANSIBLE_FORKS=50-75` * Very large (100+ nodes): Consider multiple NodeSets instead of very high fork count +[source,yaml] +---- +ansibleEEResources: + requests: + cpu: 1500m + memory: 512Mi + limits: + cpu: "2" + memory: 2Gi +# Without an explicit override this produces ANSIBLE_FORKS=2. +# Set resources under OpenStackDataPlaneNodeSet.spec. +---- + +An explicit `ANSIBLE_FORKS` in NodeSet `spec.env` takes precedence over the +Deployment-selected `spec.ansibleEEEnvConfigMapName` value, which takes +precedence over the computed default. Explicit `-f` / `--forks` supplied to +Ansible by the runner command line takes precedence over the environment. +Even an empty or invalid explicit `ANSIBLE_FORKS` is passed through unchanged; +Ansible determines how to handle it. A missing selected ConfigMap is optional +and does not prevent using the computed default. ConfigMap changes do not +update existing Jobs. Deployments are immutable and completed Jobs are not +rerun; create a new Deployment to apply changed resources or fork settings. + [source,yaml] ---- env: diff --git a/docs/assemblies/dataplane_resources.adoc b/docs/assemblies/dataplane_resources.adoc index 8fb4402831..81e41c6b73 100644 --- a/docs/assemblies/dataplane_resources.adoc +++ b/docs/assemblies/dataplane_resources.adoc @@ -276,6 +276,11 @@ OpenStackDataPlaneNodeSetSpec defines the desired state of OpenStackDataPlaneNod | []corev1.EnvVar | false +| ansibleEEResources +| CPU and memory requests and limits for the main Ansible EE Job container; unset by default. The CPU request capped by a lower limit determines default forks (ceil fractional cores; 8 with no CPU). +| corev1.ResourceRequirements +| false + | networkAttachments | NetworkAttachments is a list of NetworkAttachment resource names to pass to the ansibleee resource which allows to connect the ansibleee runner to the given network | []string diff --git a/internal/dataplane/util/ansible_execution.go b/internal/dataplane/util/ansible_execution.go index 2fa60e8983..d7cd0d4f84 100644 --- a/internal/dataplane/util/ansible_execution.go +++ b/internal/dataplane/util/ansible_execution.go @@ -109,7 +109,11 @@ func AnsibleExecution( ansibleEE.ExtraMounts = append(aeeSpec.ExtraMounts, []storage.VolMounts{ansibleEEMounts}...) ansibleEE.Env = aeeSpec.Env + ansibleEE.Resources = *aeeSpec.AnsibleEEResources.DeepCopy() ansibleEE.NodeSelector = deployment.Spec.AnsibleJobNodeSelector + if err := ansibleEE.addDefaultAnsibleForks(ctx, helper.GetClient()); err != nil { + return err + } currentJobHash := deployment.Status.AnsibleEEHashes[ansibleEE.Name] jobDef, err := ansibleEE.JobForOpenStackAnsibleEE(helper) diff --git a/internal/dataplane/util/ansible_forks.go b/internal/dataplane/util/ansible_forks.go new file mode 100644 index 0000000000..5b432219ff --- /dev/null +++ b/internal/dataplane/util/ansible_forks.go @@ -0,0 +1,64 @@ +package util //nolint:revive // util is an acceptable package name in this context + +import ( + "context" + "fmt" + "strconv" + + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const fallbackAnsibleForks int64 = 5 + +// defaultAnsibleForks uses the CPU request, capped by a lower CPU limit. +// Fractional cores are rounded up; an unspecified CPU falls back to five. +func defaultAnsibleForks(resources corev1.ResourceRequirements) (int64, bool) { + request, hasRequest := usableCPU(resources.Requests) + limit, hasLimit := usableCPU(resources.Limits) + chosen, ok := limit, hasLimit + if hasRequest && (!ok || request.Cmp(limit) < 0) { + chosen, ok = request, true + } + if !ok { + return 0, false + } + // Quantity.Value() is the ceiling of the core count, which is what forks want. + return chosen.Value(), true +} + +// usableCPU checks if a resource value (resources or limits was set) +func usableCPU(list corev1.ResourceList) (resource.Quantity, bool) { + cpu, found := list[corev1.ResourceCPU] + return cpu, found && cpu.Sign() > 0 +} + +// addDefaultAnsibleForks leaves explicit NodeSet Env and ConfigMap settings +// untouched (including empty/invalid user values). Env takes precedence over +// EnvFrom at kubelet startup, so only add an explicit default when both lack it. +func (a *EEJob) addDefaultAnsibleForks(ctx context.Context, k8sClient client.Client) error { + for _, env := range a.Env { + if env.Name == "ANSIBLE_FORKS" { + return nil + } + } + configMap := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, types.NamespacedName{Namespace: a.Namespace, Name: a.EnvConfigMapName}, configMap) + if err != nil && !k8serrors.IsNotFound(err) { + return fmt.Errorf("failed to read Ansible EE environment ConfigMap %s/%s: %w", a.Namespace, a.EnvConfigMapName, err) + } + if err == nil { + if _, present := configMap.Data["ANSIBLE_FORKS"]; present { + return nil + } + } + forks, err := defaultAnsibleForks(a.Resources) + if err != nil { + return fmt.Errorf("failed to derive Ansible forks: %w", err) + } + a.Env = append(append([]corev1.EnvVar(nil), a.Env...), corev1.EnvVar{Name: "ANSIBLE_FORKS", Value: strconv.FormatInt(forks, 10)}) + return nil +} diff --git a/internal/dataplane/util/ansible_forks_test.go b/internal/dataplane/util/ansible_forks_test.go new file mode 100644 index 0000000000..e8153e04a0 --- /dev/null +++ b/internal/dataplane/util/ansible_forks_test.go @@ -0,0 +1,126 @@ +package util //nolint:revive // util is an acceptable package name in this context + +import ( + "context" + "errors" + "testing" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func TestDefaultAnsibleForks(t *testing.T) { + tests := []struct { + name, request, limit string + want int64 + wantErr bool + }{ + {name: "unset", want: 8}, + {name: "two cores", request: "2", want: 2}, + {name: "fractional", request: "1500m", want: 2}, + {name: "sub core", request: "250m", want: 1}, + {name: "request capped", request: "4", limit: "1500m", want: 2}, + {name: "request below limit", request: "250m", limit: "4", want: 1}, + {name: "limit alone", limit: "3", want: 3}, + {name: "zero request", request: "0", wantErr: true}, + {name: "negative request", request: "-1", wantErr: true}, + {name: "zero limit", limit: "0", wantErr: true}, + {name: "overflow", limit: "2147483648", wantErr: true}, + {name: "largest supported", limit: "2147483647", want: 2147483647}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resources := corev1.ResourceRequirements{} + if tt.request != "" { + resources.Requests = corev1.ResourceList{corev1.ResourceCPU: resource.MustParse(tt.request)} + } + if tt.limit != "" { + resources.Limits = corev1.ResourceList{corev1.ResourceCPU: resource.MustParse(tt.limit)} + } + got, err := defaultAnsibleForks(resources) + if (err != nil) != tt.wantErr || (!tt.wantErr && got != tt.want) { + t.Fatalf("defaultAnsibleForks(%v) = %d, %v; want %d, error %t", resources, got, err, tt.want, tt.wantErr) + } + }) + } + t.Run("memory alone", func(t *testing.T) { + got, err := defaultAnsibleForks(corev1.ResourceRequirements{Requests: corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("2Gi")}}) + if err != nil || got != 8 { + t.Fatalf("memory-only forks = %d, %v; want 8", got, err) + } + }) +} + +func TestAddDefaultAnsibleForks(t *testing.T) { + tests := []struct { + name, configValue, explicit, want string + configExists, configHasForks, explicitExists bool + }{ + {name: "missing optional configmap", want: "2"}, + {name: "configmap without forks", configExists: true, want: "2"}, + {name: "custom configmap", configExists: true, configHasForks: true, configValue: "12"}, + {name: "empty custom value", configExists: true, configHasForks: true, configValue: ""}, + {name: "invalid custom value", configExists: true, configHasForks: true, configValue: "not-a-number"}, + {name: "nodeset wins", configExists: true, configHasForks: true, configValue: "12", explicitExists: true, explicit: "3", want: "3"}, + {name: "empty nodeset wins", configExists: true, configHasForks: true, configValue: "12", explicitExists: true, explicit: "", want: ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var objects []client.Object + if tt.configExists { + data := map[string]string{"OTHER": "unchanged"} + if tt.configHasForks { + data["ANSIBLE_FORKS"] = tt.configValue + } + objects = append(objects, &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "chosen-env", Namespace: "test-namespace"}, Data: data}) + } + h := setupTestHelper(false, objects...) + env := []corev1.EnvVar{{Name: "OTHER", Value: "unchanged"}} + if tt.explicitExists { + env = append(env, corev1.EnvVar{Name: "ANSIBLE_FORKS", Value: tt.explicit}) + } + job := EEJob{Namespace: "test-namespace", EnvConfigMapName: "chosen-env", Env: env, + Resources: corev1.ResourceRequirements{Requests: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("1500m")}}} + if err := job.addDefaultAnsibleForks(context.Background(), h.GetClient()); err != nil { + t.Fatal(err) + } + if job.Env[0].Value != "unchanged" { + t.Fatal("other environment variable changed") + } + count := 0 + for _, e := range job.Env { + if e.Name == "ANSIBLE_FORKS" { + count++ + if !tt.configHasForks || tt.explicitExists { + if e.Value != tt.want { + t.Fatalf("forks = %q; want %q", e.Value, tt.want) + } + } + } + } + if tt.configHasForks && !tt.explicitExists { + if count != 0 { + t.Fatalf("ConfigMap forks masked by %v", job.Env) + } + } else if count != 1 { + t.Fatalf("want exactly one explicit forks source: %v", job.Env) + } + }) + } +} + +type failingConfigMapClient struct{ client.Client } + +func (c failingConfigMapClient) Get(_ context.Context, _ client.ObjectKey, _ client.Object, _ ...client.GetOption) error { + return errors.New("connection lost") +} + +func TestConfigMapReadFailure(t *testing.T) { + a := EEJob{Namespace: "test-namespace", EnvConfigMapName: "chosen-env"} + err := a.addDefaultAnsibleForks(context.Background(), failingConfigMapClient{setupTestHelper(false).GetClient()}) + if err == nil { + t.Fatal("non-NotFound ConfigMap errors must not be swallowed") + } +} diff --git a/internal/dataplane/util/ansibleee.go b/internal/dataplane/util/ansibleee.go index cb61eedaff..5d35cb3560 100644 --- a/internal/dataplane/util/ansibleee.go +++ b/internal/dataplane/util/ansibleee.go @@ -60,6 +60,8 @@ type EEJob struct { Annotations map[string]string `json:"annotations,omitempty"` // Env is a list containing the environment variables to pass to the pod Env []corev1.EnvVar `json:"env,omitempty"` + // Resources applies only to the main Ansible execution container. + Resources corev1.ResourceRequirements `json:"resources,omitempty"` // NodeSelector to target subset of worker nodes running the ansible jobs NodeSelector map[string]string `json:"nodeSelector,omitempty"` } @@ -107,6 +109,7 @@ func (a *EEJob) JobForOpenStackAnsibleEE(h *helper.Helper) (*batchv1.Job, error) Name: a.Name, Args: args, Env: a.Env, + Resources: a.Resources, }}, } diff --git a/test/functional/dataplane/openstackdataplanenodeset_controller_test.go b/test/functional/dataplane/openstackdataplanenodeset_controller_test.go index 168564ad45..c809562a4c 100644 --- a/test/functional/dataplane/openstackdataplanenodeset_controller_test.go +++ b/test/functional/dataplane/openstackdataplanenodeset_controller_test.go @@ -30,6 +30,7 @@ import ( //revive:disable-next-line:dot-imports . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers" "gopkg.in/yaml.v3" + batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -2094,10 +2095,73 @@ var _ = Describe("Dataplane NodeSet Test", func() { g.Expect(ansibleEE.Spec.Template.Spec.Containers[0].EnvFrom[0].ConfigMapRef).NotTo(BeNil()) g.Expect(ansibleEE.Spec.Template.Spec.Containers[0].EnvFrom[0].ConfigMapRef.LocalObjectReference.Name).To(Equal("openstack-aee-default-env")) g.Expect(*ansibleEE.Spec.Template.Spec.Containers[0].EnvFrom[0].ConfigMapRef.Optional).To(BeTrue()) + g.Expect(ansibleEE.Spec.Template.Spec.Containers[0].Env).To(ContainElement(corev1.EnvVar{Name: "ANSIBLE_FORKS", Value: "8"})) }, th.Timeout, th.Interval).Should(Succeed()) }) }) + When("a NodeSet configures Ansible EE resources and forks", func() { + for _, tc := range []struct { + name, cmForks, nodeForks string + useCM, useNode bool + }{ + {name: "CPU-derived default"}, + {name: "selected ConfigMap override", cmForks: "17", useCM: true}, + {name: "empty selected ConfigMap override", cmForks: "", useCM: true}, + {name: "NodeSet takes precedence", cmForks: "17", nodeForks: "4", useCM: true, useNode: true}, + } { + It("propagates resources and preserves "+tc.name, func() { + nodeSetSpec := DefaultDataPlaneNodeSetSpec("edpm-compute") + nodeSetSpec["preProvisioned"] = true + nodeSetSpec["services"] = []string{"bootstrap"} + nodeSetSpec["ansibleEEResources"] = map[string]interface{}{ + "requests": map[string]string{"cpu": "1500m", "memory": "512Mi"}, + "limits": map[string]string{"cpu": "3", "memory": "2Gi"}, + } + if tc.useNode { + nodeSetSpec["env"] = []map[string]string{{"name": "ANSIBLE_FORKS", "value": tc.nodeForks}, {"name": "OTHER", "value": "intact"}} + } + deploymentSpec := DefaultDataPlaneDeploymentSpec() + deploymentSpec["ansibleEEEnvConfigMapName"] = "custom-forks-env" + if tc.useCM { + DeferCleanup(th.DeleteInstance, th.CreateConfigMap(types.NamespacedName{Name: "custom-forks-env", Namespace: namespace}, map[string]interface{}{"ANSIBLE_FORKS": tc.cmForks, "OTHER_CM": "intact"})) + } + DeferCleanup(th.DeleteInstance, CreateNetConfig(dataplaneNetConfigName, DefaultNetConfigSpec())) + DeferCleanup(th.DeleteInstance, CreateDNSMasq(dnsMasqName, DefaultDNSMasqSpec())) + DeferCleanup(th.DeleteInstance, CreateDataplaneNodeSet(dataplaneNodeSetName, nodeSetSpec)) + CreateSSHSecret(dataplaneSSHSecretName) + CreateCABundleSecret(caBundleSecretName) + SimulateDNSMasqComplete(dnsMasqName) + SimulateIPSetComplete(dataplaneNodeName) + SimulateDNSDataComplete(dataplaneNodeSetName) + DeferCleanup(th.DeleteInstance, CreateDataplaneDeployment(dataplaneDeploymentName, deploymentSpec)) + + Eventually(func(g Gomega) { + job := &batchv1.Job{} + g.Expect(th.K8sClient.Get(th.Ctx, types.NamespacedName{Name: "bootstrap-" + dataplaneDeploymentName.Name + "-" + dataplaneNodeSetName.Name, Namespace: namespace}, job)).To(Succeed()) + container := job.Spec.Template.Spec.Containers[0] + g.Expect(container.Resources.Requests.Cpu().String()).To(Equal("1500m")) + g.Expect(container.Resources.Requests.Memory().String()).To(Equal("512Mi")) + g.Expect(container.Resources.Limits.Cpu().String()).To(Equal("3")) + g.Expect(container.Resources.Limits.Memory().String()).To(Equal("2Gi")) + g.Expect(container.EnvFrom[0].ConfigMapRef.Name).To(Equal("custom-forks-env")) + forks := "2" + if tc.useNode { + forks = tc.nodeForks + g.Expect(container.Env).To(ContainElement(corev1.EnvVar{Name: "OTHER", Value: "intact"})) + } + if tc.useCM && !tc.useNode { + for _, env := range container.Env { + g.Expect(env.Name).NotTo(Equal("ANSIBLE_FORKS")) + } + } else { + g.Expect(container.Env).To(ContainElement(corev1.EnvVar{Name: "ANSIBLE_FORKS", Value: forks})) + } + }, th.Timeout, th.Interval).Should(Succeed()) + }) + } + }) + When("A Deployment specifies custom ansibleEEEnvConfigMapName", func() { BeforeEach(func() { nodeSetSpec := DefaultDataPlaneNodeSetSpec("edpm-compute") diff --git a/test/kuttl/tests/dataplane-deploy-global-service-test/01-assert.yaml b/test/kuttl/tests/dataplane-deploy-global-service-test/01-assert.yaml index b51ad4e20b..7c8b57f527 100644 --- a/test/kuttl/tests/dataplane-deploy-global-service-test/01-assert.yaml +++ b/test/kuttl/tests/dataplane-deploy-global-service-test/01-assert.yaml @@ -116,6 +116,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -224,6 +226,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -321,6 +325,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -424,6 +430,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -521,6 +529,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -618,6 +628,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -715,6 +727,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -861,6 +875,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -969,6 +985,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -1107,6 +1125,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -1245,6 +1265,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -1353,6 +1375,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -1461,6 +1485,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -1569,6 +1595,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-deploy-global-service-test/02-assert.yaml b/test/kuttl/tests/dataplane-deploy-global-service-test/02-assert.yaml index 71ed94fefe..d0e9749788 100644 --- a/test/kuttl/tests/dataplane-deploy-global-service-test/02-assert.yaml +++ b/test/kuttl/tests/dataplane-deploy-global-service-test/02-assert.yaml @@ -117,6 +117,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -213,6 +215,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-deploy-multiple-secrets/02-assert.yaml b/test/kuttl/tests/dataplane-deploy-multiple-secrets/02-assert.yaml index fb9fb974d2..ddca132393 100644 --- a/test/kuttl/tests/dataplane-deploy-multiple-secrets/02-assert.yaml +++ b/test/kuttl/tests/dataplane-deploy-multiple-secrets/02-assert.yaml @@ -148,6 +148,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -273,6 +275,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-deploy-no-nodes-test/01-assert.yaml b/test/kuttl/tests/dataplane-deploy-no-nodes-test/01-assert.yaml index 194fa98630..1de3a68e6f 100644 --- a/test/kuttl/tests/dataplane-deploy-no-nodes-test/01-assert.yaml +++ b/test/kuttl/tests/dataplane-deploy-no-nodes-test/01-assert.yaml @@ -112,6 +112,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -210,6 +212,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -315,6 +319,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -414,6 +420,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -513,6 +521,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -612,6 +622,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -753,6 +765,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -862,6 +876,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -1001,6 +1017,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -1140,6 +1158,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -1249,6 +1269,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -1358,6 +1380,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -1467,6 +1491,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-deploy-no-nodes-test/02-assert.yaml b/test/kuttl/tests/dataplane-deploy-no-nodes-test/02-assert.yaml index bd07d5cc81..0fd8779e98 100644 --- a/test/kuttl/tests/dataplane-deploy-no-nodes-test/02-assert.yaml +++ b/test/kuttl/tests/dataplane-deploy-no-nodes-test/02-assert.yaml @@ -54,6 +54,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-deploy-no-nodes-test/04-assert.yaml b/test/kuttl/tests/dataplane-deploy-no-nodes-test/04-assert.yaml index 388de5bf0c..4dc7b4f458 100644 --- a/test/kuttl/tests/dataplane-deploy-no-nodes-test/04-assert.yaml +++ b/test/kuttl/tests/dataplane-deploy-no-nodes-test/04-assert.yaml @@ -113,6 +113,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-deploy-no-nodes-test/06-assert.yaml b/test/kuttl/tests/dataplane-deploy-no-nodes-test/06-assert.yaml index 87120982d0..9ae5467cdd 100644 --- a/test/kuttl/tests/dataplane-deploy-no-nodes-test/06-assert.yaml +++ b/test/kuttl/tests/dataplane-deploy-no-nodes-test/06-assert.yaml @@ -116,6 +116,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -222,6 +224,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-deploy-no-nodes-test/07-assert.yaml b/test/kuttl/tests/dataplane-deploy-no-nodes-test/07-assert.yaml index b242505ecb..b1fc92611f 100644 --- a/test/kuttl/tests/dataplane-deploy-no-nodes-test/07-assert.yaml +++ b/test/kuttl/tests/dataplane-deploy-no-nodes-test/07-assert.yaml @@ -47,6 +47,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-deploy-tls-test/02-assert.yaml b/test/kuttl/tests/dataplane-deploy-tls-test/02-assert.yaml index 1210151716..90791199c6 100644 --- a/test/kuttl/tests/dataplane-deploy-tls-test/02-assert.yaml +++ b/test/kuttl/tests/dataplane-deploy-tls-test/02-assert.yaml @@ -180,6 +180,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -313,6 +315,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-deploy-tls-test/03-assert.yaml b/test/kuttl/tests/dataplane-deploy-tls-test/03-assert.yaml index 8f13aea95a..6e48e9eec4 100644 --- a/test/kuttl/tests/dataplane-deploy-tls-test/03-assert.yaml +++ b/test/kuttl/tests/dataplane-deploy-tls-test/03-assert.yaml @@ -186,6 +186,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ @@ -300,6 +302,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-extramounts/00-assert.yaml b/test/kuttl/tests/dataplane-extramounts/00-assert.yaml index a630827bdc..12d58b3aa5 100644 --- a/test/kuttl/tests/dataplane-extramounts/00-assert.yaml +++ b/test/kuttl/tests/dataplane-extramounts/00-assert.yaml @@ -72,6 +72,8 @@ spec: - -i - test-service-edpm-extramounts-edpm-extramounts env: + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+ diff --git a/test/kuttl/tests/dataplane-service-config/00-assert.yaml b/test/kuttl/tests/dataplane-service-config/00-assert.yaml index 88b133675c..b6e267fd7c 100644 --- a/test/kuttl/tests/dataplane-service-config/00-assert.yaml +++ b/test/kuttl/tests/dataplane-service-config/00-assert.yaml @@ -5,6 +5,9 @@ collectors: - type: command command: oc logs -n openstack-operators -l openstack.org/operator-name=openstack name: operator-logs +commands: +- script: | + oc exec -n openstack-kuttl-tests job/kuttl-service-edpm-compute-no-nodes-edpm-compute-no-nodes -- printenv ANSIBLE_FORKS | grep -qx '2' --- apiVersion: batch/v1 kind: Job @@ -54,6 +57,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "2" - name: RUNNER_PLAYBOOK value: |2+ @@ -75,7 +80,13 @@ spec: imagePullPolicy: IfNotPresent name: kuttl-service-edpm-compute-no-nodes-edpm-compute-no-nodes - resources: {} + resources: + requests: + cpu: 1500m + memory: 512Mi + limits: + cpu: "3" + memory: 2Gi terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: diff --git a/test/kuttl/tests/dataplane-service-config/00-create.yaml b/test/kuttl/tests/dataplane-service-config/00-create.yaml index 1bbeac6a72..9cd72201f4 100644 --- a/test/kuttl/tests/dataplane-service-config/00-create.yaml +++ b/test/kuttl/tests/dataplane-service-config/00-create.yaml @@ -61,6 +61,13 @@ metadata: name: edpm-compute-no-nodes spec: preProvisioned: true + ansibleEEResources: + requests: + cpu: 1500m + memory: 512Mi + limits: + cpu: "3" + memory: 2Gi env: - name: ANSIBLE_FORCE_COLOR value: "True" diff --git a/test/kuttl/tests/dataplane-service-config/01-assert-override.yaml b/test/kuttl/tests/dataplane-service-config/01-assert-override.yaml new file mode 100644 index 0000000000..603fb1ee87 --- /dev/null +++ b/test/kuttl/tests/dataplane-service-config/01-assert-override.yaml @@ -0,0 +1,28 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 600 +commands: +- script: | + oc exec -n openstack-kuttl-tests job/kuttl-service-edpm-compute-forks-override-edpm-compute-no-nodes -- printenv ANSIBLE_FORKS | grep -qx '11' +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: kuttl-service-edpm-compute-forks-override-edpm-compute-no-nodes + namespace: openstack-kuttl-tests +spec: + template: + spec: + containers: + - name: kuttl-service-edpm-compute-forks-override-edpm-compute-no-nodes + envFrom: + - configMapRef: + name: kuttl-custom-ee-env + optional: true + resources: + requests: + cpu: 1500m + memory: 512Mi + limits: + cpu: "3" + memory: 2Gi diff --git a/test/kuttl/tests/dataplane-service-config/01-create-override.yaml b/test/kuttl/tests/dataplane-service-config/01-create-override.yaml new file mode 100644 index 0000000000..f022dca0be --- /dev/null +++ b/test/kuttl/tests/dataplane-service-config/01-create-override.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: kuttl-custom-ee-env +data: + ANSIBLE_FORKS: "11" +--- +apiVersion: dataplane.openstack.org/v1beta1 +kind: OpenStackDataPlaneDeployment +metadata: + name: edpm-compute-forks-override + annotations: + dataplane.openstack.org/confirm-delete: "true" +spec: + nodeSets: + - edpm-compute-no-nodes + ansibleEEEnvConfigMapName: kuttl-custom-ee-env diff --git a/test/kuttl/tests/dataplane-service-custom-image/00-assert.yaml b/test/kuttl/tests/dataplane-service-custom-image/00-assert.yaml index f8fb78c0d1..ebf5a1f6fe 100644 --- a/test/kuttl/tests/dataplane-service-custom-image/00-assert.yaml +++ b/test/kuttl/tests/dataplane-service-custom-image/00-assert.yaml @@ -99,6 +99,8 @@ spec: - -i - custom-img-svc-edpm-compute-no-nodes-edpm-no-nodes-custom-svc env: + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_ROLE value: |2+ diff --git a/test/kuttl/tests/dataplane-service-failure/00-assert.yaml b/test/kuttl/tests/dataplane-service-failure/00-assert.yaml index ab53263c00..55334d06be 100644 --- a/test/kuttl/tests/dataplane-service-failure/00-assert.yaml +++ b/test/kuttl/tests/dataplane-service-failure/00-assert.yaml @@ -53,6 +53,8 @@ spec: env: - name: ANSIBLE_FORCE_COLOR value: "True" + - name: ANSIBLE_FORKS + value: "5" - name: RUNNER_PLAYBOOK value: |2+