Merge pull request #534 from kuba/440-no-cli

Dynamic dirs based on --config-dir and --work-dir (#440 without touching CLI)
This commit is contained in:
James Kasten 2015-06-24 19:06:33 -04:00
commit 04440179f4
17 changed files with 169 additions and 113 deletions

View file

@ -257,8 +257,8 @@ class Client(object):
:param certr: ACME "certificate" resource.
:type certr: :class:`acme.messages.Certificate`
:param str cert_path: Path to attempt to save the cert file
:param str chain_path: Path to attempt to save the chain file
:param str cert_path: Candidate path to a certificate.
:param str chain_path: Candidate path to a certificate chain.
:returns: cert_path, chain_path (absolute paths to the actual files)
:rtype: `tuple` of `str`

View file

@ -17,10 +17,15 @@ class NamespaceConfig(object):
:attr:`~letsencrypt.interfaces.IConfig.work_dir` and relative
paths defined in :py:mod:`letsencrypt.constants`:
- ``temp_checkpoint_dir``
- ``in_progress_dir``
- ``cert_key_backup``
- ``rec_token_dir``
- `accounts_dir`
- `account_keys_dir`
- `cert_dir`
- `cert_key_backup`
- `in_progress_dir`
- `key_dir`
- `rec_token_dir`
- `renewer_config_file`
- `temp_checkpoint_dir`
:ivar namespace: Namespace typically produced by
:meth:`argparse.ArgumentParser.parse_args`.
@ -35,27 +40,12 @@ class NamespaceConfig(object):
def __getattr__(self, name):
return getattr(self.namespace, name)
@property
def temp_checkpoint_dir(self): # pylint: disable=missing-docstring
return os.path.join(
self.namespace.work_dir, constants.TEMP_CHECKPOINT_DIR)
@property
def in_progress_dir(self): # pylint: disable=missing-docstring
return os.path.join(self.namespace.work_dir, constants.IN_PROGRESS_DIR)
@property
def server_path(self):
"""File path based on ``server``."""
parsed = urlparse.urlparse(self.namespace.server)
return (parsed.netloc + parsed.path).replace('/', os.path.sep)
@property
def cert_key_backup(self): # pylint: disable=missing-docstring
return os.path.join(
self.namespace.work_dir, constants.CERT_KEY_BACKUP_DIR,
self.server_path)
@property
def accounts_dir(self): #pylint: disable=missing-docstring
return os.path.join(
@ -63,11 +53,40 @@ class NamespaceConfig(object):
@property
def account_keys_dir(self): #pylint: disable=missing-docstring
return os.path.join(
self.namespace.config_dir, constants.ACCOUNTS_DIR,
self.server_path, constants.ACCOUNT_KEYS_DIR)
return os.path.join(self.accounts_dir, constants.ACCOUNT_KEYS_DIR)
@property
def backup_dir(self): # pylint: disable=missing-docstring
return os.path.join(self.namespace.work_dir, constants.BACKUP_DIR)
@property
def cert_key_backup(self): # pylint: disable=missing-docstring
return os.path.join(self.namespace.work_dir,
constants.CERT_KEY_BACKUP_DIR, self.server_path)
@property
def cert_dir(self): # pylint: disable=missing-docstring
return os.path.join(self.namespace.config_dir, constants.CERT_DIR)
@property
def in_progress_dir(self): # pylint: disable=missing-docstring
return os.path.join(self.namespace.work_dir, constants.IN_PROGRESS_DIR)
@property
def key_dir(self): # pylint: disable=missing-docstring
return os.path.join(self.namespace.config_dir, constants.KEY_DIR)
# TODO: This should probably include the server name
@property
def rec_token_dir(self): # pylint: disable=missing-docstring
return os.path.join(self.namespace.work_dir, constants.REC_TOKEN_DIR)
@property
def renewer_config_file(self): # pylint: disable=missing-docstring
return os.path.join(
self.namespace.config_dir, constants.RENEWER_CONFIG_FILENAME)
@property
def temp_checkpoint_dir(self): # pylint: disable=missing-docstring
return os.path.join(
self.namespace.work_dir, constants.TEMP_CHECKPOINT_DIR)

View file

@ -7,7 +7,6 @@ from acme import challenges
SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins"
"""Setuptools entry point group name for plugins."""
CLI_DEFAULTS = dict(
config_files=["/etc/letsencrypt/cli.ini"],
verbose_count=-(logging.WARNING / 10),
@ -16,14 +15,16 @@ CLI_DEFAULTS = dict(
rollback_checkpoints=0,
config_dir="/etc/letsencrypt",
work_dir="/var/lib/letsencrypt",
backup_dir="/var/lib/letsencrypt/backups",
key_dir="/etc/letsencrypt/keys",
certs_dir="/etc/letsencrypt/certs",
cert_path="/etc/letsencrypt/certs/cert-letsencrypt.pem",
chain_path="/etc/letsencrypt/certs/chain-letsencrypt.pem",
renewer_config_file="/etc/letsencrypt/renewer.conf",
no_verify_ssl=False,
dvsni_port=challenges.DVSNI.PORT,
# TODO: blocked by #485, values ignored
backup_dir="not used",
key_dir="not used",
certs_dir="not used",
cert_path="not used",
chain_path="not used",
renewer_config_file="not used",
)
"""Defaults for CLI flags and `.IConfig` attributes."""
@ -61,23 +62,36 @@ List of expected options parameters:
CONFIG_DIRS_MODE = 0o755
"""Directory mode for ``.IConfig.config_dir`` et al."""
TEMP_CHECKPOINT_DIR = "temp_checkpoint"
"""Temporary checkpoint directory (relative to IConfig.work_dir)."""
IN_PROGRESS_DIR = "IN_PROGRESS"
"""Directory used before a permanent checkpoint is finalized (relative to
IConfig.work_dir)."""
CERT_KEY_BACKUP_DIR = "keys-certs"
"""Directory where all certificates and keys are stored (relative to
IConfig.work_dir. Used for easy revocation."""
ACCOUNTS_DIR = "accounts"
"""Directory where all accounts are saved."""
ACCOUNT_KEYS_DIR = "keys"
"""Directory where account keys are saved. Relative to ACCOUNTS_DIR."""
"""Directory where account keys are saved. Relative to `ACCOUNTS_DIR`."""
BACKUP_DIR = "backups"
"""Directory (relative to `IConfig.work_dir`) where backups are kept."""
CERT_KEY_BACKUP_DIR = "keys-certs"
"""Directory where all certificates and keys are stored (relative to
`IConfig.work_dir`). Used for easy revocation."""
CERT_DIR = "certs"
"""See `.IConfig.cert_dir`."""
IN_PROGRESS_DIR = "IN_PROGRESS"
"""Directory used before a permanent checkpoint is finalized (relative to
`IConfig.work_dir`)."""
KEY_DIR = "keys"
"""Directory (relative to `IConfig.config_dir`) where keys are saved."""
TEMP_CHECKPOINT_DIR = "temp_checkpoint"
"""Temporary checkpoint directory (relative to `IConfig.work_dir`)."""
REC_TOKEN_DIR = "recovery_tokens"
"""Directory where all recovery tokens are saved (relative to
IConfig.work_dir)."""
`IConfig.work_dir`)."""
RENEWER_CONFIG_FILENAME = "renewer.conf"
"""Renewer config file name (relative to `IConfig.config_dir`)."""

View file

@ -55,7 +55,7 @@ def init_save_key(key_size, key_dir, keyname="key-letsencrypt.pem"):
return le_util.Key(key_path, key_pem)
def init_save_csr(privkey, names, cert_dir, csrname="csr-letsencrypt.pem"):
def init_save_csr(privkey, names, path, csrname="csr-letsencrypt.pem"):
"""Initialize a CSR with the given private key.
:param privkey: Key to include in the CSR
@ -63,7 +63,7 @@ def init_save_csr(privkey, names, cert_dir, csrname="csr-letsencrypt.pem"):
:param set names: `str` names to include in the CSR
:param str cert_dir: Certificate save directory.
:param str path: Certificate save directory.
:returns: CSR
:rtype: :class:`letsencrypt.le_util.CSR`
@ -72,9 +72,9 @@ def init_save_csr(privkey, names, cert_dir, csrname="csr-letsencrypt.pem"):
csr_pem, csr_der = make_csr(privkey.pem, names)
# Save CSR
le_util.make_or_verify_dir(cert_dir, 0o755, os.geteuid())
le_util.make_or_verify_dir(path, 0o755, os.geteuid())
csr_f, csr_filename = le_util.unique_file(
os.path.join(cert_dir, csrname), 0o644)
os.path.join(path, csrname), 0o644)
csr_f.write(csr_pem)
csr_f.close()

View file

@ -155,32 +155,29 @@ class IConfig(zope.interface.Interface):
config_dir = zope.interface.Attribute("Configuration directory.")
work_dir = zope.interface.Attribute("Working directory.")
backup_dir = zope.interface.Attribute("Configuration backups directory.")
temp_checkpoint_dir = zope.interface.Attribute(
"Temporary checkpoint directory.")
in_progress_dir = zope.interface.Attribute(
"Directory used before a permanent checkpoint is finalized.")
cert_key_backup = zope.interface.Attribute(
"Directory where all certificates and keys are stored. "
"Used for easy revocation.")
accounts_dir = zope.interface.Attribute(
"Directory where all account information is stored.")
account_keys_dir = zope.interface.Attribute(
"Directory where all account keys are stored.")
backup_dir = zope.interface.Attribute("Configuration backups directory.")
cert_dir = zope.interface.Attribute(
"Directory where newly generated Certificate Signing Requests "
"(CSRs) and certificates not enrolled in the renewer are saved.")
cert_key_backup = zope.interface.Attribute(
"Directory where all certificates and keys are stored. "
"Used for easy revocation.")
in_progress_dir = zope.interface.Attribute(
"Directory used before a permanent checkpoint is finalized.")
key_dir = zope.interface.Attribute("Keys storage.")
rec_token_dir = zope.interface.Attribute(
"Directory where all recovery tokens are saved.")
key_dir = zope.interface.Attribute("Keys storage.")
cert_dir = zope.interface.Attribute("Certificates storage.")
le_vhost_ext = zope.interface.Attribute(
"SSL vhost configuration extension.")
temp_checkpoint_dir = zope.interface.Attribute(
"Temporary checkpoint directory.")
renewer_config_file = zope.interface.Attribute(
"Location of renewal configuration file.")
cert_path = zope.interface.Attribute("Let's Encrypt certificate file path.")
chain_path = zope.interface.Attribute("Let's Encrypt chain file path.")
no_verify_ssl = zope.interface.Attribute(
"Disable SSL certificate verification.")
dvsni_port = zope.interface.Attribute(
@ -191,6 +188,11 @@ class IConfig(zope.interface.Interface):
no_simple_http_tls = zope.interface.Attribute(
"Do not use TLS when solving SimpleHTTP challenges.")
# TODO: the following are not used, but blocked by #485
le_vhost_ext = zope.interface.Attribute("not used")
cert_path = zope.interface.Attribute("not used")
chain_path = zope.interface.Attribute("not used")
class IInstaller(IPlugin):
"""Generic Let's Encrypt Installer Interface.

View file

@ -175,10 +175,10 @@ class Dvsni(object):
# test utils
def setup_ssl_options(config_dir, mod_ssl_conf):
def setup_ssl_options(config_dir, src, dest):
"""Move the ssl_options into position and return the path."""
option_path = os.path.join(config_dir, "options-ssl.conf")
shutil.copyfile(mod_ssl_conf, option_path)
option_path = os.path.join(config_dir, dest)
shutil.copyfile(src, option_path)
return option_path

View file

@ -30,23 +30,31 @@ class NamespaceConfigTest(unittest.TestCase):
@mock.patch('letsencrypt.configuration.constants')
def test_dynamic_dirs(self, constants):
constants.TEMP_CHECKPOINT_DIR = 't'
constants.IN_PROGRESS_DIR = '../p'
constants.CERT_KEY_BACKUP_DIR = 'c/'
constants.REC_TOKEN_DIR = '/r'
constants.ACCOUNTS_DIR = 'acc'
constants.ACCOUNT_KEYS_DIR = 'keys'
constants.BACKUP_DIR = 'backups'
constants.CERT_KEY_BACKUP_DIR = 'c/'
constants.CERT_DIR = 'certs'
constants.IN_PROGRESS_DIR = '../p'
constants.KEY_DIR = 'keys'
constants.REC_TOKEN_DIR = '/r'
constants.RENEWER_CONFIG_FILENAME = 'r.conf'
constants.TEMP_CHECKPOINT_DIR = 't'
self.assertEqual(self.config.temp_checkpoint_dir, '/tmp/foo/t')
self.assertEqual(self.config.in_progress_dir, '/tmp/foo/../p')
self.assertEqual(
self.config.cert_key_backup, '/tmp/foo/c/acme-server.org:443/new')
self.assertEqual(self.config.rec_token_dir, '/r')
self.assertEqual(
self.config.accounts_dir, '/tmp/config/acc/acme-server.org:443/new')
self.assertEqual(
self.config.account_keys_dir,
'/tmp/config/acc/acme-server.org:443/new/keys')
self.assertEqual(self.config.backup_dir, '/tmp/foo/backups')
self.assertEqual(self.config.cert_dir, '/tmp/config/certs')
self.assertEqual(
self.config.cert_key_backup, '/tmp/foo/c/acme-server.org:443/new')
self.assertEqual(self.config.in_progress_dir, '/tmp/foo/../p')
self.assertEqual(self.config.key_dir, '/tmp/config/keys')
self.assertEqual(self.config.rec_token_dir, '/r')
self.assertEqual(self.config.renewer_config_file, '/tmp/config/r.conf')
self.assertEqual(self.config.temp_checkpoint_dir, '/tmp/foo/t')
if __name__ == '__main__':

View file

@ -89,8 +89,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
def add_parser_arguments(cls, add):
add("server-root", default=constants.CLI_DEFAULTS["server_root"],
help="Apache server root directory.")
add("mod-ssl-conf", default=constants.CLI_DEFAULTS["mod_ssl_conf"],
help="Contains standard Apache SSL directives.")
add("ctl", default=constants.CLI_DEFAULTS["ctl"],
help="Path to the 'apache2ctl' binary, used for 'configtest' and "
"retrieving Apache2 version number.")
@ -99,6 +97,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
add("init-script", default=constants.CLI_DEFAULTS["init_script"],
help="Path to the Apache init script (used for server "
"reload/restart).")
add("le-vhost-ext", default=constants.CLI_DEFAULTS["le_vhost_ext"],
help="SSL vhost configuration extension.")
def __init__(self, *args, **kwargs):
"""Initialize an Apache Configurator.
@ -125,10 +126,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
self.vhosts = None
self._enhance_func = {"redirect": self._enable_redirect}
@property
def mod_ssl_conf(self):
"""Full absolute path to SSL configuration file."""
return os.path.join(self.config.config_dir, constants.MOD_SSL_CONF_DEST)
def prepare(self):
"""Prepare the authenticator/installer."""
self.parser = parser.ApacheParser(
self.aug, self.conf('server-root'), self.conf('mod-ssl-conf'))
self.aug, self.conf('server-root'), self.mod_ssl_conf)
# Check for errors in parsing files with Augeas
self.check_parsing_errors("httpd.aug")
@ -146,7 +152,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# on initialization
self._prepare_server_https()
temp_install(self.conf('mod-ssl-conf'))
temp_install(self.mod_ssl_conf)
def deploy_cert(self, domain, cert_path, key_path, chain_path=None):
"""Deploys certificate to specified virtual host.
@ -445,7 +451,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""Makes an ssl_vhost version of a nonssl_vhost.
Duplicates vhost and adds default ssl options
New vhost will reside as (nonssl_vhost.path) + ``IConfig.le_vhost_ext``
New vhost will reside as (nonssl_vhost.path) +
``letsencrypt_apache.constants.CLI_DEFAULTS["le_vhost_ext"]``
.. note:: This function saves the configuration
@ -459,9 +466,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
avail_fp = nonssl_vhost.filep
# Get filepath of new ssl_vhost
if avail_fp.endswith(".conf"):
ssl_fp = avail_fp[:-(len(".conf"))] + self.config.le_vhost_ext
ssl_fp = avail_fp[:-(len(".conf"))] + self.conf("le_vhost_ext")
else:
ssl_fp = avail_fp + self.config.le_vhost_ext
ssl_fp = avail_fp + self.conf("le_vhost_ext")
# First register the creation so that it is properly removed if
# configuration is rolled back
@ -1161,4 +1168,4 @@ def temp_install(options_ssl):
# Check to make sure options-ssl.conf is installed
if not os.path.isfile(options_ssl):
shutil.copyfile(constants.MOD_SSL_CONF, options_ssl)
shutil.copyfile(constants.MOD_SSL_CONF_SRC, options_ssl)

View file

@ -4,15 +4,17 @@ import pkg_resources
CLI_DEFAULTS = dict(
server_root="/etc/apache2",
mod_ssl_conf="/etc/letsencrypt/options-ssl-apache.conf",
ctl="apache2ctl",
enmod="a2enmod",
init_script="/etc/init.d/apache2",
le_vhost_ext="-le-ssl.conf",
)
"""CLI defaults."""
MOD_SSL_CONF_DEST = "options-ssl-apache.conf"
"""Name of the mod_ssl config file as saved in `IConfig.config_dir`."""
MOD_SSL_CONF = pkg_resources.resource_filename(
MOD_SSL_CONF_SRC = pkg_resources.resource_filename(
"letsencrypt_apache", "options-ssl-apache.conf")
"""Path to the Apache mod_ssl config file found in the Let's Encrypt
distribution."""

View file

@ -32,8 +32,7 @@ class TwoVhost80Test(util.ApacheTest):
"mod_loaded") as mock_load:
mock_load.return_value = True
self.config = util.get_apache_configurator(
self.config_path, self.config_dir, self.work_dir,
self.ssl_options)
self.config_path, self.config_dir, self.work_dir)
self.vh_truth = util.get_vh_truth(
self.temp_dir, "debian_apache_2_4/two_vhost_80")

View file

@ -24,8 +24,7 @@ class DvsniPerformTest(util.ApacheTest):
"mod_loaded") as mock_load:
mock_load.return_value = True
config = util.get_apache_configurator(
self.config_path, self.config_dir, self.work_dir,
self.ssl_options)
self.config_path, self.config_dir, self.work_dir)
from letsencrypt_apache import dvsni
self.sni = dvsni.ApacheDvsni(config)

View file

@ -22,7 +22,8 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods
pkg="letsencrypt_apache.tests")
self.ssl_options = common.setup_ssl_options(
self.config_dir, constants.MOD_SSL_CONF)
self.config_dir, constants.MOD_SSL_CONF_SRC,
constants.MOD_SSL_CONF_DEST)
self.config_path = os.path.join(
self.temp_dir, "debian_apache_2_4/two_vhost_80/apache2")
@ -34,7 +35,7 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods
def get_apache_configurator(
config_path, config_dir, work_dir, ssl_options, version=(2, 4, 7)):
config_path, config_dir, work_dir, version=(2, 4, 7)):
"""Create an Apache Configurator with the specified options."""
backups = os.path.join(work_dir, "backups")
@ -46,8 +47,7 @@ def get_apache_configurator(
config = configurator.ApacheConfigurator(
config=mock.MagicMock(
apache_server_root=config_path,
apache_mod_ssl_conf=ssl_options,
le_vhost_ext="-le-ssl.conf",
apache_le_vhost_ext=constants.CLI_DEFAULTS["le_vhost_ext"],
backup_dir=backups,
config_dir=config_dir,
temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"),

View file

@ -56,8 +56,6 @@ class NginxConfigurator(common.Plugin):
def add_parser_arguments(cls, add):
add("server-root", default=constants.CLI_DEFAULTS["server_root"],
help="Nginx server root directory.")
add("mod-ssl-conf", default=constants.CLI_DEFAULTS["mod_ssl_conf"],
help="Contains standard nginx SSL directives.")
add("ctl", default=constants.CLI_DEFAULTS["ctl"], help="Path to the "
"'nginx' binary, used for 'configtest' and retrieving nginx "
"version number.")
@ -91,18 +89,22 @@ class NginxConfigurator(common.Plugin):
self.reverter = reverter.Reverter(self.config)
self.reverter.recovery_routine()
@property
def mod_ssl_conf(self):
"""Full absolute path to SSL configuration file."""
return os.path.join(self.config.config_dir, constants.MOD_SSL_CONF_DEST)
# This is called in determine_authenticator and determine_installer
def prepare(self):
"""Prepare the authenticator/installer."""
self.parser = parser.NginxParser(
self.conf('server-root'),
self.conf('mod-ssl-conf'))
self.conf('server-root'), self.mod_ssl_conf)
# Set Version
if self.version is None:
self.version = self.get_version()
temp_install(self.conf('mod-ssl-conf'))
temp_install(self.mod_ssl_conf)
# Entry point in main.py for installing cert
def deploy_cert(self, domain, cert_path, key_path, chain_path=None):
@ -583,4 +585,4 @@ def temp_install(options_ssl):
# Check to make sure options-ssl.conf is installed
if not os.path.isfile(options_ssl):
shutil.copyfile(constants.MOD_SSL_CONF, options_ssl)
shutil.copyfile(constants.MOD_SSL_CONF_SRC, options_ssl)

View file

@ -4,13 +4,15 @@ import pkg_resources
CLI_DEFAULTS = dict(
server_root="/etc/nginx",
mod_ssl_conf="/etc/letsencrypt/options-ssl-nginx.conf",
ctl="nginx",
)
"""CLI defaults."""
MOD_SSL_CONF = pkg_resources.resource_filename(
MOD_SSL_CONF_DEST = "options-ssl-nginx.conf"
"""Name of the mod_ssl config file as saved in `IConfig.config_dir`."""
MOD_SSL_CONF_SRC = pkg_resources.resource_filename(
"letsencrypt_nginx", "options-ssl-nginx.conf")
"""Path to the Nginx mod_ssl config file found in the Let's Encrypt
"""Path to the nginx mod_ssl config file found in the Let's Encrypt
distribution."""

View file

@ -21,8 +21,7 @@ class NginxConfiguratorTest(util.NginxTest):
super(NginxConfiguratorTest, self).setUp()
self.config = util.get_nginx_configurator(
self.config_path, self.config_dir, self.work_dir,
self.ssl_options)
self.config_path, self.config_dir, self.work_dir)
def tearDown(self):
shutil.rmtree(self.temp_dir)

View file

@ -51,8 +51,7 @@ class DvsniPerformTest(util.NginxTest):
super(DvsniPerformTest, self).setUp()
config = util.get_nginx_configurator(
self.config_path, self.config_dir, self.work_dir,
self.ssl_options)
self.config_path, self.config_dir, self.work_dir)
from letsencrypt_nginx import dvsni
self.sni = dvsni.NginxDvsni(config)

View file

@ -20,7 +20,8 @@ class NginxTest(unittest.TestCase): # pylint: disable=too-few-public-methods
"etc_nginx", "letsencrypt_nginx.tests")
self.ssl_options = common.setup_ssl_options(
self.config_dir, constants.MOD_SSL_CONF)
self.config_dir, constants.MOD_SSL_CONF_SRC,
constants.MOD_SSL_CONF_DEST)
self.config_path = os.path.join(self.temp_dir, "etc_nginx")
@ -38,18 +39,21 @@ def get_data_filename(filename):
def get_nginx_configurator(
config_path, config_dir, work_dir, ssl_options, version=(1, 6, 2)):
config_path, config_dir, work_dir, version=(1, 6, 2)):
"""Create an Nginx Configurator with the specified options."""
backups = os.path.join(work_dir, "backups")
config = configurator.NginxConfigurator(
config=mock.MagicMock(
nginx_server_root=config_path, nginx_mod_ssl_conf=ssl_options,
le_vhost_ext="-le-ssl.conf", backup_dir=backups,
config_dir=config_dir, work_dir=work_dir,
nginx_server_root=config_path,
le_vhost_ext="-le-ssl.conf",
config_dir=config_dir,
work_dir=work_dir,
backup_dir=backups,
temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"),
in_progress_dir=os.path.join(backups, "IN_PROGRESS")),
in_progress_dir=os.path.join(backups, "IN_PROGRESS"),
),
name="nginx",
version=version)
config.prepare()