--- # Converges one stack (one loop iteration of `stack` from playbooks/unraid.yml) # to the state its host_vars entry asks for. # # `state: present` (the default) deploys; `state: absent` tears down. Removal # has to be asked for explicitly because Ansible keeps no record of what it # deployed last run — deleting a stack from a host's `stacks:` list only stops # the loop visiting it, leaving the containers running and unmanaged on the # host forever. So the entry stays put as a tombstone, with `state: absent`, # until the teardown has actually been applied. # # Unraid-only by design. Proxmox guests install apps natively rather than as # Compose stacks — see the `lxc_app` role. - name: Set stack facts ansible.builtin.set_fact: stack_local_dir: "{{ repo_root }}/src/{{ stack.src }}" stack_remote_dir: "{{ compose_projects_root }}/{{ stack.name }}" stack_state: "{{ stack.state | default('present') }}" - name: Validate requested stack state ansible.builtin.assert: that: stack_state in ['present', 'absent'] fail_msg: >- Stack '{{ stack.name }}' has state '{{ stack_state }}'; expected 'present' or 'absent'. quiet: true # Which icons this stack ships, worked out from the repo checkout on the # controller rather than from anything on the host — `is exists` and the # `fileglob` lookup both evaluate locally. Computed here rather than in # deploy.yml because both branches need it: deploy copies these files out, # remove deletes them again. # # Two naming conventions, because Unraid has two icon consumers (see # deploy.yml for the full explanation): # # icon.png the stack — Compose Manager's project row, plus the # `net.unraid.docker.icon` label of whichever service # interpolates ${STACK_ICON}. # icon-.png one container — becomes ${STACK_ICON_}, so a # multi-container stack can label each of its services # with its own image instead of sharing the stack's. # # `` is the compose service name; the variable is it uppercased with # `-` folded to `_` (icon-shelfarr-libation.png → STACK_ICON_SHELFARR_LIBATION). - name: Set stack icon facts ansible.builtin.set_fact: # Empty when the stack ships no icon.png — an empty label is what the # webgui already assumes, so it falls back to the placeholder as before. stack_icon_dest: >- {{ ((stack_local_dir ~ '/ansible/unraid/icon.png') is exists) | ternary(unraid_icons_root ~ '/' ~ stack.name ~ '.png', '') }} stack_service_icon_names: "{{ _service_icon_names }}" stack_service_icons: >- {{ dict(_service_icon_names | map('upper') | map('replace', '-', '_') | map('regex_replace', '^(.+)$', 'STACK_ICON_\1') | zip(_service_icon_names | map('regex_replace', '^(.+)$', unraid_icons_root ~ '/' ~ stack.name ~ '-\1.png'))) }} vars: _service_icon_names: >- {{ query('fileglob', stack_local_dir ~ '/ansible/unraid/icon-*.png') | map('basename') | map('regex_replace', '^icon-(.+)\.png$', '\1') | map('lower') | list }} - name: Deploy stack ansible.builtin.include_tasks: deploy.yml when: stack_state == 'present' - name: Remove stack ansible.builtin.include_tasks: remove.yml when: stack_state == 'absent'