#!/usr/bin/python3 ## A simple tool to configure services and create sql users/databases import os import subprocess import secrets import argparse # only allow aA-zZ, 0-9 and below "special" characters in passwords ALLOWED_CHARS = [ord('!'), ord('@'), ord('#'), ord('%'), ord('&')]\ + list(range(48,58))\ + list(range(64,90))\ + list(range(97,123)) # convert ints back to characters ALLOWED_CHARS = [chr(c) for c in ALLOWED_CHARS] def gen_pass(l): """gen_pass returns a password of length l from ALLOWED_CHARS""" password = "" for i in range(int(l)): password += secrets.choice(ALLOWED_CHARS) return password def get_var(key): """get_var() gets user input to confirm/replace empty keys""" while True: user_input = input( f"{key} is empty, enter value (leave blank for password):\n") if user_input == "": prompt = "provided value is empty, choose an option below\n"\ + "\t1) generate password\n"\ + "\t2) leave blank\n"\ + "\t3) back\n" choice = input(prompt) while choice not in ["1","2","3"]: print(f"error: {choice} unrecognized, please enter 1,2 or 3") choice = input(prompt) if choice == "1": return gen_pass(32) if choice == "2": return user_input if choice == "3": continue else: # run until user confirms input confirm = input(f"set {key} to {user_input} (y/N)?") if confirm in ["y", "Y"]: return user_input def parse_env(env_file): """parse_env returns a dictionary of env vars parsed from the base file :service: is a string of the service name to parse, must match folder name """ if os.path.isfile(env_file): print(f"{env_file} not found... skipping") return f = open(env_file) c = dict() for line in f: # skip comments if line[0] == "#": continue if line.isspace(): continue kv = line.strip('\n').split("=") key = kv[0] val = kv[1].strip('"') if val == "": val = get_var(key) c[key] = val return c def gen_env(kv): """gen_env takes in a dictionary and returns the formatted env file text as a string :kv: is a dictionary of strings mapping env var to value ex) "HOME": "/home/user/" """ env = "" for key, value in kv.items(): env += f"{key}=\"{value}\"\n" return env def gen_init(service, force=False): """gen_init takes in a service name and creates a service file""" service_file = f"init/{service}.service" # prevent overwrite if os.path.isfile(service_file) and not force: print(f"{service_file} already exists... skipping") return PWD = os.getenv("PWD") contents = f"""[Unit] Description=Starts {service} After=docker.service [Service] Type=oneshot RemainAfterExit=true WorkingDirectory={PWD}/{service} ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down [Install] WantedBy=multi-user.target""" f = open(service_file, 'w') f.write(contents) f.close() def enable_service(service, force=False): gen_init(service,force) subprocess.run(["sudo", "install", "-m", "644", f"init/{service}.service", "/etc/systemd/system/"]) subprocess.run(["sudo", "systemctl", "enable", f"{service}"]) subprocess.run(["sudo", "systemctl", "start", f"{service}"]) def config_service(service, force=False): """config_service processes service env vars to generate private .env file :service: is a string of the name of the folder to write the .env to also requires file service/env to source base config from :force: is an optional parameter to overwrite existing file default is False """ enable_service(service, force) priv_file = f"{service}/priv/env" # prevent overwrite if os.path.isfile(priv_file) and not force: print(f"{priv_file} already exists... skipping") return parse_env(priv_file) c = parse_env(f"{service}/env") subprocess.run(["mkdir", "-p", f"{service}/priv"]) f = open(priv_file, 'w') f.write(gen_env(c)) f.close() return c def main(): parser = argparse.ArgumentParser( description="takes in a service to configure a private .env file and generate sql users/databases") parser.add_argument('service', nargs="+", help="service to configure env for") parser.add_argument('-f', '--force', action="store_true", help="overwrite existing files") args = parser.parse_args() sql = "" for service in args.service: print(f"\nsetting up {service}...") c = config_service(service, args.force) print("success") main()