Compare commits
2 Commits
e379cdafe0
...
8b2c0cecca
Author | SHA1 | Date | |
---|---|---|---|
8b2c0cecca | |||
e6be888b91 |
53
README.md
53
README.md
@ -1,3 +1,54 @@
|
|||||||
# py-cookiecutter-templates
|
# py-cookiecutter-templates
|
||||||
|
|
||||||
Project directory structure templates for the Python Cookiecutter module
|
Project directory structure templates for the Python Cookiecutter module.
|
||||||
|
|
||||||
|
## What's Cookiecutter?
|
||||||
|
|
||||||
|
The Python package [Cookiecutter](https://github.com/cookiecutter/cookiecutter) assists in creating directory structure for whatever purpose you need such as for example a new Python project, an Ansible role or a `docker-compose` project. If you've ever wanted to put project structure best practices into version control Cookiecutter's here to help.
|
||||||
|
|
||||||
|
Cookiecutter is governed by so-called Cookiecutter templates. Most of its magic inside Cookiecutter templates happens via the [Jinja2 template engine](https://palletsprojects.com/p/jinja/) so if you're familiar with Ansible you'll feel right at home.
|
||||||
|
|
||||||
|
## Get started
|
||||||
|
|
||||||
|
Get Cookiecutter like so:
|
||||||
|
```
|
||||||
|
pip install cookiecutter
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute a template like so, `docker-compose` as an example:
|
||||||
|
```
|
||||||
|
cookiecutter https://quico.space/Quico/py-cookiecutter-templates.git --directory 'docker-compose'
|
||||||
|
```
|
||||||
|
|
||||||
|
Cookiecutter prompts you for whatever info the template needs then generates files and directories.
|
||||||
|
|
||||||
|
This is Cookiecutter prompting for info:
|
||||||
|
```
|
||||||
|
info
|
||||||
|
```
|
||||||
|
|
||||||
|
The end result is a directory structure that has everything you need to hit the ground running.
|
||||||
|
```
|
||||||
|
.
|
||||||
|
└── grafana
|
||||||
|
├── build-context
|
||||||
|
│ ├── grafana
|
||||||
|
│ │ ├── docker-data
|
||||||
|
│ │ │ └── .gitkeep
|
||||||
|
│ │ ├── Dockerfile
|
||||||
|
│ │ └── extras
|
||||||
|
│ │ └── .gitkeep
|
||||||
|
│ └── nginx
|
||||||
|
│ ├── docker-data
|
||||||
|
│ │ └── .gitkeep
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ └── extras
|
||||||
|
│ └── .gitkeep
|
||||||
|
├── common-settings.yml
|
||||||
|
├── docker-compose.override.yml
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── env
|
||||||
|
└── fully.qualified.domain.name.example
|
||||||
|
```
|
||||||
|
|
||||||
|
Each subdirectory in this repo is a Cookiecutter template, you'll recognize them from their telltale `cookiecutter.json` files. Directories usually have a readme file explaining a bit more about each individual template.
|
||||||
|
83
docker-compose/README.md
Normal file
83
docker-compose/README.md
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# Cookiecutter docker-compose template
|
||||||
|
|
||||||
|
## Get started
|
||||||
|
|
||||||
|
Get Cookiecutter like so:
|
||||||
|
```
|
||||||
|
pip install cookiecutter
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute this template like so:
|
||||||
|
```
|
||||||
|
cookiecutter https://quico.space/Quico/py-cookiecutter-templates.git --directory 'docker-compose'
|
||||||
|
```
|
||||||
|
|
||||||
|
Cookiecutter interactively prompts you for the following info, here with example answers:
|
||||||
|
```
|
||||||
|
service []: grafana
|
||||||
|
component_list [grafana]: grafana,nginx
|
||||||
|
context []: cncf
|
||||||
|
```
|
||||||
|
|
||||||
|
Done, directory structure and files for your next `docker-compose` project are ready for you to hit the ground running.
|
||||||
|
|
||||||
|
## Explanation and terminology
|
||||||
|
|
||||||
|
Each `docker-compose` project forms a *__service__* that may consist of either a single or multiple *__components__*.
|
||||||
|
|
||||||
|
The `service` variable by default is empty. In this example we've chosen to name the service `grafana`. We want `grafana` to consist of two components, namely Grafana itself and Nginx.
|
||||||
|
|
||||||
|
Syntax for a multi-component `component_list` is a comma-separated list without spaces. The string `grafana,nginx` will be treated as a list of two components. Capitalization doesn't matter, we're lowercasing all strings automatically.
|
||||||
|
|
||||||
|
The template prefills `component_list` with whatever you previously entered as the `service` name. To create directory structure for a _single-component service_ confirm the default. If `component_list` and `service` are identical directory structure will be that for a _single-component service_.
|
||||||
|
|
||||||
|
The last prompt for a *__context__* is a generic string to help you distinguish deployments. It can be whatever you want such as for example a team name, here `cncf`.
|
||||||
|
|
||||||
|
## Result
|
||||||
|
|
||||||
|
### Multi-component service
|
||||||
|
|
||||||
|
Above example of a multi-component (two in this case) `grafana` service will give you this directory structure:
|
||||||
|
```
|
||||||
|
.
|
||||||
|
└── grafana
|
||||||
|
├── build-context
|
||||||
|
│ ├── grafana
|
||||||
|
│ │ ├── docker-data
|
||||||
|
│ │ │ └── .gitkeep
|
||||||
|
│ │ ├── Dockerfile
|
||||||
|
│ │ └── extras
|
||||||
|
│ │ └── .gitkeep
|
||||||
|
│ └── nginx
|
||||||
|
│ ├── docker-data
|
||||||
|
│ │ └── .gitkeep
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ └── extras
|
||||||
|
│ └── .gitkeep
|
||||||
|
├── common-settings.yml
|
||||||
|
├── docker-compose.override.yml
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── env
|
||||||
|
└── fully.qualified.domain.name.example
|
||||||
|
```
|
||||||
|
Check out file contents over in the [examples/grafana subdir](/examples/grafana).
|
||||||
|
|
||||||
|
### Single-component
|
||||||
|
|
||||||
|
With an alternative single-component `hashicorpvault` service the result may look like this:
|
||||||
|
```
|
||||||
|
.
|
||||||
|
└── hashicorpvault
|
||||||
|
├── build-context
|
||||||
|
│ ├── docker-data
|
||||||
|
│ │ └── .gitkeep
|
||||||
|
│ ├── Dockerfile
|
||||||
|
│ └── extras
|
||||||
|
│ └── .gitkeep
|
||||||
|
├── common-settings.yml
|
||||||
|
├── docker-compose.override.yml
|
||||||
|
├── docker-compose.yml
|
||||||
|
└── env
|
||||||
|
└── fully.qualified.domain.name.example
|
||||||
|
```
|
||||||
|
Check out file contents over in the [examples/hashicorpvault subdir](/examples/hashicorpvault).
|
@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"component_s": "grafana,nginx",
|
"service": "",
|
||||||
"service": "example_service",
|
"__service_slug": "{{ cookiecutter.service.lower().replace(' ', '_').replace('-', '_') }}",
|
||||||
"context": "example_context",
|
"component_list": "{{ cookiecutter.__service_slug }}",
|
||||||
"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('-', '_') }}"
|
"__component_list_slug": "{{ cookiecutter.component_list.lower().replace(' ', '_').replace('-', '_') }}",
|
||||||
|
"context": "",
|
||||||
|
"__context_slug": "{{ cookiecutter.context.lower().replace(' ', '_').replace('-', '_') }}",
|
||||||
|
"__project_slug": "{{ cookiecutter.__service_slug }}"
|
||||||
}
|
}
|
||||||
|
@ -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/*
|
@ -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/*
|
19
docker-compose/examples/grafana/docker-compose.override.yml
Normal file
19
docker-compose/examples/grafana/docker-compose.override.yml
Normal 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}"
|
47
docker-compose/examples/grafana/docker-compose.yml
Normal file
47
docker-compose/examples/grafana/docker-compose.yml
Normal 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
|
30
docker-compose/examples/grafana/env/fully.qualified.domain.name.example
vendored
Normal file
30
docker-compose/examples/grafana/env/fully.qualified.domain.name.example
vendored
Normal 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
|
@ -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/*
|
11
docker-compose/examples/hashicorpvault/common-settings.yml
Normal file
11
docker-compose/examples/hashicorpvault/common-settings.yml
Normal 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}"
|
@ -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}"
|
28
docker-compose/examples/hashicorpvault/docker-compose.yml
Normal file
28
docker-compose/examples/hashicorpvault/docker-compose.yml
Normal 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
|
29
docker-compose/examples/hashicorpvault/env/fully.qualified.domain.name.example
vendored
Normal file
29
docker-compose/examples/hashicorpvault/env/fully.qualified.domain.name.example
vendored
Normal 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
|
@ -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 os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
component_subdirs = ["extras", "docker-data"]
|
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()
|
project_dir = os.getcwd()
|
||||||
build_context_dir_name = "build-context"
|
build_context_dir_name = "build-context"
|
||||||
build_ctx_dir_abs = os.path.join(project_dir, build_context_dir_name)
|
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:
|
for component in components:
|
||||||
|
if len(components) > 1:
|
||||||
component_dir_abs = os.path.join(build_ctx_dir_abs, component)
|
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)
|
component_dir_rel = os.path.relpath(component_dir_abs, project_dir)
|
||||||
try:
|
try:
|
||||||
os.mkdir(component_dir_abs)
|
os.makedirs(component_dir_abs, exist_ok=True)
|
||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
print(f"Dir './{component_dir_rel}' already exists when it shouldn't. Aborting and exiting 1 ...")
|
print(f"Dir './{component_dir_rel}' already exists when it shouldn't. Aborting and exiting 1 ...")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@ -42,3 +72,40 @@ for root, subdirs, filelist in os.walk(project_dir):
|
|||||||
except IOError:
|
except IOError:
|
||||||
print(f"Can't write './{gitkeep_file_rel}'. Aborting and exiting 2 ...")
|
print(f"Can't write './{gitkeep_file_rel}'. Aborting and exiting 2 ...")
|
||||||
sys.exit(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)
|
||||||
|
9
docker-compose/hooks/pre_gen_project.py
Normal file
9
docker-compose/hooks/pre_gen_project.py
Normal 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)
|
@ -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/*
|
@ -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}"
|
@ -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 %}
|
@ -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
|
@ -1,10 +1,10 @@
|
|||||||
CONTEXT={{ cookiecutter.context_canon }}
|
CONTEXT={{ cookiecutter.__context_slug }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Set something sensible here and uncomment
|
# Set something sensible here and uncomment
|
||||||
# ---
|
# ---
|
||||||
{%- set components = cookiecutter.component_s_canon.split(',') -%}
|
{%- set components = cookiecutter.__component_list_slug.split(',') -%}
|
||||||
{% for component in components %}
|
{% for component in components %}
|
||||||
# {{ component.upper() }}_VERSION=x.y.z
|
# {{ component.upper() }}_VERSION=x.y.z
|
||||||
{%- endfor %}
|
{%- endfor %}
|
@ -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 %}
|
|
@ -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 -%}
|
|
Loading…
x
Reference in New Issue
Block a user