Merge lp:~heber013/ubuntu-system-tests/adding-snap-test into lp:ubuntu-system-tests

Proposed by Heber Parrucci
Status: Merged
Merged at revision: 574
Proposed branch: lp:~heber013/ubuntu-system-tests/adding-snap-test
Merge into: lp:ubuntu-system-tests
Diff against target: 418 lines (+314/-14)
6 files modified
debian/tests/dependencies.json (+2/-0)
ubuntu_system_tests/helpers/snapd/__init__.py (+19/-0)
ubuntu_system_tests/helpers/snapd/snapd.py (+206/-0)
ubuntu_system_tests/host/target_setup.py (+21/-10)
ubuntu_system_tests/host/targets.py (+9/-4)
ubuntu_system_tests/tests/test_snapd.py (+57/-0)
To merge this branch: bzr merge lp:~heber013/ubuntu-system-tests/adding-snap-test
Reviewer Review Type Date Requested Status
platform-qa-bot continuous-integration Approve
Heber Parrucci (community) continuous-integration Needs Fixing
Review via email: mp+334762@code.launchpad.net

Commit message

Add tests for snapd command line: install, find, download a snap.

Description of the change

Add tests for snapd command line: install, find, download a snap.

@run_tests: ubuntu_system_tests.tests.test_snapd

To post a comment you must log in.
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Approve (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Approve (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Approve (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Approve (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Approve (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Approve (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote :
review: Needs Fixing (continuous-integration)
573. By Heber Parrucci

merge from trunk

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
574. By Heber Parrucci

fixing json format for dependencies.json

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/tests/dependencies.json'
--- debian/tests/dependencies.json 2017-12-22 19:35:15 +0000
+++ debian/tests/dependencies.json 2018-01-02 13:43:13 +0000
@@ -12,6 +12,7 @@
12 { "id": "python3-testtools", "version": "", "tmpinstall": "false" },12 { "id": "python3-testtools", "version": "", "tmpinstall": "false" },
13 { "id": "python3-warlock", "version": "", "tmpinstall": "false" },13 { "id": "python3-warlock", "version": "", "tmpinstall": "false" },
14 { "id": "python3-xlib", "version": "", "tmpinstall": "false" },14 { "id": "python3-xlib", "version": "", "tmpinstall": "false" },
15 { "id": "python3-yaml", "version": "", "tmpinstall": "false" },
15 { "id": "subunit", "version": "", "tmpinstall": "false" },16 { "id": "subunit", "version": "", "tmpinstall": "false" },
16 { "id": "ubuntu-app-launch-tools", "version": "", "tmpinstall": "false" },17 { "id": "ubuntu-app-launch-tools", "version": "", "tmpinstall": "false" },
17 { "id": "ubuntu-system-tests-helpers", "version": "", "tmpinstall": "false", "condition": "installed" },18 { "id": "ubuntu-system-tests-helpers", "version": "", "tmpinstall": "false", "condition": "installed" },
@@ -40,6 +41,7 @@
40 "python3-testtools",41 "python3-testtools",
41 "python3-warlock",42 "python3-warlock",
42 "python3-xlib",43 "python3-xlib",
44 "python3-yaml",
43 "subunit",45 "subunit",
44 "ubuntu-app-launch-tools",46 "ubuntu-app-launch-tools",
45 "ubuntu-system-tests-helpers",47 "ubuntu-system-tests-helpers",
4648
=== added directory 'ubuntu_system_tests/helpers/snapd'
=== added file 'ubuntu_system_tests/helpers/snapd/__init__.py'
--- ubuntu_system_tests/helpers/snapd/__init__.py 1970-01-01 00:00:00 +0000
+++ ubuntu_system_tests/helpers/snapd/__init__.py 2018-01-02 13:43:13 +0000
@@ -0,0 +1,19 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2
3#
4# Snappy Ecosystem Tests
5# Copyright (C) 2017 Canonical
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
020
=== added file 'ubuntu_system_tests/helpers/snapd/snapd.py'
--- ubuntu_system_tests/helpers/snapd/snapd.py 1970-01-01 00:00:00 +0000
+++ ubuntu_system_tests/helpers/snapd/snapd.py 2018-01-02 13:43:13 +0000
@@ -0,0 +1,206 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2
3#
4# Snappy Ecosystem Tests
5# Copyright (C) 2017 Canonical
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20
21"""Helpers around snapd command-line interface."""
22
23import json
24import logging
25from time import sleep
26
27import yaml
28from ubuntu_system_tests.helpers.testbed import run_command_with_sudo, run_command
29
30PATH_SNAP = '/usr/bin/snap'
31COMMAND_DOWNLOAD = 'download {snap} --channel={channel}'
32COMMAND_FIND = 'find {search_term}'
33COMMAND_INFO = 'info {snap}'
34COMMAND_INSTALL = 'install {snap} --channel={channel}'
35COMMAND_LIST = 'list'
36COMMAND_LOGIN = 'login {email}'
37COMMAND_LOGOUT = 'logout'
38COMMAND_REFRESH = 'refresh {snap} --channel={channel}'
39COMMAND_REMOVE = 'remove {snap}'
40COMMAND_SIGN = 'sign'
41CHANNEL_STABLE = 'stable'
42COMMAND_LIST_KEYS = 'keys'
43COMMANDS_LOGIN = """\
44sudo /usr/bin/expect \
45-c 'spawn snap login {email}' \
46-c 'expect \"Password*\"' \
47-c 'send {password}\\r' \
48-c 'interact'\
49"""
50
51
52LOGGER = logging.getLogger(__name__)
53
54
55class Snapd:
56 """Contain Snapd specific functionality to use via command
57 line interface"""
58
59 def run_snapd_command(self, command, cwd='', sudo=False):
60 """Run snapd command over ssh.
61
62 :param command: a string containing parameters for the `snap` command.
63 :param cwd: the current working directory on the remote where
64 the command should run.
65 :param sudo: whether to run the command with sudo privileges
66 :returns: stdout of the command
67 """
68 if not command.startswith(PATH_SNAP):
69 command = '{} {}'.format(PATH_SNAP, command)
70 if cwd:
71 command = 'cd {};{}'.format(cwd.rstrip(), command)
72 if sudo:
73 return run_command_with_sudo(command)
74 else:
75 return run_command(command)
76
77 def login(self, email, password):
78 """Login to snapd.
79
80 :param email: Ubuntu SSO account email address.
81 :param password: Ubuntu SSO account password.
82 """
83 run_command(COMMANDS_LOGIN.format(email=email,
84 password=password))
85 return self.is_logged_in(email)
86
87 def is_logged_in(self, email):
88 """Return bool representing if the user is logged into snapd."""
89 try:
90 return json.loads(run_command('cat ~/.snap/auth.json')).get('email') == email
91 except ValueError:
92 return False
93
94 def logout(self, email):
95 """Logout snapd current user."""
96 if self.is_logged_in(email):
97 self.run_snapd_command(COMMAND_LOGOUT)
98
99 def download(self, snap, channel=CHANNEL_STABLE):
100 """Download the requested snap.
101
102 :param snap: name of the snap to download.
103 :param channel: name of the release channel to download from.
104 """
105 command = COMMAND_DOWNLOAD.format(snap=snap, channel=channel)
106 _dir = run_command('mktemp -d').rstrip()
107 self.run_snapd_command(command, cwd=_dir)
108 return _dir
109
110 def install(self, snap, channel=CHANNEL_STABLE):
111 """Install the requested snap."""
112 return self.run_snapd_command(COMMAND_INSTALL.format(snap=snap,
113 channel=channel),
114 sudo=True)
115
116 def is_installed(self, snap):
117 """Return bool representing whether a snap is installed."""
118 try:
119 for installed_snap in Snapd._parse_output(
120 self.run_snapd_command(COMMAND_LIST)):
121 if installed_snap['name'] == snap:
122 return True
123 except KeyError:
124 return False
125 return False
126
127 def remove(self, snap):
128 """Remove a snap, if its already installed."""
129 if self.is_installed(snap):
130 self.run_snapd_command(COMMAND_REMOVE.format(snap=snap), sudo=True)
131
132 def info(self, snap, verbose=False):
133 """Query the Ubuntu store of the information about a snap.
134
135 :param snap: Name of the snap for which the info is required.
136 :param verbose: Whether to information should be detailed.
137 :return: Return a dictionary containing information about the snap.
138 """
139 command = COMMAND_INFO.format(snap=snap)
140 if verbose:
141 command = ' '.join([command, '--verbose'])
142 return yaml.load("""{}""".format(self.run_snapd_command(command)))
143
144 def refresh(self, snap, channel=CHANNEL_STABLE):
145 """Refresh the requested snap."""
146 self.run_snapd_command(COMMAND_REFRESH.format(snap=snap,
147 channel=channel))
148
149 @staticmethod
150 def _parse_output(raw_output):
151 """Pretty parse the output from snapd commands like `find` and `list`.
152
153 :param raw_output: The raw output returned from the command
154 :return: A list of dictionaries containing sorted results
155 from the output.
156 """
157 split_output = raw_output.split('\n')
158 headers = [header.lower() for header in split_output.pop(0).split()]
159 return [dict(zip(headers, line.split())) for line in split_output]
160
161 def find(self, keyword):
162 """Find snaps based on the provided filters
163
164 :param keyword: Keyword to use for the query.
165 :return: Return a list of dictionaries containing information about
166 snaps matching the `keyword`.
167 """
168 return Snapd._parse_output(
169 self.run_snapd_command(COMMAND_FIND.format(
170 search_term=keyword)))
171
172 def is_published(self, snap_name, revision, channel='stable'):
173 """Return bool representing whether a snap is published.
174
175 :param snap_name: Name of the snap to check.
176 :param revision: Revision to check.
177 :param channel: Channel to check.
178 :return: True if published, False otherwise.
179 """
180 try:
181 return int(self.info(snap_name)['channels'][channel].split()[1]
182 .strip('()')) >= int(revision)
183 except (ValueError, KeyError):
184 return False
185
186 def wait_for_publish(self, snap_name, revision, channel='stable',
187 retry_attempts=10, retry_interval=1):
188 """Waits for the requested snap to publish by checking if its info
189 can be retrieved.
190
191 :param snap_name: Name of the snap to wait for publish.
192 :param revision: Snap revision number to check.
193 :param channel: Channel to check snap in.
194 :param retry_attempts: Number of times to check for the publish.
195 :param retry_interval: Time between each attempt.
196 :raises ValueError: If the snap is not found published after the
197 given time.
198 """
199 for _ in range(retry_attempts):
200 if self.is_published(snap_name, revision, channel):
201 break
202 else:
203 sleep(retry_interval)
204 else:
205 raise ValueError('Snap not published, waited {} seconds.'.format(
206 retry_attempts * retry_interval))
0207
=== modified file 'ubuntu_system_tests/host/target_setup.py'
--- ubuntu_system_tests/host/target_setup.py 2017-09-22 13:51:52 +0000
+++ ubuntu_system_tests/host/target_setup.py 2018-01-02 13:43:13 +0000
@@ -65,8 +65,10 @@
6565
66 """Class to run setup actions directly on the target device."""66 """Class to run setup actions directly on the target device."""
6767
68 def __init__(self, config):68 def __init__(self, config, snapd_https_proxy, snapd_http_proxy):
69 self.config = config69 self.config = config
70 self.snapd_https_proxy = snapd_https_proxy
71 self.snapd_http_proxy = snapd_http_proxy
70 self.series = None72 self.series = None
71 self.release = None73 self.release = None
72 self.core_series = None74 self.core_series = None
@@ -94,6 +96,7 @@
94 with open("/etc/environment", "r") as environment:96 with open("/etc/environment", "r") as environment:
95 if "QT_LOAD_TESTABILITY" not in environment.read():97 if "QT_LOAD_TESTABILITY" not in environment.read():
96 self.enable_testability()98 self.enable_testability()
99 self.setup_snapd_proxy()
97100
98 def get_release(self):101 def get_release(self):
99 """Return float release number of running system."""102 """Return float release number of running system."""
@@ -258,16 +261,19 @@
258 self.mount_fs_rw()261 self.mount_fs_rw()
259 self._dist_upgrade()262 self._dist_upgrade()
260263
261 def setup_snapd_https_proxy(self):264 def setup_snapd_proxy(self):
262 """Setup https proxy for snapd if its defined in config."""265 """Setup proxy for snapd if it is defined in config."""
263 proxy = self.config.get('https_proxy')266 if self.snapd_https_proxy or self.snapd_http_proxy:
264 if proxy:
265 conf = '/etc/systemd/system/snapd.service.d/proxy.conf'267 conf = '/etc/systemd/system/snapd.service.d/proxy.conf'
266 self._create_folder(os.path.dirname(conf))268 self._create_folder(os.path.dirname(conf))
269 content = '[Service]\nEnvironment=SNAPD_DEBUG=1\n'
270 if self.snapd_https_proxy:
271 content += 'Environment=https_proxy={}\n'.format(self.snapd_https_proxy)
272 if self.snapd_http_proxy:
273 content += 'Environment=http_proxy={}\n'.format(self.snapd_http_proxy)
267 self._create_file(274 self._create_file(
268 conf,275 conf,
269 '[Service]\n'276 content)
270 'Environment=https_proxy={}\n'.format(proxy))
271 subprocess.check_call(['systemctl', 'daemon-reload'])277 subprocess.check_call(['systemctl', 'daemon-reload'])
272 subprocess.check_call(['systemctl', 'restart', 'snapd.service'])278 subprocess.check_call(['systemctl', 'restart', 'snapd.service'])
273279
@@ -599,10 +605,10 @@
599 ['systemctl', 'enable', service_name])605 ['systemctl', 'enable', service_name])
600606
601607
602def main(config_path, username):608def main(config_path, snapd_https_proxy, snapd_http_proxy, username):
603 with open(config_path, 'r') as f:609 with open(config_path, 'r') as f:
604 config_json = json.loads(f.read())610 config_json = json.loads(f.read())
605 runner = SetupRunner(config_json)611 runner = SetupRunner(config_json, snapd_https_proxy, snapd_http_proxy)
606 try:612 try:
607 runner.run_setup_commands(username)613 runner.run_setup_commands(username)
608 finally:614 finally:
@@ -612,7 +618,12 @@
612if __name__ == '__main__':618if __name__ == '__main__':
613 PARSER = argparse.ArgumentParser(description='Setup target.')619 PARSER = argparse.ArgumentParser(description='Setup target.')
614 PARSER.add_argument('--config_path', help='target config path')620 PARSER.add_argument('--config_path', help='target config path')
621 PARSER.add_argument('--snapd_https_proxy', help='snapd https proxy', default=None)
622 PARSER.add_argument('--snapd_http_proxy', help='snapd http proxy', default=None)
615 PARSER.add_argument('--username',623 PARSER.add_argument('--username',
616 help='Username to configure the environment')624 help='Username to configure the environment')
617 ARGS = PARSER.parse_args()625 ARGS = PARSER.parse_args()
618 sys.exit(main(ARGS.config_path, ARGS.username))626 sys.exit(main(ARGS.config_path,
627 ARGS.snapd_https_proxy,
628 ARGS.snapd_http_proxy,
629 ARGS.username))
619630
=== modified file 'ubuntu_system_tests/host/targets.py'
--- ubuntu_system_tests/host/targets.py 2017-08-10 14:11:03 +0000
+++ ubuntu_system_tests/host/targets.py 2018-01-02 13:43:13 +0000
@@ -180,10 +180,15 @@
180 def _run_target_setup(self):180 def _run_target_setup(self):
181 self._push_setup_script()181 self._push_setup_script()
182 self._push_setup_config()182 self._push_setup_config()
183 return self.run_sudo(183 command = '/usr/bin/python3 /tmp/target_setup.py --config_path /tmp/target_config.json ' \
184 '/usr/bin/python3 /tmp/target_setup.py --config_path /tmp/target_config.json '184 '--username %s' % self.config_stack.get('device_username')
185 '--username %s' % self.config_stack.get('device_username'),185 snapd_https_proxy = self.config_stack.get('snapd_https_proxy')
186 timeout=TIMEOUT_SETUP)186 snapd_http_proxy = self.config_stack.get('snapd_http_proxy')
187 if snapd_https_proxy:
188 command += ' --snapd_https_proxy %s' % snapd_https_proxy
189 if snapd_http_proxy:
190 command += ' --snapd_http_proxy %s' % snapd_http_proxy
191 return self.run_sudo(command, timeout=TIMEOUT_SETUP)
187192
188 def get_config(self, config_stack, args):193 def get_config(self, config_stack, args):
189 """Select the required config items to pass to the target setup194 """Select the required config items to pass to the target setup
190195
=== added file 'ubuntu_system_tests/tests/test_snapd.py'
--- ubuntu_system_tests/tests/test_snapd.py 1970-01-01 00:00:00 +0000
+++ ubuntu_system_tests/tests/test_snapd.py 2018-01-02 13:43:13 +0000
@@ -0,0 +1,57 @@
1# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2
3#
4# Ubuntu System Tests
5# Copyright (C) 2017 Canonical
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20import os
21
22from testtools import skip
23
24from ubuntu_system_tests.helpers.snapd.snapd import Snapd
25from ubuntu_system_tests.tests.base import BaseUbuntuSystemTestCase
26
27
28class TestSnapd(BaseUbuntuSystemTestCase):
29 """Tests for snapd"""
30
31 def setUp(self):
32 super(TestSnapd, self).setUp()
33 self.snapd = Snapd()
34 self.snap_name = 'hello-world'
35
36 def test_install_snap(self):
37 """Install snap and verify it is installed properly"""
38 self.snapd.install(self.snap_name)
39 self.assertTrue(self.snapd.is_installed(self.snap_name),
40 'The snap %s is not installed' % self.snap_name)
41
42 def test_find_snap(self):
43 """Find a snap and verify it is returned properly"""
44 self.assertTrue(self.snapd.find(self.snap_name),
45 'The snap %s was not returned' % self.snap_name)
46
47 @skip('Skip until snap download issue is solved in venonat')
48 def test_download_snap(self):
49 """Download a snap and verify it is downloaded properly"""
50 snap_dir = self.snapd.download(self.snap_name)
51 self.assertTrue(len(os.listdir(snap_dir)) > 0,
52 'The snap %s was not downloaded' % self.snap_name)
53
54 def tearDown(self):
55 super().tearDown()
56 if self.snapd.is_installed(self.snap_name):
57 self.snapd.remove(self.snap_name)

Subscribers

People subscribed via source and target branches