Mark semiprivate methods in configurator

This commit is contained in:
yan
2015-04-17 22:18:56 -07:00
parent 8caf03dcbb
commit 61b98210f9
2 changed files with 58 additions and 111 deletions
@@ -35,6 +35,12 @@ class NginxConfigurator(object):
:ivar parser: Handles low level parsing :ivar parser: Handles low level parsing
:type parser: :class:`~letsencrypt.client.plugins.nginx.parser` :type parser: :class:`~letsencrypt.client.plugins.nginx.parser`
:ivar set save_files: Files that need to be saved
:ivar str save_notes: Human-readable config change notes
:ivar reverter: saves and reverts checkpoints
:type reverter: :class:`letsencrypt.client.reverter.Reverter`
:ivar tup version: version of Nginx :ivar tup version: version of Nginx
:ivar list vhosts: All vhosts found in the configuration :ivar list vhosts: All vhosts found in the configuration
(:class:`list` of (:class:`list` of
@@ -55,11 +61,14 @@ class NginxConfigurator(object):
""" """
self.config = config self.config = config
self.save_notes = ""
# Verify that all directories and files exist with proper permissions # Verify that all directories and files exist with proper permissions
if os.geteuid() == 0: if os.geteuid() == 0:
self.verify_setup() self._verify_setup()
# Files to save
self.save_files = set()
self.save_notes = ""
# Add name_server association dict # Add name_server association dict
self.assoc = dict() self.assoc = dict()
@@ -76,6 +85,7 @@ class NginxConfigurator(object):
self.reverter = reverter.Reverter(config) self.reverter = reverter.Reverter(config)
self.reverter.recovery_routine() self.reverter.recovery_routine()
# This is called in determine_authenticator and determine_installer
def prepare(self): def prepare(self):
"""Prepare the authenticator/installer.""" """Prepare the authenticator/installer."""
self.parser = parser.NginxParser( self.parser = parser.NginxParser(
@@ -84,13 +94,14 @@ class NginxConfigurator(object):
# Set Version # Set Version
if self.version is None: if self.version is None:
self.version = self.get_version() self.version = self._get_version()
# Get all of the available vhosts # Get all of the available vhosts
self.vhosts = self.get_virtual_hosts() self.vhosts = self._get_vhosts()
temp_install(self.config.nginx_mod_ssl_conf) temp_install(self.config.nginx_mod_ssl_conf)
# Entry point in main.py for installing cert
def deploy_cert(self, domain, cert, key, cert_chain=None): def deploy_cert(self, domain, cert, key, cert_chain=None):
"""Deploys certificate to specified virtual host. """Deploys certificate to specified virtual host.
@@ -154,7 +165,7 @@ class NginxConfigurator(object):
# Make sure vhost is enabled # Make sure vhost is enabled
if not vhost.enabled: if not vhost.enabled:
self.enable_site(vhost) self._enable_site(vhost)
####################### #######################
# Vhost parsing methods # Vhost parsing methods
@@ -172,7 +183,6 @@ class NginxConfigurator(object):
""" """
# Allows for domain names to be associated with a virtual host # Allows for domain names to be associated with a virtual host
# Client isn't using create_dn_server_assoc(self, dn, vh) yet
if target_name in self.assoc: if target_name in self.assoc:
return self.assoc[target_name] return self.assoc[target_name]
# Check for servernames/aliases for ssl hosts # Check for servernames/aliases for ssl hosts
@@ -191,7 +201,7 @@ class NginxConfigurator(object):
# Check for non ssl vhosts with servernames/aliases == "name" # Check for non ssl vhosts with servernames/aliases == "name"
for vhost in self.vhosts: for vhost in self.vhosts:
if not vhost.ssl and target_name in vhost.names: if not vhost.ssl and target_name in vhost.names:
vhost = self.make_vhost_ssl(vhost) vhost = self._make_vhost_ssl(vhost)
self.assoc[target_name] = vhost self.assoc[target_name] = vhost
return vhost return vhost
@@ -201,19 +211,6 @@ class NginxConfigurator(object):
return vhost return vhost
return None return None
def create_dn_server_assoc(self, domain, vhost):
"""Create an association between a domain name and virtual host.
Helps to choose an appropriate vhost
:param str domain: domain name to associate
:param vhost: virtual host to associate with domain
:type vhost: :class:`~letsencrypt.client.plugins.nginx.obj.VirtualHost`
"""
self.assoc[domain] = vhost
def get_all_names(self): def get_all_names(self):
"""Returns all names found in the Nginx Configuration. """Returns all names found in the Nginx Configuration.
@@ -243,7 +240,7 @@ class NginxConfigurator(object):
return all_names return all_names
# TODO: make "sites-available" a configurable directory # TODO: make "sites-available" a configurable directory
def get_virtual_hosts(self): def _get_vhosts(self):
"""Returns list of virtual hosts found in the Nginx configuration. """Returns list of virtual hosts found in the Nginx configuration.
:returns: List of :returns: List of
@@ -261,7 +258,7 @@ class NginxConfigurator(object):
return vhs return vhs
def make_vhost_ssl(self, nonssl_vhost): # pylint: disable=too-many-locals def _make_vhost_ssl(self, nonssl_vhost): # pylint: disable=too-many-locals
"""Makes an ssl_vhost version of a nonssl_vhost. """Makes an ssl_vhost version of a nonssl_vhost.
Duplicates vhost and adds default ssl options Duplicates vhost and adds default ssl options
@@ -296,7 +293,7 @@ class NginxConfigurator(object):
new_file.write(line) new_file.write(line)
new_file.write("</IfModule>\n") new_file.write("</IfModule>\n")
except IOError: except IOError:
logging.fatal("Error writing/reading to file in make_vhost_ssl") logging.fatal("Error writing/reading to file in _make_vhost_ssl")
sys.exit(49) sys.exit(49)
self.aug.load() self.aug.load()
@@ -356,12 +353,13 @@ class NginxConfigurator(object):
for vhost in self.vhosts: for vhost in self.vhosts:
if vhost.ssl: if vhost.ssl:
# TODO: get the cert, key, and conf file paths # TODO: get the cert, key, and conf file paths
pass
return c_k return c_k
##################### ##################################
# enhancement methods # enhancement methods (IInstaller)
##################### ##################################
def supported_enhancements(self): # pylint: disable=no-self-use def supported_enhancements(self): # pylint: disable=no-self-use
"""Returns currently supported enhancements.""" """Returns currently supported enhancements."""
return [] return []
@@ -386,10 +384,10 @@ class NginxConfigurator(object):
except errors.LetsEncryptConfiguratorError: except errors.LetsEncryptConfiguratorError:
logging.warn("Failed %s for %s", enhancement, domain) logging.warn("Failed %s for %s", enhancement, domain)
######################### ######################################
# Nginx server management # Nginx server management (IInstaller)
######################### ######################################
def is_site_enabled(self, avail_fp): def _is_site_enabled(self, avail_fp):
"""Checks to see if the given site is enabled. """Checks to see if the given site is enabled.
.. todo:: fix hardcoded sites-enabled, check os.path.samefile .. todo:: fix hardcoded sites-enabled, check os.path.samefile
@@ -407,7 +405,7 @@ class NginxConfigurator(object):
return False return False
def enable_site(self, vhost): def _enable_site(self, vhost):
"""Enables an available site, Nginx restart required. """Enables an available site, Nginx restart required.
.. todo:: This function should number subdomains before the domain vhost .. todo:: This function should number subdomains before the domain vhost
@@ -421,7 +419,7 @@ class NginxConfigurator(object):
:rtype: bool :rtype: bool
""" """
if self.is_site_enabled(vhost.filep): if self._is_site_enabled(vhost.filep):
return True return True
if "/sites-available/" in vhost.filep: if "/sites-available/" in vhost.filep:
@@ -470,7 +468,7 @@ class NginxConfigurator(object):
return True return True
def verify_setup(self): def _verify_setup(self):
"""Verify the setup to ensure safe operating environment. """Verify the setup to ensure safe operating environment.
Make sure that files/directories are setup with appropriate permissions Make sure that files/directories are setup with appropriate permissions
@@ -483,7 +481,7 @@ class NginxConfigurator(object):
le_util.make_or_verify_dir(self.config.work_dir, 0o755, uid) le_util.make_or_verify_dir(self.config.work_dir, 0o755, uid)
le_util.make_or_verify_dir(self.config.backup_dir, 0o755, uid) le_util.make_or_verify_dir(self.config.backup_dir, 0o755, uid)
def get_version(self): def _get_version(self):
"""Return version of Nginx Server. """Return version of Nginx Server.
Version is returned as tuple. (ie. 2.4.7 = (2, 4, 7)) Version is returned as tuple. (ie. 2.4.7 = (2, 4, 7))
@@ -539,9 +537,9 @@ class NginxConfigurator(object):
version=".".join(str(i) for i in self.version)) version=".".join(str(i) for i in self.version))
) )
###################################### ###################################################
# Wrapper functions for Reverter class # Wrapper functions for Reverter class (IInstaller)
###################################### ###################################################
def save(self, title=None, temporary=False): def save(self, title=None, temporary=False):
"""Saves all changes to the configuration files. """Saves all changes to the configuration files.
@@ -571,6 +569,7 @@ class NginxConfigurator(object):
os.rename(f + '.le', f) os.rename(f + '.le', f)
else: else:
logging.warn("Expected file %s to exist", tmpfile) logging.warn("Expected file %s to exist", tmpfile)
self.save_files.remove(f)
if title and not temporary: if title and not temporary:
self.reverter.finalize_checkpoint(title) self.reverter.finalize_checkpoint(title)
@@ -602,12 +601,13 @@ class NginxConfigurator(object):
self.reverter.view_config_changes() self.reverter.view_config_changes()
########################################################################### ###########################################################################
# Challenges Section # Challenges Section for IAuthenticator
########################################################################### ###########################################################################
def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use
"""Return list of challenge preferences.""" """Return list of challenge preferences."""
return [challenges.DVSNI] return [challenges.DVSNI]
# Entry point in main.py for performing challenges
def perform(self, achalls): def perform(self, achalls):
"""Perform the configuration related challenge. """Perform the configuration related challenge.
@@ -640,6 +640,7 @@ class NginxConfigurator(object):
return responses return responses
# called after challenges are performed
def cleanup(self, achalls): def cleanup(self, achalls):
"""Revert all challenges.""" """Revert all challenges."""
self._chall_out -= len(achalls) self._chall_out -= len(achalls)
+19 -73
View File
@@ -1,8 +1,12 @@
"""NginxParser is a member object of the NginxConfigurator class.""" """NginxParser is a member object of the NginxConfigurator class."""
import glob
import logging
import os import os
import re import re
import pyparsing
from letsencrypt.client import errors from letsencrypt.client import errors
from letsencrypt.client.plugins.nginx.nginxparser import dump, load
class NginxParser(object): class NginxParser(object):
@@ -10,22 +14,19 @@ class NginxParser(object):
:ivar str root: Normalized abosulte path to the server root :ivar str root: Normalized abosulte path to the server root
directory. Without trailing slash. directory. Without trailing slash.
:ivar dict parsed: Mapping of file paths to parsed trees
""" """
def __init__(self, aug, root, ssl_options): def __init__(self, root, ssl_options):
# Find configuration root and make sure augeas can parse it. self.parsed = {}
self.aug = aug
self.root = os.path.abspath(root) self.root = os.path.abspath(root)
self.loc = self._set_locations(ssl_options) self.loc = self._set_locations(ssl_options)
self._parse_file(self.loc["root"]) self._parse_file(self.loc["root"])
# Must also attempt to parse sites-available or equivalent # Must also attempt to parse sites-available or equivalent
# Sites-available is not included naturally in configuration # Sites-available is not included naturally in configuration
self._parse_file(os.path.join(self.root, "sites-available") + "/*") self._parse_file(os.path.join(self.root, "sites-available") + "/*.conf")
# This problem has been fixed in Augeas 1.0
self.standardize_excl()
def add_dir_to_ifmodssl(self, aug_conf_path, directive, val): def add_dir_to_ifmodssl(self, aug_conf_path, directive, val):
"""Adds directive and value to IfMod ssl block. """Adds directive and value to IfMod ssl block.
@@ -246,24 +247,19 @@ class NginxParser(object):
return regex return regex
def _parse_file(self, filepath): def _parse_file(self, filepath):
"""Parse file with Augeas """Parse file
Checks to see if file_path is parsed by Augeas
If filepath isn't parsed, the file is added and Augeas is reloaded
:param str filepath: Nginx config file path :param str filepath: Nginx config file path
""" """
# Test if augeas included file for Httpd.lens files = glob.glob(filepath)
# Note: This works for augeas globs, ie. *.conf for f in files:
inc_test = self.aug.match( try:
"/augeas/load/Httpd/incl [. ='%s']" % filepath) self.parsed[f] = load(open(f))
if not inc_test: except IOError:
# Load up files logging.warn("Could not parse file: %s" % f)
# This doesn't seem to work on TravisCI except pyparsing.ParseException:
# self.aug.add_transform("Httpd.lns", [filepath]) logging.warn("Could not parse file: %s" % f)
self._add_httpd_transform(filepath)
self.aug.load()
def _add_httpd_transform(self, incl): def _add_httpd_transform(self, incl):
"""Add a transform to Augeas. """Add a transform to Augeas.
@@ -286,38 +282,6 @@ class NginxParser(object):
self.aug.set("/augeas/load/Httpd/lens", "Httpd.lns") self.aug.set("/augeas/load/Httpd/lens", "Httpd.lns")
self.aug.set("/augeas/load/Httpd/incl", incl) self.aug.set("/augeas/load/Httpd/incl", incl)
def standardize_excl(self):
"""Standardize the excl arguments for the Httpd lens in Augeas.
Note: Hack!
Standardize the excl arguments for the Httpd lens in Augeas
Servers sometimes give incorrect defaults
Note: This problem should be fixed in Augeas 1.0. Unfortunately,
Augeas 0.10 appears to be the most popular version currently.
"""
# attempt to protect against augeas error in 0.10.0 - ubuntu
# *.augsave -> /*.augsave upon augeas.load()
# Try to avoid bad httpd files
# There has to be a better way... but after a day and a half of testing
# I had no luck
# This is a hack... work around... submit to augeas if still not fixed
excl = ["*.augnew", "*.augsave", "*.dpkg-dist", "*.dpkg-bak",
"*.dpkg-new", "*.dpkg-old", "*.rpmsave", "*.rpmnew",
"*~",
self.root + "/*.augsave",
self.root + "/*~",
self.root + "/*/*augsave",
self.root + "/*/*~",
self.root + "/*/*/*.augsave",
self.root + "/*/*/*~"]
for i, excluded in enumerate(excl, 1):
self.aug.set("/augeas/load/Httpd/excl[%d]" % i, excluded)
self.aug.load()
def _set_locations(self, ssl_options): def _set_locations(self, ssl_options):
"""Set default location for directives. """Set default location for directives.
@@ -326,7 +290,7 @@ class NginxParser(object):
""" """
root = self._find_config_root() root = self._find_config_root()
default = self._set_user_config_file(root) default = os.path.join(self.root, 'nginx.conf')
temp = os.path.join(self.root, "ports.conf") temp = os.path.join(self.root, "ports.conf")
if os.path.isfile(temp): if os.path.isfile(temp):
@@ -341,7 +305,7 @@ class NginxParser(object):
def _find_config_root(self): def _find_config_root(self):
"""Find the Nginx Configuration Root file.""" """Find the Nginx Configuration Root file."""
location = ["nginx2.conf", "httpd.conf"] location = ['nginx.conf']
for name in location: for name in location:
if os.path.isfile(os.path.join(self.root, name)): if os.path.isfile(os.path.join(self.root, name)):
@@ -350,24 +314,6 @@ class NginxParser(object):
raise errors.LetsEncryptNoInstallationError( raise errors.LetsEncryptNoInstallationError(
"Could not find configuration root") "Could not find configuration root")
def _set_user_config_file(self, root):
"""Set the appropriate user configuration file
.. todo:: This will have to be updated for other distros versions
:param str root: pathname which contains the user config
"""
# Basic check to see if httpd.conf exists and
# in hierarchy via direct include
# httpd.conf was very common as a user file in Nginx 2.2
if (os.path.isfile(os.path.join(self.root, 'httpd.conf')) and
self.find_dir(
case_i("Include"), case_i("httpd.conf"), root)):
return os.path.join(self.root, 'httpd.conf')
else:
return os.path.join(self.root, 'nginx2.conf')
def case_i(string): def case_i(string):
"""Returns case insensitive regex. """Returns case insensitive regex.