Repository: simphony/simphony-remote

Issue:
remoteapprest app start error due to unfound `base_url`
```
(simremote)kit@kit-virtual-machine:jupyterhub$ remoteapprest app start 629b1d86d69bfb4b400dfee204f5e3a0
[W 160718 10:37:39 container:154] HOME (None) is not available for kit
[I 160718 10:37:39 container_manager:218] Got container image: simphonyproject/simphonic-mayavi
[I 2016-07-18 10:37:39.576 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-kit/[secret] (kit@172.16.253.129) 8.24ms
[E 160718 10:37:39 container_manager:417] Container 'remoteexec-kit-629b1d86d69bfb4b400dfee204f5e3a0' is gone
[I 160718 10:37:39 container_manager:260] Mounting these volumes: 
    /appdata/image_name/common -> /appdata
[I 160718 10:37:39 container_manager:293] Created container 'remoteexec-kit-629b1d86d69bfb4b400dfee204f5e3a0' (id: 2dcdd6621f8f736322969b25449d5c75566cf617632cb9481f865aeaf919b863) from image simphonyproject/simphonic-mayavi
[I 160718 10:37:39 container_manager:330] Started container 'remoteexec-kit-629b1d86d69bfb4b400dfee204f5e3a0' (id: 2dcdd6621f8f736322969b25449d5c75566cf617632cb9481f865aeaf919b863). Exported port reachable at 127.0.0.1:32769
[E 160718 10:37:39 rest_handler:71] Internal error during POST operation on containers
    Traceback (most recent call last):
      File "/mnt/hgfs/VM_shared/SimPhoNy/simphony-remote/remoteappmanager/rest/rest_handler.py", line 60, in post
        resource_id = yield res_handler.create(data)
      File "/home/kit/.virtualenv/simremote/lib/python3.4/site-packages/tornado/gen.py", line 1015, in run
        value = future.result()
      File "/home/kit/.virtualenv/simremote/lib/python3.4/site-packages/tornado/concurrent.py", line 237, in result
        raise_exc_info(self._exc_info)
      File "<string>", line 3, in raise_exc_info
      File "/home/kit/.virtualenv/simremote/lib/python3.4/site-packages/tornado/gen.py", line 1021, in run
        yielded = self.gen.throw(*exc_info)
      File "/mnt/hgfs/VM_shared/SimPhoNy/simphony-remote/remoteappmanager/restresources/container.py", line 38, in create
        yield self._wait_for_container_ready(container)
      File "/home/kit/.virtualenv/simremote/lib/python3.4/site-packages/tornado/gen.py", line 1015, in run
        value = future.result()
      File "/home/kit/.virtualenv/simremote/lib/python3.4/site-packages/tornado/concurrent.py", line 237, in result
        raise_exc_info(self._exc_info)
      File "<string>", line 3, in raise_exc_info
      File "/home/kit/.virtualenv/simremote/lib/python3.4/site-packages/tornado/gen.py", line 285, in wrapper
        yielded = next(result)
      File "/mnt/hgfs/VM_shared/SimPhoNy/simphony-remote/remoteappmanager/restresources/container.py", line 205, in _wait_for_container_ready
        url_path_join(self.application.command_line_config.base_url,
    AttributeError: 'CommandLineConfig' object has no attribute 'base_url'
```

Comments:

Patch:
diff --git a/remoteappmanager/db/csv_db.py b/remoteappmanager/db/csv_db.py
index 99227dc..7c8d6d9 100644
--- a/remoteappmanager/db/csv_db.py
+++ b/remoteappmanager/db/csv_db.py
@@ -44,6 +44,7 @@ Note
 """
 
 import csv
+import hashlib
 
 from remoteappmanager.db.interfaces import (
     ABCAccounting, ABCApplication, ABCApplicationPolicy)
@@ -132,9 +133,11 @@ class CSVAccounting(ABCAccounting):
 
                 # Save the configuration
                 # Note that we don't filter existing duplicate entry
+                mapping_id_prehex = '_'.join((application.image, str(count)))
                 self.all_records.setdefault(username, []).append(
-                    ('_'.join((application.image, str(count))),
-                     application, application_policy))
+                    (hashlib.md5(mapping_id_prehex.encode('u8')).hexdigest(),
+                     application,
+                     application_policy))
 
     def get_user_by_name(self, user_name):
         """ Return a CSVUser for a given user_name, or return
diff --git a/remoteappmanager/restresources/container.py b/remoteappmanager/restresources/container.py
index 864e1fd..3191c6b 100644
--- a/remoteappmanager/restresources/container.py
+++ b/remoteappmanager/restresources/container.py
@@ -202,7 +202,7 @@ class Container(Resource):
         server_url = "http://{}:{}{}/".format(
             container.ip,
             container.port,
-            url_path_join(self.application.command_line_config.base_url,
+            url_path_join(self.application.command_line_config.base_urlpath,
                           container.urlpath))
 
         yield _wait_for_http_server_2xx(


Test patch:
diff --git a/tests/db/abc_test_interfaces.py b/tests/db/abc_test_interfaces.py
index 9aa7dd5..7ac0201 100644
--- a/tests/db/abc_test_interfaces.py
+++ b/tests/db/abc_test_interfaces.py
@@ -1,5 +1,6 @@
 from abc import abstractmethod, ABCMeta
 import inspect as _inspect
+import string
 
 from remoteappmanager.db.interfaces import ABCApplication, ABCApplicationPolicy
 
@@ -77,3 +78,22 @@ class ABCTestDatabaseInterface(metaclass=ABCMeta):
 
             if temp:
                 self.fail('These are not expected: {}'.format(temp))
+
+    def test_get_apps_for_user_mapping_id_rest_compliant(self):
+        ''' Test if the mapping_id to be rest identifier complient '''
+        allowed_chars = set(string.ascii_letters+string.digits)
+        accounting = self.create_accounting()
+
+        for user in self.create_expected_users():
+            # should be ((mapping_id, Application, ApplicationPolicy),
+            #            (mapping_id, Application, ApplicationPolicy) ... )
+            actual_id_configs = accounting.get_apps_for_user(user)
+
+            if not actual_id_configs:
+                continue
+
+            for mapping_id, _, _ in actual_id_configs:
+                self.assertFalse(
+                    set(mapping_id) - allowed_chars,
+                    "mapping id should contain these characters only: {} "
+                    "Got : {}".format(allowed_chars, mapping_id))
diff --git a/tests/db/test_interfaces.py b/tests/db/test_interfaces.py
index 6b4b3a8..02ced52 100644
--- a/tests/db/test_interfaces.py
+++ b/tests/db/test_interfaces.py
@@ -27,9 +27,9 @@ class Accounting(ABCAccounting):
         return User(name=username)
 
     def get_apps_for_user(self, user):
-        return (('mapping_id1',
+        return (('abc1',
                  Application(image=user.name+'1'), ApplicationPolicy()),
-                ('mapping_id2',
+                ('abc2',
                  Application(image=user.name+'2'), ApplicationPolicy()))
 
 
diff --git a/tests/restmodel/test_container.py b/tests/restmodel/test_container.py
index f9862e9..ba87141 100644
--- a/tests/restmodel/test_container.py
+++ b/tests/restmodel/test_container.py
@@ -29,7 +29,7 @@ class TestContainer(AsyncHTTPTestCase):
         app.file_config = Mock()
         app.file_config.network_timeout = 5
         app.command_line_config = Mock()
-        app.command_line_config.base_url = "http://127.0.0.1:8000/"
+        app.command_line_config.base_urlpath = "/"
         app.reverse_proxy = Mock()
         app.reverse_proxy.add_container = mock_coro_factory()
         app.reverse_proxy.remove_container = mock_coro_factory()