blob: d26cf065a616e07f248fbe048dad62180ddb6248 [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
Chris Sosa7cd23202013-10-15 17:22:57 -070018import json
Chris Sosa7cd23202013-10-15 17:22:57 -070019import os
20import psutil
21import shutil
22import signal
Gilad Arnold7de05f72014-02-14 13:14:20 -080023import socket
Chris Sosa7cd23202013-10-15 17:22:57 -070024import subprocess
25import tempfile
26import time
27import unittest
28import urllib2
29
Amin Hassani0b4843b2019-09-25 16:38:56 -070030from string import Template
31
32from xml.dom import minidom
33
34import cros_update_progress
35import devserver_constants
36
Luis Hector Chaveza1518052018-06-14 08:19:34 -070037from chromite.lib import cros_logging as logging
38
Chris Sosa7cd23202013-10-15 17:22:57 -070039
40# Paths are relative to this script's base directory.
41LABEL = 'devserver'
42TEST_IMAGE_PATH = 'testdata/devserver'
Amin Hassani0b4843b2019-09-25 16:38:56 -070043TEST_UPDATE_PAYLOAD_NAME = 'update.gz'
44TEST_UPDATE_PAYLOAD_METADATA_NAME = 'update.gz.json'
Chris Sosa7cd23202013-10-15 17:22:57 -070045
46# Update request based on Omaha v3 protocol format.
Amin Hassani0b4843b2019-09-25 16:38:56 -070047UPDATE_REQUEST = Template("""<?xml version="1.0" encoding="UTF-8"?>
Amin Hassani495f1de2019-02-26 11:13:39 -080048<request protocol="3.0" updater="ChromeOSUpdateEngine" updaterversion="0.1.0.0" ismachine="1">
Chris Sosa7cd23202013-10-15 17:22:57 -070049 <os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></os>
Amin Hassani0b4843b2019-09-25 16:38:56 -070050 <app appid="$appid" version="11.254.2011_03_09_1814" lang="en-US" track="developer-build" board="x86-generic" hardware_class="BETA DVT" delta_okay="true">
Chris Sosa7cd23202013-10-15 17:22:57 -070051 <updatecheck></updatecheck>
52 </app>
53</request>
Amin Hassani0b4843b2019-09-25 16:38:56 -070054""")
Chris Sosa7cd23202013-10-15 17:22:57 -070055
56# RPC constants.
57STAGE = 'stage'
58IS_STAGED = 'is_staged'
59STATIC = 'static'
60UPDATE = 'update'
61CHECK_HEALTH = 'check_health'
62CONTROL_FILES = 'controlfiles'
63XBUDDY = 'xbuddy'
Prashanth Ba06d2d22014-03-07 15:35:19 -080064LIST_IMAGE_DIR = 'list_image_dir'
Chris Sosa7cd23202013-10-15 17:22:57 -070065
66# API rpcs and constants.
Chris Sosa7cd23202013-10-15 17:22:57 -070067API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
68API_TEST_IP_ADDR = '127.0.0.1'
69
70DEVSERVER_START_TIMEOUT = 15
Gilad Arnold08516112014-02-14 13:14:03 -080071DEVSERVER_START_SLEEP = 1
Gilad Arnold7de05f72014-02-14 13:14:20 -080072MAX_START_ATTEMPTS = 5
Chris Sosa7cd23202013-10-15 17:22:57 -070073
74
75class DevserverFailedToStart(Exception):
76 """Raised if we could not start the devserver."""
77
78
Gilad Arnold08516112014-02-14 13:14:03 -080079class DevserverTestBase(unittest.TestCase):
Chris Sosa7cd23202013-10-15 17:22:57 -070080 """Class containing common logic between devserver test classes."""
81
82 def setUp(self):
Gilad Arnold08516112014-02-14 13:14:03 -080083 """Creates and populates a test directory, temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -070084 self.test_data_path = tempfile.mkdtemp()
85 self.src_dir = os.path.dirname(__file__)
86
Gilad Arnold08516112014-02-14 13:14:03 -080087 # Copy the payload to the location of the update label.
Amin Hassani0b4843b2019-09-25 16:38:56 -070088 self._CreateLabelAndCopyUpdatePayloadFiles(LABEL)
Chris Sosa7cd23202013-10-15 17:22:57 -070089
90 # Copy the payload to the location of forced label.
Amin Hassani0b4843b2019-09-25 16:38:56 -070091 self._CreateLabelAndCopyUpdatePayloadFiles(API_SET_UPDATE_REQUEST)
Chris Sosa7cd23202013-10-15 17:22:57 -070092
Gilad Arnold08516112014-02-14 13:14:03 -080093 # Allocate temporary files for various devserver outputs.
94 self.pidfile = self._MakeTempFile('pid')
95 self.portfile = self._MakeTempFile('port')
96 self.logfile = self._MakeTempFile('log')
Chris Sosa7cd23202013-10-15 17:22:57 -070097
Gilad Arnold08516112014-02-14 13:14:03 -080098 # Initialize various runtime values.
99 self.devserver_url = self.port = self.pid = None
Chris Sosa7cd23202013-10-15 17:22:57 -0700100
101 def tearDown(self):
Gilad Arnold08516112014-02-14 13:14:03 -0800102 """Kill the server, remove the test directory and temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -0700103 if self.pid:
104 os.kill(self.pid, signal.SIGKILL)
105
Gilad Arnold08516112014-02-14 13:14:03 -0800106 self._RemoveFile(self.pidfile)
107 self._RemoveFile(self.portfile)
108 self._RemoveFile(self.logfile)
109 shutil.rmtree(self.test_data_path)
Chris Sosa7cd23202013-10-15 17:22:57 -0700110
111 # Helper methods begin here.
112
Amin Hassani0b4843b2019-09-25 16:38:56 -0700113 def _CreateLabelAndCopyUpdatePayloadFiles(self, label):
Gilad Arnold08516112014-02-14 13:14:03 -0800114 """Creates a label location and copies an image to it."""
Amin Hassani0b4843b2019-09-25 16:38:56 -0700115 update_dir = os.path.join(self.src_dir, TEST_IMAGE_PATH)
Gilad Arnold08516112014-02-14 13:14:03 -0800116 label_dir = os.path.join(self.test_data_path, label)
117 os.makedirs(label_dir)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700118 for name in (TEST_UPDATE_PAYLOAD_NAME, TEST_UPDATE_PAYLOAD_METADATA_NAME):
119 shutil.copy(os.path.join(update_dir, name), label_dir)
Gilad Arnold08516112014-02-14 13:14:03 -0800120
121 def _MakeTempFile(self, suffix):
122 """Return path of a newly created temporary file."""
123 with tempfile.NamedTemporaryFile(suffix='-devserver-%s' % suffix) as f:
124 name = f.name
125 f.close()
126
127 return name
128
129 def _RemoveFile(self, filename):
130 """Removes a file if it is present."""
131 if os.path.isfile(filename):
132 os.remove(filename)
133
134 def _ReadIntValueFromFile(self, path, desc):
135 """Reads a string from file and returns its conversion into an integer."""
136 if not os.path.isfile(path):
137 raise DevserverFailedToStart('Devserver did not drop %s (%r).' %
138 (desc, path))
139
140 with open(path) as f:
141 value_str = f.read()
142
143 try:
144 return int(value_str)
145 except ValueError:
146 raise DevserverFailedToStart('Devserver did not drop a valid value '
147 'in %s (%r).' % (desc, value_str))
148
149 def _StartServer(self, port=0):
Chris Sosa7cd23202013-10-15 17:22:57 -0700150 """Attempts to start devserver on |port|.
151
Gilad Arnold08516112014-02-14 13:14:03 -0800152 In the default case where port == 0, the server will bind to an arbitrary
Dan Shi2f136862016-02-11 15:38:38 -0800153 available port. If successful, this method will set the devserver's pid
Gilad Arnold08516112014-02-14 13:14:03 -0800154 (self.pid), actual listening port (self.port) and URL (self.devserver_url).
Chris Sosa7cd23202013-10-15 17:22:57 -0700155
156 Raises:
157 DevserverFailedToStart: If the devserver could not be started.
158 """
159 cmd = [
160 'python',
161 os.path.join(self.src_dir, 'devserver.py'),
162 'devserver.py',
163 '--static_dir', self.test_data_path,
164 '--pidfile', self.pidfile,
Gilad Arnold08516112014-02-14 13:14:03 -0800165 '--portfile', self.portfile,
Chris Sosa7cd23202013-10-15 17:22:57 -0700166 '--port', str(port),
167 '--logfile', self.logfile]
168
169 # Pipe all output. Use logfile to get devserver log.
170 subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
171
Gilad Arnold08516112014-02-14 13:14:03 -0800172 # Wait for devserver to start, determining its actual serving port and URL.
Chris Sosa7cd23202013-10-15 17:22:57 -0700173 current_time = time.time()
174 deadline = current_time + DEVSERVER_START_TIMEOUT
175 while current_time < deadline:
Chris Sosa7cd23202013-10-15 17:22:57 -0700176 try:
Gilad Arnold08516112014-02-14 13:14:03 -0800177 self.port = self._ReadIntValueFromFile(self.portfile, 'portfile')
178 self.devserver_url = 'http://127.0.0.1:%d' % self.port
Gabe Black3b567202015-09-23 14:07:59 -0700179 self._MakeRPC(CHECK_HEALTH, timeout=1)
Chris Sosa7cd23202013-10-15 17:22:57 -0700180 break
181 except Exception:
Gilad Arnold08516112014-02-14 13:14:03 -0800182 time.sleep(DEVSERVER_START_SLEEP)
183 current_time = time.time()
Chris Sosa7cd23202013-10-15 17:22:57 -0700184 else:
185 raise DevserverFailedToStart('Devserver failed to start within timeout.')
186
Gilad Arnold08516112014-02-14 13:14:03 -0800187 # Retrieve PID.
188 self.pid = self._ReadIntValueFromFile(self.pidfile, 'pidfile')
Chris Sosa7cd23202013-10-15 17:22:57 -0700189
Amin Hassani0b4843b2019-09-25 16:38:56 -0700190 def VerifyHandleUpdate(self, label, use_test_payload=True,
191 appid='{DEV-BUILD}'):
Chris Sosa7cd23202013-10-15 17:22:57 -0700192 """Verifies that we can send an update request to the devserver.
193
194 This method verifies (using a fake update_request blob) that the devserver
195 can interpret the payload and give us back the right payload.
196
197 Args:
198 label: Label that update is served from e.g. <board>-release/<version>
199 use_test_payload: If set to true, expects to serve payload under
200 testdata/ and does extra checks i.e. compares hash and content of
201 payload.
Gilad Arnold08516112014-02-14 13:14:03 -0800202
Chris Sosa7cd23202013-10-15 17:22:57 -0700203 Returns:
204 url of the update payload if we verified the update.
205 """
206 update_label = '/'.join([UPDATE, label])
Amin Hassani0b4843b2019-09-25 16:38:56 -0700207 response = self._MakeRPC(
208 update_label, data=UPDATE_REQUEST.substitute({'appid': appid}))
Chris Sosa7cd23202013-10-15 17:22:57 -0700209 self.assertNotEqual('', response)
210
211 # Parse the response and check if it contains the right result.
212 dom = minidom.parseString(response)
213 update = dom.getElementsByTagName('updatecheck')[0]
214 expected_static_url = '/'.join([self.devserver_url, STATIC, label])
Amin Hassani0b4843b2019-09-25 16:38:56 -0700215 url = self.VerifyV3Response(update, expected_static_url)
Chris Sosa7cd23202013-10-15 17:22:57 -0700216
217 # Verify the image we download is correct since we already know what it is.
218 if use_test_payload:
219 connection = urllib2.urlopen(url)
220 contents = connection.read()
221 connection.close()
222 self.assertEqual('Developers, developers, developers!\n', contents)
223
224 return url
225
Amin Hassani0b4843b2019-09-25 16:38:56 -0700226 def VerifyV3Response(self, update, expected_static_url):
Chris Sosa7cd23202013-10-15 17:22:57 -0700227 """Verifies the update DOM from a v3 response and returns the url."""
228 # Parse the response and check if it contains the right result.
229 urls = update.getElementsByTagName('urls')[0]
230 url = urls.getElementsByTagName('url')[0]
231
232 static_url = url.getAttribute('codebase')
233 # Static url's end in /.
234 self.assertEqual(expected_static_url + '/', static_url)
235
236 manifest = update.getElementsByTagName('manifest')[0]
237 packages = manifest.getElementsByTagName('packages')[0]
238 package = packages.getElementsByTagName('package')[0]
239 filename = package.getAttribute('name')
Amin Hassani0b4843b2019-09-25 16:38:56 -0700240 self.assertEqual(TEST_UPDATE_PAYLOAD_NAME, filename)
Chris Sosa7cd23202013-10-15 17:22:57 -0700241
242 url = os.path.join(static_url, filename)
243 return url
244
245 def _MakeRPC(self, rpc, data=None, timeout=None, **kwargs):
Gilad Arnold08516112014-02-14 13:14:03 -0800246 """Makes an RPC call to the devserver.
Chris Sosa7cd23202013-10-15 17:22:57 -0700247
248 Args:
Gilad Arnold08516112014-02-14 13:14:03 -0800249 rpc: The function to run on the devserver, e.g. 'stage'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700250 data: Optional post data to send.
251 timeout: Optional timeout to pass to urlopen.
Gilad Arnold08516112014-02-14 13:14:03 -0800252 kwargs: Optional arguments to the function, e.g. artifact_url='foo/bar'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700253
Gilad Arnold08516112014-02-14 13:14:03 -0800254 Returns:
255 The function output.
Chris Sosa7cd23202013-10-15 17:22:57 -0700256 """
257 request = '/'.join([self.devserver_url, rpc])
258 if kwargs:
259 # Join the kwargs to the URL.
260 request += '?' + '&'.join('%s=%s' % item for item in kwargs.iteritems())
261
262 output = None
263 try:
264 # Let's log output for all rpc's without timeouts because we only
265 # use timeouts to check to see if something is up and these checks tend
266 # to be small and so logging it will be extremely repetitive.
267 if not timeout:
268 logging.info('Making request using %s', request)
269
270 connection = urllib2.urlopen(request, data=data, timeout=timeout)
271 output = connection.read()
272 connection.close()
273 except urllib2.HTTPError:
274 raise
275
276 return output
277
278
Gilad Arnold08516112014-02-14 13:14:03 -0800279class AutoStartDevserverTestBase(DevserverTestBase):
280 """Test base class that automatically starts the devserver."""
281
282 def setUp(self):
283 """Initialize everything, then start the server."""
284 super(AutoStartDevserverTestBase, self).setUp()
285 self._StartServer()
286
287
Gilad Arnold7de05f72014-02-14 13:14:20 -0800288class DevserverStartTests(DevserverTestBase):
289 """Test that devserver starts up correctly."""
290
291 def testStartAnyPort(self):
292 """Starts the devserver, have it bind to an arbitrary available port."""
293 self._StartServer()
294
295 def testStartSpecificPort(self):
296 """Starts the devserver with a specific port."""
297 for _ in range(MAX_START_ATTEMPTS):
298 # This is a cheap hack to find an arbitrary unused port: we open a socket
299 # and bind it to port zero, then pull out the actual port number and
300 # close the socket. In all likelihood, this will leave us with an
301 # available port number that we can use for starting the devserver.
302 # However, this heuristic is susceptible to race conditions, hence the
303 # retry loop.
304 s = socket.socket()
305 s.bind(('', 0))
306 # s.getsockname() is definitely callable.
307 # pylint: disable=E1102
308 _, port = s.getsockname()
309 s.close()
310
311 self._StartServer(port=port)
312
313
Gilad Arnold08516112014-02-14 13:14:03 -0800314class DevserverBasicTests(AutoStartDevserverTestBase):
315 """Short running tests for the devserver (no remote deps).
Chris Sosa7cd23202013-10-15 17:22:57 -0700316
317 These are technically not unittests because they depend on being able to
318 start a devserver locally which technically requires external resources so
319 they are lumped with the remote tests here.
320 """
321
322 def testHandleUpdateV3(self):
323 self.VerifyHandleUpdate(label=LABEL)
324
Chris Sosa7cd23202013-10-15 17:22:57 -0700325 def testXBuddyLocalAlias(self):
326 """Extensive local image xbuddy unittest.
327
328 This test verifies all the local xbuddy logic by creating a new local folder
329 with the necessary update items and verifies we can use all of them.
330 """
Chris Sosa7cd23202013-10-15 17:22:57 -0700331 build_id = 'x86-generic/R32-9999.0.0-a1'
332 xbuddy_path = 'x86-generic/R32-9999.0.0-a1/test'
333 build_dir = os.path.join(self.test_data_path, build_id)
334 os.makedirs(build_dir)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700335
336 # Writing dummy files.
337 image_data = 'TEST IMAGE'
Chris Sosa7cd23202013-10-15 17:22:57 -0700338 test_image_file = os.path.join(build_dir,
339 devserver_constants.TEST_IMAGE_FILE)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700340 with open(test_image_file, 'w') as f:
341 f.write(image_data)
342
343 stateful_data = 'STATEFUL STUFFS'
Chris Sosa7cd23202013-10-15 17:22:57 -0700344 stateful_file = os.path.join(build_dir, devserver_constants.STATEFUL_FILE)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700345 with open(stateful_file, 'w') as f:
346 f.write(stateful_data)
Chris Sosa7cd23202013-10-15 17:22:57 -0700347
Amin Hassani0b4843b2019-09-25 16:38:56 -0700348 update_dir = os.path.join(self.src_dir, TEST_IMAGE_PATH)
349 for name in (TEST_UPDATE_PAYLOAD_NAME, TEST_UPDATE_PAYLOAD_METADATA_NAME):
350 shutil.copy(os.path.join(update_dir, name), build_dir)
351 with open(os.path.join(build_dir, TEST_UPDATE_PAYLOAD_NAME), 'r') as f:
352 update_data = f.read()
Chris Sosa7cd23202013-10-15 17:22:57 -0700353
Amin Hassani0b4843b2019-09-25 16:38:56 -0700354 for item, data in zip(['full_payload', 'test', 'stateful'],
355 [update_data, image_data, stateful_data]):
Chris Sosa7cd23202013-10-15 17:22:57 -0700356
357 xbuddy_path = '/'.join([build_id, item])
358 logging.info('Testing xbuddy path %s', xbuddy_path)
359 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]))
360 self.assertEqual(response, data)
361
362 expected_dir = '/'.join([self.devserver_url, STATIC, build_id])
363 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
364 self.assertEqual(response, expected_dir)
365
Yu-Ju Hong51495eb2013-12-12 17:08:43 -0800366 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]),
367 relative_path=True)
368 self.assertEqual(response, build_id)
369
Chris Sosa7cd23202013-10-15 17:22:57 -0700370 xbuddy_path = '/'.join([build_id, 'test'])
371 logging.info('Testing for_update for %s', xbuddy_path)
372 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
373 expected_path = '/'.join([self.devserver_url, UPDATE, build_id])
374 self.assertTrue(response, expected_path)
375
376 logging.info('Verifying the actual payload data')
377 url = self.VerifyHandleUpdate(build_id, use_test_payload=False)
378 logging.info('Verify the actual content of the update payload')
379 connection = urllib2.urlopen(url)
380 contents = connection.read()
381 connection.close()
382 self.assertEqual(update_data, contents)
383
384 def testPidFile(self):
385 """Test that using a pidfile works correctly."""
386 with open(self.pidfile, 'r') as f:
387 pid = f.read()
Chris Sosa7cd23202013-10-15 17:22:57 -0700388 # Let's assert some process information about the devserver.
Dan Shi2f136862016-02-11 15:38:38 -0800389 self.assertTrue(pid.strip().isdigit())
Chris Sosa7cd23202013-10-15 17:22:57 -0700390 process = psutil.Process(int(pid))
391 self.assertTrue(process.is_running())
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700392 self.assertTrue('devserver.py' in process.cmdline())
Chris Sosa7cd23202013-10-15 17:22:57 -0700393
394
Gilad Arnold08516112014-02-14 13:14:03 -0800395class DevserverExtendedTests(AutoStartDevserverTestBase):
Chris Sosa7cd23202013-10-15 17:22:57 -0700396 """Longer running integration tests that test interaction with Google Storage.
397
398 Note: due to the interaction with Google Storage, these tests both require
399 1) runner has access to the Google Storage bucket where builders store builds.
400 2) time. These tests actually download the artifacts needed.
401 """
402
xixuan52c2fba2016-05-20 17:02:48 -0700403 def testCrosAU(self):
404 """Tests core autotest workflow where we trigger CrOS auto-update.
405
406 It mainly tests the following API:
407 a. 'get_au_status'
408 b. 'handler_cleanup'
409 c. 'kill_au_proc'
410 """
411 host_name = '100.0.0.0'
xixuan2a0970a2016-08-10 12:12:44 -0700412 p = subprocess.Popen(['sleep 100'], shell=True, preexec_fn=os.setsid)
413 pid = os.getpgid(p.pid)
xixuan52c2fba2016-05-20 17:02:48 -0700414 status = 'updating'
415 progress_tracker = cros_update_progress.AUProgress(host_name, pid)
416 progress_tracker.WriteStatus(status)
417
418 logging.info('Retrieving auto-update status for process %d', pid)
419 response = self._MakeRPC('get_au_status', host_name=host_name, pid=pid)
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700420 self.assertFalse(json.loads(response)['finished'])
421 self.assertEqual(json.loads(response)['status'], status)
xixuan52c2fba2016-05-20 17:02:48 -0700422
423 progress_tracker.WriteStatus(cros_update_progress.FINISHED)
424 logging.info('Mock auto-update process is finished')
425 response = self._MakeRPC('get_au_status', host_name=host_name, pid=pid)
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700426 self.assertTrue(json.loads(response)['finished'])
427 self.assertEqual(json.loads(response)['status'],
428 cros_update_progress.FINISHED)
xixuan52c2fba2016-05-20 17:02:48 -0700429
430 logging.info('Delete auto-update track status file')
431 self.assertTrue(os.path.exists(progress_tracker.track_status_file))
432 self._MakeRPC('handler_cleanup', host_name=host_name, pid=pid)
433 self.assertFalse(os.path.exists(progress_tracker.track_status_file))
434
435 logging.info('Kill the left auto-update processes for host %s', host_name)
436 progress_tracker.WriteStatus(cros_update_progress.FINISHED)
437 response = self._MakeRPC('kill_au_proc', host_name=host_name)
438 self.assertEqual(response, 'True')
439 self.assertFalse(os.path.exists(progress_tracker.track_status_file))
440 self.assertFalse(cros_update_progress.IsProcessAlive(pid))
441
442
Chris Sosa7cd23202013-10-15 17:22:57 -0700443 def testStageAndUpdate(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700444 """Tests core stage/update autotest workflow where with a test payload."""
Amin Hassani0b4843b2019-09-25 16:38:56 -0700445 build_id = 'eve-release/R78-12499.0.0'
Chris Sosa7cd23202013-10-15 17:22:57 -0700446 archive_url = 'gs://chromeos-image-archive/%s' % build_id
447
448 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
449 artifacts='full_payload,stateful')
450 self.assertEqual(response, 'False')
451
452 logging.info('Staging update artifacts')
453 self._MakeRPC(STAGE, archive_url=archive_url,
454 artifacts='full_payload,stateful')
455 logging.info('Staging complete. '
456 'Verifying files exist and are staged in the staging '
457 'directory.')
458 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
459 artifacts='full_payload,stateful')
460 self.assertEqual(response, 'True')
461 staged_dir = os.path.join(self.test_data_path, build_id)
462 self.assertTrue(os.path.isdir(staged_dir))
463 self.assertTrue(os.path.exists(
464 os.path.join(staged_dir, devserver_constants.UPDATE_FILE)))
465 self.assertTrue(os.path.exists(
Amin Hassani0b4843b2019-09-25 16:38:56 -0700466 os.path.join(staged_dir, devserver_constants.UPDATE_METADATA_FILE)))
467 self.assertTrue(os.path.exists(
Chris Sosa7cd23202013-10-15 17:22:57 -0700468 os.path.join(staged_dir, devserver_constants.STATEFUL_FILE)))
469
470 logging.info('Verifying we can update using the stage update artifacts.')
Amin Hassani0b4843b2019-09-25 16:38:56 -0700471 self.VerifyHandleUpdate(build_id, use_test_payload=False,
472 appid='{01906EA2-3EB2-41F1-8F62-F0B7120EFD2E}')
Chris Sosa7cd23202013-10-15 17:22:57 -0700473
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700474 @unittest.skip('crbug.com/640063 Broken test.')
Chris Sosa7cd23202013-10-15 17:22:57 -0700475 def testStageAutotestAndGetPackages(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700476 """Another stage/update autotest workflow test with a test payload."""
477 build_id = 'eve-release/R69-10782.0.0'
Chris Sosa7cd23202013-10-15 17:22:57 -0700478 archive_url = 'gs://chromeos-image-archive/%s' % build_id
479 autotest_artifacts = 'autotest,test_suites,au_suite'
480 logging.info('Staging autotest artifacts (may take a while).')
481 self._MakeRPC(STAGE, archive_url=archive_url, artifacts=autotest_artifacts)
482
483 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
484 artifacts=autotest_artifacts)
485 self.assertEqual(response, 'True')
486
487 # Verify the files exist and are staged in the staging directory.
488 logging.info('Checking directories exist after we staged the files.')
489 staged_dir = os.path.join(self.test_data_path, build_id)
490 autotest_dir = os.path.join(staged_dir, 'autotest')
491 package_dir = os.path.join(autotest_dir, 'packages')
492 self.assertTrue(os.path.isdir(staged_dir))
493 self.assertTrue(os.path.isdir(autotest_dir))
494 self.assertTrue(os.path.isdir(package_dir))
495
496 control_files = self._MakeRPC(CONTROL_FILES, build=build_id,
497 suite_name='bvt')
498 logging.info('Checking for known control file in bvt suite.')
499 self.assertTrue('client/site_tests/platform_FilePerms/'
500 'control' in control_files)
501
502 def testRemoteXBuddyAlias(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'
505 xbuddy_path = 'remote/eve/R69-10782.0.0/full_payload'
506 xbuddy_bad_path = 'remote/eve/R32-9999.9999.9999'
Chris Sosa7cd23202013-10-15 17:22:57 -0700507 logging.info('Staging artifacts using xbuddy.')
508 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
509
510 logging.info('Verifying static url returned is valid.')
511 expected_static_url = '/'.join([self.devserver_url, STATIC, build_id])
512 self.assertEqual(response, expected_static_url)
513
514 logging.info('Checking for_update returns an update_url for what we just '
515 'staged.')
516 expected_update_url = '/'.join([self.devserver_url, UPDATE, build_id])
517 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
518 self.assertEqual(response, expected_update_url)
519
520 logging.info('Now give xbuddy a bad path.')
521 self.assertRaises(urllib2.HTTPError,
522 self._MakeRPC,
523 '/'.join([XBUDDY, xbuddy_bad_path]))
524
Prashanth Ba06d2d22014-03-07 15:35:19 -0800525 def testListImageDir(self):
526 """Verifies that we can list the contents of the image directory."""
527 build_id = 'x86-mario-release/R32-4810.0.0'
528 archive_url = 'gs://chromeos-image-archive/%s' % build_id
529 build_dir = os.path.join(self.test_data_path, build_id)
530 shutil.rmtree(build_dir, ignore_errors=True)
531
532 logging.info('checking for %s on an unstaged build.', LIST_IMAGE_DIR)
533 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
534 self.assertTrue(archive_url in response and 'not been staged' in response)
535
536 logging.info('Checking for %s on a staged build.', LIST_IMAGE_DIR)
537 fake_file_name = 'fake_file'
538 try:
539 os.makedirs(build_dir)
540 open(os.path.join(build_dir, fake_file_name), 'w').close()
541 except OSError:
542 logging.error('Could not create files to imitate staged content. '
543 'Build dir %s, file %s', build_dir, fake_file_name)
544 raise
545 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
546 self.assertTrue(fake_file_name in response)
547 shutil.rmtree(build_dir, ignore_errors=True)
Chris Sosa7cd23202013-10-15 17:22:57 -0700548
549if __name__ == '__main__':
550 logging_format = '%(levelname)-8s: %(message)s'
551 logging.basicConfig(level=logging.DEBUG, format=logging_format)
552 unittest.main()