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

Proposed by Heber Parrucci
Status: Superseded
Proposed branch: lp:~canonical-platform-qa/snappy-ecosystem-tests/register-snap-tests
Merge into: lp:snappy-ecosystem-tests
Diff against target: 1698 lines (+1186/-162)
30 files modified
README.rst (+12/-7)
pylint.cfg (+4/-4)
requirements-unit-tests.txt (+1/-1)
requirements.txt (+2/-1)
run_checks (+2/-2)
run_system_tests (+1/-0)
snappy_ecosystem_tests/configs/ecosystem_tests.cfg (+8/-3)
snappy_ecosystem_tests/environment/__init__.py (+19/-0)
snappy_ecosystem_tests/environment/constants.py (+82/-0)
snappy_ecosystem_tests/environment/data/__init__.py (+19/-0)
snappy_ecosystem_tests/environment/data/snapcraft.py (+21/-0)
snappy_ecosystem_tests/environment/data/snapd.py (+88/-0)
snappy_ecosystem_tests/environment/managers.py (+142/-0)
snappy_ecosystem_tests/environment/setup.py (+46/-0)
snappy_ecosystem_tests/helpers/snapcraft/client.py (+177/-23)
snappy_ecosystem_tests/helpers/snapd/snapd.py (+5/-1)
snappy_ecosystem_tests/helpers/snapd/staging_builder.py (+2/-55)
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+317796@code.launchpad.net

This proposal has been superseded by a proposal from 2017-03-02.

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 :
review: Needs Fixing (continuous-integration)
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 :

minor inline comment about test name otherwise LGTM

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

Implementing generic filters and adding 2 automated tests:
Snapcraft lists a new snap registered via RESTful API
Snapcraft lists a new private snap registered via RESTful API

Revision history for this message
Heber Parrucci (heber013) wrote :

> 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 :
review: Needs Fixing (continuous-integration)
21. By Heber Parrucci

fixing pylint

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

Adding: unittests for filter utils and minor improvements to filters.

23. By Heber Parrucci

adding missing file: unittests for filters

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

Adding standalone script to print store versions before execution.

25. By Heber Parrucci

Adding missing script

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

fixing pylint

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

merge from trunk

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

Updating snapcraft helpers to run over ssh

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 :

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

Revision history for this message
Heber Parrucci (heber013) wrote :

> 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.

29. By Heber Parrucci

Adding support for 2 remotes host users: snapd and snapcraft since they could be in different hosts/VMs.
Updating the clients helpers to use the corresponding credentials.
Updating README.rst with new info about remote users.

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 :

> > 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.

30. By Heber Parrucci

Merge from setup branch and adding capability to get the ip given the container name to ssh properly

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

added minor suggestion.

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

Added another comment/suggestion.

Revision history for this message
Heber Parrucci (heber013) wrote :

Reply inline Thanks!

31. By Heber Parrucci

Checking snapcraft config file to see if user is logged in as suggested in a comment.

Revision history for this message
Heber Parrucci (heber013) wrote :

Comment addressed

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

merge from trunk

33. By Omer Akram

merge with trunk

Unmerged revisions

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-02 14:15:33 +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-02 14:15:33 +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-02 14:15:33 +0000
68@@ -1,1 +1,1 @@
69-unittest2
70+testtools
71
72=== modified file 'requirements.txt'
73--- requirements.txt 2017-02-21 21:07:33 +0000
74+++ requirements.txt 2017-03-02 14:15:33 +0000
75@@ -14,7 +14,8 @@
76 pexpect
77 pymacaroons==0.9.2
78 requests-toolbelt==0.6.0
79-#chromedriver_installer
80 pylxd
81 pyyaml
82+retrying
83 paramiko
84+chromedriver_installer
85
86=== modified file 'run_checks'
87--- run_checks 2017-02-20 13:10:18 +0000
88+++ run_checks 2017-03-02 14:15:33 +0000
89@@ -51,9 +51,9 @@
90
91 if [ "$proxy" ]; then
92 echo "Using proxy: $proxy to install dependencies"
93- pip install --proxy $proxy -r requirements-unit-tests.txt
94+ pip3 install --proxy $proxy -r requirements-unit-tests.txt
95 else
96- pip install -r requirements-unit-tests.txt
97+ pip3 install -r requirements-unit-tests.txt
98 fi
99
100 ve_unit_tests/bin/python3 -m unittest discover snappy_ecosystem_tests.unittests
101
102=== modified file 'run_system_tests'
103--- run_system_tests 2017-02-13 16:00:04 +0000
104+++ run_system_tests 2017-03-02 14:15:33 +0000
105@@ -20,6 +20,7 @@
106
107 . ./mk-venv "$@"
108
109+ve/bin/python3 ./snappy_ecosystem_tests/utils/store_versions.py
110 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
111
112 deactivate
113
114=== modified file 'snappy_ecosystem_tests/configs/ecosystem_tests.cfg'
115--- snappy_ecosystem_tests/configs/ecosystem_tests.cfg 2017-02-10 19:20:45 +0000
116+++ snappy_ecosystem_tests/configs/ecosystem_tests.cfg 2017-03-02 14:15:33 +0000
117@@ -1,7 +1,5 @@
118-[web-ui]
119+[selenium]
120 browser=chrome
121-stage_url=https://myapps.developer.staging.ubuntu.com/
122-production_url=https://myapps.developer.ubuntu.com/
123
124 [store]
125 # store to use in tests. Possible values: staging or production
126@@ -12,9 +10,16 @@
127 upload=https://upload.apps.staging.ubuntu.com/
128 sso=https://login.staging.ubuntu.com/api/v2/
129 search=https://search.apps.staging.ubuntu.com/
130+web=https://myapps.developer.staging.ubuntu.com/
131
132 [production_urls]
133 root_api=https://myapps.developer.ubuntu.com/dev/api/
134 upload=https://upload.apps.ubuntu.com/
135 sso=https://login.ubuntu.com/api/v2/
136 search=https://search.apps.ubuntu.com/
137+web=https://myapps.developer.ubuntu.com/
138+
139+[time_between_registrations]
140+# Time in seconds between each snap registration
141+staging=10
142+production=180
143
144=== added directory 'snappy_ecosystem_tests/environment'
145=== added file 'snappy_ecosystem_tests/environment/__init__.py'
146--- snappy_ecosystem_tests/environment/__init__.py 1970-01-01 00:00:00 +0000
147+++ snappy_ecosystem_tests/environment/__init__.py 2017-03-02 14:15:33 +0000
148@@ -0,0 +1,19 @@
149+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
150+
151+#
152+# Snappy Ecosystem Tests
153+# Copyright (C) 2017 Canonical
154+#
155+# This program is free software: you can redistribute it and/or modify
156+# it under the terms of the GNU General Public License as published by
157+# the Free Software Foundation, either version 3 of the License, or
158+# (at your option) any later version.
159+#
160+# This program is distributed in the hope that it will be useful,
161+# but WITHOUT ANY WARRANTY; without even the implied warranty of
162+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
163+# GNU General Public License for more details.
164+#
165+# You should have received a copy of the GNU General Public License
166+# along with this program. If not, see <http://www.gnu.org/licenses/>.
167+#
168
169=== added file 'snappy_ecosystem_tests/environment/constants.py'
170--- snappy_ecosystem_tests/environment/constants.py 1970-01-01 00:00:00 +0000
171+++ snappy_ecosystem_tests/environment/constants.py 2017-03-02 14:15:33 +0000
172@@ -0,0 +1,82 @@
173+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
174+
175+#
176+# Snappy Ecosystem Tests
177+# Copyright (C) 2017 Canonical
178+#
179+# This program is free software: you can redistribute it and/or modify
180+# it under the terms of the GNU General Public License as published by
181+# the Free Software Foundation, either version 3 of the License, or
182+# (at your option) any later version.
183+#
184+# This program is distributed in the hope that it will be useful,
185+# but WITHOUT ANY WARRANTY; without even the implied warranty of
186+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
187+# GNU General Public License for more details.
188+#
189+# You should have received a copy of the GNU General Public License
190+# along with this program. If not, see <http://www.gnu.org/licenses/>.
191+#
192+
193+"""Constants used accross the environment setup"""
194+
195+SNAPD = 'snapd'
196+SNAPCRAFT = 'snapcraft'
197+
198+DEPENDENCIES = {
199+ SNAPD : [],
200+ SNAPCRAFT : [],
201+ "shared" : ["expect"]
202+}
203+
204+PROFILES = {
205+ "staging":{
206+ SNAPD:{
207+ "channel": "xenial",
208+ "package_name": 'snapd',
209+ "environment_variables": [
210+ {
211+ "name": "UBUNTU_STORE_API_ROOT_URL",
212+ "value": "https://myapps.developer.ubuntu.com/dev/api/"
213+ },
214+ {
215+ "name": "UBUNTU_STORE_UPLOAD_ROOT_URL",
216+ "value": "https://upload.apps.ubuntu.com"
217+ },
218+ {
219+ "name":"UBUNTU_SSO_API_ROOT_URL",
220+ "value":"https://login.ubuntu.com/api/v2/"
221+ },
222+ {
223+ "name":"UBUNTU_STORE_SEARCH_ROOT_URL",
224+ "value":"https://search.apps.ubuntu.com/"
225+ }
226+ ]
227+ },
228+ SNAPCRAFT: {
229+ "channel": "xenial",
230+ "package_name": 'snapcraft',
231+ "environment_variables": [
232+ {
233+ "name": "UBUNTU_STORE_API_ROOT_URL",
234+ "value": "https://myapps.developer.ubuntu.com/dev/api/"
235+ },
236+ {
237+ "name": "UBUNTU_STORE_UPLOAD_ROOT_URL",
238+ "value": "https://upload.apps.ubuntu.com"
239+ },
240+ {
241+ "name":"UBUNTU_SSO_API_ROOT_URL",
242+ "value":"https://login.ubuntu.com/api/v2/"
243+ },
244+ {
245+ "name":"UBUNTU_STORE_SEARCH_ROOT_URL",
246+ "value":"https://search.apps.ubuntu.com/"
247+ }
248+ ]
249+ },
250+ "snapweb":{
251+ "url": "https://myapps.developer.ubuntu.com/"
252+ },
253+ }
254+}
255
256=== added directory 'snappy_ecosystem_tests/environment/data'
257=== added file 'snappy_ecosystem_tests/environment/data/__init__.py'
258--- snappy_ecosystem_tests/environment/data/__init__.py 1970-01-01 00:00:00 +0000
259+++ snappy_ecosystem_tests/environment/data/__init__.py 2017-03-02 14:15:33 +0000
260@@ -0,0 +1,19 @@
261+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
262+
263+#
264+# Snappy Ecosystem Tests
265+# Copyright (C) 2017 Canonical
266+#
267+# This program is free software: you can redistribute it and/or modify
268+# it under the terms of the GNU General Public License as published by
269+# the Free Software Foundation, either version 3 of the License, or
270+# (at your option) any later version.
271+#
272+# This program is distributed in the hope that it will be useful,
273+# but WITHOUT ANY WARRANTY; without even the implied warranty of
274+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
275+# GNU General Public License for more details.
276+#
277+# You should have received a copy of the GNU General Public License
278+# along with this program. If not, see <http://www.gnu.org/licenses/>.
279+#
280
281=== added file 'snappy_ecosystem_tests/environment/data/snapcraft.py'
282--- snappy_ecosystem_tests/environment/data/snapcraft.py 1970-01-01 00:00:00 +0000
283+++ snappy_ecosystem_tests/environment/data/snapcraft.py 2017-03-02 14:15:33 +0000
284@@ -0,0 +1,21 @@
285+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
286+
287+#
288+# Snappy Ecosystem Tests
289+# Copyright (C) 2017 Canonical
290+#
291+# This program is free software: you can redistribute it and/or modify
292+# it under the terms of the GNU General Public License as published by
293+# the Free Software Foundation, either version 3 of the License, or
294+# (at your option) any later version.
295+#
296+# This program is distributed in the hope that it will be useful,
297+# but WITHOUT ANY WARRANTY; without even the implied warranty of
298+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
299+# GNU General Public License for more details.
300+#
301+# You should have received a copy of the GNU General Public License
302+# along with this program. If not, see <http://www.gnu.org/licenses/>.
303+#
304+"""Snapcraft data"""
305+SNAPCRAFT_CONTAINER_NAME = 'snapcraft'
306
307=== added file 'snappy_ecosystem_tests/environment/data/snapd.py'
308--- snappy_ecosystem_tests/environment/data/snapd.py 1970-01-01 00:00:00 +0000
309+++ snappy_ecosystem_tests/environment/data/snapd.py 2017-03-02 14:15:33 +0000
310@@ -0,0 +1,88 @@
311+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
312+
313+#
314+# Snappy Ecosystem Tests
315+# Copyright (C) 2017 Canonical
316+#
317+# This program is free software: you can redistribute it and/or modify
318+# it under the terms of the GNU General Public License as published by
319+# the Free Software Foundation, either version 3 of the License, or
320+# (at your option) any later version.
321+#
322+# This program is distributed in the hope that it will be useful,
323+# but WITHOUT ANY WARRANTY; without even the implied warranty of
324+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
325+# GNU General Public License for more details.
326+#
327+# You should have received a copy of the GNU General Public License
328+# along with this program. If not, see <http://www.gnu.org/licenses/>.
329+#
330+"""snapd data"""
331+SNAPD_CONTAINER_NAME = 'snapd'
332+
333+CONTAINER_HOME = '/root'
334+DIRECTORY_CLONE = '{}/src/github.com/snapcore/snapd'.format(CONTAINER_HOME)
335+DOMAIN_PING = 'ubuntu.com'
336+ENV_STORE_API_ROOT = 'UBUNTU_STORE_API_ROOT_URL'
337+ENV_STORE_SEARCH_ROOT = 'UBUNTU_STORE_SEARCH_ROOT_URL'
338+ENV_STORE_UPLOAD_ROOT = 'UBUNTU_STORE_UPLOAD_ROOT_URL'
339+ENV_SSO_API_ROOT = 'UBUNTU_SSO_API_ROOT_URL'
340+PACKAGES_SNAPD = ['snapd', 'snap-confine', 'ubuntu-core-launcher']
341+COMMAND_REMOVE_SNAPD = 'apt purge -y {}'.format(' '.join(PACKAGES_SNAPD))
342+COMMAND_APT_BUILD_DEP = 'apt build-dep -y ./'
343+COMMAND_APT_INSTALL = 'apt install -y {}'
344+COMMAND_APT_INSTALL_STAGING_DEBS = COMMAND_APT_INSTALL.format(
345+ ' '.join(['../{}*.deb'.format(pkg) for pkg in PACKAGES_SNAPD]))
346+COMMAND_APT_UPDATE = 'apt update'
347+COMMAND_APT_UPGRADE = 'apt dist-upgrade -y'
348+COMMAND_BUILD_SNAPD = 'DEB_BUILD_OPTIONS="nocheck testkeys" ' \
349+ 'dpkg-buildpackage -tc -b'
350+COMMAND_EXPORT_STAGING_STORE_VAR = 'echo SNAPPY_USE_STAGING_STORE=1 >> ' \
351+ '/etc/environment'
352+COMMAND_GET_GOVENDOR = 'go get -v github.com/kardianos/govendor'
353+COMMAND_GIT_CLONE = 'git clone {} {}'
354+COMMAND_GOVENDOR_SYNC = 'govendor sync'
355+REPOSITORY_GIT = 'https://github.com/snapcore/snapd'
356+URL_API_ROOT = 'https://myapps.developer.staging.ubuntu.com/dev/api/'
357+URL_SEARCH_ROOT = 'https://search.apps.staging.ubuntu.com/'
358+URL_UPLOAD_ROOT = 'https://upload.apps.staging.ubuntu.com/'
359+URL_SSO_API_ROOT = 'https://login.staging.ubuntu.com/api/v2/'
360+
361+CONTAINER_SETUP = [
362+ COMMAND_REMOVE_SNAPD,
363+ COMMAND_APT_UPDATE,
364+ # Hold open-iscsi which is causing apt upgrade errors while running
365+ # inside container.
366+ 'apt-mark hold open-iscsi',
367+ COMMAND_APT_UPGRADE,
368+ # Needed for snaps to work inside a lxd container
369+ # ref: https://stgraber.org/2016/12/07/running-snaps-in-lxd-containers/
370+ COMMAND_APT_INSTALL.format('squashfuse'),
371+ COMMAND_GIT_CLONE.format(REPOSITORY_GIT, DIRECTORY_CLONE),
372+ {'command': COMMAND_APT_BUILD_DEP, 'cwd': DIRECTORY_CLONE},
373+ {'command': COMMAND_GET_GOVENDOR, 'cwd': DIRECTORY_CLONE},
374+ {'command': COMMAND_GOVENDOR_SYNC, 'cwd': DIRECTORY_CLONE},
375+ {'command': COMMAND_BUILD_SNAPD, 'cwd': DIRECTORY_CLONE},
376+ COMMAND_EXPORT_STAGING_STORE_VAR,
377+ {'command': COMMAND_APT_INSTALL_STAGING_DEBS, 'cwd': DIRECTORY_CLONE}
378+]
379+
380+CONTAINER_ENV_VARS = {
381+ 'GOPATH': CONTAINER_HOME,
382+ 'SNAPPY_USE_STAGING_STORE': '1',
383+ ENV_STORE_API_ROOT: URL_API_ROOT,
384+ ENV_STORE_SEARCH_ROOT: URL_SEARCH_ROOT,
385+ ENV_STORE_UPLOAD_ROOT: URL_UPLOAD_ROOT,
386+ ENV_SSO_API_ROOT: URL_SSO_API_ROOT,
387+}
388+
389+DEFAULT_CONTAINER_CONFIG = {
390+ "name": "snapd",
391+ "source": {
392+ "type": "image",
393+ "mode": "pull",
394+ "server": "https://cloud-images.ubuntu.com/releases",
395+ "protocol": "simplestreams",
396+ "fingerprint": "16.04"
397+ }
398+}
399
400=== added file 'snappy_ecosystem_tests/environment/managers.py'
401--- snappy_ecosystem_tests/environment/managers.py 1970-01-01 00:00:00 +0000
402+++ snappy_ecosystem_tests/environment/managers.py 2017-03-02 14:15:33 +0000
403@@ -0,0 +1,142 @@
404+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
405+
406+#
407+# Ubuntu System Tests
408+# Copyright (C) 2017 Canonical
409+#
410+# This program is free software: you can redistribute it and/or modify
411+# it under the terms of the GNU General Public License as published by
412+# the Free Software Foundation, either version 3 of the License, or
413+# (at your option) any later version.
414+#
415+# This program is distributed in the hope that it will be useful,
416+# but WITHOUT ANY WARRANTY; without even the implied warranty of
417+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
418+# GNU General Public License for more details.
419+#
420+# You should have received a copy of the GNU General Public License
421+# along with this program. If not, see <http://www.gnu.org/licenses/>.
422+#
423+
424+"""Managers to setup the environment"""
425+
426+import os
427+import shlex
428+from pylxd import Client
429+from pylxd.exceptions import NotFound, LXDAPIException
430+from retrying import retry
431+
432+from snappy_ecosystem_tests.environment import constants
433+from snappy_ecosystem_tests.environment.constants import (
434+ PROFILES, DEPENDENCIES)
435+from snappy_ecosystem_tests.environment.data.snapcraft import (
436+ SNAPCRAFT_CONTAINER_NAME)
437+from snappy_ecosystem_tests.environment.data.snapd import (
438+ DEFAULT_CONTAINER_CONFIG, SNAPD_CONTAINER_NAME)
439+
440+def get_manager(mode):
441+ """Get the manager for the given mode"""
442+ supported_modules = {
443+ "lxd": LxdManager
444+ }
445+ try:
446+ manager = supported_modules[mode]
447+ return manager()
448+ except KeyError:
449+ raise RuntimeError("Mode: {} is not supported".format(mode))
450+
451+
452+class LxdManager:
453+ """Manage lxd containers"""
454+
455+ def __init__(self):
456+ self.client = Client()
457+
458+ def launch(self, container_name, release='16.04'):
459+ """Launch a container"""
460+ try:
461+ container = self.client.containers.get(container_name)
462+ if container.status == "Running":
463+ container.stop(wait=True)
464+ container.delete(wait=True)
465+ except NotFound:
466+ pass
467+ container_config = DEFAULT_CONTAINER_CONFIG.copy()
468+ container_config['source']['alias'] = release
469+ container_config['name'] = container_name
470+ container = self.client.containers.create(container_config, wait=True)
471+ container.start(wait=True)
472+
473+ def execute_command(self, container_name, command):
474+ """execute command"""
475+ container = self.client.containers.get(container_name)
476+ container.execute(command)
477+
478+ def install(self, container_name, packages, channel=None):
479+ """Install a packages"""
480+ container = self.client.containers.get(container_name)
481+ container.execute(shlex.split('apt-get update'))
482+ install_command = 'apt-get install {} -y'.format(packages)
483+ if channel:
484+ install_command += ' -t {}'.format(channel)
485+ container.execute(shlex.split(install_command))
486+
487+ def install_dependencies(self, container_name, container_type):
488+ """Install a dependencies"""
489+ self.install(
490+ container_name,
491+ DEPENDENCIES[container_type] + DEPENDENCIES['shared'])
492+
493+
494+ def configure(self, container_name, container_type, profile):
495+ """Configure a given container"""
496+ container = self.client.containers.get(container_name)
497+ for variable in \
498+ PROFILES[profile][container_type]['environment_variables']:
499+ container.config.update({'environment.{}'.format(
500+ variable['name']): variable['value']})
501+ container.save(wait=True)
502+
503+ @retry(stop_max_attempt_number=5, wait_fixed=3000,
504+ retry_on_exception=lambda exception: isinstance(
505+ exception, LXDAPIException))
506+ def enable_ssh(self, container_name):
507+ """Enable the ssh connection on the container"""
508+ container = self.client.containers.get(container_name)
509+ pub_key = open(
510+ os.path.expanduser('~') + '/.ssh/id_rsa.pub').read().strip('\n')
511+ container.files.put('/home/ubuntu/.ssh/authorized_keys', pub_key)
512+
513+
514+ def setup(self, profile):
515+ """setup the container based on the profile"""
516+ self._setup(SNAPD_CONTAINER_NAME, constants.SNAPD, profile)
517+ self._setup(
518+ SNAPCRAFT_CONTAINER_NAME, constants.SNAPCRAFT, profile)
519+
520+
521+ def _setup(self, container_name, container_type, profile):
522+ """
523+ Launch a container, enable ssh, install dependencies
524+ and setup the needed environment variables
525+ """
526+ self.launch(container_name)
527+ self.enable_ssh(container_name)
528+ self.install(
529+ container_name,
530+ packages=PROFILES[profile][container_type]["package_name"],
531+ channel=PROFILES[profile][container_type]["channel"])
532+ self.install_dependencies(container_name, container_type)
533+ self.configure(container_name, container_type, profile)
534+
535+ def get_ip(self, container_name):
536+ """Gets the ip address for a given container"""
537+ networks = self.client.containers.get(
538+ container_name).state().network['eth0']['addresses']
539+
540+ for network in networks:
541+ if network['address']:
542+ return network['address']
543+ raise RuntimeError(
544+ "The container {} does not have an IPV4 connection".format(
545+ container_name))
546
547=== added file 'snappy_ecosystem_tests/environment/setup.py'
548--- snappy_ecosystem_tests/environment/setup.py 1970-01-01 00:00:00 +0000
549+++ snappy_ecosystem_tests/environment/setup.py 2017-03-02 14:15:33 +0000
550@@ -0,0 +1,46 @@
551+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
552+
553+#
554+# Ubuntu System Tests
555+# Copyright (C) 2017 Canonical
556+#
557+# This program is free software: you can redistribute it and/or modify
558+# it under the terms of the GNU General Public License as published by
559+# the Free Software Foundation, either version 3 of the License, or
560+# (at your option) any later version.
561+#
562+# This program is distributed in the hope that it will be useful,
563+# but WITHOUT ANY WARRANTY; without even the implied warranty of
564+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
565+# GNU General Public License for more details.
566+#
567+# You should have received a copy of the GNU General Public License
568+# along with this program. If not, see <http://www.gnu.org/licenses/>.
569+#
570+
571+"""Standalone script to setup an ecosystem environment"""
572+
573+import argparse
574+
575+import logging
576+
577+from snappy_ecosystem_tests.environment.managers import get_manager
578+
579+logging.basicConfig(level=logging.DEBUG)
580+
581+def main(mode, profile):
582+ """Main script"""
583+ manager = get_manager(mode)
584+ manager.setup(profile)
585+
586+
587+if __name__ == '__main__':
588+ PARSER = argparse.ArgumentParser(description=
589+ 'Setup a snappy ecosystem environment.')
590+ PARSER.add_argument('--mode', default="lxd",
591+ help='lxd')
592+ PARSER.add_argument('--profile', default="staging",
593+ help='Profile to configure the environment')
594+ ARGS = PARSER.parse_args()
595+
596+ main(ARGS.mode, ARGS.profile)
597
598=== modified file 'snappy_ecosystem_tests/helpers/snapcraft/client.py'
599--- snappy_ecosystem_tests/helpers/snapcraft/client.py 2017-02-15 19:35:54 +0000
600+++ snappy_ecosystem_tests/helpers/snapcraft/client.py 2017-03-02 14:15:33 +0000
601@@ -20,14 +20,50 @@
602
603 """Snapcraft client helpers"""
604
605+import logging
606+import subprocess
607+import time
608+
609 import pexpect
610
611-
612-# login credentials exported by shell environment
613+from snappy_ecosystem_tests.models.snap import Snap
614+from snappy_ecosystem_tests.utils import ssh
615+from snappy_ecosystem_tests.utils.commands import build_command
616+from snappy_ecosystem_tests.utils.filters import filter_list
617+from snappy_ecosystem_tests.utils.storeconfig import get_current_store
618+
619+from snappy_ecosystem_tests.commons.config import CONFIG_STACK
620+
621 from snappy_ecosystem_tests.utils import storeconfig
622
623+# login credentials gotten from config file or shell environment
624+from snappy_ecosystem_tests.utils.user import (
625+ get_snapcraft_remote_host_credentials
626+)
627+
628 LOGIN_EMAIL, LOGIN_PASSWORD = storeconfig.get_store_credentials()
629
630+# Commands
631+PATH_SNAPCRAFT = '/usr/bin/snapcraft'
632+COMMANDS_LOGIN = """\
633+/usr/bin/expect \
634+-c 'spawn {snapcraft} login' \
635+-c 'expect Email:' \
636+-c 'send {email}\\r' \
637+-c 'expect Password:' \
638+-c 'send {password}\\r' \
639+-c 'interact'\
640+"""
641+
642+COMMAND_LOGOUT = 'logout'
643+COMMAND_REGISTER = 'register'
644+COMMAND_LIST_REGISTERED = 'list-registered'
645+
646+LOGGER = logging.getLogger(__name__)
647+
648+HOSTNAME, USERNAME, PORT = get_snapcraft_remote_host_credentials()
649+SNAPCRAFT_CONFIG_FILE_PATH = '~/.config/snapcraft/snapcraft.cfg'
650+
651
652 class Snapcraft(object):
653 """Contain Snapcraft specific functionality to use via command
654@@ -37,13 +73,49 @@
655 self._login = False
656 self._cleanup()
657
658+ @staticmethod
659+ def _run_snapcraft_command_ssh(command, debug=True):
660+ """
661+ Run snapcraft command via ssh
662+ :param command: the command to be executed.
663+ Can be a single string or a list of strings
664+ :param debug: whether to enable debug mode
665+ :return: the command's output
666+ """
667+ if not command.startswith(PATH_SNAPCRAFT):
668+ command = build_snapcraft_command(command)
669+ if debug:
670+ command += ' -d'
671+ return ssh.run_command(command, hostname=HOSTNAME, username=USERNAME,
672+ port=PORT)
673+
674+ @staticmethod
675+ def assert_logged_in():
676+ """Assert that an user is logged.
677+ :raise ValueError: in case login is false
678+ """
679+ if Snapcraft.is_logged_in() is False:
680+ raise ValueError("User is not logged in, "
681+ "please login before using this command")
682+
683+ @staticmethod
684+ def is_logged_in():
685+ """Return bool representing if the user is logged into snapcraft."""
686+ try:
687+ output = ssh.run_command(
688+ 'cat %s' % SNAPCRAFT_CONFIG_FILE_PATH,
689+ hostname=HOSTNAME, username=USERNAME, port=PORT)
690+ return output is not ''
691+ except ValueError:
692+ return False
693+
694 def _cleanup(self):
695 """Perform cleanup actions"""
696 self.logout()
697
698 def logout(self):
699 """logout of snapcraft store session"""
700- child = pexpect.spawn("snapcraft logout")
701+ child = pexpect.spawn(build_snapcraft_command(COMMAND_LOGOUT))
702 err = child.expect('Credentials cleared.')
703 child.terminate(True)
704 child.wait()
705@@ -52,23 +124,105 @@
706
707 self._login = False
708
709- def login(self):
710- """login to store using the credential environment variables"""
711- child = pexpect.spawn("snapcraft login")
712- child.expect('Email: ')
713- child.sendline(LOGIN_EMAIL)
714- child.expect('Password:')
715- child.sendline(LOGIN_PASSWORD)
716- err = child.expect('Login successful')
717- if err is not 0:
718- raise ValueError("Failed to login")
719-
720- self._login = True
721-
722- def list_registered(self):
723- """call snapcraft list-registered command,
724- raise exception if not logged in"""
725- if self._login is False:
726- raise ValueError("User is not logged in, "
727- "please login before using this command")
728- return pexpect.spawnu("snapcraft list-registered").read()
729+ @staticmethod
730+ def login(email=LOGIN_EMAIL, password=LOGIN_PASSWORD):
731+ """Login to Snapcraft. If email and/or password are not provided,
732+ use the default ones.
733+ :param email: the SSO email
734+ :param password: the email password
735+ """
736+ ssh.run_command(
737+ COMMANDS_LOGIN.format(email=email, password=password,
738+ snapcraft=PATH_SNAPCRAFT),
739+ hostname=HOSTNAME, username=USERNAME, port=PORT)
740+ return Snapcraft.is_logged_in()
741+
742+ def list_registered(self, debug=True, raw=False):
743+ """Call snapcraft list-registered command,
744+ raise exception if not logged in
745+ :param debug: whether to enable debug mode
746+ :param raw: whether to return the output as a raw.
747+ If False, return a list of Snap instances
748+ """
749+ self.assert_logged_in()
750+ registered_snaps_output = self._run_snapcraft_command_ssh(
751+ COMMAND_LIST_REGISTERED,
752+ debug=debug)
753+ if raw:
754+ return registered_snaps_output
755+ else:
756+ snaps = registered_snaps_output.splitlines()
757+ # if debug is set, the first 3 lines of output are the API calls
758+ return parse_snaps_raw(snaps if debug is False else snaps[3:])
759+
760+ def filter_snaps(self, **predicates):
761+ """
762+ Return a list of the snaps that match with all the given predicates.
763+ Examples:
764+ filter_snap() --> return all the snaps
765+ filter_snap(name='my_name') --> return the snaps that matches with
766+ name 'my_name'
767+ filter_snap(name='my_name', private=True) --> return the snaps that
768+ matches with name 'my_name' and are private.
769+ filter_snap(price='5') --> return the snaps with price '5'.
770+ :return: A list of snap instances
771+ """
772+ return filter_list(self.list_registered(raw=False), **predicates)
773+
774+ def register(self, snap_name, private=False, wait=True):
775+ """
776+ Register a snap name in the store.
777+ :param snap_name: the snap name to register.
778+ :param private: whether the snap name is private.
779+ :param wait: wheter to wait after registration to hitting the store
780+ restriction on following registrations
781+ :return: True if the snap was registered successfully, False otherwise.
782+ """
783+ self.assert_logged_in()
784+ LOGGER.info('About to register snap: %s', snap_name)
785+ command = build_snapcraft_command(COMMAND_REGISTER, snap_name)
786+ if private:
787+ command += ' --private'
788+ try:
789+ Snapcraft._run_snapcraft_command_ssh(command)
790+ except subprocess.CalledProcessError as _e:
791+ LOGGER.error('Unable to register snap: %s', _e.output)
792+ return False
793+ if wait:
794+ time.sleep(int(CONFIG_STACK.get('time_between_registrations',
795+ get_current_store())))
796+ LOGGER.info('Snap %s registered successfully!!!', snap_name)
797+ return True
798+
799+
800+def build_snapcraft_command(*args, base_command=PATH_SNAPCRAFT):
801+ """
802+ Build a snapcraft command with arguments
803+ :param args: list of arguments for the command
804+ :param base_command: snapcraft executable that is going to be used
805+ :return: the snapcraft command ready to be executed
806+ """
807+ return build_command(base_command, *args)
808+
809+
810+def parse_snaps_raw(raw):
811+ """
812+ Parse snaps raw output to a list of Snaps object
813+ :param raw: the raw that contains the snaps info.
814+ It assumes that the first line is the output Header like:
815+ (Name, Since, Price, etc.) to get the attributes order.
816+ :return: A list of snaps instances
817+ """
818+ attributes_order = {}
819+ for _i, att in enumerate(raw[0].split()):
820+ attributes_order[att.lower()] = _i
821+ snaps = []
822+ for snap_raw in raw[1:]:
823+ attrs = snap_raw.split()
824+ private = attrs[attributes_order.get('visibility')].lower() == 'private'
825+ snaps.append(Snap(name=attrs[attributes_order.get('name')],
826+ since=attrs[attributes_order.get('since')],
827+ private=private,
828+ price=attrs[attributes_order.get('price')],
829+ notes=attrs[attributes_order.get('notes')]))
830+ return snaps
831
832=== modified file 'snappy_ecosystem_tests/helpers/snapd/snapd.py'
833--- snappy_ecosystem_tests/helpers/snapd/snapd.py 2017-02-21 15:17:12 +0000
834+++ snappy_ecosystem_tests/helpers/snapd/snapd.py 2017-03-02 14:15:33 +0000
835@@ -26,6 +26,7 @@
836 import yaml
837
838 from snappy_ecosystem_tests.utils import ssh
839+from snappy_ecosystem_tests.utils.user import get_snapd_remote_host_credentials
840
841 PATH_SNAP = '/usr/bin/snap'
842 COMMAND_DOWNLOAD = 'download {snap} --channel={channel}'
843@@ -47,6 +48,8 @@
844 """
845 LOGGER = logging.getLogger(__name__)
846
847+HOSTNAME, USERNAME, PORT = get_snapd_remote_host_credentials()
848+
849
850 def run_snapd_command_ssh(parameters, cwd=''):
851 """Run snapd command over ssh.
852@@ -59,7 +62,8 @@
853 if cwd:
854 return ssh.run_command(
855 'cd {}; {} {}'.format(cwd, PATH_SNAP, parameters))
856- return ssh.run_command('{} {}'.format(PATH_SNAP, parameters))
857+ return ssh.run_command('{} {}'.format(PATH_SNAP, parameters),
858+ hostname=HOSTNAME, username=USERNAME, port=PORT)
859
860
861 def login(email, password):
862
863=== modified file 'snappy_ecosystem_tests/helpers/snapd/staging_builder.py'
864--- snappy_ecosystem_tests/helpers/snapd/staging_builder.py 2017-02-21 13:46:08 +0000
865+++ snappy_ecosystem_tests/helpers/snapd/staging_builder.py 2017-03-02 14:15:33 +0000
866@@ -24,63 +24,10 @@
867 import sys
868 from time import sleep
869
870+from snappy_ecosystem_tests.environment.data.snapd import (
871+ CONTAINER_HOME, CONTAINER_ENV_VARS, CONTAINER_SETUP)
872 from snappy_ecosystem_tests.utils.lxd import Container, launch_container
873
874-CONTAINER_HOME = '/root'
875-DIRECTORY_CLONE = '{}/src/github.com/snapcore/snapd'.format(CONTAINER_HOME)
876-ENV_STORE_API_ROOT = 'UBUNTU_STORE_API_ROOT_URL'
877-ENV_STORE_SEARCH_ROOT = 'UBUNTU_STORE_SEARCH_ROOT_URL'
878-ENV_STORE_UPLOAD_ROOT = 'UBUNTU_STORE_UPLOAD_ROOT_URL'
879-ENV_SSO_API_ROOT = 'UBUNTU_SSO_API_ROOT_URL'
880-PACKAGES_SNAPD = ['snapd', 'snap-confine', 'ubuntu-core-launcher']
881-COMMAND_REMOVE_SNAPD = 'apt purge -y {}'.format(' '.join(PACKAGES_SNAPD))
882-COMMAND_APT_BUILD_DEP = 'apt build-dep -y ./'
883-COMMAND_APT_INSTALL = 'apt install -y {}'
884-COMMAND_APT_INSTALL_STAGING_DEBS = COMMAND_APT_INSTALL.format(
885- ' '.join(['../{}*.deb'.format(pkg) for pkg in PACKAGES_SNAPD]))
886-COMMAND_APT_UPDATE = 'apt update'
887-COMMAND_APT_UPGRADE = 'apt dist-upgrade -y'
888-COMMAND_BUILD_SNAPD = 'DEB_BUILD_OPTIONS="nocheck testkeys" ' \
889- 'dpkg-buildpackage -tc -b'
890-COMMAND_EXPORT_STAGING_STORE_VAR = 'echo SNAPPY_USE_STAGING_STORE=1 >> ' \
891- '/etc/environment'
892-COMMAND_GET_GOVENDOR = 'go get -v github.com/kardianos/govendor'
893-COMMAND_GIT_CLONE = 'git clone {} {}'
894-COMMAND_GOVENDOR_SYNC = 'govendor sync'
895-REPOSITORY_GIT = 'https://github.com/snapcore/snapd'
896-URL_API_ROOT = 'https://myapps.developer.staging.ubuntu.com/dev/api/'
897-URL_SEARCH_ROOT = 'https://search.apps.staging.ubuntu.com/'
898-URL_UPLOAD_ROOT = 'https://upload.apps.staging.ubuntu.com/'
899-URL_SSO_API_ROOT = 'https://login.staging.ubuntu.com/api/v2/'
900-
901-CONTAINER_SETUP = [
902- COMMAND_REMOVE_SNAPD,
903- COMMAND_APT_UPDATE,
904- # Hold open-iscsi which is causing apt upgrade errors while running
905- # inside container.
906- 'apt-mark hold open-iscsi',
907- COMMAND_APT_UPGRADE,
908- # Needed for snaps to work inside a lxd container
909- # ref: https://stgraber.org/2016/12/07/running-snaps-in-lxd-containers/
910- COMMAND_APT_INSTALL.format('squashfuse'),
911- COMMAND_GIT_CLONE.format(REPOSITORY_GIT, DIRECTORY_CLONE),
912- {'command': COMMAND_APT_BUILD_DEP, 'cwd': DIRECTORY_CLONE},
913- {'command': COMMAND_GET_GOVENDOR, 'cwd': DIRECTORY_CLONE},
914- {'command': COMMAND_GOVENDOR_SYNC, 'cwd': DIRECTORY_CLONE},
915- {'command': COMMAND_BUILD_SNAPD, 'cwd': DIRECTORY_CLONE},
916- COMMAND_EXPORT_STAGING_STORE_VAR,
917- {'command': COMMAND_APT_INSTALL_STAGING_DEBS, 'cwd': DIRECTORY_CLONE}
918-]
919-
920-CONTAINER_ENV_VARS = {
921- 'GOPATH': CONTAINER_HOME,
922- 'SNAPPY_USE_STAGING_STORE': '1',
923- ENV_STORE_API_ROOT: URL_API_ROOT,
924- ENV_STORE_SEARCH_ROOT: URL_SEARCH_ROOT,
925- ENV_STORE_UPLOAD_ROOT: URL_UPLOAD_ROOT,
926- ENV_SSO_API_ROOT: URL_SSO_API_ROOT,
927-}
928-
929 LOGGER = logging.getLogger(__name__)
930
931
932
933=== modified file 'snappy_ecosystem_tests/helpers/store_apis/rest_apis.py'
934--- snappy_ecosystem_tests/helpers/store_apis/rest_apis.py 2017-02-17 18:41:32 +0000
935+++ snappy_ecosystem_tests/helpers/store_apis/rest_apis.py 2017-03-02 14:15:33 +0000
936@@ -45,6 +45,8 @@
937 from snappy_ecosystem_tests.helpers.snapcraft.options import ProjectOptions
938 from snappy_ecosystem_tests.helpers.store_apis import errors
939 from snappy_ecosystem_tests.helpers.store_apis.upload import upload_files
940+from snappy_ecosystem_tests.models.snap import Snap
941+from snappy_ecosystem_tests.utils.filters import filter_list
942
943 LOGGER = logging.getLogger(__name__)
944
945@@ -193,16 +195,28 @@
946 """Get current account information"""
947 return self._refresh_if_necessary(self.sca.get_account_information)
948
949+ def get_registered_snaps(self, series=constants.DEFAULT_SERIES):
950+ """Get registered snaps for current user.
951+ First refresh macaroon if necessary"""
952+ return self._refresh_if_necessary(self.sca.get_registered_snaps,
953+ series)
954+
955+ def filter_snaps(self, series=constants.DEFAULT_SERIES, **predicates):
956+ """Filter snaps that match with given predicates
957+ First refresh macaroon if necessary"""
958+ return self._refresh_if_necessary(self.sca.filter_snaps,
959+ series=series, **predicates)
960+
961 def register_key(self, account_key_request):
962 """Register a key for the current user"""
963 return self._refresh_if_necessary(
964 self.sca.register_key, account_key_request)
965
966- def register(self, snap_name, is_private=False):
967+ def register(self, snap_name, private=False):
968 """Register a package name for the current user
969 First refresh macaroon if necessary"""
970 return self._refresh_if_necessary(
971- self.sca.register, snap_name, is_private, constants.DEFAULT_SERIES)
972+ self.sca.register, snap_name, private, constants.DEFAULT_SERIES)
973
974 def push_precheck(self, snap_name):
975 """Do a snap push as a pre-check (dry_run: do not actually push)
976@@ -555,6 +569,36 @@
977 else:
978 raise errors.StoreAccountInformationError(response)
979
980+ def get_registered_snaps(self, series=constants.DEFAULT_SERIES):
981+ """
982+ Get the registered snaps for the current user
983+ :param series: get only snaps that match with this series
984+ :return: the list of registered snaps
985+ """
986+ account_info = self.get_account_information()
987+ user_snaps = account_info.get('snaps')
988+ snaps_dict = user_snaps.get(series)
989+ snaps = []
990+ for name, snap_data in snaps_dict.items():
991+ snaps.append(Snap(name=name, **snap_data))
992+ return snaps
993+
994+ def filter_snaps(self, series=constants.DEFAULT_SERIES, **predicates):
995+ """
996+ Return a list of the snaps that match with all the given predicates.
997+ :param: series: only look in the snaps that match with this series
998+ Examples:
999+ filter_snap() --> return all the snaps
1000+ filter_snap(name='my_name') --> return the snaps that matches with
1001+ name 'my_name'
1002+ filter_snap(name='my_name', private=True) --> return the snaps that
1003+ matches with name 'my_name' and are private.
1004+ filter_snap(price='5') --> return the snaps that with price '5'.
1005+ :return: A list of snap instances
1006+ """
1007+ return filter_list(self.get_registered_snaps(series=series),
1008+ **predicates)
1009+
1010 def register_key(self, account_key_request):
1011 """Register a key for a user with the given request
1012 :param account_key_request: the key to register
1013@@ -568,20 +612,21 @@
1014 if not response.ok:
1015 raise errors.StoreKeyRegistrationError(response)
1016
1017- def register(self, snap_name, is_private, series):
1018+ def register(self, snap_name, private, series):
1019 """Register a snap name in the store
1020 :param snap_name: the name to be registered
1021- :param is_private: whether the snap name is private or not
1022+ :param private: whether the snap name is private or not
1023 :param series: the snap's series to be registered
1024 """
1025 auth = _macaroon_auth(self.conf)
1026- data = dict(snap_name=snap_name, is_private=is_private,
1027+ data = dict(snap_name=snap_name, is_private=private,
1028 series=series)
1029 response = self.post(
1030 'register-name/', data=json.dumps(data),
1031- headers=dict({'Authorization': auth}, **JSON_ACCEPT))
1032+ headers=dict({'Authorization': auth}, **JSON_CONTENT_TYPE))
1033 if not response.ok:
1034 raise errors.StoreRegistrationError(snap_name, response)
1035+ return True
1036
1037 def snap_push_precheck(self, snap_name):
1038 """Do a snap push pre-check (dry_run: do not actually push)
1039
1040=== added directory 'snappy_ecosystem_tests/models'
1041=== added file 'snappy_ecosystem_tests/models/__init__.py'
1042--- snappy_ecosystem_tests/models/__init__.py 1970-01-01 00:00:00 +0000
1043+++ snappy_ecosystem_tests/models/__init__.py 2017-03-02 14:15:33 +0000
1044@@ -0,0 +1,19 @@
1045+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1046+
1047+#
1048+# Snappy Ecosystem Tests
1049+# Copyright (C) 2017 Canonical
1050+#
1051+# This program is free software: you can redistribute it and/or modify
1052+# it under the terms of the GNU General Public License as published by
1053+# the Free Software Foundation, either version 3 of the License, or
1054+# (at your option) any later version.
1055+#
1056+# This program is distributed in the hope that it will be useful,
1057+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1058+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1059+# GNU General Public License for more details.
1060+#
1061+# You should have received a copy of the GNU General Public License
1062+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1063+#
1064
1065=== added file 'snappy_ecosystem_tests/models/snap.py'
1066--- snappy_ecosystem_tests/models/snap.py 1970-01-01 00:00:00 +0000
1067+++ snappy_ecosystem_tests/models/snap.py 2017-03-02 14:15:33 +0000
1068@@ -0,0 +1,39 @@
1069+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1070+
1071+#
1072+# Snappy Ecosystem Tests
1073+# Copyright (C) 2017 Canonical
1074+#
1075+# This program is free software: you can redistribute it and/or modify
1076+# it under the terms of the GNU General Public License as published by
1077+# the Free Software Foundation, either version 3 of the License, or
1078+# (at your option) any later version.
1079+#
1080+# This program is distributed in the hope that it will be useful,
1081+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1082+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1083+# GNU General Public License for more details.
1084+#
1085+# You should have received a copy of the GNU General Public License
1086+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1087+#
1088+
1089+"""Business model classes for snaps"""
1090+
1091+
1092+class Snap:
1093+ """Represents a snap entity."""
1094+
1095+ def __init__(self, name='', price=None, private=False, since=None,
1096+ _id=None, status=None, notes=None, **kwargs):
1097+ self.name = name
1098+ self.price = price
1099+ self.private = private
1100+ self.since = since
1101+ self._id = _id or kwargs.get('snap-id')
1102+ self.status = status
1103+ self.notes = notes
1104+
1105+ def is_private(self):
1106+ """Return True if the snap is private, False otherwise"""
1107+ return self.private
1108
1109=== modified file 'snappy_ecosystem_tests/run.py'
1110--- snappy_ecosystem_tests/run.py 2017-02-15 19:14:19 +0000
1111+++ snappy_ecosystem_tests/run.py 2017-03-02 14:15:33 +0000
1112@@ -20,28 +20,38 @@
1113
1114 """Entry point to run snappy ecosystem tests"""
1115
1116+import argparse
1117+import os
1118 import re
1119 import sys
1120 import pytest
1121
1122-FILTER_ARGUMENTS = '--proxy'
1123-
1124-
1125-def _filter_arguments():
1126- """Remove from command line arguments all the ones that are in
1127- FILTER_ARGUMENTS list."""
1128- filtered_args = sys.argv[1:]
1129- for i, arg in enumerate(sys.argv[1:]):
1130- if arg in FILTER_ARGUMENTS:
1131- filtered_args.pop(i + 1)
1132- filtered_args.pop(i)
1133- return filtered_args
1134-
1135
1136 def _parse_arguments():
1137 """Parse and filter command line arguments to makethem pytest compatible"""
1138 sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
1139- return _filter_arguments()
1140+ parser = argparse.ArgumentParser(description=
1141+ 'Run snappy ecosystem tests.')
1142+ parser.add_argument('--proxy',
1143+ help='proxy to use for creating the virtual env.',
1144+ required=False)
1145+ parser.add_argument('--target', default="lxd",
1146+ help='Target on which tests will be executed.')
1147+ ecosystem_args, pytest_args = parser.parse_known_args()
1148+ _set_env_variables(ecosystem_args)
1149+ return pytest_args
1150+
1151+
1152+def _set_env_variables(args):
1153+ """
1154+ Set environment variables with ecosystem arguments passed
1155+ in the command line.
1156+ :param args: The arguments to store in env variables
1157+ """
1158+ for arg in vars(args):
1159+ value = getattr(args, arg, None)
1160+ if value:
1161+ os.environ[arg] = value
1162
1163 if __name__ == '__main__':
1164 sys.exit(pytest.main(args=_parse_arguments() + ['-p', 'no:cacheprovider']))
1165
1166=== added file 'snappy_ecosystem_tests/tests/test_register_snap.py'
1167--- snappy_ecosystem_tests/tests/test_register_snap.py 1970-01-01 00:00:00 +0000
1168+++ snappy_ecosystem_tests/tests/test_register_snap.py 2017-03-02 14:15:33 +0000
1169@@ -0,0 +1,116 @@
1170+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1171+
1172+#
1173+# Snappy Ecosystem Tests
1174+# Copyright (C) 2017 Canonical
1175+#
1176+# This program is free software: you can redistribute it and/or modify
1177+# it under the terms of the GNU General Public License as published by
1178+# the Free Software Foundation, either version 3 of the License, or
1179+# (at your option) any later version.
1180+#
1181+# This program is distributed in the hope that it will be useful,
1182+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1183+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1184+# GNU General Public License for more details.
1185+#
1186+# You should have received a copy of the GNU General Public License
1187+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1188+#
1189+
1190+"""Tests for Registering a snap name in the store"""
1191+
1192+import uuid
1193+
1194+from snappy_ecosystem_tests.helpers.snapcraft.client import Snapcraft
1195+from snappy_ecosystem_tests.helpers.store_apis.rest_apis import Store
1196+from snappy_ecosystem_tests.helpers.test_base import SnappyEcosystemTestCase
1197+from snappy_ecosystem_tests.utils.storeconfig import get_store_credentials
1198+
1199+
1200+class RegisterSnapNameTestCase(SnappyEcosystemTestCase):
1201+
1202+ def setUp(self):
1203+ super().setUp()
1204+ self.store = Store()
1205+ self.snapcraft = Snapcraft()
1206+ self.store.login(*get_store_credentials())
1207+ self.addCleanup(self.store.logout)
1208+ self.snapcraft.login()
1209+ self.addCleanup(self.snapcraft.logout)
1210+
1211+ @staticmethod
1212+ def _get_random_snap_name(prefix='ecosystem-test'):
1213+ """Get a random snap name using uuid
1214+ :param prefix: the string prefix,
1215+ if not provided, use default one.
1216+ """
1217+ return '{}{}'.format(prefix, int(uuid.uuid4()))
1218+
1219+ def test_register_public_snap_name_using_snapcraft(self):
1220+ """Test register a public snap name via snapcraft
1221+ and verify it via REST API"""
1222+ # Register snap via Snapcraft
1223+ snap_name = self._get_random_snap_name()
1224+ self.assertTrue(self.snapcraft.register(snap_name),
1225+ 'Unable to register snap')
1226+
1227+ # Verify the registered snap via REST API
1228+ snaps = self.store.filter_snaps(name=snap_name)
1229+ self.assertEqual(1, len(snaps),
1230+ 'Expected 1 snap with name %s but found %s'
1231+ % (snap_name, len(snaps)))
1232+ snap = snaps[0]
1233+ self.assertEqual(snap_name, snap.name)
1234+ self.assertFalse(snap.is_private())
1235+
1236+ def test_register_private_snap_name_using_snapcraft(self):
1237+ """Test register a private snap name via snapcraft
1238+ and verify it via REST API"""
1239+ # Register snap via Snapcraft
1240+ snap_name = self._get_random_snap_name()
1241+ self.assertTrue(self.snapcraft.register(snap_name, private=True),
1242+ 'Unable to register snap')
1243+
1244+ # Verify the registered snap via REST API
1245+ snaps = self.store.filter_snaps(name=snap_name)
1246+ self.assertEqual(1, len(snaps),
1247+ 'Expected 1 snap with name %s but found %s'
1248+ % (snap_name, len(snaps)))
1249+ snap = snaps[0]
1250+ self.assertEqual(snap_name, snap.name)
1251+ self.assertTrue(snap.is_private())
1252+
1253+ def test_register_public_snap_name_using_api(self):
1254+ """Verify that snapcraft lists a new snap registered via
1255+ RESTful API when using the command snapcraft list-registered"""
1256+ # Register snap via API
1257+ snap_name = self._get_random_snap_name()
1258+ self.assertTrue(self.store.register(snap_name),
1259+ 'Unable to register snap')
1260+
1261+ # Verify the registered snap via Snapcraft
1262+ snaps = self.snapcraft.filter_snaps(name=snap_name)
1263+ self.assertEqual(1, len(snaps),
1264+ 'Expected 1 snap with name %s but found %s'
1265+ % (snap_name, len(snaps)))
1266+ snap = snaps[0]
1267+ self.assertEqual(snap_name, snap.name)
1268+ self.assertFalse(snap.is_private())
1269+
1270+ def test_register_private_snap_name_using_api(self):
1271+ """Verify that snapcraft lists a new private snap registered via
1272+ RESTful API when using the command snapcraft list-registered"""
1273+ # Register snap via API
1274+ snap_name = self._get_random_snap_name()
1275+ self.assertTrue(self.store.register(snap_name, private=True),
1276+ 'Unable to register snap')
1277+
1278+ # Verify the registered snap via Snapcraft
1279+ snaps = self.snapcraft.filter_snaps(name=snap_name)
1280+ self.assertEqual(1, len(snaps),
1281+ 'Expected 1 snap with name %s but found %s'
1282+ % (snap_name, len(snaps)))
1283+ snap = snaps[0]
1284+ self.assertEqual(snap_name, snap.name)
1285+ self.assertTrue(snap.is_private())
1286
1287=== added file 'snappy_ecosystem_tests/unittests/test_filters.py'
1288--- snappy_ecosystem_tests/unittests/test_filters.py 1970-01-01 00:00:00 +0000
1289+++ snappy_ecosystem_tests/unittests/test_filters.py 2017-03-02 14:15:33 +0000
1290@@ -0,0 +1,113 @@
1291+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1292+
1293+#
1294+# Snappy Ecosystem Tests
1295+# Copyright (C) 2017 Canonical
1296+#
1297+# This program is free software: you can redistribute it and/or modify
1298+# it under the terms of the GNU General Public License as published by
1299+# the Free Software Foundation, either version 3 of the License, or
1300+# (at your option) any later version.
1301+#
1302+# This program is distributed in the hope that it will be useful,
1303+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1304+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1305+# GNU General Public License for more details.
1306+#
1307+# You should have received a copy of the GNU General Public License
1308+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1309+#
1310+
1311+"""Unit tests cases for filter utils"""
1312+
1313+from testtools import (
1314+ TestCase,
1315+ ExpectedException
1316+)
1317+
1318+from snappy_ecosystem_tests.utils.filters import (
1319+ object_filter,
1320+ filter_list
1321+)
1322+
1323+
1324+class DummyObject: # pylint: disable=too-few-public-methods
1325+ """Dummy object to be used in unit tests"""
1326+
1327+ def __init__(self, **kwargs):
1328+ for _k, _v in kwargs.items():
1329+ setattr(self, _k, _v)
1330+
1331+
1332+class FilterTestCase(TestCase):
1333+
1334+ def setUp(self):
1335+ super().setUp()
1336+ self.obj = DummyObject(name='dummy', desc='this is dummy')
1337+ self.obj_2 = DummyObject(name='another-dummy',
1338+ desc='this is another dummy')
1339+ self.objects = [self.obj, self.obj_2]
1340+
1341+ def test_object_filter_with_single_argument(self):
1342+ """Filter successfully passing one argument"""
1343+ objects = list(filter(object_filter(name='dummy'), self.objects))
1344+ self.assertIsNotNone(objects)
1345+ self.assertEqual(len(objects), 1)
1346+ self.assertEqual(objects[0], self.obj)
1347+
1348+ def test_object_filter_with_multiple_arguments(self):
1349+ """Filter successfully passing two arguments"""
1350+ objects = list(filter(object_filter(name='dummy',
1351+ desc='this is dummy'),
1352+ self.objects))
1353+ self.assertIsNotNone(objects)
1354+ self.assertEqual(len(objects), 1)
1355+ self.assertEqual(objects[0], self.obj)
1356+
1357+ def test_object_filter_does_not_match(self):
1358+ """Return empty list when no object match criteria"""
1359+ objects = list(filter(object_filter(name='dummy',
1360+ desc='any desc'),
1361+ self.objects))
1362+ self.assertEqual([], objects)
1363+
1364+ def test_object_filter_return_all(self):
1365+ """Get all objects when no filter is provided"""
1366+ objects = list(filter(object_filter(), self.objects))
1367+ self.assertEqual(len(objects), len(self.objects))
1368+
1369+ def test_object_filter_raises_attribute_error_if_invalid_filter_name(self):
1370+ """Object filter raises AttributeError when invalid filter
1371+ is provided"""
1372+ with ExpectedException(AttributeError):
1373+ list(filter(object_filter(invalid='dummy'), self.objects))
1374+
1375+ def test_filter_list_with_single_argument(self):
1376+ """Filter List successfully passing one argument"""
1377+ objects = filter_list(self.objects, name='dummy')
1378+ self.assertIsNotNone(objects)
1379+ self.assertEqual(len(objects), 1)
1380+ self.assertEqual(objects[0], self.obj)
1381+
1382+ def test_filter_list_with_multiple_arguments(self):
1383+ """Filter List successfully passing multiple argument"""
1384+ objects = filter_list(self.objects, name='another-dummy',
1385+ desc='this is another dummy')
1386+ self.assertIsNotNone(objects)
1387+ self.assertEqual(len(objects), 1)
1388+ self.assertEqual(objects[0], self.obj_2)
1389+
1390+ def test_filter_list_does_not_match(self):
1391+ """Filter List returns empty list when no object match criteria"""
1392+ objects = filter_list(self.objects, desc='desc does not match')
1393+ self.assertEqual([], objects)
1394+
1395+ def test_filter_list_return_all(self):
1396+ """Filter list returns all objects when no filter is provided"""
1397+ objects = filter_list(self.objects)
1398+ self.assertEqual(len(objects), len(self.objects))
1399+
1400+ def test_filter_list_raises_attribute_error_if_invalid_filter_name(self):
1401+ """Filter list raises AttributeError when invalid filter is provided"""
1402+ with ExpectedException(AttributeError):
1403+ filter_list(self.objects, invalid='invalid filter')
1404
1405=== removed file 'snappy_ecosystem_tests/unittests/test_snapcraft.py'
1406--- snappy_ecosystem_tests/unittests/test_snapcraft.py 2017-02-13 16:00:04 +0000
1407+++ snappy_ecosystem_tests/unittests/test_snapcraft.py 1970-01-01 00:00:00 +0000
1408@@ -1,31 +0,0 @@
1409-# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1410-
1411-#
1412-# Snappy Ecosystem Tests
1413-# Copyright (C) 2017 Canonical
1414-#
1415-# This program is free software: you can redistribute it and/or modify
1416-# it under the terms of the GNU General Public License as published by
1417-# the Free Software Foundation, either version 3 of the License, or
1418-# (at your option) any later version.
1419-#
1420-# This program is distributed in the hope that it will be useful,
1421-# but WITHOUT ANY WARRANTY; without even the implied warranty of
1422-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1423-# GNU General Public License for more details.
1424-#
1425-# You should have received a copy of the GNU General Public License
1426-# along with this program. If not, see <http://www.gnu.org/licenses/>.
1427-#
1428-
1429-"""Unit tests cases for snapcraft"""
1430-
1431-from unittest2 import TestCase
1432-
1433-
1434-class SnapcraftTestCase(TestCase):
1435-
1436- def test_snapcraft_dummy_test(self):
1437- """Dummy test case"""
1438- _a = True
1439- self.assertTrue(_a)
1440
1441=== added file 'snappy_ecosystem_tests/utils/commands.py'
1442--- snappy_ecosystem_tests/utils/commands.py 1970-01-01 00:00:00 +0000
1443+++ snappy_ecosystem_tests/utils/commands.py 2017-03-02 14:15:33 +0000
1444@@ -0,0 +1,31 @@
1445+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1446+
1447+#
1448+# Snappy Ecosystem Tests
1449+# Copyright (C) 2017 Canonical
1450+#
1451+# This program is free software: you can redistribute it and/or modify
1452+# it under the terms of the GNU General Public License as published by
1453+# the Free Software Foundation, either version 3 of the License, or
1454+# (at your option) any later version.
1455+#
1456+# This program is distributed in the hope that it will be useful,
1457+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1458+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1459+# GNU General Public License for more details.
1460+#
1461+# You should have received a copy of the GNU General Public License
1462+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1463+#
1464+
1465+"""Manage commands to be executed in a shell"""
1466+
1467+
1468+def build_command(command, *args):
1469+ """
1470+ Build a command with arguments
1471+ :param command: the executable that is going to be used
1472+ :param args: list of arguments for the command
1473+ :return: the command ready to be executed
1474+ """
1475+ return'{c} {a}'.format(c=command, a=' '.join(args)).strip()
1476
1477=== added file 'snappy_ecosystem_tests/utils/filters.py'
1478--- snappy_ecosystem_tests/utils/filters.py 1970-01-01 00:00:00 +0000
1479+++ snappy_ecosystem_tests/utils/filters.py 2017-03-02 14:15:33 +0000
1480@@ -0,0 +1,51 @@
1481+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1482+
1483+#
1484+# Snappy Ecosystem Tests
1485+# Copyright (C) 2017 Canonical
1486+#
1487+# This program is free software: you can redistribute it and/or modify
1488+# it under the terms of the GNU General Public License as published by
1489+# the Free Software Foundation, either version 3 of the License, or
1490+# (at your option) any later version.
1491+#
1492+# This program is distributed in the hope that it will be useful,
1493+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1494+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1495+# GNU General Public License for more details.
1496+#
1497+# You should have received a copy of the GNU General Public License
1498+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1499+#
1500+
1501+"""Filter utils"""
1502+
1503+
1504+def object_filter(**predicates):
1505+ """
1506+ Higher-order function to build and return an object filter function
1507+ :param predicates: key/values that represent
1508+ the conditions to look for in the object instance.
1509+ :return: a function to be used with filter or lambda expressions.
1510+ :raise: AttributeError if any of the attributes provided in predicates
1511+ does not exists
1512+ """
1513+ def wrapped_filter(instance):
1514+ """wrapped function to be returned when calling object_filter"""
1515+ return all(getattr(instance,
1516+ param) == val for param, val in predicates.items())
1517+ return wrapped_filter
1518+
1519+
1520+def filter_list(objects_list, **predicates):
1521+ """
1522+ Filter a list of objects with the given predicates
1523+ :param objects_list: the target list that contains
1524+ the objects to be filtered
1525+ :param predicates: key/values that represent
1526+ the conditions to look for in the object list.
1527+ :return: A list with the objects that match all the predicates
1528+ :raise: AttributeError if any of the attributes provided in predicates
1529+ does not exists
1530+ """
1531+ return list(filter(object_filter(**predicates), objects_list))
1532
1533=== modified file 'snappy_ecosystem_tests/utils/ssh.py'
1534--- snappy_ecosystem_tests/utils/ssh.py 2017-02-22 18:56:04 +0000
1535+++ snappy_ecosystem_tests/utils/ssh.py 2017-03-02 14:15:33 +0000
1536@@ -22,7 +22,7 @@
1537
1538 import paramiko
1539
1540-from snappy_ecosystem_tests.utils.user import get_remote_host_credentials
1541+from snappy_ecosystem_tests.utils.user import get_snapd_remote_host_credentials
1542
1543
1544 class SSHManager:
1545@@ -83,7 +83,9 @@
1546 :raises ValueError: if command exits with non-zero status.
1547 :return: the stdout of the command.
1548 """
1549- _hostname, _username, _port = get_remote_host_credentials()
1550+ # Take snapd remote credentials as default if they are not provided.
1551+ # Just arbitrary... Could have been other ones.
1552+ _hostname, _username, _port = get_snapd_remote_host_credentials()
1553 ssh = SSHManager.get_instance(
1554 hostname or _hostname, username or _username, port or _port)
1555 _, stdout, stderr = ssh.exec_command(command)
1556
1557=== added file 'snappy_ecosystem_tests/utils/store_versions.py'
1558--- snappy_ecosystem_tests/utils/store_versions.py 1970-01-01 00:00:00 +0000
1559+++ snappy_ecosystem_tests/utils/store_versions.py 2017-03-02 14:15:33 +0000
1560@@ -0,0 +1,74 @@
1561+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
1562+
1563+#
1564+# Snappy Ecosystem Tests
1565+# Copyright (C) 2017 Canonical
1566+#
1567+# This program is free software: you can redistribute it and/or modify
1568+# it under the terms of the GNU General Public License as published by
1569+# the Free Software Foundation, either version 3 of the License, or
1570+# (at your option) any later version.
1571+#
1572+# This program is distributed in the hope that it will be useful,
1573+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1574+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1575+# GNU General Public License for more details.
1576+#
1577+# You should have received a copy of the GNU General Public License
1578+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1579+#
1580+
1581+"""Utility for printing Store versions of: CPI, SCA and SSO.
1582+To be called as a standalone script.
1583+"""
1584+
1585+import requests
1586+
1587+DEFAULT_HEADER = "X-BZR-REVISION-NUMBER"
1588+
1589+SERVICES_INFO = dict(
1590+ CPI=('https://search.apps.ubuntu.com/api/v1',
1591+ 'https://search.apps.staging.ubuntu.com/api/v1'),
1592+ SCA=('https://myapps.developer.ubuntu.com/',
1593+ 'https://myapps.developer.staging.ubuntu.com/'),
1594+ SSO=('https://login.ubuntu.com/',
1595+ 'https://login.staging.ubuntu.com/'),
1596+)
1597+
1598+
1599+def get_deployed_revno(service, environment):
1600+ """Get revno from service."""
1601+ prod, stag = SERVICES_INFO[service]
1602+ if environment == 'staging':
1603+ url = stag
1604+ else:
1605+ url = prod
1606+ with requests.Session() as session:
1607+ resp = session.get(url)
1608+ if resp.status_code == 200:
1609+ revno = resp.headers[DEFAULT_HEADER]
1610+ else:
1611+ print("{} 500'ed: {}".format(service, resp.text))
1612+ revno = 0
1613+ return service, environment, revno
1614+
1615+
1616+def print_versions():
1617+ """Print Store components version"""
1618+ coros = []
1619+ for service in SERVICES_INFO:
1620+ coro = get_deployed_revno(service, 'staging')
1621+ coros.append(coro)
1622+ coro = get_deployed_revno(service, 'production')
1623+ coros.append(coro)
1624+
1625+ data = {(service, kind): value for service, kind, value in coros}
1626+ print("#########################-STORE VERSIONS-#########################")
1627+ for service in sorted(SERVICES_INFO):
1628+ print("{}: Staging: {:>5}".format(service,
1629+ data[(service, 'staging')]))
1630+ print("{}: Production: {:>5}".format(service,
1631+ data[(service, 'production')]))
1632+
1633+if __name__ == '__main__':
1634+ print_versions()
1635
1636=== modified file 'snappy_ecosystem_tests/utils/storeconfig.py'
1637--- snappy_ecosystem_tests/utils/storeconfig.py 2017-02-15 19:14:19 +0000
1638+++ snappy_ecosystem_tests/utils/storeconfig.py 2017-03-02 14:15:33 +0000
1639@@ -27,8 +27,8 @@
1640 USER_CONFIG_STACK
1641 )
1642
1643-URL_WEB_STORE_PRODUCTION = CONFIG_STACK.get('web-ui', 'production_url')
1644-URL_WEB_STORE_STAGING = CONFIG_STACK.get('web-ui', 'stage_url')
1645+URL_WEB_STORE_PRODUCTION = CONFIG_STACK.get('production_urls', 'web')
1646+URL_WEB_STORE_STAGING = CONFIG_STACK.get('staging_urls', 'web')
1647
1648
1649 def get_store_credentials():
1650
1651=== modified file 'snappy_ecosystem_tests/utils/user.py'
1652--- snappy_ecosystem_tests/utils/user.py 2017-02-22 15:39:40 +0000
1653+++ snappy_ecosystem_tests/utils/user.py 2017-03-02 14:15:33 +0000
1654@@ -23,13 +23,34 @@
1655 import os
1656
1657 from snappy_ecosystem_tests.commons.config import USER_CONFIG_STACK
1658-
1659-
1660-def get_remote_host_credentials():
1661- """Return credentials for remote machine, to run commands on."""
1662- return (USER_CONFIG_STACK.get('user', 'hostname_remote',
1663- default=os.environ.get('hostname_remote')),
1664- USER_CONFIG_STACK.get('user', 'username_remote',
1665- default=os.environ.get('username_remote')),
1666- USER_CONFIG_STACK.get('user', 'port_remote',
1667- default=os.environ.get('port_remote')))
1668+from snappy_ecosystem_tests.environment.managers import get_manager
1669+
1670+MANAGER = get_manager(os.environ['target'])
1671+
1672+
1673+def get_snapd_remote_host_credentials():
1674+ """Return credentials for snapd remote machine, to run commands on."""
1675+ _ip = MANAGER.get_ip(USER_CONFIG_STACK.get(
1676+ 'user', 'snapd_hostname_remote',
1677+ default=os.environ.get('snapd_hostname_remote')))
1678+ return (_ip,
1679+ USER_CONFIG_STACK.get('user', 'snapd_username_remote',
1680+ default=os.environ.get(
1681+ 'snapd_username_remote')),
1682+ USER_CONFIG_STACK.get('user', 'snapd_port_remote',
1683+ default=os.environ.get(
1684+ 'snapd_port_remote')))
1685+
1686+
1687+def get_snapcraft_remote_host_credentials():
1688+ """Return credentials for snapcraft remote machine, to run commands on."""
1689+ _ip = MANAGER.get_ip(USER_CONFIG_STACK.get(
1690+ 'user', 'snapcraft_hostname_remote',
1691+ default=os.environ.get('snapcraft_snapcraft_remote')))
1692+ return (_ip,
1693+ USER_CONFIG_STACK.get('user', 'snapcraft_username_remote',
1694+ default=os.environ.get(
1695+ 'snapcraft_username_remote')),
1696+ USER_CONFIG_STACK.get('user', 'snapcraft_port_remote',
1697+ default=os.environ.get(
1698+ 'snapcraft_port_remote')))

Subscribers

People subscribed via source and target branches