blob: 9251c43c6db0969fe2b7cb8183cef7761e3e9817 [file] [log] [blame]
Chris Sosa47a7d4e2012-03-28 18:26:551# 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
Yu-Ju Honge61cbe92012-07-10 21:10:269import re
Chris Sosa47a7d4e2012-03-28 18:26:5510import shutil
11import subprocess
12
13import gsutil_util
14
15
16# Names of artifacts we care about.
Chris Masone816e38c2012-05-02 19:22:3617DEBUG_SYMBOLS = 'debug.tgz'
Chris Sosa47a7d4e2012-03-28 18:26:5518STATEFUL_UPDATE = 'stateful.tgz'
19TEST_IMAGE = 'chromiumos_test_image.bin'
20ROOT_UPDATE = 'update.gz'
Yu-Ju Honge61cbe92012-07-10 21:10:2621AUTOTEST_PACKAGE = 'autotest.tar'
22AUTOTEST_ZIPPED_PACKAGE = 'autotest.tar.bz2'
Chris Sosa47a7d4e2012-03-28 18:26:5523TEST_SUITES_PACKAGE = 'test_suites.tar.bz2'
24
25
26class ArtifactDownloadError(Exception):
27 """Error used to signify an issue processing an artifact."""
28 pass
29
30
31class DownloadableArtifact(object):
32 """Wrapper around an artifact to download from gsutil.
33
34 The purpose of this class is to download objects from Google Storage
35 and install them to a local directory. There are two main functions, one to
36 download/prepare the artifacts in to a temporary staging area and the second
37 to stage it into its final destination.
38 """
39 def __init__(self, gs_path, tmp_staging_dir, install_path, synchronous=False):
40 """Args:
41 gs_path: Path to artifact in google storage.
42 tmp_staging_dir: Temporary working directory maintained by caller.
43 install_path: Final destination of artifact.
44 synchronous: If True, artifact must be downloaded in the foreground.
45 """
46 self._gs_path = gs_path
47 self._tmp_staging_dir = tmp_staging_dir
48 self._tmp_stage_path = os.path.join(tmp_staging_dir,
49 os.path.basename(self._gs_path))
50 self._synchronous = synchronous
51 self._install_path = install_path
52
53 if not os.path.isdir(self._tmp_staging_dir):
54 os.makedirs(self._tmp_staging_dir)
55
56 if not os.path.isdir(os.path.dirname(self._install_path)):
57 os.makedirs(os.path.dirname(self._install_path))
58
59 def Download(self):
60 """Stages the artifact from google storage to a local staging directory."""
61 gsutil_util.DownloadFromGS(self._gs_path, self._tmp_stage_path)
62
63 def Synchronous(self):
64 """Returns False if this artifact can be downloaded in the background."""
65 return self._synchronous
66
67 def Stage(self):
68 """Moves the artifact from the tmp staging directory to the final path."""
69 shutil.move(self._tmp_stage_path, self._install_path)
70
71 def __str__(self):
72 """String representation for the download."""
73 return '->'.join([self._gs_path, self._tmp_staging_dir, self._install_path])
74
75
76class AUTestPayload(DownloadableArtifact):
77 """Wrapper for AUTest delta payloads which need additional setup."""
78 def Stage(self):
79 super(AUTestPayload, self).Stage()
80
81 payload_dir = os.path.dirname(self._install_path)
82 # Setup necessary symlinks for updating.
83 os.symlink(os.path.join(os.pardir, os.pardir, TEST_IMAGE),
84 os.path.join(payload_dir, TEST_IMAGE))
85 os.symlink(os.path.join(os.pardir, os.pardir, STATEFUL_UPDATE),
86 os.path.join(payload_dir, STATEFUL_UPDATE))
87
88
89class Tarball(DownloadableArtifact):
90 """Wrapper around an artifact to download from gsutil which is a tarball."""
91
92 def _ExtractTarball(self, exclude=None):
Yu-Ju Honge61cbe92012-07-10 21:10:2693 """Detects whether the tarball is compressed or not based on the file
94 extension and extracts the tarball into the install_path with optional
95 exclude path."""
96
Chris Sosa47a7d4e2012-03-28 18:26:5597 exclude_str = '--exclude=%s' % exclude if exclude else ''
Yu-Ju Honge61cbe92012-07-10 21:10:2698 tarball = os.path.basename(self._tmp_stage_path)
99
100 if re.search('.tar.bz2$', tarball):
101 compress_str = '--use-compress-prog=pbzip2'
102 else:
103 compress_str = ''
104
105 cmd = 'tar xf %s %s %s --directory=%s' % (
106 self._tmp_stage_path, exclude_str, compress_str, self._install_path)
Chris Sosa47a7d4e2012-03-28 18:26:55107 msg = 'An error occurred when attempting to untar %s' % self._tmp_stage_path
Yu-Ju Honge61cbe92012-07-10 21:10:26108
Chris Sosa47a7d4e2012-03-28 18:26:55109 try:
110 subprocess.check_call(cmd, shell=True)
111 except subprocess.CalledProcessError, e:
112 raise ArtifactDownloadError('%s %s' % (msg, e))
113
114 def Stage(self):
115 """Changes directory into the install path and untars the tarball."""
116 if not os.path.isdir(self._install_path):
117 os.makedirs(self._install_path)
118
119 self._ExtractTarball()
120
121
122class AutotestTarball(Tarball):
123 """Wrapper around the autotest tarball to download from gsutil."""
124
125 def Stage(self):
126 """Untars the autotest tarball into the install path excluding test suites.
127 """
128 if not os.path.isdir(self._install_path):
129 os.makedirs(self._install_path)
130
131 self._ExtractTarball(exclude='autotest/test_suites')
132 autotest_dir = os.path.join(self._install_path, 'autotest')
133 autotest_pkgs_dir = os.path.join(autotest_dir, 'packages')
134 if not os.path.exists(autotest_pkgs_dir):
135 os.makedirs(autotest_pkgs_dir)
136
137 if not os.path.exists(os.path.join(autotest_pkgs_dir, 'packages.checksum')):
138 cmd = 'autotest/utils/packager.py upload --repository=%s --all' % (
139 autotest_pkgs_dir)
140 msg = 'Failed to create autotest packages!'
141 try:
142 subprocess.check_call(cmd, cwd=self._tmp_staging_dir,
143 shell=True)
144 except subprocess.CalledProcessError, e:
145 raise ArtifactDownloadError('%s %s' % (msg, e))
146 else:
147 cherrypy.log('Using pre-generated packages from autotest',
148 'DEVSERVER_UTIL')
149
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 19:22:36154
155
156class DebugTarball(Tarball):
157 """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))