blob: 1139dfe0e09e985c76260af6695a2d2bd1cb7827 [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.
Yu-Ju Honge61cbe92012-07-10 14:10:26 -070017AUTOTEST_PACKAGE = 'autotest.tar'
18AUTOTEST_ZIPPED_PACKAGE = 'autotest.tar.bz2'
Vadim Bendeburyd3698392012-12-27 18:21:32 -080019DEBUG_SYMBOLS = 'debug.tgz'
20FIRMWARE_ARCHIVE = 'firmware_from_source.tar.bz2'
Gilad Arnold6f99b982012-09-12 10:49:40 -070021IMAGE_ARCHIVE = 'image.zip'
Vadim Bendeburyd3698392012-12-27 18:21:32 -080022ROOT_UPDATE = 'update.gz'
23STATEFUL_UPDATE = 'stateful.tgz'
24TEST_IMAGE = 'chromiumos_test_image.bin'
25TEST_SUITES_PACKAGE = 'test_suites.tar.bz2'
Chris Sosa6a3697f2013-01-29 16:44:43 -080026AU_SUITE_PACKAGE = 'au_control.tar.bz2'
Chris Sosa47a7d4e2012-03-28 11:26:55 -070027
28
29class ArtifactDownloadError(Exception):
30 """Error used to signify an issue processing an artifact."""
31 pass
32
33
Gilad Arnoldc65330c2012-09-20 15:17:48 -070034class BuildArtifact(log_util.Loggable):
Chris Sosa47a7d4e2012-03-28 11:26:55 -070035 """Wrapper around an artifact to download from gsutil.
36
37 The purpose of this class is to download objects from Google Storage
38 and install them to a local directory. There are two main functions, one to
39 download/prepare the artifacts in to a temporary staging area and the second
40 to stage it into its final destination.
41 """
42 def __init__(self, gs_path, tmp_staging_dir, install_path, synchronous=False):
43 """Args:
44 gs_path: Path to artifact in google storage.
45 tmp_staging_dir: Temporary working directory maintained by caller.
46 install_path: Final destination of artifact.
47 synchronous: If True, artifact must be downloaded in the foreground.
48 """
Chris Sosa6a3697f2013-01-29 16:44:43 -080049 super(BuildArtifact, self).__init__()
Chris Sosa47a7d4e2012-03-28 11:26:55 -070050 self._gs_path = gs_path
51 self._tmp_staging_dir = tmp_staging_dir
52 self._tmp_stage_path = os.path.join(tmp_staging_dir,
53 os.path.basename(self._gs_path))
54 self._synchronous = synchronous
55 self._install_path = install_path
56
57 if not os.path.isdir(self._tmp_staging_dir):
58 os.makedirs(self._tmp_staging_dir)
59
60 if not os.path.isdir(os.path.dirname(self._install_path)):
61 os.makedirs(os.path.dirname(self._install_path))
62
63 def Download(self):
64 """Stages the artifact from google storage to a local staging directory."""
65 gsutil_util.DownloadFromGS(self._gs_path, self._tmp_stage_path)
66
67 def Synchronous(self):
68 """Returns False if this artifact can be downloaded in the background."""
69 return self._synchronous
70
71 def Stage(self):
72 """Moves the artifact from the tmp staging directory to the final path."""
73 shutil.move(self._tmp_stage_path, self._install_path)
74
75 def __str__(self):
76 """String representation for the download."""
77 return '->'.join([self._gs_path, self._tmp_staging_dir, self._install_path])
78
79
Gilad Arnoldc65330c2012-09-20 15:17:48 -070080class AUTestPayloadBuildArtifact(BuildArtifact):
Chris Sosa47a7d4e2012-03-28 11:26:55 -070081 """Wrapper for AUTest delta payloads which need additional setup."""
82 def Stage(self):
Gilad Arnoldc65330c2012-09-20 15:17:48 -070083 super(AUTestPayloadBuildArtifact, self).Stage()
Chris Sosa47a7d4e2012-03-28 11:26:55 -070084
85 payload_dir = os.path.dirname(self._install_path)
86 # Setup necessary symlinks for updating.
87 os.symlink(os.path.join(os.pardir, os.pardir, TEST_IMAGE),
88 os.path.join(payload_dir, TEST_IMAGE))
89 os.symlink(os.path.join(os.pardir, os.pardir, STATEFUL_UPDATE),
90 os.path.join(payload_dir, STATEFUL_UPDATE))
91
92
Gilad Arnoldc65330c2012-09-20 15:17:48 -070093class TarballBuildArtifact(BuildArtifact):
Chris Sosa47a7d4e2012-03-28 11:26:55 -070094 """Wrapper around an artifact to download from gsutil which is a tarball."""
95
96 def _ExtractTarball(self, exclude=None):
Yu-Ju Honge61cbe92012-07-10 14:10:26 -070097 """Detects whether the tarball is compressed or not based on the file
98 extension and extracts the tarball into the install_path with optional
99 exclude path."""
100
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700101 exclude_str = '--exclude=%s' % exclude if exclude else ''
Yu-Ju Honge61cbe92012-07-10 14:10:26 -0700102 tarball = os.path.basename(self._tmp_stage_path)
103
104 if re.search('.tar.bz2$', tarball):
105 compress_str = '--use-compress-prog=pbzip2'
106 else:
107 compress_str = ''
108
109 cmd = 'tar xf %s %s %s --directory=%s' % (
110 self._tmp_stage_path, exclude_str, compress_str, self._install_path)
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700111 msg = 'An error occurred when attempting to untar %s' % self._tmp_stage_path
Yu-Ju Honge61cbe92012-07-10 14:10:26 -0700112
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700113 try:
114 subprocess.check_call(cmd, shell=True)
115 except subprocess.CalledProcessError, e:
116 raise ArtifactDownloadError('%s %s' % (msg, e))
117
118 def Stage(self):
119 """Changes directory into the install path and untars the tarball."""
120 if not os.path.isdir(self._install_path):
121 os.makedirs(self._install_path)
122
123 self._ExtractTarball()
124
125
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700126class AutotestTarballBuildArtifact(TarballBuildArtifact):
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700127 """Wrapper around the autotest tarball to download from gsutil."""
128
129 def Stage(self):
130 """Untars the autotest tarball into the install path excluding test suites.
131 """
132 if not os.path.isdir(self._install_path):
133 os.makedirs(self._install_path)
134
135 self._ExtractTarball(exclude='autotest/test_suites')
136 autotest_dir = os.path.join(self._install_path, 'autotest')
137 autotest_pkgs_dir = os.path.join(autotest_dir, 'packages')
138 if not os.path.exists(autotest_pkgs_dir):
139 os.makedirs(autotest_pkgs_dir)
140
141 if not os.path.exists(os.path.join(autotest_pkgs_dir, 'packages.checksum')):
142 cmd = 'autotest/utils/packager.py upload --repository=%s --all' % (
143 autotest_pkgs_dir)
144 msg = 'Failed to create autotest packages!'
145 try:
146 subprocess.check_call(cmd, cwd=self._tmp_staging_dir,
147 shell=True)
148 except subprocess.CalledProcessError, e:
149 raise ArtifactDownloadError('%s %s' % (msg, e))
150 else:
Gilad Arnoldf5843132012-09-25 00:31:20 -0700151 self._Log('Using pre-generated packages from autotest')
Chris Sosa47a7d4e2012-03-28 11:26:55 -0700152
153 # TODO(scottz): Remove after we have moved away from the old test_scheduler
154 # code.
155 cmd = 'cp %s/* %s' % (autotest_pkgs_dir, autotest_dir)
156 subprocess.check_call(cmd, shell=True)
Chris Masone816e38c2012-05-02 12:22:36 -0700157
158
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700159class DebugTarballBuildArtifact(TarballBuildArtifact):
Chris Masone816e38c2012-05-02 12:22:36 -0700160 """Wrapper around the debug symbols tarball to download from gsutil."""
161
Chris Sosa6a3697f2013-01-29 16:44:43 -0800162 def _ExtractTarball(self, _exclude=None):
Chris Masone816e38c2012-05-02 12:22:36 -0700163 """Extracts debug/breakpad from the tarball into the install_path."""
164 cmd = 'tar xzf %s --directory=%s debug/breakpad' % (
165 self._tmp_stage_path, self._install_path)
166 msg = 'An error occurred when attempting to untar %s' % self._tmp_stage_path
167 try:
168 subprocess.check_call(cmd, shell=True)
169 except subprocess.CalledProcessError, e:
170 raise ArtifactDownloadError('%s %s' % (msg, e))
Gilad Arnold6f99b982012-09-12 10:49:40 -0700171
172
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700173class ZipfileBuildArtifact(BuildArtifact):
Gilad Arnold6f99b982012-09-12 10:49:40 -0700174 """A downloadable artifact that is a zipfile.
175
176 This class defines an extra public method for setting the list of files to be
177 extracted upon staging. Staging amounts to unzipping the desired files to the
178 install path.
179
180 """
181
182 def __init__(self, gs_path, tmp_staging_dir, install_path, synchronous=False,
183 unzip_file_list=None):
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700184 super(ZipfileBuildArtifact, self).__init__(
Gilad Arnold6f99b982012-09-12 10:49:40 -0700185 gs_path, tmp_staging_dir, install_path, synchronous)
186 self._unzip_file_list = unzip_file_list
187
188 def _Unzip(self):
189 """Unzip files into the install path."""
190
191 cmd = 'unzip -o %s -d %s%s' % (
192 self._tmp_stage_path,
193 os.path.join(self._install_path),
194 (' ' + ' '.join(self._unzip_file_list)
195 if self._unzip_file_list else ''))
Gilad Arnoldc65330c2012-09-20 15:17:48 -0700196 self._Log('unzip command: %s' % cmd)
Gilad Arnold6f99b982012-09-12 10:49:40 -0700197 msg = 'An error occurred when attempting to unzip %s' % self._tmp_stage_path
198
199 try:
200 subprocess.check_call(cmd, shell=True)
201 except subprocess.CalledProcessError, e:
202 raise ArtifactDownloadError('%s %s' % (msg, e))
203
204 def Stage(self):
205 """Unzip files into the install path."""
206 if not os.path.isdir(self._install_path):
207 os.makedirs(self._install_path)
208
209 self._Unzip()