feat: distribution is a dict and nodes and cluster are top level (#1288)

* feat: distribution is a dict and nodes and cluster are top level

Signed-off-by: Devin Buhl <devin@buhl.casa>

* chore: housekeeping

Signed-off-by: Devin Buhl <devin@buhl.casa>

---------

Signed-off-by: Devin Buhl <devin@buhl.casa>
This commit is contained in:
Devin Buhl authored and GitHub committed 2024-02-01 20:31:42 -05:00
1 parent cba35dc66a
commit 89673cd17b
55 files changed
+330 -302

No files matched your search

+11 -1
View File
@@ -6,10 +6,15 @@ from typing import Any
from typing import Any
from netaddr import IPNetwork
from bcrypt import hashpw, gensalt
import makejinja
import validation
def encrypt(value: str) -> str:
return hashpw(value.encode(), gensalt(rounds=10)).decode("ascii")
def nthhost(value: str, query: int) -> str:
value = IPNetwork(value)
try:
@@ -20,6 +25,7 @@ def nthhost(value: str, query: int) -> str:
return False
return value
def import_filter(file: Path) -> Callable[[dict[str, Any]], bool]:
module_path = file.relative_to(Path.cwd()).with_suffix("")
module_name = str(module_path).replace("/", ".")
@@ -31,6 +37,7 @@ def import_filter(file: Path) -> Callable[[dict[str, Any]], bool]:
spec.loader.exec_module(module)
return module.main
class Plugin(makejinja.plugin.Plugin):
def __init__(self, data: dict[str, Any], config: makejinja.config.Config):
self._data = data
@@ -45,12 +52,15 @@ class Plugin(makejinja.plugin.Plugin):
validation.validate(data)
def filters(self) -> makejinja.plugin.Filters:
return [nthhost]
return [encrypt, nthhost]
def path_filters(self):
return [self._mjfilter_func]
def _mjfilter_func(self, path: Path) -> bool:
return not any(
path.is_relative_to(excluded_dir) for excluded_dir in self._excluded_dirs
+11 -8
View File
@@ -10,7 +10,6 @@ GLOBAL_CLI_TOOLS = ["age", "cloudflared", "flux", "sops", "jq", "kubeconform", "
TALOS_CLI_TOOLS = ["talosctl", "talhelper"]
K0S_CLI_TOOLS = ["k0sctl"]
def required(*keys: str):
def wrapper_outter(func: Callable):
@wraps(func)
@@ -22,7 +21,6 @@ def required(*keys: str):
return wrapper
return wrapper_outter
def _validate_network(network: str, family: int) -> str:
try:
network = netaddr.IPNetwork(network)
@@ -40,9 +38,10 @@ def validate_python_version() -> None:
@required("distribution")
def validate_cli_tools(distribution: str, **_) -> None:
if distribution not in DISTRIBUTIONS:
raise ValueError(f"Invalid distribution {distribution}")
def validate_cli_tools(distribution: dict, **_) -> None:
distro = distribution.get("type")
if distro not in DISTRIBUTIONS:
raise ValueError(f"Invalid distribution {distro}")
for tool in GLOBAL_CLI_TOOLS:
if not which(tool):
raise ValueError(f"Missing required CLI tool {tool}")
@@ -55,9 +54,10 @@ def validate_cli_tools(distribution: str, **_) -> None:
@required("distribution")
def validate_distribution(distribution: str, **_) -> None:
if distribution not in DISTRIBUTIONS:
raise ValueError(f"Invalid distribution {distribution}")
def validate_distribution(distribution: dict, **_) -> None:
distro = distribution.get("type")
if distro not in DISTRIBUTIONS:
raise ValueError(f"Invalid distribution {distro}")
@required("timezone")
@@ -98,6 +98,9 @@ def validate_cluster_networks(cluster: dict, feature_gates: dict, **_) -> None:
def massage_config(data: dict) -> dict:
data["distribution"] = data.get("distribution", {})
data["nodes"] = data.get("nodes", [])
data["cluster"] = data.get("cluster", {})
data["flux"] = data.get("flux", {})
data["cloudflare"] = data.get("cloudflare", {})
data["feature_gates"] = data.get("feature_gates", {})
+2 -2
View File
@@ -1,7 +1,7 @@
{% if flux.enabled %}
---
creation_rules:
{% if distribution in ['talos'] %}
{% if distribution.type in ["talos"] %}
- # IMPORTANT: This rule MUST be above the others
path_regex: talos/.*\.sops\.ya?ml
key_groups:
@@ -13,7 +13,7 @@ creation_rules:
key_groups:
- age:
- "{{ flux.sops_age_public_key }}"
{% if distribution in ['k0s', 'k3s'] %}
{% if distribution.type in ["k0s", "k3s"] %}
- path_regex: ansible/.*\.sops\.ya?ml
key_groups:
- age:
+1 -1
View File
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k0s', 'k3s']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["k0s", "k3s"]
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k0s', 'k3s']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["k0s", "k3s"]
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k3s']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["k3s"]
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k3s']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["k3s"]
@@ -1 +1,10 @@
main = lambda data: data.get("distribution") in ['k3s']
main = lambda data: (
data.get("distribution").get("type", "k3s") in ["k3s"] and
len(
list(
filter(
lambda item: "controller" in item and item["controller"] is False, data.get("nodes").get("inventory")
)
)
) > 0
)
@@ -3,17 +3,17 @@ kubernetes:
children:
master:
hosts:
{% for item in cluster.nodes.inventory %}
{% for item in nodes.inventory %}
{% if item.controller %}
"{{ item.name }}":
ansible_user: "{{ item.ssh_username }}"
ansible_host: "{{ item.address }}"
{% endif %}
{% endfor %}
{% if cluster.nodes.inventory | selectattr('controller', 'equalto', False) | list | length %}
{% if nodes.inventory | selectattr('controller', 'equalto', False) | list | length %}
worker:
hosts:
{% for item in cluster.nodes.inventory %}
{% for item in nodes.inventory %}
{% if not item.controller %}
"{{ item.name }}":
ansible_user: "{{ item.ssh_username }}"
@@ -1,4 +1,4 @@
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
---
- name: Cluster Installation
hosts: kubernetes
@@ -10,7 +10,7 @@
ansible.builtin.pause:
seconds: 5
tasks:
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
- name: Ensure Kubernetes is running
ansible.builtin.include_role:
name: xanmanning.k3s
@@ -21,9 +21,9 @@
- name: Upgrade kube-vip
ansible.builtin.template:
src: templates/kube-vip-ds.yaml.j2
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
dest: "{% raw %}{{ k3s_server_manifests_dir }}{% endraw %}/kube-vip-ds.yaml"
{% elif distribution in ['k0s'] %}
{% elif distribution.type in ["k0s"] %}
dest: "/var/lib/k0s/manifests/kube-vip/kube-vip-ds.yaml"
{% endif %}
mode: preserve
@@ -1,4 +1,4 @@
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
---
- name: Cluster Nuke
hosts: kubernetes
@@ -1,4 +1,4 @@
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
---
- name: Prepare System
hosts: kubernetes
@@ -62,8 +62,8 @@
mode: '0644'
dest: /etc/resolv.conf
content: |
search {{ cluster.nodes.search_domain|default('.', true) }}
{% for item in cluster.nodes.dns_servers %}
search {{ nodes.search_domain|default('.', true) }}
{% for item in nodes.dns_servers | default(['1.1.1.1', '1.0.0.1']) %}
nameserver {{ item }}
{% endfor %}
@@ -12,9 +12,9 @@
seconds: 5
tasks:
- name: Details
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
ansible.builtin.command: "k3s kubectl get node {% raw %}{{ inventory_hostname }}{% endraw %} -o json"
{% elif distribution in ['k0s'] %}
{% elif distribution.type in ["k0s"] %}
ansible.builtin.command: "k0s kubectl get node {% raw %}{{ inventory_hostname }}{% endraw %} -o json"
{% endif %}
register: kubectl_get_node
@@ -32,9 +32,9 @@
- name: Cordon
kubernetes.core.k8s_drain:
name: "{% raw %}{{ inventory_hostname }}{% endraw %}"
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
kubeconfig: /etc/rancher/k3s/k3s.yaml
{% elif distribution in ['k0s'] %}
{% elif distribution.type in ["k0s"] %}
kubeconfig: /var/lib/k0s/pki/admin.conf
{% endif %}
state: cordon
@@ -43,9 +43,9 @@
- name: Drain
kubernetes.core.k8s_drain:
name: "{% raw %}{{ inventory_hostname }}{% endraw %}"
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
kubeconfig: /etc/rancher/k3s/k3s.yaml
{% elif distribution in ['k0s'] %}
{% elif distribution.type in ["k0s"] %}
kubeconfig: /var/lib/k0s/pki/admin.conf
{% endif %}
state: drain
@@ -78,9 +78,9 @@
- name: Uncordon
kubernetes.core.k8s_drain:
name: "{% raw %}{{ inventory_hostname }}{% endraw %}"
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
kubeconfig: /etc/rancher/k3s/k3s.yaml
{% elif distribution in ['k0s'] %}
{% elif distribution.type in ["k0s"] %}
kubeconfig: /var/lib/k0s/pki/admin.conf
{% endif %}
state: uncordon
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k3s']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["k3s"]
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k3s']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["k3s"]
@@ -8,7 +8,7 @@ collections:
version: 8.3.0
- name: kubernetes.core
version: 3.0.0
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
roles:
- name: xanmanning.k3s
src: https://github.com/PyratLabs/ansible-role-k3s
@@ -1 +1 @@
main = lambda data: data.get("flux").get("enabled") == True
main = lambda data: data.get("flux", {}).get("enabled", False) == True
@@ -1 +1 @@
main = lambda data: data.get("cloudflare").get("enabled") == True
main = lambda data: data.get("cloudflare", {}).get("enabled", False) == True
@@ -1 +0,0 @@
main = lambda data: data.get("flux").get("github").get("webhook").get("enabled") == True
@@ -3,4 +3,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./namespace.yaml
{% if flux.github.webhook.enabled %}
- ./webhooks/ks.yaml
{% endif %}
@@ -0,0 +1,6 @@
main = lambda data: (
data.get("flux", {})
.get("github", {})
.get("webhook", {})
.get("enabled", False) == True
)
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['talos']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["talos"]
@@ -4,11 +4,11 @@ kind: Kustomization
resources:
- ./namespace.yaml
- ./cilium/ks.yaml
{% if distribution in ['talos'] %}
{% if distribution.type in ["talos"] %}
- ./kubelet-csr-approver/ks.yaml
{% endif %}
- ./metrics-server/ks.yaml
{% if distribution in ['k0s', 'talos'] %}
{% if distribution.type in ["k0s", "talos"] %}
- ./spegel/ks.yaml
{% endif %}
- ./reloader/ks.yaml
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k0s', 'talos']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["k0s", "talos"]
@@ -24,10 +24,10 @@ spec:
keepHistory: false
values:
spegel:
{% if distribution in ['k0s'] %}
{% if distribution.type in ["k0s"] %}
containerdSock: /run/k0s/containerd.sock
containerdRegistryConfigPath: /var/lib/k0s/containerd/certs.d
{% elif distribution in ['talos'] %}
{% elif distribution.type in ["talos"] %}
containerdSock: /run/containerd/containerd.sock
containerdRegistryConfigPath: /etc/cri/conf.d/hosts
{% endif %}
@@ -1 +1 @@
main = lambda data: data.get("cloudflare").get("enabled") == True
main = lambda data: data.get("cloudflare", {}).get("enabled", False) == True
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k0s']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["k0s"]
@@ -18,7 +18,7 @@ spec:
controllers:
discovery:
selector: {}
{% if cluster.nodes.inventory | selectattr('controller', 'equalto', False) | list | length %}
{% if nodes.inventory | selectattr('controller', 'equalto', False) | list | length %}
workers:
discovery:
selector: {}
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k3s']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["k3s"]
@@ -3,15 +3,15 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./namespace.yaml
{% if distribution in ['k3s', 'talos'] %}
{% if distribution.type in ['k3s', 'talos'] %}
- ./system-upgrade-controller/ks.yaml
{% endif %}
{% if distribution in ['k0s'] %}
{% if distribution.type in ["k0s"] %}
- ./k0s/ks.yaml
{% endif %}
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
- ./k3s/ks.yaml
{% endif %}
{% if distribution in ['talos'] and cluster.nodes.talos.schematics.enabled %}
{% if distribution.type in ["talos"] and nodes.talos.schematics.enabled %}
- ./talos/ks.yaml
{% endif %}
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k3s', 'talos']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ['k3s', 'talos']
@@ -11,7 +11,7 @@ subjects:
- kind: ServiceAccount
name: system-upgrade
namespace: system-upgrade
{% if distribution in ['talos'] %}
{% if distribution.type in ["talos"] %}
---
apiVersion: talos.dev/v1alpha1
kind: ServiceAccount
@@ -1,6 +1,7 @@
main = lambda data: data.get("distribution") in ['talos'] and (
data.get("cluster", {})
.get("nodes", {})
.get("talos", {})
.get("schematics", {})
.get("enabled", False) == True)
main = lambda data: (
data.get("distribution", {}).get("type", "k3s") in ["talos"] and
data.get("distribution", {})
.get("talos", {})
.get("schematics", {})
.get("enabled", False) == True
)
@@ -88,6 +88,6 @@ spec:
args:
- --nodes=$(NODE_IP)
- upgrade
- --image=factory.talos.dev/installer/{{ cluster.nodes.talos.schematics.id }}:$(SYSTEM_UPGRADE_PLAN_LATEST_VERSION)
- --image=factory.talos.dev/installer/{{ nodes.talos.schematics.id }}:$(SYSTEM_UPGRADE_PLAN_LATEST_VERSION)
- --preserve=true
- --wait=false
@@ -1 +1 @@
main = lambda data: data.get("flux").get("enabled") == True
main = lambda data: data.get("flux", {}).get("enabled", False) == True
@@ -1 +1 @@
main = lambda data: data.get("flux").get("enabled") == True
main = lambda data: data.get("flux", {}).get("enabled", False) == True
@@ -12,10 +12,10 @@ resources:
- ./jetstack.yaml
- ./metrics-server.yaml
- ./openebs.yaml
{% if distribution in ['talos'] %}
{% if distribution.type in ["talos"] %}
- ./postfinance.yaml
{% endif %}
- ./stakater.yaml
{% if distribution in ['k0s', 'talos'] %}
{% if distribution.type in ["k0s", "talos"] %}
- ./xenitab.yaml
{% endif %}
@@ -1,4 +1,4 @@
{% if distribution in ['talos'] %}
{% if distribution.type in ["talos"] %}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@@ -1,4 +1,4 @@
{% if distribution in ['k0s', 'talos'] %}
{% if distribution.type in ["k0s", "talos"] %}
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
@@ -7,7 +7,7 @@ metadata:
data:
TIMEZONE: "{{ timezone }}"
CLUSTER_CIDR: "{{ cluster.pod_network.split(',')[0] }}"
NODE_CIDR: "{{ cluster.nodes.host_network }}"
NODE_CIDR: "{{ nodes.host_network }}"
{% if feature_gates.dual_stack_ipv4_first %}
CLUSTER_CIDR_V6: "{{ cluster.pod_network.split(',')[1] }}"
{% endif %}
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['k0s']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["k0s"]
@@ -5,7 +5,7 @@ metadata:
name: home-kubernetes
spec:
hosts:
{% for item in cluster.nodes.inventory %}
{% for item in nodes.inventory %}
{% if item.controller %}
- role: controller+worker
{% else %}
@@ -35,8 +35,8 @@ apt-get install -y --no-install-recommends \
chattr -i /etc/resolv.conf
rm -f /etc/resolv.conf
cat <<EOF > /etc/resolv.conf
search {{ cluster.nodes.search_domain|default('.', true) }}
{% for item in cluster.nodes.dns_servers %}
search {{ nodes.search_domain|default('.', true) }}
{% for item in nodes.dns_servers | default(['1.1.1.1', '1.0.0.1']) %}
nameserver {{ item }}
{% endfor %}
EOF
@@ -1 +1 @@
main = lambda data: data.get("distribution") in ['talos']
main = lambda data: data.get("distribution", {}).get("type", "k3s") in ["talos"]
@@ -22,7 +22,7 @@ cniConfig:
name: none
nodes:
{% for item in cluster.nodes.inventory %}
{% for item in nodes.inventory %}
- hostname: "{{ item.name }}"
ipAddress: "{{ item.address }}"
{% if item.talos_disk_device.startswith('/') %}
@@ -35,26 +35,26 @@ nodes:
networkInterfaces:
- interface: eth0
dhcp: false
{% if cluster.nodes.talos.vlan %}
{% if distribution.talos.vlan %}
vlans:
- vlanId: {{ cluster.nodes.talos.vlan }}
- vlanId: {{ distribution.talos.vlan }}
addresses:
- "{{ item.address }}/{{ cluster.nodes.host_network.split('/') | last }}"
- "{{ item.address }}/{{ nodes.host_network.split('/') | last }}"
mtu: 1500
routes:
- network: 0.0.0.0/0
gateway: "{{ cluster.nodes.host_network | nthhost(1) }}"
gateway: "{{ nodes.host_network | nthhost(1) }}"
{% if item.controller %}
vip:
ip: "{{ cluster.endpoint_vip }}"
{% endif %}
{% else %}
addresses:
- "{{ item.address }}/{{ cluster.nodes.host_network.split('/') | last }}"
- "{{ item.address }}/{{ nodes.host_network.split('/') | last }}"
mtu: 1500
routes:
- network: 0.0.0.0/0
gateway: "{{ cluster.nodes.host_network | nthhost(1) }}"
gateway: "{{ nodes.host_network | nthhost(1) }}"
{% if item.controller %}
vip:
ip: "{{ cluster.endpoint_vip }}"
@@ -63,10 +63,10 @@ nodes:
{% endfor %}
controlPlane:
{% if cluster.nodes.talos.schematics.enabled %}
{% if distribution.talos.schematics.enabled %}
schematic:
customization:
{{ cluster.nodes.talos.schematics.customization | indent(6, first=True) }}
{{ distribution.talos.schematics.customization | indent(6, first=True) }}
{% endif %}
patches:
# Configure containerd
@@ -110,7 +110,7 @@ controlPlane:
rotate-server-certificates: true
nodeIP:
validSubnets:
- "{{ cluster.nodes.host_network }}"
- "{{ nodes.host_network }}"
# Enable KubePrism
- &kubePrismPatch |-
@@ -125,7 +125,7 @@ controlPlane:
machine:
network:
nameservers:
{% for item in cluster.nodes.dns_servers %}
{% for item in nodes.dns_servers | default(['1.1.1.1', '1.0.0.1']) %}
- {{ item }}
{% endfor %}
@@ -165,7 +165,7 @@ controlPlane:
extraArgs:
listen-metrics-urls: http://0.0.0.0:2381
advertisedSubnets:
- "{{ cluster.nodes.host_network }}"
- "{{ nodes.host_network }}"
# Disable default API server admission plugins.
- |-
@@ -183,12 +183,12 @@ controlPlane:
allowedKubernetesNamespaces:
- system-upgrade
{% if cluster.nodes.inventory | selectattr('controller', 'equalto', False) | list | length %}
{% if nodes.inventory | selectattr('controller', 'equalto', False) | list | length %}
worker:
{% if cluster.nodes.talos.schematics.enabled %}
{% if distribution.talos.schematics.enabled %}
schematic:
customization:
{{ cluster.nodes.talos.schematics.customization | indent(6, first=True) }}
{{ distribution.talos.schematics.customization | indent(6, first=True) }}
{% endif %}
patches:
- *containerdPatch
@@ -6,9 +6,9 @@ cluster:
id: 1
containerRuntime:
integration: containerd
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
socketPath: /var/run/k3s/containerd/containerd.sock
{% elif distribution in ['k0s'] %}
{% elif distribution.type in ["k0s"] %}
socketPath: /var/run/k0s/containerd.sock
{% endif %}
endpointRoutes:
@@ -60,13 +60,13 @@ ipv6NativeRoutingCIDR: "${CLUSTER_CIDR_V6}"
ipv6:
enabled: true
{% endif %}
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
k8sServiceHost: 127.0.0.1
k8sServicePort: 6444
{% elif distribution in ['k0s'] %}
{% elif distribution.type in ["k0s"] %}
k8sServiceHost: localhost
k8sServicePort: 7443
{% elif distribution in ['talos'] %}
{% elif distribution.type in ["talos"] %}
k8sServiceHost: 127.0.0.1
k8sServicePort: 7445
{% endif %}
@@ -6,9 +6,9 @@ cluster:
id: 1
containerRuntime:
integration: containerd
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
socketPath: /var/run/k3s/containerd/containerd.sock
{% elif distribution in ['k0s'] %}
{% elif distribution.type in ["k0s"] %}
socketPath: /var/run/k0s/containerd.sock
{% endif %}
endpointRoutes:
@@ -23,13 +23,13 @@ ipv6NativeRoutingCIDR: "{{ cluster.pod_network_v6 }}"
ipv6:
enabled: true
{% endif %}
{% if distribution in ['k3s'] %}
{% if distribution.type in ["k3s"] %}
k8sServiceHost: 127.0.0.1
k8sServicePort: 6444
{% elif distribution in ['k0s'] %}
{% elif distribution.type in ["k0s"] %}
k8sServiceHost: localhost
k8sServicePort: 7443
{% elif distribution in ['talos'] %}
{% elif distribution.type in ["talos"] %}
k8sServiceHost: 127.0.0.1
k8sServicePort: 7445
{% endif %}
@@ -1,2 +1,2 @@
providerRegex: ^({{ (cluster.nodes.inventory | map(attribute='name') | join('|')) }})$
providerRegex: ^({{ (nodes.inventory | map(attribute='name') | join('|')) }})$
bypassDnsResolution: true