Merge lp:~canonical-platform-qa/snappy-ecosystem-tests/register-snap-tests into lp:snappy-ecosystem-tests
- register-snap-tests
- Merge into trunk
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 |
Related bugs: |
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:/
https:/
https:/
https:/
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_
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:19
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
I Ahmad (iahmad) wrote : Posted in a previous version of this proposal | # |
minor inline comment about test name otherwise LGTM
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:20
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
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.
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:20
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:21
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:23
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:25
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:26
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:27
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:28
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
I Ahmad (iahmad) wrote : Posted in a previous version of this proposal | # |
Getting these errors when tried to run locally http://
Heber Parrucci (heber013) wrote : Posted in a previous version of this proposal | # |
> Getting these errors when tried to run locally
> http://
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/
[user]
user_email=
user_password=
hostname_
username_
port_remote=22
And replace in snappy_
http://
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.
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:29
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
I Ahmad (iahmad) wrote : Posted in a previous version of this proposal | # |
> > Getting these errors when tried to run locally
> > http://
>
> 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/
> [user]
> user_email=
> user_password=
> hostname_
> username_
> port_remote=22
>
> And replace in snappy_
> by:
> http://
>
> 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.
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:30
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
I Ahmad (iahmad) : Posted in a previous version of this proposal | # |
Omer Akram (om26er) wrote : Posted in a previous version of this proposal | # |
added minor suggestion.
Omer Akram (om26er) wrote : Posted in a previous version of this proposal | # |
Added another comment/suggestion.
Heber Parrucci (heber013) wrote : Posted in a previous version of this proposal | # |
Reply inline Thanks!
Heber Parrucci (heber013) wrote : Posted in a previous version of this proposal | # |
Comment addressed
platform-qa-bot (platform-qa-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:31
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : | # |
PASSED: Continuous integration, rev:31
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 32. By Heber Parrucci
-
merge from trunk
platform-qa-bot (platform-qa-bot) wrote : | # |
PASSED: Continuous integration, rev:32
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 33. By Omer Akram
-
merge with trunk
platform-qa-bot (platform-qa-bot) wrote : | # |
PASSED: Continuous integration, rev:33
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
I Ahmad (iahmad) wrote : | # |
LGTM, please go ahead with landing.
Omer Akram (om26er) wrote : | # |
added a comment. You seem to be using pexect on the host.
Omer Akram (om26er) wrote : | # |
a few more inline comments.
platform-qa-bot (platform-qa-bot) : | # |
Heber Parrucci (heber013) wrote : | # |
Thanks for reviewing! Will fix the bug separately.
Preview Diff
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'))) |
FAILED: Continuous integration, rev:18 /platform- qa-jenkins. ubuntu. com/job/ snappy- ecosystem- tests-ci/ 65/ /platform- qa-jenkins. ubuntu. com/job/ generic- update- mp/1652/ console
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild: /platform- qa-jenkins. ubuntu. com/job/ snappy- ecosystem- tests-ci/ 65/rebuild
https:/