Merge lp:~larryprice/libertine/libertine-service into lp:libertine

Proposed by Larry Price
Status: Merged
Approved by: Christopher Townsend
Approved revision: 372
Merged at revision: 334
Proposed branch: lp:~larryprice/libertine/libertine-service
Merge into: lp:libertine
Diff against target: 2873 lines (+2532/-14)
46 files modified
data/CMakeLists.txt (+1/-1)
data/com.canonical.libertine.ContainerManager.service (+3/-0)
debian/libertine-tools.install (+2/-0)
debian/python3-libertine.install (+1/-0)
python/libertine/ChrootContainer.py (+3/-1)
python/libertine/ContainersConfig.py (+7/-5)
python/libertine/HostInfo.py (+1/-1)
python/libertine/Libertine.py (+1/-1)
python/libertine/LxcContainer.py (+6/-3)
python/libertine/service/apt.py (+77/-0)
python/libertine/service/container.py (+139/-0)
python/libertine/service/manager.py (+115/-0)
python/libertine/service/progress.py (+83/-0)
python/libertine/service/task_dispatcher.py (+114/-0)
python/libertine/service/tasks/__init__.py (+39/-0)
python/libertine/service/tasks/app_info_task.py (+35/-0)
python/libertine/service/tasks/base_task.py (+94/-0)
python/libertine/service/tasks/container_info_task.py (+29/-0)
python/libertine/service/tasks/create_task.py (+72/-0)
python/libertine/service/tasks/destroy_task.py (+42/-0)
python/libertine/service/tasks/install_task.py (+49/-0)
python/libertine/service/tasks/list_apps_task.py (+30/-0)
python/libertine/service/tasks/list_task.py (+25/-0)
python/libertine/service/tasks/remove_task.py (+48/-0)
python/libertine/service/tasks/search_task.py (+27/-0)
python/libertine/service/tasks/update_task.py (+39/-0)
tests/unit/CMakeLists.txt (+1/-0)
tests/unit/pytest.ini (+2/-0)
tests/unit/service/CMakeLists.txt (+12/-0)
tests/unit/service/tasks/CMakeLists.txt (+10/-0)
tests/unit/service/tasks/test_app_info_task.py (+61/-0)
tests/unit/service/tasks/test_container_info_task.py (+44/-0)
tests/unit/service/tasks/test_create_task.py (+234/-0)
tests/unit/service/tasks/test_destroy_task.py (+80/-0)
tests/unit/service/tasks/test_install_task.py (+76/-0)
tests/unit/service/tasks/test_list_apps_task.py (+59/-0)
tests/unit/service/tasks/test_list_task.py (+45/-0)
tests/unit/service/tasks/test_remove_task.py (+76/-0)
tests/unit/service/tasks/test_search_task.py (+43/-0)
tests/unit/service/tasks/test_update_task.py (+80/-0)
tests/unit/service/test_apt.py (+134/-0)
tests/unit/service/test_container.py (+202/-0)
tests/unit/service/test_task_dispatcher.py (+148/-0)
tools/CMakeLists.txt (+1/-1)
tools/libertine-container-manager (+1/-1)
tools/libertined (+141/-0)
To merge this branch: bzr merge lp:~larryprice/libertine/libertine-service
Reviewer Review Type Date Requested Status
Christopher Townsend Approve
Libertine CI Bot continuous-integration Approve
Review via email: mp+309794@code.launchpad.net

Commit message

Initial implementation of a libertine d-bus service.

Description of the change

Initial implementation of a libertine d-bus service.

How do it work? Every command is asynchronous. After making a call to the service, you'll get an object path back. You can listen on that object path for incoming data or listen for the task to finish with finished (success) or error (failure). It currently emits a processing signal every .5 seconds to let things like the Scopes SDK know there is an ongoing process, but we might want to consider removing this if we don't plan to be used by a specific scope.

What's not implemented? All things configure, fix-integrity, exec, merge. There is currently no way to get output directly from the running command (such as install/remove/create information). It would probably be a good idea to create an integration test which demonstrates how to use the service.

To post a comment you must log in.
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
356. By Larry Price

Merge

357. By Larry Price

attempt to get this file in

358. By Larry Price

checkpoint

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
359. By Larry Price

trying to fix test_task_dispatcher

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
360. By Larry Price

Removed appstream (find it at lp:~larryprice/+junk/libertine-service-with-appstream)

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

FAILED: Continuous integration, rev:360
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/202/
Executed test runs:
    FAILURE: https://jenkins.canonical.com/libertine/job/build/451/console
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/453
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/433/console
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/433
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/433/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/433
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/433/artifact/output/*zip*/output.zip
    FAILURE: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/433/console
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/433
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/433/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/433
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/433/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/202/rebuild

review: Needs Fixing (continuous-integration)
361. By Larry Price

Restructure checking task equality for the sake of vivid

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
362. By Larry Price

use a real function instead of a magic function to test fuzzy equality

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:362
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/204/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/453
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/356
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/356
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/356
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/356
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/356
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/356
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/455
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/435
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/435/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/435
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/435/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/435
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/435/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/435
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/435/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/435
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/435/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/435
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/435/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/204/rebuild

review: Approve (continuous-integration)
363. By Larry Price

Merge

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:363
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/205/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/454
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/357
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/357
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/357
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/357
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/357
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/357
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/456
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/436
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/436/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/436
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/436/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/436
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/436/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/436
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/436/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/436
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/436/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/436
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/436/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/205/rebuild

review: Approve (continuous-integration)
364. By Larry Price

Update for status reporting and delayed callback

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:364
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/206/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/455
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/358
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/358
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/358
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/358
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/358
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/358
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/457
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/437
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/437/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/437
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/437/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/437
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/437/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/437
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/437/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/437
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/437/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/437
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/437/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/206/rebuild

review: Approve (continuous-integration)
365. By Larry Price

address minor threading issues

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:365
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/207/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/456
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/359
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/359
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/359
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/359
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/359
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/359
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/458
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/438
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/438/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/438
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/438/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/438
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/438/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/438
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/438/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/438
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/438/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/438
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/438/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/207/rebuild

review: Approve (continuous-integration)
366. By Larry Price

update task retrieval

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:366
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/208/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/457
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/360
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/360
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/360
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/360
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/360
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/360
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/459
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/439
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/439/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/439
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/439/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/439
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/439/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/439
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/439/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/439
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/439/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/439
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/439/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/208/rebuild

review: Approve (continuous-integration)
367. By Larry Price

upstreaming changes from integration branch

368. By Larry Price

fixing all the things

369. By Larry Price

stop double-running tests

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:368
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/209/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/458
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/361
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/361
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/361
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/361
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/361
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/361
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/460
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/440
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/440/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/440
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/440/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/440
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/440/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/440
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/440/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/440
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/440/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/440
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/440/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/209/rebuild

review: Approve (continuous-integration)
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:369
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/210/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/459
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/362
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/362
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/362
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/362
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/362
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/362
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/461
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/441
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/441/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/441
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/441/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/441
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/441/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/441
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/441/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/441
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/441/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/441
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/441/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/210/rebuild

review: Approve (continuous-integration)
370. By Larry Price

upstreaming changes from integration tests

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:370
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/211/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/460
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/363
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/363
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/363
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/363
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/363
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/363
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/462
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/442
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/442/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/442
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/442/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/442
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/442/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/442
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/442/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/442
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/442/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/442
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/442/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/211/rebuild

review: Approve (continuous-integration)
371. By Larry Price

remove unused appstream dep

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

FAILED: Continuous integration, rev:371
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/227/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/480
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/377
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/377
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/377
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/377
    FAILURE: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/377/console
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/377
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/482
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/462
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/462/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/462
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/462/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/462
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/462/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/462
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/462/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/462
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/462/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/462
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/462/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/227/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:371
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/228/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/481
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/378
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/378
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/378
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/378
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/378
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/378
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/483
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/463
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/463/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/463
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/463/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/463
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/463/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/463
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/463/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/463
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/463/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/463
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/463/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/228/rebuild

review: Approve (continuous-integration)
372. By Larry Price

merge

Revision history for this message
Libertine CI Bot (libertine-ci-bot) wrote :

PASSED: Continuous integration, rev:372
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/233/
Executed test runs:
    SUCCESS: https://jenkins.canonical.com/libertine/job/build/489
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=vivid+overlay,testname=default/385
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=default/385
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=amd64,release=zesty,testname=default/385
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=vivid+overlay,testname=default/385
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=xenial+overlay,testname=default/385
    SUCCESS: https://jenkins.canonical.com/libertine/job/test-0-autopkgtest/label=i386,release=zesty,testname=default/385
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-0-fetch/491
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/471
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=vivid+overlay/471/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/471
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=xenial+overlay/471/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/471
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=amd64,release=zesty/471/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/471
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=vivid+overlay/471/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/471
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=xenial+overlay/471/artifact/output/*zip*/output.zip
    SUCCESS: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/471
        deb: https://jenkins.canonical.com/libertine/job/build-2-binpkg/arch=i386,release=zesty/471/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://jenkins.canonical.com/libertine/job/lp-libertine-ci/233/rebuild

review: Approve (continuous-integration)
Revision history for this message
Christopher Townsend (townsend) wrote :

Ok, let's get this in:)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/CMakeLists.txt'
2--- data/CMakeLists.txt 2016-10-07 18:58:30 +0000
3+++ data/CMakeLists.txt 2016-11-16 17:34:14 +0000
4@@ -8,7 +8,7 @@
5 DESTINATION ${CMAKE_INSTALL_DATADIR}/upstart/sessions)
6 install(FILES libertine-lxc-sudo
7 DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/sudoers.d)
8-install(FILES com.canonical.libertine.LxcManager.service
9+install(FILES com.canonical.libertine.LxcManager.service com.canonical.libertine.ContainerManager.service
10 DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/services)
11
12 configure_file("python3-libertine-chroot.click-hook.in"
13
14=== added file 'data/com.canonical.libertine.ContainerManager.service'
15--- data/com.canonical.libertine.ContainerManager.service 1970-01-01 00:00:00 +0000
16+++ data/com.canonical.libertine.ContainerManager.service 2016-11-16 17:34:14 +0000
17@@ -0,0 +1,3 @@
18+[D-BUS Service]
19+Name=com.canonical.libertine.ContainerManager
20+Exec=/usr/bin/libertined --cache-output
21
22=== modified file 'debian/libertine-tools.install'
23--- debian/libertine-tools.install 2016-10-26 20:48:08 +0000
24+++ debian/libertine-tools.install 2016-11-16 17:34:14 +0000
25@@ -1,4 +1,6 @@
26 usr/bin/libertine-container-manager
27 usr/bin/libertine-launch
28+usr/bin/libertined
29+usr/share/dbus-1/services/com.canonical.libertine.ContainerManager.service
30 usr/share/bash-completion/completions/libertine-container-manager
31 usr/share/man
32
33=== modified file 'debian/python3-libertine.install'
34--- debian/python3-libertine.install 2016-10-13 18:28:34 +0000
35+++ debian/python3-libertine.install 2016-11-16 17:34:14 +0000
36@@ -4,4 +4,5 @@
37 usr/lib/python*/*/libertine/Libertine.py
38 usr/lib/python*/*/libertine/__init__.py
39 usr/lib/python*/*/libertine/launcher
40+usr/lib/python*/*/libertine/service
41 usr/lib/python*/*/libertine/utils.py
42
43=== modified file 'python/libertine/ChrootContainer.py'
44--- python/libertine/ChrootContainer.py 2016-11-16 16:38:16 +0000
45+++ python/libertine/ChrootContainer.py 2016-11-16 17:34:14 +0000
46@@ -64,8 +64,10 @@
47 container_root = os.path.join(utils.get_libertine_containers_dir_path(), self.container_id)
48 try:
49 shutil.rmtree(container_root)
50+ return True
51 except Exception as e:
52 print("%s" % e)
53+ return False
54
55 def create_libertine_container(self, password=None, multiarch=False, verbosity=1):
56 # Create the actual chroot
57@@ -147,7 +149,7 @@
58 def update_packages(self, verbosity=1):
59 retcode = super().update_packages(verbosity)
60 self._run_ldconfig(verbosity)
61- return retcode
62+ return retcode == 0
63
64 def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True):
65 returncode = super().install_package(package_name, verbosity, no_dialog, update_cache)
66
67=== modified file 'python/libertine/ContainersConfig.py'
68--- python/libertine/ContainersConfig.py 2016-11-09 18:25:33 +0000
69+++ python/libertine/ContainersConfig.py 2016-11-16 17:34:14 +0000
70@@ -28,6 +28,7 @@
71 if (os.path.exists(container_config_file) and
72 os.path.getsize(container_config_file) != 0):
73 with open(container_config_file, 'r') as fd:
74+ fcntl.flock(fd, fcntl.LOCK_EX)
75 container_list = json.load(fd)
76
77 return container_list
78@@ -41,18 +42,18 @@
79 container_list["_warning"] = "This file is automatically generated by Libertine and should not be manually edited."
80
81 with open(container_config_file, 'w') as fd:
82- fcntl.lockf(fd, fcntl.LOCK_EX)
83+ fcntl.flock(fd, fcntl.LOCK_EX)
84 json.dump(container_list, fd, sort_keys=True, indent=4)
85 fd.write('\n')
86- fcntl.lockf(fd, fcntl.LOCK_UN)
87
88
89 def container_config_hash():
90 checksum = md5()
91 container_config_file = libertine.utils.get_libertine_database_file_path()
92 if (os.path.exists(container_config_file) and os.path.getsize(container_config_file) != 0):
93- with open(container_config_file, "rb") as f:
94- for chunk in iter(lambda: f.read(128 * checksum.block_size), b""):
95+ with open(container_config_file, "rb") as fd:
96+ fcntl.flock(fd, fcntl.LOCK_EX)
97+ for chunk in iter(lambda: fd.read(128 * checksum.block_size), b""):
98 checksum.update(chunk)
99 return checksum.hexdigest()
100
101@@ -90,7 +91,8 @@
102 return container[key]
103
104 def _get_array_object_value_by_key(self, container_id, array_key, object_key, matcher, key):
105- for item in self._get_value_by_key(container_id, array_key):
106+ items = self._get_value_by_key(container_id, array_key) or []
107+ for item in items:
108 if item[object_key] == matcher:
109 return item[key]
110
111
112=== modified file 'python/libertine/HostInfo.py'
113--- python/libertine/HostInfo.py 2016-07-07 20:00:10 +0000
114+++ python/libertine/HostInfo.py 2016-11-16 17:34:14 +0000
115@@ -36,7 +36,7 @@
116
117 return distinfo.get('CODENAME', 'n/a')
118
119- def is_distro_valid(self, distro, force):
120+ def is_distro_valid(self, distro, force=False):
121 if force:
122 return UbuntuDistroInfo().valid(distro)
123
124
125=== modified file 'python/libertine/Libertine.py'
126--- python/libertine/Libertine.py 2016-11-10 16:36:05 +0000
127+++ python/libertine/Libertine.py 2016-11-16 17:34:14 +0000
128@@ -166,7 +166,7 @@
129 :param verbosity: the chattiness of the output on a range from 0 to 2
130 """
131 self.update_apt_cache(verbosity)
132- return self.run_in_container(apt_command_prefix(verbosity) + '--force-yes dist-upgrade')
133+ return self.run_in_container(apt_command_prefix(verbosity) + '--force-yes dist-upgrade') == 0
134
135 def install_package(self, package_name, verbosity=1, no_dialog=False, update_cache=True):
136 """
137
138=== modified file 'python/libertine/LxcContainer.py'
139--- python/libertine/LxcContainer.py 2016-11-10 20:43:40 +0000
140+++ python/libertine/LxcContainer.py 2016-11-16 17:34:14 +0000
141@@ -199,9 +199,12 @@
142 return super().update_packages(verbosity)
143
144 def destroy_libertine_container(self):
145- if self.container.defined:
146- self.container.stop()
147- self.container.destroy()
148+ if not self.container.defined:
149+ return False
150+
151+ self.container.stop()
152+ self.container.destroy()
153+ return True
154
155 def create_libertine_container(self, password=None, multiarch=False, verbosity=1):
156 if password is None:
157
158=== added directory 'python/libertine/service'
159=== added file 'python/libertine/service/__init__.py'
160=== added file 'python/libertine/service/apt.py'
161--- python/libertine/service/apt.py 1970-01-01 00:00:00 +0000
162+++ python/libertine/service/apt.py 2016-11-16 17:34:14 +0000
163@@ -0,0 +1,77 @@
164+# Copyright 2016 Canonical Ltd.
165+#
166+# This program is free software: you can redistribute it and/or modify
167+# it under the terms of the GNU General Public License as published by
168+# the Free Software Foundation; version 3 of the License.
169+#
170+# This program is distributed in the hope that it will be useful,
171+# but WITHOUT ANY WARRANTY; without even the implied warranty of
172+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
173+# GNU General Public License for more details.
174+#
175+# You should have received a copy of the GNU General Public License
176+# along with this program. If not, see <http://www.gnu.org/licenses/>.
177+
178+import apt
179+import re
180+
181+import gi
182+gi.require_version('Libertine', '1')
183+from gi.repository import Libertine
184+
185+from libertine import utils
186+from os import path
187+from threading import Lock
188+
189+
190+class AptCache(object):
191+ """
192+ Class to find app information using apt-cache
193+ """
194+ def __init__(self, container_id):
195+ super(AptCache, self).__init__()
196+ self._container = container_id
197+ self._cache = None
198+ self._lock = Lock()
199+
200+ def search(self, query):
201+ self._load()
202+
203+ pkg_keys = [key for key in self._cache.keys() if re.match(query, key)]
204+ apps = []
205+ for key in pkg_keys:
206+ apps.append(self._app_to_dict(key))
207+ return apps
208+
209+ def app_info(self, app_id):
210+ self._load()
211+ return self._app_to_dict(app_id)
212+
213+ def _app_to_dict(self, app_id):
214+ app_data = {}
215+ if app_id in self._cache.keys():
216+ app = self._cache[app_id]
217+ app_data["name"] = app.name
218+ app_data["id"] = app.name
219+ app_data["package"] = app.name
220+ if len(app.versions) > 0:
221+ app_data["summary"] = app.versions[0].summary
222+ app_data["website"] = app.versions[0].homepage
223+ app_data["description"] = app.versions[0].description
224+ app_data["package"] = app.name
225+
226+ return app_data
227+
228+ def _load(self):
229+ with self._lock:
230+ if self._cache is None:
231+ try:
232+ utils.get_logger().debug("Trying aptcache for container %s" % self._container)
233+ container_path = Libertine.container_path(self._container)
234+ if not container_path or not path.exists(container_path):
235+ raise PermissionError
236+
237+ self._cache = apt.Cache(rootdir=container_path)
238+ except PermissionError:
239+ utils.get_logger().debug("Trying system aptcache")
240+ self._cache = apt.Cache()
241
242=== added file 'python/libertine/service/container.py'
243--- python/libertine/service/container.py 1970-01-01 00:00:00 +0000
244+++ python/libertine/service/container.py 2016-11-16 17:34:14 +0000
245@@ -0,0 +1,139 @@
246+# Copyright 2016 Canonical Ltd.
247+#
248+# This program is free software: you can redistribute it and/or modify
249+# it under the terms of the GNU General Public License as published by
250+# the Free Software Foundation; version 3 of the License.
251+#
252+# This program is distributed in the hope that it will be useful,
253+# but WITHOUT ANY WARRANTY; without even the implied warranty of
254+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
255+# GNU General Public License for more details.
256+#
257+# You should have received a copy of the GNU General Public License
258+# along with this program. If not, see <http://www.gnu.org/licenses/>.
259+
260+from libertine.service.tasks import *
261+from libertine import utils
262+from libertine.service import apt
263+
264+
265+class Container(object):
266+ def __init__(self, container_id, config, lock, connection, callback):
267+ self._id = container_id
268+ self._connection = connection
269+ self._callback = callback
270+ self._config = config
271+ self._lock = lock
272+ self._tasks = []
273+ self._cache = apt.AptCache(self.id)
274+
275+ def _cleanup_task(self, task):
276+ utils.get_logger().debug("cleaning up tasks for container '%s'" % self.id)
277+
278+ if task in self._tasks:
279+ self._tasks.remove(task)
280+
281+ if len(self._tasks) == 0:
282+ self._callback(self)
283+
284+ @property
285+ def id(self):
286+ return self._id
287+
288+ @property
289+ def tasks(self):
290+ return [task.id for task in self._tasks if task.running]
291+
292+ def search(self, query):
293+ utils.get_logger().debug("search container '%s' for package '%s'" % (self.id, query))
294+
295+ task = SearchTask(self.id, self._cache, query, self._connection, self._cleanup_task)
296+ self._tasks.append(task)
297+ task.start()
298+
299+ return task.id
300+
301+ def app_info(self, package_name):
302+ utils.get_logger().debug("get info for package '%s' in container '%s'" % (package_name, self.id))
303+
304+ related_task_ids = [t.id for t in self._tasks if t.package == package_name and t.running]
305+ task = AppInfoTask(self.id, self._cache, package_name, related_task_ids, self._config, self._connection, self._cleanup_task)
306+
307+ self._tasks.append(task)
308+ task.start()
309+ return task.id
310+
311+ def install(self, package_name):
312+ utils.get_logger().debug("Install package '%s' from container '%s'" % (package_name, self.id))
313+
314+ tasks = [t for t in self._tasks if t.matches(package_name, InstallTask) and t.running]
315+ if len(tasks) > 0:
316+ utils.get_logger().debug("Install already in progress for '%s':'%s'" % (package_name, self.id))
317+ return tasks[0].id
318+
319+ task = InstallTask(package_name, self.id, self._config, self._lock, self._connection, self._cleanup_task)
320+ self._tasks.append(task)
321+ task.start()
322+ return task.id
323+
324+ def remove(self, package_name):
325+ utils.get_logger().debug("Remove package '%s' from container '%s'" % (package_name, self.id))
326+
327+ tasks = [t for t in self._tasks if t.matches(package_name, RemoveTask) and t.running]
328+ if len(tasks) > 0:
329+ utils.get_logger().debug("Remove already in progress for '%s':'%s'" % (package_name, self.id))
330+ return tasks[0].id
331+
332+ task = RemoveTask(package_name, self.id, self._config, self._lock, self._connection, self._cleanup_task)
333+ self._tasks.append(task)
334+ task.start()
335+ return task.id
336+
337+ def create(self, container_name, distro, container_type, enable_multiarch):
338+ utils.get_logger().debug("Create container with ID '%s'" % self.id)
339+
340+ tasks = [t for t in self._tasks if t.matches(self.id, CreateTask) and t.running]
341+ if len(tasks) > 0:
342+ utils.get_logger().debug("Create already in progress for '%s'" % self.id)
343+ return tasks[0].id
344+
345+ task = CreateTask(self.id, container_name, distro, container_type, enable_multiarch,
346+ self._config, self._lock, self._connection, self._cleanup_task)
347+ self._tasks.append(task)
348+ task.start()
349+ return task.id
350+
351+ def destroy(self):
352+ utils.get_logger().debug("Destroy container with ID '%s'" % self.id)
353+
354+ tasks = [t for t in self._tasks if t.matches(self.id, DestroyTask) and t.running]
355+ if len(tasks) > 0:
356+ utils.get_logger().debug("Destroy already in progress for '%s'" % self.id)
357+ return tasks[0].id
358+
359+ task = DestroyTask(self.id, self._config, self._lock, self._connection, self._cleanup_task)
360+ self._tasks.append(task)
361+ task.start()
362+ return task.id
363+
364+ def update(self):
365+ utils.get_logger().debug("Update container with ID '%s'" % self.id)
366+
367+ tasks = [t for t in self._tasks if t.matches(self.id, UpdateTask) and t.running]
368+ if len(tasks) > 0:
369+ utils.get_logger().debug("Update already in progress for '%s'" % self.id)
370+ return tasks[0].id
371+
372+ task = UpdateTask(self.id, self._config, self._lock, self._connection, self._cleanup_task)
373+ self._tasks.append(task)
374+ task.start()
375+ return task.id
376+
377+ def list_apps(self):
378+ utils.get_logger().debug("List all apps in container '%s'" % self.id)
379+
380+ task = ListAppsTask(self.id, self._config, self._connection, self._cleanup_task)
381+
382+ self._tasks.append(task)
383+ task.start()
384+ return task.id
385
386=== added file 'python/libertine/service/manager.py'
387--- python/libertine/service/manager.py 1970-01-01 00:00:00 +0000
388+++ python/libertine/service/manager.py 2016-11-16 17:34:14 +0000
389@@ -0,0 +1,115 @@
390+# Copyright 2016 Canonical Ltd.
391+#
392+# This program is free software: you can redistribute it and/or modify
393+# it under the terms of the GNU General Public License as published by
394+# the Free Software Foundation; version 3 of the License.
395+#
396+# This program is distributed in the hope that it will be useful,
397+# but WITHOUT ANY WARRANTY; without even the implied warranty of
398+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
399+# GNU General Public License for more details.
400+#
401+# You should have received a copy of the GNU General Public License
402+# along with this program. If not, see <http://www.gnu.org/licenses/>.
403+
404+import dbus
405+import dbus.service
406+import libertine.service.task_dispatcher
407+from dbus.mainloop.glib import DBusGMainLoop
408+from libertine.service import container
409+from libertine import utils
410+
411+
412+LIBERTINE_MANAGER_NAME = "com.canonical.libertine.Service"
413+LIBERTINE_MANAGER_INTERFACE = LIBERTINE_MANAGER_NAME
414+LIBERTINE_STORE_PATH = "/Manager"
415+
416+
417+class Manager(dbus.service.Object):
418+ def __init__(self):
419+ utils.get_logger().debug("creating service")
420+ DBusGMainLoop(set_as_default=True)
421+ try:
422+ bus_name = dbus.service.BusName(LIBERTINE_MANAGER_NAME,
423+ bus=dbus.SessionBus(),
424+ do_not_queue=True)
425+ except dbus.exceptions.NameExistsException:
426+ utils.get_logger().warning("service is already running")
427+ raise
428+
429+ super().__init__(bus_name, LIBERTINE_STORE_PATH)
430+
431+ self._dispatcher = libertine.service.task_dispatcher.TaskDispatcher(self.connection)
432+
433+ # Information
434+
435+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
436+ in_signature='ss',
437+ out_signature='o')
438+ def search(self, container_id, search_string):
439+ utils.get_logger().debug("search('{}', '{}') called".format(container_id, search_string))
440+ return self._dispatcher.search(container_id, search_string)
441+
442+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
443+ in_signature='ss',
444+ out_signature='o')
445+ def app_info(self, container_id, app_id):
446+ utils.get_logger().debug("app_info('{}', '{}') called".format(container_id, app_id))
447+ return self._dispatcher.app_info(container_id, app_id)
448+
449+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
450+ in_signature='s',
451+ out_signature='o')
452+ def container_info(self, container_id):
453+ utils.get_logger().debug("container_info('{}')".format(container_id))
454+ return self._dispatcher.container_info(container_id)
455+
456+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
457+ in_signature='s',
458+ out_signature='o')
459+ def list_apps(self, container_id):
460+ utils.get_logger().debug("list_apps('{}')".format(container_id))
461+ return self._dispatcher.list_apps(container_id)
462+
463+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
464+ out_signature='o')
465+ def list(self):
466+ utils.get_logger().debug("list()")
467+ return self._dispatcher.list()
468+
469+ # Operations
470+
471+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
472+ in_signature='ssssb',
473+ out_signature='o')
474+ def create(self, container_id, container_name='', distro='', container_type='', enable_multiarch=False):
475+ utils.get_logger().debug("create('{}', '{}', '{}', '{}', '{}')".format(container_id, container_name, distro, container_type, enable_multiarch))
476+ return self._dispatcher.create(container_id, container_name, distro, container_type, enable_multiarch)
477+
478+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
479+ in_signature='s',
480+ out_signature='o')
481+ def destroy(self, container_id):
482+ utils.get_logger().debug("destroy('{}')".format(container_id))
483+ return self._dispatcher.destroy(container_id)
484+
485+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
486+ in_signature='s',
487+ out_signature='o')
488+ def update(self, container_id):
489+ utils.get_logger().debug("update('{}')".format(container_id))
490+ return self._dispatcher.update(container_id)
491+
492+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
493+ in_signature='ss',
494+ out_signature='o')
495+ def install(self, container_id, package_name):
496+ utils.get_logger().debug("install('%s', '%s')" % (container_id, package_name))
497+ return self._dispatcher.install(container_id, package_name)
498+
499+ @dbus.service.method(LIBERTINE_MANAGER_INTERFACE,
500+ in_signature='ss',
501+ out_signature='o')
502+ def remove(self, container_id, package_name):
503+ utils.get_logger().debug("remove('%s', '%s')" % (container_id, package_name))
504+ return self._dispatcher.remove(container_id, package_name)
505
506=== added file 'python/libertine/service/progress.py'
507--- python/libertine/service/progress.py 1970-01-01 00:00:00 +0000
508+++ python/libertine/service/progress.py 2016-11-16 17:34:14 +0000
509@@ -0,0 +1,83 @@
510+# Copyright 2016 Canonical Ltd.
511+#
512+# This program is free software: you can redistribute it and/or modify
513+# it under the terms of the GNU General Public License as published by
514+# the Free Software Foundation; version 3 of the License.
515+#
516+# This program is distributed in the hope that it will be useful,
517+# but WITHOUT ANY WARRANTY; without even the implied warranty of
518+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
519+# GNU General Public License for more details.
520+#
521+# You should have received a copy of the GNU General Public License
522+# along with this program. If not, see <http://www.gnu.org/licenses/>.
523+
524+import dbus.service
525+import threading
526+from libertine import utils
527+from time import time
528+
529+DOWNLOAD_INTERFACE = "com.canonical.applications.Download"
530+PROGRESS_INTERFACE = "com.canonical.libertine.Progress"
531+
532+class Progress(dbus.service.Object):
533+ def __init__(self, connection):
534+ utils.get_logger().debug("creating a Progress object")
535+ self._finished = False
536+ self._result = []
537+ self._error = ''
538+ dbus.service.Object.__init__(self, conn=connection, object_path=("/Progress/%s" % hex(int(time()*10000000))[2:]))
539+
540+ self.emit_processing()
541+
542+ @property
543+ def id(self):
544+ return self._object_path
545+
546+ def emit_processing(self):
547+ if not self.done:
548+ self.processing(self.id)
549+ threading.Timer(0.5, self.emit_processing).start()
550+
551+ @property
552+ def done(self):
553+ return self._finished
554+
555+ @dbus.service.signal(DOWNLOAD_INTERFACE)
556+ def processing(self, path):
557+ utils.get_logger().debug("emit processing('%s')" % path)
558+
559+ @dbus.service.signal(DOWNLOAD_INTERFACE)
560+ def finished(self, path):
561+ utils.get_logger().debug("emit finished('%s')" % path)
562+ self._finished = True
563+
564+ @dbus.service.signal(DOWNLOAD_INTERFACE)
565+ def progress(self, received, total):
566+ utils.get_logger().debug("emit progress(%d, %d)" % (received, total))
567+
568+ @dbus.service.signal(DOWNLOAD_INTERFACE)
569+ def error(self, message):
570+ utils.get_logger().error("emit error(%s)" % message)
571+ self._error = message
572+ self._finished = True
573+
574+ @dbus.service.signal(PROGRESS_INTERFACE)
575+ def data(self, message):
576+ utils.get_logger().debug("emit data(%s)" % message)
577+ self._result.append(message)
578+
579+ @dbus.service.method(PROGRESS_INTERFACE, out_signature='b')
580+ def running(self):
581+ utils.get_logger().debug("running()")
582+ return not self.done
583+
584+ @dbus.service.method(PROGRESS_INTERFACE, out_signature='s')
585+ def result(self):
586+ utils.get_logger().debug("result()")
587+ return "\n".join(self._result)
588+
589+ @dbus.service.method(PROGRESS_INTERFACE, out_signature='s')
590+ def last_error(self):
591+ utils.get_logger().debug("last_error()")
592+ return self._error
593
594=== added file 'python/libertine/service/task_dispatcher.py'
595--- python/libertine/service/task_dispatcher.py 1970-01-01 00:00:00 +0000
596+++ python/libertine/service/task_dispatcher.py 2016-11-16 17:34:14 +0000
597@@ -0,0 +1,114 @@
598+# Copyright 2016 Canonical Ltd.
599+#
600+# This program is free software: you can redistribute it and/or modify
601+# it under the terms of the GNU General Public License as published by
602+# the Free Software Foundation; version 3 of the License.
603+#
604+# This program is distributed in the hope that it will be useful,
605+# but WITHOUT ANY WARRANTY; without even the implied warranty of
606+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
607+# GNU General Public License for more details.
608+#
609+# You should have received a copy of the GNU General Public License
610+# along with this program. If not, see <http://www.gnu.org/licenses/>.
611+
612+
613+import libertine.ContainersConfig
614+from libertine.service.container import Container
615+from libertine.service.tasks import *
616+from threading import Lock
617+from libertine import utils
618+
619+
620+class TaskDispatcher(object):
621+ def __init__(self, connection):
622+ self._connection = connection
623+ self._config = libertine.ContainersConfig.ContainersConfig()
624+ self._lock = Lock()
625+ self._containerless_tasks = []
626+ self._tasks = []
627+ self._containers = []
628+
629+ def _cleanup_task(self, task):
630+ utils.get_logger().debug("cleaning up containerless task '%s'" % task.id)
631+ if task in self._tasks:
632+ self._tasks.remove(task)
633+
634+ def _cleanup_container(self, container):
635+ utils.get_logger().debug("cleaning up container '%s'" % container)
636+ if container in self._containers:
637+ self._containers.remove(container)
638+
639+ def _find_or_create_container(self, container_id):
640+ utils.get_logger().debug("finding or creating container '%s'" % container_id)
641+ container = self._find_container(container_id)
642+ if container is not None:
643+ utils.get_logger().debug("using existing container '%s'" % container_id)
644+ return container
645+ container = Container(container_id, self._config, self._lock, self._connection, self._cleanup_container)
646+ self._containers.append(container)
647+
648+ return container
649+
650+ def _find_container(self, container_id):
651+ containers = [c for c in self._containers if c.id == container_id]
652+ if len(containers) > 0:
653+ return containers[0]
654+
655+ # Tasks (usually) run within a container
656+
657+ def search(self, container_id, query):
658+ utils.get_logger().debug("dispatching search in container '%s' for package '%s'" % (container_id, query))
659+ return self._find_or_create_container(container_id).search(query)
660+
661+ def app_info(self, container_id, app_id):
662+ utils.get_logger().debug("dispatching app_info in container '%s' for package '%s'" % (container_id, app_id))
663+ return self._find_or_create_container(container_id).app_info(app_id)
664+
665+ def install(self, container_id, package_name):
666+ utils.get_logger().debug("dispatching install of package '%s' from container '%s'" % (package_name, container_id))
667+ return self._find_or_create_container(container_id).install(package_name)
668+
669+ def remove(self, container_id, package_name):
670+ utils.get_logger().debug("dispatching remove of package '%s' from container '%s'" % (package_name, container_id))
671+ return self._find_or_create_container(container_id).remove(package_name)
672+
673+ def create(self, container_id, container_name, distro, container_type, enable_multiarch):
674+ utils.get_logger().debug("dispatching create of container '%s'" % container_id)
675+ return self._find_or_create_container(container_id).create(container_name, distro, container_type, enable_multiarch)
676+
677+ def destroy(self, container_id):
678+ utils.get_logger().debug("dispatching destroy container '%s'" % container_id)
679+ return self._find_or_create_container(container_id).destroy()
680+
681+ def update(self, container_id):
682+ utils.get_logger().debug("dispatching update container '%s'" % container_id)
683+ return self._find_or_create_container(container_id).update()
684+
685+ def list_apps(self, container_id):
686+ utils.get_logger().debug("dispatching list all apps in container '%s'" % container_id)
687+ return self._find_or_create_container(container_id).list_apps()
688+
689+ # Containerless Tasks
690+
691+ def container_info(self, container_id):
692+ utils.get_logger().debug("dispatching get info for container '%s'" % container_id)
693+
694+ related_task_ids = []
695+ container = self._find_container(container_id)
696+ if container is not None:
697+ related_task_ids = container.tasks
698+ task = ContainerInfoTask(container_id, related_task_ids, self._config, self._connection, self._cleanup_task)
699+ self._tasks.append(task)
700+ task.start()
701+
702+ return task.id
703+
704+ def list(self):
705+ utils.get_logger().debug("dispatching list all containers")
706+
707+ task = ListTask(self._connection, self._cleanup_task)
708+ self._tasks.append(task)
709+ task.start()
710+
711+ return task.id
712
713=== added directory 'python/libertine/service/tasks'
714=== added file 'python/libertine/service/tasks/__init__.py'
715--- python/libertine/service/tasks/__init__.py 1970-01-01 00:00:00 +0000
716+++ python/libertine/service/tasks/__init__.py 2016-11-16 17:34:14 +0000
717@@ -0,0 +1,39 @@
718+# Copyright 2016 Canonical Ltd.
719+#
720+# This program is free software: you can redistribute it and/or modify it
721+# under the terms of the GNU General Public License version 3, as published
722+# by the Free Software Foundation.
723+#
724+# This program is distributed in the hope that it will be useful, but
725+# WITHOUT ANY WARRANTY; without even the implied warranties of
726+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
727+# PURPOSE. See the GNU General Public License for more details.
728+#
729+# You should have received a copy of the GNU General Public License along
730+# with this program. If not, see <http://www.gnu.org/licenses/>.
731+
732+from .base_task import BaseTask
733+from .app_info_task import AppInfoTask
734+from .container_info_task import ContainerInfoTask
735+from .create_task import CreateTask
736+from .destroy_task import DestroyTask
737+from .install_task import InstallTask
738+from .remove_task import RemoveTask
739+from .search_task import SearchTask
740+from .update_task import UpdateTask
741+from .list_task import ListTask
742+from .list_apps_task import ListAppsTask
743+
744+__all__ = [
745+ 'AppInfoTask',
746+ 'BaseTask',
747+ 'ContainerInfoTask',
748+ 'CreateTask',
749+ 'DestroyTask',
750+ 'InstallTask',
751+ 'RemoveTask',
752+ 'SearchTask',
753+ 'UpdateTask',
754+ 'ListTask',
755+ 'ListAppsTask'
756+ ]
757
758=== added file 'python/libertine/service/tasks/app_info_task.py'
759--- python/libertine/service/tasks/app_info_task.py 1970-01-01 00:00:00 +0000
760+++ python/libertine/service/tasks/app_info_task.py 2016-11-16 17:34:14 +0000
761@@ -0,0 +1,35 @@
762+# Copyright 2016 Canonical Ltd.
763+#
764+# This program is free software: you can redistribute it and/or modify
765+# it under the terms of the GNU General Public License as published by
766+# the Free Software Foundation; version 3 of the License.
767+#
768+# This program is distributed in the hope that it will be useful,
769+# but WITHOUT ANY WARRANTY; without even the implied warranty of
770+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
771+# GNU General Public License for more details.
772+#
773+# You should have received a copy of the GNU General Public License
774+# along with this program. If not, see <http://www.gnu.org/licenses/>.
775+
776+
777+from .base_task import BaseTask
778+from libertine import utils
779+
780+
781+class AppInfoTask(BaseTask):
782+ def __init__(self, container_id, cache, app_id, tasks, config, connection, callback):
783+ super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)
784+ self._cache = cache
785+ self._app_id = app_id
786+ self._tasks = tasks
787+
788+ def _run(self):
789+ app = self._cache.app_info(self._app_id)
790+ if app == {}:
791+ self._progress.error("Could not find app info for '%s' in container '%s'" % (self._app_id, self._container))
792+ return
793+
794+ app['status'] = self._config.get_package_install_status(self._container, app['package']) or ''
795+ app['task_ids'] = self._tasks
796+ self._progress.data(str(app))
797
798=== added file 'python/libertine/service/tasks/base_task.py'
799--- python/libertine/service/tasks/base_task.py 1970-01-01 00:00:00 +0000
800+++ python/libertine/service/tasks/base_task.py 2016-11-16 17:34:14 +0000
801@@ -0,0 +1,94 @@
802+# Copyright 2016 Canonical Ltd.
803+#
804+# This program is free software: you can redistribute it and/or modify
805+# it under the terms of the GNU General Public License as published by
806+# the Free Software Foundation; version 3 of the License.
807+#
808+# This program is distributed in the hope that it will be useful,
809+# but WITHOUT ANY WARRANTY; without even the implied warranty of
810+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
811+# GNU General Public License for more details.
812+#
813+# You should have received a copy of the GNU General Public License
814+# along with this program. If not, see <http://www.gnu.org/licenses/>.
815+
816+import libertine.service.progress
817+import threading
818+from abc import ABCMeta, abstractmethod
819+from libertine import utils
820+
821+
822+class BaseTask(metaclass=ABCMeta):
823+ """
824+ Abstract class for performing long-running, synchronous operations on a
825+ Libertine container. Child classes must implement _run, which will execute
826+ in a separate thread. Override _before to implement pre-execution actions
827+ without locking; if _before returns False, _run will not be executed.
828+ """
829+ def __init__(self, lock, container_id, config, connection, callback):
830+ self._lock = lock
831+ self._container = container_id
832+ self._config = config
833+ self._callback = callback
834+ self._connection = connection
835+ self._progress = None
836+ self._instant_callback = False
837+
838+ def matches(self, container, klass):
839+ return self._container == container and self.__class__ == klass
840+
841+ @property
842+ def id(self):
843+ if self._progress is not None:
844+ return self._progress.id
845+ else:
846+ return None
847+
848+ @property
849+ def container(self):
850+ return self._container or ''
851+
852+ @property
853+ def package(self):
854+ return ''
855+
856+ @property
857+ def running(self):
858+ return not self._progress.done
859+
860+ def _delayed_callback(self):
861+ if self._instant_callback:
862+ self._callback(self)
863+ else:
864+ threading.Timer(30, lambda: self._callback(self)).start()
865+
866+ def start(self):
867+ self._progress = libertine.service.progress.Progress(self._connection)
868+ thread = threading.Thread(target=self.run)
869+ thread.start()
870+ return thread
871+
872+ def run(self):
873+ if not self._before():
874+ self._progress.finished(self.container)
875+ self._delayed_callback()
876+ return
877+
878+ if self._lock is not None:
879+ with self._lock:
880+ self._run()
881+ else:
882+ self._run()
883+
884+ if self.running:
885+ self._progress.finished(self.container)
886+ utils.refresh_libertine_scope()
887+
888+ self._delayed_callback()
889+
890+ @abstractmethod
891+ def _run(self):
892+ pass
893+
894+ def _before(self):
895+ return True
896
897=== added file 'python/libertine/service/tasks/container_info_task.py'
898--- python/libertine/service/tasks/container_info_task.py 1970-01-01 00:00:00 +0000
899+++ python/libertine/service/tasks/container_info_task.py 2016-11-16 17:34:14 +0000
900@@ -0,0 +1,29 @@
901+# Copyright 2016 Canonical Ltd.
902+#
903+# This program is free software: you can redistribute it and/or modify
904+# it under the terms of the GNU General Public License as published by
905+# the Free Software Foundation; version 3 of the License.
906+#
907+# This program is distributed in the hope that it will be useful,
908+# but WITHOUT ANY WARRANTY; without even the implied warranty of
909+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
910+# GNU General Public License for more details.
911+#
912+# You should have received a copy of the GNU General Public License
913+# along with this program. If not, see <http://www.gnu.org/licenses/>.
914+
915+
916+from .base_task import BaseTask
917+from libertine import utils
918+
919+
920+class ContainerInfoTask(BaseTask):
921+ def __init__(self, container_id, tasks, config, connection, callback):
922+ super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)
923+ self._tasks = tasks
924+
925+ def _run(self):
926+ container = {'id': str(self._container)}
927+ container['status'] = self._config._get_value_by_key(self._container, 'installStatus') or ''
928+ container['task_ids'] = self._tasks
929+ self._progress.data(str(container))
930
931=== added file 'python/libertine/service/tasks/create_task.py'
932--- python/libertine/service/tasks/create_task.py 1970-01-01 00:00:00 +0000
933+++ python/libertine/service/tasks/create_task.py 2016-11-16 17:34:14 +0000
934@@ -0,0 +1,72 @@
935+# Copyright 2016 Canonical Ltd.
936+#
937+# This program is free software: you can redistribute it and/or modify
938+# it under the terms of the GNU General Public License as published by
939+# the Free Software Foundation; version 3 of the License.
940+#
941+# This program is distributed in the hope that it will be useful,
942+# but WITHOUT ANY WARRANTY; without even the implied warranty of
943+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
944+# GNU General Public License for more details.
945+#
946+# You should have received a copy of the GNU General Public License
947+# along with this program. If not, see <http://www.gnu.org/licenses/>.
948+
949+
950+from .base_task import BaseTask
951+from libertine import LibertineContainer, utils
952+from libertine.HostInfo import HostInfo
953+
954+
955+class CreateTask(BaseTask):
956+ def __init__(self, container_id, container_name, distro, container_type, enable_multiarch, config, lock, connection, callback):
957+ super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)
958+ self._name = container_name
959+ self._distro = distro
960+ self._type = container_type
961+ self._multiarch = enable_multiarch
962+
963+ def _run(self):
964+ utils.get_logger().debug("Creating container '%s'" % self._container)
965+
966+ try:
967+ container = LibertineContainer(self._container, self._config)
968+
969+ if not container.create_libertine_container(password='', multiarch=self._multiarch):
970+ self._config.delete_container(self._container)
971+ self._progress.error("Creating container '%s' failed" % self._container)
972+ else:
973+ self._config.update_container_install_status(self._container, "ready")
974+ except RuntimeError as e:
975+ self._progress.error(str(e))
976+ self._config.delete_container(self._container)
977+
978+ def _before(self):
979+ utils.get_logger().debug("CreateTask::_before")
980+ if self._config.container_exists(self._container):
981+ self._progress.error("Container '%s' already exists" % self._container)
982+ return False
983+
984+ info = HostInfo()
985+ if not self._distro:
986+ self._distro = info.get_host_distro_release()
987+ elif not info.is_distro_valid(self._distro):
988+ self._progress.error("Invalid distro '%s'." % self._distro)
989+ return False
990+
991+ if not self._type:
992+ self._type = info.select_container_type_by_kernel()
993+ elif self._type == 'lxc' and not info.has_lxc_support():
994+ self._progress.error("System kernel does not support lxc type containers. Please either use chroot or leave empty.")
995+ return False
996+
997+ if not self._name:
998+ self._name = "Ubuntu \'" + info.get_distro_codename(self._distro) + "\'"
999+
1000+ self._config.add_new_container(self._container, self._name, self._type, self._distro)
1001+
1002+ if self._multiarch:
1003+ self._config.update_container_multiarch_support(self._container, 'enabled')
1004+
1005+ self._config.update_container_install_status(self._container, 'installing')
1006+ return True
1007
1008=== added file 'python/libertine/service/tasks/destroy_task.py'
1009--- python/libertine/service/tasks/destroy_task.py 1970-01-01 00:00:00 +0000
1010+++ python/libertine/service/tasks/destroy_task.py 2016-11-16 17:34:14 +0000
1011@@ -0,0 +1,42 @@
1012+# Copyright 2016 Canonical Ltd.
1013+#
1014+# This program is free software: you can redistribute it and/or modify
1015+# it under the terms of the GNU General Public License as published by
1016+# the Free Software Foundation; version 3 of the License.
1017+#
1018+# This program is distributed in the hope that it will be useful,
1019+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1020+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1021+# GNU General Public License for more details.
1022+#
1023+# You should have received a copy of the GNU General Public License
1024+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1025+
1026+
1027+from .base_task import BaseTask
1028+from libertine import LibertineContainer, utils
1029+
1030+
1031+class DestroyTask(BaseTask):
1032+ def __init__(self, container_id, config, lock, connection, callback):
1033+ super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)
1034+
1035+ def _run(self):
1036+ utils.get_logger().debug("Destroying container '%s'" % self._container)
1037+
1038+ container = LibertineContainer(self._container, self._config)
1039+ if not container.destroy_libertine_container():
1040+ self._progress.error("Destroying container '%s' failed" % self._container)
1041+ self._config.update_container_install_status(self._container, "ready")
1042+ return
1043+
1044+ self._config.delete_container(self._container)
1045+
1046+ def _before(self):
1047+ utils.get_logger().debug("CreateTask::_before")
1048+ if self._config._get_value_by_key(self._container, 'installStatus') != 'ready':
1049+ self._progress.error("Container '%s' does not exist" % self._container)
1050+ return False
1051+
1052+ self._config.update_container_install_status(self._container, 'removing')
1053+ return True
1054
1055=== added file 'python/libertine/service/tasks/install_task.py'
1056--- python/libertine/service/tasks/install_task.py 1970-01-01 00:00:00 +0000
1057+++ python/libertine/service/tasks/install_task.py 2016-11-16 17:34:14 +0000
1058@@ -0,0 +1,49 @@
1059+# Copyright 2016 Canonical Ltd.
1060+#
1061+# This program is free software: you can redistribute it and/or modify
1062+# it under the terms of the GNU General Public License as published by
1063+# the Free Software Foundation; version 3 of the License.
1064+#
1065+# This program is distributed in the hope that it will be useful,
1066+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1067+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1068+# GNU General Public License for more details.
1069+#
1070+# You should have received a copy of the GNU General Public License
1071+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1072+
1073+
1074+from .base_task import BaseTask
1075+from libertine import LibertineContainer, utils
1076+
1077+
1078+class InstallTask(BaseTask):
1079+ def __init__(self, package_name, container_id, config, lock, connection, callback):
1080+ super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)
1081+ self._package = package_name
1082+
1083+ def matches(self, package, klass):
1084+ return self._package == package and self.__class__ == klass
1085+
1086+ @property
1087+ def package(self):
1088+ return self._package
1089+
1090+ def _run(self):
1091+ utils.get_logger().debug("Installing package '%s'" % self._package)
1092+ container = LibertineContainer(self._container, self._config)
1093+ if container.install_package(self._package):
1094+ self._config.update_package_install_status(self._container, self._package, "installed")
1095+ else:
1096+ self._config.delete_package(self._container, self._package)
1097+ self._progress.error("Package installation failed for '%s'" % self._package)
1098+
1099+ def _before(self):
1100+ utils.get_logger().debug("InstallTask::_before")
1101+ if self._config.package_exists(self._container, self._package):
1102+ self._progress.error("Package '%s' already exists, skipping install" % self._package)
1103+ return False
1104+ else:
1105+ self._config.add_new_package(self._container, self._package)
1106+ self._config.update_package_install_status(self._container, self._package, "installing")
1107+ return True
1108
1109=== added file 'python/libertine/service/tasks/list_apps_task.py'
1110--- python/libertine/service/tasks/list_apps_task.py 1970-01-01 00:00:00 +0000
1111+++ python/libertine/service/tasks/list_apps_task.py 2016-11-16 17:34:14 +0000
1112@@ -0,0 +1,30 @@
1113+# Copyright 2016 Canonical Ltd.
1114+#
1115+# This program is free software: you can redistribute it and/or modify
1116+# it under the terms of the GNU General Public License as published by
1117+# the Free Software Foundation; version 3 of the License.
1118+#
1119+# This program is distributed in the hope that it will be useful,
1120+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1121+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1122+# GNU General Public License for more details.
1123+#
1124+# You should have received a copy of the GNU General Public License
1125+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1126+
1127+
1128+from .base_task import BaseTask
1129+from libertine import LibertineContainer, utils
1130+
1131+
1132+class ListAppsTask(BaseTask):
1133+ def __init__(self, container_id, config, connection, callback):
1134+ super().__init__(lock=None, container_id=container_id, config=config, connection=connection, callback=callback)
1135+
1136+ def _run(self):
1137+ utils.get_logger().debug("Listing apps in container '%s'" % self._container)
1138+ if not self._config.container_exists(self._container):
1139+ self._progress.error("Container '%s' does not exist, skipping list" % self._container)
1140+ else:
1141+ container = LibertineContainer(self._container, self._config)
1142+ self._progress.data(str(container.list_app_launchers(use_json=True)))
1143
1144=== added file 'python/libertine/service/tasks/list_task.py'
1145--- python/libertine/service/tasks/list_task.py 1970-01-01 00:00:00 +0000
1146+++ python/libertine/service/tasks/list_task.py 2016-11-16 17:34:14 +0000
1147@@ -0,0 +1,25 @@
1148+# Copyright 2016 Canonical Ltd.
1149+#
1150+# This program is free software: you can redistribute it and/or modify
1151+# it under the terms of the GNU General Public License as published by
1152+# the Free Software Foundation; version 3 of the License.
1153+#
1154+# This program is distributed in the hope that it will be useful,
1155+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1156+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1157+# GNU General Public License for more details.
1158+#
1159+# You should have received a copy of the GNU General Public License
1160+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1161+
1162+
1163+from .base_task import BaseTask
1164+from libertine import utils
1165+
1166+
1167+class ListTask(BaseTask):
1168+ def __init__(self, connection, callback):
1169+ super().__init__(lock=None, container_id=None, config=None, connection=connection, callback=callback)
1170+
1171+ def _run(self):
1172+ self._progress.data(str(utils.Libertine.list_containers()))
1173
1174=== added file 'python/libertine/service/tasks/remove_task.py'
1175--- python/libertine/service/tasks/remove_task.py 1970-01-01 00:00:00 +0000
1176+++ python/libertine/service/tasks/remove_task.py 2016-11-16 17:34:14 +0000
1177@@ -0,0 +1,48 @@
1178+# Copyright 2016 Canonical Ltd.
1179+#
1180+# This program is free software: you can redistribute it and/or modify
1181+# it under the terms of the GNU General Public License as published by
1182+# the Free Software Foundation; version 3 of the License.
1183+#
1184+# This program is distributed in the hope that it will be useful,
1185+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1186+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1187+# GNU General Public License for more details.
1188+#
1189+# You should have received a copy of the GNU General Public License
1190+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1191+
1192+
1193+from .base_task import BaseTask
1194+from libertine import LibertineContainer, utils
1195+
1196+
1197+class RemoveTask(BaseTask):
1198+ def __init__(self, package_name, container_id, config, lock, connection, callback):
1199+ super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)
1200+ self._package = package_name
1201+
1202+ def matches(self, package, klass):
1203+ return self._package == package and self.__class__ == klass
1204+
1205+ @property
1206+ def package(self):
1207+ return self._package
1208+
1209+ def _run(self):
1210+ utils.get_logger().debug("Removing package '%s'" % self._package)
1211+ container = LibertineContainer(self._container, self._config)
1212+ if container.remove_package(self._package):
1213+ self._config.delete_package(self._container, self._package)
1214+ else:
1215+ self._config.update_package_install_status(self._container, self._package, 'installed')
1216+ self._progress.error("Package removal failed for '%s'" % self._package)
1217+
1218+ def _before(self):
1219+ utils.get_logger().debug("RemoveTask::_before")
1220+ if self._config.get_package_install_status(self._container, self._package) == 'installed':
1221+ self._config.update_package_install_status(self._container, self._package, "removing")
1222+ return True
1223+ else:
1224+ self._progress.error("Package '%s' not installed, skipping remove" % self._package)
1225+ return False
1226
1227=== added file 'python/libertine/service/tasks/search_task.py'
1228--- python/libertine/service/tasks/search_task.py 1970-01-01 00:00:00 +0000
1229+++ python/libertine/service/tasks/search_task.py 2016-11-16 17:34:14 +0000
1230@@ -0,0 +1,27 @@
1231+# Copyright 2016 Canonical Ltd.
1232+#
1233+# This program is free software: you can redistribute it and/or modify
1234+# it under the terms of the GNU General Public License as published by
1235+# the Free Software Foundation; version 3 of the License.
1236+#
1237+# This program is distributed in the hope that it will be useful,
1238+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1239+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1240+# GNU General Public License for more details.
1241+#
1242+# You should have received a copy of the GNU General Public License
1243+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1244+
1245+
1246+from .base_task import BaseTask
1247+from libertine import utils
1248+
1249+
1250+class SearchTask(BaseTask):
1251+ def __init__(self, container_id, cache, query, connection, callback):
1252+ super().__init__(lock=None, container_id=container_id, config=None, connection=connection, callback=callback)
1253+ self._cache = cache
1254+ self._query = query
1255+
1256+ def _run(self):
1257+ self._progress.data(str(self._cache.search(self._query)))
1258
1259=== added file 'python/libertine/service/tasks/update_task.py'
1260--- python/libertine/service/tasks/update_task.py 1970-01-01 00:00:00 +0000
1261+++ python/libertine/service/tasks/update_task.py 2016-11-16 17:34:14 +0000
1262@@ -0,0 +1,39 @@
1263+# Copyright 2016 Canonical Ltd.
1264+#
1265+# This program is free software: you can redistribute it and/or modify
1266+# it under the terms of the GNU General Public License as published by
1267+# the Free Software Foundation; version 3 of the License.
1268+#
1269+# This program is distributed in the hope that it will be useful,
1270+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1271+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1272+# GNU General Public License for more details.
1273+#
1274+# You should have received a copy of the GNU General Public License
1275+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1276+
1277+
1278+from .base_task import BaseTask
1279+from libertine import LibertineContainer, utils
1280+
1281+
1282+class UpdateTask(BaseTask):
1283+ def __init__(self, container_id, config, lock, connection, callback):
1284+ super().__init__(lock=lock, container_id=container_id, config=config, connection=connection, callback=callback)
1285+
1286+ def _run(self):
1287+ utils.get_logger().debug("Updating container '%s'" % self._container)
1288+ container = LibertineContainer(self._container, self._config)
1289+ self._config.update_container_install_status(self._container, "updating")
1290+ if not container.update_libertine_container():
1291+ self._progress.error("Failed to update container '%s'" % self._container)
1292+
1293+ self._config.update_container_install_status(self._container, "ready")
1294+
1295+ def _before(self):
1296+ utils.get_logger().debug("UpdateTask::_before")
1297+ if not self._config.container_exists(self._container):
1298+ self._progress.error("Container '%s' does not exist, skipping update" % self._container)
1299+ return False
1300+ else:
1301+ return True
1302
1303=== modified file 'tests/unit/CMakeLists.txt'
1304--- tests/unit/CMakeLists.txt 2016-09-27 00:55:28 +0000
1305+++ tests/unit/CMakeLists.txt 2016-11-16 17:34:14 +0000
1306@@ -30,3 +30,4 @@
1307 ENVIRONMENT
1308 "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};CMAKE_BINARY_DIR=${CMAKE_BINARY_DIR};PYTHONPATH=${CMAKE_SOURCE_DIR}/python;CMAKE_SOURCE_DIR=${CMAKE_SOURCE_DIR}")
1309
1310+add_subdirectory(service)
1311
1312=== added file 'tests/unit/pytest.ini'
1313--- tests/unit/pytest.ini 1970-01-01 00:00:00 +0000
1314+++ tests/unit/pytest.ini 2016-11-16 17:34:14 +0000
1315@@ -0,0 +1,2 @@
1316+[pytest]
1317+norecursedirs = service
1318
1319=== added directory 'tests/unit/service'
1320=== added file 'tests/unit/service/CMakeLists.txt'
1321--- tests/unit/service/CMakeLists.txt 1970-01-01 00:00:00 +0000
1322+++ tests/unit/service/CMakeLists.txt 2016-11-16 17:34:14 +0000
1323@@ -0,0 +1,12 @@
1324+function(create_service_unit_test test_name)
1325+ add_test(${test_name} /usr/bin/python3 -m testtools.run ${test_name})
1326+ set_tests_properties(${test_name}
1327+ PROPERTIES ENVIRONMENT
1328+ "GI_TYPELIB_PATH=${CMAKE_BINARY_DIR}/liblibertine;LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/liblibertine:${LD_LIBRARY_PATH};PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_SOURCE_DIR}/python;LIBERTINE_DATA_DIR=${CMAKE_CURRENT_SOURCE_DIR}")
1329+endfunction(create_service_unit_test)
1330+
1331+create_service_unit_test(test_container)
1332+create_service_unit_test(test_apt)
1333+create_service_unit_test(test_task_dispatcher)
1334+
1335+add_subdirectory(tasks)
1336
1337=== added directory 'tests/unit/service/containerroot'
1338=== added directory 'tests/unit/service/containerroot/var'
1339=== added directory 'tests/unit/service/containerroot/var/cache'
1340=== added directory 'tests/unit/service/containerroot/var/cache/app-info'
1341=== added directory 'tests/unit/service/containerroot/var/cache/app-info/gv'
1342=== added file 'tests/unit/service/containerroot/var/cache/app-info/gv/en_US.gvz'
1343=== added directory 'tests/unit/service/tasks'
1344=== added file 'tests/unit/service/tasks/CMakeLists.txt'
1345--- tests/unit/service/tasks/CMakeLists.txt 1970-01-01 00:00:00 +0000
1346+++ tests/unit/service/tasks/CMakeLists.txt 2016-11-16 17:34:14 +0000
1347@@ -0,0 +1,10 @@
1348+create_service_unit_test(test_app_info_task)
1349+create_service_unit_test(test_container_info_task)
1350+create_service_unit_test(test_create_task)
1351+create_service_unit_test(test_destroy_task)
1352+create_service_unit_test(test_install_task)
1353+create_service_unit_test(test_list_task)
1354+create_service_unit_test(test_list_apps_task)
1355+create_service_unit_test(test_remove_task)
1356+create_service_unit_test(test_search_task)
1357+create_service_unit_test(test_update_task)
1358
1359=== added file 'tests/unit/service/tasks/test_app_info_task.py'
1360--- tests/unit/service/tasks/test_app_info_task.py 1970-01-01 00:00:00 +0000
1361+++ tests/unit/service/tasks/test_app_info_task.py 2016-11-16 17:34:14 +0000
1362@@ -0,0 +1,61 @@
1363+# Copyright 2016 Canonical Ltd.
1364+#
1365+# This program is free software: you can redistribute it and/or modify it
1366+# under the terms of the GNU General Public License version 3, as published
1367+# by the Free Software Foundation.
1368+#
1369+# This program is distributed in the hope that it will be useful, but
1370+# WITHOUT ANY WARRANTY; without even the implied warranties of
1371+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1372+# PURPOSE. See the GNU General Public License for more details.
1373+#
1374+# You should have received a copy of the GNU General Public License along
1375+# with this program. If not, see <http://www.gnu.org/licenses/>.
1376+
1377+
1378+import unittest.mock
1379+from unittest import TestCase
1380+from libertine.service import tasks, apt
1381+from libertine.ContainersConfig import ContainersConfig
1382+
1383+
1384+class TestAppInfoTask(TestCase):
1385+ def setUp(self):
1386+ self.config = unittest.mock.create_autospec(ContainersConfig)
1387+ self.cache = unittest.mock.create_autospec(apt.AptCache)
1388+ self.connection = unittest.mock.Mock()
1389+
1390+ def test_app_not_found_causes_error(self):
1391+ self.called_with = None
1392+ def callback(t):
1393+ self.called_with = t
1394+
1395+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1396+ progress = MockProgress.return_value
1397+ self.cache.app_info.return_value = {}
1398+ task = tasks.AppInfoTask('palpatine', self.cache, 'lightside', [1, 2], self.config, self.connection, callback)
1399+ task._instant_callback = True
1400+ task.start().join()
1401+
1402+ progress.error.assert_called_once_with('Could not find app info for \'lightside\' in container \'palpatine\'')
1403+
1404+ self.assertEqual(task, self.called_with)
1405+
1406+ def test_success_sends_data(self):
1407+ self.called_with = None
1408+ def callback(t):
1409+ self.called_with = t
1410+
1411+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1412+ progress = MockProgress.return_value
1413+ progress.done = False
1414+
1415+ self.cache.app_info.return_value = {'package': 'darkside-common'}
1416+ self.config.get_package_install_status.return_value = 'installed'
1417+ task = tasks.AppInfoTask('palpatine', self.cache, 'darkside', [1, 2, 3], self.config, self.connection, callback)
1418+ task._instant_callback = True
1419+ task.start().join()
1420+
1421+ progress.data.assert_called_once_with(str({'package': 'darkside-common', 'status': 'installed', 'task_ids': [1, 2, 3]}))
1422+
1423+ self.assertEqual(task, self.called_with)
1424
1425=== added file 'tests/unit/service/tasks/test_container_info_task.py'
1426--- tests/unit/service/tasks/test_container_info_task.py 1970-01-01 00:00:00 +0000
1427+++ tests/unit/service/tasks/test_container_info_task.py 2016-11-16 17:34:14 +0000
1428@@ -0,0 +1,44 @@
1429+# Copyright 2016 Canonical Ltd.
1430+#
1431+# This program is free software: you can redistribute it and/or modify it
1432+# under the terms of the GNU General Public License version 3, as published
1433+# by the Free Software Foundation.
1434+#
1435+# This program is distributed in the hope that it will be useful, but
1436+# WITHOUT ANY WARRANTY; without even the implied warranties of
1437+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1438+# PURPOSE. See the GNU General Public License for more details.
1439+#
1440+# You should have received a copy of the GNU General Public License along
1441+# with this program. If not, see <http://www.gnu.org/licenses/>.
1442+
1443+
1444+import unittest.mock
1445+from unittest import TestCase
1446+from libertine.service import tasks
1447+from libertine.ContainersConfig import ContainersConfig
1448+
1449+
1450+class TestContainerInfoTask(TestCase):
1451+ def setUp(self):
1452+ self.config = unittest.mock.create_autospec(ContainersConfig)
1453+ self.connection = unittest.mock.Mock()
1454+
1455+ def test_success_sends_data(self):
1456+ self.called_with = None
1457+ def callback(t):
1458+ self.called_with = t
1459+
1460+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1461+ progress = MockProgress.return_value
1462+ progress.done = False
1463+
1464+ self.config._get_value_by_key.return_value = 'ready'
1465+ task = tasks.ContainerInfoTask('palpatine', [1, 2, 3], self.config, self.connection, callback)
1466+ task._instant_callback = True
1467+ task.start().join()
1468+
1469+ progress.data.assert_called_once_with(str({'id': 'palpatine', 'status': 'ready', 'task_ids': [1, 2, 3]}))
1470+ progress.finished.assert_called_once_with('palpatine')
1471+
1472+ self.assertEqual(task, self.called_with)
1473
1474=== added file 'tests/unit/service/tasks/test_create_task.py'
1475--- tests/unit/service/tasks/test_create_task.py 1970-01-01 00:00:00 +0000
1476+++ tests/unit/service/tasks/test_create_task.py 2016-11-16 17:34:14 +0000
1477@@ -0,0 +1,234 @@
1478+# Copyright 2016 Canonical Ltd.
1479+#
1480+# This program is free software: you can redistribute it and/or modify it
1481+# under the terms of the GNU General Public License version 3, as published
1482+# by the Free Software Foundation.
1483+#
1484+# This program is distributed in the hope that it will be useful, but
1485+# WITHOUT ANY WARRANTY; without even the implied warranties of
1486+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1487+# PURPOSE. See the GNU General Public License for more details.
1488+#
1489+# You should have received a copy of the GNU General Public License along
1490+# with this program. If not, see <http://www.gnu.org/licenses/>.
1491+
1492+
1493+import unittest.mock
1494+from unittest import TestCase
1495+from libertine.service import tasks
1496+from libertine.ContainersConfig import ContainersConfig
1497+
1498+
1499+class TestCreateTask(TestCase):
1500+ def setUp(self):
1501+ self.config = unittest.mock.create_autospec(ContainersConfig)
1502+ self.connection = unittest.mock.Mock()
1503+ self.lock = unittest.mock.MagicMock()
1504+ self.called_with = None
1505+
1506+ def callback(self, task):
1507+ self.called_with = task
1508+
1509+ def test_success_creates_lxc_container(self):
1510+ self.config.container_exists.return_value = False
1511+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1512+ progress = MockProgress.return_value
1513+ progress.done = False
1514+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)
1515+ task._instant_callback = True
1516+
1517+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
1518+ MockHostInfo.return_value.is_distro_valid.return_value = True
1519+ MockHostInfo.return_value.has_lxc_support.return_value = True
1520+
1521+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
1522+ MockContainer.return_value.create_libertine_container.return_value = True
1523+ task.start().join()
1524+
1525+ progress.finished.assert_called_once_with('palpatine')
1526+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
1527+ self.config.update_container_install_status.assert_has_calls([
1528+ unittest.mock.call('palpatine', 'installing'),
1529+ unittest.mock.call('palpatine', 'ready')
1530+ ], any_order=True)
1531+ self.assertEqual(task, self.called_with)
1532+
1533+ def test_success_creates_chroot_container(self):
1534+ self.config.container_exists.return_value = False
1535+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1536+ progress = MockProgress.return_value
1537+ progress.done = False
1538+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', False, self.config, self.lock, self.connection, self.callback)
1539+ task._instant_callback = True
1540+
1541+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
1542+ MockHostInfo.return_value.is_distro_valid.return_value = True
1543+
1544+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
1545+ MockContainer.return_value.create_libertine_container.return_value = True
1546+ task.start().join()
1547+
1548+ progress.finished.assert_called_once_with('palpatine')
1549+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty')
1550+ self.config.update_container_install_status.assert_has_calls([
1551+ unittest.mock.call('palpatine', 'installing'),
1552+ unittest.mock.call('palpatine', 'ready')
1553+ ], any_order=True)
1554+ self.assertEqual(task, self.called_with)
1555+
1556+ def test_container_runtime_error_sends_error(self):
1557+ self.config.container_exists.return_value = False
1558+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1559+ progress = MockProgress.return_value
1560+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)
1561+ task._instant_callback = True
1562+
1563+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
1564+ MockHostInfo.return_value.is_distro_valid.return_value = True
1565+ MockHostInfo.return_value.has_lxc_support.return_value = True
1566+
1567+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
1568+ MockContainer.return_value.create_libertine_container.side_effect = RuntimeError('a great disturbance')
1569+ task.start().join()
1570+
1571+ progress.error.assert_called_once_with('a great disturbance')
1572+
1573+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
1574+ self.config.update_container_install_status.assert_called_once_with('palpatine', 'installing')
1575+ self.config.delete_container.assert_called_once_with('palpatine')
1576+
1577+ self.assertEqual(task, self.called_with)
1578+
1579+ def test_failed_container_exists_sends_error(self):
1580+ self.config.container_exists.return_value = True
1581+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1582+ progress = MockProgress.return_value
1583+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)
1584+ task._instant_callback = True
1585+ task.start().join()
1586+
1587+ progress.error.assert_called_once_with('Container \'palpatine\' already exists')
1588+ self.assertEqual(task, self.called_with)
1589+
1590+ def test_container_invalid_distro_error_sends_error(self):
1591+ self.config.container_exists.return_value = False
1592+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1593+ progress = MockProgress.return_value
1594+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
1595+ MockHostInfo.return_value.is_distro_valid.return_value = False
1596+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'vesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)
1597+ task._instant_callback = True
1598+ task.start().join()
1599+
1600+ progress.error.assert_called_once_with('Invalid distro \'vesty\'.')
1601+ self.assertEqual(task, self.called_with)
1602+
1603+ def test_container_improper_lxc_error_sends_error(self):
1604+ self.config.container_exists.return_value = False
1605+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1606+ progress = MockProgress.return_value
1607+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
1608+ MockHostInfo.return_value.is_distro_valid.return_value = True
1609+ MockHostInfo.return_value.has_lxc_support.return_value = False
1610+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'lxc', False, self.config, self.lock, self.connection, self.callback)
1611+ task._instant_callback = True
1612+ task.start().join()
1613+
1614+ progress.error.assert_called_once_with('System kernel does not support lxc type containers. Please either use chroot or leave empty.')
1615+ self.assertEqual(task, self.called_with)
1616+
1617+ def test_sets_generic_name_when_empty(self):
1618+ self.config.container_exists.return_value = False
1619+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1620+ progress = MockProgress.return_value
1621+ progress.done = False
1622+ task = tasks.CreateTask('palpatine', None, 'zesty', 'chroot', False, self.config, self.lock, self.connection, self.callback)
1623+ task._instant_callback = True
1624+
1625+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
1626+ MockHostInfo.return_value.is_distro_valid.return_value = True
1627+ MockHostInfo.return_value.get_distro_codename.return_value = 'Zesty Zapus 17.04'
1628+
1629+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
1630+ MockContainer.return_value.create_libertine_container.return_value = True
1631+ task.start().join()
1632+
1633+ progress.finished.assert_called_once_with('palpatine')
1634+ self.config.add_new_container.assert_called_once_with('palpatine', 'Ubuntu \'Zesty Zapus 17.04\'', 'chroot', 'zesty')
1635+ self.config.update_container_install_status.assert_has_calls([
1636+ unittest.mock.call('palpatine', 'installing'),
1637+ unittest.mock.call('palpatine', 'ready')
1638+ ], any_order=True)
1639+ self.assertEqual(task, self.called_with)
1640+
1641+ def test_sets_multiarch(self):
1642+ self.config.container_exists.return_value = False
1643+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1644+ progress = MockProgress.return_value
1645+ progress.done = False
1646+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', 'chroot', True, self.config, self.lock, self.connection, self.callback)
1647+ task._instant_callback = True
1648+
1649+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
1650+ MockHostInfo.return_value.is_distro_valid.return_value = True
1651+
1652+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
1653+ MockContainer.return_value.create_libertine_container.return_value = True
1654+ task.start().join()
1655+
1656+ progress.finished.assert_called_once_with('palpatine')
1657+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'chroot', 'zesty')
1658+ self.config.update_container_multiarch_support.assert_called_once_with('palpatine', 'enabled')
1659+ self.config.update_container_install_status.assert_has_calls([
1660+ unittest.mock.call('palpatine', 'installing'),
1661+ unittest.mock.call('palpatine', 'ready')
1662+ ], any_order=True)
1663+ self.assertEqual(task, self.called_with)
1664+
1665+ def test_sets_default_type(self):
1666+ self.config.container_exists.return_value = False
1667+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1668+ progress = MockProgress.return_value
1669+ progress.done = False
1670+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', 'zesty', None, False, self.config, self.lock, self.connection, self.callback)
1671+ task._instant_callback = True
1672+
1673+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
1674+ MockHostInfo.return_value.is_distro_valid.return_value = True
1675+ MockHostInfo.return_value.select_container_type_by_kernel.return_value = 'lxc'
1676+
1677+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
1678+ MockContainer.return_value.create_libertine_container.return_value = True
1679+ task.start().join()
1680+
1681+ progress.finished.assert_called_once_with('palpatine')
1682+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
1683+ self.config.update_container_install_status.assert_has_calls([
1684+ unittest.mock.call('palpatine', 'installing'),
1685+ unittest.mock.call('palpatine', 'ready')
1686+ ], any_order=True)
1687+ self.assertEqual(task, self.called_with)
1688+
1689+ def test_sets_default_distro(self):
1690+ self.config.container_exists.return_value = False
1691+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1692+ progress = MockProgress.return_value
1693+ progress.done = False
1694+ task = tasks.CreateTask('palpatine', 'Emperor Palpatine', None, 'lxc', False, self.config, self.lock, self.connection, self.callback)
1695+ task._instant_callback = True
1696+
1697+ with unittest.mock.patch('libertine.service.tasks.create_task.HostInfo') as MockHostInfo:
1698+ MockHostInfo.return_value.has_lxc_support.return_value = True
1699+ MockHostInfo.return_value.get_host_distro_release.return_value = 'zesty'
1700+
1701+ with unittest.mock.patch('libertine.service.tasks.create_task.LibertineContainer') as MockContainer:
1702+ MockContainer.return_value.create_libertine_container.return_value = True
1703+ task.start().join()
1704+
1705+ progress.finished.assert_called_once_with('palpatine')
1706+ self.config.add_new_container.assert_called_once_with('palpatine', 'Emperor Palpatine', 'lxc', 'zesty')
1707+ self.config.update_container_install_status.assert_has_calls([
1708+ unittest.mock.call('palpatine', 'installing'),
1709+ unittest.mock.call('palpatine', 'ready')
1710+ ], any_order=True)
1711+ self.assertEqual(task, self.called_with)
1712
1713=== added file 'tests/unit/service/tasks/test_destroy_task.py'
1714--- tests/unit/service/tasks/test_destroy_task.py 1970-01-01 00:00:00 +0000
1715+++ tests/unit/service/tasks/test_destroy_task.py 2016-11-16 17:34:14 +0000
1716@@ -0,0 +1,80 @@
1717+# Copyright 2016 Canonical Ltd.
1718+#
1719+# This program is free software: you can redistribute it and/or modify it
1720+# under the terms of the GNU General Public License version 3, as published
1721+# by the Free Software Foundation.
1722+#
1723+# This program is distributed in the hope that it will be useful, but
1724+# WITHOUT ANY WARRANTY; without even the implied warranties of
1725+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1726+# PURPOSE. See the GNU General Public License for more details.
1727+#
1728+# You should have received a copy of the GNU General Public License along
1729+# with this program. If not, see <http://www.gnu.org/licenses/>.
1730+
1731+
1732+import unittest.mock
1733+from unittest import TestCase
1734+from libertine.service import tasks
1735+from libertine.ContainersConfig import ContainersConfig
1736+
1737+
1738+class TestDestroyTask(TestCase):
1739+ def setUp(self):
1740+ self.config = unittest.mock.create_autospec(ContainersConfig)
1741+ self.connection = unittest.mock.Mock()
1742+ self.lock = unittest.mock.MagicMock()
1743+ self.called_with = None
1744+
1745+ def callback(self, task):
1746+ self.called_with = task
1747+
1748+ def test_sends_error_on_non_ready_container(self):
1749+ self.config._get_value_by_key.return_value = ''
1750+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1751+ progress = MockProgress.return_value
1752+ progress.done = False
1753+ task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback)
1754+ task._instant_callback = True
1755+
1756+ with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
1757+ MockContainer.return_value.destroy_libertine_container.return_value = True
1758+ task.start().join()
1759+
1760+ progress.error.assert_called_once_with('Container \'palpatine\' does not exist')
1761+ self.assertEqual(task, self.called_with)
1762+
1763+ def test_sends_error_on_failed_destroy(self):
1764+ self.config._get_value_by_key.return_value = 'ready'
1765+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1766+ progress = MockProgress.return_value
1767+ task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback)
1768+ task._instant_callback = True
1769+
1770+ with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
1771+ MockContainer.return_value.destroy_libertine_container.return_value = False
1772+ task.start().join()
1773+
1774+ progress.error.assert_called_once_with('Destroying container \'palpatine\' failed')
1775+ self.config.update_container_install_status.assert_has_calls([
1776+ unittest.mock.call('palpatine', 'removing'),
1777+ unittest.mock.call('palpatine', 'ready')
1778+ ], any_order=True)
1779+ self.assertEqual(task, self.called_with)
1780+
1781+ def test_successfully_destroys(self):
1782+ self.config._get_value_by_key.return_value = 'ready'
1783+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1784+ progress = MockProgress.return_value
1785+ progress.done = False
1786+ task = tasks.DestroyTask('palpatine', self.config, self.lock, self.connection, self.callback)
1787+ task._instant_callback = True
1788+
1789+ with unittest.mock.patch('libertine.service.tasks.destroy_task.LibertineContainer') as MockContainer:
1790+ MockContainer.return_value.destroy_libertine_container.return_value = True
1791+ task.start().join()
1792+
1793+ progress.finished.assert_called_once_with('palpatine')
1794+ self.config.update_container_install_status.assert_called_once_with('palpatine', 'removing')
1795+ self.config.delete_container.assert_called_once_with('palpatine')
1796+ self.assertEqual(task, self.called_with)
1797
1798=== added file 'tests/unit/service/tasks/test_install_task.py'
1799--- tests/unit/service/tasks/test_install_task.py 1970-01-01 00:00:00 +0000
1800+++ tests/unit/service/tasks/test_install_task.py 2016-11-16 17:34:14 +0000
1801@@ -0,0 +1,76 @@
1802+# Copyright 2016 Canonical Ltd.
1803+#
1804+# This program is free software: you can redistribute it and/or modify it
1805+# under the terms of the GNU General Public License version 3, as published
1806+# by the Free Software Foundation.
1807+#
1808+# This program is distributed in the hope that it will be useful, but
1809+# WITHOUT ANY WARRANTY; without even the implied warranties of
1810+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1811+# PURPOSE. See the GNU General Public License for more details.
1812+#
1813+# You should have received a copy of the GNU General Public License along
1814+# with this program. If not, see <http://www.gnu.org/licenses/>.
1815+
1816+
1817+import unittest.mock
1818+from unittest import TestCase
1819+from libertine.service import tasks
1820+from libertine.ContainersConfig import ContainersConfig
1821+
1822+
1823+class TestInstallTask(TestCase):
1824+ def setUp(self):
1825+ self.config = unittest.mock.create_autospec(ContainersConfig)
1826+ self.connection = unittest.mock.Mock()
1827+ self.lock = unittest.mock.MagicMock()
1828+ self.called_with = None
1829+
1830+ def callback(self, task):
1831+ self.called_with = task
1832+
1833+ def test_sends_error_on_existing_package(self):
1834+ self.config.package_exists.return_value = True
1835+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1836+ progress = MockProgress.return_value
1837+ task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
1838+ task._instant_callback = True
1839+ task.start().join()
1840+
1841+ progress.error.assert_called_once_with('Package \'darkside-common\' already exists, skipping install')
1842+ self.assertEqual(task, self.called_with)
1843+
1844+ def test_sends_error_on_failed_install(self):
1845+ self.config.package_exists.return_value = False
1846+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1847+ progress = MockProgress.return_value
1848+ task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
1849+ task._instant_callback = True
1850+
1851+ with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer:
1852+ MockContainer.return_value.install_package.return_value = False
1853+ task.start().join()
1854+
1855+ progress.error.assert_called_once_with("Package installation failed for 'darkside-common'")
1856+ self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'installing')
1857+ self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common')
1858+ self.assertEqual(task, self.called_with)
1859+
1860+ def test_successfully_install(self):
1861+ self.config.package_exists.return_value = False
1862+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1863+ progress = MockProgress.return_value
1864+ progress.done = False
1865+ task = tasks.InstallTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
1866+ task._instant_callback = True
1867+
1868+ with unittest.mock.patch('libertine.service.tasks.install_task.LibertineContainer') as MockContainer:
1869+ MockContainer.return_value.install_package.return_value = True
1870+ task.start().join()
1871+
1872+ progress.finished.assert_called_once_with('palpatine')
1873+ self.config.update_package_install_status.assert_has_calls([
1874+ unittest.mock.call('palpatine', 'darkside-common', 'installing'),
1875+ unittest.mock.call('palpatine', 'darkside-common', 'installed')
1876+ ], any_order=True)
1877+ self.assertEqual(task, self.called_with)
1878
1879=== added file 'tests/unit/service/tasks/test_list_apps_task.py'
1880--- tests/unit/service/tasks/test_list_apps_task.py 1970-01-01 00:00:00 +0000
1881+++ tests/unit/service/tasks/test_list_apps_task.py 2016-11-16 17:34:14 +0000
1882@@ -0,0 +1,59 @@
1883+# Copyright 2016 Canonical Ltd.
1884+#
1885+# This program is free software: you can redistribute it and/or modify it
1886+# under the terms of the GNU General Public License version 3, as published
1887+# by the Free Software Foundation.
1888+#
1889+# This program is distributed in the hope that it will be useful, but
1890+# WITHOUT ANY WARRANTY; without even the implied warranties of
1891+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1892+# PURPOSE. See the GNU General Public License for more details.
1893+#
1894+# You should have received a copy of the GNU General Public License along
1895+# with this program. If not, see <http://www.gnu.org/licenses/>.
1896+
1897+
1898+import unittest.mock
1899+from unittest import TestCase
1900+from libertine.service import tasks
1901+from libertine.ContainersConfig import ContainersConfig
1902+
1903+
1904+class TestListAppsTask(TestCase):
1905+ def setUp(self):
1906+ self.config = unittest.mock.create_autospec(ContainersConfig)
1907+ self.connection = unittest.mock.Mock()
1908+ self.lock = unittest.mock.MagicMock()
1909+ self.called_with = None
1910+
1911+ def callback(self, task):
1912+ self.called_with = task
1913+
1914+ def test_sends_error_on_non_existent_container(self):
1915+ self.config.container_exists.return_value = False
1916+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1917+ progress = MockProgress.return_value
1918+ task = tasks.ListAppsTask('palpatine', self.config, self.connection, self.callback)
1919+ task._instant_callback = True
1920+
1921+ with unittest.mock.patch('libertine.service.tasks.list_apps_task.LibertineContainer') as MockContainer:
1922+ task.start().join()
1923+
1924+ progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping list')
1925+ self.assertEqual(task, self.called_with)
1926+
1927+ def test_successfully_lists_apps(self):
1928+ self.config.container_exists.return_value = True
1929+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1930+ progress = MockProgress.return_value
1931+ progress.done = False
1932+ task = tasks.ListAppsTask('palpatine', self.config, self.connection, self.callback)
1933+ task._instant_callback = True
1934+
1935+ with unittest.mock.patch('libertine.service.tasks.list_apps_task.LibertineContainer') as MockContainer:
1936+ MockContainer.return_value.list_app_launchers.return_value = 'jarjar\nsidius'
1937+ task.start().join()
1938+
1939+ progress.finished.assert_called_once_with('palpatine')
1940+ progress.data.assert_called_once_with('jarjar\nsidius')
1941+ self.assertEqual(task, self.called_with)
1942
1943=== added file 'tests/unit/service/tasks/test_list_task.py'
1944--- tests/unit/service/tasks/test_list_task.py 1970-01-01 00:00:00 +0000
1945+++ tests/unit/service/tasks/test_list_task.py 2016-11-16 17:34:14 +0000
1946@@ -0,0 +1,45 @@
1947+# Copyright 2016 Canonical Ltd.
1948+#
1949+# This program is free software: you can redistribute it and/or modify it
1950+# under the terms of the GNU General Public License version 3, as published
1951+# by the Free Software Foundation.
1952+#
1953+# This program is distributed in the hope that it will be useful, but
1954+# WITHOUT ANY WARRANTY; without even the implied warranties of
1955+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1956+# PURPOSE. See the GNU General Public License for more details.
1957+#
1958+# You should have received a copy of the GNU General Public License along
1959+# with this program. If not, see <http://www.gnu.org/licenses/>.
1960+
1961+
1962+import unittest.mock
1963+from unittest import TestCase
1964+from libertine.service import tasks
1965+from libertine.ContainersConfig import ContainersConfig
1966+
1967+
1968+class TestListTask(TestCase):
1969+ def setUp(self):
1970+ self.config = unittest.mock.create_autospec(ContainersConfig)
1971+ self.connection = unittest.mock.Mock()
1972+
1973+ def test_success_sends_data(self):
1974+ self.called_with = None
1975+ def callback(t):
1976+ self.called_with = t
1977+
1978+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
1979+ progress = MockProgress.return_value
1980+ progress.done = False
1981+ task = tasks.ListTask(self.connection, callback)
1982+ task._instant_callback = True
1983+
1984+ with unittest.mock.patch('libertine.service.tasks.list_task.utils.Libertine') as MockLibertine:
1985+ MockLibertine.list_containers.return_value = 'palpatine\nvader\nmaul'
1986+ task.start().join()
1987+
1988+ progress.data.assert_called_once_with('palpatine\nvader\nmaul')
1989+ progress.finished.assert_called_once_with('')
1990+
1991+ self.assertEqual(task, self.called_with)
1992
1993=== added file 'tests/unit/service/tasks/test_remove_task.py'
1994--- tests/unit/service/tasks/test_remove_task.py 1970-01-01 00:00:00 +0000
1995+++ tests/unit/service/tasks/test_remove_task.py 2016-11-16 17:34:14 +0000
1996@@ -0,0 +1,76 @@
1997+# Copyright 2016 Canonical Ltd.
1998+#
1999+# This program is free software: you can redistribute it and/or modify it
2000+# under the terms of the GNU General Public License version 3, as published
2001+# by the Free Software Foundation.
2002+#
2003+# This program is distributed in the hope that it will be useful, but
2004+# WITHOUT ANY WARRANTY; without even the implied warranties of
2005+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2006+# PURPOSE. See the GNU General Public License for more details.
2007+#
2008+# You should have received a copy of the GNU General Public License along
2009+# with this program. If not, see <http://www.gnu.org/licenses/>.
2010+
2011+
2012+import unittest.mock
2013+from unittest import TestCase
2014+from libertine.service import tasks
2015+from libertine.ContainersConfig import ContainersConfig
2016+
2017+
2018+class TestRemoveTask(TestCase):
2019+ def setUp(self):
2020+ self.config = unittest.mock.create_autospec(ContainersConfig)
2021+ self.connection = unittest.mock.Mock()
2022+ self.lock = unittest.mock.MagicMock()
2023+ self.called_with = None
2024+
2025+ def callback(self, task):
2026+ self.called_with = task
2027+
2028+ def test_sends_error_on_non_installed_package(self):
2029+ self.config.get_package_install_status.return_value = 'installing'
2030+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2031+ progress = MockProgress.return_value
2032+ task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
2033+ task._instant_callback = True
2034+ task.start().join()
2035+
2036+ progress.error.assert_called_once_with('Package \'darkside-common\' not installed, skipping remove')
2037+ self.assertEqual(task, self.called_with)
2038+
2039+ def test_sends_error_on_failed_install(self):
2040+ self.config.get_package_install_status.return_value = 'installed'
2041+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2042+ progress = MockProgress.return_value
2043+ task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
2044+ task._instant_callback = True
2045+
2046+ with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer:
2047+ MockContainer.return_value.remove_package.return_value = False
2048+ task.start().join()
2049+
2050+ progress.error.assert_called_once_with("Package removal failed for 'darkside-common'")
2051+ self.config.update_package_install_status.assert_has_calls([
2052+ unittest.mock.call('palpatine', 'darkside-common', 'removing'),
2053+ unittest.mock.call('palpatine', 'darkside-common', 'installed')
2054+ ], any_order=True)
2055+ self.assertEqual(task, self.called_with)
2056+
2057+ def test_successfully_install(self):
2058+ self.config.get_package_install_status.return_value = 'installed'
2059+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2060+ progress = MockProgress.return_value
2061+ progress.done = False
2062+ task = tasks.RemoveTask('darkside-common', 'palpatine', self.config, self.lock, self.connection, self.callback)
2063+ task._instant_callback = True
2064+
2065+ with unittest.mock.patch('libertine.service.tasks.remove_task.LibertineContainer') as MockContainer:
2066+ MockContainer.return_value.remove_package.return_value = True
2067+ task.start().join()
2068+
2069+ progress.finished.assert_called_once_with('palpatine')
2070+ self.config.update_package_install_status.assert_called_once_with('palpatine', 'darkside-common', 'removing')
2071+ self.config.delete_package.assert_called_once_with('palpatine', 'darkside-common')
2072+ self.assertEqual(task, self.called_with)
2073
2074=== added file 'tests/unit/service/tasks/test_search_task.py'
2075--- tests/unit/service/tasks/test_search_task.py 1970-01-01 00:00:00 +0000
2076+++ tests/unit/service/tasks/test_search_task.py 2016-11-16 17:34:14 +0000
2077@@ -0,0 +1,43 @@
2078+# Copyright 2016 Canonical Ltd.
2079+#
2080+# This program is free software: you can redistribute it and/or modify it
2081+# under the terms of the GNU General Public License version 3, as published
2082+# by the Free Software Foundation.
2083+#
2084+# This program is distributed in the hope that it will be useful, but
2085+# WITHOUT ANY WARRANTY; without even the implied warranties of
2086+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2087+# PURPOSE. See the GNU General Public License for more details.
2088+#
2089+# You should have received a copy of the GNU General Public License along
2090+# with this program. If not, see <http://www.gnu.org/licenses/>.
2091+
2092+
2093+import unittest.mock
2094+from unittest import TestCase
2095+from libertine.service import tasks, apt
2096+
2097+
2098+class TestSearchTask(TestCase):
2099+ def setUp(self):
2100+ self.connection = unittest.mock.Mock()
2101+ self.lock = unittest.mock.MagicMock()
2102+ self.cache = unittest.mock.create_autospec(apt.AptCache)
2103+ self.called_with = None
2104+
2105+ def callback(self, task):
2106+ self.called_with = task
2107+
2108+ def test_successfully_lists_apps(self):
2109+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2110+ progress = MockProgress.return_value
2111+ progress.done = False
2112+ task = tasks.SearchTask('palpatine', self.cache, 'jarjar', self.connection, self.callback)
2113+ task._instant_callback = True
2114+
2115+ self.cache.search.return_value = ['jarjar', 'sidius']
2116+ task.start().join()
2117+
2118+ progress.finished.assert_called_once_with('palpatine')
2119+ progress.data.assert_called_once_with(str(['jarjar', 'sidius']))
2120+ self.assertEqual(task, self.called_with)
2121
2122=== added file 'tests/unit/service/tasks/test_update_task.py'
2123--- tests/unit/service/tasks/test_update_task.py 1970-01-01 00:00:00 +0000
2124+++ tests/unit/service/tasks/test_update_task.py 2016-11-16 17:34:14 +0000
2125@@ -0,0 +1,80 @@
2126+# Copyright 2016 Canonical Ltd.
2127+#
2128+# This program is free software: you can redistribute it and/or modify it
2129+# under the terms of the GNU General Public License version 3, as published
2130+# by the Free Software Foundation.
2131+#
2132+# This program is distributed in the hope that it will be useful, but
2133+# WITHOUT ANY WARRANTY; without even the implied warranties of
2134+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2135+# PURPOSE. See the GNU General Public License for more details.
2136+#
2137+# You should have received a copy of the GNU General Public License along
2138+# with this program. If not, see <http://www.gnu.org/licenses/>.
2139+
2140+
2141+import unittest.mock
2142+from unittest import TestCase
2143+from libertine.service import tasks
2144+from libertine.ContainersConfig import ContainersConfig
2145+
2146+
2147+class TestUpdateTask(TestCase):
2148+ def setUp(self):
2149+ self.config = unittest.mock.create_autospec(ContainersConfig)
2150+ self.connection = unittest.mock.Mock()
2151+ self.lock = unittest.mock.MagicMock()
2152+ self.called_with = None
2153+
2154+ def callback(self, task):
2155+ self.called_with = task
2156+
2157+ def test_sends_error_on_non_existent_container(self):
2158+ self.config.container_exists.return_value = False
2159+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2160+ progress = MockProgress.return_value
2161+ task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback)
2162+ task._instant_callback = True
2163+
2164+ with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
2165+ task.start().join()
2166+
2167+ progress.error.assert_called_once_with('Container \'palpatine\' does not exist, skipping update')
2168+ self.assertEqual(task, self.called_with)
2169+
2170+ def test_sends_error_on_failed_update(self):
2171+ self.config.container_exists.return_value = True
2172+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2173+ progress = MockProgress.return_value
2174+ task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback)
2175+ task._instant_callback = True
2176+
2177+ with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
2178+ MockContainer.return_value.update_libertine_container.return_value = False
2179+ task.start().join()
2180+
2181+ progress.error.assert_called_once_with('Failed to update container \'palpatine\'')
2182+ self.config.update_container_install_status.assert_has_calls([
2183+ unittest.mock.call('palpatine', 'updating'),
2184+ unittest.mock.call('palpatine', 'ready')
2185+ ], any_order=True)
2186+ self.assertEqual(task, self.called_with)
2187+
2188+ def test_successfully_updates(self):
2189+ self.config.container_exists.return_value = True
2190+ with unittest.mock.patch('libertine.service.tasks.base_task.libertine.service.progress.Progress') as MockProgress:
2191+ progress = MockProgress.return_value
2192+ progress.done = False
2193+ task = tasks.UpdateTask('palpatine', self.config, self.lock, self.connection, self.callback)
2194+ task._instant_callback = True
2195+
2196+ with unittest.mock.patch('libertine.service.tasks.update_task.LibertineContainer') as MockContainer:
2197+ MockContainer.return_value.update_libertine_container.return_value = True
2198+ task.start().join()
2199+
2200+ progress.finished.assert_called_once_with('palpatine')
2201+ self.config.update_container_install_status.assert_has_calls([
2202+ unittest.mock.call('palpatine', 'updating'),
2203+ unittest.mock.call('palpatine', 'ready')
2204+ ], any_order=True)
2205+ self.assertEqual(task, self.called_with)
2206
2207=== added file 'tests/unit/service/test_apt.py'
2208--- tests/unit/service/test_apt.py 1970-01-01 00:00:00 +0000
2209+++ tests/unit/service/test_apt.py 2016-11-16 17:34:14 +0000
2210@@ -0,0 +1,134 @@
2211+# Copyright 2016 Canonical Ltd.
2212+#
2213+# This program is free software: you can redistribute it and/or modify it
2214+# under the terms of the GNU General Public License version 3, as published
2215+# by the Free Software Foundation.
2216+#
2217+# This program is distributed in the hope that it will be useful, but
2218+# WITHOUT ANY WARRANTY; without even the implied warranties of
2219+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2220+# PURPOSE. See the GNU General Public License for more details.
2221+#
2222+# You should have received a copy of the GNU General Public License along
2223+# with this program. If not, see <http://www.gnu.org/licenses/>.
2224+
2225+import os
2226+import unittest.mock
2227+from unittest import TestCase
2228+from libertine.service import apt
2229+
2230+
2231+def build_mock_app(name, summary, website, description):
2232+ app = unittest.mock.Mock()
2233+ app.name = name
2234+ version = unittest.mock.Mock()
2235+ version.summary = summary
2236+ version.homepage = website
2237+ version.description = description
2238+ app.versions = [version]
2239+ return app
2240+
2241+
2242+class TestAptCache(TestCase):
2243+ def test_search_returns_empty_when_no_matching_results(self):
2244+ with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
2245+ MockCache.return_value.keys.return_value = []
2246+
2247+ with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
2248+ MockLibertine.container_path.return_value = '/some/junk'
2249+ self.assertEqual(apt.AptCache('palpatine').search('vim'), [])
2250+
2251+ def test_search_returns_matching_results(self):
2252+ with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
2253+ MockCache.return_value = {
2254+ "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"),
2255+ "gedit": build_mock_app("gedit", "text editor", "gedit.com", "visual text editor"),
2256+ "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "edit bitmap images"),
2257+ "vim-common": build_mock_app("vim-common", "common vim stuff", "vim.common", "dependencies")
2258+ }
2259+
2260+ with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
2261+ MockLibertine.container_path.return_value = '/some/junk'
2262+ results = apt.AptCache('palpatine').search('vim')
2263+
2264+ self.assertEqual(len(results), 2)
2265+ results = sorted(results, key=lambda xx: xx['id'])
2266+ self.assertEqual(results[0]['description'], 'who even uses raw vi')
2267+ self.assertEqual(results[0]['name'], 'vim')
2268+ self.assertEqual(results[0]['id'], 'vim')
2269+ self.assertEqual(results[0]['summary'], 'vi improved')
2270+ self.assertEqual(results[0]['website'], 'vim.com')
2271+ self.assertEqual(results[0]['package'], 'vim')
2272+
2273+ self.assertEqual(results[1]['description'], 'dependencies')
2274+ self.assertEqual(results[1]['name'], 'vim-common')
2275+ self.assertEqual(results[1]['id'], 'vim-common')
2276+ self.assertEqual(results[1]['summary'], 'common vim stuff')
2277+ self.assertEqual(results[1]['website'], 'vim.common')
2278+ self.assertEqual(results[1]['package'], 'vim-common')
2279+
2280+ MockCache.assert_called_once_with()
2281+
2282+ def test_app_info_returns_empty_dict_when_no_such_app_exists(self):
2283+ with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
2284+ MockCache.return_value = {}
2285+ with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
2286+ MockLibertine.container_path.return_value = '/some/junk'
2287+ self.assertEqual(apt.AptCache('palpatine').app_info("vim"), {})
2288+ MockCache.assert_called_once_with()
2289+
2290+ def test_app_info_returns_values_for_app(self):
2291+ with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
2292+ MockCache.return_value = {
2293+ "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"),
2294+ "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "visual text editor"),
2295+ }
2296+ with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
2297+ MockLibertine.container_path.return_value = '/some/junk'
2298+ self.assertEqual(apt.AptCache('palpatine').app_info("vim"), {
2299+ 'name': 'vim',
2300+ 'id': 'vim',
2301+ 'package': 'vim',
2302+ 'summary': 'vi improved',
2303+ 'description': 'who even uses raw vi',
2304+ 'website': 'vim.com'
2305+ })
2306+ MockCache.assert_called_once_with()
2307+
2308+ def test_loads_cache_from_container_directory(self):
2309+ with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
2310+ MockCache.return_value = {
2311+ "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"),
2312+ "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "visual text editor"),
2313+ }
2314+ with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
2315+ containerpath = "%s/containerroot" % os.path.dirname(os.path.realpath(__file__))
2316+ MockLibertine.container_path.return_value = containerpath
2317+ self.assertEqual(apt.AptCache('palpatine').app_info("vim"), {
2318+ 'name': 'vim',
2319+ 'id': 'vim',
2320+ 'package': 'vim',
2321+ 'summary': 'vi improved',
2322+ 'description': 'who even uses raw vi',
2323+ 'website': 'vim.com'
2324+ })
2325+
2326+ MockCache.assert_called_once_with(rootdir=containerpath)
2327+
2328+ def test_loads_cache_only_once(self):
2329+ with unittest.mock.patch('libertine.service.apt.apt.Cache') as MockCache:
2330+ MockCache.return_value = {
2331+ "vim": build_mock_app("vim", "vi improved", "vim.com", "who even uses raw vi"),
2332+ "gimp": build_mock_app("gimp", "foss photoshop", "gimp.com", "visual text editor"),
2333+ }
2334+ with unittest.mock.patch('libertine.service.apt.Libertine') as MockLibertine:
2335+ MockLibertine.container_path.return_value = '/some/junk'
2336+ cache = apt.AptCache('palpatine')
2337+ cache.app_info("vim")
2338+ cache.app_info("vim")
2339+
2340+ MockCache.assert_called_once_with()
2341+
2342+
2343+if __name__ == '__main__':
2344+ unittest.main()
2345
2346=== added file 'tests/unit/service/test_container.py'
2347--- tests/unit/service/test_container.py 1970-01-01 00:00:00 +0000
2348+++ tests/unit/service/test_container.py 2016-11-16 17:34:14 +0000
2349@@ -0,0 +1,202 @@
2350+# Copyright 2016 Canonical Ltd.
2351+#
2352+# This program is free software: you can redistribute it and/or modify it
2353+# under the terms of the GNU General Public License version 3, as published
2354+# by the Free Software Foundation.
2355+#
2356+# This program is distributed in the hope that it will be useful, but
2357+# WITHOUT ANY WARRANTY; without even the implied warranties of
2358+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2359+# PURPOSE. See the GNU General Public License for more details.
2360+#
2361+# You should have received a copy of the GNU General Public License along
2362+# with this program. If not, see <http://www.gnu.org/licenses/>.
2363+
2364+import unittest.mock
2365+from unittest import TestCase
2366+from libertine.service import container
2367+from libertine.service import tasks
2368+
2369+
2370+class TestContainer(TestCase):
2371+ def setUp(self):
2372+ self._connection = unittest.mock.Mock()
2373+ self._config = unittest.mock.Mock()
2374+ self._lock = unittest.mock.Mock()
2375+
2376+ def test_search_creates_search_task(self):
2377+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2378+ cache = MockCache.return_value
2379+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2380+ with unittest.mock.patch('libertine.service.container.SearchTask') as MockSearchTask:
2381+ c.search('darkseid')
2382+ MockSearchTask.assert_called_once_with('palpatine', cache, 'darkseid', self._connection, unittest.mock.ANY)
2383+ MockSearchTask.return_value.start.assert_called_once_with()
2384+
2385+ def test_app_info_creates_app_info_task(self):
2386+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2387+ cache = MockCache.return_value
2388+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2389+ with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask:
2390+ c.app_info('force')
2391+ MockAppInfoTask.assert_called_once_with('palpatine', cache, 'force', [], self._config, self._connection, unittest.mock.ANY)
2392+ MockAppInfoTask.return_value.start.assert_called_once_with()
2393+
2394+ def test_app_info_gets_related_task_info(self):
2395+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2396+ cache = MockCache.return_value
2397+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2398+ with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
2399+ MockInstallTask.return_value.package = 'darkside'
2400+ MockInstallTask.return_value.matches.return_value = False
2401+ install_task_id = c.install('darkside')
2402+ with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:
2403+ MockRemoveTask.return_value.package = 'darkside'
2404+ remove_task_id = c.remove('darkside')
2405+ with unittest.mock.patch('libertine.service.container.AppInfoTask') as MockAppInfoTask:
2406+ c.app_info('darkside')
2407+ MockAppInfoTask.assert_called_once_with('palpatine', cache, 'darkside', [install_task_id, remove_task_id],
2408+ self._config, self._connection, unittest.mock.ANY)
2409+
2410+ def test_install_creates_install_task(self):
2411+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2412+ cache = MockCache.return_value
2413+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2414+ with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
2415+ c.install('force')
2416+ MockInstallTask.assert_called_once_with('force', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
2417+ MockInstallTask.return_value.start.assert_called_once_with()
2418+
2419+ def test_install_only_calls_once_when_unfinished(self):
2420+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2421+ cache = MockCache.return_value
2422+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2423+ with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
2424+ c.install('darkside')
2425+ c.install('darkside')
2426+ c.install('darkside')
2427+ # MockInstallTask.assert_called_once_with('darkside', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
2428+ MockInstallTask.return_value.start.assert_called_once_with()
2429+
2430+ def test_remove_creates_remove_task(self):
2431+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2432+ cache = MockCache.return_value
2433+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2434+ with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:
2435+ c.remove('force')
2436+ MockRemoveTask.assert_called_once_with('force', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
2437+ MockRemoveTask.return_value.start.assert_called_once_with()
2438+
2439+ def test_remove_only_calls_once_when_unfinished(self):
2440+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2441+ cache = MockCache.return_value
2442+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2443+ with unittest.mock.patch('libertine.service.container.RemoveTask') as MockRemoveTask:
2444+ c.remove('darkside')
2445+ c.remove('darkside')
2446+ c.remove('darkside')
2447+ # MockRemoveTask.assert_called_once_with('darkside', 'palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
2448+ MockRemoveTask.return_value.start.assert_called_once_with()
2449+
2450+ def test_create_creates_create_task(self):
2451+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2452+ cache = MockCache.return_value
2453+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2454+ with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask:
2455+ c.create('Emperor Palpatine', 'zesty', 'lxd', False)
2456+ MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,
2457+ self._config, self._lock, self._connection, unittest.mock.ANY)
2458+ MockCreateTask.return_value.start.assert_called_once_with()
2459+
2460+ def test_create_only_calls_once_when_unfinished(self):
2461+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2462+ cache = MockCache.return_value
2463+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2464+ with unittest.mock.patch('libertine.service.container.CreateTask') as MockCreateTask:
2465+ c.create('Emperor Palpatine', 'zesty', 'lxd', False)
2466+ c.create('Emperor Palpatine', 'zesty', 'lxd', False)
2467+ c.create('Emperor Palpatine', 'zesty', 'lxd', False)
2468+ # MockCreateTask.assert_called_once_with('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False,
2469+ # self._config, self._lock, self._connection, unittest.mock.ANY)
2470+ MockCreateTask.return_value.start.assert_called_once_with()
2471+
2472+ def test_destroy_creates_destroy_task(self):
2473+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2474+ cache = MockCache.return_value
2475+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2476+ with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask:
2477+ c.destroy()
2478+ # MockDestroyTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
2479+ MockDestroyTask.return_value.start.assert_called_once_with()
2480+
2481+ def test_destroy_only_calls_once_when_unfinished(self):
2482+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2483+ cache = MockCache.return_value
2484+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2485+ with unittest.mock.patch('libertine.service.container.DestroyTask') as MockDestroyTask:
2486+ c.destroy()
2487+ c.destroy()
2488+ c.destroy()
2489+ # MockDestroyTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
2490+ MockDestroyTask.return_value.start.assert_called_once_with()
2491+
2492+ def test_update_creates_update_task(self):
2493+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2494+ cache = MockCache.return_value
2495+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2496+ with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask:
2497+ c.update()
2498+ MockUpdateTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
2499+ MockUpdateTask.return_value.start.assert_called_once_with()
2500+
2501+ def test_update_only_calls_once_when_unfinished(self):
2502+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2503+ cache = MockCache.return_value
2504+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2505+ with unittest.mock.patch('libertine.service.container.UpdateTask') as MockUpdateTask:
2506+ c.update()
2507+ c.update()
2508+ c.update()
2509+ # MockUpdateTask.assert_called_once_with('palpatine', self._config, self._lock, self._connection, unittest.mock.ANY)
2510+ MockUpdateTask.return_value.start.assert_called_once_with()
2511+
2512+ def test_list_apps_creates_list_apps_task(self):
2513+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2514+ cache = MockCache.return_value
2515+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2516+ with unittest.mock.patch('libertine.service.container.ListAppsTask') as MockListAppsTask:
2517+ c.list_apps()
2518+ MockListAppsTask.assert_called_once_with('palpatine', self._config, self._connection, unittest.mock.ANY)
2519+ MockListAppsTask.return_value.start.assert_called_once_with()
2520+
2521+ def test_removes_task_during_callback(self):
2522+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2523+ cache = MockCache.return_value
2524+ c = container.Container('palpatine', self._config, self._lock, self._connection, lambda task: task)
2525+ with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
2526+ MockInstallTask.return_value.package = 'force'
2527+ c.install('force')
2528+ self.assertEqual(1, len(MockInstallTask.return_value.start.mock_calls)) # ensure initial mocks were called
2529+ c.install('force')
2530+ self.assertEqual(1, len(MockInstallTask.return_value.start.mock_calls)) # ensure no more mocks were called
2531+ name, args, kwargs = MockInstallTask.mock_calls[0]
2532+ args[len(args)-1](MockInstallTask.return_value.start.return_value)
2533+ c.install('force')
2534+ self.assertEqual(2, len(MockInstallTask.return_value.start.mock_calls)) # ensure mocks were called again
2535+
2536+ def test_completing_all_tasks_fires_callback(self):
2537+ with unittest.mock.patch('libertine.service.container.apt.AptCache') as MockCache:
2538+ cache = MockCache.return_value
2539+ self._container_id = None
2540+ def callback(container):
2541+ self._container_id = container.id
2542+ c = container.Container('palpatine', self._config, self._lock, self._connection, callback)
2543+ with unittest.mock.patch('libertine.service.container.InstallTask') as MockInstallTask:
2544+ c.install('force')
2545+ name, args, kwargs = MockInstallTask.mock_calls[0]
2546+ args[len(args)-1](MockInstallTask.return_value)
2547+ self.assertEqual('palpatine', self._container_id)
2548+
2549+
2550+if __name__ == '__main__':
2551+ unittest.main()
2552
2553=== added file 'tests/unit/service/test_task_dispatcher.py'
2554--- tests/unit/service/test_task_dispatcher.py 1970-01-01 00:00:00 +0000
2555+++ tests/unit/service/test_task_dispatcher.py 2016-11-16 17:34:14 +0000
2556@@ -0,0 +1,148 @@
2557+# Copyright 2016 Canonical Ltd.
2558+#
2559+# This program is free software: you can redistribute it and/or modify it
2560+# under the terms of the GNU General Public License version 3, as published
2561+# by the Free Software Foundation.
2562+#
2563+# This program is distributed in the hope that it will be useful, but
2564+# WITHOUT ANY WARRANTY; without even the implied warranties of
2565+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2566+# PURPOSE. See the GNU General Public License for more details.
2567+#
2568+# You should have received a copy of the GNU General Public License along
2569+# with this program. If not, see <http://www.gnu.org/licenses/>.
2570+
2571+import unittest.mock
2572+from unittest import TestCase
2573+from libertine.service import task_dispatcher
2574+
2575+
2576+class TestTaskDispatcher(TestCase):
2577+ def setUp(self):
2578+ self._connection = unittest.mock.Mock()
2579+ self._config_patcher = unittest.mock.patch('libertine.service.task_dispatcher.libertine.ContainersConfig.ContainersConfig')
2580+ self._config_patcher.start()
2581+ self._dispatcher = task_dispatcher.TaskDispatcher(self._connection)
2582+
2583+ def tearDown(self):
2584+ self._config_patcher.stop()
2585+
2586+ def test_search_calls_search_on_container(self):
2587+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2588+ c = MockContainer.return_value
2589+ c.search.return_value = 123
2590+ self.assertEqual(123, self._dispatcher.search('palpatine', 'sith'))
2591+ c.search.assert_called_once_with('sith')
2592+
2593+ def test_app_info_calls_app_info_on_container(self):
2594+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2595+ c = MockContainer.return_value
2596+ c.app_info.return_value = 123
2597+ self.assertEqual(123, self._dispatcher.app_info('palpatine', 'deathstar'))
2598+ c.app_info.assert_called_once_with('deathstar')
2599+
2600+ def test_install_calls_install_on_container(self):
2601+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2602+ c = MockContainer.return_value
2603+ c.install.return_value = 123
2604+ self.assertEqual(123, self._dispatcher.install('palpatine', 'darkside'))
2605+ c.install.assert_called_once_with('darkside')
2606+
2607+ def test_remove_calls_remove_on_container(self):
2608+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2609+ c = MockContainer.return_value
2610+ c.remove.return_value = 123
2611+ self.assertEqual(123, self._dispatcher.remove('palpatine', 'lightside'))
2612+ c.remove.assert_called_once_with('lightside')
2613+
2614+ def test_create_calls_create_on_container(self):
2615+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2616+ c = MockContainer.return_value
2617+ c.create.return_value = 123
2618+ self.assertEqual(123, self._dispatcher.create('palpatine', 'Emperor Palpatine', 'zesty', 'lxd', False))
2619+ c.create.assert_called_once_with('Emperor Palpatine', 'zesty', 'lxd', False)
2620+
2621+ def test_destroy_calls_destroy_on_container(self):
2622+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2623+ c = MockContainer.return_value
2624+ c.destroy.return_value = 123
2625+ self.assertEqual(123, self._dispatcher.destroy('palpatine'))
2626+ c.destroy.assert_called_once_with()
2627+
2628+ def test_update_calls_update_on_container(self):
2629+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2630+ c = MockContainer.return_value
2631+ c.update.return_value = 123
2632+ self.assertEqual(123, self._dispatcher.update('palpatine'))
2633+ c.update.assert_called_once_with()
2634+
2635+ def test_list_apps_calls_list_apps_on_container(self):
2636+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2637+ c = MockContainer.return_value
2638+ c.list_apps.return_value = 123
2639+ self.assertEqual(123, self._dispatcher.list_apps('palpatine'))
2640+ c.list_apps.assert_called_once_with()
2641+
2642+ def test_containers_reused_on_subsequent_calls(self):
2643+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2644+ c = MockContainer.return_value
2645+ c.id = 'palpatine'
2646+ self._dispatcher.list_apps('palpatine')
2647+ self._dispatcher.list_apps('palpatine')
2648+ self._dispatcher.list_apps('palpatine')
2649+ MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY)
2650+
2651+ def test_container_callback_removes_container(self):
2652+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2653+ c = MockContainer.return_value
2654+ c.id = 'palpatine'
2655+ self._dispatcher.list_apps('palpatine')
2656+ MockContainer.assert_called_once_with('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY)
2657+ name, args, kwargs = MockContainer.mock_calls[0]
2658+ args[len(args)-1](MockContainer.return_value)
2659+ self._dispatcher.list_apps('palpatine')
2660+ MockContainer.assert_has_calls([ # verify container constructed twice
2661+ unittest.mock.call('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY),
2662+ unittest.mock.call('palpatine', unittest.mock.ANY, unittest.mock.ANY, self._connection, unittest.mock.ANY)
2663+ ], any_order=True)
2664+
2665+ def test_container_info_creates_container_info_task(self):
2666+ with unittest.mock.patch('libertine.service.task_dispatcher.ContainerInfoTask') as MockContainerInfoTask:
2667+ task = MockContainerInfoTask.return_value
2668+ task.id = 123
2669+ self.assertEqual(123, self._dispatcher.container_info('palpatine'))
2670+ MockContainerInfoTask.assert_called_once_with('palpatine', [], unittest.mock.ANY, self._connection, unittest.mock.ANY)
2671+ task.start.assert_called_once_with()
2672+
2673+ def test_container_info_forwards_container_task_ids(self):
2674+ with unittest.mock.patch('libertine.service.task_dispatcher.ContainerInfoTask') as MockContainerInfoTask:
2675+ task = MockContainerInfoTask.return_value
2676+ with unittest.mock.patch('libertine.service.task_dispatcher.Container') as MockContainer:
2677+ c = MockContainer.return_value
2678+ c.tasks = [1, 2, 3]
2679+ c.id = 'palpatine'
2680+ self._dispatcher.list_apps('palpatine') # creates container
2681+ self._dispatcher.container_info('palpatine')
2682+ MockContainerInfoTask.assert_called_once_with('palpatine', [1, 2, 3], unittest.mock.ANY, self._connection, unittest.mock.ANY)
2683+ task.start.assert_called_once_with()
2684+
2685+ def test_list_creates_list_task(self):
2686+ with unittest.mock.patch('libertine.service.task_dispatcher.ListTask') as MockListTask:
2687+ task = MockListTask.return_value
2688+ task.id = 123
2689+ self.assertEqual(123, self._dispatcher.list())
2690+ MockListTask.assert_called_once_with(self._connection, unittest.mock.ANY)
2691+ task.start.assert_called_once_with()
2692+
2693+ def test_containerless_tasks_are_cleaned_up(self):
2694+ with unittest.mock.patch('libertine.service.task_dispatcher.ListTask') as MockListTask:
2695+ self._dispatcher.list()
2696+ MockListTask.assert_called_once_with(self._connection, unittest.mock.ANY)
2697+ name, args, kwargs = MockListTask.mock_calls[0]
2698+ self.assertEqual(1, len(self._dispatcher._tasks))
2699+ args[len(args)-1](MockListTask.return_value)
2700+ self.assertEqual(0, len(self._dispatcher._tasks))
2701+
2702+
2703+if __name__ == '__main__':
2704+ unittest.main()
2705
2706=== modified file 'tools/CMakeLists.txt'
2707--- tools/CMakeLists.txt 2016-08-04 01:39:08 +0000
2708+++ tools/CMakeLists.txt 2016-11-16 17:34:14 +0000
2709@@ -1,4 +1,4 @@
2710-install(PROGRAMS libertine-container-manager libertine-launch libertine-lxc-manager libertine-xmir libertine-lxc-setup
2711+install(PROGRAMS libertine-container-manager libertine-launch libertine-lxc-manager libertine-xmir libertine-lxc-setup libertined
2712 DESTINATION ${CMAKE_INSTALL_BINDIR})
2713 install(FILES libertine-launch.1 libertine-container-manager.1 libertine-lxc-manager.1 libertine-xmir.1
2714 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
2715
2716=== modified file 'tools/libertine-container-manager'
2717--- tools/libertine-container-manager 2016-11-09 18:25:33 +0000
2718+++ tools/libertine-container-manager 2016-11-16 17:34:14 +0000
2719@@ -183,7 +183,7 @@
2720 container = LibertineContainer(container_id)
2721
2722 self.containers_config.update_container_install_status(container_id, "updating")
2723- if container.update_libertine_container(args.verbosity) is not 0:
2724+ if not container.update_libertine_container(args.verbosity):
2725 self.containers_config.update_container_install_status(container_id, "ready")
2726 sys.exit(1)
2727
2728
2729=== added file 'tools/libertined'
2730--- tools/libertined 1970-01-01 00:00:00 +0000
2731+++ tools/libertined 2016-11-16 17:34:14 +0000
2732@@ -0,0 +1,141 @@
2733+#!/usr/bin/python3
2734+# -*- coding: utf-8 -*-
2735+
2736+# Copyright 2016 Canonical Ltd.
2737+#
2738+# This program is free software: you can redistribute it and/or modify
2739+# it under the terms of the GNU General Public License as published by
2740+# the Free Software Foundation; version 3 of the License.
2741+#
2742+# This program is distributed in the hope that it will be useful,
2743+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2744+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2745+# GNU General Public License for more details.
2746+#
2747+# You should have received a copy of the GNU General Public License
2748+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2749+
2750+import argparse
2751+import signal
2752+import sys
2753+import os
2754+from gi.repository import GLib, GObject
2755+from libertine import utils
2756+from libertine.service import manager
2757+
2758+
2759+class OutputRedirector(object):
2760+ def __init__(self, config):
2761+ self.config = config
2762+ self.cache_path = '%s/.cache/libertined' % os.environ['HOME']
2763+ self.cache_file = '%s/libertined.log' % self.cache_path
2764+ self.output_file = None
2765+ self.copied_stdout = None
2766+ self.copied_stderr = None
2767+
2768+ def _fileno(self, file_or_fd):
2769+ return getattr(file_or_fd, 'fileno', lambda: file_or_fd)()
2770+
2771+ def _do_redirect(self, stream):
2772+ fd = self._fileno(stream)
2773+ copied_stream = os.fdopen(os.dup(fd), 'wb')
2774+ stream.flush()
2775+ os.dup2(self._fileno(self.output_file), fd)
2776+ return copied_stream, fd
2777+
2778+ def _undo_redirect(self, stream, stream_fd, copied_stream):
2779+ stream.flush()
2780+ os.dup2(copied_stream.fileno(), stream_fd)
2781+ copied_stream.close()
2782+
2783+ def _rotate_logs(self):
2784+ os.makedirs(self.cache_path, exist_ok=True)
2785+ num_backups = 3
2786+ for i in range(num_backups, 0, -1):
2787+ filename = '%s.%i' % (self.cache_file, i)
2788+ if os.path.exists(filename):
2789+ if i == num_backups:
2790+ os.remove(filename)
2791+ else:
2792+ os.rename(filename, '%s.%i' % (self.cache_file, i+1))
2793+ if os.path.exists(self.cache_file):
2794+ os.rename(self.cache_file, '%s.1' % self.cache_file)
2795+
2796+ def __enter__(self):
2797+ if self.config.debug:
2798+ os.environ['LIBERTINE_DEBUG'] = '1'
2799+ else:
2800+ if self.config.cache_output:
2801+ os.environ['LIBERTINE_DEBUG'] = '1'
2802+ self._rotate_logs()
2803+ self.output_file = open(self.cache_file, 'w')
2804+ else:
2805+ self.output_file = open(os.devnull, 'w')
2806+
2807+ self.copied_stdout, self.stdout_fd = self._do_redirect(sys.stdout)
2808+ self.copied_stderr, self.stderr_fd = self._do_redirect(sys.stderr)
2809+
2810+ def __exit__(self, type, value, tb):
2811+ if self.copied_stdout and self.stdout_fd:
2812+ self._undo_redirect(sys.stdout, self.stdout_fd, self.copied_stdout)
2813+ if self.copied_stderr and self.stderr_fd:
2814+ self._undo_redirect(sys.stderr, self.stderr_fd, self.copied_stderr)
2815+ if self.output_file:
2816+ self.output_file.close()
2817+
2818+
2819+class Config(object):
2820+ def __init__(self):
2821+ self._arg_parser = argparse.ArgumentParser(description=u'Libertine Store service')
2822+ self._arg_parser.add_argument(u'-l', u"--use-local-cache",
2823+ action='store_true',
2824+ default=False,
2825+ help=u"use local cache instead of system cache")
2826+ self._arg_parser.add_argument(u'-d', u"--debug",
2827+ action='store_true',
2828+ default=False,
2829+ help=u"allow all output on stdout")
2830+ self._arg_parser.add_argument(u'-c', u"--cache-output",
2831+ action='store_true',
2832+ default=False,
2833+ help=u"Log to $HOME/.cache/libertined/ instead of stdout")
2834+ args = self._arg_parser.parse_args(namespace=Config)
2835+
2836+
2837+class Loop(object):
2838+ def __init__(self):
2839+ GLib.unix_signal_add(GLib.PRIORITY_HIGH,
2840+ signal.SIGTERM,
2841+ self.sigterm,
2842+ None)
2843+ self.loop = GLib.MainLoop()
2844+
2845+ def sigterm(self, code):
2846+ utils.get_logger().info("terminate ('%s') signal received" % code)
2847+ self.shutdown()
2848+
2849+ def shutdown(self):
2850+ utils.get_logger().info("shutting service down")
2851+ self.loop.quit()
2852+
2853+ def run(self):
2854+ utils.get_logger().debug("entering main loop")
2855+ self.loop.run()
2856+
2857+
2858+def main():
2859+ config = Config()
2860+
2861+ with OutputRedirector(config):
2862+ utils.get_logger().info("Starting libertine service")
2863+ service = manager.Manager()
2864+ loop = Loop()
2865+ try:
2866+ loop.run()
2867+ except KeyboardInterrupt:
2868+ utils.get_logger().debug("keyboard interrupt received")
2869+ loop.shutdown()
2870+
2871+
2872+if __name__ == '__main__':
2873+ main()

Subscribers

People subscribed via source and target branches