Merge lp:~canonical-platform-qa/snappy-ecosystem-tests/register-snap-tests into lp:snappy-ecosystem-tests

Proposed by Heber Parrucci
Status: Merged
Approved by: Heber Parrucci
Approved revision: 33
Merged at revision: 26
Proposed branch: lp:~canonical-platform-qa/snappy-ecosystem-tests/register-snap-tests
Merge into: lp:snappy-ecosystem-tests
Prerequisite: lp:~sbaldassin/snappy-ecosystem-tests/ecosystem_deployment
Diff against target: 1153 lines (+764/-104)
21 files modified
README.rst (+12/-7)
pylint.cfg (+4/-4)
requirements-unit-tests.txt (+1/-1)
requirements.txt (+1/-0)
run_system_tests (+1/-0)
snappy_ecosystem_tests/configs/ecosystem_tests.cfg (+8/-3)
snappy_ecosystem_tests/helpers/snapcraft/client.py (+177/-23)
snappy_ecosystem_tests/helpers/snapd/snapd.py (+5/-1)
snappy_ecosystem_tests/helpers/store_apis/rest_apis.py (+51/-6)
snappy_ecosystem_tests/models/__init__.py (+19/-0)
snappy_ecosystem_tests/models/snap.py (+39/-0)
snappy_ecosystem_tests/run.py (+24/-14)
snappy_ecosystem_tests/tests/test_register_snap.py (+116/-0)
snappy_ecosystem_tests/unittests/test_filters.py (+113/-0)
snappy_ecosystem_tests/unittests/test_snapcraft.py (+0/-31)
snappy_ecosystem_tests/utils/commands.py (+31/-0)
snappy_ecosystem_tests/utils/filters.py (+51/-0)
snappy_ecosystem_tests/utils/ssh.py (+4/-2)
snappy_ecosystem_tests/utils/store_versions.py (+74/-0)
snappy_ecosystem_tests/utils/storeconfig.py (+2/-2)
snappy_ecosystem_tests/utils/user.py (+31/-10)
To merge this branch: bzr merge lp:~canonical-platform-qa/snappy-ecosystem-tests/register-snap-tests
Reviewer Review Type Date Requested Status
platform-qa-bot continuous-integration Approve
Omer Akram (community) Needs Fixing
I Ahmad (community) Approve
prod-platform-qa continuous-integration Pending
Review via email: mp+318782@code.launchpad.net

This proposal supersedes a proposal from 2017-02-20.

Commit message

Automate scenarios:
* Successfully register a package name in snap store via snapcraft register command and check with RESTful API
* Successfully register a private package name in snap store via snapcraft and check with RESTful API
* Snapcraft lists a new snap registered via RESTful API
* Snapcraft lists a new private snap registered via RESTful API

Description of the change

This change includes 4 automated scenarios:
Automate:

Successfully register a package name in snap store via snapcraft register command and check with RESTful API

Successfully register a private package name in snap store via snapcraft and check with RESTful API

Snapcraft lists a new snap registered via RESTful API

Snapcraft lists a new private snap registered via RESTful API

For details refer to:

https://prod.practitest.com/p/2505/tests/781190/edit
https://prod.practitest.com/p/2505/tests/781173/edit
https://prod.practitest.com/p/2505/tests/781174/edit
https://prod.practitest.com/p/2505/tests/781201/edit

Note: we should include as part of cleanUp process the 'un-register' snap name. But currently there is not API for doing it. Therefore, every time you run the tests a new random name will be registered.

To run the tests:
./run_system_tests snappy_ecosystem_tests/tests/test_register_snap.py

To post a comment you must log in.
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
I Ahmad (iahmad) wrote : Posted in a previous version of this proposal

minor inline comment about test name otherwise LGTM

review: Approve
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Heber Parrucci (heber013) wrote : Posted in a previous version of this proposal

> minor inline comment about test name otherwise LGTM

Thanks for reviewing. Names were updated. Also, I added generic filters mechanism and 2 new tests. Feel free to review it again.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
I Ahmad (iahmad) wrote : Posted in a previous version of this proposal

Getting these errors when tried to run locally http://paste.ubuntu.com/24057813/

Revision history for this message
Heber Parrucci (heber013) wrote : Posted in a previous version of this proposal

> Getting these errors when tried to run locally
> http://paste.ubuntu.com/24057813/

Yes. It is because snapcraft helpers were updated to run the commands over ssh.
I was able to run the tests by connecting by ssh to my localhost. However, I needed to add the environment variables each time I sens the command via ssh... But it will not be needed once we have the containers setup in place, because we configure the env variables as part of the setup process.

If you still would like to tests it locally, add in ~/.config/ecosystem_tests.cfg:
[user]
user_email=^STORE_USER^
user_password=^STORE_PASSWORD^
hostname_remote=localhost
username_remote=^LOCAL_USERNAME^
port_remote=22

And replace in snappy_ecosystem_tests/utils/ssh.py the function run_command by:
http://paste.ubuntu.com/24058432/

As I said it is just a workaround to be able to run the tests on the localhost, it will not be needed once we have the setup environment in place.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
I Ahmad (iahmad) wrote : Posted in a previous version of this proposal

> > Getting these errors when tried to run locally
> > http://paste.ubuntu.com/24057813/
>
> Yes. It is because snapcraft helpers were updated to run the commands over
> ssh.
> I was able to run the tests by connecting by ssh to my localhost. However, I
> needed to add the environment variables each time I sens the command via
> ssh... But it will not be needed once we have the containers setup in place,
> because we configure the env variables as part of the setup process.
>
> If you still would like to tests it locally, add in
> ~/.config/ecosystem_tests.cfg:
> [user]
> user_email=^STORE_USER^
> user_password=^STORE_PASSWORD^
> hostname_remote=localhost
> username_remote=^LOCAL_USERNAME^
> port_remote=22
>
> And replace in snappy_ecosystem_tests/utils/ssh.py the function run_command
> by:
> http://paste.ubuntu.com/24058432/
>
> As I said it is just a workaround to be able to run the tests on the
> localhost, it will not be needed once we have the setup environment in place.

OK, then I think we should have the setup environment changes landed first with updated readme instructions.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
I Ahmad (iahmad) : Posted in a previous version of this proposal
Revision history for this message
Omer Akram (om26er) wrote : Posted in a previous version of this proposal

added minor suggestion.

review: Needs Fixing
Revision history for this message
Omer Akram (om26er) wrote : Posted in a previous version of this proposal

Added another comment/suggestion.

Revision history for this message
Heber Parrucci (heber013) wrote : Posted in a previous version of this proposal

Reply inline Thanks!

Revision history for this message
Heber Parrucci (heber013) wrote : Posted in a previous version of this proposal

Comment addressed

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
32. By Heber Parrucci

merge from trunk

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
33. By Omer Akram

merge with trunk

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

LGTM, please go ahead with landing.

review: Approve
Revision history for this message
Omer Akram (om26er) wrote :

added a comment. You seem to be using pexect on the host.

review: Needs Fixing
Revision history for this message
Omer Akram (om26er) wrote :

a few more inline comments.

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

Thanks for reviewing! Will fix the bug separately.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.rst'
2--- README.rst 2017-02-22 17:40:11 +0000
3+++ README.rst 2017-03-03 10:32:00 +0000
4@@ -69,17 +69,22 @@
5 [user]
6 user_email=^USER_NAME^
7 user_password=^USER_PASSWORD^
8-hostname_remote=^SSH_HOSTNAME^
9-username_remote=^SSH_USERNAME^
10-port_remote=^SSH_PORT^
11+snapd_hostname_remote=^SNAPD_SSH_HOSTNAME^
12+snapd_username_remote=^SNAPD_SSH_USERNAME^
13+snapd_port_remote=^SNAPD_SSH_PORT^
14+snapcraft_hostname_remote=^SNAPCRAFT_SSH_HOSTNAME^
15+snapcraft_username_remote=^SNAPCRAFT_SSH_USERNAME^
16+snapcraft_port_remote=^SNAPCRAFT_SSH_PORT^
17
18 option 2 - Set the following environment variables:
19 user_email=^USER_NAME^
20 user_password=^USER_PASSWORD^
21-hostname_remote=^SSH_HOSTNAME^
22-username_remote=^SSH_USERNAME^
23-port_remote=^SSH_PORT^
24-
25+snapd_hostname_remote=^SNAPD_SSH_HOSTNAME^
26+snapd_username_remote=^SNAPD_SSH_USERNAME^
27+snapd_port_remote=^SNAPD_SSH_PORT^
28+snapcraft_hostname_remote=^SNAPCRAFT_SSH_HOSTNAME^
29+snapcraft_username_remote=^SNAPCRAFT_SSH_USERNAME^
30+snapcraft_port_remote=^SNAPCRAFT_SSH_PORT^
31
32 Changing store:
33 ===============
34
35=== modified file 'pylint.cfg'
36--- pylint.cfg 2017-02-21 13:46:08 +0000
37+++ pylint.cfg 2017-03-03 10:32:00 +0000
38@@ -169,10 +169,10 @@
39 inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
40
41 # Regular expression matching correct function names
42-function-rgx=[a-z_][a-z0-9_]{2,30}$
43+function-rgx=[a-z_][a-z0-9_]{2,40}$
44
45 # Naming hint for function names
46-function-name-hint=[a-z_][a-z0-9_]{2,30}$
47+function-name-hint=[a-z_][a-z0-9_]{2,40}$
48
49 # Regular expression matching correct attribute names
50 attr-rgx=[a-z_][a-z0-9_]{2,30}$
51@@ -181,10 +181,10 @@
52 attr-name-hint=[a-z_][a-z0-9_]{2,30}$
53
54 # Regular expression matching correct method names
55-method-rgx=[a-z_][a-z0-9_]{2,30}$|setUp|tearDown
56+method-rgx=[a-z_][a-z0-9_]{2,30}$|setUp|tearDown|test_*
57
58 # Naming hint for method names
59-method-name-hint=[a-z_][a-z0-9_]{2,30}$|setUp|tearDown
60+method-name-hint=[a-z_][a-z0-9_]{2,30}$|setUp|tearDown|test_*
61
62 # Regular expression matching correct class attribute names
63 class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
64
65=== modified file 'requirements-unit-tests.txt'
66--- requirements-unit-tests.txt 2017-02-10 18:18:45 +0000
67+++ requirements-unit-tests.txt 2017-03-03 10:32:00 +0000
68@@ -1,1 +1,1 @@
69-unittest2
70+testtools
71
72=== modified file 'requirements.txt'
73--- requirements.txt 2017-03-01 10:43:34 +0000
74+++ requirements.txt 2017-03-03 10:32:00 +0000
75@@ -18,3 +18,4 @@
76 pyyaml
77 retrying
78 paramiko
79+chromedriver_installer
80
81=== modified file 'run_system_tests'
82--- run_system_tests 2017-02-13 16:00:04 +0000
83+++ run_system_tests 2017-03-03 10:32:00 +0000
84@@ -20,6 +20,7 @@
85
86 . ./mk-venv "$@"
87
88+ve/bin/python3 ./snappy_ecosystem_tests/utils/store_versions.py
89 ve/bin/python3 ./snappy_ecosystem_tests/run.py "$@" -c snappy_ecosystem_tests/configs/pytest.cfg --ignore snappy_ecosystem_tests/unittests/ --junitxml snappy-ecosystem-results.xml
90
91 deactivate
92
93=== modified file 'snappy_ecosystem_tests/configs/ecosystem_tests.cfg'
94--- snappy_ecosystem_tests/configs/ecosystem_tests.cfg 2017-02-10 19:20:45 +0000
95+++ snappy_ecosystem_tests/configs/ecosystem_tests.cfg 2017-03-03 10:32:00 +0000
96@@ -1,7 +1,5 @@
97-[web-ui]
98+[selenium]
99 browser=chrome
100-stage_url=https://myapps.developer.staging.ubuntu.com/
101-production_url=https://myapps.developer.ubuntu.com/
102
103 [store]
104 # store to use in tests. Possible values: staging or production
105@@ -12,9 +10,16 @@
106 upload=https://upload.apps.staging.ubuntu.com/
107 sso=https://login.staging.ubuntu.com/api/v2/
108 search=https://search.apps.staging.ubuntu.com/
109+web=https://myapps.developer.staging.ubuntu.com/
110
111 [production_urls]
112 root_api=https://myapps.developer.ubuntu.com/dev/api/
113 upload=https://upload.apps.ubuntu.com/
114 sso=https://login.ubuntu.com/api/v2/
115 search=https://search.apps.ubuntu.com/
116+web=https://myapps.developer.ubuntu.com/
117+
118+[time_between_registrations]
119+# Time in seconds between each snap registration
120+staging=10
121+production=180
122
123=== modified file 'snappy_ecosystem_tests/helpers/snapcraft/client.py'
124--- snappy_ecosystem_tests/helpers/snapcraft/client.py 2017-02-15 19:35:54 +0000
125+++ snappy_ecosystem_tests/helpers/snapcraft/client.py 2017-03-03 10:32:00 +0000
126@@ -20,14 +20,50 @@
127
128 """Snapcraft client helpers"""
129
130+import logging
131+import subprocess
132+import time
133+
134 import pexpect
135
136-
137-# login credentials exported by shell environment
138+from snappy_ecosystem_tests.models.snap import Snap
139+from snappy_ecosystem_tests.utils import ssh
140+from snappy_ecosystem_tests.utils.commands import build_command
141+from snappy_ecosystem_tests.utils.filters import filter_list
142+from snappy_ecosystem_tests.utils.storeconfig import get_current_store
143+
144+from snappy_ecosystem_tests.commons.config import CONFIG_STACK
145+
146 from snappy_ecosystem_tests.utils import storeconfig
147
148+# login credentials gotten from config file or shell environment
149+from snappy_ecosystem_tests.utils.user import (
150+ get_snapcraft_remote_host_credentials
151+)
152+
153 LOGIN_EMAIL, LOGIN_PASSWORD = storeconfig.get_store_credentials()
154
155+# Commands
156+PATH_SNAPCRAFT = '/usr/bin/snapcraft'
157+COMMANDS_LOGIN = """\
158+/usr/bin/expect \
159+-c 'spawn {snapcraft} login' \
160+-c 'expect Email:' \
161+-c 'send {email}\\r' \
162+-c 'expect Password:' \
163+-c 'send {password}\\r' \
164+-c 'interact'\
165+"""
166+
167+COMMAND_LOGOUT = 'logout'
168+COMMAND_REGISTER = 'register'
169+COMMAND_LIST_REGISTERED = 'list-registered'
170+
171+LOGGER = logging.getLogger(__name__)
172+
173+HOSTNAME, USERNAME, PORT = get_snapcraft_remote_host_credentials()
174+SNAPCRAFT_CONFIG_FILE_PATH = '~/.config/snapcraft/snapcraft.cfg'
175+
176
177 class Snapcraft(object):
178 """Contain Snapcraft specific functionality to use via command
179@@ -37,13 +73,49 @@
180 self._login = False
181 self._cleanup()
182
183+ @staticmethod
184+ def _run_snapcraft_command_ssh(command, debug=True):
185+ """
186+ Run snapcraft command via ssh
187+ :param command: the command to be executed.
188+ Can be a single string or a list of strings
189+ :param debug: whether to enable debug mode
190+ :return: the command's output
191+ """
192+ if not command.startswith(PATH_SNAPCRAFT):
193+ command = build_snapcraft_command(command)
194+ if debug:
195+ command += ' -d'
196+ return ssh.run_command(command, hostname=HOSTNAME, username=USERNAME,
197+ port=PORT)
198+
199+ @staticmethod
200+ def assert_logged_in():
201+ """Assert that an user is logged.
202+ :raise ValueError: in case login is false
203+ """
204+ if Snapcraft.is_logged_in() is False:
205+ raise ValueError("User is not logged in, "
206+ "please login before using this command")
207+
208+ @staticmethod
209+ def is_logged_in():
210+ """Return bool representing if the user is logged into snapcraft."""
211+ try:
212+ output = ssh.run_command(
213+ 'cat %s' % SNAPCRAFT_CONFIG_FILE_PATH,
214+ hostname=HOSTNAME, username=USERNAME, port=PORT)
215+ return output is not ''
216+ except ValueError:
217+ return False
218+
219 def _cleanup(self):
220 """Perform cleanup actions"""
221 self.logout()
222
223 def logout(self):
224 """logout of snapcraft store session"""
225- child = pexpect.spawn("snapcraft logout")
226+ child = pexpect.spawn(build_snapcraft_command(COMMAND_LOGOUT))
227 err = child.expect('Credentials cleared.')
228 child.terminate(True)
229 child.wait()
230@@ -52,23 +124,105 @@
231
232 self._login = False
233
234- def login(self):
235- """login to store using the credential environment variables"""
236- child = pexpect.spawn("snapcraft login")
237- child.expect('Email: ')
238- child.sendline(LOGIN_EMAIL)
239- child.expect('Password:')
240- child.sendline(LOGIN_PASSWORD)
241- err = child.expect('Login successful')
242- if err is not 0:
243- raise ValueError("Failed to login")
244-
245- self._login = True
246-
247- def list_registered(self):
248- """call snapcraft list-registered command,
249- raise exception if not logged in"""
250- if self._login is False:
251- raise ValueError("User is not logged in, "
252- "please login before using this command")
253- return pexpect.spawnu("snapcraft list-registered").read()
254+ @staticmethod
255+ def login(email=LOGIN_EMAIL, password=LOGIN_PASSWORD):
256+ """Login to Snapcraft. If email and/or password are not provided,
257+ use the default ones.
258+ :param email: the SSO email
259+ :param password: the email password
260+ """
261+ ssh.run_command(
262+ COMMANDS_LOGIN.format(email=email, password=password,
263+ snapcraft=PATH_SNAPCRAFT),
264+ hostname=HOSTNAME, username=USERNAME, port=PORT)
265+ return Snapcraft.is_logged_in()
266+
267+ def list_registered(self, debug=True, raw=False):
268+ """Call snapcraft list-registered command,
269+ raise exception if not logged in
270+ :param debug: whether to enable debug mode
271+ :param raw: whether to return the output as a raw.
272+ If False, return a list of Snap instances
273+ """
274+ self.assert_logged_in()
275+ registered_snaps_output = self._run_snapcraft_command_ssh(
276+ COMMAND_LIST_REGISTERED,
277+ debug=debug)
278+ if raw:
279+ return registered_snaps_output
280+ else:
281+ snaps = registered_snaps_output.splitlines()
282+ # if debug is set, the first 3 lines of output are the API calls
283+ return parse_snaps_raw(snaps if debug is False else snaps[3:])
284+
285+ def filter_snaps(self, **predicates):
286+ """
287+ Return a list of the snaps that match with all the given predicates.
288+ Examples:
289+ filter_snap() --> return all the snaps
290+ filter_snap(name='my_name') --> return the snaps that matches with
291+ name 'my_name'
292+ filter_snap(name='my_name', private=True) --> return the snaps that
293+ matches with name 'my_name' and are private.
294+ filter_snap(price='5') --> return the snaps with price '5'.
295+ :return: A list of snap instances
296+ """
297+ return filter_list(self.list_registered(raw=False), **predicates)
298+
299+ def register(self, snap_name, private=False, wait=True):
300+ """
301+ Register a snap name in the store.
302+ :param snap_name: the snap name to register.
303+ :param private: whether the snap name is private.
304+ :param wait: wheter to wait after registration to hitting the store
305+ restriction on following registrations
306+ :return: True if the snap was registered successfully, False otherwise.
307+ """
308+ self.assert_logged_in()
309+ LOGGER.info('About to register snap: %s', snap_name)
310+ command = build_snapcraft_command(COMMAND_REGISTER, snap_name)
311+ if private:
312+ command += ' --private'
313+ try:
314+ Snapcraft._run_snapcraft_command_ssh(command)
315+ except subprocess.CalledProcessError as _e:
316+ LOGGER.error('Unable to register snap: %s', _e.output)
317+ return False
318+ if wait:
319+ time.sleep(int(CONFIG_STACK.get('time_between_registrations',
320+ get_current_store())))
321+ LOGGER.info('Snap %s registered successfully!!!', snap_name)
322+ return True
323+
324+
325+def build_snapcraft_command(*args, base_command=PATH_SNAPCRAFT):
326+ """
327+ Build a snapcraft command with arguments
328+ :param args: list of arguments for the command
329+ :param base_command: snapcraft executable that is going to be used
330+ :return: the snapcraft command ready to be executed
331+ """
332+ return build_command(base_command, *args)
333+
334+
335+def parse_snaps_raw(raw):
336+ """
337+ Parse snaps raw output to a list of Snaps object
338+ :param raw: the raw that contains the snaps info.
339+ It assumes that the first line is the output Header like:
340+ (Name, Since, Price, etc.) to get the attributes order.
341+ :return: A list of snaps instances
342+ """
343+ attributes_order = {}
344+ for _i, att in enumerate(raw[0].split()):
345+ attributes_order[att.lower()] = _i
346+ snaps = []
347+ for snap_raw in raw[1:]:
348+ attrs = snap_raw.split()
349+ private = attrs[attributes_order.get('visibility')].lower() == 'private'
350+ snaps.append(Snap(name=attrs[attributes_order.get('name')],
351+ since=attrs[attributes_order.get('since')],
352+ private=private,
353+ price=attrs[attributes_order.get('price')],
354+ notes=attrs[attributes_order.get('notes')]))
355+ return snaps
356
357=== modified file 'snappy_ecosystem_tests/helpers/snapd/snapd.py'
358--- snappy_ecosystem_tests/helpers/snapd/snapd.py 2017-02-28 21:52:46 +0000
359+++ snappy_ecosystem_tests/helpers/snapd/snapd.py 2017-03-03 10:32:00 +0000
360@@ -26,6 +26,7 @@
361 import yaml
362
363 from snappy_ecosystem_tests.utils import ssh
364+from snappy_ecosystem_tests.utils.user import get_snapd_remote_host_credentials
365
366 PATH_SNAP = '/usr/bin/snap'
367 COMMAND_DOWNLOAD = 'download {snap} --channel={channel}'
368@@ -47,6 +48,8 @@
369 """
370 LOGGER = logging.getLogger(__name__)
371
372+HOSTNAME, USERNAME, PORT = get_snapd_remote_host_credentials()
373+
374
375 def run_snapd_command_ssh(parameters, cwd=''):
376 """Run snapd command over ssh.
377@@ -56,7 +59,8 @@
378 should run.
379 :returns: stdout of the command
380 """
381- return ssh.run_command('{} {}'.format(PATH_SNAP, parameters), cwd=cwd)
382+ return ssh.run_command('{} {}'.format(PATH_SNAP, parameters), cwd=cwd,
383+ hostname=HOSTNAME, username=USERNAME, port=PORT)
384
385
386 def login(email, password):
387
388=== modified file 'snappy_ecosystem_tests/helpers/store_apis/rest_apis.py'
389--- snappy_ecosystem_tests/helpers/store_apis/rest_apis.py 2017-02-17 18:41:32 +0000
390+++ snappy_ecosystem_tests/helpers/store_apis/rest_apis.py 2017-03-03 10:32:00 +0000
391@@ -45,6 +45,8 @@
392 from snappy_ecosystem_tests.helpers.snapcraft.options import ProjectOptions
393 from snappy_ecosystem_tests.helpers.store_apis import errors
394 from snappy_ecosystem_tests.helpers.store_apis.upload import upload_files
395+from snappy_ecosystem_tests.models.snap import Snap
396+from snappy_ecosystem_tests.utils.filters import filter_list
397
398 LOGGER = logging.getLogger(__name__)
399
400@@ -193,16 +195,28 @@
401 """Get current account information"""
402 return self._refresh_if_necessary(self.sca.get_account_information)
403
404+ def get_registered_snaps(self, series=constants.DEFAULT_SERIES):
405+ """Get registered snaps for current user.
406+ First refresh macaroon if necessary"""
407+ return self._refresh_if_necessary(self.sca.get_registered_snaps,
408+ series)
409+
410+ def filter_snaps(self, series=constants.DEFAULT_SERIES, **predicates):
411+ """Filter snaps that match with given predicates
412+ First refresh macaroon if necessary"""
413+ return self._refresh_if_necessary(self.sca.filter_snaps,
414+ series=series, **predicates)
415+
416 def register_key(self, account_key_request):
417 """Register a key for the current user"""
418 return self._refresh_if_necessary(
419 self.sca.register_key, account_key_request)
420
421- def register(self, snap_name, is_private=False):
422+ def register(self, snap_name, private=False):
423 """Register a package name for the current user
424 First refresh macaroon if necessary"""
425 return self._refresh_if_necessary(
426- self.sca.register, snap_name, is_private, constants.DEFAULT_SERIES)
427+ self.sca.register, snap_name, private, constants.DEFAULT_SERIES)
428
429 def push_precheck(self, snap_name):
430 """Do a snap push as a pre-check (dry_run: do not actually push)
431@@ -555,6 +569,36 @@
432 else:
433 raise errors.StoreAccountInformationError(response)
434
435+ def get_registered_snaps(self, series=constants.DEFAULT_SERIES):
436+ """
437+ Get the registered snaps for the current user
438+ :param series: get only snaps that match with this series
439+ :return: the list of registered snaps
440+ """
441+ account_info = self.get_account_information()
442+ user_snaps = account_info.get('snaps')
443+ snaps_dict = user_snaps.get(series)
444+ snaps = []
445+ for name, snap_data in snaps_dict.items():
446+ snaps.append(Snap(name=name, **snap_data))
447+ return snaps
448+
449+ def filter_snaps(self, series=constants.DEFAULT_SERIES, **predicates):
450+ """
451+ Return a list of the snaps that match with all the given predicates.
452+ :param: series: only look in the snaps that match with this series
453+ Examples:
454+ filter_snap() --> return all the snaps
455+ filter_snap(name='my_name') --> return the snaps that matches with
456+ name 'my_name'
457+ filter_snap(name='my_name', private=True) --> return the snaps that
458+ matches with name 'my_name' and are private.
459+ filter_snap(price='5') --> return the snaps that with price '5'.
460+ :return: A list of snap instances
461+ """
462+ return filter_list(self.get_registered_snaps(series=series),
463+ **predicates)
464+
465 def register_key(self, account_key_request):
466 """Register a key for a user with the given request
467 :param account_key_request: the key to register
468@@ -568,20 +612,21 @@
469 if not response.ok:
470 raise errors.StoreKeyRegistrationError(response)
471
472- def register(self, snap_name, is_private, series):
473+ def register(self, snap_name, private, series):
474 """Register a snap name in the store
475 :param snap_name: the name to be registered
476- :param is_private: whether the snap name is private or not
477+ :param private: whether the snap name is private or not
478 :param series: the snap's series to be registered
479 """
480 auth = _macaroon_auth(self.conf)
481- data = dict(snap_name=snap_name, is_private=is_private,
482+ data = dict(snap_name=snap_name, is_private=private,
483 series=series)
484 response = self.post(
485 'register-name/', data=json.dumps(data),
486- headers=dict({'Authorization': auth}, **JSON_ACCEPT))
487+ headers=dict({'Authorization': auth}, **JSON_CONTENT_TYPE))
488 if not response.ok:
489 raise errors.StoreRegistrationError(snap_name, response)
490+ return True
491
492 def snap_push_precheck(self, snap_name):
493 """Do a snap push pre-check (dry_run: do not actually push)
494
495=== added directory 'snappy_ecosystem_tests/models'
496=== added file 'snappy_ecosystem_tests/models/__init__.py'
497--- snappy_ecosystem_tests/models/__init__.py 1970-01-01 00:00:00 +0000
498+++ snappy_ecosystem_tests/models/__init__.py 2017-03-03 10:32:00 +0000
499@@ -0,0 +1,19 @@
500+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
501+
502+#
503+# Snappy Ecosystem Tests
504+# Copyright (C) 2017 Canonical
505+#
506+# This program is free software: you can redistribute it and/or modify
507+# it under the terms of the GNU General Public License as published by
508+# the Free Software Foundation, either version 3 of the License, or
509+# (at your option) any later version.
510+#
511+# This program is distributed in the hope that it will be useful,
512+# but WITHOUT ANY WARRANTY; without even the implied warranty of
513+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
514+# GNU General Public License for more details.
515+#
516+# You should have received a copy of the GNU General Public License
517+# along with this program. If not, see <http://www.gnu.org/licenses/>.
518+#
519
520=== added file 'snappy_ecosystem_tests/models/snap.py'
521--- snappy_ecosystem_tests/models/snap.py 1970-01-01 00:00:00 +0000
522+++ snappy_ecosystem_tests/models/snap.py 2017-03-03 10:32:00 +0000
523@@ -0,0 +1,39 @@
524+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
525+
526+#
527+# Snappy Ecosystem Tests
528+# Copyright (C) 2017 Canonical
529+#
530+# This program is free software: you can redistribute it and/or modify
531+# it under the terms of the GNU General Public License as published by
532+# the Free Software Foundation, either version 3 of the License, or
533+# (at your option) any later version.
534+#
535+# This program is distributed in the hope that it will be useful,
536+# but WITHOUT ANY WARRANTY; without even the implied warranty of
537+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
538+# GNU General Public License for more details.
539+#
540+# You should have received a copy of the GNU General Public License
541+# along with this program. If not, see <http://www.gnu.org/licenses/>.
542+#
543+
544+"""Business model classes for snaps"""
545+
546+
547+class Snap:
548+ """Represents a snap entity."""
549+
550+ def __init__(self, name='', price=None, private=False, since=None,
551+ _id=None, status=None, notes=None, **kwargs):
552+ self.name = name
553+ self.price = price
554+ self.private = private
555+ self.since = since
556+ self._id = _id or kwargs.get('snap-id')
557+ self.status = status
558+ self.notes = notes
559+
560+ def is_private(self):
561+ """Return True if the snap is private, False otherwise"""
562+ return self.private
563
564=== modified file 'snappy_ecosystem_tests/run.py'
565--- snappy_ecosystem_tests/run.py 2017-02-15 19:14:19 +0000
566+++ snappy_ecosystem_tests/run.py 2017-03-03 10:32:00 +0000
567@@ -20,28 +20,38 @@
568
569 """Entry point to run snappy ecosystem tests"""
570
571+import argparse
572+import os
573 import re
574 import sys
575 import pytest
576
577-FILTER_ARGUMENTS = '--proxy'
578-
579-
580-def _filter_arguments():
581- """Remove from command line arguments all the ones that are in
582- FILTER_ARGUMENTS list."""
583- filtered_args = sys.argv[1:]
584- for i, arg in enumerate(sys.argv[1:]):
585- if arg in FILTER_ARGUMENTS:
586- filtered_args.pop(i + 1)
587- filtered_args.pop(i)
588- return filtered_args
589-
590
591 def _parse_arguments():
592 """Parse and filter command line arguments to makethem pytest compatible"""
593 sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
594- return _filter_arguments()
595+ parser = argparse.ArgumentParser(description=
596+ 'Run snappy ecosystem tests.')
597+ parser.add_argument('--proxy',
598+ help='proxy to use for creating the virtual env.',
599+ required=False)
600+ parser.add_argument('--target', default="lxd",
601+ help='Target on which tests will be executed.')
602+ ecosystem_args, pytest_args = parser.parse_known_args()
603+ _set_env_variables(ecosystem_args)
604+ return pytest_args
605+
606+
607+def _set_env_variables(args):
608+ """
609+ Set environment variables with ecosystem arguments passed
610+ in the command line.
611+ :param args: The arguments to store in env variables
612+ """
613+ for arg in vars(args):
614+ value = getattr(args, arg, None)
615+ if value:
616+ os.environ[arg] = value
617
618 if __name__ == '__main__':
619 sys.exit(pytest.main(args=_parse_arguments() + ['-p', 'no:cacheprovider']))
620
621=== added file 'snappy_ecosystem_tests/tests/test_register_snap.py'
622--- snappy_ecosystem_tests/tests/test_register_snap.py 1970-01-01 00:00:00 +0000
623+++ snappy_ecosystem_tests/tests/test_register_snap.py 2017-03-03 10:32:00 +0000
624@@ -0,0 +1,116 @@
625+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
626+
627+#
628+# Snappy Ecosystem Tests
629+# Copyright (C) 2017 Canonical
630+#
631+# This program is free software: you can redistribute it and/or modify
632+# it under the terms of the GNU General Public License as published by
633+# the Free Software Foundation, either version 3 of the License, or
634+# (at your option) any later version.
635+#
636+# This program is distributed in the hope that it will be useful,
637+# but WITHOUT ANY WARRANTY; without even the implied warranty of
638+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
639+# GNU General Public License for more details.
640+#
641+# You should have received a copy of the GNU General Public License
642+# along with this program. If not, see <http://www.gnu.org/licenses/>.
643+#
644+
645+"""Tests for Registering a snap name in the store"""
646+
647+import uuid
648+
649+from snappy_ecosystem_tests.helpers.snapcraft.client import Snapcraft
650+from snappy_ecosystem_tests.helpers.store_apis.rest_apis import Store
651+from snappy_ecosystem_tests.helpers.test_base import SnappyEcosystemTestCase
652+from snappy_ecosystem_tests.utils.storeconfig import get_store_credentials
653+
654+
655+class RegisterSnapNameTestCase(SnappyEcosystemTestCase):
656+
657+ def setUp(self):
658+ super().setUp()
659+ self.store = Store()
660+ self.snapcraft = Snapcraft()
661+ self.store.login(*get_store_credentials())
662+ self.addCleanup(self.store.logout)
663+ self.snapcraft.login()
664+ self.addCleanup(self.snapcraft.logout)
665+
666+ @staticmethod
667+ def _get_random_snap_name(prefix='ecosystem-test'):
668+ """Get a random snap name using uuid
669+ :param prefix: the string prefix,
670+ if not provided, use default one.
671+ """
672+ return '{}{}'.format(prefix, int(uuid.uuid4()))
673+
674+ def test_register_public_snap_name_using_snapcraft(self):
675+ """Test register a public snap name via snapcraft
676+ and verify it via REST API"""
677+ # Register snap via Snapcraft
678+ snap_name = self._get_random_snap_name()
679+ self.assertTrue(self.snapcraft.register(snap_name),
680+ 'Unable to register snap')
681+
682+ # Verify the registered snap via REST API
683+ snaps = self.store.filter_snaps(name=snap_name)
684+ self.assertEqual(1, len(snaps),
685+ 'Expected 1 snap with name %s but found %s'
686+ % (snap_name, len(snaps)))
687+ snap = snaps[0]
688+ self.assertEqual(snap_name, snap.name)
689+ self.assertFalse(snap.is_private())
690+
691+ def test_register_private_snap_name_using_snapcraft(self):
692+ """Test register a private snap name via snapcraft
693+ and verify it via REST API"""
694+ # Register snap via Snapcraft
695+ snap_name = self._get_random_snap_name()
696+ self.assertTrue(self.snapcraft.register(snap_name, private=True),
697+ 'Unable to register snap')
698+
699+ # Verify the registered snap via REST API
700+ snaps = self.store.filter_snaps(name=snap_name)
701+ self.assertEqual(1, len(snaps),
702+ 'Expected 1 snap with name %s but found %s'
703+ % (snap_name, len(snaps)))
704+ snap = snaps[0]
705+ self.assertEqual(snap_name, snap.name)
706+ self.assertTrue(snap.is_private())
707+
708+ def test_register_public_snap_name_using_api(self):
709+ """Verify that snapcraft lists a new snap registered via
710+ RESTful API when using the command snapcraft list-registered"""
711+ # Register snap via API
712+ snap_name = self._get_random_snap_name()
713+ self.assertTrue(self.store.register(snap_name),
714+ 'Unable to register snap')
715+
716+ # Verify the registered snap via Snapcraft
717+ snaps = self.snapcraft.filter_snaps(name=snap_name)
718+ self.assertEqual(1, len(snaps),
719+ 'Expected 1 snap with name %s but found %s'
720+ % (snap_name, len(snaps)))
721+ snap = snaps[0]
722+ self.assertEqual(snap_name, snap.name)
723+ self.assertFalse(snap.is_private())
724+
725+ def test_register_private_snap_name_using_api(self):
726+ """Verify that snapcraft lists a new private snap registered via
727+ RESTful API when using the command snapcraft list-registered"""
728+ # Register snap via API
729+ snap_name = self._get_random_snap_name()
730+ self.assertTrue(self.store.register(snap_name, private=True),
731+ 'Unable to register snap')
732+
733+ # Verify the registered snap via Snapcraft
734+ snaps = self.snapcraft.filter_snaps(name=snap_name)
735+ self.assertEqual(1, len(snaps),
736+ 'Expected 1 snap with name %s but found %s'
737+ % (snap_name, len(snaps)))
738+ snap = snaps[0]
739+ self.assertEqual(snap_name, snap.name)
740+ self.assertTrue(snap.is_private())
741
742=== added file 'snappy_ecosystem_tests/unittests/test_filters.py'
743--- snappy_ecosystem_tests/unittests/test_filters.py 1970-01-01 00:00:00 +0000
744+++ snappy_ecosystem_tests/unittests/test_filters.py 2017-03-03 10:32:00 +0000
745@@ -0,0 +1,113 @@
746+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
747+
748+#
749+# Snappy Ecosystem Tests
750+# Copyright (C) 2017 Canonical
751+#
752+# This program is free software: you can redistribute it and/or modify
753+# it under the terms of the GNU General Public License as published by
754+# the Free Software Foundation, either version 3 of the License, or
755+# (at your option) any later version.
756+#
757+# This program is distributed in the hope that it will be useful,
758+# but WITHOUT ANY WARRANTY; without even the implied warranty of
759+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
760+# GNU General Public License for more details.
761+#
762+# You should have received a copy of the GNU General Public License
763+# along with this program. If not, see <http://www.gnu.org/licenses/>.
764+#
765+
766+"""Unit tests cases for filter utils"""
767+
768+from testtools import (
769+ TestCase,
770+ ExpectedException
771+)
772+
773+from snappy_ecosystem_tests.utils.filters import (
774+ object_filter,
775+ filter_list
776+)
777+
778+
779+class DummyObject: # pylint: disable=too-few-public-methods
780+ """Dummy object to be used in unit tests"""
781+
782+ def __init__(self, **kwargs):
783+ for _k, _v in kwargs.items():
784+ setattr(self, _k, _v)
785+
786+
787+class FilterTestCase(TestCase):
788+
789+ def setUp(self):
790+ super().setUp()
791+ self.obj = DummyObject(name='dummy', desc='this is dummy')
792+ self.obj_2 = DummyObject(name='another-dummy',
793+ desc='this is another dummy')
794+ self.objects = [self.obj, self.obj_2]
795+
796+ def test_object_filter_with_single_argument(self):
797+ """Filter successfully passing one argument"""
798+ objects = list(filter(object_filter(name='dummy'), self.objects))
799+ self.assertIsNotNone(objects)
800+ self.assertEqual(len(objects), 1)
801+ self.assertEqual(objects[0], self.obj)
802+
803+ def test_object_filter_with_multiple_arguments(self):
804+ """Filter successfully passing two arguments"""
805+ objects = list(filter(object_filter(name='dummy',
806+ desc='this is dummy'),
807+ self.objects))
808+ self.assertIsNotNone(objects)
809+ self.assertEqual(len(objects), 1)
810+ self.assertEqual(objects[0], self.obj)
811+
812+ def test_object_filter_does_not_match(self):
813+ """Return empty list when no object match criteria"""
814+ objects = list(filter(object_filter(name='dummy',
815+ desc='any desc'),
816+ self.objects))
817+ self.assertEqual([], objects)
818+
819+ def test_object_filter_return_all(self):
820+ """Get all objects when no filter is provided"""
821+ objects = list(filter(object_filter(), self.objects))
822+ self.assertEqual(len(objects), len(self.objects))
823+
824+ def test_object_filter_raises_attribute_error_if_invalid_filter_name(self):
825+ """Object filter raises AttributeError when invalid filter
826+ is provided"""
827+ with ExpectedException(AttributeError):
828+ list(filter(object_filter(invalid='dummy'), self.objects))
829+
830+ def test_filter_list_with_single_argument(self):
831+ """Filter List successfully passing one argument"""
832+ objects = filter_list(self.objects, name='dummy')
833+ self.assertIsNotNone(objects)
834+ self.assertEqual(len(objects), 1)
835+ self.assertEqual(objects[0], self.obj)
836+
837+ def test_filter_list_with_multiple_arguments(self):
838+ """Filter List successfully passing multiple argument"""
839+ objects = filter_list(self.objects, name='another-dummy',
840+ desc='this is another dummy')
841+ self.assertIsNotNone(objects)
842+ self.assertEqual(len(objects), 1)
843+ self.assertEqual(objects[0], self.obj_2)
844+
845+ def test_filter_list_does_not_match(self):
846+ """Filter List returns empty list when no object match criteria"""
847+ objects = filter_list(self.objects, desc='desc does not match')
848+ self.assertEqual([], objects)
849+
850+ def test_filter_list_return_all(self):
851+ """Filter list returns all objects when no filter is provided"""
852+ objects = filter_list(self.objects)
853+ self.assertEqual(len(objects), len(self.objects))
854+
855+ def test_filter_list_raises_attribute_error_if_invalid_filter_name(self):
856+ """Filter list raises AttributeError when invalid filter is provided"""
857+ with ExpectedException(AttributeError):
858+ filter_list(self.objects, invalid='invalid filter')
859
860=== removed file 'snappy_ecosystem_tests/unittests/test_snapcraft.py'
861--- snappy_ecosystem_tests/unittests/test_snapcraft.py 2017-02-13 16:00:04 +0000
862+++ snappy_ecosystem_tests/unittests/test_snapcraft.py 1970-01-01 00:00:00 +0000
863@@ -1,31 +0,0 @@
864-# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
865-
866-#
867-# Snappy Ecosystem Tests
868-# Copyright (C) 2017 Canonical
869-#
870-# This program is free software: you can redistribute it and/or modify
871-# it under the terms of the GNU General Public License as published by
872-# the Free Software Foundation, either version 3 of the License, or
873-# (at your option) any later version.
874-#
875-# This program is distributed in the hope that it will be useful,
876-# but WITHOUT ANY WARRANTY; without even the implied warranty of
877-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
878-# GNU General Public License for more details.
879-#
880-# You should have received a copy of the GNU General Public License
881-# along with this program. If not, see <http://www.gnu.org/licenses/>.
882-#
883-
884-"""Unit tests cases for snapcraft"""
885-
886-from unittest2 import TestCase
887-
888-
889-class SnapcraftTestCase(TestCase):
890-
891- def test_snapcraft_dummy_test(self):
892- """Dummy test case"""
893- _a = True
894- self.assertTrue(_a)
895
896=== added file 'snappy_ecosystem_tests/utils/commands.py'
897--- snappy_ecosystem_tests/utils/commands.py 1970-01-01 00:00:00 +0000
898+++ snappy_ecosystem_tests/utils/commands.py 2017-03-03 10:32:00 +0000
899@@ -0,0 +1,31 @@
900+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
901+
902+#
903+# Snappy Ecosystem Tests
904+# Copyright (C) 2017 Canonical
905+#
906+# This program is free software: you can redistribute it and/or modify
907+# it under the terms of the GNU General Public License as published by
908+# the Free Software Foundation, either version 3 of the License, or
909+# (at your option) any later version.
910+#
911+# This program is distributed in the hope that it will be useful,
912+# but WITHOUT ANY WARRANTY; without even the implied warranty of
913+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
914+# GNU General Public License for more details.
915+#
916+# You should have received a copy of the GNU General Public License
917+# along with this program. If not, see <http://www.gnu.org/licenses/>.
918+#
919+
920+"""Manage commands to be executed in a shell"""
921+
922+
923+def build_command(command, *args):
924+ """
925+ Build a command with arguments
926+ :param command: the executable that is going to be used
927+ :param args: list of arguments for the command
928+ :return: the command ready to be executed
929+ """
930+ return'{c} {a}'.format(c=command, a=' '.join(args)).strip()
931
932=== added file 'snappy_ecosystem_tests/utils/filters.py'
933--- snappy_ecosystem_tests/utils/filters.py 1970-01-01 00:00:00 +0000
934+++ snappy_ecosystem_tests/utils/filters.py 2017-03-03 10:32:00 +0000
935@@ -0,0 +1,51 @@
936+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
937+
938+#
939+# Snappy Ecosystem Tests
940+# Copyright (C) 2017 Canonical
941+#
942+# This program is free software: you can redistribute it and/or modify
943+# it under the terms of the GNU General Public License as published by
944+# the Free Software Foundation, either version 3 of the License, or
945+# (at your option) any later version.
946+#
947+# This program is distributed in the hope that it will be useful,
948+# but WITHOUT ANY WARRANTY; without even the implied warranty of
949+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
950+# GNU General Public License for more details.
951+#
952+# You should have received a copy of the GNU General Public License
953+# along with this program. If not, see <http://www.gnu.org/licenses/>.
954+#
955+
956+"""Filter utils"""
957+
958+
959+def object_filter(**predicates):
960+ """
961+ Higher-order function to build and return an object filter function
962+ :param predicates: key/values that represent
963+ the conditions to look for in the object instance.
964+ :return: a function to be used with filter or lambda expressions.
965+ :raise: AttributeError if any of the attributes provided in predicates
966+ does not exists
967+ """
968+ def wrapped_filter(instance):
969+ """wrapped function to be returned when calling object_filter"""
970+ return all(getattr(instance,
971+ param) == val for param, val in predicates.items())
972+ return wrapped_filter
973+
974+
975+def filter_list(objects_list, **predicates):
976+ """
977+ Filter a list of objects with the given predicates
978+ :param objects_list: the target list that contains
979+ the objects to be filtered
980+ :param predicates: key/values that represent
981+ the conditions to look for in the object list.
982+ :return: A list with the objects that match all the predicates
983+ :raise: AttributeError if any of the attributes provided in predicates
984+ does not exists
985+ """
986+ return list(filter(object_filter(**predicates), objects_list))
987
988=== modified file 'snappy_ecosystem_tests/utils/ssh.py'
989--- snappy_ecosystem_tests/utils/ssh.py 2017-03-01 18:57:02 +0000
990+++ snappy_ecosystem_tests/utils/ssh.py 2017-03-03 10:32:00 +0000
991@@ -23,7 +23,7 @@
992 import logging
993 import paramiko
994
995-from snappy_ecosystem_tests.utils.user import get_remote_host_credentials
996+from snappy_ecosystem_tests.utils.user import get_snapd_remote_host_credentials
997
998 LOGGER = logging.getLogger(__name__)
999
1000@@ -113,7 +113,9 @@
1001 :raises ValueError: if command exits with non-zero status.
1002 :return: the stdout of the command.
1003 """
1004- _hostname, _username, _port = get_remote_host_credentials()
1005+ # Take snapd remote credentials as default if they are not provided.
1006+ # Just arbitrary... Could have been other ones.
1007+ _hostname, _username, _port = get_snapd_remote_host_credentials()
1008 ssh = SSHManager.get_instance(
1009 hostname or _hostname, username or _username, port or _port)
1010 return ssh.exec_command(command, cwd=cwd)
1011
1012=== added file 'snappy_ecosystem_tests/utils/store_versions.py'
1013--- snappy_ecosystem_tests/utils/store_versions.py 1970-01-01 00:00:00 +0000
1014+++ snappy_ecosystem_tests/utils/store_versions.py 2017-03-03 10:32:00 +0000
1015@@ -0,0 +1,74 @@
1016+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1017+
1018+#
1019+# Snappy Ecosystem Tests
1020+# Copyright (C) 2017 Canonical
1021+#
1022+# This program is free software: you can redistribute it and/or modify
1023+# it under the terms of the GNU General Public License as published by
1024+# the Free Software Foundation, either version 3 of the License, or
1025+# (at your option) any later version.
1026+#
1027+# This program is distributed in the hope that it will be useful,
1028+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1029+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1030+# GNU General Public License for more details.
1031+#
1032+# You should have received a copy of the GNU General Public License
1033+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1034+#
1035+
1036+"""Utility for printing Store versions of: CPI, SCA and SSO.
1037+To be called as a standalone script.
1038+"""
1039+
1040+import requests
1041+
1042+DEFAULT_HEADER = "X-BZR-REVISION-NUMBER"
1043+
1044+SERVICES_INFO = dict(
1045+ CPI=('https://search.apps.ubuntu.com/api/v1',
1046+ 'https://search.apps.staging.ubuntu.com/api/v1'),
1047+ SCA=('https://myapps.developer.ubuntu.com/',
1048+ 'https://myapps.developer.staging.ubuntu.com/'),
1049+ SSO=('https://login.ubuntu.com/',
1050+ 'https://login.staging.ubuntu.com/'),
1051+)
1052+
1053+
1054+def get_deployed_revno(service, environment):
1055+ """Get revno from service."""
1056+ prod, stag = SERVICES_INFO[service]
1057+ if environment == 'staging':
1058+ url = stag
1059+ else:
1060+ url = prod
1061+ with requests.Session() as session:
1062+ resp = session.get(url)
1063+ if resp.status_code == 200:
1064+ revno = resp.headers[DEFAULT_HEADER]
1065+ else:
1066+ print("{} 500'ed: {}".format(service, resp.text))
1067+ revno = 0
1068+ return service, environment, revno
1069+
1070+
1071+def print_versions():
1072+ """Print Store components version"""
1073+ coros = []
1074+ for service in SERVICES_INFO:
1075+ coro = get_deployed_revno(service, 'staging')
1076+ coros.append(coro)
1077+ coro = get_deployed_revno(service, 'production')
1078+ coros.append(coro)
1079+
1080+ data = {(service, kind): value for service, kind, value in coros}
1081+ print("#########################-STORE VERSIONS-#########################")
1082+ for service in sorted(SERVICES_INFO):
1083+ print("{}: Staging: {:>5}".format(service,
1084+ data[(service, 'staging')]))
1085+ print("{}: Production: {:>5}".format(service,
1086+ data[(service, 'production')]))
1087+
1088+if __name__ == '__main__':
1089+ print_versions()
1090
1091=== modified file 'snappy_ecosystem_tests/utils/storeconfig.py'
1092--- snappy_ecosystem_tests/utils/storeconfig.py 2017-02-15 19:14:19 +0000
1093+++ snappy_ecosystem_tests/utils/storeconfig.py 2017-03-03 10:32:00 +0000
1094@@ -27,8 +27,8 @@
1095 USER_CONFIG_STACK
1096 )
1097
1098-URL_WEB_STORE_PRODUCTION = CONFIG_STACK.get('web-ui', 'production_url')
1099-URL_WEB_STORE_STAGING = CONFIG_STACK.get('web-ui', 'stage_url')
1100+URL_WEB_STORE_PRODUCTION = CONFIG_STACK.get('production_urls', 'web')
1101+URL_WEB_STORE_STAGING = CONFIG_STACK.get('staging_urls', 'web')
1102
1103
1104 def get_store_credentials():
1105
1106=== modified file 'snappy_ecosystem_tests/utils/user.py'
1107--- snappy_ecosystem_tests/utils/user.py 2017-02-22 15:39:40 +0000
1108+++ snappy_ecosystem_tests/utils/user.py 2017-03-03 10:32:00 +0000
1109@@ -23,13 +23,34 @@
1110 import os
1111
1112 from snappy_ecosystem_tests.commons.config import USER_CONFIG_STACK
1113-
1114-
1115-def get_remote_host_credentials():
1116- """Return credentials for remote machine, to run commands on."""
1117- return (USER_CONFIG_STACK.get('user', 'hostname_remote',
1118- default=os.environ.get('hostname_remote')),
1119- USER_CONFIG_STACK.get('user', 'username_remote',
1120- default=os.environ.get('username_remote')),
1121- USER_CONFIG_STACK.get('user', 'port_remote',
1122- default=os.environ.get('port_remote')))
1123+from snappy_ecosystem_tests.environment.managers import get_manager
1124+
1125+MANAGER = get_manager(os.environ['target'])
1126+
1127+
1128+def get_snapd_remote_host_credentials():
1129+ """Return credentials for snapd remote machine, to run commands on."""
1130+ _ip = MANAGER.get_ip(USER_CONFIG_STACK.get(
1131+ 'user', 'snapd_hostname_remote',
1132+ default=os.environ.get('snapd_hostname_remote')))
1133+ return (_ip,
1134+ USER_CONFIG_STACK.get('user', 'snapd_username_remote',
1135+ default=os.environ.get(
1136+ 'snapd_username_remote')),
1137+ USER_CONFIG_STACK.get('user', 'snapd_port_remote',
1138+ default=os.environ.get(
1139+ 'snapd_port_remote')))
1140+
1141+
1142+def get_snapcraft_remote_host_credentials():
1143+ """Return credentials for snapcraft remote machine, to run commands on."""
1144+ _ip = MANAGER.get_ip(USER_CONFIG_STACK.get(
1145+ 'user', 'snapcraft_hostname_remote',
1146+ default=os.environ.get('snapcraft_snapcraft_remote')))
1147+ return (_ip,
1148+ USER_CONFIG_STACK.get('user', 'snapcraft_username_remote',
1149+ default=os.environ.get(
1150+ 'snapcraft_username_remote')),
1151+ USER_CONFIG_STACK.get('user', 'snapcraft_port_remote',
1152+ default=os.environ.get(
1153+ 'snapcraft_port_remote')))

Subscribers

People subscribed via source and target branches