diff --git a/docs/helm-configuration.md b/docs/helm-configuration.md index 3e8d5770f7..69f69b7e22 100644 --- a/docs/helm-configuration.md +++ b/docs/helm-configuration.md @@ -424,10 +424,24 @@ affinity: ``` When `node-feature-discovery.enabled=true`, the chart also merges in a -`nodeAffinity` rule that requires hardware virtualization support. If you set -`affinity.nodeAffinity` yourself, your required `nodeSelectorTerms` are -combined with the built-in virtualization terms. Other affinity fields -(`podAffinity`, `podAntiAffinity`, and so on) are passed through as-is. +`nodeAffinity` rule that requires hardware virtualization support. + +!!! note "How NFD and user nodeAffinity are combined" + Within a single `nodeSelectorTerm`, `matchExpressions` and `matchFields` are + **AND**-ed (all must match). Across `nodeSelectorTerms`, terms are **OR**-ed + (any one term may match). + + If you set `affinity.nodeAffinity` yourself, only your **required** + `nodeSelectorTerms` participate in the merge. They are combined with the + built-in virtualization terms as **(NFD OR-group) AND (user OR-group)**: + each built-in term is AND-ed with each of your required terms. Multiple + user required terms remain OR among themselves. NFD virtualization + requirements cannot be bypassed by user affinity. + + If you set `nodeAffinity` without `requiredDuringSchedulingIgnoredDuringExecution`, + the built-in NFD required terms are still applied. Other affinity fields + (`podAffinity`, `podAntiAffinity`, and `preferredDuringSchedulingIgnoredDuringExecution`) + are passed through unchanged. ### Multiple Kata installations on the Same Node diff --git a/tests/functional/kata-deploy/kata-deploy-scheduling.bats b/tests/functional/kata-deploy/kata-deploy-scheduling.bats index 53c24e1b05..b4af9f6929 100644 --- a/tests/functional/kata-deploy/kata-deploy-scheduling.bats +++ b/tests/functional/kata-deploy/kata-deploy-scheduling.bats @@ -37,6 +37,18 @@ extract_kata_deploy_ds() { ' "${RENDERED}" } +# Count nodeSelectorTerms under requiredDuringSchedulingIgnoredDuringExecution in a manifest. +count_required_node_selector_terms() { + local manifest="${1}" + echo "${manifest}" | awk ' + /requiredDuringSchedulingIgnoredDuringExecution:/ { in_req = 1; next } + in_req && /preferredDuringSchedulingIgnoredDuringExecution:/ { exit } + in_req && /^ [a-zA-Z]/ { exit } + in_req && /- match(Expressions|Fields):/ { count++ } + END { print count + 0 } + ' +} + # ============================================================================= # Template Rendering Tests (no cluster required) # ============================================================================= @@ -150,15 +162,66 @@ EOF render_chart -f "${values_file}" --set node-feature-discovery.enabled=true rm -f "${values_file}" - local ds + local ds term_count ds=$(extract_kata_deploy_ds) + term_count=$(count_required_node_selector_terms "${ds}") + [[ "${term_count}" -eq 6 ]] echo "${ds}" | grep -q "node.cloud/reserved" echo "${ds}" | grep -q "platform-team" echo "${ds}" | grep -q "feature.node.kubernetes.io/cpu-cpuid.VMX" echo "${ds}" | grep -q "feature.node.kubernetes.io/cpu-cpuid.SVM" } +@test "Helm template: NFD enabled applies virtualization nodeAffinity when user sets no affinity" { + render_chart --set node-feature-discovery.enabled=true + + local ds term_count + ds=$(extract_kata_deploy_ds) + term_count=$(count_required_node_selector_terms "${ds}") + + [[ "${term_count}" -eq 6 ]] + echo "${ds}" | grep -q "affinity:" + echo "${ds}" | grep -q "feature.node.kubernetes.io/cpu-cpuid.VMX" + echo "${ds}" | grep -q "feature.node.kubernetes.io/cpu-cpuid.SVM" +} + +@test "Helm template: NFD merge preserves podAntiAffinity" { + local values_file + values_file=$(mktemp) + cat > "${values_file}" < "${values_file}" < "${values_file}" < "${values_file}" < "${values_file}" <