/
/
/
Ansible role that deployes services on my runner machine
1---
2# Runner Services - Macro Checker
3#
4# Natural-language calorie and macro tracker (repository: my_macro_checker).
5# Built by that repository's CI and pulled from Harbor, like the dnd images.
6# One compose project: app, PostgreSQL, nightly pg_dump, private SearXNG.
7
8- name: Create Macro Checker directories
9 file:
10 path: "{{ item }}"
11 state: directory
12 owner: "{{ runner_user }}"
13 group: "{{ runner_group }}"
14 mode: '2775'
15 loop:
16 - "{{ macros_config_dir }}"
17 - "{{ macros_config_dir }}/searxng"
18 - "{{ macros_nevo_dir }}"
19 - "{{ macros_backup_dir }}"
20
21# The database directory is created but never chowned afterwards: the postgres
22# image takes it over as UID 70 on first start, and a later `owner:` here
23# would take it back and stop the database.
24- name: Check the Macro Checker database directory
25 stat:
26 path: "{{ macros_db_dir }}"
27 register: macros_db_dir_stat
28
29- name: Create the Macro Checker database directory
30 file:
31 path: "{{ macros_db_dir }}"
32 state: directory
33 mode: '0700'
34 when: not macros_db_dir_stat.stat.exists
35
36# --- NEVO ---------------------------------------------------------------------
37# The dataset may not be redistributed, so it lives neither in the image nor in
38# this repository. Copied from the controller when it is there; `force: no` so
39# a deploy from a machine without it never removes it from the runner.
40- name: Check for the NEVO dataset on the controller
41 stat:
42 path: "{{ macros_nevo_src }}"
43 delegate_to: localhost
44 become: false
45 register: macros_nevo_local
46
47- name: Copy the NEVO dataset to the runner
48 copy:
49 src: "{{ macros_nevo_src }}"
50 dest: "{{ macros_nevo_dir }}/{{ macros_nevo_file }}"
51 owner: "{{ runner_user }}"
52 group: "{{ runner_group }}"
53 mode: '0644'
54 force: no
55 when: macros_nevo_local.stat.exists
56 notify: restart macros
57
58- name: Check the NEVO dataset on the runner
59 stat:
60 path: "{{ macros_nevo_dir }}/{{ macros_nevo_file }}"
61 register: macros_nevo_remote
62
63# Reported rather than asserted: the app runs without NEVO (Albert Heijn, Open
64# Food Facts and the web still answer), it just loses the generic foods.
65- name: Report a missing NEVO dataset
66 debug:
67 msg: >-
68 {{ macros_nevo_file }} is not in {{ macros_nevo_dir }} and was not found
69 at {{ macros_nevo_src }} on the controller. The Macro Checker will run
70 without generic (NEVO) foods. Download the dataset from RIVM and set
71 macros_nevo_src, then re-run with --tags macros.
72 when: not macros_nevo_remote.stat.exists
73
74# --- configuration -------------------------------------------------------------
75- name: Create Macro Checker environment file
76 template:
77 src: macros.env.j2
78 dest: "{{ macros_config_dir }}/macros.env"
79 owner: "{{ runner_user }}"
80 group: "{{ runner_group }}"
81 # Holds the login password and the LiteLLM key.
82 mode: '0640'
83 notify: restart macros
84
85- name: Create Macro Checker SearXNG settings
86 template:
87 src: macros-searxng-settings.yml.j2
88 dest: "{{ macros_config_dir }}/searxng/settings.yml"
89 owner: "{{ runner_user }}"
90 group: "{{ runner_group }}"
91 mode: '0644'
92 notify: restart macros
93
94- name: Create Macro Checker Docker Compose file
95 template:
96 src: macros-compose.yml.j2
97 dest: "{{ macros_config_dir }}/docker-compose.yml"
98 owner: "{{ runner_user }}"
99 group: "{{ runner_group }}"
100 # Carries the database password inline.
101 mode: '0640'
102 notify: restart macros
103
104# --- image & start ---------------------------------------------------------------
105# Ansible runs as root, which has its own docker credential store.
106- name: Log in to Harbor for the Macro Checker image
107 community.docker.docker_login:
108 registry_url: "{{ macros_registry }}"
109 username: "{{ macros_registry_username }}"
110 password: "{{ macros_registry_password }}"
111 reauthorize: true
112 no_log: true
113
114- name: Pull the Macro Checker image
115 community.docker.docker_image:
116 name: "{{ macros_image }}"
117 source: pull
118 force_source: true
119 register: macros_image_pull
120
121- name: Start the Macro Checker
122 community.docker.docker_compose_v2:
123 project_src: "{{ macros_config_dir }}"
124 state: present
125 # A newly pulled :latest must replace the running container.
126 recreate: "{{ 'always' if macros_image_pull is changed else 'auto' }}"
127 register: macros_start_result
128 check_mode: no
129
130# `changed` from compose says a container was created, not that the app inside
131# works. Ask the running service, as the Almanac tasks do.
132- name: Wait for the Macro Checker to answer
133 uri:
134 url: "http://127.0.0.1:{{ macros_port }}/health"
135 status_code: 200
136 register: macros_health
137 retries: 20
138 delay: 3
139 until: macros_health is succeeded
140 check_mode: no
141
142# From inside the container, with the real token: the API route needs a login,
143# and what matters is whether the container can reach Mealie by name.
144# Reported rather than asserted: the tracker works without Mealie, and on a
145# first run Mealie is deployed after this.
146- name: Check the Macro Checker's link to Mealie
147 command: >-
148 docker exec macros python -c
149 "import json; from app.sources import mealie; print(json.dumps(mealie.ping()))"
150 register: macros_mealie_ping
151 changed_when: false
152 failed_when: false
153 when: macros_mealie_enabled | bool
154 check_mode: no
155
156- name: Report a broken Mealie link
157 debug:
158 msg: >-
159 The Macro Checker cannot use Mealie: {{ macros_mealie_ping.stdout | default('') }}
160 {{ macros_mealie_ping.stderr | default('') }}. Recipe import and nutrition
161 write-back are unavailable until it can (check vault_macros.mealie_api_token
162 and that Mealie is running).
163 when:
164 - macros_mealie_enabled | bool
165 - '"\"ok\": true" not in (macros_mealie_ping.stdout | default(""))'
166
167- name: Require the Macro Checker to enforce authentication
168 uri:
169 url: "http://127.0.0.1:{{ macros_port }}/api/recipes"
170 status_code: 401
171 check_mode: no
172
173- name: Display Macro Checker deployment summary
174 debug:
175 msg: |
176 Macro Checker:
177 - Status: {{ 'Started/updated' if macros_start_result is changed else 'Already running' }}
178 - Web UI: http://{{ ansible_default_ipv4.address }}:{{ macros_port }}
179 - Image: {{ macros_image }}
180 - Config: {{ macros_config_dir }}
181 - Database: PostgreSQL ({{ macros_db_dir }})
182 - Backups: {{ macros_backup_dir }} (nightly, {{ macros_backup_keep_days }} days)
183 - NEVO: {{ 'present' if macros_nevo_remote.stat.exists else 'MISSING - generic foods unavailable' }}
184 - LLM: {{ macros_llm_model }} via {{ macros_llm_base_url }}
185