--- - name: Prepare k3s synchronization metadata hosts: k3s_hosts gather_facts: false run_once: true tasks: - name: Initialize k3s sync source host ansible.builtin.set_fact: k3s_sync_source_host: "" delegate_to: localhost delegate_facts: true - name: Select k3s init server as sync source ansible.builtin.set_fact: k3s_sync_source_host: "{{ item }}" loop: "{{ groups['k3s_hosts'] }}" when: hostvars[item].k3s_cluster_init | default(false) | bool delegate_to: localhost delegate_facts: true - name: Require a k3s init server as sync source ansible.builtin.assert: that: - hostvars['localhost'].k3s_sync_source_host | length > 0 fail_msg: "No k3s init server found. Ensure a host has k3s_cluster_init: true." - name: Ensure sync source is included in this run ansible.builtin.assert: that: - hostvars['localhost'].k3s_sync_source_host in ansible_play_hosts_all fail_msg: "Sync source {{ hostvars['localhost'].k3s_sync_source_host }} is not in this run. Include it in --limit." - name: Create local temp directory for k3s sync ansible.builtin.command: mktemp -d -p /tmp k3s-sync-XXXXXX register: k3s_sync_tmpdir changed_when: true delegate_to: localhost become: false - name: Ensure k3s sync temp directory is writable ansible.builtin.file: path: "{{ k3s_sync_tmpdir.stdout }}" state: directory mode: "1777" delegate_to: localhost - name: Store k3s sync metadata on controller ansible.builtin.set_fact: k3s_sync_source_host: "{{ hostvars['localhost'].k3s_sync_source_host }}" k3s_sync_tmpdir_path: "{{ k3s_sync_tmpdir.stdout }}" k3s_sync_tls_bundle: "{{ k3s_sync_tmpdir.stdout }}/k3s-tls.tgz" delegate_to: localhost delegate_facts: true - name: Collect k3s token and certs from init server hosts: k3s_hosts become: true gather_facts: false run_once: true vars: k3s_sync_source_host: "{{ hostvars['localhost'].k3s_sync_source_host | default('') }}" k3s_sync_tls_bundle: "{{ hostvars['localhost'].k3s_sync_tls_bundle | default('') }}" tasks: - name: Require a k3s sync source ansible.builtin.assert: that: - k3s_sync_source_host | length > 0 fail_msg: "k3s sync source is empty. Ensure the init server is reachable." - name: Ensure k3s node token exists ansible.builtin.stat: path: /var/lib/rancher/k3s/server/node-token register: k3s_node_token delegate_to: "{{ k3s_sync_source_host }}" - name: Fail when k3s node token is missing ansible.builtin.fail: msg: "k3s node token not found at /var/lib/rancher/k3s/server/node-token" when: not k3s_node_token.stat.exists - name: Read k3s node token ansible.builtin.slurp: src: /var/lib/rancher/k3s/server/node-token register: k3s_node_token_raw delegate_to: "{{ k3s_sync_source_host }}" - name: Store k3s sync token on controller ansible.builtin.set_fact: k3s_sync_token: "{{ k3s_node_token_raw.content | b64decode | trim }}" delegate_to: localhost delegate_facts: true - name: Check for k3s tls directory ansible.builtin.stat: path: /var/lib/rancher/k3s/server/tls register: k3s_tls_dir delegate_to: "{{ k3s_sync_source_host }}" - name: Create k3s tls bundle ansible.builtin.archive: path: /var/lib/rancher/k3s/server/tls dest: /tmp/k3s-tls.tgz format: gz when: k3s_tls_dir.stat.exists delegate_to: "{{ k3s_sync_source_host }}" - name: Fetch k3s tls bundle ansible.builtin.fetch: src: /tmp/k3s-tls.tgz dest: "{{ k3s_sync_tls_bundle }}" flat: true when: k3s_tls_dir.stat.exists delegate_to: "{{ k3s_sync_source_host }}" - name: Mark tls bundle presence on controller ansible.builtin.set_fact: k3s_sync_tls_bundle_present: "{{ k3s_tls_dir.stat.exists }}" delegate_to: localhost delegate_facts: true - name: Remove temporary tls bundle from source ansible.builtin.file: path: /tmp/k3s-tls.tgz state: absent when: k3s_tls_dir.stat.exists delegate_to: "{{ k3s_sync_source_host }}" - name: Synchronize k3s token and certs to servers hosts: k3s_hosts become: true serial: 1 gather_facts: false vars: k3s_sync_source_host: "{{ hostvars['localhost'].k3s_sync_source_host }}" k3s_sync_token: "{{ hostvars['localhost'].k3s_sync_token | default('') }}" k3s_sync_tls_bundle: "{{ hostvars['localhost'].k3s_sync_tls_bundle | default('') }}" k3s_sync_tls_bundle_present: "{{ hostvars['localhost'].k3s_sync_tls_bundle_present | default(false) }}" pre_tasks: - name: Require k3s sync token ansible.builtin.assert: that: - k3s_sync_token | length > 0 fail_msg: "k3s sync token is empty. Check the init server token." tasks: - name: Stop k3s before syncing ansible.builtin.import_role: name: k3s tasks_from: stop when: inventory_hostname != k3s_sync_source_host - name: Sync k3s token and certs ansible.builtin.import_role: name: k3s tasks_from: sync when: inventory_hostname != k3s_sync_source_host - name: Start k3s after syncing ansible.builtin.import_role: name: k3s tasks_from: start when: inventory_hostname != k3s_sync_source_host - name: Update k3s vault token on controller hosts: k3s_hosts gather_facts: false run_once: true vars: k3s_sync_token: "{{ hostvars['localhost'].k3s_sync_token | default('') }}" vault_k3s_path: "{{ playbook_dir }}/../inventory/group_vars/all/vault_k3s.yml" vault_pass_default: "{{ playbook_dir }}/../../.vault_pass" tasks: - name: Skip vault update when disabled ansible.builtin.meta: end_play when: not (k3s_sync_update_vault | default(true) | bool) - name: Require k3s sync token for vault update ansible.builtin.assert: that: - k3s_sync_token | length > 0 fail_msg: "k3s sync token is empty. Unable to update vault." - name: Check for default vault password file ansible.builtin.stat: path: "{{ vault_pass_default }}" register: vault_pass_default_stat delegate_to: localhost - name: Determine vault password file ansible.builtin.set_fact: k3s_vault_password_file: >- {{ k3s_vault_password_file | default(lookup('env', 'ANSIBLE_VAULT_PASSWORD_FILE') | default('', true), true) }} delegate_to: localhost delegate_facts: true - name: Fallback to default vault password file ansible.builtin.set_fact: k3s_vault_password_file: "{{ vault_pass_default }}" when: - (hostvars['localhost'].k3s_vault_password_file | default('')) | length == 0 - vault_pass_default_stat.stat.exists delegate_to: localhost delegate_facts: true - name: Require vault password file ansible.builtin.assert: that: - (hostvars['localhost'].k3s_vault_password_file | default('')) | length > 0 fail_msg: "Set k3s_vault_password_file or ANSIBLE_VAULT_PASSWORD_FILE to update vault." - name: Ensure vault file exists ansible.builtin.stat: path: "{{ vault_k3s_path }}" register: vault_k3s_file delegate_to: localhost - name: Fail when vault file is missing ansible.builtin.fail: msg: "Vault file not found at {{ vault_k3s_path }}" when: not vault_k3s_file.stat.exists - name: Check if vault file is encrypted ansible.builtin.command: "head -n 1 {{ vault_k3s_path }}" register: vault_k3s_head changed_when: false delegate_to: localhost - name: Mark vault encryption state ansible.builtin.set_fact: vault_k3s_encrypted: "{{ (vault_k3s_head.stdout | default('')) is search('^\\$ANSIBLE_VAULT') }}" delegate_to: localhost delegate_facts: true - name: Decrypt vault_k3s.yml ansible.builtin.command: >- ansible-vault decrypt {{ vault_k3s_path }} --vault-password-file {{ hostvars['localhost'].k3s_vault_password_file }} changed_when: true delegate_to: localhost when: hostvars['localhost'].vault_k3s_encrypted | default(false) - name: Update vault k3s token ansible.builtin.lineinfile: path: "{{ vault_k3s_path }}" regexp: '^vault_k3s_token:' line: "vault_k3s_token: \"{{ k3s_sync_token }}\"" delegate_to: localhost - name: Encrypt vault_k3s.yml ansible.builtin.command: >- ansible-vault encrypt {{ vault_k3s_path }} --vault-password-file {{ hostvars['localhost'].k3s_vault_password_file }} changed_when: true delegate_to: localhost when: hostvars['localhost'].vault_k3s_encrypted | default(false) - name: Update local kubeconfig on controller hosts: k3s_hosts gather_facts: false run_once: true vars: # Use myrddin as default if not defined k3s_server_url_resolved: "{{ k3s_server_url | default('https://myrddin.prole.org:6443') }}" k3s_sync_source_host: "{{ hostvars['localhost'].k3s_sync_source_host }}" tasks: - name: Fetch k3s.yaml from init server ansible.builtin.slurp: src: /etc/rancher/k3s/k3s.yaml register: k3s_yaml_raw delegate_to: "{{ k3s_sync_source_host }}" become: true - name: Write k3s kubeconfig to local project ansible.builtin.shell: | # shellcheck disable=SC1091 source "{{ playbook_dir }}/../../etc/prole_cfg.sh" cfg_path="$(_prole_kubeconfig_path)" echo "Writing to $cfg_path" mkdir -p "$(dirname "$cfg_path")" echo "{{ k3s_yaml_raw.content | b64decode }}" | sed "s|127.0.0.1:6443|{{ k3s_server_url_resolved | regex_replace('^https://', '') }}|g" > "$cfg_path" ls -l "$cfg_path" delegate_to: localhost become: false register: kubeconfig_write_debug - name: Debug kubeconfig write ansible.builtin.debug: var: kubeconfig_write_debug.stdout_lines