Files
kubespray/roles/kubernetes/control-plane/templates/kubeadm-config.v1beta4.yaml.j2
Chad Swenson 8443f370d4 Structured AuthorizationConfiguration (#11852)
Adds the ability to configure the Kubernetes API server with a structured authorization configuration file.

Structured AuthorizationConfiguration is a new feature in Kubernetes v1.29+ (GA in v1.32) that configures the API server's authorization modes with a structured configuration file.
AuthorizationConfiguration files offer features not available with the `--authorization-mode` flag, although Kubespray supports both methods and authorization-mode remains the default for now.

Note: Because the `--authorization-config` and `--authorization-mode` flags are mutually exclusive, the `authorization_modes` ansible variable is ignored when `kube_apiserver_use_authorization_config_file` is set to true. The two features cannot be used at the same time.

Docs: https://kubernetes.io/docs/reference/access-authn-authz/authorization/#configuring-the-api-server-using-an-authorization-config-file
Blog + Examples: https://kubernetes.io/blog/2024/04/26/multi-webhook-and-modular-authorization-made-much-easier/
KEP: https://github.com/kubernetes/enhancements/tree/master/keps/sig-auth/3221-structured-authorization-configuration

I tested this all the way back to k8s v1.29 when AuthorizationConfiguration was first introduced as an alpha feature, although v1.29 required some additional workarounds with `kubeadm_patches`, which I included in example comments.

I also included some example comments with CEL expressions that allowed me to configure webhook authorizers without hitting kubeadm 1.29+ issues that block cluster creation and upgrades such as this one: https://github.com/kubernetes/cloud-provider-openstack/issues/2575.
My workaround configures the webhook to ignore requests from kubeadm and system components, which prevents fatal errors from webhooks that are not available yet, and should be authorized by Node or RBAC anyway.
2025-01-07 09:14:28 +01:00

554 lines
20 KiB
Django/Jinja

apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
{% if kubeadm_token is defined %}
bootstrapTokens:
- token: "{{ kubeadm_token }}"
description: "kubespray kubeadm bootstrap token"
ttl: "24h"
{% endif %}
localAPIEndpoint:
advertiseAddress: {{ kube_apiserver_address }}
bindPort: {{ kube_apiserver_port }}
{% if kubeadm_certificate_key is defined %}
certificateKey: {{ kubeadm_certificate_key }}
{% endif %}
nodeRegistration:
{% if kube_override_hostname | default('') %}
name: "{{ kube_override_hostname }}"
{% endif %}
{% if 'kube_control_plane' in group_names and 'kube_node' not in group_names %}
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
{% else %}
taints: []
{% endif %}
criSocket: {{ cri_socket }}
{% if cloud_provider == "external" %}
kubeletExtraArgs:
- name: cloud-provider
value: external
{% endif %}
{% if kubeadm_patches | length > 0 %}
patches:
directory: {{ kubeadm_patches_dir }}
{% endif %}
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
clusterName: {{ cluster_name }}
encryptionAlgorithm: {{ kube_asymmetric_encryption_algorithm }}
etcd:
{% if etcd_deployment_type != "kubeadm" %}
external:
endpoints:
{% for endpoint in etcd_access_addresses.split(',') %}
- {{ endpoint }}
{% endfor %}
caFile: {{ etcd_cert_dir }}/{{ kube_etcd_cacert_file }}
certFile: {{ etcd_cert_dir }}/{{ kube_etcd_cert_file }}
keyFile: {{ etcd_cert_dir }}/{{ kube_etcd_key_file }}
{% elif etcd_deployment_type == "kubeadm" %}
local:
imageRepository: "{{ etcd_image_repo | regex_replace("/etcd$","") }}"
imageTag: "{{ etcd_image_tag }}"
dataDir: "{{ etcd_data_dir }}"
extraArgs:
- name: metrics
value: {{ etcd_metrics }}
- name: election-timeout
value: "{{ etcd_election_timeout }}"
- name: heartbeat-interval
value: "{{ etcd_heartbeat_interval }}"
- name: auto-compaction-retention
value: "{{ etcd_compaction_retention }}"
{% if etcd_listen_metrics_urls is defined %}
- name: listen-metrics-urls
value: "{{ etcd_listen_metrics_urls }}"
{% endif %}
{% if etcd_snapshot_count is defined %}
- name: snapshot-count
value: "{{ etcd_snapshot_count }}"
{% endif %}
{% if etcd_quota_backend_bytes is defined %}
- name: quota-backend-bytes
value: "{{ etcd_quota_backend_bytes }}"
{% endif %}
{% if etcd_max_request_bytes is defined %}
- name: max-request-bytes
value: "{{ etcd_max_request_bytes }}"
{% endif %}
{% if etcd_log_level is defined %}
- name: log-level
value: "{{ etcd_log_level }}"
{% endif %}
{% for key, value in etcd_extra_vars.items() %}
- name: {{ key }}
value: "{{ value }}"
{% endfor %}
serverCertSANs:
{% for san in etcd_cert_alt_names %}
- "{{ san }}"
{% endfor %}
{% for san in etcd_cert_alt_ips %}
- "{{ san }}"
{% endfor %}
peerCertSANs:
{% for san in etcd_cert_alt_names %}
- "{{ san }}"
{% endfor %}
{% for san in etcd_cert_alt_ips %}
- "{{ san }}"
{% endfor %}
{% endif %}
dns:
imageRepository: {{ coredns_image_repo | regex_replace('/coredns(?!/coredns).*$', '') }}
imageTag: {{ coredns_image_tag }}
networking:
dnsDomain: {{ dns_domain }}
serviceSubnet: "{{ kube_service_addresses }}{{ ',' + kube_service_addresses_ipv6 if enable_dual_stack_networks else '' }}"
{% if kube_network_plugin is defined and kube_network_plugin not in ["kube-ovn"] %}
podSubnet: "{{ kube_pods_subnet }}{{ ',' + kube_pods_subnet_ipv6 if enable_dual_stack_networks else '' }}"
{% endif %}
{% if kubeadm_feature_gates %}
featureGates:
{% for feature in kubeadm_feature_gates %}
{{ feature | replace("=", ": ") }}
{% endfor %}
{% endif %}
kubernetesVersion: {{ kube_version }}
{% if kubeadm_config_api_fqdn is defined %}
controlPlaneEndpoint: {{ kubeadm_config_api_fqdn }}:{{ loadbalancer_apiserver.port | default(kube_apiserver_port) }}
{% else %}
controlPlaneEndpoint: {{ ip | default(fallback_ip) }}:{{ kube_apiserver_port }}
{% endif %}
certificatesDir: {{ kube_cert_dir }}
imageRepository: {{ kube_image_repo }}
apiServer:
extraArgs:
{% if kube_apiserver_etcd_compaction_interval is defined %}
- name: etcd-compaction-interval
value: "{{ kube_apiserver_etcd_compaction_interval }}"
{% endif %}
{% if kube_apiserver_pod_eviction_not_ready_timeout_seconds is defined %}
- name: default-not-ready-toleration-seconds
value: "{{ kube_apiserver_pod_eviction_not_ready_timeout_seconds }}"
{% endif %}
{% if kube_apiserver_pod_eviction_unreachable_timeout_seconds is defined %}
- name: default-unreachable-toleration-seconds
value: "{{ kube_apiserver_pod_eviction_unreachable_timeout_seconds }}"
{% endif %}
{% if kube_api_anonymous_auth is defined %}
- name: anonymous-auth
value: "{{ kube_api_anonymous_auth }}"
{% endif %}
{% if kube_apiserver_use_authorization_config_file %}
- name: authorization-config
value: "{{ kube_config_dir }}/apiserver-authorization-config.yaml"
{% else %}
- name: authorization-mode
value: "{{ authorization_modes | join(',') }}"
{% endif %}
- name: bind-address
value: "{{ kube_apiserver_bind_address }}"
{% if kube_apiserver_enable_admission_plugins | length > 0 %}
- name: enable-admission-plugins
value: "{{ kube_apiserver_enable_admission_plugins | join(',') }}"
{% endif %}
{% if kube_apiserver_admission_control_config_file %}
- name: admission-control-config-file
value: "{{ kube_config_dir }}/admission-controls.yaml"
{% endif %}
{% if kube_apiserver_disable_admission_plugins | length > 0 %}
- name: disable-admission-plugins
value: "{{ kube_apiserver_disable_admission_plugins | join(',') }}"
{% endif %}
- name: apiserver-count
value: "{{ kube_apiserver_count }}"
- name: endpoint-reconciler-type
value: lease
{% if etcd_events_cluster_enabled %}
- name: etcd-servers-overrides
value: "/events#{{ etcd_events_access_addresses_semicolon }}"
{% endif %}
- name: service-node-port-range
value: "{{ kube_apiserver_node_port_range }}"
- name: service-cluster-ip-range
value: "{{ kube_service_addresses }}{{ ',' + kube_service_addresses_ipv6 if enable_dual_stack_networks else '' }}"
- name: kubelet-preferred-address-types
value: "{{ kubelet_preferred_address_types }}"
- name: profiling
value: "{{ kube_profiling }}"
- name: request-timeout
value: "{{ kube_apiserver_request_timeout }}"
- name: enable-aggregator-routing
value: "{{ kube_api_aggregator_routing }}"
{% if kube_apiserver_service_account_lookup %}
- name: service-account-lookup
value: "{{ kube_apiserver_service_account_lookup }}"
{% endif %}
{% if kube_oidc_auth | default(false) and kube_oidc_url is defined and kube_oidc_client_id is defined %}
- name: oidc-issuer-url
value: "{{ kube_oidc_url }}"
- name: oidc-client-id
value: "{{ kube_oidc_client_id }}"
{% if kube_oidc_ca_file is defined %}
- name: oidc-ca-file
value: "{{ kube_oidc_ca_file }}"
{% endif %}
{% if kube_oidc_username_claim is defined %}
- name: oidc-username-claim
value: "{{ kube_oidc_username_claim }}"
{% endif %}
{% if kube_oidc_groups_claim is defined %}
- name: oidc-groups-claim
value: "{{ kube_oidc_groups_claim }}"
{% endif %}
{% if kube_oidc_username_prefix is defined %}
- name: oidc-username-prefix
value: "{{ kube_oidc_username_prefix }}"
{% endif %}
{% if kube_oidc_groups_prefix is defined %}
- name: oidc-groups-prefix
value: "{{ kube_oidc_groups_prefix }}"
{% endif %}
{% endif %}
{% if kube_webhook_token_auth | default(false) %}
- name: authentication-token-webhook-config-file
value: "{{ kube_config_dir }}/webhook-token-auth-config.yaml"
{% endif %}
{% if kube_webhook_authorization and not kube_apiserver_use_authorization_config_file %}
- name: authorization-webhook-config-file
value: "{{ kube_config_dir }}/webhook-authorization-config.yaml"
{% endif %}
{% if kube_encrypt_secret_data %}
- name: encryption-provider-config
value: "{{ kube_cert_dir }}/secrets_encryption.yaml"
{% endif %}
- name: storage-backend
value: "{{ kube_apiserver_storage_backend }}"
{% if kube_api_runtime_config | length > 0 %}
- name: runtime-config
value: "{{ kube_api_runtime_config | join(',') }}"
{% endif %}
- name: allow-privileged
value: "true"
{% if kubernetes_audit or kubernetes_audit_webhook %}
- name: audit-policy-file
value: "{{ audit_policy_file }}"
{% endif %}
{% if kubernetes_audit %}
- name: audit-log-path
value: "{{ audit_log_path }}"
- name: audit-log-maxage
value: "{{ audit_log_maxage }}"
- name: audit-log-maxbackup
value: "{{ audit_log_maxbackups }}"
- name: audit-log-maxsize
value: "{{ audit_log_maxsize }}"
{% endif %}
{% if kubernetes_audit_webhook %}
- name: audit-webhook-config-file
value: "{{ audit_webhook_config_file }}"
- name: audit-webhook-mode
value: "{{ audit_webhook_mode }}"
{% if audit_webhook_mode == "batch" %}
- name: audit-webhook-batch-max-size
value: "{{ audit_webhook_batch_max_size }}"
- name: audit-webhook-batch-max-wait
value: "{{ audit_webhook_batch_max_wait }}"
{% endif %}
{% endif %}
{% for key in kube_kubeadm_apiserver_extra_args %}
- name: "{{ key }}"
value: "{{ kube_kubeadm_apiserver_extra_args[key] }}"
{% endfor %}
{% if kube_apiserver_feature_gates or kube_feature_gates %}
- name: feature-gates
value: "{{ kube_apiserver_feature_gates | default(kube_feature_gates, true) | join(',') }}"
{% endif %}
{% if tls_min_version is defined %}
- name: tls-min-version
value: "{{ tls_min_version }}"
{% endif %}
{% if tls_cipher_suites is defined %}
- name: tls-cipher-suites
value: "{% for tls in tls_cipher_suites %}{{ tls }}{{ ',' if not loop.last else '' }}{% endfor %}"
{% endif %}
{% if event_ttl_duration is defined %}
- name: event-ttl
value: "{{ event_ttl_duration }}"
{% endif %}
{% if kubelet_rotate_server_certificates %}
- name: kubelet-certificate-authority
value: "{{ kube_cert_dir }}/ca.crt"
{% endif %}
{% if kube_apiserver_tracing %}
- name: tracing-config-file
value: "{{ kube_config_dir }}/tracing/apiserver-tracing.yaml"
{% endif %}
{% if kubernetes_audit or kube_token_auth or kube_webhook_token_auth or apiserver_extra_volumes or ssl_ca_dirs | length %}
extraVolumes:
{% if kube_token_auth %}
- name: token-auth-config
hostPath: {{ kube_token_dir }}
mountPath: {{ kube_token_dir }}
{% endif %}
{% if kube_webhook_token_auth | default(false) %}
- name: webhook-token-auth-config
hostPath: {{ kube_config_dir }}/webhook-token-auth-config.yaml
mountPath: {{ kube_config_dir }}/webhook-token-auth-config.yaml
{% endif %}
{% if kube_webhook_authorization | default(false) %}
- name: webhook-authorization-config
hostPath: {{ kube_config_dir }}/webhook-authorization-config.yaml
mountPath: {{ kube_config_dir }}/webhook-authorization-config.yaml
{% endif %}
{% if kube_apiserver_use_authorization_config_file %}
- name: authorization-config
hostPath: {{ kube_config_dir }}/apiserver-authorization-config.yaml
mountPath: {{ kube_config_dir }}/apiserver-authorization-config.yaml
{% endif %}
{% if kubernetes_audit or kubernetes_audit_webhook %}
- name: {{ audit_policy_name }}
hostPath: {{ audit_policy_hostpath }}
mountPath: {{ audit_policy_mountpath }}
{% if audit_log_path != "-" %}
- name: {{ audit_log_name }}
hostPath: {{ audit_log_hostpath }}
mountPath: {{ audit_log_mountpath }}
readOnly: false
{% endif %}
{% endif %}
{% if kube_apiserver_admission_control_config_file %}
- name: admission-control-configs
hostPath: {{ kube_config_dir }}/admission-controls
mountPath: {{ kube_config_dir }}
readOnly: false
pathType: DirectoryOrCreate
{% endif %}
{% if kube_apiserver_tracing %}
- name: tracing
hostPath: {{ kube_config_dir }}/tracing
mountPath: {{ kube_config_dir }}/tracing
readOnly: true
pathType: DirectoryOrCreate
{% endif %}
{% for volume in apiserver_extra_volumes %}
- name: {{ volume.name }}
hostPath: {{ volume.hostPath }}
mountPath: {{ volume.mountPath }}
readOnly: {{ volume.readOnly | d(not (volume.writable | d(false))) }}
{% endfor %}
{% if ssl_ca_dirs | length %}
{% for dir in ssl_ca_dirs %}
- name: {{ dir | regex_replace('^/(.*)$', '\\1' ) | regex_replace('/', '-') }}
hostPath: {{ dir }}
mountPath: {{ dir }}
readOnly: true
{% endfor %}
{% endif %}
{% endif %}
certSANs:
{% for san in apiserver_sans %}
- "{{ san }}"
{% endfor %}
controllerManager:
extraArgs:
- name: node-monitor-grace-period
value: "{{ kube_controller_node_monitor_grace_period }}"
- name: node-monitor-period
value: "{{ kube_controller_node_monitor_period }}"
{% if kube_network_plugin is defined and kube_network_plugin not in ["kube-ovn"] %}
- name: cluster-cidr
value: "{{ kube_pods_subnet }}{{ ',' + kube_pods_subnet_ipv6 if enable_dual_stack_networks else '' }}"
{% endif %}
- name: service-cluster-ip-range
value: "{{ kube_service_addresses }}{{ ',' + kube_service_addresses_ipv6 if enable_dual_stack_networks else '' }}"
{% if kube_network_plugin is defined and kube_network_plugin == "calico" and not calico_ipam_host_local %}
- name: allocate-node-cidrs
value: "false"
{% else %}
{% if enable_dual_stack_networks %}
- name: node-cidr-mask-size-ipv4
value: "{{ kube_network_node_prefix }}"
- name: node-cidr-mask-size-ipv6
value: "{{ kube_network_node_prefix_ipv6 }}"
{% else %}
- name: node-cidr-mask-size
value: "{{ kube_network_node_prefix }}"
{% endif %}
{% endif %}
- name: profiling
value: "{{ kube_profiling }}"
- name: terminated-pod-gc-threshold
value: "{{ kube_controller_terminated_pod_gc_threshold }}"
- name: bind-address
value: "{{ kube_controller_manager_bind_address }}"
- name: leader-elect-lease-duration
value: "{{ kube_controller_manager_leader_elect_lease_duration }}"
- name: leader-elect-renew-deadline
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(',') }}"
{% endif %}
{% for key in kube_kubeadm_controller_extra_args %}
- name: "{{ key }}"
value: "{{ kube_kubeadm_controller_extra_args[key] }}"
{% endfor %}
{% if cloud_provider is defined and cloud_provider in ["openstack", "azure", "vsphere", "aws", "gce"] %}
- name: cloud-provider
value: "{{ cloud_provider }}"
- name: cloud-config
value: "{{ kube_config_dir }}/cloud_config"
{% endif %}
{% if kube_network_plugin is defined and kube_network_plugin not in ["cloud"] %}
- name: configure-cloud-routes
value: "false"
{% endif %}
{% if kubelet_flexvolumes_plugins_dir is defined %}
- name: flex-volume-plugin-dir
value: "{{ kubelet_flexvolumes_plugins_dir }}"
{% endif %}
{% if tls_min_version is defined %}
- name: tls-min-version
value: "{{ tls_min_version }}"
{% endif %}
{% if tls_cipher_suites is defined %}
- name: tls-cipher-suites
value: "{% for tls in tls_cipher_suites %}{{ tls }}{{ ',' if not loop.last else '' }}{% endfor %}"
{% endif %}
{% if cloud_provider is defined and cloud_provider in ["openstack", "azure", "vsphere", "aws", "gce"] or controller_manager_extra_volumes %}
extraVolumes:
{% if cloud_provider is defined and cloud_provider in ["openstack"] and openstack_cacert is defined %}
- name: openstackcacert
hostPath: "{{ kube_config_dir }}/openstack-cacert.pem"
mountPath: "{{ kube_config_dir }}/openstack-cacert.pem"
{% endif %}
{% if cloud_provider is defined and cloud_provider in ["openstack", "azure", "vsphere", "aws", "gce"] %}
- name: cloud-config
hostPath: {{ kube_config_dir }}/cloud_config
mountPath: {{ kube_config_dir }}/cloud_config
{% endif %}
{% for volume in controller_manager_extra_volumes %}
- name: {{ volume.name }}
hostPath: {{ volume.hostPath }}
mountPath: {{ volume.mountPath }}
readOnly: {{ volume.readOnly | d(not (volume.writable | d(false))) }}
{% endfor %}
{% endif %}
scheduler:
extraArgs:
- name: bind-address
value: "{{ kube_scheduler_bind_address }}"
- name: config
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(',') }}"
{% endif %}
- name: profiling
value: "{{ kube_profiling }}"
{% if kube_kubeadm_scheduler_extra_args | length > 0 %}
{% for key in kube_kubeadm_scheduler_extra_args %}
- name: "{{ key }}"
value: "{{ kube_kubeadm_scheduler_extra_args[key] }}"
{% endfor %}
{% endif %}
{% if tls_min_version is defined %}
- name: tls-min-version
value: "{{ tls_min_version }}"
{% endif %}
{% if tls_cipher_suites is defined %}
- name: tls-cipher-suites
value: "{% for tls in tls_cipher_suites %}{{ tls }}{{ ',' if not loop.last else '' }}{% endfor %}"
{% endif %}
extraVolumes:
- name: kubescheduler-config
hostPath: {{ kube_config_dir }}/kubescheduler-config.yaml
mountPath: {{ kube_config_dir }}/kubescheduler-config.yaml
readOnly: true
{% if scheduler_extra_volumes %}
{% for volume in scheduler_extra_volumes %}
- name: {{ volume.name }}
hostPath: {{ volume.hostPath }}
mountPath: {{ volume.mountPath }}
readOnly: {{ volume.readOnly | d(not (volume.writable | d(false))) }}
{% endfor %}
{% endif %}
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
bindAddress: {{ kube_proxy_bind_address }}
clientConnection:
acceptContentTypes: {{ kube_proxy_client_accept_content_types }}
burst: {{ kube_proxy_client_burst }}
contentType: {{ kube_proxy_client_content_type }}
kubeconfig: {{ kube_proxy_client_kubeconfig }}
qps: {{ kube_proxy_client_qps }}
{% if kube_network_plugin is defined and kube_network_plugin not in ["kube-ovn"] %}
clusterCIDR: "{{ kube_pods_subnet }}{{ ',' + kube_pods_subnet_ipv6 if enable_dual_stack_networks else '' }}"
{% endif %}
configSyncPeriod: {{ kube_proxy_config_sync_period }}
conntrack:
maxPerCore: {{ kube_proxy_conntrack_max_per_core }}
min: {{ kube_proxy_conntrack_min }}
tcpCloseWaitTimeout: {{ kube_proxy_conntrack_tcp_close_wait_timeout }}
tcpEstablishedTimeout: {{ kube_proxy_conntrack_tcp_established_timeout }}
enableProfiling: {{ kube_proxy_enable_profiling }}
healthzBindAddress: {{ kube_proxy_healthz_bind_address }}
hostnameOverride: "{{ kube_override_hostname }}"
iptables:
masqueradeAll: {{ kube_proxy_masquerade_all }}
masqueradeBit: {{ kube_proxy_masquerade_bit }}
minSyncPeriod: {{ kube_proxy_min_sync_period }}
syncPeriod: {{ kube_proxy_sync_period }}
ipvs:
excludeCIDRs: {{ kube_proxy_exclude_cidrs }}
minSyncPeriod: {{ kube_proxy_min_sync_period }}
scheduler: {{ kube_proxy_scheduler }}
syncPeriod: {{ kube_proxy_sync_period }}
strictARP: {{ kube_proxy_strict_arp }}
tcpTimeout: {{ kube_proxy_tcp_timeout }}
tcpFinTimeout: {{ kube_proxy_tcp_fin_timeout }}
udpTimeout: {{ kube_proxy_udp_timeout }}
metricsBindAddress: {{ kube_proxy_metrics_bind_address }}
mode: {{ kube_proxy_mode }}
nodePortAddresses: {{ kube_proxy_nodeport_addresses }}
oomScoreAdj: {{ kube_proxy_oom_score_adj }}
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 %}
{% endif %}
{# DNS settings for kubelet #}
{% if enable_nodelocaldns %}
{% set kubelet_cluster_dns = [nodelocaldns_ip] %}
{% elif dns_mode in ['coredns'] %}
{% set kubelet_cluster_dns = [skydns_server] %}
{% elif dns_mode == 'coredns_dual' %}
{% set kubelet_cluster_dns = [skydns_server,skydns_server_secondary] %}
{% elif dns_mode == 'manual' %}
{% set kubelet_cluster_dns = [manual_dns_server] %}
{% else %}
{% set kubelet_cluster_dns = [] %}
{% endif %}
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
clusterDNS:
{% for dns_address in kubelet_cluster_dns %}
- {{ dns_address }}
{% endfor %}
{% if kubelet_feature_gates or kube_feature_gates %}
{% set feature_gates = ( kubelet_feature_gates | default(kube_feature_gates, true) ) %}
featureGates:
{% for feature in feature_gates %}
{{ feature | replace("=", ": ") }}
{% endfor %}
{% endif %}