Skip to content

Instantly share code, notes, and snippets.

@nosoop
Last active July 29, 2016 14:28
Show Gist options
  • Save nosoop/76f16e1ecfcf262d7598a2c9a737fe67 to your computer and use it in GitHub Desktop.
Save nosoop/76f16e1ecfcf262d7598a2c9a737fe67 to your computer and use it in GitHub Desktop.
Site configuration manager for nginx.
#!/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