--- - name: Guard - require explicit confirmation for k3s server refresh ansible.builtin.assert: that: - k3s_role == 'server' - k3s_state == 'present' - (k3s_refresh_confirm | default('')) == 'YES' fail_msg: >- Refusing to refresh k3s server without explicit confirmation. Set k3s_refresh_confirm=YES and re-run (this is destructive: it wipes k3s state). - name: Read current k3s config (to detect current data-dir) ansible.builtin.stat: path: /etc/rancher/k3s/config.yaml register: _k3s_refresh_config_stat - name: Slurp current k3s config ansible.builtin.slurp: src: /etc/rancher/k3s/config.yaml register: _k3s_refresh_config_slurp when: _k3s_refresh_config_stat.stat.exists - name: Set refresh source/target data-dir ansible.builtin.set_fact: _k3s_refresh_cfg: >- {{ (_k3s_refresh_config_slurp.content | b64decode | from_yaml) if (_k3s_refresh_config_stat.stat.exists | bool) else {} }} k3s_refresh_source_data_dir: >- {{ _k3s_refresh_cfg.get('data-dir') | default((k3s_data_dir | default('/var/lib/rancher/k3s')), true) }} k3s_refresh_target_data_dir: "{{ k3s_data_dir | default('/var/lib/rancher/k3s') }}" - name: Preflight - check external datastore TCP reachability (best-effort) ansible.builtin.wait_for: host: "{{ k3s_datastore_mariadb_host }}" port: "{{ k3s_datastore_mariadb_port | default(3306) }}" connect_timeout: 2 timeout: 5 state: started when: - (k3s_datastore_endpoint | default('') | length) > 0 - k3s_datastore_mariadb_host is defined changed_when: false failed_when: false - name: Create refresh backup directory ansible.builtin.file: path: "{{ k3s_refresh_backup_root }}/{{ inventory_hostname }}/{{ ansible_date_time.iso8601_basic_short }}" state: directory mode: "0700" register: _k3s_refresh_backup_dir - name: Stat k3s token/tls artifacts (source data-dir) ansible.builtin.stat: path: "{{ item }}" loop: - "{{ k3s_refresh_source_data_dir }}/server/token" - "{{ k3s_refresh_source_data_dir }}/server/node-token" # Minimal TLS set to preserve agent trust (CA hash) while allowing k3s to # regenerate component creds and `server/cred/*.kubeconfig`. - "{{ k3s_refresh_source_data_dir }}/server/tls/server-ca.crt" - "{{ k3s_refresh_source_data_dir }}/server/tls/server-ca.key" - "{{ k3s_refresh_source_data_dir }}/server/tls/client-ca.crt" - "{{ k3s_refresh_source_data_dir }}/server/tls/client-ca.key" - "{{ k3s_refresh_source_data_dir }}/server/tls/request-header-ca.crt" - "{{ k3s_refresh_source_data_dir }}/server/tls/request-header-ca.key" - "{{ k3s_refresh_source_data_dir }}/server/tls/service.key" - "{{ k3s_refresh_source_data_dir }}/server/tls/service.current.key" register: _k3s_refresh_artifacts - name: Back up k3s token/tls artifacts (remote->remote) ansible.builtin.copy: remote_src: true src: "{{ item.item }}" dest: "{{ _k3s_refresh_backup_dir.path }}/" mode: preserve loop: "{{ _k3s_refresh_artifacts.results }}" when: item.stat.exists - name: Read preserved server token (source data-dir) ansible.builtin.slurp: src: "{{ k3s_refresh_source_data_dir }}/server/token" register: _k3s_refresh_token_slurp when: >- {{ ( _k3s_refresh_artifacts.results | selectattr('item', 'equalto', (k3s_refresh_source_data_dir ~ '/server/token')) | map(attribute='stat.exists') | first ) | default(false) }} no_log: true - name: Ensure we have a token to preserve ansible.builtin.assert: that: - (k3s_token | default('') | length) > 0 or (_k3s_refresh_token_slurp is defined) fail_msg: >- No existing token file was found under {{ k3s_refresh_source_data_dir }}/server/token, and k3s_token is empty. Refusing to proceed because this would change the join token. - name: Force k3s_token to preserved token (so rendered config matches what we restore) ansible.builtin.set_fact: k3s_token: "{{ (_k3s_refresh_token_slurp.content | b64decode | trim) if (_k3s_refresh_token_slurp is defined) else k3s_token }}" no_log: true - name: Stop k3s services before refresh ansible.builtin.service: name: "{{ item }}" state: stopped enabled: false loop: - k3s - k3s-agent failed_when: false - name: Uninstall and wipe k3s state (blank slate) ansible.builtin.import_tasks: cleanup.yml - name: Wipe target k3s data-dir (blank slate except restored token/CA) ansible.builtin.file: path: "{{ k3s_refresh_target_data_dir }}" state: absent - name: Recreate target server directory ansible.builtin.file: path: "{{ k3s_refresh_target_data_dir }}/server" state: directory mode: "0700" - name: Recreate target tls directory ansible.builtin.file: path: "{{ k3s_refresh_target_data_dir }}/server/tls" state: directory mode: "0700" - name: Restore preserved minimal tls artifacts into target data-dir ansible.builtin.copy: remote_src: true src: "{{ item }}" dest: "{{ k3s_refresh_target_data_dir }}/server/tls/" mode: preserve loop: - "{{ _k3s_refresh_backup_dir.path }}/server-ca.crt" - "{{ _k3s_refresh_backup_dir.path }}/server-ca.key" - "{{ _k3s_refresh_backup_dir.path }}/client-ca.crt" - "{{ _k3s_refresh_backup_dir.path }}/client-ca.key" - "{{ _k3s_refresh_backup_dir.path }}/request-header-ca.crt" - "{{ _k3s_refresh_backup_dir.path }}/request-header-ca.key" - "{{ _k3s_refresh_backup_dir.path }}/service.key" - "{{ _k3s_refresh_backup_dir.path }}/service.current.key" failed_when: false - name: Install and start k3s ansible.builtin.import_tasks: install.yml - name: Configure k3s add-ons ansible.builtin.import_tasks: configure.yml when: k3s_refresh_run_configure | bool - name: Verify k3s API is responsive after refresh ansible.builtin.command: k3s kubectl get nodes register: _k3s_refresh_nodes changed_when: false until: _k3s_refresh_nodes.rc == 0 retries: 18 delay: 10 - name: Read k3s node-token after refresh ansible.builtin.slurp: src: "{{ k3s_refresh_target_data_dir }}/server/node-token" register: _k3s_refresh_node_token changed_when: false until: _k3s_refresh_node_token is succeeded retries: 18 delay: 10 no_log: true - name: Assert node-token is preserved (when token is in node-token format) ansible.builtin.assert: that: - (k3s_token | default('')) == (_k3s_refresh_node_token.content | b64decode | trim) fail_msg: >- Refusing to continue: expected regenerated node-token to match preserved k3s_token, but it differs. This indicates the join token changed. when: - (k3s_token | default('')) is match('^K10[0-9a-f]+::server:.+')