complete rewrite

This commit is contained in:
Moritz 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
import subprocess
from tabulate import tabulate
import click
import dotenv
from jinja2 import Environment, FileSystemLoader
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):
@ -29,10 +35,115 @@ def read_config(filepath):
return yaml.safe_load(template.render())
INSTANCE = read_config("config.yml")
DEFAULTS = read_config("~/.abra/defaults.yml")
COMBINE = read_config(os.path.dirname(
os.path.realpath(__file__)) + "/combine.yml")
""" Get value from nested dicts, return None if one of the keys does not exists """
def get_value(dict, *keys):
_element = dict
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):
@ -46,6 +157,7 @@ def abra(*args, machine_output=False, ignore_error=False):
if process.stdout:
logging.debug(process.stdout.decode())
if process.returncode and not ignore_error:
breakpoint()
raise RuntimeError(
f'{" ".join(command)} \n STDOUT: \n {process.stdout.decode()} \n STDERR: {process.stderr.decode()}')
if machine_output:
@ -66,12 +178,12 @@ def write_env_header(path):
file.write(header + old_content)
def new_app(recipe):
domain = map_subdomain(recipe)
server = INSTANCE["server"] if INSTANCE.get(
"server") else INSTANCE["domain"]
print(f'create {recipe} config on {server} at {domain}')
path = get_env_path(recipe)
def new_app(recipe, domain, server):
path = get_env_path(server, domain)
if path.exists():
print(f'remove {path}')
path.unlink()
logging.info(f'create {recipe} config on {server} at {domain}')
out = abra("app", "new", recipe, "-n", "-s", server, "-D", domain)
if not "app has been created" in out:
raise RuntimeError(f'App "{recipe}" creation failed')
@ -80,71 +192,106 @@ def new_app(recipe):
logging.info(f'{recipe} created on {server} at {domain}')
def map_subdomain(recipe):
if ((INSTANCE.get('apps') and (app := INSTANCE['apps'].get(recipe)) and (subdomain := app.get('subdomain'))) or
((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"):
def update_configs(path, config):
if uncomment_keys := config.get("uncomment"):
uncomment(uncomment_keys, path, True)
if envs := configs.get("env"):
if envs := config.get("env"):
uncomment(envs.keys(), path)
for key, value in envs.items():
logging.debug(f'set {key}={value} in {path}')
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():
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):
# TODO parse json
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)
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):
logging.debug(f'Uncomment {keys} in {path}')
with open(path, "r") as file:
@ -179,8 +313,13 @@ def uncomment(keys, path, match_all=False):
file.write(line)
def insert_domains(path, app):
domain = map_subdomain(app)
def exchange_domains(instance_config, apps, path):
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}')
with open(path, "r") as file:
content = file.read()
@ -189,21 +328,15 @@ def insert_domains(path, app):
file.write(content)
def execute_cmds(app):
domain = map_subdomain(app)
all_cmds = []
if (app_conf := DEFAULTS.get(app)) and (cmds := app_conf.get("execute")):
all_cmds += cmds
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
def execute_cmds(app_config):
domain = app_config['app_domain']
if not (all_cmds:= app_config.get('execute')):
logging.info(f"No post deploy cmds for {domain}")
return
for cmd in all_cmds:
container = cmd.split()[0]
cmd = cmd.split()[1]
logging.info(f"Run '{cmd}' in {domain}:{container}")
print(f"Run '{cmd}' in {domain}:{container}")
if container == "local":
print(abra("app", "cmd", "--local", domain, cmd, ignore_error=True))
else:
@ -212,81 +345,138 @@ def execute_cmds(app):
@click.group()
@click.option('-l', '--log', 'loglevel')
@click.option('-c', '--config', 'config')
def cli(loglevel, config):
global INSTANCE
@click.option('-p', '--pool_path', 'pool_path')
@click.option('-c', '--config_path', 'config_path', default=".")
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:
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
raise ValueError('Invalid log level: %s' % loglevel)
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()
def init_server():
""" Initialize the server """
new_app("traefik")
new_app("backup-bot-two")
@click.option('-a', '--apps', multiple=True)
def setup(apps):
pass
@cli.command()
@click.option('-a', '--apps', multiple=True)
def setup_apps(apps):
""" Configure and connect the apps """
if not apps:
apps = INSTANCE["apps"]
for app in apps:
new_app(app)
if configs := DEFAULTS.get(app):
logging.info(f'set defaults for {app}')
set_configs(app, configs)
if configs := INSTANCE["apps"].get(app):
logging.info(f'set extra configs for {app}')
set_configs(app, configs)
for app in apps:
print(f'create connections for {app}')
connect_apps(app)
for app in apps:
logging.info(f'generate secrets for {app}')
generate_secrets(app)
def config(apps):
""" Configure the apps """
for instance, instance_config in CONFIGS.items():
if not apps:
apps = instance_config.keys()
for app in apps:
if app not in instance_config:
logging.error(f"Could not find any '{app}' configuration for instance {instance}")
exit(1)
app_config = instance_config[app]
domain = app_config['app_domain']
server = app_config["server"]
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:
if app not in instance_config:
logging.error(f"Could not find any '{app}' configuration for instance {instance}")
exit(1)
app_config = instance_config[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():
server = INSTANCE["server"] if INSTANCE.get("server") else INSTANCE["domain"]
deployed = abra("app", "ls", "-S", "-s", server, "-m", machine_output=True)
deployed_apps = filter(
lambda x: x["status"] == "deployed", deployed[server]["apps"])
deployed_domains = [app["domain"] for app in deployed_apps]
return deployed_domains
def get_deployed_apps(apps):
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_domains = [app["domain"] for app in deployed[server]["apps"] if app["status"] == "deployed"]
deployed_apps.extend(deployed_domains)
return deployed_apps
@cli.command()
@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 """
deployed_domains = get_deployed_apps()
for app in INSTANCE["apps"]:
domain = map_subdomain(app)
if (apps and app in apps) or (not apps and domain not in deployed_domains):
print(f'deploy {domain}')
print(abra("app", "deploy", "-C", "-n", domain))
logging.info(f'execute commands for {domain}')
execute_cmds(app)
deployed_domains = get_deployed_apps(apps)
for _, instance_config in CONFIGS.items():
if not apps:
apps = instance_config.keys()
for app in apps:
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}')
execute_cmds(app_config)
@cli.command()
@click.option('-a', '--apps', multiple=True)
def undeploy_apps(apps):
def undeploy(apps):
""" Undeploy all the apps """
deployed_domains = get_deployed_apps()
if not apps:
apps = INSTANCE["apps"]
for app in apps:
domain = map_subdomain(app)
print(f'undeploy {domain}')
if domain in deployed_domains:
deployed_domains = get_deployed_apps(apps)
for _, instance_config in CONFIGS.items():
if not apps:
apps = instance_config.keys()
for app in apps:
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(abra("app", "undeploy", "-n", domain))
@ -294,42 +484,74 @@ def undeploy_apps(apps):
@click.option('-a', '--apps', multiple=True)
def cmds(apps):
""" execute all post deploy cmds """
deployed_domains = get_deployed_apps()
if not apps:
apps = INSTANCE["apps"]
for app in apps:
domain = map_subdomain(app)
if domain in deployed_domains:
deployed_domains = get_deployed_apps(apps)
for _, instance_config in CONFIGS.items():
if not apps:
apps = instance_config.keys()
for app in apps:
app_config = instance_config[app]
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}')
execute_cmds(app)
execute_cmds(app_config)
@cli.command()
@click.option('-a', '--apps', multiple=True)
def purge_apps(apps):
def purge(apps):
""" Completely remove all the apps """
if not apps:
apps = INSTANCE["apps"]
for app in apps:
domain = map_subdomain(app)
logging.info(f'purge {domain}')
abra("app", "rm", "-n", domain)
print(f"{domain} purged")
# TODO: check for deployed apps
pool_apps = print_all_apps(apps)
domains = list(zip(*sum(pool_apps.values(), [])))[1]
if input(f"Do you really want to purge these apps? Type YES: ") == "YES":
for domain in domains:
logging.info(f'purge {domain}')
abra("app", "rm", "-n", domain)
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()
@click.option('-a', '--apps', multiple=True)
def purge_secrets(apps):
""" Remove all the apps secrets """
if not apps:
apps = INSTANCE["apps"]
for app in apps:
domain = map_subdomain(app)
logging.info(f'purge secrets from {domain}')
stored_secrets = abra("app", "secret", "ls", domain)
if "true" in stored_secrets:
abra("app", "secret", "rm", "-a", domain)
print(f"secrets removed for {domain}")
# TODO: check for deployed apps
pool_apps = print_all_apps(apps)
domains = list(zip(*sum(pool_apps.values(), [])))[1]
if input(f"Do you really want to purge the secrets for these apps? Type YES: ") == "YES":
for domain in domains:
logging.info(f'purge {domain}')
abra("app","secret" ,"rm", "-a", domain)
print(f"Secrets for {domain} purged")
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