From 7cd6aa2681a19a3d65fc049017ccf1d96be03666 Mon Sep 17 00:00:00 2001 From: Severin Neumann Date: Tue, 7 Jan 2025 10:24:59 +0100 Subject: [PATCH] Add docker compose generator (#72) --- scripts/generators/docker-compose/.gitignore | 3 + scripts/generators/docker-compose/Dockerfile | 18 +++++ .../docker-compose/createComposeFile.py | 67 +++++++++++++++++ .../docker-compose/docker-compose.j2 | 72 +++++++++++++++++++ .../docker-compose/requirements.txt | 4 ++ 5 files changed, 164 insertions(+) create mode 100644 scripts/generators/docker-compose/.gitignore create mode 100644 scripts/generators/docker-compose/Dockerfile create mode 100644 scripts/generators/docker-compose/createComposeFile.py create mode 100644 scripts/generators/docker-compose/docker-compose.j2 create mode 100644 scripts/generators/docker-compose/requirements.txt diff --git a/scripts/generators/docker-compose/.gitignore b/scripts/generators/docker-compose/.gitignore new file mode 100644 index 0000000..7afd369 --- /dev/null +++ b/scripts/generators/docker-compose/.gitignore @@ -0,0 +1,3 @@ +docker-compose.yaml +config.yaml +example.yml \ No newline at end of file diff --git a/scripts/generators/docker-compose/Dockerfile b/scripts/generators/docker-compose/Dockerfile new file mode 100644 index 0000000..c888c7c --- /dev/null +++ b/scripts/generators/docker-compose/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.14-alpine + +WORKDIR /app + +RUN apk addgroup -S appgroup && adduser -S appuser -G appgroup + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY createComposeFile.py . + +COPY docker-compose.j2 . + +RUN chown -R appuser:appgroup /app && chmod +x createComposeFile.py +USER appuser + +# Define the default entrypoint to run the script +ENTRYPOINT ["python", "createComposeFile.py", "--template", "/app/docker-compose.j2"] diff --git a/scripts/generators/docker-compose/createComposeFile.py b/scripts/generators/docker-compose/createComposeFile.py new file mode 100644 index 0000000..1afe0bf --- /dev/null +++ b/scripts/generators/docker-compose/createComposeFile.py @@ -0,0 +1,67 @@ +import argparse +from jinja2 import Environment, FileSystemLoader +import os +import yaml + +debug_mode = False + +def read_yaml(file_path): + with open(os.path.expanduser(file_path), 'r') as file: + # Parse the YAML file + data = yaml.safe_load(file) + return data + +def main(): + global debug_mode + parser = argparse.ArgumentParser(description='Process some configuration file.') + parser.add_argument( + '--config', + type=str, + default='config.yaml', # Set your default config file here + help='Path to the configuration file (default: config.yaml)' + ) + parser.add_argument( + '--debug', + action='store_true', + help='Enable debug mode' + ) + parser.add_argument( + '--template', + type=str, + default='docker-compose.j2', + help='Path to the Jinja2 template file (default: docker-compose.j2)' + ) + parser.add_argument( + '--output', + type=str, + default='docker-compose.yaml', + help='Path to the output file (default: docker-compose.yaml)' + ) + args = parser.parse_args() + config_path = args.config + debug_mode = args.debug + template_path = args.template + output_path = args.output + + yaml_data = read_yaml(config_path) + if isinstance(yaml_data, dict): + env = Environment(loader=FileSystemLoader('.')) + + template = env.get_template(template_path) + + rendered_content = template.render(yaml_data) + + with open(output_path, 'w') as output_file: + output_file.write(rendered_content) + + else: + raise Exception(f"Failed to read the configuration file {config_file}: The top-level structure is not a dictionary.") + +try: + main() +except Exception as e: + if not debug_mode: + print(e) + else: + raise e + exit(1) \ No newline at end of file diff --git a/scripts/generators/docker-compose/docker-compose.j2 b/scripts/generators/docker-compose/docker-compose.j2 new file mode 100644 index 0000000..f9077b3 --- /dev/null +++ b/scripts/generators/docker-compose/docker-compose.j2 @@ -0,0 +1,72 @@ +--- +{%- set global = global | default({}) -%} +{%- set imageNamePrefix = global.imageNamePrefix | default('ghcr.io/cisco-open/') -%} +{%- set imageNameSuffix = global.imageNameSuffix | default('latest') -%} +{%- set serviceDefaultPort = global.serviceDefaultPort | default(80) -%} +{# Ensure the variable always ends with a slash #} +{%- set imageNamePrefix = imageNamePrefix if imageNamePrefix.endswith('/') else imageNamePrefix + '/' %} +services: +{%- if services is defined and services is mapping %} + ## services + {%- for name, details in services.items() %} + {{ name }}: + image: {{ imageNamePrefix }}app-simulator-services-{{ details.type }}:{{ imageNameSuffix }} + {%- if details.port is defined %} + ports: + - "{{ details.port }}:8080" + {%- endif %} + {%- if serviceDefaultPort != 8080 %} + environment: + SERVICE_DEFAULT_PORT: "{{ serviceDefaultPort }}" + {%- if serviceDefaultPort <= 1024 %} + cap_add: + - NET_BIND_SERVICE + {%- endif %} + {%- endif %} + configs: + - source: service_{{ name | replace("-", "_") }}_config + target: /config.json + {%- endfor %} +{%- endif -%} +{%- if databases is defined and databases is mapping %} + ## databases + {%- for name, details in databases.items() %} + {{ name }}: + image: {{ imageNamePrefix }}app-simulator-databases-{{ details.type }}:{{ imageNameSuffix }} + configs: + - source: database_{{ name | replace("-", "_") }}_config + target: /config.json + {%- endfor %} +{%- endif -%} +{%- if loaders is defined and loaders is mapping %} + ## loaders + {%- for name, details in loaders.items() %} + {{ name }}: + image: {{ imageNamePrefix }}app-simulator-loaders-{{ details.type }}:{{ imageNameSuffix }} + configs: + - source: loader_{{ name | replace("-", "_") }}_config + target: /config.json + {%- endfor %} +{%- endif %} +configs: +{%- if services is defined and services is mapping %} +{%- for name, details in services.items() %} + service_{{ name | replace("-", "_") }}_config: + content: | + {{ details | tojson }} +{%- endfor -%} +{%- endif %} +{%- if databases is defined and databases is mapping %} +{%- for name, details in databases.items() %} + database_{{ name | replace("-", "_") }}_config: + content: | + {{ details | tojson }} +{%- endfor -%} +{%- endif %} +{%- if loaders is defined and loaders is mapping %} +{%- for name, details in loaders.items() %} + loader_{{ name | replace("-", "_") }}_config: + content: | + {{ details | tojson }} +{%- endfor -%} +{%- endif %} \ No newline at end of file diff --git a/scripts/generators/docker-compose/requirements.txt b/scripts/generators/docker-compose/requirements.txt new file mode 100644 index 0000000..6d43419 --- /dev/null +++ b/scripts/generators/docker-compose/requirements.txt @@ -0,0 +1,4 @@ +Jinja2==3.1.4 +opentelemetry-api==1.29.0 +opentelemetry-sdk==1.29.0 +PyYAML==6.0.2