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