Index: scripts/slave/recipe_modules/gae_sdk/api.py |
diff --git a/scripts/slave/recipe_modules/gae_sdk/api.py b/scripts/slave/recipe_modules/gae_sdk/api.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..493f087d986e94690b34f0f03cdbfae3eb2f6861 |
--- /dev/null |
+++ b/scripts/slave/recipe_modules/gae_sdk/api.py |
@@ -0,0 +1,146 @@ |
+# Copyright 2016 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import collections |
+import re |
+ |
+from recipe_engine import recipe_api |
+ |
+ |
+class GaeSdkApi(recipe_api.RecipeApi): |
+ |
+ PLAT_PYTHON = 'python' |
+ PLAT_GO = 'go' |
+ |
+ # The Google Storage GAE SDK bucket base. All SDK packages are stored in here |
+ # under a basename + version ZIP file. |
+ GS_BUCKET_BASE = 'gs://appengine-sdks/featured' |
+ # The GS path to the "LATEST" YAML file. |
+ GS_VERSION_YAML = '%s/VERSION' % (GS_BUCKET_BASE,) |
+ |
+ # Hacky regex for the "release" YAML variable. |
+ RE_RELEASE = re.compile(r'^release:\s+"([^"]+)"$') |
+ |
+ # Map of {Platform => {Arch => (base, dirname)}}. Platform names are |
+ # "recipe_engine/platform" values. |
+ _PKG_MAP = { |
+ PLAT_PYTHON: { |
+ 'all': ('google_appengine_', 'google_appengine'), |
+ }, |
+ PLAT_GO: { |
+ 'linux-amd64': ('go_appengine_sdk_linux_amd64-', 'go_appengine'), |
+ 'linux-386': ('go_appengine_sdk_linux_386-', 'go_appengine'), |
+ 'mac-amd64': ('go_appengine_sdk_darwin_amd64-', 'go_appengine'), |
+ }, |
+ } |
+ |
+ # Map of architecture bitness to CIPD bitness suffix. |
+ _BITS_MAP = { |
+ 64: 'amd64', |
+ 32: '386', |
iannucci
2016/09/30 21:05:21
hm... I didn't realize it was 386 instead of x86.
dnj
2016/09/30 22:54:54
I've generally seen i386 / i686 vs. amd64 / x86_64
|
+ } |
+ |
+ |
+ class VersionParseError(Exception): |
+ pass |
+ |
+ |
+ class PackageNotFound(Exception): |
+ def __init__(self, plat, arch): |
+ super(GaeSdkApi.PackageNotFound, self).__init__( |
+ 'Package not found for %s on %s' % (plat, arch)) |
+ self.plat = plat |
+ self.arch = arch |
+ |
+ |
+ def __init__(self, *args, **kwargs): |
+ super(GaeSdkApi, self).__init__(*args, **kwargs) |
+ self._latest = None |
+ |
+ def _step_name(self, desc): |
+ return 'GAE SDK: %s' % (desc,) |
+ |
+ @property |
+ def latest_ref(self): |
+ return 'latest' |
+ |
+ def version_tag(self, version): |
+ return ('gae_sdk_version', version) |
+ |
+ def _package_spec(self, plat, arch): |
+ plat_dict = self._PKG_MAP.get(plat, {}) |
+ for a in (arch, 'all'): |
+ spec = plat_dict.get(a) |
+ if spec: |
+ download_base, dirname = spec |
+ pkg_name = 'infra/gae_sdk/%s/%s' % (plat, a) |
+ return pkg_name, download_base, dirname |
+ raise self.PackageNotFound(plat, arch) |
+ |
+ def package(self, plat, arch=None): |
+ arch = arch or '%s-%s' % ( |
+ self.m.platform.name, self._BITS_MAP[self.m.platform.bits]) |
+ pkg_name, _, _ = self._package_spec(plat, arch) |
+ return pkg_name |
+ |
+ @property |
+ def platforms(self): |
+ return sorted(self._PKG_MAP.keys()) |
+ |
+ @property |
+ def all_packages(self): |
+ for plat, arch_dict in sorted(self._PKG_MAP.items()): |
+ for arch in sorted(arch_dict.keys()): |
+ yield plat, arch |
+ |
+ def latest_upstream_version(self): |
+ if self._latest is None: |
+ step_result = self.m.gsutil.cat( |
+ self.GS_VERSION_YAML, |
+ name=self._step_name('Get Latest'), |
+ stdout=self.m.raw_io.output()) |
+ self._latest = self._parse_latest_yaml(step_result.stdout) |
+ step_result.presentation.step_text += ' %s' % (self._latest,) |
+ return self._latest |
+ |
+ @classmethod |
+ def _parse_latest_yaml(cls, text): |
+ # Rather than import a YAML parser, we will specifically search for the |
+ # string: |
+ # |
+ # release: "<version>" |
+ for line in text.splitlines(): |
+ m = cls.RE_RELEASE.match(line) |
+ if m: |
+ return m.group(1) |
+ raise cls.VersionParseError('Could not parse release version from YAML.') |
+ |
+ def download_and_unpack(self, plat, arch, version): |
iannucci
2016/09/30 21:05:21
can we move this to the packaging recipe? Otherwis
dnj
2016/09/30 22:54:54
Done.
|
+ # Get the package base for this OS. |
+ _, base, dirname = self._package_spec(plat, arch) |
+ name = '%s%s.zip' % (base, version) |
+ artifact_url = '%s/%s' % (self.GS_BUCKET_BASE, name) |
+ |
+ tdir = self.m.path.mkdtemp('gae_sdk') |
+ dst = tdir.join(name) # Store the ZIP file here. |
+ unzip_dir = tdir.join('unpack') # Unzip contents here. |
+ self.m.gsutil.download_url( |
+ artifact_url, |
+ dst, |
+ name=self._step_name('Download %s %s' % (plat, arch,))) |
+ self.m.zip.unzip( |
+ self._step_name('Unzip %s %s' % (plat, arch)), |
+ dst, |
+ unzip_dir, |
+ quiet=True) |
+ |
+ pkg_dir = unzip_dir.join(dirname) |
+ self.m.path.mock_add_paths(pkg_dir) |
+ assert self.m.path.exists(pkg_dir), ( |
+ 'Package directory [%s] does not exist' % (pkg_dir,)) |
+ return pkg_dir |
+ |
+ def fetch(self, plat, dst): |
iannucci
2016/09/30 21:05:21
it should somehow be made clear that this is reall
dnj
2016/09/30 22:54:54
Done.
|
+ pkg = self.package(plat) |
+ self.m.cipd.ensure(dst, {pkg: self.latest_ref}) |