complete rewrite

This commit is contained in:
2023-10-20 02:00:28 +02:00
parent c4c5ab7695
commit fb0b67fbc3
4 changed files with 710 additions and 168 deletions

View File

@ -6,10 +6,16 @@ import logging
from pathlib import Path from pathlib import Path
import subprocess import subprocess
from tabulate import tabulate
import click import click
import dotenv import dotenv
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
import yaml import yaml
import re
COMBINE_PATH = os.path.dirname(os.path.realpath(__file__)) + "/combine.yml"
CONFIG_FILE_NAME = 'alaka.yaml'
CONFIGS = {}
def read_config(filepath): def read_config(filepath):
@ -29,10 +35,115 @@ def read_config(filepath):
return yaml.safe_load(template.render()) return yaml.safe_load(template.render())
INSTANCE = read_config("config.yml") """ Get value from nested dicts, return None if one of the keys does not exists """
DEFAULTS = read_config("~/.abra/defaults.yml") def get_value(dict, *keys):
COMBINE = read_config(os.path.dirname( _element = dict
os.path.realpath(__file__)) + "/combine.yml") for key in keys:
try:
_element = _element[key]
except KeyError:
return
return _element
def merge_dict(dict1, dict2):
""" Merge two nested dicts recursively, the second overwrites the first one"""
merged_dict = dict1.copy()
for key, value in dict2.items():
if key in merged_dict and isinstance(value, dict) and isinstance(merged_dict[key], dict):
merged_dict[key] = merge_dict(merged_dict[key], value)
elif key in merged_dict and isinstance(value, list) and isinstance(merged_dict[key], list):
merged_dict[key] = list(set(merged_dict[key] + value))
else:
merged_dict[key] = value
return merged_dict
def merge_pool_configs(dir_path):
dir_path = Path(dir_path).absolute()
merged_configs = {}
for root, _, files in os.walk(dir_path):
no_config = True
for file in files:
if file == 'alaka.yaml':
file_path = os.path.join(root, file)
config = read_config(file_path)
# Merge the config with the merged config from the parent dir
if par_config := merged_configs.get(os.path.dirname(root)):
merged_configs[root] = merge_dict(par_config, config)
else:
merged_configs[root] = config
no_config = False
if no_config:
merged_configs[root] = merged_configs.get(os.path.dirname(root))
return merged_configs
def merge_instance_configs(pool_config, instance_domain, instance_config):
merged_config = {}
for app, app_config in instance_config.items():
if not (server:= get_value(pool_config, 'GLOBALS', 'server')):
server = instance_domain
if app_config and pool_config.get(app):
merged_config[app] = merge_dict(pool_config[app], app_config)
elif app_config:
merged_config[app] = app_config
elif pool_config.get(app):
merged_config[app] = pool_config[app]
else:
merged_config[app] = {}
merged_config[app]['app_domain'] = map_subdomain(app, instance_domain, merged_config[app])
if not merged_config[app].get('server'):
merged_config[app]['server'] = server
return merged_config
def map_subdomain(recipe, instance_domain, app_config):
if subdomain:= app_config.get('subdomain'):
domain = subdomain.replace("example.com", instance_domain)
else:
domain = f"{recipe}.{instance_domain}"
return domain
def get_merged_instance_configs(pool_path, pool_configs):
pool_path = Path(pool_path).absolute()
if pool_path.is_file():
parent_path = os.path.dirname(pool_path)
instance_config = read_config(pool_path)
domain = pool_path.name.removesuffix('.yml').removesuffix('.yaml')
merged_config = merge_instance_configs(pool_configs[parent_path], domain, instance_config)
return {pool_path.name: merged_config}
instances = {}
for root, _, files in os.walk(Path(pool_path)):
for file in files:
# This pattern matches for files of the format "<domain>.yml" or "<domain>.yaml"
pattern = r"^(?:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,6}(?:\.yaml|\.yml)$"
if re.match(pattern, file):
instance_config = read_config(f'{root}/{file}')
domain = file.removesuffix('.yml').removesuffix('.yaml')
merged_config = merge_instance_configs(pool_configs[root], domain, instance_config)
instances[domain] = merged_config
return instances
def merge_connection_configs(configs):
connection_config = read_config(COMBINE_PATH)
extend_shared_secrets(connection_config)
merged_configs = configs.copy()
for _, instance_config in merged_configs.items():
for target_app, source_apps in connection_config.items():
for source_app, target_conf in source_apps.items():
if target_app in instance_config and source_app in instance_config:
instance_config[target_app] = merge_dict(target_conf, instance_config[target_app])
return merged_configs
"""Add a layer containing the source app"""
def extend_shared_secrets(connection_config):
for _, source_apps in connection_config.items():
for source_app, target_conf in source_apps.items():
if shared_secrets:= target_conf.get('shared_secrets'):
target_conf['shared_secrets'] = {source_app: shared_secrets}
def abra(*args, machine_output=False, ignore_error=False): def abra(*args, machine_output=False, ignore_error=False):
@ -46,6 +157,7 @@ def abra(*args, machine_output=False, ignore_error=False):
if process.stdout: if process.stdout:
logging.debug(process.stdout.decode()) logging.debug(process.stdout.decode())
if process.returncode and not ignore_error: if process.returncode and not ignore_error:
breakpoint()
raise RuntimeError( raise RuntimeError(
f'{" ".join(command)} \n STDOUT: \n {process.stdout.decode()} \n STDERR: {process.stderr.decode()}') f'{" ".join(command)} \n STDOUT: \n {process.stdout.decode()} \n STDERR: {process.stderr.decode()}')
if machine_output: if machine_output:
@ -66,12 +178,12 @@ def write_env_header(path):
file.write(header + old_content) file.write(header + old_content)
def new_app(recipe): def new_app(recipe, domain, server):
domain = map_subdomain(recipe) path = get_env_path(server, domain)
server = INSTANCE["server"] if INSTANCE.get( if path.exists():
"server") else INSTANCE["domain"] print(f'remove {path}')
print(f'create {recipe} config on {server} at {domain}') path.unlink()
path = get_env_path(recipe) logging.info(f'create {recipe} config on {server} at {domain}')
out = abra("app", "new", recipe, "-n", "-s", server, "-D", domain) out = abra("app", "new", recipe, "-n", "-s", server, "-D", domain)
if not "app has been created" in out: if not "app has been created" in out:
raise RuntimeError(f'App "{recipe}" creation failed') raise RuntimeError(f'App "{recipe}" creation failed')
@ -80,71 +192,106 @@ def new_app(recipe):
logging.info(f'{recipe} created on {server} at {domain}') logging.info(f'{recipe} created on {server} at {domain}')
def map_subdomain(recipe): def update_configs(path, config):
if ((INSTANCE.get('apps') and (app := INSTANCE['apps'].get(recipe)) and (subdomain := app.get('subdomain'))) or if uncomment_keys := config.get("uncomment"):
((app := DEFAULTS.get(recipe)) and (subdomain := app.get('subdomain')))):
domain = subdomain.replace("example.com", INSTANCE["domain"])
else:
domain = f"{recipe}.{INSTANCE['domain']}"
return domain
def generate_secrets(recipe):
domain = map_subdomain(recipe)
stored_secrets = abra("app", "secret", "ls", domain).splitlines()
if any("false" in line for line in stored_secrets):
abra("app", "secret", "generate", "-a", domain)
print(f"secrets for {domain} generated")
def get_env_path(recipe):
domain = map_subdomain(recipe)
server = INSTANCE["server"] if INSTANCE.get(
"server") else INSTANCE["domain"]
return Path(f"~/.abra/servers/{server}/{domain}.env").expanduser()
def connect_apps(target_app):
path = get_env_path(target_app)
target_conf = COMBINE.get(target_app)
for source_app in INSTANCE["apps"]:
if target_conf and (configs := target_conf.get(source_app)):
logging.info(f'connect {target_app} with {source_app}')
set_configs(target_app, configs)
if shared_secrets := configs.get("shared_secrets"):
logging.info(
f'share secrets between {target_app} and {source_app}')
share_secrets(target_app, source_app, shared_secrets)
insert_domains(path, source_app)
def set_configs(recipe, configs):
path = get_env_path(recipe)
if uncomment_keys := configs.get("uncomment"):
uncomment(uncomment_keys, path, True) uncomment(uncomment_keys, path, True)
if envs := configs.get("env"): if envs := config.get("env"):
uncomment(envs.keys(), path) uncomment(envs.keys(), path)
for key, value in envs.items(): for key, value in envs.items():
logging.debug(f'set {key}={value} in {path}') logging.debug(f'set {key}={value} in {path}')
dotenv.set_key(path, key, value, quote_mode="never") dotenv.set_key(path, key, value, quote_mode="never")
if secrets := configs.get("secrets"):
domain = map_subdomain(recipe)
def generate_all_secrets(domain):
stored_secrets = abra("app", "secret", "ls", domain).splitlines()
if any("false" in line for line in stored_secrets):
logging.info(f"Generate all secrets for {domain}")
abra("app", "secret", "generate", "-a", domain)
print(f"secrets for {domain} generated")
def get_env_path(server, domain):
return Path(f"~/.abra/servers/{server}/{domain}.env").expanduser()
# def connect_apps(target_app):
# path = get_env_path(target_app)
# target_conf = COMBINE.get(target_app)
# for source_app in INSTANCE["apps"]:
# if target_conf and (configs := target_conf.get(source_app)):
# logging.info(f'connect {target_app} with {source_app}')
# update_configs(target_app, configs)
# if shared_secrets := configs.get("shared_secrets"):
# logging.info(
# f'share secrets between {target_app} and {source_app}')
# share_secrets(target_app, source_app, shared_secrets)
# insert_domains(path, source_app)
def exchange_secrets(app1, instance_config, apps):
#TODO: check this function
app1_config = instance_config[app1]
app1_domain = app1_config['app_domain']
for app2 in apps:
app2_config = instance_config[app2]
app2_domain = app2_config['app_domain']
if app1_shared_secrets := get_value(app1_config, "shared_secrets", app2):
logging.info(f'share secrets between {app1_domain} and {app2_domain}')
share_secrets(app1_domain, app2_domain, app1_shared_secrets)
if app2_shared_secrets := get_value(app2_config, "shared_secrets", app1):
logging.info(f'share secrets between {app1_domain} and {app2_domain}')
share_secrets(app2_domain, app1_domain, app2_shared_secrets)
def str2bool(value):
return value.lower() in ("yes", "true", "t", "1")
def share_secrets(app1_domain, app2_domain, secrets):
app1_stored_secrets = abra("app", "secret", "ls", app1_domain, machine_output=True)
app1_stored_secrets = {x['name']: str2bool(x['created-on-server']) for x in app1_stored_secrets}
app2_stored_secrets = abra("app", "secret", "ls", app2_domain, machine_output=True)
app2_stored_secrets = {x['name']: str2bool(x['created-on-server']) for x in app2_stored_secrets}
for app2_secret in secrets:
app1_secret = secrets[app2_secret]
# TODO: test if both apps have the secret available
try:
app1_secret_is_stored = app1_stored_secrets[app1_secret]
except KeyError:
logging.error(f"{app1_domain} does not contain secret {app1_secret}")
continue
try:
app2_secret_is_stored = app2_stored_secrets[app2_secret]
except KeyError:
logging.error(f"{app2_domain} does not contain secret {app2_secret}")
continue
if app1_secret_is_stored and not app2_secret_is_stored:
secret = get_secret(app1_domain, app1_secret)
insert_secret(app2_domain, app2_secret, secret)
elif app2_secret_is_stored and not app1_secret_is_stored:
secret = get_secret(app2_domain, app2_secret)
insert_secret(app1_domain, app1_secret, secret)
elif not any([app1_secret_is_stored, app2_secret_is_stored]):
secret = generate_secret(app1_domain, app1_secret)
insert_secret(app2_domain, app2_secret, secret)
def get_secret(domain, secret_name):
# TODO: use "abra secret get <secret_name>"
secret = abra("app", "run", domain, "worker", "cat", f"/var/run/secrets/{secret_name}")
return secret
def generate_secret(domain, secret_name):
secret = abra("app", "secret", "generate", domain, secret_name, "v1", machine_output=True)
return secret[0]['value']
def insert_secrets_from_conf(domain, config):
logging.info(f"Insert secrets for {domain}")
if secrets := config.get("secrets"):
for secret_name, secret in secrets.items(): for secret_name, secret in secrets.items():
insert_secret(domain, secret_name, secret) insert_secret(domain, secret_name, secret)
def share_secrets(target, source, secrets):
target_domain = map_subdomain(target)
source_domain = map_subdomain(source)
stored_secrets = abra("app", "secret", "ls", target_domain).splitlines()
for source_secret in secrets:
target_secret = secrets[source_secret]
if not any(target_secret in line and "true" in line for line in stored_secrets):
secret = get_generated_secret(source_domain, source_secret)
target_secret = secrets[source_secret]
insert_secret(target_domain, target_secret, secret)
def insert_secret(domain, secret_name, secret): def insert_secret(domain, secret_name, secret):
# TODO parse json # TODO parse json
stored_secrets = abra("app", "secret", "ls", domain).splitlines() stored_secrets = abra("app", "secret", "ls", domain).splitlines()
@ -152,19 +299,6 @@ def insert_secret(domain, secret_name, secret):
abra("app", "secret", "insert", domain, secret_name, "v1", secret) abra("app", "secret", "insert", domain, secret_name, "v1", secret)
def get_generated_secret(domain, secret_name):
# TODO: use "abra secret get <secret_name>"
stored_secrets = abra("app", "secret", "ls", domain).splitlines()
if not any(secret_name in line and "true" in line for line in stored_secrets):
secret = abra("app", "secret", "generate", domain, secret_name, "v1")
secret = secret.splitlines()[3].split("|")[2].strip()
else:
secret = abra(
"app", "run", domain, "worker", "cat", f"/var/run/secrets/{secret_name}"
)
return secret
def uncomment(keys, path, match_all=False): def uncomment(keys, path, match_all=False):
logging.debug(f'Uncomment {keys} in {path}') logging.debug(f'Uncomment {keys} in {path}')
with open(path, "r") as file: with open(path, "r") as file:
@ -179,8 +313,13 @@ def uncomment(keys, path, match_all=False):
file.write(line) file.write(line)
def insert_domains(path, app): def exchange_domains(instance_config, apps, path):
domain = map_subdomain(app) for app in apps:
domain = instance_config[app]['app_domain']
insert_domains(path, app, domain)
def insert_domains(path, app, domain):
logging.debug(f'replace all {app}.example.com with {domain} in {path}') logging.debug(f'replace all {app}.example.com with {domain} in {path}')
with open(path, "r") as file: with open(path, "r") as file:
content = file.read() content = file.read()
@ -189,21 +328,15 @@ def insert_domains(path, app):
file.write(content) file.write(content)
def execute_cmds(app): def execute_cmds(app_config):
domain = map_subdomain(app) domain = app_config['app_domain']
all_cmds = [] if not (all_cmds:= app_config.get('execute')):
if (app_conf := DEFAULTS.get(app)) and (cmds := app_conf.get("execute")): logging.info(f"No post deploy cmds for {domain}")
all_cmds += cmds return
if COMBINE.get(app):
for target_app, target_conf in COMBINE[app].items():
if target_app in INSTANCE["apps"] and (cmds := target_conf.get("execute")):
all_cmds += cmds
if (app_conf := INSTANCE["apps"][app]) and (cmds := app_conf.get("execute")):
all_cmds += cmds
for cmd in all_cmds: for cmd in all_cmds:
container = cmd.split()[0] container = cmd.split()[0]
cmd = cmd.split()[1] cmd = cmd.split()[1]
logging.info(f"Run '{cmd}' in {domain}:{container}") print(f"Run '{cmd}' in {domain}:{container}")
if container == "local": if container == "local":
print(abra("app", "cmd", "--local", domain, cmd, ignore_error=True)) print(abra("app", "cmd", "--local", domain, cmd, ignore_error=True))
else: else:
@ -212,81 +345,138 @@ def execute_cmds(app):
@click.group() @click.group()
@click.option('-l', '--log', 'loglevel') @click.option('-l', '--log', 'loglevel')
@click.option('-c', '--config', 'config') @click.option('-p', '--pool_path', 'pool_path')
def cli(loglevel, config): @click.option('-c', '--config_path', 'config_path', default=".")
global INSTANCE def cli(loglevel, pool_path, config_path):
global CONFIGS
#combine_config = read_config(COMBINE_PATH)
pool_configs = merge_pool_configs(config_path)
instance_configs = get_merged_instance_configs(pool_path, pool_configs)
CONFIGS = merge_connection_configs(instance_configs)
if loglevel: if loglevel:
numeric_level = getattr(logging, loglevel.upper(), None) numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int): if not isinstance(numeric_level, int):
raise ValueError('Invalid log level: %s' % loglevel) raise ValueError('Invalid log level: %s' % loglevel)
logging.basicConfig(level=numeric_level) logging.basicConfig(level=numeric_level)
if config:
INSTANCE = read_config(config)
#@cli.command()
#def init_server():
# """ Initialize the server """
# new_app("traefik")
# new_app("backup-bot-two")
@cli.command() @cli.command()
def init_server(): @click.option('-a', '--apps', multiple=True)
""" Initialize the server """ def setup(apps):
new_app("traefik") pass
new_app("backup-bot-two")
@cli.command() @cli.command()
@click.option('-a', '--apps', multiple=True) @click.option('-a', '--apps', multiple=True)
def setup_apps(apps): def config(apps):
""" Configure and connect the apps """ """ Configure the apps """
for instance, instance_config in CONFIGS.items():
if not apps: if not apps:
apps = INSTANCE["apps"] apps = instance_config.keys()
for app in apps: for app in apps:
new_app(app) if app not in instance_config:
if configs := DEFAULTS.get(app): logging.error(f"Could not find any '{app}' configuration for instance {instance}")
logging.info(f'set defaults for {app}') exit(1)
set_configs(app, configs) app_config = instance_config[app]
if configs := INSTANCE["apps"].get(app): domain = app_config['app_domain']
logging.info(f'set extra configs for {app}') server = app_config["server"]
set_configs(app, configs) path = get_env_path(server, domain)
print(f'Setup {app} config on {server} at {domain}')
new_app(app, domain, server)
logging.info(f'set configs for {app} at {instance}')
update_configs(path, app_config)
exchange_domains(instance_config, instance_config.keys(), path)
@cli.command()
@click.option('-a', '--apps', multiple=True)
def secrets(apps):
""" Configure the apps """
for instance, instance_config in CONFIGS.items():
if not apps:
apps = instance_config.keys()
for app in apps: for app in apps:
print(f'create connections for {app}') if app not in instance_config:
connect_apps(app) logging.error(f"Could not find any '{app}' configuration for instance {instance}")
for app in apps: exit(1)
logging.info(f'generate secrets for {app}') app_config = instance_config[app]
generate_secrets(app) domain = app_config['app_domain']
print(f"Create secrets for {domain}")
insert_secrets_from_conf(domain, app_config)
exchange_secrets(app, instance_config, apps)
generate_all_secrets(domain)
def get_deployed_apps(): def get_deployed_apps(apps):
server = INSTANCE["server"] if INSTANCE.get("server") else INSTANCE["domain"] deployed_apps = []
processed_server = []
for _, instance_config in CONFIGS.items():
if not apps:
apps = instance_config.keys()
for app in apps:
server = instance_config[app]['server']
if server in processed_server:
continue
processed_server.append(server)
deployed = abra("app", "ls", "-S", "-s", server, "-m", machine_output=True) deployed = abra("app", "ls", "-S", "-s", server, "-m", machine_output=True)
deployed_apps = filter( deployed_domains = [app["domain"] for app in deployed[server]["apps"] if app["status"] == "deployed"]
lambda x: x["status"] == "deployed", deployed[server]["apps"]) deployed_apps.extend(deployed_domains)
deployed_domains = [app["domain"] for app in deployed_apps] return deployed_apps
return deployed_domains
@cli.command() @cli.command()
@click.option('-a', '--apps', multiple=True) @click.option('-a', '--apps', multiple=True)
def deploy_apps(apps): @click.option('-r', '--run-cmds', is_flag=True)
def deploy(apps, run_cmds):
""" Deploy all the apps """ """ Deploy all the apps """
deployed_domains = get_deployed_apps() deployed_domains = get_deployed_apps(apps)
for app in INSTANCE["apps"]: for _, instance_config in CONFIGS.items():
domain = map_subdomain(app) if not apps:
if (apps and app in apps) or (not apps and domain not in deployed_domains): apps = instance_config.keys()
print(f'deploy {domain}') for app in apps:
print(abra("app", "deploy", "-C", "-n", domain)) app_config = instance_config[app]
domain = app_config['app_domain']
if domain in deployed_domains:
print(f"{domain} is already deployed")
continue
version = app_config.get('version')
if not version:
version = 'latest'
cmd = ["deploy", "-n"]
if version == 'chaos':
cmd.append("-chaos")
if not run_cmds:
cmd.append("--no-converge-checks")
cmd.append(domain)
if version not in ['latest', 'chaos']:
cmd.append(version)
print(f'deploy {domain} with version "{version}"')
print(abra("app", *cmd))
if run_cmds:
logging.info(f'execute commands for {domain}') logging.info(f'execute commands for {domain}')
execute_cmds(app) execute_cmds(app_config)
@cli.command() @cli.command()
@click.option('-a', '--apps', multiple=True) @click.option('-a', '--apps', multiple=True)
def undeploy_apps(apps): def undeploy(apps):
""" Undeploy all the apps """ """ Undeploy all the apps """
deployed_domains = get_deployed_apps() deployed_domains = get_deployed_apps(apps)
for _, instance_config in CONFIGS.items():
if not apps: if not apps:
apps = INSTANCE["apps"] apps = instance_config.keys()
for app in apps: for app in apps:
domain = map_subdomain(app) app_config = instance_config[app]
domain = app_config['app_domain']
if domain not in deployed_domains:
print(f"{domain} is not deployed")
continue
print(f'undeploy {domain}') print(f'undeploy {domain}')
if domain in deployed_domains:
print(abra("app", "undeploy", "-n", domain)) print(abra("app", "undeploy", "-n", domain))
@ -294,42 +484,74 @@ def undeploy_apps(apps):
@click.option('-a', '--apps', multiple=True) @click.option('-a', '--apps', multiple=True)
def cmds(apps): def cmds(apps):
""" execute all post deploy cmds """ """ execute all post deploy cmds """
deployed_domains = get_deployed_apps() deployed_domains = get_deployed_apps(apps)
for _, instance_config in CONFIGS.items():
if not apps: if not apps:
apps = INSTANCE["apps"] apps = instance_config.keys()
for app in apps: for app in apps:
domain = map_subdomain(app) app_config = instance_config[app]
if domain in deployed_domains: domain = app_config['app_domain']
if domain not in deployed_domains:
print(f"{domain} is not deployed")
continue
logging.info(f'execute commands for {domain}') logging.info(f'execute commands for {domain}')
execute_cmds(app) execute_cmds(app_config)
@cli.command() @cli.command()
@click.option('-a', '--apps', multiple=True) @click.option('-a', '--apps', multiple=True)
def purge_apps(apps): def purge(apps):
""" Completely remove all the apps """ """ Completely remove all the apps """
if not apps: # TODO: check for deployed apps
apps = INSTANCE["apps"] pool_apps = print_all_apps(apps)
for app in apps: domains = list(zip(*sum(pool_apps.values(), [])))[1]
domain = map_subdomain(app) if input(f"Do you really want to purge these apps? Type YES: ") == "YES":
for domain in domains:
logging.info(f'purge {domain}') logging.info(f'purge {domain}')
abra("app", "rm", "-n", domain) abra("app", "rm", "-n", domain)
print(f"{domain} purged") print(f"{domain} purged")
@cli.command()
@click.option('-a', '--apps', multiple=True)
def ls(apps):
""" List all the apps """
print_all_apps(apps)
def print_all_apps(apps):
pool_apps = list_apps(apps)
for instance, instance_apps in pool_apps.items():
print(instance)
print(tabulate(instance_apps))
print()
return pool_apps
def list_apps(apps):
pool_apps = {}
for instance, instance_config in CONFIGS.items():
pool_apps[instance] = []
if not apps:
apps = instance_config.keys()
for app in apps:
domain = instance_config[app]['app_domain']
pool_apps[instance].append((app, domain))
return pool_apps
@cli.command() @cli.command()
@click.option('-a', '--apps', multiple=True) @click.option('-a', '--apps', multiple=True)
def purge_secrets(apps): def purge_secrets(apps):
""" Remove all the apps secrets """ """ Remove all the apps secrets """
if not apps: # TODO: check for deployed apps
apps = INSTANCE["apps"] pool_apps = print_all_apps(apps)
for app in apps: domains = list(zip(*sum(pool_apps.values(), [])))[1]
domain = map_subdomain(app) if input(f"Do you really want to purge the secrets for these apps? Type YES: ") == "YES":
logging.info(f'purge secrets from {domain}') for domain in domains:
stored_secrets = abra("app", "secret", "ls", domain) logging.info(f'purge {domain}')
if "true" in stored_secrets:
abra("app","secret" ,"rm", "-a", domain) abra("app","secret" ,"rm", "-a", domain)
print(f"secrets removed for {domain}") print(f"Secrets for {domain} purged")
if __name__ == '__main__': if __name__ == '__main__':

BIN
config_file_structure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

320
config_structure.drawio Normal file
View File

@ -0,0 +1,320 @@
<mxfile host="Electron" modified="2023-10-09T15:41:25.929Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.0.2 Chrome/114.0.5735.289 Electron/25.8.4 Safari/537.36" etag="TpoyeJYr0o4faOI_h5Ok" version="22.0.2" type="device">
<diagram name="Page-1" id="UFZ3J2dV5uHkDdLlwz5H">
<mxGraphModel dx="3050" dy="1328" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="EmT_t4AcRQ52VDmL_bbM-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-7" target="EmT_t4AcRQ52VDmL_bbM-68">
<mxGeometry relative="1" as="geometry">
<mxPoint x="515" y="590.0000000000005" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-27" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-7" target="EmT_t4AcRQ52VDmL_bbM-66">
<mxGeometry relative="1" as="geometry">
<mxPoint x="675" y="570" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-28" value="Abritrary Layer of inhereting Global Configurations" style="swimlane;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-30" y="80" width="790" height="450" as="geometry">
<mxRectangle x="-30" y="80" width="330" height="30" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-2" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); font-size: 12px;&quot;&gt;&lt;b&gt;Global Org/Coop Config&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="310" y="30" width="150" height="130" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-1" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;&lt;span style=&quot;background-color: initial; text-align: left;&quot;&gt;smtp from address&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;app subdomain&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;install commands&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;language&lt;/span&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="327.5" y="70" width="115" height="80" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-96" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="EmT_t4AcRQ52VDmL_bbM-28" source="EmT_t4AcRQ52VDmL_bbM-5" target="EmT_t4AcRQ52VDmL_bbM-55">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="253" y="310" />
<mxPoint x="435" y="310" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-5" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color);&quot;&gt;&lt;b style=&quot;border-color: var(--border-color);&quot;&gt;Stable Pool Config&lt;/b&gt;&lt;/p&gt;&lt;div style=&quot;border-color: var(--border-color);&quot;&gt;&lt;br style=&quot;border-color: var(--border-color);&quot;&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="177.5" y="200" width="150" height="90" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="EmT_t4AcRQ52VDmL_bbM-28" source="EmT_t4AcRQ52VDmL_bbM-2" target="EmT_t4AcRQ52VDmL_bbM-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-95" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="EmT_t4AcRQ52VDmL_bbM-28" source="EmT_t4AcRQ52VDmL_bbM-6" target="EmT_t4AcRQ52VDmL_bbM-53">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="253" y="310" />
<mxPoint x="275" y="310" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-6" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;&lt;span style=&quot;background-color: initial; text-align: left;&quot;&gt;app versions&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;app configs&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="195" y="240" width="115" height="40" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-7" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color);&quot;&gt;&lt;b style=&quot;border-color: var(--border-color);&quot;&gt;Testing Pool Config&lt;/b&gt;&lt;/p&gt;&lt;div style=&quot;border-color: var(--border-color);&quot;&gt;&lt;br style=&quot;border-color: var(--border-color);&quot;&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="550" y="200" width="150" height="110" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-22" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="EmT_t4AcRQ52VDmL_bbM-28" source="EmT_t4AcRQ52VDmL_bbM-2" target="EmT_t4AcRQ52VDmL_bbM-7">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="385" y="180" />
<mxPoint x="625" y="180" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-15" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color);&quot;&gt;&lt;b&gt;Sub-Org Config&lt;/b&gt;&lt;/p&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="40" y="340" width="150" height="90" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="EmT_t4AcRQ52VDmL_bbM-28" source="EmT_t4AcRQ52VDmL_bbM-5" target="EmT_t4AcRQ52VDmL_bbM-15">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="253" y="310" />
<mxPoint x="115" y="310" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-16" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;&lt;span style=&quot;background-color: initial; text-align: left;&quot;&gt;org branding/theme&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;server: sub_org&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="57.5" y="380" width="115" height="40" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-51" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;&lt;span style=&quot;background-color: initial; text-align: left;&quot;&gt;app versions&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;app configs&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;server: &lt;/span&gt;test &lt;br&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="567.5" y="240" width="115" height="60" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-53" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color);&quot;&gt;&lt;b style=&quot;border-color: var(--border-color);&quot;&gt;Stable Server 1&lt;/b&gt;&lt;/p&gt;&lt;div style=&quot;border-color: var(--border-color);&quot;&gt;&lt;br style=&quot;border-color: var(--border-color);&quot;&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="200" y="340" width="150" height="80" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-54" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;server: server1&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="217.5" y="380" width="115" height="30" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-55" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color);&quot;&gt;&lt;b style=&quot;border-color: var(--border-color);&quot;&gt;Stable Server 2&lt;/b&gt;&lt;/p&gt;&lt;div style=&quot;border-color: var(--border-color);&quot;&gt;&lt;br style=&quot;border-color: var(--border-color);&quot;&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="360" y="340" width="150" height="80" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-56" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;server:server2&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-28">
<mxGeometry x="377.5" y="380" width="115" height="30" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-42" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-33" target="EmT_t4AcRQ52VDmL_bbM-38">
<mxGeometry relative="1" as="geometry">
<mxPoint x="-620" y="320" as="targetPoint" />
<Array as="points">
<mxPoint x="-685" y="322" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-91" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-33" target="EmT_t4AcRQ52VDmL_bbM-39">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-685" y="1150" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-33" value="&lt;b&gt;my_it_org&lt;/b&gt;" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#82b366;labelPosition=center;verticalLabelPosition=top;verticalAlign=bottom;outlineConnect=0;align=center;shape=mxgraph.office.concepts.folder;fillColor=#d5e8d4;" vertex="1" parent="1">
<mxGeometry x="-710" y="160" width="50" height="45" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-36" value="alaka.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#fff2cc;strokeColor=#d6b656;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-585" y="210" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-48" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-38" target="EmT_t4AcRQ52VDmL_bbM-45">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-560" y="470" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-85" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-38" target="EmT_t4AcRQ52VDmL_bbM-73">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-560" y="708" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-88" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-38" target="EmT_t4AcRQ52VDmL_bbM-74">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-560" y="938" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-38" value="&lt;b&gt;stable&lt;br&gt;&lt;/b&gt;" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#82b366;labelPosition=center;verticalLabelPosition=top;verticalAlign=bottom;outlineConnect=0;align=center;shape=mxgraph.office.concepts.folder;fillColor=#d5e8d4;" vertex="1" parent="1">
<mxGeometry x="-585" y="300" width="50" height="45" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-39" value="&lt;b&gt;testing&lt;br&gt;&lt;/b&gt;" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#82b366;labelPosition=center;verticalLabelPosition=top;verticalAlign=bottom;outlineConnect=0;align=center;shape=mxgraph.office.concepts.folder;fillColor=#d5e8d4;" vertex="1" parent="1">
<mxGeometry x="-580" y="1120" width="50" height="45" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-41" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-33" target="EmT_t4AcRQ52VDmL_bbM-36">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-685" y="235" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-45" value="&lt;b&gt;sub_org&lt;br&gt;&lt;/b&gt;" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#82b366;labelPosition=center;verticalLabelPosition=top;verticalAlign=bottom;outlineConnect=0;align=center;shape=mxgraph.office.concepts.folder;fillColor=#d5e8d4;" vertex="1" parent="1">
<mxGeometry x="-495" y="440" width="50" height="45" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-46" value="alaka.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#fff2cc;strokeColor=#d6b656;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-490" y="360" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-47" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-38" target="EmT_t4AcRQ52VDmL_bbM-46">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-560" y="385" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-49" value="instance4.example.com.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#dae8fc;strokeColor=#6c8ebf;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-485" y="1280" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-58" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-53">
<mxGeometry relative="1" as="geometry">
<mxPoint x="215" y="590.0000000000005" as="targetPoint" />
<Array as="points">
<mxPoint x="245" y="570" />
<mxPoint x="215" y="570" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-59" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-15" target="EmT_t4AcRQ52VDmL_bbM-13">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="85" y="570" />
<mxPoint x="45" y="570" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-70" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-55" target="EmT_t4AcRQ52VDmL_bbM-64">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="405" y="570" />
<mxPoint x="360" y="570" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-71" value="alaka.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#fff2cc;strokeColor=#d6b656;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-485" y="1200" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-73" value="&lt;b&gt;stable_server_1&lt;br&gt;&lt;/b&gt;" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#82b366;labelPosition=center;verticalLabelPosition=top;verticalAlign=bottom;outlineConnect=0;align=center;shape=mxgraph.office.concepts.folder;fillColor=#d5e8d4;" vertex="1" parent="1">
<mxGeometry x="-495" y="685" width="50" height="45" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-74" value="&lt;b&gt;stable_server_2&lt;br&gt;&lt;/b&gt;" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#82b366;labelPosition=center;verticalLabelPosition=top;verticalAlign=bottom;outlineConnect=0;align=center;shape=mxgraph.office.concepts.folder;fillColor=#d5e8d4;" vertex="1" parent="1">
<mxGeometry x="-495" y="915" width="50" height="45" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-75" value="alaka.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#fff2cc;strokeColor=#d6b656;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-400" y="510" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-76" value="alaka.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#fff2cc;strokeColor=#d6b656;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-400" y="750" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-77" value="instance2.example.com.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#dae8fc;strokeColor=#6c8ebf;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-400" y="825" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-78" value="instance1.example.com.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#dae8fc;strokeColor=#6c8ebf;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-400" y="590" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-79" value="alaka.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#fff2cc;strokeColor=#d6b656;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-400" y="990" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-80" value="instance3.example.com.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#dae8fc;strokeColor=#6c8ebf;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-400" y="1070" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-81" value="instance5.example.com.yaml" style="whiteSpace=wrap;html=1;shape=mxgraph.basic.document;fillColor=#dae8fc;strokeColor=#6c8ebf;labelPosition=center;verticalLabelPosition=top;align=center;verticalAlign=bottom;fontStyle=1" vertex="1" parent="1">
<mxGeometry x="-485" y="1390" width="40" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-82" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-45" target="EmT_t4AcRQ52VDmL_bbM-75">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-470" y="535" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-83" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-45" target="EmT_t4AcRQ52VDmL_bbM-78">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-470" y="615" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-86" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-73" target="EmT_t4AcRQ52VDmL_bbM-76">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-470" y="775" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-87" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-73" target="EmT_t4AcRQ52VDmL_bbM-77">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-470" y="850" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-89" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-74" target="EmT_t4AcRQ52VDmL_bbM-79">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-470" y="1015" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-90" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-74" target="EmT_t4AcRQ52VDmL_bbM-80">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-470" y="1095" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-92" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-39" target="EmT_t4AcRQ52VDmL_bbM-71">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-555" y="1225" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-93" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-39" target="EmT_t4AcRQ52VDmL_bbM-49">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-555" y="1305" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-94" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="EmT_t4AcRQ52VDmL_bbM-39" target="EmT_t4AcRQ52VDmL_bbM-81">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="-555" y="1415" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-29" value="Configuration per Instance of interconnected Apps&amp;nbsp;" style="swimlane;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="-30" y="530" width="790" height="180" as="geometry">
<mxRectangle x="-30" y="530" width="330" height="30" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-13" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); font-size: 12px;&quot;&gt;&lt;b&gt;Instance 1 Config&lt;/b&gt;&lt;/p&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-29">
<mxGeometry x="10" y="60" width="130" height="100" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-14" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;&lt;span style=&quot;background-color: initial; text-align: left;&quot;&gt;domain&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;apps&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;instance configs&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-29">
<mxGeometry x="17.5" y="100" width="115" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-62" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); font-size: 12px;&quot;&gt;&lt;b&gt;Instance 2 Config&lt;/b&gt;&lt;/p&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-29">
<mxGeometry x="170" y="60" width="130" height="100" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-63" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;&lt;span style=&quot;background-color: initial; text-align: left;&quot;&gt;domain&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;apps&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;instance configs&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-29">
<mxGeometry x="177.5" y="100" width="115" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-64" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); font-size: 12px;&quot;&gt;&lt;b&gt;Instance 3 Config&lt;/b&gt;&lt;/p&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-29">
<mxGeometry x="325" y="60" width="130" height="100" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-65" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;&lt;span style=&quot;background-color: initial; text-align: left;&quot;&gt;domain&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;apps&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;instance configs&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-29">
<mxGeometry x="332.5" y="100" width="115" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-66" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); font-size: 12px;&quot;&gt;&lt;b&gt;Instance 5 Config&lt;/b&gt;&lt;/p&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-29">
<mxGeometry x="640" y="60" width="130" height="100" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-67" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;&lt;span style=&quot;background-color: initial; text-align: left;&quot;&gt;domain&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;apps&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;instance configs&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-29">
<mxGeometry x="647.5" y="100" width="115" height="50" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-68" value="&lt;p style=&quot;border-color: var(--border-color); color: rgb(0, 0, 0); font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(251, 251, 251); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot;&gt;&lt;/p&gt;&lt;p style=&quot;border-color: var(--border-color); font-size: 12px;&quot;&gt;&lt;b&gt;Instance 4 Config&lt;/b&gt;&lt;/p&gt;" style="rounded=1;whiteSpace=wrap;html=1;verticalAlign=top;horizontal=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-29">
<mxGeometry x="490" y="60" width="130" height="100" as="geometry" />
</mxCell>
<mxCell id="EmT_t4AcRQ52VDmL_bbM-69" value="&lt;pre style=&quot;text-align: left; font-size: 10px;&quot;&gt;&lt;span style=&quot;background-color: initial; text-align: left;&quot;&gt;domain&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;apps&lt;br&gt;&lt;/span&gt;&lt;span style=&quot;background-color: initial;&quot;&gt;instance configs&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="EmT_t4AcRQ52VDmL_bbM-29">
<mxGeometry x="497.5" y="100" width="115" height="50" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

BIN
config_structure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB