fix: configure Kubernetes feature gates with dictionaries (#13608)

* fix: configure Kubernetes feature gates with dictionaries

* docs: update feature gate settings to dictionaries
This commit is contained in:
Frank
2026-10-15 15:26:39 +00:00
committed by GitHub
parent 6b3cbe1cbb
commit bebc536fae
11 changed files with 59 additions and 57 deletions
@@ -111,9 +111,7 @@ networking:
{% endif %}
{% if kubeadm_feature_gates %}
featureGates:
{% for feature in kubeadm_feature_gates %}
{{ feature | replace("=", ": ") }}
{% endfor %}
{{ kubeadm_feature_gates | to_nice_yaml(indent=2) | indent(2) }}
{% endif %}
kubernetesVersion: v{{ kube_version }}
{% if kube_apiserver_endpoint is defined %}
@@ -264,7 +262,7 @@ apiServer:
{% endfor %}
{% if kube_apiserver_feature_gates or kube_feature_gates %}
- name: feature-gates
value: "{{ kube_apiserver_feature_gates | default(kube_feature_gates, true) | join(',') }}"
value: "{% for key, value in (kube_apiserver_feature_gates | default(kube_feature_gates, true)).items() %}{{ key }}={{ value | lower }}{{ ',' if not loop.last else '' }}{% endfor %}"
{% endif %}
{% if tls_min_version is defined %}
- name: tls-min-version
@@ -402,7 +400,7 @@ controllerManager:
value: "{{ kube_controller_manager_leader_elect_renew_deadline }}"
{% if kube_controller_feature_gates or kube_feature_gates %}
- name: feature-gates
value: "{{ kube_controller_feature_gates | default(kube_feature_gates, true) | join(',') }}"
value: "{% for key, value in (kube_controller_feature_gates | default(kube_feature_gates, true)).items() %}{{ key }}={{ value | lower }}{{ ',' if not loop.last else '' }}{% endfor %}"
{% endif %}
{% for key in kube_kubeadm_controller_extra_args %}
- name: "{{ key }}"
@@ -441,7 +439,7 @@ scheduler:
value: "{{ kube_config_dir }}/kubescheduler-config.yaml"
{% if kube_scheduler_feature_gates or kube_feature_gates %}
- name: feature-gates
value: "{{ kube_scheduler_feature_gates | default(kube_feature_gates, true) | join(',') }}"
value: "{% for key, value in (kube_scheduler_feature_gates | default(kube_feature_gates, true)).items() %}{{ key }}={{ value | lower }}{{ ',' if not loop.last else '' }}{% endfor %}"
{% endif %}
- name: profiling
value: "{{ kube_profiling }}"
@@ -564,9 +562,7 @@ portRange: {{ kube_proxy_port_range }}
{% if kube_proxy_feature_gates or kube_feature_gates %}
{% set feature_gates = ( kube_proxy_feature_gates | default(kube_feature_gates, true) ) %}
featureGates:
{% for feature in feature_gates %}
{{ feature | replace("=", ": ") }}
{% endfor %}
{{ feature_gates | to_nice_yaml(indent=2) | indent(2) }}
{% endif %}
{# DNS settings for kubelet #}
{% if enable_nodelocaldns %}
@@ -590,19 +586,7 @@ clusterDNS:
{% for dns_address in kubelet_cluster_dns %}
- {{ dns_address }}
{% endfor %}
{% set disable_websockets_to_kubelet = (container_manager == 'docker' and kube_version is version('1.36.0', '>=')) %}
{% set feature_gates = ( kubelet_feature_gates | default(kube_feature_gates, true) ) %}
{% if disable_websockets_to_kubelet %}
{% set feature_gates = feature_gates | reject('search', '^ExtendWebSocketsToKubelet=') | list %}
{% endif %}
{% if feature_gates or disable_websockets_to_kubelet %}
{% if _kubelet_feature_gates %}
featureGates:
{% if disable_websockets_to_kubelet %}
# cri-dockerd does not support WebSocket streaming
# https://github.com/kubernetes-sigs/kubespray/issues/13360
ExtendWebSocketsToKubelet: false
{% endif %}
{% for feature in feature_gates %}
{{ feature | replace("=", ": ") }}
{% endfor %}
{{ _kubelet_feature_gates | to_nice_yaml(indent=2) | indent(2) }}
{% endif %}
@@ -81,21 +81,9 @@ resolvConf: "{{ kube_resolv_conf }}"
{% if kubelet_config_extra_args %}
{{ kubelet_config_extra_args | to_nice_yaml(indent=2) }}
{% endif %}
{% set disable_websockets_to_kubelet = (container_manager == 'docker' and kube_version is version('1.36.0', '>=')) %}
{% set kubelet_gates = (kubelet_feature_gates | default(kube_feature_gates, true)) %}
{% if disable_websockets_to_kubelet %}
{% set kubelet_gates = kubelet_gates | reject('search', '^ExtendWebSocketsToKubelet=') | list %}
{% endif %}
{% if kubelet_gates or disable_websockets_to_kubelet %}
{% if _kubelet_feature_gates %}
featureGates:
{% if disable_websockets_to_kubelet %}
# cri-dockerd does not support WebSocket streaming
# https://github.com/kubernetes-sigs/kubespray/issues/13360
ExtendWebSocketsToKubelet: false
{% endif %}
{% for feature in kubelet_gates %}
{{ feature | replace("=", ": ") }}
{% endfor %}
{{ _kubelet_feature_gates | to_nice_yaml(indent=2) | indent(2) }}
{% endif %}
{% if tls_min_version is defined %}
tlsMinVersion: {{ tls_min_version }}
@@ -633,15 +633,14 @@ additional_sysctl: []
# kernel.pid_max: 0
additional_sysctl_min: {}
## List of key=value pairs that describe feature gates for
## the k8s cluster.
kube_feature_gates: []
kube_apiserver_feature_gates: []
kube_controller_feature_gates: []
kube_scheduler_feature_gates: []
kube_proxy_feature_gates: []
kubelet_feature_gates: []
kubeadm_feature_gates: []
## Dictionaries of feature gate names and boolean values for the k8s cluster.
kube_feature_gates: {}
kube_apiserver_feature_gates: {}
kube_controller_feature_gates: {}
kube_scheduler_feature_gates: {}
kube_proxy_feature_gates: {}
kubelet_feature_gates: {}
kubeadm_feature_gates: {}
local_volume_provisioner_storage_class: local-storage
local_volume_provisioner_base_dir: /mnt/disks
@@ -20,6 +20,15 @@ etcd_supported_versions:
kube_proxy_deployed: "{{ 'addon/kube-proxy' not in kubeadm_init_phases_skip }}"
# cri-dockerd does not support WebSocket streaming to the kubelet.
# https://github.com/kubernetes-sigs/kubespray/issues/13360
_kubelet_feature_gates: >-
{{ (kubelet_feature_gates | default(kube_feature_gates, true)) | combine(
{'ExtendWebSocketsToKubelet': false}
if container_manager == 'docker' and kube_version is version('1.36.0', '>=') else
{}
) }}
# Sysctl floors: only applied when the node's current value is lower than
# the desired one, so pre-tuned images and newer kernels are not regressed.
# Values are aligned with OpenShift Node Tuning Operator and kops, and
+18
View File
@@ -84,6 +84,24 @@
run_once: true
when: not ignore_assert_errors
- name: Stop if feature gates are not dictionaries of booleans
assert:
that:
- lookup('ansible.builtin.vars', item) is mapping
- lookup('ansible.builtin.vars', item) | dict2items | rejectattr('value', 'boolean') | length == 0
fail_msg: >-
{{ item }} must be a dictionary of feature gate names and boolean values.
Replace lists such as ["NodeSwap=true"] with {NodeSwap: true}.
loop:
- kube_feature_gates
- kube_apiserver_feature_gates
- kube_controller_feature_gates
- kube_scheduler_feature_gates
- kube_proxy_feature_gates
- kubelet_feature_gates
- kubeadm_feature_gates
when: not ignore_assert_errors
- name: Stop if even number of etcd hosts
assert:
that: groups.get('etcd', groups.kube_control_plane) | length is not divisibleby 2