Flesh out docker-compose Cookiecutter template

This commit is contained in:
hygienic-books 2022-06-04 23:36:18 +02:00
parent e6be888b91
commit 8b2c0cecca
27 changed files with 413 additions and 39 deletions

View File

@ -1,6 +1,9 @@
{
"component_s": "grafana,nginx",
"service": "example_service",
"context": "example_context",
"project_slug": "{% if cookiecutter.service is defined and cookiecutter.service|length > 0 %}{{ cookiecutter.service.lower().replace(' ', '_').replace('-', '_') }}-{% endif %}{{ cookiecutter.component_s.lower().replace(' ', '_').replace('-', '_') }}-{{ cookiecutter.context.lower().replace(' ', '_').replace('-', '_') }}"
"service": "",
"__service_slug": "{{ cookiecutter.service.lower().replace(' ', '_').replace('-', '_') }}",
"component_list": "{{ cookiecutter.__service_slug }}",
"__component_list_slug": "{{ cookiecutter.component_list.lower().replace(' ', '_').replace('-', '_') }}",
"context": "",
"__context_slug": "{{ cookiecutter.context.lower().replace(' ', '_').replace('-', '_') }}",
"__project_slug": "{{ cookiecutter.__service_slug }}"
}

View File

@ -0,0 +1,14 @@
# For the remainder of this Dockerfile EXAMPLE_ARG_FOR_DOCKERFILE will be
# available with a value of 'must_be_available_in_dockerfile', check out the env
# file at 'env/fully.qualified.domain.name.example' for reference.
# ARG EXAMPLE_ARG_FOR_DOCKERFILE
# Another env var, this one's needed in the example build step below:
# ARG GRAFANA_VERSION
# Example
# FROM "grafana:${GRAFANA_VERSION}"
# RUN apt-get update && \
# apt-get -y install \
# somepackage-6.q16-6-extra && \
# rm -rf /var/lib/apt/lists/*

View File

@ -0,0 +1,14 @@
# For the remainder of this Dockerfile EXAMPLE_ARG_FOR_DOCKERFILE will be
# available with a value of 'must_be_available_in_dockerfile', check out the env
# file at 'env/fully.qualified.domain.name.example' for reference.
# ARG EXAMPLE_ARG_FOR_DOCKERFILE
# Another env var, this one's needed in the example build step below:
# ARG NGINX_VERSION
# Example
# FROM "nginx:${NGINX_VERSION}"
# RUN apt-get update && \
# apt-get -y install \
# somepackage-6.q16-6-extra && \
# rm -rf /var/lib/apt/lists/*

View File

@ -0,0 +1,19 @@
services:
grafana-build:
image: "grafana:${GRAFANA_VERSION}"
profiles: ["build", "build-grafana"]
build:
context: "build-context/grafana"
dockerfile: Dockerfile
args:
EXAMPLE_ARG_FOR_DOCKERFILE: "${EXAMPLE_ARG_FROM_ENV_FILE}"
GRAFANA_VERSION: "${GRAFANA_VERSION}"
nginx-build:
image: "nginx:${NGINX_VERSION}"
profiles: ["build", "build-nginx"]
build:
context: "build-context/nginx"
dockerfile: Dockerfile
args:
EXAMPLE_ARG_FOR_DOCKERFILE: "${EXAMPLE_ARG_FROM_ENV_FILE}"
NGINX_VERSION: "${NGINX_VERSION}"

View File

@ -0,0 +1,47 @@
services:
grafana:
image: "grafana:${GRAFANA_VERSION}"
container_name: "grafana-grafana-${CONTEXT}"
networks:
grafana-cncf:
profiles: ["full", "grafana"]
extends:
file: common-settings.yml
service: common-settings
ports:
# - "8080:80"
volumes:
# - /opt/docker-data/grafana-grafana-cncf/grafana/data/db:/usr/lib/grafana
# - /opt/docker-data/grafana-grafana-cncf/grafana/data/logs:/var/log/grafana
# - /opt/docker-data/grafana-grafana-cncf/grafana/config:/etc/grafana
environment:
# GRAFANA_USER: ${GRAFANA_USER}
# GRAFANA_PASSWORD: ${GRAFANA_PASSWORD}
nginx:
image: "nginx:${NGINX_VERSION}"
container_name: "grafana-nginx-${CONTEXT}"
networks:
grafana-cncf:
profiles: ["full", "nginx"]
extends:
file: common-settings.yml
service: common-settings
ports:
# - "8080:80"
volumes:
# - /opt/docker-data/grafana-nginx-cncf/nginx/data/db:/usr/lib/nginx
# - /opt/docker-data/grafana-nginx-cncf/nginx/data/logs:/var/log/nginx
# - /opt/docker-data/grafana-nginx-cncf/nginx/config:/etc/nginx
environment:
# NGINX_USER: ${NGINX_USER}
# NGINX_PASSWORD: ${NGINX_PASSWORD}
networks:
grafana-cncf:
name: grafana-cncf
driver: bridge
driver_opts:
com.docker.network.enable_ipv6: "false"
ipam:
driver: default
config:
# - subnet: 172.21.184.0/24

View File

@ -0,0 +1,30 @@
CONTEXT=cncf
# Set something sensible here and uncomment
# ---
# GRAFANA_VERSION=x.y.z
# NGINX_VERSION=x.y.z
# A ${LOCATION} var is usually not needed. It may be helpful when a ${CONTEXT}
# extends over more than one location e.g. to bind-mount location-specific
# config files or certificates into a container.
# ---
# LOCATION=
# Feel free to leave defaults. They apply while these vars are commented out
# ---
# RESTARTPOLICY=unless-stopped
# TIMEZONE=Etc/UTC
# See 'docker-compose.override.yml' for how to make a variable available in
# a Dockerfile
# ---
# EXAMPLE_ARG_FROM_ENV_FILE=must_be_available_in_dockerfile

View File

@ -0,0 +1,14 @@
# For the remainder of this Dockerfile EXAMPLE_ARG_FOR_DOCKERFILE will be
# available with a value of 'must_be_available_in_dockerfile', check out the env
# file at 'env/fully.qualified.domain.name.example' for reference.
# ARG EXAMPLE_ARG_FOR_DOCKERFILE
# Another env var, this one's needed in the example build step below:
# ARG HASHICORPVAULT_VERSION
# Example
# FROM "hashicorpvault:${HASHICORPVAULT_VERSION}"
# RUN apt-get update && \
# apt-get -y install \
# somepackage-6.q16-6-extra && \
# rm -rf /var/lib/apt/lists/*

View File

@ -0,0 +1,11 @@
services:
common-settings:
environment:
TZ: "${TIMEZONE:-Etc/UTC}"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "10"
compress: "true"
restart: "${RESTARTPOLICY:-unless-stopped}"

View File

@ -0,0 +1,10 @@
services:
hashicorpvault-build:
image: "hashicorpvault:${HASHICORPVAULT_VERSION}"
profiles: ["build"]
build:
context: "build-context/hashicorpvault"
dockerfile: Dockerfile
args:
EXAMPLE_ARG_FOR_DOCKERFILE: "${EXAMPLE_ARG_FROM_ENV_FILE}"
HASHICORPVAULT_VERSION: "${HASHICORPVAULT_VERSION}"

View File

@ -0,0 +1,28 @@
services:
hashicorpvault:
image: "hashicorpvault:${HASHICORPVAULT_VERSION}"
container_name: "hashicorpvault-${CONTEXT}"
networks:
hashicorpvault-fsf:
extends:
file: common-settings.yml
service: common-settings
ports:
# - "8080:80"
volumes:
# - /opt/docker-data/hashicorpvault-fsf/data/db:/usr/lib/hashicorpvault
# - /opt/docker-data/hashicorpvault-fsf/data/logs:/var/log/hashicorpvault
# - /opt/docker-data/hashicorpvault-fsf/config:/etc/hashicorpvault
environment:
# HASHICORPVAULT_USER: ${HASHICORPVAULT_USER}
# HASHICORPVAULT_PASSWORD: ${HASHICORPVAULT_PASSWORD}
networks:
hashicorpvault-fsf:
name: hashicorpvault-fsf
driver: bridge
driver_opts:
com.docker.network.enable_ipv6: "false"
ipam:
driver: default
config:
# - subnet: 172.21.184.0/24

View File

@ -0,0 +1,29 @@
CONTEXT=fsf
# Set something sensible here and uncomment
# ---
# HASHICORPVAULT_VERSION=x.y.z
# A ${LOCATION} var is usually not needed. It may be helpful when a ${CONTEXT}
# extends over more than one location e.g. to bind-mount location-specific
# config files or certificates into a container.
# ---
# LOCATION=
# Feel free to leave defaults. They apply while these vars are commented out
# ---
# RESTARTPOLICY=unless-stopped
# TIMEZONE=Etc/UTC
# See 'docker-compose.override.yml' for how to make a variable available in
# a Dockerfile
# ---
# EXAMPLE_ARG_FROM_ENV_FILE=must_be_available_in_dockerfile

View File

@ -1,23 +1,53 @@
#print("{% set components = cookiecutter.component_s.split(',') -%}
#{% for component in components -%}
# {{ component }}
# {%- if not loop.last -%}
# ,
# {%- endif %}
#{%- endfor %}")
import os
import re
import shutil
import sys
component_subdirs = ["extras", "docker-data"]
components = ["grafana", "nginx"]
components = {% set components = cookiecutter.__component_list_slug.split(',') -%}
[
{%- for component in components -%}
"{{ component }}"
{%- if not loop.last -%}
,
{%- endif %}
{%- endfor -%}
]
project_dir = os.getcwd()
build_context_dir_name = "build-context"
build_ctx_dir_abs = os.path.join(project_dir, build_context_dir_name)
base_dockerfile_abs = os.path.join(build_ctx_dir_abs, "Dockerfile")
base_dockerfile_rel = os.path.relpath(base_dockerfile_abs, project_dir)
re_string_component = "~~component~~"
re_pattern_component = re.compile(re_string_component, re.IGNORECASE)
re_string_component_upper = "~~component_upper~~"
re_pattern_component_upper = re.compile(re_string_component_upper, re.IGNORECASE)
def render_dockerfile(arg_dockerfile_abs: str, arg_component: str) -> None:
dockerfile_abs = arg_dockerfile_abs
component = arg_component
try:
with open(dockerfile_abs, 'r+') as dockerfile:
text = dockerfile.read()
text = re.sub(re_pattern_component, component, text)
text = re.sub(re_pattern_component_upper, component.upper(), text)
dockerfile.seek(0)
dockerfile.write(text)
dockerfile.truncate()
except OSError:
print(f"Unable to open Dockerfile './{component_dockerfile_rel}'. Aborting and exiting 5 ...")
sys.exit(5)
for component in components:
component_dir_abs = os.path.join(build_ctx_dir_abs, component)
if len(components) > 1:
component_dir_abs = os.path.join(build_ctx_dir_abs, component)
else:
component_dir_abs = build_ctx_dir_abs
component_dir_rel = os.path.relpath(component_dir_abs, project_dir)
try:
os.mkdir(component_dir_abs)
os.makedirs(component_dir_abs, exist_ok=True)
except FileExistsError:
print(f"Dir './{component_dir_rel}' already exists when it shouldn't. Aborting and exiting 1 ...")
sys.exit(1)
@ -42,3 +72,40 @@ for root, subdirs, filelist in os.walk(project_dir):
except IOError:
print(f"Can't write './{gitkeep_file_rel}'. Aborting and exiting 2 ...")
sys.exit(2)
if len(components) > 1:
if subdir_name in components:
component_dir_abs = os.path.join(root, subdir_name)
component_dockerfile_abs = os.path.join(component_dir_abs, "Dockerfile")
component_dockerfile_rel = os.path.relpath(component_dockerfile_abs, project_dir)
try:
shutil.copyfile(base_dockerfile_abs, component_dockerfile_abs)
render_dockerfile(component_dockerfile_abs, subdir_name)
except shutil.SameFileError:
print(f"Unable to copy base_dockerfile_abs ('{base_dockerfile_abs}') to "
f"component_dockerfile_abs ('{component_dockerfile_abs}'). They're the same file. "
f"Aborting and exiting 4 ...")
sys.exit(4)
except shutil.SpecialFileError:
print(f"Unable to copy base_dockerfile_abs ('{base_dockerfile_abs}') to "
f"component_dockerfile_abs ('{component_dockerfile_abs}'). Source is a named pipe. "
f"Aborting and exiting 4 ...")
sys.exit(4)
except FileNotFoundError:
print(f"Unable to copy base_dockerfile_abs ('{base_dockerfile_abs}') to "
f"component_dockerfile_abs ('{component_dockerfile_abs}'). Destination dir does not exist. "
f"Aborting and exiting 4 ...")
sys.exit(4)
except OSError:
print(f"Unable to copy base_dockerfile_abs ('{base_dockerfile_abs}') to "
f"component_dockerfile_abs ('{component_dockerfile_abs}'). Source file likely does not exist. "
f"Aborting and exiting 4 ...")
sys.exit(4)
else:
render_dockerfile(base_dockerfile_abs, components[0])
if len(components) > 1:
try:
os.remove(base_dockerfile_abs)
except OSError:
print(f"Unable to delete original template Dockerfile at './{base_dockerfile_rel}'. "
f"Aborting and exiting 6 ...")
sys.exit(6)

View File

@ -0,0 +1,9 @@
import sys
service_slug = "{{ cookiecutter.__service_slug }}"
component_list_slug = "{{ cookiecutter.__component_list_slug }}"
context_slug = "{{ cookiecutter.__context_slug }}"
for v in (service_slug, component_list_slug, context_slug):
if not v:
print(f"Please answer all prompts with a non-empty string. Aborting and existing 3 ...")
sys.exit(3)

View File

@ -0,0 +1,14 @@
# For the remainder of this Dockerfile EXAMPLE_ARG_FOR_DOCKERFILE will be
# available with a value of 'must_be_available_in_dockerfile', check out the env
# file at 'env/fully.qualified.domain.name.example' for reference.
# ARG EXAMPLE_ARG_FOR_DOCKERFILE
# Another env var, this one's needed in the example build step below:
# ARG ~~component_upper~~_VERSION
# Example
# FROM "~~component~~:${~~component_upper~~_VERSION}"
# RUN apt-get update && \
# apt-get -y install \
# somepackage-6.q16-6-extra && \
# rm -rf /var/lib/apt/lists/*

View File

@ -0,0 +1,11 @@
services:
common-settings:
environment:
TZ: "${TIMEZONE:-Etc/UTC}"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "10"
compress: "true"
restart: "${RESTARTPOLICY:-unless-stopped}"

View File

@ -0,0 +1,25 @@
services:
{%- if ',' in cookiecutter.__component_list_slug -%}
{%- set components = cookiecutter.__component_list_slug.split(',') -%}
{% for component in components %}
{{ component }}-build:
image: "{{ component }}:${% raw %}{{% endraw %}{{ component.upper() }}_VERSION{% raw %}}{% endraw %}"
profiles: ["build", "build-{{ component }}"]
build:
context: "build-context/{{ component }}"
dockerfile: Dockerfile
args:
EXAMPLE_ARG_FOR_DOCKERFILE: "${EXAMPLE_ARG_FROM_ENV_FILE}"
{{ component.upper() }}_VERSION: "${% raw %}{{% endraw %}{{ component.upper() }}_VERSION{% raw %}}{% endraw %}"
{%- endfor %}
{%- else %}
{{ cookiecutter.__component_list_slug }}-build:
image: "{{ cookiecutter.__component_list_slug }}:${% raw %}{{% endraw %}{{ cookiecutter.__component_list_slug.upper() }}_VERSION{% raw %}}{% endraw %}"
profiles: ["build"]
build:
context: "build-context/{{ cookiecutter.__component_list_slug }}"
dockerfile: Dockerfile
args:
EXAMPLE_ARG_FOR_DOCKERFILE: "${EXAMPLE_ARG_FROM_ENV_FILE}"
{{ cookiecutter.__component_list_slug.upper() }}_VERSION: "${% raw %}{{% endraw %}{{ cookiecutter.__component_list_slug.upper() }}_VERSION{% raw %}}{% endraw %}"
{%- endif %}

View File

@ -0,0 +1,52 @@
services:
{%- if ',' in cookiecutter.__component_list_slug -%}
{%- set components = cookiecutter.__component_list_slug.split(',') -%}
{%- for component in components %}
{{ component }}:
image: "{{ component }}:${% raw %}{{% endraw %}{{ component.upper() }}_VERSION{% raw %}}{% endraw %}"
container_name: "{{ cookiecutter.__service_slug }}-{{ component }}-${CONTEXT}"
networks:
{{ cookiecutter.__service_slug }}-{{ cookiecutter.__context_slug }}:
profiles: ["full", "{{ component }}"]
extends:
file: common-settings.yml
service: common-settings
ports:
# - "8080:80"
volumes:
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-{{ component }}-{{ cookiecutter.__context_slug }}/{{ component }}/data/db:/usr/lib/{{ component }}
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-{{ component }}-{{ cookiecutter.__context_slug }}/{{ component }}/data/logs:/var/log/{{ component }}
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-{{ component }}-{{ cookiecutter.__context_slug }}/{{ component }}/config:/etc/{{ component }}
environment:
# {{ component.upper() }}_USER: ${% raw %}{{% endraw %}{{ component.upper() }}_USER{% raw %}}{% endraw %}
# {{ component.upper() }}_PASSWORD: ${% raw %}{{% endraw %}{{ component.upper() }}_PASSWORD{% raw %}}{% endraw %}
{%- endfor -%}
{%- else %}
{{ cookiecutter.__component_list_slug }}:
image: "{{ cookiecutter.__component_list_slug }}:${% raw %}{{% endraw %}{{ cookiecutter.__component_list_slug.upper() }}_VERSION{% raw %}}{% endraw %}"
container_name: "{{ cookiecutter.__service_slug }}-${CONTEXT}"
networks:
{{ cookiecutter.__service_slug }}-{{ cookiecutter.__context_slug }}:
extends:
file: common-settings.yml
service: common-settings
ports:
# - "8080:80"
volumes:
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-{{ cookiecutter.__context_slug }}/data/db:/usr/lib/{{ cookiecutter.__service_slug }}
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-{{ cookiecutter.__context_slug }}/data/logs:/var/log/{{ cookiecutter.__service_slug }}
# - /opt/docker-data/{{ cookiecutter.__service_slug }}-{{ cookiecutter.__context_slug }}/config:/etc/{{ cookiecutter.__service_slug }}
environment:
# {{ cookiecutter.__component_list_slug.upper() }}_USER: ${% raw %}{{% endraw %}{{ cookiecutter.__component_list_slug.upper() }}_USER{% raw %}}{% endraw %}
# {{ cookiecutter.__component_list_slug.upper() }}_PASSWORD: ${% raw %}{{% endraw %}{{ cookiecutter.__component_list_slug.upper() }}_PASSWORD{% raw %}}{% endraw %}
{%- endif %}
networks:
{{ cookiecutter.__service_slug }}-{{ cookiecutter.__context_slug }}:
name: {{ cookiecutter.__service_slug }}-{{ cookiecutter.__context_slug }}
driver: bridge
driver_opts:
com.docker.network.enable_ipv6: "false"
ipam:
driver: default
config:
# - subnet: 172.21.184.0/24

View File

@ -1,10 +1,10 @@
CONTEXT={{ cookiecutter.context_canon }}
CONTEXT={{ cookiecutter.__context_slug }}
# Set something sensible here and uncomment
# ---
{%- set components = cookiecutter.component_s_canon.split(',') -%}
{%- set components = cookiecutter.__component_list_slug.split(',') -%}
{% for component in components %}
# {{ component.upper() }}_VERSION=x.y.z
{%- endfor %}

View File

@ -1,12 +0,0 @@
services:
{%- set components = cookiecutter.component_s_canon.split(',') -%}
{% for component in components %}
{{ component }}-build:
image: "{{ component }}:${% raw %}{{% endraw %}{{ component.upper() }}_VERSION{% raw %}}{% endraw %}"
profiles: ["build", "build-{{ component }}"]
build:
context: "build-context/{{ component }}"
dockerfile: Dockerfile
args:
EXAMPLE_ARG_FOR_DOCKERFILE: ${EXAMPLE_ARG_FROM_ENV_FILE}
{%- endfor %}

View File

@ -1,11 +0,0 @@
services:
{%- if ',' in cookiecutter.component_s_canon -%}
{%- set components = cookiecutter.component_s_canon.split(',') -%}
{% for component in components %}
{{ component }}:
container_name: {{ cookiecutter.service_canon }}-{{ component }}-${CONTEXT}
{%- endfor %}
{%- else -%}
{{ component }}:
container_name: {{ cookiecutter.component_s_canon }}-${CONTEXT}
{%- endif -%}