Repository: jupyterhub/jupyterhub

Issue:
Jupyterhub Run via Sudo Tries to Create File
Hi, 

We are attempting to run the jupyterhub as rhea, and we are getting this permission error, because rhea doesn't have a home to write this file in. 

```
$ jupyterhub --LocalProcessSpawner.set_user=sudo --JupyterHubApp.ip=xxxxxx.xxxx --JupyterHubApp.port=80
Traceback (most recent call last):
  File "/usr/local/bin/jupyterhub", line 4, in <module>
    main()
  File "/home/ychen07/src/ipython/IPython/config/application.py", line 548, in launch_instance
    app.initialize(argv)
  File "<string>", line 2, in initialize
  File "/home/ychen07/src/ipython/IPython/config/application.py", line 74, in catch_config_error
    return method(app, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/jupyterhub/app.py", line 594, in initialize
    self.init_db()
  File "/usr/local/lib/python2.7/dist-packages/jupyterhub/app.py", line 349, in init_db
    **self.db_kwargs
  File "/usr/local/lib/python2.7/dist-packages/jupyterhub/orm.py", line 381, in new_session
    Base.metadata.create_all(engine)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/schema.py", line 3352, in create_all
    tables=tables)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1616, in _run_visitor
    with self._optional_conn_ctx_manager(connection) as conn:
  File "/usr/lib/python2.7/contextlib.py", line 17, in __enter__
    return self.gen.next()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1609, in _optional_conn_ctx_manager
    with self.contextual_connect() as conn:
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1799, in contextual_connect
    self.pool.connect(),
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/pool.py", line 338, in connect
    return _ConnectionFairy._checkout(self)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/pool.py", line 641, in _checkout
    fairy = _ConnectionRecord.checkout(pool)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/pool.py", line 440, in checkout
    rec = pool._do_get()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/pool.py", line 1118, in _do_get
    return self.connection
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/langhelpers.py", line 725, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/pool.py", line 1090, in connection
    return _ConnectionRecord(self)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/pool.py", line 411, in __init__
    self.connection = self.__connect()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/pool.py", line 537, in __connect
    connection = self.__pool._creator()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/strategies.py", line 96, in connect
    connection_invalidated=invalidated
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/compat.py", line 199, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/strategies.py", line 90, in connect
    return dialect.connect(*cargs, **cparams)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/default.py", line 377, in connect
    return self.dbapi.connect(*cargs, **cparams)
sqlalchemy.exc.OperationalError: (OperationalError) unable to open database file None None
```



Discussion:
That sounds like you ran it once as root, then another time with sudo in the same directory? It needs write access to `jupyterhub.sqlite`.  There are a few issues that prevent the `sudo` case from working properly right now.

On Fri, Oct 3, 2014 at 12:49 PM, Min RK notifications@github.com wrote:

> That sounds like you ran it once as root, then another time with sudo in
> the same directory? It needs write access to jupyterhub.sqlite. There are
> a few issues that prevent the sudo case from working properly right now.

No, we are using the sudo mode running as rhea... it cannot create files.

> —
> Reply to this email directly or view it on GitHub
> https://github.com/jupyter/jupyterhub/issues/57#issuecomment-57822272.

Are you running it in a directory owned by rhea (or otherwise where rhea has write permissions)? Otherwise, it doesn't have permission to create files.

On Fri, Oct 3, 2014 at 1:16 PM, Min RK notifications@github.com wrote:

> Are you running it in a directory owned by rhea? Otherwise, it doesn't
> have permission to create files.

No, rhea doesn't own anything. We could set it up that way (in a writable
dir), but that isn't suggested on the jupyterhub wiki, and is a change from
the way that it worked a couple of weeks ago.

> —
> Reply to this email directly or view it on GitHub
> https://github.com/jupyter/jupyterhub/issues/57#issuecomment-57825646.

Ah, that's right. It didn't used to persist state to disk. It does now, which allows any part of the Hub to go down (Hub, proxy, or single-user servers) and recover without having to restart the whole thing.

I'll try to clarify that in the docs and log output.

Ok, thanks! Is there a setting to make it run without a persistent file
(in-memory db, for example)? I think it is great to have, but not every
situation may want that.

On Fri, Oct 3, 2014 at 2:04 PM, Min RK notifications@github.com wrote:

> I'll try to clarify that in the docs and log output.
> 
> —
> Reply to this email directly or view it on GitHub
> https://github.com/jupyter/jupyterhub/issues/57#issuecomment-57831881.

```
jupyterhub --db ':memory:'
```


Resolving patch:
diff --git a/jupyterhub/app.py b/jupyterhub/app.py
--- a/jupyterhub/app.py
+++ b/jupyterhub/app.py
@@ -19,6 +19,8 @@
 
 from jinja2 import Environment, FileSystemLoader
 
+from sqlalchemy.exc import OperationalError
+
 import tornado.httpserver
 import tornado.options
 from tornado.ioloop import IOLoop, PeriodicCallback
@@ -347,11 +349,31 @@ def init_handlers(self):
             (r'(.*)', handlers.Template404),
         ])
     
+    def _check_db_path(self, path):
+        """More informative log messages for failed filesystem access"""
+        path = os.path.abspath(path)
+        parent, fname = os.path.split(path)
+        user = getpass.getuser()
+        if not os.path.isdir(parent):
+            self.log.error("Directory %s does not exist", parent)
+        if os.path.exists(parent) and not os.access(parent, os.W_OK):
+            self.log.error("%s cannot create files in %s", user, parent)
+        if os.path.exists(path) and not os.access(path, os.W_OK):
+            self.log.error("%s cannot edit %s", user, path)
+    
     def init_db(self):
         """Create the database connection"""
-        self.db = orm.new_session(self.db_url, reset=self.reset_db, echo=self.debug_db,
-            **self.db_kwargs
-        )
+        self.log.debug("Connecting to db: %s", self.db_url)
+        try:
+            self.db = orm.new_session(self.db_url, reset=self.reset_db, echo=self.debug_db,
+                **self.db_kwargs
+            )
+        except OperationalError as e:
+            self.log.error("Failed to connect to db: %s", self.db_url)
+            self.log.debug("Database error was:", exc_info=True)
+            if self.db_url.startswith('sqlite:///'):
+                self._check_db_path(self.db_url.split(':///', 1)[1])
+            self.exit(1)
     
     def init_hub(self):
         """Load the Hub config into the database"""


Test patch: