blob: 502da782e42daa88758a51c545216522e5856724 [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
Chris Sosa7cd23202013-10-15 17:22:57 -070020import shutil
21import signal
Gilad Arnold7de05f72014-02-14 13:14:20 -080022import socket
Chris Sosa7cd23202013-10-15 17:22:57 -070023import subprocess
24import tempfile
25import time
26import unittest
Chris Sosa7cd23202013-10-15 17:22:57 -070027
Amin Hassani0b4843b2019-09-25 16:38:56 -070028from string import Template
29
30from xml.dom import minidom
31
Amin Hassani469f5702019-10-21 15:35:06 -070032from six.moves import urllib
33
34import psutil # pylint: disable=import-error
35
Amin Hassani0b4843b2019-09-25 16:38:56 -070036import cros_update_progress
Amin Hassani0b4843b2019-09-25 16:38:56 -070037
Amin Hassani469f5702019-10-21 15:35:06 -070038import setup_chromite # pylint: disable=unused-import
Luis Hector Chaveza1518052018-06-14 08:19:34 -070039from chromite.lib import cros_logging as logging
Achuith Bhandarkar662fb722019-10-31 16:12:49 -070040from chromite.lib.xbuddy import devserver_constants
Luis Hector Chaveza1518052018-06-14 08:19:34 -070041
Chris Sosa7cd23202013-10-15 17:22:57 -070042
43# Paths are relative to this script's base directory.
44LABEL = 'devserver'
45TEST_IMAGE_PATH = 'testdata/devserver'
Amin Hassani0b4843b2019-09-25 16:38:56 -070046TEST_UPDATE_PAYLOAD_NAME = 'update.gz'
47TEST_UPDATE_PAYLOAD_METADATA_NAME = 'update.gz.json'
Chris Sosa7cd23202013-10-15 17:22:57 -070048
49# Update request based on Omaha v3 protocol format.
Amin Hassani0b4843b2019-09-25 16:38:56 -070050UPDATE_REQUEST = Template("""<?xml version="1.0" encoding="UTF-8"?>
Amin Hassani495f1de2019-02-26 11:13:39 -080051<request protocol="3.0" updater="ChromeOSUpdateEngine" updaterversion="0.1.0.0" ismachine="1">
Chris Sosa7cd23202013-10-15 17:22:57 -070052 <os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></os>
Amin Hassani0b4843b2019-09-25 16:38:56 -070053 <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 -070054 <updatecheck></updatecheck>
55 </app>
56</request>
Amin Hassani0b4843b2019-09-25 16:38:56 -070057""")
Chris Sosa7cd23202013-10-15 17:22:57 -070058
59# RPC constants.
60STAGE = 'stage'
61IS_STAGED = 'is_staged'
62STATIC = 'static'
63UPDATE = 'update'
64CHECK_HEALTH = 'check_health'
65CONTROL_FILES = 'controlfiles'
66XBUDDY = 'xbuddy'
Prashanth Ba06d2d22014-03-07 15:35:19 -080067LIST_IMAGE_DIR = 'list_image_dir'
Chris Sosa7cd23202013-10-15 17:22:57 -070068
69# API rpcs and constants.
Chris Sosa7cd23202013-10-15 17:22:57 -070070API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
71API_TEST_IP_ADDR = '127.0.0.1'
72
73DEVSERVER_START_TIMEOUT = 15
Gilad Arnold08516112014-02-14 13:14:03 -080074DEVSERVER_START_SLEEP = 1
Gilad Arnold7de05f72014-02-14 13:14:20 -080075MAX_START_ATTEMPTS = 5
Chris Sosa7cd23202013-10-15 17:22:57 -070076
77
78class DevserverFailedToStart(Exception):
79 """Raised if we could not start the devserver."""
80
81
Gilad Arnold08516112014-02-14 13:14:03 -080082class DevserverTestBase(unittest.TestCase):
Chris Sosa7cd23202013-10-15 17:22:57 -070083 """Class containing common logic between devserver test classes."""
84
85 def setUp(self):
Gilad Arnold08516112014-02-14 13:14:03 -080086 """Creates and populates a test directory, temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -070087 self.test_data_path = tempfile.mkdtemp()
88 self.src_dir = os.path.dirname(__file__)
89
Gilad Arnold08516112014-02-14 13:14:03 -080090 # Copy the payload to the location of the update label.
Amin Hassani0b4843b2019-09-25 16:38:56 -070091 self._CreateLabelAndCopyUpdatePayloadFiles(LABEL)
Chris Sosa7cd23202013-10-15 17:22:57 -070092
93 # Copy the payload to the location of forced label.
Amin Hassani0b4843b2019-09-25 16:38:56 -070094 self._CreateLabelAndCopyUpdatePayloadFiles(API_SET_UPDATE_REQUEST)
Chris Sosa7cd23202013-10-15 17:22:57 -070095
Gilad Arnold08516112014-02-14 13:14:03 -080096 # Allocate temporary files for various devserver outputs.
97 self.pidfile = self._MakeTempFile('pid')
98 self.portfile = self._MakeTempFile('port')
99 self.logfile = self._MakeTempFile('log')
Chris Sosa7cd23202013-10-15 17:22:57 -0700100
Gilad Arnold08516112014-02-14 13:14:03 -0800101 # Initialize various runtime values.
102 self.devserver_url = self.port = self.pid = None
Chris Sosa7cd23202013-10-15 17:22:57 -0700103
104 def tearDown(self):
Gilad Arnold08516112014-02-14 13:14:03 -0800105 """Kill the server, remove the test directory and temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -0700106 if self.pid:
107 os.kill(self.pid, signal.SIGKILL)
108
Gilad Arnold08516112014-02-14 13:14:03 -0800109 self._RemoveFile(self.pidfile)
110 self._RemoveFile(self.portfile)
111 self._RemoveFile(self.logfile)
112 shutil.rmtree(self.test_data_path)
Chris Sosa7cd23202013-10-15 17:22:57 -0700113
114 # Helper methods begin here.
115
Amin Hassani0b4843b2019-09-25 16:38:56 -0700116 def _CreateLabelAndCopyUpdatePayloadFiles(self, label):
Gilad Arnold08516112014-02-14 13:14:03 -0800117 """Creates a label location and copies an image to it."""
Amin Hassani0b4843b2019-09-25 16:38:56 -0700118 update_dir = os.path.join(self.src_dir, TEST_IMAGE_PATH)
Gilad Arnold08516112014-02-14 13:14:03 -0800119 label_dir = os.path.join(self.test_data_path, label)
120 os.makedirs(label_dir)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700121 for name in (TEST_UPDATE_PAYLOAD_NAME, TEST_UPDATE_PAYLOAD_METADATA_NAME):
122 shutil.copy(os.path.join(update_dir, name), label_dir)
Gilad Arnold08516112014-02-14 13:14:03 -0800123
124 def _MakeTempFile(self, suffix):
125 """Return path of a newly created temporary file."""
126 with tempfile.NamedTemporaryFile(suffix='-devserver-%s' % suffix) as f:
127 name = f.name
128 f.close()
129
130 return name
131
132 def _RemoveFile(self, filename):
133 """Removes a file if it is present."""
134 if os.path.isfile(filename):
135 os.remove(filename)
136
137 def _ReadIntValueFromFile(self, path, desc):
138 """Reads a string from file and returns its conversion into an integer."""
139 if not os.path.isfile(path):
140 raise DevserverFailedToStart('Devserver did not drop %s (%r).' %
141 (desc, path))
142
143 with open(path) as f:
144 value_str = f.read()
145
146 try:
147 return int(value_str)
148 except ValueError:
149 raise DevserverFailedToStart('Devserver did not drop a valid value '
150 'in %s (%r).' % (desc, value_str))
151
152 def _StartServer(self, port=0):
Chris Sosa7cd23202013-10-15 17:22:57 -0700153 """Attempts to start devserver on |port|.
154
Gilad Arnold08516112014-02-14 13:14:03 -0800155 In the default case where port == 0, the server will bind to an arbitrary
Dan Shi2f136862016-02-11 15:38:38 -0800156 available port. If successful, this method will set the devserver's pid
Gilad Arnold08516112014-02-14 13:14:03 -0800157 (self.pid), actual listening port (self.port) and URL (self.devserver_url).
Chris Sosa7cd23202013-10-15 17:22:57 -0700158
159 Raises:
160 DevserverFailedToStart: If the devserver could not be started.
161 """
162 cmd = [
163 'python',
164 os.path.join(self.src_dir, 'devserver.py'),
165 'devserver.py',
166 '--static_dir', self.test_data_path,
167 '--pidfile', self.pidfile,
Gilad Arnold08516112014-02-14 13:14:03 -0800168 '--portfile', self.portfile,
Chris Sosa7cd23202013-10-15 17:22:57 -0700169 '--port', str(port),
170 '--logfile', self.logfile]
171
172 # Pipe all output. Use logfile to get devserver log.
173 subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
174
Gilad Arnold08516112014-02-14 13:14:03 -0800175 # Wait for devserver to start, determining its actual serving port and URL.
Chris Sosa7cd23202013-10-15 17:22:57 -0700176 current_time = time.time()
177 deadline = current_time + DEVSERVER_START_TIMEOUT
178 while current_time < deadline:
Chris Sosa7cd23202013-10-15 17:22:57 -0700179 try:
Gilad Arnold08516112014-02-14 13:14:03 -0800180 self.port = self._ReadIntValueFromFile(self.portfile, 'portfile')
181 self.devserver_url = 'http://127.0.0.1:%d' % self.port
Gabe Black3b567202015-09-23 14:07:59 -0700182 self._MakeRPC(CHECK_HEALTH, timeout=1)
Chris Sosa7cd23202013-10-15 17:22:57 -0700183 break
184 except Exception:
Gilad Arnold08516112014-02-14 13:14:03 -0800185 time.sleep(DEVSERVER_START_SLEEP)
186 current_time = time.time()
Chris Sosa7cd23202013-10-15 17:22:57 -0700187 else:
188 raise DevserverFailedToStart('Devserver failed to start within timeout.')
189
Gilad Arnold08516112014-02-14 13:14:03 -0800190 # Retrieve PID.
191 self.pid = self._ReadIntValueFromFile(self.pidfile, 'pidfile')
Chris Sosa7cd23202013-10-15 17:22:57 -0700192
Amin Hassani0b4843b2019-09-25 16:38:56 -0700193 def VerifyHandleUpdate(self, label, use_test_payload=True,
194 appid='{DEV-BUILD}'):
Chris Sosa7cd23202013-10-15 17:22:57 -0700195 """Verifies that we can send an update request to the devserver.
196
197 This method verifies (using a fake update_request blob) that the devserver
198 can interpret the payload and give us back the right payload.
199
200 Args:
201 label: Label that update is served from e.g. <board>-release/<version>
202 use_test_payload: If set to true, expects to serve payload under
203 testdata/ and does extra checks i.e. compares hash and content of
204 payload.
Gilad Arnold08516112014-02-14 13:14:03 -0800205
Chris Sosa7cd23202013-10-15 17:22:57 -0700206 Returns:
207 url of the update payload if we verified the update.
208 """
209 update_label = '/'.join([UPDATE, label])
Amin Hassani0b4843b2019-09-25 16:38:56 -0700210 response = self._MakeRPC(
211 update_label, data=UPDATE_REQUEST.substitute({'appid': appid}))
Chris Sosa7cd23202013-10-15 17:22:57 -0700212 self.assertNotEqual('', response)
213
214 # Parse the response and check if it contains the right result.
215 dom = minidom.parseString(response)
216 update = dom.getElementsByTagName('updatecheck')[0]
217 expected_static_url = '/'.join([self.devserver_url, STATIC, label])
Amin Hassani0b4843b2019-09-25 16:38:56 -0700218 url = self.VerifyV3Response(update, expected_static_url)
Chris Sosa7cd23202013-10-15 17:22:57 -0700219
220 # Verify the image we download is correct since we already know what it is.
221 if use_test_payload:
Amin Hassani469f5702019-10-21 15:35:06 -0700222 connection = urllib.request.urlopen(url)
Chris Sosa7cd23202013-10-15 17:22:57 -0700223 contents = connection.read()
224 connection.close()
225 self.assertEqual('Developers, developers, developers!\n', contents)
226
227 return url
228
Amin Hassani0b4843b2019-09-25 16:38:56 -0700229 def VerifyV3Response(self, update, expected_static_url):
Chris Sosa7cd23202013-10-15 17:22:57 -0700230 """Verifies the update DOM from a v3 response and returns the url."""
231 # Parse the response and check if it contains the right result.
232 urls = update.getElementsByTagName('urls')[0]
233 url = urls.getElementsByTagName('url')[0]
234
235 static_url = url.getAttribute('codebase')
236 # Static url's end in /.
237 self.assertEqual(expected_static_url + '/', static_url)
238
239 manifest = update.getElementsByTagName('manifest')[0]
240 packages = manifest.getElementsByTagName('packages')[0]
241 package = packages.getElementsByTagName('package')[0]
242 filename = package.getAttribute('name')
Amin Hassani0b4843b2019-09-25 16:38:56 -0700243 self.assertEqual(TEST_UPDATE_PAYLOAD_NAME, filename)
Chris Sosa7cd23202013-10-15 17:22:57 -0700244
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.
Amin Hassani469f5702019-10-21 15:35:06 -0700263 request += '?' + '&'.join('%s=%s' % item for item in kwargs.items())
Chris Sosa7cd23202013-10-15 17:22:57 -0700264
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
Amin Hassani469f5702019-10-21 15:35:06 -0700273 # pylint: disable=redundant-keyword-arg
274 connection = urllib.request.urlopen(request, data=data, timeout=timeout)
Chris Sosa7cd23202013-10-15 17:22:57 -0700275 output = connection.read()
276 connection.close()
Amin Hassani469f5702019-10-21 15:35:06 -0700277 except urllib.error.HTTPError:
Chris Sosa7cd23202013-10-15 17:22:57 -0700278 raise
279
280 return output
281
282
Gilad Arnold08516112014-02-14 13:14:03 -0800283class AutoStartDevserverTestBase(DevserverTestBase):
284 """Test base class that automatically starts the devserver."""
285
286 def setUp(self):
287 """Initialize everything, then start the server."""
288 super(AutoStartDevserverTestBase, self).setUp()
289 self._StartServer()
290
291
Gilad Arnold7de05f72014-02-14 13:14:20 -0800292class DevserverStartTests(DevserverTestBase):
293 """Test that devserver starts up correctly."""
294
295 def testStartAnyPort(self):
296 """Starts the devserver, have it bind to an arbitrary available port."""
297 self._StartServer()
298
299 def testStartSpecificPort(self):
300 """Starts the devserver with a specific port."""
301 for _ in range(MAX_START_ATTEMPTS):
302 # This is a cheap hack to find an arbitrary unused port: we open a socket
303 # and bind it to port zero, then pull out the actual port number and
304 # close the socket. In all likelihood, this will leave us with an
305 # available port number that we can use for starting the devserver.
306 # However, this heuristic is susceptible to race conditions, hence the
307 # retry loop.
308 s = socket.socket()
309 s.bind(('', 0))
310 # s.getsockname() is definitely callable.
311 # pylint: disable=E1102
312 _, port = s.getsockname()
313 s.close()
314
315 self._StartServer(port=port)
316
317
Gilad Arnold08516112014-02-14 13:14:03 -0800318class DevserverBasicTests(AutoStartDevserverTestBase):
319 """Short running tests for the devserver (no remote deps).
Chris Sosa7cd23202013-10-15 17:22:57 -0700320
321 These are technically not unittests because they depend on being able to
322 start a devserver locally which technically requires external resources so
323 they are lumped with the remote tests here.
324 """
325
326 def testHandleUpdateV3(self):
327 self.VerifyHandleUpdate(label=LABEL)
328
Chris Sosa7cd23202013-10-15 17:22:57 -0700329 def testXBuddyLocalAlias(self):
330 """Extensive local image xbuddy unittest.
331
332 This test verifies all the local xbuddy logic by creating a new local folder
333 with the necessary update items and verifies we can use all of them.
334 """
Chris Sosa7cd23202013-10-15 17:22:57 -0700335 build_id = 'x86-generic/R32-9999.0.0-a1'
336 xbuddy_path = 'x86-generic/R32-9999.0.0-a1/test'
337 build_dir = os.path.join(self.test_data_path, build_id)
338 os.makedirs(build_dir)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700339
340 # Writing dummy files.
341 image_data = 'TEST IMAGE'
Chris Sosa7cd23202013-10-15 17:22:57 -0700342 test_image_file = os.path.join(build_dir,
343 devserver_constants.TEST_IMAGE_FILE)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700344 with open(test_image_file, 'w') as f:
345 f.write(image_data)
346
347 stateful_data = 'STATEFUL STUFFS'
Chris Sosa7cd23202013-10-15 17:22:57 -0700348 stateful_file = os.path.join(build_dir, devserver_constants.STATEFUL_FILE)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700349 with open(stateful_file, 'w') as f:
350 f.write(stateful_data)
Chris Sosa7cd23202013-10-15 17:22:57 -0700351
Amin Hassani0b4843b2019-09-25 16:38:56 -0700352 update_dir = os.path.join(self.src_dir, TEST_IMAGE_PATH)
353 for name in (TEST_UPDATE_PAYLOAD_NAME, TEST_UPDATE_PAYLOAD_METADATA_NAME):
354 shutil.copy(os.path.join(update_dir, name), build_dir)
355 with open(os.path.join(build_dir, TEST_UPDATE_PAYLOAD_NAME), 'r') as f:
356 update_data = f.read()
Chris Sosa7cd23202013-10-15 17:22:57 -0700357
Amin Hassani0b4843b2019-09-25 16:38:56 -0700358 for item, data in zip(['full_payload', 'test', 'stateful'],
359 [update_data, image_data, stateful_data]):
Chris Sosa7cd23202013-10-15 17:22:57 -0700360
361 xbuddy_path = '/'.join([build_id, item])
362 logging.info('Testing xbuddy path %s', xbuddy_path)
363 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]))
364 self.assertEqual(response, data)
365
366 expected_dir = '/'.join([self.devserver_url, STATIC, build_id])
367 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
368 self.assertEqual(response, expected_dir)
369
Yu-Ju Hong51495eb2013-12-12 17:08:43 -0800370 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]),
371 relative_path=True)
372 self.assertEqual(response, build_id)
373
Chris Sosa7cd23202013-10-15 17:22:57 -0700374 xbuddy_path = '/'.join([build_id, 'test'])
375 logging.info('Testing for_update for %s', xbuddy_path)
376 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
377 expected_path = '/'.join([self.devserver_url, UPDATE, build_id])
378 self.assertTrue(response, expected_path)
379
380 logging.info('Verifying the actual payload data')
381 url = self.VerifyHandleUpdate(build_id, use_test_payload=False)
382 logging.info('Verify the actual content of the update payload')
Amin Hassani469f5702019-10-21 15:35:06 -0700383 connection = urllib.request.urlopen(url)
Chris Sosa7cd23202013-10-15 17:22:57 -0700384 contents = connection.read()
385 connection.close()
386 self.assertEqual(update_data, contents)
387
388 def testPidFile(self):
389 """Test that using a pidfile works correctly."""
390 with open(self.pidfile, 'r') as f:
391 pid = f.read()
Chris Sosa7cd23202013-10-15 17:22:57 -0700392 # Let's assert some process information about the devserver.
Dan Shi2f136862016-02-11 15:38:38 -0800393 self.assertTrue(pid.strip().isdigit())
Chris Sosa7cd23202013-10-15 17:22:57 -0700394 process = psutil.Process(int(pid))
395 self.assertTrue(process.is_running())
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700396 self.assertTrue('devserver.py' in process.cmdline())
Chris Sosa7cd23202013-10-15 17:22:57 -0700397
Amin Hassani28df4212019-10-28 10:16:50 -0700398 def testFileInfo(self):
399 """Verifies the fileinfo API."""
400 response = self._MakeRPC('/'.join(['api', 'fileinfo', LABEL,
401 TEST_UPDATE_PAYLOAD_NAME]))
402 self.assertEqual(
403 response,
404 '{"sha256": "hgfZ19hcsA5OGUN3f4SDtoO0kzT24o+JsGgjNVCVEy0=", '
405 '"size": 36}')
406
Chris Sosa7cd23202013-10-15 17:22:57 -0700407
Gilad Arnold08516112014-02-14 13:14:03 -0800408class DevserverExtendedTests(AutoStartDevserverTestBase):
Chris Sosa7cd23202013-10-15 17:22:57 -0700409 """Longer running integration tests that test interaction with Google Storage.
410
411 Note: due to the interaction with Google Storage, these tests both require
412 1) runner has access to the Google Storage bucket where builders store builds.
413 2) time. These tests actually download the artifacts needed.
414 """
415
xixuan52c2fba2016-05-20 17:02:48 -0700416 def testCrosAU(self):
417 """Tests core autotest workflow where we trigger CrOS auto-update.
418
419 It mainly tests the following API:
420 a. 'get_au_status'
421 b. 'handler_cleanup'
422 c. 'kill_au_proc'
423 """
424 host_name = '100.0.0.0'
xixuan2a0970a2016-08-10 12:12:44 -0700425 p = subprocess.Popen(['sleep 100'], shell=True, preexec_fn=os.setsid)
426 pid = os.getpgid(p.pid)
xixuan52c2fba2016-05-20 17:02:48 -0700427 status = 'updating'
428 progress_tracker = cros_update_progress.AUProgress(host_name, pid)
429 progress_tracker.WriteStatus(status)
430
431 logging.info('Retrieving auto-update status for process %d', pid)
432 response = self._MakeRPC('get_au_status', host_name=host_name, pid=pid)
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700433 self.assertFalse(json.loads(response)['finished'])
434 self.assertEqual(json.loads(response)['status'], status)
xixuan52c2fba2016-05-20 17:02:48 -0700435
436 progress_tracker.WriteStatus(cros_update_progress.FINISHED)
437 logging.info('Mock auto-update process is finished')
438 response = self._MakeRPC('get_au_status', host_name=host_name, pid=pid)
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700439 self.assertTrue(json.loads(response)['finished'])
440 self.assertEqual(json.loads(response)['status'],
441 cros_update_progress.FINISHED)
xixuan52c2fba2016-05-20 17:02:48 -0700442
443 logging.info('Delete auto-update track status file')
444 self.assertTrue(os.path.exists(progress_tracker.track_status_file))
445 self._MakeRPC('handler_cleanup', host_name=host_name, pid=pid)
446 self.assertFalse(os.path.exists(progress_tracker.track_status_file))
447
448 logging.info('Kill the left auto-update processes for host %s', host_name)
449 progress_tracker.WriteStatus(cros_update_progress.FINISHED)
450 response = self._MakeRPC('kill_au_proc', host_name=host_name)
451 self.assertEqual(response, 'True')
452 self.assertFalse(os.path.exists(progress_tracker.track_status_file))
453 self.assertFalse(cros_update_progress.IsProcessAlive(pid))
454
455
Chris Sosa7cd23202013-10-15 17:22:57 -0700456 def testStageAndUpdate(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700457 """Tests core stage/update autotest workflow where with a test payload."""
Amin Hassani0b4843b2019-09-25 16:38:56 -0700458 build_id = 'eve-release/R78-12499.0.0'
Chris Sosa7cd23202013-10-15 17:22:57 -0700459 archive_url = 'gs://chromeos-image-archive/%s' % build_id
460
461 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
462 artifacts='full_payload,stateful')
463 self.assertEqual(response, 'False')
464
465 logging.info('Staging update artifacts')
466 self._MakeRPC(STAGE, archive_url=archive_url,
467 artifacts='full_payload,stateful')
468 logging.info('Staging complete. '
469 'Verifying files exist and are staged in the staging '
470 'directory.')
471 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
472 artifacts='full_payload,stateful')
473 self.assertEqual(response, 'True')
474 staged_dir = os.path.join(self.test_data_path, build_id)
475 self.assertTrue(os.path.isdir(staged_dir))
476 self.assertTrue(os.path.exists(
477 os.path.join(staged_dir, devserver_constants.UPDATE_FILE)))
478 self.assertTrue(os.path.exists(
Amin Hassani0b4843b2019-09-25 16:38:56 -0700479 os.path.join(staged_dir, devserver_constants.UPDATE_METADATA_FILE)))
480 self.assertTrue(os.path.exists(
Chris Sosa7cd23202013-10-15 17:22:57 -0700481 os.path.join(staged_dir, devserver_constants.STATEFUL_FILE)))
482
483 logging.info('Verifying we can update using the stage update artifacts.')
Amin Hassani0b4843b2019-09-25 16:38:56 -0700484 self.VerifyHandleUpdate(build_id, use_test_payload=False,
485 appid='{01906EA2-3EB2-41F1-8F62-F0B7120EFD2E}')
Chris Sosa7cd23202013-10-15 17:22:57 -0700486
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700487 @unittest.skip('crbug.com/640063 Broken test.')
Chris Sosa7cd23202013-10-15 17:22:57 -0700488 def testStageAutotestAndGetPackages(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700489 """Another stage/update autotest workflow test with a test payload."""
490 build_id = 'eve-release/R69-10782.0.0'
Chris Sosa7cd23202013-10-15 17:22:57 -0700491 archive_url = 'gs://chromeos-image-archive/%s' % build_id
492 autotest_artifacts = 'autotest,test_suites,au_suite'
493 logging.info('Staging autotest artifacts (may take a while).')
494 self._MakeRPC(STAGE, archive_url=archive_url, artifacts=autotest_artifacts)
495
496 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
497 artifacts=autotest_artifacts)
498 self.assertEqual(response, 'True')
499
500 # Verify the files exist and are staged in the staging directory.
501 logging.info('Checking directories exist after we staged the files.')
502 staged_dir = os.path.join(self.test_data_path, build_id)
503 autotest_dir = os.path.join(staged_dir, 'autotest')
504 package_dir = os.path.join(autotest_dir, 'packages')
505 self.assertTrue(os.path.isdir(staged_dir))
506 self.assertTrue(os.path.isdir(autotest_dir))
507 self.assertTrue(os.path.isdir(package_dir))
508
509 control_files = self._MakeRPC(CONTROL_FILES, build=build_id,
510 suite_name='bvt')
511 logging.info('Checking for known control file in bvt suite.')
512 self.assertTrue('client/site_tests/platform_FilePerms/'
513 'control' in control_files)
514
515 def testRemoteXBuddyAlias(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700516 """Another stage/update autotest workflow test with a test payload."""
517 build_id = 'eve-release/R69-10782.0.0'
518 xbuddy_path = 'remote/eve/R69-10782.0.0/full_payload'
519 xbuddy_bad_path = 'remote/eve/R32-9999.9999.9999'
Chris Sosa7cd23202013-10-15 17:22:57 -0700520 logging.info('Staging artifacts using xbuddy.')
521 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
522
523 logging.info('Verifying static url returned is valid.')
524 expected_static_url = '/'.join([self.devserver_url, STATIC, build_id])
525 self.assertEqual(response, expected_static_url)
526
527 logging.info('Checking for_update returns an update_url for what we just '
528 'staged.')
529 expected_update_url = '/'.join([self.devserver_url, UPDATE, build_id])
530 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), for_update=True)
531 self.assertEqual(response, expected_update_url)
532
533 logging.info('Now give xbuddy a bad path.')
Amin Hassani469f5702019-10-21 15:35:06 -0700534 self.assertRaises(urllib.error.HTTPError,
Chris Sosa7cd23202013-10-15 17:22:57 -0700535 self._MakeRPC,
536 '/'.join([XBUDDY, xbuddy_bad_path]))
537
Prashanth Ba06d2d22014-03-07 15:35:19 -0800538 def testListImageDir(self):
539 """Verifies that we can list the contents of the image directory."""
540 build_id = 'x86-mario-release/R32-4810.0.0'
541 archive_url = 'gs://chromeos-image-archive/%s' % build_id
542 build_dir = os.path.join(self.test_data_path, build_id)
543 shutil.rmtree(build_dir, ignore_errors=True)
544
545 logging.info('checking for %s on an unstaged build.', LIST_IMAGE_DIR)
546 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
547 self.assertTrue(archive_url in response and 'not been staged' in response)
548
549 logging.info('Checking for %s on a staged build.', LIST_IMAGE_DIR)
550 fake_file_name = 'fake_file'
551 try:
552 os.makedirs(build_dir)
553 open(os.path.join(build_dir, fake_file_name), 'w').close()
554 except OSError:
555 logging.error('Could not create files to imitate staged content. '
556 'Build dir %s, file %s', build_dir, fake_file_name)
557 raise
558 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
559 self.assertTrue(fake_file_name in response)
560 shutil.rmtree(build_dir, ignore_errors=True)
Chris Sosa7cd23202013-10-15 17:22:57 -0700561
Amin Hassani28df4212019-10-28 10:16:50 -0700562
Chris Sosa7cd23202013-10-15 17:22:57 -0700563if __name__ == '__main__':
564 logging_format = '%(levelname)-8s: %(message)s'
565 logging.basicConfig(level=logging.DEBUG, format=logging_format)
566 unittest.main()