Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
test_backend_failure.py
1 #!/usr/bin/env python3
2 
3 import logging
4 import argparse
5 
6 from os import getcwd, chdir
7 
8 from test_fixtures import with_aktualizr, with_uptane_backend, KeyStore, with_secondary, with_path,\
9  DownloadInterruptionHandler, MalformedJsonHandler, with_director, with_imagerepo, InstallManager,\
10  with_install_manager, with_images, MalformedImageHandler, with_customrepo, SlowRetrievalHandler, \
11  RedirectHandler, with_sysroot, with_treehub
12 
13 
14 logger = logging.getLogger(__file__)
15 
16 """
17 Verifies whether aktualizr is updatable after director metadata download failure
18 with follow-up successful metadata download.
19 
20 Currently, it's tested against two types of metadata download/parsing failure:
21  - download interruption - metadata file download is interrupted once|three times, after that it's successful
22  - malformed json - aktualizr receives malformed json/metadata as a response to the first request for metadata,
23  a response to subsequent request is successful
24 
25 Note: Aktualizr doesn't send any installation report in manifest in case of metadata download failure
26 https://saeljira.it.here.com/browse/OTA-3730
27 """
28 @with_uptane_backend(start_generic_server=True)
29 @with_path(paths=['/1.root.json', '/targets.json'])
30 @with_director(handlers=[
31  DownloadInterruptionHandler(number_of_failures=1),
32  MalformedJsonHandler(number_of_failures=1),
33  DownloadInterruptionHandler(number_of_failures=3),
34  ], start=False)
35 @with_aktualizr(start=False, run_mode='full')
36 @with_install_manager()
37 def test_backend_failure_sanity_director_update_after_metadata_download_failure(install_mngr, director,
38  aktualizr, **kwargs):
39  with director:
40  with aktualizr:
41  install_result = director.wait_for_install()
42  install_result = install_result and install_mngr.are_images_installed()
43  return install_result
44 
45 
46 """
47 Verifies whether aktualizr is updatable after image metadata download failure
48 with follow-up successful metadata download.
49 
50 Currently, it's tested against two types of metadata download/parsing failure:
51  - download interruption - metadata file download is interrupted once|three times, after that it's successful
52  - malformed json - aktualizr receives malformed json/metadata as a response to the first request for metadata,
53  a response to subsequent request is successful
54 
55 Note: Aktualizr doesn't send any installation report in manifest in case of metadata download failure
56 """
57 @with_uptane_backend(start_generic_server=True)
58 @with_path(paths=['/1.root.json', '/timestamp.json', '/snapshot.json', '/targets.json'])
59 @with_imagerepo(handlers=[
60  DownloadInterruptionHandler(number_of_failures=1),
61  MalformedJsonHandler(number_of_failures=1),
62  DownloadInterruptionHandler(number_of_failures=3),
63  ])
64 @with_director(start=False)
65 @with_aktualizr(start=False, run_mode='full')
66 @with_install_manager()
67 def test_backend_failure_sanity_imagerepo_update_after_metadata_download_failure(install_mngr, director,
68  aktualizr, **kwargs):
69  with aktualizr:
70  with director:
71  install_result = director.wait_for_install()
72  logger.info('Director install result: {}'.format(install_result))
73  install_result = install_result and install_mngr.are_images_installed()
74  logger.info('Are images installed: {}'.format(install_result))
75  return install_result
76 
77 
78 """
79 Verifies whether aktualizr is updatable after image download failure
80 with follow-up successful download.
81 
82 Currently, it's tested against two types of image download failure:
83  - download interruption - file download is interrupted once, after that it's successful
84  - malformed image - image download is successful but it's malformed. It happens once after that it's successful
85 """
86 @with_uptane_backend(start_generic_server=True)
87 @with_images(images_to_install=[(('primary-hw-ID-001', 'primary-ecu-id'), 'primary-image.img')])
88 @with_imagerepo(handlers=[
89  DownloadInterruptionHandler(number_of_failures=1, url='/targets/primary-image.img'),
90  MalformedImageHandler(number_of_failures=1, url='/targets/primary-image.img'),
91  ])
92 @with_director(start=False)
93 @with_aktualizr(start=False, run_mode='full', id=('primary-hw-ID-001', 'primary-ecu-id'))
94 @with_install_manager()
95 def test_backend_failure_sanity_imagerepo_update_after_image_download_failure(install_mngr, director,
96  aktualizr, **kwargs):
97  with aktualizr:
98  with director:
99  install_result = director.wait_for_install()
100  install_result = install_result and install_mngr.are_images_installed()
101  return install_result
102 
103 
104 """
105  Verifies whether aktualizr is updatable after malformed image is downloaded
106  from a custom image server with follow-up successful download.
107 """
108 @with_uptane_backend(start_generic_server=True)
109 @with_customrepo(handlers=[
110  DownloadInterruptionHandler(number_of_failures=1, url='/primary-image.img'),
111  MalformedImageHandler(number_of_failures=1, url='/primary-image.img')
112  # TODO: this test fails too, although httpclient.cc sets
113  # CURLOPT_LOW_SPEED_TIME and CURLOPT_LOW_SPEED_TIME
114  # https://saeljira.it.here.com/browse/OTA-3737
115  #SlowRetrievalHandler(url='/primary-image.img')
116  ])
117 @with_imagerepo()
118 @with_director(start=False)
119 @with_aktualizr(start=False, run_mode='full')
120 def test_backend_failure_sanity_customrepo_update_after_image_download_failure(uptane_repo, custom_repo, director,
121  aktualizr, **kwargs):
122  update_hash = uptane_repo.add_image(aktualizr.id, 'primary-image.img',
123  custom_url=custom_repo.base_url + '/' + 'primary-image.img')
124 
125  with aktualizr:
126  with director:
127  install_result = director.wait_for_install()
128 
129  return install_result and update_hash == aktualizr.get_current_image_info(aktualizr.id)
130 
131 
132 """
133  Verifies whether aktualizr is updatable after failure of object(s) download from Treehub/ostree repo
134  with follow-up successful download.
135 
136  Currently, it's tested against two types of object download failure:
137  - download interruption - object download is interrupted once, after that it's successful
138  - malformed object - object download is successful but it's malformed. It happens once after that it's successful
139 """
140 @with_uptane_backend(start_generic_server=True)
141 @with_director()
142 @with_treehub(handlers=[
143  DownloadInterruptionHandler(url='/objects/41/5ce9717fc7a5f4d743a4f911e11bd3ed83930e46756303fd13a3eb7ed35892.filez'),
144  MalformedImageHandler(url='/objects/41/5ce9717fc7a5f4d743a4f911e11bd3ed83930e46756303fd13a3eb7ed35892.filez'),
145 
146  # TODO: ostree objects download is not resilient to `Slow Retrieval Attack`
147  # https://saeljira.it.here.com/browse/OTA-3737
148  #SlowRetrievalHandler(url='/objects/6b/1604b586fcbe052bbc0bd9e1c8040f62e085ca2e228f37df957ac939dff361.filez'),
149 
150  # TODO: Limit a number of HTTP redirects within a single request processing
151  # https://saeljira.it.here.com/browse/OTA-3729
152  #RedirectHandler(number_of_redirects=1000, url='/objects/41/5ce9717fc7a5f4d743a4f911e11bd3ed83930e46756303fd13a3eb7ed35892.filez')
153 ])
154 @with_sysroot()
155 @with_aktualizr(start=False, run_mode='once')
156 def test_backend_failure_sanity_treehub_update_after_image_download_failure(uptane_repo,
157  aktualizr,
158  director,
159  uptane_server,
160  sysroot, treehub):
161  target_rev = treehub.revision
162  uptane_repo.add_ostree_target(aktualizr.id, target_rev)
163  with aktualizr:
164  aktualizr.wait_for_completion()
165 
166  pending_rev = aktualizr.get_primary_pending_version()
167  if pending_rev != target_rev:
168  logger.error("Pending version {} != the target one {}".format(pending_rev, target_rev))
169  return False
170 
171  sysroot.update_revision(pending_rev)
172  aktualizr.emulate_reboot()
173 
174  with aktualizr:
175  aktualizr.wait_for_completion()
176 
177  result = director.get_install_result() and (target_rev == aktualizr.get_current_primary_image_info())
178  return result
179 
180 
181 """
182  Verifies that aktualizr does not install an image which contains files with wrong checksums
183 """
184 @with_uptane_backend(start_generic_server=True)
185 @with_director()
186 @with_treehub(handlers=[
187  MalformedImageHandler(url='/objects/41/5ce9717fc7a5f4d743a4f911e11bd3ed83930e46756303fd13a3eb7ed35892.filez',
188  number_of_failures=-1, fake_filez=True),
189 
190 ])
191 @with_sysroot()
192 @with_aktualizr(start=False, run_mode='once', output_logs=True)
193 def test_backend_failure_bad_ostree_checksum(uptane_repo,
194  aktualizr,
195  director,
196  uptane_server,
197  sysroot, treehub):
198  target_rev = treehub.revision
199  uptane_repo.add_ostree_target(aktualizr.id, target_rev)
200  with aktualizr:
201  aktualizr.wait_for_completion()
202 
203  pending_rev = aktualizr.get_primary_pending_version()
204  if pending_rev == target_rev:
205  logger.error("Pending version {} == the target one {}".format(pending_rev, target_rev))
206  return False
207  return True
208 
209 """
210  Verifies if aktualizr supports redirects - update is successful after redirect
211  Note: should aktualizr support unlimited number of redirects
212 """
213 @with_uptane_backend(start_generic_server=True)
214 # TODO: Limit a number of HTTP redirects within a single request processing
215 # https://saeljira.it.here.com/browse/OTA-3729
216 @with_customrepo(handlers=[
217  RedirectHandler(number_of_redirects=10, url='/primary-image.img')
218  ])
219 @with_imagerepo()
220 @with_director()
221 @with_aktualizr(run_mode='once', output_logs=True)
222 def test_backend_failure_sanity_customrepo_update_redirect(aktualizr, uptane_repo,
223  custom_repo, director, **kwargs):
224  update_hash = uptane_repo.add_image(aktualizr.id, 'primary-image.img',
225  custom_url=custom_repo.base_url + '/' + 'primary-image.img')
226  install_result = director.wait_for_install()
227  return install_result and update_hash == aktualizr.get_current_image_info(aktualizr.id)
228 
229 """
230  Verifies if aktualizr rejects redirects over 10 times - update fails after redirect
231 """
232 @with_uptane_backend(start_generic_server=True)
233 @with_customrepo(handlers=[
234  RedirectHandler(number_of_redirects=(11 * 3 + 1), url='/primary-image.img')
235  ])
236 @with_imagerepo()
237 @with_director()
238 @with_aktualizr(start=False, run_mode='once', output_logs=True)
239 def test_backend_failure_sanity_customrepo_unsuccessful_update_redirect(aktualizr, uptane_repo,
240  custom_repo, director, **kwargs):
241  update_hash = uptane_repo.add_image(aktualizr.id, 'primary-image.img',
242  custom_url=custom_repo.base_url + '/' + 'primary-image.img')
243  with aktualizr:
244  aktualizr.wait_for_completion()
245 
246  return not director.get_install_result()
247 
248 """
249  Verifies whether an update fails if director metadata download fails or they are malformed
250  - download is interrupted three times
251  - malformed json is received
252 """
253 @with_uptane_backend(start_generic_server=True)
254 @with_path(paths=['/1.root.json', '/targets.json'])
255 @with_imagerepo()
256 @with_director(handlers=[
257  DownloadInterruptionHandler(number_of_failures=3),
258  MalformedJsonHandler(number_of_failures=1),
259  ])
260 @with_aktualizr(run_mode='once')
261 @with_install_manager()
262 def test_backend_failure_sanity_director_unsuccessful_download(install_mngr, aktualizr,
263  director, **kwargs):
264  aktualizr.wait_for_completion()
265  return not (director.get_install_result() or install_mngr.are_images_installed())
266 
267 
268 """
269  Verifies whether an update fails if repo metadata download fails or they are malformed
270  - download is interrupted three times
271  - malformed json is received
272 """
273 @with_uptane_backend(start_generic_server=True)
274 @with_path(paths=['/1.root.json', '/timestamp.json', '/snapshot.json', '/targets.json'])
275 @with_imagerepo(handlers=[
276  DownloadInterruptionHandler(number_of_failures=3),
277  MalformedJsonHandler(number_of_failures=1),
278  ])
279 @with_director()
280 @with_aktualizr(run_mode='once')
281 @with_install_manager()
282 def test_backend_failure_sanity_imagerepo_unsuccessful_download(install_mngr, aktualizr,
283  director, **kwargs):
284  aktualizr.wait_for_completion()
285  return not (director.get_install_result() or install_mngr.are_images_installed())
286 
287 
288 if __name__ == "__main__":
289  logging.basicConfig(level=logging.INFO)
290 
291  parser = argparse.ArgumentParser(description='Test backend failure')
292  parser.add_argument('-b', '--build-dir', help='build directory', default='build')
293  parser.add_argument('-s', '--src-dir', help='source directory', default='.')
294  parser.add_argument('-o', '--ostree', help='ostree support', default='OFF')
295  input_params = parser.parse_args()
296 
297  KeyStore.base_dir = input_params.src_dir
298  initial_cwd = getcwd()
299  chdir(input_params.build_dir)
300 
301  test_suite = [
302  test_backend_failure_sanity_director_update_after_metadata_download_failure,
303  test_backend_failure_sanity_imagerepo_update_after_metadata_download_failure,
304  test_backend_failure_sanity_imagerepo_update_after_image_download_failure,
305  test_backend_failure_sanity_customrepo_update_after_image_download_failure,
306  test_backend_failure_sanity_director_unsuccessful_download,
307  test_backend_failure_sanity_imagerepo_unsuccessful_download,
308  test_backend_failure_sanity_customrepo_update_redirect,
309  test_backend_failure_sanity_customrepo_unsuccessful_update_redirect,
310  ]
311 
312  if input_params.ostree == 'ON':
313  test_suite.append(test_backend_failure_sanity_treehub_update_after_image_download_failure)
314  test_suite.append(test_backend_failure_bad_ostree_checksum)
315 
316  test_suite_run_result = True
317  for test in test_suite:
318  logger.info('>>> Running {}...'.format(test.__name__))
319  test_run_result = test()
320  logger.info('>>> {}: {}\n'.format('OK' if test_run_result else 'FAILED', test.__name__))
321  test_suite_run_result = test_suite_run_result and test_run_result
322 
323  chdir(initial_cwd)
324  exit(0 if test_suite_run_result else 1)