Pretty massive overhaul so that generated charts follow better current iterations (including making use of subcharts etc...)

This commit is contained in:
Alan Bridgeman 2025-12-30 19:01:55 -06:00
parent 826b9a7198
commit 37baf7e410
17 changed files with 2240 additions and 735 deletions

View file

@ -1,134 +1,515 @@
import json
import os, sys, json
from src.Ingress import Ingress
from src.Service import Service
from src.Dependency import Dependency
from src.Database import Database
from src.HashicorpVault import HashicorpVault
from src.MongoDB import MongoDB
from src.AzureTableStorage import AzureTableStorage
from src.Redis import Redis
from src.LoggingSidecar import LoggingSidecar
from src.OAuth import OAuth
from src.ThirdPartyService import ThirdPartyService
from src.Deployment import Deployment
from src.HelmChart import HelmChart
if __name__ == '__main__':
with open('input.json', 'r') as f:
data = json.load(f)
def print_help():
"""Print help message."""
print('Usage: python create-helm-chart.py [input_file]')
print()
print('This script generates a Helm chart based on an input JSON file (default - input.json).')
def parse_args(args: list[str]) -> dict[str, str]:
"""Parse command line arguments.
Args:
args (list[str]): List of command line arguments.
Returns:
dict[str, str]: Dictionary of results from parsing.
"""
input_file = 'input.json'
# Check if any arguments were provided
if len(args) > 1:
# Verify the correct number of arguments were provided
if len(args) == 2:
if args[1] == '--help':
# if the user requested help, display it and exit
print_help()
exit(0)
else:
# If a single argument was provided, assume it's the input file
input_file = args[1]
else:
print(f'Invalid number of arguments: {len(sys.argv)}')
print()
print_help()
exit(1)
return { 'input_file': input_file }
def get_chart_info(data: dict[str, any]) -> dict[str, any]:
"""Extract Helm chart information from input JSON data.
Note, this function also performs validation on the input data to ensure required fields are present.
And it also provides default values as needed.
Args:
data (dict[str, any]): Input JSON data.
Returns:
dict[str, any]: Extracted Helm chart information.
"""
# Verify top level 'chart' field exists
if 'chart' not in data:
raise Exception('No chart information found in input JSON file. But chart information is required to create a Helm chart.')
# --- Required Fields ---
# Verify 'name' field exists
if 'name' not in data['chart']:
raise Exception('No chart name found in input JSON file. But chart name is required to create a Helm chart.')
# The API version of the Helm chart itself
api_version = data['chart']['apiVersion']
# The version of the application that the Helm chart is deploying
app_version = data['chart']['appVersion']
# A description of the Helm chart
chart_description = data['chart']['description']
# The URL of the Helm chart's home page
chart_homepage = data['chart']['homepage']
# The maintainers of the Helm chart
maintainers = data['chart']['maintainers']
# The name of the Helm chart
chart_name = data['chart']['name']
# The sources of the Helm chart
sources = data['chart']['sources']
# The version of the Helm chart
# --- Required Fields with Defaults ---
# The API version of the Helm chart itself (defaults to `v2` if not specified)
# `v2` is the default because we make heavy use of dependencies, which are not supported (or supported as well) in `v1` charts
api_version = 'v2'
if 'apiVersion' in data['chart']:
api_version = data['chart']['apiVersion']
# The version of the Helm chart (defaults to `1.0.0` if not specified)
# `1.0.0` is the default because it's a reasonable starting version for a new chart
chart_version = '1.0.0'
if 'version' in data['chart']:
chart_version = data['chart']['version']
# --- Optional Fields ---
# The version of the application that the Helm chart is deploying
# Note, can be unset (variable being `None`)
app_version = None
if 'appVersion' in data['chart']:
app_version = data['chart']['appVersion']
# A description of the Helm chart
# Note, can be unset (variable being `None`)
chart_description = None
if 'description' in data['chart']:
chart_description = data['chart']['description']
# The URL of the Helm chart's home page
# Note, can be unset (variable being `None`)
chart_homepage = None
if 'homepage' in data['chart']:
chart_homepage = data['chart']['homepage']
# The maintainers of the Helm chart
# Note, can be unset (variable being `None`)
maintainers = None
if 'maintainers' in data['chart']:
maintainers = data['chart']['maintainers']
# The sources of the Helm chart
sources = None
if 'sources' in data['chart']:
sources = data['chart']['sources']
return {
'api_version': api_version,
'app_version': app_version,
'chart_description': chart_description,
'chart_homepage': chart_homepage,
'maintainers': maintainers,
'chart_name': chart_name,
'sources': sources,
'chart_version': chart_version
}
def get_image_info(data: dict[str, any]) -> dict[str, str]:
"""Extract image information from input JSON data.
Args:
data (dict[str, any]): Input JSON data.
Returns:
dict[str, str]: Extracted image information.
"""
# Verify top level 'image' field exists
if 'image' not in data:
raise Exception('No image information found in input JSON file. But image information is required to create a Helm chart.')
# Verify 'repository' field exists
if 'repository' not in data['image']:
raise Exception('No image repository found in input JSON file. But image repository is required to create a Helm chart.')
# Note, the 'repository' can be in a few different forms:
# 1. Just the registry/repository itself, specifying `name` separately
# 2. As an encompassing value, representing some combination of `<registry/repository>/<name>`
#
# Note, shouldn't use `<registry/repository>:<tag>` and specify `name` separately as it causes incorrect output and is confusing.
# But `<registry/repository>/<name>` and `<tag>` separately is perfectly valid.
#
# This multi-format nature is why it and only it is required, while `name` and `tag` are optional.
#
image_repository = data['image']['repository']
# Append 'name' field to repository if it exists
if 'name' in data['image']:
image_repository += '/' + data['image']['name']
if 'tag' not in data['image']:
raise Exception('No image tag found in input JSON file. But image tag is required to create a Helm chart.')
image_tag = data['image']['tag']
# Append 'tag' field to repository if it exists
#if 'tag' in data['image']:
# image_repository += ':' + data['image']['tag']
# The image pull policy (defaults to `IfNotPresent` if not specified)
# `IfNotPresent` is the default because it's a reasonable default for most use cases
image_pull_policy = 'IfNotPresent'
if 'pullPolicy' in data['image']:
image_pull_policy = data['image']['pullPolicy']
return {
'image_repository': image_repository,
'image_tag': image_tag,
'image_pull_policy': image_pull_policy
}
def get_ingress_info(data: dict[str, any]) -> dict[str, str]:
"""Extract ingress information from input JSON data.
Args:
data (dict[str, any]): Input JSON data.
Returns:
dict[str, str]: Extracted ingress information.
"""
# Verify top level 'ingress' field exists
if 'ingress' not in data:
raise Exception('No ingress information found in input JSON file. But ingress information is required to create a Helm chart.')
# Verify 'hostname' field exists
if 'hostname' not in data['ingress']:
raise Exception('No ingress hostname found in input JSON file. But ingress hostname is required to create a Helm chart.')
hostname = data['ingress']['hostname']
ingress = Ingress(hostname)
service = Service()
return {
'hostname': hostname
}
templates = [ingress, service]
def get_db_info(data: dict[str, any]) -> tuple[bool, dict[str, Database | Dependency | None]]:
"""Extract database information from input JSON data.
Args:
data (dict[str, any]): Input JSON data.
Returns:
tuple[bool, dict[str, Database | Dependency]]: A tuple where the first element indicates whether a database is used,
and the second element is a dictionary containing the database object and the dependency object.
"""
uses_db = False
uses_secrets_vault = False
nosql = None
uses_cache = False
third_party_services = []
extra_env_vars = {}
db = None
db_dependency = None
if 'db' in data and data['db'] != False:
db_name = data['db']['name']
db_host = data['db']['host']
db_user = data['db']['user']
db_password = data['db']['password']
if 'name' not in data['db']:
raise Exception('No database name found in input JSON file. But database name is required to create the Helm chart with database support.')
db = Database(db_name, db_host, db_user, db_password)
db_name = data['db']['name']
if 'host' not in data['db']:
raise Exception('No database host found in input JSON file. But database host is required to create the Helm chart with database support.')
db_host = data['db']['host']
if 'user' not in data['db']:
raise Exception('No database user found in input JSON file. But database user is required to create the Helm chart with database support.')
db_user = data['db']['user']
if 'password' not in data['db']:
raise Exception('No database password found in input JSON file. But database password is required to create the Helm chart with database support.')
db_password = data['db']['password']
uses_db = True
templates.append(db)
db = Database(db_name, db_host, db_user, db_password)
db_dependency = Dependency('db-deploy', '1.0.2', 'https://helm.bridgemanaccessible.ca/', 'database', 'database.enabled')
return uses_db, { 'db': db, 'db_dependency': db_dependency }
def get_vault_info(data: dict[str, any]) -> tuple[bool, dict[str, HashicorpVault | Dependency | None]]:
"""Extract HashiCorp Vault information from input JSON data.
Args:
data (dict[str, any]): Input JSON data.
Returns:
tuple[bool, dict[str, HashicorpVault | Dependency]]: A tuple where the first element indicates whether Vault is used,
and the second element is a dictionary containing the Vault object and the dependency object.
"""
uses_vault = False
vault = None
vault_dependency = None
if 'vault' in data and data['vault'] != False:
if 'image' not in data['vault']:
raise Exception('No Vault image information found in input JSON file. But Vault image information is required to create the Helm chart with Vault support.')
if 'repository' not in data['vault']['image'] or 'tag' not in data['vault']['image']:
raise Exception('Incomplete Vault image information found in input JSON file. Both repository and tag are required to create the Helm chart with Vault support.')
vault_image = {
'repository': data['vault']['image']['repository'],
'tag': data['vault']['image']['tag']
}
if 'hostname' not in data['vault']:
raise Exception('No Vault hostname found in input JSON file. But Vault hostname is required to create the Helm chart with Vault support.')
vault_hostname = data['vault']['hostname']
vault_storage_class = data['vault']['storageClass']
#vault_storage_class = data['vault']['storageClass']
vault = HashicorpVault(image=vault_image, hostname=vault_hostname, storage_class=vault_storage_class)
if 'policyCapabilities' not in data['vault']:
raise Exception('No Vault policy capabilities found in input JSON file. But Vault policy capabilities are required to create the Helm chart with Vault support.')
uses_secrets_vault = True
vault_policy_capabilities = data['vault']['policyCapabilities']
templates.append(vault)
uses_vault = True
vault = HashicorpVault(image=vault_image, hostname=vault_hostname, policy_capabilities=vault_policy_capabilities)
vault_dependency = Dependency('ba-custom-hashicorp-vault', '1.0.6', 'https://helm.bridgemanaccessible.ca/', 'vault', 'vault.enabled')
return uses_vault, { 'vault': vault, 'vault_dependency': vault_dependency }
def get_nosql_info(data: dict[str, any]) -> tuple[bool, dict[str, MongoDB | AzureTableStorage | Dependency | None]]:
"""Extract NoSQL information from input JSON data.
Args:
data (dict[str, any]): Input JSON data.
Returns:
tuple[bool, Database | None]: A tuple where the first element indicates whether a NoSQL database is used,
and the second element is the NoSQL database object if used, otherwise None.
"""
nosql = None
nosql_dependency = None
if 'nosql' in data and data['nosql'] != False:
# Default to MongoDB
nosql_type = 'mongodb'
if 'type' in data['nosql']:
nosql_type = data['nosql']['type']
if 'dbName' not in data['nosql']:
raise Exception('No NoSQL database name found in input JSON file. But NoSQL database name is required to create the Helm chart with NoSQL database support.')
nosql_db_name = data['nosql']['dbName']
if 'groupings' not in data['nosql']:
raise Exception('No NoSQL groupings found in input JSON file. But NoSQL groupings are required to create the Helm chart with NoSQL database support.')
groupings = data['nosql']['groupings']
if nosql_type == 'mongodb':
if 'user' not in data['nosql']:
raise Exception('No NoSQL database user found in input JSON file. But NoSQL database user is required to create the Helm chart with NoSQL (MongoDB) database support.')
nosql_user = data['nosql']['user']
if 'password' not in data['nosql']:
raise Exception('No NoSQL database password found in input JSON file. But NoSQL database password is required to create the Helm chart with NoSQL (MongoDB) database support.')
nosql_password = data['nosql']['password']
tables = data['nosql']['tables']
mongo = MongoDB(nosql_db_name, nosql_user, nosql_password, tables)
mongo = MongoDB(nosql_db_name, nosql_user, nosql_password, groupings)
nosql = mongo
elif nosql_type == 'az_table_storage':
nosql_key = data['nosql']['key']
templates.append(mongo)
az_table_storage = AzureTableStorage(nosql_db_name, nosql_key, groupings)
nosql = az_table_storage
nosql_dependency = Dependency('nosql-deploy', '1.0.5', 'https://helm.bridgemanaccessible.ca/', 'nosql', 'nosql.enabled')
return nosql is not None, { 'nosql': nosql, 'nosql_dependency': nosql_dependency }
def get_cache_info(data: dict[str, any]) -> tuple[bool, dict[str, Redis | Dependency | None]]:
"""Extract cache information from input JSON data.
Args:
data (dict[str, any]): Input JSON data.
Returns:
tuple[bool, dict[str, Redis | Dependency]]: A tuple where the first element indicates whether a cache is used,
and the second element is a dictionary containing the cache object and the dependency object.
"""
uses_cache = False
cache = None
cache_dependency = None
if 'cache' in data and data['cache'] != False:
cache_password = data['cache']['password']
if 'password' not in data['cache']:
raise Exception('No cache password found in input JSON file. But cache password is required to create the Helm chart with cache support.')
redis = Redis(cache_password)
cache_password = data['cache']['password']
uses_cache = True
templates.append(redis)
cache = Redis(cache_password)
cache_dependency = Dependency('cache-deploy', '1.0.7', 'https://helm.bridgemanaccessible.ca/', 'cache', 'cache.enabled')
return uses_cache, { 'cache': cache, 'cache_dependency': cache_dependency }
def get_oauth_info(data: dict[str, any]) -> tuple[bool, dict[str, OAuth | None]]:
"""Extract OAuth information from input JSON data.
Args:
data (dict[str, any]): Input JSON data.
Returns:
tuple[bool, dict[str, OAuth | None]]: A tuple where the first element indicates whether OAuth is used,
and the second element is a dictionary containing the OAuth object if used, otherwise None.
"""
uses_oauth = False
oauth = None
if 'oauth' in data and data['oauth'] != False:
if 'baseAppUrl' not in data['oauth']:
raise Exception('No OAuth base application URL found in input JSON file. But OAuth base application URL is required to create the Helm chart with OAuth support.')
base_app_url = data['oauth']['baseAppUrl']
if 'appAbbreviation' not in data['oauth']:
raise Exception('No OAuth application abbreviation found in input JSON file. But OAuth application abbreviation is required to create the Helm chart with OAuth support.')
app_abbreviation = data['oauth']['appAbbreviation']
if 'appName' not in data['oauth']:
raise Exception('No OAuth application name found in input JSON file. But OAuth application name is required to create the Helm chart with OAuth support.')
app_name = data['oauth']['appName']
if 'serviceName' not in data['oauth']:
raise Exception('No OAuth service name found in input JSON file. But OAuth service name is required to create the Helm chart with OAuth support.')
service_name = data['oauth']['serviceName']
if 'devPort' not in data['oauth']:
raise Exception('No OAuth development port found in input JSON file. But OAuth development port is required to create the Helm chart with OAuth support.')
dev_port = data['oauth']['devPort']
oauth = OAuth(base_app_url, app_abbreviation, app_name, service_name, dev_port)
if 'appRegContactEmail' not in data['oauth']:
raise Exception('No OAuth application registration contact email found in input JSON file. But OAuth application registration contact email is required to create the Helm chart with OAuth support.')
templates.append(oauth)
app_reg_contact_email = data['oauth']['appRegContactEmail']
uses_oauth = True
oauth = OAuth(base_app_url, app_abbreviation, app_name, service_name, dev_port, app_reg_contact_email)
return uses_oauth, { 'oauth': oauth }
def get_logging_info(data: dict[str, any]) -> tuple[bool, dict[str, LoggingSidecar | Dependency | None]]:
"""Extract logging information from input JSON data.
Args:
data (dict[str, any]): Input JSON data.
Returns:
tuple[bool, dict[str, LoggingSidecar | Dependency | None]]: A tuple where the first element indicates whether logging is used,
and the second element is a dictionary containing the Logging object if used, otherwise None.
"""
uses_logging = False
logging = None
logging_dependency = None
if 'logging' in data and data['logging'] != False:
uses_logging = True
log_aggregator_username = data['logging']['username']
log_aggregator_password = data['logging']['password']
logging = LoggingSidecar(log_aggregator_username, log_aggregator_password)
logging_dependency = Dependency('ba-logging-sidecar', '1.0.2', 'https://helm.bridgemanaccessible.ca/', 'loggingSidecar', 'loggingSidecar.enabled')
return uses_logging, { 'logging': logging, 'logging_dependency': logging_dependency }
def get_third_party_services_info(data: dict[str, any]) -> list[ThirdPartyService]:
"""Extract third-party services information from input JSON data.
Args:
data (dict[str, any]): Input JSON data.
Returns:
list[ThirdPartyService]: Extracted third-party services.
"""
third_party_services = []
if 'thirdPartyServices' in data:
if 'openai' in data['thirdPartyServices']:
openai_api_key = data['thirdPartyServices']['openai']['apiKey']
for service_name in data['thirdPartyServices'].keys():
service_vars = {}
openai = ThirdPartyService('openai', False, api_key=openai_api_key)
for key in data['thirdPartyServices'][service_name].keys():
# If the key is already in snake_case, skip it
if '_' not in key:
# Convert from camelCase to snake_case
new_key = ''
for i, c in enumerate(key):
if c.isupper() and i != 0:
new_key += '_' + c.lower()
else:
new_key += c.lower()
third_party_services.append(openai)
# Add the snake_case key to the dictionary with the value of the camelCase key
service_vars[new_key] = data['thirdPartyServices'][service_name][key]
templates.append(openai)
service = ThirdPartyService(service_name, True, **service_vars) #merchant_id=moneris_merchant_id, store_id=moneris_store_id, ht_profile_id=moneris_ht_profile_id, test_merchant_id=moneris_test_merchant_id, test_store_id=moneris_test_store_id, test_ht_profile_id=moneris_test_ht_profile_id)
if 'stripe' in data['thirdPartyServices']:
stripe_public_key = data['thirdPartyServices']['stripe']['publicKey']
stripe_secret_key = data['thirdPartyServices']['stripe']['secretKey']
stripe_test_public_key = data['thirdPartyServices']['stripe']['testPublicKey']
stripe_test_secret_key = data['thirdPartyServices']['stripe']['testSecretKey']
third_party_services.append(service)
stripe = ThirdPartyService('stripe', True, public_key=stripe_public_key, secret_key=stripe_secret_key, test_public_key=stripe_test_public_key, test_secret_key=stripe_test_secret_key)
return third_party_services
third_party_services.append(stripe)
def get_extra_env_vars(data: dict[str, any]) -> dict[str, str]:
"""Extract extra environment variables from input JSON data.
templates.append(stripe)
Args:
data (dict[str, any]): Input JSON data.
Returns:
dict[str, str]: Extracted extra environment variables.
"""
extra_env_vars = {}
if 'extraEnvVars' in data:
extra_env_vars = data['extraEnvVars']
@ -136,16 +517,105 @@ if __name__ == '__main__':
if not isinstance(value, dict) and value.find("'") != -1:
extra_env_vars[key] = value.replace("'", '"')
return extra_env_vars
if __name__ == '__main__':
# Parse command line arguments
command_line_parsing_results = parse_args(sys.argv)
input_file = command_line_parsing_results['input_file']
# Verify the input file exists
if not os.path.exists(input_file):
print(f'{input_file} file not found. Please create/specify a valid {input_file} (should be based on the input.example.json file).')
exit(1)
# Load the input JSON file
# Note, we ASSUME the JSON file is valid JSON (we have no error handling for invalid JSON here)
with open(input_file, 'r') as f:
data = json.load(f)
# Extract Helm chart information from input JSON data
chart_data = get_chart_info(data)
# Extract image information from input JSON data
image_data = get_image_info(data)
# Extract ingress information from input JSON data
ingress_data = get_ingress_info(data)
ingress = Ingress(ingress_data['hostname'])
service = Service()
templates = [ingress, service]
dependencies = []
uses_db, db_objs = get_db_info(data)
uses_secrets_vault, vault_objs = get_vault_info(data)
uses_nosql, nosql_objs = get_nosql_info(data)
uses_cache, cache_objs = get_cache_info(data)
uses_oauth, oauth_objs = get_oauth_info(data)
uses_logging, logging_objs = get_logging_info(data)
third_party_services = get_third_party_services_info(data)
extra_env_vars = get_extra_env_vars(data)
if uses_db:
templates.append(db_objs['db'])
dependencies.append(db_objs['db_dependency'])
if uses_secrets_vault:
templates.append(vault_objs['vault'])
dependencies.append(vault_objs['vault_dependency'])
if uses_nosql:
templates.append(nosql_objs['nosql'])
dependencies.append(nosql_objs['nosql_dependency'])
if uses_cache:
templates.append(cache_objs['cache'])
dependencies.append(cache_objs['cache_dependency'])
if uses_oauth:
templates.append(oauth_objs['oauth'])
if uses_logging:
templates.append(logging_objs['logging'])
dependencies.append(logging_objs['logging_dependency'])
if len(third_party_services) > 0:
for third_party_service in third_party_services:
templates.append(third_party_service)
deployment = Deployment(
image_data['image_repository'],
image_data['image_tag'],
port=data['port'] if 'port' in data else 8080,
image_pull_policy=image_data['image_pull_policy'],
uses_oauth=uses_oauth,
uses_db=uses_db,
uses_secrets_vault=uses_secrets_vault,
nosql=nosql_objs['nosql'],
uses_cache=uses_cache,
third_party_services=third_party_services,
**extra_env_vars
)
deployment = Deployment(image_repository, image_pull_policy=image_pull_policy, uses_db=uses_db, uses_secrets_vault=uses_secrets_vault, nosql=nosql, uses_cache=uses_cache, third_party_services=third_party_services, **extra_env_vars)
templates.append(deployment)
#templates = [ingress, service, db, vault, mongo, redis, oauth, deployment, stripe, openai]
helmChart = HelmChart(chart_name, chart_description, maintainers, chart_homepage, sources, app_version, chart_version, api_version, *templates)
helmChart = HelmChart(
chart_data['chart_name'],
chart_data['chart_version'],
chart_data['api_version'],
chart_data['app_version'],
chart_data['chart_description'],
chart_data['maintainers'],
chart_data['chart_homepage'],
chart_data['sources'],
dependencies,
*templates
)
helmChart.create_templates_folder()
helmChart.write_yaml()
helmChart.write_values_yaml()
helmChart.write_filled_in_values_yaml()
helmChart.write_helmignore()
try:

View file

@ -1,6 +1,6 @@
{
"chart": {
"apiVersion": "v1",
"apiVersion": "v2",
"appVersion": "1.0.0",
"description": "A Helm chart for deploying <service name>.",
"homepage": "<Helm Chart Homepage>",
@ -35,13 +35,20 @@
"tag": "<Vault Image Tag>"
},
"hostname": "<DNS Name where the vault will be hosted>",
"storageClass": "<Storage Class Name>"
"storageClass": "<Storage Class Name>",
"policyCapabilities": [
"create",
"read",
"update",
"delete",
"list"
]
},
"nosql": {
"dbName": "<NoSQL Database Name>",
"user": "<NoSQL Database User>",
"password": "<NoSQL Database Password>",
"tables": {
"groupings": {
"<Table Environment Variable Name>": {
"name": "<Table Intermediate Name (used in Helm template files)>",
"value": "<Actual Table Name>"
@ -51,6 +58,10 @@
"cache": {
"password": "<Cache Password>"
},
"logging": {
"username": "<Log Aggregator Username>",
"password": "<Log Aggregator Password>"
},
"oauth": {
"baseAppUrl": "<Base URL of the App>",
"appAbbreviation": "<App Abbreviation>",
@ -61,14 +72,8 @@
"clientSecret": ""
},
"thirdPartyServices": {
"openai": {
"apiKey": "<OpenAI API Key>"
},
"stripe": {
"publicKey": "<Stripe Public Key>",
"secretKey": "<Stripe Secret Key>",
"testPublicKey": "<Stripe Test Public Key>",
"testSecretKey": "<Stripe Test Secret Key>"
"<service-name>": {
"<key>": "<value>"
}
},
"extraEnvVars": {

View file

@ -39,7 +39,7 @@ class Cache (Template):
f.write(' ' + '{{- else }}' + '\n')
f.write(' ' + 'hostname: {{ .Values.cache.hostname }}' + '\n')
f.write(' ' + '{{- end }}' + '\n')
f.write(' ' + 'port: {{ .Values.cache.port }}' + '\n')
f.write(' ' + 'port: {{ .Values.cache.port | quote }}' + '\n')
# Create the credentials secret file
with open('templates/cache-credentials-secret.yaml', 'w') as f:

View file

@ -49,94 +49,100 @@ class Database (Template):
self.instance_id = instance_id
def write(self):
# ===========================================================
# DEPRECATED FILES - USING HELM DEPENDENCY (SUBCHART) INSTEAD
# ===========================================================
pass
# Config Map file for use within the Postgres Controller namespace
# This is required by the operator to function properly
with open('templates/db-credentials-config-map-postgres-controller.yaml', 'w') as f:
f.write('{{- if and (eq .Values.database.type "postgres") (.Values.database.create) -}}' + '\n')
f.write('apiVersion: v1' + '\n')
f.write('kind: ConfigMap' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n')
f.write(' ' + 'namespace: postgres-controller' + '\n')
f.write('data:' + '\n')
f.write(' ' + 'db-host: {{ .Values.database.host }}' + '\n')
f.write(' ' + 'db-name: {{ .Values.database.name }}' + '\n')
f.write(' ' + 'db-user: {{ .Values.database.user }}' + '\n')
f.write(' ' + '{{- if .Values.database.port }}' + '\n')
f.write(' ' + 'db-port: {{ .Values.database.port | quote }}' + '\n')
f.write(' ' + '{{- else }}' + '\n')
f.write(' ' + 'db-port: "5432"' + '\n')
f.write(' ' + '{{- end }}' + '\n')
f.write('{{- end -}}' + '\n')
#with open('templates/db-credentials-config-map-postgres-controller.yaml', 'w') as f:
# f.write('{{- if and (eq .Values.database.type "postgres") (.Values.database.create) -}}' + '\n')
# f.write('apiVersion: v1' + '\n')
# f.write('kind: ConfigMap' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n')
# f.write(' ' + 'namespace: postgres-controller' + '\n')
# f.write('data:' + '\n')
# f.write(' ' + 'db-host: {{ .Values.database.host }}' + '\n')
# f.write(' ' + 'db-name: {{ .Values.database.name }}' + '\n')
# f.write(' ' + 'db-user: {{ .Values.database.user }}' + '\n')
# f.write(' ' + '{{- if .Values.database.port }}' + '\n')
# f.write(' ' + 'db-port: {{ .Values.database.port | quote }}' + '\n')
# f.write(' ' + '{{- else }}' + '\n')
# f.write(' ' + 'db-port: "5432"' + '\n')
# f.write(' ' + '{{- end }}' + '\n')
# f.write('{{- end -}}' + '\n')
# Config Map file in the same namespace as the app
with open('templates/db-credentials-config-map.yaml', 'w') as f:
f.write('apiVersion: v1' + '\n')
f.write('kind: ConfigMap' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n')
f.write('data:' + '\n')
f.write(' ' + 'db-host: {{ .Values.database.host }}' + '\n')
f.write(' ' + 'db-name: {{ .Values.database.name }}' + '\n')
f.write(' ' + 'db-user: {{ .Values.database.user }}' + '\n')
f.write(' ' + '{{- if .Values.database.port }}' + '\n')
f.write(' ' + 'db-port: {{ .Values.database.port | quote }}' + '\n')
f.write(' ' + '{{- else }}' + '\n')
f.write(' ' + 'db-port: "5432"' + '\n')
f.write(' ' + '{{- end }}' + '\n')
#with open('templates/db-credentials-config-map.yaml', 'w') as f:
# f.write('apiVersion: v1' + '\n')
# f.write('kind: ConfigMap' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n')
# f.write('data:' + '\n')
# f.write(' ' + 'db-host: {{ .Values.database.host }}' + '\n')
# f.write(' ' + 'db-name: {{ .Values.database.name }}' + '\n')
# f.write(' ' + 'db-user: {{ .Values.database.user }}' + '\n')
# f.write(' ' + '{{- if .Values.database.port }}' + '\n')
# f.write(' ' + 'db-port: {{ .Values.database.port | quote }}' + '\n')
# f.write(' ' + '{{- else }}' + '\n')
# f.write(' ' + 'db-port: "5432"' + '\n')
# f.write(' ' + '{{- end }}' + '\n')
# Secret file for the password to access the database for use within the Postgres Controller namespace
# This is required by the operator to function properly
with open('templates/db-password-secret-postgres-controller.yaml', 'w') as f:
f.write('{{- if and (eq .Values.database.type "postgres") (.Values.database.create) -}}' + '\n')
f.write('apiVersion: v1' + '\n')
f.write('kind: Secret' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-db-password' + '\n')
f.write(' ' + 'namespace: postgres-controller' + '\n')
f.write('type: Opaque' + '\n')
f.write('data:' + '\n')
f.write(' ' + 'password: {{ .Values.database.password | b64enc }}' + '\n')
f.write('{{- end -}}' + '\n')
#with open('templates/db-password-secret-postgres-controller.yaml', 'w') as f:
# f.write('{{- if and (eq .Values.database.type "postgres") (.Values.database.create) -}}' + '\n')
# f.write('apiVersion: v1' + '\n')
# f.write('kind: Secret' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: {{ .Release.Name }}-db-password' + '\n')
# f.write(' ' + 'namespace: postgres-controller' + '\n')
# f.write('type: Opaque' + '\n')
# f.write('data:' + '\n')
# f.write(' ' + 'password: {{ .Values.database.password | b64enc }}' + '\n')
# f.write('{{- end -}}' + '\n')
# Secret file for the password to access the database in the same namespace as the app
with open('templates/db-password-secret.yaml', 'w') as f:
f.write('apiVersion: v1' + '\n')
f.write('kind: Secret' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-db-password' + '\n')
f.write('type: Opaque' + '\n')
f.write('data:' + '\n')
f.write(' ' + 'password: {{ .Values.database.password | b64enc }}' + '\n')
#with open('templates/db-password-secret.yaml', 'w') as f:
# f.write('apiVersion: v1' + '\n')
# f.write('kind: Secret' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: {{ .Release.Name }}-db-password' + '\n')
# f.write('type: Opaque' + '\n')
# f.write('data:' + '\n')
# f.write(' ' + 'password: {{ .Values.database.password | b64enc }}' + '\n')
# Custom Resource Definition (CRD) file to create the database using the operator
with open('templates/database.yaml', 'w') as f:
f.write('{{- if and (eq .Values.database.type "postgres") (.Values.database.create) -}}' + '\n')
f.write('apiVersion: postgresql.org/v1' + '\n')
f.write('kind: PostgresDatabase' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-db' + '\n')
f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
f.write('spec:' + '\n')
f.write(' ' + 'dbName:' + '\n')
f.write(' ' + ' ' + 'envFrom:' + '\n')
f.write(' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: {{ .Release.Name }}-db-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'namespace: postgres-controller' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-name' + '\n')
f.write(' ' + 'dbRoleName:' + '\n')
f.write(' ' + ' ' + 'envFrom:' + '\n')
f.write(' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: {{ .Release.Name }}-db-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'namespace: postgres-controller' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-user' + '\n')
f.write(' ' + 'dbRolePassword:' + '\n')
f.write(' ' + ' ' + 'envFrom:' + '\n')
f.write(' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: {{ .Release.Name }}-db-password' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'namespace: postgres-controller' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'key: password' + '\n')
f.write('{{- if .Values.database.instance_id }}' + '\n')
f.write(' ' + 'dbInstanceId: {{ .Values.database.instance_id }}' + '\n')
f.write('{{- end }}' + '\n')
f.write('{{- end -}}' + '\n')
#with open('templates/database.yaml', 'w') as f:
# f.write('{{- if and (eq .Values.database.type "postgres") (.Values.database.create) -}}' + '\n')
# f.write('apiVersion: postgresql.org/v1' + '\n')
# f.write('kind: PostgresDatabase' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: {{ .Release.Name }}-db' + '\n')
# f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
# f.write('spec:' + '\n')
# f.write(' ' + 'dbName:' + '\n')
# f.write(' ' + ' ' + 'envFrom:' + '\n')
# f.write(' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + '- name: {{ .Release.Name }}-db-credentials' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'namespace: postgres-controller' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-name' + '\n')
# f.write(' ' + 'dbRoleName:' + '\n')
# f.write(' ' + ' ' + 'envFrom:' + '\n')
# f.write(' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + '- name: {{ .Release.Name }}-db-credentials' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'namespace: postgres-controller' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-user' + '\n')
# f.write(' ' + 'dbRolePassword:' + '\n')
# f.write(' ' + ' ' + 'envFrom:' + '\n')
# f.write(' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + '- name: {{ .Release.Name }}-db-password' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'namespace: postgres-controller' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'key: password' + '\n')
# f.write('{{- if .Values.database.instance_id }}' + '\n')
# f.write(' ' + 'dbInstanceId: {{ .Values.database.instance_id }}' + '\n')
# f.write('{{- end }}' + '\n')
# f.write('{{- end -}}' + '\n')

7
src/Dependency.py Normal file
View file

@ -0,0 +1,7 @@
class Dependency:
def __init__(self, name: str, version: str, repository: str, alias: str = None, conditional_var: str = None):
self.name = name
self.version = version
self.repository = repository
self.alias = alias
self.conditional_var = conditional_var

View file

@ -3,7 +3,7 @@ from .NoSQL import NoSQL
from .ThirdPartyService import ThirdPartyService
class Deployment (Template):
def __init__(self, image_repository: str, image_tag: str = 'v1.0.0', image_pull_policy: str = 'IfNotPresent', replica_count: int = 1, port: int = 8080, env: str = 'production', uses_oauth: bool = True, uses_db: bool = False, uses_secrets_vault: bool = False, nosql: NoSQL | None = None, uses_cache: bool = False, third_party_services: list[ThirdPartyService] = [], **extra_env_vars: dict[str, str | dict[str, str]]):
def __init__(self, image_repository: str, image_tag: str = 'v1.0.0', image_pull_policy: str = 'IfNotPresent', replica_count: int = 1, port: int = 8080, env: str = 'production', resources: dict[str, any] = { 'requests': { 'cpu': '200m', 'memory': '512Mi', 'ephemeralStorage': '50Mi' }, 'limits': { 'cpu': '1000m', 'memory': '512Mi', 'ephemeralStorage': '1Gi' } }, uses_oauth: bool = True, uses_db: bool = False, uses_secrets_vault: bool = False, nosql: NoSQL | None = None, uses_cache: bool = False, third_party_services: list[ThirdPartyService] = [], **extra_env_vars: dict[str, str | dict[str, str]]):
"""A class for creating a/some template(s) related to the Deployment for the app.
Args:
@ -13,6 +13,7 @@ class Deployment (Template):
replica_count (int, Optional): The number of replicas of the app to be running. Default 1
port (int, Optional): The port the app will be running on. Default 8080
env (str, Optional): The environment the app will be running in. Default 'production'
resources (dict[str, any], Optional): The resources to be allocated to the Deployment. Default { 'requests': { 'cpu': '200m', 'memory': '512Mi', 'ephemeralStorage': '50Mi' }, 'limits': { 'cpu': '1000m', 'memory': '512Mi', 'ephemeralStorage': '1Gi' } }
uses_oauth (bool, Optional): Whether or not OAuth is to be used. Determines if OAuth related environment variables need to be set on the Deployment. Default True
uses_db (bool, Optional): Whether or not a database is to be used. Determines if database related environment variables need to be set on the Deployment. Default False
uses_secrets_vault (bool, Optional): Whether or not a secrets vault is to be used. Determines if secrets vault related environment variables need to be set on the Deployment. Default False
@ -30,6 +31,7 @@ class Deployment (Template):
self.replica_count = replica_count
self.env = env
self.port = port
self.resources = resources
self.uses_oauth = uses_oauth
self.uses_db = uses_db
self.uses_secrets_vault = uses_secrets_vault
@ -129,32 +131,35 @@ class Deployment (Template):
output = ''
output += ' ' + ' ' + ' ' + ' ' + '# OAuth Implementation Stuff' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: BASE_APP_URL' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: base-app-url' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: APP_ABBRV' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: app-abbreviation' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: APP_NAME' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: app-name' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: SERVICE_NAME' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: service-name' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: DEV_PORT' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: dev-port' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- /* This injects the YAML defined in the `_oauth.tpl` file */ -}}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- include "oauth.envVars" . | nindent 8 }}' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '# OAuth Implementation Stuff' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: BASE_APP_URL' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: base-app-url' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: APP_ABBRV' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: app-abbreviation' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: APP_NAME' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: app-name' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: SERVICE_NAME' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: service-name' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: DEV_PORT' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: dev-port' + '\n'
return output
@ -163,32 +168,34 @@ class Deployment (Template):
output = ''
output += ' ' + ' ' + ' ' + ' ' + '# Database credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: DB_HOST' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-host' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: DB_NAME' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-name' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: DB_PASSWORD' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-password' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: password' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: DB_PORT' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-port' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: DB_USER' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-user' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- include "db.envVars" . | nindent 8 }}' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '# Database credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: DB_HOST' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-host' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: DB_NAME' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-name' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: DB_PASSWORD' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-password' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: password' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: DB_PORT' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-port' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: DB_USER' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-user' + '\n'
return output
@ -197,32 +204,35 @@ class Deployment (Template):
output = ''
output += ' ' + ' ' + ' ' + ' ' + '# NoSQL Credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- if eq .Values.nosql.type "mongodb" }}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: STORAGE_ACCOUNT_CONNECTION_STRING' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-mongo-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: connection-string' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- else if eq .Values.nosql.type "azure" }}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: STORAGE_ACCOUNT_KEY' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-azure-tables-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: key' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: STORAGE_ACCOUNT_NAME' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-azure-tables-config' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: name' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- end }}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '# NoSQL Table Names' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- include "nosql.envVars" . | nindent 8 }}' + '\n'
for key, value in self.nosql.tables.items():
#output += ' ' + ' ' + ' ' + ' ' + '# NoSQL Credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '{{- if eq .Values.nosql.type "mongodb" }}' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: STORAGE_ACCOUNT_CONNECTION_STRING' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-mongo-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: connection-string' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '{{- else if eq .Values.nosql.type "azure" }}' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: STORAGE_ACCOUNT_KEY' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-azure-tables-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: key' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: STORAGE_ACCOUNT_NAME' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-azure-tables-config' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: name' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '{{- end }}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '# NoSQL Grouping Names' + '\n'
for key, value in self.nosql.groupings.items():
output += ' ' + ' ' + ' ' + ' ' + f'- name: {key.upper()}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-storage-tables' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-nosql-grouping-config' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + f'key: {value["name"]}' + '\n'
return output
@ -232,8 +242,8 @@ class Deployment (Template):
output = ''
output += ' ' + ' ' + ' ' + ' ' + '# -- Secrets Vault (Hashicorp Vault OR Azure Key Vault) --' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- if .Values.vault.enabled }}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '# -- Secrets Vault (Hashicorp Vault OR Azure Key Vault) --' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- if eq .Values.vault.type "azure" }}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: KEYVAULT_CLIENT_ID' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
@ -276,22 +286,24 @@ class Deployment (Template):
output = ''
output += ' ' + ' ' + ' ' + ' ' + '# Caching Server Variables' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: CACHE_HOSTNAME' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Relese.name }}-cache-configmap' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: hostname' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: CACHE_PORT' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-cache-configmap' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: port' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '- name: CACHE_PASSWORD' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-cache-credentials' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: password' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- include "cache.envVars" . | nindent 8 }}' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '# Caching Server Variables' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: CACHE_HOSTNAME' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-cache-configmap' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: hostname' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: CACHE_PORT' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-cache-configmap' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: port' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + '- name: CACHE_PASSWORD' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-cache-credentials' + '\n'
#output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: password' + '\n'
return output
@ -304,12 +316,15 @@ class Deployment (Template):
for third_party in self.third_party_services:
output += ' ' + ' ' + ' ' + ' ' + '{{- if .Values.thirdParty.' + third_party.name + '.enabled }}' + '\n'
for var in third_party.vars:
output += ' ' + ' ' + ' ' + ' ' + '- name: ' + third_party.name.upper() + '_' + var.upper() + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n'
output += ' ' + ' ' ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-' + third_party.name + '-secret' + '\n'
output += ' ' + ' ' ' ' + ' ' + ' ' + ' ' + ' ' + f'key: {var.replace("_", "-")}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- /* This injects the YAML defined in the `_thirdParty.tpl` file */ -}}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{ include "' + third_party.name.lower() + '.envVars" . | nindent 8 }}' + '\n'
#for var in third_party.vars:
# output += ' ' + ' ' + ' ' + ' ' + '- name: ' + third_party.name.upper() + '_' + var.upper() + '\n'
# output += ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n'
# output += ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n'
# output += ' ' + ' ' ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-' + third_party.name + '-secret' + '\n'
# output += ' ' + ' ' ' ' + ' ' + ' ' + ' ' + ' ' + f'key: {var.replace("_", "-")}' + '\n'
output += ' ' + ' ' + ' ' + ' ' + '{{- end }}' + '\n'
@ -337,15 +352,15 @@ class Deployment (Template):
f.write(' ' + ' ' + 'spec:' + '\n')
f.write(' ' + ' ' + ' ' + 'containers:' + '\n')
f.write(' ' + ' ' + ' ' + '- name: {{ .Release.Name }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'image: {{ .Values.image.repository }}:{{ .Values.image.tag }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'imagePullPolicy: {{ .Values.image.pullPolicy }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'image: {{ .Values.app.image.repository }}:{{ .Values.app.image.tag }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'imagePullPolicy: {{ .Values.app.image.pullPolicy }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'ports:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- containerPort: {{ .Values.container.port }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- containerPort: {{ .Values.app.container.port }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'env:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: NODE_ENV' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'value: {{ .Values.container.env }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'value: {{ .Values.app.container.env }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: PORT' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'value: "{{ .Values.container.port }}"' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'value: "{{ .Values.app.container.port }}"' + '\n')
# Add extra environment variables
f.write(self.create_extra_env_vars_deployment_env_vars())
@ -368,19 +383,115 @@ class Deployment (Template):
if len(self.third_party_services) > 0:
f.write(self.create_third_party_services_deployment_env_vars())
f.write(' ' + ' ' + ' ' + ' ' + '{{- if .Values.loggingSidecar.enabled }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' +'{{- include "logging.envVars" . | nindent 8 }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' +'{{- end }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '{{- if ne .Values.app.restoreFromBackup "" }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '# Due to subtleties related to how the entrypoint scripts detects how/when to proceed' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '# This environment variable indicates if the entrypoint should wait for a restore to complete' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: RESTORE_FROM_BACKUP' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'value: {{ .Values.app.restoreFromBackup | quote }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '{{- end }}' + '\n')
# Because of the way we implement Hashicorp Vault we need to mount the role_vars shared volume
# This is because the Vault container populates this shared volume with the app credentials.
# It's done this way because we don't know the credentials needed to access the vault at start time (because their generated by the Vault container)
# So, we need a mechanism to get these credentials in relatively real-time once they've been generated
if self.uses_secrets_vault:
f.write(' ' + ' ' + ' ' + ' ' + '{{- if .Values.vault.create.enabled }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'volumeMounts:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: role-vars' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'mountPath: /role_vars' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'readOnly: true' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '{{- end }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'resources:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' +' ' + 'requests:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' +' ' + ' ' + 'cpu: {{ .Values.app.resources.requests.cpu }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' +' ' + ' ' + 'memory: {{ .Values.app.resources.requests.memory }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' +' ' + ' ' + 'ephemeral-storage: {{ .Values.app.resources.requests.ephemeralStorage }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' +' ' + 'limits:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' +' ' + ' ' + 'cpu: {{ .Values.app.resources.limits.cpu }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' +' ' + ' ' + 'memory: {{ .Values.app.resources.limits.memory }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' +' ' + ' ' + 'ephemeral-storage: {{ .Values.app.resources.limits.ephemeralStorage }}' + '\n')
f.write(' ' + ' ' + ' ' + '{{- if .Values.loggingSidecar.enabled }}' + '\n')
f.write(' ' + ' ' + ' ' + '# Logging sidecar for sending logs to a log aggregator' + '\n')
f.write(' ' + ' ' + ' ' + '{{ include "logging.sidecar" . | nindent 6 }}' + '\n')
f.write(' ' + ' ' + ' ' + '{{- end }}' + '\n')
f.write(' ' + ' ' + ' ' + '{{- if .Values.backupSidecar.enabled }}' + '\n')
f.write(' ' + ' ' + ' ' + '# Backup sidecar for backing up service data' + '\n')
f.write(' ' + ' ' + ' ' + '{{- /* This injects the YAML defined in the `_sidecar.tpl` file */ -}}' + '\n')
f.write(' ' + ' ' + ' ' + '{{ include "backupSidecar" . | nindent 6 }}' + '\n')
f.write(' ' + ' ' + ' ' + '{{- end }}' + '\n')
if self.uses_secrets_vault:
f.write(' ' + ' ' + ' ' + 'volumes:' + '\n')
f.write(' ' + ' ' + ' ' + '- name: role-vars' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'persistentVolumeClaim:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'claimName: {{ .Release.Name }}-vault-role-vars' + '\n')
f.write(' ' + ' ' + ' ' + '{{- if .Values.vault.create.snapshotServer.enabled }}' + '\n')
f.write(' ' + ' ' + ' ' + '- name: creds' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'persistentVolumeClaim:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'claimName: {{ .Release.Name }}-vault-creds' + '\n')
f.write(' ' + ' ' + ' ' + '{{- end }}' + '\n')
def write_sidecars_template_functions_file(self):
with open('templates/_sidecars.tpl', 'w') as f:
f.write('{{- define "backupSidecar" -}}' + '\n')
f.write('- name: {{ .Values.backupSidecar.name }}' + '\n')
f.write(' ' + 'image: {{ .Values.backupSidecar.image.repository }}:{{ .Values.backupSidecar.image.tag }}' + '\n')
f.write(' ' + 'imagePullPolicy: {{ .Values.backupSidecar.image.pullPolicy }}' + '\n')
f.write(' ' + 'ports:' + '\n')
f.write(' ' + '- containerPort: {{ .Values.backupSidecar.port }}' + '\n')
f.write(' ' + 'env:' + '\n')
f.write(' ' + '# Release name (used to identify the service/release the backups came from in remote storage)' + '\n')
f.write(' ' + '- name: RELEASE_NAME' + '\n')
f.write(' ' + ' ' + 'value: {{ .Release.Name }}' + '\n')
f.write(' ' + '{{- include "db.envVars" . | nindent 2 -}}' + '\n')
f.write(' ' + '{{- if .Values.vault.create.snapshotServer.enabled }}' + '\n')
f.write(' ' + '- name: VAULT_NAME' + '\n')
f.write(' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-vault-secret' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'key: vault-name' + '\n')
f.write(' ' + '- name: VAULT_SNAPSHOT_SERVER_PORT' + '\n')
f.write(' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-vault-snapshot-config' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'key: port' + '\n')
f.write(' ' + '{{- end }}' + '\n')
f.write(' ' + '{{- include "nosql.envVars" . | nindent 2 }}' + '\n')
f.write(' ' + '# Redis is used for BullMQ, which is how we schedule backups' + '\n')
f.write(' ' + '# We use this instead of, for instance cron jobs, as it lets us deal with failures' + '\n')
f.write(' ' + '{{- include "cache.envVars" . | nindent 2 }}' + '\n')
f.write(' ' + 'resources:' + '\n')
f.write(' ' + ' ' + 'requests:' + '\n')
f.write(' ' + ' ' + ' ' + 'cpu: {{ .Values.backupSidecar.resources.requests.cpu }}' + '\n')
f.write(' ' + ' ' + ' ' + 'memory: {{ .Values.backupSidecar.resources.requests.memory }}' + '\n')
f.write(' ' + ' ' + ' ' + 'ephemeral-storage: {{ .Values.backupSidecar.resources.requests.ephemeralStorage }}' + '\n')
f.write(' ' + ' ' + 'limits:' + '\n')
f.write(' ' + ' ' + ' ' + 'cpu: {{ .Values.backupSidecar.resources.limits.cpu }}' + '\n')
f.write(' ' + ' ' + ' ' + 'memory: {{ .Values.backupSidecar.resources.limits.memory }}' + '\n')
f.write(' ' + ' ' + ' ' + 'ephemeral-storage: {{ .Values.backupSidecar.resources.limits.ephemeralStorage }}' + '\n')
f.write(' ' + '{{- if .Values.vault.create.snapshotServer.enabled }}' + '\n')
f.write(' ' + 'volumeMounts:' + '\n')
f.write(' ' + '# Mount for a shared volume for Vault credentials' + '\n')
f.write(' ' + '# This is separate from the app\'s `role vars` volume because it includes other credentials' + '\n')
f.write(' ' + '# In particular, the unseal keys which we require when/if we restore from the backup' + '\n')
f.write(' ' + '# This volume is also read-only where the `role-vars` is read-write (see description below for why)' + '\n')
f.write(' ' + '- name: creds' + '\n')
f.write(' ' + ' ' + 'mountPath: /vault-creds' + '\n')
f.write(' ' + ' ' + 'readOnly: true' + '\n')
f.write(' ' + '# Mount for a shared volume for the Vault\'s role variables for the app' + '\n')
f.write(' ' + '# This is required by the backup sidecar because if a restart of the app occurs AFTER a vault has been reset (ex. vault using a different container instance),' + '\n')
f.write(' ' + '# despite the vault data being restored the app would receive incorrect credentials (because this is ONLY written during setup of the vault)' + '\n')
f.write(' ' + '# The backup sidecar mitigates this by doing it\'s own write (to overwrite) once it\'s done a restore' + '\n')
f.write(' ' + '- name: role-vars' + '\n')
f.write(' ' + ' ' + 'mountPath: /role_vars' + '\n')
f.write(' ' + '{{- end }}' + '\n')
f.write('{{- end -}}' + '\n')
def write(self):
"""Writes files related to the Deployment of the app."""
@ -388,5 +499,8 @@ class Deployment (Template):
# Create any needed secrets or configmaps for the extra environment variables
self.write_extra_env_vars_files()
# Create the sidecar template functions file
self.write_sidecars_template_functions_file()
# Create the Deployment file
self.write_deployment_file()

View file

@ -1,15 +1,15 @@
from .SecretsVault import SecretsVault
class HashicorpVault(SecretsVault):
def __init__(self, create: bool = True, image: dict[str, str] | None = None, hostname: str | None = None, port: int = 8200, storage_class: str | None = None, storage_size: str = '512Mi'):
def __init__(self, create: bool = True, image: dict[str, str] | None = None, hostname: str | None = None, port: int = 8200, snapshot_server: bool = True, policy_capabilities: list[str] = ['read', 'create', 'update']):
super().__init__('hashicorp')
self.create = create
self.image = image
self.hostname = hostname
self.port = port
self.storage_class = storage_class
self.storage_size = storage_size
self.snapshot_server = snapshot_server
self.policy_capabilities = policy_capabilities
def write_ingress(self):
with open('templates/vault-ingress.yaml', 'w') as f:
@ -99,6 +99,8 @@ class HashicorpVault(SecretsVault):
f.write(' ' + ' ' + ' ' + ' ' + 'env:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: VAULT_ADDR' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'value: http://0.0.0.0:8200' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: POLICY_CAPABILITIES' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'value: {{ .Values.vault.create.policyCapabilities | join "," }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: ROLE_ID_SECRET_NAME' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'value: VAULT_ROLE_ID' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: SECRET_ID_SECRET_NAME' + '\n')
@ -154,8 +156,14 @@ class HashicorpVault(SecretsVault):
f.write('{{- end -}}')
def write(self):
self.write_ingress()
self.write_service()
self.write_role_vars_persistent_volume_claim()
self.write_deployment()
self.write_secret()
# ===========================================================
# DEPRECATED FILES - USING HELM DEPENDENCY (SUBCHART) INSTEAD
# ===========================================================
pass
#self.write_ingress()
#self.write_service()
#self.write_role_vars_persistent_volume_claim()
#self.write_deployment()
#self.write_secret()

File diff suppressed because it is too large Load diff

9
src/LoggingSidecar.py Normal file
View file

@ -0,0 +1,9 @@
from .Template import Template
class LoggingSidecar(Template):
def __init__(self, log_aggregator_username: str, log_aggregator_password: str):
self.log_aggregator_username = log_aggregator_username
self.log_aggregator_password = log_aggregator_password
def write(self):
pass

View file

@ -1,8 +1,8 @@
from .NoSQL import NoSQL
class MongoDB (NoSQL):
def __init__(self, db_name: str, user: str, password: str, tables: dict[str, str], create: bool = True, replica_count: int = 3, tls_enabled: bool = False):
super().__init__('mongodb', db_name, tables, create)
def __init__(self, db_name: str, user: str, password: str, collections: dict[str, str], create: bool = True, replica_count: int = 3, tls_enabled: bool = False):
super().__init__('mongodb', db_name, collections, create)
self.user = user
self.password = password
@ -10,105 +10,111 @@ class MongoDB (NoSQL):
self.tls_enabled = tls_enabled
def write(self):
super().write()
# ===========================================================
# DEPRECATED FILES - USING HELM DEPENDENCY (SUBCHART) INSTEAD
# ===========================================================
with open('templates/mongo-service-account-database.yaml', 'w') as f:
f.write('{{- if and (eq .Values.nosql.type "mongodb") (.Values.nosql.create) -}}' + '\n')
f.write('apiVersion: v1' + '\n')
f.write('kind: ServiceAccount' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: mongodb-database' + '\n')
f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
f.write('{{- end -}}')
pass
with open('templates/mongo-role-database.yaml', 'w') as f:
f.write('{{- if and (eq .Values.nosql.type "mongodb") (.Values.nosql.create) -}}' + '\n')
f.write('apiVersion: rbac.authorization.k8s.io/v1' + '\n')
f.write('kind: Role' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: mongodb-database' + '\n')
f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
f.write('rules:' + '\n')
f.write(' ' + '- apiGroups:' + '\n')
f.write(' ' + ' ' + '- ""' + '\n')
f.write(' ' + ' ' + 'resources:' + '\n')
f.write(' ' + ' ' + '- secrets' + '\n')
f.write(' ' + ' ' + 'verbs:' + '\n')
f.write(' ' + ' ' + '- get' + '\n')
f.write(' ' + '- apiGroups:' + '\n')
f.write(' ' + ' ' + '- ""' + '\n')
f.write(' ' + ' ' + 'resources:' + '\n')
f.write(' ' + ' ' + '- pods' + '\n')
f.write(' ' + ' ' + 'verbs:' + '\n')
f.write(' ' + ' ' + '- patch' + '\n')
f.write(' ' + ' ' + '- delete' + '\n')
f.write(' ' + ' ' + '- get' + '\n')
f.write('{{- end -}}')
#super().write()
with open('templates/mongo-role-binding-database.yaml', 'w') as f:
f.write('{{- if and (eq .Values.nosql.type "mongodb") (.Values.nosql.create) -}}' + '\n')
f.write('apiVersion: rbac.authorization.k8s.io/v1' + '\n')
f.write('kind: RoleBinding' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: mongodb-database' + '\n')
f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
f.write('subjects:' + '\n')
f.write('- kind: ServiceAccount' + '\n')
f.write(' ' + 'name: mongodb-database' + '\n')
f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
f.write('roleRef:' + '\n')
f.write(' ' + 'kind: Role' + '\n')
f.write(' ' + 'name: mongodb-database' + '\n')
f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
f.write(' ' + 'apiGroup: rbac.authorization.k8s.io' + '\n')
f.write('{{- end -}}')
#with open('templates/mongo-service-account-database.yaml', 'w') as f:
# f.write('{{- if and (eq .Values.nosql.type "mongodb") (.Values.nosql.create) -}}' + '\n')
# f.write('apiVersion: v1' + '\n')
# f.write('kind: ServiceAccount' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: mongodb-database' + '\n')
# f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
# f.write('{{- end -}}')
with open('templates/mongo-credentials-secret.yaml', 'w') as f:
f.write('{{- if eq .Values.nosql.type "mongodb" -}}' + '\n')
f.write('apiVersion: v1' + '\n')
f.write('kind: Secret' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-mongo-credentials' + '\n')
f.write('type: Opaque' + '\n')
f.write('data:' + '\n')
f.write(' ' + 'user: {{ .Values.nosql.user | b64enc }}' + '\n')
f.write(' ' + 'password: {{ .Values.nosql.password | b64enc }}' + '\n')
f.write(' ' + '{{- if and (.Values.nosql.connectionString) (not .values.nosql.create) }}' + '\n')
f.write(' ' + 'connection-string: {{ .Values.nosql.connectionString | b64enc }}' + '\n')
f.write(' ' + '{{- else if .Values.nosql.create }}' + '\n')
f.write(' ' + 'connection-string: {{ printf "mongodb://%s:%s@%s-mongo-svc.%s.svc.cluster.local:27017/%s?replicaSet=%s-mongo" .Values.nosql.user .Values.nosql.password .Release.Name .Release.Namespace .Values.nosql.name .Release.Name | b64enc }}' + '\n')
f.write(' ' + '{{- end }}' + '\n')
f.write('{{- end -}}')
#with open('templates/mongo-role-database.yaml', 'w') as f:
# f.write('{{- if and (eq .Values.nosql.type "mongodb") (.Values.nosql.create) -}}' + '\n')
# f.write('apiVersion: rbac.authorization.k8s.io/v1' + '\n')
# f.write('kind: Role' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: mongodb-database' + '\n')
# f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
# f.write('rules:' + '\n')
# f.write(' ' + '- apiGroups:' + '\n')
# f.write(' ' + ' ' + '- ""' + '\n')
# f.write(' ' + ' ' + 'resources:' + '\n')
# f.write(' ' + ' ' + '- secrets' + '\n')
# f.write(' ' + ' ' + 'verbs:' + '\n')
# f.write(' ' + ' ' + '- get' + '\n')
# f.write(' ' + '- apiGroups:' + '\n')
# f.write(' ' + ' ' + '- ""' + '\n')
# f.write(' ' + ' ' + 'resources:' + '\n')
# f.write(' ' + ' ' + '- pods' + '\n')
# f.write(' ' + ' ' + 'verbs:' + '\n')
# f.write(' ' + ' ' + '- patch' + '\n')
# f.write(' ' + ' ' + '- delete' + '\n')
# f.write(' ' + ' ' + '- get' + '\n')
# f.write('{{- end -}}')
with open('templates/mongo.yaml', 'w') as f:
f.write('{{- if and (eq .Values.nosql.type "mongodb") (.Values.nosql.create) -}}' + '\n')
f.write('apiVersion: mongodbcommunity.mongodb.com/v1' + '\n')
f.write('kind: MongoDBCommunity' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-mongo' + '\n')
f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
f.write('spec:' + '\n')
f.write(' ' + 'members: {{ .Values.nosql.replicaCount }}' + '\n')
f.write(' ' + 'type: ReplicaSet' + '\n')
f.write(' ' + 'version: 4.4.0' + '\n')
f.write(' ' + 'security:' + '\n')
f.write(' ' + ' ' + 'authentication:' + '\n')
f.write(' ' + ' ' + ' ' + 'ignoreUnknownUsers: true' + '\n')
f.write(' ' + ' ' + ' ' + 'modes:' + '\n')
f.write(' ' + ' ' + ' ' + '- SCRAM' + '\n')
f.write(' ' + ' ' + 'tls:' + '\n')
f.write(' ' + ' ' + ' ' + 'enabled: {{ .Values.nosql.tls.enabled }}' + '\n')
f.write(' ' + 'readinessProbe:' + '\n')
f.write(' ' + ' ' + 'initialDelaySeconds: 30' + '\n')
f.write(' ' + ' ' + 'periodSeconds: 10' + '\n')
f.write(' ' + 'users:' + '\n')
f.write(' ' + ' ' + '- name: {{ .Values.nosql.user }}' + '\n')
f.write(' ' + ' ' + ' ' + 'db: {{ .Values.nosql.name }}' + '\n')
f.write(' ' + ' ' + ' ' + 'passwordSecretRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-mongo-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'key: password' + '\n')
f.write(' ' + ' ' + ' ' + 'roles:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: readWrite' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'db: {{ .Values.nosql.name }}' + '\n')
f.write(' ' + ' ' + ' ' + 'scramCredentialsSecretName: {{ .Release.Name }}-mongo-scram' + '\n')
f.write('{{- end -}}')
#with open('templates/mongo-role-binding-database.yaml', 'w') as f:
# f.write('{{- if and (eq .Values.nosql.type "mongodb") (.Values.nosql.create) -}}' + '\n')
# f.write('apiVersion: rbac.authorization.k8s.io/v1' + '\n')
# f.write('kind: RoleBinding' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: mongodb-database' + '\n')
# f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
# f.write('subjects:' + '\n')
# f.write('- kind: ServiceAccount' + '\n')
# f.write(' ' + 'name: mongodb-database' + '\n')
# f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
# f.write('roleRef:' + '\n')
# f.write(' ' + 'kind: Role' + '\n')
# f.write(' ' + 'name: mongodb-database' + '\n')
# f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
# f.write(' ' + 'apiGroup: rbac.authorization.k8s.io' + '\n')
# f.write('{{- end -}}')
#with open('templates/mongo-credentials-secret.yaml', 'w') as f:
# f.write('{{- if eq .Values.nosql.type "mongodb" -}}' + '\n')
# f.write('apiVersion: v1' + '\n')
# f.write('kind: Secret' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: {{ .Release.Name }}-mongo-credentials' + '\n')
# f.write('type: Opaque' + '\n')
# f.write('data:' + '\n')
# f.write(' ' + 'user: {{ .Values.nosql.user | b64enc }}' + '\n')
# f.write(' ' + 'password: {{ .Values.nosql.password | b64enc }}' + '\n')
# f.write(' ' + '{{- if and (.Values.nosql.connectionString) (not .values.nosql.create) }}' + '\n')
# f.write(' ' + 'connection-string: {{ .Values.nosql.connectionString | b64enc }}' + '\n')
# f.write(' ' + '{{- else if .Values.nosql.create }}' + '\n')
# f.write(' ' + 'connection-string: {{ printf "mongodb://%s:%s@%s-mongo-svc.%s.svc.cluster.local:27017/%s?replicaSet=%s-mongo" .Values.nosql.user .Values.nosql.password .Release.Name .Release.Namespace .Values.nosql.name .Release.Name | b64enc }}' + '\n')
# f.write(' ' + '{{- end }}' + '\n')
# f.write('{{- end -}}')
#with open('templates/mongo.yaml', 'w') as f:
# f.write('{{- if and (eq .Values.nosql.type "mongodb") (.Values.nosql.create) -}}' + '\n')
# f.write('apiVersion: mongodbcommunity.mongodb.com/v1' + '\n')
# f.write('kind: MongoDBCommunity' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: {{ .Release.Name }}-mongo' + '\n')
# f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
# f.write('spec:' + '\n')
# f.write(' ' + 'members: {{ .Values.nosql.replicaCount }}' + '\n')
# f.write(' ' + 'type: ReplicaSet' + '\n')
# f.write(' ' + 'version: 4.4.0' + '\n')
# f.write(' ' + 'security:' + '\n')
# f.write(' ' + ' ' + 'authentication:' + '\n')
# f.write(' ' + ' ' + ' ' + 'ignoreUnknownUsers: true' + '\n')
# f.write(' ' + ' ' + ' ' + 'modes:' + '\n')
# f.write(' ' + ' ' + ' ' + '- SCRAM' + '\n')
# f.write(' ' + ' ' + 'tls:' + '\n')
# f.write(' ' + ' ' + ' ' + 'enabled: {{ .Values.nosql.tls.enabled }}' + '\n')
# f.write(' ' + 'readinessProbe:' + '\n')
# f.write(' ' + ' ' + 'initialDelaySeconds: 30' + '\n')
# f.write(' ' + ' ' + 'periodSeconds: 10' + '\n')
# f.write(' ' + 'users:' + '\n')
# f.write(' ' + ' ' + '- name: {{ .Values.nosql.user }}' + '\n')
# f.write(' ' + ' ' + ' ' + 'db: {{ .Values.nosql.name }}' + '\n')
# f.write(' ' + ' ' + ' ' + 'passwordSecretRef:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-mongo-credentials' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + 'key: password' + '\n')
# f.write(' ' + ' ' + ' ' + 'roles:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + '- name: readWrite' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'db: {{ .Values.nosql.name }}' + '\n')
# f.write(' ' + ' ' + ' ' + 'scramCredentialsSecretName: {{ .Release.Name }}-mongo-scram' + '\n')
# f.write('{{- end -}}')

View file

@ -1,13 +1,13 @@
from .Template import Template
class NoSQL (Template):
def __init__(self, type: str, db_name: str, tables: dict[str, dict[str, str]], create: bool = True):
def __init__(self, type: str, db_name: str, groupings: dict[str, dict[str, str]], create: bool = True):
"""Use of a NoSQL storage system
Args:
type (str): The type of NoSQL storage to use
db_name (str): The name of the NoSQL storage to connect to
tables (dict[str, dict[str, str]]): A dictionary of NoSQL tables/collections
groupings (dict[str, dict[str, str]]): A dictionary of NoSQL groupings (tables/collections)
create (bool, optional): Whether to create the NoSQL resources as part of the Helm deployment. Defaults to True.
"""
@ -15,24 +15,24 @@ class NoSQL (Template):
self.type = type
self.db_name = db_name
self.tables = tables
self.groupings = groupings
self.create = create
def write(self):
with open('templates/storage-tables-config-map.yaml', 'w') as f:
with open('templates/nosql-grouping-config-map.yaml', 'w') as f:
f.write('apiVersion: v1' + '\n')
f.write('kind: ConfigMap' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-storage-tables' +'\n')
f.write(' ' + 'name: {{ .Release.Name }}-nosql-grouping' +'\n')
f.write(' ' + 'labels:' + '\n')
f.write(' ' + ' ' + 'app: {{ .Release.Name }}' + '\n')
f.write('data:' + '\n')
# Loop over tables dictionary and get the values
for value in self.tables.values():
for value in self.groupings.values():
snake_case_name = value['name'].split('-')[0]
for token in value['name'].split('-'):
if token != snake_case_name:
snake_case_name += token.capitalize()
f.write(' ' + value['name'] + ': {{ .Values.tables.' + snake_case_name + ' }}' + '\n')
f.write(' ' + value['name'] + ': {{ .Values.nosql.grouping.' + snake_case_name + ' }}' + '\n')

View file

@ -1,7 +1,7 @@
from .Template import Template
class OAuth (Template):
def __init__(self, base_app_url: str, app_abbreviation: str, app_name: str, service_name: str, dev_port: str):
def __init__(self, base_app_url: str, app_abbreviation: str, app_name: str, service_name: str, dev_port: str, app_reg_contact_email: str):
"""A class for creating a/some template(s) related to OAuth implementation."""
self.base_app_url = base_app_url
@ -9,6 +9,7 @@ class OAuth (Template):
self.app_name = app_name
self.service_name = service_name
self.dev_port = dev_port
self.app_reg_contact_email = app_reg_contact_email
def write(self):
with open('templates/oauth-credentials-config-map.yaml', 'w') as f:
@ -24,3 +25,39 @@ class OAuth (Template):
f.write(' ' + 'app-name: {{ .Values.oauth.appName }}' + '\n')
f.write(' ' + 'service-name: {{ .Values.oauth.serviceName }}' + '\n')
f.write(' ' + 'dev-port: {{ .Values.oauth.devPort | quote }}' + '\n')
f.write(' ' + 'app-reg-contact-email: {{ .Values.oauth.appRegContactEmail }}' + '\n')
with open('templates/_oauth.tpl', 'w') as f:
f.write('{{- define "oauth.envVars" -}}' + '\n')
f.write('# OAuth Implementation Stuff' + '\n')
f.write('- name: BASE_APP_URL' + '\n')
f.write(' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n')
f.write(' ' + ' ' + ' ' + 'key: base-app-url' + '\n')
f.write('- name: APP_ABBRV' + '\n')
f.write(' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n')
f.write(' ' + ' ' + ' ' + 'key: app-abbreviation' + '\n')
f.write('- name: APP_NAME' + '\n')
f.write(' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n')
f.write(' ' + ' ' + ' ' + 'key: app-name' + '\n')
f.write('- name: SERVICE_NAME' + '\n')
f.write(' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n')
f.write(' ' + ' ' + ' ' + 'key: service-name' + '\n')
f.write('- name: DEV_PORT' + '\n')
f.write(' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n')
f.write(' ' + ' ' + ' ' + 'key: dev-port' + '\n')
f.write('- name: APP_REG_CONTACT_EMAIL' + '\n')
f.write(' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n')
f.write(' ' + ' ' + ' ' + 'key: app-reg-contact-email' + '\n')
f.write('{{- end -}}')

View file

@ -23,76 +23,81 @@ class Redis (Cache):
self.image = image
def write(self):
# ===========================================================
# DEPRECATED FILES - USING HELM DEPENDENCY (SUBCHART) INSTEAD
# ===========================================================
pass
# Call parent class's method/function to write the generic cache templates
super().write_generic_cache_templates('redis', '{{ .Release.Name }}-redis')
#super().write_generic_cache_templates('redis', '{{ .Release.Name }}-redis')
# Create the Redis service file
with open('templates/redis-service.yaml', 'w') as f:
f.write('{{- if and (.Values.cache.type "redis") (.Values.cache.create) -}}' + '\n')
f.write('apiVersion: v1' + '\n')
f.write('kind: Service' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-redis' + '\n')
f.write(' ' + 'labels:' + '\n')
f.write(' ' + ' ' + 'app: redis' + '\n')
f.write('spec:' + '\n')
f.write(' ' + 'ports:' + '\n')
f.write(' ' + ' ' + '- port: {{ .Values.cache.port }}' + '\n')
f.write(' ' + ' ' + ' ' + 'targetPort: {{ .Values.cache.port }}' + '\n')
f.write(' ' + 'selector:' + '\n')
f.write(' ' + ' ' + 'app: redis' + '\n')
f.write(' ' + 'type: ClusterIP' + '\n')
f.write('{{- end -}}')
#with open('templates/redis-service.yaml', 'w') as f:
# f.write('{{- if and (eq .Values.cache.type "redis") (.Values.cache.create) -}}' + '\n')
# f.write('apiVersion: v1' + '\n')
# f.write('kind: Service' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: {{ .Release.Name }}-redis' + '\n')
# f.write(' ' + 'labels:' + '\n')
# f.write(' ' + ' ' + 'app: redis' + '\n')
# f.write('spec:' + '\n')
# f.write(' ' + 'ports:' + '\n')
# f.write(' ' + ' ' + '- port: {{ .Values.cache.port }}' + '\n')
# f.write(' ' + ' ' + ' ' + 'targetPort: {{ .Values.cache.port }}' + '\n')
# f.write(' ' + 'selector:' + '\n')
# f.write(' ' + ' ' + 'app: redis' + '\n')
# f.write(' ' + 'type: ClusterIP' + '\n')
# f.write('{{- end -}}')
# Create the Redis deployment file
with open('templates/redis-deployment.yaml', 'w') as f:
f.write('{{- if and (.Values.cache.type "redis") (.Values.cache.create) -}}' + '\n')
f.write('apiVersion: apps/v1' + '\n')
f.write('kind: Deployment' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-redis' + '\n')
f.write(' ' + 'labels:' + '\n')
f.write(' ' + ' ' + 'app: redis' + '\n')
f.write('spec:' + '\n')
f.write(' ' + 'replicas: {{ .Values.cache.replicaCount }}' + '\n')
f.write(' ' + 'selector:' + '\n')
f.write(' ' + ' ' + 'matchLabels:' + '\n')
f.write(' ' + ' ' + ' ' + 'app: redis' + '\n')
f.write(' ' + 'template:' + '\n')
f.write(' ' + ' ' + 'metadata:' + '\n')
f.write(' ' + ' ' + ' ' + 'labels:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'app: redis' + '\n')
f.write(' ' + ' ' + 'spec:' + '\n')
f.write(' ' + ' ' + ' ' + 'containers:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: redis' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'image: {{ .Values.cache.image.repository | default "bitnami/redis" }}:{{ .Values.cache.image.tag | default "7.0.5" }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'ports:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- containerPort: {{ .Values.cache.port }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '{{- if .Values.cache.tls.enabled }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- containerPort: {{ .Values.cache.tls.port }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '{{- end }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'env:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- name: ALLOW_EMPTY_PASSWORD' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'value: "false"' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- name: REDIS_PASSWORD' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-cache-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: password' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- name: REDIS_DISABLE_COMMANDS' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'value: "FLUSHDB,FLUSHALL"' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '# TLS configuration' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '#- name: REDIS_TLS_ENABLED' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '# value: "{{ .Values.cache.tls.enabled }}"' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '#- name: REDIS_TLS_AUTH_CLIENTS' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '# value: "yes"' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '#- name: REDIS_TLS_PORT_NUMBER' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '# value: "{{ .Values.cache.tls.port }}"' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'volumeMounts:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- name: redis-data' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'mountPath: /bitnami/redis' + '\n')
f.write(' ' + ' ' + ' ' + 'volumes:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: redis-data' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'emptyDir: {}' + '\n')
f.write('{{- end -}}')
#with open('templates/redis-deployment.yaml', 'w') as f:
# f.write('{{- if and (eq .Values.cache.type "redis") (.Values.cache.create) -}}' + '\n')
# f.write('apiVersion: apps/v1' + '\n')
# f.write('kind: Deployment' + '\n')
# f.write('metadata:' + '\n')
# f.write(' ' + 'name: {{ .Release.Name }}-redis' + '\n')
# f.write(' ' + 'labels:' + '\n')
# f.write(' ' + ' ' + 'app: redis' + '\n')
# f.write('spec:' + '\n')
# f.write(' ' + 'replicas: {{ .Values.cache.replicaCount }}' + '\n')
# f.write(' ' + 'selector:' + '\n')
# f.write(' ' + ' ' + 'matchLabels:' + '\n')
# f.write(' ' + ' ' + ' ' + 'app: redis' + '\n')
# f.write(' ' + 'template:' + '\n')
# f.write(' ' + ' ' + 'metadata:' + '\n')
# f.write(' ' + ' ' + ' ' + 'labels:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + 'app: redis' + '\n')
# f.write(' ' + ' ' + 'spec:' + '\n')
# f.write(' ' + ' ' + ' ' + 'containers:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + '- name: redis' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'image: {{ .Values.cache.image.repository | default "bitnami/redis" }}:{{ .Values.cache.image.tag | default "7.0.5" }}' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'ports:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- containerPort: {{ .Values.cache.port }}' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '{{- if .Values.cache.tls.enabled }}' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- containerPort: {{ .Values.cache.tls.port }}' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '{{- end }}' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'env:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- name: ALLOW_EMPTY_PASSWORD' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'value: "false"' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- name: REDIS_PASSWORD' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-cache-credentials' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: password' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- name: REDIS_DISABLE_COMMANDS' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'value: "FLUSHDB,FLUSHALL"' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '# TLS configuration' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '#- name: REDIS_TLS_ENABLED' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '# value: "{{ .Values.cache.tls.enabled }}"' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '#- name: REDIS_TLS_AUTH_CLIENTS' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '# value: "yes"' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '#- name: REDIS_TLS_PORT_NUMBER' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '# value: "{{ .Values.cache.tls.port }}"' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'volumeMounts:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '- name: redis-data' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'mountPath: /bitnami/redis' + '\n')
# f.write(' ' + ' ' + ' ' + 'volumes:' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + '- name: redis-data' + '\n')
# f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'emptyDir: {}' + '\n')
# f.write('{{- end -}}')

View file

@ -20,4 +20,4 @@ class Service (Template):
f.write(' ' + 'ports:' + '\n')
f.write(' ' + ' ' + '- protocol: TCP' + '\n')
f.write(' ' + ' ' + ' ' + 'port: 80' + '\n')
f.write(' ' + ' ' + ' ' + 'targetPort: {{ .Values.container.port }}' + '\n')
f.write(' ' + ' ' + ' ' + 'targetPort: {{ .Values.app.container.port }}' + '\n')

View file

@ -14,7 +14,7 @@ class ThirdPartyService (Template):
f.write('apiVersion: v1' + '\n')
f.write('kind: Secret' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-' + self.name + '-secret' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-' + self.name.lower() + '-secret' + '\n')
f.write(' ' + 'labels:' + '\n')
f.write(' ' + ' ' + 'app: {{ .Release.Name }}' + '\n')
f.write('type: Opaque' + '\n')
@ -28,3 +28,15 @@ class ThirdPartyService (Template):
f.write(' ' + key.replace('_', '-') + ': {{ .Values.thirdParty.' + self.name + '.' + snake_case_name + ' | b64enc }}' + '\n')
f.write('{{- end -}}' + '\n')
with open('templates/_thirdParty.tpl', 'a') as f:
f.write('{{- define "' + self.name.lower() + '.envVars" -}}' + '\n')
f.write(f'# {self.name} Environment Variables' + '\n')
for key, value in self.vars.items():
f.write('- name: ' + self.name.upper() + '_' + key.upper() + '\n')
f.write(' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + 'secretKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-' + self.name.lower() + '-secret' + '\n')
f.write(' ' + ' ' + ' ' + 'key: ' + key.replace('_', '-') + '\n')
f.write('{{- end -}}' + '\n')
f.write('\n')

1
test/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
real-values

View file

@ -0,0 +1,97 @@
{
"chart": {
"apiVersion": "v2",
"appVersion": "1.0.0",
"description": "A Helm chart for deploying <service name>.",
"homepage": "<Helm Chart Homepage>",
"maintainers": [
{
"name": "<Author Name>",
"email": "<Author Email>"
}
],
"name": "<Helm Chart Name>",
"sources": [
"<Helm Chart Source>"
],
"version": "1.0.0"
},
"image": {
"repository": "<Registry URL (if applicable)>/<Image Name>",
"pullPolicy": "IfNotPresent"
},
"ingress": {
"hostname": "<DNS Name Where The App Will Be Hosted>"
},
"db": {
"name": "<Database Name>",
"host": "<Database Host>",
"user": "<Database User>",
"password": "<Database Password>"
},
"vault": {
"image": {
"repository": "<Vault Image Repository>",
"tag": "<Vault Image Tag>"
},
"hostname": "<DNS Name where the vault will be hosted>",
"storageClass": "<Storage Class Name>",
"policyCapabilities": [
"create",
"read",
"update",
"delete",
"list"
]
},
"nosql": {
"dbName": "<NoSQL Database Name>",
"user": "<NoSQL Database User>",
"password": "<NoSQL Database Password>",
"groupings": {
"<Table Environment Variable Name>": {
"name": "<Table Intermediate Name (used in Helm template files)>",
"value": "<Actual Table Name>"
}
}
},
"cache": {
"password": "<Cache Password>"
},
"logging": {
"username": "<Log Aggregator Username>",
"password": "<Log Aggregator Password>"
},
"oauth": {
"baseAppUrl": "<Base URL of the App>",
"appAbbreviation": "<App Abbreviation>",
"appName": "<App Name>",
"serviceName": "<Service Name>",
"devPort": "<Dev Port>",
"clientId": "",
"clientSecret": ""
},
"thirdPartyServices": {
"<service-name>": {
"<key>": "<value>"
}
},
"extraEnvVars": {
"<Sensitive Environment Variable Name>": {
"type": "Secret",
"name": "{{ .Release.Name }}-<Sensitive Value Name (ex. private-token, etc...)>",
"key": "<Key Used Within The Secret (ex. token, etc...)>",
"description": "<Description Of The Environment Variable>",
"value": "<Value for the Environment Variable>"
},
"<Configurable Environment Variable Name>": {
"type": "ConfigMap",
"name": "{{ .Release.Name }}-<Configurable Value Name (ex. external-service-host, etc...)>",
"key": "<Key Used Within The ConfigMap (ex. extern-host, etc...)>",
"description": "<Description Of The Environment Variable>",
"value": "<Value for the Environment Variable>"
},
"<Environment Variable Name>": "'<Quoted Value>'"
},
"registry": "<Helm Registry URL to publish to (if applicable)>"
}