Commits

Anonymous committed 713f7b2

Teach rbuild client to speak with API versioned rbuilders and non-versioned
ones alike. Also updated module imports to use non-deprecated module.

Comments (0)

Files changed (7)

 
 from conary.lib import util
 
-from rpath_common.proddef import api1 as proddef
+from rpath_proddef import api1 as proddef
 
 from rbuild import errors
 from rbuild import pluginapi

plugins/rebase.py

 """
 import os
 
-from rpath_common.proddef import api1 as proddef
+from rpath_proddef import api1 as proddef
 
 from rbuild.pluginapi import command
 from rbuild import pluginapi

rbuild/facade/__init__.py

             return xmlrpclib._Method.__call__(self, *args)
         except xmlrpclib.ProtocolError:
             eType, eValue, eTraceback = sys.exc_info()
-            eValue.url = eValue.url.__safe_str__()
+            # eValue.url = eValue.url.__safe_str__()
             try:
                 raise eType, eValue, eTraceback
             finally:

rbuild/facade/apifinder.py

+# Copyright (c) 2011 rPath, Inc.
+#
+# This program is distributed under the terms of the Common Public License,
+# version 1.0. A copy of this license should have been distributed with this
+# source file in a file called LICENSE. If it is not present, the license
+# is always available at http://www.rpath.com/permanent/licenses/CPL-1.0.
+#
+# This program is distributed in the hope that it will be useful, but
+# without any warranty; without even the implied warranty of merchantability
+# or fitness for a particular purpose. See the Common Public License for
+# full details.
+
+# NOTE -- this is (currently) a copy of the module in rpath-tools
+# at some point in the future this should be moved into a common
+# lib.
+
+import urllib2
+import urlparse
+import xml.etree.ElementTree as ET
+from collections import namedtuple
+
+ApiFinderResult = namedtuple('ApiFinderResult', ['version', 'url'])
+
+class ApiFinder(object):
+    '''
+    Determines an rBuilder service location in a API version agnostic
+    way.   Example: 
+
+    results = ApiFinder('dhcp244.eng.rpath.com').url('inventory')
+    results.version
+    results.url
+
+    The latest version matching the constraints will always be returned.
+
+    minVersion and maxVersion can be set to exclude using a future
+    version or a "too old" version. 
+
+    For SSL usage, for instance from rpath-register (which must proxy)
+    pass in an appropriate OpenerDirector otherwise urllib2 will be used, 
+    making things usuable with a RUS but sufficient for direct access.
+    '''
+
+    def __init__(self, server, opener=None, secure=False,
+        minVersion=0, maxVersion=99999):
+        self.server = server
+        self.tipData = None
+        self.secure = secure
+        self.opener = opener
+        if minVersion is None:
+            minVersion = 0
+        if maxVersion is None:
+            maxVersion = 999999
+        self.minVersion = minVersion
+        self.maxVersion = maxVersion
+
+    def _read(self, url):
+        ''' 
+        GET a URL, using the OpenerDirector if supplied.
+        '''
+        url = self._secureUrl(url)
+        if self.opener:
+            return self.opener.open(url).read()
+        else:
+            return urllib2.urlopen(url).read()
+
+    def _secureUrl(self, url):
+        '''
+        When running through a RUS, the self discovery
+        URLs need modification.
+        '''
+        if self.secure:
+            return url.replace("http://","https://")
+        return url
+
+    def url(self, purpose):
+        '''
+        Retrieves a url for a specific service function
+        '''
+        versioned = False
+        number = False
+        if self.tipData is None:
+            versioned, number, self.tipData = self._findTip()
+        doc = ET.fromstring(self.tipData) 
+        found = doc.find(purpose)
+        if found is None:
+            raise Exception("element not found: %s" % found)
+        result = None
+        if versioned:
+            result = found.attrib['id']
+        else:
+            result = found.attrib['href']
+        return ApiFinderResult(version=number, url=self._secureUrl(result))
+
+    def _bestVersion(self, versionElts):
+        '''
+        Returns (version, URL) for the closest matching version to the
+        specified version.  Will possibly try an older version, never
+        a newer version.
+        '''
+        versions = {}
+        # convert list of XML api_version elements
+        # who have attributes name='v1', name='v2', etc
+        # to a dict of version number -> elements
+        def toVersionNum(x):
+            return int(x.attrib['name'].replace('v',''))
+        versions = dict( [ (toVersionNum(x), x) for x in versionElts ] )
+        versionNums = versions.keys()
+        versionNums.sort()
+        versionNums.reverse()
+        for x in versionNums:
+            if x >= self.minVersion and x <= self.maxVersion:
+                return (x, versions[x].attrib['id'])
+        raise Exception("unable to find a compatible API version %s<x<%s" % (
+            self.minVersion, self.maxVersion))
+
+    def _findTip(self):
+        '''
+        Finds the document that describes the services endpoints.
+        Returns tuple of whether the API is versioned or not (v0)
+        and the data from that document.
+        '''
+        url = urlparse.urlunsplit(['http', self.server, 'api', None, None])
+
+        tipData = self._read(url)
+        doc = ET.fromstring(tipData)
+        number = 0
+        if doc.tag == 'api':
+            # API is versioned, return highest if multiple supported
+            tipUrl = None
+            versionsCol = doc.find("api_versions")
+            versions = versionsCol.findall("api_version")
+            number, tipUrl = self._bestVersion(versions)
+            tipData = self._read(tipUrl)
+            return (True, number, tipData)
+        elif doc.tag == 'rbuilderStatus':
+            # initial unversioned API, return this document directly
+            return (False, number, tipData)
+
+

rbuild/facade/rbuilderfacade.py

 from conary.lib import util
 from conary.lib.cfg import ConfigFile
 
-from rpath_common.proddef import api1 as proddef
+from rpath_proddef import api1 as proddef
 from rbuild import constants
 from rbuild import errors
 from rbuild import facade
+from apifinder import ApiFinder
 
 class _rBuilderConfig(ConfigFile):
     serverUrl = None
         _AbstractRbuilderClient.__init__(self, rbuilderUrl, user, pw, handle)
         scheme, _, _, host, port, path, query, fragment = util.urlSplit(rbuilderUrl)
         self._url = util.urlUnsplit((scheme, user, pw, host, port, path, query, fragment))
-        self._basePath = '/api'
 
+        server_port = host
+        if port is not None:
+            server_port = "%s:%s" % (host, port)
+        results = ApiFinder(server_port).url('')
+        self._basePath = results.url
         self._api = None
 
     @property

rbuild/productstore/abstract.py

 # full details.
 #
 
-from rpath_common.proddef import api1 as proddef
+from rpath_proddef import api1 as proddef
 
 from rbuild import errors
 from rbuild.internal.internal_types import WeakReference

rbuild/productstore/dirstore.py

 from conary.lib import cfg
 from conary.lib import cfgtypes
 
-from rpath_common.proddef import api1 as proddef
+from rpath_proddef import api1 as proddef
 
 from rbuild import errors
 from rbuild.productstore.abstract import ProductStore