273 lines
9.7 KiB
Python
273 lines
9.7 KiB
Python
"""
|
|
Functionality:
|
|
- Check environment at startup
|
|
- Process config file overwrites from env var
|
|
- Stop startup on error
|
|
- python management.py ta_envcheck
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
from time import sleep
|
|
|
|
from common.src.env_settings import EnvironmentSettings
|
|
from django.core.management.base import BaseCommand, CommandError
|
|
from user.models import Account
|
|
|
|
LOGO = """
|
|
|
|
.... .....
|
|
...'',;:cc,. .;::;;,'...
|
|
..,;:cccllclc, .:ccllllcc;,..
|
|
..,:cllcc:;,'.',. ....'',;ccllc:,..
|
|
..;cllc:,'.. ...,:cccc:'.
|
|
.;cccc;.. ..,:ccc:'.
|
|
.ckkkOkxollllllllllllc. .,:::;. .,cclc;
|
|
.:0MMMMMMMMMMMMMMMMMMMX: .cNMMMWx. .;clc:
|
|
.;lOXK0000KNMMMMX00000KO; ;KMMMMMNl. .;ccl:,.
|
|
.;:c:'.....kMMMNo........ 'OMMMWMMMK: '::;;'.
|
|
....... .xMMMNl .dWMMXdOMMMO' ........
|
|
.:cc:;. .xMMMNc .lNMMNo.:XMMWx. .:cl:.
|
|
.:llc,. .:xxxd, ;KMMMk. .oWMMNl. .:llc'
|
|
.cll:. .;:;;:::,. 'OMMMK:';''kWMMK: .;llc,
|
|
.cll:. .,;;;;;;,. .,xWMMNl.:l:.;KMMMO' .;llc'
|
|
.:llc. .cOOOk; .lKNMMWx..:l:..lNMMWx. .:llc'
|
|
.;lcc,. .xMMMNc :KMMMM0, .:lc. .xWMMNl.'ccl:.
|
|
.cllc. .xMMMNc 'OMMMMXc...:lc...,0MMMKl:lcc,.
|
|
.,ccl:. .xMMMNc .xWMMMWo.,;;:lc;;;.cXMMMXdcc;.
|
|
.,clc:. .xMMMNc .lNMMMWk. .':clc:,. .dWMMW0o;.
|
|
.,clcc,. .ckkkx; .okkkOx, .';,. 'kKKK0l.
|
|
.':lcc:'..... . .. ..,;cllc,.
|
|
.,cclc,.... ....;clc;..
|
|
..,:,..,c:'.. ...';:,..,:,.
|
|
....:lcccc:;,'''.....'',;;:clllc,....
|
|
.'',;:cllllllccccclllllcc:,'..
|
|
...'',,;;;;;;;;;,''...
|
|
.....
|
|
|
|
"""
|
|
|
|
TOPIC = """
|
|
#######################
|
|
# Environment Setup #
|
|
#######################
|
|
|
|
"""
|
|
|
|
EXPECTED_ENV_VARS = [
|
|
"TA_USERNAME",
|
|
"TA_PASSWORD",
|
|
"ELASTIC_PASSWORD",
|
|
"ES_URL",
|
|
"TA_HOST",
|
|
]
|
|
FILE_FALLBACK = [
|
|
"ELASTIC_PASSWORD",
|
|
"TA_PASSWORD",
|
|
]
|
|
UNEXPECTED_ENV_VARS = {
|
|
"TA_UWSGI_PORT": "Has been replaced with 'TA_BACKEND_PORT'",
|
|
"REDIS_HOST": "Has been replaced with 'REDIS_CON' connection string",
|
|
"REDIS_PORT": "Has been consolidated in 'REDIS_CON' connection string",
|
|
"ENABLE_CAST": "That is now a toggle in setting and DISABLE_STATIC_AUTH",
|
|
}
|
|
INST = "https://github.com/tubearchivist/tubearchivist#installing-and-updating"
|
|
NGINX = "/etc/nginx/sites-available/default"
|
|
|
|
|
|
class Command(BaseCommand):
|
|
"""command framework"""
|
|
|
|
# pylint: disable=no-member
|
|
help = "Check environment before startup"
|
|
|
|
def handle(self, *args, **options):
|
|
"""run all commands"""
|
|
self.stdout.write(LOGO)
|
|
self.stdout.write(TOPIC)
|
|
self._additional_auth_vars_expectations()
|
|
self._expected_vars()
|
|
self._unexpected_vars()
|
|
self._elastic_user_overwrite()
|
|
self._ta_port_overwrite()
|
|
self._ta_backend_port_overwrite()
|
|
self._disable_static_auth()
|
|
self._create_superuser()
|
|
|
|
def _additional_auth_vars_expectations(self):
|
|
"""conditionally add additional expectations for auth modes"""
|
|
ldap_required_env = [
|
|
"TA_LDAP_SERVER_URI",
|
|
"TA_LDAP_BIND_DN",
|
|
"TA_LDAP_BIND_PASSWORD",
|
|
"TA_LDAP_USER_BASE",
|
|
"TA_LDAP_USER_FILTER",
|
|
]
|
|
_login_auth_mode = (
|
|
os.environ.get("TA_LOGIN_AUTH_MODE") or "single"
|
|
).casefold()
|
|
if _login_auth_mode == "local":
|
|
UNEXPECTED_ENV_VARS["TA_LDAP"] = (
|
|
"TA_LDAP is not valid with current auth mode"
|
|
)
|
|
UNEXPECTED_ENV_VARS["TA_ENABLE_AUTH_PROXY"] = (
|
|
"TA_ENABLE_AUTH_PROXY is not valid with current auth mode"
|
|
)
|
|
elif _login_auth_mode == "ldap":
|
|
EXPECTED_ENV_VARS.extend(ldap_required_env)
|
|
UNEXPECTED_ENV_VARS["TA_ENABLE_AUTH_PROXY"] = (
|
|
"TA_ENABLE_AUTH_PROXY is not valid with current auth mode"
|
|
)
|
|
elif _login_auth_mode == "forwardauth":
|
|
UNEXPECTED_ENV_VARS["TA_LDAP"] = (
|
|
"TA_LDAP is not valid with current auth mode"
|
|
)
|
|
elif _login_auth_mode == "ldap_local":
|
|
EXPECTED_ENV_VARS.extend(ldap_required_env)
|
|
UNEXPECTED_ENV_VARS["TA_ENABLE_AUTH_PROXY"] = (
|
|
"TA_ENABLE_AUTH_PROXY is not valid with current auth mode"
|
|
)
|
|
else:
|
|
if bool(os.environ.get("TA_LDAP")):
|
|
EXPECTED_ENV_VARS.extend(ldap_required_env)
|
|
UNEXPECTED_ENV_VARS["TA_ENABLE_AUTH_PROXY"] = (
|
|
"TA_ENABLE_AUTH_PROXY is not valid with current auth mode"
|
|
)
|
|
if bool(os.environ.get("TA_ENABLE_AUTH_PROXY")):
|
|
UNEXPECTED_ENV_VARS["TA_LDAP"] = (
|
|
"TA_LDAP is not valid with current auth mode"
|
|
)
|
|
|
|
def _expected_vars(self):
|
|
"""check if expected env vars are set"""
|
|
self.stdout.write("[1] checking expected env vars")
|
|
env = os.environ
|
|
for var in EXPECTED_ENV_VARS:
|
|
if var in env:
|
|
continue
|
|
|
|
if var in FILE_FALLBACK and f"{var}_FILE" in env:
|
|
continue
|
|
|
|
message = f" 🗙 expected env var {var} not set\n {INST}"
|
|
self.stdout.write(self.style.ERROR(message))
|
|
sleep(60)
|
|
raise CommandError(message)
|
|
|
|
message = " ✓ all expected env vars are set"
|
|
self.stdout.write(self.style.SUCCESS(message))
|
|
|
|
def _unexpected_vars(self):
|
|
"""check for unexpected env vars"""
|
|
self.stdout.write("[2] checking for unexpected env vars")
|
|
for var, message in UNEXPECTED_ENV_VARS.items():
|
|
if not os.environ.get(var):
|
|
continue
|
|
|
|
message = (
|
|
f" 🗙 unexpected env var {var} found\n"
|
|
f" {message} \n"
|
|
" see release notes for a list of all changes."
|
|
)
|
|
|
|
self.stdout.write(self.style.ERROR(message))
|
|
sleep(60)
|
|
raise CommandError(message)
|
|
|
|
message = " ✓ no unexpected env vars found"
|
|
self.stdout.write(self.style.SUCCESS(message))
|
|
|
|
def _elastic_user_overwrite(self):
|
|
"""check for ELASTIC_USER overwrite"""
|
|
self.stdout.write("[3] check ES user overwrite")
|
|
env = EnvironmentSettings.ES_USER
|
|
self.stdout.write(self.style.SUCCESS(f" ✓ ES user is set to {env}"))
|
|
|
|
def _ta_port_overwrite(self):
|
|
"""set TA_PORT overwrite for nginx"""
|
|
self.stdout.write("[4] check TA_PORT overwrite")
|
|
overwrite = EnvironmentSettings.TA_PORT
|
|
if not overwrite:
|
|
self.stdout.write(self.style.SUCCESS(" TA_PORT is not set"))
|
|
return
|
|
|
|
regex = re.compile(r"listen [0-9]{1,5}")
|
|
to_overwrite = f"listen {overwrite}"
|
|
changed = file_overwrite(NGINX, regex, to_overwrite)
|
|
if changed:
|
|
message = f" ✓ TA_PORT changed to {overwrite}"
|
|
else:
|
|
message = f" ✓ TA_PORT already set to {overwrite}"
|
|
|
|
self.stdout.write(self.style.SUCCESS(message))
|
|
|
|
def _ta_backend_port_overwrite(self):
|
|
"""set TA_BACKEND_PORT overwrite"""
|
|
self.stdout.write("[5] check TA_BACKEND_PORT overwrite")
|
|
overwrite = EnvironmentSettings.TA_BACKEND_PORT
|
|
if not overwrite:
|
|
message = " TA_BACKEND_PORT is not set"
|
|
self.stdout.write(self.style.SUCCESS(message))
|
|
return
|
|
|
|
# modify nginx conf
|
|
regex = re.compile(r"proxy_pass http://localhost:[0-9]{1,5}")
|
|
to_overwrite = f"proxy_pass http://localhost:{overwrite}"
|
|
changed = file_overwrite(NGINX, regex, to_overwrite)
|
|
|
|
if changed:
|
|
message = f" ✓ TA_BACKEND_PORT changed to {overwrite}"
|
|
else:
|
|
message = f" ✓ TA_BACKEND_PORT already set to {overwrite}"
|
|
|
|
self.stdout.write(self.style.SUCCESS(message))
|
|
|
|
def _disable_static_auth(self):
|
|
"""cast workaround, remove auth for static files in nginx"""
|
|
self.stdout.write("[7] check DISABLE_STATIC_AUTH overwrite")
|
|
overwrite = EnvironmentSettings.DISABLE_STATIC_AUTH
|
|
if not overwrite:
|
|
self.stdout.write(
|
|
self.style.SUCCESS(" DISABLE_STATIC_AUTH is not set")
|
|
)
|
|
return
|
|
|
|
regex = re.compile(r"[^\S\r\n]*auth_request /api/ping/;\n")
|
|
changed = file_overwrite(NGINX, regex, "")
|
|
if changed:
|
|
message = " ✓ process nginx to disable static auth"
|
|
else:
|
|
message = " ✓ static auth is already disabled in nginx"
|
|
|
|
self.stdout.write(self.style.SUCCESS(message))
|
|
|
|
def _create_superuser(self):
|
|
"""create superuser if not exist"""
|
|
self.stdout.write("[8] create superuser")
|
|
is_created = Account.objects.filter(is_superuser=True)
|
|
if is_created:
|
|
message = " superuser already created"
|
|
self.stdout.write(self.style.SUCCESS(message))
|
|
return
|
|
|
|
name = EnvironmentSettings.TA_USERNAME
|
|
password = EnvironmentSettings.TA_PASSWORD
|
|
Account.objects.create_superuser(name, password)
|
|
message = f" ✓ new superuser with name {name} created"
|
|
self.stdout.write(self.style.SUCCESS(message))
|
|
|
|
|
|
def file_overwrite(file_path, regex, overwrite):
|
|
"""change file content from old to overwrite, return true when changed"""
|
|
with open(file_path, "r", encoding="utf-8") as f:
|
|
file_content = f.read()
|
|
|
|
changed = re.sub(regex, overwrite, file_content)
|
|
if changed == file_content:
|
|
return False
|
|
|
|
with open(file_path, "w", encoding="utf-8") as f:
|
|
f.write(changed)
|
|
|
|
return True
|