blob: bf168851796a5b1c46416a8485797e6e9674a7ab [file] [log] [blame]
Chris Sosa47a7d4e2012-03-28 11:26:55 -07001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Module containing classes that wrap artifact downloads."""
6
7import cherrypy
8import os
9import shutil
10import subprocess
11
12import gsutil_util
13
14
15# Names of artifacts we care about.
Chris Masone816e38c2012-05-02 12:22:36 -070016DEBUG_SYMBOLS = 'debug.tgz'
Chris Sosa47a7d4e2012-03-28 11:26:55 -070017STATEFUL_UPDATE = 'stateful.tgz'
18TEST_IMAGE = 'chromiumos_test_image.bin'
19ROOT_UPDATE = 'update.gz'
20AUTOTEST_PACKAGE = 'autotest.tar.bz2'
21TEST_SUITES_PACKAGE = 'test_suites.tar.bz2'
22
23
24class ArtifactDownloadError(Exception):
25 """Error used to signify an issue processing an artifact."""
26 pass
27
28
29class DownloadableArtifact(object):
30 """Wrapper around an artifact to download from gsutil.
31
32 The purpose of this class is to download objects from Google Storage
33 and install them to a local directory. There are two main functions, one to
34 download/prepare the artifacts in to a temporary staging area and the second
35 to stage it into its final destination.
36 """
37 def __init__(self, gs_path, tmp_staging_dir, install_path, synchronous=False):
38 """Args:
39 gs_path: Path to artifact in google storage.
40 tmp_staging_dir: Temporary working directory maintained by caller.
41 install_path: Final destination of artifact.
42 synchronous: If True, artifact must be downloaded in the foreground.
43 """
44 self._gs_path = gs_path
45 self._tmp_staging_dir = tmp_staging_dir
46 self._tmp_stage_path = os.path.join(tmp_staging_dir,
47 os.path.basename(self._gs_path))
48 self._synchronous = synchronous
49 self._install_path = install_path
50
51 if not os.path.isdir(self._tmp_staging_dir):
52 os.makedirs(self._tmp_staging_dir)
53
54 if not os.path.isdir(os.path.dirname(self._install_path)):
55 os.makedirs(os.path.dirname(self._install_path))
56
57 def Download(self):
58 """Stages the artifact from google storage to a local staging directory."""
59 gsutil_util.DownloadFromGS(self._gs_path, self._tmp_stage_path)
60
61 def Synchronous(self):
62 """Returns False if this artifact can be downloaded in the background."""
63 return self._synchronous
64
65 def Stage(self):
66 """Moves the artifact from the tmp staging directory to the final path."""
67 shutil.move(self._tmp_stage_path, self._install_path)
68
69 def __str__(self):
70 """String representation for the download."""
71 return '->'.join([self._gs_path, self._tmp_staging_dir, self._install_path])
72
73
74class AUTestPayload(DownloadableArtifact):
75 """Wrapper for AUTest delta payloads which need additional setup."""
76 def Stage(self):
77 super(AUTestPayload, self).Stage()
78
79 payload_dir = os.path.dirname(self._install_path)
80 # Setup necessary symlinks for updating.
81 os.symlink(os.path.join(os.pardir, os.pardir, TEST_IMAGE),
82 os.path.join(payload_dir, TEST_IMAGE))
83 os.symlink(os.path.join(os.pardir, os.pardir, STATEFUL_UPDATE),
84 os.path.join(payload_dir, STATEFUL_UPDATE))
85
86
87class Tarball(DownloadableArtifact):
88 """Wrapper around an artifact to download from gsutil which is a tarball."""
89
90 def _ExtractTarball(self, exclude=None):
91 """Extracts the tarball into the install_path with optional exclude path."""
92 exclude_str = '--exclude=%s' % exclude if exclude else ''
93 cmd = 'tar xf %s %s --use-compress-prog=pbzip2 --directory=%s' % (
94 self._tmp_stage_path, exclude_str, self._install_path)
95 msg = 'An error occurred when attempting to untar %s' % self._tmp_stage_path
96 try:
97 subprocess.check_call(cmd, shell=True)
98 except subprocess.CalledProcessError, e:
99 raise ArtifactDownloadError('%s %s' % (msg, e))
100
101 def Stage(self):
102 """Changes directory into the install path and untars the tarball."""
103 if not os.path.isdir(self._install_path):
104 os.makedirs(self._install_path)
105
106 self._ExtractTarball()
107
108
109class AutotestTarball(Tarball):
110 """Wrapper around the autotest tarball to download from gsutil."""
111
112 def Stage(self):
113 """Untars the autotest tarball into the install path excluding test suites.
114 """
115 if not os.path.isdir(self._install_path):
116 os.makedirs(self._install_path)
117
118 self._ExtractTarball(exclude='autotest/test_suites')
119 autotest_dir = os.path.join(self._install_path, 'autotest')
120 autotest_pkgs_dir = os.path.join(autotest_dir, 'packages')
121 if not os.path.exists(autotest_pkgs_dir):
122 os.makedirs(autotest_pkgs_dir)
123
124 if not os.path.exists(os.path.join(autotest_pkgs_dir, 'packages.checksum')):
125 cmd = 'autotest/utils/packager.py upload --repository=%s --all' % (
126 autotest_pkgs_dir)
127 msg = 'Failed to create autotest packages!'
128 try:
129 subprocess.check_call(cmd, cwd=self._tmp_staging_dir,
130 shell=True)
131 except subprocess.CalledProcessError, e:
132 raise ArtifactDownloadError('%s %s' % (msg, e))
133 else:
134 cherrypy.log('Using pre-generated packages from autotest',
135 'DEVSERVER_UTIL')
136
137 # TODO(scottz): Remove after we have moved away from the old test_scheduler
138 # code.
139 cmd = 'cp %s/* %s' % (autotest_pkgs_dir, autotest_dir)
140 subprocess.check_call(cmd, shell=True)
Chris Masone816e38c2012-05-02 12:22:36 -0700141
142
143class DebugTarball(Tarball):
144 """Wrapper around the debug symbols tarball to download from gsutil."""
145
146 def _ExtractTarball(self):
147 """Extracts debug/breakpad from the tarball into the install_path."""
148 cmd = 'tar xzf %s --directory=%s debug/breakpad' % (
149 self._tmp_stage_path, self._install_path)
150 msg = 'An error occurred when attempting to untar %s' % self._tmp_stage_path
151 try:
152 subprocess.check_call(cmd, shell=True)
153 except subprocess.CalledProcessError, e:
154 raise ArtifactDownloadError('%s %s' % (msg, e))