Compare commits

...

20 Commits

Author SHA1 Message Date
c81588265b Small changes 2022-04-09 19:01:24 +02:00
55a487fed3 Confirmation of chosen preset self-service portal 2022-04-09 11:08:46 +02:00
777d0cfbd7 Added enum for self-service options 2022-04-09 10:54:13 +02:00
b546e03e37 Added more comments 2022-04-08 22:32:30 +02:00
6608e1216b Updated ansible.cfg template to attempt more retries 2022-04-08 22:32:13 +02:00
61428cf80a Formatted files using yapf 2022-04-08 21:22:16 +02:00
db13dfb04b Removed unnecessary comments 2022-04-08 21:21:34 +02:00
b99fe97267 Added comments 2022-04-08 21:19:18 +02:00
acda51a6b4 Added default values for taking input 2022-04-08 14:20:40 +02:00
cd736a2606 Fixed ansible collection requirement install 2022-04-08 14:20:22 +02:00
a1c7052e85 Added self-service-portal wrapper around CLI 2022-04-08 13:14:55 +02:00
efe5901a55 Refactored service.py and added recovery cmd 2022-04-07 14:02:04 +02:00
7ed3be574a Only shows section in vagrantfile when applicable 2022-04-07 14:01:41 +02:00
a330bb17d1 Added resource mngmnt option for machine types 2022-03-15 23:08:54 +01:00
4f3de27656 Removed README.md 2022-03-15 08:41:04 +01:00
7b00e6d893 Prevent accidental .ssh overwrite fuck up 2022-03-15 08:35:34 +01:00
63b959c8cd Better indication no postgres instance detected 2022-03-15 08:34:58 +01:00
04a7ff551c Fixed broken ansible package install_deps.sh 2022-03-15 08:30:07 +01:00
0b574e24bb Added annotations import to service.py 2022-03-14 22:53:20 +01:00
52b3dad70c Fixed install_deps.sh for ubuntu 20.04 2022-03-14 22:53:08 +01:00
8 changed files with 598 additions and 203 deletions

View File

@ -1,17 +0,0 @@
# infra-as-code
## Setup
In order to create customers a one-time setup has to be done. Execute the following command to get started:
```sh
./install_deps.sh
```
You can now start creating customers!
## Create customer
```sh
./self_service.sh
```
## Remove a customer
```sh
./rm_customer.sh $CUSTOMER_NAME
```

378
cli.py Executable file
View File

@ -0,0 +1,378 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import dataclasses
import sys
from os import path
from pathlib import Path
import os
import time
from typing import Any, DefaultDict, Iterable, Mapping
from jinja2 import Template
import itertools
import subprocess as sub
import shutil
import string
import secrets
from dataclasses import dataclass
TEMPLATE_DIR = "./templates"
#region util
def gen_pass(pass_length: int = 20) -> str:
"""Generates a simple password with the provided length
Args:
pass_length (int, optional): Length of password. Defaults to 20.
Returns:
str: The generated password.
"""
alphabet = string.ascii_letters + string.digits
password = "".join(secrets.choice(alphabet) for _ in range(pass_length))
return password
def check_positive(value: str) -> int:
"""`argparse` type helper to check whether a given input is a positive integer.
Args:
value (str): Input to validate
Raises:
Exception: Input is neither a decimal or positive
Returns:
int: The parsed input if valid
"""
ivalue = int(value)
if ivalue < 0:
raise Exception(f"Supplied number must be >= 0")
return ivalue
def encode_member(member: str, mapping: Mapping[str, Any]) -> str:
"""Encodes the member-entry of an inventory file with additional mappings.
Args:
member (str)
mapping (Mapping[str, Any])
Returns:
str: member with mapping encoded
"""
return member + " " + " ".join([f"{k}={v}" for k, v in mapping.items()])
def iter_ips(ip_format: str, start_octet: int):
"""Simple iterator for generating ip's
Args:
ip_format (str)
start_octet (int)
Yields:
Yeah idk too lazy too look up what the type annotation for a generator is
"""
ip_int = start_octet
ip_fmt = ip_format
while ip_int < 255:
yield ip_fmt.format(ip_int)
ip_int += 1
def copy_template(src: str, dest: str, mapping: Mapping[str, Any] = {}):
"""Templates and writes a template file using Jinja as templating engine.
Args:
src (str): content of the file
dest (str): place to write templated file to
mapping (Mapping[str, Any], optional): datamapping. Defaults to {}.
"""
c = Path(src).read_text()
t: Template = Template(c)
r = t.render(mapping)
Path(dest).write_text(r)
#endregion
#region models
@dataclass
class MachineResources:
cpus: int
mem: int
@staticmethod
def from_prompt() -> "MachineResources":
"""Generate `MachineResources` from prompt.
Raises:
Exception
Exception
Returns:
MachineResources
"""
cpus = input(
"How many processors would you like to assign (default=1): ")
if not cpus:
cpus = "1"
if not cpus.isdigit() or int(cpus) < 0:
raise Exception("Expected a postive amount of processors")
mem = input(
"How many megabytes of RAM would you like to assign (default=1024): "
)
if not mem:
mem = "1024"
if not mem.isdigit() or int(mem) < 0:
raise Exception("Expected a postive amount of memory")
return MachineResources(cpus=int(cpus), mem=int(mem))
class InventoryWriter:
"""
Helper class for generating Ansible inventory files.
"""
def __init__(self, location: str) -> None:
self._file_handle = Path(location)
self._groups: dict[str, set[str]] = DefaultDict(set)
def add(self, name: str, members: Iterable[str]):
self._groups[name] |= set(members)
def _build_group(self, name: str, members: set[str]):
fmt = f"[{name}]\n" + "\n".join(members)
return fmt
def flush(self):
txt = ""
for name, members in self._groups.items():
txt += self._build_group(name, members) + "\n\n"
self._file_handle.write_text(txt, encoding="utf8")
#endregion
#region CLI positional flows
def list_envs(args: argparse.Namespace):
try:
customer_path = path.join("customers", args.customer_name, "envs")
print(" ".join(os.listdir(customer_path)))
except FileNotFoundError:
print(f"Customer `{args.customer_name}` does not exist.")
def delete_env(args: argparse.Namespace):
for env in args.env_names:
env_path = path.join("customers", args.customer_name, "envs", env)
sub.call(["vagrant", "destroy", "-f"], cwd=env_path)
shutil.rmtree(env_path)
print(f"Deleted `{env}` from customer `{ args.customer_name}`")
def recover_env(args: argparse.Namespace):
for env in args.env_names:
env_path = path.join("customers", args.customer_name, "envs", env)
sub.call(["vagrant", "up"], cwd=env_path)
# Artificial sleep waiting for sshd on VM to correctly start
print("Waiting on virtual machines...")
time.sleep(5)
sub.call(["ansible-playbook", "../../../../site.yml"], cwd=env_path)
print(f"Recovered `{env}` from customer `{ args.customer_name}`")
def create_env(args: argparse.Namespace):
"""
Managing environments works in a declarative way and thus it can
also be used for updating.
"""
if (args.num_nginx_web + args.num_nginx_lb + args.num_postgres) == 0:
raise Exception("At least one item should be deployed")
env_path = path.join("customers", args.customer_name, "envs",
args.env_name)
Path(env_path).mkdir(exist_ok=True, parents=True)
vagrant_mapping: dict[str, Any] = {
"webserver_specs": None,
"loadbalancers_specs": None,
"postgres_specs": None,
"num_webserver": args.num_nginx_web,
"num_loadbalancers": args.num_nginx_lb,
"num_postgres": args.num_postgres,
"env": args.env_name,
"customer_name": args.customer_name,
"ip_int": args.ip_int,
"ip_format": args.ip_format.replace("{}", "%d"),
}
if args.num_nginx_web > 0:
print("\nNginx webserver resources:")
web_specs = MachineResources.from_prompt()
vagrant_mapping["webserver_specs"] = dataclasses.asdict(web_specs)
if args.num_nginx_lb > 0:
print("\nNginx loadbalancer resources: ")
lb_specs = MachineResources.from_prompt()
vagrant_mapping["loadbalancers_specs"] = dataclasses.asdict(lb_specs)
if args.num_postgres > 0:
print("\nPostgresql machine resources: ")
psql_specs = MachineResources.from_prompt()
vagrant_mapping["postgres_specs"] = dataclasses.asdict(psql_specs)
# Template `ansible.cfg`
src = path.join(TEMPLATE_DIR, "ansible.cfg.template")
dest = path.join(env_path, "ansible.cfg")
copy_template(src=src, dest=dest)
# Create inventory file
inv_path = path.join(env_path, "inventory")
iw = InventoryWriter(inv_path)
ip_generator = iter_ips(args.ip_format, args.ip_int)
web_ips = itertools.islice(ip_generator, args.num_nginx_web)
iw.add("webserver", web_ips)
lb_ips = itertools.islice(ip_generator, args.num_nginx_lb)
iw.add("loadbalancer", lb_ips)
def psql_gen_pass(x: str) -> str:
return encode_member(x, {"psql_pass": gen_pass()})
psql_ips = list(itertools.islice(ip_generator, args.num_postgres))
psql_ips = map(psql_gen_pass, psql_ips)
iw.add("postgresql", psql_ips)
iw.flush()
# Template `Vagrantfile`
src = path.join(TEMPLATE_DIR, "Vagrantfile.template")
dest = path.join(env_path, "Vagrantfile")
should_reload = Path(dest).exists()
copy_template(src=src, dest=dest, mapping=vagrant_mapping)
# Generate .ssh if it doesn't exist already
ssh_dir = path.join(env_path, ".ssh")
Path(ssh_dir).mkdir(exist_ok=True)
rsa_path = path.join(ssh_dir, "id_rsa")
if not Path(rsa_path).exists():
print(end="\n")
sub.call([
"ssh-keygen",
"-t",
"rsa",
"-b",
"2048",
"-f",
rsa_path,
])
# Provision and configure machines
# Create VM's that do not exist yet
sub.call(["vagrant", "up"], cwd=env_path)
# Update VM's that already existed
if should_reload:
sub.call(["vagrant", "reload", "--provision"], cwd=env_path)
# Artificial sleep waiting for sshd on VM to correctly start
print("Waiting on virtual machines...")
time.sleep(5)
sub.call(["ansible-playbook", "../../../../site.yml"], cwd=env_path)
#endregion
def main() -> int:
parser = argparse.ArgumentParser()
sub_parser = parser.add_subparsers()
# CLI definition for positional arg "list"
list_parser = sub_parser.add_parser(
"list", help="list customer-owned environments")
list_parser.add_argument("customer_name",
type=str,
help="name of the customer")
list_parser.set_defaults(func=list_envs)
# CLI definition for positional arg "create"
cenv_parser = sub_parser.add_parser("create",
help="create/update an environment")
cenv_parser.add_argument("customer_name",
type=str,
help="name of the customer")
cenv_parser.add_argument("env_name",
type=str,
help="name of the environment")
cenv_parser.add_argument(
"--num-postgres",
type=check_positive,
help="number of postgres databases",
default=0,
)
cenv_parser.add_argument(
"--num-nginx-web",
type=check_positive,
help="number of nginx webservers",
default=0,
)
cenv_parser.add_argument(
"--num-nginx-lb",
type=check_positive,
help="number of nginx loadbalancers",
default=0,
)
cenv_parser.add_argument("--ip-format",
type=str,
help="format of ip",
default="192.168.56.{}")
cenv_parser.add_argument("--ip-int",
type=check_positive,
help="4th octet to start at",
default="10")
cenv_parser.set_defaults(func=create_env)
# CLI definition for positional arg "delete"
denv_parser = sub_parser.add_parser("delete", help="delete an environment")
denv_parser.add_argument("customer_name",
type=str,
help="name of the customer")
denv_parser.add_argument("env_names",
type=str,
nargs="+",
help="name of one or more environments")
denv_parser.set_defaults(func=delete_env)
# CLI definition for positional arg "recover"
denv_parser = sub_parser.add_parser("recover",
help="attempts to recover an env")
denv_parser.add_argument("customer_name",
type=str,
help="name of the customer")
denv_parser.add_argument("env_names",
type=str,
nargs="+",
help="name of one or more environments")
denv_parser.set_defaults(func=recover_env)
# Parse args
args = parser.parse_args(sys.argv[1:])
args.func(args)
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash
sudo apt-get update -y && sudo apt-get upgrade -y
sudo apt update --yes && sudo apt upgrade --yes
sudo apt install --yes virtualbox vagrant ansible
ansible-galaxy install -r ./requirements.yml
ansible-galaxy collection install -r requirements.yml

View File

@ -1,4 +1,4 @@
---
# ansible-galaxy requirements for this repository
collections:
- 'community.postgresql'
- community.postgresql

View File

@ -28,6 +28,8 @@
var_dump(pg_fetch_all($result));
?>
</p>
{% else %}
<p>No postgres instance detected.</p>
{% endif %}
</body>
</html>

372
service.py Executable file → Normal file
View File

@ -1,203 +1,225 @@
#!/usr/bin/env python3
import argparse
import sys
from os import path
from __future__ import annotations
from pathlib import Path
import os
import time
from typing import Any, Callable, DefaultDict, Iterable, Mapping
from jinja2 import Template
import itertools
from os import path
import subprocess as sub
import shutil
import string
import secrets
import re
from typing import Any, Callable
from enum import Enum
TEMPLATE_DIR = "./templates"
P_BANNER = """
██╗ ██╗███████╗██╗ ██╗ ██╗ ██████╗ ███╗ ███╗
██║ ██║██╔════╝██║ ██║ ██╔╝██╔═══██╗████╗ ████║
██║ █╗ ██║█████╗ ██║ █████╔╝ ██║ ██║██╔████╔██║
██║███╗██║██╔══╝ ██║ ██╔═██╗ ██║ ██║██║╚██╔╝██║
╚███╔███╔╝███████╗███████╗██║ ██╗╚██████╔╝██║ ╚═╝ ██║
╚══╝╚══╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
"""
P_OPTIONS = """
Opties:
0) Afsluiten
1) Maak/Wijzig omgeving(en)
2) Vernietig omgeving(en)
3) Recover omgeving(en)
4) List omgeving(en)
"""
class PortalOptions(Enum):
EXIT = "0"
CREATE = "1"
DESTROY = "2"
RECOVER = "3"
LIST = "4"
def gen_pass() -> str:
alphabet = string.ascii_letters + string.digits
password = "".join(secrets.choice(alphabet) for _ in range(20))
return password
RE_TEXT = re.compile(r"^\w+$")
RE_NUM = re.compile(r"^\d+$")
RE_ANY = re.compile(r".+")
def check_positive(value: str):
ivalue = int(value)
if ivalue <= 0:
raise Exception("Supplied number must be >= 0")
return ivalue
class Prompter:
def __init__(self, ps1: str = "") -> None:
self.ps1 = ps1
def encode_member(member: str, mapping: Mapping[str, Any]) -> str:
return member + " " + " ".join([f"{k}={v}" for k, v in mapping.items()])
def _build_prompt(self, prompt: str) -> str:
return self.ps1 + prompt
def take_input(self,
prompt: str,
pattern: re.Pattern[Any],
default: str = "") -> str:
prompt = self._build_prompt(prompt)
return Prompter.input(prompt, lambda x: bool(pattern.match(x)),
default)
def iter_ips(ip_format: str, start_octet: int):
ip_int = start_octet
ip_fmt = ip_format
while ip_int < 255:
yield ip_fmt.format(ip_int)
ip_int += 1
def take_choice(self, prompt: str, options: list[str]) -> str:
prompt = self._build_prompt(prompt)
return Prompter.input(prompt, lambda x: x in options)
def take_confirmation(self, prompt: str, default: str = "y") -> bool:
prompt = self._build_prompt(prompt)
i = Prompter.input(prompt, lambda x: x.lower() in ["y", "n"], default=default)
return i == "y"
class InventoryWriter:
def __init__(self, location: str) -> None:
self._file_handle = Path(location)
self._groups: dict[str, set[str]] = DefaultDict(set)
@staticmethod
def input(prompt: str,
validate: Callable[[str], bool],
default: str = "") -> str:
while True:
i = input(prompt)
if i == "":
i = default
if validate(i):
return i
def add(self, name: str, members: Iterable[str]):
self._groups[name] |= set(members)
def get_env_list(customer: str) -> list[str]:
"""Fetches and parses a list of a customer's environments
def _build_group(self, name: str, members: set[str]):
fmt = f"[{name}]\n" + "\n".join(members)
return fmt
Args:
customer (str): The customer to fetch environments from
def flush(self):
txt = ""
for name, members in self._groups.items():
txt += self._build_group(name, members) + "\n\n"
self._file_handle.write_text(txt, encoding="utf8")
def copy_template(src: str, dest: str, mapping: Mapping[str, Any] = {}):
c = Path(src).read_text()
t: Template = Template(c)
r = t.render(mapping)
Path(dest).write_text(r)
def list_envs(args: argparse.Namespace):
try:
customer_path = path.join("customers", args.customer_name, "envs")
print(" ".join(os.listdir(customer_path)))
except FileNotFoundError:
raise Exception(f"Customer `{args.customer_name}` does not exist.")
def delete_env(args: argparse.Namespace):
for env in args.env_names:
env_path = path.join("customers", args.customer_name, "envs", env)
sub.call(["vagrant", "destroy", "-f"], cwd=env_path)
shutil.rmtree(env_path)
print(f"Deleted `{env}` from customer `{ args.customer_name}`")
def create_env(args: argparse.Namespace):
if (args.num_nginx_web + args.num_nginx_lb + args.num_postgres) == 0:
raise Exception("At least one item should be deployed")
env_path = path.join("customers", args.customer_name, "envs", args.env_name)
Path(env_path).mkdir(exist_ok=True, parents=True)
# Template `ansible.cfg`
src = path.join(TEMPLATE_DIR, "ansible.cfg.template")
dest = path.join(env_path, "ansible.cfg")
copy_template(src=src, dest=dest)
# Create inventory file
inv_path = path.join(env_path, "inventory")
iw = InventoryWriter(inv_path)
ip_generator = iter_ips(args.ip_format, args.ip_int)
web_ips = itertools.islice(ip_generator, args.num_nginx_web)
iw.add("webserver", web_ips)
lb_ips = itertools.islice(ip_generator, args.num_nginx_lb)
iw.add("loadbalancer", lb_ips)
psql_gen_pass: Callable[[str], str] = lambda x: encode_member(
x, {"psql_pass": gen_pass()}
)
psql_ips = list(itertools.islice(ip_generator, args.num_postgres))
psql_ips = map(psql_gen_pass, psql_ips)
iw.add("postgresql", psql_ips)
iw.flush()
# Template `Vagrantfile`
src = path.join(TEMPLATE_DIR, "Vagrantfile.template")
dest = path.join(env_path, "Vagrantfile")
mapping = {
"env": args.env_name,
"customer_name": args.customer_name,
"ip_int": args.ip_int,
"ip_format": args.ip_format.replace("{}", "%d"),
"num_webserver": args.num_nginx_web,
"num_loadbalancers": args.num_nginx_lb,
"num_postgres": args.num_postgres,
}
copy_template(src=src, dest=dest, mapping=mapping)
# Generate .ssh
ssh_dir = path.join(env_path, ".ssh")
Path(ssh_dir).mkdir(exist_ok=True)
ssh_key_cmd = [
"ssh-keygen",
"-t",
"rsa",
"-b",
"2048",
"-f",
path.join(ssh_dir, "id_rsa"),
]
sub.call(ssh_key_cmd)
# Provision and configure machines
sub.call(["vagrant", "up"], cwd=env_path)
time.sleep(1)
sub.call(["ansible-playbook", "../../../../site.yml"], cwd=env_path)
Returns:
list[str]: The parsed environments
"""
return sub.check_output(["python3", "cli.py", "list",
customer]).decode().strip("\n").split(" ")
def main() -> int:
parser = argparse.ArgumentParser()
sub_parser = parser.add_subparsers()
print(P_BANNER)
customer_name = Prompter.input("Wat is uw naam: ",
lambda x: bool(RE_TEXT.match(x)))
"""
Ensures the necessary customer setup.
NOTE: The customer and the `envs` directory inside of it never get removed.
"""
envs_dir = path.join("customers", customer_name, "envs")
Path(envs_dir).mkdir(parents=True, exist_ok=True)
list_parser = sub_parser.add_parser("list", help="list customer-owned environments")
list_parser.add_argument("customer_name", type=str, help="name of the customer")
list_parser.set_defaults(func=list_envs)
p = Prompter(ps1=f"{customer_name} > ")
while True:
print(P_OPTIONS)
choice = p.take_input("Keuze: ", RE_ANY)
print(end="\n")
cenv_parser = sub_parser.add_parser("create", help="create a new environment")
cenv_parser.add_argument("customer_name", type=str, help="name of the customer")
cenv_parser.add_argument("env_name", type=str, help="name of the environment")
cenv_parser.add_argument(
"--num-postgres",
type=check_positive,
help="number of postgres databases",
default=0,
)
cenv_parser.add_argument(
"--num-nginx-web",
type=check_positive,
help="number of nginx webservers",
default=0,
)
cenv_parser.add_argument(
"--num-nginx-lb",
type=check_positive,
help="number of nginx loadbalancers",
default=0,
)
cenv_parser.add_argument(
"--ip-format", type=str, help="format of ip", default="192.168.56.{}"
)
cenv_parser.add_argument(
"--ip-int", type=check_positive, help="4th octet to start at", default=10
)
cenv_parser.set_defaults(func=create_env)
if choice == PortalOptions.EXIT.value:
"""
Used to break out of the loop and exit the portal.
"""
break
denv_parser = sub_parser.add_parser("delete", help="delete an environment")
denv_parser.add_argument("customer_name", type=str, help="name of the customer")
denv_parser.add_argument(
"env_names", type=str, nargs="+", help="name of one or more environments"
)
if choice == PortalOptions.CREATE.value:
"""
Used to either update or create an environment.
"""
envs = get_env_list(customer_name)
print(
f"NOTE: Kies een albestaande omgevingen {envs} of iets nieuws..."
)
fmt = "Omgevingsnaam: "
env_name = p.take_input(fmt, RE_TEXT)
denv_parser.set_defaults(func=delete_env)
# Loop until customer has confirmed their desired combination of machines
while True:
templates = ["production", "acceptance", "test", "custom"]
fmt = f"Het type omgeving {templates}: "
template_name = p.take_choice(fmt, templates)
if template_name == "custom":
"""
Asks for all machine's individually
"""
fmt = "Aantal nginx webservers (default=1): "
amnt_nginx_web = p.take_input(fmt, RE_NUM, default="1")
fmt = "Aantal nginx loadbalancers (default=1): "
amnt_nginx_lb = p.take_input(fmt, RE_NUM, default="1")
fmt = "Aantal postgres instances (default=1): "
amnt_psql = p.take_input(fmt, RE_NUM, default="1")
elif template_name == "production":
amnt_nginx_web = "2"
amnt_nginx_lb = "1"
amnt_psql = "1"
elif template_name == "acceptance":
amnt_nginx_web = "1"
amnt_nginx_lb = "0"
amnt_psql = "1"
# elif template_name == "test":
else:
amnt_nginx_web = "1"
amnt_nginx_lb = "0"
amnt_psql = "0"
# TODO: migrate different machine types to a dict model
print(end="\n")
print("Deze omgeving bevat:")
if amnt_nginx_web > "0":
print(f" - {amnt_nginx_web} Nginx webserver(s)")
if amnt_nginx_lb > "0":
print(f" - {amnt_nginx_lb} Nginx loadbalancer(s)")
if amnt_psql > "0":
print(f" - {amnt_psql} Postgres instance(s)")
print(end="\n")
if p.take_confirmation("Bevestig (Y/n): "):
break
"""
Define the format for templating ip-addresses.`{}` is required
and will be subbed at runtime with the correct octet.
"""
print(end="\n")
fmt = "NOTE: `{}` will be replaced with the correct octet at runtime"
print(fmt)
fmt = "Format of ip (default=192.168.56.{}): "
ip_format = p.take_input(fmt, RE_ANY, default="192.168.56.{}")
fmt = "Number to start formatting the IP from (default=10): "
ip_int = p.take_input(fmt, RE_NUM, default="10")
sub.call([
"python3", "cli.py", "create", customer_name, env_name,
"--num-nginx-web", amnt_nginx_web, "--num-postgres", amnt_psql,
"--num-nginx-lb", amnt_nginx_lb, "--ip-format", ip_format,
"--ip-int", ip_int
])
print(f"Omgeving `{env_name}` successvol gemaakt.")
if choice == PortalOptions.DESTROY.value:
"""
Deletes all traces of the chosen environment.
"""
envs = get_env_list(customer_name)
fmt = f"Omgevingsnaam {envs}: "
env_name = p.take_choice(fmt, envs)
sub.call(["python3", "cli.py", "delete", customer_name, env_name])
if choice == PortalOptions.RECOVER.value:
"""
Allows the customer to "force" their environment into the desired `up-state`.
This `up-state` is a representation of the Vagrantfile, Ansible inventory and
.ssh directory of that specific environment. The .ssh directory is only
necessary in case of an existing storage-volume. Otherwise a new one
keypair can be manually created or created using option 1.
"""
envs = get_env_list(customer_name)
fmt = f"Omgevingsnaam {envs}: "
env_name = p.take_choice(fmt, envs)
sub.call(["python3", "cli.py", "recover", customer_name, env_name])
if choice == PortalOptions.LIST.value:
"""
This branch displays the customer's existing environments
"""
print("De volgende omgeving(en) zijn aanwezig: ")
for env in get_env_list(customer_name):
print(f" - {env}")
args = parser.parse_args(sys.argv[1:])
args.func(args)
return 0

View File

@ -10,11 +10,10 @@ Vagrant.configure("2") do |config|
config.ssh.insert_key = false
config.ssh.private_key_path = ["./.ssh/id_rsa","~/.vagrant.d/insecure_private_key"]
num_webserver = {{ num_webserver }}
num_loadbalancer = {{ num_loadbalancers }}
num_postgresql = {{ num_postgres }}
(1..num_webserver).each do |nth|
{% if webserver_specs is not none %}
(1..{{ num_webserver }}).each do |nth|
machine_id = "{{ customer_name }}-{{ env }}-web%d" % [nth]
machine_ip = increment_ip()
@ -26,14 +25,17 @@ Vagrant.configure("2") do |config|
web.vm.provision "file", source: "./.ssh/id_rsa.pub", destination: "~/.ssh/authorized_keys"
web.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
vb.memory = {{ webserver_specs["mem"] }}
vb.cpus = {{ webserver_specs["cpus"] }}
vb.gui = false
vb.name = machine_id
end
end
end
{% endif %}
(1..num_loadbalancer).each do |nth|
{% if loadbalancers_specs is not none %}
(1..{{ num_loadbalancers }}).each do |nth|
machine_id = "{{ customer_name }}-{{ env }}-lb%d" % [nth]
machine_ip = increment_ip()
@ -45,14 +47,17 @@ Vagrant.configure("2") do |config|
web.vm.provision "file", source: "./.ssh/id_rsa.pub", destination: "~/.ssh/authorized_keys"
web.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
vb.memory = {{ loadbalancers_specs["mem"] }}
vb.cpus = {{ loadbalancers_specs["cpus"] }}
vb.gui = false
vb.name = machine_id
end
end
end
{% endif %}
(1..num_postgresql).each do |nth|
{% if postgres_specs is not none %}
(1..{{ num_postgres }}).each do |nth|
machine_id = "{{ customer_name }}-{{ env }}-db%d" % [nth]
machine_ip = increment_ip()
@ -64,10 +69,12 @@ Vagrant.configure("2") do |config|
web.vm.provision "file", source: "./.ssh/id_rsa.pub", destination: "~/.ssh/authorized_keys"
web.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
vb.memory = {{ postgres_specs["mem"] }}
vb.cpus = {{ postgres_specs["cpus"] }}
vb.gui = false
vb.name = machine_id
end
end
end
{% endif %}
end

View File

@ -3,3 +3,6 @@ private_key_file = "./.ssh/id_rsa"
remote_user = "vagrant"
inventory = ./inventory
host_key_checking = False
[ssh_connection]
retries=2