Merge lp:~townsend/libertine/release-1.5 into lp:libertine/trunk
- release-1.5
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Larry Price | ||||||||
Approved revision: | 182 | ||||||||
Merged at revision: | 179 | ||||||||
Proposed branch: | lp:~townsend/libertine/release-1.5 | ||||||||
Merge into: | lp:libertine/trunk | ||||||||
Diff against target: |
3383 lines (+1572/-672) 45 files modified
.bzrignore (+8/-0) CMakeLists.txt (+1/-1) common/ContainerConfigList.h (+1/-2) data/CMakeLists.txt (+4/-6) data/com.canonical.libertine.LxdManager.service (+3/-0) data/libertine-lxd-sudo (+1/-0) data/python3-libertine-chroot.click-hook.in (+0/-4) data/snap-runner.wrapper (+11/-0) debian/changelog (+20/-0) debian/control (+17/-4) debian/python3-libertine-chroot.install (+0/-1) debian/python3-libertine-lxd.install (+5/-0) debian/python3-libertine.install (+1/-0) debian/rules (+1/-1) liblibertine/libertine.cpp (+41/-26) parts/plugins/utils.py (+63/-0) parts/plugins/x-libertine-deps.py (+155/-0) parts/plugins/x-libertine.py (+45/-0) python/CMakeLists.txt (+1/-1) python/libertine/AppDiscovery.py (+1/-3) python/libertine/ChrootContainer.py (+30/-31) python/libertine/ContainersConfig.py (+3/-3) python/libertine/HostInfo.py (+5/-0) python/libertine/Libertine.py (+113/-125) python/libertine/LxcContainer.py (+67/-77) python/libertine/LxdContainer.py (+455/-0) python/libertine/launcher/session.py (+10/-13) python/libertine/launcher/task.py (+3/-3) python/libertine/lifecycle/ContainerLifecycleService.py (+108/-0) python/libertine/lifecycle/ContainerLifecycleServiceRunner.py (+46/-0) python/libertine/lifecycle/LifecycleResult.py (+37/-0) python/libertine/lifecycle/__init__.py (+23/-0) python/libertine/utils.py (+24/-3) snapcraft.yaml (+32/-0) tests/unit/test_libertine_gir.py (+2/-4) tests/unit/test_logger.py (+5/-1) tools/CMakeLists.txt (+4/-5) tools/libertine-container-manager (+55/-44) tools/libertine-container-manager.1 (+8/-2) tools/libertine-lxc-manager (+46/-123) tools/libertine-lxd-manager (+57/-0) tools/libertine-lxd-manager.1 (+9/-0) tools/libertine-lxd-setup (+40/-0) tools/libertined (+11/-6) tools/update-puritine-containers (+0/-183) |
||||||||
To merge this branch: | bzr merge lp:~townsend/libertine/release-1.5 | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Larry Price | Approve | ||
Review via email: mp+314179@code.launchpad.net |
Commit message
* Drop support for the Puritine click package as that is Vivid only and a Vivid Libertine branch exists for any future fixes.
* Only set the lxc log when a container is defined during class init. (LP: #1653973)
* Bump version to 1.5 for new upstream release.
* Logic for bundling libertine as a snap built from source.
* Catch exceptions raised during container creation.
* Initial implementation of lxd backend. (LP: #1580612)
* Use the libertine logger and LIBERTINE_DEBUG variable everywhere.
* Update configure bind-mount logic given new lxd backend.
* Use dpkg to find package name when installing local deb.
* Create d-bus service for lxd container management.
Description of the change
- 181. By Christopher Townsend
-
Update the version for the snap package as well.
- 182. By Larry Price
-
Merge from lp:libertine for fixes during testing and review.
Preview Diff
1 | === modified file '.bzrignore' | |||
2 | --- .bzrignore 2016-10-25 17:27:50 +0000 | |||
3 | +++ .bzrignore 2017-01-05 20:19:05 +0000 | |||
4 | @@ -2,3 +2,11 @@ | |||
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 | 6 | |||
10 | 7 | prime/ | ||
11 | 8 | stage/ | ||
12 | 9 | *.snap | ||
13 | 10 | parts/* | ||
14 | 11 | !parts/plugins | ||
15 | 12 | parts/plugins/__pycache__ | ||
16 | 5 | 13 | ||
17 | === modified file 'CMakeLists.txt' | |||
18 | --- CMakeLists.txt 2016-11-03 18:28:54 +0000 | |||
19 | +++ CMakeLists.txt 2017-01-05 20:19:05 +0000 | |||
20 | @@ -2,7 +2,7 @@ | |||
21 | 2 | cmake_policy(SET CMP0048 NEW) | 2 | cmake_policy(SET CMP0048 NEW) |
22 | 3 | 3 | ||
23 | 4 | project(libertine | 4 | project(libertine |
25 | 5 | VERSION 1.4.3) | 5 | VERSION 1.5) |
26 | 6 | 6 | ||
27 | 7 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}") | 7 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}") |
28 | 8 | 8 | ||
29 | 9 | 9 | ||
30 | === modified file 'common/ContainerConfigList.h' | |||
31 | --- common/ContainerConfigList.h 2016-09-26 18:17:07 +0000 | |||
32 | +++ common/ContainerConfigList.h 2017-01-05 20:19:05 +0000 | |||
33 | @@ -55,11 +55,10 @@ | |||
34 | 55 | * Display roles for a container config. | 55 | * Display roles for a container config. |
35 | 56 | */ | 56 | */ |
36 | 57 | enum class DataRole | 57 | enum class DataRole |
37 | 58 | : int | ||
38 | 59 | { | 58 | { |
39 | 60 | ContainerId = Qt::UserRole + 1, /**< The container ID */ | 59 | ContainerId = Qt::UserRole + 1, /**< The container ID */ |
40 | 61 | ContainerName, /**< The container name */ | 60 | ContainerName, /**< The container name */ |
42 | 62 | ContainerType, /**< The type of container - lxc or chroot */ | 61 | ContainerType, /**< The type of container */ |
43 | 63 | DistroSeries, /**< The distro from which the container was built */ | 62 | DistroSeries, /**< The distro from which the container was built */ |
44 | 64 | InstallStatus, /**< Current container install status */ | 63 | InstallStatus, /**< Current container install status */ |
45 | 65 | Error /**< last role (error) */ | 64 | Error /**< last role (error) */ |
46 | 66 | 65 | ||
47 | === modified file 'data/CMakeLists.txt' | |||
48 | --- data/CMakeLists.txt 2016-11-01 20:10:58 +0000 | |||
49 | +++ data/CMakeLists.txt 2017-01-05 20:19:05 +0000 | |||
50 | @@ -6,11 +6,9 @@ | |||
51 | 6 | DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}) | 6 | DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}) |
52 | 7 | install(FILES libertine-xmir.conf | 7 | install(FILES libertine-xmir.conf |
53 | 8 | DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions) | 8 | DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions) |
55 | 9 | install(FILES libertine-lxc-sudo | 9 | install(FILES libertine-lxc-sudo libertine-lxd-sudo |
56 | 10 | DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sudoers.d) | 10 | DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sudoers.d) |
58 | 11 | install(FILES com.canonical.libertine.LxcManager.service com.canonical.libertine.ContainerManager.service | 11 | install(FILES com.canonical.libertine.ContainerManager.service |
59 | 12 | com.canonical.libertine.LxcManager.service | ||
60 | 13 | com.canonical.libertine.LxdManager.service | ||
61 | 12 | DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/services) | 14 | DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/services) |
62 | 13 | |||
63 | 14 | configure_file("python3-libertine-chroot.click-hook.in" | ||
64 | 15 | "${CMAKE_SOURCE_DIR}/debian/python3-libertine-chroot.click-hook" | ||
65 | 16 | @ONLY) | ||
66 | 17 | 15 | ||
67 | === added file 'data/com.canonical.libertine.LxdManager.service' | |||
68 | --- data/com.canonical.libertine.LxdManager.service 1970-01-01 00:00:00 +0000 | |||
69 | +++ data/com.canonical.libertine.LxdManager.service 2017-01-05 20:19:05 +0000 | |||
70 | @@ -0,0 +1,3 @@ | |||
71 | 1 | [D-BUS Service] | ||
72 | 2 | Name=com.canonical.libertine.LxdManager | ||
73 | 3 | Exec=/usr/bin/libertine-lxd-manager | ||
74 | 0 | 4 | ||
75 | === added file 'data/libertine-lxd-sudo' | |||
76 | --- data/libertine-lxd-sudo 1970-01-01 00:00:00 +0000 | |||
77 | +++ data/libertine-lxd-sudo 2017-01-05 20:19:05 +0000 | |||
78 | @@ -0,0 +1,1 @@ | |||
79 | 1 | ALL ALL=(ALL) NOPASSWD:/usr/bin/libertine-lxd-setup | ||
80 | 0 | 2 | ||
81 | === removed file 'data/python3-libertine-chroot.click-hook.in' | |||
82 | --- data/python3-libertine-chroot.click-hook.in 2016-09-06 12:36:47 +0000 | |||
83 | +++ data/python3-libertine-chroot.click-hook.in 1970-01-01 00:00:00 +0000 | |||
84 | @@ -1,4 +0,0 @@ | |||
85 | 1 | Pattern: ${home}/.cache/libertine/puritine/${id} | ||
86 | 2 | Exec: @CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@/libertine/update-puritine-containers | ||
87 | 3 | User-Level: yes | ||
88 | 4 | Hook-Name: puritine | ||
89 | 5 | 0 | ||
90 | === added file 'data/snap-runner.wrapper' | |||
91 | --- data/snap-runner.wrapper 1970-01-01 00:00:00 +0000 | |||
92 | +++ data/snap-runner.wrapper 2017-01-05 20:19:05 +0000 | |||
93 | @@ -0,0 +1,11 @@ | |||
94 | 1 | #!/bin/bash | ||
95 | 2 | # additional env modifications on top of desktop-launch | ||
96 | 3 | |||
97 | 4 | export LD_LIBRARY_PATH=$SNAP/usr/lib/$ARCH/fakechroot:$LD_LIBRARY_PATH | ||
98 | 5 | export DEBOOTSTRAP_DIR=$SNAP/usr/share/debootstrap | ||
99 | 6 | |||
100 | 7 | # Useful debug variables | ||
101 | 8 | # export FAKECHROOT_DEBUG=1 | ||
102 | 9 | # export LIBERTINE_DEBUG=1 | ||
103 | 10 | |||
104 | 11 | exec $SNAP/bin/desktop-launch $@ | ||
105 | 0 | 12 | ||
106 | === modified file 'debian/changelog' | |||
107 | --- debian/changelog 2016-12-05 21:10:03 +0000 | |||
108 | +++ debian/changelog 2017-01-05 20:19:05 +0000 | |||
109 | @@ -1,3 +1,23 @@ | |||
110 | 1 | libertine (1.5-0ubuntu1) UNRELEASED; urgency=medium | ||
111 | 2 | |||
112 | 3 | [ Chris Townsend ] | ||
113 | 4 | * Drop support for the Puritine click package as that is Vivid only | ||
114 | 5 | and a Vivid Libertine branch exists for any future fixes. | ||
115 | 6 | * Only set the lxc log when a container is defined during class init. | ||
116 | 7 | (LP: #1653973) | ||
117 | 8 | * Bump version to 1.5 for new upstream release. | ||
118 | 9 | |||
119 | 10 | [ Larry Price ] | ||
120 | 11 | * Logic for bundling libertine as a snap built from source. | ||
121 | 12 | * Catch exceptions raised during container creation. | ||
122 | 13 | * Initial implementation of lxd backend. (LP: #1580612) | ||
123 | 14 | * Use the libertine logger and LIBERTINE_DEBUG variable everywhere. | ||
124 | 15 | * Update configure bind-mount logic given new lxd backend. | ||
125 | 16 | * Use dpkg to find package name when installing local deb. | ||
126 | 17 | * Create d-bus service for lxd container management. | ||
127 | 18 | |||
128 | 19 | -- Chris Townsend <christopher.townsend@canonical.com> Thu, 05 Jan 2017 11:46:42 -0500 | ||
129 | 20 | |||
130 | 1 | libertine (1.4.4+17.04.20161205-0ubuntu1) zesty; urgency=medium | 21 | libertine (1.4.4+17.04.20161205-0ubuntu1) zesty; urgency=medium |
131 | 2 | 22 | ||
132 | 3 | [ Chris Townsend ] | 23 | [ Chris Townsend ] |
133 | 4 | 24 | ||
134 | === modified file 'debian/control' | |||
135 | --- debian/control 2016-11-22 17:47:05 +0000 | |||
136 | +++ debian/control 2017-01-05 20:19:05 +0000 | |||
137 | @@ -2,8 +2,7 @@ | |||
138 | 2 | Section: utils | 2 | Section: utils |
139 | 3 | Priority: extra | 3 | Priority: extra |
140 | 4 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> | 4 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
143 | 5 | Build-Depends: click-dev, | 5 | Build-Depends: cmake, |
142 | 6 | cmake, | ||
144 | 7 | cmake-extras, | 6 | cmake-extras, |
145 | 8 | debhelper (>= 9), | 7 | debhelper (>= 9), |
146 | 9 | dh-translations, | 8 | dh-translations, |
147 | @@ -61,8 +60,7 @@ | |||
148 | 61 | Depends: libertine-qt-common, | 60 | Depends: libertine-qt-common, |
149 | 62 | libertine-tools, | 61 | libertine-tools, |
150 | 63 | python3-libertine-lxc, | 62 | python3-libertine-lxc, |
153 | 64 | qml-module-qtquick2, | 63 | libsystemsettings1, |
152 | 65 | qtdeclarative5-ubuntu-ui-toolkit-plugin, | ||
154 | 66 | ${misc:Depends}, | 64 | ${misc:Depends}, |
155 | 67 | ${shlibs:Depends} | 65 | ${shlibs:Depends} |
156 | 68 | Enhances: ubuntu-system-settings | 66 | Enhances: ubuntu-system-settings |
157 | @@ -184,6 +182,21 @@ | |||
158 | 184 | Libertine sandbox. It requires support for unprivileged LXC containers in the | 182 | Libertine sandbox. It requires support for unprivileged LXC containers in the |
159 | 185 | Linux kernel. | 183 | Linux kernel. |
160 | 186 | 184 | ||
161 | 185 | Package: python3-libertine-lxd | ||
162 | 186 | Architecture: any | ||
163 | 187 | Section: python | ||
164 | 188 | Multi-Arch: allowed | ||
165 | 189 | Depends: lxd, | ||
166 | 190 | python3-libertine, | ||
167 | 191 | python3-pexpect, | ||
168 | 192 | python3-pylxd, | ||
169 | 193 | ${misc:Depends}, | ||
170 | 194 | ${python3:Depends} | ||
171 | 195 | Description: Python3 scripts for the Libertine application sandbox | ||
172 | 196 | This package provides the LXD-based container back end module for the | ||
173 | 197 | Libertine sandbox. It requires support for unprivileged LXD containers in the | ||
174 | 198 | Linux kernel. | ||
175 | 199 | |||
176 | 187 | Package: python3-libertine-chroot | 200 | Package: python3-libertine-chroot |
177 | 188 | Architecture: any | 201 | Architecture: any |
178 | 189 | Section: python | 202 | Section: python |
179 | 190 | 203 | ||
180 | === modified file 'debian/python3-libertine-chroot.install' | |||
181 | --- debian/python3-libertine-chroot.install 2016-10-07 21:09:41 +0000 | |||
182 | +++ debian/python3-libertine-chroot.install 2017-01-05 20:19:05 +0000 | |||
183 | @@ -1,2 +1,1 @@ | |||
184 | 1 | usr/lib/*/libertine/update-puritine-containers | ||
185 | 2 | usr/lib/python*/*/libertine/ChrootContainer.py | 1 | usr/lib/python*/*/libertine/ChrootContainer.py |
186 | 3 | 2 | ||
187 | === added file 'debian/python3-libertine-lxd.install' | |||
188 | --- debian/python3-libertine-lxd.install 1970-01-01 00:00:00 +0000 | |||
189 | +++ debian/python3-libertine-lxd.install 2017-01-05 20:19:05 +0000 | |||
190 | @@ -0,0 +1,5 @@ | |||
191 | 1 | etc/sudoers.d/libertine-lxd-sudo | ||
192 | 2 | usr/bin/libertine-lxd-setup | ||
193 | 3 | usr/bin/libertine-lxd-manager | ||
194 | 4 | usr/lib/python*/*/libertine/LxdContainer.py | ||
195 | 5 | usr/share/dbus-1/services/com.canonical.libertine.LxdManager.service | ||
196 | 0 | 6 | ||
197 | === modified file 'debian/python3-libertine.install' | |||
198 | --- debian/python3-libertine.install 2016-10-28 19:37:32 +0000 | |||
199 | +++ debian/python3-libertine.install 2017-01-05 20:19:05 +0000 | |||
200 | @@ -4,5 +4,6 @@ | |||
201 | 4 | usr/lib/python*/*/libertine/Libertine.py | 4 | usr/lib/python*/*/libertine/Libertine.py |
202 | 5 | usr/lib/python*/*/libertine/__init__.py | 5 | usr/lib/python*/*/libertine/__init__.py |
203 | 6 | usr/lib/python*/*/libertine/launcher | 6 | usr/lib/python*/*/libertine/launcher |
204 | 7 | usr/lib/python*/*/libertine/lifecycle | ||
205 | 7 | usr/lib/python*/*/libertine/service | 8 | usr/lib/python*/*/libertine/service |
206 | 8 | usr/lib/python*/*/libertine/utils.py | 9 | usr/lib/python*/*/libertine/utils.py |
207 | 9 | 10 | ||
208 | === modified file 'debian/rules' | |||
209 | --- debian/rules 2016-12-05 15:32:46 +0000 | |||
210 | +++ debian/rules 2017-01-05 20:19:05 +0000 | |||
211 | @@ -1,7 +1,7 @@ | |||
212 | 1 | #!/usr/bin/make -f | 1 | #!/usr/bin/make -f |
213 | 2 | 2 | ||
214 | 3 | %: | 3 | %: |
216 | 4 | dh $@ --with python3,gir,click | 4 | dh $@ --with python3,gir |
217 | 5 | 5 | ||
218 | 6 | override_dh_auto_test: | 6 | override_dh_auto_test: |
219 | 7 | dbus-run-session -- dh_auto_test | 7 | dbus-run-session -- dh_auto_test |
220 | 8 | 8 | ||
221 | === modified file 'liblibertine/libertine.cpp' | |||
222 | --- liblibertine/libertine.cpp 2016-10-26 13:58:19 +0000 | |||
223 | +++ liblibertine/libertine.cpp 2017-01-05 20:19:05 +0000 | |||
224 | @@ -67,8 +67,16 @@ | |||
225 | 67 | g_dir_close(dir); | 67 | g_dir_close(dir); |
226 | 68 | return nullptr; | 68 | return nullptr; |
227 | 69 | } | 69 | } |
230 | 70 | } | 70 | |
231 | 71 | 71 | ||
232 | 72 | gchar* | ||
233 | 73 | id_from_list_index(const ContainerConfigList& container_list, guint index) | ||
234 | 74 | { | ||
235 | 75 | return (gchar*)container_list.data(container_list.index(index, 0), | ||
236 | 76 | (int)ContainerConfigList::DataRole::ContainerId) | ||
237 | 77 | .toString().toStdString().c_str(); | ||
238 | 78 | } | ||
239 | 79 | } | ||
240 | 72 | 80 | ||
241 | 73 | gchar** | 81 | gchar** |
242 | 74 | libertine_list_apps_for_container(const gchar* container_id) | 82 | libertine_list_apps_for_container(const gchar* container_id) |
243 | @@ -137,61 +145,68 @@ | |||
244 | 137 | gchar * | 145 | gchar * |
245 | 138 | libertine_container_path(const gchar * container_id) | 146 | libertine_container_path(const gchar * container_id) |
246 | 139 | { | 147 | { |
247 | 148 | g_return_val_if_fail(container_id != nullptr, nullptr); | ||
248 | 149 | LibertineConfig config; | ||
249 | 150 | ContainerConfigList container_list(&config); | ||
250 | 140 | gchar * path = nullptr; | 151 | gchar * path = nullptr; |
251 | 141 | g_return_val_if_fail(container_id != nullptr, nullptr); | ||
252 | 142 | 152 | ||
254 | 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) |
255 | 154 | { | ||
256 | 155 | path = g_build_filename("/", "var", "lib", "lxd", "containers", container_id, "rootfs", nullptr); | ||
257 | 156 | } | ||
258 | 157 | else | ||
259 | 158 | { | ||
260 | 159 | path = g_build_filename(g_get_user_cache_dir(), "libertine-container", container_id, "rootfs", nullptr); | ||
261 | 160 | } | ||
262 | 144 | 161 | ||
263 | 145 | if (g_file_test(path, G_FILE_TEST_EXISTS)) | 162 | if (g_file_test(path, G_FILE_TEST_EXISTS)) |
264 | 146 | { | 163 | { |
265 | 147 | return path; | 164 | return path; |
266 | 148 | } | 165 | } |
272 | 149 | else | 166 | |
273 | 150 | { | 167 | g_free(path); |
274 | 151 | g_free(path); | 168 | return nullptr; |
270 | 152 | return nullptr; | ||
271 | 153 | } | ||
275 | 154 | } | 169 | } |
276 | 155 | 170 | ||
277 | 156 | 171 | ||
278 | 157 | gchar * | 172 | gchar * |
279 | 158 | libertine_container_home_path(const gchar * container_id) | 173 | libertine_container_home_path(const gchar * container_id) |
280 | 159 | { | 174 | { |
281 | 175 | g_return_val_if_fail(container_id != nullptr, nullptr); | ||
282 | 176 | LibertineConfig config; | ||
283 | 177 | ContainerConfigList container_list(&config); | ||
284 | 160 | gchar * path = nullptr; | 178 | gchar * path = nullptr; |
285 | 161 | g_return_val_if_fail(container_id != nullptr, nullptr); | ||
286 | 162 | 179 | ||
288 | 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) |
289 | 181 | { | ||
290 | 182 | path = g_build_filename("/", "var", "lib", "lxd", "containers", container_id, "rootfs", "home", g_get_user_name(), nullptr); | ||
291 | 183 | } | ||
292 | 184 | else | ||
293 | 185 | { | ||
294 | 186 | path = g_build_filename(g_get_user_data_dir(), "libertine-container", "user-data", container_id, nullptr); | ||
295 | 187 | } | ||
296 | 164 | 188 | ||
297 | 165 | if (g_file_test(path, G_FILE_TEST_EXISTS)) | 189 | if (g_file_test(path, G_FILE_TEST_EXISTS)) |
298 | 166 | { | 190 | { |
299 | 167 | return path; | 191 | return path; |
300 | 168 | } | 192 | } |
301 | 169 | else | ||
302 | 170 | { | ||
303 | 171 | g_free(path); | ||
304 | 172 | return nullptr; | ||
305 | 173 | } | ||
306 | 174 | 193 | ||
307 | 194 | g_free(path); | ||
308 | 195 | return nullptr; | ||
309 | 175 | } | 196 | } |
310 | 176 | 197 | ||
311 | 177 | 198 | ||
312 | 178 | gchar * | 199 | gchar * |
313 | 179 | libertine_container_name(const gchar * container_id) | 200 | libertine_container_name(const gchar * container_id) |
314 | 180 | { | 201 | { |
315 | 181 | guint container_count; | ||
316 | 182 | guint i; | ||
317 | 183 | gchar * container_name = nullptr; | 202 | gchar * container_name = nullptr; |
318 | 184 | LibertineConfig config; | 203 | LibertineConfig config; |
319 | 185 | ContainerConfigList container_list(&config); | 204 | ContainerConfigList container_list(&config); |
325 | 186 | QVariant id; | 205 | guint container_count = (guint)container_list.size(); |
326 | 187 | 206 | ||
327 | 188 | container_count = (guint)container_list.size(); | 207 | for (guint i = 0; i < container_count; ++i) |
323 | 189 | |||
324 | 190 | for (i = 0; i < container_count; ++i) | ||
328 | 191 | { | 208 | { |
332 | 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) |
330 | 193 | |||
331 | 194 | if (g_strcmp0((gchar *)id.toString().toStdString().c_str(), container_id) == 0) | ||
333 | 195 | { | 210 | { |
334 | 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); |
335 | 197 | container_name = g_strdup(name.toString().toStdString().c_str()); | 212 | container_name = g_strdup(name.toString().toStdString().c_str()); |
336 | 198 | 213 | ||
337 | === added directory 'parts' | |||
338 | === added directory 'parts/plugins' | |||
339 | === added file 'parts/plugins/utils.py' | |||
340 | --- parts/plugins/utils.py 1970-01-01 00:00:00 +0000 | |||
341 | +++ parts/plugins/utils.py 2017-01-05 20:19:05 +0000 | |||
342 | @@ -0,0 +1,63 @@ | |||
343 | 1 | # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- | ||
344 | 2 | # | ||
345 | 3 | # Copyright (C) 2016 Canonical Ltd | ||
346 | 4 | # | ||
347 | 5 | # This program is free software: you can redistribute it and/or modify | ||
348 | 6 | # it under the terms of the GNU General Public License version 3 as | ||
349 | 7 | # published by the Free Software Foundation. | ||
350 | 8 | # | ||
351 | 9 | # This program is distributed in the hope that it will be useful, | ||
352 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
353 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
354 | 12 | # GNU General Public License for more details. | ||
355 | 13 | # | ||
356 | 14 | # You should have received a copy of the GNU General Public License | ||
357 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
358 | 16 | |||
359 | 17 | import re | ||
360 | 18 | from snapcraft import file_utils | ||
361 | 19 | |||
362 | 20 | |||
363 | 21 | def fix_shebangs(path): | ||
364 | 22 | file_utils.replace_in_file(path, re.compile(r''), | ||
365 | 23 | re.compile(r'^#!.*python'), | ||
366 | 24 | r'#!/usr/bin/env python') | ||
367 | 25 | |||
368 | 26 | |||
369 | 27 | def _sanitize(dep): | ||
370 | 28 | return re.sub(r"\(.*\)", "", re.sub(r"\[.*\]", "", dep)).replace(',', '').strip() | ||
371 | 29 | |||
372 | 30 | |||
373 | 31 | class DependsParser(object): | ||
374 | 32 | def __init__(self): | ||
375 | 33 | self.keyword = 'Depends:' | ||
376 | 34 | self._parsing = False | ||
377 | 35 | self._deps = [] | ||
378 | 36 | self._packages = [] | ||
379 | 37 | |||
380 | 38 | @property | ||
381 | 39 | def deps(self): | ||
382 | 40 | return [dep for dep in self._deps if dep not in self._packages] | ||
383 | 41 | |||
384 | 42 | def parse(self, line): | ||
385 | 43 | if self._parsing: | ||
386 | 44 | if ':' in line: | ||
387 | 45 | if not line.strip().startswith('${'): | ||
388 | 46 | self._parsing = False | ||
389 | 47 | else: | ||
390 | 48 | self._deps.append(_sanitize(line)) | ||
391 | 49 | |||
392 | 50 | if line.startswith(self.keyword): | ||
393 | 51 | self._parsing = True | ||
394 | 52 | possible_dep = line.lstrip(self.keyword) | ||
395 | 53 | if not possible_dep.isspace() and not possible_dep.strip().startswith('${'): | ||
396 | 54 | self._deps.append(_sanitize(possible_dep)) | ||
397 | 55 | |||
398 | 56 | if line.startswith('Package:'): | ||
399 | 57 | self._packages.append(line.lstrip('Package:').strip()) | ||
400 | 58 | |||
401 | 59 | |||
402 | 60 | class BuildDependsParser(DependsParser): | ||
403 | 61 | def __init__(self): | ||
404 | 62 | super().__init__() | ||
405 | 63 | self.keyword = 'Build-Depends:' | ||
406 | 0 | 64 | ||
407 | === added file 'parts/plugins/x-libertine-deps.py' | |||
408 | --- parts/plugins/x-libertine-deps.py 1970-01-01 00:00:00 +0000 | |||
409 | +++ parts/plugins/x-libertine-deps.py 2017-01-05 20:19:05 +0000 | |||
410 | @@ -0,0 +1,155 @@ | |||
411 | 1 | # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- | ||
412 | 2 | # | ||
413 | 3 | # Copyright (C) 2016 Canonical Ltd | ||
414 | 4 | # | ||
415 | 5 | # This program is free software: you can redistribute it and/or modify | ||
416 | 6 | # it under the terms of the GNU General Public License version 3 as | ||
417 | 7 | # published by the Free Software Foundation. | ||
418 | 8 | # | ||
419 | 9 | # This program is distributed in the hope that it will be useful, | ||
420 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
421 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
422 | 12 | # GNU General Public License for more details. | ||
423 | 13 | # | ||
424 | 14 | # You should have received a copy of the GNU General Public License | ||
425 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
426 | 16 | |||
427 | 17 | |||
428 | 18 | import fileinput | ||
429 | 19 | import os | ||
430 | 20 | import re | ||
431 | 21 | import shlex | ||
432 | 22 | import snapcraft.plugins.nil | ||
433 | 23 | import sys | ||
434 | 24 | import subprocess | ||
435 | 25 | import utils # local | ||
436 | 26 | |||
437 | 27 | |||
438 | 28 | def _arch(): | ||
439 | 29 | cmd = subprocess.Popen(shlex.split('dpkg-architecture -qDEB_HOST_ARCH_CPU'), stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||
440 | 30 | out, err = cmd.communicate() | ||
441 | 31 | arch = str(out, 'utf-8').strip() | ||
442 | 32 | if arch == 'amd64': | ||
443 | 33 | return 'x86_64-linux-gnu' | ||
444 | 34 | elif arch == 'armhf': | ||
445 | 35 | return 'arm-linux-gnueabihf' | ||
446 | 36 | elif arch == 'arm64': | ||
447 | 37 | return 'aarch64-linux-gnu' | ||
448 | 38 | |||
449 | 39 | return '{}-linux-gnu'.format(arch) | ||
450 | 40 | |||
451 | 41 | |||
452 | 42 | class LibertineDependenciesPlugin(snapcraft.plugins.nil.NilPlugin): | ||
453 | 43 | def __init__(self, name, options, project): | ||
454 | 44 | super().__init__(name, options, project) | ||
455 | 45 | self._arch = _arch() | ||
456 | 46 | |||
457 | 47 | deps_parser = utils.DependsParser() | ||
458 | 48 | with open('debian/control') as control: | ||
459 | 49 | for line in control.readlines(): | ||
460 | 50 | deps_parser.parse(line) | ||
461 | 51 | self.stage_packages.extend(deps_parser.deps) | ||
462 | 52 | |||
463 | 53 | self._ignore_duplicate_files() | ||
464 | 54 | |||
465 | 55 | @classmethod | ||
466 | 56 | def schema(cls): | ||
467 | 57 | return super().schema() | ||
468 | 58 | |||
469 | 59 | def enable_cross_compilation(self): | ||
470 | 60 | return super().enable_cross_compilation() | ||
471 | 61 | |||
472 | 62 | def env(self, root): | ||
473 | 63 | return super().env(root) + ['ARCH={}'.format(self._arch)] | ||
474 | 64 | |||
475 | 65 | # By design, snapcraft ignores all pre- and post-inst scripts which | ||
476 | 66 | # result in a bunch of packages that just don't work. | ||
477 | 67 | def _run_preinst_postinst(self): | ||
478 | 68 | # We need to create the lxc-usernet | ||
479 | 69 | if not os.path.exists(self.installdir + '/etc/lxc/lxc-usernet'): | ||
480 | 70 | with open(self.installdir + '/etc/lxc/lxc-usernet', 'w') as f: | ||
481 | 71 | f.write('# USERNAME TYPE BRIDGE COUNT') | ||
482 | 72 | |||
483 | 73 | # We need to run update-alternatives to create /usr/bin/fakeroot | ||
484 | 74 | admindir = self.installdir + '/var/lib/dpkg/alternatives' | ||
485 | 75 | altdir = self.installdir + '/etc/alternatives' | ||
486 | 76 | os.makedirs(admindir, exist_ok=True) | ||
487 | 77 | os.makedirs(altdir, exist_ok=True) | ||
488 | 78 | subprocess.Popen(shlex.split('\ | ||
489 | 79 | update-alternatives --admindir {0} --altdir {1} --install \ | ||
490 | 80 | {2}/usr/bin/fakeroot fakeroot {2}/usr/bin/fakeroot-sysv 50 \ | ||
491 | 81 | --slave {2}/usr/share/man/man1/fakeroot.1.gz \ | ||
492 | 82 | fakeroot.1.gz {2}/usr/share/man/man1/fakeroot-sysv.1.gz \ | ||
493 | 83 | --slave {2}/usr/share/man/man1/faked.1.gz \ | ||
494 | 84 | faked.1.gz {2}/usr/share/man/man1/faked-sysv.1.gz \ | ||
495 | 85 | --slave {2}/usr/share/man/es/man1/fakeroot.1.gz \ | ||
496 | 86 | fakeroot.es.1.gz {2}/usr/share/man/es/man1/fakeroot-sysv.1.gz \ | ||
497 | 87 | --slave {2}/usr/share/man/es/man1/faked.1.gz \ | ||
498 | 88 | faked.es.1.gz {2}/usr/share/man/es/man1/faked-sysv.1.gz \ | ||
499 | 89 | --slave {2}/usr/share/man/fr/man1/fakeroot.1.gz \ | ||
500 | 90 | fakeroot.fr.1.gz {2}/usr/share/man/fr/man1/fakeroot-sysv.1.gz \ | ||
501 | 91 | --slave {2}/usr/share/man/fr/man1/faked.1.gz \ | ||
502 | 92 | faked.fr.1.gz {2}/usr/share/man/fr/man1/faked-sysv.1.gz \ | ||
503 | 93 | --slave {2}/usr/share/man/sv/man1/fakeroot.1.gz \ | ||
504 | 94 | fakeroot.sv.1.gz {2}/usr/share/man/sv/man1/fakeroot-sysv.1.gz \ | ||
505 | 95 | --slave {2}/usr/share/man/sv/man1/faked.1.gz \ | ||
506 | 96 | faked.sv.1.gz {2}/usr/share/man/sv/man1/faked-sysv.1.gz\ | ||
507 | 97 | '.format(admindir, altdir, self.installdir))).wait() | ||
508 | 98 | subprocess.Popen(shlex.split('\ | ||
509 | 99 | update-alternatives --admindir {0} --altdir {1} --install \ | ||
510 | 100 | {2}/usr/bin/fakeroot fakeroot {2}/usr/bin/fakeroot-tcp 30 \ | ||
511 | 101 | --slave {2}/usr/share/man/man1/fakeroot.1.gz \ | ||
512 | 102 | fakeroot.1.gz {2}/usr/share/man/man1/fakeroot-tcp.1.gz \ | ||
513 | 103 | --slave {2}/usr/share/man/man1/faked.1.gz \ | ||
514 | 104 | faked.1.gz {2}/usr/share/man/man1/faked-tcp.1.gz \ | ||
515 | 105 | --slave {2}/usr/share/man/es/man1/fakeroot.1.gz \ | ||
516 | 106 | fakeroot.es.1.gz {2}/usr/share/man/es/man1/fakeroot-tcp.1.gz \ | ||
517 | 107 | --slave {2}/usr/share/man/es/man1/faked.1.gz \ | ||
518 | 108 | faked.es.1.gz {2}/usr/share/man/es/man1/faked-tcp.1.gz \ | ||
519 | 109 | --slave {2}/usr/share/man/fr/man1/fakeroot.1.gz \ | ||
520 | 110 | fakeroot.fr.1.gz {2}/usr/share/man/fr/man1/fakeroot-tcp.1.gz \ | ||
521 | 111 | --slave {2}/usr/share/man/fr/man1/faked.1.gz \ | ||
522 | 112 | faked.fr.1.gz {2}/usr/share/man/fr/man1/faked-tcp.1.gz \ | ||
523 | 113 | --slave {2}/usr/share/man/sv/man1/fakeroot.1.gz \ | ||
524 | 114 | fakeroot.sv.1.gz {2}/usr/share/man/sv/man1/fakeroot-tcp.1.gz \ | ||
525 | 115 | --slave {2}/usr/share/man/sv/man1/faked.1.gz \ | ||
526 | 116 | faked.sv.1.gz {2}/usr/share/man/sv/man1/faked-tcp.1.gz\ | ||
527 | 117 | '.format(admindir, altdir, self.installdir))).wait() | ||
528 | 118 | |||
529 | 119 | def _fix_symlinks(self): | ||
530 | 120 | for root, dirs, files in os.walk(self.installdir): | ||
531 | 121 | for f in files: | ||
532 | 122 | path = '{}/{}'.format(root, f) | ||
533 | 123 | if os.path.islink(path): | ||
534 | 124 | link = os.readlink(path) | ||
535 | 125 | if link.startswith('/'): # needs fixing | ||
536 | 126 | os.remove(path) | ||
537 | 127 | os.symlink(os.path.relpath(link, root), path) | ||
538 | 128 | |||
539 | 129 | def _fix_fakeroot(self): | ||
540 | 130 | for line in fileinput.FileInput('{}/usr/bin/fakeroot'.format(self.installdir), inplace=True): | ||
541 | 131 | if line.startswith('FAKEROOT_PREFIX='): | ||
542 | 132 | sys.stdout.write('FAKEROOT_PREFIX=${SNAP}/usr # Updated by x-libertine.py\n') | ||
543 | 133 | elif line.startswith('FAKEROOT_BINDIR='): | ||
544 | 134 | sys.stdout.write('FAKEROOT_BINDIR=${SNAP}/usr/bin # Updated by x-libertine.py\n') | ||
545 | 135 | elif line.startswith('PATHS='): | ||
546 | 136 | sys.stdout.write('PATHS=${FAKEROOT_PREFIX}/lib/%s/libfakeroot:' \ | ||
547 | 137 | '${FAKEROOT_PREFIX}/lib64/libfakeroot:' \ | ||
548 | 138 | '${FAKEROOT_PREFIX}/lib32/libfakeroot' \ | ||
549 | 139 | ' # Updated by x-libertine.py\n' % self._arch) | ||
550 | 140 | else: | ||
551 | 141 | sys.stdout.write(line) | ||
552 | 142 | |||
553 | 143 | def _ignore_duplicate_files(self): | ||
554 | 144 | self.options.stage.extend([ | ||
555 | 145 | '-usr/lib/{}/liblibertine.so*'.format(self._arch) | ||
556 | 146 | ]) | ||
557 | 147 | |||
558 | 148 | def build(self): | ||
559 | 149 | super().build() | ||
560 | 150 | |||
561 | 151 | utils.fix_shebangs(self.installdir + '/usr/bin') | ||
562 | 152 | |||
563 | 153 | self._run_preinst_postinst() | ||
564 | 154 | self._fix_symlinks() | ||
565 | 155 | self._fix_fakeroot() | ||
566 | 0 | 156 | ||
567 | === added file 'parts/plugins/x-libertine.py' | |||
568 | --- parts/plugins/x-libertine.py 1970-01-01 00:00:00 +0000 | |||
569 | +++ parts/plugins/x-libertine.py 2017-01-05 20:19:05 +0000 | |||
570 | @@ -0,0 +1,45 @@ | |||
571 | 1 | # -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- | ||
572 | 2 | # | ||
573 | 3 | # Copyright (C) 2016 Canonical Ltd | ||
574 | 4 | # | ||
575 | 5 | # This program is free software: you can redistribute it and/or modify | ||
576 | 6 | # it under the terms of the GNU General Public License version 3 as | ||
577 | 7 | # published by the Free Software Foundation. | ||
578 | 8 | # | ||
579 | 9 | # This program is distributed in the hope that it will be useful, | ||
580 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
581 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
582 | 12 | # GNU General Public License for more details. | ||
583 | 13 | # | ||
584 | 14 | # You should have received a copy of the GNU General Public License | ||
585 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
586 | 16 | |||
587 | 17 | |||
588 | 18 | import snapcraft.plugins.cmake | ||
589 | 19 | import utils # local | ||
590 | 20 | |||
591 | 21 | class LibertinePlugin(snapcraft.plugins.cmake.CMakePlugin): | ||
592 | 22 | def __init__(self, name, options, project): | ||
593 | 23 | super().__init__(name, options, project) | ||
594 | 24 | |||
595 | 25 | deps = utils.BuildDependsParser() | ||
596 | 26 | |||
597 | 27 | with open('debian/control') as control: | ||
598 | 28 | for line in control.readlines(): | ||
599 | 29 | deps.parse(line) | ||
600 | 30 | |||
601 | 31 | self.build_packages.extend(deps.deps) | ||
602 | 32 | self.options.configflags.extend(['-DCMAKE_INSTALL_PREFIX=/usr']) | ||
603 | 33 | |||
604 | 34 | @classmethod | ||
605 | 35 | def schema(cls): | ||
606 | 36 | return super().schema() | ||
607 | 37 | |||
608 | 38 | def enable_cross_compilation(self): | ||
609 | 39 | return super().enable_cross_compilation() | ||
610 | 40 | |||
611 | 41 | def build(self): | ||
612 | 42 | super().build() | ||
613 | 43 | |||
614 | 44 | # Fix all shebangs to use the in-snap python. | ||
615 | 45 | utils.fix_shebangs(self.installdir + '/usr/bin') | ||
616 | 0 | 46 | ||
617 | === modified file 'python/CMakeLists.txt' | |||
618 | --- python/CMakeLists.txt 2015-07-20 15:00:27 +0000 | |||
619 | +++ python/CMakeLists.txt 2017-01-05 20:19:05 +0000 | |||
620 | @@ -1,4 +1,4 @@ | |||
622 | 1 | execute_process(COMMAND python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" | 1 | execute_process(COMMAND /usr/bin/python3 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())" |
623 | 2 | OUTPUT_VARIABLE python_site_packages | 2 | OUTPUT_VARIABLE python_site_packages |
624 | 3 | OUTPUT_STRIP_TRAILING_WHITESPACE) | 3 | OUTPUT_STRIP_TRAILING_WHITESPACE) |
625 | 4 | 4 | ||
626 | 5 | 5 | ||
627 | === modified file 'python/libertine/AppDiscovery.py' | |||
628 | --- python/libertine/AppDiscovery.py 2016-01-04 17:41:08 +0000 | |||
629 | +++ python/libertine/AppDiscovery.py 2017-01-05 20:19:05 +0000 | |||
630 | @@ -188,7 +188,7 @@ | |||
631 | 188 | if desktop_file_is_showable(desktop_entry): | 188 | if desktop_file_is_showable(desktop_entry): |
632 | 189 | yield AppInfo(desktop_file_name, desktop_entry, icon_cache) | 189 | yield AppInfo(desktop_file_name, desktop_entry, icon_cache) |
633 | 190 | except Exception as ex: | 190 | except Exception as ex: |
635 | 191 | print("error processing {}: {}".format(desktop_file_name, ex), file=sys.stderr) | 191 | utils.get_logger().error("error processing {}: {}".format(desktop_file_name, ex)) |
636 | 192 | 192 | ||
637 | 193 | 193 | ||
638 | 194 | class AppLauncherCache(object): | 194 | class AppLauncherCache(object): |
639 | @@ -218,5 +218,3 @@ | |||
640 | 218 | default=lambda o: o.__dict__, | 218 | default=lambda o: o.__dict__, |
641 | 219 | sort_keys=True, | 219 | sort_keys=True, |
642 | 220 | indent=4) | 220 | indent=4) |
643 | 221 | |||
644 | 222 | |||
645 | 223 | 221 | ||
646 | === modified file 'python/libertine/ChrootContainer.py' | |||
647 | --- python/libertine/ChrootContainer.py 2016-12-02 17:46:47 +0000 | |||
648 | +++ python/libertine/ChrootContainer.py 2017-01-05 20:19:05 +0000 | |||
649 | @@ -71,10 +71,10 @@ | |||
650 | 71 | shutil.rmtree(container_root) | 71 | shutil.rmtree(container_root) |
651 | 72 | return True | 72 | return True |
652 | 73 | except Exception as e: | 73 | except Exception as e: |
654 | 74 | print("%s" % e) | 74 | utils.get_logger().error("%s" % e) |
655 | 75 | return False | 75 | return False |
656 | 76 | 76 | ||
658 | 77 | def create_libertine_container(self, password=None, multiarch=False, verbosity=1): | 77 | def create_libertine_container(self, password=None, multiarch=False): |
659 | 78 | # Create the actual chroot | 78 | # Create the actual chroot |
660 | 79 | command_line = "{} fakeroot debootstrap --verbose --variant=fakechroot {} {}".format( | 79 | command_line = "{} fakeroot debootstrap --verbose --variant=fakechroot {} {}".format( |
661 | 80 | self._build_fakechroot_command(), self.installed_release, self.root_path) | 80 | self._build_fakechroot_command(), self.installed_release, self.root_path) |
662 | @@ -83,12 +83,12 @@ | |||
663 | 83 | cmd.wait() | 83 | cmd.wait() |
664 | 84 | 84 | ||
665 | 85 | if cmd.returncode != 0: | 85 | if cmd.returncode != 0: |
667 | 86 | print("Failed to create container") | 86 | utils.get_logger().error("Failed to create container") |
668 | 87 | self.destroy_libertine_container() | 87 | self.destroy_libertine_container() |
669 | 88 | return False | 88 | return False |
670 | 89 | 89 | ||
671 | 90 | # Remove symlinks as they can cause ill-behaved recursive behavior in the chroot | 90 | # Remove symlinks as they can cause ill-behaved recursive behavior in the chroot |
673 | 91 | print("Fixing chroot symlinks...") | 91 | utils.get_logger().info("Fixing chroot symlinks...") |
674 | 92 | os.remove(os.path.join(self.root_path, 'dev')) | 92 | os.remove(os.path.join(self.root_path, 'dev')) |
675 | 93 | os.remove(os.path.join(self.root_path, 'proc')) | 93 | os.remove(os.path.join(self.root_path, 'proc')) |
676 | 94 | 94 | ||
677 | @@ -109,8 +109,8 @@ | |||
678 | 109 | else: | 109 | else: |
679 | 110 | archive = "deb http://archive.ubuntu.com/ubuntu " | 110 | archive = "deb http://archive.ubuntu.com/ubuntu " |
680 | 111 | 111 | ||
683 | 112 | if verbosity == 1: | 112 | utils.get_logger().info("Updating chroot's sources.list entries...") |
684 | 113 | print("Updating chroot's sources.list entries...") | 113 | |
685 | 114 | with open(os.path.join(self.root_path, 'etc', 'apt', 'sources.list'), 'a') as fd: | 114 | with open(os.path.join(self.root_path, 'etc', 'apt', 'sources.list'), 'a') as fd: |
686 | 115 | fd.write(archive + self.installed_release + "-updates main\n") | 115 | fd.write(archive + self.installed_release + "-updates main\n") |
687 | 116 | fd.write(archive + self.installed_release + " universe\n") | 116 | fd.write(archive + self.installed_release + " universe\n") |
688 | @@ -121,46 +121,43 @@ | |||
689 | 121 | utils.create_libertine_user_data_dir(self.container_id) | 121 | utils.create_libertine_user_data_dir(self.container_id) |
690 | 122 | 122 | ||
691 | 123 | if multiarch and self.architecture == 'amd64': | 123 | if multiarch and self.architecture == 'amd64': |
694 | 124 | if verbosity == 1: | 124 | utils.get_logger().info("Adding i386 multiarch support...") |
693 | 125 | print("Adding i386 multiarch support...") | ||
695 | 126 | self.run_in_container("dpkg --add-architecture i386") | 125 | self.run_in_container("dpkg --add-architecture i386") |
696 | 127 | 126 | ||
700 | 128 | if verbosity == 1: | 127 | utils.get_logger().info("Updating the contents of the container after creation...") |
701 | 129 | print("Updating the contents of the container after creation...") | 128 | self.update_packages() |
699 | 130 | self.update_packages(verbosity) | ||
702 | 131 | 129 | ||
703 | 132 | for package in self.default_packages: | 130 | for package in self.default_packages: |
706 | 133 | if not self.install_package(package, verbosity, update_cache=False): | 131 | if not self.install_package(package, update_cache=False): |
707 | 134 | print("Failure installing %s during container creation" % package) | 132 | utils.get_logger().error("Failure installing %s during container creation" % package) |
708 | 135 | self.destroy_libertine_container() | 133 | self.destroy_libertine_container() |
709 | 136 | return False | 134 | return False |
710 | 137 | 135 | ||
711 | 138 | if self.installed_release == "vivid" or self.installed_release == "xenial": | 136 | if self.installed_release == "vivid" or self.installed_release == "xenial": |
716 | 139 | if verbosity == 1: | 137 | utils.get_logger().info("Installing the Stable Overlay PPA...") |
717 | 140 | print("Installing the Stable Overlay PPA...") | 138 | if not self.install_package("software-properties-common", update_cache=False): |
718 | 141 | if not self.install_package("software-properties-common", verbosity, update_cache=False): | 139 | utils.get_logger().error("Failure installing software-properties-common during container creation") |
715 | 142 | print("Failure installing software-properties-common during container creation") | ||
719 | 143 | self.destroy_libertine_container() | 140 | self.destroy_libertine_container() |
720 | 144 | return False | 141 | return False |
721 | 145 | 142 | ||
722 | 146 | self.run_in_container("add-apt-repository ppa:ci-train-ppa-service/stable-phone-overlay -y") | 143 | self.run_in_container("add-apt-repository ppa:ci-train-ppa-service/stable-phone-overlay -y") |
724 | 147 | self.update_packages(verbosity) | 144 | self.update_packages() |
725 | 148 | 145 | ||
726 | 149 | # Check if the container was created as root and chown the user directories as necessary | 146 | # Check if the container was created as root and chown the user directories as necessary |
727 | 150 | chown_recursive_dirs(utils.get_libertine_container_userdata_dir_path(self.container_id)) | 147 | chown_recursive_dirs(utils.get_libertine_container_userdata_dir_path(self.container_id)) |
728 | 151 | 148 | ||
729 | 152 | return True | 149 | return True |
730 | 153 | 150 | ||
734 | 154 | def update_packages(self, verbosity=1): | 151 | def update_packages(self): |
735 | 155 | retcode = super().update_packages(verbosity) | 152 | retcode = super().update_packages() |
736 | 156 | self._run_ldconfig(verbosity) | 153 | self._run_ldconfig() |
737 | 157 | return retcode == 0 | 154 | return retcode == 0 |
738 | 158 | 155 | ||
741 | 159 | def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True): | 156 | def install_package(self, package_name, no_dialog=False, update_cache=True): |
742 | 160 | returncode = super().install_package(package_name, verbosity, no_dialog, update_cache) | 157 | returncode = super().install_package(package_name, no_dialog, update_cache) |
743 | 161 | 158 | ||
744 | 162 | if returncode: | 159 | if returncode: |
746 | 163 | self._run_ldconfig(verbosity) | 160 | self._run_ldconfig() |
747 | 164 | 161 | ||
748 | 165 | return returncode | 162 | return returncode |
749 | 166 | 163 | ||
750 | @@ -197,8 +194,12 @@ | |||
751 | 197 | mounts = self._sanitize_bind_mounts(utils.get_common_xdg_user_directories() + \ | 194 | mounts = self._sanitize_bind_mounts(utils.get_common_xdg_user_directories() + \ |
752 | 198 | ContainersConfig().get_container_bind_mounts(self.container_id)) | 195 | ContainersConfig().get_container_bind_mounts(self.container_id)) |
753 | 199 | for user_dir in utils.generate_binding_directories(mounts, home_path): | 196 | for user_dir in utils.generate_binding_directories(mounts, home_path): |
756 | 200 | user_dir_path = os.path.join(home_path, user_dir[1]) | 197 | if os.path.isabs(user_dir[1]): |
757 | 201 | bind_mounts += " -b \"%s:%s\"" % (user_dir[0], user_dir_path) | 198 | path = user_dir[1] |
758 | 199 | else: | ||
759 | 200 | path = os.path.join(home_path, user_dir[1]) | ||
760 | 201 | |||
761 | 202 | bind_mounts += " -b \"%s:%s\"" % (user_dir[0], path) | ||
762 | 202 | 203 | ||
763 | 203 | proot_cmd += bind_mounts | 204 | proot_cmd += bind_mounts |
764 | 204 | 205 | ||
765 | @@ -233,16 +234,14 @@ | |||
766 | 233 | 234 | ||
767 | 234 | args = shlex.split(proot_cmd) | 235 | args = shlex.split(proot_cmd) |
768 | 235 | args.extend(app_exec_line) | 236 | args.extend(app_exec_line) |
771 | 236 | app = psutil.Popen(args, env=environ) | 237 | return psutil.Popen(args, env=environ) |
770 | 237 | return app | ||
772 | 238 | 238 | ||
773 | 239 | def finish_application(self, app): | 239 | def finish_application(self, app): |
774 | 240 | utils.terminate_window_manager(self._window_manager) | 240 | utils.terminate_window_manager(self._window_manager) |
775 | 241 | app.wait() | 241 | app.wait() |
776 | 242 | 242 | ||
780 | 243 | def _run_ldconfig(self, verbosity=1): | 243 | def _run_ldconfig(self): |
781 | 244 | if verbosity == 1: | 244 | utils.get_logger().info("Refreshing the container's dynamic linker run-time bindings...") |
779 | 245 | print("Refreshing the container's dynamic linker run-time bindings...") | ||
782 | 246 | 245 | ||
783 | 247 | command_line = self._build_privileged_proot_cmd() + " ldconfig.REAL" | 246 | command_line = self._build_privileged_proot_cmd() + " ldconfig.REAL" |
784 | 248 | 247 | ||
785 | 249 | 248 | ||
786 | === modified file 'python/libertine/ContainersConfig.py' | |||
787 | --- python/libertine/ContainersConfig.py 2016-11-16 17:58:38 +0000 | |||
788 | +++ python/libertine/ContainersConfig.py 2017-01-05 20:19:05 +0000 | |||
789 | @@ -214,12 +214,12 @@ | |||
790 | 214 | 214 | ||
791 | 215 | def check_container_id(self, container_id): | 215 | def check_container_id(self, container_id): |
792 | 216 | if container_id and not self.container_exists(container_id): | 216 | if container_id and not self.container_exists(container_id): |
794 | 217 | print("Container id \'%s\' does not exist." % container_id, file=sys.stderr) | 217 | libertine.utils.get_logger().error("Container id \'%s\' does not exist." % container_id) |
795 | 218 | sys.exit(1) | 218 | sys.exit(1) |
796 | 219 | elif not container_id: | 219 | elif not container_id: |
797 | 220 | container_id = self.get_default_container_id() | 220 | container_id = self.get_default_container_id() |
798 | 221 | if container_id is None: | 221 | if container_id is None: |
800 | 222 | print("No default container available.") | 222 | libertine.utils.get_logger().error("No default container available.") |
801 | 223 | sys.exit(1) | 223 | sys.exit(1) |
802 | 224 | 224 | ||
803 | 225 | return container_id | 225 | return container_id |
804 | @@ -260,7 +260,7 @@ | |||
805 | 260 | 260 | ||
806 | 261 | def delete_container(self, container_id): | 261 | def delete_container(self, container_id): |
807 | 262 | if not self.container_list: | 262 | if not self.container_list: |
809 | 263 | print("Unable to delete container. No containers defined.") | 263 | libertine.utils.get_logger().error("Unable to delete container. No containers defined.") |
810 | 264 | sys.exit(1) | 264 | sys.exit(1) |
811 | 265 | 265 | ||
812 | 266 | container = self._get_container_entry(container_id) | 266 | container = self._get_container_entry(container_id) |
813 | 267 | 267 | ||
814 | === modified file 'python/libertine/HostInfo.py' | |||
815 | --- python/libertine/HostInfo.py 2016-10-28 18:11:12 +0000 | |||
816 | +++ python/libertine/HostInfo.py 2017-01-05 20:19:05 +0000 | |||
817 | @@ -13,6 +13,7 @@ | |||
818 | 13 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 13 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
819 | 14 | 14 | ||
820 | 15 | import lsb_release | 15 | import lsb_release |
821 | 16 | import os | ||
822 | 16 | import platform | 17 | import platform |
823 | 17 | import subprocess | 18 | import subprocess |
824 | 18 | 19 | ||
825 | @@ -69,3 +70,7 @@ | |||
826 | 69 | parser.error("Failed to determine the local architecture.") | 70 | parser.error("Failed to determine the local architecture.") |
827 | 70 | 71 | ||
828 | 71 | return dpkg.stdout.read().strip() | 72 | return dpkg.stdout.read().strip() |
829 | 73 | |||
830 | 74 | def get_host_timezone(self): | ||
831 | 75 | with open(os.path.join('/', 'etc', 'timezone'), 'r') as fd: | ||
832 | 76 | return fd.read().strip('\n') | ||
833 | 72 | 77 | ||
834 | === modified file 'python/libertine/Libertine.py' | |||
835 | --- python/libertine/Libertine.py 2016-11-16 17:58:38 +0000 | |||
836 | +++ python/libertine/Libertine.py 2017-01-05 20:19:05 +0000 | |||
837 | @@ -1,4 +1,4 @@ | |||
839 | 1 | # Copyright 2015-2106 Canonical Ltd. | 1 | # Copyright 2015-2016 Canonical Ltd. |
840 | 2 | # | 2 | # |
841 | 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 |
842 | 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 |
843 | @@ -20,31 +20,32 @@ | |||
844 | 20 | import os | 20 | import os |
845 | 21 | import shutil | 21 | import shutil |
846 | 22 | 22 | ||
847 | 23 | from . import utils | ||
848 | 23 | from libertine.ContainersConfig import ContainersConfig | 24 | from libertine.ContainersConfig import ContainersConfig |
849 | 24 | from libertine.HostInfo import HostInfo | 25 | from libertine.HostInfo import HostInfo |
850 | 25 | 26 | ||
851 | 26 | 27 | ||
867 | 27 | def apt_args_for_verbosity_level(verbosity): | 28 | def _apt_args_for_verbosity_level(): |
868 | 28 | """ | 29 | """ |
869 | 29 | Maps numeric verbosity levels onto APT command-line arguments. | 30 | Maps debug levels to APT command-line arguments. |
870 | 30 | 31 | """ | |
871 | 31 | :param verbosity: 0 is quiet, 1 is normal, 2 is incontinent | 32 | if 'LIBERTINE_DEBUG' not in os.environ or os.environ['LIBERTINE_DEBUG'] == '0': |
872 | 32 | """ | 33 | return '--quiet=2' |
873 | 33 | return { | 34 | |
874 | 34 | 0: '--quiet=2', | 35 | if os.environ['LIBERTINE_DEBUG'] == '1': |
875 | 35 | 1: '--assume-yes', | 36 | return '--quiet=1 --assume-yes' |
876 | 36 | 2: '--quiet=1 --assume-yes --option APT::Status-Fd=1' | 37 | |
877 | 37 | }.get(verbosity, '') | 38 | return '--assume-yes --option APT::Status-Fd=1' |
878 | 38 | 39 | ||
879 | 39 | 40 | ||
880 | 40 | def apt_command_prefix(verbosity): | 41 | def _apt_command_prefix(): |
881 | 41 | return '/usr/bin/apt-get ' + apt_args_for_verbosity_level(verbosity) + \ | 42 | return '/usr/bin/apt-get ' + _apt_args_for_verbosity_level() + \ |
882 | 42 | ' --option Apt::Cmd::Disable-Script-Warning=true --option Dpkg::Progress-Fancy=1' + \ | 43 | ' --option Apt::Cmd::Disable-Script-Warning=true --option Dpkg::Progress-Fancy=1' + \ |
883 | 43 | ' --option Apt::Color=1 ' | 44 | ' --option Apt::Color=1 ' |
884 | 44 | 45 | ||
885 | 45 | 46 | ||
886 | 46 | def handle_runtime_error(error): | 47 | def handle_runtime_error(error): |
888 | 47 | print("%s" % error) | 48 | utils.get_logger().error("%s" % error) |
889 | 48 | return False | 49 | return False |
890 | 49 | 50 | ||
891 | 50 | 51 | ||
892 | @@ -92,41 +93,33 @@ | |||
893 | 92 | 'maliit-inputcontext-gtk3', | 93 | 'maliit-inputcontext-gtk3', |
894 | 93 | 'maliit-framework'] | 94 | 'maliit-framework'] |
895 | 94 | 95 | ||
911 | 95 | def create_libertine_container(self, password=None, multiarch=False, verbosity=1): | 96 | def create_libertine_container(self, password=None, multiarch=False): |
912 | 96 | pass | 97 | pass |
913 | 97 | 98 | ||
914 | 98 | def destroy_libertine_container(self, verbosity=1): | 99 | def destroy_libertine_container(self): |
915 | 99 | pass | 100 | pass |
916 | 100 | 101 | ||
917 | 101 | def copy_package_to_container(self, package_path): | 102 | def copy_file_to_container(self, source, dest): |
918 | 102 | """ | 103 | """ |
919 | 103 | Copies a Debian package from the host to a pre-determined place | 104 | Copies a file from the host to the given path in the container. |
920 | 104 | in a container. | 105 | |
921 | 105 | 106 | :param source: The full path to the file on the host. | |
922 | 106 | :param package_path: The full path to the Debian package located | 107 | :param dest: The relative path to the file in the container without |
923 | 107 | on the host. | 108 | the root path. |
924 | 108 | """ | 109 | """ |
925 | 109 | if os.path.exists(os.path.join(self.root_path, 'tmp', package_path.split('/')[-1])): | 110 | if os.path.exists(os.path.join(self.root_path, dest)): |
926 | 110 | return False | 111 | return False |
927 | 111 | 112 | ||
929 | 112 | shutil.copy2(package_path, os.path.join(self.root_path, 'tmp')) | 113 | shutil.copy2(source, os.path.join(self.root_path, dest.lstrip('/'))) |
930 | 113 | return True | 114 | return True |
931 | 114 | 115 | ||
947 | 115 | def delete_package_in_container(self, package_path): | 116 | def delete_file_in_container(self, path): |
948 | 116 | """ | 117 | """ |
949 | 117 | Deletes a previously copied in Debian package in a container. | 118 | Deletes a file within the container. |
950 | 118 | 119 | ||
951 | 119 | :param package_path: The full path to the Debian package located | 120 | :param path: The path to the file without the container root path. |
952 | 120 | on the host. | 121 | """ |
953 | 121 | """ | 122 | os.remove(os.path.join(self.root_path, path.lstrip('/'))) |
939 | 122 | os.remove(os.path.join(self.root_path, 'tmp', package_path.split('/')[-1])) | ||
940 | 123 | |||
941 | 124 | def is_running(self): | ||
942 | 125 | """ | ||
943 | 126 | Indicates if the container is 'running'. The definition of 'running' | ||
944 | 127 | depends on the type of the container. | ||
945 | 128 | """ | ||
946 | 129 | return False | ||
954 | 130 | 123 | ||
955 | 131 | def start_container(self): | 124 | def start_container(self): |
956 | 132 | """ | 125 | """ |
957 | @@ -151,91 +144,85 @@ | |||
958 | 151 | """ | 144 | """ |
959 | 152 | pass | 145 | pass |
960 | 153 | 146 | ||
962 | 154 | def update_apt_cache(self, verbosity=1): | 147 | def update_apt_cache(self): |
963 | 155 | """ | 148 | """ |
964 | 156 | Updates the apt cache in the container. | 149 | Updates the apt cache in the container. |
965 | 157 | |||
966 | 158 | :param verbosity: the chattiness of the output on a range from 0 to 2 | ||
967 | 159 | """ | 150 | """ |
969 | 160 | return self.run_in_container(apt_command_prefix(verbosity) + 'update') | 151 | return self.run_in_container(_apt_command_prefix() + 'update') |
970 | 161 | 152 | ||
972 | 162 | def update_packages(self, verbosity=1): | 153 | def update_packages(self): |
973 | 163 | """ | 154 | """ |
974 | 164 | Updates all packages installed in the container. | 155 | Updates all packages installed in the container. |
975 | 165 | |||
976 | 166 | :param verbosity: the chattiness of the output on a range from 0 to 2 | ||
977 | 167 | """ | 156 | """ |
980 | 168 | self.update_apt_cache(verbosity) | 157 | self.update_apt_cache() |
981 | 169 | return self.run_in_container(apt_command_prefix(verbosity) + '--force-yes dist-upgrade') == 0 | 158 | return self.run_in_container(_apt_command_prefix() + '--force-yes dist-upgrade') == 0 |
982 | 170 | 159 | ||
984 | 171 | def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True): | 160 | def install_package(self, package_name, no_dialog=False, update_cache=True): |
985 | 172 | """ | 161 | """ |
986 | 173 | Installs a named package in the container. | 162 | Installs a named package in the container. |
987 | 174 | 163 | ||
988 | 175 | :param package_name: The name of the package as APT understands it or | 164 | :param package_name: The name of the package as APT understands it or |
989 | 176 | a full path to a Debian package on the host. | 165 | a full path to a Debian package on the host. |
990 | 177 | :param verbosity: the chattiness of the output on a range from 0 to 2 | ||
991 | 178 | """ | 166 | """ |
992 | 179 | if update_cache: | 167 | if update_cache: |
994 | 180 | self.update_apt_cache(verbosity) | 168 | self.update_apt_cache() |
995 | 181 | 169 | ||
996 | 182 | if package_name.endswith('.deb'): | 170 | if package_name.endswith('.deb'): |
1007 | 183 | delete_package = self.copy_package_to_container(package_name) | 171 | if not os.path.exists(package_name): |
1008 | 184 | 172 | utils.get_logger().error("File {} does not exist.".format(package_name)) | |
1009 | 185 | self.run_in_container('ls -la ' + os.environ['HOME']) | 173 | return False |
1010 | 186 | self.run_in_container('dpkg -i ' + | 174 | |
1011 | 187 | os.path.join('/', 'tmp', package_name.split('/')[-1])) | 175 | dest = os.path.join('/', 'tmp', package_name.split('/')[-1]) |
1012 | 188 | 176 | file_created = self.copy_file_to_container(package_name, dest) | |
1013 | 189 | ret = self.run_in_container(apt_command_prefix(verbosity) + " install -f") == 0 | 177 | |
1014 | 190 | 178 | self.run_in_container('dpkg -i {}'.format(dest)) | |
1015 | 191 | if delete_package: | 179 | ret = self.run_in_container(_apt_command_prefix() + " install -f") == 0 |
1016 | 192 | self.delete_package_in_container(package_name) | 180 | |
1017 | 181 | if file_created: | ||
1018 | 182 | self.delete_file_in_container(dest) | ||
1019 | 193 | 183 | ||
1020 | 194 | return ret | 184 | return ret |
1021 | 195 | else: | 185 | else: |
1022 | 196 | if no_dialog: | 186 | if no_dialog: |
1023 | 197 | os.environ['DEBIAN_FRONTEND'] = 'teletype' | 187 | os.environ['DEBIAN_FRONTEND'] = 'teletype' |
1025 | 198 | return self.run_in_container(apt_command_prefix(verbosity) + " install '" + package_name + "'") == 0 | 188 | return self.run_in_container(_apt_command_prefix() + " install '" + package_name + "'") == 0 |
1026 | 199 | 189 | ||
1028 | 200 | def remove_package(self, package_name, verbosity=1): | 190 | def remove_package(self, package_name): |
1029 | 201 | """ | 191 | """ |
1030 | 202 | Removes a package from the container. | 192 | Removes a package from the container. |
1031 | 203 | 193 | ||
1032 | 204 | :param package_name: The name of the package to be removed. | 194 | :param package_name: The name of the package to be removed. |
1033 | 205 | :param verbosity: The verbosity level of the progress reporting. | ||
1034 | 206 | """ | 195 | """ |
1036 | 207 | if self.run_in_container(apt_command_prefix(verbosity) + " purge '" + package_name + "'") != 0: | 196 | if self.run_in_container(_apt_command_prefix() + " purge '" + package_name + "'") != 0: |
1037 | 208 | return False | 197 | return False |
1039 | 209 | return self.run_in_container(apt_command_prefix(verbosity) + "autoremove --purge") == 0 | 198 | return self.run_in_container(_apt_command_prefix() + "autoremove --purge") == 0 |
1040 | 210 | 199 | ||
1042 | 211 | def configure_multiarch(self, should_enable, verbosity=1): | 200 | def configure_multiarch(self, should_enable): |
1043 | 212 | """ | 201 | """ |
1044 | 213 | Enables or disables multiarch repositories. | 202 | Enables or disables multiarch repositories. |
1045 | 214 | 203 | ||
1046 | 215 | :param should_enable: Whether or not to enable multiarch support. | 204 | :param should_enable: Whether or not to enable multiarch support. |
1047 | 216 | :param verbosity: the chattiness of the output on a range from 0 to 2 | ||
1048 | 217 | """ | 205 | """ |
1049 | 218 | if should_enable: | 206 | if should_enable: |
1050 | 219 | ret = self.run_in_container("dpkg --add-architecture i386") | 207 | ret = self.run_in_container("dpkg --add-architecture i386") |
1051 | 220 | if ret or ret == 0: | 208 | if ret or ret == 0: |
1053 | 221 | self.update_apt_cache(verbosity) | 209 | self.update_apt_cache() |
1054 | 222 | return ret | 210 | return ret |
1055 | 223 | else: | 211 | else: |
1057 | 224 | self.run_in_container(apt_command_prefix(verbosity) + "purge \".*:i386\"") | 212 | self.run_in_container(_apt_command_prefix() + "purge \".*:i386\"") |
1058 | 225 | return self.run_in_container("dpkg --remove-architecture i386") | 213 | return self.run_in_container("dpkg --remove-architecture i386") |
1059 | 226 | 214 | ||
1061 | 227 | def configure_add_archive(self, archive, public_key_file, verbosity=1): | 215 | def configure_add_archive(self, archive, public_key_file): |
1062 | 228 | """ | 216 | """ |
1063 | 229 | Adds the given archive. If this archive requires a key, prompt user. | 217 | Adds the given archive. If this archive requires a key, prompt user. |
1064 | 230 | 218 | ||
1065 | 231 | :param archive: The configuration command to run. | 219 | :param archive: The configuration command to run. |
1066 | 232 | :param public_key_file: file containing the public key used to sign this archive | 220 | :param public_key_file: file containing the public key used to sign this archive |
1067 | 233 | :param verbosity: the chattiness of the output on a range from 0 to 2 | ||
1068 | 234 | """ | 221 | """ |
1069 | 235 | if not os.path.exists(os.path.join(self.root_path, 'usr', 'bin', 'add-apt-repository')): | 222 | if not os.path.exists(os.path.join(self.root_path, 'usr', 'bin', 'add-apt-repository')): |
1071 | 236 | self.install_package("software-properties-common", verbosity) | 223 | self.install_package("software-properties-common") |
1072 | 237 | if 'https://' in archive and not os.path.exists(os.path.join(self.root_path, 'usr', 'lib', 'apt', 'methods', 'https')): | 224 | if 'https://' in archive and not os.path.exists(os.path.join(self.root_path, 'usr', 'lib', 'apt', 'methods', 'https')): |
1074 | 238 | self.install_package("apt-transport-https", verbosity) | 225 | self.install_package("apt-transport-https") |
1075 | 239 | 226 | ||
1076 | 240 | retcode = self.run_in_container("add-apt-repository -y " + archive) | 227 | retcode = self.run_in_container("add-apt-repository -y " + archive) |
1077 | 241 | if retcode is 0 and public_key_file is not None: | 228 | if retcode is 0 and public_key_file is not None: |
1078 | @@ -244,12 +231,11 @@ | |||
1079 | 244 | 231 | ||
1080 | 245 | return retcode | 232 | return retcode |
1081 | 246 | 233 | ||
1083 | 247 | def configure_remove_archive(self, archive, verbosity=1): | 234 | def configure_remove_archive(self, archive): |
1084 | 248 | """ | 235 | """ |
1085 | 249 | Removes the given archive. | 236 | Removes the given archive. |
1086 | 250 | 237 | ||
1087 | 251 | :param archive: The configuration command to run. | 238 | :param archive: The configuration command to run. |
1088 | 252 | :param verbosity: the chattiness of the output on a range from 0 to 2 | ||
1089 | 253 | """ | 239 | """ |
1090 | 254 | return self.run_in_container("add-apt-repository -y -r " + archive) | 240 | return self.run_in_container("add-apt-repository -y -r " + archive) |
1091 | 255 | 241 | ||
1092 | @@ -272,19 +258,19 @@ | |||
1093 | 272 | super().__init__(container_id) | 258 | super().__init__(container_id) |
1094 | 273 | self.container_type = "mock" | 259 | self.container_type = "mock" |
1095 | 274 | 260 | ||
1109 | 275 | def create_libertine_container(self, password=None, multiarch=False, verbosity=1): | 261 | def create_libertine_container(self, password=None, multiarch=False): |
1110 | 276 | return True | 262 | return True |
1111 | 277 | 263 | ||
1112 | 278 | def destroy_libertine_container(self, verbosity=1): | 264 | def destroy_libertine_container(self): |
1113 | 279 | return True | 265 | return True |
1114 | 280 | 266 | ||
1115 | 281 | def update_packages(self, verbosity=1): | 267 | def update_packages(self): |
1116 | 282 | return True | 268 | return True |
1117 | 283 | 269 | ||
1118 | 284 | def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True): | 270 | def install_package(self, package_name, no_dialog=False, update_cache=True): |
1119 | 285 | return True | 271 | return True |
1120 | 286 | 272 | ||
1121 | 287 | def remove_package(self, package_name, verbosity=1, no_dialog=False): | 273 | def remove_package(self, package_name, no_dialog=False): |
1122 | 288 | return True | 274 | return True |
1123 | 289 | 275 | ||
1124 | 290 | def run_in_container(self, command_string): | 276 | def run_in_container(self, command_string): |
1125 | @@ -335,6 +321,9 @@ | |||
1126 | 335 | if container_type == "lxc": | 321 | if container_type == "lxc": |
1127 | 336 | from libertine.LxcContainer import LibertineLXC | 322 | from libertine.LxcContainer import LibertineLXC |
1128 | 337 | self.container = LibertineLXC(container_id) | 323 | self.container = LibertineLXC(container_id) |
1129 | 324 | elif container_type == "lxd": | ||
1130 | 325 | from libertine.LxdContainer import LibertineLXD | ||
1131 | 326 | self.container = LibertineLXD(container_id, self.containers_config) | ||
1132 | 338 | elif container_type == "chroot": | 327 | elif container_type == "chroot": |
1133 | 339 | from libertine.ChrootContainer import LibertineChroot | 328 | from libertine.ChrootContainer import LibertineChroot |
1134 | 340 | self.container = LibertineChroot(container_id) | 329 | self.container = LibertineChroot(container_id) |
1135 | @@ -365,48 +354,47 @@ | |||
1136 | 365 | """ | 354 | """ |
1137 | 366 | return self.container.destroy_libertine_container() | 355 | return self.container.destroy_libertine_container() |
1138 | 367 | 356 | ||
1140 | 368 | def create_libertine_container(self, password=None, multiarch=False, verbosity=1): | 357 | def create_libertine_container(self, password=None, multiarch=False): |
1141 | 369 | """ | 358 | """ |
1142 | 370 | Creates the container. | 359 | Creates the container. |
1143 | 371 | """ | 360 | """ |
1144 | 372 | self.container.architecture = HostInfo().get_host_architecture() | 361 | self.container.architecture = HostInfo().get_host_architecture() |
1145 | 373 | self.container.installed_release = self.containers_config.get_container_distro(self.container_id) | 362 | self.container.installed_release = self.containers_config.get_container_distro(self.container_id) |
1146 | 374 | 363 | ||
1148 | 375 | return self.container.create_libertine_container(password, multiarch, verbosity) | 364 | return self.container.create_libertine_container(password, multiarch) |
1149 | 376 | 365 | ||
1151 | 377 | def update_libertine_container(self, verbosity=1): | 366 | def update_libertine_container(self): |
1152 | 378 | """ | 367 | """ |
1153 | 379 | Updates the contents of the container. | 368 | Updates the contents of the container. |
1154 | 380 | """ | 369 | """ |
1155 | 381 | try: | 370 | try: |
1156 | 382 | with ContainerRunning(self.container): | 371 | with ContainerRunning(self.container): |
1158 | 383 | return self.container.update_packages(verbosity) | 372 | return self.container.update_packages() |
1159 | 384 | except RuntimeError as e: | 373 | except RuntimeError as e: |
1160 | 385 | return handle_runtime_error(e) | 374 | return handle_runtime_error(e) |
1161 | 386 | 375 | ||
1163 | 387 | def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True): | 376 | def install_package(self, package_name, no_dialog=False, update_cache=True): |
1164 | 388 | """ | 377 | """ |
1165 | 389 | Installs a package in the container. | 378 | Installs a package in the container. |
1166 | 390 | """ | 379 | """ |
1167 | 391 | try: | 380 | try: |
1168 | 392 | with ContainerRunning(self.container): | 381 | with ContainerRunning(self.container): |
1170 | 393 | return self.container.install_package(package_name, verbosity, no_dialog, update_cache) | 382 | return self.container.install_package(package_name, no_dialog, update_cache) |
1171 | 394 | except RuntimeError as e: | 383 | except RuntimeError as e: |
1172 | 395 | return handle_runtime_error(e) | 384 | return handle_runtime_error(e) |
1173 | 396 | 385 | ||
1175 | 397 | def remove_package(self, package_name, verbosity=1, no_dialog=False): | 386 | def remove_package(self, package_name, no_dialog=False): |
1176 | 398 | """ | 387 | """ |
1177 | 399 | Removes a package from the container. | 388 | Removes a package from the container. |
1178 | 400 | 389 | ||
1179 | 401 | :param package_name: The name of the package to be removed. | 390 | :param package_name: The name of the package to be removed. |
1180 | 402 | :param verbosity: The verbosity level of the progress reporting. | ||
1181 | 403 | """ | 391 | """ |
1182 | 404 | try: | 392 | try: |
1183 | 405 | with ContainerRunning(self.container): | 393 | with ContainerRunning(self.container): |
1184 | 406 | if no_dialog: | 394 | if no_dialog: |
1185 | 407 | os.environ['DEBIAN_FRONTEND'] = 'teletype' | 395 | os.environ['DEBIAN_FRONTEND'] = 'teletype' |
1186 | 408 | 396 | ||
1188 | 409 | return self.container.remove_package(package_name, verbosity) | 397 | return self.container.remove_package(package_name) |
1189 | 410 | except RuntimeError as e: | 398 | except RuntimeError as e: |
1190 | 411 | return handle_runtime_error(e) | 399 | return handle_runtime_error(e) |
1191 | 412 | 400 | ||
1192 | @@ -484,23 +472,23 @@ | |||
1193 | 484 | except RuntimeError as e: | 472 | except RuntimeError as e: |
1194 | 485 | return handle_runtime_error(e) | 473 | return handle_runtime_error(e) |
1195 | 486 | 474 | ||
1214 | 487 | def configure_multiarch(self, should_enable, verbosity=1): | 475 | def configure_multiarch(self, should_enable): |
1215 | 488 | try: | 476 | try: |
1216 | 489 | with ContainerRunning(self.container): | 477 | with ContainerRunning(self.container): |
1217 | 490 | return self.container.configure_multiarch(should_enable, verbosity) | 478 | return self.container.configure_multiarch(should_enable) |
1218 | 491 | except RuntimeError as e: | 479 | except RuntimeError as e: |
1219 | 492 | return handle_runtime_error(e) | 480 | return handle_runtime_error(e) |
1220 | 493 | 481 | ||
1221 | 494 | def configure_add_archive(self, archive, key, verbosity): | 482 | def configure_add_archive(self, archive, key): |
1222 | 495 | try: | 483 | try: |
1223 | 496 | with ContainerRunning(self.container): | 484 | with ContainerRunning(self.container): |
1224 | 497 | return self.container.configure_add_archive(archive, key, verbosity) | 485 | return self.container.configure_add_archive(archive, key) |
1225 | 498 | except RuntimeError as e: | 486 | except RuntimeError as e: |
1226 | 499 | return handle_runtime_error(e) | 487 | return handle_runtime_error(e) |
1227 | 500 | 488 | ||
1228 | 501 | def configure_remove_archive(self, archive, verbosity): | 489 | def configure_remove_archive(self, archive): |
1229 | 502 | try: | 490 | try: |
1230 | 503 | with ContainerRunning(self.container): | 491 | with ContainerRunning(self.container): |
1231 | 504 | return self.container.configure_remove_archive(archive, verbosity) | 492 | return self.container.configure_remove_archive(archive) |
1232 | 505 | except RuntimeError as e: | 493 | except RuntimeError as e: |
1233 | 506 | return handle_runtime_error(e) | 494 | return handle_runtime_error(e) |
1234 | 507 | 495 | ||
1235 | === modified file 'python/libertine/LxcContainer.py' | |||
1236 | --- python/libertine/LxcContainer.py 2016-11-16 17:33:42 +0000 | |||
1237 | +++ python/libertine/LxcContainer.py 2017-01-05 20:19:05 +0000 | |||
1238 | @@ -23,8 +23,9 @@ | |||
1239 | 23 | import sys | 23 | import sys |
1240 | 24 | import tempfile | 24 | import tempfile |
1241 | 25 | 25 | ||
1242 | 26 | from .lifecycle import LifecycleResult | ||
1243 | 26 | from .Libertine import BaseContainer | 27 | from .Libertine import BaseContainer |
1245 | 27 | from . import utils | 28 | from . import utils, HostInfo |
1246 | 28 | 29 | ||
1247 | 29 | 30 | ||
1248 | 30 | home_path = os.environ['HOME'] | 31 | home_path = os.environ['HOME'] |
1249 | @@ -33,7 +34,7 @@ | |||
1250 | 33 | LIBERTINE_LXC_MANAGER_PATH = "/LxcManager" | 34 | LIBERTINE_LXC_MANAGER_PATH = "/LxcManager" |
1251 | 34 | 35 | ||
1252 | 35 | 36 | ||
1254 | 36 | def check_lxc_net_entry(entry): | 37 | def _check_lxc_net_entry(entry): |
1255 | 37 | lxc_net_file = open('/etc/lxc/lxc-usernet') | 38 | lxc_net_file = open('/etc/lxc/lxc-usernet') |
1256 | 38 | found = False | 39 | found = False |
1257 | 39 | 40 | ||
1258 | @@ -45,14 +46,14 @@ | |||
1259 | 45 | return found | 46 | return found |
1260 | 46 | 47 | ||
1261 | 47 | 48 | ||
1263 | 48 | def setup_host_environment(username): | 49 | def _setup_host_environment(username): |
1264 | 49 | lxc_net_entry = "%s veth lxcbr0 10" % str(username) | 50 | lxc_net_entry = "%s veth lxcbr0 10" % str(username) |
1265 | 50 | 51 | ||
1267 | 51 | if not check_lxc_net_entry(lxc_net_entry): | 52 | if not _check_lxc_net_entry(lxc_net_entry): |
1268 | 52 | subprocess.Popen(["sudo", "libertine-lxc-setup", str(username)]).wait() | 53 | subprocess.Popen(["sudo", "libertine-lxc-setup", str(username)]).wait() |
1269 | 53 | 54 | ||
1270 | 54 | 55 | ||
1272 | 55 | def get_lxc_default_config_path(): | 56 | def _get_lxc_default_config_path(): |
1273 | 56 | return os.path.join(home_path, '.config', 'lxc') | 57 | return os.path.join(home_path, '.config', 'lxc') |
1274 | 57 | 58 | ||
1275 | 58 | 59 | ||
1276 | @@ -74,21 +75,43 @@ | |||
1277 | 74 | return container | 75 | return container |
1278 | 75 | 76 | ||
1279 | 76 | 77 | ||
1283 | 77 | def lxc_start(container, lxc_log_file): | 78 | def _dump_lxc_log(logfile): |
1284 | 78 | container.append_config_item("lxc.logfile", lxc_log_file) | 79 | if os.path.exists(logfile): |
1285 | 79 | container.append_config_item("lxc.logpriority", "3") | 80 | try: |
1286 | 81 | with open(logfile, 'r') as fd: | ||
1287 | 82 | for line in fd: | ||
1288 | 83 | print(line.lstrip()) | ||
1289 | 84 | except Exception as ex: | ||
1290 | 85 | utils.get_logger().error("Could not open LXC log file: %s" % ex) | ||
1291 | 86 | |||
1292 | 87 | |||
1293 | 88 | def get_logfile(container): | ||
1294 | 89 | try: | ||
1295 | 90 | logfile = container.get_config_item('lxc.logfile') | ||
1296 | 91 | except: | ||
1297 | 92 | logfile = None | ||
1298 | 93 | |||
1299 | 94 | if not container.running or logfile is None or not os.path.exists(logfile): | ||
1300 | 95 | logfile = os.path.join(tempfile.mkdtemp(), 'lxc-start.log') | ||
1301 | 96 | container.append_config_item("lxc.logfile", logfile) | ||
1302 | 97 | container.append_config_item("lxc.logpriority", "3") | ||
1303 | 98 | |||
1304 | 99 | return logfile | ||
1305 | 100 | |||
1306 | 101 | def lxc_start(container): | ||
1307 | 102 | lxc_log_file = get_logfile(container) | ||
1308 | 80 | 103 | ||
1309 | 81 | if not container.start(): | 104 | if not container.start(): |
1311 | 82 | return (False, "Container failed to start") | 105 | return LifecycleResult("Container failed to start.") |
1312 | 83 | 106 | ||
1313 | 84 | if not container.wait("RUNNING", 10): | 107 | if not container.wait("RUNNING", 10): |
1315 | 85 | return (False, "Container failed to enter the RUNNING state") | 108 | return LifecycleResult("Container failed to enter the RUNNING state.") |
1316 | 86 | 109 | ||
1317 | 87 | if not container.get_ips(timeout=30): | 110 | if not container.get_ips(timeout=30): |
1318 | 88 | lxc_stop(container) | 111 | lxc_stop(container) |
1320 | 89 | return (False, "Not able to connect to the network.") | 112 | return LifecycleResult("Not able to connect to the network.") |
1321 | 90 | 113 | ||
1323 | 91 | return (True, "") | 114 | return LifecycleResult() |
1324 | 92 | 115 | ||
1325 | 93 | 116 | ||
1326 | 94 | def lxc_stop(container): | 117 | def lxc_stop(container): |
1327 | @@ -96,13 +119,6 @@ | |||
1328 | 96 | container.stop() | 119 | container.stop() |
1329 | 97 | 120 | ||
1330 | 98 | 121 | ||
1331 | 99 | def get_host_timezone(): | ||
1332 | 100 | with open(os.path.join('/', 'etc', 'timezone'), 'r') as fd: | ||
1333 | 101 | host_timezone = fd.read().strip('\n') | ||
1334 | 102 | |||
1335 | 103 | return host_timezone | ||
1336 | 104 | |||
1337 | 105 | |||
1338 | 106 | class EnvLxcSettings(contextlib.ExitStack): | 122 | class EnvLxcSettings(contextlib.ExitStack): |
1339 | 107 | """ | 123 | """ |
1340 | 108 | Helper object providing a way to set the proxies for testing | 124 | Helper object providing a way to set the proxies for testing |
1341 | @@ -137,42 +153,31 @@ | |||
1342 | 137 | super().__init__(container_id) | 153 | super().__init__(container_id) |
1343 | 138 | self.container_type = "lxc" | 154 | self.container_type = "lxc" |
1344 | 139 | self.container = lxc_container(container_id) | 155 | self.container = lxc_container(container_id) |
1345 | 140 | self._set_lxc_log() | ||
1346 | 141 | self.lxc_manager_interface = None | 156 | self.lxc_manager_interface = None |
1347 | 142 | self.window_manager = None | 157 | self.window_manager = None |
1348 | 158 | self.host_info = HostInfo.HostInfo() | ||
1349 | 143 | 159 | ||
1350 | 144 | utils.set_session_dbus_env_var() | 160 | utils.set_session_dbus_env_var() |
1351 | 145 | 161 | ||
1352 | 146 | try: | 162 | try: |
1353 | 147 | bus = dbus.SessionBus() | 163 | bus = dbus.SessionBus() |
1356 | 148 | lxc_mgr_service = bus.get_object(get_lxc_manager_dbus_name(), get_lxc_manager_dbus_path()) | 164 | self.lxc_manager_interface = bus.get_object(get_lxc_manager_dbus_name(), get_lxc_manager_dbus_path()) |
1355 | 149 | self.lxc_manager_interface = dbus.Interface(lxc_mgr_service, get_lxc_manager_dbus_name()) | ||
1357 | 150 | except dbus.exceptions.DBusException: | 165 | except dbus.exceptions.DBusException: |
1358 | 151 | pass | 166 | pass |
1359 | 152 | 167 | ||
1360 | 153 | def is_running(self): | ||
1361 | 154 | return self.container.running | ||
1362 | 155 | |||
1363 | 156 | def timezone_needs_update(self): | 168 | def timezone_needs_update(self): |
1364 | 157 | host_timezone = get_host_timezone() | ||
1365 | 158 | |||
1366 | 159 | with open(os.path.join(self.root_path, 'etc', 'timezone'), 'r') as fd: | 169 | with open(os.path.join(self.root_path, 'etc', 'timezone'), 'r') as fd: |
1373 | 160 | container_timezone = fd.read().strip('\n') | 170 | return fd.read().strip('\n') != self.host_info.get_host_timezone() |
1368 | 161 | |||
1369 | 162 | if host_timezone == container_timezone: | ||
1370 | 163 | return False | ||
1371 | 164 | else: | ||
1372 | 165 | return True | ||
1374 | 166 | 171 | ||
1375 | 167 | def start_container(self): | 172 | def start_container(self): |
1376 | 168 | if self.lxc_manager_interface: | 173 | if self.lxc_manager_interface: |
1378 | 169 | (result, error) = self.lxc_manager_interface.operation_start(self.container_id, self.lxc_log_file) | 174 | result = LifecycleResult.from_dict(self.lxc_manager_interface.operation_start(self.container_id)) |
1379 | 170 | else: | 175 | else: |
1381 | 171 | (result, error) = lxc_start(self.container, self.lxc_log_file) | 176 | result = lxc_start(self.container) |
1382 | 172 | 177 | ||
1386 | 173 | if not result: | 178 | if not result.success: |
1387 | 174 | self._dump_lxc_log() | 179 | _dump_lxc_log(result.logfile) |
1388 | 175 | raise RuntimeError(error) | 180 | raise RuntimeError(result.error) |
1389 | 176 | 181 | ||
1390 | 177 | if self.run_in_container("mountpoint -q /tmp/.X11-unix") == 0: | 182 | if self.run_in_container("mountpoint -q /tmp/.X11-unix") == 0: |
1391 | 178 | self.run_in_container("umount /tmp/.X11-unix") | 183 | self.run_in_container("umount /tmp/.X11-unix") |
1392 | @@ -189,14 +194,14 @@ | |||
1393 | 189 | cmd_args = shlex.split(command_string) | 194 | cmd_args = shlex.split(command_string) |
1394 | 190 | return self.container.attach_wait(lxc.attach_run_command, cmd_args) | 195 | return self.container.attach_wait(lxc.attach_run_command, cmd_args) |
1395 | 191 | 196 | ||
1397 | 192 | def update_packages(self, verbosity=1): | 197 | def update_packages(self): |
1398 | 193 | if self.timezone_needs_update(): | 198 | if self.timezone_needs_update(): |
1399 | 194 | self.run_in_container("bash -c \'echo \"{}\" >/etc/timezone\'".format( | 199 | self.run_in_container("bash -c \'echo \"{}\" >/etc/timezone\'".format( |
1401 | 195 | get_host_timezone())) | 200 | self.host_info.get_host_timezone())) |
1402 | 196 | self.run_in_container("rm -f /etc/localtime") | 201 | self.run_in_container("rm -f /etc/localtime") |
1403 | 197 | self.run_in_container("dpkg-reconfigure -f noninteractive tzdata") | 202 | self.run_in_container("dpkg-reconfigure -f noninteractive tzdata") |
1404 | 198 | 203 | ||
1406 | 199 | return super().update_packages(verbosity) | 204 | return super().update_packages() |
1407 | 200 | 205 | ||
1408 | 201 | def destroy_libertine_container(self): | 206 | def destroy_libertine_container(self): |
1409 | 202 | if not self.container.defined: | 207 | if not self.container.defined: |
1410 | @@ -206,7 +211,7 @@ | |||
1411 | 206 | self.container.destroy() | 211 | self.container.destroy() |
1412 | 207 | return True | 212 | return True |
1413 | 208 | 213 | ||
1415 | 209 | def create_libertine_container(self, password=None, multiarch=False, verbosity=1): | 214 | def create_libertine_container(self, password=None, multiarch=False): |
1416 | 210 | if password is None: | 215 | if password is None: |
1417 | 211 | return False | 216 | return False |
1418 | 212 | 217 | ||
1419 | @@ -214,10 +219,10 @@ | |||
1420 | 214 | user_id = os.getuid() | 219 | user_id = os.getuid() |
1421 | 215 | group_id = os.getgid() | 220 | group_id = os.getgid() |
1422 | 216 | 221 | ||
1424 | 217 | setup_host_environment(username) | 222 | _setup_host_environment(username) |
1425 | 218 | 223 | ||
1426 | 219 | # Generate the default lxc default config, if it doesn't exist | 224 | # Generate the default lxc default config, if it doesn't exist |
1428 | 220 | config_path = get_lxc_default_config_path() | 225 | config_path = _get_lxc_default_config_path() |
1429 | 221 | config_file = "%s/default.conf" % config_path | 226 | config_file = "%s/default.conf" % config_path |
1430 | 222 | 227 | ||
1431 | 223 | if not os.path.exists(config_path): | 228 | if not os.path.exists(config_path): |
1432 | @@ -236,25 +241,27 @@ | |||
1433 | 236 | fd.write("lxc.id_map = u %s %s %s\n" % (user_id + 1, (user_id + 1) + 100000, 65536 - (user_id + 1))) | 241 | fd.write("lxc.id_map = u %s %s %s\n" % (user_id + 1, (user_id + 1) + 100000, 65536 - (user_id + 1))) |
1434 | 237 | fd.write("lxc.id_map = g %s %s %s\n" % (group_id + 1, (group_id + 1) + 100000, 65536 - (user_id + 1))) | 242 | fd.write("lxc.id_map = g %s %s %s\n" % (group_id + 1, (group_id + 1) + 100000, 65536 - (user_id + 1))) |
1435 | 238 | 243 | ||
1436 | 244 | self.container.load_config(config_file) | ||
1437 | 245 | |||
1438 | 239 | utils.create_libertine_user_data_dir(self.container_id) | 246 | utils.create_libertine_user_data_dir(self.container_id) |
1439 | 240 | 247 | ||
1440 | 241 | with EnvLxcSettings(): | 248 | with EnvLxcSettings(): |
1441 | 249 | lxc_logfile = get_logfile(self.container) | ||
1442 | 242 | if not self.container.create("download", 0, | 250 | if not self.container.create("download", 0, |
1443 | 243 | {"dist": "ubuntu", | 251 | {"dist": "ubuntu", |
1444 | 244 | "release": self.installed_release, | 252 | "release": self.installed_release, |
1445 | 245 | "arch": self.architecture}): | 253 | "arch": self.architecture}): |
1448 | 246 | print("Failed to create container") | 254 | utils.get_logger().error("Failed to create container") |
1449 | 247 | self._dump_lxc_log() | 255 | _dump_lxc_log(lxc_logfile) |
1450 | 248 | return False | 256 | return False |
1451 | 249 | 257 | ||
1452 | 250 | self.create_libertine_config() | 258 | self.create_libertine_config() |
1453 | 251 | 259 | ||
1456 | 252 | if verbosity == 1: | 260 | utils.get_logger().info("starting container ...") |
1455 | 253 | print("starting container ...") | ||
1457 | 254 | try: | 261 | try: |
1458 | 255 | self.start_container() | 262 | self.start_container() |
1459 | 256 | except RuntimeError as e: | 263 | except RuntimeError as e: |
1461 | 257 | print("Container failed to start: %s" % e) | 264 | utils.get_logger().error("Container failed to start: %s" % e) |
1462 | 258 | self.destroy_libertine_container() | 265 | self.destroy_libertine_container() |
1463 | 259 | return False | 266 | return False |
1464 | 260 | 267 | ||
1465 | @@ -263,22 +270,19 @@ | |||
1466 | 263 | str(user_id), crypt.crypt(password), str(username))) | 270 | str(user_id), crypt.crypt(password), str(username))) |
1467 | 264 | 271 | ||
1468 | 265 | if multiarch and self.architecture == 'amd64': | 272 | if multiarch and self.architecture == 'amd64': |
1471 | 266 | if verbosity == 1: | 273 | utils.get_logger().info("Adding i386 multiarch support...") |
1470 | 267 | print("Adding i386 multiarch support...") | ||
1472 | 268 | self.run_in_container("dpkg --add-architecture i386") | 274 | self.run_in_container("dpkg --add-architecture i386") |
1473 | 269 | 275 | ||
1477 | 270 | if verbosity == 1: | 276 | utils.get_logger().info("Updating the contents of the container after creation...") |
1478 | 271 | print("Updating the contents of the container after creation...") | 277 | self.update_packages() |
1476 | 272 | self.update_packages(verbosity) | ||
1479 | 273 | 278 | ||
1480 | 274 | for package in self.default_packages: | 279 | for package in self.default_packages: |
1483 | 275 | if not self.install_package(package, verbosity, update_cache=False): | 280 | if not self.install_package(package, update_cache=False): |
1484 | 276 | print("Failure installing %s during container creation" % package) | 281 | utils.get_logger().error("Failure installing %s during container creation" % package) |
1485 | 277 | self.destroy_libertine_container() | 282 | self.destroy_libertine_container() |
1486 | 278 | return False | 283 | return False |
1487 | 279 | 284 | ||
1490 | 280 | if verbosity == 1: | 285 | utils.get_logger().info("stopping container ...") |
1489 | 281 | print("stopping container ...") | ||
1491 | 282 | self.stop_container() | 286 | self.stop_container() |
1492 | 283 | 287 | ||
1493 | 284 | return True | 288 | return True |
1494 | @@ -316,17 +320,17 @@ | |||
1495 | 316 | 320 | ||
1496 | 317 | def start_application(self, app_exec_line, environ): | 321 | def start_application(self, app_exec_line, environ): |
1497 | 318 | if self.lxc_manager_interface == None: | 322 | if self.lxc_manager_interface == None: |
1499 | 319 | print("No interface to libertine-lxc-manager. Failing application launch.") | 323 | utils.get_logger().error("No interface to libertine-lxc-manager. Failing application launch.") |
1500 | 320 | return | 324 | return |
1501 | 321 | 325 | ||
1502 | 322 | os.environ.clear() | 326 | os.environ.clear() |
1503 | 323 | os.environ.update(environ) | 327 | os.environ.update(environ) |
1504 | 324 | 328 | ||
1506 | 325 | (result, error) = self.lxc_manager_interface.app_start(self.container_id, self.lxc_log_file) | 329 | result = LifecycleResult.from_dict(self.lxc_manager_interface.app_start(self.container_id)) |
1507 | 326 | 330 | ||
1511 | 327 | if not result: | 331 | if not result.success: |
1512 | 328 | self._dump_lxc_log() | 332 | _dump_lxc_log(get_logfile(self.container)) |
1513 | 329 | print("%s" % error) | 333 | utils.get_logger().error("%s" % result.error) |
1514 | 330 | return | 334 | return |
1515 | 331 | 335 | ||
1516 | 332 | self.window_manager = self.container.attach(lxc.attach_run_command, | 336 | self.window_manager = self.container.attach(lxc.attach_run_command, |
1517 | @@ -348,17 +352,3 @@ | |||
1518 | 348 | 352 | ||
1519 | 349 | # Tell libertine-lxc-manager that the app has stopped. | 353 | # Tell libertine-lxc-manager that the app has stopped. |
1520 | 350 | self.lxc_manager_interface.app_stop(self.container_id) | 354 | self.lxc_manager_interface.app_stop(self.container_id) |
1521 | 351 | |||
1522 | 352 | def _set_lxc_log(self): | ||
1523 | 353 | self.lxc_log_file = os.path.join(tempfile.mkdtemp(), 'lxc-start.log') | ||
1524 | 354 | self.container.append_config_item("lxc.logfile", self.lxc_log_file) | ||
1525 | 355 | self.container.append_config_item("lxc.logpriority", "3") | ||
1526 | 356 | |||
1527 | 357 | def _dump_lxc_log(self): | ||
1528 | 358 | if os.path.exists(self.lxc_log_file): | ||
1529 | 359 | try: | ||
1530 | 360 | with open(self.lxc_log_file, 'r') as fd: | ||
1531 | 361 | for line in fd: | ||
1532 | 362 | print(line.lstrip()) | ||
1533 | 363 | except Exception as ex: | ||
1534 | 364 | print("Could not open LXC log file: %s" % ex, file=sys.stderr) | ||
1535 | 365 | 355 | ||
1536 | === added file 'python/libertine/LxdContainer.py' | |||
1537 | --- python/libertine/LxdContainer.py 1970-01-01 00:00:00 +0000 | |||
1538 | +++ python/libertine/LxdContainer.py 2017-01-05 20:19:05 +0000 | |||
1539 | @@ -0,0 +1,455 @@ | |||
1540 | 1 | # Copyright 2016 Canonical Ltd. | ||
1541 | 2 | # | ||
1542 | 3 | # This program is free software: you can redistribute it and/or modify it | ||
1543 | 4 | # under the terms of the GNU General Public License version 3, as published | ||
1544 | 5 | # by the Free Software Foundation. | ||
1545 | 6 | # | ||
1546 | 7 | # This program is distributed in the hope that it will be useful, but | ||
1547 | 8 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
1548 | 9 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
1549 | 10 | # PURPOSE. See the GNU General Public License for more details. | ||
1550 | 11 | # | ||
1551 | 12 | # You should have received a copy of the GNU General Public License along | ||
1552 | 13 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1553 | 14 | |||
1554 | 15 | import crypt | ||
1555 | 16 | import dbus | ||
1556 | 17 | import os | ||
1557 | 18 | import psutil | ||
1558 | 19 | import pylxd | ||
1559 | 20 | import shlex | ||
1560 | 21 | import subprocess | ||
1561 | 22 | import time | ||
1562 | 23 | |||
1563 | 24 | from .lifecycle import LifecycleResult | ||
1564 | 25 | from libertine import Libertine, utils, HostInfo | ||
1565 | 26 | |||
1566 | 27 | |||
1567 | 28 | LIBERTINE_LXC_MANAGER_NAME = "com.canonical.libertine.LxdManager" | ||
1568 | 29 | LIBERTINE_LXC_MANAGER_PATH = "/LxdManager" | ||
1569 | 30 | |||
1570 | 31 | |||
1571 | 32 | def get_lxd_manager_dbus_name(): | ||
1572 | 33 | return LIBERTINE_LXC_MANAGER_NAME | ||
1573 | 34 | |||
1574 | 35 | |||
1575 | 36 | def get_lxd_manager_dbus_path(): | ||
1576 | 37 | return LIBERTINE_LXC_MANAGER_PATH | ||
1577 | 38 | |||
1578 | 39 | |||
1579 | 40 | def _get_devices_map(): | ||
1580 | 41 | devices = { | ||
1581 | 42 | '/dev/tty0': {'path': '/dev/tty0', 'type': 'unix-char'}, | ||
1582 | 43 | '/dev/tty7': {'path': '/dev/tty7', 'type': 'unix-char'}, | ||
1583 | 44 | '/dev/tty8': {'path': '/dev/tty8', 'type': 'unix-char'}, | ||
1584 | 45 | '/dev/fb0': {'path': '/dev/fb0', 'type': 'unix-char'}, | ||
1585 | 46 | 'x11-socket': {'source': '/tmp/.X11-unix', 'path': '/tmp/.X11-unix', 'type': 'disk', 'optional': 'true'}, | ||
1586 | 47 | } | ||
1587 | 48 | |||
1588 | 49 | if os.path.exists('/dev/video0'): | ||
1589 | 50 | devices['/dev/video0'] = {'path': '/dev/video0', 'type': 'unix-char'} | ||
1590 | 51 | |||
1591 | 52 | # every regular file in /dev/snd | ||
1592 | 53 | for f in ['/dev/snd/{}'.format(f) for f in os.listdir('/dev/snd') if not os.path.isdir('/dev/snd/{}'.format(f))]: | ||
1593 | 54 | devices[f] = {'path': f, 'type': 'unix-char'} | ||
1594 | 55 | |||
1595 | 56 | # every regular file in /dev/dri | ||
1596 | 57 | for f in ['/dev/dri/{}'.format(f) for f in os.listdir('/dev/dri') if not os.path.isdir('/dev/dri/{}'.format(f))]: | ||
1597 | 58 | devices[f] = {'path': f, 'type': 'unix-char'} | ||
1598 | 59 | |||
1599 | 60 | # Some devices require special mappings for snap | ||
1600 | 61 | if utils.is_snap_environment(): | ||
1601 | 62 | devices['x11-socket']['source'] = '/var/lib/snapd/hostfs/tmp/.X11-unix' | ||
1602 | 63 | |||
1603 | 64 | return devices | ||
1604 | 65 | |||
1605 | 66 | def _readlink(source): | ||
1606 | 67 | while os.path.islink(source): | ||
1607 | 68 | new_source = os.readlink(source) | ||
1608 | 69 | if not os.path.isabs(new_source): | ||
1609 | 70 | new_source = os.path.join(os.path.dirname(source), new_source) | ||
1610 | 71 | source = new_source | ||
1611 | 72 | |||
1612 | 73 | return source | ||
1613 | 74 | |||
1614 | 75 | def _setup_lxd(): | ||
1615 | 76 | utils.get_logger().info("Running LXD setup.") | ||
1616 | 77 | import pexpect | ||
1617 | 78 | child = pexpect.spawnu('sudo libertine-lxd-setup {}'.format(os.environ['USER']), env={'TERM': 'dumb'}) | ||
1618 | 79 | |||
1619 | 80 | while True: | ||
1620 | 81 | index = child.expect(['.+\[default=.+\].*', pexpect.EOF, pexpect.TIMEOUT, | ||
1621 | 82 | # The following are required for lxd=2.0.x | ||
1622 | 83 | '.+\[yes/no\].*', | ||
1623 | 84 | '.+\(e.g. (?P<example>[a-z0-9\.:]+)\).+']) | ||
1624 | 85 | if index == 0: | ||
1625 | 86 | child.sendline() | ||
1626 | 87 | elif index == 1: | ||
1627 | 88 | return True | ||
1628 | 89 | elif index == 2: | ||
1629 | 90 | return False | ||
1630 | 91 | elif index == 3: | ||
1631 | 92 | child.sendline('yes') | ||
1632 | 93 | elif index == 4: | ||
1633 | 94 | child.sendline(child.match.group('example')) | ||
1634 | 95 | |||
1635 | 96 | if child.exitstatus is not None: | ||
1636 | 97 | return child.exitstatus == 0 | ||
1637 | 98 | |||
1638 | 99 | |||
1639 | 100 | def _setup_bind_mount_service(container, uid, username): | ||
1640 | 101 | utils.get_logger().info("Creating systemd mount override service") | ||
1641 | 102 | service = ''' | ||
1642 | 103 | [Unit] | ||
1643 | 104 | Description=Fix system mounts for libertine | ||
1644 | 105 | |||
1645 | 106 | [Service] | ||
1646 | 107 | ExecStart=/usr/bin/libertine-lxd-mount-update | ||
1647 | 108 | |||
1648 | 109 | [Install] | ||
1649 | 110 | WantedBy=multi-user.target | ||
1650 | 111 | '''[1:-1] | ||
1651 | 112 | container.files.put('/etc/systemd/system/libertine-lxd-mount-update.service', service.encode('utf-8')) | ||
1652 | 113 | container.execute(shlex.split('chmod 644 /etc/systemd/system/libertine-lxd-mount-update.service')) | ||
1653 | 114 | |||
1654 | 115 | utils.get_logger().info("Creating mount update shell script") | ||
1655 | 116 | script = ''' | ||
1656 | 117 | #!/bin/sh | ||
1657 | 118 | |||
1658 | 119 | mkdir -p /run/user/{uid} | ||
1659 | 120 | chown {username}:{username} /run/user/{uid} | ||
1660 | 121 | chmod 755 /run/user/{uid} | ||
1661 | 122 | mount -o bind /var/tmp/run/user/{uid} /run/user/{uid} | ||
1662 | 123 | |||
1663 | 124 | chgrp audio /dev/snd/* | ||
1664 | 125 | chgrp video /dev/dri/* | ||
1665 | 126 | [ -n /dev/video0 ] && chgrp video /dev/video0 | ||
1666 | 127 | '''[1:-1] | ||
1667 | 128 | container.files.put('/usr/bin/libertine-lxd-mount-update', script.format(uid=uid, username=username).encode('utf-8')) | ||
1668 | 129 | container.execute(shlex.split('chmod 755 /usr/bin/libertine-lxd-mount-update')) | ||
1669 | 130 | |||
1670 | 131 | utils.get_logger().info("Enabling systemd mount update service") | ||
1671 | 132 | container.execute(shlex.split('systemctl enable libertine-lxd-mount-update.service')) | ||
1672 | 133 | |||
1673 | 134 | |||
1674 | 135 | def lxd_container(client, container_id): | ||
1675 | 136 | try: | ||
1676 | 137 | return client.containers.get(container_id) | ||
1677 | 138 | except pylxd.exceptions.LXDAPIException: | ||
1678 | 139 | return None | ||
1679 | 140 | |||
1680 | 141 | |||
1681 | 142 | def _wait_for_network(container): | ||
1682 | 143 | for retries in range(0, 10): | ||
1683 | 144 | out, err = container.execute(shlex.split('ping -c 1 ubuntu.com')) | ||
1684 | 145 | if out: | ||
1685 | 146 | utils.get_logger().info("Network connection active") | ||
1686 | 147 | return True | ||
1687 | 148 | time.sleep(1) | ||
1688 | 149 | return False | ||
1689 | 150 | |||
1690 | 151 | |||
1691 | 152 | def lxd_start(container): | ||
1692 | 153 | if container.status != 'Running': | ||
1693 | 154 | container.start(wait=True) | ||
1694 | 155 | |||
1695 | 156 | container.sync(rollback=True) # required for pylxd=2.0.x | ||
1696 | 157 | |||
1697 | 158 | if container.status != 'Running': | ||
1698 | 159 | return LifecycleResult("Container {} failed to start".format(container.name)) | ||
1699 | 160 | |||
1700 | 161 | return LifecycleResult() | ||
1701 | 162 | |||
1702 | 163 | |||
1703 | 164 | def lxd_stop(container, wait): | ||
1704 | 165 | if container.status == 'Stopped': | ||
1705 | 166 | return LifecycleResult() | ||
1706 | 167 | |||
1707 | 168 | container.stop(wait=wait) | ||
1708 | 169 | container.sync(rollback=True) # required for pylxd=2.0.x | ||
1709 | 170 | |||
1710 | 171 | if wait and container.status != 'Stopped': | ||
1711 | 172 | return LifecycleResult("Container {} failed to stop".format(container.name)) | ||
1712 | 173 | |||
1713 | 174 | return LifecycleResult() | ||
1714 | 175 | |||
1715 | 176 | |||
1716 | 177 | def update_bind_mounts(container, config): | ||
1717 | 178 | home_path = os.environ['HOME'] | ||
1718 | 179 | |||
1719 | 180 | container.devices.clear() | ||
1720 | 181 | container.devices['root'] = {'type': 'disk', 'path': '/'} | ||
1721 | 182 | |||
1722 | 183 | if os.path.exists(os.path.join(home_path, '.config', 'dconf')): | ||
1723 | 184 | container.devices['dconf'] = { | ||
1724 | 185 | 'type': 'disk', | ||
1725 | 186 | 'source': os.path.join(home_path, '.config', 'dconf'), | ||
1726 | 187 | 'path': os.path.join(home_path, '.config', 'dconf') | ||
1727 | 188 | } | ||
1728 | 189 | |||
1729 | 190 | run_user = '/run/user/{}'.format(os.getuid()) | ||
1730 | 191 | container.devices[run_user] = {'source': run_user, 'path': '/var/tmp{}'.format(run_user), 'type': 'disk'} | ||
1731 | 192 | |||
1732 | 193 | mounts = utils.get_common_xdg_user_directories() + \ | ||
1733 | 194 | config.get_container_bind_mounts(container.name) | ||
1734 | 195 | for user_dir in utils.generate_binding_directories(mounts, home_path): | ||
1735 | 196 | if not os.path.exists(user_dir[0]): | ||
1736 | 197 | utils.get_logger().warning('Bind-mount path \'{}\' does not exist.'.format(user_dir[0])) | ||
1737 | 198 | continue | ||
1738 | 199 | |||
1739 | 200 | if os.path.isabs(user_dir[1]): | ||
1740 | 201 | path = user_dir[1] | ||
1741 | 202 | else: | ||
1742 | 203 | path = os.path.join(home_path, user_dir[1]) | ||
1743 | 204 | |||
1744 | 205 | utils.get_logger().debug("Mounting {}:{} in container {}".format(user_dir[0], path, container.name)) | ||
1745 | 206 | |||
1746 | 207 | container.devices[user_dir[1]] = { | ||
1747 | 208 | 'source': _readlink(user_dir[0]), | ||
1748 | 209 | 'path': path, | ||
1749 | 210 | 'optional': 'true', | ||
1750 | 211 | 'type': 'disk' | ||
1751 | 212 | } | ||
1752 | 213 | |||
1753 | 214 | try: | ||
1754 | 215 | container.save(wait=True) | ||
1755 | 216 | except pylxd.exceptions.LXDAPIException as e: | ||
1756 | 217 | utils.get_logger().warning('Saving bind mounts for container \'{}\' raised: {}'.format(container.name, str(e))) | ||
1757 | 218 | # This is most likely the result of the container currently running | ||
1758 | 219 | |||
1759 | 220 | |||
1760 | 221 | def update_libertine_profile(client): | ||
1761 | 222 | try: | ||
1762 | 223 | profile = client.profiles.get('libertine') | ||
1763 | 224 | |||
1764 | 225 | utils.get_logger().info('Updating existing lxd profile.') | ||
1765 | 226 | profile.devices = _get_devices_map() | ||
1766 | 227 | profile.config['raw.idmap'] = 'both 1000 1000' | ||
1767 | 228 | |||
1768 | 229 | try: | ||
1769 | 230 | profile.save() | ||
1770 | 231 | except pylxd.exceptions.LXDAPIException as e: | ||
1771 | 232 | utils.get_logger().warning('Saving libertine lxd profile raised: {}'.format(str(e))) | ||
1772 | 233 | # This is most likely the result of an older container currently | ||
1773 | 234 | # running and/or containing a conflicting device entry | ||
1774 | 235 | except pylxd.exceptions.LXDAPIException: | ||
1775 | 236 | utils.get_logger().info('Creating libertine lxd profile.') | ||
1776 | 237 | client.profiles.create('libertine', config={'raw.idmap': 'both 1000 1000'}, devices=_get_devices_map()) | ||
1777 | 238 | |||
1778 | 239 | |||
1779 | 240 | class LibertineLXD(Libertine.BaseContainer): | ||
1780 | 241 | def __init__(self, name, config): | ||
1781 | 242 | super().__init__(name) | ||
1782 | 243 | self._id = name | ||
1783 | 244 | self._config = config | ||
1784 | 245 | self._host_info = HostInfo.HostInfo() | ||
1785 | 246 | self._container = None | ||
1786 | 247 | self._matchbox_pid = None | ||
1787 | 248 | |||
1788 | 249 | if not _setup_lxd(): | ||
1789 | 250 | raise Exception("Failed to setup lxd.") | ||
1790 | 251 | |||
1791 | 252 | self._client = pylxd.Client() | ||
1792 | 253 | self._window_manager = None | ||
1793 | 254 | self.root_path = '{}/containers/{}/rootfs'.format(os.getenv('LXD_DIR', '/var/lib/lxd'), name) | ||
1794 | 255 | |||
1795 | 256 | utils.set_session_dbus_env_var() | ||
1796 | 257 | try: | ||
1797 | 258 | bus = dbus.SessionBus() | ||
1798 | 259 | self._manager = bus.get_object(get_lxd_manager_dbus_name(), get_lxd_manager_dbus_path()) | ||
1799 | 260 | except dbus.exceptions.DBusException: | ||
1800 | 261 | utils.get_logger().warning("D-Bus Service not found.") | ||
1801 | 262 | self._manager = None | ||
1802 | 263 | |||
1803 | 264 | |||
1804 | 265 | def create_libertine_container(self, password=None, multiarch=False): | ||
1805 | 266 | if self._try_get_container(): | ||
1806 | 267 | utils.get_logger().error("Container already exists") | ||
1807 | 268 | return False | ||
1808 | 269 | |||
1809 | 270 | update_libertine_profile(self._client) | ||
1810 | 271 | |||
1811 | 272 | utils.get_logger().info("Creating container '%s' with distro '%s'" % (self._id, self.installed_release)) | ||
1812 | 273 | create = subprocess.Popen(shlex.split('lxc launch ubuntu-daily:{distro} {id} --profile ' | ||
1813 | 274 | 'default --profile libertine'.format( | ||
1814 | 275 | distro=self.installed_release, id=self._id))) | ||
1815 | 276 | if create.wait() is not 0: | ||
1816 | 277 | utils.get_logger().error("Creating container '{}' failed with code '{}'".format(self._id, create.returncode)) | ||
1817 | 278 | return False | ||
1818 | 279 | |||
1819 | 280 | if not self.start_container(): | ||
1820 | 281 | utils.get_logger().error("Failed to start container '{}'".format(self._id)) | ||
1821 | 282 | self.destroy_libertine_container() | ||
1822 | 283 | return False | ||
1823 | 284 | |||
1824 | 285 | username = os.environ['USER'] | ||
1825 | 286 | uid = str(os.getuid()) | ||
1826 | 287 | self.run_in_container("userdel -r ubuntu") | ||
1827 | 288 | self.run_in_container("useradd -u {} -U -p {} -G sudo,audio,video {}".format( | ||
1828 | 289 | uid, crypt.crypt(''), username)) | ||
1829 | 290 | self.run_in_container("mkdir -p /home/{}".format(username)) | ||
1830 | 291 | self.run_in_container("chown {0}:{0} /home/{0}".format(username)) | ||
1831 | 292 | |||
1832 | 293 | _setup_bind_mount_service(self._container, uid, username) | ||
1833 | 294 | |||
1834 | 295 | if multiarch and self.architecture == 'amd64': | ||
1835 | 296 | utils.get_logger().info("Adding i386 multiarch support to container '{}'".format(self._id)) | ||
1836 | 297 | self.run_in_container("dpkg --add-architecture i386") | ||
1837 | 298 | |||
1838 | 299 | self.update_packages() | ||
1839 | 300 | |||
1840 | 301 | for package in self.default_packages: | ||
1841 | 302 | utils.get_logger().info("Installing package '%s' in container '%s'" % (package, self._id)) | ||
1842 | 303 | if not self.install_package(package, no_dialog=True, update_cache=False): | ||
1843 | 304 | utils.get_logger().error("Failure installing '%s' during container creation" % package) | ||
1844 | 305 | self.destroy_libertine_container() | ||
1845 | 306 | return False | ||
1846 | 307 | |||
1847 | 308 | return True | ||
1848 | 309 | |||
1849 | 310 | def update_packages(self): | ||
1850 | 311 | if not self._timezone_in_sync(): | ||
1851 | 312 | utils.get_logger().info("Re-syncing timezones") | ||
1852 | 313 | self.run_in_container("bash -c 'echo \"%s\" > /etc/timezone'" % self._host_info.get_host_timezone()) | ||
1853 | 314 | self.run_in_container("rm -f /etc/localtime") | ||
1854 | 315 | self.run_in_container("dpkg-reconfigure -f noninteractive tzdata") | ||
1855 | 316 | |||
1856 | 317 | return super().update_packages() | ||
1857 | 318 | |||
1858 | 319 | def destroy_libertine_container(self): | ||
1859 | 320 | if not self._try_get_container(): | ||
1860 | 321 | utils.get_logger().error("No such container '%s'" % self._id) | ||
1861 | 322 | return False | ||
1862 | 323 | |||
1863 | 324 | self.stop_container(wait=True) | ||
1864 | 325 | self._container.delete() | ||
1865 | 326 | return True | ||
1866 | 327 | |||
1867 | 328 | def _timezone_in_sync(self): | ||
1868 | 329 | proc = subprocess.Popen(self._lxc_args('cat /etc/timezone'), stdout=subprocess.PIPE) | ||
1869 | 330 | out, err = proc.communicate() | ||
1870 | 331 | return out.decode('UTF-8').strip('\n') == self._host_info.get_host_timezone() | ||
1871 | 332 | |||
1872 | 333 | def _lxc_args(self, command, environ={}): | ||
1873 | 334 | env_as_args = ' ' | ||
1874 | 335 | for k, v in environ.items(): | ||
1875 | 336 | env_as_args += '--env {}="{}" '.format(k, v) | ||
1876 | 337 | |||
1877 | 338 | return shlex.split('lxc exec {}{}-- {}'.format(self._id, | ||
1878 | 339 | env_as_args, | ||
1879 | 340 | command)) | ||
1880 | 341 | |||
1881 | 342 | def run_in_container(self, command): | ||
1882 | 343 | proc = subprocess.Popen(self._lxc_args(command)) | ||
1883 | 344 | return proc.wait() | ||
1884 | 345 | |||
1885 | 346 | def start_container(self): | ||
1886 | 347 | if not self._try_get_container(): | ||
1887 | 348 | return False | ||
1888 | 349 | |||
1889 | 350 | if self._manager: | ||
1890 | 351 | result = LifecycleResult.from_dict(self._manager.operation_start(self._id)) | ||
1891 | 352 | else: | ||
1892 | 353 | result = lxd_start(self._container) | ||
1893 | 354 | |||
1894 | 355 | if not result.success: | ||
1895 | 356 | utils.get_logger().error(result.error) | ||
1896 | 357 | return False | ||
1897 | 358 | |||
1898 | 359 | if not _wait_for_network(self._container): | ||
1899 | 360 | utils.get_logger().warning("Network unavailable in container '{}'".format(self._id)) | ||
1900 | 361 | |||
1901 | 362 | return result.success | ||
1902 | 363 | |||
1903 | 364 | def stop_container(self, wait=False): | ||
1904 | 365 | if not self._try_get_container(): | ||
1905 | 366 | return False | ||
1906 | 367 | |||
1907 | 368 | if self._manager: | ||
1908 | 369 | result = LifecycleResult.from_dict(self._manager.operation_stop(self._id)) | ||
1909 | 370 | else: | ||
1910 | 371 | result = lxd_stop(self._container, wait) | ||
1911 | 372 | |||
1912 | 373 | if not result.success: | ||
1913 | 374 | utils.get_logger().error(result.error) | ||
1914 | 375 | |||
1915 | 376 | return result.success | ||
1916 | 377 | |||
1917 | 378 | def _get_matchbox_pids(self): | ||
1918 | 379 | p = subprocess.Popen(self._lxc_args('pgrep matchbox'), stdout=subprocess.PIPE) | ||
1919 | 380 | out, err = p.communicate() | ||
1920 | 381 | return out.decode('utf-8').split('\n') | ||
1921 | 382 | |||
1922 | 383 | # FIXME: Remove once window management logic has been moved to the host | ||
1923 | 384 | def _start_window_manager(self, args): | ||
1924 | 385 | args.extend(utils.setup_window_manager(self._id)) | ||
1925 | 386 | |||
1926 | 387 | if 'matchbox-window-manager' in args: | ||
1927 | 388 | pids = self._get_matchbox_pids() | ||
1928 | 389 | |||
1929 | 390 | self._window_manager = psutil.Popen(args) | ||
1930 | 391 | |||
1931 | 392 | time.sleep(.25) # Give matchbox a moment to start in the container | ||
1932 | 393 | if self._window_manager.poll() is not None: | ||
1933 | 394 | utils.get_logger().warning("Window manager terminated prematurely with exit code '{}'".format( | ||
1934 | 395 | self._window_manager.returncode)) | ||
1935 | 396 | self._window_manager = None | ||
1936 | 397 | elif 'matchbox-window-manager' in args: | ||
1937 | 398 | after_pids = self._get_matchbox_pids() | ||
1938 | 399 | if len(after_pids) > len(pids): | ||
1939 | 400 | self._matchbox_pid = (set(after_pids) - set(pids)).pop() | ||
1940 | 401 | |||
1941 | 402 | def start_application(self, app_exec_line, environ): | ||
1942 | 403 | if not self._try_get_container(): | ||
1943 | 404 | utils.get_logger().error("Could not get container '{}'".format(self._id)) | ||
1944 | 405 | return None | ||
1945 | 406 | |||
1946 | 407 | if self._manager: | ||
1947 | 408 | result = LifecycleResult.from_dict(self._manager.app_start(self._id)) | ||
1948 | 409 | else: | ||
1949 | 410 | update_libertine_profile(self._client) | ||
1950 | 411 | update_bind_mounts(self._container, self._config) | ||
1951 | 412 | result = lxd_start(self._container) | ||
1952 | 413 | |||
1953 | 414 | if not result.success: | ||
1954 | 415 | utils.get_logger().error(result.error) | ||
1955 | 416 | return False | ||
1956 | 417 | |||
1957 | 418 | args = self._lxc_args("sudo -E -u {} env PATH={}".format(os.environ['USER'], environ['PATH']), environ) | ||
1958 | 419 | |||
1959 | 420 | self._start_window_manager(args.copy()) | ||
1960 | 421 | |||
1961 | 422 | args.extend(app_exec_line) | ||
1962 | 423 | return psutil.Popen(args) | ||
1963 | 424 | |||
1964 | 425 | def finish_application(self, app): | ||
1965 | 426 | if self._window_manager is not None: | ||
1966 | 427 | utils.terminate_window_manager(self._window_manager) | ||
1967 | 428 | self._window_manager = None | ||
1968 | 429 | |||
1969 | 430 | # This is a workaround for an issue on xenial where the process | ||
1970 | 431 | # running the window manager is not killed with the application | ||
1971 | 432 | if self._matchbox_pid is not None: | ||
1972 | 433 | utils.get_logger().debug("Manually killing matchbox-window-manager") | ||
1973 | 434 | self.run_in_container("kill -9 {}".format(self._matchbox_pid)) | ||
1974 | 435 | self._matchbox_pid = None | ||
1975 | 436 | |||
1976 | 437 | app.wait() | ||
1977 | 438 | |||
1978 | 439 | if self._manager: | ||
1979 | 440 | self._manager.app_stop(self.container_id) | ||
1980 | 441 | else: | ||
1981 | 442 | lxd_stop(self._container, False) | ||
1982 | 443 | |||
1983 | 444 | def copy_file_to_container(self, source, dest): | ||
1984 | 445 | with open(source, 'rb') as f: | ||
1985 | 446 | return self._container.files.put(dest, f.read()) | ||
1986 | 447 | |||
1987 | 448 | def delete_file_in_container(self, path): | ||
1988 | 449 | return self.run_in_container('rm {}'.format(path)) | ||
1989 | 450 | |||
1990 | 451 | def _try_get_container(self): | ||
1991 | 452 | if self._container is None: | ||
1992 | 453 | self._container = lxd_container(self._client, self._id) | ||
1993 | 454 | |||
1994 | 455 | return self._container is not None | ||
1995 | 0 | 456 | ||
1996 | === modified file 'python/libertine/launcher/session.py' | |||
1997 | --- python/libertine/launcher/session.py 2016-10-27 16:57:03 +0000 | |||
1998 | +++ python/libertine/launcher/session.py 2017-01-05 20:19:05 +0000 | |||
1999 | @@ -29,9 +29,6 @@ | |||
2000 | 29 | from .task import LaunchServiceTask, TaskType | 29 | from .task import LaunchServiceTask, TaskType |
2001 | 30 | 30 | ||
2002 | 31 | 31 | ||
2003 | 32 | log = utils.get_logger() | ||
2004 | 33 | |||
2005 | 34 | |||
2006 | 35 | def translate_to_real_address(abstract_address): | 32 | def translate_to_real_address(abstract_address): |
2007 | 36 | """Translate the notional text address to a real UNIX-domain address string. | 33 | """Translate the notional text address to a real UNIX-domain address string. |
2008 | 37 | 34 | ||
2009 | @@ -101,12 +98,12 @@ | |||
2010 | 101 | b = from_socket.recv(4096) | 98 | b = from_socket.recv(4096) |
2011 | 102 | if len(b) > 0: | 99 | if len(b) > 0: |
2012 | 103 | to_socket.sendall(b) | 100 | to_socket.sendall(b) |
2014 | 104 | log.debug('copied {} bytes from fd to {}'.format(len(b), from_socket, to_socket)) | 101 | utils.get_logger().debug('copied {} bytes from fd to {}'.format(len(b), from_socket, to_socket)) |
2015 | 105 | else: | 102 | else: |
2017 | 106 | log.info('close detected on {}'.format(from_socket)) | 103 | utils.get_logger().info('close detected on {}'.format(from_socket)) |
2018 | 107 | return len(b) | 104 | return len(b) |
2019 | 108 | except Exception as e: | 105 | except Exception as e: |
2021 | 109 | log.debug(e) | 106 | utils.get_logger().debug(e) |
2022 | 110 | return 0 | 107 | return 0 |
2023 | 111 | 108 | ||
2024 | 112 | def _close_up_shop(self, session): | 109 | def _close_up_shop(self, session): |
2025 | @@ -172,7 +169,7 @@ | |||
2026 | 172 | with suppress(AttributeError): | 169 | with suppress(AttributeError): |
2027 | 173 | for task_config in self._config.prelaunch_tasks: | 170 | for task_config in self._config.prelaunch_tasks: |
2028 | 174 | if task_config.task_type == TaskType.LAUNCH_SERVICE: | 171 | if task_config.task_type == TaskType.LAUNCH_SERVICE: |
2030 | 175 | log.info("launching {}".format(task_config.datum[0])) | 172 | utils.get_logger().info("launching {}".format(task_config.datum[0])) |
2031 | 176 | task = LaunchServiceTask(task_config) | 173 | task = LaunchServiceTask(task_config) |
2032 | 177 | self._child_processes.append(task) | 174 | self._child_processes.append(task) |
2033 | 178 | task.start(self._config.host_environ) | 175 | task.start(self._config.host_environ) |
2034 | @@ -267,7 +264,7 @@ | |||
2035 | 267 | create a socket bridge to the host when a connection from the session is | 264 | create a socket bridge to the host when a connection from the session is |
2036 | 268 | made. | 265 | made. |
2037 | 269 | """ | 266 | """ |
2039 | 270 | log.debug('creating bridge listener for {} on {}'. | 267 | utils.get_logger().debug('creating bridge listener for {} on {}'. |
2040 | 271 | format(bridge_config.env_var, bridge_config.session_address)) | 268 | format(bridge_config.env_var, bridge_config.session_address)) |
2041 | 272 | sock = socket(AF_UNIX, SOCK_STREAM) | 269 | sock = socket(AF_UNIX, SOCK_STREAM) |
2042 | 273 | sock.bind(translate_to_real_address(bridge_config.session_address)) | 270 | sock.bind(translate_to_real_address(bridge_config.session_address)) |
2043 | @@ -286,7 +283,7 @@ | |||
2044 | 286 | """ | 283 | """ |
2045 | 287 | (bridge_config, sock) = datum | 284 | (bridge_config, sock) = datum |
2046 | 288 | conn = sock.accept() | 285 | conn = sock.accept() |
2048 | 289 | log.debug('connection of session socket {} accepted'.format(bridge_config.session_address)) | 286 | utils.get_logger().debug('connection of session socket {} accepted'.format(bridge_config.session_address)) |
2049 | 290 | self.add_bridge_pair(BridgePair(conn[0], bridge_config.host_address)) | 287 | self.add_bridge_pair(BridgePair(conn[0], bridge_config.host_address)) |
2050 | 291 | 288 | ||
2051 | 292 | def _ensure_paths_exist(self): | 289 | def _ensure_paths_exist(self): |
2052 | @@ -316,17 +313,17 @@ | |||
2053 | 316 | data = os.read(fd, 4) | 313 | data = os.read(fd, 4) |
2054 | 317 | sig = struct.unpack('%uB' % len(data), data) | 314 | sig = struct.unpack('%uB' % len(data), data) |
2055 | 318 | if sig[0] == signal.SIGCHLD: | 315 | if sig[0] == signal.SIGCHLD: |
2057 | 319 | log.info('SIGCHLD received') | 316 | utils.get_logger().info('SIGCHLD received') |
2058 | 320 | if self._handle_child_died(): | 317 | if self._handle_child_died(): |
2059 | 321 | raise StopIteration('launched program exited') | 318 | raise StopIteration('launched program exited') |
2060 | 322 | elif sig[0] == signal.SIGINT: | 319 | elif sig[0] == signal.SIGINT: |
2062 | 323 | log.info('SIGINT received') | 320 | utils.get_logger().info('SIGINT received') |
2063 | 324 | raise StopIteration('keyboard interrupt') | 321 | raise StopIteration('keyboard interrupt') |
2064 | 325 | elif sig[0] == signal.SIGTERM: | 322 | elif sig[0] == signal.SIGTERM: |
2066 | 326 | log.info('SIGTERM received') | 323 | utils.get_logger().info('SIGTERM received') |
2067 | 327 | raise StopIteration('terminate') | 324 | raise StopIteration('terminate') |
2068 | 328 | else: | 325 | else: |
2070 | 329 | log.warning('unknown signal {} received'.format(sig[0])) | 326 | utils.get_logger().warning('unknown signal {} received'.format(sig[0])) |
2071 | 330 | 327 | ||
2072 | 331 | def _set_signal_handlers(self): | 328 | def _set_signal_handlers(self): |
2073 | 332 | """Set the signal handlers.""" | 329 | """Set the signal handlers.""" |
2074 | 333 | 330 | ||
2075 | === modified file 'python/libertine/launcher/task.py' | |||
2076 | --- python/libertine/launcher/task.py 2016-10-27 17:25:08 +0000 | |||
2077 | +++ python/libertine/launcher/task.py 2017-01-05 20:19:05 +0000 | |||
2078 | @@ -44,7 +44,7 @@ | |||
2079 | 44 | 44 | ||
2080 | 45 | def __init__(self, config): | 45 | def __init__(self, config): |
2081 | 46 | """ | 46 | """ |
2083 | 47 | :param config: The task configuraiton. | 47 | :param config: The task configuration. |
2084 | 48 | :type config: TaskConfig | 48 | :type config: TaskConfig |
2085 | 49 | 49 | ||
2086 | 50 | The constructor unpacks the service commandline from the config datum. | 50 | The constructor unpacks the service commandline from the config datum. |
2087 | @@ -54,13 +54,13 @@ | |||
2088 | 54 | 54 | ||
2089 | 55 | def start(self, environ=None): | 55 | def start(self, environ=None): |
2090 | 56 | """Start the service. | 56 | """Start the service. |
2092 | 57 | 57 | ||
2093 | 58 | :param env: An alternate environment dictionary. | 58 | :param env: An alternate environment dictionary. |
2094 | 59 | """ | 59 | """ |
2095 | 60 | self._process = Popen(self._command_line, env=environ) | 60 | self._process = Popen(self._command_line, env=environ) |
2096 | 61 | 61 | ||
2097 | 62 | def stop(self): | 62 | def stop(self): |
2099 | 63 | """Shuts the service down. """ | 63 | """Shuts the service down.""" |
2100 | 64 | try: | 64 | try: |
2101 | 65 | self._process.terminate() | 65 | self._process.terminate() |
2102 | 66 | except ProcessLookupError: | 66 | except ProcessLookupError: |
2103 | 67 | 67 | ||
2104 | === added directory 'python/libertine/lifecycle' | |||
2105 | === added file 'python/libertine/lifecycle/ContainerLifecycleService.py' | |||
2106 | --- python/libertine/lifecycle/ContainerLifecycleService.py 1970-01-01 00:00:00 +0000 | |||
2107 | +++ python/libertine/lifecycle/ContainerLifecycleService.py 2017-01-05 20:19:05 +0000 | |||
2108 | @@ -0,0 +1,108 @@ | |||
2109 | 1 | #!/usr/bin/python3 | ||
2110 | 2 | # -*- coding: utf-8 -*- | ||
2111 | 3 | |||
2112 | 4 | # Copyright (C) 2016 Canonical Ltd. | ||
2113 | 5 | |||
2114 | 6 | # This program is free software: you can redistribute it and/or modify | ||
2115 | 7 | # it under the terms of the GNU General Public License as published by | ||
2116 | 8 | # the Free Software Foundation; version 3 of the License. | ||
2117 | 9 | # | ||
2118 | 10 | # This program is distributed in the hope that it will be useful, | ||
2119 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2120 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2121 | 13 | # GNU General Public License for more details. | ||
2122 | 14 | # | ||
2123 | 15 | # You should have received a copy of the GNU General Public License | ||
2124 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2125 | 17 | |||
2126 | 18 | |||
2127 | 19 | import dbus.exceptions | ||
2128 | 20 | import dbus.service | ||
2129 | 21 | |||
2130 | 22 | from .LifecycleResult import LifecycleResult | ||
2131 | 23 | from collections import Counter | ||
2132 | 24 | from dbus.mainloop.glib import DBusGMainLoop | ||
2133 | 25 | from libertine import utils | ||
2134 | 26 | |||
2135 | 27 | |||
2136 | 28 | LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE = 'com.canonical.libertine.ContainerLifecycle' | ||
2137 | 29 | |||
2138 | 30 | |||
2139 | 31 | class ContainerLifecycleService(dbus.service.Object): | ||
2140 | 32 | def __init__(self, service_name, service_path): | ||
2141 | 33 | self._apps = Counter() | ||
2142 | 34 | self._operations = Counter() | ||
2143 | 35 | |||
2144 | 36 | DBusGMainLoop(set_as_default=True) | ||
2145 | 37 | try: | ||
2146 | 38 | bus_name = dbus.service.BusName(service_name, | ||
2147 | 39 | bus=dbus.SessionBus(), | ||
2148 | 40 | do_not_queue=True) | ||
2149 | 41 | except dbus.exceptions.NameExistsException: | ||
2150 | 42 | utils.get_logger().error("service is already running") | ||
2151 | 43 | raise | ||
2152 | 44 | super().__init__(bus_name, service_path) | ||
2153 | 45 | |||
2154 | 46 | def start(self, container): | ||
2155 | 47 | raise NotImplementedError("Subclasses must implement start(container)") | ||
2156 | 48 | |||
2157 | 49 | def stop(self, container): | ||
2158 | 50 | raise NotImplementedError("Subclasses must implement stop(container)") | ||
2159 | 51 | |||
2160 | 52 | @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE, | ||
2161 | 53 | in_signature='s', | ||
2162 | 54 | out_signature='a{ss}') | ||
2163 | 55 | def app_start(self, container): | ||
2164 | 56 | utils.get_logger().debug("app_start({})".format(container)) | ||
2165 | 57 | if self._operations[container] != 0: | ||
2166 | 58 | return LifecycleResult("Libertine container operation already running: cannot launch application.") | ||
2167 | 59 | |||
2168 | 60 | result = self.start(container, True) | ||
2169 | 61 | |||
2170 | 62 | if result.success: | ||
2171 | 63 | self._apps[container] += 1 | ||
2172 | 64 | |||
2173 | 65 | return result.to_dict() | ||
2174 | 66 | |||
2175 | 67 | @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE, | ||
2176 | 68 | in_signature='s', | ||
2177 | 69 | out_signature='a{ss}') | ||
2178 | 70 | def app_stop(self, container): | ||
2179 | 71 | utils.get_logger().debug("app_stop({})".format(container)) | ||
2180 | 72 | self._apps[container] -= 1 | ||
2181 | 73 | result = LifecycleResult() | ||
2182 | 74 | |||
2183 | 75 | if self._apps[container] == 0: | ||
2184 | 76 | result = self.stop(container) | ||
2185 | 77 | del self._apps[container] | ||
2186 | 78 | |||
2187 | 79 | return result.to_dict() | ||
2188 | 80 | |||
2189 | 81 | @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE, | ||
2190 | 82 | in_signature='s', | ||
2191 | 83 | out_signature='a{ss}') | ||
2192 | 84 | def operation_start(self, container): | ||
2193 | 85 | utils.get_logger().debug("operation_start({})".format(container)) | ||
2194 | 86 | if self._apps[container] != 0: | ||
2195 | 87 | return LifecycleResult("Application already running in container: cannot run operation.") | ||
2196 | 88 | |||
2197 | 89 | result = self.start(container, False) | ||
2198 | 90 | |||
2199 | 91 | if result.success: | ||
2200 | 92 | self._operations[container] += 1 | ||
2201 | 93 | |||
2202 | 94 | return result.to_dict() | ||
2203 | 95 | |||
2204 | 96 | @dbus.service.method(LIBERTINE_CONTAINER_LIFECYCLE_INTERFACE, | ||
2205 | 97 | in_signature='s', | ||
2206 | 98 | out_signature='a{ss}') | ||
2207 | 99 | def operation_stop(self, container): | ||
2208 | 100 | utils.get_logger().debug("operation_stop({})".format(container)) | ||
2209 | 101 | self._operations[container] -= 1 | ||
2210 | 102 | result = LifecycleResult() | ||
2211 | 103 | |||
2212 | 104 | if self._operations[container] == 0: | ||
2213 | 105 | result = self.stop(container) | ||
2214 | 106 | del self._operations[container] | ||
2215 | 107 | |||
2216 | 108 | return result.to_dict() | ||
2217 | 0 | 109 | ||
2218 | === added file 'python/libertine/lifecycle/ContainerLifecycleServiceRunner.py' | |||
2219 | --- python/libertine/lifecycle/ContainerLifecycleServiceRunner.py 1970-01-01 00:00:00 +0000 | |||
2220 | +++ python/libertine/lifecycle/ContainerLifecycleServiceRunner.py 2017-01-05 20:19:05 +0000 | |||
2221 | @@ -0,0 +1,46 @@ | |||
2222 | 1 | #!/usr/bin/python3 | ||
2223 | 2 | # -*- coding: utf-8 -*- | ||
2224 | 3 | |||
2225 | 4 | # Copyright (C) 2016 Canonical Ltd. | ||
2226 | 5 | |||
2227 | 6 | # This program is free software: you can redistribute it and/or modify | ||
2228 | 7 | # it under the terms of the GNU General Public License as published by | ||
2229 | 8 | # the Free Software Foundation; version 3 of the License. | ||
2230 | 9 | # | ||
2231 | 10 | # This program is distributed in the hope that it will be useful, | ||
2232 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2233 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2234 | 13 | # GNU General Public License for more details. | ||
2235 | 14 | # | ||
2236 | 15 | # You should have received a copy of the GNU General Public License | ||
2237 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2238 | 17 | |||
2239 | 18 | import signal | ||
2240 | 19 | |||
2241 | 20 | from gi.repository import GLib | ||
2242 | 21 | from libertine import utils | ||
2243 | 22 | |||
2244 | 23 | |||
2245 | 24 | class ContainerLifecycleServiceRunner(object): | ||
2246 | 25 | def __init__(self, service): | ||
2247 | 26 | self._service = service | ||
2248 | 27 | |||
2249 | 28 | def _sigterm(self, sig): | ||
2250 | 29 | utils.get_logger().warning("Received SIGTERM") | ||
2251 | 30 | self._shutdown() | ||
2252 | 31 | |||
2253 | 32 | def _shutdown(self): | ||
2254 | 33 | utils.get_logger().info("Shutting down") | ||
2255 | 34 | GLib.MainLoop().quit() | ||
2256 | 35 | |||
2257 | 36 | def run(self): | ||
2258 | 37 | GLib.unix_signal_add(GLib.PRIORITY_HIGH, | ||
2259 | 38 | signal.SIGTERM, | ||
2260 | 39 | self._sigterm, | ||
2261 | 40 | None) | ||
2262 | 41 | |||
2263 | 42 | try: | ||
2264 | 43 | utils.get_logger().info("Starting main loop") | ||
2265 | 44 | GLib.MainLoop().run() | ||
2266 | 45 | except KeyboardInterrupt: | ||
2267 | 46 | self._shutdown() | ||
2268 | 0 | 47 | ||
2269 | === added file 'python/libertine/lifecycle/LifecycleResult.py' | |||
2270 | --- python/libertine/lifecycle/LifecycleResult.py 1970-01-01 00:00:00 +0000 | |||
2271 | +++ python/libertine/lifecycle/LifecycleResult.py 2017-01-05 20:19:05 +0000 | |||
2272 | @@ -0,0 +1,37 @@ | |||
2273 | 1 | #!/usr/bin/python3 | ||
2274 | 2 | # -*- coding: utf-8 -*- | ||
2275 | 3 | |||
2276 | 4 | # Copyright (C) 2016 Canonical Ltd. | ||
2277 | 5 | |||
2278 | 6 | # This program is free software: you can redistribute it and/or modify | ||
2279 | 7 | # it under the terms of the GNU General Public License as published by | ||
2280 | 8 | # the Free Software Foundation; version 3 of the License. | ||
2281 | 9 | # | ||
2282 | 10 | # This program is distributed in the hope that it will be useful, | ||
2283 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2284 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2285 | 13 | # GNU General Public License for more details. | ||
2286 | 14 | # | ||
2287 | 15 | # You should have received a copy of the GNU General Public License | ||
2288 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2289 | 17 | |||
2290 | 18 | class LifecycleResult(object): | ||
2291 | 19 | def __init__(self, error=''): | ||
2292 | 20 | self._error = error | ||
2293 | 21 | |||
2294 | 22 | @property | ||
2295 | 23 | def error(self): | ||
2296 | 24 | return self._error or '' | ||
2297 | 25 | |||
2298 | 26 | @property | ||
2299 | 27 | def success(self): | ||
2300 | 28 | return self.error == '' | ||
2301 | 29 | |||
2302 | 30 | @classmethod | ||
2303 | 31 | def from_dict(kls, d): | ||
2304 | 32 | return LifecycleResult(d.get('error', None)) | ||
2305 | 33 | |||
2306 | 34 | def to_dict(self): | ||
2307 | 35 | return { | ||
2308 | 36 | 'error': self.error | ||
2309 | 37 | } | ||
2310 | 0 | 38 | ||
2311 | === added file 'python/libertine/lifecycle/__init__.py' | |||
2312 | --- python/libertine/lifecycle/__init__.py 1970-01-01 00:00:00 +0000 | |||
2313 | +++ python/libertine/lifecycle/__init__.py 2017-01-05 20:19:05 +0000 | |||
2314 | @@ -0,0 +1,23 @@ | |||
2315 | 1 | # Copyright 2016 Canonical Ltd. | ||
2316 | 2 | # | ||
2317 | 3 | # This program is free software: you can redistribute it and/or modify it | ||
2318 | 4 | # under the terms of the GNU General Public License version 3, as published | ||
2319 | 5 | # by the Free Software Foundation. | ||
2320 | 6 | # | ||
2321 | 7 | # This program is distributed in the hope that it will be useful, but | ||
2322 | 8 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
2323 | 9 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
2324 | 10 | # PURPOSE. See the GNU General Public License for more details. | ||
2325 | 11 | # | ||
2326 | 12 | # You should have received a copy of the GNU General Public License along | ||
2327 | 13 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2328 | 14 | |||
2329 | 15 | from .ContainerLifecycleServiceRunner import ContainerLifecycleServiceRunner | ||
2330 | 16 | from .ContainerLifecycleService import ContainerLifecycleService | ||
2331 | 17 | from .LifecycleResult import LifecycleResult | ||
2332 | 18 | |||
2333 | 19 | __all__ = [ | ||
2334 | 20 | 'ContainerLifecycleServiceRunner', | ||
2335 | 21 | 'ContainerLifecycleService', | ||
2336 | 22 | 'LifecycleResult' | ||
2337 | 23 | ] | ||
2338 | 0 | 24 | ||
2339 | === modified file 'python/libertine/utils.py' | |||
2340 | --- python/libertine/utils.py 2016-12-02 16:38:11 +0000 | |||
2341 | +++ python/libertine/utils.py 2017-01-05 20:19:05 +0000 | |||
2342 | @@ -25,6 +25,7 @@ | |||
2343 | 25 | require_version('Libertine', '1') | 25 | require_version('Libertine', '1') |
2344 | 26 | from gi.repository import Libertine | 26 | from gi.repository import Libertine |
2345 | 27 | 27 | ||
2346 | 28 | |||
2347 | 28 | def get_logger(): | 29 | def get_logger(): |
2348 | 29 | logger = logging.getLogger('__libertine_logger__') | 30 | logger = logging.getLogger('__libertine_logger__') |
2349 | 30 | 31 | ||
2350 | @@ -40,13 +41,27 @@ | |||
2351 | 40 | logger.addHandler(stream_handler) | 41 | logger.addHandler(stream_handler) |
2352 | 41 | 42 | ||
2353 | 42 | if 'LIBERTINE_DEBUG' in os.environ: | 43 | if 'LIBERTINE_DEBUG' in os.environ: |
2355 | 43 | logger.setLevel(logging.DEBUG) | 44 | if os.environ['LIBERTINE_DEBUG'] == '0': |
2356 | 45 | logger.setLevel(logging.WARNING) | ||
2357 | 46 | elif os.environ['LIBERTINE_DEBUG'] == '1': | ||
2358 | 47 | logger.setLevel(logging.INFO) | ||
2359 | 48 | else: | ||
2360 | 49 | logger.setLevel(logging.DEBUG) | ||
2361 | 44 | else: | 50 | else: |
2362 | 45 | logger.setLevel(logging.WARNING) | 51 | logger.setLevel(logging.WARNING) |
2363 | 46 | 52 | ||
2364 | 47 | return logger | 53 | return logger |
2365 | 48 | 54 | ||
2366 | 49 | 55 | ||
2367 | 56 | def set_environmental_verbosity(verbosity): | ||
2368 | 57 | # Set debug levels if not overridden in environment | ||
2369 | 58 | if 'LIBERTINE_DEBUG' not in os.environ: | ||
2370 | 59 | if verbosity is None: | ||
2371 | 60 | os.environ['LIBERTINE_DEBUG'] = '1' | ||
2372 | 61 | else: | ||
2373 | 62 | os.environ['LIBERTINE_DEBUG'] = str(verbosity) | ||
2374 | 63 | |||
2375 | 64 | |||
2376 | 50 | def get_libertine_container_rootfs_path(container_id): | 65 | def get_libertine_container_rootfs_path(container_id): |
2377 | 51 | path = Libertine.container_path(container_id) | 66 | path = Libertine.container_path(container_id) |
2378 | 52 | 67 | ||
2379 | @@ -112,8 +127,8 @@ | |||
2380 | 112 | 127 | ||
2381 | 113 | name = dir | 128 | name = dir |
2382 | 114 | if name.startswith(prefix): | 129 | if name.startswith(prefix): |
2385 | 115 | name = name.replace(prefix, '', 1) | 130 | name = name.replace(prefix, '', 1).lstrip('/') |
2386 | 116 | name = name.lstrip('/') | 131 | |
2387 | 117 | if name in names: | 132 | if name in names: |
2388 | 118 | binding_dirs.append((dir, "%s (%i)" % (name, names.count(name)))) | 133 | binding_dirs.append((dir, "%s (%i)" % (name, names.count(name)))) |
2389 | 119 | else: | 134 | else: |
2390 | @@ -201,3 +216,9 @@ | |||
2391 | 201 | return True | 216 | return True |
2392 | 202 | else: | 217 | else: |
2393 | 203 | return False | 218 | return False |
2394 | 219 | |||
2395 | 220 | |||
2396 | 221 | def get_deb_package_name(package): | ||
2397 | 222 | pkg_name_proc = subprocess.Popen(shlex.split('dpkg --field {} Package'.format(package)), stdout=subprocess.PIPE) | ||
2398 | 223 | out, err = pkg_name_proc.communicate() | ||
2399 | 224 | return out.decode('utf-8').strip() | ||
2400 | 204 | 225 | ||
2401 | === added file 'snapcraft.yaml' | |||
2402 | --- snapcraft.yaml 1970-01-01 00:00:00 +0000 | |||
2403 | +++ snapcraft.yaml 2017-01-05 20:19:05 +0000 | |||
2404 | @@ -0,0 +1,32 @@ | |||
2405 | 1 | name: libertine | ||
2406 | 2 | version: "1.5" | ||
2407 | 3 | summary: Libertine suite | ||
2408 | 4 | description: | | ||
2409 | 5 | Suite for maintaining deb-based applications in a non-deb environment | ||
2410 | 6 | confinement: devmode | ||
2411 | 7 | grade: devel | ||
2412 | 8 | |||
2413 | 9 | apps: | ||
2414 | 10 | libertine-container-manager: | ||
2415 | 11 | command: usr/bin/snap-runner.wrapper libertine-container-manager | ||
2416 | 12 | libertine-manager-app: | ||
2417 | 13 | command: usr/bin/snap-runner.wrapper libertine-manager-app | ||
2418 | 14 | libertine-launch: | ||
2419 | 15 | command: usr/bin/snap-runner.wrapper libertine-launch | ||
2420 | 16 | |||
2421 | 17 | # For debugging purposes | ||
2422 | 18 | bash: | ||
2423 | 19 | command: usr/bin/snap-runner.wrapper bash | ||
2424 | 20 | |||
2425 | 21 | parts: | ||
2426 | 22 | libertine-source: | ||
2427 | 23 | plugin: libertine | ||
2428 | 24 | source: . | ||
2429 | 25 | after: | ||
2430 | 26 | - desktop-qt5 | ||
2431 | 27 | libertine-deps: | ||
2432 | 28 | plugin: libertine-deps | ||
2433 | 29 | env: | ||
2434 | 30 | plugin: dump | ||
2435 | 31 | organize: | ||
2436 | 32 | data/snap-runner.wrapper: usr/bin/snap-runner.wrapper | ||
2437 | 0 | 33 | ||
2438 | === modified file 'tests/unit/test_libertine_gir.py' | |||
2439 | --- tests/unit/test_libertine_gir.py 2016-08-28 00:17:54 +0000 | |||
2440 | +++ tests/unit/test_libertine_gir.py 2017-01-05 20:19:05 +0000 | |||
2441 | @@ -49,14 +49,12 @@ | |||
2442 | 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')) |
2443 | 50 | 50 | ||
2444 | 51 | def test_container_name(self): | 51 | def test_container_name(self): |
2445 | 52 | container_id = 'wily' | ||
2446 | 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'}): |
2448 | 54 | container_name = Libertine.container_name(container_id) | 53 | container_name = Libertine.container_name('wily') |
2449 | 55 | 54 | ||
2450 | 56 | self.assertThat(container_name, Equals("Ubuntu 'Wily Werewolf'")) | 55 | self.assertThat(container_name, Equals("Ubuntu 'Wily Werewolf'")) |
2451 | 57 | 56 | ||
2454 | 58 | container_id = 'wily-2' | 57 | container_name = Libertine.container_name('wily-2') |
2453 | 59 | container_name = Libertine.container_name(container_id) | ||
2455 | 60 | 58 | ||
2456 | 61 | self.assertThat(container_name, Equals("Ubuntu 'Wily Werewolf' (2)")) | 59 | self.assertThat(container_name, Equals("Ubuntu 'Wily Werewolf' (2)")) |
2457 | 62 | 60 | ||
2458 | 63 | 61 | ||
2459 | === modified file 'tests/unit/test_logger.py' | |||
2460 | --- tests/unit/test_logger.py 2016-08-25 01:49:07 +0000 | |||
2461 | +++ tests/unit/test_logger.py 2017-01-05 20:19:05 +0000 | |||
2462 | @@ -34,6 +34,11 @@ | |||
2463 | 34 | def test_logger_on_with_env_var(self): | 34 | def test_logger_on_with_env_var(self): |
2464 | 35 | with patch.dict('os.environ', {'LIBERTINE_DEBUG': '1'}): | 35 | with patch.dict('os.environ', {'LIBERTINE_DEBUG': '1'}): |
2465 | 36 | l = libertine.utils.get_logger() | 36 | l = libertine.utils.get_logger() |
2466 | 37 | self.assertThat(l.getEffectiveLevel(), Equals(logging.INFO)) | ||
2467 | 38 | |||
2468 | 39 | def test_logger_on_with_env_var(self): | ||
2469 | 40 | with patch.dict('os.environ', {'LIBERTINE_DEBUG': '2'}): | ||
2470 | 41 | l = libertine.utils.get_logger() | ||
2471 | 37 | self.assertThat(l.getEffectiveLevel(), Equals(logging.DEBUG)) | 42 | self.assertThat(l.getEffectiveLevel(), Equals(logging.DEBUG)) |
2472 | 38 | 43 | ||
2473 | 39 | def test_logger_only_inits_once(self): | 44 | def test_logger_only_inits_once(self): |
2474 | @@ -42,4 +47,3 @@ | |||
2475 | 42 | l2 = libertine.utils.get_logger() | 47 | l2 = libertine.utils.get_logger() |
2476 | 43 | l3 = libertine.utils.get_logger() | 48 | l3 = libertine.utils.get_logger() |
2477 | 44 | self.assertThat(len(l3.handlers), Equals(1)) | 49 | self.assertThat(len(l3.handlers), Equals(1)) |
2478 | 45 | |||
2479 | 46 | 50 | ||
2480 | === modified file 'tools/CMakeLists.txt' | |||
2481 | --- tools/CMakeLists.txt 2016-10-28 19:39:13 +0000 | |||
2482 | +++ tools/CMakeLists.txt 2017-01-05 20:19:05 +0000 | |||
2483 | @@ -1,10 +1,9 @@ | |||
2485 | 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-lxd-manager |
2486 | 2 | libertine-xmir libertine-lxc-setup libertine-lxd-setup libertined | ||
2487 | 2 | DESTINATION ${CMAKE_INSTALL_BINDIR}) | 3 | DESTINATION ${CMAKE_INSTALL_BINDIR}) |
2489 | 3 | install(FILES libertine-launch.1 libertine-container-manager.1 libertine-lxc-manager.1 libertine-xmir.1 | 4 | install(FILES libertine-launch.1 libertine-container-manager.1 libertine-lxc-manager.1 |
2490 | 5 | libertine-lxd-manager.1 libertine-xmir.1 | ||
2491 | 4 | DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 | 6 | DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 |
2492 | 5 | COMPONENT doc) | 7 | COMPONENT doc) |
2493 | 6 | install(PROGRAMS update-puritine-containers | ||
2494 | 7 | DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/libertine) | ||
2495 | 8 | |||
2496 | 9 | install(FILES completions/libertine-container-manager | 8 | install(FILES completions/libertine-container-manager |
2497 | 10 | DESTINATION ${DESTDIR}/usr/share/bash-completion/completions/) | 9 | DESTINATION ${DESTDIR}/usr/share/bash-completion/completions/) |
2498 | 11 | 10 | ||
2499 | === modified file 'tools/libertine-container-manager' | |||
2500 | --- tools/libertine-container-manager 2016-11-16 17:33:42 +0000 | |||
2501 | +++ tools/libertine-container-manager 2017-01-05 20:19:05 +0000 | |||
2502 | @@ -23,7 +23,6 @@ | |||
2503 | 23 | import sys | 23 | import sys |
2504 | 24 | import re | 24 | import re |
2505 | 25 | 25 | ||
2506 | 26 | from apt.debfile import DebPackage | ||
2507 | 27 | from libertine import LibertineContainer | 26 | from libertine import LibertineContainer |
2508 | 28 | from libertine.ContainersConfig import ContainersConfig | 27 | from libertine.ContainersConfig import ContainersConfig |
2509 | 29 | from libertine.HostInfo import HostInfo | 28 | from libertine.HostInfo import HostInfo |
2510 | @@ -39,22 +38,22 @@ | |||
2511 | 39 | password = None | 38 | password = None |
2512 | 40 | 39 | ||
2513 | 41 | if args.distro and not self.host_info.is_distro_valid(args.distro, args.force): | 40 | if args.distro and not self.host_info.is_distro_valid(args.distro, args.force): |
2515 | 42 | print("Invalid distro %s" % args.distro, file=sys.stderr) | 41 | libertine.utils.get_logger().error("Invalid distro %s" % args.distro) |
2516 | 43 | sys.exit(1) | 42 | sys.exit(1) |
2517 | 44 | 43 | ||
2518 | 45 | if self.containers_config.container_exists(args.id): | 44 | if self.containers_config.container_exists(args.id): |
2520 | 46 | print("Container id '%s' is already used." % args.id, file=sys.stderr) | 45 | libertine.utils.get_logger().error("Container id '%s' is already used." % args.id) |
2521 | 47 | sys.exit(1) | 46 | sys.exit(1) |
2522 | 48 | elif re.match("^[a-z0-9][a-z0-9+.-]+$", args.id) is None: | 47 | elif re.match("^[a-z0-9][a-z0-9+.-]+$", args.id) is None: |
2524 | 49 | print("Container id '%s' invalid. ID must be of form ([a-z0-9][a-z0-9+.-]+)." % args.id, file=sys.stderr) | 48 | libertine.utils.get_logger().error("Container id '%s' invalid. ID must be of form ([a-z0-9][a-z0-9+.-]+)." % args.id) |
2525 | 50 | sys.exit(1) | 49 | sys.exit(1) |
2526 | 51 | 50 | ||
2527 | 52 | if not args.type: | 51 | if not args.type: |
2528 | 53 | container_type = self.host_info.select_container_type_by_kernel() | 52 | container_type = self.host_info.select_container_type_by_kernel() |
2529 | 54 | else: | 53 | else: |
2533 | 55 | if args.type == 'lxc' and not self.host_info.has_lxc_support(): | 54 | if (args.type == 'lxc' or args.type == 'lxd') and not self.host_info.has_lxc_support(): |
2534 | 56 | print("System kernel does not support lxc type containers. " | 55 | libertine.utils.get_logger().error("System kernel does not support lxc type containers. " |
2535 | 57 | "Please either use chroot or omit the -t option.") | 56 | "Please either use chroot or omit the -t option.") |
2536 | 58 | sys.exit(1) | 57 | sys.exit(1) |
2537 | 59 | container_type = args.type | 58 | container_type = args.type |
2538 | 60 | 59 | ||
2539 | @@ -64,8 +63,8 @@ | |||
2540 | 64 | host_distro = self.host_info.get_host_distro_release() | 63 | host_distro = self.host_info.get_host_distro_release() |
2541 | 65 | 64 | ||
2542 | 66 | if args.distro != host_distro: | 65 | if args.distro != host_distro: |
2545 | 67 | print("The container distribution needs to match the host ditribution for chroot" | 66 | libertine.utils.get_logger().error("The container distribution needs to match the host ditribution for chroot" |
2546 | 68 | " based containers. Please either use \'%s\' or omit the -d/--distro option." | 67 | " based containers. Please either use \'%s\' or omit the -d/--distro option." |
2547 | 69 | % host_distro) | 68 | % host_distro) |
2548 | 70 | sys.exit(1) | 69 | sys.exit(1) |
2549 | 71 | 70 | ||
2550 | @@ -88,11 +87,18 @@ | |||
2551 | 88 | multiarch = 'enabled' | 87 | multiarch = 'enabled' |
2552 | 89 | self.containers_config.update_container_multiarch_support(args.id, multiarch) | 88 | self.containers_config.update_container_multiarch_support(args.id, multiarch) |
2553 | 90 | 89 | ||
2557 | 91 | container = LibertineContainer(args.id) | 90 | try: |
2558 | 92 | self.containers_config.update_container_install_status(args.id, "installing") | 91 | container = LibertineContainer(args.id) |
2559 | 93 | if not container.create_libertine_container(password, args.multiarch, args.verbosity): | 92 | self.containers_config.update_container_install_status(args.id, "installing") |
2560 | 93 | if not container.create_libertine_container(password, args.multiarch): | ||
2561 | 94 | libertine.utils.get_logger().error("Failed to create container") | ||
2562 | 95 | self.containers_config.delete_container(args.id) | ||
2563 | 96 | sys.exit(1) | ||
2564 | 97 | except Exception as e: | ||
2565 | 98 | libertine.utils.get_logger().error("Failed to create container: '{}'".format(str(e))) | ||
2566 | 94 | self.containers_config.delete_container(args.id) | 99 | self.containers_config.delete_container(args.id) |
2567 | 95 | sys.exit(1) | 100 | sys.exit(1) |
2568 | 101 | |||
2569 | 96 | self.containers_config.update_container_install_status(args.id, "ready") | 102 | self.containers_config.update_container_install_status(args.id, "ready") |
2570 | 97 | 103 | ||
2571 | 98 | libertine.utils.refresh_libertine_scope() | 104 | libertine.utils.refresh_libertine_scope() |
2572 | @@ -119,16 +125,16 @@ | |||
2573 | 119 | 125 | ||
2574 | 120 | if is_debian_package: | 126 | if is_debian_package: |
2575 | 121 | if os.path.exists(args.package): | 127 | if os.path.exists(args.package): |
2577 | 122 | package = DebPackage(args.package).pkgname | 128 | package = libertine.utils.get_deb_package_name(args.package) |
2578 | 123 | else: | 129 | else: |
2580 | 124 | print("%s does not exist." % args.package) | 130 | libertine.utils.get_logger().error("%s does not exist." % args.package) |
2581 | 125 | sys.exit(1) | 131 | sys.exit(1) |
2582 | 126 | else: | 132 | else: |
2583 | 127 | package = args.package | 133 | package = args.package |
2584 | 128 | 134 | ||
2585 | 129 | if self.containers_config.package_exists(container_id, package): | 135 | if self.containers_config.package_exists(container_id, package): |
2586 | 130 | if not is_debian_package: | 136 | if not is_debian_package: |
2588 | 131 | print("Package \'%s\' is already installed." % package) | 137 | libertine.utils.get_logger().error("Package '%s' is already installed." % package) |
2589 | 132 | sys.exit(1) | 138 | sys.exit(1) |
2590 | 133 | else: | 139 | else: |
2591 | 134 | self.containers_config.add_new_package(container_id, package) | 140 | self.containers_config.add_new_package(container_id, package) |
2592 | @@ -136,7 +142,9 @@ | |||
2593 | 136 | container = LibertineContainer(container_id) | 142 | container = LibertineContainer(container_id) |
2594 | 137 | 143 | ||
2595 | 138 | self.containers_config.update_package_install_status(container_id, package, "installing") | 144 | self.containers_config.update_package_install_status(container_id, package, "installing") |
2597 | 139 | if not container.install_package(args.package, args.verbosity, args.no_dialog): | 145 | if not container.install_package(args.package, args.no_dialog): |
2598 | 146 | libertine.utils.get_logger().error("Package '{}' failed to install in container '{}'" | ||
2599 | 147 | .format(package, container_id)) | ||
2600 | 140 | self.containers_config.delete_package(container_id, package) | 148 | self.containers_config.delete_package(container_id, package) |
2601 | 141 | sys.exit(1) | 149 | sys.exit(1) |
2602 | 142 | 150 | ||
2603 | @@ -144,12 +152,12 @@ | |||
2604 | 144 | 152 | ||
2605 | 145 | libertine.utils.refresh_libertine_scope() | 153 | libertine.utils.refresh_libertine_scope() |
2606 | 146 | 154 | ||
2608 | 147 | def remove_package_by_name(self, container_id, package_name, verbosity=1, no_dialog=False): | 155 | def remove_package_by_name(self, container_id, package_name, no_dialog=False): |
2609 | 148 | fallback_status = self.containers_config.get_package_install_status(container_id, package_name) | 156 | fallback_status = self.containers_config.get_package_install_status(container_id, package_name) |
2610 | 149 | self.containers_config.update_package_install_status(container_id, package_name, "removing") | 157 | self.containers_config.update_package_install_status(container_id, package_name, "removing") |
2611 | 150 | 158 | ||
2612 | 151 | container = LibertineContainer(container_id) | 159 | container = LibertineContainer(container_id) |
2614 | 152 | if not container.remove_package(package_name, verbosity, no_dialog) and fallback_status == 'installed': | 160 | if not container.remove_package(package_name, no_dialog) and fallback_status == 'installed': |
2615 | 153 | self.containers_config.update_package_install_status(container_id, package_name, fallback_status) | 161 | self.containers_config.update_package_install_status(container_id, package_name, fallback_status) |
2616 | 154 | return False | 162 | return False |
2617 | 155 | 163 | ||
2618 | @@ -162,10 +170,12 @@ | |||
2619 | 162 | container_id = self.containers_config.check_container_id(args.id) | 170 | container_id = self.containers_config.check_container_id(args.id) |
2620 | 163 | 171 | ||
2621 | 164 | if self.containers_config.get_package_install_status(container_id, args.package) != 'installed': | 172 | if self.containers_config.get_package_install_status(container_id, args.package) != 'installed': |
2623 | 165 | print("Package \'%s\' is not installed." % args.package) | 173 | libertine.utils.get_logger().error("Package \'%s\' is not installed." % args.package) |
2624 | 166 | sys.exit(1) | 174 | sys.exit(1) |
2625 | 167 | 175 | ||
2627 | 168 | if not self.remove_package_by_name(container_id, args.package, args.verbosity, args.no_dialog): | 176 | if not self.remove_package_by_name(container_id, args.package, args.no_dialog): |
2628 | 177 | libertine.utils.get_logger().error("Package '{}' failed to be removed from container '{}'" | ||
2629 | 178 | .format(args.package, container_id)) | ||
2630 | 169 | sys.exit(1) | 179 | sys.exit(1) |
2631 | 170 | 180 | ||
2632 | 171 | libertine.utils.refresh_libertine_scope() | 181 | libertine.utils.refresh_libertine_scope() |
2633 | @@ -175,6 +185,8 @@ | |||
2634 | 175 | 185 | ||
2635 | 176 | container = LibertineContainer(container_id) | 186 | container = LibertineContainer(container_id) |
2636 | 177 | if container.search_package_cache(args.search_string) is not 0: | 187 | if container.search_package_cache(args.search_string) is not 0: |
2637 | 188 | libertine.utils.get_logger().error("Search for '{}' in container '{}' exited with non-zero status" | ||
2638 | 189 | .format(args.id, args.search_string)) | ||
2639 | 178 | sys.exit(1) | 190 | sys.exit(1) |
2640 | 179 | 191 | ||
2641 | 180 | def update(self, args): | 192 | def update(self, args): |
2642 | @@ -183,7 +195,7 @@ | |||
2643 | 183 | container = LibertineContainer(container_id) | 195 | container = LibertineContainer(container_id) |
2644 | 184 | 196 | ||
2645 | 185 | self.containers_config.update_container_install_status(container_id, "updating") | 197 | self.containers_config.update_container_install_status(container_id, "updating") |
2647 | 186 | if not container.update_libertine_container(args.verbosity): | 198 | if not container.update_libertine_container(): |
2648 | 187 | self.containers_config.update_container_install_status(container_id, "ready") | 199 | self.containers_config.update_container_install_status(container_id, "ready") |
2649 | 188 | sys.exit(1) | 200 | sys.exit(1) |
2650 | 189 | 201 | ||
2651 | @@ -208,10 +220,10 @@ | |||
2652 | 208 | if not container.exec_command(args.command): | 220 | if not container.exec_command(args.command): |
2653 | 209 | sys.exit(1) | 221 | sys.exit(1) |
2654 | 210 | 222 | ||
2656 | 211 | def delete_archive_by_name(self, container_id, archive_name, verbosity=1): | 223 | def delete_archive_by_name(self, container_id, archive_name): |
2657 | 212 | if self.containers_config.get_archive_install_status(container_id, archive_name) == 'installed': | 224 | if self.containers_config.get_archive_install_status(container_id, archive_name) == 'installed': |
2658 | 213 | self.containers_config.update_archive_install_status(container_id, archive_name, 'removing') | 225 | self.containers_config.update_archive_install_status(container_id, archive_name, 'removing') |
2660 | 214 | if LibertineContainer(container_id).configure_remove_archive("\"" + archive_name + "\"", verbosity) is not 0: | 226 | if LibertineContainer(container_id).configure_remove_archive("\"" + archive_name + "\"") is not 0: |
2661 | 215 | self.containers_config.update_archive_install_status(container_id, archive_name, 'installed') | 227 | self.containers_config.update_archive_install_status(container_id, archive_name, 'installed') |
2662 | 216 | return False | 228 | return False |
2663 | 217 | 229 | ||
2664 | @@ -230,17 +242,17 @@ | |||
2665 | 230 | 242 | ||
2666 | 231 | current_multiarch = self.containers_config.get_container_multiarch_support(container_id) | 243 | current_multiarch = self.containers_config.get_container_multiarch_support(container_id) |
2667 | 232 | if current_multiarch == multiarch: | 244 | if current_multiarch == multiarch: |
2669 | 233 | print("i386 multiarch support is already %s" % multiarch) | 245 | libertine.utils.get_logger().error("i386 multiarch support is already %s" % multiarch) |
2670 | 234 | sys.exit(1) | 246 | sys.exit(1) |
2671 | 235 | 247 | ||
2673 | 236 | if container.configure_multiarch(args.multiarch, args.verbosity) is not 0: | 248 | if container.configure_multiarch(args.multiarch) is not 0: |
2674 | 237 | sys.exit(1) | 249 | sys.exit(1) |
2675 | 238 | 250 | ||
2676 | 239 | self.containers_config.update_container_multiarch_support(container_id, multiarch) | 251 | self.containers_config.update_container_multiarch_support(container_id, multiarch) |
2677 | 240 | 252 | ||
2678 | 241 | elif args.archive is not None: | 253 | elif args.archive is not None: |
2679 | 242 | if args.archive_name is None: | 254 | if args.archive_name is None: |
2681 | 243 | print("Configure archive called with no archive name. See configure --help for usage.") | 255 | libertine.utils.get_logger().error("Configure archive called with no archive name. See configure --help for usage.") |
2682 | 244 | sys.exit(1) | 256 | sys.exit(1) |
2683 | 245 | 257 | ||
2684 | 246 | archive_name = args.archive_name.strip("\'\"") | 258 | archive_name = args.archive_name.strip("\'\"") |
2685 | @@ -248,12 +260,12 @@ | |||
2686 | 248 | 260 | ||
2687 | 249 | if args.archive == 'add': | 261 | if args.archive == 'add': |
2688 | 250 | if self.containers_config.archive_exists(container_id, archive_name): | 262 | if self.containers_config.archive_exists(container_id, archive_name): |
2690 | 251 | print("%s already added in container." % archive_name) | 263 | libertine.utils.get_logger().error("%s already added in container." % archive_name) |
2691 | 252 | sys.exit(1) | 264 | sys.exit(1) |
2692 | 253 | 265 | ||
2693 | 254 | self.containers_config.add_container_archive(container_id, archive_name) | 266 | self.containers_config.add_container_archive(container_id, archive_name) |
2694 | 255 | self.containers_config.update_archive_install_status(container_id, archive_name, 'installing') | 267 | self.containers_config.update_archive_install_status(container_id, archive_name, 'installing') |
2696 | 256 | if container.configure_add_archive(archive_name_esc, args.public_key_file, args.verbosity) is not 0: | 268 | if container.configure_add_archive(archive_name_esc, args.public_key_file) is not 0: |
2697 | 257 | self.containers_config.delete_container_archive(container_id, archive_name) | 269 | self.containers_config.delete_container_archive(container_id, archive_name) |
2698 | 258 | sys.exit(1) | 270 | sys.exit(1) |
2699 | 259 | 271 | ||
2700 | @@ -261,48 +273,47 @@ | |||
2701 | 261 | 273 | ||
2702 | 262 | elif args.archive == 'remove': | 274 | elif args.archive == 'remove': |
2703 | 263 | if not self.containers_config.archive_exists(container_id, archive_name): | 275 | if not self.containers_config.archive_exists(container_id, archive_name): |
2705 | 264 | print("%s is not added in container." % archive_name) | 276 | libertine.utils.get_logger().error("%s is not added in container." % archive_name) |
2706 | 265 | sys.exit(1) | 277 | sys.exit(1) |
2707 | 266 | 278 | ||
2708 | 267 | if not self.delete_archive_by_name(container_id, archive_name): | 279 | if not self.delete_archive_by_name(container_id, archive_name): |
2710 | 268 | print("%s was not properly deleted." % archive_name) | 280 | libertine.utils.get_logger().error("%s was not properly deleted." % archive_name) |
2711 | 269 | sys.exit(1) | 281 | sys.exit(1) |
2712 | 270 | 282 | ||
2713 | 271 | elif args.bind_mount is not None: | 283 | elif args.bind_mount is not None: |
2714 | 272 | if args.mount_path is None: | 284 | if args.mount_path is None: |
2716 | 273 | print("Configure bind-mounts called without mount path. See configure --help for usage") | 285 | libertine.utils.get_logger().error("Configure bind-mounts called without mount path. See configure --help for usage") |
2717 | 274 | sys.exit(1) | 286 | sys.exit(1) |
2718 | 275 | 287 | ||
2719 | 276 | mount_path = args.mount_path.rstrip('/') | 288 | mount_path = args.mount_path.rstrip('/') |
2720 | 277 | 289 | ||
2721 | 278 | # validate bind-mount | 290 | # validate bind-mount |
2722 | 279 | if not mount_path.startswith(os.environ['HOME']) and not mount_path.startswith('/media/%s' % os.environ['USER']): | 291 | if not mount_path.startswith(os.environ['HOME']) and not mount_path.startswith('/media/%s' % os.environ['USER']): |
2724 | 280 | print("Cannot mount '%s', mount path must be in $HOME or /media/$USER." % mount_path) | 292 | libertine.utils.get_logger().error("Cannot mount {}, mount path must be in {} or /media/{}.".format(mount_path, os.environ['HOME'], os.environ['USER'])) |
2725 | 281 | sys.exit(1) | 293 | sys.exit(1) |
2726 | 282 | if mount_path.startswith('/media/%s' % os.environ['USER']) and \ | 294 | if mount_path.startswith('/media/%s' % os.environ['USER']) and \ |
2727 | 283 | self.containers_config.get_container_type(container_id) == 'lxc': | 295 | self.containers_config.get_container_type(container_id) == 'lxc': |
2729 | 284 | print("/media mounts not currently supported in lxc.") | 296 | libertine.utils.get_logger().error("/media mounts not currently supported in lxc.") |
2730 | 285 | sys.exit(1) | 297 | sys.exit(1) |
2731 | 286 | if not os.path.isdir(mount_path): | 298 | if not os.path.isdir(mount_path): |
2733 | 287 | print("Cannot mount '%s', mount path must be an existing directory." % mount_path) | 299 | libertine.utils.get_logger().error("Cannot mount '%s', mount path must be an existing directory." % mount_path) |
2734 | 288 | sys.exit(1) | 300 | sys.exit(1) |
2735 | 289 | 301 | ||
2736 | 290 | # update database with new bind-mount | 302 | # update database with new bind-mount |
2737 | 291 | container_bind_mounts = self.containers_config.get_container_bind_mounts(container_id) | 303 | container_bind_mounts = self.containers_config.get_container_bind_mounts(container_id) |
2738 | 292 | if args.bind_mount == 'add': | 304 | if args.bind_mount == 'add': |
2739 | 293 | if mount_path in container_bind_mounts: | 305 | if mount_path in container_bind_mounts: |
2741 | 294 | print("Cannot add mount '%s', bind-mount already exists." % mount_path) | 306 | libertine.utils.get_logger().error("Cannot add mount '%s', bind-mount already exists." % mount_path) |
2742 | 295 | sys.exit(1) | 307 | sys.exit(1) |
2743 | 296 | self.containers_config.add_new_bind_mount(container_id, mount_path) | 308 | self.containers_config.add_new_bind_mount(container_id, mount_path) |
2744 | 297 | elif args.bind_mount == 'remove': | 309 | elif args.bind_mount == 'remove': |
2745 | 298 | if mount_path not in container_bind_mounts: | 310 | if mount_path not in container_bind_mounts: |
2747 | 299 | print("Cannot remove mount '%s', bind-mount does not exist." % mount_path) | 311 | libertine.utils.get_logger().error("Cannot remove mount '%s', bind-mount does not exist." % mount_path) |
2748 | 300 | sys.exit(1) | 312 | sys.exit(1) |
2749 | 301 | self.containers_config.delete_bind_mount(container_id, mount_path) | 313 | self.containers_config.delete_bind_mount(container_id, mount_path) |
2750 | 302 | 314 | ||
2751 | 303 | |||
2752 | 304 | else: | 315 | else: |
2754 | 305 | print("Configure called with no subcommand. See configure --help for usage.") | 316 | libertine.utils.get_logger().error("Configure called with no subcommand. See configure --help for usage.") |
2755 | 306 | sys.exit(1) | 317 | sys.exit(1) |
2756 | 307 | 318 | ||
2757 | 308 | 319 | ||
2758 | @@ -340,17 +351,17 @@ | |||
2759 | 340 | parser = argparse.ArgumentParser(description="Legacy X application support for Unity 8") | 351 | parser = argparse.ArgumentParser(description="Legacy X application support for Unity 8") |
2760 | 341 | 352 | ||
2761 | 342 | if not os.geteuid(): | 353 | if not os.geteuid(): |
2763 | 343 | print("Please do not run %s using sudo" % parser.prog) | 354 | libertine.utils.get_logger().error("Please do not run %s using sudo" % parser.prog) |
2764 | 344 | sys.exit(1) | 355 | sys.exit(1) |
2765 | 345 | 356 | ||
2766 | 346 | container_manager = LibertineContainerManager() | 357 | container_manager = LibertineContainerManager() |
2767 | 347 | 358 | ||
2768 | 348 | parser.add_argument('-q', '--quiet', | 359 | parser.add_argument('-q', '--quiet', |
2769 | 349 | action='store_const', dest='verbosity', const=0, | 360 | action='store_const', dest='verbosity', const=0, |
2772 | 350 | help=('do not print status updates on stdout')) | 361 | help=('disables all non-vital output')) |
2773 | 351 | parser.add_argument('-v', '--verbose', | 362 | parser.add_argument('-v', '--verbosity', |
2774 | 352 | action='store_const', dest='verbosity', const=2, | 363 | action='store_const', dest='verbosity', const=2, |
2776 | 353 | help=('extra verbose output')) | 364 | help=('enables debug output')) |
2777 | 354 | subparsers = parser.add_subparsers(dest="subparser_name", | 365 | subparsers = parser.add_subparsers(dest="subparser_name", |
2778 | 355 | title="subcommands", | 366 | title="subcommands", |
2779 | 356 | metavar='create, destroy, install-package, remove-package, search-cache, update, list, list-apps, configure') | 367 | metavar='create, destroy, install-package, remove-package, search-cache, update, list, list-apps, configure') |
2780 | @@ -556,8 +567,8 @@ | |||
2781 | 556 | 567 | ||
2782 | 557 | # Actually parse the args | 568 | # Actually parse the args |
2783 | 558 | args = parser.parse_args() | 569 | args = parser.parse_args() |
2786 | 559 | if args.verbosity is None: | 570 | |
2787 | 560 | args.verbosity = 1 | 571 | libertine.utils.set_environmental_verbosity(args.verbosity) |
2788 | 561 | 572 | ||
2789 | 562 | if args.subparser_name == None: | 573 | if args.subparser_name == None: |
2790 | 563 | parser.print_help() | 574 | parser.print_help() |
2791 | 564 | 575 | ||
2792 | === modified file 'tools/libertine-container-manager.1' | |||
2793 | --- tools/libertine-container-manager.1 2016-10-14 14:44:17 +0000 | |||
2794 | +++ tools/libertine-container-manager.1 2017-01-05 20:19:05 +0000 | |||
2795 | @@ -26,10 +26,10 @@ | |||
2796 | 26 | .SH OPTIONS | 26 | .SH OPTIONS |
2797 | 27 | .TP | 27 | .TP |
2798 | 28 | .BR \-q ", " \-\-quiet "" | 28 | .BR \-q ", " \-\-quiet "" |
2800 | 29 | do not print status updates on stdout | 29 | disable all non-vital output |
2801 | 30 | .TP | 30 | .TP |
2802 | 31 | .BR \-v ", " \-\-verbose "" | 31 | .BR \-v ", " \-\-verbose "" |
2804 | 32 | extra verbose output | 32 | enable debug output |
2805 | 33 | .TP | 33 | .TP |
2806 | 34 | .BR \-h ", " \-\-help "" | 34 | .BR \-h ", " \-\-help "" |
2807 | 35 | Print a summary of the command line options and exit. | 35 | Print a summary of the command line options and exit. |
2808 | @@ -319,6 +319,12 @@ | |||
2809 | 319 | Clear default container. | 319 | Clear default container. |
2810 | 320 | .RE | 320 | .RE |
2811 | 321 | .TP | 321 | .TP |
2812 | 322 | .BR | ||
2813 | 323 | |||
2814 | 324 | .SH ENVIRONMENT VARIABLES | ||
2815 | 325 | .TP | ||
2816 | 326 | .BR LIBERTINE_DEBUG | ||
2817 | 327 | Overrides verbosity arguments. 0 for quiet, 1 for standard, 2 for debug. | ||
2818 | 322 | 328 | ||
2819 | 323 | .SH SEE ALSO | 329 | .SH SEE ALSO |
2820 | 324 | .UR https://launchpad.net/libertine | 330 | .UR https://launchpad.net/libertine |
2821 | 325 | 331 | ||
2822 | === modified file 'tools/libertine-lxc-manager' | |||
2823 | --- tools/libertine-lxc-manager 2016-11-09 20:59:27 +0000 | |||
2824 | +++ tools/libertine-lxc-manager 2017-01-05 20:19:05 +0000 | |||
2825 | @@ -16,90 +16,50 @@ | |||
2826 | 16 | # You should have received a copy of the GNU General Public License | 16 | # You should have received a copy of the GNU General Public License |
2827 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
2828 | 18 | 18 | ||
2829 | 19 | import dbus | ||
2830 | 20 | import dbus.service | ||
2831 | 21 | import libertine.LxcContainer | 19 | import libertine.LxcContainer |
2832 | 22 | import libertine.utils | 20 | import libertine.utils |
2833 | 23 | import os | 21 | import os |
2834 | 24 | import shlex | 22 | import shlex |
2835 | 25 | import signal | ||
2836 | 26 | import subprocess | 23 | import subprocess |
2837 | 27 | 24 | ||
2838 | 28 | from collections import Counter | ||
2839 | 29 | from dbus.mainloop.glib import DBusGMainLoop | ||
2840 | 30 | from gi.repository import GLib | ||
2841 | 31 | from libertine.ContainersConfig import ContainersConfig | 25 | from libertine.ContainersConfig import ContainersConfig |
2845 | 32 | 26 | from libertine.lifecycle import * | |
2846 | 33 | 27 | ||
2844 | 34 | home_path = os.environ['HOME'] | ||
2847 | 35 | 28 | ||
2848 | 36 | LIBERTINE_LXC_MANAGER_NAME = libertine.LxcContainer.get_lxc_manager_dbus_name() | 29 | LIBERTINE_LXC_MANAGER_NAME = libertine.LxcContainer.get_lxc_manager_dbus_name() |
2849 | 37 | LIBERTINE_LXC_MANAGER_PATH = libertine.LxcContainer.get_lxc_manager_dbus_path() | 30 | LIBERTINE_LXC_MANAGER_PATH = libertine.LxcContainer.get_lxc_manager_dbus_path() |
2850 | 38 | 31 | ||
2851 | 39 | 32 | ||
2853 | 40 | class Service(dbus.service.Object): | 33 | class Service(ContainerLifecycleService): |
2854 | 41 | 34 | ||
2855 | 42 | def __init__(self): | 35 | def __init__(self): |
2916 | 43 | self.is_pulse_setup = False | 36 | super().__init__(LIBERTINE_LXC_MANAGER_NAME, LIBERTINE_LXC_MANAGER_PATH) |
2917 | 44 | self.app_counter = Counter() | 37 | self._home = os.environ['HOME'] |
2918 | 45 | self.containers_config = ContainersConfig() | 38 | self._containers_config = ContainersConfig() |
2919 | 46 | self.operation_counter = Counter() | 39 | self._is_pulse_setup = False |
2920 | 47 | 40 | ||
2921 | 48 | DBusGMainLoop(set_as_default=True) | 41 | def start(self, container_id, launchable): |
2922 | 49 | try: | 42 | container = libertine.LxcContainer.lxc_container(container_id) |
2923 | 50 | bus_name = dbus.service.BusName(LIBERTINE_LXC_MANAGER_NAME, | 43 | |
2924 | 51 | bus=dbus.SessionBus(), | 44 | if not container.defined: |
2925 | 52 | do_not_queue=True) | 45 | return LifecycleResult("Container {} is not valid".format(container_id)) |
2926 | 53 | except dbus.exceptions.NameExistsException: | 46 | |
2927 | 54 | print("service is already running") | 47 | if launchable and not self._is_pulse_setup: |
2928 | 55 | raise | 48 | self._setup_pulse() |
2929 | 56 | super().__init__(bus_name, LIBERTINE_LXC_MANAGER_PATH) | 49 | |
2930 | 57 | 50 | if not container.running: | |
2931 | 58 | @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME, | 51 | if launchable: |
2932 | 59 | in_signature='ss', | 52 | self._dynamic_bind_mounts(container, container_id) |
2933 | 60 | out_signature='(bs)') | 53 | |
2934 | 61 | def app_start(self, container_id, lxc_logfile): | 54 | return libertine.LxcContainer.lxc_start(container) |
2935 | 62 | if self.operation_counter[container_id] != 0: | 55 | |
2936 | 63 | return (False, "Libertine container operation already running: cannot launch application.") | 56 | return LifecycleResult() |
2937 | 64 | 57 | ||
2938 | 65 | (started, reason) = self._launch_lxc_container(container_id, lxc_logfile) | 58 | def stop(self, container_id): |
2939 | 66 | 59 | container = libertine.LxcContainer.lxc_container(container_id) | |
2940 | 67 | if started: | 60 | libertine.LxcContainer.lxc_stop(container) |
2941 | 68 | self.app_counter[container_id] += 1 | 61 | |
2942 | 69 | 62 | return LifecycleResult() # no error case | |
2883 | 70 | return (started, reason) | ||
2884 | 71 | |||
2885 | 72 | @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME, | ||
2886 | 73 | in_signature='s') | ||
2887 | 74 | def app_stop(self, container_id): | ||
2888 | 75 | self.app_counter[container_id] -= 1 | ||
2889 | 76 | |||
2890 | 77 | if self.app_counter[container_id] == 0: | ||
2891 | 78 | self._stop_lxc_container(container_id) | ||
2892 | 79 | del self.app_counter[container_id] | ||
2893 | 80 | |||
2894 | 81 | @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME, | ||
2895 | 82 | in_signature='ss', | ||
2896 | 83 | out_signature='(bs)') | ||
2897 | 84 | def operation_start(self, container_id, lxc_log_file): | ||
2898 | 85 | if self.app_counter[container_id] != 0: | ||
2899 | 86 | return (False, "Application already running in container: cannot run operation.") | ||
2900 | 87 | |||
2901 | 88 | (started, reason) = self._launch_lxc_container(container_id, lxc_log_file, launchable=False) | ||
2902 | 89 | |||
2903 | 90 | if started: | ||
2904 | 91 | self.operation_counter[container_id] += 1 | ||
2905 | 92 | |||
2906 | 93 | return (started, reason) | ||
2907 | 94 | |||
2908 | 95 | @dbus.service.method(LIBERTINE_LXC_MANAGER_NAME, | ||
2909 | 96 | in_signature='s') | ||
2910 | 97 | def operation_stop(self, container_id): | ||
2911 | 98 | self.operation_counter[container_id] -= 1 | ||
2912 | 99 | |||
2913 | 100 | if self.operation_counter[container_id] == 0: | ||
2914 | 101 | self._stop_lxc_container(container_id) | ||
2915 | 102 | del self.operation_counter[container_id] | ||
2943 | 103 | 63 | ||
2944 | 104 | def _setup_pulse(self): | 64 | def _setup_pulse(self): |
2945 | 105 | pulse_socket_path = os.path.join(libertine.utils.get_libertine_runtime_dir(), 'pulse_socket') | 65 | pulse_socket_path = os.path.join(libertine.utils.get_libertine_runtime_dir(), 'pulse_socket') |
2946 | @@ -118,42 +78,26 @@ | |||
2947 | 118 | 78 | ||
2948 | 119 | self.is_pulse_setup = True | 79 | self.is_pulse_setup = True |
2949 | 120 | 80 | ||
2950 | 121 | def _launch_lxc_container(self, container_id, lxc_log_file, launchable=True): | ||
2951 | 122 | container = libertine.LxcContainer.lxc_container(container_id) | ||
2952 | 123 | |||
2953 | 124 | if not container.defined: | ||
2954 | 125 | return (False, "Container {} is not valid".format(container_id)) | ||
2955 | 126 | |||
2956 | 127 | if launchable and not self.is_pulse_setup: | ||
2957 | 128 | self._setup_pulse() | ||
2958 | 129 | |||
2959 | 130 | if not container.running: | ||
2960 | 131 | if launchable: | ||
2961 | 132 | self._dynamic_bind_mounts(container, container_id) | ||
2962 | 133 | |||
2963 | 134 | return libertine.LxcContainer.lxc_start(container, lxc_log_file) | ||
2964 | 135 | |||
2965 | 136 | return (True, "") | ||
2966 | 137 | |||
2967 | 138 | def _stop_lxc_container(self, container_id): | ||
2968 | 139 | container = libertine.LxcContainer.lxc_container(container_id) | ||
2969 | 140 | |||
2970 | 141 | libertine.LxcContainer.lxc_stop(container) | ||
2971 | 142 | |||
2972 | 143 | def _dynamic_bind_mounts(self, container, container_id): | 81 | def _dynamic_bind_mounts(self, container, container_id): |
2974 | 144 | self.containers_config.refresh_database() | 82 | self._containers_config.refresh_database() |
2975 | 145 | mounts = self._sanitize_bind_mounts(libertine.utils.get_common_xdg_user_directories() + \ | 83 | mounts = self._sanitize_bind_mounts(libertine.utils.get_common_xdg_user_directories() + \ |
2977 | 146 | self.containers_config.get_container_bind_mounts(container_id)) | 84 | self._containers_config.get_container_bind_mounts(container_id)) |
2978 | 147 | 85 | ||
2979 | 148 | data_dir = libertine.utils.get_libertine_container_userdata_dir_path(container_id) | 86 | data_dir = libertine.utils.get_libertine_container_userdata_dir_path(container_id) |
2985 | 149 | for user_dir in libertine.utils.generate_binding_directories(mounts, home_path): | 87 | for user_dir in libertine.utils.generate_binding_directories(mounts, self._home): |
2986 | 150 | user_dir_fullpath = os.path.join(data_dir, user_dir[1]) | 88 | if os.path.isabs(user_dir[1]): |
2987 | 151 | if not os.path.exists(user_dir_fullpath): | 89 | path = user_dir[1].strip('/') |
2988 | 152 | os.makedirs(user_dir_fullpath, exist_ok=True) | 90 | fullpath = os.path.join(libertine.utils.get_libertine_container_rootfs_path(container_id), path) |
2989 | 153 | 91 | else: | |
2990 | 92 | path = "{}/{}".format(self._home.strip('/'), user_dir[1]) | ||
2991 | 93 | fullpath = os.path.join(data_dir, user_dir[1]) | ||
2992 | 94 | |||
2993 | 95 | os.makedirs(fullpath, exist_ok=True) | ||
2994 | 96 | |||
2995 | 97 | libertine.utils.get_logger().debug("Mounting {}:{} in container {}".format(user_dir[0], path, container_id)) | ||
2996 | 154 | xdg_user_dir_entry = ( | 98 | xdg_user_dir_entry = ( |
2999 | 155 | "%s %s/%s none bind,create=dir,optional" | 99 | "%s %s none bind,create=dir,optional" |
3000 | 156 | % (user_dir[0], home_path.strip('/'), user_dir[1]) | 100 | % (user_dir[0], path) |
3001 | 157 | ) | 101 | ) |
3002 | 158 | container.append_config_item("lxc.mount.entry", xdg_user_dir_entry) | 102 | container.append_config_item("lxc.mount.entry", xdg_user_dir_entry) |
3003 | 159 | 103 | ||
3004 | @@ -161,26 +105,5 @@ | |||
3005 | 161 | return [mount.replace(" ", "\\040") for mount in mounts] | 105 | return [mount.replace(" ", "\\040") for mount in mounts] |
3006 | 162 | 106 | ||
3007 | 163 | 107 | ||
3008 | 164 | def sigterm(self): | ||
3009 | 165 | shutdown() | ||
3010 | 166 | |||
3011 | 167 | |||
3012 | 168 | def shutdown(): | ||
3013 | 169 | GLib.MainLoop().quit() | ||
3014 | 170 | |||
3015 | 171 | |||
3016 | 172 | def main(): | ||
3017 | 173 | service = Service() | ||
3018 | 174 | GLib.unix_signal_add(GLib.PRIORITY_HIGH, | ||
3019 | 175 | signal.SIGTERM, | ||
3020 | 176 | sigterm, | ||
3021 | 177 | None) | ||
3022 | 178 | |||
3023 | 179 | try: | ||
3024 | 180 | GLib.MainLoop().run() | ||
3025 | 181 | except KeyboardInterrupt: | ||
3026 | 182 | shutdown() | ||
3027 | 183 | |||
3028 | 184 | |||
3029 | 185 | if __name__ == '__main__': | 108 | if __name__ == '__main__': |
3031 | 186 | main() | 109 | ContainerLifecycleServiceRunner(Service()).run() |
3032 | 187 | 110 | ||
3033 | === added file 'tools/libertine-lxd-manager' | |||
3034 | --- tools/libertine-lxd-manager 1970-01-01 00:00:00 +0000 | |||
3035 | +++ tools/libertine-lxd-manager 2017-01-05 20:19:05 +0000 | |||
3036 | @@ -0,0 +1,57 @@ | |||
3037 | 1 | #!/usr/bin/python3 | ||
3038 | 2 | # -*- coding: utf-8 -*- | ||
3039 | 3 | |||
3040 | 4 | # Copyright (C) 2016 Canonical Ltd. | ||
3041 | 5 | |||
3042 | 6 | # This program is free software: you can redistribute it and/or modify | ||
3043 | 7 | # it under the terms of the GNU General Public License as published by | ||
3044 | 8 | # the Free Software Foundation; version 3 of the License. | ||
3045 | 9 | # | ||
3046 | 10 | # This program is distributed in the hope that it will be useful, | ||
3047 | 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3048 | 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3049 | 13 | # GNU General Public License for more details. | ||
3050 | 14 | # | ||
3051 | 15 | # You should have received a copy of the GNU General Public License | ||
3052 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3053 | 17 | |||
3054 | 18 | import libertine.LxdContainer | ||
3055 | 19 | import libertine.utils | ||
3056 | 20 | import pylxd | ||
3057 | 21 | |||
3058 | 22 | from libertine.ContainersConfig import ContainersConfig | ||
3059 | 23 | from libertine.lifecycle import * | ||
3060 | 24 | |||
3061 | 25 | |||
3062 | 26 | LIBERTINE_LXD_MANAGER_NAME = libertine.LxdContainer.get_lxd_manager_dbus_name() | ||
3063 | 27 | LIBERTINE_LXD_MANAGER_PATH = libertine.LxdContainer.get_lxd_manager_dbus_path() | ||
3064 | 28 | |||
3065 | 29 | |||
3066 | 30 | class Service(ContainerLifecycleService): | ||
3067 | 31 | |||
3068 | 32 | def __init__(self): | ||
3069 | 33 | super().__init__(LIBERTINE_LXD_MANAGER_NAME, LIBERTINE_LXD_MANAGER_PATH) | ||
3070 | 34 | self._config = ContainersConfig() | ||
3071 | 35 | self._client = pylxd.Client() | ||
3072 | 36 | |||
3073 | 37 | def start(self, container_id, launchable): | ||
3074 | 38 | container = libertine.LxdContainer.lxd_container(self._client, container_id) | ||
3075 | 39 | |||
3076 | 40 | if not container: | ||
3077 | 41 | return LifecycleResult("Container {} is not valid".format(container_id)) | ||
3078 | 42 | |||
3079 | 43 | if container.status != 'Running': | ||
3080 | 44 | libertine.LxdContainer.update_libertine_profile(self._client) | ||
3081 | 45 | if launchable: | ||
3082 | 46 | libertine.LxdContainer.update_bind_mounts(container, self._config) | ||
3083 | 47 | |||
3084 | 48 | return libertine.LxdContainer.lxd_start(container) | ||
3085 | 49 | |||
3086 | 50 | return LifecycleResult() | ||
3087 | 51 | |||
3088 | 52 | def stop(self, container_id): | ||
3089 | 53 | return libertine.LxdContainer.lxd_stop(libertine.LxdContainer.lxd_container(self._client, container_id), False) | ||
3090 | 54 | |||
3091 | 55 | |||
3092 | 56 | if __name__ == '__main__': | ||
3093 | 57 | ContainerLifecycleServiceRunner(Service()).run() | ||
3094 | 0 | 58 | ||
3095 | === added file 'tools/libertine-lxd-manager.1' | |||
3096 | --- tools/libertine-lxd-manager.1 1970-01-01 00:00:00 +0000 | |||
3097 | +++ tools/libertine-lxd-manager.1 2017-01-05 20:19:05 +0000 | |||
3098 | @@ -0,0 +1,9 @@ | |||
3099 | 1 | .TH libertine-lxd-manager "1" "Dec 2016" "libertine-lxd-manager 0.99" "User Commands" | ||
3100 | 2 | |||
3101 | 3 | .SH NAME | ||
3102 | 4 | libertine-lxd-manager \- monitors LXD activity performed by libertine | ||
3103 | 5 | |||
3104 | 6 | .SH DESCRIPTION | ||
3105 | 7 | usage: libertine\-lxd\-manager | ||
3106 | 8 | .PP | ||
3107 | 9 | monitors LXD activity performed by libertine | ||
3108 | 0 | 10 | ||
3109 | === added file 'tools/libertine-lxd-setup' | |||
3110 | --- tools/libertine-lxd-setup 1970-01-01 00:00:00 +0000 | |||
3111 | +++ tools/libertine-lxd-setup 2017-01-05 20:19:05 +0000 | |||
3112 | @@ -0,0 +1,40 @@ | |||
3113 | 1 | #!/bin/sh | ||
3114 | 2 | |||
3115 | 3 | if [ -z $1 ]; then | ||
3116 | 4 | echo "Usage: $0 username" | ||
3117 | 5 | exit 1 | ||
3118 | 6 | fi | ||
3119 | 7 | |||
3120 | 8 | USERNAME=$1 | ||
3121 | 9 | |||
3122 | 10 | # Create the lxd group and add given user | ||
3123 | 11 | if [ -z "`groups ${USERNAME} | grep lxd`" ]; then | ||
3124 | 12 | groupadd --force --system lxd | ||
3125 | 13 | usermod -G lxd -a $USERNAME | ||
3126 | 14 | fi | ||
3127 | 15 | |||
3128 | 16 | # Map the given user to the container root user | ||
3129 | 17 | uid=`id --user ${USERNAME}` | ||
3130 | 18 | idmap="root:$uid:1" | ||
3131 | 19 | if [ -z "`grep ${idmap} /etc/subuid`" ]; then | ||
3132 | 20 | echo ${idmap} | tee -a /etc/subuid /etc/subgid | ||
3133 | 21 | fi | ||
3134 | 22 | |||
3135 | 23 | # find the right lxc command | ||
3136 | 24 | lxc=`which lxc` | ||
3137 | 25 | if [ -z "${lxc}" ]; then | ||
3138 | 26 | if [ -n `which lxd.lxc` ]; then | ||
3139 | 27 | lxc=`which lxd.lxc` | ||
3140 | 28 | else | ||
3141 | 29 | echo "No lxc command found on this system." | ||
3142 | 30 | exit 1 | ||
3143 | 31 | fi | ||
3144 | 32 | fi | ||
3145 | 33 | |||
3146 | 34 | # Run lxd init if there are no containers already on the system | ||
3147 | 35 | if [ 3 -ge `${lxc} list | wc -l` ]; then | ||
3148 | 36 | lxd init | ||
3149 | 37 | fi | ||
3150 | 38 | |||
3151 | 39 | mkdir -p /home/$USERNAME/.config/lxc | ||
3152 | 40 | chown -R $USERNAME:$USERNAME /home/$USERNAME/.config/lxc | ||
3153 | 0 | 41 | ||
3154 | === modified file 'tools/libertined' | |||
3155 | --- tools/libertined 2016-10-26 17:05:34 +0000 | |||
3156 | +++ tools/libertined 2017-01-05 20:19:05 +0000 | |||
3157 | @@ -62,11 +62,10 @@ | |||
3158 | 62 | os.rename(self.cache_file, '%s.1' % self.cache_file) | 62 | os.rename(self.cache_file, '%s.1' % self.cache_file) |
3159 | 63 | 63 | ||
3160 | 64 | def __enter__(self): | 64 | def __enter__(self): |
3164 | 65 | if self.config.debug: | 65 | utils.set_environmental_verbosity(self.config.verbosity) |
3165 | 66 | os.environ['LIBERTINE_DEBUG'] = '1' | 66 | |
3166 | 67 | else: | 67 | if not self.config.debug: |
3167 | 68 | if self.config.cache_output: | 68 | if self.config.cache_output: |
3168 | 69 | os.environ['LIBERTINE_DEBUG'] = '1' | ||
3169 | 70 | self._rotate_logs() | 69 | self._rotate_logs() |
3170 | 71 | self.output_file = open(self.cache_file, 'w') | 70 | self.output_file = open(self.cache_file, 'w') |
3171 | 72 | else: | 71 | else: |
3172 | @@ -94,7 +93,13 @@ | |||
3173 | 94 | self._arg_parser.add_argument(u'-d', u"--debug", | 93 | self._arg_parser.add_argument(u'-d', u"--debug", |
3174 | 95 | action='store_true', | 94 | action='store_true', |
3175 | 96 | default=False, | 95 | default=False, |
3177 | 97 | help=u"allow all output on stdout") | 96 | help=u"print output to stdout") |
3178 | 97 | self._arg_parser.add_argument('-q', '--quiet', action='store_const', | ||
3179 | 98 | dest='verbosity', const=0, | ||
3180 | 99 | help=('disables all non-vital output')) | ||
3181 | 100 | self._arg_parser.add_argument('-v', '--verbosity', action='store_const', | ||
3182 | 101 | dest='verbosity', const=2, | ||
3183 | 102 | help=('enables debug output')) | ||
3184 | 98 | self._arg_parser.add_argument(u'-c', u"--cache-output", | 103 | self._arg_parser.add_argument(u'-c', u"--cache-output", |
3185 | 99 | action='store_true', | 104 | action='store_true', |
3186 | 100 | default=False, | 105 | default=False, |
3187 | @@ -111,7 +116,7 @@ | |||
3188 | 111 | self.loop = GLib.MainLoop() | 116 | self.loop = GLib.MainLoop() |
3189 | 112 | 117 | ||
3190 | 113 | def sigterm(self, code): | 118 | def sigterm(self, code): |
3192 | 114 | utils.get_logger().info("terminate ('%s') signal received" % code) | 119 | utils.get_logger().debug("terminate ('%s') signal received" % code) |
3193 | 115 | self.shutdown() | 120 | self.shutdown() |
3194 | 116 | 121 | ||
3195 | 117 | def shutdown(self): | 122 | def shutdown(self): |
3196 | 118 | 123 | ||
3197 | === removed file 'tools/update-puritine-containers' | |||
3198 | --- tools/update-puritine-containers 2016-10-25 15:24:08 +0000 | |||
3199 | +++ tools/update-puritine-containers 1970-01-01 00:00:00 +0000 | |||
3200 | @@ -1,183 +0,0 @@ | |||
3201 | 1 | #!/usr/bin/python3 | ||
3202 | 2 | # -*- coding: utf-8 -*- | ||
3203 | 3 | |||
3204 | 4 | # Copyright (C) 2016 Canonical Ltd. | ||
3205 | 5 | # Author: Christopher Townsend <christopher.townsend@canonical.com> | ||
3206 | 6 | |||
3207 | 7 | # This program is free software: you can redistribute it and/or modify | ||
3208 | 8 | # it under the terms of the GNU General Public License as published by | ||
3209 | 9 | # the Free Software Foundation; version 3 of the License. | ||
3210 | 10 | # | ||
3211 | 11 | # This program is distributed in the hope that it will be useful, | ||
3212 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3213 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3214 | 14 | # GNU General Public License for more details. | ||
3215 | 15 | # | ||
3216 | 16 | # You should have received a copy of the GNU General Public License | ||
3217 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3218 | 18 | |||
3219 | 19 | import json | ||
3220 | 20 | import libertine.utils | ||
3221 | 21 | import os | ||
3222 | 22 | import shlex | ||
3223 | 23 | import shutil | ||
3224 | 24 | import subprocess | ||
3225 | 25 | |||
3226 | 26 | from gi.repository import GLib, Gio | ||
3227 | 27 | |||
3228 | 28 | puritine_hook_dir = os.path.join(os.environ['HOME'], '.cache', 'libertine', 'puritine') | ||
3229 | 29 | puritine_symlink_farm_file = os.path.join(puritine_hook_dir, 'PuritineSymlinkFarm.json') | ||
3230 | 30 | puritine_click_config_file = os.path.join('libertine-config', 'libertine', 'ContainersConfig.json') | ||
3231 | 31 | |||
3232 | 32 | |||
3233 | 33 | def symlink_farm_entries_count(): | ||
3234 | 34 | if (puritine_symlink_farm_list and | ||
3235 | 35 | puritine_symlink_farm_list['customContainers']): | ||
3236 | 36 | return len(puritine_symlink_farm_list['customContainers']) | ||
3237 | 37 | |||
3238 | 38 | return 0 | ||
3239 | 39 | |||
3240 | 40 | |||
3241 | 41 | def puritine_symlink_exists(symlink): | ||
3242 | 42 | for file in os.listdir(puritine_hook_dir): | ||
3243 | 43 | if file == symlink: | ||
3244 | 44 | return True | ||
3245 | 45 | |||
3246 | 46 | return False | ||
3247 | 47 | |||
3248 | 48 | |||
3249 | 49 | def get_symlink_name(puritine_symlink_farm_list, container_id): | ||
3250 | 50 | if puritine_symlink_farm_list: | ||
3251 | 51 | for container in puritine_symlink_farm_list['customContainers']: | ||
3252 | 52 | if container['id'] == container_id: | ||
3253 | 53 | return (puritine_symlink_farm_list['customContainers'].index(container), container['symlinkName']) | ||
3254 | 54 | |||
3255 | 55 | return (0, None) | ||
3256 | 56 | |||
3257 | 57 | |||
3258 | 58 | def find_removed_puritine_symlinks(puritine_symlink_farm_list): | ||
3259 | 59 | for container in puritine_symlink_farm_list['customContainers']: | ||
3260 | 60 | if puritine_symlink_exists(container['symlinkName']): | ||
3261 | 61 | continue | ||
3262 | 62 | else: | ||
3263 | 63 | manager_cmd = "libertine-container-manager destroy -i " + container['id'] | ||
3264 | 64 | cmd = shlex.split(manager_cmd) | ||
3265 | 65 | subprocess.Popen(cmd).wait() | ||
3266 | 66 | puritine_symlink_farm_list['customContainers'].remove(container) | ||
3267 | 67 | |||
3268 | 68 | return puritine_symlink_farm_list | ||
3269 | 69 | |||
3270 | 70 | |||
3271 | 71 | def find_new_or_updated_puritine_symlinks(puritine_symlink_farm_list): | ||
3272 | 72 | symlinks = os.listdir(puritine_hook_dir) | ||
3273 | 73 | |||
3274 | 74 | for symlink in symlinks: | ||
3275 | 75 | puritine_click_path = os.path.join(puritine_hook_dir, symlink) | ||
3276 | 76 | if not os.path.islink(puritine_click_path): | ||
3277 | 77 | continue | ||
3278 | 78 | |||
3279 | 79 | config_file_path = os.path.join(puritine_click_path, puritine_click_config_file) | ||
3280 | 80 | |||
3281 | 81 | with open(config_file_path, 'r') as fd: | ||
3282 | 82 | container_list = json.load(fd) | ||
3283 | 83 | |||
3284 | 84 | container_id = container_list['containerList'][0]['id'] | ||
3285 | 85 | |||
3286 | 86 | (index, symlink_name) = get_symlink_name(puritine_symlink_farm_list, container_id) | ||
3287 | 87 | |||
3288 | 88 | if symlink_name: | ||
3289 | 89 | # Package exists and symlink name is the same- no update | ||
3290 | 90 | if symlink_name == symlink: | ||
3291 | 91 | continue | ||
3292 | 92 | else: | ||
3293 | 93 | puritine_symlink_farm_list['customContainers'][index]['symlinkName'] = symlink | ||
3294 | 94 | else: | ||
3295 | 95 | symlink_obj = {'symlinkName': symlink, 'id': container_id} | ||
3296 | 96 | if 'customContainers' not in puritine_symlink_farm_list: | ||
3297 | 97 | puritine_symlink_farm_list['customContainers'] = [symlink_obj] | ||
3298 | 98 | else: | ||
3299 | 99 | puritine_symlink_farm_list['customContainers'].append(symlink_obj) | ||
3300 | 100 | |||
3301 | 101 | puritine_click_rootfs_path = os.path.join(puritine_click_path, 'libertine-data', 'libertine-container', | ||
3302 | 102 | container_id, 'rootfs') | ||
3303 | 103 | |||
3304 | 104 | libertine_containers_path = libertine.utils.get_libertine_containers_dir_path() | ||
3305 | 105 | puritine_container_path = os.path.join(libertine_containers_path, container_id) | ||
3306 | 106 | puritine_container_rootfs_path = os.path.join(puritine_container_path, 'rootfs') | ||
3307 | 107 | |||
3308 | 108 | if not os.path.exists(puritine_container_path): | ||
3309 | 109 | os.makedirs(puritine_container_path) | ||
3310 | 110 | else: | ||
3311 | 111 | os.remove(puritine_container_rootfs_path) | ||
3312 | 112 | |||
3313 | 113 | # Link to the click packages container | ||
3314 | 114 | os.symlink(puritine_click_rootfs_path, puritine_container_rootfs_path) | ||
3315 | 115 | |||
3316 | 116 | # Copy any user data that does not exist | ||
3317 | 117 | libertine_user_data_path = libertine.utils.get_libertine_container_userdata_dir_path(container_id) | ||
3318 | 118 | if not os.path.exists(libertine_user_data_path): | ||
3319 | 119 | puritine_click_user_data_path = os.path.join(puritine_click_path, 'libertine-config', 'libertine-container', | ||
3320 | 120 | 'user-data', container_id) | ||
3321 | 121 | shutil.copytree(puritine_click_user_data_path, libertine_user_data_path) | ||
3322 | 122 | |||
3323 | 123 | # Update the main ContainerConfig.json | ||
3324 | 124 | manager_cmd = "libertine-container-manager merge-configs -f " + config_file_path | ||
3325 | 125 | cmd = shlex.split(manager_cmd) | ||
3326 | 126 | subprocess.Popen(cmd).wait() | ||
3327 | 127 | |||
3328 | 128 | return puritine_symlink_farm_list | ||
3329 | 129 | |||
3330 | 130 | |||
3331 | 131 | def favorite_libertine_scope(): | ||
3332 | 132 | libertine_scope_value = 'scope://libertine-scope.ubuntu_libertine-scope' | ||
3333 | 133 | click_scope_value = 'scope://clickscope' | ||
3334 | 134 | schema = 'com.canonical.Unity.Dash' | ||
3335 | 135 | schema_key = 'favorite-scopes' | ||
3336 | 136 | |||
3337 | 137 | settings = Gio.Settings.new(schema) | ||
3338 | 138 | |||
3339 | 139 | favorites = settings.get_value(schema_key) | ||
3340 | 140 | |||
3341 | 141 | array = favorites.dup_strv() | ||
3342 | 142 | |||
3343 | 143 | if not libertine_scope_value in array: | ||
3344 | 144 | array.insert(array.index(click_scope_value) + 1, libertine_scope_value) | ||
3345 | 145 | new_favorites = GLib.Variant.new_strv(array) | ||
3346 | 146 | settings.set_value(schema_key, new_favorites) | ||
3347 | 147 | del new_favorites | ||
3348 | 148 | |||
3349 | 149 | del array | ||
3350 | 150 | del favorites | ||
3351 | 151 | del settings | ||
3352 | 152 | |||
3353 | 153 | |||
3354 | 154 | if __name__ == '__main__': | ||
3355 | 155 | puritine_symlink_farm_list = {} | ||
3356 | 156 | update_libertine_scope = libertine.utils.set_session_dbus_env_var() | ||
3357 | 157 | |||
3358 | 158 | if not os.path.exists(puritine_hook_dir): | ||
3359 | 159 | os.makedirs(puritine_hook_dir) | ||
3360 | 160 | |||
3361 | 161 | if (os.path.exists(puritine_symlink_farm_file) and | ||
3362 | 162 | os.path.getsize(puritine_symlink_farm_file) != 0): | ||
3363 | 163 | with open(puritine_symlink_farm_file, 'r') as fd: | ||
3364 | 164 | puritine_symlink_farm_list = json.load(fd) | ||
3365 | 165 | |||
3366 | 166 | count_at_start = symlink_farm_entries_count() | ||
3367 | 167 | |||
3368 | 168 | puritine_symlink_farm_list = find_new_or_updated_puritine_symlinks(puritine_symlink_farm_list) | ||
3369 | 169 | |||
3370 | 170 | if puritine_symlink_farm_list: | ||
3371 | 171 | puritine_symlink_farm_list = find_removed_puritine_symlinks(puritine_symlink_farm_list) | ||
3372 | 172 | |||
3373 | 173 | count_at_finish = symlink_farm_entries_count() | ||
3374 | 174 | |||
3375 | 175 | with open(puritine_symlink_farm_file, 'w') as fd: | ||
3376 | 176 | json.dump(puritine_symlink_farm_list, fd, sort_keys=True, indent=4) | ||
3377 | 177 | fd.write('\n') | ||
3378 | 178 | |||
3379 | 179 | if count_at_start == 0 and count_at_finish > 0 and update_libertine_scope: | ||
3380 | 180 | favorite_libertine_scope() | ||
3381 | 181 | |||
3382 | 182 | if update_libertine_scope: | ||
3383 | 183 | libertine.utils.refresh_libertine_scope() |
okok let's do this