Merge lp:~canonical-platform-qa/snappy-ecosystem-tests/snapd-staging-build-script into lp:snappy-ecosystem-tests
- snapd-staging-build-script
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Heber Parrucci |
Approved revision: | 33 |
Merged at revision: | 22 |
Proposed branch: | lp:~canonical-platform-qa/snappy-ecosystem-tests/snapd-staging-build-script |
Merge into: | lp:snappy-ecosystem-tests |
Diff against target: |
332 lines (+294/-2) 5 files modified
README.rst (+16/-0) pylint.cfg (+1/-1) requirements.txt (+2/-1) snappy_ecosystem_tests/helpers/snapd/staging_builder.py (+150/-0) snappy_ecosystem_tests/utils/lxd.py (+125/-0) |
To merge this branch: | bzr merge lp:~canonical-platform-qa/snappy-ecosystem-tests/snapd-staging-build-script |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Heber Parrucci (community) | Approve | ||
platform-qa-bot | continuous-integration | Approve | |
Santiago Baldassin (community) | Abstain | ||
I Ahmad (community) | Approve | ||
Review via email: mp+316514@code.launchpad.net |
Commit message
script: build snapd in lxd container to enable staging servers
Description of the change
script: build snapd in lxd container to enable staging servers
Omer Akram (om26er) wrote : | # |
I Ahmad (iahmad) wrote : | # |
Could you please add some instructions about how to use it, as well as if it is standalone utility then add these instructions to the README file.
Omer Akram (om26er) wrote : | # |
Updated the branch with some cleanups and also added instructions on how to setup the environment.
Please make sure to have all updates applied to Ubuntu 16.04 host when running.
Omer Akram (om26er) wrote : | # |
I initially wrote this script in bash but later ported it to python. So if anyone interested in the shell version look here: https:/
I Ahmad (iahmad) wrote : | # |
Go ahead with merge, we will know if any issues or improvements needed once test cases are added.
platform-qa-bot (platform-qa-bot) : | # |
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
None: https:/
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:8
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Santiago Baldassin (sbaldassin) wrote : | # |
Minor comments inline plus if we really want to move from bash to python then maybe we should take advantage of the available python libraries and replace the subprocess calls with python calls
- 9. By Omer Akram
-
reduce duplicated call
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:9
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Omer Akram (om26er) wrote : | # |
Thanks for the review, I have replied to your comments
- 10. By Omer Akram
-
merge with trunk
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:10
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Santiago Baldassin (sbaldassin) wrote : | # |
Replies inline. Thanks
- 11. By Omer Akram
-
Replace lxd shell calls with pylxd
- 12. By Omer Akram
-
Merge with trunk
- 13. By Omer Akram
-
remove logging config, to be set by the test runner
- 14. By Omer Akram
-
better worded errors
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:12
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:14
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Omer Akram (om26er) wrote : | # |
Added replies and pushed changes.
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:14
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 15. By Omer Akram
-
add more docstrings, make pylint happier
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:15
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Santiago Baldassin (sbaldassin) wrote : | # |
Hi Omer, I still find this confusing. Starting with a Container class that has a container attribute. I also don't understand why we redefine methods like start, stop, delete, etc instead of calling the ones provided by the library. I've re-written this just as an example of what I mean: https:/
- 16. By Omer Akram
-
Incorporate changes suggested by sbalda, more improvements
Omer Akram (om26er) wrote : | # |
I updated the approach based on some of the suggestions in your paste. I still kept a class that inherits from pylxd.models.
I have also addressed some of your concerns as well.
Note: there is an outstanding issue with inheritance of Container class, I have sent a query to pylxd upstream developers for that.
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:16
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:16
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 17. By Omer Akram
-
address pylint errors
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:17
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Santiago Baldassin (sbaldassin) wrote : | # |
Looks better to me, however, there's an issue to be fixed.
By the way, I think the inheritance implemented in this mp is causing the issue in pylxd: https:/
If you tried the same sequence of events but without recreating the object, it is working just fine:
https:/
Last but not least, if you want to redefine the Container methods, like exec or delete, you'll have to do it in pylxd because they'll always use their class to create the objects. Another option is to move those methods to the builder and use the Container class provided by pylxd
- 18. By Omer Akram
-
Container inheritance is broken, move to delegation
Omer Akram (om26er) wrote : | # |
Thanks for the review, I have pushed the updated code. I have moved away from inheritance as that's broken upstream. The approach in the pull request that I created was rejected by upstream.
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:18
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Santiago Baldassin (sbaldassin) wrote : | # |
Minor comments inline. I'm abstaining from now since I still think that the way this is modeled is not right. Maybe someone else could chime in here
Omer Akram (om26er) wrote : | # |
Replied inline. I believe there is nothing wrong with the current approach, we are just trying to extend the pylxd.models.
Santiago Baldassin (sbaldassin) wrote : | # |
Replies inline
Santiago Baldassin (sbaldassin) wrote : | # |
By the way, the current approach forces you to keep two instances of the same container created by pylxd
Heber Parrucci (heber013) wrote : | # |
Comments inline. Thanks!
- 19. By Omer Akram
-
Remove custom exception
- 20. By Omer Akram
-
pylint happy
- 21. By Omer Akram
-
merge with trunk
- 22. By Omer Akram
-
make pylint finally super happy
Omer Akram (om26er) wrote : | # |
Thanks heber, replied inline and also pylint should be happy now.
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:22
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Heber Parrucci (heber013) wrote : | # |
Reply inline
- 23. By Omer Akram
-
Some sanity checks to Container code, improve project structure
- 24. By Omer Akram
-
Update readme
- 25. By Omer Akram
-
Remove delegate updation code
Omer Akram (om26er) wrote : | # |
I have made some structural changes to the code and replied inline.
Omer Akram (om26er) wrote : | # |
Santiago, added replies.
- 26. By Omer Akram
-
Add message on missing commandline arguments for snapd builder script
- 27. By Omer Akram
-
ignore pylint errors as its not able to find members of the class as they are dynamic
Santiago Baldassin (sbaldassin) wrote : | # |
Minor comment inline
- 28. By Omer Akram
-
Replace sys.argv with argparse
- 29. By Omer Akram
-
pylint happy
Omer Akram (om26er) wrote : | # |
This branch is 10 days old and I would now expect that we land it instead of more nitpicks.
Santiago Baldassin (sbaldassin) wrote : | # |
Minor comment inline. By the way, please do not expect a branch to be merged based on the time it has been for out there for review, which is not a valid reason to merge a change.
Based on the previous comments/
Heber Parrucci (heber013) wrote : | # |
Comments inline.
One thing, I was getting an error when the sh creates the virtual env:
http://
It is because "pylxd" depends on "cryptography" and it has a ssl extention. I solved the problem by installing OpenSSL development package manually:
$ sudo apt-get install libssl-dev
How do we solve this issue automatically?
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:29
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Omer Akram (om26er) wrote : | # |
Heber, replied inline. Not sure how we can fix that error. Probably instead of virtualenv we could resort to a container, then installing that dep would be plain and simple and won't pollute anything else.
Omer Akram (om26er) wrote : | # |
Santiago, replied in line.
If we really want a code to work we have to land it :) Playing around in circles does not help. This branch was initially ported from raw lxd commands to pylxd with no concrete benefits and now after all the reasons were provided on why the Container class looks the way it is because inheritance is not supported by upstream and current approach is very natural for classes that cannot be inherited, it feels kind of like "my way or the highway" ;)
Heber Parrucci (heber013) wrote : | # |
Comments inline
- 30. By Omer Akram
-
remove pylint suppress thats not needed
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:30
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Omer Akram (om26er) wrote : | # |
Heber, replied inline.
Santiago Baldassin (sbaldassin) wrote : | # |
> Santiago, replied in line.
>
> If we really want a code to work we have to land it :) Playing around in
With all do respect, I strongly disagree. We do NOT have to land code that we think is not ready to be landed just because we want the code to work.
> circles does not help. This branch was initially ported from raw lxd commands
> to pylxd with no concrete benefits and now after all the reasons were provided
Using pylxd does have concrete benefits on the long term, that's why we decided to use it. If you think that the previous approach is the right one, I'd be happy to revisit the approach once again
> on why the Container class looks the way it is because inheritance is not
> supported by upstream and current approach is very natural for classes that
> cannot be inherited, it feels kind of like "my way or the highway" ;)
I'm sorry Homer but Heber and myself clearly expressed that we'd preferred a different approach and yet you intend to get this merged
Santiago Baldassin (sbaldassin) wrote : | # |
Reply inline
Omer Akram (om26er) wrote : | # |
minor query inline.
Santiago Baldassin (sbaldassin) wrote : | # |
reply inline
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:30
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 31. By Omer Akram
-
merge with trunk
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:31
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:31
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:31
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
platform-qa-bot (platform-qa-bot) wrote : | # |
FAILED: Continuous integration, rev:31
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
- 32. By Santiago Baldassin
-
Ignoring chrome_installer to make sure it is not causing side effects
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 Santiago Baldassin
-
Adding TODOs and updating pylint.cfg
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:/
Heber Parrucci (heber013) wrote : | # |
Code LGTM and TODOs make sense.
Preview Diff
1 | === modified file 'README.rst' |
2 | --- README.rst 2017-02-16 14:24:21 +0000 |
3 | +++ README.rst 2017-02-21 13:46:31 +0000 |
4 | @@ -98,3 +98,19 @@ |
5 | |
6 | If you are behind a proxy you can specify --proxy option |
7 | |
8 | + |
9 | +How to setup snapd staging environment |
10 | +====================================== |
11 | + |
12 | +Requirements |
13 | +1. Ubuntu 16.04.1+ with all updates applied |
14 | +2. LXD >= 2.4.1 |
15 | + |
16 | +To enable staging for snapd, it needs to be re-built with special flags, the script in |
17 | +tests/utils/build_snapd_staging.py have all the bits in place to achieve that. It can be |
18 | +run as a standalone script or could be incorporated into snapd tests. Just run the script |
19 | +to setup the environment. |
20 | + |
21 | +$ bzr branch lp:snappy-ecosystem-tests |
22 | +$ cd snappy-ecosystem-tests/ |
23 | +$ python3 -m snappy_ecosystem_tests.snapd.staging_builder my_new_container_name 16.10 |
24 | |
25 | === modified file 'pylint.cfg' |
26 | --- pylint.cfg 2017-02-15 19:35:54 +0000 |
27 | +++ pylint.cfg 2017-02-21 13:46:31 +0000 |
28 | @@ -304,7 +304,7 @@ |
29 | # List of class names for which member attributes should not be checked (useful |
30 | # for classes with dynamically set attributes). This supports the use of |
31 | # qualified names. |
32 | -ignored-classes=optparse.Values,thread._local,_thread._local |
33 | +ignored-classes=optparse.Values,thread._local,_thread._local,ContainerManager |
34 | |
35 | # List of members which are set dynamically and missed by pylint inference |
36 | # system, and so shouldn't trigger E1101 when accessed. Python regular |
37 | |
38 | === modified file 'requirements.txt' |
39 | --- requirements.txt 2017-02-15 14:22:53 +0000 |
40 | +++ requirements.txt 2017-02-21 13:46:31 +0000 |
41 | @@ -14,4 +14,5 @@ |
42 | pexpect |
43 | pymacaroons==0.9.2 |
44 | requests-toolbelt==0.6.0 |
45 | -chromedriver_installer |
46 | +#chromedriver_installer |
47 | +pylxd |
48 | |
49 | === added file 'snappy_ecosystem_tests/helpers/snapd/staging_builder.py' |
50 | --- snappy_ecosystem_tests/helpers/snapd/staging_builder.py 1970-01-01 00:00:00 +0000 |
51 | +++ snappy_ecosystem_tests/helpers/snapd/staging_builder.py 2017-02-21 13:46:31 +0000 |
52 | @@ -0,0 +1,150 @@ |
53 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
54 | + |
55 | +# |
56 | +# Snappy Ecosystem Tests |
57 | +# Copyright (C) 2017 Canonical |
58 | +# |
59 | +# This program is free software: you can redistribute it and/or modify |
60 | +# it under the terms of the GNU General Public License as published by |
61 | +# the Free Software Foundation, either version 3 of the License, or |
62 | +# (at your option) any later version. |
63 | +# |
64 | +# This program is distributed in the hope that it will be useful, |
65 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
66 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
67 | +# GNU General Public License for more details. |
68 | +# |
69 | +# You should have received a copy of the GNU General Public License |
70 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
71 | +# |
72 | + |
73 | +"""Module to enable staging environment for snapd.""" |
74 | + |
75 | +import logging |
76 | +import sys |
77 | +from time import sleep |
78 | + |
79 | +from snappy_ecosystem_tests.utils.lxd import Container, launch_container |
80 | + |
81 | +CONTAINER_HOME = '/root' |
82 | +DIRECTORY_CLONE = '{}/src/github.com/snapcore/snapd'.format(CONTAINER_HOME) |
83 | +ENV_STORE_API_ROOT = 'UBUNTU_STORE_API_ROOT_URL' |
84 | +ENV_STORE_SEARCH_ROOT = 'UBUNTU_STORE_SEARCH_ROOT_URL' |
85 | +ENV_STORE_UPLOAD_ROOT = 'UBUNTU_STORE_UPLOAD_ROOT_URL' |
86 | +ENV_SSO_API_ROOT = 'UBUNTU_SSO_API_ROOT_URL' |
87 | +PACKAGES_SNAPD = ['snapd', 'snap-confine', 'ubuntu-core-launcher'] |
88 | +COMMAND_REMOVE_SNAPD = 'apt purge -y {}'.format(' '.join(PACKAGES_SNAPD)) |
89 | +COMMAND_APT_BUILD_DEP = 'apt build-dep -y ./' |
90 | +COMMAND_APT_INSTALL = 'apt install -y {}' |
91 | +COMMAND_APT_INSTALL_STAGING_DEBS = COMMAND_APT_INSTALL.format( |
92 | + ' '.join(['../{}*.deb'.format(pkg) for pkg in PACKAGES_SNAPD])) |
93 | +COMMAND_APT_UPDATE = 'apt update' |
94 | +COMMAND_APT_UPGRADE = 'apt dist-upgrade -y' |
95 | +COMMAND_BUILD_SNAPD = 'DEB_BUILD_OPTIONS="nocheck testkeys" ' \ |
96 | + 'dpkg-buildpackage -tc -b' |
97 | +COMMAND_EXPORT_STAGING_STORE_VAR = 'echo SNAPPY_USE_STAGING_STORE=1 >> ' \ |
98 | + '/etc/environment' |
99 | +COMMAND_GET_GOVENDOR = 'go get -v github.com/kardianos/govendor' |
100 | +COMMAND_GIT_CLONE = 'git clone {} {}' |
101 | +COMMAND_GOVENDOR_SYNC = 'govendor sync' |
102 | +REPOSITORY_GIT = 'https://github.com/snapcore/snapd' |
103 | +URL_API_ROOT = 'https://myapps.developer.staging.ubuntu.com/dev/api/' |
104 | +URL_SEARCH_ROOT = 'https://search.apps.staging.ubuntu.com/' |
105 | +URL_UPLOAD_ROOT = 'https://upload.apps.staging.ubuntu.com/' |
106 | +URL_SSO_API_ROOT = 'https://login.staging.ubuntu.com/api/v2/' |
107 | + |
108 | +CONTAINER_SETUP = [ |
109 | + COMMAND_REMOVE_SNAPD, |
110 | + COMMAND_APT_UPDATE, |
111 | + # Hold open-iscsi which is causing apt upgrade errors while running |
112 | + # inside container. |
113 | + 'apt-mark hold open-iscsi', |
114 | + COMMAND_APT_UPGRADE, |
115 | + # Needed for snaps to work inside a lxd container |
116 | + # ref: https://stgraber.org/2016/12/07/running-snaps-in-lxd-containers/ |
117 | + COMMAND_APT_INSTALL.format('squashfuse'), |
118 | + COMMAND_GIT_CLONE.format(REPOSITORY_GIT, DIRECTORY_CLONE), |
119 | + {'command': COMMAND_APT_BUILD_DEP, 'cwd': DIRECTORY_CLONE}, |
120 | + {'command': COMMAND_GET_GOVENDOR, 'cwd': DIRECTORY_CLONE}, |
121 | + {'command': COMMAND_GOVENDOR_SYNC, 'cwd': DIRECTORY_CLONE}, |
122 | + {'command': COMMAND_BUILD_SNAPD, 'cwd': DIRECTORY_CLONE}, |
123 | + COMMAND_EXPORT_STAGING_STORE_VAR, |
124 | + {'command': COMMAND_APT_INSTALL_STAGING_DEBS, 'cwd': DIRECTORY_CLONE} |
125 | +] |
126 | + |
127 | +CONTAINER_ENV_VARS = { |
128 | + 'GOPATH': CONTAINER_HOME, |
129 | + 'SNAPPY_USE_STAGING_STORE': '1', |
130 | + ENV_STORE_API_ROOT: URL_API_ROOT, |
131 | + ENV_STORE_SEARCH_ROOT: URL_SEARCH_ROOT, |
132 | + ENV_STORE_UPLOAD_ROOT: URL_UPLOAD_ROOT, |
133 | + ENV_SSO_API_ROOT: URL_SSO_API_ROOT, |
134 | +} |
135 | + |
136 | +LOGGER = logging.getLogger(__name__) |
137 | + |
138 | + |
139 | +class StagingBuilder: |
140 | + """Class to build and activate snapd staging environment.""" |
141 | + def __init__(self, container): |
142 | + if not isinstance(container, Container): |
143 | + raise RuntimeError( |
144 | + '`container` must be an instance of ' |
145 | + 'snappy_ecosystem_tests.utils.lxd.Container') |
146 | + self.container = container |
147 | + |
148 | + def _setup_staging(self): |
149 | + """Export environment variables required to enable snapd/snapcraft |
150 | + staging.""" |
151 | + LOGGER.info('Exporting staging environment variables') |
152 | + updated_path = '{}/bin:{}'.format( |
153 | + CONTAINER_HOME, self.container.execute('echo $PATH')) |
154 | + CONTAINER_ENV_VARS['PATH'] = updated_path |
155 | + for key, value in CONTAINER_ENV_VARS.items(): |
156 | + self.container.set_environment_variable(key, value, save=False) |
157 | + self.container.save(wait=True) |
158 | + LOGGER.info('Exported staging environment variables') |
159 | + |
160 | + def _wait_for_internet(self, attempts_count=10, attempt_interval=1): |
161 | + """Wait for internet connectivity in the container.""" |
162 | + for _ in range(attempts_count): |
163 | + if self.container.is_internet_working(): |
164 | + break |
165 | + else: |
166 | + LOGGER.info('Sleeping for %s second(s)', attempt_interval) |
167 | + sleep(attempt_interval) |
168 | + else: |
169 | + LOGGER.error( |
170 | + 'Failed to get internet access in container %s after %s ' |
171 | + 'seconds', self.container.name, |
172 | + attempts_count * attempt_interval) |
173 | + sys.exit(1) |
174 | + |
175 | + def _setup_and_build(self): |
176 | + """Setup the container, pulls and builds snapd with staging enabled. |
177 | + """ |
178 | + for command in CONTAINER_SETUP: |
179 | + if isinstance(command, dict): |
180 | + self.container.execute(**command) |
181 | + else: |
182 | + self.container.execute(command) |
183 | + |
184 | + def build(self): |
185 | + """Setup lxd container environment for snapd staging enabled builds.""" |
186 | + self._setup_staging() |
187 | + self._wait_for_internet() |
188 | + self._setup_and_build() |
189 | + |
190 | + |
191 | +if __name__ == '__main__': |
192 | + # TODO remove local import |
193 | + import argparse |
194 | + logging.basicConfig(level=logging.INFO) |
195 | + PARSER = argparse.ArgumentParser( |
196 | + description='Builder for snapd staging environment.') |
197 | + PARSER.add_argument('container_name', help='name for the new container.') |
198 | + PARSER.add_argument( |
199 | + 'ubuntu_release', help='Ubuntu release version for the container.') |
200 | + ARGS = PARSER.parse_args() |
201 | + StagingBuilder(launch_container(name=ARGS.container_name, |
202 | + release=ARGS.ubuntu_release)).build() |
203 | |
204 | === added file 'snappy_ecosystem_tests/utils/lxd.py' |
205 | --- snappy_ecosystem_tests/utils/lxd.py 1970-01-01 00:00:00 +0000 |
206 | +++ snappy_ecosystem_tests/utils/lxd.py 2017-02-21 13:46:31 +0000 |
207 | @@ -0,0 +1,125 @@ |
208 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
209 | + |
210 | +# |
211 | +# Snappy Ecosystem Tests |
212 | +# Copyright (C) 2017 Canonical |
213 | +# |
214 | +# This program is free software: you can redistribute it and/or modify |
215 | +# it under the terms of the GNU General Public License as published by |
216 | +# the Free Software Foundation, either version 3 of the License, or |
217 | +# (at your option) any later version. |
218 | +# |
219 | +# This program is distributed in the hope that it will be useful, |
220 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
221 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
222 | +# GNU General Public License for more details. |
223 | +# |
224 | +# You should have received a copy of the GNU General Public License |
225 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
226 | +# |
227 | + |
228 | +"""Module to manipulate lxd containers.""" |
229 | + |
230 | +import logging |
231 | +import shlex |
232 | + |
233 | +import pylxd |
234 | +from pylxd import models |
235 | + |
236 | +DEFAULT_CONTAINER_CONFIG = { |
237 | + 'name': 'snapd', |
238 | + 'source': { |
239 | + 'type': 'image', |
240 | + 'protocol': 'simplestreams', |
241 | + 'server': 'https://cloud-images.ubuntu.com/daily', |
242 | + 'alias': '16.04' |
243 | + } |
244 | +} |
245 | +DOMAIN_PING = 'ubuntu.com' |
246 | +LOGGER = logging.getLogger(__name__) |
247 | + |
248 | + |
249 | +def launch_container(name, release, delete_existing=True): |
250 | + """Launch a lxd container of the requested name and release and return |
251 | + its object.""" |
252 | + lxd_client = pylxd.Client() |
253 | + if delete_existing and lxd_client.containers.exists(name): |
254 | + Container(lxd_client.containers.get(name)).delete() |
255 | + container_config = DEFAULT_CONTAINER_CONFIG.copy() |
256 | + container_config['source']['alias'] = release |
257 | + container_config['name'] = name |
258 | + container = Container( |
259 | + lxd_client.containers.create(container_config, wait=True)) |
260 | + container.start(wait=True) |
261 | + return container |
262 | + |
263 | + |
264 | +class Container: |
265 | + """A Class representing a lxd container. |
266 | + |
267 | + Extends and overrides some functionalities of pylxd.models.Container |
268 | + instance through delegation since that class does not support inheritance. |
269 | + """ |
270 | + def __init__(self, container_instance): |
271 | + # TODO: move to inheritance once upstream supports it. |
272 | + if not isinstance(container_instance, models.Container): |
273 | + raise RuntimeError( |
274 | + '`container_instance` must be an instance of ' |
275 | + 'pylxd.models.Container' |
276 | + ) |
277 | + #TODO: Remove this as the StageBuilder class already has an |
278 | + # instance of the container |
279 | + self._delegate = container_instance |
280 | + |
281 | + def __getattr__(self, name): |
282 | + """Set the container instance as delegate.""" |
283 | + return getattr(self._delegate, name) |
284 | + |
285 | + @staticmethod |
286 | + def _assemble_container_commands(command, cwd=''): |
287 | + """Format and split the requested command to be container friendly.""" |
288 | + exec_command_base = 'sh -c \'{}\'' |
289 | + if cwd: |
290 | + command = 'cd {}; {}'.format(cwd, command) |
291 | + LOGGER.info('Run command: %s', command) |
292 | + return shlex.split(exec_command_base.format(command)) |
293 | + |
294 | + #TODO move the following methods to another place which does not require |
295 | + # a container class |
296 | + def execute(self, command, environment=None, cwd=''): |
297 | + """Execute command in the container.""" |
298 | + response = self._delegate.execute( |
299 | + self._assemble_container_commands(command, cwd=cwd), |
300 | + environment=environment or {} |
301 | + ) |
302 | + if response.exit_code == 0: |
303 | + LOGGER.info(response.stdout) |
304 | + return response.stdout.strip() |
305 | + raise ValueError(response.stderr) |
306 | + |
307 | + def set_environment_variable(self, key, value, save=True): |
308 | + """Exports environment variable to be available to |
309 | + Container.execute().""" |
310 | + self._delegate.config.update({'environment.{}'.format(key): value}) |
311 | + if save: |
312 | + self._delegate.save(wait=True) |
313 | + |
314 | + def is_running(self): |
315 | + """Returns bool representing whether the container is running.""" |
316 | + return self._delegate.status == 'Running' |
317 | + |
318 | + def delete(self, stop=True, wait=True): |
319 | + """Stops and deletes the container.""" |
320 | + if stop and self.is_running(): |
321 | + self._delegate.stop(wait=True) |
322 | + LOGGER.info('Deleting container %s', self.name) |
323 | + self._delegate.delete(wait=wait) |
324 | + LOGGER.info('Deleted container %s', self.name) |
325 | + |
326 | + def is_internet_working(self, ping_domain=DOMAIN_PING): |
327 | + """Return bool representing if the container have internet access.""" |
328 | + try: |
329 | + self.execute('ping -c 1 {}'.format(ping_domain)) |
330 | + return True |
331 | + except ValueError: |
332 | + return False |
Tested with Ubuntu 16.04.1 and 16.10 hosts, worked with both.