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