prole/infrastructure/playbooks/k3s_sync.yml
chrisfu cad49cc0a6 Summary of recent repairs and infrastructure updates
Repairs and improvements:
- iSCSI: Added cleanup tasks to remove stale mounts and fstab entries. Improved robustness of iSCSI target management and added 'iscsi_absent_mounts' support.
- K3s:
    - Updated service start logic to accept 'activating' state, preventing premature failure during slow startups.
    - Improved service stop logic to safely handle missing or not-found services.
    - Ensured 'prole-installer' ServiceAccount and ClusterRoleBinding exist for K8s administration.
    - Added leader election and etcd tuning arguments (forgiving leases) to config.yaml.j2.
    - Removed deprecated 'prole-port-forwards' systemd service.
- Installer & Scripts:
    - Updated legacy_tk.py to support K3s mode, secret resolution for passwords, and better environment management (including ~/.prole/env.sh for service mode).
    - Updated init_ansible.sh to support PROLE_VAULT_PASS_FILE and ANSIBLE_VAULT_PASSWORD_FILE.
    - Improved directory and kubeconfig path resolution in prole_cfg.sh to support fallback to ~/.prole.
    - Enhanced Grafana password resolution in init_monitoring.sh.
    - Added automatic application of iSCSI StorageClass and PersistentVolumes in init_openbao.sh.
- General: Switched conf/prole.cfg to k3s deployment mode and updated vault_k3s.yml token.

New Ansible Tasks and Playbooks:
- infrastructure/playbooks/iscsi_cleanup.yml: Automates logout and removal of stale iSCSI node records.
- infrastructure/playbooks/prole_logs_migrate.yml: Orchestrates /prole/logs migration to iSCSI storage.
- infrastructure/playbooks/tmp_bao_dir.yml: Ensures host-level storage directories for OpenBao.
- infrastructure/playbooks/tmp_mount.yml: Utility to verify and enforce host-level mounts.
- infrastructure/playbooks/k3s_sync.yml: Added tasks to start K3s after sync and update local kubeconfig on the controller.
- Added 'Unmount stale iSCSI mounts' and 'Remove stale iSCSI fstab entries' to the iscsi role.
- Added 'Ensure prole-installer service account exists' to the k3s role.
2026-02-15 17:51:57 -08:00

289 lines
10 KiB
YAML

---
- 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