Merge lp:~townsend/libertine/0.99.12 into lp:libertine/trunk

Proposed by Christopher Townsend
Status: Merged
Approved by: Stephen M. Webb
Approved revision: 120
Merged at revision: 120
Proposed branch: lp:~townsend/libertine/0.99.12
Merge into: lp:libertine/trunk
Diff against target: 1203 lines (+717/-77)
19 files modified
debian/changelog (+14/-0)
libertine/ContainerArchivesList.cpp (+94/-0)
libertine/ContainerArchivesList.h (+81/-0)
libertine/ContainerConfig.cpp (+123/-17)
libertine/ContainerConfig.h (+40/-14)
libertine/ContainerConfigList.cpp (+15/-1)
libertine/ContainerConfigList.h (+4/-0)
libertine/ContainerManager.cpp (+10/-0)
libertine/ContainerManager.h (+1/-0)
libertine/libertine.cpp (+3/-0)
libertine/libertine.h (+2/-0)
libertine/qml/ConfigureContainer.qml (+34/-36)
libertine/qml/ExtraArchivesView.qml (+167/-0)
liblibertine/CMakeLists.txt (+1/-0)
python/libertine/ChrootContainer.py (+12/-3)
python/libertine/Libertine.py (+15/-4)
python/libertine/LxcContainer.py (+2/-0)
python/libertine/utils.py (+4/-0)
tools/libertine-container-manager (+95/-2)
To merge this branch: bzr merge lp:~townsend/libertine/0.99.12
Reviewer Review Type Date Requested Status
Stephen M. Webb (community) Approve
Review via email: mp+289286@code.launchpad.net

Commit message

* Enable feature to add and remove extra archives in the container. This is for PPA's only right now.
* add multiarch support to chroot containers too.
* When killing processes in the container, need to wait() after terminating
processes and to actually terminate and wait() on the main process. Caused
app launching issues in LXC based containers.

To post a comment you must log in.
Revision history for this message
Stephen M. Webb (bregma) :
review: Approve
lp:~townsend/libertine/0.99.12 updated
121. By Christopher Townsend

When killing processes in the container, need to wait() after terminating
processes and to actually terminate and wait() on the main process. Caused
app launching issues in LXC based containers. (LP: #1558588)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2016-03-02 22:06:45 +0000
3+++ debian/changelog 2016-03-21 12:56:41 +0000
4@@ -1,3 +1,17 @@
5+libertine (0.99.12-0ubuntu1) UNRELEASED; urgency=medium
6+
7+ [ Chris Townsend ]
8+ * Enable feature to add and remove extra archives in the container. This is
9+ for PPA's only right now. (LP: #1541901)
10+ * When killing processes in the container, need to wait() after terminating
11+ processes and to actually terminate and wait() on the main process. Caused
12+ app launching issues in LXC based containers. (LP: #1558588)
13+
14+ [ Stephen M. Webb ]
15+ * add multiarch support to chroot containers too. (LP: #1556303)
16+
17+ -- Chris Townsend <christopher.townsend@canonical.com> Wed, 16 Mar 2016 15:38:04 -0400
18+
19 libertine (0.99.11+16.04.20160302-0ubuntu1) xenial; urgency=medium
20
21 [ Chris Townsend ]
22
23=== added file 'libertine/ContainerArchivesList.cpp'
24--- libertine/ContainerArchivesList.cpp 1970-01-01 00:00:00 +0000
25+++ libertine/ContainerArchivesList.cpp 2016-03-21 12:56:41 +0000
26@@ -0,0 +1,94 @@
27+/**
28+ * @file ContainerArchivesList.cpp
29+ * @brief Libertine Manager list of extra container archives, ie, PPAs
30+ */
31+/*
32+ * Copyright 2016 Canonical Ltd
33+ *
34+ * Libertine is free software: you can redistribute it and/or modify it under
35+ * the terms of the GNU General Public License, version 3, as published by the
36+ * Free Software Foundation.
37+ *
38+ * Libertine is distributed in the hope that it will be useful, but WITHOUT ANY
39+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
40+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
41+ *
42+ * You should have received a copy of the GNU General Public License
43+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
44+ */
45+#include "libertine/ContainerArchivesList.h"
46+#include "libertine/ContainerConfigList.h"
47+
48+ContainerArchivesList::
49+ContainerArchivesList(ContainerConfigList* container_config_list,
50+ QObject* parent)
51+: QAbstractListModel(parent)
52+, container_config_list_(container_config_list)
53+{ }
54+
55+
56+ContainerArchivesList::
57+~ContainerArchivesList()
58+{ }
59+
60+
61+void ContainerArchivesList::
62+setContainerArchives(QString const& container_id)
63+{
64+ archives_ = container_config_list_->getArchivesForContainer(container_id);
65+
66+ beginResetModel();
67+ endResetModel();
68+}
69+
70+
71+bool ContainerArchivesList::
72+empty() const noexcept
73+{ return archives_->empty(); }
74+
75+
76+ContainerArchivesList::size_type ContainerArchivesList::
77+size() const noexcept
78+{ return archives_->count(); }
79+
80+
81+int ContainerArchivesList::
82+rowCount(QModelIndex const&) const
83+{
84+ return this->size();
85+}
86+
87+
88+QHash<int, QByteArray> ContainerArchivesList::
89+roleNames() const
90+{
91+ QHash<int, QByteArray> roles;
92+ roles[static_cast<int>(DataRole::ArchiveName)] = "archiveName";
93+ roles[static_cast<int>(DataRole::ArchiveStatus)] = "archiveStatus";
94+
95+ return roles;
96+}
97+
98+
99+QVariant ContainerArchivesList::
100+data(QModelIndex const& index, int role) const
101+{
102+ QVariant result;
103+
104+ if (index.isValid() && index.row() <= archives_->count())
105+ {
106+ switch (static_cast<DataRole>(role))
107+ {
108+ case DataRole::ArchiveName:
109+ result = (*archives_)[index.row()]->archive_name();
110+ break;
111+ case DataRole::ArchiveStatus:
112+ result = (*archives_)[index.row()]->archive_status();
113+ break;
114+ case DataRole::Error:
115+ break;
116+ }
117+ }
118+
119+ return result;
120+}
121
122=== added file 'libertine/ContainerArchivesList.h'
123--- libertine/ContainerArchivesList.h 1970-01-01 00:00:00 +0000
124+++ libertine/ContainerArchivesList.h 2016-03-21 12:56:41 +0000
125@@ -0,0 +1,81 @@
126+/**
127+ * @file ContainerArchivesList.h
128+ * @brief Libertine Manager list of extra container archives (PPAs)
129+ */
130+/*
131+ * Copyright 2016 Canonical Ltd
132+ *
133+ * Libertine is free software: you can redistribute it and/or modify it under
134+ * the terms of the GNU General Public License, version 3, as published by the
135+ * Free Software Foundation.
136+ *
137+ * Libertine is distributed in the hope that it will be useful, but WITHOUT ANY
138+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
139+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
140+ *
141+ * You should have received a copy of the GNU General Public License
142+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
143+ */
144+#ifndef _CONTAINER_ARCHIVES_LIST_H_
145+#define _CONTAINER_ARCHIVES_LIST_H_
146+
147+#include "libertine/ContainerConfig.h"
148+
149+#include <QtCore/QAbstractListModel>
150+#include <QtCore/QList>
151+#include <QtCore/QObject>
152+#include <QtCore/QString>
153+
154+
155+class ContainerArchives;
156+class ContainerConfigList;
157+
158+class ContainerArchivesList
159+: public QAbstractListModel
160+{
161+ Q_OBJECT
162+
163+public:
164+ using ArchivesList = QList<ContainerArchives*>;
165+ using iterator = ArchivesList::iterator;
166+ using size_type = ArchivesList::size_type;
167+
168+ enum class DataRole
169+ : int
170+ {
171+ ArchiveName = Qt::UserRole + 1,
172+ ArchiveStatus,
173+ Error
174+ };
175+
176+public:
177+ explicit
178+ ContainerArchivesList(ContainerConfigList* container_config_list,
179+ QObject* parent = nullptr);
180+
181+ ~ContainerArchivesList();
182+
183+ Q_INVOKABLE void
184+ setContainerArchives(QString const& container_id);
185+
186+ Q_INVOKABLE bool
187+ empty() const noexcept;
188+
189+ size_type
190+ size() const noexcept;
191+
192+ int
193+ rowCount(QModelIndex const& parent = QModelIndex()) const;
194+
195+ QHash<int, QByteArray>
196+ roleNames() const;
197+
198+ QVariant
199+ data(QModelIndex const& index, int role = Qt::DisplayRole) const;
200+
201+private:
202+ ContainerConfigList* container_config_list_;
203+ ArchivesList* archives_;
204+};
205+
206+#endif /* _CONTAINER_ARCHIVES_LIST_H_ */
207
208=== modified file 'libertine/ContainerConfig.cpp'
209--- libertine/ContainerConfig.cpp 2016-02-10 19:31:10 +0000
210+++ libertine/ContainerConfig.cpp 2016-03-21 12:56:41 +0000
211@@ -178,15 +178,15 @@
212 return ContainerConfig::InstallStatus::New;
213 }
214
215- const static struct { QString string; ContainerApps::AppStatus enumeration; } app_status_names[] =
216+ const static struct { QString string; CurrentStatus enumeration; } status_names[] =
217 {
218- { QObject::tr("new"), ContainerApps::AppStatus::New },
219- { QObject::tr("installing"), ContainerApps::AppStatus::Installing },
220- { QObject::tr("installed"), ContainerApps::AppStatus::Installed },
221- { QObject::tr("failed"), ContainerApps::AppStatus::Failed },
222- { QObject::tr("removing"), ContainerApps::AppStatus::Removing },
223- { QObject::tr("removed"), ContainerApps::AppStatus::Removed },
224- { QString(), ContainerApps::AppStatus::New }
225+ { QObject::tr("new"), CurrentStatus::New },
226+ { QObject::tr("installing"), CurrentStatus::Installing },
227+ { QObject::tr("installed"), CurrentStatus::Installed },
228+ { QObject::tr("failed"), CurrentStatus::Failed },
229+ { QObject::tr("removing"), CurrentStatus::Removing },
230+ { QObject::tr("removed"), CurrentStatus::Removed },
231+ { QString(), CurrentStatus::New }
232 };
233
234 QString
235@@ -207,10 +207,10 @@
236 return package_name;
237 }
238
239- ContainerApps::AppStatus
240+ CurrentStatus
241 extract_app_status_from_json(QJsonObject const& json_object)
242 {
243- ContainerApps::AppStatus app_status = ContainerApps::AppStatus::New;
244+ CurrentStatus app_status = CurrentStatus::New;
245
246 QJsonValue value = json_object["appStatus"];
247 if (value != QJsonValue::Undefined)
248@@ -221,7 +221,7 @@
249 QString s = value.toString();
250 if (s.length() > 0)
251 {
252- for (auto const& name: app_status_names)
253+ for (auto const& name: status_names)
254 {
255 if (0 == s.compare(name.string, Qt::CaseInsensitive))
256 {
257@@ -241,7 +241,7 @@
258 {
259 QList<ContainerApps*> container_apps;
260 QString package_name;
261- ContainerApps::AppStatus app_status;
262+ CurrentStatus app_status;
263
264 QJsonArray installed_apps = json_object["installedApps"].toArray();
265
266@@ -255,12 +255,78 @@
267 }
268 return container_apps;
269 }
270+
271+ QString
272+ extract_archive_name_from_json(QJsonObject const& json_object)
273+ {
274+ QString archive_name;
275+
276+ QJsonValue value = json_object["archiveName"];
277+ if (value != QJsonValue::Undefined)
278+ {
279+ QJsonValue::Type value_type = value.type();
280+ if (value_type == QJsonValue::String)
281+ {
282+ archive_name = value.toString();
283+ }
284+ }
285+
286+ return archive_name;
287+ }
288+
289+ CurrentStatus
290+ extract_archive_status_from_json(QJsonObject const& json_object)
291+ {
292+ CurrentStatus archive_status = CurrentStatus::New;
293+
294+ QJsonValue value = json_object["archiveStatus"];
295+ if (value != QJsonValue::Undefined)
296+ {
297+ QJsonValue::Type value_type = value.type();
298+ if (value_type == QJsonValue::String)
299+ {
300+ QString s = value.toString();
301+ if (s.length() > 0)
302+ {
303+ for (auto const& name: status_names)
304+ {
305+ if (0 == s.compare(name.string, Qt::CaseInsensitive))
306+ {
307+ archive_status = name.enumeration;
308+ break;
309+ }
310+ }
311+ }
312+ }
313+ }
314+
315+ return archive_status;
316+ }
317+
318+ QList<ContainerArchives*>
319+ extract_container_archives_from_json(QJsonObject const& json_object)
320+ {
321+ QList<ContainerArchives*> container_archives;
322+ QString archive_name;
323+ CurrentStatus archive_status;
324+
325+ QJsonArray extra_archives = json_object["extraArchives"].toArray();
326+
327+ for (auto const& archive: extra_archives)
328+ {
329+ archive_name = extract_archive_name_from_json(archive.toObject());
330+ archive_status = extract_archive_status_from_json(archive.toObject());
331+
332+ container_archives.append(new ContainerArchives(archive_name, archive_status));
333+ }
334+ return container_archives;
335+ }
336 } // anonymous namespace
337
338
339 ContainerApps::
340 ContainerApps(QString const& package_name,
341- ContainerApps::AppStatus app_status,
342+ CurrentStatus app_status,
343 QObject* parent)
344 : QObject(parent)
345 , package_name_(package_name)
346@@ -280,7 +346,32 @@
347
348 QString const& ContainerApps::
349 app_status() const
350-{ return app_status_names[(int)app_status_].string; }
351+{ return status_names[(int)app_status_].string; }
352+
353+
354+ContainerArchives::
355+ContainerArchives(QString const& archive_name,
356+ CurrentStatus archive_status,
357+ QObject* parent)
358+: QObject(parent)
359+, archive_name_(archive_name)
360+, archive_status_(archive_status)
361+{ }
362+
363+
364+ContainerArchives::
365+~ContainerArchives()
366+{ }
367+
368+
369+QString const& ContainerArchives::
370+archive_name() const
371+{ return archive_name_; }
372+
373+
374+QString const& ContainerArchives::
375+archive_status() const
376+{ return status_names[(int)archive_status_].string; }
377
378
379 ContainerConfig::
380@@ -316,6 +407,7 @@
381 , multiarch_support_(extract_multiarch_support_from_json(json_object))
382 , install_status_(extract_install_status_from_json(json_object))
383 , container_apps_(extract_container_apps_from_json(json_object))
384+, container_archives_(extract_container_archives_from_json(json_object))
385 { }
386
387
388@@ -375,12 +467,19 @@
389 { return container_apps_; }
390
391
392+QList<ContainerArchives*> & ContainerConfig::
393+container_archives()
394+{ return container_archives_; }
395+
396+
397 QJsonObject ContainerConfig::
398 toJson() const
399 {
400 QJsonObject json_object,
401- app_object;
402- QJsonArray apps;
403+ app_object,
404+ archive_object;
405+ QJsonArray apps,
406+ archives;
407
408 json_object["id"] = container_id_;
409 json_object["name"] = container_name_;
410@@ -397,10 +496,17 @@
411 for (auto const& container_app: container_apps_)
412 {
413 app_object["packageName"] = container_app->package_name();
414- app_object["appStatus"] = app_status_names[0].string;
415+ app_object["appStatus"] = status_names[0].string;
416 apps.append(app_object);
417 }
418 json_object["installedApps"] = apps;
419
420+ for (auto const& container_archive: container_archives_)
421+ {
422+ archive_object["archiveName"] = container_archive->archive_name();
423+ archives.append(archive_object);
424+ }
425+ json_object["extraArchives"] = archives;
426+
427 return json_object;
428 }
429
430=== modified file 'libertine/ContainerConfig.h'
431--- libertine/ContainerConfig.h 2016-02-10 19:31:10 +0000
432+++ libertine/ContainerConfig.h 2016-03-21 12:56:41 +0000
433@@ -24,18 +24,17 @@
434 #include <QtCore/QString>
435
436
437+enum class CurrentStatus { New, Installing, Installed, Failed, Removing, Removed };
438+
439+
440 class ContainerApps
441 : public QObject
442 {
443 Q_OBJECT
444
445 public:
446- enum class AppStatus
447- { New, Installing, Installed, Failed, Removing, Removed };
448-
449-public:
450 ContainerApps(QString const& package_name,
451- AppStatus app_status,
452+ CurrentStatus app_status,
453 QObject* parent = nullptr);
454 ~ContainerApps();
455
456@@ -46,8 +45,31 @@
457 app_status() const;
458
459 private:
460- QString package_name_;
461- AppStatus app_status_;
462+ QString package_name_;
463+ CurrentStatus app_status_;
464+};
465+
466+
467+class ContainerArchives
468+: public QObject
469+{
470+ Q_OBJECT
471+
472+public:
473+ ContainerArchives(QString const& archive_name,
474+ CurrentStatus archive_status,
475+ QObject* parent = nullptr);
476+ ~ContainerArchives();
477+
478+ QString const&
479+ archive_name() const;
480+
481+ QString const&
482+ archive_status() const;
483+
484+private:
485+ QString archive_name_;
486+ CurrentStatus archive_status_;
487 };
488
489
490@@ -107,6 +129,9 @@
491 QList<ContainerApps*> &
492 container_apps();
493
494+ QList<ContainerArchives*> &
495+ container_archives();
496+
497 QJsonObject
498 toJson() const;
499
500@@ -115,13 +140,14 @@
501 void installStatusChanged();
502
503 private:
504- QString container_id_;
505- QString container_name_;
506- QString container_type_;
507- QString distro_series_;
508- QString multiarch_support_;
509- InstallStatus install_status_;
510- QList<ContainerApps*> container_apps_;
511+ QString container_id_;
512+ QString container_name_;
513+ QString container_type_;
514+ QString distro_series_;
515+ QString multiarch_support_;
516+ InstallStatus install_status_;
517+ QList<ContainerApps*> container_apps_;
518+ QList<ContainerArchives*> container_archives_;
519 };
520
521 #endif /* CONTAINER_CONTAINERCONFIG_H */
522
523=== modified file 'libertine/ContainerConfigList.cpp'
524--- libertine/ContainerConfigList.cpp 2016-02-11 15:25:09 +0000
525+++ libertine/ContainerConfigList.cpp 2016-03-21 12:56:41 +0000
526@@ -121,7 +121,7 @@
527 {
528 if (config->container_id() == container_id)
529 {
530- config->container_apps().append(new ContainerApps(package_name, ContainerApps::AppStatus::New, this));
531+ config->container_apps().append(new ContainerApps(package_name, CurrentStatus::New, this));
532 break;
533 }
534 }
535@@ -193,6 +193,20 @@
536 }
537
538
539+QList<ContainerArchives*> * ContainerConfigList::
540+getArchivesForContainer(QString const& container_id)
541+{
542+ for (auto const& config: configs_)
543+ {
544+ if (config->container_id() == container_id)
545+ {
546+ return &(config->container_archives());
547+ }
548+ }
549+ return nullptr;
550+}
551+
552+
553 QString ContainerConfigList::
554 getContainerType(QString const& container_id)
555 {
556
557=== modified file 'libertine/ContainerConfigList.h'
558--- libertine/ContainerConfigList.h 2016-02-11 15:25:09 +0000
559+++ libertine/ContainerConfigList.h 2016-03-21 12:56:41 +0000
560@@ -25,6 +25,7 @@
561
562
563 class ContainerApps;
564+class ContainerArchives;
565 class ContainerConfig;
566 class LibertineConfig;
567
568@@ -112,6 +113,9 @@
569 Q_INVOKABLE QString
570 getAppVersion(QString const& app_info);
571
572+ QList<ContainerArchives*> *
573+ getArchivesForContainer(QString const& container_id);
574+
575 Q_INVOKABLE QString
576 getContainerType(QString const& container_id);
577
578
579=== modified file 'libertine/ContainerManager.cpp'
580--- libertine/ContainerManager.cpp 2016-02-11 15:59:00 +0000
581+++ libertine/ContainerManager.cpp 2016-03-21 12:56:41 +0000
582@@ -395,6 +395,9 @@
583 void ContainerManagerWorker::
584 configureContainer(QStringList configure_command)
585 {
586+ QByteArray error_msg;
587+ bool result = true;
588+
589 QProcess libertine_cli_tool;
590 QString exec_line = libertine_container_manager_tool;
591 QStringList args;
592@@ -408,6 +411,13 @@
593
594 libertine_cli_tool.waitForFinished(-1);
595
596+ if (libertine_cli_tool.exitCode() != 0)
597+ {
598+ error_msg = libertine_cli_tool.readAllStandardOutput();
599+ result = false;
600+ }
601+
602+ emit finishedConfigure(result, QString(error_msg));
603 emit finished();
604 quit();
605 }
606
607=== modified file 'libertine/ContainerManager.h'
608--- libertine/ContainerManager.h 2016-02-11 16:25:49 +0000
609+++ libertine/ContainerManager.h 2016-03-21 12:56:41 +0000
610@@ -146,6 +146,7 @@
611 void finishedRemove(bool result, QString const& error_msg);
612 void finishedSearch(bool result, QList<QString> packageList);
613 void finishedCommand(QString const& command_output);
614+ void finishedConfigure(bool result, QString const& error_msg);
615 };
616
617 #endif /* CONTAINER_CONTAINERMANAGER_H_ */
618
619=== modified file 'libertine/libertine.cpp'
620--- libertine/libertine.cpp 2016-01-20 17:34:19 +0000
621+++ libertine/libertine.cpp 2016-03-21 12:56:41 +0000
622@@ -21,6 +21,7 @@
623 #include <cstdlib>
624 #include "libertine/ContainerManager.h"
625 #include "libertine/ContainerAppsList.h"
626+#include "libertine/ContainerArchivesList.h"
627 #include "libertine/ContainerConfig.h"
628 #include "libertine/ContainerConfigList.h"
629 #include "libertine/libertine.h"
630@@ -105,6 +106,7 @@
631
632 containers_ = new ContainerConfigList(config_.data(), this);
633 container_apps_ = new ContainerAppsList(containers_, this);
634+ container_archives_ = new ContainerArchivesList(containers_, this);
635 password_helper_ = new PasswordHelper();
636
637 initialize_view();
638@@ -131,6 +133,7 @@
639 QQmlContext* ctxt = view_.rootContext();
640 ctxt->setContextProperty("containerConfigList", containers_);
641 ctxt->setContextProperty("containerAppsList", container_apps_);
642+ ctxt->setContextProperty("containerArchivesList", container_archives_);
643 ctxt->setContextProperty("passwordHelper", password_helper_);
644
645 view_.setSource(QUrl::fromLocalFile(main_qml_source_file_));
646
647=== modified file 'libertine/libertine.h'
648--- libertine/libertine.h 2016-01-20 17:34:19 +0000
649+++ libertine/libertine.h 2016-03-21 12:56:41 +0000
650@@ -30,6 +30,7 @@
651 class LibertineConfig;
652 class PasswordHelper;
653 class ContainerAppsList;
654+class ContainerArchivesList;
655
656
657 class Libertine
658@@ -55,6 +56,7 @@
659 QFileSystemWatcher watcher_;
660 ContainerConfigList* containers_;
661 ContainerAppsList* container_apps_;
662+ ContainerArchivesList* container_archives_;
663 PasswordHelper* password_helper_;
664 QQuickView view_;
665 };
666
667=== modified file 'libertine/qml/ConfigureContainer.qml'
668--- libertine/qml/ConfigureContainer.qml 2016-02-11 15:25:09 +0000
669+++ libertine/qml/ConfigureContainer.qml 2016-03-21 12:56:41 +0000
670@@ -1,5 +1,5 @@
671 /**
672- * @file configureContainer.qml
673+ * @file ConfigureContainer.qml
674 * @brief Libertine configure container view
675 */
676 /*
677@@ -19,51 +19,49 @@
678 import Libertine 1.0
679 import QtQuick 2.4
680 import Ubuntu.Components 1.2
681+import Ubuntu.Components.ListItems 1.2 as ListItem
682
683
684 Page {
685 id: configureView
686 title: i18n.tr("Configure ") + mainView.currentContainer
687
688- Item {
689- visible: containerConfigList.getHostArchitecture() == 'x86_64' ? true : false
690- CheckBox {
691- id: multiarchCheckBox
692- anchors {
693- left: parent.left
694- top: parent.top
695- leftMargin: configureView.leftMargin
696- }
697- checked: containerConfigList.getContainerMultiarchSupport(mainView.currentContainer) == 'enabled' ? true : false
698- onClicked: {
699- var comp = Qt.createComponent("ContainerManager.qml")
700- if (multiarchCheckBox.checked) {
701- var worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
702- "containerId": mainView.currentContainer,
703- "containerType": containerConfigList.getContainerType(mainView.currentContainer),
704- "data_list": ["--multiarch", "enable"]})
705- worker.start()
706+ Column {
707+ anchors.left: parent.left
708+ anchors.right: parent.right
709+
710+ ListItem.Standard {
711+ visible: containerConfigList.getHostArchitecture() == 'x86_64' ? true : false
712+ control: CheckBox {
713+ checked: containerConfigList.getContainerMultiarchSupport(mainView.currentContainer) == 'enabled' ? true : false
714+ onClicked: {
715+ var comp = Qt.createComponent("ContainerManager.qml")
716+ if (multiarchCheckBox.checked) {
717+ var worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
718+ "containerId": mainView.currentContainer,
719+ "containerType": containerConfigList.getContainerType(mainView.currentContainer),
720+ "data_list": ["--multiarch", "enable"]})
721+ worker.start()
722+ }
723+ else {
724+ var worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
725+ "containerId": mainView.currentContainer,
726+ "containerType": containerConfigList.getContainerType(mainView.currentContainer),
727+ "data_list": ["--multiarch", "disable"]})
728+ worker.start()
729+ }
730 }
731- else {
732- var worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
733- "containerId": mainView.currentContainer,
734- "containerType": containerConfigList.getContainerType(mainView.currentContainer),
735- "data_list": ["--multiarch", "disable"]})
736- worker.start()
737- }
738- }
739- }
740- Label {
741- anchors {
742- left: multiarchCheckBox.right
743- right: parent.right
744- top: parent.bottom
745- verticalCenter: parent.verticalCenter
746- leftMargin: units.gu(2)
747- rightMargin: configureView.rightMargin
748 }
749 text: i18n.tr("i386 multiarch support")
750 }
751
752+ ListItem.SingleValue {
753+ text: i18n.tr("Additional archives and PPAs")
754+ progression: true
755+ onClicked: {
756+ containerArchivesList.setContainerArchives(mainView.currentContainer)
757+ pageStack.push(Qt.resolvedUrl("ExtraArchivesView.qml"))
758+ }
759+ }
760 }
761 }
762
763=== added file 'libertine/qml/ExtraArchivesView.qml'
764--- libertine/qml/ExtraArchivesView.qml 1970-01-01 00:00:00 +0000
765+++ libertine/qml/ExtraArchivesView.qml 2016-03-21 12:56:41 +0000
766@@ -0,0 +1,167 @@
767+/**
768+ * @file ExtraArchiveView.qml
769+ * @brief Libertine container add archive view
770+ */
771+/*
772+ * Copyright 2016 Canonical Ltd
773+ *
774+ * Libertine is free software: you can redistribute it and/or modify it under
775+ * the terms of the GNU General Public License, version 3, as published by the
776+ * Free Software Foundation.
777+ *
778+ * Libertine is distributed in the hope that it will be useful, but WITHOUT ANY
779+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
780+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
781+ *
782+ * You should have received a copy of the GNU General Public License
783+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
784+ */
785+import Libertine 1.0
786+import QtQuick 2.4
787+import Ubuntu.Components 1.2
788+import Ubuntu.Components.Popups 1.2
789+
790+Page {
791+ id: extraArchiveView
792+ title: i18n.tr("Additional Archives and PPAs")
793+ property var archive_name: null
794+ property var worker: null
795+
796+ head.actions: [
797+ Action {
798+ iconName: "add"
799+ text: i18n.tr("add")
800+ description: i18n.tr("Add a new PPA")
801+ onTriggered: PopupUtils.open(addArchivePopup)
802+ }
803+ ]
804+
805+ Component {
806+ id: addArchivePopup
807+ Dialog {
808+ id: addArchiveDialog
809+ title: i18n.tr("Add additional PPA")
810+ text: i18n.tr("Enter name of PPA in the form ppa:user/ppa-name:")
811+
812+ TextField {
813+ id: extraArchiveString
814+ onAccepted: {
815+ PopupUtils.close(addArchiveDialog)
816+ addArchive(text)
817+ }
818+ }
819+ Button {
820+ text: i18n.tr("OK")
821+ color: UbuntuColors.green
822+ onClicked: {
823+ PopupUtils.close(addArchiveDialog)
824+ addArchive(extraArchiveString.text)
825+ }
826+ }
827+ Button {
828+ text: i18n.tr("Cancel")
829+ color: UbuntuColors.red
830+ onClicked: PopupUtils.close(addArchiveDialog)
831+ }
832+ Component.onCompleted: {
833+ extraArchiveString.forceActiveFocus()
834+ }
835+ }
836+ }
837+
838+ UbuntuListView {
839+ id: extraArchiveList
840+ anchors.fill: parent
841+ model: containerArchivesList
842+ delegate: ListItem {
843+ ActivityIndicator {
844+ id: extraArchiveActivity
845+ anchors.verticalCenter: parent.verticalCenter
846+ visible: (archiveStatus === i18n.tr("installing") ||
847+ archiveStatus === i18n.tr("removing")) ? true : false
848+ running: extraArchiveActivity.visible
849+ }
850+ Label {
851+ anchors {
852+ verticalCenter: parent.verticalCenter
853+ left: extraArchiveActivity.running ? extraArchiveActivity.right : parent.left
854+ leftMargin: units.gu(2)
855+ }
856+ text: archiveName
857+ }
858+ leadingActions: ListItemActions {
859+ actions: [
860+ Action {
861+ iconName: "delete"
862+ text: i18n.tr("remove")
863+ description: i18n.tr("Remove extra archive")
864+ onTriggered: {
865+ deleteArchive(archiveName)
866+ }
867+ }
868+ ]
869+ }
870+ }
871+ }
872+
873+ function addArchive(archive) {
874+ var comp = Qt.createComponent("ContainerManager.qml")
875+ worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
876+ "containerId": mainView.currentContainer,
877+ "containerType": containerConfigList.getContainerType(mainView.currentContainer),
878+ "data_list": ["--add-archive", archive]})
879+ worker.finishedConfigure.connect(finishedConfigure)
880+ worker.start()
881+ }
882+
883+ function deleteArchive(archive) {
884+ var comp = Qt.createComponent("ContainerManager.qml")
885+ worker = comp.createObject(mainView, {"containerAction": ContainerManagerWorker.Configure,
886+ "containerId": mainView.currentContainer,
887+ "containerType": containerConfigList.getContainerType(mainView.currentContainer),
888+ "data_list": ["--delete-archive", archive]})
889+ worker.finishedConfigure.connect(finishedConfigure)
890+ worker.start()
891+ }
892+
893+ Component {
894+ id: addFailedPopup
895+
896+ Dialog {
897+ property var error_msg: null
898+ id: addFailedDialog
899+ title: i18n.tr("Adding archive failed")
900+ text: error_msg
901+
902+ Button {
903+ text: i18n.tr("Dismiss")
904+ onClicked: PopupUtils.close(addFailedDialog)
905+ }
906+ }
907+ }
908+
909+ Component.onCompleted: {
910+ containerConfigList.configChanged.connect(reloadArchives)
911+ }
912+
913+ Component.onDestruction: {
914+ containerConfigList.configChanged.disconnect(reloadArchives)
915+
916+ if (worker) {
917+ worker.finishedConfigure.disconnect(finishedConfigure)
918+ }
919+ }
920+
921+ function reloadArchives() {
922+ containerArchivesList.setContainerArchives(mainView.currentContainer)
923+ }
924+
925+ function finishedConfigure(result, error_msg) {
926+ if (result) {
927+ containerArchivesList.setContainerArchives(mainView.currentContainer)
928+ }
929+ else {
930+ PopupUtils.open(addFailedPopup, null, {'error_msg': error_msg})
931+ }
932+ }
933+}
934
935=== modified file 'liblibertine/CMakeLists.txt'
936--- liblibertine/CMakeLists.txt 2015-10-07 18:59:50 +0000
937+++ liblibertine/CMakeLists.txt 2016-03-21 12:56:41 +0000
938@@ -11,6 +11,7 @@
939 ${libertine_src}/ContainerConfig.cpp
940 ${libertine_src}/ContainerManager.cpp
941 ${libertine_src}/ContainerAppsList.cpp
942+ ${libertine_src}/ContainerArchivesList.cpp
943 ${libertine_src}/PasswordHelper.cpp
944 )
945 set_target_properties(libertine-common PROPERTIES
946
947=== modified file 'python/libertine/ChrootContainer.py'
948--- python/libertine/ChrootContainer.py 2016-02-08 16:47:18 +0000
949+++ python/libertine/ChrootContainer.py 2016-03-21 12:56:41 +0000
950@@ -67,8 +67,9 @@
951 def destroy_libertine_container(self):
952 shutil.rmtree(self.root_path)
953
954- def create_libertine_container(self, password=None, verbosity=1):
955+ def create_libertine_container(self, password=None, multiarch=False, verbosity=1):
956 installed_release = self.get_container_distro(self.container_id)
957+ architecture = utils.get_host_architecture()
958
959 # Create the actual chroot
960 if installed_release == "trusty":
961@@ -96,7 +97,7 @@
962 fd.write("done\n")
963 os.fchmod(fd.fileno(), 0o755)
964
965- # Add universe and -updates to the chroot's sources.list
966+ # Add universe, multiverse, and -updates to the chroot's sources.list
967 if (utils.get_host_architecture() == 'armhf'):
968 archive = "deb http://ports.ubuntu.com/ubuntu-ports "
969 else:
970@@ -105,9 +106,11 @@
971 if verbosity == 1:
972 print("Updating chroot's sources.list entries...")
973 with open(os.path.join(self.root_path, 'etc', 'apt', 'sources.list'), 'a') as fd:
974+ fd.write(archive + installed_release + "-updates main\n")
975 fd.write(archive + installed_release + " universe\n")
976- fd.write(archive + installed_release + "-updates main\n")
977 fd.write(archive + installed_release + "-updates universe\n")
978+ fd.write(archive + installed_release + " multiverse\n")
979+ fd.write(archive + installed_release + "-updates multiverse\n")
980
981 utils.create_libertine_user_data_dir(self.container_id)
982
983@@ -151,10 +154,16 @@
984 args = shlex.split(command_line)
985 cmd = subprocess.Popen(args).wait()
986
987+ if multiarch and architecture == 'amd64':
988+ if verbosity == 1:
989+ print("Adding i386 multiarch support...")
990+ self.run_in_container("dpkg --add-architecture i386")
991+
992 if verbosity == 1:
993 print("Updating the contents of the container after creation...")
994 self.update_packages(verbosity)
995 self.install_package("libnss-extrausers", verbosity)
996+ self.install_package("software-properties-common", verbosity)
997
998 if verbosity == 1:
999 print("Installing Matchbox as the Xmir window manager...")
1000
1001=== modified file 'python/libertine/Libertine.py'
1002--- python/libertine/Libertine.py 2016-02-10 19:31:10 +0000
1003+++ python/libertine/Libertine.py 2016-03-21 12:56:41 +0000
1004@@ -18,6 +18,7 @@
1005 import contextlib
1006 import json
1007 import libertine.utils
1008+import os
1009
1010
1011 def get_container_type(container_id):
1012@@ -133,10 +134,20 @@
1013 """
1014 if command == 'multiarch':
1015 if args[0] == 'enable':
1016- self.run_in_container("dpkg --add-architecture i386")
1017+ return self.run_in_container("dpkg --add-architecture i386")
1018 else:
1019 self.run_in_container(apt_command_prefix(verbosity) + "purge \".*:i386\"")
1020- self.run_in_container("dpkg --remove-architecture i386")
1021+ return self.run_in_container("dpkg --remove-architecture i386")
1022+
1023+ elif command == 'add-archive':
1024+ if not os.path.exists(os.path.join(self.root_path, 'usr', 'bin', 'add-apt-repository')):
1025+ self.update_packages(verbosity)
1026+ self.install_package("software-properties-common", verbosity)
1027+
1028+ return self.run_in_container("add-apt-repository -y " + args[0])
1029+
1030+ elif command == 'delete-archive':
1031+ return self.run_in_container("add-apt-repository -y -r " + args[0])
1032
1033 def get_container_distro(self, container_id):
1034 """
1035@@ -323,8 +334,8 @@
1036 :rtype: The output of the given command.
1037 """
1038 with ContainerRunning(self.container):
1039- self.container.run_in_container(exec_line)
1040+ return self.container.run_in_container(exec_line)
1041
1042 def configure_command(self, command, *args):
1043 with ContainerRunning(self.container):
1044- self.container.configure_command(command, *args)
1045+ return self.container.configure_command(command, *args)
1046
1047=== modified file 'python/libertine/LxcContainer.py'
1048--- python/libertine/LxcContainer.py 2016-02-09 20:59:51 +0000
1049+++ python/libertine/LxcContainer.py 2016-03-21 12:56:41 +0000
1050@@ -189,6 +189,8 @@
1051 print("Updating the contents of the container after creation...")
1052 self.update_packages(verbosity)
1053
1054+ self.install_package("software-properties-common", verbosity)
1055+
1056 if verbosity == 1:
1057 print("Installing Matchbox as the Xmir window manager...")
1058 self.install_package('matchbox', verbosity=verbosity)
1059
1060=== modified file 'python/libertine/utils.py'
1061--- python/libertine/utils.py 2015-12-23 02:34:16 +0000
1062+++ python/libertine/utils.py 2016-03-21 12:56:41 +0000
1063@@ -125,3 +125,7 @@
1064 def terminate_window_manager(window_manager):
1065 for child in window_manager.children():
1066 child.terminate()
1067+ child.wait()
1068+
1069+ window_manager.terminate()
1070+ window_manager.wait()
1071
1072=== modified file 'tools/libertine-container-manager'
1073--- tools/libertine-container-manager 2016-02-10 19:31:10 +0000
1074+++ tools/libertine-container-manager 2016-03-21 12:56:41 +0000
1075@@ -127,6 +127,64 @@
1076 return container['multiarch']
1077
1078
1079+def update_archive_install_status(container_id, archive_name, new_status):
1080+ container_list = read_container_config_file()
1081+
1082+ for container in container_list['containerList']:
1083+ if container['id'] == container_id:
1084+ for archive in container['extraArchives']:
1085+ if archive['archiveName'] == archive_name:
1086+ archive['archiveStatus'] = new_status
1087+ write_container_config_file(container_list)
1088+ return
1089+
1090+
1091+def add_container_archive(container_id, archive_name):
1092+ container_list = read_container_config_file()
1093+
1094+ for container in container_list['containerList']:
1095+ if container['id'] == container_id:
1096+ archive_obj = {'archiveName': archive_name, 'archiveStatus': 'new'}
1097+
1098+ if 'extraArchives' not in container:
1099+ container['extraArchives'] = [archive_obj]
1100+ else:
1101+ container['extraArchives'].append(archive_obj)
1102+
1103+ write_container_config_file(container_list)
1104+ break
1105+
1106+
1107+def delete_container_archive(container_id, archive_name):
1108+ container_list = read_container_config_file()
1109+
1110+ for container in container_list['containerList']:
1111+ if container['id'] == container_id:
1112+ for archive in container['extraArchives']:
1113+ if archive['archiveName'] == archive_name:
1114+ container['extraArchives'].remove(archive)
1115+ write_container_config_file(container_list)
1116+ return
1117+
1118+ print("%s does not exist." % archive_name)
1119+ sys.exit(1)
1120+
1121+
1122+def archive_exists(container_id, archive_name):
1123+ container_list = read_container_config_file()
1124+
1125+ for container in container_list['containerList']:
1126+ if container['id'] == container_id:
1127+ if 'extraArchives' not in container:
1128+ return False
1129+ else:
1130+ for archive in container['extraArchives']:
1131+ if archive['archiveName'] == archive_name:
1132+ return True
1133+
1134+ return False
1135+
1136+
1137 def add_new_container(id, name, type, distro):
1138 if not os.path.exists(libertine.utils.get_libertine_database_dir_path()):
1139 os.makedirs(libertine.utils.get_libertine_database_dir_path())
1140@@ -379,7 +437,7 @@
1141
1142 container = LibertineContainer(args.id)
1143
1144- container.exec_command(args.command)
1145+ sys.exit(container.exec_command(args.command))
1146
1147
1148 def configure(args):
1149@@ -399,11 +457,36 @@
1150 current_multiarch = get_container_multiarch_support(args.id)
1151 if current_multiarch == multiarch:
1152 print("i386 multiarch support is already %s" % multiarch)
1153- sys.exit(0)
1154+ sys.exit(1)
1155
1156 container.configure_command('multiarch', args.multiarch)
1157 update_container_multiarch_support(args.id, multiarch)
1158
1159+ elif args.add_archive:
1160+ if archive_exists(args.id, args.add_archive):
1161+ print("%s already added in container." % args.add_archive)
1162+ sys.exit(1)
1163+
1164+ add_container_archive(args.id, args.add_archive)
1165+ update_archive_install_status(args.id, args.add_archive, 'installing')
1166+ if container.configure_command('add-archive', args.add_archive) != 0:
1167+ delete_container_archive(args.id, args.add_archive)
1168+ sys.exit(1)
1169+
1170+ update_archive_install_status(args.id, args.add_archive, 'installed')
1171+
1172+
1173+ elif args.delete_archive:
1174+ if not archive_exists(args.id, args.delete_archive):
1175+ print("%s is not added in container." % args.delete_archive)
1176+ sys.exit(1)
1177+
1178+ update_archive_install_status(args.id, args.delete_archive, 'removing')
1179+ if container.configure_command('delete-archive', args.delete_archive) != 0:
1180+ sys.exit(1)
1181+
1182+ delete_container_archive(args.id, args.delete_archive)
1183+
1184
1185 if __name__ == '__main__':
1186 parser = argparse.ArgumentParser(description="Legacy X application support for Unity 8")
1187@@ -543,6 +626,16 @@
1188 help=("Enables or disables i386 multiarch support for amd64 Libertine "
1189 "containers. This option has no effect when the Libertine "
1190 "container is i386."))
1191+ parser_configure.add_argument(
1192+ '-a', '--add-archive',
1193+ metavar='Archive name',
1194+ help=("Adds an archive (PPA) in the specified Libertine container. Needs to be "
1195+ "in the form of \"ppa:user/ppa-name\"."))
1196+ parser_configure.add_argument(
1197+ '-d', '--delete-archive',
1198+ metavar='Archive name',
1199+ help=("Deletes an existing archive (PPA) in the specified Libertine container. "
1200+ "Needs to be in the form of \"ppa:user/ppa-name\"."))
1201 parser_configure.set_defaults(func=configure)
1202
1203 # Actually parse the args

Subscribers

People subscribed via source and target branches