182 lines
4.9 KiB
Python
Executable File
182 lines
4.9 KiB
Python
Executable File
#!/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()
|