Last active
July 29, 2016 14:28
-
-
Save nosoop/76f16e1ecfcf262d7598a2c9a737fe67 to your computer and use it in GitHub Desktop.
Site configuration manager for nginx.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# This script allows slightly easier management of nginx vhosts. | |
import sys, os, argparse, subprocess | |
SUDO_BIN = '/usr/bin/sudo' | |
NGINX_CONF = '/etc/nginx/' | |
SITES_AVAILABLE = 'sites-available/' | |
SITES_ENABLED = 'sites-enabled/' | |
resolved_links = [] | |
# Store list of resolved file paths from enabled sites | |
for file in os.listdir(NGINX_CONF + SITES_ENABLED): | |
filepath = os.path.join(os.path.dirname(NGINX_CONF + SITES_ENABLED), file) | |
resolved_links.append(os.path.realpath(filepath)) | |
def eprint(*args, **kwargs): | |
print(*args, file=sys.stderr, **kwargs) | |
def site_available_path(site): | |
return os.path.join(os.path.dirname(NGINX_CONF + SITES_AVAILABLE), site) | |
def site_mtime(site): | |
""" | |
Returns the mtime of the site's configuration. | |
""" | |
if site_available(site): | |
return os.path.getmtime(site_available_path(site)) | |
else: | |
return 0 | |
def site_available(site): | |
""" | |
Returns whether or not a vhost configuration file with ``site`` exists. | |
""" | |
return os.path.isfile(site_available_path(site)) | |
def site_enabled(site): | |
""" | |
Returns whether or not a vhost configuration file has a symlink pointing to it in | |
sites-enabled. The symlink file may have a different name than the vhost configuration file | |
that it links to. | |
""" | |
for link in resolved_links: | |
if site in os.path.basename(link): | |
return True | |
return False | |
def site_enable(site): | |
""" | |
Enables a vhost configuration by linking a copy of it to sites-enabled. | |
""" | |
available_dir = os.path.dirname(NGINX_CONF + SITES_AVAILABLE) | |
enabled_dir = os.path.dirname(NGINX_CONF + SITES_ENABLED) | |
linkpath = os.path.join(os.path.relpath(available_dir, enabled_dir), args.site) | |
destpath = os.path.join(os.path.dirname(NGINX_CONF + SITES_ENABLED), args.site) | |
return subprocess.call([ SUDO_BIN, "/bin/ln", "-s", linkpath, destpath]) == 0 | |
def site_disable(site): | |
""" | |
Deletes all symlinks that point to the specified configuration file from sites-enabled. | |
""" | |
removed_links = [] | |
for file in os.listdir(NGINX_CONF + SITES_ENABLED): | |
filepath = os.path.join(os.path.dirname(NGINX_CONF + SITES_ENABLED), file) | |
if os.path.basename(os.path.realpath(filepath)) == args.site: | |
returncode = subprocess.call([ SUDO_BIN, "/bin/rm", filepath]) | |
if returncode == 0: | |
removed_links.append(filepath) | |
return removed_links | |
def site_edit(site): | |
""" | |
Edits the specified configuration file. Will open to a blank file if it doesn't exist. | |
""" | |
filepath = os.path.join(os.path.dirname(NGINX_CONF + SITES_AVAILABLE), site) | |
returncode = subprocess.call([ SUDO_BIN, "-e", filepath]) | |
def site_import_stdin(site): | |
""" | |
Imports data from stdin to a new configuration file. | |
""" | |
p = subprocess.Popen([ SUDO_BIN, "/usr/bin/tee", site_available_path(args.site) ], | |
stdin=subprocess.PIPE, stdout=subprocess.PIPE) | |
output, error = p.communicate(sys.stdin.buffer.read()) | |
print('{} lines written to site config {}'.format( | |
output.decode("utf-8").count('\n'), args.site)) | |
def nginx_test(): | |
""" | |
Tests currently available configurations. | |
""" | |
subprocess.call([ SUDO_BIN, "/usr/sbin/nginx", "-t"]) | |
def sudo_will_prompt(): | |
""" | |
Checks if sudo will prompt for password. | |
""" | |
return subprocess.call([ SUDO_BIN, "-n", "true" ]) != 0 | |
def main(args): | |
if args.command == 'list': | |
# Iterate available sites and check if they have corresponding symlinks. | |
for file in os.listdir(NGINX_CONF + SITES_AVAILABLE): | |
site = os.path.basename(file) | |
if site_enabled(site): | |
print(' [ x ] {}'.format(site)) | |
else: | |
print(' [ ] {}'.format(site)) | |
elif args.command == 'test': | |
nginx_test() | |
elif args.site is None: | |
# Past this ``if`` statement should be actions that require a site to work with. | |
eprint('no site specified') | |
elif args.command == 'disable': | |
removed_links = site_disable(args.site) | |
for filepath in removed_links: | |
print('removed link {}'.format(filepath)) | |
elif args.command == 'enable': | |
if site_available(args.site): | |
if site_enabled(args.site): | |
eprint('site is already enabled') | |
elif site_enable(args.site): | |
print('created link for site {}'.format(args.site)) | |
else: | |
eprint('invalid site {}'.format(args.site)) | |
elif args.command == 'edit': | |
update_time = site_mtime(args.site) | |
site_edit(args.site) | |
if site_mtime(args.site) > update_time: | |
if site_enabled(args.site): | |
if sudo_will_prompt(): | |
# Took too long to edit the site, sudo will prompt again. | |
print('requesting elevated permission to test nginx config') | |
nginx_test() | |
elif args.command == 'export': | |
config = open(site_available_path(args.site)) | |
print(config.read()) | |
elif args.command == 'import': | |
if site_available(args.site): | |
eprint('site {} already exists, not overwriting'.format(args.site)) | |
elif sys.stdin.isatty(): | |
eprint('not allowed to read from terminal, pass the data through stdin') | |
else: | |
site_import_stdin(args.site) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser() | |
parser.add_argument('command', choices=['list', 'enable', 'disable', 'edit', 'test', 'export', 'import'], | |
metavar='command', help='action to take') | |
parser.add_argument('site', nargs='?', | |
help='configuration file in sites-available to modify') | |
args = parser.parse_args() | |
main(args) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment