Merge lp:~canonical-platform-qa/snappy-ecosystem-tests/snapd-staging-build-script into lp:snappy-ecosystem-tests

Proposed by Omer Akram
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
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

To post a comment you must log in.
Revision history for this message
Omer Akram (om26er) wrote :

Tested with Ubuntu 16.04.1 and 16.10 hosts, worked with both.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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://pastebin.canonical.com/178467/

Revision history for this message
I Ahmad (iahmad) wrote :

Go ahead with merge, we will know if any issues or improvements needed once test cases are added.

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

FAILED: Autolanding.
More details in the following jenkins job:
https://platform-qa-jenkins.ubuntu.com/job/snappy-ecosystem-tests-autoland/1/
Executed test runs:
    None: https://platform-qa-jenkins.ubuntu.com/job/generic-land-mp/2655/console

review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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

review: Needs Fixing
9. By Omer Akram

reduce duplicated call

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Omer Akram (om26er) wrote :

Thanks for the review, I have replied to your comments

10. By Omer Akram

merge with trunk

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Omer Akram (om26er) wrote :

Added replies and pushed changes.

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

add more docstrings, make pylint happier

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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://pastebin.ubuntu.com/23988552/

review: Needs Information
16. By Omer Akram

Incorporate changes suggested by sbalda, more improvements

Revision history for this message
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.Container as we want to extend a bit of functionality of that class.

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.

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

address pylint errors

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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://github.com/lxc/pylxd/pull/220
If you tried the same sequence of events but without recreating the object, it is working just fine:
https://pastebin.ubuntu.com/23997027/

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

review: Needs Fixing
18. By Omer Akram

Container inheritance is broken, move to delegation

Revision history for this message
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.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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

review: Abstain
Revision history for this message
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.Container class with delegation as inheritance is not supported upstream.

Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

Replies inline

review: Abstain
Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

By the way, the current approach forces you to keep two instances of the same container created by pylxd

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

Comments inline. Thanks!

review: Needs Fixing
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

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

Thanks heber, replied inline and also pylint should be happy now.

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

Reply inline

review: Needs Fixing
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

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

I have made some structural changes to the code and replied inline.

Revision history for this message
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

Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

Minor comment inline

28. By Omer Akram

Replace sys.argv with argparse

29. By Omer Akram

pylint happy

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

This branch is 10 days old and I would now expect that we land it instead of more nitpicks.

Revision history for this message
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/discussions, I'm abstaining

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

Comments inline.
One thing, I was getting an error when the sh creates the virtual env:

http://paste.ubuntu.com/24033643/

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?

review: Needs Fixing
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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.

Revision history for this message
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" ;)

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

Comments inline

review: Needs Fixing
30. By Omer Akram

remove pylint suppress thats not needed

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Omer Akram (om26er) wrote :

Heber, replied inline.

Revision history for this message
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

Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

Reply inline

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

minor query inline.

Revision history for this message
Santiago Baldassin (sbaldassin) wrote :

reply inline

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

merge with trunk

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
32. By Santiago Baldassin

Ignoring chrome_installer to make sure it is not causing side effects

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

Adding TODOs and updating pylint.cfg

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

Code LGTM and TODOs make sense.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README.rst'
2--- README.rst 2017-02-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

Subscribers

People subscribed via source and target branches