k3s.yml

# Actualiza k3s nodo a nodo: PRIMERO los masters (de uno en uno, sin perder nunca la
# mayoría de etcd) y DESPUÉS los workers. Solo el binario (docs/operacion.md).
#
# Variables: version=v1.X.Y+k3sN (obligatoria), nodos=<grupo o nodo> (por defecto todos).
# - Parche (1.34.6 → 1.34.12): sin drain; los contenedores siguen corriendo.
# - Cambio de rama (1.34 → 1.35): drain antes y uncordon después. Como mucho una rama más
#   (lo impide también nodo-mantenimiento). Antes, revisar compatibilidades (operacion.md).
# En simulación (--check) solo dice qué haría.

- name: Comprobar la versión pedida
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Formato v1.X.Y+k3sN
      ansible.builtin.assert:
        that: version is defined and version is match('^v1\.[0-9]{1,2}\.[0-9]{1,3}\+k3s[0-9]{1,2}$')
        fail_msg: "Indica la versión, p. ej. v1.34.12+k3s1"
        quiet: true

- name: Actualizar k3s en los masters
  hosts: "{{ nodos | default('all') }}:&masters"
  gather_facts: false
  serial: 1
  order: inventory
  any_errors_fatal: true
  tasks:
    - name: Actualizar este master
      ansible.builtin.include_tasks: tareas/k3s_nodo.yml

- name: Actualizar k3s en los workers
  hosts: "{{ nodos | default('all') }}:&workers"
  gather_facts: false
  serial: 1
  order: inventory
  any_errors_fatal: true
  tasks:
    - name: Ningún worker por delante de los masters
      ansible.builtin.shell: |
        set -o pipefail
        kubectl --kubeconfig {{ kubeconfig }} get nodes -l node-role.kubernetes.io/control-plane=true \
          -o jsonpath='{range .items[*]}{.metadata.name} {.status.nodeInfo.kubeletVersion}{"\n"}{end}' \
          | while read -r n v; do
              [ "$(printf '%s\n%s\n' "$v" "{{ version }}" | sort -V | head -1)" = "{{ version }}" ] || echo "$n ($v)"
            done
      args:
        executable: /bin/bash
      register: masters_atrasados
      changed_when: false
      failed_when: masters_atrasados.rc != 0
      delegate_to: localhost
      run_once: true
      check_mode: false

    - name: Parar si algún master va por detrás
      ansible.builtin.fail:
        msg: "Primero los masters: aún no están en {{ version }}: {{ masters_atrasados.stdout_lines | join(', ') }}"
      when: masters_atrasados.stdout != '' and not ansible_check_mode
      run_once: true

    - name: Aviso en simulación
      ansible.builtin.debug:
        msg: "SIMULACIÓN: los workers se actualizarían después de los masters ({{ masters_atrasados.stdout_lines | join(', ') }} aún no están en {{ version }})"
      when: masters_atrasados.stdout != '' and ansible_check_mode
      run_once: true

    - name: Actualizar este worker
      ansible.builtin.include_tasks: tareas/k3s_nodo.yml