Skip to content

Commit 039dbc9

Browse files
authored
v2 rewrite (#38)
* trying something new * adds templating for var substitution * fixed regex for services: * provides an option to optionally override inventory_hostname with docker_compose_hostname variable * easter egg * simplified regex_replace operations * updated v2 readme * yaml formatting * cleanup ready for release * multiple services clarification * custom hostnames * variable interpolation * clarity * fuzz
1 parent 38587bc commit 039dbc9

File tree

4 files changed

+92
-294
lines changed

4 files changed

+92
-294
lines changed

README.md

Lines changed: 60 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,68 @@
11
# ansible-role-docker-compose-generator
22

3-
Pass this role a hash and it will generate a docker-compose.yml file. The following structure is supported and is designed to be passed to the role using `group_vars`.
3+
This role is designed to ingest directories of `compose.yaml` files and output a sanitised version to a remote host using Ansible.
44

5-
Rendered files are output to the `output` directory.
5+
> ⚠️ **Warning:** v1 of this role used a completely different data structure. See [v1 vs v2 of this role](#v1-of-this-role-vs-v2)
6+
7+
## Usage
8+
9+
Import this role into your Ansible setup either as a [git submodule](https://blog.ktz.me/git-submodules-for-fun-and-profit-with-ansible/) or via Ansible Galaxy.
10+
11+
In the root of your git repo create a `services` directory and populate it as follows:
12+
13+
```
14+
.
15+
└── services
16+
├── ansible-hostname1
17+
│ ├── librespeed
18+
│ │ └── compose.yaml
19+
│ ├── jellyfin
20+
│ │ └── compose.yaml
21+
└── ansible-hostname2
22+
└── uptime-kuma
23+
└── compose.yaml
24+
```
25+
26+
Each `compose.yaml` file is a standard format file, for example librespeed looks like this:
627

728
```
8-
---
9-
10-
# global vars
11-
global_env_vars:
12-
- "PUID=1313"
13-
- "PGID=1313"
14-
appdata_path: /opt/appdata
15-
container_config_path: /config
16-
container_data_path: /data
17-
18-
# container definitions
19-
containers:
20-
- service_name: letsencrypt
21-
active: true
22-
image: linuxserver/letsencrypt
23-
container_name: le
29+
services:
30+
librespeed:
31+
image: lscr.io/linuxserver/librespeed
32+
container_name: librespeed
2433
ports:
25-
- 80:80
26-
- 443:443
27-
volumes:
28-
- "{{ appdata_path }}/letsencrypt/config:{{ container_config_path }}"
29-
restart: always
30-
depends_on:
31-
- unifi
32-
- nextcloud
33-
- quassel
34-
include_global_env_vars: true
34+
- 8008:80
3535
environment:
36-
37-
- URL=some.tld
38-
- SUBDOMAINS=nc, irc, unifi
39-
- ONLY_SUBDOMAINS=true
40-
- DHLEVEL=4096
41-
- TZ=Europe/London
42-
- VALIDATION=http
43-
mem_limit: 256m
44-
- service_name: nextcloud
45-
active: true
46-
image: nextcloud
47-
container_name: nextcloud
48-
include_global_env_vars: false
49-
volumes:
50-
- "{{ appdata_path }}/nextcloud/html:/var/www/html"
51-
- "{{ appdata_path }}/nextcloud/apps:/var/www/html/custom_apps"
52-
- "{{ appdata_path }}/nextcloud/config:/var/www/html/config"
53-
- "{{ appdata_path }}/nextcloud/data:/var/www/html/data"
54-
- "{{ appdata_path }}/nextcloud/theme:/var/www/html/themes"
55-
mem_limit: 256m
56-
restart: "{{ unless_stopped }}"
57-
ports:
58-
- "8081:80"
59-
- service_name: unifi
60-
active: true
61-
image: linuxserver/unifi
62-
container_name: unifi
63-
mem_limit: 512m
64-
volumes:
65-
- "{{ appdata_path }}/unifi:{{ container_config_path }}"
66-
depends_on:
67-
- service: mongodb
68-
condition: service_started
69-
include_global_env_vars: true
70-
restart: "{{ unless_stopped }}"
71-
- service_name: quassel
72-
active: true
73-
image: linuxserver/quassel
74-
container_name: quassel
75-
include_global_env_vars: true
76-
volumes:
77-
- "{{ appdata_path }}/quassel:{{ container_config_path }}"
78-
mem_limit: 128m
79-
ports:
80-
- "4242:4242"
36+
- "TZ={{ host_timezone }}"
37+
- "PASSWORD={{ testpass }}"
38+
restart: unless-stopped
8139
```
40+
41+
Notice that variable interpolation is supported. The source of these variables can be either an encryped secrets file via Ansible vault (read more about that [here](https://blog.ktz.me/secret-management-with-docker-compose-and-ansible/) - or see [ironicbadger/infra](https://github.com/ironicbadger/infra) for an implemented example), an `env` file you manually place alongside the `compose.yaml` on the remote host (see [docker compose variable interpolation](https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/#interpolation-syntax)), or any other standard Ansible variable source.
42+
43+
Multiple services per compose file are also supported. Useful to run a database alongside an app, for example.
44+
45+
By default, if a `compose.yaml` file is found it will be included in the automation, and placed into the output `compose.yaml` on the remote host. This file is placed under the `docker_compose_generator_output_path` which is the home folder of the ssh user. The role also supports disabling specific compose files by matching the name of the file against a `host_var` or `group_var` file with the following variable:
46+
47+
```
48+
disabled_compose_files:
49+
- jellyfin
50+
```
51+
52+
## Custom hostnames
53+
54+
By default, the role is looking for a directory structure under `services/` which matches your Ansible hostname. If your hostname doesn't match the name of this directory for some reason (maybe it's an IP address, rather than a hostname), you can override the name with the variable:
55+
56+
```
57+
docker_compose_hostname: my-custom-hostname
58+
```
59+
60+
## v1 of this role vs v2
61+
62+
v1 of this role used a large custom data structure and an ever more complex jinja2 based templating approach. The custom nature of this approach added friction when adding new services and made it difficult to copy/paste from upstream repositories to try things out quickly.
63+
64+
v2 supports using standalone, native compose files. This makes it much more straightforward to try out new software without needing to 'convert' it to work with the v1 custom data structures.
65+
66+
If you find any edge cases I've missed for v2, please open an issue or PR. I'd be happy to review.
67+
68+
Special thanks goes to [u/fuzzymistborn](https://github.com/fuzzymistborn) for the spark for the idea to make this change. As ever, you can find a full working example of my usage of this role over at [ironicbadger/infra](https://github.com/ironicbadger/infra).

defaults/main.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
---
21
docker_compose_generator_output_path: "~"
32
docker_compose_generator_uid: "1000"
43
docker_compose_generator_gid: "1000"

tasks/main.yml

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,37 @@
44
path: "{{ docker_compose_generator_output_path }}"
55
state: directory
66

7-
- name: write docker-compose file
8-
template:
9-
src: ../templates/docker-compose.yml.j2
10-
dest: "{{ docker_compose_generator_output_path }}/docker-compose.yml"
7+
- name: Find all compose files
8+
set_fact:
9+
compose_files: >-
10+
{{
11+
query('filetree', playbook_dir + '/services/' +
12+
(docker_compose_hostname | default(inventory_hostname))) |
13+
selectattr('state', 'eq', 'file') |
14+
selectattr('path', 'regex', 'compose.ya?ml$') |
15+
list
16+
}}
17+
18+
- name: Build combined compose content
19+
set_fact:
20+
combined_compose: |
21+
# Generated by ironicbadger.docker-compose-generator
22+
# badger badger badger mushroom mushrooooom...
23+
24+
services:
25+
{% for file in compose_files %}
26+
{% set service_name = file.src | dirname | basename %}
27+
28+
{% if service_name not in (disabled_compose_files | default([])) %}
29+
{{ lookup('template', file.src)
30+
| regex_replace('^(---|services:)\s*\n*', '')
31+
| regex_replace('^', ' ') }}
32+
{% endif %}
33+
{% endfor %}
34+
35+
- name: Write combined compose file
36+
copy:
37+
content: "{{ combined_compose }}"
38+
dest: "{{ docker_compose_generator_output_path }}/compose.yaml"
1139
owner: "{{ docker_compose_generator_uid }}"
1240
group: "{{ docker_compose_generator_gid }}"

0 commit comments

Comments
 (0)