blob: 2aee33c6f87fb6df8b9f4f60ae3bcd9b0212020b [file] [log] [blame]
Amin Hassani3587fb32021-04-28 10:10:01 -07001#!/usr/bin/env python3
Luis Hector Chaveza1518052018-06-14 08:19:34 -07002# -*- 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 os
Chris Sosa7cd23202013-10-15 17:22:57 -070019import shutil
Gilad Arnold7de05f72014-02-14 13:14:20 -080020import socket
Chris Sosa7cd23202013-10-15 17:22:57 -070021import subprocess
Amin Hassaniba847c82019-12-06 16:30:56 -080022import sys
Chris Sosa7cd23202013-10-15 17:22:57 -070023import tempfile
24import time
25import unittest
Chris Sosa7cd23202013-10-15 17:22:57 -070026
Amin Hassani0b4843b2019-09-25 16:38:56 -070027from string import Template
28
Amin Hassani2aa34282020-11-18 01:18:19 +000029from xml.dom import minidom
30
Amin Hassani6eec8792020-01-09 14:06:48 -080031import requests
32
Amin Hassani2aa34282020-11-18 01:18:19 +000033from six.moves import urllib
34
Amin Hassani469f5702019-10-21 15:35:06 -070035import psutil # pylint: disable=import-error
36
Amin Hassani469f5702019-10-21 15:35:06 -070037import setup_chromite # pylint: disable=unused-import
Luis Hector Chaveza1518052018-06-14 08:19:34 -070038from chromite.lib import cros_logging as logging
Achuith Bhandarkar662fb722019-10-31 16:12:49 -070039from chromite.lib.xbuddy import devserver_constants
Luis Hector Chaveza1518052018-06-14 08:19:34 -070040
Chris Sosa7cd23202013-10-15 17:22:57 -070041
42# Paths are relative to this script's base directory.
43LABEL = 'devserver'
44TEST_IMAGE_PATH = 'testdata/devserver'
Amin Hassani0b4843b2019-09-25 16:38:56 -070045TEST_UPDATE_PAYLOAD_NAME = 'update.gz'
46TEST_UPDATE_PAYLOAD_METADATA_NAME = 'update.gz.json'
Chris Sosa7cd23202013-10-15 17:22:57 -070047
48# Update request based on Omaha v3 protocol format.
Amin Hassani0b4843b2019-09-25 16:38:56 -070049UPDATE_REQUEST = Template("""<?xml version="1.0" encoding="UTF-8"?>
Amin Hassani495f1de2019-02-26 11:13:39 -080050<request protocol="3.0" updater="ChromeOSUpdateEngine" updaterversion="0.1.0.0" ismachine="1">
Chris Sosa7cd23202013-10-15 17:22:57 -070051 <os version="Indy" platform="Chrome OS" sp="0.11.254.2011_03_09_1814_i686"></os>
Amin Hassani0b4843b2019-09-25 16:38:56 -070052 <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 -070053 <updatecheck></updatecheck>
54 </app>
55</request>
Amin Hassani0b4843b2019-09-25 16:38:56 -070056""")
Chris Sosa7cd23202013-10-15 17:22:57 -070057
58# RPC constants.
59STAGE = 'stage'
60IS_STAGED = 'is_staged'
61STATIC = 'static'
62UPDATE = 'update'
63CHECK_HEALTH = 'check_health'
64CONTROL_FILES = 'controlfiles'
65XBUDDY = 'xbuddy'
Prashanth Ba06d2d22014-03-07 15:35:19 -080066LIST_IMAGE_DIR = 'list_image_dir'
Chris Sosa7cd23202013-10-15 17:22:57 -070067
68# API rpcs and constants.
Chris Sosa7cd23202013-10-15 17:22:57 -070069API_SET_UPDATE_REQUEST = 'new_update-test/the-new-update'
70API_TEST_IP_ADDR = '127.0.0.1'
71
72DEVSERVER_START_TIMEOUT = 15
Gilad Arnold08516112014-02-14 13:14:03 -080073DEVSERVER_START_SLEEP = 1
Gilad Arnold7de05f72014-02-14 13:14:20 -080074MAX_START_ATTEMPTS = 5
Chris Sosa7cd23202013-10-15 17:22:57 -070075
76
77class DevserverFailedToStart(Exception):
78 """Raised if we could not start the devserver."""
79
80
Gilad Arnold08516112014-02-14 13:14:03 -080081class DevserverTestBase(unittest.TestCase):
Chris Sosa7cd23202013-10-15 17:22:57 -070082 """Class containing common logic between devserver test classes."""
83
84 def setUp(self):
Gilad Arnold08516112014-02-14 13:14:03 -080085 """Creates and populates a test directory, temporary files."""
Chris Sosa7cd23202013-10-15 17:22:57 -070086 self.test_data_path = tempfile.mkdtemp()
87 self.src_dir = os.path.dirname(__file__)
88
Gilad Arnold08516112014-02-14 13:14:03 -080089 # Copy the payload to the location of the update label.
Amin Hassani0b4843b2019-09-25 16:38:56 -070090 self._CreateLabelAndCopyUpdatePayloadFiles(LABEL)
Chris Sosa7cd23202013-10-15 17:22:57 -070091
92 # Copy the payload to the location of forced label.
Amin Hassani0b4843b2019-09-25 16:38:56 -070093 self._CreateLabelAndCopyUpdatePayloadFiles(API_SET_UPDATE_REQUEST)
Chris Sosa7cd23202013-10-15 17:22:57 -070094
Gilad Arnold08516112014-02-14 13:14:03 -080095 # Allocate temporary files for various devserver outputs.
96 self.pidfile = self._MakeTempFile('pid')
97 self.portfile = self._MakeTempFile('port')
98 self.logfile = self._MakeTempFile('log')
Chris Sosa7cd23202013-10-15 17:22:57 -070099
Gilad Arnold08516112014-02-14 13:14:03 -0800100 # Initialize various runtime values.
101 self.devserver_url = self.port = self.pid = None
Amin Hassaniba847c82019-12-06 16:30:56 -0800102 self.devserver = 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."""
Amin Hassaniba847c82019-12-06 16:30:56 -0800106
107 self._StopServer()
Chris Sosa7cd23202013-10-15 17:22:57 -0700108
Gilad Arnold08516112014-02-14 13:14:03 -0800109 self._RemoveFile(self.pidfile)
110 self._RemoveFile(self.portfile)
Amin Hassaniba847c82019-12-06 16:30:56 -0800111 # If the unittest did not succeed, print out the devserver log.
112 if sys.exc_info() != (None, None, None):
113 with open(self.logfile, 'r') as f:
114 logging.info('--- BEGINNING OF DEVSERVER LOG ---')
115 logging.info(f.read())
116 logging.info('--- ENDING OF DEVSERVER LOG ---')
Gilad Arnold08516112014-02-14 13:14:03 -0800117 self._RemoveFile(self.logfile)
118 shutil.rmtree(self.test_data_path)
Chris Sosa7cd23202013-10-15 17:22:57 -0700119
120 # Helper methods begin here.
121
Amin Hassani0b4843b2019-09-25 16:38:56 -0700122 def _CreateLabelAndCopyUpdatePayloadFiles(self, label):
Gilad Arnold08516112014-02-14 13:14:03 -0800123 """Creates a label location and copies an image to it."""
Amin Hassani0b4843b2019-09-25 16:38:56 -0700124 update_dir = os.path.join(self.src_dir, TEST_IMAGE_PATH)
Gilad Arnold08516112014-02-14 13:14:03 -0800125 label_dir = os.path.join(self.test_data_path, label)
126 os.makedirs(label_dir)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700127 for name in (TEST_UPDATE_PAYLOAD_NAME, TEST_UPDATE_PAYLOAD_METADATA_NAME):
128 shutil.copy(os.path.join(update_dir, name), label_dir)
Gilad Arnold08516112014-02-14 13:14:03 -0800129
130 def _MakeTempFile(self, suffix):
131 """Return path of a newly created temporary file."""
132 with tempfile.NamedTemporaryFile(suffix='-devserver-%s' % suffix) as f:
133 name = f.name
134 f.close()
135
136 return name
137
138 def _RemoveFile(self, filename):
139 """Removes a file if it is present."""
140 if os.path.isfile(filename):
141 os.remove(filename)
142
143 def _ReadIntValueFromFile(self, path, desc):
144 """Reads a string from file and returns its conversion into an integer."""
145 if not os.path.isfile(path):
146 raise DevserverFailedToStart('Devserver did not drop %s (%r).' %
147 (desc, path))
148
149 with open(path) as f:
150 value_str = f.read()
151
152 try:
153 return int(value_str)
154 except ValueError:
155 raise DevserverFailedToStart('Devserver did not drop a valid value '
156 'in %s (%r).' % (desc, value_str))
157
158 def _StartServer(self, port=0):
Chris Sosa7cd23202013-10-15 17:22:57 -0700159 """Attempts to start devserver on |port|.
160
Gilad Arnold08516112014-02-14 13:14:03 -0800161 In the default case where port == 0, the server will bind to an arbitrary
Dan Shi2f136862016-02-11 15:38:38 -0800162 available port. If successful, this method will set the devserver's pid
Gilad Arnold08516112014-02-14 13:14:03 -0800163 (self.pid), actual listening port (self.port) and URL (self.devserver_url).
Chris Sosa7cd23202013-10-15 17:22:57 -0700164
165 Raises:
166 DevserverFailedToStart: If the devserver could not be started.
167 """
168 cmd = [
Chris Sosa7cd23202013-10-15 17:22:57 -0700169 os.path.join(self.src_dir, 'devserver.py'),
Chris Sosa7cd23202013-10-15 17:22:57 -0700170 '--static_dir', self.test_data_path,
171 '--pidfile', self.pidfile,
Gilad Arnold08516112014-02-14 13:14:03 -0800172 '--portfile', self.portfile,
Chris Sosa7cd23202013-10-15 17:22:57 -0700173 '--port', str(port),
Amin Hassaniba847c82019-12-06 16:30:56 -0800174 '--logfile', self.logfile,
175 ]
Chris Sosa7cd23202013-10-15 17:22:57 -0700176
177 # Pipe all output. Use logfile to get devserver log.
Amin Hassaniba847c82019-12-06 16:30:56 -0800178 self.devserver = subprocess.Popen(cmd, stderr=subprocess.PIPE,
179 stdout=subprocess.PIPE)
Chris Sosa7cd23202013-10-15 17:22:57 -0700180
Gilad Arnold08516112014-02-14 13:14:03 -0800181 # Wait for devserver to start, determining its actual serving port and URL.
Chris Sosa7cd23202013-10-15 17:22:57 -0700182 current_time = time.time()
183 deadline = current_time + DEVSERVER_START_TIMEOUT
Amin Hassaniba847c82019-12-06 16:30:56 -0800184 error = None
Chris Sosa7cd23202013-10-15 17:22:57 -0700185 while current_time < deadline:
Chris Sosa7cd23202013-10-15 17:22:57 -0700186 try:
Gilad Arnold08516112014-02-14 13:14:03 -0800187 self.port = self._ReadIntValueFromFile(self.portfile, 'portfile')
188 self.devserver_url = 'http://127.0.0.1:%d' % self.port
Gabe Black3b567202015-09-23 14:07:59 -0700189 self._MakeRPC(CHECK_HEALTH, timeout=1)
Chris Sosa7cd23202013-10-15 17:22:57 -0700190 break
Amin Hassaniba847c82019-12-06 16:30:56 -0800191 except Exception as e:
192 error = e
Gilad Arnold08516112014-02-14 13:14:03 -0800193 time.sleep(DEVSERVER_START_SLEEP)
194 current_time = time.time()
Chris Sosa7cd23202013-10-15 17:22:57 -0700195 else:
Amin Hassaniba847c82019-12-06 16:30:56 -0800196 raise DevserverFailedToStart(
197 'Devserver failed to start within timeout with error: %s' % error)
Chris Sosa7cd23202013-10-15 17:22:57 -0700198
Gilad Arnold08516112014-02-14 13:14:03 -0800199 # Retrieve PID.
200 self.pid = self._ReadIntValueFromFile(self.pidfile, 'pidfile')
Chris Sosa7cd23202013-10-15 17:22:57 -0700201
Amin Hassaniba847c82019-12-06 16:30:56 -0800202 def _StopServer(self):
203 """Stops the current running devserver."""
204 if not self.pid:
205 return
206
207 self.devserver.terminate()
208
209 # Just to flush the stdout/stderr so python3 doesn't complain about the
210 # unclosed file.
211 self.devserver.communicate()
212
213 self.devserver.wait()
214
215 self.pid = None
216 self.devserver = None
217
Amin Hassani2aa34282020-11-18 01:18:19 +0000218
219 def VerifyHandleUpdate(self, label, use_test_payload=True,
220 appid='{DEV-BUILD}'):
221 """Verifies that we can send an update request to the devserver.
222
223 This method verifies (using a fake update_request blob) that the devserver
224 can interpret the payload and give us back the right payload.
225
226 Args:
227 label: Label that update is served from e.g. <board>-release/<version>
228 use_test_payload: If set to true, expects to serve payload under
229 testdata/ and does extra checks i.e. compares hash and content of
230 payload.
231 appid: The APP ID of the board.
232
233 Returns:
234 url of the update payload if we verified the update.
235 """
236 update_label = '/'.join([UPDATE, label])
237 response = self._MakeRPC(
238 update_label, data=UPDATE_REQUEST.substitute({'appid': appid}),
239 critical_update=True)
240 self.assertNotEqual('', response)
241 self.assertIn('deadline="now"', response)
242
243 # Parse the response and check if it contains the right result.
244 dom = minidom.parseString(response)
245 update = dom.getElementsByTagName('updatecheck')[0]
246 expected_static_url = '/'.join([self.devserver_url, STATIC, label])
247 url = self.VerifyV3Response(update, expected_static_url)
248
249 # Verify the image we download is correct since we already know what it is.
250 if use_test_payload:
251 connection = urllib.request.urlopen(url)
252 contents = connection.read().decode('utf-8')
253 connection.close()
254 self.assertEqual('Developers, developers, developers!\n', contents)
255
256 return url
257
258 def VerifyV3Response(self, update, expected_static_url):
259 """Verifies the update DOM from a v3 response and returns the url."""
260 # Parse the response and check if it contains the right result.
261 urls = update.getElementsByTagName('urls')[0]
262 url = urls.getElementsByTagName('url')[0]
263
264 static_url = url.getAttribute('codebase')
265 # Static url's end in /.
266 self.assertEqual(expected_static_url + '/', static_url)
267
268 manifest = update.getElementsByTagName('manifest')[0]
269 packages = manifest.getElementsByTagName('packages')[0]
270 package = packages.getElementsByTagName('package')[0]
271 filename = package.getAttribute('name')
272 self.assertEqual(TEST_UPDATE_PAYLOAD_NAME, filename)
273
274 return os.path.join(static_url, filename)
275
Chris Sosa7cd23202013-10-15 17:22:57 -0700276 def _MakeRPC(self, rpc, data=None, timeout=None, **kwargs):
Gilad Arnold08516112014-02-14 13:14:03 -0800277 """Makes an RPC call to the devserver.
Chris Sosa7cd23202013-10-15 17:22:57 -0700278
279 Args:
Gilad Arnold08516112014-02-14 13:14:03 -0800280 rpc: The function to run on the devserver, e.g. 'stage'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700281 data: Optional post data to send.
282 timeout: Optional timeout to pass to urlopen.
Gilad Arnold08516112014-02-14 13:14:03 -0800283 kwargs: Optional arguments to the function, e.g. artifact_url='foo/bar'.
Chris Sosa7cd23202013-10-15 17:22:57 -0700284
Gilad Arnold08516112014-02-14 13:14:03 -0800285 Returns:
286 The function output.
Chris Sosa7cd23202013-10-15 17:22:57 -0700287 """
288 request = '/'.join([self.devserver_url, rpc])
289 if kwargs:
290 # Join the kwargs to the URL.
Amin Hassani6eec8792020-01-09 14:06:48 -0800291 request += '?' + '&'.join('%s=%s' % (k, v) for k, v in kwargs.items())
Chris Sosa7cd23202013-10-15 17:22:57 -0700292
Amin Hassani6eec8792020-01-09 14:06:48 -0800293 response = (requests.post(request, data=data, timeout=timeout) if data
294 else requests.get(request, timeout=timeout))
295 response.raise_for_status()
296 return response.text
Chris Sosa7cd23202013-10-15 17:22:57 -0700297
298
Gilad Arnold08516112014-02-14 13:14:03 -0800299class AutoStartDevserverTestBase(DevserverTestBase):
300 """Test base class that automatically starts the devserver."""
301
302 def setUp(self):
303 """Initialize everything, then start the server."""
304 super(AutoStartDevserverTestBase, self).setUp()
305 self._StartServer()
306
307
Gilad Arnold7de05f72014-02-14 13:14:20 -0800308class DevserverStartTests(DevserverTestBase):
309 """Test that devserver starts up correctly."""
310
311 def testStartAnyPort(self):
312 """Starts the devserver, have it bind to an arbitrary available port."""
313 self._StartServer()
314
315 def testStartSpecificPort(self):
316 """Starts the devserver with a specific port."""
317 for _ in range(MAX_START_ATTEMPTS):
318 # This is a cheap hack to find an arbitrary unused port: we open a socket
319 # and bind it to port zero, then pull out the actual port number and
320 # close the socket. In all likelihood, this will leave us with an
321 # available port number that we can use for starting the devserver.
322 # However, this heuristic is susceptible to race conditions, hence the
323 # retry loop.
324 s = socket.socket()
325 s.bind(('', 0))
Gilad Arnold7de05f72014-02-14 13:14:20 -0800326 _, port = s.getsockname()
327 s.close()
328
329 self._StartServer(port=port)
Amin Hassaniba847c82019-12-06 16:30:56 -0800330 self._StopServer()
Gilad Arnold7de05f72014-02-14 13:14:20 -0800331
332
Gilad Arnold08516112014-02-14 13:14:03 -0800333class DevserverBasicTests(AutoStartDevserverTestBase):
334 """Short running tests for the devserver (no remote deps).
Chris Sosa7cd23202013-10-15 17:22:57 -0700335
336 These are technically not unittests because they depend on being able to
337 start a devserver locally which technically requires external resources so
338 they are lumped with the remote tests here.
339 """
340
Amin Hassani2aa34282020-11-18 01:18:19 +0000341 def testHandleUpdateV3(self):
342 self.VerifyHandleUpdate(label=LABEL)
343
Chris Sosa7cd23202013-10-15 17:22:57 -0700344 def testXBuddyLocalAlias(self):
345 """Extensive local image xbuddy unittest.
346
347 This test verifies all the local xbuddy logic by creating a new local folder
348 with the necessary update items and verifies we can use all of them.
349 """
Chris Sosa7cd23202013-10-15 17:22:57 -0700350 build_id = 'x86-generic/R32-9999.0.0-a1'
351 xbuddy_path = 'x86-generic/R32-9999.0.0-a1/test'
352 build_dir = os.path.join(self.test_data_path, build_id)
353 os.makedirs(build_dir)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700354
355 # Writing dummy files.
356 image_data = 'TEST IMAGE'
Chris Sosa7cd23202013-10-15 17:22:57 -0700357 test_image_file = os.path.join(build_dir,
358 devserver_constants.TEST_IMAGE_FILE)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700359 with open(test_image_file, 'w') as f:
360 f.write(image_data)
361
362 stateful_data = 'STATEFUL STUFFS'
Chris Sosa7cd23202013-10-15 17:22:57 -0700363 stateful_file = os.path.join(build_dir, devserver_constants.STATEFUL_FILE)
Amin Hassani0b4843b2019-09-25 16:38:56 -0700364 with open(stateful_file, 'w') as f:
365 f.write(stateful_data)
Chris Sosa7cd23202013-10-15 17:22:57 -0700366
Amin Hassani0b4843b2019-09-25 16:38:56 -0700367 update_dir = os.path.join(self.src_dir, TEST_IMAGE_PATH)
368 for name in (TEST_UPDATE_PAYLOAD_NAME, TEST_UPDATE_PAYLOAD_METADATA_NAME):
369 shutil.copy(os.path.join(update_dir, name), build_dir)
370 with open(os.path.join(build_dir, TEST_UPDATE_PAYLOAD_NAME), 'r') as f:
371 update_data = f.read()
Chris Sosa7cd23202013-10-15 17:22:57 -0700372
Amin Hassani0b4843b2019-09-25 16:38:56 -0700373 for item, data in zip(['full_payload', 'test', 'stateful'],
374 [update_data, image_data, stateful_data]):
Chris Sosa7cd23202013-10-15 17:22:57 -0700375
376 xbuddy_path = '/'.join([build_id, item])
377 logging.info('Testing xbuddy path %s', xbuddy_path)
Amin Hassani2aa34282020-11-18 01:18:19 +0000378 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]))
Chris Sosa7cd23202013-10-15 17:22:57 -0700379 self.assertEqual(response, data)
380
381 expected_dir = '/'.join([self.devserver_url, STATIC, build_id])
382 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
383 self.assertEqual(response, expected_dir)
384
Yu-Ju Hong51495eb2013-12-12 17:08:43 -0800385 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]),
386 relative_path=True)
387 self.assertEqual(response, build_id)
388
Amin Hassani2aa34282020-11-18 01:18:19 +0000389 logging.info('Verifying the actual payload data')
390 url = self.VerifyHandleUpdate(build_id, use_test_payload=False)
391 logging.info('Verify the actual content of the update payload')
392 connection = urllib.request.urlopen(url)
393 contents = connection.read().decode('utf-8')
394 connection.close()
395 self.assertEqual(update_data, contents)
396
Chris Sosa7cd23202013-10-15 17:22:57 -0700397 def testPidFile(self):
398 """Test that using a pidfile works correctly."""
399 with open(self.pidfile, 'r') as f:
400 pid = f.read()
Chris Sosa7cd23202013-10-15 17:22:57 -0700401 # Let's assert some process information about the devserver.
Dan Shi2f136862016-02-11 15:38:38 -0800402 self.assertTrue(pid.strip().isdigit())
Chris Sosa7cd23202013-10-15 17:22:57 -0700403 process = psutil.Process(int(pid))
404 self.assertTrue(process.is_running())
Amin Hassaniba847c82019-12-06 16:30:56 -0800405 self.assertIn('./devserver.py', process.cmdline())
Chris Sosa7cd23202013-10-15 17:22:57 -0700406
Gilad Arnold08516112014-02-14 13:14:03 -0800407class DevserverExtendedTests(AutoStartDevserverTestBase):
Chris Sosa7cd23202013-10-15 17:22:57 -0700408 """Longer running integration tests that test interaction with Google Storage.
409
410 Note: due to the interaction with Google Storage, these tests both require
411 1) runner has access to the Google Storage bucket where builders store builds.
412 2) time. These tests actually download the artifacts needed.
413 """
414
Amin Hassani2aa34282020-11-18 01:18:19 +0000415 def testStageAndUpdate(self):
416 """Tests core stage/update autotest workflow where with a test payload."""
417 build_id = 'eve-release/R78-12499.0.0'
418 archive_url = 'gs://chromeos-image-archive/%s' % build_id
419
420 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
421 artifacts='full_payload,stateful')
422 self.assertEqual(response, 'False')
423
424 logging.info('Staging update artifacts')
425 self._MakeRPC(STAGE, archive_url=archive_url,
426 artifacts='full_payload,stateful')
427 logging.info('Staging complete. '
428 'Verifying files exist and are staged in the staging '
429 'directory.')
430 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
431 artifacts='full_payload,stateful')
432 self.assertEqual(response, 'True')
433 staged_dir = os.path.join(self.test_data_path, build_id)
434 self.assertTrue(os.path.isdir(staged_dir))
435 self.assertTrue(os.path.exists(
436 os.path.join(staged_dir, devserver_constants.UPDATE_FILE)))
437 self.assertTrue(os.path.exists(
438 os.path.join(staged_dir, devserver_constants.UPDATE_METADATA_FILE)))
439 self.assertTrue(os.path.exists(
440 os.path.join(staged_dir, devserver_constants.STATEFUL_FILE)))
441
442 logging.info('Verifying we can update using the stage update artifacts.')
443 self.VerifyHandleUpdate(build_id, use_test_payload=False,
444 appid='{01906EA2-3EB2-41F1-8F62-F0B7120EFD2E}')
445
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700446 @unittest.skip('crbug.com/640063 Broken test.')
Chris Sosa7cd23202013-10-15 17:22:57 -0700447 def testStageAutotestAndGetPackages(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700448 """Another stage/update autotest workflow test with a test payload."""
449 build_id = 'eve-release/R69-10782.0.0'
Chris Sosa7cd23202013-10-15 17:22:57 -0700450 archive_url = 'gs://chromeos-image-archive/%s' % build_id
451 autotest_artifacts = 'autotest,test_suites,au_suite'
452 logging.info('Staging autotest artifacts (may take a while).')
453 self._MakeRPC(STAGE, archive_url=archive_url, artifacts=autotest_artifacts)
454
455 response = self._MakeRPC(IS_STAGED, archive_url=archive_url,
456 artifacts=autotest_artifacts)
457 self.assertEqual(response, 'True')
458
459 # Verify the files exist and are staged in the staging directory.
460 logging.info('Checking directories exist after we staged the files.')
461 staged_dir = os.path.join(self.test_data_path, build_id)
462 autotest_dir = os.path.join(staged_dir, 'autotest')
463 package_dir = os.path.join(autotest_dir, 'packages')
464 self.assertTrue(os.path.isdir(staged_dir))
465 self.assertTrue(os.path.isdir(autotest_dir))
466 self.assertTrue(os.path.isdir(package_dir))
467
468 control_files = self._MakeRPC(CONTROL_FILES, build=build_id,
469 suite_name='bvt')
470 logging.info('Checking for known control file in bvt suite.')
Amin Hassaniba847c82019-12-06 16:30:56 -0800471 self.assertIn('client/site_tests/platform_FilePerms/control', control_files)
Chris Sosa7cd23202013-10-15 17:22:57 -0700472
473 def testRemoteXBuddyAlias(self):
Luis Hector Chaveza1518052018-06-14 08:19:34 -0700474 """Another stage/update autotest workflow test with a test payload."""
475 build_id = 'eve-release/R69-10782.0.0'
476 xbuddy_path = 'remote/eve/R69-10782.0.0/full_payload'
477 xbuddy_bad_path = 'remote/eve/R32-9999.9999.9999'
Chris Sosa7cd23202013-10-15 17:22:57 -0700478 logging.info('Staging artifacts using xbuddy.')
479 response = self._MakeRPC('/'.join([XBUDDY, xbuddy_path]), return_dir=True)
480
481 logging.info('Verifying static url returned is valid.')
482 expected_static_url = '/'.join([self.devserver_url, STATIC, build_id])
483 self.assertEqual(response, expected_static_url)
484
Chris Sosa7cd23202013-10-15 17:22:57 -0700485 logging.info('Now give xbuddy a bad path.')
Amin Hassani6eec8792020-01-09 14:06:48 -0800486 self.assertRaises(requests.exceptions.RequestException,
Chris Sosa7cd23202013-10-15 17:22:57 -0700487 self._MakeRPC,
488 '/'.join([XBUDDY, xbuddy_bad_path]))
489
Prashanth Ba06d2d22014-03-07 15:35:19 -0800490 def testListImageDir(self):
491 """Verifies that we can list the contents of the image directory."""
492 build_id = 'x86-mario-release/R32-4810.0.0'
493 archive_url = 'gs://chromeos-image-archive/%s' % build_id
494 build_dir = os.path.join(self.test_data_path, build_id)
495 shutil.rmtree(build_dir, ignore_errors=True)
496
497 logging.info('checking for %s on an unstaged build.', LIST_IMAGE_DIR)
498 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
Amin Hassaniba847c82019-12-06 16:30:56 -0800499 self.assertIn(archive_url, response)
500 self.assertIn('not been staged', response)
Prashanth Ba06d2d22014-03-07 15:35:19 -0800501
502 logging.info('Checking for %s on a staged build.', LIST_IMAGE_DIR)
503 fake_file_name = 'fake_file'
504 try:
505 os.makedirs(build_dir)
506 open(os.path.join(build_dir, fake_file_name), 'w').close()
507 except OSError:
508 logging.error('Could not create files to imitate staged content. '
509 'Build dir %s, file %s', build_dir, fake_file_name)
510 raise
511 response = self._MakeRPC(LIST_IMAGE_DIR, archive_url=archive_url)
Amin Hassaniba847c82019-12-06 16:30:56 -0800512 self.assertIn(fake_file_name, response)
Prashanth Ba06d2d22014-03-07 15:35:19 -0800513 shutil.rmtree(build_dir, ignore_errors=True)
Chris Sosa7cd23202013-10-15 17:22:57 -0700514
Amin Hassani28df4212019-10-28 10:16:50 -0700515
Chris Sosa7cd23202013-10-15 17:22:57 -0700516if __name__ == '__main__':
Chris Sosa7cd23202013-10-15 17:22:57 -0700517 unittest.main()