Repository: simphony/simphony-remote

Issue:
System user Spawner's config_file_path is not configurable
Which is not consistent with the VirtualUserSpawner and the documentation.
All it needs is a `config=True` in the traitlet's meta.

Comments:

Patch:
diff --git a/remoteappmanager/cli/remoteappdb/__main__.py b/remoteappmanager/cli/remoteappdb/__main__.py
index ab76d4f..e4144ab 100644
--- a/remoteappmanager/cli/remoteappdb/__main__.py
+++ b/remoteappmanager/cli/remoteappdb/__main__.py
@@ -94,6 +94,24 @@ def get_docker_client():
     return client
 
 
+def is_sqlitedb_url(db_url):
+    """Returns True if the url refers to a sqlite database"""
+    return db_url.startswith("sqlite:///")
+
+
+def sqlitedb_present(db_url):
+    """Checks if the db url is present.
+    Remote urls are always assumed to be present, so this method
+    concerns mostly sqlite databases."""
+
+    if not db_url.startswith("sqlite:///"):
+        raise ValueError("db_url {} does not refer to a "
+                         "sqlite database.".format(db_url))
+
+    path = sqlite_url_to_path(db_url)
+    return os.path.exists(path)
+
+
 class RemoteAppDBContext(object):
     def __init__(self, db_url):
         db_url = normalise_to_url(db_url)
@@ -120,11 +138,9 @@ def init(ctx):
     db_url = db.url
 
     # Check if the database already exists
-    if db_url.startswith("sqlite:///"):
-        path = sqlite_url_to_path(db_url)
-        if os.path.exists(path):
-            raise click.UsageError("Refusing to overwrite database "
-                                   "at {}".format(db_url))
+    if is_sqlitedb_url(db_url) and sqlitedb_present(db_url):
+        raise click.UsageError("Refusing to overwrite database "
+                               "at {}".format(db_url))
     db.reset()
 
 
@@ -133,9 +149,18 @@ def init(ctx):
 
 
 @cli.group()
-def user():
+@click.pass_context
+def user(ctx):
     """Subcommand to manage users."""
-    pass
+    db = ctx.obj.db
+    db_url = db.url
+
+    # sqlite driver for sqlalchemy creates an empty file on commit as a side
+    # effect. We don't want this creation to happen, so before attempting
+    # the creation we stop short if we already find out that the file is
+    # missing and cannot possibly be initialized.
+    if is_sqlitedb_url(db_url) and not sqlitedb_present(db_url):
+        raise click.UsageError("Could not find database at {}".format(db_url))
 
 
 @user.command()
@@ -228,9 +253,14 @@ def list(ctx, no_decoration, show_apps):
 
 
 @cli.group()
-def app():
+@click.pass_context
+def app(ctx):
     """Subcommand to manage applications."""
-    pass
+    db = ctx.obj.db
+    db_url = db.url
+
+    if is_sqlitedb_url(db_url) and not sqlitedb_present(db_url):
+        raise click.UsageError("Could not find database at {}".format(db_url))
 
 
 @app.command()  # noqa
diff --git a/remoteappmanager/spawner.py b/remoteappmanager/spawner.py
index a226fa9..8637879 100644
--- a/remoteappmanager/spawner.py
+++ b/remoteappmanager/spawner.py
@@ -20,7 +20,7 @@ class Spawner(LocalProcessSpawner):
     proxy = Any()
 
     #: The path of the configuration file for the cmd executable
-    config_file_path = Unicode()
+    config_file_path = Unicode(config=True)
 
     def __init__(self, **kwargs):
         super().__init__(**kwargs)


Test patch:
diff --git a/tests/cli/test_remoteappdb.py b/tests/cli/test_remoteappdb.py
index 004a99e..57db987 100644
--- a/tests/cli/test_remoteappdb.py
+++ b/tests/cli/test_remoteappdb.py
@@ -25,9 +25,23 @@ class TestRemoteAppDbCLI(TempMixin, unittest.TestCase):
 
         return result.exit_code, result.output
 
+    def test_is_sqlitedb_url(self):
+        self.assertTrue(remoteappdb.is_sqlitedb_url("sqlite:///foo.db"))
+        self.assertFalse(remoteappdb.is_sqlitedb_url("whatever:///foo.db"))
+
+    def test_sqlite_present(self):
+        self.assertTrue(remoteappdb.sqlitedb_present(
+            "sqlite:///"+self.db))
+        self.assertFalse(remoteappdb.sqlitedb_present(
+            "sqlite:///"+self.db+"whatever"))
+
     def test_init_command(self):
         self.assertTrue(os.path.exists(self.db))
 
+        # This should fail because the database is already present
+        exit_code, output = self._remoteappdb("init")
+        self.assertNotEqual(exit_code, 0)
+
     def test_user_create(self):
         _, out = self._remoteappdb("user create foo")
         self.assertEqual(out, "1\n")
@@ -174,3 +188,13 @@ class TestRemoteAppDbCLI(TempMixin, unittest.TestCase):
         _, out = self._remoteappdb("user list --show-apps --no-decoration")
         self.assertEqual(len(out.split('\n')), 2)
         self.assertNotIn("myapp", out)
+
+    def test_commands_noinit(self):
+        # Remove the conveniently setup database
+        os.remove(self.db)
+
+        exit_code, out = self._remoteappdb("user create foo")
+        self.assertNotEqual(exit_code, 0)
+
+        exit_code, out = self._remoteappdb("app create foo")
+        self.assertNotEqual(exit_code, 0)