--- - name: Collect Prosody Onion domains. register: onion_result shell: > cat {{ tor_onion_services_dir | default('/var/lib/tor') + '/' + item.name + '/hostname' | quote }} | cut -d ' ' -f 1 | sort | uniq loop: "{{ prosody_virtualhost_onions }}" changed_when: false - name: Register Prosody Onion VirtualHosts. when: not ansible_check_mode set_fact: prosody_onion_virtualhosts: "{{ onion_result.results | map(attribute='stdout_lines') | list | flatten | unique }}" # This task is a bit of a clever hack: it updates the `prosody_config` # dictionary in-place by using the `|combine()` filter, after looping # over the `options` dictionary in the `prosody_virtualhost_onions` # variable. In order to do this entirely in a single task, some Jinja # tricks are employed, notably using no-op `if` blocks to actually # update the values of the items in the `item.1.options` dictionary. - name: Insert Prosody Onion VirtualHosts to Prosody configuration. when: prosody_onion_virtualhosts is defined set_fact: prosody_config: >- {%- if item.1.options is defined -%} {%- for k, v in item.1.options.items() -%} {%- if v is string -%} {%- if item.1.options.update({k: v | replace('__PROSODY_DOMAIN__', item.0)}) -%}{%- endif -%} {%- elif v is mapping -%}{#- TODO: Handle dictionaries as well? -#} {%- elif v is sequence -%} {%- for x in v -%} {%- if item.1.options[k].__setitem__(loop.index0, x | replace('__PROSODY_DOMAIN__', item.0)) -%}{%- endif -%} {%- endfor -%} {%- endif -%} {%- endfor -%} {%- endif -%} {{ prosody_config | combine({ 'VirtualHosts': prosody_config.VirtualHosts + [{ 'domain': item.0 } | combine(item.1.options | default({}), recursive=True)] }, recursive=True) }} loop: "{{ prosody_onion_virtualhosts | product(prosody_virtualhost_onions) | list }}"