--- - name: Skip k3s when state is absent ansible.builtin.meta: end_host when: k3s_state == "absent" - name: Set k3s service name ansible.builtin.set_fact: k3s_service_name: "{{ 'k3s' if k3s_role == 'server' else 'k3s-agent' }}" - name: Check if k3s service is installed ansible.builtin.stat: path: "/etc/systemd/system/{{ k3s_service_name }}.service" register: k3s_service - name: Check if k3s binary exists ansible.builtin.stat: path: /usr/local/bin/k3s register: k3s_bin - name: Determine if k3s needs installation ansible.builtin.set_fact: k3s_needs_install: "{{ not k3s_bin.stat.exists or not k3s_service.stat.exists }}" - name: Install k3s dependencies ansible.builtin.package: name: - curl - iptables state: present - name: Preflight - ensure rancher mountpoint is mounted ansible.builtin.command: "findmnt -n {{ k3s_rancher_mountpoint | default('/var/lib/rancher') }}" register: rancher_mounted changed_when: false failed_when: rancher_mounted.rc != 0 - name: Preflight - ensure cgroup kernel params are present ansible.builtin.command: "cat /proc/cmdline" register: k3s_proc_cmdline changed_when: false - name: Fail if cgroup params are missing (reboot required) ansible.builtin.fail: msg: "Missing cgroup params in /proc/cmdline: {{ ['cgroup_memory=1', 'cgroup_enable=memory'] | reject('in', k3s_proc_cmdline.stdout) | list }}. Reboot required before k3s install." when: (['cgroup_memory=1', 'cgroup_enable=memory'] | reject('in', k3s_proc_cmdline.stdout) | list) | length > 0 - name: Guardrail - ensure rancher mountpoint is not on SD/rootfs ansible.builtin.command: "findmnt -n -o SOURCE {{ k3s_rancher_mountpoint | default('/var/lib/rancher') }}" register: rancher_source changed_when: false - name: Fail if rancher mountpoint is on SD ansible.builtin.fail: msg: "{{ k3s_rancher_mountpoint | default('/var/lib/rancher') }} is on SD/rootfs ({{ rancher_source.stdout }}). Refusing to proceed." when: rancher_source.stdout is search("mmcblk0") or rancher_source.stdout is search("/dev/mmc") - name: Preflight - ensure required mountpoints are mounted ansible.builtin.command: "findmnt -n {{ item }}" register: k3s_required_mounts_present changed_when: false failed_when: k3s_required_mounts_present.rc != 0 loop: "{{ k3s_required_mounts | default([]) }}" when: (k3s_required_mounts | default([])) | length > 0 - name: Guardrail - ensure required mountpoints are not on SD ansible.builtin.command: "findmnt -n -o SOURCE {{ item }}" register: k3s_required_mounts_sources changed_when: false loop: "{{ k3s_required_mounts | default([]) }}" when: (k3s_required_mounts | default([])) | length > 0 - name: Fail if required mountpoint is on SD ansible.builtin.fail: msg: "{{ item.item }} is on SD/rootfs ({{ item.stdout }}). Refusing to proceed." loop: "{{ k3s_required_mounts_sources.results }}" when: item.stdout is search("mmcblk0") or item.stdout is search("/dev/mmc") - name: Validate join configuration ansible.builtin.assert: that: - k3s_server_url is defined - k3s_server_url | length > 0 - k3s_token is defined - k3s_token | length > 0 fail_msg: "k3s_server_url and k3s_token are required for non-init nodes." when: not k3s_cluster_init | bool - name: Check for existing k3s server node token ansible.builtin.stat: path: /var/lib/rancher/k3s/server/node-token register: k3s_server_node_token when: - k3s_guard_token_drift | bool - k3s_role == "server" - k3s_cluster_init | bool - name: Read existing k3s server node token ansible.builtin.slurp: src: /var/lib/rancher/k3s/server/node-token register: k3s_server_node_token_raw when: - k3s_guard_token_drift | bool - k3s_role == "server" - k3s_cluster_init | bool - k3s_server_node_token.stat.exists - name: Fail when vault k3s token is missing for existing cluster ansible.builtin.fail: msg: >- vault_k3s_token is empty but /var/lib/rancher/k3s/server/node-token exists. Refusing to overwrite k3s config. Update vault_k3s.yml or run the k3s sync playbook. when: - k3s_guard_token_drift | bool - k3s_role == "server" - k3s_cluster_init | bool - k3s_server_node_token.stat.exists - (k3s_token | default('') | trim) | length == 0 - name: Fail on k3s token drift from live cluster ansible.builtin.fail: msg: >- vault_k3s_token does not match the live k3s server node-token. Refusing to overwrite k3s config. Run infrastructure/playbooks/k3s_sync.yml or update vault_k3s.yml to match the live token. when: - k3s_guard_token_drift | bool - k3s_role == "server" - k3s_cluster_init | bool - k3s_server_node_token.stat.exists - (k3s_token | default('') | trim) | length > 0 - (k3s_server_node_token_raw.content | b64decode | trim) != (k3s_token | default('') | trim) - name: Ensure k3s config directory exists ansible.builtin.file: path: /etc/rancher/k3s state: directory mode: "0755" - name: Load registry defaults from prole.cfg ansible.builtin.set_fact: k3s_prole_k3s_server_cfg: "{{ lookup('ansible.builtin.ini', 'PROLE_K3S_SERVER section=Global file=' + playbook_dir + '/../../conf/prole.cfg', default='') }}" k3s_prole_service_namespace_cfg: "{{ lookup('ansible.builtin.ini', 'SERVICE_NAMESPACE section=Global file=' + playbook_dir + '/../../conf/prole.cfg', default='') }}" when: k3s_state == "present" and k3s_registry_config_enabled | bool - name: Resolve k3s registry host ansible.builtin.set_fact: k3s_registry_host_resolved: >- {{ (k3s_registry_host | default('')) if (k3s_registry_host | default('') | length > 0) else (k3s_prole_k3s_server_cfg | default('')) if (k3s_prole_k3s_server_cfg | default('') | length > 0) else (k3s_server_url | default('')) if (k3s_server_url | default('') | length > 0) else inventory_hostname }} when: k3s_state == "present" and k3s_registry_config_enabled | bool - name: Normalize k3s registry host ansible.builtin.set_fact: k3s_registry_host_resolved: "{{ k3s_registry_host_resolved | regex_replace('^https?://', '') | regex_replace('/.*$', '') | regex_replace(':.*$', '') }}" when: k3s_state == "present" and k3s_registry_config_enabled | bool - name: Resolve k3s registry namespace ansible.builtin.set_fact: k3s_registry_namespace_resolved: >- {{ (k3s_registry_namespace | default('')) if (k3s_registry_namespace | default('') | length > 0) else (k3s_prole_service_namespace_cfg | default('')) if (k3s_prole_service_namespace_cfg | default('') | length > 0) else 'default' }} when: k3s_state == "present" and k3s_registry_config_enabled | bool - name: Validate k3s registry host ansible.builtin.assert: that: - k3s_registry_host_resolved | length > 0 fail_msg: "k3s registry host could not be resolved (set k3s_registry_host or PROLE_K3S_SERVER)." when: k3s_state == "present" and k3s_registry_config_enabled | bool - name: Render k3s registries config ansible.builtin.template: src: registries.yaml.j2 dest: "{{ k3s_registry_config_path }}" mode: "0644" notify: Restart k3s when: k3s_state == "present" and k3s_registry_config_enabled | bool - name: Ensure kubeconfig group exists ansible.builtin.group: name: "{{ k3s_kubeconfig_group }}" state: present - name: Ensure kubeconfig users are in group (server) ansible.builtin.user: name: "{{ item }}" groups: "{{ k3s_kubeconfig_group }}" append: true loop: "{{ k3s_kubeconfig_users }}" when: k3s_role == "server" and (k3s_kubeconfig_users | length) > 0 - name: Ensure rancher directories are group-accessible ansible.builtin.file: path: "{{ item }}" state: directory owner: root group: "{{ k3s_kubeconfig_group }}" mode: "0750" loop: - /etc/rancher - /etc/rancher/k3s - "{{ k3s_rancher_mountpoint | default('/var/lib/rancher') }}" when: k3s_state != "absent" - name: Render k3s config ansible.builtin.template: src: config.yaml.j2 dest: /etc/rancher/k3s/config.yaml mode: "0640" notify: Restart k3s - name: Build k3s install environment ansible.builtin.set_fact: k3s_install_env: >- {{ {'INSTALL_K3S_EXEC': k3s_role} | combine((k3s_version | length > 0) | ternary({'INSTALL_K3S_VERSION': k3s_version}, {})) }} - name: Install k3s ansible.builtin.shell: | curl -sfL https://get.k3s.io | sh - environment: "{{ k3s_install_env }}" args: creates: /usr/local/bin/k3s when: k3s_needs_install - name: Ensure k3s service state ansible.builtin.service: name: "{{ k3s_service_name }}" state: "{{ 'started' if k3s_state == 'present' else 'stopped' }}" enabled: "{{ k3s_state == 'present' }}" when: k3s_service.stat.exists or k3s_bin.stat.exists - name: Ensure prole config directory exists ansible.builtin.file: path: "{{ prole_conf }}" state: directory mode: "0755" when: k3s_state == "present" - name: Sync prole.cfg to k3s hosts ansible.builtin.copy: src: "{{ playbook_dir }}/../../conf/prole.cfg" dest: "{{ prole_conf }}/prole.cfg" mode: "0644" when: k3s_state == "present" - name: Ensure prole home directory exists ansible.builtin.file: path: "{{ prole_home }}" state: directory mode: "0755" when: k3s_state == "present" - name: Ensure prole service directory exists ansible.builtin.file: path: "{{ prole_home }}/etc" state: directory mode: "0755" when: k3s_state == "present" - name: Sync init_port_forwards.sh to k3s hosts ansible.builtin.copy: src: "{{ playbook_dir }}/../../etc/init_port_forwards.sh" dest: "{{ prole_home }}/etc/init_port_forwards.sh" mode: "0755" when: k3s_state == "present" - name: Sync prole_cfg.sh to k3s hosts ansible.builtin.copy: src: "{{ playbook_dir }}/../../etc/prole_cfg.sh" dest: "{{ prole_home }}/etc/prole_cfg.sh" mode: "0755" when: k3s_state == "present" - name: Select k3s init server for port-forward token ansible.builtin.set_fact: prole_port_forward_source_host: "{{ item }}" loop: "{{ groups['k3s_hosts'] }}" when: hostvars[item].k3s_cluster_init | default(false) | bool delegate_to: localhost delegate_facts: true run_once: true - name: Require k3s init server for port-forward token ansible.builtin.assert: that: - hostvars['localhost'].prole_port_forward_source_host | default('') | length > 0 fail_msg: "No k3s init server found for port-forward token." delegate_to: localhost run_once: true - name: Ensure prole-installer token secret exists ansible.builtin.shell: | k3s kubectl -n kube-system apply -f - <<'EOF' apiVersion: v1 kind: Secret metadata: name: prole-installer-token annotations: kubernetes.io/service-account.name: prole-installer type: kubernetes.io/service-account-token EOF delegate_to: "{{ hostvars['localhost'].prole_port_forward_source_host }}" run_once: true when: k3s_state == "present" - name: Read prole-installer token for port forwards ansible.builtin.shell: > k3s kubectl -n kube-system get secret prole-installer-token -o jsonpath='{.data.token}' | base64 -d delegate_to: "{{ hostvars['localhost'].prole_port_forward_source_host }}" run_once: true register: prole_installer_token changed_when: false when: k3s_state == "present" - name: Store prole-installer token on controller ansible.builtin.set_fact: prole_port_forward_token: "{{ prole_installer_token.stdout | default('') }}" delegate_to: localhost delegate_facts: true run_once: true when: k3s_state == "present" - name: Set prole port-forward token for host ansible.builtin.set_fact: prole_port_forward_token: "{{ hostvars['localhost'].prole_port_forward_token }}" when: - k3s_state == "present" - hostvars['localhost'].prole_port_forward_token | default('') | length > 0 - name: Render kubeconfig for port forwards ansible.builtin.template: src: prole-kubeconfig.yaml.j2 dest: "{{ prole_port_forward_kubeconfig }}" mode: "0640" owner: root group: "{{ k3s_kubeconfig_group }}" when: k3s_state == "present" - name: Install prole port-forwards systemd unit ansible.builtin.template: src: prole-port-forwards.service.j2 dest: "/etc/systemd/system/{{ prole_port_forward_service_name }}.service" mode: "0644" when: k3s_state == "present" - name: Enable and start prole port-forwards service ansible.builtin.systemd: name: "{{ prole_port_forward_service_name }}" enabled: true state: started daemon_reload: true when: k3s_state == "present"