blob: 3971f75cf43775442fe634b2b3603b71f03311c3 [file] [log] [blame]
Luis Hector Chaveza1518052018-06-14 08:19:34 -07001#!/usr/bin/env python2
2# -*- coding: utf-8 -*-
Chris Sosa7cd23202013-10-15 17:22:57 -07003# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Integration tests for the devserver.
8
9This module is responsible for testing the actual devserver APIs and should be
10run whenever changes are made to the devserver.
11
xixuan52c2fba2016-05-20 17:02:48 -070012To run the integration test for devserver:
13 python ./devserver_integration_test.py
Chris Sosa7cd23202013-10-15 17:22:57 -070014"""
15
Gabe Black3b567202015-09-23 14:07:59 -070016from __future__ import print_function
17
xixuan52c2fba2016-05-20 17:02:48 -070018import cros_update_progress
Chris Sosa7cd23202013-10-15 17:22:57 -070019import devserver_constants
20import json
Chris Sosa7cd23202013-10-15 17:22:57 -070021from xml.dom import minidom
22import os
23import psutil
24import shutil
25import signal
Gilad Arnold7de05f72014-02-14 13:14:20 -080026import socket
Chris Sosa7cd23202013-10-15 17:22:57 -070027import subprocess
28import tempfile
29import time
30import unittest
31import urllib2
32
Luis Hector Chaveza1518052018-06-14 08:19:34 -070033from chromite.lib import cros_logging as logging
34
Chris Sosa7cd23202013-10-15 17:22:57 -070035
36# Paths are relative to this script's base directory.
37LABEL = 'devserver'
38TEST_IMAGE_PATH = 'testdata/devserver'
39TEST_IMAGE_NAME = 'update.gz'
40EXPECTED_HASH = 'kGcOinJ0vA8vdYX53FN0F5BdwfY='
41
42# Update request based on Omaha v3 protocol format.
43UPDATE_REQUEST = """<?xml version="1.0" encoding="UTF-8"?>
Amin Hassani495f1de2019-02-26 11:13:39 -080044<request protocol="3.0" updater="ChromeOSUpdateEngine" updaterversion="0.1.0.0" ismachine="1">
Chris Sosa7cd23202013-10-15 17:22:57 -070045 <os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></os>
46 <app appid="{DEV-BUILD}" version="11.254.2011_03_09_1814" lang="en-US" track="developer-build" board="x86-generic" hardware_class="BETA DVT" delta_okay="true">
47 <updatecheck></updatecheck>
48 </app>
49</request>
50"""
51
52# RPC constants.
53STAGE = 'stage'
54IS_STAGED = 'is_staged'
55STATIC = 'static'
56UPDATE = 'update'
57CHECK_HEALTH = 'check_health'
58CONTROL_FILES = 'controlfiles'
59XBUDDY = 'xbuddy'
Prashanth Ba06d2d22014-03-07 15:35:19 -080060LIST_IMAGE_DIR = 'list_image_dir'
Chris Sosa7cd23202013-10-15 17:22:57 -070061
62# API rpcs and constants.
63API_HOST_INFO = 'api/hostinfo'
64API_SET_NEXT_UPDATE = 'api/setnextupdate'
65API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
66API_TEST_IP_ADDR = '127.0.0.1'
67
68DEVSERVER_START_TIMEOUT = 15
Gilad Arnold08516112014-02-14 13:14:03 -080069DEVSERVER_START_SLEEP = 1
Gilad Arnold7de05f72014-02-14 13:14:20 -080070MAX_START_ATTEMPTS = 5
Chris Sosa7cd23202013-10-15 17:22:57 -070071
72
73class DevserverFailedToStart(Exception):
74 """Raised if we could not start the devserver."""
75
76
Gilad Arnold08516112014-02-14 13:14:03 -080077class DevserverTestBase(unittest.TestCase):
Chris Sosa7cd23202013-10-15 17:22:57 -070078 """Class containing common logic between devserver test classes."""
79
80 def setUp(self):
Gilad Arnold08516112014-02-14 13:14:03 -080081 """Creates and populates a test directory, temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -070082 self.test_data_path = tempfile.mkdtemp()
83 self.src_dir = os.path.dirname(__file__)
84
85 # Current location of testdata payload.
86 image_src = os.path.join(self.src_dir, TEST_IMAGE_PATH, TEST_IMAGE_NAME)
87
Gilad Arnold08516112014-02-14 13:14:03 -080088 # Copy the payload to the location of the update label.
89 self._CreateLabelAndCopyImage(LABEL, image_src)
Chris Sosa7cd23202013-10-15 17:22:57 -070090
91 # Copy the payload to the location of forced label.
Gilad Arnold08516112014-02-14 13:14:03 -080092 self._CreateLabelAndCopyImage(API_SET_UPDATE_REQUEST, image_src)
Chris Sosa7cd23202013-10-15 17:22:57 -070093
Gilad Arnold08516112014-02-14 13:14:03 -080094 # Allocate temporary files for various devserver outputs.
95 self.pidfile = self._MakeTempFile('pid')
96 self.portfile = self._MakeTempFile('port')
97 self.logfile = self._MakeTempFile('log')
Chris Sosa7cd23202013-10-15 17:22:57 -070098
Gilad Arnold08516112014-02-14 13:14:03 -080099 # Initialize various runtime values.
100 self.devserver_url = self.port = self.pid = None
Chris Sosa7cd23202013-10-15 17:22:57 -0700101
102 def tearDown(self):
Gilad Arnold08516112014-02-14 13:14:03 -0800103 """Kill the server, remove the test directory and temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -0700104 if self.pid:
105 os.kill(self.pid, signal.SIGKILL)
106
Gilad Arnold08516112014-02-14 13:14:03 -0800107 self._RemoveFile(self.pidfile)
108 self._RemoveFile(self.portfile)
109 self._RemoveFile(self.logfile)
110 shutil.rmtree(self.test_data_path)
Chris Sosa7cd23202013-10-15 17:22:57 -0700111
112 # Helper methods begin here.
113
Gilad Arnold08516112014-02-14 13:14:03 -0800114 def _CreateLabelAndCopyImage(self, label, image):
115 """Creates a label location and copies an image to it."""
116 label_dir = os.path.join(self.test_data_path, label)
117 os.makedirs(label_dir)
118 shutil.copy(image, os.path.join(label_dir, TEST_IMAGE_NAME))
119
120 def _MakeTempFile(self, suffix):
121 """Return path of a newly created temporary file."""
122 with tempfile.NamedTemporaryFile(suffix='-devserver-%s' % suffix) as f:
123 name = f.name
124 f.close()
125
126 return name
127
128 def _RemoveFile(self, filename):
129 """Removes a file if it is present."""
130 if os.path.isfile(filename):
131 os.remove(filename)
132
133 def _ReadIntValueFromFile(self, path, desc):
134 """Reads a string from file and returns its conversion into an integer."""
135 if not os.path.isfile(path):
136 raise DevserverFailedToStart('Devserver did not drop %s (%r).' %
137 (desc, path))
138
139 with open(path) as f:
140 value_str = f.read()
141
142 try:
143 return int(value_str)
144 except ValueError:
145 raise DevserverFailedToStart('Devserver did not drop a valid value '
146 'in %s (%r).' % (desc, value_str))
147
148 def _StartServer(self, port=0):
Chris Sosa7cd23202013-10-15 17:22:57 -0700149 """Attempts to start devserver on |port|.
150
Gilad Arnold08516112014-02-14 13:14:03 -0800151 In the default case where port == 0, the server will bind to an arbitrary
Dan Shi2f136862016-02-11 15:38:38 -0800152 available port. If successful, this method will set the devserver's pid
Gilad Arnold08516112014-02-14 13:14:03 -0800153 (self.pid), actual listening port (self.port) and URL (self.devserver_url).
Chris Sosa7cd23202013-10-15 17:22:57 -0700154
155 Raises:
156 DevserverFailedToStart: If the devserver could not be started.
157 """
158 cmd = [
159 'python',
160 os.path.join(self.src_dir, 'devserver.py'),
161 'devserver.py',
162 '--static_dir', self.test_data_path,
163 '--pidfile', self.pidfile,
Gilad Arnold08516112014-02-14 13:14:03 -0800164 '--portfile', self.portfile,
Chris Sosa7cd23202013-10-15 17:22:57 -0700165 '--port', str(port),
166 '--logfile', self.logfile]
167
168 # Pipe all output. Use logfile to get devserver log.
169 subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
170
Gilad Arnold08516112014-02-14 13:14:03 -0800171 # Wait for devserver to start, determining its actual serving port and URL.
Chris Sosa7cd23202013-10-15 17:22:57 -0700172 current_time = time.time()
173 deadline = current_time + DEVSERVER_START_TIMEOUT
174 while current_time < deadline:
Chris Sosa7cd23202013-10-15 17:22:57 -0700175 try:
Gilad Arnold08516112014-02-14 13:14:03 -0800176 self.port = self._ReadIntValueFromFile(self.portfile, 'portfile')
177 self.devserver_url = 'http://127.0.0.1:%d' % self.port
Gabe Black3b567202015-09-23 14:07:59 -0700178 self._MakeRPC(CHECK_HEALTH, timeout=1)
Chris Sosa7cd23202013-10-15 17:22:57 -0700179 break
180 except Exception:
Gilad Arnold08516112014-02-14 13:14:03 -0800181 time.sleep(DEVSERVER_START_SLEEP)
182 current_time = time.time()
Chris Sosa7cd23202013-10-15 17:22:57 -0700183 else:
184 raise DevserverFailedToStart('Devserver failed to start within timeout.')
185
Gilad Arnold08516112014-02-14 13:14:03 -0800186 # Retrieve PID.
187 self.pid = self._ReadIntValueFromFile(self.pidfile, 'pidfile')
Chris Sosa7cd23202013-10-15 17:22:57 -0700188
189 def VerifyHandleUpdate(self, label, use_test_payload=True):
190 """Verifies that we can send an update request to the devserver.
191
192 This method verifies (using a fake update_request blob) that the devserver
193 can interpret the payload and give us back the right payload.
194
195 Args:
196 label: Label that update is served from e.g. <board>-release/<version>
197 use_test_payload: If set to true, expects to serve payload under
198 testdata/ and does extra checks i.e. compares hash and content of
199 payload.
Gilad Arnold08516112014-02-14 13:14:03 -0800200
Chris Sosa7cd23202013-10-15 17:22:57 -0700201 Returns:
202 url of the update payload if we verified the update.
203 """
204 update_label = '/'.join([UPDATE, label])
205 response = self._MakeRPC(update_label, data=UPDATE_REQUEST)
206 self.assertNotEqual('', response)
207
208 # Parse the response and check if it contains the right result.
209 dom = minidom.parseString(response)
210 update = dom.getElementsByTagName('updatecheck')[0]
211 expected_static_url = '/'.join([self.devserver_url, STATIC, label])
212 expected_hash = EXPECTED_HASH if use_test_payload else None
213 url = self.VerifyV3Response(update, expected_static_url,
214 expected_hash=expected_hash)
215
216 # Verify the image we download is correct since we already know what it is.
217 if use_test_payload:
218 connection = urllib2.urlopen(url)
219 contents = connection.read()
220 connection.close()
221 self.assertEqual('Developers, developers, developers!\n', contents)
222
223 return url
224
225 def VerifyV3Response(self, update, expected_static_url, expected_hash):
226 """Verifies the update DOM from a v3 response and returns the url."""
227 # Parse the response and check if it contains the right result.
228 urls = update.getElementsByTagName('urls')[0]
229 url = urls.getElementsByTagName('url')[0]
230
231 static_url = url.getAttribute('codebase')
232 # Static url's end in /.
233 self.assertEqual(expected_static_url + '/', static_url)
234
235 manifest = update.getElementsByTagName('manifest')[0]
236 packages = manifest.getElementsByTagName('packages')[0]
237 package = packages.getElementsByTagName('package')[0]
238 filename = package.getAttribute('name')
239 self.assertEqual(TEST_IMAGE_NAME, filename)
240
241 if expected_hash:
242 hash_value = package.getAttribute('hash')
243 self.assertEqual(EXPECTED_HASH, hash_value)
244
245 url = os.path.join(static_url, filename)
246 return url
247
248 def _MakeRPC(self, rpc, data=None, timeout=None, **kwargs):
Gilad Arnold08516112014-02-14 13:14:03 -0800249 """Makes an RPC call to the devserver.
Chris Sosa7cd23202013-10-15 17:22:57 -0700250
251 Args:
Gilad Arnold08516112014-02-14 13:14:03 -0800252 rpc: The function to run on the devserver, e.g. 'stage'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700253 data: Optional post data to send.
254 timeout: Optional timeout to pass to urlopen.
Gilad Arnold08516112014-02-14 13:14:03 -0800255 kwargs: Optional arguments to the function, e.g. artifact_url='foo/bar'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700256
Gilad Arnold08516112014-02-14 13:14:03 -0800257 Returns:
258 The function output.
Chris Sosa7cd23202013-10-15 17:22:57 -0700259 """
260 request = '/'.join([self.devserver_url, rpc])
261 if kwargs:
262 # Join the kwargs to the URL.
263 request += '?' + '&'.join('%s=%s' % item for item in kwargs.iteritems())
264
265 output = None
266 try:
267 # Let's log output for all rpc's without timeouts because we only
268 # use timeouts to check to see if something is up and these checks tend
269 # to be small and so logging it will be extremely repetitive.
270 if not timeout:
271 logging.info('Making request using %s', request)
272
273 connection = urllib2.urlopen(request, data=data, timeout=timeout)
274 output = connection.read()
275 connection.close()
276 except urllib2.HTTPError:
277 raise
278
279 return output
280
281
Gilad Arnold08516112014-02-14 13:14:03 -0800282class AutoStartDevserverTestBase(DevserverTestBase):
283 """Test base class that automatically starts the devserver."""
284
285 def setUp(self):
286 """Initialize everything, then start the server."""
287 super(AutoStartDevserverTestBase, self).setUp()
288 self._StartServer()
289
290
Gilad Arnold7de05f72014-02-14 13:14:20 -0800291class DevserverStartTests(DevserverTestBase):
292 """Test that devserver starts up correctly."""
293
294 def testStartAnyPort(self):
295 """Starts the devserver, have it bind to an arbitrary available port."""
296 self._StartServer()
297
298 def testStartSpecificPort(self):
299 """Starts the devserver with a specific port."""
300 for _ in range(MAX_START_ATTEMPTS):
301 # This is a cheap hack to find an arbitrary unused port: we open a socket
302 # and bind it to port zero, then pull out the actual port number and
303 # close the socket. In all likelihood, this will leave us with an
304 # available port number that we can use for starting the devserver.
305 # However, this heuristic is susceptible to race conditions, hence the
306 # retry loop.
307 s = socket.socket()
308 s.bind(('', 0))
309 # s.getsockname() is definitely callable.
310 # pylint: disable=E1102
311 _, port = s.getsockname()
312 s.close()
313
314 self._StartServer(port=port)
315
316
Gilad Arnold08516112014-02-14 13:14:03 -0800317class DevserverBasicTests(AutoStartDevserverTestBase):
318 """Short running tests for the devserver (no remote deps).
Chris Sosa7cd23202013-10-15 17:22:57 -0700319
320 These are technically not unittests because they depend on being able to
321 start a devserver locally which technically requires external resources so
322 they are lumped with the remote tests here.
323 """
324
325 def testHandleUpdateV3(self):
326 self.VerifyHandleUpdate(label=LABEL)
327
328 def testApiBadSetNextUpdateRequest(self):
329 """Tests sending a bad setnextupdate request."""
330 # Send bad request and ensure it fails...
331 self.assertRaises(urllib2.URLError,
332 self._MakeRPC,
333 '/'.join([API_SET_NEXT_UPDATE, API_TEST_IP_ADDR]))
334
335 def testApiBadSetNextUpdateURL(self):
336 """Tests contacting a bad setnextupdate url."""
337 # Send bad request and ensure it fails...
338 self.assertRaises(urllib2.URLError,
339 self._MakeRPC, API_SET_NEXT_UPDATE)
340
341 def testApiBadHostInfoURL(self):
342 """Tests contacting a bad hostinfo url."""
343 # Host info should be invalid without a specified address.
344 self.assertRaises(urllib2.URLError,
345 self._MakeRPC, API_HOST_INFO)
346
347 def testApiHostInfoAndSetNextUpdate(self):
348 """Tests using the setnextupdate and hostinfo api commands."""
349 # Send setnextupdate command.
350 self._MakeRPC('/'.join([API_SET_NEXT_UPDATE, API_TEST_IP_ADDR]),
351 data=API_SET_UPDATE_REQUEST)
352
353 # Send hostinfo command and verify the setnextupdate worked.
354 response = self._MakeRPC('/'.join([API_HOST_INFO, API_TEST_IP_ADDR]))
355
356 self.assertEqual(
357 json.loads(response)['forced_update_label'], API_SET_UPDATE_REQUEST)
358
359 def testXBuddyLocalAlias(self):
360 """Extensive local image xbuddy unittest.
361
362 This test verifies all the local xbuddy logic by creating a new local folder
363 with the necessary update items and verifies we can use all of them.
364 """
365 update_data = 'TEST UPDATE'
366 image_data = 'TEST IMAGE'
367 stateful_data = 'STATEFUL STUFFS'
368 build_id = 'x86-generic/R32-9999.0.0-a1'
369 xbuddy_path = 'x86-generic/R32-9999.0.0-a1/test'
370 build_dir = os.path.join(self.test_data_path, build_id)
371 os.makedirs(build_dir)
372 test_image_file = os.path.join(build_dir,
373 devserver_constants.TEST_IMAGE_FILE)
374 update_file = os.path.join(build_dir, devserver_constants.UPDATE_FILE)
375 stateful_file = os.path.join(build_dir, devserver_constants.STATEFUL_FILE)
376
377 logging.info('Creating dummy files')
378
379 for item, filename, data in zip(
380 ['full_payload', 'test', 'stateful'],
381 [update_file, test_image_file, stateful_file],
382 [update_data, image_data, stateful_data]):
383 logging.info('Creating file %s', filename)
384 with open(filename, 'w') as fh:
385 fh.write(data)
386
387 xbuddy_path = '/'.join([build_id, item])
388 logging.info('Testing xbuddy path %s', xbuddy_path)
389 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]))
390 self.assertEqual(response, data)
391
392 expected_dir = '/'.join([self.devserver_url, STATIC, build_id])
393 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
394 self.assertEqual(response, expected_dir)
395
Yu-Ju Hong51495eb2013-12-12 17:08:43 -0800396 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]),
397 relative_path=True)
398 self.assertEqual(response, build_id)
399
Chris Sosa7cd23202013-10-15 17:22:57 -0700400 xbuddy_path = '/'.join([build_id, 'test'])
401 logging.info('Testing for_update for %s', xbuddy_path)
402 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
403 expected_path = '/'.join([self.devserver_url, UPDATE, build_id])
404 self.assertTrue(response, expected_path)
405
406 logging.info('Verifying the actual payload data')
407 url = self.VerifyHandleUpdate(build_id, use_test_payload=False)
408 logging.info('Verify the actual content of the update payload')
409 connection = urllib2.urlopen(url)
410 contents = connection.read()
411 connection.close()
412 self.assertEqual(update_data, contents)
413
414 def testPidFile(self):
415 """Test that using a pidfile works correctly."""
416 with open(self.pidfile, 'r') as f:
417 pid = f.read()
Chris Sosa7cd23202013-10-15 17:22:57 -0700418 # Let's assert some process information about the devserver.
Dan Shi2f136862016-02-11 15:38:38 -0800419 self.assertTrue(pid.strip().isdigit())
Chris Sosa7cd23202013-10-15 17:22:57 -0700420 process = psutil.Process(int(pid))
421 self.assertTrue(process.is_running())
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700422 self.assertTrue('devserver.py' in process.cmdline())
Chris Sosa7cd23202013-10-15 17:22:57 -0700423
424
Gilad Arnold08516112014-02-14 13:14:03 -0800425class DevserverExtendedTests(AutoStartDevserverTestBase):
Chris Sosa7cd23202013-10-15 17:22:57 -0700426 """Longer running integration tests that test interaction with Google Storage.
427
428 Note: due to the interaction with Google Storage, these tests both require
429 1) runner has access to the Google Storage bucket where builders store builds.
430 2) time. These tests actually download the artifacts needed.
431 """
432
xixuan52c2fba2016-05-20 17:02:48 -0700433 def testCrosAU(self):
434 """Tests core autotest workflow where we trigger CrOS auto-update.
435
436 It mainly tests the following API:
437 a. 'get_au_status'
438 b. 'handler_cleanup'
439 c. 'kill_au_proc'
440 """
441 host_name = '100.0.0.0'
xixuan2a0970a2016-08-10 12:12:44 -0700442 p = subprocess.Popen(['sleep 100'], shell=True, preexec_fn=os.setsid)
443 pid = os.getpgid(p.pid)
xixuan52c2fba2016-05-20 17:02:48 -0700444 status = 'updating'
445 progress_tracker = cros_update_progress.AUProgress(host_name, pid)
446 progress_tracker.WriteStatus(status)
447
448 logging.info('Retrieving auto-update status for process %d', pid)
449 response = self._MakeRPC('get_au_status', host_name=host_name, pid=pid)
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700450 self.assertFalse(json.loads(response)['finished'])
451 self.assertEqual(json.loads(response)['status'], status)
xixuan52c2fba2016-05-20 17:02:48 -0700452
453 progress_tracker.WriteStatus(cros_update_progress.FINISHED)
454 logging.info('Mock auto-update process is finished')
455 response = self._MakeRPC('get_au_status', host_name=host_name, pid=pid)
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700456 self.assertTrue(json.loads(response)['finished'])
457 self.assertEqual(json.loads(response)['status'],
458 cros_update_progress.FINISHED)
xixuan52c2fba2016-05-20 17:02:48 -0700459
460 logging.info('Delete auto-update track status file')
461 self.assertTrue(os.path.exists(progress_tracker.track_status_file))
462 self._MakeRPC('handler_cleanup', host_name=host_name, pid=pid)
463 self.assertFalse(os.path.exists(progress_tracker.track_status_file))
464
465 logging.info('Kill the left auto-update processes for host %s', host_name)
466 progress_tracker.WriteStatus(cros_update_progress.FINISHED)
467 response = self._MakeRPC('kill_au_proc', host_name=host_name)
468 self.assertEqual(response, 'True')
469 self.assertFalse(os.path.exists(progress_tracker.track_status_file))
470 self.assertFalse(cros_update_progress.IsProcessAlive(pid))
471
472
Chris Sosa7cd23202013-10-15 17:22:57 -0700473 def testStageAndUpdate(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700474 """Tests core stage/update autotest workflow where with a test payload."""
475 build_id = 'eve-release/R69-10782.0.0'
Chris Sosa7cd23202013-10-15 17:22:57 -0700476 archive_url = 'gs://chromeos-image-archive/%s' % build_id
477
478 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
479 artifacts='full_payload,stateful')
480 self.assertEqual(response, 'False')
481
482 logging.info('Staging update artifacts')
483 self._MakeRPC(STAGE, archive_url=archive_url,
484 artifacts='full_payload,stateful')
485 logging.info('Staging complete. '
486 'Verifying files exist and are staged in the staging '
487 'directory.')
488 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
489 artifacts='full_payload,stateful')
490 self.assertEqual(response, 'True')
491 staged_dir = os.path.join(self.test_data_path, build_id)
492 self.assertTrue(os.path.isdir(staged_dir))
493 self.assertTrue(os.path.exists(
494 os.path.join(staged_dir, devserver_constants.UPDATE_FILE)))
495 self.assertTrue(os.path.exists(
496 os.path.join(staged_dir, devserver_constants.STATEFUL_FILE)))
497
498 logging.info('Verifying we can update using the stage update artifacts.')
499 self.VerifyHandleUpdate(build_id, use_test_payload=False)
500
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700501 @unittest.skip('crbug.com/640063 Broken test.')
Chris Sosa7cd23202013-10-15 17:22:57 -0700502 def testStageAutotestAndGetPackages(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700503 """Another stage/update autotest workflow test with a test payload."""
504 build_id = 'eve-release/R69-10782.0.0'
Chris Sosa7cd23202013-10-15 17:22:57 -0700505 archive_url = 'gs://chromeos-image-archive/%s' % build_id
506 autotest_artifacts = 'autotest,test_suites,au_suite'
507 logging.info('Staging autotest artifacts (may take a while).')
508 self._MakeRPC(STAGE, archive_url=archive_url, artifacts=autotest_artifacts)
509
510 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
511 artifacts=autotest_artifacts)
512 self.assertEqual(response, 'True')
513
514 # Verify the files exist and are staged in the staging directory.
515 logging.info('Checking directories exist after we staged the files.')
516 staged_dir = os.path.join(self.test_data_path, build_id)
517 autotest_dir = os.path.join(staged_dir, 'autotest')
518 package_dir = os.path.join(autotest_dir, 'packages')
519 self.assertTrue(os.path.isdir(staged_dir))
520 self.assertTrue(os.path.isdir(autotest_dir))
521 self.assertTrue(os.path.isdir(package_dir))
522
523 control_files = self._MakeRPC(CONTROL_FILES, build=build_id,
524 suite_name='bvt')
525 logging.info('Checking for known control file in bvt suite.')
526 self.assertTrue('client/site_tests/platform_FilePerms/'
527 'control' in control_files)
528
529 def testRemoteXBuddyAlias(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700530 """Another stage/update autotest workflow test with a test payload."""
531 build_id = 'eve-release/R69-10782.0.0'
532 xbuddy_path = 'remote/eve/R69-10782.0.0/full_payload'
533 xbuddy_bad_path = 'remote/eve/R32-9999.9999.9999'
Chris Sosa7cd23202013-10-15 17:22:57 -0700534 logging.info('Staging artifacts using xbuddy.')
535 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
536
537 logging.info('Verifying static url returned is valid.')
538 expected_static_url = '/'.join([self.devserver_url, STATIC, build_id])
539 self.assertEqual(response, expected_static_url)
540
541 logging.info('Checking for_update returns an update_url for what we just '
542 'staged.')
543 expected_update_url = '/'.join([self.devserver_url, UPDATE, build_id])
544 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
545 self.assertEqual(response, expected_update_url)
546
547 logging.info('Now give xbuddy a bad path.')
548 self.assertRaises(urllib2.HTTPError,
549 self._MakeRPC,
550 '/'.join([XBUDDY, xbuddy_bad_path]))
551
Prashanth Ba06d2d22014-03-07 15:35:19 -0800552 def testListImageDir(self):
553 """Verifies that we can list the contents of the image directory."""
554 build_id = 'x86-mario-release/R32-4810.0.0'
555 archive_url = 'gs://chromeos-image-archive/%s' % build_id
556 build_dir = os.path.join(self.test_data_path, build_id)
557 shutil.rmtree(build_dir, ignore_errors=True)
558
559 logging.info('checking for %s on an unstaged build.', LIST_IMAGE_DIR)
560 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
561 self.assertTrue(archive_url in response and 'not been staged' in response)
562
563 logging.info('Checking for %s on a staged build.', LIST_IMAGE_DIR)
564 fake_file_name = 'fake_file'
565 try:
566 os.makedirs(build_dir)
567 open(os.path.join(build_dir, fake_file_name), 'w').close()
568 except OSError:
569 logging.error('Could not create files to imitate staged content. '
570 'Build dir %s, file %s', build_dir, fake_file_name)
571 raise
572 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
573 self.assertTrue(fake_file_name in response)
574 shutil.rmtree(build_dir, ignore_errors=True)
Chris Sosa7cd23202013-10-15 17:22:57 -0700575
576if __name__ == '__main__':
577 logging_format = '%(levelname)-8s: %(message)s'
578 logging.basicConfig(level=logging.DEBUG, format=logging_format)
579 unittest.main()