Initial Commit

This commit is contained in:
Alan Bridgeman 2025-02-11 12:52:05 -06:00
parent ff52718a85
commit b3b43329cd
22 changed files with 1582 additions and 0 deletions

52
src/Cache.py Normal file
View file

@ -0,0 +1,52 @@
from .Template import Template
class Cache (Template):
def __init__(self, password: str, hostName: str, port: str, create: bool):
"""A class for creating a/some template(s) related to a cache server.
Args:
password (str): The password to access the cache server.
hostName (str): The hostname of the cache server.
port (str): The port of the cache server.
create (bool): Whether or not to create the cache server as part of the Helm Chart deployment.
"""
super().__init__()
self.password = password
self.hostName = hostName
self.port = port
self.create = create
def write_generic_cache_templates(self, type: str, default_hostname: str):
"""Write the generic cache templates to a file.
Args:
type (str): The type of cache server. Relevant if the cache server is to be created as the default hostname will be used if the type matches with whats provided in the `values.yaml` file.
default_hostname (str): The default hostname of the cache server. Used if the cache server is to be created and the type matches with whats provided in the `values.yaml` file.
"""
# Create the configmap file that holds the hostname and port of the cache server
with open('templates/cache-configmap.yaml', 'w') as f:
f.write('apiVersion: v1' + '\n')
f.write('kind: ConfigMap' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-cache-configmap' + '\n')
f.write(' ' + 'namespace: {{ .Release.Namespace }}' + '\n')
f.write('data:' + '\n')
f.write(' ' + '{{- if and (eq .Values.cache.type "' + type + '") (.Values.cache.create) }}' + '\n')
f.write(' ' + f'hostname: {default_hostname}' + '\n')
f.write(' ' + '{{- else }}' + '\n')
f.write(' ' + 'hostname: {{ .Values.cache.hostname }}' + '\n')
f.write(' ' + '{{- end }}' + '\n')
f.write(' ' + 'port: {{ .Values.cache.port }}' + '\n')
# Create the credentials secret file
with open('templates/cache-credentials-secret.yaml', 'w') as f:
f.write('apiVersion: v1' + '\n')
f.write('kind: Secret' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-cache-credentials' + '\n')
f.write('type: Opaque' + '\n')
f.write('data:' + '\n')
f.write(' ' + 'password: {{ .Values.cache.password | b64enc }}' + '\n')

142
src/Database.py Normal file
View file

@ -0,0 +1,142 @@
from .Template import Template
class Database (Template):
def __init__(self, name: str, host: str, user: str, password: str, create: bool = True, port: int = 5432, instance_id: str = ''):
"""A class for creating a/some template(s) related to a database.
Args:
name (str): The name of the database to be used.
host (str): The host that the database is located on.
user (str): The user that is used to access the database.
password (str): The password that is used to access the database.
create (bool, Optional): If set to `True`, the database will be created as part of the deployment. This uses the [`postgres-controller` CRD](https://github.com/AlanBridgeman/postgres-controller) to create the database. Default True
port (int, Optional): The port that the database listens on. Default 5432
instance_id (str, Optional): Allows for distinguishing between multiple database instances/servers. Default ''
"""
# The type of the relational database that is used.
#
# The following table lists the possible values for this field:
#
# | Value | Description |
# | ---------- | ------------------------------------------ |
# | `postgres` | Uses PostgreSQL as the relational database |
#
# Note, for use of `postgres`, it uses a [`postgres-controller` CRD](https://github.com/AlanBridgeman/postgres-controller) to create the database
#
self.type = 'postgres'
# If set to `true`, the database will be created as part of the deployment
# This uses the [`postgres-controller` CRD](https://github.com/AlanBridgeman/postgres-controller) to create the database
self.create = create
# The host that the database is located on
self.host = host
# The name of the database to be used
self.name = name
# The user that is used to access the database
self.user = user
# The password that is used to access the database
self.password = password
# The port that the database listens on
self.port = port
# Allows for distinguishing between multiple database instances/servers
self.instance_id = instance_id
def write(self):
# 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')
# 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')
# 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')
# 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')
# 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')

238
src/Deployment.py Normal file
View file

@ -0,0 +1,238 @@
from .Template import Template
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, 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:
image_repository (str): The repository of the image to be used for the Deployment.
image_tag (str, Optional): The tag of the image to be used for the Deployment. Default 'v1.0.0'
image_pull_policy (str, Optional): The image pull policy to be used for the Deployment. Default 'IfNotPresent'
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'
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
nosql (NoSQL, Optional): The NoSQL template. If set, Determines if NoSQL database related environment variables need to be set on the Deployment. We require the object to get table names to set appropriate environment variables on the Deployment. Default None
uses_cache (bool, Optional): Whether or not a cache server is to be used. Determines if cache related environment variables need to be set on the Deployment. Default False
third_party_services (list[ThirdPartyService], Optional): The third party services to be used. Determines if third party service related environment variables need to be set on the Deployment. Default empty list (`[]`)
extra_env_vars (dict[str, str | dict[str, str]]): Extra environment variables to be set on the Deployment. The key is the name of the environment variable and the value, if it's a string, is the value of the environment variable. If the value is a dictionary, than a file for the value as a secret or configmap is created and referenced by the environment variable.
"""
super().__init__()
self.image_repository = image_repository
self.image_tag = image_tag
self.image_pull_policy = image_pull_policy
self.replica_count = replica_count
self.env = env
self.port = port
self.uses_oauth = uses_oauth
self.uses_db = uses_db
self.nosql = nosql
self.uses_cache = uses_cache
self.third_party_services = third_party_services
self.extra_env_vars = extra_env_vars
def write(self):
"""Write the Deployment template to a file."""
for value in self.extra_env_vars.values():
if isinstance(value, dict):
if value['type'] == 'Secret':
filename = value['name']
if filename.startswith('{{ .Release.Name }}'):
filename = filename.replace('{{ .Release.Name }}-', '')
snake_case_name = filename.split('-')[0]
for token in filename.split('-'):
if token != snake_case_name:
snake_case_name += token.capitalize()
with open(f'templates/{filename}-secret.yaml', 'w') as f:
f.write('apiVersion: v1' + '\n')
f.write('kind: Secret' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + f'name: {value["name"]}' + '\n')
f.write('type: Opaque' + '\n')
f.write('data:' + '\n')
f.write(' ' + f'{value["key"]}: ' + '{{ .Values.' + snake_case_name + ' | b64enc }}' + '\n')
with open(f'templates/deployment.yaml', 'w') as f:
f.write('apiVersion: apps/v1' + '\n')
f.write('kind: Deployment' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}' + '\n')
f.write(' ' + 'labels:' + '\n')
f.write(' ' + ' ' + 'app: {{ .Release.Name }}' + '\n')
f.write('spec:' + '\n')
f.write(' ' + 'replicas: {{ .Values.replicaCount }}' + '\n')
f.write(' ' + 'selector:' + '\n')
f.write(' ' + ' ' + 'matchLabels:' + '\n')
f.write(' ' + ' ' + ' ' + 'app: {{ .Release.Name }}' + '\n')
f.write(' ' + 'template:' + '\n')
f.write(' ' + ' ' + 'metadata:' + '\n')
f.write(' ' + ' ' + ' ' + 'labels:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'app: {{ .Release.Name }}' + '\n')
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(' ' + ' ' + ' ' + ' ' + 'ports:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- containerPort: {{ .Values.container.port }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'env:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: NODE_ENV' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'value: {{ .Values.container.env }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: PORT' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'value: "{{ .Values.container.port }}"' + '\n')
for key, value in self.extra_env_vars.items():
# Check if the value is a dictionary or a string
if isinstance(value, dict):
f.write(' ' + ' ' + ' ' + ' ' + f'- name: {key.upper()}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
if value['type'] == 'Secret':
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
elif value['type'] == 'ConfigMap':
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: ' + value['name'] + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: ' + value['key'] + '\n')
else:
f.write(' ' + ' ' + ' ' + ' ' + f'- name: {key.upper()}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + f'value: {value}' + '\n')
if self.uses_oauth:
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: CLIENT_ID' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: client-id' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: CLIENT_SECRET' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-oauth-client-secret' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: client-secret' + '\n')
if self.uses_db:
f.write(' ' + ' ' + ' ' + ' ' + '# Database credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: DB_HOST' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-host' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: DB_NAME' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-name' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: DB_PASSWORD' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-password' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: password' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: DB_PORT' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-port' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: DB_USER' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-db-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: db-user' + '\n')
if self.nosql is not None:
f.write(' ' + ' ' + ' ' + ' ' + '# NoSQL Credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '{{- if eq .Values.nosql.type "mongodb" }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: STORAGE_ACCOUNT_CONNECTION_STRING' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-mongo-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: connection-string' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '{{- else if eq .Values.nosql.type "azure" }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: STORAGE_ACCOUNT_KEY' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-azure-tables-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: key' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: STORAGE_ACCOUNT_NAME' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-azure-tables-credentials' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: name' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '{{- end }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '# NoSQL Table Names' + '\n')
for key, value in self.nosql.tables.items():
f.write(' ' + ' ' + ' ' + ' ' + f'- name: {key.upper()}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-storage-tables' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + f'key: {value["name"]}' + '\n')
if self.uses_cache:
f.write(' ' + ' ' + ' ' + ' ' + '# Caching Server Variables' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: CACHE_HOSTNAME' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Relese.name }}-cache-configmap' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: hostname' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: CACHE_PORT' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'configMapKeyRef:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-cache-configmap' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: port' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '- name: CACHE_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(' ' + ' ' + ' ' + ' ' + '# Third-Party Integrations' + '\n')
for third_party in self.third_party_services:
f.write(' ' + ' ' + ' ' + ' ' + '{{- if .Values.thirdParty.' + third_party.name + '.enabled }}' + '\n')
for var in third_party.vars:
f.write(' ' + ' ' + ' ' + ' ' + '- name: ' + third_party.name.upper() + '_' + var.upper() + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
f.write(' ' + ' ' ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-' + third_party.name + '-secret' + '\n')
f.write(' ' + ' ' ' ' + ' ' + ' ' + ' ' + ' ' + f'key: {var.replace("_", "-")}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + '{{- end }}' + '\n')
#f.write(' ' + ' ' + ' ' + ' ' + '{{- if .Values.thirdParty.openai.enabled }}' + '\n')
#f.write(' ' + ' ' + ' ' + ' ' + '- name: OPEANAI_API_KEY' + '\n')
#f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'valueFrom:' + '\n')
#f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'secretKeyRef:' + '\n')
#f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}-openai-secret' + '\n')
#f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'key: api-key' + '\n')
#f.write(' ' + ' ' + ' ' + ' ' + '{{- end }}' + '\n')

401
src/HelmChart.py Normal file
View file

@ -0,0 +1,401 @@
import os, subprocess
from .Template import Template
from .Ingress import Ingress
from .Database import Database
from .NoSQL import NoSQL
from .MongoDB import MongoDB
from .Redis import Redis
from .OAuth import OAuth
from .ThirdPartyService import ThirdPartyService
from .Deployment import Deployment
class HelmChart:
def __init__(self, chartName: str, chartDescription: str, maintainers: list[dict[str, str]], chartHomepage: str, sources: list[str], appVersion: str = '1.0.0', chartVersion: str = '1.0.0', apiVersion: str = 'v1', *templates: Template):
"""A class for creating a Helm chart.
Args:
chartName (str): The name of the Helm chart.
chartDescription (str): A description of the Helm chart.
maintainers (list[dict[str, str]]): The maintainers of the Helm chart.
chartHomepage (str): The URL of the Helm chart's home page
sources (list[str]): The sources of the Helm chart.
appVersion (str, Optional): The version of the application that the Helm chart is deploying. Default '1.0.0'
chartVersion (str, Optional): The version of the Helm chart. Default '1.0.0'
apiVersion (str, Optional): The API version of the Helm chart itself. Default 'v1'
Templates (Template, Optional): The templates for the Helm chart. Default None
"""
self.chartName = chartName
self.chartDescription = chartDescription
self.maintainers = maintainers
self.chartHomepage = chartHomepage
self.sources = sources
self.appVersion = appVersion
self.chartVersion = chartVersion
self.apiVersion = apiVersion
self.templates = templates
def create_templates_folder(self):
"""Create the templates folder for the Helm chart."""
os.mkdir('templates')
for template in self.templates:
template.write()
def write_yaml(self):
"""Write the Chart.yaml file for the Helm chart."""
with open('Chart.yaml', 'w') as f:
f.write(f'apiVersion: {self.apiVersion}' + '\n')
f.write(f'appVersion: "{self.appVersion}"' + '\n')
f.write(f'description: {self.chartDescription}' + '\n')
f.write(f'home: {self.chartHomepage}' + '\n')
f.write('maintainers:' + '\n')
for maintainer in self.maintainers:
f.write(' ' + f'- email: {maintainer["email"]}' + '\n')
f.write(' ' + ' ' + f'name: {maintainer["name"]}' + '\n')
f.write(f'name: {self.chartName}' + '\n')
f.write('sources:' + '\n')
for source in self.sources:
f.write(f'- {source}' + '\n')
f.write(f'version: "{self.chartVersion}"' + '\n')
def write_values_yaml(self):
"""Write the values.yaml file for the Helm chart."""
# Get the Deployment and Ingress templates from the templates provided
deployment_template = next(template for template in self.templates if isinstance(template, Deployment))
ingress_template = next(template for template in self.templates if isinstance(template, Ingress))
with open('values.yaml', 'w') as f:
f.write('# The number of instances (replicas) of the app to run' + '\n')
f.write(f'replicaCount: {deployment_template.replica_count}' + '\n')
f.write('\n')
f.write('image:' + '\n')
f.write(' ' + '# The repository of the image to use for the app' + '\n')
f.write(' ' + '# Should be in the format `<Image Repository (Ex. containers.example.com)>/<Image Name (Ex. app)>`' + '\n')
f.write(' ' + f'repository: "{deployment_template.image_repository}"' + '\n')
f.write(' ' + '# The specific image tag to use. It\'s recommended to use some kind of versioning tag scheme as it makes updating the container without having to fully redeploy easier.' + '\n')
f.write(' ' + '# Ex. v1.0.0' + '\n')
f.write(' ' + f'tag: "{deployment_template.image_tag}"' + '\n')
f.write(' ' + '# How often the image should be pulled. The possible values are "Always", "Never", and "IfNotPresent"' + '\n')
f.write(' ' + '# It\'s recommended for production to use "IfNotPresent" to avoid pulling the image every time the pod starts' + '\n')
f.write(' ' + '# Though, for development, "Always" is recommended to ensure the latest changes are being tested' + '\n')
f.write(' ' + f'pullPolicy: "{deployment_template.image_pull_policy}"' + '\n')
f.write('\n')
f.write('container:' + '\n')
f.write(' ' + '# The port that the container listens on (Ex. 8080)' + '\n')
f.write(' ' + f'port: {deployment_template.port}' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The environment that the container is running in (Ex. development, production, etc...)' + '\n')
f.write(' ' + '# This is used for the NODE_ENV environment variable' + '\n')
f.write(' ' + f'env: "{deployment_template.env}"' + '\n')
f.write('\n')
f.write('ingress:' + '\n')
f.write(' ' + '# We want an ingress resource if we are deploying to a cluster that has a ingress controller/load balancer' + '\n')
f.write(' ' + '# This includes most public cloud providers like EKS, GKE, and AKS' + '\n')
f.write(' ' + 'enabled: true' + '\n')
f.write(' ' + '# The DNS Name (Ex. app.example.com) where the app will be accessible' + '\n')
f.write(' ' + f'host: "{ingress_template.hostname}"' + '\n')
f.write(' ' + '# The class of the ingress controller that is being used (defaulted here to an NGINX ingress controller as it\'s popular for Kubernetes clusters)' + '\n')
f.write(' ' + 'class: nginx' + '\n')
f.write('\n')
for value in deployment_template.extra_env_vars.values():
if isinstance(value, dict):
# If a description is provided for the environment variable than we want to include it as a comment above the value in the `values.yaml` file.
if 'description' in value:
f.write(f'# {value["description"]}' + '\n')
var_name = value["name"].replace('{{ .Release.Name }}-', '')
snake_case_name = var_name.split('-')[0]
for token in var_name.split('-'):
if token != snake_case_name:
snake_case_name += token.capitalize()
f.write(f'{snake_case_name}: "{value["value"]}"' + '\n')
#f.write('privateRegistryToken: "<Private Registry Token>"' + '\n')
f.write('\n')
# If a OAuth template is included in the provided templates than we want to include the appropriate section to the `values.yaml` file.
if any(isinstance(template, OAuth) for template in self.templates):
# Get the OAuth template from the templates provided
oauth_template = next(template for template in self.templates if isinstance(template, OAuth))
f.write('# Configuration for using OAuth within the app' + '\n')
f.write('oauth:' + '\n')
f.write(' ' + f'baseAppUrl: "{oauth_template.base_app_url}"' + '\n')
f.write(' ' + f'appAbbreviation: "{oauth_template.app_abbreviation}"' + '\n')
f.write(' ' + f'appName: "{oauth_template.app_name}"' + '\n')
f.write(' ' + f'serviceName: "{oauth_template.service_name}"' + '\n')
f.write(' ' + f'devPort: "{oauth_template.dev_port}"' + '\n')
f.write(' ' + f'clientId: "{oauth_template.client_id}"' + '\n')
f.write(' ' + f'clientSecret: "{oauth_template.client_secret}"' + '\n')
f.write('\n')
# If a Database template is included in the provided templates than we want to include the appropriate section to the `values.yaml` file.
if any(isinstance(template, Database) for template in self.templates):
# Get the Database template from the templates provided
database_template = next(template for template in self.templates if isinstance(template, Database))
f.write('# Configuration for the relational database' + '\n')
f.write('database:' + '\n')
f.write(' ' + '# The type of the relational database that is used.' + '\n')
f.write(' ' + '# ' + '\n')
f.write(' ' + '# The following table lists the possible values for this field:' + '\n')
f.write(' ' + '# ' + '\n')
f.write(' ' + '# | Value | Description |' + '\n')
f.write(' ' + '# | ---------- | ------------------------------------------ |' + '\n')
f.write(' ' + '# | `postgres` | Uses PostgreSQL as the relational database |' + '\n')
f.write(' ' + '# ' + '\n')
f.write(' ' + '# Note, for use of `postgres`, it uses a [`postgres-controller` CRD](https://github.com/AlanBridgeman/postgres-controller) to create the database' + '\n')
f.write(' ' + '# ' + '\n')
f.write(' ' + f'type: "{database_template.type}"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# If set to `true`, the database will be created as part of the deployment' + '\n')
f.write(' ' + '# This uses the [`postgres-controller` CRD](https://github.com/AlanBridgeman/postgres-controller) to create the database' + '\n')
f.write(' ' + f'create: {str(database_template.create).lower()}' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The host that the database is located on ' + '\n')
f.write(' ' + f'host: "{database_template.host}"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The name of the database to be used' + '\n')
f.write(' ' + f'name: "{database_template.name}"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The user that is used to access the database' + '\n')
f.write(' ' + f'user: "{database_template.user}"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The password that is used to access the database' + '\n')
f.write(' ' + f'password: "{database_template.password}"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The port that the database listens on' + '\n')
f.write(' ' + f'#port: {database_template.port}' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# Allows for distinguishing between multiple database instances/servers' + '\n')
f.write(' ' + f'#instance_id: "{database_template.instance_id}"' + '\n')
f.write('\n')
# If a Database template is included in the provided templates than we want to include the appropriate section to the `values.yaml` file.
if any(isinstance(template, NoSQL) for template in self.templates):
nosql_template = next(template for template in self.templates if isinstance(template, NoSQL))
f.write('# Configuration the NoSQL database' + '\n')
f.write('# Within the parlance of the system these are often called "properties" databases (and store less structured data)' + '\n')
f.write('nosql:' + '\n')
f.write(' ' + '# Determines the type of NoSQL storage that is used' + '\n')
f.write(' ' + '# ' + '\n')
f.write(' ' + '# The following table lists the possible values for this field:' + '\n')
f.write(' ' + '# ' + '\n')
f.write(' ' + '# | Value | Description |' + '\n')
f.write(' ' + '# | --------- | ------------------------------------------------------------------------------------------ |' + '\n')
f.write(' ' + '# | `mongodb` | Uses MongoDB as the NoSQL database for the default account properties database |' + '\n')
f.write(' ' + '# | `azure` | Uses Azure Table Storage as the NoSQL database for the default account properties database |' + '\n')
f.write(' ' + '# ' + '\n')
f.write(' ' + f'type: {nosql_template.type}' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# If to create a resource as part of the deployment process' + '\n')
f.write(' ' + '# ONLY relevant if `type` is set to `mongodb`' + '\n')
f.write(' ' + '# This uses the [MongoDBCommunity CRD](https://github.com/mongodb/mongodb-kubernetes-operator) to create the resource' + '\n')
f.write(' ' + f'create: {str(nosql_template.create).lower()}' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The number of replicas/members as part of the Mongo deployment' + '\n')
f.write(' ' + '# See the `member` parameter of the [MongoDBCommunity CRD](https://github.com/mongodb/mongodb-kubernetes-operator) for more information' + '\n')
f.write(' ' + '# ONLY relevant if `type` is set to `mongodb` and `create` is set to `true`' + '\n')
if isinstance(nosql_template, MongoDB):
f.write(' ' + f'replicaCount: {nosql_template.replica_count}' + '\n')
else:
f.write(' ' + '#replicaCount: <Number of replicas>' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The TLS configuration for the connection to the NoSQL database' + '\n')
f.write(' ' + '# ONLY relevant if `type` is set to `mongodb` and `create` is set to `true`' + '\n')
f.write(' ' + 'tls:' + '\n')
f.write(' ' + ' ' + '# If to use TLS for the connection to the NoSQL database' + '\n')
if isinstance(nosql_template, MongoDB):
f.write(' ' + ' ' + f'enabled: {str(nosql_template.tls_enabled).lower()}' + '\n')
else:
f.write(' ' + ' ' + 'enabled: <true/false>' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The connection string used to access the NoSQL database' + '\n')
f.write(' ' + '# ONLY relevant if `type` is set to `mongodb` and `create` is set to `false`' + '\n')
f.write(' ' + '# Should be in the following format: `mongodb://<hostname>:<port>`' + '\n')
f.write(' ' + '#connectionString: "mongodb://mongo.example.com:27017"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The key used to access the NoSQL database' + '\n')
f.write(' ' + '# ONLY relevant if `type` is set to `azure`' + '\n')
f.write(' ' + '#key: ""' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The name of the NoSQL database' + '\n')
f.write(' ' + f'name: "{nosql_template.db_name}"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The username used to access the NoSQL database' + '\n')
f.write(' ' + '# ONLY relevant if `type` is set to `mongodb`' + '\n')
if isinstance(nosql_template, MongoDB):
f.write(' ' + f'user: "{nosql_template.user}"' + '\n')
else:
f.write(' ' + 'user: "<mongo user>"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The password used to access the NoSQL database' + '\n')
f.write(' ' + '# ONLY relevant if `type` is set to `mongodb`' + '\n')
if isinstance(nosql_template, MongoDB):
f.write(' ' + f'password: "{nosql_template.password}"' + '\n')
else:
f.write(' ' + 'password: "<mongo password>"' + '\n')
f.write('\n')
f.write('# Configurable NoSQL information groupings' + '\n')
f.write('# For Azure Table STorage these are table names' + '\n')
f.write('# For MongoDB these are collection names' + '\n')
f.write('tables:' + '\n')
for value in nosql_template.tables.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(' ' + f'{snake_case_name}: "{value["value"]}"' + '\n')
#f.write(' ' + 'activityProperties: activity' + '\n')
#f.write(' ' + 'eventProperties: eventProps' + '\n')
#f.write(' ' + 'localeProperties: locales' + '\n')
#f.write(' ' + 'organizerProperties: organizerProps' + '\n')
#f.write(' ' + 'userProperties: userProps' + '\n')
#f.write(' ' + 'templates: templates' + '\n')
f.write('\n')
# If a Redis template is included in the provided templates than we want to include the appropriate section to the values.yaml file.
if any(isinstance(template, Redis) for template in self.templates):
# Get the Redis template from the templates provided
redis_template = next(template for template in self.templates if isinstance(template, Redis))
f.write('# Configuration for cache server' + '\n')
f.write('cache:' + '\n')
f.write(' ' + 'type: "redis"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# If to create a Redis instance/resource as part of the deployment process' + '\n')
f.write(' ' + f'create: {str(redis_template.create).lower()}' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The image to use for the Redis instance' + '\n')
f.write(' ' + '# ONLY relevant if `create` is set to `true`' + '\n')
# If the image dictionary is not empty than we want to include the image values
if redis_template.create and len(redis_template.image) > 0:
f.write(' ' + 'image' + '\n')
# Loop through the image dictionary and write the image values
for key, value in redis_template.image.items():
f.write(' ' + ' ' + f'{key}: "{value}"' + '\n')
else:
f.write(' ' + 'image: {}' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The number of replicas of the Redis instance' + '\n')
f.write(' ' + '# ONLY relevant if `create` is set to `true`' + '\n')
if redis_template.create:
f.write(' ' + f'replicaCount: {redis_template.replicaCount}' + '\n')
else:
f.write(' ' + '#replicaCount: <Number of replicas (Ex. 1)>' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# Hostname of the Redis server' + '\n')
f.write(' ' + '# ONLY relevant if `create` is set to `false`' + '\n')
if redis_template.create:
f.write(' ' + '#hostName: "<Redis Host Name>"' + '\n')
else:
f.write(' ' + f'hostName: "{redis_template.hostName}"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The password to use for the Redis server' + '\n')
f.write(' ' + f'password: "{redis_template.password}"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# The port of the Redis server' + '\n')
f.write(' ' + f'port: "{redis_template.port}"' + '\n')
f.write(' ' + '\n')
f.write(' ' + '# Redis TLS Configurations' + '\n')
f.write(' ' + 'tls:' + '\n')
f.write(' ' + ' ' + '# If TLS is enabled for the Redis instance' + '\n')
f.write(' ' + ' ' + f'enabled: {str(redis_template.tls_enabled).lower()}' + '\n')
f.write(' ' + ' ' + '\n')
f.write(' ' + ' ' + '# The port of the Redis instance for TLS' + '\n')
f.write(' ' + ' ' + '# ONLY relevant if `tls.enabled` is set to `true`' + '\n')
if redis_template.tls_enabled:
f.write(' ' + ' ' + f'port: "{redis_template.tls_port}"' + '\n')
else:
f.write(' ' + ' ' + '#port: "<TLS Port (Ex. 6380)>"' + '\n')
f.write(' ' + '\n')
f.write('# Configurations for integration with third-party services' + '\n')
f.write('thirdParty:' + '\n')
for template in self.templates:
if isinstance(template, ThirdPartyService):
f.write(' ' + '# Configurations for the ' + template.name.capitalize() + ' integration' + '\n')
f.write(' ' + f'{template.name}:' + '\n')
f.write(' ' + ' ' + '# If the integration is enabled' + '\n')
f.write(' ' + ' ' + f'enabled: {str(template.enabled).lower()}' + '\n')
f.write(' ' + ' ' + '\n')
for key, value in template.vars.items():
snake_case_name = key.split('_')[0]
for token in key.split('_'):
if token != snake_case_name:
snake_case_name += token.capitalize()
f.write(' ' + ' ' + f'{snake_case_name}: {value}' + '\n')
f.write(' ' + ' ' + '\n')
#f.write(' ' + '# Configuration for the OpenAI integration' + '\n')
#f.write(' ' + 'openai:' + '\n')
#f.write(' ' + ' ' + '# If the OpenAI integration is enabled' + '\n')
#f.write(' ' + ' ' + 'enabled: true' + '\n')
#f.write(' ' + ' ' + '\n')
#f.write(' ' + ' ' + '# The OpenAI API key' + '\n')
#f.write(' ' + ' ' + 'apiKey: ""' + '\n')
def write_helmignore(self):
with open('.helmignore', 'w') as f:
f.write('# Ignore the ignore file' + '\n')
f.write('.helmignore' + '\n')
f.write('# Ignore the Helm chart\'s packaged tarball' + '\n')
f.write('*.tgz' + '\n')
f.write('# Ignore git files (In case done in the same directory as code)' + '\n')
f.write('.git' + '\n')
f.write('.gitignore' + '\n')
f.write('# Ignore the README file (In case done in the same directory as code)' + '\n')
f.write('README.md' + '\n')
f.write('# Ignore the requirements file (In case done in the same directory as code)' + '\n')
f.write('requirements.txt' + '\n')
f.write('# Ignore this file (In case done in the same directory as code)' + '\n')
f.write('create-helm-chart.py' + '\n')
def package(self):
"""Package the Helm chart for publishing."""
result = subprocess.run(['helm', 'package', '.'])
if result.returncode != 0:
raise Exception('Failed to package the Helm chart.')
def push(self, registry: str):
"""Push the Helm chart to the Helm remote registry.
Args:
registry (str): The URL of the Helm remote registry
"""
if not os.path.exists(f'{self.chartName}-{self.chartVersion}.tgz'):
raise Exception('The Helm chart has not been packaged yet.')
print(f'Pushing {self.chartName}-{self.chartVersion}.tgz to {registry}')
subprocess.run(['helm', 'push', f'{self.chartName}-{self.chartVersion}.tgz', f'oci://{registry}'])

39
src/Ingress.py Normal file
View file

@ -0,0 +1,39 @@
from .Template import Template
class Ingress (Template):
def __init__(self, hostname: str):
"""A class for creating a/some template(s) related to the Ingress for the app.
Args:
hostname (str): The hostname that the Ingress will be accessed on.
"""
super().__init__()
self.hostname = hostname
def write(self):
"""Write the Ingress template to a file."""
with open(f'templates/ingress.yaml', 'w') as f:
f.write('{{- if .Values.ingress.enabled -}}' + '\n')
f.write('apiVersion: networking.k8s.io/v1' + '\n')
f.write('kind: Ingress' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}' + '\n')
f.write(' ' + 'annotations:' + '\n')
f.write(' ' + ' ' + 'nginx.ingress.kubernetes.io/rewrite-target: /' + '\n')
f.write('spec:' + '\n')
f.write(' ' + 'rules:' + '\n')
f.write(' ' + '- host: {{ .Values.ingress.host }}' + '\n')
f.write(' ' + ' ' + 'http:' + '\n')
f.write(' ' + ' ' + ' ' + 'paths:' + '\n')
f.write(' ' + ' ' + ' ' + '- path: /' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'pathType: Prefix' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + 'backend:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + 'service:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'name: {{ .Release.Name }}' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'port:' + '\n')
f.write(' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + 'number: 80' + '\n')
f.write(' ' + 'ingressClassName: {{ .Values.ingress.class }}' + '\n')
f.write('{{- end -}}')

114
src/MongoDB.py Normal file
View file

@ -0,0 +1,114 @@
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)
self.user = user
self.password = password
self.replica_count = replica_count
self.tls_enabled = tls_enabled
def write(self):
super().write()
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-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-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 -}}')

29
src/NoSQL.py Normal file
View file

@ -0,0 +1,29 @@
from .Template import Template
class NoSQL (Template):
def __init__(self, type: str, db_name: str, tables: dict[str, dict[str, str]], create: bool = True):
super().__init__()
self.type = type
self.db_name = db_name
self.tables = tables
self.create = create
def write(self):
with open('templates/storage-tables-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(' ' + '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():
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')

40
src/OAuth.py Normal file
View file

@ -0,0 +1,40 @@
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, client_id: str, client_secret: str):
"""A class for creating a/some template(s) related to OAuth implementation."""
self.base_app_url = base_app_url
self.app_abbreviation = app_abbreviation
self.app_name = app_name
self.service_name = service_name
self.dev_port = dev_port
self.client_id = client_id
self.client_secret = client_secret
def write(self):
with open('templates/oauth-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 }}-oauth-credentials' + '\n')
f.write(' ' + 'labels:' + '\n')
f.write(' ' + ' ' + 'app: {{ .Release.Name }}' + '\n')
f.write('data:' + '\n')
f.write(' ' + 'base-app-url: {{ .Values.oauth.baseAppUrl }}' + '\n')
f.write(' ' + 'app-abbreviation: {{ .Values.oauth.appAbbreviation }}' + '\n')
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(' ' + 'client-id: {{ .Values.oauth.clientId | quote }}' + '\n')
with open('templates/oauth-client-secret.yaml', 'w') as f:
f.write('apiVersion: v1' + '\n')
f.write('kind: Secret' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}-oauth-client-secret' + '\n')
f.write(' ' + 'labels:' + '\n')
f.write(' ' + ' ' + 'app: {{ .Release.Name }}' + '\n')
f.write('type: Opaque' + '\n')
f.write('data:' + '\n')
f.write(' ' + 'client-secret: {{ .Values.oauth.clientSecret | b64enc }}' + '\n')

98
src/Redis.py Normal file
View file

@ -0,0 +1,98 @@
from .Cache import Cache
class Redis (Cache):
def __init__(self, password: str, create: bool = True, hostName: str = '{{ .Release.Name }}-redis', replicaCount: int = 1, port: str = '6379', tls_enabled: bool = False, tls_port: str = '6380', image: dict[str, str] = {}):
"""A class for creating a/some template(s) related to a Redis server.
Args:
password (str): The password to use to access the Redis instance.
create (bool, Optional): Whether or not to create the Redis instance as part of the Helm Chart. Default True
hostName (str, Optional): The hostname of the Redis instance. Default '{{ .Release.Name }}-redis'
replicaCount (int, Optional): The number of replicas of the Redis instance. Default 1
port (str, Optional): The port of the Redis instance. Default '6379'
tls_enabled (bool, Optional): Whether or not to enable TLS for the Redis instance. Default False
tls_port (str, Optional): The port of the Redis instance for TLS. Default '6380'
image (dict[str, str], Optional): The image of the Redis instance. Default {}
"""
super().__init__(password, hostName, port, create)
self.replicaCount = replicaCount
self.tls_enabled = tls_enabled
self.tls_port = tls_port
self.image = image
def write(self):
# Call parent class's method/function to write the generic cache templates
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 -}}')
# 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 -}}')

23
src/Service.py Normal file
View file

@ -0,0 +1,23 @@
from .Template import Template
class Service (Template):
def __init__(self):
"""A class for creating a/some template(s) related to the Service for the app."""
super().__init__()
def write(self):
"""Write the Service template to a file."""
with open(f'templates/service.yaml', 'w') as f:
f.write('apiVersion: v1' + '\n')
f.write('kind: Service' + '\n')
f.write('metadata:' + '\n')
f.write(' ' + 'name: {{ .Release.Name }}' + '\n')
f.write('spec:' + '\n')
f.write(' ' + 'selector:' + '\n')
f.write(' ' + ' ' + 'app: {{ .Release.Name }}' + '\n')
f.write(' ' + 'ports:' + '\n')
f.write(' ' + ' ' + '- protocol: TCP' + '\n')
f.write(' ' + ' ' + ' ' + 'port: 80' + '\n')
f.write(' ' + ' ' + ' ' + 'targetPort: {{ .Values.container.port }}' + '\n')

10
src/Template.py Normal file
View file

@ -0,0 +1,10 @@
from abc import ABC, abstractmethod
class Template(ABC):
"""An abstract class for creating a/some template(s)."""
@abstractmethod
def write(self):
"""Write the template to a file."""
pass

30
src/ThirdPartyService.py Normal file
View file

@ -0,0 +1,30 @@
from .Template import Template
class ThirdPartyService (Template):
def __init__(self, name: str, enabled: bool, **vars: str):
super().__init__()
self.name = name
self.enabled = enabled
self.vars = vars
def write(self):
with open(f'templates/{self.name}-secret.yaml', 'w') as f:
f.write('{{- if .Values.thirdParty.' + self.name + '.enabled -}}' + '\n')
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(' ' + 'labels:' + '\n')
f.write(' ' + ' ' + 'app: {{ .Release.Name }}' + '\n')
f.write('type: Opaque' + '\n')
f.write('data:' + '\n')
for key, value in self.vars.items():
snake_case_name = key.split('_')[0]
for token in key.split('_'):
if token != snake_case_name:
snake_case_name += token.capitalize()
f.write(' ' + key.replace('_', '-') + ': {{ .Values.thirdParty.' + self.name + '.' + snake_case_name + ' | b64enc }}' + '\n')
#f.write(' ' + 'api-key: {{ .Values.thirdParty.openai.apiKey | b64enc }}' + '\n')
f.write('{{- end -}}' + '\n')