Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Christopher Townsend | ||||
Approved revision: | 366 | ||||
Merged at revision: | 351 | ||||
Proposed branch: | lp:~larryprice/libertine/lxd | ||||
Merge into: | lp:libertine | ||||
Diff against target: |
862 lines (+520/-88) 16 files modified
.bzrignore (+1/-0) common/ContainerConfigList.h (+1/-2) data/CMakeLists.txt (+1/-1) data/libertine-lxd-sudo (+1/-0) debian/control (+15/-0) debian/python3-libertine-lxd.install (+3/-0) liblibertine/libertine.cpp (+41/-26) python/libertine/ChrootContainer.py (+1/-2) python/libertine/HostInfo.py (+5/-0) python/libertine/Libertine.py (+28/-31) python/libertine/LxcContainer.py (+4/-20) python/libertine/LxdContainer.py (+375/-0) tests/unit/test_libertine_gir.py (+2/-4) tools/CMakeLists.txt (+1/-1) tools/libertine-container-manager (+1/-1) tools/libertine-lxd-setup (+40/-0) |
||||
To merge this branch: | bzr merge lp:~larryprice/libertine/lxd | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Christopher Townsend | Approve | ||
Libertine CI Bot | continuous-integration | Approve | |
Review via email: mp+311980@code.launchpad.net |
Commit message
Initial implementation of lxd backend.
Description of the change
Initial implementation of lxd backend.
Known limitations include:
* Writing to the bind-mounted XDG directories (waiting on a new feature in the lxc cli to land)
* Stopping container when no applications or operations are running (evaluating necessity, waiting for verification of d-bus functionality within snappy)
Libertine CI Bot (libertine-ci-bot) wrote : | # |
- 334. By Larry Price
-
use built-in method to get type
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:334
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Christopher Townsend (townsend) wrote : | # |
debian/
Larry Price (larryprice) wrote : | # |
> debian/
> control. It is created at package build time.
My bad, I must have done a bzr add debian on accident. I might add that to .bzrignore shortly.
- 335. By Larry Price
-
Devices map update should really be a complete refresh
- 336. By Larry Price
-
click-hook should not have been committed
- 337. By Larry Price
-
ignore click-hooks
- 338. By Larry Price
-
merge
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:337
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:338
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Christopher Townsend (townsend) wrote : | # |
As discussed on irc, the need to run 'lxc init' and anything else needed to get an LXD container up and running needs to be "automated" so that a user does not have to drop into a terminal and run things. Not only would this make it more user friendly, but if a user is using the Libertine Manager GUI, they would have a very poor user experience.
Perhaps doing something similar to what we do for LXC's would be a good first step, ie, the sudoers file.
Larry Price (larryprice) wrote : | # |
> As discussed on irc, the need to run 'lxc init' and anything else needed to
> get an LXD container up and running needs to be "automated" so that a user
> does not have to drop into a terminal and run things. Not only would this
> make it more user friendly, but if a user is using the Libertine Manager GUI,
> they would have a very poor user experience.
>
> Perhaps doing something similar to what we do for LXC's would be a good first
> step, ie, the sudoers file.
Agreed - I plan on looking into that later today or tomorrow. I'll also be merging in some necessary changes I discovered while snapping.
- 339. By Larry Price
-
merge
- 340. By Larry Price
-
Changes from snap development
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:340
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 341. By Larry Price
-
setup for lxd
- 342. By Larry Price
-
second draft of init script
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:342
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 343. By Larry Price
-
Use pexpect to run through defaults of lxd
- 344. By Larry Price
-
Merge
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:344
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 345. By Larry Price
-
add newline back in
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:345
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 346. By Larry Price
-
idmap updates for writing to user home directory
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:346
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 347. By Larry Price
-
did not need the usermod
- 348. By Larry Price
-
Many updates found to help fix initialization and work on xenial
- 349. By Larry Price
-
Create .config/lxc and make sure it has the right permissions
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:349
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 350. By Larry Price
-
Check that a username argument is given at the top of the setup file
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:350
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 351. By Larry Price
-
modifying groups and info instead of debug
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:351
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 352. By Larry Price
-
fix terminal getting eaten
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:352
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 353. By Larry Price
-
need exception var
- 354. By Larry Price
-
app wait works just as well...
- 355. By Larry Price
-
wait for window manager to die before opening app
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:355
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 356. By Larry Price
-
chown home dir
- 357. By Larry Price
-
ugly bind-mount fixer-upper
- 358. By Larry Price
-
merge
- 359. By Larry Price
-
accidentally changed useradd command
- 360. By Larry Price
-
fix service based on real life
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:360
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 361. By Larry Price
-
up sleep time slightly
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:361
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 362. By Larry Price
-
merge
- 363. By Larry Price
-
User needs the audio and video groups, and mounts need to have correct group permissions
- 364. By Larry Price
-
forget about PULSE?
Libertine CI Bot (libertine-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:363
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:364
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 365. By Larry Price
-
fixes for matchbox not closing in xenial
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:365
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 366. By Larry Price
-
ensure correct PATH and add slight time delay for matchbox
Libertine CI Bot (libertine-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:366
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Christopher Townsend (townsend) wrote : | # |
Ok, I think this is in good enough shape to merge into devel. Let's flesh it out a bit more in devel before releasing in lp:libertine/trunk.
Good work!
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2016-11-29 20:38:41 +0000 |
3 | +++ .bzrignore 2016-12-15 21:01:30 +0000 |
4 | @@ -2,6 +2,7 @@ |
5 | po/Makefile.in.in |
6 | __pycache__ |
7 | tests/unit/.cache |
8 | +*.click-hook |
9 | |
10 | prime/ |
11 | stage/ |
12 | |
13 | === modified file 'common/ContainerConfigList.h' |
14 | --- common/ContainerConfigList.h 2016-09-26 18:17:07 +0000 |
15 | +++ common/ContainerConfigList.h 2016-12-15 21:01:30 +0000 |
16 | @@ -55,11 +55,10 @@ |
17 | * Display roles for a container config. |
18 | */ |
19 | enum class DataRole |
20 | - : int |
21 | { |
22 | ContainerId = Qt::UserRole + 1, /**< The container ID */ |
23 | ContainerName, /**< The container name */ |
24 | - ContainerType, /**< The type of container - lxc or chroot */ |
25 | + ContainerType, /**< The type of container */ |
26 | DistroSeries, /**< The distro from which the container was built */ |
27 | InstallStatus, /**< Current container install status */ |
28 | Error /**< last role (error) */ |
29 | |
30 | === modified file 'data/CMakeLists.txt' |
31 | --- data/CMakeLists.txt 2016-12-06 20:52:55 +0000 |
32 | +++ data/CMakeLists.txt 2016-12-15 21:01:30 +0000 |
33 | @@ -6,7 +6,7 @@ |
34 | DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}) |
35 | install(FILES libertine-xmir.conf |
36 | DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions) |
37 | -install(FILES libertine-lxc-sudo |
38 | +install(FILES libertine-lxc-sudo libertine-lxd-sudo |
39 | DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sudoers.d) |
40 | install(FILES com.canonical.libertine.LxcManager.service com.canonical.libertine.ContainerManager.service |
41 | DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/services) |
42 | |
43 | === added file 'data/libertine-lxd-sudo' |
44 | --- data/libertine-lxd-sudo 1970-01-01 00:00:00 +0000 |
45 | +++ data/libertine-lxd-sudo 2016-12-15 21:01:30 +0000 |
46 | @@ -0,0 +1,1 @@ |
47 | +ALL ALL=(ALL) NOPASSWD:/usr/bin/libertine-lxd-setup |
48 | |
49 | === modified file 'debian/control' |
50 | --- debian/control 2016-12-06 21:55:36 +0000 |
51 | +++ debian/control 2016-12-15 21:01:30 +0000 |
52 | @@ -182,6 +182,21 @@ |
53 | Libertine sandbox. It requires support for unprivileged LXC containers in the |
54 | Linux kernel. |
55 | |
56 | +Package: python3-libertine-lxd |
57 | +Architecture: any |
58 | +Section: python |
59 | +Multi-Arch: allowed |
60 | +Depends: lxd, |
61 | + python3-libertine, |
62 | + python3-pexpect, |
63 | + python3-pylxd, |
64 | + ${misc:Depends}, |
65 | + ${python3:Depends} |
66 | +Description: Python3 scripts for the Libertine application sandbox |
67 | + This package provides the LXD-based container back end module for the |
68 | + Libertine sandbox. It requires support for unprivileged LXD containers in the |
69 | + Linux kernel. |
70 | + |
71 | Package: python3-libertine-chroot |
72 | Architecture: any |
73 | Section: python |
74 | |
75 | === added file 'debian/python3-libertine-lxd.install' |
76 | --- debian/python3-libertine-lxd.install 1970-01-01 00:00:00 +0000 |
77 | +++ debian/python3-libertine-lxd.install 2016-12-15 21:01:30 +0000 |
78 | @@ -0,0 +1,3 @@ |
79 | +etc/sudoers.d/libertine-lxd-sudo |
80 | +usr/lib/python*/*/libertine/LxdContainer.py |
81 | +usr/bin/libertine-lxd-setup |
82 | |
83 | === modified file 'liblibertine/libertine.cpp' |
84 | --- liblibertine/libertine.cpp 2016-10-26 13:58:19 +0000 |
85 | +++ liblibertine/libertine.cpp 2016-12-15 21:01:30 +0000 |
86 | @@ -67,8 +67,16 @@ |
87 | g_dir_close(dir); |
88 | return nullptr; |
89 | } |
90 | -} |
91 | - |
92 | + |
93 | + |
94 | +gchar* |
95 | +id_from_list_index(const ContainerConfigList& container_list, guint index) |
96 | +{ |
97 | + return (gchar*)container_list.data(container_list.index(index, 0), |
98 | + (int)ContainerConfigList::DataRole::ContainerId) |
99 | + .toString().toStdString().c_str(); |
100 | +} |
101 | +} |
102 | |
103 | gchar** |
104 | libertine_list_apps_for_container(const gchar* container_id) |
105 | @@ -137,61 +145,68 @@ |
106 | gchar * |
107 | libertine_container_path(const gchar * container_id) |
108 | { |
109 | + g_return_val_if_fail(container_id != nullptr, nullptr); |
110 | + LibertineConfig config; |
111 | + ContainerConfigList container_list(&config); |
112 | gchar * path = nullptr; |
113 | - g_return_val_if_fail(container_id != nullptr, nullptr); |
114 | |
115 | - path = g_build_filename(g_get_user_cache_dir(), "libertine-container", container_id, "rootfs", nullptr); |
116 | + if (g_strcmp0((gchar*)container_list.getContainerType(container_id).toStdString().c_str(), "lxd") == 0) |
117 | + { |
118 | + path = g_build_filename("/", "var", "lib", "lxd", "containers", container_id, "rootfs", nullptr); |
119 | + } |
120 | + else |
121 | + { |
122 | + path = g_build_filename(g_get_user_cache_dir(), "libertine-container", container_id, "rootfs", nullptr); |
123 | + } |
124 | |
125 | if (g_file_test(path, G_FILE_TEST_EXISTS)) |
126 | { |
127 | return path; |
128 | } |
129 | - else |
130 | - { |
131 | - g_free(path); |
132 | - return nullptr; |
133 | - } |
134 | + |
135 | + g_free(path); |
136 | + return nullptr; |
137 | } |
138 | |
139 | |
140 | gchar * |
141 | libertine_container_home_path(const gchar * container_id) |
142 | { |
143 | + g_return_val_if_fail(container_id != nullptr, nullptr); |
144 | + LibertineConfig config; |
145 | + ContainerConfigList container_list(&config); |
146 | gchar * path = nullptr; |
147 | - g_return_val_if_fail(container_id != nullptr, nullptr); |
148 | |
149 | - path = g_build_filename(g_get_user_data_dir(), "libertine-container", "user-data", container_id, nullptr); |
150 | + if (g_strcmp0((gchar*)container_list.getContainerType(container_id).toStdString().c_str(), "lxd") == 0) |
151 | + { |
152 | + path = g_build_filename("/", "var", "lib", "lxd", "containers", container_id, "rootfs", "home", g_get_user_name(), nullptr); |
153 | + } |
154 | + else |
155 | + { |
156 | + path = g_build_filename(g_get_user_data_dir(), "libertine-container", "user-data", container_id, nullptr); |
157 | + } |
158 | |
159 | if (g_file_test(path, G_FILE_TEST_EXISTS)) |
160 | { |
161 | return path; |
162 | } |
163 | - else |
164 | - { |
165 | - g_free(path); |
166 | - return nullptr; |
167 | - } |
168 | |
169 | + g_free(path); |
170 | + return nullptr; |
171 | } |
172 | |
173 | |
174 | gchar * |
175 | libertine_container_name(const gchar * container_id) |
176 | { |
177 | - guint container_count; |
178 | - guint i; |
179 | gchar * container_name = nullptr; |
180 | LibertineConfig config; |
181 | ContainerConfigList container_list(&config); |
182 | - QVariant id; |
183 | - |
184 | - container_count = (guint)container_list.size(); |
185 | - |
186 | - for (i = 0; i < container_count; ++i) |
187 | + guint container_count = (guint)container_list.size(); |
188 | + |
189 | + for (guint i = 0; i < container_count; ++i) |
190 | { |
191 | - id = container_list.data(container_list.index(i, 0), (int)ContainerConfigList::DataRole::ContainerId); |
192 | - |
193 | - if (g_strcmp0((gchar *)id.toString().toStdString().c_str(), container_id) == 0) |
194 | + if (g_strcmp0(id_from_list_index(container_list, i), container_id) == 0) |
195 | { |
196 | QVariant name = container_list.data(container_list.index(i, 0), (int)ContainerConfigList::DataRole::ContainerName); |
197 | container_name = g_strdup(name.toString().toStdString().c_str()); |
198 | |
199 | === modified file 'python/libertine/ChrootContainer.py' |
200 | --- python/libertine/ChrootContainer.py 2016-12-02 17:46:47 +0000 |
201 | +++ python/libertine/ChrootContainer.py 2016-12-15 21:01:30 +0000 |
202 | @@ -233,8 +233,7 @@ |
203 | |
204 | args = shlex.split(proot_cmd) |
205 | args.extend(app_exec_line) |
206 | - app = psutil.Popen(args, env=environ) |
207 | - return app |
208 | + return psutil.Popen(args, env=environ) |
209 | |
210 | def finish_application(self, app): |
211 | utils.terminate_window_manager(self._window_manager) |
212 | |
213 | === modified file 'python/libertine/HostInfo.py' |
214 | --- python/libertine/HostInfo.py 2016-10-28 18:11:12 +0000 |
215 | +++ python/libertine/HostInfo.py 2016-12-15 21:01:30 +0000 |
216 | @@ -13,6 +13,7 @@ |
217 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
218 | |
219 | import lsb_release |
220 | +import os |
221 | import platform |
222 | import subprocess |
223 | |
224 | @@ -69,3 +70,7 @@ |
225 | parser.error("Failed to determine the local architecture.") |
226 | |
227 | return dpkg.stdout.read().strip() |
228 | + |
229 | + def get_host_timezone(self): |
230 | + with open(os.path.join('/', 'etc', 'timezone'), 'r') as fd: |
231 | + return fd.read().strip('\n') |
232 | |
233 | === modified file 'python/libertine/Libertine.py' |
234 | --- python/libertine/Libertine.py 2016-11-16 17:58:38 +0000 |
235 | +++ python/libertine/Libertine.py 2016-12-15 21:01:30 +0000 |
236 | @@ -1,4 +1,4 @@ |
237 | -# Copyright 2015-2106 Canonical Ltd. |
238 | +# Copyright 2015-2016 Canonical Ltd. |
239 | # |
240 | # This program is free software: you can redistribute it and/or modify it |
241 | # under the terms of the GNU General Public License version 3, as published |
242 | @@ -98,35 +98,27 @@ |
243 | def destroy_libertine_container(self, verbosity=1): |
244 | pass |
245 | |
246 | - def copy_package_to_container(self, package_path): |
247 | + def copy_file_to_container(self, source, dest): |
248 | """ |
249 | - Copies a Debian package from the host to a pre-determined place |
250 | - in a container. |
251 | + Copies a file from the host to the given path in the container. |
252 | |
253 | - :param package_path: The full path to the Debian package located |
254 | - on the host. |
255 | + :param source: The full path to the file on the host. |
256 | + :param dest: The relative path to the file in the container without |
257 | + the root path. |
258 | """ |
259 | - if os.path.exists(os.path.join(self.root_path, 'tmp', package_path.split('/')[-1])): |
260 | + if os.path.exists(os.path.join(self.root_path, dest)): |
261 | return False |
262 | |
263 | - shutil.copy2(package_path, os.path.join(self.root_path, 'tmp')) |
264 | + shutil.copy2(source, os.path.join(self.root_path, dest.lstrip('/'))) |
265 | return True |
266 | |
267 | - def delete_package_in_container(self, package_path): |
268 | - """ |
269 | - Deletes a previously copied in Debian package in a container. |
270 | - |
271 | - :param package_path: The full path to the Debian package located |
272 | - on the host. |
273 | - """ |
274 | - os.remove(os.path.join(self.root_path, 'tmp', package_path.split('/')[-1])) |
275 | - |
276 | - def is_running(self): |
277 | - """ |
278 | - Indicates if the container is 'running'. The definition of 'running' |
279 | - depends on the type of the container. |
280 | - """ |
281 | - return False |
282 | + def delete_file_in_container(self, path): |
283 | + """ |
284 | + Deletes a file within the container. |
285 | + |
286 | + :param path: The path to the file without the container root path. |
287 | + """ |
288 | + os.remove(os.path.join(self.root_path, path.lstrip('/'))) |
289 | |
290 | def start_container(self): |
291 | """ |
292 | @@ -180,16 +172,18 @@ |
293 | self.update_apt_cache(verbosity) |
294 | |
295 | if package_name.endswith('.deb'): |
296 | - delete_package = self.copy_package_to_container(package_name) |
297 | - |
298 | - self.run_in_container('ls -la ' + os.environ['HOME']) |
299 | - self.run_in_container('dpkg -i ' + |
300 | - os.path.join('/', 'tmp', package_name.split('/')[-1])) |
301 | - |
302 | + if not os.path.exists(package_name): |
303 | + utils.get_logger().error("File {} does not exist.".format(package_name)) |
304 | + return False |
305 | + |
306 | + dest = os.path.join('/', 'tmp', package_name.split('/')[-1]) |
307 | + file_created = self.copy_file_to_container(package_name, dest) |
308 | + |
309 | + self.run_in_container('dpkg -i {}'.format(dest)) |
310 | ret = self.run_in_container(apt_command_prefix(verbosity) + " install -f") == 0 |
311 | |
312 | - if delete_package: |
313 | - self.delete_package_in_container(package_name) |
314 | + if file_created: |
315 | + self.delete_file_in_container(dest) |
316 | |
317 | return ret |
318 | else: |
319 | @@ -335,6 +329,9 @@ |
320 | if container_type == "lxc": |
321 | from libertine.LxcContainer import LibertineLXC |
322 | self.container = LibertineLXC(container_id) |
323 | + elif container_type == "lxd": |
324 | + from libertine.LxdContainer import LibertineLXD |
325 | + self.container = LibertineLXD(container_id, self.containers_config) |
326 | elif container_type == "chroot": |
327 | from libertine.ChrootContainer import LibertineChroot |
328 | self.container = LibertineChroot(container_id) |
329 | |
330 | === modified file 'python/libertine/LxcContainer.py' |
331 | --- python/libertine/LxcContainer.py 2016-11-16 17:33:42 +0000 |
332 | +++ python/libertine/LxcContainer.py 2016-12-15 21:01:30 +0000 |
333 | @@ -24,7 +24,7 @@ |
334 | import tempfile |
335 | |
336 | from .Libertine import BaseContainer |
337 | -from . import utils |
338 | +from . import utils, HostInfo |
339 | |
340 | |
341 | home_path = os.environ['HOME'] |
342 | @@ -96,13 +96,6 @@ |
343 | container.stop() |
344 | |
345 | |
346 | -def get_host_timezone(): |
347 | - with open(os.path.join('/', 'etc', 'timezone'), 'r') as fd: |
348 | - host_timezone = fd.read().strip('\n') |
349 | - |
350 | - return host_timezone |
351 | - |
352 | - |
353 | class EnvLxcSettings(contextlib.ExitStack): |
354 | """ |
355 | Helper object providing a way to set the proxies for testing |
356 | @@ -140,6 +133,7 @@ |
357 | self._set_lxc_log() |
358 | self.lxc_manager_interface = None |
359 | self.window_manager = None |
360 | + self.host_info = HostInfo.HostInfo() |
361 | |
362 | utils.set_session_dbus_env_var() |
363 | |
364 | @@ -150,19 +144,9 @@ |
365 | except dbus.exceptions.DBusException: |
366 | pass |
367 | |
368 | - def is_running(self): |
369 | - return self.container.running |
370 | - |
371 | def timezone_needs_update(self): |
372 | - host_timezone = get_host_timezone() |
373 | - |
374 | with open(os.path.join(self.root_path, 'etc', 'timezone'), 'r') as fd: |
375 | - container_timezone = fd.read().strip('\n') |
376 | - |
377 | - if host_timezone == container_timezone: |
378 | - return False |
379 | - else: |
380 | - return True |
381 | + return fd.read().strip('\n') != self.host_info.get_host_timezone() |
382 | |
383 | def start_container(self): |
384 | if self.lxc_manager_interface: |
385 | @@ -192,7 +176,7 @@ |
386 | def update_packages(self, verbosity=1): |
387 | if self.timezone_needs_update(): |
388 | self.run_in_container("bash -c \'echo \"{}\" >/etc/timezone\'".format( |
389 | - get_host_timezone())) |
390 | + self.host_info.get_host_timezone())) |
391 | self.run_in_container("rm -f /etc/localtime") |
392 | self.run_in_container("dpkg-reconfigure -f noninteractive tzdata") |
393 | |
394 | |
395 | === added file 'python/libertine/LxdContainer.py' |
396 | --- python/libertine/LxdContainer.py 1970-01-01 00:00:00 +0000 |
397 | +++ python/libertine/LxdContainer.py 2016-12-15 21:01:30 +0000 |
398 | @@ -0,0 +1,375 @@ |
399 | +# Copyright 2016 Canonical Ltd. |
400 | +# |
401 | +# This program is free software: you can redistribute it and/or modify it |
402 | +# under the terms of the GNU General Public License version 3, as published |
403 | +# by the Free Software Foundation. |
404 | +# |
405 | +# This program is distributed in the hope that it will be useful, but |
406 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
407 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
408 | +# PURPOSE. See the GNU General Public License for more details. |
409 | +# |
410 | +# You should have received a copy of the GNU General Public License along |
411 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
412 | + |
413 | +import crypt |
414 | +import os |
415 | +import psutil |
416 | +import pylxd |
417 | +import shlex |
418 | +import subprocess |
419 | +import time |
420 | +from libertine import Libertine, utils, HostInfo |
421 | + |
422 | + |
423 | +def _get_devices_map(): |
424 | + devices = { |
425 | + '/dev/tty0': {'path': '/dev/tty0', 'type': 'unix-char'}, |
426 | + '/dev/tty7': {'path': '/dev/tty7', 'type': 'unix-char'}, |
427 | + '/dev/tty8': {'path': '/dev/tty8', 'type': 'unix-char'}, |
428 | + '/dev/fb0': {'path': '/dev/fb0', 'type': 'unix-char'}, |
429 | + 'x11-socket': {'source': '/tmp/.X11-unix', 'path': '/tmp/.X11-unix', 'type': 'disk', 'optional': 'true'}, |
430 | + } |
431 | + |
432 | + if os.path.exists('/dev/video0'): |
433 | + devices['/dev/video0'] = {'path': '/dev/video0', 'type': 'unix-char'} |
434 | + |
435 | + # every regular file in /dev/snd |
436 | + for f in ['/dev/snd/{}'.format(f) for f in os.listdir('/dev/snd') if not os.path.isdir('/dev/snd/{}'.format(f))]: |
437 | + devices[f] = {'path': f, 'type': 'unix-char'} |
438 | + |
439 | + # every regular file in /dev/dri |
440 | + for f in ['/dev/dri/{}'.format(f) for f in os.listdir('/dev/dri') if not os.path.isdir('/dev/dri/{}'.format(f))]: |
441 | + devices[f] = {'path': f, 'type': 'unix-char'} |
442 | + |
443 | + # Some devices require special mappings for snap |
444 | + if utils.is_snap_environment(): |
445 | + devices['x11-socket']['source'] = '/var/lib/snapd/hostfs/tmp/.X11-unix' |
446 | + |
447 | + return devices |
448 | + |
449 | +def _readlink(source): |
450 | + while os.path.islink(source): |
451 | + new_source = os.readlink(source) |
452 | + if not os.path.isabs(new_source): |
453 | + new_source = os.path.join(os.path.dirname(source), new_source) |
454 | + source = new_source |
455 | + |
456 | + return source |
457 | + |
458 | +def _setup_lxd(): |
459 | + import pexpect |
460 | + child = pexpect.spawnu('sudo libertine-lxd-setup {}'.format(os.environ['USER']), env={'TERM': 'dumb'}) |
461 | + |
462 | + while True: |
463 | + index = child.expect(['.+\[default=.+\].*', pexpect.EOF, pexpect.TIMEOUT, |
464 | + # The following are required for lxd=2.0.x |
465 | + '.+\[yes/no\].*', |
466 | + '.+\(e.g. (?P<example>[a-z0-9\.:]+)\).+']) |
467 | + if index == 0: |
468 | + child.sendline() |
469 | + elif index == 1: |
470 | + return True |
471 | + elif index == 2: |
472 | + return False |
473 | + elif index == 3: |
474 | + child.sendline('yes') |
475 | + elif index == 4: |
476 | + child.sendline(child.match.group('example')) |
477 | + |
478 | + if child.exitstatus is not None: |
479 | + return child.exitstatus == 0 |
480 | + |
481 | + |
482 | +def _setup_bind_mount_service(container, uid, username): |
483 | + utils.get_logger().info("Creating systemd mount override service") |
484 | + service = ''' |
485 | +[Unit] |
486 | +Description=Fix system mounts for libertine |
487 | + |
488 | +[Service] |
489 | +ExecStart=/usr/bin/libertine-lxd-mount-update |
490 | + |
491 | +[Install] |
492 | +WantedBy=multi-user.target |
493 | +'''[1:-1] |
494 | + container.files.put('/etc/systemd/system/libertine-lxd-mount-update.service', service.encode('utf-8')) |
495 | + |
496 | + utils.get_logger().info("Creating mount update shell script") |
497 | + script = ''' |
498 | +#!/bin/sh |
499 | + |
500 | +mkdir -p /run/user/{uid} |
501 | +chown {username}:{username} /run/user/{uid} |
502 | +mount -o bind /var/tmp/run/user/{uid} /run/user/{uid} |
503 | + |
504 | +chgrp audio /dev/snd/* |
505 | +chgrp video /dev/dri/* |
506 | +[ -n /dev/video0 ] && chgrp video /dev/video0 |
507 | +'''[1:-1] |
508 | + container.files.put('/usr/bin/libertine-lxd-mount-update', script.format(uid=uid, username=username).encode('utf-8')) |
509 | + container.execute(shlex.split('chmod +x /usr/bin/libertine-lxd-mount-update')) |
510 | + |
511 | + utils.get_logger().info("Enabling systemd mount update service") |
512 | + container.execute(shlex.split('systemctl enable libertine-lxd-mount-update.service')) |
513 | + |
514 | + |
515 | +class LibertineLXD(Libertine.BaseContainer): |
516 | + def __init__(self, name, config): |
517 | + super().__init__(name) |
518 | + self._id = name |
519 | + self._config = config |
520 | + self._host_info = HostInfo.HostInfo() |
521 | + self._container = None |
522 | + self._matchbox_pid = None |
523 | + |
524 | + if not _setup_lxd(): |
525 | + raise Exception("Failed to setup lxd.") |
526 | + |
527 | + self._client = pylxd.Client() |
528 | + self._window_manager = None |
529 | + self.root_path = '{}/containers/{}/rootfs'.format(os.getenv('LXD_DIR', '/var/lib/lxd'), name) |
530 | + |
531 | + def _update_libertine_profile(self): |
532 | + try: |
533 | + profile = self._client.profiles.get('libertine') |
534 | + |
535 | + utils.get_logger().info('Updating existing lxd profile.') |
536 | + profile.devices = _get_devices_map() |
537 | + profile.config['raw.idmap'] = 'both 1000 1000' |
538 | + |
539 | + try: |
540 | + profile.save() |
541 | + except pylxd.exceptions.LXDAPIException as e: |
542 | + utils.get_logger().warning('Saving libertine lxd profile raised: {}'.format(str(e))) |
543 | + # This is most likely the result of an older container currently |
544 | + # running and/or containing a conflicting device entry |
545 | + except pylxd.exceptions.LXDAPIException: |
546 | + utils.get_logger().info('Creating libertine lxd profile.') |
547 | + self._client.profiles.create('libertine', config={'raw.idmap': 'both 1000 1000'}, devices=_get_devices_map()) |
548 | + |
549 | + def create_libertine_container(self, password=None, multiarch=False, verbosity=1): |
550 | + if self._try_get_container(): |
551 | + utils.get_logger().error("Container already exists") |
552 | + return False |
553 | + |
554 | + self._update_libertine_profile() |
555 | + |
556 | + utils.get_logger().info("Creating container '%s' with distro '%s'" % (self._id, self.installed_release)) |
557 | + self._container = self._client.containers.create({'name': self._id, |
558 | + 'profiles': ['default', 'libertine'], |
559 | + 'source': {"type": "image", |
560 | + "protocol": "simplestreams", |
561 | + "server": "https://cloud-images.ubuntu.com/daily", |
562 | + "alias": self.installed_release}}, |
563 | + wait=True) |
564 | + |
565 | + if not self.start_container(): |
566 | + utils.get_logger().error("Failed to start container '{}'".format(self._id)) |
567 | + self.destroy_libertine_container() |
568 | + return False |
569 | + |
570 | + username = os.environ['USER'] |
571 | + uid = str(os.getuid()) |
572 | + self.run_in_container("userdel -r ubuntu") |
573 | + self.run_in_container("useradd -u {} -U -p {} -G sudo,audio,video {}".format( |
574 | + uid, crypt.crypt(''), username)) |
575 | + self.run_in_container("mkdir -p /home/{}".format(username)) |
576 | + self.run_in_container("chown {0}:{0} /home/{0}".format(username)) |
577 | + |
578 | + _setup_bind_mount_service(self._container, uid, username) |
579 | + |
580 | + if multiarch and self.architecture == 'amd64': |
581 | + utils.get_logger().info("Adding i386 multiarch support to container '{}'".format(self._id)) |
582 | + self.run_in_container("dpkg --add-architecture i386") |
583 | + |
584 | + self.update_packages() |
585 | + |
586 | + for package in self.default_packages: |
587 | + utils.get_logger().info("Installing package '%s' in container '%s'" % (package, self._id)) |
588 | + if not self.install_package(package, verbosity=1, no_dialog=True, update_cache=False): |
589 | + utils.get_logger().error("Failure installing '%s' during container creation" % package) |
590 | + self.destroy_libertine_container() |
591 | + return False |
592 | + |
593 | + return True |
594 | + |
595 | + def _wait_for_network(self): |
596 | + for retries in range(0, 10): |
597 | + out, err = self._container.execute(shlex.split('ping -c 1 ubuntu.com')) |
598 | + if out: |
599 | + utils.get_logger().info("Network connection active") |
600 | + return True |
601 | + time.sleep(1) |
602 | + return False |
603 | + |
604 | + def update_packages(self, verbosity=1): |
605 | + if not self._timezone_in_sync(): |
606 | + utils.get_logger().info("Re-syncing timezones") |
607 | + self.run_in_container("bash -c 'echo \"%s\" > /etc/timezone'" % self._host_info.get_host_timezone()) |
608 | + self.run_in_container("rm -f /etc/localtime") |
609 | + self.run_in_container("dpkg-reconfigure -f noninteractive tzdata") |
610 | + |
611 | + return super().update_packages() |
612 | + |
613 | + def destroy_libertine_container(self): |
614 | + if not self._try_get_container(): |
615 | + utils.get_logger().error("No such container '%s'" % self._id) |
616 | + return False |
617 | + |
618 | + self.stop_container(wait=True) |
619 | + self._container.delete() |
620 | + return True |
621 | + |
622 | + def _timezone_in_sync(self): |
623 | + proc = subprocess.Popen(self._lxc_args('cat /etc/timezone'), stdout=subprocess.PIPE) |
624 | + out, err = proc.communicate() |
625 | + return out.decode('UTF-8').strip('\n') == self._host_info.get_host_timezone() |
626 | + |
627 | + def _lxc_args(self, command, environ={}): |
628 | + env_as_args = ' ' |
629 | + for k, v in environ.items(): |
630 | + env_as_args += '--env {}="{}" '.format(k, v) |
631 | + |
632 | + return shlex.split('lxc exec {}{}-- {}'.format(self._id, |
633 | + env_as_args, |
634 | + command)) |
635 | + |
636 | + def run_in_container(self, command): |
637 | + proc = subprocess.Popen(self._lxc_args(command)) |
638 | + return proc.wait() |
639 | + |
640 | + def start_container(self): |
641 | + if not self._try_get_container(): |
642 | + return False |
643 | + |
644 | + if self._container.status == 'Running': |
645 | + return True |
646 | + |
647 | + self._container.start(wait=True) |
648 | + |
649 | + # Connect to network |
650 | + if not self._wait_for_network(): |
651 | + utils.get_logger().error("Network unavailable in container '{}'".format(self._id)) |
652 | + return False |
653 | + |
654 | + self._container.sync(rollback=True) # required for pylxd=2.0.x |
655 | + |
656 | + return self._container.status == 'Running' |
657 | + |
658 | + def stop_container(self, wait=False): |
659 | + if not self._try_get_container(): |
660 | + return False |
661 | + |
662 | + if self._container.status == 'Stopped': |
663 | + return True |
664 | + |
665 | + self._container.stop(wait=wait) |
666 | + |
667 | + return not wait or self._container.status == 'Stopped' |
668 | + |
669 | + def _update_bind_mounts(self): |
670 | + home_path = os.environ['HOME'] |
671 | + |
672 | + self._container.devices.clear() |
673 | + self._container.devices['root'] = {'type': 'disk', 'path': '/'} |
674 | + |
675 | + if os.path.exists(os.path.join(home_path, '.config', 'dconf')): |
676 | + self._container.devices['dconf'] = { |
677 | + 'type': 'disk', |
678 | + 'source': os.path.join(home_path, '.config', 'dconf'), |
679 | + 'path': os.path.join(home_path, '.config', 'dconf') |
680 | + } |
681 | + |
682 | + run_user = '/run/user/{}'.format(os.getuid()) |
683 | + self._container.devices[run_user] = {'source': run_user, 'path': '/var/tmp{}'.format(run_user), 'type': 'disk'} |
684 | + |
685 | + mounts = utils.get_common_xdg_user_directories() + \ |
686 | + self._config.get_container_bind_mounts(self._id) |
687 | + for user_dir in utils.generate_binding_directories(mounts, home_path): |
688 | + if not os.path.exists(user_dir[0]): |
689 | + utils.get_logger().warning('Bind-mount path \'{}\' does not exist.'.format(user_dir[0])) |
690 | + continue |
691 | + |
692 | + self._container.devices[user_dir[1]] = { |
693 | + 'source': _readlink(user_dir[0]), |
694 | + 'path': os.path.join(home_path, user_dir[1]), |
695 | + 'type': 'disk' |
696 | + } |
697 | + |
698 | + try: |
699 | + self._container.save(wait=True) |
700 | + except pylxd.exceptions.LXDAPIException as e: |
701 | + utils.get_logger().warning('Saving bind mounts for container \'{}\' raised: {}'.format(self._id, str(e))) |
702 | + # This is most likely the result of the container currently running |
703 | + |
704 | + def _get_matchbox_pids(self): |
705 | + p = subprocess.Popen(self._lxc_args('pgrep matchbox'), stdout=subprocess.PIPE) |
706 | + out, err = p.communicate() |
707 | + return out.decode('utf-8').split('\n') |
708 | + |
709 | + # FIXME: Remove once window management logic has been moved to the host |
710 | + def _start_window_manager(self, args): |
711 | + args.extend(utils.setup_window_manager(self._id)) |
712 | + |
713 | + if 'matchbox-window-manager' in args: |
714 | + pids = self._get_matchbox_pids() |
715 | + |
716 | + self._window_manager = psutil.Popen(args) |
717 | + |
718 | + time.sleep(.25) # Give matchbox a moment to start in the container |
719 | + if self._window_manager.poll() is not None: |
720 | + utils.get_logger().warning("Window manager terminated prematurely with exit code '{}'".format( |
721 | + self._window_manager.returncode)) |
722 | + self._window_manager = None |
723 | + elif 'matchbox-window-manager' in args: |
724 | + after_pids = self._get_matchbox_pids() |
725 | + if len(after_pids) > len(pids): |
726 | + self._matchbox_pid = (set(after_pids) - set(pids)).pop() |
727 | + |
728 | + def start_application(self, app_exec_line, environ): |
729 | + if not self._try_get_container(): |
730 | + utils.get_logger().error("Could not get container '{}'".format(self._id)) |
731 | + return None |
732 | + |
733 | + self._update_libertine_profile() |
734 | + self._update_bind_mounts() |
735 | + self.start_container() |
736 | + |
737 | + args = self._lxc_args("sudo -E -u {} env PATH={}".format(os.environ['USER'], environ['PATH']), environ) |
738 | + |
739 | + self._start_window_manager(args.copy()) |
740 | + |
741 | + args.extend(app_exec_line) |
742 | + return psutil.Popen(args) |
743 | + |
744 | + def finish_application(self, app): |
745 | + if self._window_manager is not None: |
746 | + utils.terminate_window_manager(self._window_manager) |
747 | + self._window_manager = None |
748 | + |
749 | + # This is a workaround for an issue on xenial where the process |
750 | + # running the window manager is not killed with the application |
751 | + if self._matchbox_pid is not None: |
752 | + utils.get_logger().debug("Manually killing matchbox-window-manager") |
753 | + self.run_in_container("kill -9 {}".format(self._matchbox_pid)) |
754 | + self._matchbox_pid = None |
755 | + |
756 | + app.wait() |
757 | + |
758 | + def copy_file_to_container(self, source, dest): |
759 | + with open(source, 'rb') as f: |
760 | + return self._container.files.put(dest, f.read()) |
761 | + |
762 | + def delete_file_in_container(self, path): |
763 | + return self.run_in_container('rm {}'.format(path)) |
764 | + |
765 | + def _try_get_container(self): |
766 | + if self._container is None: |
767 | + try: |
768 | + self._container = self._client.containers.get(self._id) |
769 | + except pylxd.exceptions.LXDAPIException: |
770 | + self._container = None |
771 | + return False |
772 | + |
773 | + return True |
774 | |
775 | === modified file 'tests/unit/test_libertine_gir.py' |
776 | --- tests/unit/test_libertine_gir.py 2016-08-28 00:17:54 +0000 |
777 | +++ tests/unit/test_libertine_gir.py 2016-12-15 21:01:30 +0000 |
778 | @@ -49,14 +49,12 @@ |
779 | self.assertThat(container_home_path, Equals(self.cmake_source_dir + '/libertine-home/libertine-container/user-data/wily')) |
780 | |
781 | def test_container_name(self): |
782 | - container_id = 'wily' |
783 | with patch.dict('os.environ', {'XDG_DATA_HOME': self.cmake_source_dir + '/libertine-config'}): |
784 | - container_name = Libertine.container_name(container_id) |
785 | + container_name = Libertine.container_name('wily') |
786 | |
787 | self.assertThat(container_name, Equals("Ubuntu 'Wily Werewolf'")) |
788 | |
789 | - container_id = 'wily-2' |
790 | - container_name = Libertine.container_name(container_id) |
791 | + container_name = Libertine.container_name('wily-2') |
792 | |
793 | self.assertThat(container_name, Equals("Ubuntu 'Wily Werewolf' (2)")) |
794 | |
795 | |
796 | === modified file 'tools/CMakeLists.txt' |
797 | --- tools/CMakeLists.txt 2016-12-06 20:52:55 +0000 |
798 | +++ tools/CMakeLists.txt 2016-12-15 21:01:30 +0000 |
799 | @@ -1,4 +1,4 @@ |
800 | -install(PROGRAMS libertine-container-manager libertine-launch libertine-lxc-manager libertine-xmir libertine-lxc-setup libertined |
801 | +install(PROGRAMS libertine-container-manager libertine-launch libertine-lxc-manager libertine-xmir libertine-lxc-setup libertine-lxd-setup libertined |
802 | DESTINATION ${CMAKE_INSTALL_BINDIR}) |
803 | install(FILES libertine-launch.1 libertine-container-manager.1 libertine-lxc-manager.1 libertine-xmir.1 |
804 | DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 |
805 | |
806 | === modified file 'tools/libertine-container-manager' |
807 | --- tools/libertine-container-manager 2016-12-12 15:54:41 +0000 |
808 | +++ tools/libertine-container-manager 2016-12-15 21:01:30 +0000 |
809 | @@ -52,7 +52,7 @@ |
810 | if not args.type: |
811 | container_type = self.host_info.select_container_type_by_kernel() |
812 | else: |
813 | - if args.type == 'lxc' and not self.host_info.has_lxc_support(): |
814 | + if (args.type == 'lxc' or args.type == 'lxd') and not self.host_info.has_lxc_support(): |
815 | print("System kernel does not support lxc type containers. " |
816 | "Please either use chroot or omit the -t option.") |
817 | sys.exit(1) |
818 | |
819 | === added file 'tools/libertine-lxd-setup' |
820 | --- tools/libertine-lxd-setup 1970-01-01 00:00:00 +0000 |
821 | +++ tools/libertine-lxd-setup 2016-12-15 21:01:30 +0000 |
822 | @@ -0,0 +1,40 @@ |
823 | +#!/bin/sh |
824 | + |
825 | +if [ -z $1 ]; then |
826 | + echo "Usage: $0 username" |
827 | + exit 1 |
828 | +fi |
829 | + |
830 | +USERNAME=$1 |
831 | + |
832 | +# Create the lxd group and add given user |
833 | +if [ -z "`groups ${USERNAME} | grep lxd`" ]; then |
834 | + groupadd --force --system lxd |
835 | + usermod -G lxd -a $USERNAME |
836 | +fi |
837 | + |
838 | +# Map the given user to the container root user |
839 | +uid=`id --user ${USERNAME}` |
840 | +idmap="root:$uid:1" |
841 | +if [ -z "`grep ${idmap} /etc/subuid`" ]; then |
842 | + echo ${idmap} | tee -a /etc/subuid /etc/subgid |
843 | +fi |
844 | + |
845 | +# find the right lxc command |
846 | +lxc=`which lxc` |
847 | +if [ -z "${lxc}" ]; then |
848 | + if [ -n `which lxd.lxc` ]; then |
849 | + lxc=`which lxd.lxc` |
850 | + else |
851 | + echo "No lxc command found on this system." |
852 | + exit 1 |
853 | + fi |
854 | +fi |
855 | + |
856 | +# Run lxd init if there are no containers already on the system |
857 | +if [ 3 -ge `${lxc} list | wc -l` ]; then |
858 | + lxd init |
859 | +fi |
860 | + |
861 | +mkdir -p /home/$USERNAME/.config/lxc |
862 | +chown -R $USERNAME:$USERNAME /home/$USERNAME/.config/lxc |
FAILED: Continuous integration, rev:333 /jenkins. canonical. com/libertine/ job/lp- libertine- ci/237/ /jenkins. canonical. com/libertine/ job/build/ 496/console /jenkins. canonical. com/libertine/ job/build- 0-fetch/ 498 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= vivid+overlay/ 478 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= vivid+overlay/ 478/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= xenial+ overlay/ 478/console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= zesty/478 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=amd64, release= zesty/478/ artifact/ output/ *zip*/output. zip /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= vivid+overlay/ 478 /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= vivid+overlay/ 478/artifact/ output/ *zip*/output. zip /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= xenial+ overlay/ 478/console /jenkins. canonical. com/libertine/ job/build- 2-binpkg/ arch=i386, release= zesty/478/ console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /jenkins. canonical. com/libertine/ job/lp- libertine- ci/237/ rebuild
https:/