Repository: vertexproject/synapse

Issue:
Support telepath aliasing for cryotanks
Let's say I have the following aliases file.

~/.syn/aliases.yaml
```
---
cryo: tcp://user:pass@host:port/cryocell
...
```

The telepath expansion when running `python -m synapse.tools.cryo.cat cryo/tank_name` doesn't work while `python -m synapse.tools.cryo.list cryo` does.

Could a change akin to the following be made to support cryo aliases?
```
diff --git a/synapse/telepath.py b/synapse/telepath.py
index 2f057177..590e7322 100644
--- a/synapse/telepath.py
+++ b/synapse/telepath.py
@@ -427,7 +427,12 @@ async def openurl(url, **opts):
     if url.find('://') == -1:
         newurl = alias(url)
         if newurl is None:
-            raise s_exc.BadUrl(f':// not found in [{url}] and no alias found!')
+            # Cryotank
+            cryo_alias, sep, tank = url.rpartition("/")
+            newurl = alias(cryo_alias)
+            if newurl is None:
+                raise s_exc.BadUrl(f':// not found in [{url}] and no alias found!')
+            newurl = sep.join((newurl, tank))
         url = newurl

     info = s_urlhelp.chopurl(url)
```



Discussion:

Patch:
diff --git a/synapse/telepath.py b/synapse/telepath.py
index 2f0571772..cf5131317 100644
--- a/synapse/telepath.py
+++ b/synapse/telepath.py
@@ -393,13 +393,43 @@ class Proxy(s_coro.Fini):
 def alias(name):
     '''
     Resolve a telepath alias via ~/.syn/aliases.yaml
+
+    Args:
+        name (str): Name of the alias to resolve.
+
+    Notes:
+        An exact match against the aliases will always be returned first.
+        If no exact match is found and the name contains a '/' in it, the
+        value before the slash is looked up and the remainder of the path
+        is joined to any result. This is done to support dynamic Telepath
+        share names.
+
+    Returns:
+        str: The url string, if present in the alias.  None will be returned
+        if there are no matches.
     '''
     path = s_common.getSynPath('aliases.yaml')
     if not os.path.isfile(path):
         return None
 
     conf = s_common.yamlload(path)
-    return conf.get(name)
+
+    # Is there an exact match - if so, return it.
+    url = conf.get(name)
+    if url:
+        return url
+
+    # Since telepath supports dynamic shared object access,
+    # slice a name at the first '/', look up using that value
+    # and then append the second value to it.
+    dynname = None
+    if '/' in name:
+        name, dynname = name.split('/', 1)
+    url = conf.get(name)
+    if url and dynname:
+        url = '/'.join([url, dynname])
+
+    return url
 
 @s_glob.synchelp
 async def openurl(url, **opts):


Test patch:
diff --git a/synapse/tests/test_telepath.py b/synapse/tests/test_telepath.py
index 70e2d1d3c..09bcee161 100644
--- a/synapse/tests/test_telepath.py
+++ b/synapse/tests/test_telepath.py
@@ -6,9 +6,11 @@ logger = logging.getLogger(__name__)
 
 import synapse.exc as s_exc
 import synapse.glob as s_glob
+import synapse.common as s_common
 import synapse.telepath as s_telepath
 
 import synapse.lib.share as s_share
+import synapse.lib.scope as s_scope
 import synapse.tests.common as s_test
 
 from synapse.tests.utils import SyncToAsyncCMgr
@@ -25,6 +27,13 @@ class CustomShare(s_share.Share):
     def boo(self, x):
         return x
 
+class Beep:
+    def __init__(self, path):
+        self.path = path
+
+    def beep(self):
+        return f'{self.path}: beep'
+
 class Foo:
 
     def __init__(self):
@@ -99,6 +108,20 @@ class TeleApi:
         return CustomShare(self.link, 42)
 
 class TeleAware(s_telepath.Aware):
+    def __init__(self):
+        s_telepath.Aware.__init__(self)
+        self.beeps = {}
+
+    def _initBeep(self, path):
+        beep = self.beeps.get(path)
+        if beep:
+            return beep
+        beep = Beep(path)
+        self.beeps[path] = beep
+        return beep
+
+    def onTeleOpen(self, link, path):
+        return self._initBeep(path[1])
 
     def getTeleApi(self, link, mesg):
         return TeleApi(self, link)
@@ -249,12 +272,16 @@ class TeleTest(s_test.SynTest):
 
         with self.getTestDmon() as dmon:
             dmon.share('woke', item)
-            proxy = dmon._getTestProxy('woke')
-            self.eq(10, proxy.getFooBar(20, 10))
+            with dmon._getTestProxy('woke') as proxy:
+                self.eq(10, proxy.getFooBar(20, 10))
+
+                # check a custom share works
+                obj = proxy.customshare()
+                self.eq(999, obj.boo(999))
 
-            # check a custom share works
-            obj = proxy.customshare()
-            self.eq(999, obj.boo(999))
+            # check that a dynamic share works
+            with dmon._getTestProxy('woke/up') as proxy:
+                self.eq('up: beep', proxy.beep())
 
     def test_telepath_auth(self):
 
@@ -282,3 +309,39 @@ class TeleTest(s_test.SynTest):
             host, port = dmon.listen('tcp://127.0.0.1:0/')
 
             self.raises(s_exc.BadMesgVers, s_telepath.openurl, 'tcp://127.0.0.1/', port=port)
+
+    def test_alias(self):
+        item = TeleAware()
+        name = 'item'
+
+        with self.getTestDmon() as dmon:
+            addr = dmon.listen('tcp://127.0.0.1:0')
+            dmon.share(name, item)
+            dirn = s_scope.get('dirn')
+
+            url = f'tcp://{addr[0]}:{addr[1]}/{name}'
+            beepbeep_alias = url + '/beepbeep'
+            aliases = {name: url,
+                       f'{name}/borp': beepbeep_alias}
+
+            with self.setSynDir(dirn):
+                fp = s_common.getSynPath('aliases.yaml')
+                s_common.yamlsave(aliases, fp)
+
+                # None existent aliases return None
+                self.none(s_telepath.alias('newp'))
+                self.none(s_telepath.alias('newp/path'))
+
+                # An exact match wins
+                self.eq(s_telepath.alias(name), url)
+                self.eq(s_telepath.alias(f'{name}/borp'), beepbeep_alias)
+                # Dynamic aliases are valid.
+                self.eq(s_telepath.alias(f'{name}/beepbeep'), beepbeep_alias)
+
+                with s_telepath.openurl(name) as prox:
+                    self.eq(10, prox.getFooBar(20, 10))
+
+                # Check to see that we can connect to an aliased name
+                # with a dynamic share attached to it.
+                with s_telepath.openurl(f'{name}/bar') as prox:
+                    self.eq('bar: beep', prox.beep())
diff --git a/synapse/tests/utils.py b/synapse/tests/utils.py
index 5647d715a..f59547bc7 100644
--- a/synapse/tests/utils.py
+++ b/synapse/tests/utils.py
@@ -798,6 +798,24 @@ class SynTest(unittest.TestCase):
 
         self.raises(exc, testfunc)
 
+    @contextlib.contextmanager
+    def setSynDir(self, dirn):
+        '''
+        Sets s_common.syndir to a specific directory and then unsets it afterwards.
+
+        Args:
+            dirn (str): Directory to set syndir to.
+
+        Notes:
+            This is to be used as a context manager.
+        '''
+        olddir = s_common.syndir
+        try:
+            s_common.syndir = dirn
+            yield None
+        finally:
+            s_common.syndir = olddir
+
     def eq(self, x, y, msg=None):
         '''
         Assert X is equal to Y