Skip to content

Commit 1ce6645

Browse files
authored
Tests and updates for compose generator (#86)
1 parent 9764bfc commit 1ce6645

File tree

7 files changed

+280
-69
lines changed

7 files changed

+280
-69
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@
77
*.vsix
88
# Ignore local venv environment
99
.venv
10+
__pycache__
11+
.pytest_cache
12+
1013
# Ignore mac os x .DS_Store files
1114
.DS_Store

scripts/generators/docker-compose/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ RUN chown -R appuser:appgroup /app && chmod +x createComposeFile.py
1515
USER appuser
1616

1717
# Define the default entrypoint to run the script
18-
ENTRYPOINT ["python", "createComposeFile.py", "--template", "/app/docker-compose.j2"]
18+
ENTRYPOINT ["python", "createComposeFile.py", "--template", "docker-compose.j2"]

scripts/generators/docker-compose/createComposeFile.py

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,59 @@
11
import argparse
2+
from deepmerge import always_merger
3+
import inflect
24
from jinja2 import Environment, FileSystemLoader
35
import os
46
import yaml
7+
# Initialize inflect engine
8+
p = inflect.engine()
9+
510

611
debug_mode = False
712

13+
defaultValues = {
14+
"imageNamePrefix": "ghcr.io/cisco-open/",
15+
"imageNameSuffix": "latest",
16+
"defaultPorts": {
17+
"services": 80, # this is different from the "default default" on purpose.
18+
"databases": 5432,
19+
"loaders": 6000 # not used
20+
},
21+
# Keep the default ports for each service type here
22+
# We need them to verify in the docker compose generation if they have been
23+
# changed by the user.
24+
"_defaultDefaultPorts": {
25+
"services": 8080,
26+
"databases": 5432,
27+
"loaders": 6000
28+
}
29+
}
30+
31+
def plural_to_singular(word):
32+
return p.singular_noun(word) or word
33+
834
def read_yaml(file_path):
935
with open(os.path.expanduser(file_path), 'r') as file:
1036
# Parse the YAML file
1137
data = yaml.safe_load(file)
1238
return data
1339

40+
def render_compose_file(template, data, defaultValues):
41+
globalVars = data.get('global', {})
42+
43+
config = {
44+
"global": always_merger.merge(defaultValues, globalVars),
45+
"scopes": {
46+
"services": data.get('services', {}),
47+
"databases": data.get('databases', {}),
48+
"loaders": data.get('loaders', {}),
49+
}
50+
}
51+
52+
return template.render(config)
53+
1454
def main():
1555
global debug_mode
56+
global defaultValues
1657
parser = argparse.ArgumentParser(description='Process some configuration file.')
1758
parser.add_argument(
1859
'--config',
@@ -47,21 +88,25 @@ def main():
4788
if isinstance(yaml_data, dict):
4889
env = Environment(loader=FileSystemLoader('.'))
4990

91+
env.filters['singularize'] = plural_to_singular
92+
5093
template = env.get_template(template_path)
5194

52-
rendered_content = template.render(yaml_data)
95+
rendered_content = render_compose_file(template, data, defaultValues)
5396

5497
with open(output_path, 'w') as output_file:
5598
output_file.write(rendered_content)
5699

57100
else:
58101
raise Exception(f"Failed to read the configuration file {config_file}: The top-level structure is not a dictionary.")
59102

60-
try:
61-
main()
62-
except Exception as e:
63-
if not debug_mode:
64-
print(e)
65-
else:
66-
raise e
67-
exit(1)
103+
104+
if __name__ == "__main__":
105+
try:
106+
main()
107+
except Exception as e:
108+
if not debug_mode:
109+
print(e)
110+
else:
111+
raise e
112+
exit(1)
Lines changed: 18 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,33 @@
1-
---
2-
{%- set global = global | default({}) -%}
3-
{%- set imageNamePrefix = global.imageNamePrefix | default('ghcr.io/cisco-open/') -%}
4-
{%- set imageNameSuffix = global.imageNameSuffix | default('latest') -%}
5-
{%- set serviceDefaultPort = global.serviceDefaultPort | default(80) -%}
6-
{# Ensure the variable always ends with a slash #}
7-
{%- set imageNamePrefix = imageNamePrefix if imageNamePrefix.endswith('/') else imageNamePrefix + '/' %}
81
services:
9-
{%- if services is defined and services is mapping %}
10-
## services
11-
{%- for name, details in services.items() %}
2+
{%- for scope, scopeDetails in scopes.items() %}
3+
## {{ scope }}
4+
{%- for name, details in scopeDetails.items() %}
125
{{ name }}:
13-
image: {{ imageNamePrefix }}app-simulator-services-{{ details.type }}:{{ imageNameSuffix }}
6+
image: {{ global.imageNamePrefix }}app-simulator-{{ scope }}-{{ details.type }}:{{global.imageNameSuffix }}
147
{%- if details.port is defined %}
158
ports:
16-
- "{{ details.port }}:8080"
9+
- "{{ details.port }}:{{ global.defaultPorts[scope] }}"
1710
{%- endif %}
18-
{%- if serviceDefaultPort != 8080 %}
11+
{%- if global.defaultPorts is defined -%}
12+
{%- if global.defaultPorts[scope] is defined and global.defaultPorts[scope] != global._defaultDefaultPorts[scope] %}
1913
environment:
20-
SERVICE_DEFAULT_PORT: "{{ serviceDefaultPort }}"
21-
{%- if serviceDefaultPort <= 1024 %}
14+
- {{ scope | singularize | upper }}_DEFAULT_PORT={{ global.defaultPorts[scope] }}
15+
{%- endif %}
16+
{%- if global.defaultPorts[scope] <= 1024 %}
2217
cap_add:
23-
- NET_BIND_SERVICE
24-
{%- endif %}
18+
- NET_BIND_SERVICE
19+
{%- endif %}
2520
{%- endif %}
2621
configs:
27-
- source: service_{{ name | replace("-", "_") }}_config
28-
target: /config.json
29-
{%- endfor %}
30-
{%- endif -%}
31-
{%- if databases is defined and databases is mapping %}
32-
## databases
33-
{%- for name, details in databases.items() %}
34-
{{ name }}:
35-
image: {{ imageNamePrefix }}app-simulator-databases-{{ details.type }}:{{ imageNameSuffix }}
36-
configs:
37-
- source: database_{{ name | replace("-", "_") }}_config
38-
target: /config.json
39-
{%- endfor %}
40-
{%- endif -%}
41-
{%- if loaders is defined and loaders is mapping %}
42-
## loaders
43-
{%- for name, details in loaders.items() %}
44-
{{ name }}:
45-
image: {{ imageNamePrefix }}app-simulator-loaders-{{ details.type }}:{{ imageNameSuffix }}
46-
configs:
47-
- source: loader_{{ name | replace("-", "_") }}_config
22+
- source: {{ scope }}_{{ name | replace("-", "_") | lower }}_config
4823
target: /config.json
4924
{%- endfor %}
50-
{%- endif %}
25+
{%- endfor %}
5126
configs:
52-
{%- if services is defined and services is mapping %}
53-
{%- for name, details in services.items() %}
54-
service_{{ name | replace("-", "_") }}_config:
55-
content: |
56-
{{ details | tojson }}
57-
{%- endfor -%}
58-
{%- endif %}
59-
{%- if databases is defined and databases is mapping %}
60-
{%- for name, details in databases.items() %}
61-
database_{{ name | replace("-", "_") }}_config:
62-
content: |
63-
{{ details | tojson }}
64-
{%- endfor -%}
65-
{%- endif %}
66-
{%- if loaders is defined and loaders is mapping %}
67-
{%- for name, details in loaders.items() %}
68-
loader_{{ name | replace("-", "_") }}_config:
27+
{%- for scope, scopeDetails in scopes.items() %}
28+
{%- for name, details in scopeDetails.items() %}
29+
{{ scope }}_{{ name | replace("-", "_") | lower }}_config:
6930
content: |
7031
{{ details | tojson }}
7132
{%- endfor -%}
72-
{%- endif %}
33+
{%- endfor %}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1+
deepmerge==2.0
2+
inflect==7.5.0
13
Jinja2==3.1.4
2-
opentelemetry-api==1.29.0
3-
opentelemetry-sdk==1.29.0
44
PyYAML==6.0.2
5+
pytest==8.3.4

scripts/generators/docker-compose/tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)