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