Add --url and --api-auth-token options to `lnt create` (#195)
Replace --hostname/--hostsuffix with a single --url option that sets
zorgURL directly, and add --api-auth-token to write the API token into
the generated config. This eliminates the need for callers to manually
append settings to lnt.cfg after creation.
Also simplify the generated WSGI template to not suggest that it should
be run manually, which is incorrect (use `lnt runserver` instead).
Fixes #180
diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh
index b1c6ea6..226084f 100755
--- a/docker/docker-entrypoint.sh
+++ b/docker/docker-entrypoint.sh
@@ -13,12 +13,8 @@
--wsgi lnt_wsgi.py \
--tmp-dir /tmp/lnt \
--db-dir "${DB_PATH}" \
- --default-db "${DB_NAME}"
- if [[ "${token}" == *"'"* ]]; then
- echo "Invalid API auth token containing single quote ('): that character is used as a delimiter in the config file"
- exit 1
- fi
- echo "api_auth_token = '${token}'" >> /var/lib/lnt/instance/lnt.cfg
+ --default-db "${DB_NAME}" \
+ --api-auth-token "${token}"
fi
# Run the server under gunicorn.
diff --git a/docs/tools.rst b/docs/tools.rst
index f88695f..8e396ff 100644
--- a/docs/tools.rst
+++ b/docs/tools.rst
@@ -110,6 +110,10 @@
The default server will have a sqlite3 database named *default*. You can
specify to use PostgreSQL using ``--db-dir postgresql://user@hostname``.
+ Use ``--url`` to set the externally-visible base URL of the server (used in
+ email reports). Use ``--api-auth-token`` to set the REST API authentication
+ token.
+
``lnt import <instance path> <file>+``
Import an LNT data file into a database. You can use ``--database`` to
select the database to write to. Note that by default this will also
diff --git a/lnt/lnttool/create.py b/lnt/lnttool/create.py
index 983fceb..e397087 100644
--- a/lnt/lnttool/create.py
+++ b/lnt/lnttool/create.py
@@ -1,5 +1,4 @@
import click
-import platform
kConfigVersion = (0, 1, 0)
@@ -34,7 +33,7 @@
secret_key = %(secret_key)r
# REST API authentication
-# api_auth_token = 'secret'
+%(api_auth_token_line)s
# The list of available databases, and their properties. At a minimum, there
# should be a 'default' entry for the default database.
@@ -61,17 +60,9 @@
"""
kWSGITemplate = """\
-#!%(python_executable)s
-# -*- Python -*-
-
import lnt.server.ui.app
-application = lnt.server.ui.app.App.create_standalone(
- %(cfg_path)r)
-
-if __name__ == "__main__":
- import werkzeug
- werkzeug.run_simple('%(hostname)s', 8000, application)
+application = lnt.server.ui.app.App.create_standalone(%(cfg_path)r)
"""
@@ -93,14 +84,14 @@
help="name for the default db")
@click.option("--secret-key", default=None,
help="secret key to use for this installation")
-@click.option("--hostname", default=platform.uname()[1], show_default=True,
- help="host name of the server")
-@click.option("--hostsuffix", default="perf", show_default=True,
- help="suffix at which WSGI app lives")
+@click.option("--url", default="http://localhost", show_default=True,
+ help="base URL of the server, used in email reports")
+@click.option("--api-auth-token", default=None,
+ help="authentication token for the REST API")
@click.option("--show-sql", is_flag=True,
help="show SQL statements executed during construction")
def action_create(instance_path, name, config, wsgi, tmp_dir, db_dir,
- profile_dir, default_db, secret_key, hostname, hostsuffix,
+ profile_dir, default_db, secret_key, url, api_auth_token,
show_sql):
"""create an LLVM nightly test installation
@@ -116,7 +107,6 @@
import logging
import os
import random
- import sys
init_logger(logging.INFO if show_sql else logging.WARNING,
show_sql=show_sql)
@@ -125,9 +115,8 @@
if os.path.exists(basepath):
raise SystemExit("error: invalid path: %r already exists" % basepath)
- hosturl = "http://%s/%s" % (hostname, hostsuffix)
+ hosturl = url
- python_executable = sys.executable
cfg_path = os.path.join(basepath, config)
tmp_path = os.path.join(basepath, tmp_dir)
wsgi_path = os.path.join(basepath, wsgi)
@@ -139,6 +128,11 @@
).hexdigest()
)
+ if api_auth_token is not None:
+ api_auth_token_line = "api_auth_token = %r" % api_auth_token
+ else:
+ api_auth_token_line = "# api_auth_token = 'secret'"
+
os.mkdir(instance_path)
os.mkdir(tmp_path)
os.mkdir(schemas_path)
@@ -172,7 +166,7 @@
print(' host URL : %s' % hosturl)
print()
print('You can execute:')
- print(' %s' % wsgi_path)
+ print(' lnt runserver %s' % basepath)
print('to test your installation with the builtin server.')
print()
print('For production use configure this application to run with any')
diff --git a/tests/lnttool/create.shtest b/tests/lnttool/create.shtest
new file mode 100644
index 0000000..82b41a4
--- /dev/null
+++ b/tests/lnttool/create.shtest
@@ -0,0 +1,75 @@
+# Test 'lnt create' end-to-end.
+
+###############################################################################
+# Default creation: verify the generated config and instance file hierarchy
+###############################################################################
+# RUN: rm -rf %t.instance
+# RUN: lnt create %t.instance > %t.create-output
+
+# -- Config file contains all expected fields with default values
+# RUN: filecheck --check-prefix CHECK-CFG %s < %t.instance/lnt.cfg
+
+# CHECK-CFG: config_version = (0, 1, 0)
+# CHECK-CFG: name = 'LNT'
+# CHECK-CFG: zorgURL = 'http://localhost'
+# CHECK-CFG: tmp_dir = 'lnt_tmp'
+# CHECK-CFG: db_dir = 'data'
+# CHECK-CFG: profile_dir = 'data/profiles'
+# CHECK-CFG: secret_key =
+# CHECK-CFG: # api_auth_token = 'secret'
+# CHECK-CFG: databases = {
+# CHECK-CFG-NEXT: 'default' : { 'path' : 'lnt.db' },
+# CHECK-CFG: nt_emailer = {
+
+# -- Directory structure is created
+# RUN: test -d %t.instance/lnt_tmp
+# RUN: test -d %t.instance/data
+# RUN: test -d %t.instance/schemas
+# RUN: test -f %t.instance/data/lnt.db
+
+###############################################################################
+# Make sure that custom options all take effect
+###############################################################################
+# RUN: rm -rf %t.custom
+# RUN: lnt create %t.custom \
+# RUN: --name "My LNT" \
+# RUN: --url https://example.com/lnt \
+# RUN: --api-auth-token mytoken \
+# RUN: --secret-key deadbeef \
+# RUN: --default-db custom.db \
+# RUN: --db-dir databases \
+# RUN: --tmp-dir mytmp \
+# RUN: --profile-dir my/profiles
+# RUN: filecheck --check-prefix CHECK-CUSTOM %s < %t.custom/lnt.cfg
+
+# CHECK-CUSTOM: name = 'My LNT'
+# CHECK-CUSTOM: zorgURL = 'https://example.com/lnt'
+# CHECK-CUSTOM: tmp_dir = 'mytmp'
+# CHECK-CUSTOM: db_dir = 'databases'
+# CHECK-CUSTOM: profile_dir = 'my/profiles'
+# CHECK-CUSTOM: secret_key = 'deadbeef'
+# CHECK-CUSTOM: api_auth_token = 'mytoken'
+# CHECK-CUSTOM: 'default' : { 'path' : 'custom.db' },
+
+# RUN: test -d %t.custom/mytmp
+# RUN: test -d %t.custom/databases
+# RUN: test -f %t.custom/databases/custom.db
+
+###############################################################################
+# Error: creating into an existing path fails.
+###############################################################################
+# RUN: rm -rf %t.exists && mkdir %t.exists
+# RUN: not lnt create %t.exists 2> %t.exists-err
+# RUN: filecheck --check-prefix CHECK-EXISTS %s < %t.exists-err
+
+# CHECK-EXISTS: error: invalid path: {{.*}} already exists
+
+###############################################################################
+# The generated instance can be loaded by lnt runserver (smoke test: just
+# verify that 'lnt import' can talk to the instance, which requires loading
+# the config and database).
+###############################################################################
+# RUN: lnt import %t.instance %{shared_inputs}/sample-a-small.plist --show-sample-count > %t.import-output
+# RUN: filecheck --check-prefix CHECK-IMPORT %s < %t.import-output
+
+# CHECK-IMPORT: Import succeeded.
diff --git a/tests/utils/with_temporary_instance.py b/tests/utils/with_temporary_instance.py
index 0611c8f..ab9581b 100755
--- a/tests/utils/with_temporary_instance.py
+++ b/tests/utils/with_temporary_instance.py
@@ -46,7 +46,13 @@
db_name = os.environ['LNT_TEST_DB_NAME']
# 1. Create the LNT instance.
- subprocess.check_call(['lnt', 'create', dest_dir, '--db-dir', db_uri, '--default-db', db_name])
+ subprocess.check_call([
+ 'lnt', 'create', dest_dir,
+ '--db-dir', db_uri,
+ '--default-db', db_name,
+ '--api-auth-token', 'test_token',
+ '--url', 'http://localhost/perf',
+ ])
# 2. Symlink schema YAML files into the instance.
script_dir = os.path.dirname(os.path.abspath(__file__))
@@ -58,13 +64,7 @@
os.path.join(schemas_dst, schema),
)
- # 3. Append test configuration to lnt.cfg.
- cfg_path = os.path.join(dest_dir, 'lnt.cfg')
- with open(cfg_path, 'a') as f:
- f.write("\napi_auth_token = \"test_token\"\n")
- f.write("zorgURL = 'http://localhost/perf'\n")
-
- # 4. Import JSON report files from each DATA_DIR (or individual file).
+ # 3. Import JSON report files from each DATA_DIR (or individual file).
for data_path in data_dirs:
if os.path.isdir(data_path):
json_files = sorted(glob.glob(os.path.join(data_path, '*.json')))
@@ -76,7 +76,7 @@
suite = data.get('schema', 'nts')
subprocess.check_call(['lnt', 'import', '-s', suite, '--merge', 'append', dest_dir, json_file])
- # 5. Exec the wrapped command.
+ # 4. Exec the wrapped command.
os.execvp(command[0], command)