blob: 44af4a52993e3ffdf985cc2e2e9a0cce9351f236 [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
Chris Sosa47a7d4e2012-03-28 11:26:55 -07007import os
Yu-Ju Honge61cbe92012-07-10 14:10:26 -07008import re
Chris Sosa47a7d4e2012-03-28 11:26:55 -07009import shutil
10import subprocess
11
12import gsutil_util
Gilad Arnoldc65330c2012-09-20 15:17:48 -070013import log_util
Chris Sosa47a7d4e2012-03-28 11:26:55 -070014
15
16# Names of artifacts we care about.
Chris Masone816e38c2012-05-02 12:22:36 -070017DEBUG_SYMBOLS = 'debug.tgz'
Chris Sosa47a7d4e2012-03-28 11:26:55 -070018STATEFUL_UPDATE = 'stateful.tgz'
19TEST_IMAGE = 'chromiumos_test_image.bin'
20ROOT_UPDATE = 'update.gz'
Yu-Ju Honge61cbe92012-07-10 14:10:26 -070021AUTOTEST_PACKAGE = 'autotest.tar'
22AUTOTEST_ZIPPED_PACKAGE = 'autotest.tar.bz2'
Chris Sosa47a7d4e2012-03-28 11:26:55 -070023TEST_SUITES_PACKAGE = 'test_suites.tar.bz2'
Gilad Arnold6f99b982012-09-12 10:49:40 -070024IMAGE_ARCHIVE = 'image.zip'
Chris Sosa47a7d4e2012-03-28 11:26:55 -070025
26
27class ArtifactDownloadError(Exception):
28 """Error used to signify an issue processing an artifact."""
29 pass
30
31
Gilad Arnoldc65330c2012-09-20 15:17:48 -070032class BuildArtifact(log_util.Loggable):
Chris Sosa47a7d4e2012-03-28 11:26:55 -070033 """Wrapper around an artifact to download from gsutil.
34
35 The purpose of this class is to download objects from Google Storage
36 and install them to a local directory. There are two main functions, one to
37 download/prepare the artifacts in to a temporary staging area and the second
38 to stage it into its final destination.
39 """
40 def __init__(self, gs_path, tmp_staging_dir, install_path, synchronous=False):
41 """Args:
42 gs_path: Path to artifact in google storage.
43 tmp_staging_dir: Temporary working directory maintained by caller.
44 install_path: Final destination of artifact.
45 synchronous: If True, artifact must be downloaded in the foreground.
46 """
47 self._gs_path = gs_path
48 self._tmp_staging_dir = tmp_staging_dir
49 self._tmp_stage_path = os.path.join(tmp_staging_dir,
50 os.path.basename(self._gs_path))
51 self._synchronous = synchronous
52 self._install_path = install_path
53
54 if not os.path.isdir(self._tmp_staging_dir):
55 os.makedirs(self._tmp_staging_dir)
56
57 if not os.path.isdir(os.path.dirname(self._install_path)):
58 os.makedirs(os.path.dirname(self._install_path))
59
60 def Download(self):
61 """Stages the artifact from google storage to a local staging directory."""
62 gsutil_util.DownloadFromGS(self._gs_path, self._tmp_stage_path)
63
64 def Synchronous(self):
65 """Returns False if this artifact can be downloaded in the background."""
66 return self._synchronous
67
68 def Stage(self):
69 """Moves the artifact from the tmp staging directory to the final path."""
70 shutil.move(self._tmp_stage_path, self._install_path)
71
72 def __str__(self):
73 """String representation for the download."""
74 return '->'.join([self._gs_path, self._tmp_staging_dir, self._install_path])
75
76
Gilad Arnoldc65330c2012-09-20 15:17:48 -070077class AUTestPayloadBuildArtifact(BuildArtifact):
Chris Sosa47a7d4e2012-03-28 11:26:55 -070078 """Wrapper for AUTest delta payloads which need additional setup."""
79 def Stage(self):
Gilad Arnoldc65330c2012-09-20 15:17:48 -070080 super(AUTestPayloadBuildArtifact, self).Stage()
Chris Sosa47a7d4e2012-03-28 11:26:55 -070081
82 payload_dir = os.path.dirname(self._install_path)
83 # Setup necessary symlinks for updating.
84 os.symlink(os.path.join(os.pardir, os.pardir, TEST_IMAGE),
85 os.path.join(payload_dir, TEST_IMAGE))
86 os.symlink(os.path.join(os.pardir, os.pardir, STATEFUL_UPDATE),
87 os.path.join(payload_dir, STATEFUL_UPDATE))
88
89
Gilad Arnoldc65330c2012-09-20 15:17:48 -070090class TarballBuildArtifact(BuildArtifact):
Chris Sosa47a7d4e2012-03-28 11:26:55 -070091 """Wrapper around an artifact to download from gsutil which is a tarball."""
92
93 def _ExtractTarball(self, exclude=None):
Yu-Ju Honge61cbe92012-07-10 14:10:26 -070094 """Detects whether the tarball is compressed or not based on the file
95 extension and extracts the tarball into the install_path with optional
96 exclude path."""
97
Chris Sosa47a7d4e2012-03-28 11:26:55 -070098 exclude_str = '--exclude=%s' % exclude if exclude else ''
Yu-Ju Honge61cbe92012-07-10 14:10:26 -070099 tarball = os.path.basename(self._tmp_stage_path)
100
101 if re.search('.tar.bz2$', tarball):
102 compress_str = '--use-compress-prog=pbzip2'
103 else:
104 compress_str = ''
105
106 cmd = 'tar xf %s %s %s --directory=%s' % (
107 self._tmp_stage_path, exclude_str, compress_str, self._install_path)
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700108 msg = 'An error occurred when attempting to untar %s' % self._tmp_stage_path
Yu-Ju Honge61cbe92012-07-10 14:10:26 -0700109
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700110 try:
111 subprocess.check_call(cmd, shell=True)
112 except subprocess.CalledProcessError, e:
113 raise ArtifactDownloadError('%s %s' % (msg, e))
114
115 def Stage(self):
116 """Changes directory into the install path and untars the tarball."""
117 if not os.path.isdir(self._install_path):
118 os.makedirs(self._install_path)
119
120 self._ExtractTarball()
121
122
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700123class AutotestTarballBuildArtifact(TarballBuildArtifact):
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700124 """Wrapper around the autotest tarball to download from gsutil."""
125
126 def Stage(self):
127 """Untars the autotest tarball into the install path excluding test suites.
128 """
129 if not os.path.isdir(self._install_path):
130 os.makedirs(self._install_path)
131
132 self._ExtractTarball(exclude='autotest/test_suites')
133 autotest_dir = os.path.join(self._install_path, 'autotest')
134 autotest_pkgs_dir = os.path.join(autotest_dir, 'packages')
135 if not os.path.exists(autotest_pkgs_dir):
136 os.makedirs(autotest_pkgs_dir)
137
138 if not os.path.exists(os.path.join(autotest_pkgs_dir, 'packages.checksum')):
139 cmd = 'autotest/utils/packager.py upload --repository=%s --all' % (
140 autotest_pkgs_dir)
141 msg = 'Failed to create autotest packages!'
142 try:
143 subprocess.check_call(cmd, cwd=self._tmp_staging_dir,
144 shell=True)
145 except subprocess.CalledProcessError, e:
146 raise ArtifactDownloadError('%s %s' % (msg, e))
147 else:
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700148 self._LOG('Using pre-generated packages from autotest')
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700149
150 # TODO(scottz): Remove after we have moved away from the old test_scheduler
151 # code.
152 cmd = 'cp %s/* %s' % (autotest_pkgs_dir, autotest_dir)
153 subprocess.check_call(cmd, shell=True)
Chris Masone816e38c2012-05-02 12:22:36 -0700154
155
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700156class DebugTarballBuildArtifact(TarballBuildArtifact):
Chris Masone816e38c2012-05-02 12:22:36 -0700157 """Wrapper around the debug symbols tarball to download from gsutil."""
158
159 def _ExtractTarball(self):
160 """Extracts debug/breakpad from the tarball into the install_path."""
161 cmd = 'tar xzf %s --directory=%s debug/breakpad' % (
162 self._tmp_stage_path, self._install_path)
163 msg = 'An error occurred when attempting to untar %s' % self._tmp_stage_path
164 try:
165 subprocess.check_call(cmd, shell=True)
166 except subprocess.CalledProcessError, e:
167 raise ArtifactDownloadError('%s %s' % (msg, e))
Gilad Arnold6f99b982012-09-12 10:49:40 -0700168
169
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700170class ZipfileBuildArtifact(BuildArtifact):
Gilad Arnold6f99b982012-09-12 10:49:40 -0700171 """A downloadable artifact that is a zipfile.
172
173 This class defines an extra public method for setting the list of files to be
174 extracted upon staging. Staging amounts to unzipping the desired files to the
175 install path.
176
177 """
178
179 def __init__(self, gs_path, tmp_staging_dir, install_path, synchronous=False,
180 unzip_file_list=None):
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700181 super(ZipfileBuildArtifact, self).__init__(
Gilad Arnold6f99b982012-09-12 10:49:40 -0700182 gs_path, tmp_staging_dir, install_path, synchronous)
183 self._unzip_file_list = unzip_file_list
184
185 def _Unzip(self):
186 """Unzip files into the install path."""
187
188 cmd = 'unzip -o %s -d %s%s' % (
189 self._tmp_stage_path,
190 os.path.join(self._install_path),
191 (' ' + ' '.join(self._unzip_file_list)
192 if self._unzip_file_list else ''))
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700193 self._Log('unzip command: %s' % cmd)
Gilad Arnold6f99b982012-09-12 10:49:40 -0700194 msg = 'An error occurred when attempting to unzip %s' % self._tmp_stage_path
195
196 try:
197 subprocess.check_call(cmd, shell=True)
198 except subprocess.CalledProcessError, e:
199 raise ArtifactDownloadError('%s %s' % (msg, e))
200
201 def Stage(self):
202 """Unzip files into the install path."""
203 if not os.path.isdir(self._install_path):
204 os.makedirs(self._install_path)
205
206 self._Unzip()