--- - name: Dry run - Join a server node to an existing k3s HA cluster hosts: k3s become: true vars: k3s_server_url: "https://{{ k3s_first_server_ip }}:6443" pre_tasks: - name: "[DRY RUN] Detect network interface for ansible_host" ansible.builtin.shell: | set -o pipefail ip route get {{ ansible_host }} | grep -oP 'dev \K\w+' | head -1 register: detected_iface changed_when: false failed_when: false - name: "[DRY RUN] Set fact for node interface" ansible.builtin.set_fact: k3s_node_iface: "{{ detected_iface.stdout | default('') }}" when: detected_iface.rc == 0 and detected_iface.stdout | length > 0 - name: "[DRY RUN] Warn if mac-shim interface detected" ansible.builtin.debug: msg: "WARNING: mac-shim-like interface detected on {{ inventory_hostname }} (iface: {{ k3s_node_iface }}). This can cause k3s cluster join issues." when: - k3s_node_iface is defined - "'mac' in k3s_node_iface or 'shim' in k3s_node_iface" - name: "[DRY RUN] Check if k3s is installed" ansible.builtin.command: k3s --version register: k3s_installed changed_when: false failed_when: false - name: "[DRY RUN] Display k3s installation status" ansible.builtin.debug: msg: "k3s installation: {{ 'FOUND' if k3s_installed.rc == 0 else 'NOT FOUND' }}" - name: "[DRY RUN] Check if k3s service is active" ansible.builtin.systemd: name: k3s register: k3s_service_active changed_when: false failed_when: false - name: "[DRY RUN] Display k3s service status" ansible.builtin.debug: msg: "k3s service: {{ 'ACTIVE' if k3s_service_active.status.ActiveState == 'active' else 'NOT ACTIVE' }}" - name: "[DRY RUN] Check if node is already in cluster" ansible.builtin.shell: | set -o pipefail k3s kubectl get nodes --no-headers | grep -w "{{ ansible_hostname }}" || true register: node_in_cluster changed_when: false failed_when: false when: k3s_service_active.status.ActiveState == 'active' - name: "[DRY RUN] Display cluster membership status" ansible.builtin.debug: msg: "Node in cluster: {{ 'YES' if node_in_cluster.stdout | default('') | length > 0 else 'NO' }}" when: k3s_service_active.status.ActiveState == 'active' - name: "[DRY RUN] Validate required vault variables" ansible.builtin.assert: that: - k3s_token is defined - k3s_token | length > 0 fail_msg: "k3s_token is not defined. Create vault/k3s-cluster.yml with 'ansible-vault create vault/k3s-cluster.yml' and set k3s_token." when: not (k3s_installed.rc == 0 and k3s_service_active.rc == 0 and node_in_cluster.stdout | default('') | length > 0) - name: "[DRY RUN] Validate vault variables when already joined" ansible.builtin.assert: that: - k3s_token is defined - k3s_token | length > 0 fail_msg: "k3s_token is not defined or empty in vault" success_msg: "k3s_token is defined and accessible" when: k3s_installed.rc == 0 and k3s_service_active.status.ActiveState == 'active' and node_in_cluster.stdout | default('') | length > 0 tasks: - name: "[DRY RUN] Check if node would be skipped" ansible.builtin.debug: msg: "Node {{ inventory_hostname }} is already installed and joined - would be SKIPPED" when: - k3s_installed.rc == 0 - k3s_service_active.status.ActiveState == 'active' - node_in_cluster.stdout | default('') | length > 0 - name: "[DRY RUN] Display what would be executed" ansible.builtin.debug: msg: - "Would execute:" - " curl -sfL https://get.k3s.io | K3S_TOKEN='' K3S_CONFIG=/etc/rancher/k3s/config.yaml sh -s - server \\" - " --server {{ k3s_server_url }} \\" - " --node-ip {{ ansible_host }} \\" - " {% if k3s_node_iface is defined and k3s_node_iface | length > 0 %}--flannel-iface {{ k3s_node_iface }}{% endif %}" when: - not (k3s_installed.rc == 0 and k3s_service_active.status.ActiveState == 'active' and node_in_cluster.stdout | default('') | length > 0) - name: "[DRY RUN] Check connectivity to existing k3s server" ansible.builtin.wait_for: host: "{{ k3s_first_server_ip }}" port: 6443 timeout: 5 register: server_connectivity ignore_errors: true when: - not (k3s_installed.rc == 0 and k3s_service_active.status.ActiveState == 'active' and node_in_cluster.stdout | default('') | length > 0) - name: "[DRY RUN] Display server connectivity" ansible.builtin.debug: msg: "Connectivity to {{ k3s_first_server_ip }}:6443: {{ 'REACHABLE' if server_connectivity is succeeded else 'NOT REACHABLE' }}" when: - not (k3s_installed.rc == 0 and k3s_service_active.status.ActiveState == 'active' and node_in_cluster.stdout | default('') | length > 0) - name: "[DRY RUN] Display current cluster nodes (if accessible)" ansible.builtin.command: k3s kubectl get nodes register: current_nodes changed_when: false failed_when: false when: k3s_service_active.status.ActiveState == 'active' - name: "[DRY RUN] Show cluster nodes" ansible.builtin.debug: msg: "{{ current_nodes.stdout_lines | default(['Unable to retrieve cluster nodes']) }}" when: k3s_service_active.status.ActiveState == 'active' - name: "[DRY RUN] Check encryption status" ansible.builtin.command: k3s secrets-encrypt status register: encryption_status changed_when: false failed_when: false when: k3s_service_active.status.ActiveState == 'active' - name: "[DRY RUN] Display encryption status" ansible.builtin.debug: msg: "{{ encryption_status.stdout_lines }}" when: - k3s_service_active.status.ActiveState == 'active' - encryption_status is defined - encryption_status.stdout_lines is defined - name: "[DRY RUN] Summary" ansible.builtin.debug: msg: - "=== DRY RUN SUMMARY ===" - "Host: {{ inventory_hostname }}" - "Network interface: {{ k3s_node_iface | default('auto') }}" - "k3s installed: {{ 'YES' if k3s_installed.rc == 0 else 'NO' }}" - "k3s active: {{ 'YES' if k3s_service_active.status.ActiveState == 'active' else 'NO' }}" - "In cluster: {{ 'YES' if node_in_cluster.stdout | default('') | length > 0 else 'NO' }}" - |- Action: {%- if k3s_installed.rc == 0 and k3s_service_active.status.ActiveState == 'active' and node_in_cluster.stdout | default('') | length > 0 %} SKIP (already joined) {%- else %} JOIN CLUSTER {%- endif %}