Merge lp:~sbaldassin/snappy-ecosystem-tests/merge_builder_snapd into lp:snappy-ecosystem-tests
- merge_builder_snapd
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Heber Parrucci |
Approved revision: | 50 |
Merged at revision: | 49 |
Proposed branch: | lp:~sbaldassin/snappy-ecosystem-tests/merge_builder_snapd |
Merge into: | lp:snappy-ecosystem-tests |
Diff against target: |
467 lines (+135/-182) 7 files modified
snappy_ecosystem_tests/helpers/snapcraft/build_snap.py (+0/-161) snappy_ecosystem_tests/helpers/snapcraft/client.py (+123/-4) snappy_ecosystem_tests/helpers/snapd/snapd.py (+5/-5) snappy_ecosystem_tests/helpers/test_base.py (+0/-1) snappy_ecosystem_tests/tests/test_install_private_snap.py (+2/-7) snappy_ecosystem_tests/utils/ssh.py (+5/-3) snappy_ecosystem_tests/utils/user.py (+0/-1) |
To merge this branch: | bzr merge lp:~sbaldassin/snappy-ecosystem-tests/merge_builder_snapd |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Heber Parrucci (community) | Approve | ||
platform-qa-bot | continuous-integration | Approve | |
Review via email:
|
Commit message
Merge Snapcraft and SnapBuilder in a single class
Description of the change
This mp merge Snapcraft and SnapBuilder in a single class as they represent the same class. We'll need to instantiate two different objects, pointing to the builder container and the snapcraft container respectively
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
platform-qa-bot (platform-qa-bot) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Heber Parrucci (heber013) wrote : | # |
Looks good. One comment inline
- 48. By Santiago Baldassin
-
Addressing comments from the reviews
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Santiago Baldassin (sbaldassin) wrote : | # |
Thanks Heber. Comments addressed
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
platform-qa-bot (platform-qa-bot) wrote : | # |
PASSED: Continuous integration, rev:48
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Heber Parrucci (heber013) wrote : | # |
There are some tests failing in this branch that pass on trunk, see log http://
Seems that the time between snaps registration is broken.
- 49. By Santiago Baldassin
-
Merge from trunk
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
platform-qa-bot (platform-qa-bot) wrote : | # |
PASSED: Continuous integration, rev:49
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 50. By Santiago Baldassin
-
Merge from trunk
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
platform-qa-bot (platform-qa-bot) wrote : | # |
PASSED: Continuous integration, rev:50
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Heber Parrucci (heber013) wrote : | # |
Tests are passing properly now http://
Let's land it.
Preview Diff
1 | === removed file 'snappy_ecosystem_tests/helpers/snapcraft/build_snap.py' |
2 | --- snappy_ecosystem_tests/helpers/snapcraft/build_snap.py 2017-03-16 11:40:13 +0000 |
3 | +++ snappy_ecosystem_tests/helpers/snapcraft/build_snap.py 1970-01-01 00:00:00 +0000 |
4 | @@ -1,161 +0,0 @@ |
5 | -# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
6 | - |
7 | -# |
8 | -# Snappy Ecosystem Tests |
9 | -# Copyright (C) 2017 Canonical |
10 | -# |
11 | -# This program is free software: you can redistribute it and/or modify |
12 | -# it under the terms of the GNU General Public License as published by |
13 | -# the Free Software Foundation, either version 3 of the License, or |
14 | -# (at your option) any later version. |
15 | -# |
16 | -# This program is distributed in the hope that it will be useful, |
17 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | -# GNU General Public License for more details. |
20 | -# |
21 | -# You should have received a copy of the GNU General Public License |
22 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
23 | -# |
24 | - |
25 | -"""Module to build a snap in a remote machine.""" |
26 | - |
27 | -import os |
28 | -from pathlib import PurePath |
29 | - |
30 | -from snappy_ecosystem_tests.utils.ssh import SSHManager |
31 | - |
32 | -COMMANDS_SETUP = [ |
33 | - 'apt update', |
34 | - 'apt dist-upgrade -y', |
35 | - 'apt install snapcraft -y' |
36 | -] |
37 | -SNAPCRAFT_YAML = """\ |
38 | -name: {snap_name} |
39 | -version: {version} |
40 | -summary: {summary} |
41 | -description: | |
42 | - {description} |
43 | - |
44 | -grade: {grade} |
45 | -confinement: {confinement} |
46 | - |
47 | -parts: |
48 | - {snap_name}: |
49 | - plugin: nil |
50 | - |
51 | -apps: |
52 | - {snap_name}: |
53 | - command: echo ok |
54 | -""" |
55 | -SUMMARY_SNAP_DEFAULT = 'A test snap.' |
56 | -DESCRIPTION_SNAP_DEFAULT = 'A very awesome test snap.' |
57 | -GRADE_SNAP_DEFAULT = 'stable' |
58 | -CONFINEMENT_SNAP_DEFAULT = 'strict' |
59 | - |
60 | - |
61 | -class SnapBuilder: |
62 | - """Class to build a snap on a remote machine and download its content.""" |
63 | - def __init__(self, hostname, username, port=22): |
64 | - self.hostname = hostname |
65 | - self.username = username |
66 | - self.port = port |
67 | - |
68 | - @property |
69 | - def ssh(self): |
70 | - """Property tu ensure we get an active connection for every command |
71 | - execution over ssh""" |
72 | - return SSHManager.get_instance(self.hostname, self.username, self.port) |
73 | - |
74 | - def run_command_ssh(self, command, cwd=''): |
75 | - """Run command over ssh with optional cwd.""" |
76 | - if cwd: |
77 | - command = 'cd {}; {}'.format(cwd, command) |
78 | - return self.ssh.exec_command(command) |
79 | - |
80 | - def is_host_setup(self): |
81 | - """Return bool representing whether the remote host is setup by |
82 | - checking if snapcraft package is installed.""" |
83 | - try: |
84 | - self.ssh.exec_command('which snapcraft') |
85 | - return True |
86 | - except ValueError: |
87 | - return False |
88 | - |
89 | - def setup_host(self): |
90 | - """Setup the host to build a snap, currently only installs |
91 | - snapcraft.""" |
92 | - for command in COMMANDS_SETUP: |
93 | - self.ssh.exec_command(command) |
94 | - |
95 | - def build(self, name, version, summary=SUMMARY_SNAP_DEFAULT, |
96 | - description=DESCRIPTION_SNAP_DEFAULT, grade=GRADE_SNAP_DEFAULT, |
97 | - confinement=CONFINEMENT_SNAP_DEFAULT): |
98 | - """Build a new snap in the remote host and return its remote path. |
99 | - |
100 | - :param name: name of the snap to build. |
101 | - :param version: version number for the snap to build. |
102 | - :param summary: short summary of the snap to build. |
103 | - :param description: long description of the snap. |
104 | - :param grade: stability grade of the snap e.g. stable or devel. |
105 | - :param confinement: confinement policy for the snap e.g. |
106 | - classic, devmode or strict. |
107 | - :raises ValueError: If an error is occurred during snap build. |
108 | - :return: Remote path of the newly build snap package. |
109 | - """ |
110 | - if not self.is_host_setup(): |
111 | - self.setup_host() |
112 | - tempdir = self.ssh.exec_command('mktemp -d') |
113 | - self.run_command_ssh('mkdir snap', cwd=tempdir) |
114 | - self.run_command_ssh('echo "{}" > snap/snapcraft.yaml'.format( |
115 | - SNAPCRAFT_YAML.format( |
116 | - snap_name=name, |
117 | - version=version, |
118 | - summary=summary, |
119 | - description=description, |
120 | - grade=grade, |
121 | - confinement=confinement |
122 | - ) |
123 | - ), cwd=tempdir) |
124 | - self.run_command_ssh('snapcraft', cwd=tempdir) |
125 | - return os.path.join( |
126 | - tempdir, |
127 | - '{name}_{version}_{arch}.snap'.format( |
128 | - name=name, |
129 | - version=version, |
130 | - arch=self.ssh.exec_command('dpkg --print-architecture') |
131 | - ) |
132 | - ) |
133 | - |
134 | - def build_and_pull(self, name, version, summary=SUMMARY_SNAP_DEFAULT, |
135 | - description=DESCRIPTION_SNAP_DEFAULT, |
136 | - grade=GRADE_SNAP_DEFAULT, |
137 | - confinement=CONFINEMENT_SNAP_DEFAULT, |
138 | - output_location='.'): |
139 | - """Build a snap based on the provided arguments and download it. |
140 | - |
141 | - :param name: name of the snap to build. |
142 | - :param version: version number for the snap to build. |
143 | - :param summary: short summary of the snap to build. |
144 | - :param description: long description of the snap. |
145 | - :param grade: stability grade of the snap e.g. stable or devel. |
146 | - :param confinement: confinement policy for the snap e.g. |
147 | - classic, devmode or strict. |
148 | - :param output_location: Local location to download the newly built |
149 | - snap to. Defaults to pwd. |
150 | - :raises ValueError: If an error is occurred during snap build. |
151 | - :return: Local path of the newly built and downloaded snap package. |
152 | - """ |
153 | - remote_snap_path = self.build( |
154 | - name=name, |
155 | - version=version, |
156 | - summary=summary, |
157 | - description=description, |
158 | - grade=grade, |
159 | - confinement=confinement |
160 | - ) |
161 | - output_path_absolute = os.path.abspath( |
162 | - os.path.join(output_location, PurePath(remote_snap_path).name) |
163 | - ) |
164 | - self.ssh.pull(remote_snap_path, output_path_absolute) |
165 | - return output_path_absolute |
166 | |
167 | === modified file 'snappy_ecosystem_tests/helpers/snapcraft/client.py' |
168 | --- snappy_ecosystem_tests/helpers/snapcraft/client.py 2017-03-16 16:42:44 +0000 |
169 | +++ snappy_ecosystem_tests/helpers/snapcraft/client.py 2017-03-22 12:29:23 +0000 |
170 | @@ -21,9 +21,12 @@ |
171 | """Snapcraft client helpers""" |
172 | |
173 | import logging |
174 | +import os |
175 | import subprocess |
176 | import time |
177 | |
178 | +from pathlib import PurePath |
179 | + |
180 | from snappy_ecosystem_tests.models.snap import Snap |
181 | from snappy_ecosystem_tests.utils.commands import build_command |
182 | from snappy_ecosystem_tests.utils.filters import filter_list |
183 | @@ -61,6 +64,34 @@ |
184 | SNAPCRAFT_CONFIG_FILE_PATH = '~/.config/snapcraft/snapcraft.cfg' |
185 | |
186 | |
187 | +COMMANDS_SETUP = [ |
188 | + 'apt update', |
189 | + 'apt dist-upgrade -y', |
190 | + 'apt install snapcraft -y' |
191 | +] |
192 | +SNAPCRAFT_YAML = """\ |
193 | +name: {snap_name} |
194 | +version: {version} |
195 | +summary: {summary} |
196 | +description: | |
197 | + {description} |
198 | + |
199 | +grade: {grade} |
200 | +confinement: {confinement} |
201 | + |
202 | +parts: |
203 | + {snap_name}: |
204 | + plugin: nil |
205 | + |
206 | +apps: |
207 | + {snap_name}: |
208 | + command: echo ok |
209 | +""" |
210 | +SUMMARY_SNAP_DEFAULT = 'A test snap.' |
211 | +DESCRIPTION_SNAP_DEFAULT = 'A very awesome test snap.' |
212 | +GRADE_SNAP_DEFAULT = 'stable' |
213 | +CONFINEMENT_SNAP_DEFAULT = 'strict' |
214 | + |
215 | class Snapcraft: |
216 | """Contain Snapcraft specific functionality to use via command |
217 | line interface""" |
218 | @@ -75,7 +106,22 @@ |
219 | execution over ssh""" |
220 | return SSHManager.get_instance(HOSTNAME, USERNAME, PORT) |
221 | |
222 | - def _run_snapcraft_command_ssh(self, command, debug=True): |
223 | + def is_host_setup(self): |
224 | + """Return bool representing whether the remote host is setup by |
225 | + checking if snapcraft package is installed.""" |
226 | + try: |
227 | + self.ssh.run_command('which snapcraft') |
228 | + return True |
229 | + except ValueError: |
230 | + return False |
231 | + |
232 | + def setup_host(self): |
233 | + """Setup the host to build a snap, currently only installs |
234 | + snapcraft.""" |
235 | + for command in COMMANDS_SETUP: |
236 | + self.ssh.run_command(command) |
237 | + |
238 | + def _run_snapcraft_command_ssh(self, command='', debug=True, cwd=''): |
239 | """ |
240 | Run snapcraft command via ssh |
241 | :param command: the command to be executed. |
242 | @@ -87,7 +133,7 @@ |
243 | command = build_snapcraft_command(command) |
244 | if debug: |
245 | command += ' -d' |
246 | - return self.ssh.exec_command(command) |
247 | + return self.ssh.run_command(command, cwd=cwd) |
248 | |
249 | def assert_logged_in(self): |
250 | """Assert that an user is logged. |
251 | @@ -100,7 +146,7 @@ |
252 | def is_logged_in(self): |
253 | """Return bool representing if the user is logged into snapcraft.""" |
254 | try: |
255 | - output = self.ssh.exec_command( |
256 | + output = self.ssh.run_command( |
257 | 'cat %s' % SNAPCRAFT_CONFIG_FILE_PATH) |
258 | return output is not '' |
259 | except ValueError: |
260 | @@ -123,7 +169,7 @@ |
261 | :param email: the SSO email |
262 | :param password: the email password |
263 | """ |
264 | - self.ssh.exec_command( |
265 | + self.ssh.run_command( |
266 | COMMANDS_LOGIN.format(email=email, password=password, |
267 | snapcraft=PATH_SNAPCRAFT)) |
268 | return self.is_logged_in() |
269 | @@ -185,6 +231,79 @@ |
270 | LOGGER.info('Snap %s registered successfully!!!', snap_name) |
271 | return True |
272 | |
273 | + def build(self, name, version, summary=SUMMARY_SNAP_DEFAULT, |
274 | + description=DESCRIPTION_SNAP_DEFAULT, grade=GRADE_SNAP_DEFAULT, |
275 | + confinement=CONFINEMENT_SNAP_DEFAULT): |
276 | + """Build a new snap in the remote host and return its remote path. |
277 | + |
278 | + :param name: name of the snap to build. |
279 | + :param version: version number for the snap to build. |
280 | + :param summary: short summary of the snap to build. |
281 | + :param description: long description of the snap. |
282 | + :param grade: stability grade of the snap e.g. stable or devel. |
283 | + :param confinement: confinement policy for the snap e.g. |
284 | + classic, devmode or strict. |
285 | + :raises ValueError: If an error is occurred during snap build. |
286 | + :return: Remote path of the newly build snap package. |
287 | + """ |
288 | + if not self.is_host_setup(): |
289 | + self.setup_host() |
290 | + tempdir = self.ssh.run_command('mktemp -d') |
291 | + self.ssh.run_command('mkdir snap', cwd=tempdir) |
292 | + self.ssh.run_command( |
293 | + 'echo "{}" > snap/snapcraft.yaml'.format( |
294 | + SNAPCRAFT_YAML.format( |
295 | + snap_name=name, |
296 | + version=version, |
297 | + summary=summary, |
298 | + description=description, |
299 | + grade=grade, |
300 | + confinement=confinement |
301 | + ) |
302 | + ), cwd=tempdir) |
303 | + self._run_snapcraft_command_ssh(cwd=tempdir) |
304 | + return os.path.join( |
305 | + tempdir, |
306 | + '{name}_{version}_{arch}.snap'.format( |
307 | + name=name, |
308 | + version=version, |
309 | + arch=self.ssh.run_command('dpkg --print-architecture') |
310 | + ) |
311 | + ) |
312 | + |
313 | + def build_and_pull(self, name, version, summary=SUMMARY_SNAP_DEFAULT, |
314 | + description=DESCRIPTION_SNAP_DEFAULT, |
315 | + grade=GRADE_SNAP_DEFAULT, |
316 | + confinement=CONFINEMENT_SNAP_DEFAULT, |
317 | + output_location='.'): |
318 | + """Build a snap based on the provided arguments and download it. |
319 | + |
320 | + :param name: name of the snap to build. |
321 | + :param version: version number for the snap to build. |
322 | + :param summary: short summary of the snap to build. |
323 | + :param description: long description of the snap. |
324 | + :param grade: stability grade of the snap e.g. stable or devel. |
325 | + :param confinement: confinement policy for the snap e.g. |
326 | + classic, devmode or strict. |
327 | + :param output_location: Local location to download the newly built |
328 | + snap to. Defaults to pwd. |
329 | + :raises ValueError: If an error is occurred during snap build. |
330 | + :return: Local path of the newly built and downloaded snap package. |
331 | + """ |
332 | + remote_snap_path = self.build( |
333 | + name=name, |
334 | + version=version, |
335 | + summary=summary, |
336 | + description=description, |
337 | + grade=grade, |
338 | + confinement=confinement |
339 | + ) |
340 | + output_path_absolute = os.path.abspath( |
341 | + os.path.join(output_location, PurePath(remote_snap_path).name) |
342 | + ) |
343 | + self.ssh.pull(remote_snap_path, output_path_absolute) |
344 | + return output_path_absolute |
345 | + |
346 | |
347 | def build_snapcraft_command(*args, base_command=PATH_SNAPCRAFT): |
348 | """ |
349 | |
350 | === modified file 'snappy_ecosystem_tests/helpers/snapd/snapd.py' |
351 | --- snappy_ecosystem_tests/helpers/snapd/snapd.py 2017-03-19 15:27:11 +0000 |
352 | +++ snappy_ecosystem_tests/helpers/snapd/snapd.py 2017-03-22 12:29:23 +0000 |
353 | @@ -74,7 +74,7 @@ |
354 | command = '{} {}'.format(PATH_SNAP, command) |
355 | if cwd: |
356 | command = '{}; {}'.format(cwd, command) |
357 | - return self.ssh.exec_command(command) |
358 | + return self.ssh.run_command(command) |
359 | |
360 | def login(self, email, password): |
361 | """Login to snapd. |
362 | @@ -82,15 +82,15 @@ |
363 | :param email: Ubuntu SSO account email address. |
364 | :param password: Ubuntu SSO account password. |
365 | """ |
366 | - self.ssh.exec_command(COMMANDS_LOGIN.format(email=email, |
367 | - password=password)) |
368 | + self.ssh.run_command(COMMANDS_LOGIN.format(email=email, |
369 | + password=password)) |
370 | return self.is_logged_in(email) |
371 | |
372 | def is_logged_in(self, email): |
373 | """Return bool representing if the user is logged into snapd.""" |
374 | try: |
375 | return json.loads( |
376 | - self.ssh.exec_command( |
377 | + self.ssh.run_command( |
378 | 'cat ~/.snap/auth.json')).get('email') == email |
379 | except ValueError: |
380 | return False |
381 | @@ -108,7 +108,7 @@ |
382 | """ |
383 | command = COMMAND_DOWNLOAD.format(snap=snap, channel=channel) |
384 | self.run_snapd_command_ssh(command, |
385 | - cwd=self.ssh.exec_command('mktemp -d')) |
386 | + cwd=self.ssh.run_command('mktemp -d')) |
387 | |
388 | def install(self, snap, channel=CHANNEL_STABLE): |
389 | """Install the requested snap.""" |
390 | |
391 | === modified file 'snappy_ecosystem_tests/helpers/test_base.py' |
392 | --- snappy_ecosystem_tests/helpers/test_base.py 2017-03-08 17:29:20 +0000 |
393 | +++ snappy_ecosystem_tests/helpers/test_base.py 2017-03-22 12:29:23 +0000 |
394 | @@ -15,7 +15,6 @@ |
395 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
396 | |
397 | """Base Snappy Ecosystem test""" |
398 | - |
399 | import testscenarios |
400 | import testtools |
401 | |
402 | |
403 | === modified file 'snappy_ecosystem_tests/tests/test_install_private_snap.py' |
404 | --- snappy_ecosystem_tests/tests/test_install_private_snap.py 2017-03-17 20:50:55 +0000 |
405 | +++ snappy_ecosystem_tests/tests/test_install_private_snap.py 2017-03-22 12:29:23 +0000 |
406 | @@ -19,16 +19,12 @@ |
407 | # |
408 | |
409 | """Tests for Installing private snaps.""" |
410 | - |
411 | +from snappy_ecosystem_tests.helpers.snapcraft.client import Snapcraft |
412 | from snappy_ecosystem_tests.helpers.snapd.snapd import Snapd |
413 | from snappy_ecosystem_tests.helpers.store_apis.rest_apis import Store |
414 | -from snappy_ecosystem_tests.helpers.snapcraft.build_snap import SnapBuilder |
415 | from snappy_ecosystem_tests.helpers.test_base import SnappyEcosystemTestCase |
416 | from snappy_ecosystem_tests.tests.commons import get_random_snap_name |
417 | from snappy_ecosystem_tests.utils.storeconfig import get_store_credentials |
418 | -from snappy_ecosystem_tests.utils.user import ( |
419 | - get_snapcraft_remote_host_credentials |
420 | -) |
421 | |
422 | |
423 | class RegisterInstallPrivateSnapTestCase(SnappyEcosystemTestCase): |
424 | @@ -42,8 +38,7 @@ |
425 | self.addCleanup(self.snapd.logout, email) |
426 | self.store.login(email, password) |
427 | self.addCleanup(self.store.logout) |
428 | - self.snap_builder = SnapBuilder( |
429 | - *get_snapcraft_remote_host_credentials()) |
430 | + self.snap_builder = Snapcraft() |
431 | |
432 | def test_upload_and_install_private_snap(self): |
433 | """Test to register and upload a private snap via the REST API |
434 | |
435 | === modified file 'snappy_ecosystem_tests/utils/ssh.py' |
436 | --- snappy_ecosystem_tests/utils/ssh.py 2017-03-09 17:24:06 +0000 |
437 | +++ snappy_ecosystem_tests/utils/ssh.py 2017-03-22 12:29:23 +0000 |
438 | @@ -30,12 +30,14 @@ |
439 | class SSHClient(paramiko.SSHClient): |
440 | """Extended SSHClient.""" |
441 | |
442 | - def exec_command(self, command, bufsize=-1, timeout=None, get_pty=False, |
443 | - environment=None): |
444 | + def run_command(self, command, bufsize=-1, timeout=None, |
445 | + get_pty=False, cwd=''): |
446 | """Execute the given command over ssh.""" |
447 | + if cwd: |
448 | + command = 'cd {}; {}'.format(cwd, command) |
449 | _, stdout, stderr = super().exec_command( |
450 | command, bufsize, timeout, |
451 | - get_pty, environment=environment) |
452 | + get_pty) |
453 | if stdout.channel.recv_exit_status() != 0: |
454 | raise ValueError(stderr.read().decode().strip()) |
455 | response = stdout.read().decode().strip() |
456 | |
457 | === modified file 'snappy_ecosystem_tests/utils/user.py' |
458 | --- snappy_ecosystem_tests/utils/user.py 2017-03-09 10:41:13 +0000 |
459 | +++ snappy_ecosystem_tests/utils/user.py 2017-03-22 12:29:23 +0000 |
460 | @@ -27,7 +27,6 @@ |
461 | |
462 | MANAGER = get_manager(os.environ['target']) |
463 | |
464 | - |
465 | def get_snapd_remote_host_credentials(): |
466 | """Return credentials for snapd remote machine, to run commands on.""" |
467 | _ip = MANAGER.driver.get_ip(USER_CONFIG_STACK.get( |
PASSED: Continuous integration, rev:47 /platform- qa-jenkins. ubuntu. com/job/ snappy- ecosystem- tests-ci/ 295/ /platform- qa-jenkins. ubuntu. com/job/ generic- update- mp/2150/ console
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild: /platform- qa-jenkins. ubuntu. com/job/ snappy- ecosystem- tests-ci/ 295/rebuild
https:/