Merge ~silverdrake11/ubuntu/+source/landscape-client:ubuntu/mantic-devel into ubuntu/+source/landscape-client:ubuntu/mantic-devel

Proposed by Kevin Nasto
Status: Merged
Merged at revision: 08afd8dad6238cb5f8f50865e928296f20169759
Proposed branch: ~silverdrake11/ubuntu/+source/landscape-client:ubuntu/mantic-devel
Merge into: ubuntu/+source/landscape-client:ubuntu/mantic-devel
Diff against target: 57671 lines (+22146/-11153)
300 files modified
.flake8 (+5/-0)
.gitignore (+6/-0)
.pre-commit-config.yaml (+43/-0)
LICENSE (+5/-5)
Makefile (+45/-4)
README (+103/-2)
debian/README.source (+1/-1)
debian/changelog (+37/-15)
debian/copyright (+0/-1)
debian/landscape-client.postrm (+1/-1)
debian/watch (+1/-1)
dev/landscape-client-vm (+2/-2)
dev/null (+0/-66)
dev/upload-to-ppa (+1/-1)
display_py2_testresults (+6/-4)
example.conf (+17/-1)
landscape-client.conf (+0/-1)
landscape/__init__.py (+2/-2)
landscape/client/accumulate.py (+15/-8)
landscape/client/amp.py (+9/-7)
landscape/client/broker/amp.py (+24/-19)
landscape/client/broker/client.py (+44/-25)
landscape/client/broker/config.py (+62/-31)
landscape/client/broker/exchange.py (+109/-65)
landscape/client/broker/exchangestore.py (+21/-8)
landscape/client/broker/ping.py (+40/-20)
landscape/client/broker/registration.py (+62/-43)
landscape/client/broker/server.py (+35/-19)
landscape/client/broker/service.py (+60/-24)
landscape/client/broker/store.py (+78/-19)
landscape/client/broker/tests/helpers.py (+82/-45)
landscape/client/broker/tests/test_amp.py (+28/-19)
landscape/client/broker/tests/test_client.py (+33/-18)
landscape/client/broker/tests/test_config.py (+28/-17)
landscape/client/broker/tests/test_exchange.py (+276/-154)
landscape/client/broker/tests/test_exchangestore.py (+21/-12)
landscape/client/broker/tests/test_ping.py (+42/-27)
landscape/client/broker/tests/test_registration.py (+136/-85)
landscape/client/broker/tests/test_server.py (+109/-54)
landscape/client/broker/tests/test_service.py (+7/-7)
landscape/client/broker/tests/test_store.py (+173/-68)
landscape/client/broker/tests/test_transport.py (+75/-35)
landscape/client/broker/transport.py (+60/-27)
landscape/client/configuration.py (+326/-232)
landscape/client/deployment.py (+95/-50)
landscape/client/lockfile.py (+2/-2)
landscape/client/manager/aptsources.py (+43/-21)
landscape/client/manager/config.py (+41/-22)
landscape/client/manager/customgraph.py (+75/-34)
landscape/client/manager/fakepackagemanager.py (+40/-24)
landscape/client/manager/hardwareinfo.py (+10/-4)
landscape/client/manager/keystonetoken.py (+23/-14)
landscape/client/manager/manager.py (+2/-2)
landscape/client/manager/packagemanager.py (+30/-16)
landscape/client/manager/plugin.py (+17/-9)
landscape/client/manager/processkiller.py (+35/-21)
landscape/client/manager/scriptexecution.py (+97/-55)
landscape/client/manager/service.py (+21/-13)
landscape/client/manager/shutdownmanager.py (+47/-29)
landscape/client/manager/snapmanager.py (+271/-0)
landscape/client/manager/store.py (+30/-16)
landscape/client/manager/tests/test_aptsources.py (+237/-92)
landscape/client/manager/tests/test_config.py (+26/-13)
landscape/client/manager/tests/test_customgraph.py (+460/-229)
landscape/client/manager/tests/test_fakepackagemanager.py (+39/-20)
landscape/client/manager/tests/test_hardwareinfo.py (+9/-6)
landscape/client/manager/tests/test_keystonetoken.py (+20/-15)
landscape/client/manager/tests/test_manager.py (+2/-2)
landscape/client/manager/tests/test_packagemanager.py (+51/-29)
landscape/client/manager/tests/test_plugin.py (+44/-17)
landscape/client/manager/tests/test_processkiller.py (+173/-79)
landscape/client/manager/tests/test_scriptexecution.py (+377/-176)
landscape/client/manager/tests/test_service.py (+8/-6)
landscape/client/manager/tests/test_shutdownmanager.py (+48/-18)
landscape/client/manager/tests/test_snapmanager.py (+259/-0)
landscape/client/manager/tests/test_store.py (+10/-12)
landscape/client/manager/tests/test_usermanager.py (+1068/-563)
landscape/client/manager/usermanager.py (+70/-44)
landscape/client/monitor/activeprocessinfo.py (+18/-10)
landscape/client/monitor/aptpreferences.py (+10/-7)
landscape/client/monitor/cephusage.py (+49/-22)
landscape/client/monitor/computerinfo.py (+75/-38)
landscape/client/monitor/computertags.py (+1/-1)
landscape/client/monitor/computeruptime.py (+24/-13)
landscape/client/monitor/config.py (+32/-11)
landscape/client/monitor/cpuusage.py (+40/-19)
landscape/client/monitor/livepatch.py (+71/-0)
landscape/client/monitor/loadaverage.py (+34/-15)
landscape/client/monitor/memoryinfo.py (+37/-16)
landscape/client/monitor/monitor.py (+10/-5)
landscape/client/monitor/mountinfo.py (+48/-28)
landscape/client/monitor/networkactivity.py (+30/-13)
landscape/client/monitor/networkdevice.py (+12/-9)
landscape/client/monitor/packagemonitor.py (+54/-29)
landscape/client/monitor/plugin.py (+15/-7)
landscape/client/monitor/processorinfo.py (+44/-23)
landscape/client/monitor/rebootrequired.py (+14/-8)
landscape/client/monitor/service.py (+41/-17)
landscape/client/monitor/snapmonitor.py (+44/-0)
landscape/client/monitor/swiftusage.py (+37/-18)
landscape/client/monitor/temperature.py (+40/-18)
landscape/client/monitor/tests/test_activeprocessinfo.py (+679/-288)
landscape/client/monitor/tests/test_aptpreferences.py (+84/-41)
landscape/client/monitor/tests/test_cephusage.py (+28/-14)
landscape/client/monitor/tests/test_computerinfo.py (+108/-56)
landscape/client/monitor/tests/test_computertags.py (+14/-13)
landscape/client/monitor/tests/test_computeruptime.py (+112/-40)
landscape/client/monitor/tests/test_config.py (+6/-4)
landscape/client/monitor/tests/test_cpuusage.py (+14/-10)
landscape/client/monitor/tests/test_livepatch.py (+140/-0)
landscape/client/monitor/tests/test_loadaverage.py (+52/-23)
landscape/client/monitor/tests/test_memoryinfo.py (+48/-22)
landscape/client/monitor/tests/test_monitor.py (+12/-6)
landscape/client/monitor/tests/test_mountinfo.py (+333/-139)
landscape/client/monitor/tests/test_networkactivity.py (+46/-25)
landscape/client/monitor/tests/test_networkdevice.py (+7/-6)
landscape/client/monitor/tests/test_packagemonitor.py (+40/-26)
landscape/client/monitor/tests/test_plugin.py (+31/-14)
landscape/client/monitor/tests/test_processorinfo.py (+106/-78)
landscape/client/monitor/tests/test_rebootrequired.py (+49/-27)
landscape/client/monitor/tests/test_service.py (+11/-9)
landscape/client/monitor/tests/test_snapmonitor.py (+50/-0)
landscape/client/monitor/tests/test_swiftusage.py (+154/-91)
landscape/client/monitor/tests/test_temperature.py (+59/-35)
landscape/client/monitor/tests/test_ubuntuproinfo.py (+5/-5)
landscape/client/monitor/tests/test_ubuntuprorebootrequired.py (+29/-0)
landscape/client/monitor/tests/test_updatemanager.py (+16/-10)
landscape/client/monitor/tests/test_usermonitor.py (+327/-144)
landscape/client/monitor/ubuntuproinfo.py (+11/-7)
landscape/client/monitor/ubuntuprorebootrequired.py (+27/-0)
landscape/client/monitor/updatemanager.py (+11/-12)
landscape/client/monitor/usermonitor.py (+40/-21)
landscape/client/package/changer.py (+134/-69)
landscape/client/package/releaseupgrader.py (+101/-49)
landscape/client/package/reporter.py (+200/-126)
landscape/client/package/taskhandler.py (+42/-25)
landscape/client/package/tests/test_changer.py (+705/-355)
landscape/client/package/tests/test_releaseupgrader.py (+349/-214)
landscape/client/package/tests/test_reporter.py (+663/-303)
landscape/client/package/tests/test_taskhandler.py (+82/-39)
landscape/client/patch.py (+8/-6)
landscape/client/reactor.py (+1/-1)
landscape/client/service.py (+30/-17)
landscape/client/serviceconfig.py (+130/-0)
landscape/client/snap/__init__.py (+0/-0)
landscape/client/snap/http.py (+302/-0)
landscape/client/snap/tests/__init__.py (+0/-0)
landscape/client/snap/tests/test_http.py (+51/-0)
landscape/client/tests/clock.py (+66/-46)
landscape/client/tests/helpers.py (+76/-53)
landscape/client/tests/subunit.py (+133/-106)
landscape/client/tests/test_accumulate.py (+3/-2)
landscape/client/tests/test_amp.py (+33/-27)
landscape/client/tests/test_configuration.py (+1241/-862)
landscape/client/tests/test_deployment.py (+44/-29)
landscape/client/tests/test_diff.py (+4/-4)
landscape/client/tests/test_lockfile.py (+7/-3)
landscape/client/tests/test_patch.py (+11/-8)
landscape/client/tests/test_reactor.py (+1/-2)
landscape/client/tests/test_service.py (+14/-11)
landscape/client/tests/test_serviceconfig.py (+223/-0)
landscape/client/tests/test_watchdog.py (+377/-226)
landscape/client/upgraders/__init__.py (+5/-2)
landscape/client/upgraders/tests/test_broker.py (+1/-3)
landscape/client/upgraders/tests/test_monitor.py (+1/-3)
landscape/client/upgraders/tests/test_package.py (+1/-3)
landscape/client/user/changes.py (+13/-7)
landscape/client/user/management.py (+142/-68)
landscape/client/user/provider.py (+83/-35)
landscape/client/user/tests/helpers.py (+78/-35)
landscape/client/user/tests/test_changes.py (+121/-63)
landscape/client/user/tests/test_management.py (+406/-168)
landscape/client/user/tests/test_provider.py (+503/-230)
landscape/client/watchdog.py (+187/-96)
landscape/constants.py (+15/-2)
landscape/lib/amp.py (+94/-50)
landscape/lib/apt/package/facade.py (+178/-107)
landscape/lib/apt/package/skeleton.py (+89/-44)
landscape/lib/apt/package/store.py (+111/-84)
landscape/lib/apt/package/testing.py (+243/-186)
landscape/lib/apt/package/tests/test_facade.py (+1063/-411)
landscape/lib/apt/package/tests/test_skeleton.py (+90/-37)
landscape/lib/apt/package/tests/test_store.py (+87/-55)
landscape/lib/backoff.py (+1/-1)
landscape/lib/base64.py (+0/-2)
landscape/lib/bootstrap.py (+4/-8)
landscape/lib/bpickle.py (+67/-63)
landscape/lib/cli.py (+9/-4)
landscape/lib/cloud.py (+8/-4)
landscape/lib/compat.py (+5/-0)
landscape/lib/config.py (+52/-33)
landscape/lib/disk.py (+44/-17)
landscape/lib/fd.py (+0/-1)
landscape/lib/fetch.py (+40/-19)
landscape/lib/format.py (+5/-5)
landscape/lib/fs.py (+0/-1)
landscape/lib/gpg.py (+25/-14)
landscape/lib/jiffies.py (+3/-3)
landscape/lib/juju.py (+3/-4)
landscape/lib/lock.py (+2/-2)
landscape/lib/log.py (+0/-2)
landscape/lib/logging.py (+51/-23)
landscape/lib/lsb_release.py (+4/-3)
landscape/lib/message.py (+3/-2)
landscape/lib/monitor.py (+70/-37)
landscape/lib/network.py (+63/-37)
landscape/lib/persist.py (+53/-35)
landscape/lib/plugin.py (+2/-4)
landscape/lib/process.py (+35/-17)
landscape/lib/reactor.py (+31/-19)
landscape/lib/schema.py (+63/-36)
landscape/lib/scriptcontent.py (+1/-1)
landscape/lib/sequenceranges.py (+9/-8)
landscape/lib/store.py (+3/-0)
landscape/lib/sysstats.py (+42/-26)
landscape/lib/testing.py (+184/-115)
landscape/lib/tests/test_amp.py (+128/-84)
landscape/lib/tests/test_backoff.py (+5/-6)
landscape/lib/tests/test_bootstrap.py (+17/-12)
landscape/lib/tests/test_bpickle.py (+11/-13)
landscape/lib/tests/test_cloud.py (+56/-27)
landscape/lib/tests/test_config.py (+113/-75)
landscape/lib/tests/test_disk.py (+42/-24)
landscape/lib/tests/test_encoding.py (+12/-11)
landscape/lib/tests/test_fd.py (+4/-5)
landscape/lib/tests/test_fetch.py (+253/-170)
landscape/lib/tests/test_format.py (+15/-11)
landscape/lib/tests/test_fs.py (+35/-27)
landscape/lib/tests/test_gpg.py (+37/-21)
landscape/lib/tests/test_juju.py (+43/-25)
landscape/lib/tests/test_lock.py (+4/-4)
landscape/lib/tests/test_logging.py (+22/-10)
landscape/lib/tests/test_lsb_release.py (+46/-29)
landscape/lib/tests/test_monitor.py (+54/-34)
landscape/lib/tests/test_network.py (+217/-138)
landscape/lib/tests/test_persist.py (+94/-74)
landscape/lib/tests/test_plugin.py (+8/-6)
landscape/lib/tests/test_process.py (+33/-24)
landscape/lib/tests/test_reactor.py (+26/-12)
landscape/lib/tests/test_schema.py (+55/-32)
landscape/lib/tests/test_scriptcontent.py (+7/-6)
landscape/lib/tests/test_sequenceranges.py (+25/-21)
landscape/lib/tests/test_sysstats.py (+82/-48)
landscape/lib/tests/test_tag.py (+23/-19)
landscape/lib/tests/test_timestamp.py (+1/-1)
landscape/lib/tests/test_twisted_util.py (+27/-12)
landscape/lib/tests/test_versioning.py (+2/-3)
landscape/lib/tests/test_vm_info.py (+10/-8)
landscape/lib/tests/test_warning.py (+9/-6)
landscape/lib/twisted_util.py (+43/-18)
landscape/lib/user.py (+1/-2)
landscape/lib/versioning.py (+6/-4)
landscape/lib/vm_info.py (+4/-3)
landscape/lib/warning.py (+0/-1)
landscape/message_schemas/__init__.py (+0/-1)
landscape/message_schemas/message.py (+8/-4)
landscape/message_schemas/server_bound.py (+682/-374)
landscape/message_schemas/test_message.py (+15/-9)
landscape/sysinfo/deployment.py (+67/-29)
landscape/sysinfo/disk.py (+26/-16)
landscape/sysinfo/landscapelink.py (+3/-3)
landscape/sysinfo/load.py (+4/-3)
landscape/sysinfo/loggedinusers.py (+2/-2)
landscape/sysinfo/memory.py (+9/-6)
landscape/sysinfo/network.py (+15/-7)
landscape/sysinfo/processes.py (+18/-8)
landscape/sysinfo/sysinfo.py (+40/-21)
landscape/sysinfo/temperature.py (+4/-5)
landscape/sysinfo/testplugin.py (+1/-2)
landscape/sysinfo/tests/test_deployment.py (+93/-52)
landscape/sysinfo/tests/test_disk.py (+113/-50)
landscape/sysinfo/tests/test_landscapelink.py (+7/-5)
landscape/sysinfo/tests/test_load.py (+4/-7)
landscape/sysinfo/tests/test_loggedinusers.py (+14/-10)
landscape/sysinfo/tests/test_memory.py (+8/-7)
landscape/sysinfo/tests/test_network.py (+54/-39)
landscape/sysinfo/tests/test_processes.py (+38/-17)
landscape/sysinfo/tests/test_sysinfo.py (+182/-136)
landscape/sysinfo/tests/test_temperature.py (+11/-9)
man/landscape-client.1 (+4/-4)
man/landscape-client.txt (+4/-4)
man/landscape-config.1 (+28/-20)
man/landscape-config.txt (+18/-14)
man/landscape-sysinfo.txt (+6/-7)
pqm-tests.sh (+0/-1)
pyproject.toml (+13/-0)
scripts/landscape-broker (+3/-1)
scripts/landscape-client (+5/-2)
scripts/landscape-config (+4/-1)
scripts/landscape-manager (+3/-1)
scripts/landscape-monitor (+3/-1)
scripts/landscape-package-changer (+3/-1)
scripts/landscape-package-reporter (+3/-1)
scripts/landscape-release-upgrader (+3/-1)
scripts/landscape-sysinfo (+6/-2)
setup.py (+34/-29)
setup_client.py (+30/-29)
setup_lib.py (+19/-18)
setup_sysinfo.py (+8/-9)
snap/snapcraft.yaml (+98/-0)
Reviewer Review Type Date Requested Status
Lucas Kanashiro (community) Approve
git-ubuntu import Pending
Review via email: mp+449306@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Kevin Nasto (silverdrake11) wrote :
Revision history for this message
Lucas Kanashiro (lucaskanashiro) wrote :

Thanks for this MP Kevin!

Initially, I needed to update the d/watch to be able to fetch the upstream tarball:

diff --git a/debian/watch b/debian/watch
index 3592b51..ba7ea91 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,3 +1,3 @@
 version=4
 opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/landscape-client-$1\.tar\.gz/ \
- https://github.com/CanonicalLtd/landscape-client/tags .*/?(\d{2}\.\d.\d)\.tar\.gz
+ https://github.com/CanonicalLtd/landscape-client/tags .*/?(\d{2}\.\d{2})\.tar\.gz

I'd recommend to incorporate that change to your package.

I talked to Kevin privately and also asked him to update this line:

https://github.com/canonical/landscape-client/blob/23.08/landscape/__init__.py#L2

to 23.08 (new upstream release).

The only other change related to the package itself that I found is in this commit:

commit a0ecf857a8a883d4c1bb67e5c563de7c6047cc50
Author: Kevin Nasto <email address hidden>
Date: Fri Mar 10 13:25:12 2023 -0600

    Changed gpg file name to be less generic (LP: #2008432) (#137)

This is the change and I believe it is fine to get in:

diff --git a/debian/landscape-client.postrm b/debian/landscape-client.postrm
index 10f79b8..aa34ed2 100644
--- a/debian/landscape-client.postrm
+++ b/debian/landscape-client.postrm
@@ -29,7 +29,7 @@ case "$1" in
     rm -f "${LOG_DIR}/package-reporter.log"*
     rm -f "${LOG_DIR}/package-changer.log"*

- rm -f "${GPG_DIR}/landscape-server"*.asc
+ rm -f "${GPG_DIR}/landscape-server-mirror"*.asc

     rm -rf "${DATA_DIR}/client"
     rm -rf "${DATA_DIR}/.gnupg"

There are 2 lintian errors which are red flags in a first glance:

E: landscape-client source: alien-tag dh_python-is-obsolete [debian/source.lintian-overrides:4]
N:
N: The given override refers to an unknown tag.
N:
N: Please refer to Format of override files (Section 2.4.1) in the Lintian
N: User's Manual for details.
N:
N: Visibility: error
N: Show-Always: yes
N: Check: debian/lintian-overrides/mystery
N:
N:
E: landscape-common: depends-on-obsolete-package Depends: lsb-base
N:
N: The package depends on a package that has been superseded. If the
N: superseded package is part of an ORed group, it should not be the first
N: package in the group.
N:
N: Visibility: error
N: Show-Always: no
N: Check: fields/package-relations
N:
N:

The first one seems to me you added a lintian overrides which will be used just in some ubuntu releases. But it seems this tag does not exist anymore, at least in Mantic's lintian version. I'd remove that TBH.

The second one is about the dependency on lsb-base. I'd consider its removal as well since it is obsolete, but if you are thinking about backporting this to other releases, please check if it is obsolete there as well.

review: Needs Information
Revision history for this message
Lucas Kanashiro (lucaskanashiro) :
review: Needs Fixing
Revision history for this message
Lucas Kanashiro (lucaskanashiro) wrote :

Thanks for addressing my comments Kevin!

Since you decided to remove the dependency on lsb-base, I think you should remove it from here as well:

https://github.com/canonical/landscape-client/blob/23.08/setup_lib.py#L21

review: Needs Fixing
Revision history for this message
Lucas Kanashiro (lucaskanashiro) wrote :

As discussed with Kevin, let's keep lsb-base so he can test it later to make sure it will not impact other supported ubuntu releases.

There are also some improvements that can be made in the package reported by lintian, I will not block this MP because of this, the feature freeze is coming, let's get this uploaded.

review: Approve
Revision history for this message
Lucas Kanashiro (lucaskanashiro) wrote :

Package uploaded:

Uploading landscape-client_23.08-0ubuntu1.dsc
Uploading landscape-client_23.08.orig.tar.gz
Uploading landscape-client_23.08-0ubuntu1.debian.tar.xz
Uploading landscape-client_23.08-0ubuntu1_source.buildinfo
Uploading landscape-client_23.08-0ubuntu1_source.changes

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.flake8 b/.flake8
2new file mode 100644
3index 0000000..c3a3cd2
4--- /dev/null
5+++ b/.flake8
6@@ -0,0 +1,5 @@
7+[flake8]
8+max-line-length = 79
9+max-complexity = 18
10+select = B,C,E,F,W,T4,B9
11+ignore = E203, W503
12diff --git a/.gitignore b/.gitignore
13index ca1df98..8ce4558 100644
14--- a/.gitignore
15+++ b/.gitignore
16@@ -25,3 +25,9 @@ tags
17 _last_py2_res
18 *.pyc
19 .cache
20+_trial_temp*
21+*.snap
22+landscape-client_amd64*.txt
23+landscape-client_arm64*.txt
24+landscape-client_ppc64el*.txt
25+landscape-client_s390x*.txt
26diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
27new file mode 100644
28index 0000000..df96450
29--- /dev/null
30+++ b/.pre-commit-config.yaml
31@@ -0,0 +1,43 @@
32+# See https://pre-commit.com for more information
33+# See https://pre-commit.com/hooks.html for more hooks
34+repos:
35+- repo: https://github.com/pre-commit/pre-commit-hooks
36+ rev: v4.4.0
37+ hooks:
38+ - id: trailing-whitespace
39+ - id: end-of-file-fixer
40+ - id: check-yaml
41+ - id: check-added-large-files
42+ - id: debug-statements
43+- repo: https://github.com/pre-commit/pre-commit-hooks
44+ rev: v2.3.0
45+ hooks:
46+ - id: flake8
47+ args:
48+ - "--max-line-length=79"
49+ - "--select=B,C,E,F,W,T4,B9"
50+ - "--ignore=E203,W503"
51+- repo: https://github.com/psf/black
52+ rev: 22.12.0
53+ hooks:
54+ - id: black
55+ args:
56+ - --line-length=79
57+ - --include='\.pyi?$'
58+- repo: https://github.com/asottile/reorder_python_imports
59+ rev: v2.3.0
60+ hooks:
61+ - id: reorder-python-imports
62+ args: [--py3-plus]
63+- repo: https://github.com/asottile/add-trailing-comma
64+ rev: v2.0.1
65+ hooks:
66+ - id: add-trailing-comma
67+ args: [--py36-plus]
68+exclude: >
69+ (?x)(
70+ \.git
71+ | \.csv$
72+ | \.__pycache__
73+ | \.log$
74+ )
75diff --git a/LICENSE b/LICENSE
76index 5b6e7c6..dcfa4c2 100644
77--- a/LICENSE
78+++ b/LICENSE
79@@ -55,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all.
80
81 The precise terms and conditions for copying, distribution and
82 modification follow.
83-
84
85+
86 GNU GENERAL PUBLIC LICENSE
87 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
88
89@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
90 License. (Exception: if the Program itself is interactive but
91 does not normally print such an announcement, your work based on
92 the Program is not required to print an announcement.)
93-
94
95+
96 These requirements apply to the modified work as a whole. If
97 identifiable sections of that work are not derived from the Program,
98 and can be reasonably considered independent and separate works in
99@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
100 access to copy the source code from the same place counts as
101 distribution of the source code, even though third parties are not
102 compelled to copy the source along with the object code.
103-
104
105+
106 4. You may not copy, modify, sublicense, or distribute the Program
107 except as expressly provided under this License. Any attempt
108 otherwise to copy, modify, sublicense or distribute the Program is
109@@ -225,7 +225,7 @@ impose that choice.
110
111 This section is intended to make thoroughly clear what is believed to
112 be a consequence of the rest of this License.
113-
114
115+
116 8. If the distribution and/or use of the Program is restricted in
117 certain countries either by patents or by copyrighted interfaces, the
118 original copyright holder who places the Program under this License
119@@ -278,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
120 POSSIBILITY OF SUCH DAMAGES.
121
122 END OF TERMS AND CONDITIONS
123-
124
125+
126 How to Apply These Terms to Your New Programs
127
128 If you develop a new program, and you want it to be of the greatest
129diff --git a/Makefile b/Makefile
130index fe183ec..09ade0e 100644
131--- a/Makefile
132+++ b/Makefile
133@@ -2,9 +2,15 @@ PYDOCTOR ?= pydoctor
134 TXT2MAN ?= txt2man
135 PYTHON2 ?= python2
136 PYTHON3 ?= python3
137+SNAPCRAFT = SNAPCRAFT_BUILD_INFO=1 snapcraft
138 TRIAL ?= -m twisted.trial
139 TRIAL_ARGS ?=
140
141+# PEP8 rules ignored:
142+# W503 https://www.flake8rules.com/rules/W503.html
143+# E203 Whitespace before ':' (enforced by Black)
144+PEP8_IGNORED = W503,E203
145+
146 .PHONY: help
147 help: ## Print help about available targets
148 @grep -h -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
149@@ -15,11 +21,15 @@ depends: depends3 ## py2 is deprecated
150
151 .PHONY: depends2
152 depends2:
153- sudo apt-get -y install python-twisted-core python-distutils-extra python-mock python-configobj python-netifaces python-pycurl
154+ sudo apt-get -y install python-twisted-core python-distutils-extra python-mock python-configobj python-netifaces python-pycurl python-pip
155+ pip install pre-commit
156+ pre-commit install
157
158 .PHONY: depends3
159 depends3:
160- sudo apt-get -y install python3-twisted python3-distutils-extra python3-mock python3-configobj python3-netifaces python3-pycurl
161+ sudo apt-get -y install python3-twisted python3-distutils-extra python3-mock python3-configobj python3-netifaces python3-pycurl python3-pip
162+ pip3 install pre-commit
163+ pre-commit install
164
165 all: build
166
167@@ -56,13 +66,15 @@ coverage:
168
169 .PHONY: lint
170 lint:
171- $(PYTHON3) -m flake8 --ignore E24,E121,E123,E125,E126,E221,E226,E266,E704,E265,W504 \
172- `find landscape -name \*.py`
173+ $(PYTHON3) -m flake8 --ignore $(PEP8_IGNORED) `find landscape -name \*.py`
174
175 .PHONY: pyflakes
176 pyflakes:
177 -pyflakes `find landscape -name \*.py`
178
179+pre-commit:
180+ -pre-commit run -a
181+
182 clean:
183 -find landscape -name __pycache__ -exec rm -rf {} \;
184 -find landscape -name \*.pyc -exec rm -f {} \;
185@@ -115,6 +127,35 @@ tags:
186 etags:
187 -etags --languages=python -R .
188
189+snap-install:
190+ sudo snap install --devmode landscape-client_0.1_amd64.snap
191+.PHONY: snap-install
192+
193+snap-remote-build:
194+ snapcraft remote-build
195+.PHONY: snap-remote-build
196+
197+snap-remove:
198+ sudo snap remove --purge landscape-client
199+.PHONY: snap-remove
200+
201+snap-shell: snap-install
202+ sudo snap run --shell landscape-client.landscape-client
203+.PHONY: snap-shell
204+
205+snap-debug:
206+ $(SNAPCRAFT) -v --debug
207+.PHONY: snap-debug
208+
209+snap-clean: snap-remove
210+ $(SNAPCRAFT) clean
211+ -rm landscape-client_0.1_amd64.snap
212+.PHONY: snap-clean
213+
214+snap:
215+ $(SNAPCRAFT)
216+.PHONY: snap
217+
218 include Makefile.packaging
219
220 .DEFAULT_GOAL := help
221diff --git a/README b/README
222index 06be0e1..8751ef3 100644
223--- a/README
224+++ b/README
225@@ -5,7 +5,7 @@
226
227 Add our beta PPA to get the latest updates to the landscape-client package
228
229-#### Add repo to an Ubuntu series
230+#### Add repo to an Ubuntu series
231 ```
232 sudo add-apt-repository ppa:landscape/self-hosted-beta
233 ```
234@@ -50,7 +50,7 @@ monitor_only = true
235
236 ## Running
237
238-Now you can complete the configuration of your client and register with the
239+Now you can complete the configuration of your client and register with the
240 Landscape service. There are two ways to do this:
241
242 1. `sudo landscape-config` and answer interactive prompts to finalize your configuration
243@@ -86,3 +86,104 @@ Before opening a PR, make sure to run the full testsuite and lint
244 make check3
245 make lint
246 ```
247+
248+### Building the Landscape Client snap
249+
250+First, you need to ensure that you have the appropriate tools installed:
251+```
252+$ sudo snap install snapcraft --classic
253+$ lxd init --auto
254+```
255+
256+There are various make targets defined to assist in the lifecycle of
257+building and testing the snap. To simply build the snap with the minimum
258+of debug information displayed:
259+```
260+$ make snap
261+```
262+
263+If you would prefer to see more information displayed showing the progress
264+of the build, and would like to get dropped into a debug shell within the
265+snap container in the event of an error:
266+```
267+$ make snap-debug
268+```
269+
270+To install the resulting snap:
271+```
272+$ make snap-install
273+```
274+
275+To remove a previously installed snap:
276+```
277+$ make snap-remove
278+```
279+
280+To clean the intermediate files as well as the snap itself from the local
281+build environment:
282+```
283+$ make snap-clean
284+```
285+
286+To enter into a shell environment within the snap container:
287+```
288+$ make snap-shell
289+```
290+
291+If you wish to upload the snap to the store, you will first need to get
292+credentials with the store that allow you to log in locally and publish
293+binaries. This can be done at:
294+
295+https://snapcraft.io/docs/creating-your-developer-account
296+
297+After obtaining and confirming your store credentials, you then need to
298+log in using the snapcraft tool:
299+```
300+$ snapcraft login
301+```
302+
303+Since snapcraft version 7.x and higher, the credentials are stored in the
304+gnome keyring on your local workstation. If you are building in an
305+environment without a GUI (e.g. in a multipass or lxc container), you
306+will need to install the gnome keyring tools:
307+```
308+$ sudo apt install gnome-keyring
309+```
310+
311+You will then need to initialze the default keyring as follows:
312+```
313+$ dbus-run-session -- bash
314+$ gnome-keyring-daemon --unlock
315+```
316+The gnome-keyring-daemon will prompt you (without a prompt) to type in
317+the initial unlock password (typically the same password for the account
318+you are using - if you are using the default multipass or lxc "ubuntu"
319+login, use sudo passwd ubuntu to set it to a known value before doing
320+the above step).
321+
322+Type the login password and hit <ENTER> followed by <CTRL>+D to end
323+the input.
324+
325+At this point, you should be able to log into snapcraft:
326+```
327+$ snapcraft login
328+```
329+You will be prompted for your UbuntuOne email address, password and,
330+if set up this way, your second factor for authentication. If you
331+are successful, you should be able to query your login credentials
332+with:
333+```
334+$ snapcraft whoami
335+```
336+A common mistake that first-time users of this process might make
337+is that after running the gnome-keyring-daemon command, they will
338+exit the dbus session shell. Do NOT do that. Do all subsequent
339+work in that bash shell that dbus set up for you because it will
340+have access to your gnome-keyring.
341+
342+If you need to leave the environment and get back in, keep in mind
343+that you do not have to be logged into the store UNLESS you are
344+uploading the snap or otherwise manipulating things in your store
345+account. You will need to repeat the dbus-run-session and
346+gnome-keyring-daemon steps BEFORE logging in if you do need to be
347+logged into the store.
348diff --git a/debian/README.source b/debian/README.source
349index d21d7e4..20371fc 100644
350--- a/debian/README.source
351+++ b/debian/README.source
352@@ -12,4 +12,4 @@ appreciated if you also updated the UPSTREAM_VERSION variable in
353 landscape/__init__.py to include the entire new client version number. This
354 helps us keep track of exact version of clients in use. There's no need to
355 update the DEBIAN_REVISION variable, as it gets automatically set at build
356-time.
357+time.
358diff --git a/debian/changelog b/debian/changelog
359index 4ba984f..2479e40 100644
360--- a/debian/changelog
361+++ b/debian/changelog
362@@ -1,3 +1,25 @@
363+landscape-client (23.08-0ubuntu1) mantic; urgency=medium
364+
365+ * New upstream release 23.08
366+ - Restructure landscape-config wizard (LP: #2031673)
367+ - Add SnapMonitor, SnapManager, and related machinery (LP: #2031682)
368+ - Limit message data directory size (LP: #2004591)
369+ - Add zfs and btrfs to stable fs-lists (LP: #1499104)
370+ - Test for broken control file (LP: #1813442)
371+ - Config man page and help info fixes (LP: #1662222)
372+ - Removed unnecesary select to the database (LP: #1994951)
373+ - Last apt-key removal (LP: #1990435)
374+ - Added a logging level check to provide a better error (LP: #2027521)
375+ - Broker is able to bootstrap the landscape-client folder (LP: #1868730)
376+ - Changed gpg file name to be less generic (LP: #2008432)
377+ - Improve Dependency data is read from the deb packages (LP: #1813442)
378+ - Fix error in swift plugin (LP: #2031674)
379+ - Repository profiles do not overwrite all configurations (LP: #2031680)
380+ - Remove old plugins (LP: #1989968)
381+ - Send ubuntu pro reboot output (LP: #2031684)
382+
383+ -- Kevin Nasto <kevin.nasto@canonical.com> Thu, 17 Aug 2023 13:41:21 -0500
384+
385 landscape-client (23.02-0ubuntu1) lunar; urgency=medium
386
387 * New upstream release 23.02:
388@@ -934,11 +956,11 @@ landscape-client (1.0.21.1-0ubuntu2) intrepid; urgency=low
389
390 landscape-client (1.0.21.1-0ubuntu1) intrepid; urgency=low
391
392- * New upstream version:
393+ * New upstream version:
394 * Add ok-no-register option to landscape-config script to not fail if
395 dbus isn't started or landscape-client isn't running.
396 * lower timeout related to package management in landscape.
397- * debian/control: Depend on cron.
398+ * debian/control: Depend on cron.
399 * debian/landscape-client.postinst: use ok-no-register option so that the
400 postinst script doesn't fail when run from the installer. (LP: #274573).
401
402@@ -986,7 +1008,7 @@ landscape-client (1.0.18-0ubuntu4) intrepid; urgency=low
403 [ Mathias Gug ]
404 * debian/landscape-common.postinst, debian/landscape-common.prerm: don't
405 call update-motd init script as it's no longer available in the
406- update-motd package. Call directly /usr/sbin/update-motd instead.
407+ update-motd package. Call directly /usr/sbin/update-motd instead.
408 (LP: #271854)
409
410 -- Mathias Gug <mathiaz@ubuntu.com> Thu, 18 Sep 2008 16:47:08 -0400
411@@ -1006,15 +1028,15 @@ landscape-client (1.0.18-0ubuntu2) intrepid; urgency=low
412 command. A landscape account is not required to use this package.
413 - landscape-client: has all the binaries required to run the
414 landscape-client. Requires a landscape account.
415- - debian/control:
416+ - debian/control:
417 + move some dependencies to landscape-client so that
418- landscape-common doesn't install unecessary packages in the
419+ landscape-common doesn't install unecessary packages in the
420 default -server install.
421 + move python-gobject to a Pre-Depends so that landscape-config can
422 register the system during the postinst (LP: #268838).
423 * debian/control:
424 - depend on python-smartpm instead of smartpm-core.
425- * debian/landscape-client.postrm: delete /etc/landscape/client.conf when
426+ * debian/landscape-client.postrm: delete /etc/landscape/client.conf when
427 the package is purged.
428 * debian/landscape-client.postinst: remove sysinfo_in_motd debconf question
429 as it wasn't used.
430@@ -1030,7 +1052,7 @@ landscape-client (1.0.18-0ubuntu2) intrepid; urgency=low
431
432 landscape-client (1.0.18-0ubuntu1) intrepid; urgency=low
433
434- * New upstream release
435+ * New upstream release
436
437 -- Rick Clark <rick.clark@ubuntu.com> Mon, 08 Sep 2008 16:35:57 -0500
438
439@@ -1149,7 +1171,7 @@ landscape-client (1.0.11-hardy1-landscape1) hardy; urgency=low
440
441 landscape-client (1.0.10-hardy1-landscape1) hardy; urgency=low
442
443- * Change the utilisation of the csv module to be compatible with
444+ * Change the utilisation of the csv module to be compatible with
445 python 2.4 as used in Dapper.
446
447 -- Andreas Hasenack <andreas@canonical.com> Thu, 12 Jun 2008 17:58:05 +0000
448@@ -1398,7 +1420,7 @@ landscape-client (0.10.3-1ubuntu1) feisty; urgency=low
449 fixed (#114829).
450
451 -- Landscape Team <landscape-team@canonical.com> Mon, 15 May 2007 16:31:00 -0700
452-
453+
454 landscape-client (0.10.2-1ubuntu1) feisty; urgency=low
455
456 * New upstream release.
457@@ -1412,7 +1434,7 @@ landscape-client (0.10.1-1ubuntu1) feisty; urgency=low
458 * Minor fix in package management plugin timings.
459
460 -- Landscape Team <landscape-devel@lists.canonical.com> Thu, 10 May 2007 10:00:00 -0700
461-
462+
463 landscape-client (0.10.0-1ubuntu1) feisty; urgency=low
464
465 * New upstream release.
466@@ -1422,9 +1444,9 @@ landscape-client (0.10.0-1ubuntu1) feisty; urgency=low
467 * The client uses the new operations system. Support for the now
468 unused action info system is gone.
469 * A minor bpickle bug was fixed.
470-
471+
472 -- Jamshed Kakar <jamshed.kakar@canonical.com> Mon, 7 May 2007 20:16:00 -0700
473-
474+
475 landscape-client (0.9.6-1ubuntu1) feisty; urgency=low
476
477 * New upstream release.
478@@ -1507,7 +1529,7 @@ landscape-client (0.8.1-1) unstable; urgency=low
479 * New upstream release.
480 * Account registration log message no longer exposes account
481 password.
482-
483+
484 -- Jamshed Kakar <jamshed.kakar@canonical.com> Thu, 18 Jan 2007 23:07:00 -0800
485
486 landscape-client (0.8.0-1) unstable; urgency=low
487@@ -1518,7 +1540,7 @@ landscape-client (0.8.0-1) unstable; urgency=low
488 * Client includes an SSL certificate to verify the server with.
489 * Package depends on python2.4-pycurl, which is necessary for the
490 new SSL support.
491-
492+
493 -- Jamshed Kakar <jamshed.kakar@canonical.com> Thu, 18 Jan 2007 10:48:00 -0800
494
495 landscape-client (0.7.0-1) unstable; urgency=low
496@@ -1529,7 +1551,7 @@ landscape-client (0.7.0-1) unstable; urgency=low
497 added to control log verbosity.
498 * Package depends on perl-modules, which is necessary for bare-bones
499 server installs.
500-
501+
502 -- Jamshed Kakar <jamshed.kakar@canonical.com> Tue, 2 Jan 2007 12:54:00 -0800
503
504 landscape-client (0.6.1-1) unstable; urgency=low
505diff --git a/debian/copyright b/debian/copyright
506index 479d234..8b528c8 100644
507--- a/debian/copyright
508+++ b/debian/copyright
509@@ -33,4 +33,3 @@ License:
510
511 On Debian systems, the complete text of the GNU General
512 Public License can be found in `/usr/share/common-licenses/GPL-2'.
513-
514diff --git a/debian/landscape-client.postrm b/debian/landscape-client.postrm
515index 10f79b8..aa34ed2 100644
516--- a/debian/landscape-client.postrm
517+++ b/debian/landscape-client.postrm
518@@ -29,7 +29,7 @@ case "$1" in
519 rm -f "${LOG_DIR}/package-reporter.log"*
520 rm -f "${LOG_DIR}/package-changer.log"*
521
522- rm -f "${GPG_DIR}/landscape-server"*.asc
523+ rm -f "${GPG_DIR}/landscape-server-mirror"*.asc
524
525 rm -rf "${DATA_DIR}/client"
526 rm -rf "${DATA_DIR}/.gnupg"
527diff --git a/debian/source.lintian-overrides b/debian/source.lintian-overrides
528deleted file mode 100644
529index 720d1be..0000000
530--- a/debian/source.lintian-overrides
531+++ /dev/null
532@@ -1,4 +0,0 @@
533-# we use dh_python or dh_python2 depending on the ubuntu release
534-# the package is being built on, this is detected dynamically
535-# in the rules file
536-landscape-client source: dh_python-is-obsolete
537\ No newline at end of file
538diff --git a/debian/watch b/debian/watch
539index 3592b51..ba7ea91 100644
540--- a/debian/watch
541+++ b/debian/watch
542@@ -1,3 +1,3 @@
543 version=4
544 opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/landscape-client-$1\.tar\.gz/ \
545- https://github.com/CanonicalLtd/landscape-client/tags .*/?(\d{2}\.\d.\d)\.tar\.gz
546+ https://github.com/CanonicalLtd/landscape-client/tags .*/?(\d{2}\.\d{2})\.tar\.gz
547diff --git a/dev/landscape-client-vm b/dev/landscape-client-vm
548index 4d13b6f..a2a1e57 100755
549--- a/dev/landscape-client-vm
550+++ b/dev/landscape-client-vm
551@@ -37,7 +37,7 @@ landscape-devel account on the Landscape staging server (or you can specify
552 another account with the --account parameter).
553
554 The built VM will be stored under ./build/intrepid along with some other
555-files. To launch the VM, cd to ./build/intrepid and issue:
556+files. To launch the VM, cd to ./build/intrepid and issue:
557 $ ./run
558 Once it's booted you can log into it with:
559 $ ./ssh
560@@ -136,7 +136,7 @@ cat > script <<EOF
561 #!/bin/sh -e
562 chown landscape /etc/landscape/client.conf
563 chmod 600 /etc/landscape/client.conf
564-apt-key add /root/ppa-key
565+cp /root/ppa-key /etc/apt/trusted.gpg.d/landscape-server-mirror-root-ppa-key.asc
566 echo "RUN=1" > /etc/default/landscape-client
567 EOF
568 chmod 755 script
569diff --git a/dev/upload-to-ppa b/dev/upload-to-ppa
570index 2cc01b8..f65b42d 100755
571--- a/dev/upload-to-ppa
572+++ b/dev/upload-to-ppa
573@@ -112,7 +112,7 @@ for release in $releases; do
574 else
575 message="Built for $codename, no source changes"
576 fi
577- cp debian/changelog ../
578+ cp debian/changelog ../
579 dch --force-distribution -b -v $version -D $codename -m $message
580 dpkg-buildpackage -S $source_opt -k$key
581 dput $ppa ../${package}_${version}_source.changes
582diff --git a/display_py2_testresults b/display_py2_testresults
583index 021681d..e683c1d 100755
584--- a/display_py2_testresults
585+++ b/display_py2_testresults
586@@ -1,5 +1,5 @@
587 #!/usr/bin/python
588-with open('_last_py2_res', 'r') as py2res:
589+with open("_last_py2_res", "r") as py2res:
590 lines = py2res.readlines()
591
592 lastline = lines[-1]
593@@ -7,7 +7,9 @@ lastline = lines[-1]
594 time, total, total, err, fail, skip = lastline.split()
595
596 if "".join((err, fail, skip)) != "000":
597- print("Python 2: \033[91mFAILED\033[0m (skips={}, failures={}, "
598- "errors={}, total={})".format(skip, fail, err, total))
599+ print(
600+ "Python 2: \033[91mFAILED\033[0m (skips={}, failures={}, "
601+ "errors={}, total={})".format(skip, fail, err, total),
602+ )
603 else:
604- print("Python 2: \033[92mOK\033[0m (total={})".format(total))
605+ print(f"Python 2: \033[92mOK\033[0m (total={total})")
606diff --git a/example.conf b/example.conf
607index 4b7fd94..16b710e 100644
608--- a/example.conf
609+++ b/example.conf
610@@ -77,7 +77,6 @@ ignore_sigusr1 = False
611 #
612 # ActiveProcessInfo - lists active processes
613 # ComputerInfo - various information
614-# HardwareInventory - information provided by the "lshw" command
615 # LoadAverage - load information
616 # MemoryInfo - memory information
617 # MountInfo - information about mount points (space available, used)
618@@ -86,9 +85,17 @@ ignore_sigusr1 = False
619 # PackageMonitor - packages installed, available, versions
620 # UserMonitor - users, groups
621 # RebootRequired - whether a reboot is required or not
622+# AptPreferences - system APT preferences configuration
623 # NetworkActivity - network information (TX, RX)
624 # NetworkDevice - a list of connected network devices
625 # UpdateManager - controls when distribution upgrades are prompted
626+# CPUUsage - CPU usage information
627+# SwiftUsage - Swift cluster usage
628+# CephUsage - Ceph usage information
629+# ComputerTags - changes in computer tags
630+# UbuntuProInfo - Ubuntu Pro registration information
631+# LivePatch - Livepath status information
632+# UbuntuProRebootRequired - informs if the system needs to be rebooted
633 #
634 # The special value "ALL" is an alias for the full list of plugins.
635 monitor_plugins = ALL
636@@ -130,6 +137,9 @@ apt_update_interval = 21600
637 # The number of seconds between package monitor runs.
638 package_monitor_interval = 1800
639
640+# The number of seconds between snap monitor runs.
641+snap_monitor_interval = 1800
642+
643 # The URL of the http proxy to use, if any.
644 # This value is optional.
645 #
646@@ -182,3 +192,9 @@ script_users = ALL
647 # The default is 512kB
648 # 2MB is allowed in this example
649 #script_output_limit=2048
650+
651+# Whether files in /etc/apt/sources.list.d are removed when a repository
652+# profile is added to this machine.
653+#
654+# The default is True
655+#manage_sources_list_d = True
656diff --git a/landscape-client.conf b/landscape-client.conf
657index cf96100..af4faaf 100644
658--- a/landscape-client.conf
659+++ b/landscape-client.conf
660@@ -9,4 +9,3 @@ log_dir = /tmp/landscape/
661 log_level = debug
662 pid_file = /tmp/landscape/landscape-client.pid
663 ping_url = http://localhost:8081/ping
664-
665diff --git a/landscape/__init__.py b/landscape/__init__.py
666index 23f8f79..1077067 100644
667--- a/landscape/__init__.py
668+++ b/landscape/__init__.py
669@@ -1,6 +1,6 @@
670 DEBIAN_REVISION = ""
671-UPSTREAM_VERSION = "23.02"
672-VERSION = "%s%s" % (UPSTREAM_VERSION, DEBIAN_REVISION)
673+UPSTREAM_VERSION = "23.08"
674+VERSION = f"{UPSTREAM_VERSION}{DEBIAN_REVISION}"
675
676 # The minimum server API version that all Landscape servers are known to speak
677 # and support. It can serve as fallback in case higher versions are not there.
678diff --git a/landscape/client/accumulate.py b/landscape/client/accumulate.py
679index 2d17c06..ce14c88 100644
680--- a/landscape/client/accumulate.py
681+++ b/landscape/client/accumulate.py
682@@ -72,24 +72,31 @@ representative data at each step boundary.
683 """
684
685
686-class Accumulator(object):
687-
688+class Accumulator:
689 def __init__(self, persist, step_size):
690 self._persist = persist
691 self._step_size = step_size
692
693 def __call__(self, new_timestamp, new_free_space, key):
694 previous_timestamp, accumulated_value = self._persist.get(key, (0, 0))
695- accumulated_value, step_data = \
696- accumulate(previous_timestamp, accumulated_value,
697- new_timestamp, new_free_space, self._step_size)
698+ accumulated_value, step_data = accumulate(
699+ previous_timestamp,
700+ accumulated_value,
701+ new_timestamp,
702+ new_free_space,
703+ self._step_size,
704+ )
705 self._persist.set(key, (new_timestamp, accumulated_value))
706 return step_data
707
708
709-def accumulate(previous_timestamp, accumulated_value,
710- new_timestamp, new_value,
711- step_size):
712+def accumulate(
713+ previous_timestamp,
714+ accumulated_value,
715+ new_timestamp,
716+ new_value,
717+ step_size,
718+):
719 previous_step = previous_timestamp // step_size
720 new_step = new_timestamp // step_size
721 step_boundary = new_step * step_size
722diff --git a/landscape/client/amp.py b/landscape/client/amp.py
723index df4ef92..5b5d448 100644
724--- a/landscape/client/amp.py
725+++ b/landscape/client/amp.py
726@@ -10,14 +10,15 @@ This module implements a few conveniences built around L{landscape.lib.amp} to
727 let the various services connect to each other in an easy and idiomatic way,
728 and have them respond to standard requests like "ping" or "exit".
729 """
730-import os
731 import logging
732+import os
733
734-from landscape.lib.amp import (
735- MethodCallClientFactory, MethodCallServerFactory, RemoteObject)
736+from landscape.lib.amp import MethodCallClientFactory
737+from landscape.lib.amp import MethodCallServerFactory
738+from landscape.lib.amp import RemoteObject
739
740
741-class ComponentPublisher(object):
742+class ComponentPublisher:
743 """Publish a Landscape client component using a UNIX socket.
744
745 Other Landscape client processes can then connect to the socket and invoke
746@@ -72,7 +73,7 @@ def remote(method):
747 return method
748
749
750-class ComponentConnector(object):
751+class ComponentConnector:
752 """Utility superclass for creating connections with a Landscape component.
753
754 @cvar component: The class of the component to connect to, it is expected
755@@ -90,6 +91,7 @@ class ComponentConnector(object):
756
757 @see: L{MethodCallClientFactory}.
758 """
759+
760 factory = MethodCallClientFactory
761 component = None # Must be defined by sub-classes
762 remote = RemoteObject
763@@ -123,14 +125,14 @@ class ComponentConnector(object):
764 factory.factor = factor
765
766 def fire_reconnect(ignored):
767- self._reactor.fire("%s-reconnect" % self.component.name)
768+ self._reactor.fire(f"{self.component.name}-reconnect")
769
770 def connected(remote):
771 factory.notifyOnConnect(fire_reconnect)
772 return remote
773
774 def log_error(failure):
775- logging.error("Error while connecting to %s", self.component.name)
776+ logging.error(f"Error while connecting to {self.component.name}")
777 return failure
778
779 socket_path = _get_socket_path(self.component, self._config)
780diff --git a/landscape/client/broker/amp.py b/landscape/client/broker/amp.py
781index 94a1fa2..2278b08 100644
782--- a/landscape/client/broker/amp.py
783+++ b/landscape/client/broker/amp.py
784@@ -1,16 +1,19 @@
785-from twisted.internet.defer import maybeDeferred, execute, succeed
786+from twisted.internet.defer import execute
787+from twisted.internet.defer import maybeDeferred
788+from twisted.internet.defer import succeed
789 from twisted.python.compat import iteritems
790
791-from landscape.lib.amp import RemoteObject, MethodCallArgument
792-from landscape.client.amp import ComponentConnector, get_remote_methods
793-from landscape.client.broker.server import BrokerServer
794+from landscape.client.amp import ComponentConnector
795+from landscape.client.amp import get_remote_methods
796 from landscape.client.broker.client import BrokerClient
797-from landscape.client.monitor.monitor import Monitor
798+from landscape.client.broker.server import BrokerServer
799 from landscape.client.manager.manager import Manager
800+from landscape.client.monitor.monitor import Monitor
801+from landscape.lib.amp import MethodCallArgument
802+from landscape.lib.amp import RemoteObject
803
804
805 class RemoteBroker(RemoteObject):
806-
807 def call_if_accepted(self, type, callable, *args):
808 """Call C{callable} if C{type} is an accepted message type."""
809 deferred_types = self.get_accepted_message_types()
810@@ -18,6 +21,7 @@ class RemoteBroker(RemoteObject):
811 def got_accepted_types(result):
812 if type in result:
813 return callable(*args)
814+
815 deferred_types.addCallback(got_accepted_types)
816 return deferred_types
817
818@@ -30,11 +34,10 @@ class RemoteBroker(RemoteObject):
819 callable will be fired.
820 """
821 result = self.listen_events(list(handlers.keys()))
822- return result.addCallback(
823- lambda args: handlers[args[0]](**args[1]))
824+ return result.addCallback(lambda args: handlers[args[0]](**args[1]))
825
826
827-class FakeRemoteBroker(object):
828+class FakeRemoteBroker:
829 """Looks like L{RemoteBroker}, but actually talks to local objects."""
830
831 def __init__(self, exchanger, message_store, broker_server):
832@@ -48,16 +51,19 @@ class FakeRemoteBroker(object):
833 that they're encodable with AMP.
834 """
835 original = getattr(self.broker_server, name, None)
836- if (name in get_remote_methods(self.broker_server) and
837- original is not None and
838- callable(original)
839- ):
840+ if (
841+ name in get_remote_methods(self.broker_server)
842+ and original is not None
843+ and callable(original)
844+ ):
845+
846 def method(*args, **kwargs):
847 for arg in args:
848 assert MethodCallArgument.check(arg)
849 for k, v in iteritems(kwargs):
850 assert MethodCallArgument.check(v)
851 return execute(original, *args, **kwargs)
852+
853 return method
854 else:
855 raise AttributeError(name)
856@@ -76,8 +82,7 @@ class FakeRemoteBroker(object):
857 callable will be fired.
858 """
859 result = self.broker_server.listen_events(handlers.keys())
860- return result.addCallback(
861- lambda args: handlers[args[0]](**args[1]))
862+ return result.addCallback(lambda args: handlers[args[0]](**args[1]))
863
864 def register(self):
865 return succeed(None)
866@@ -114,8 +119,8 @@ def get_component_registry():
867 RemoteBrokerConnector,
868 RemoteClientConnector,
869 RemoteMonitorConnector,
870- RemoteManagerConnector
871+ RemoteManagerConnector,
872 ]
873- return dict(
874- (connector.component.name, connector)
875- for connector in all_connectors)
876+ return {
877+ connector.component.name: connector for connector in all_connectors
878+ }
879diff --git a/landscape/client/broker/client.py b/landscape/client/broker/client.py
880index 042c18a..edbc2c2 100644
881--- a/landscape/client/broker/client.py
882+++ b/landscape/client/broker/client.py
883@@ -1,19 +1,23 @@
884-from logging import info, exception, error, debug
885-import sys
886 import random
887+import sys
888+from logging import debug
889+from logging import error
890+from logging import exception
891+from logging import info
892
893-from twisted.internet.defer import maybeDeferred, succeed
894+from twisted.internet.defer import maybeDeferred
895+from twisted.internet.defer import succeed
896
897+from landscape.client.amp import remote
898 from landscape.lib.format import format_object
899 from landscape.lib.twisted_util import gather_results
900-from landscape.client.amp import remote
901
902
903 class HandlerNotFoundError(Exception):
904 """A handler for the given message type was not found."""
905
906
907-class BrokerClientPlugin(object):
908+class BrokerClientPlugin:
909 """A convenience for writing L{BrokerClient} plugins.
910
911 This provides a register method which will set up a bunch of
912@@ -32,6 +36,7 @@ class BrokerClientPlugin(object):
913 sent. See L{landscape.broker.server.BrokerServer.send_message} for
914 more details.
915 """
916+
917 run_interval = 5
918 run_immediately = False
919 scope = None # Global scope
920@@ -58,8 +63,10 @@ class BrokerClientPlugin(object):
921 if acceptance:
922 return callable(*args, **kwargs)
923
924- self.client.reactor.call_on(("message-type-acceptance-changed", type),
925- acceptance_changed)
926+ self.client.reactor.call_on(
927+ ("message-type-acceptance-changed", type),
928+ acceptance_changed,
929+ )
930
931 def _resynchronize(self, scopes=None):
932 """
933@@ -106,18 +113,27 @@ class BrokerClientPlugin(object):
934 if self.run_immediately:
935 self._run_with_error_log()
936 if self.run_interval is not None:
937- delay = (random.random() * self.run_interval *
938- self.client.config.stagger_launch)
939- debug("delaying start of %s for %d seconds",
940- format_object(self), delay)
941+ delay = (
942+ random.random()
943+ * self.run_interval
944+ * self.client.config.stagger_launch
945+ )
946+ debug(
947+ "delaying start of %s for %d seconds",
948+ format_object(self),
949+ delay,
950+ )
951 self._loop = self.client.reactor.call_later(
952- delay, self._start_loop)
953+ delay,
954+ self._start_loop,
955+ )
956
957 def _start_loop(self):
958 """Launch the client loop."""
959 self._loop = self.client.reactor.call_every(
960 self.run_interval,
961- self._run_with_error_log)
962+ self._run_with_error_log,
963+ )
964
965 def _run_with_error_log(self):
966 """Wrap self.run in a Deferred with a logging error handler."""
967@@ -134,7 +150,7 @@ class BrokerClientPlugin(object):
968 return failure
969
970
971-class BrokerClient(object):
972+class BrokerClient:
973 """Basic plugin registry for clients that have to deal with the broker.
974
975 This knows about the needs of a client when dealing with the Landscape
976@@ -148,10 +164,11 @@ class BrokerClient(object):
977
978 @param reactor: A L{LandscapeReactor}.
979 """
980+
981 name = "client"
982
983 def __init__(self, reactor, config):
984- super(BrokerClient, self).__init__()
985+ super().__init__()
986 self.reactor = reactor
987 self.broker = None
988 self.config = config
989@@ -179,7 +196,7 @@ class BrokerClient(object):
990 """
991 info("Registering plugin %s.", format_object(plugin))
992 self._plugins.append(plugin)
993- if hasattr(plugin, 'plugin_name'):
994+ if hasattr(plugin, "plugin_name"):
995 self._plugin_names[plugin.plugin_name] = plugin
996 plugin.register(self)
997
998@@ -210,15 +227,16 @@ class BrokerClient(object):
999 @return: The return value of the handler, if found.
1000 @raises: HandlerNotFoundError if the handler was not found
1001 """
1002- type = message["type"]
1003- handler = self._registered_messages.get(type)
1004+ typ = message["type"]
1005+ handler = self._registered_messages.get(typ)
1006 if handler is None:
1007- raise HandlerNotFoundError(type)
1008+ raise HandlerNotFoundError(typ)
1009 try:
1010 return handler(message)
1011 except Exception:
1012- exception("Error running message handler for type %r: %r"
1013- % (type, handler))
1014+ exception(
1015+ f"Error running message handler for type {typ!r}: {handler!r}",
1016+ )
1017
1018 @remote
1019 def message(self, message):
1020@@ -259,8 +277,9 @@ class BrokerClient(object):
1021 results = self.reactor.fire((event_type, message_type), acceptance)
1022 else:
1023 results = self.reactor.fire(event_type, *args, **kwargs)
1024- return gather_results([
1025- maybeDeferred(lambda x: x, result) for result in results])
1026+ return gather_results(
1027+ [maybeDeferred(lambda x: x, result) for result in results],
1028+ )
1029
1030 def handle_reconnect(self):
1031 """Called when the connection with the broker is established again.
1032@@ -273,8 +292,8 @@ class BrokerClient(object):
1033 - Re-register ourselves as client, so the broker knows we exist and
1034 will talk to us firing events and dispatching messages.
1035 """
1036- for type in self._registered_messages:
1037- self.broker.register_client_accepted_message_type(type)
1038+ for typ in self._registered_messages:
1039+ self.broker.register_client_accepted_message_type(typ)
1040 self.broker.register_client(self.name)
1041
1042 @remote
1043diff --git a/landscape/client/broker/config.py b/landscape/client/broker/config.py
1044index 71e65e1..6b222fb 100644
1045--- a/landscape/client/broker/config.py
1046+++ b/landscape/client/broker/config.py
1047@@ -1,5 +1,4 @@
1048 """Configuration class for the broker."""
1049-
1050 import os
1051
1052 from landscape.client.deployment import Configuration
1053@@ -12,7 +11,7 @@ class BrokerConfiguration(Configuration):
1054 """
1055
1056 def __init__(self):
1057- super(BrokerConfiguration, self).__init__()
1058+ super().__init__()
1059 self._original_http_proxy = os.environ.get("http_proxy")
1060 self._original_https_proxy = os.environ.get("https_proxy")
1061
1062@@ -33,35 +32,67 @@ class BrokerConfiguration(Configuration):
1063 - C{http_proxy}
1064 - C{https_proxy}
1065 """
1066- parser = super(BrokerConfiguration, self).make_parser()
1067+ parser = super().make_parser()
1068
1069- parser.add_option("-a", "--account-name", metavar="NAME",
1070- help="The account this computer belongs to.")
1071- parser.add_option("-p", "--registration-key", metavar="KEY",
1072- help="The account-wide key used for "
1073- "registering clients.")
1074- parser.add_option("-t", "--computer-title", metavar="TITLE",
1075- help="The title of this computer")
1076- parser.add_option("--exchange-interval", default=15 * 60, type="int",
1077- metavar="INTERVAL",
1078- help="The number of seconds between server "
1079- "exchanges.")
1080- parser.add_option("--urgent-exchange-interval", default=1 * 60,
1081- type="int", metavar="INTERVAL",
1082- help="The number of seconds between urgent server "
1083- "exchanges.")
1084- parser.add_option("--ping-interval", default=30, type="int",
1085- metavar="INTERVAL",
1086- help="The number of seconds between pings.")
1087- parser.add_option("--http-proxy", metavar="URL",
1088- help="The URL of the HTTP proxy, if one is needed.")
1089- parser.add_option("--https-proxy", metavar="URL",
1090- help="The URL of the HTTPS proxy, if one is needed.")
1091- parser.add_option("--access-group", default="",
1092- help="Suggested access group for this computer.")
1093- parser.add_option("--tags",
1094- help="Comma separated list of tag names to be sent "
1095- "to the server.")
1096+ parser.add_option(
1097+ "-a",
1098+ "--account-name",
1099+ metavar="NAME",
1100+ help="The account this computer belongs to.",
1101+ )
1102+ parser.add_option(
1103+ "-p",
1104+ "--registration-key",
1105+ metavar="KEY",
1106+ help="The account-wide key used for " "registering clients.",
1107+ )
1108+ parser.add_option(
1109+ "-t",
1110+ "--computer-title",
1111+ metavar="TITLE",
1112+ help="The title of this computer",
1113+ )
1114+ parser.add_option(
1115+ "--exchange-interval",
1116+ default=15 * 60,
1117+ type="int",
1118+ metavar="INTERVAL",
1119+ help="The number of seconds between server " "exchanges.",
1120+ )
1121+ parser.add_option(
1122+ "--urgent-exchange-interval",
1123+ default=1 * 60,
1124+ type="int",
1125+ metavar="INTERVAL",
1126+ help="The number of seconds between urgent server " "exchanges.",
1127+ )
1128+ parser.add_option(
1129+ "--ping-interval",
1130+ default=30,
1131+ type="int",
1132+ metavar="INTERVAL",
1133+ help="The number of seconds between pings.",
1134+ )
1135+ parser.add_option(
1136+ "--http-proxy",
1137+ metavar="URL",
1138+ help="The URL of the HTTP proxy, if one is needed.",
1139+ )
1140+ parser.add_option(
1141+ "--https-proxy",
1142+ metavar="URL",
1143+ help="The URL of the HTTPS proxy, if one is needed.",
1144+ )
1145+ parser.add_option(
1146+ "--access-group",
1147+ default="",
1148+ help="Suggested access group for this computer.",
1149+ )
1150+ parser.add_option(
1151+ "--tags",
1152+ help="Comma separated list of tag names to be sent "
1153+ "to the server.",
1154+ )
1155
1156 return parser
1157
1158@@ -78,7 +109,7 @@ class BrokerConfiguration(Configuration):
1159 C{http_proxy} and C{https_proxy} environment variables based on
1160 that config data.
1161 """
1162- super(BrokerConfiguration, self).load(args)
1163+ super().load(args)
1164 if self.http_proxy:
1165 os.environ["http_proxy"] = self.http_proxy
1166 elif self._original_http_proxy:
1167diff --git a/landscape/client/broker/exchange.py b/landscape/client/broker/exchange.py
1168index 924cb9a..31f32ff 100644
1169--- a/landscape/client/broker/exchange.py
1170+++ b/landscape/client/broker/exchange.py
1171@@ -342,23 +342,28 @@ Diagram::
1172 14. Schedule exchange
1173
1174 """
1175-import time
1176 import logging
1177-from landscape.lib.hashlib import md5
1178+import time
1179
1180-from twisted.internet.defer import Deferred, succeed
1181-from landscape.lib.compat import _PY3
1182+from twisted.internet.defer import Deferred
1183+from twisted.internet.defer import succeed
1184
1185+from landscape import CLIENT_API
1186+from landscape import DEFAULT_SERVER_API
1187+from landscape import SERVER_API
1188 from landscape.lib.backoff import ExponentialBackoff
1189-from landscape.lib.fetch import HTTPCodeError, PyCurlError
1190+from landscape.lib.compat import _PY3
1191+from landscape.lib.fetch import HTTPCodeError
1192+from landscape.lib.fetch import PyCurlError
1193 from landscape.lib.format import format_delta
1194-from landscape.lib.message import got_next_expected, RESYNC
1195-from landscape.lib.versioning import is_version_higher, sort_versions
1196-
1197-from landscape import DEFAULT_SERVER_API, SERVER_API, CLIENT_API
1198+from landscape.lib.hashlib import md5
1199+from landscape.lib.message import got_next_expected
1200+from landscape.lib.message import RESYNC
1201+from landscape.lib.versioning import is_version_higher
1202+from landscape.lib.versioning import sort_versions
1203
1204
1205-class MessageExchange(object):
1206+class MessageExchange:
1207 """Schedule and handle message exchanges with the server.
1208
1209 The L{MessageExchange} is the place where messages are sent to go out
1210@@ -376,8 +381,16 @@ class MessageExchange(object):
1211 # The highest server API that we are capable of speaking
1212 _api = SERVER_API
1213
1214- def __init__(self, reactor, store, transport, registration_info,
1215- exchange_store, config, max_messages=100):
1216+ def __init__(
1217+ self,
1218+ reactor,
1219+ store,
1220+ transport,
1221+ registration_info,
1222+ exchange_store,
1223+ config,
1224+ max_messages=100,
1225+ ):
1226 """
1227 @param reactor: The L{LandscapeReactor} used to fire events in response
1228 to messages received by the server.
1229@@ -421,15 +434,16 @@ class MessageExchange(object):
1230 A message is considered obsolete if the secure ID changed since it was
1231 received.
1232 """
1233- if 'operation-id' not in message:
1234+ if "operation-id" not in message:
1235 return False
1236
1237- operation_id = message['operation-id']
1238+ operation_id = message["operation-id"]
1239 context = self._exchange_store.get_message_context(operation_id)
1240 if context is None:
1241 logging.warning(
1242- "No message context for message with operation-id: %s"
1243- % operation_id)
1244+ "No message context for message with "
1245+ f"operation-id: {operation_id}",
1246+ )
1247 return False
1248
1249 # Compare the current secure ID with the one that was in effect when
1250@@ -450,7 +464,7 @@ class MessageExchange(object):
1251 max_bytes = self._max_log_text_bytes
1252 if len(value) > max_bytes:
1253 value = value[:max_bytes]
1254- value += '...MESSAGE TRUNCATED DUE TO SIZE'
1255+ value += "...MESSAGE TRUNCATED DUE TO SIZE"
1256 message[field] = value
1257
1258 def send(self, message, urgent=False):
1259@@ -463,14 +477,15 @@ class MessageExchange(object):
1260 """
1261 if self._message_is_obsolete(message):
1262 logging.info(
1263- "Response message with operation-id %s was discarded "
1264- "because the client's secure ID has changed in the meantime"
1265- % message.get('operation-id'))
1266+ "Response message with operation-id "
1267+ f"{message.get('operation-id')} was discarded "
1268+ "because the client's secure ID has changed in the meantime",
1269+ )
1270 return None
1271
1272 # These fields sometimes have really long output we need to trim
1273- self.truncate_message_field('err', message)
1274- self.truncate_message_field('result-text', message)
1275+ self.truncate_message_field("err", message)
1276+ self.truncate_message_field("result-text", message)
1277
1278 if "timestamp" not in message:
1279 message["timestamp"] = int(self._reactor.time())
1280@@ -535,12 +550,16 @@ class MessageExchange(object):
1281 def _handle_set_intervals(self, message):
1282 if "exchange" in message:
1283 self._config.exchange_interval = message["exchange"]
1284- logging.info("Exchange interval set to %d seconds." %
1285- self._config.exchange_interval)
1286+ logging.info(
1287+ "Exchange interval set "
1288+ f"to {self._config.exchange_interval:d} seconds.",
1289+ )
1290 if "urgent-exchange" in message:
1291 self._config.urgent_exchange_interval = message["urgent-exchange"]
1292- logging.info("Urgent exchange interval set to %d seconds." %
1293- self._config.urgent_exchange_interval)
1294+ logging.info(
1295+ "Urgent exchange interval set "
1296+ f"to {self._config.urgent_exchange_interval:d} seconds.",
1297+ )
1298 self._config.write()
1299
1300 def exchange(self):
1301@@ -565,19 +584,24 @@ class MessageExchange(object):
1302
1303 start_time = time.time()
1304 if self._urgent_exchange:
1305- logging.info("Starting urgent message exchange with %s."
1306- % self._transport.get_url())
1307+ logging.info(
1308+ "Starting urgent message exchange "
1309+ f"with {self._transport.get_url()}.",
1310+ )
1311 else:
1312- logging.info("Starting message exchange with %s."
1313- % self._transport.get_url())
1314+ logging.info(
1315+ f"Starting message exchange with {self._transport.get_url()}.",
1316+ )
1317
1318 deferred = Deferred()
1319
1320 def exchange_completed():
1321 self.schedule_exchange(force=True)
1322 self._reactor.fire("exchange-done")
1323- logging.info("Message exchange completed in %s.",
1324- format_delta(time.time() - start_time))
1325+ logging.info(
1326+ "Message exchange completed in %s.",
1327+ format_delta(time.time() - start_time),
1328+ )
1329 deferred.callback(None)
1330
1331 def handle_result(result):
1332@@ -627,7 +651,7 @@ class MessageExchange(object):
1333 # The error returned is an SSL error, most likely the server
1334 # is using a self-signed certificate. Let's fire a special
1335 # event so that the GUI can display a nice message.
1336- logging.error("Message exchange failed: %s" % error.message)
1337+ logging.error(f"Message exchange failed: {error.message}")
1338 ssl_error = True
1339
1340 self._reactor.fire("exchange-failed", ssl_error=ssl_error)
1341@@ -636,16 +660,19 @@ class MessageExchange(object):
1342 logging.info("Message exchange failed.")
1343 exchange_completed()
1344
1345- self._reactor.call_in_thread(handle_result, handle_failure,
1346- self._transport.exchange, payload,
1347- self._registration_info.secure_id,
1348- self._get_exchange_token(),
1349- payload.get("server-api"))
1350+ self._reactor.call_in_thread(
1351+ handle_result,
1352+ handle_failure,
1353+ self._transport.exchange,
1354+ payload,
1355+ self._registration_info.secure_id,
1356+ self._get_exchange_token(),
1357+ payload.get("server-api"),
1358+ )
1359 return deferred
1360
1361 def is_urgent(self):
1362- """Return a bool showing whether there is an urgent exchange scheduled.
1363- """
1364+ """Return bool showing whether there is an urgent exchange scheduled"""
1365 return self._urgent_exchange
1366
1367 def schedule_exchange(self, urgent=False, force=False):
1368@@ -666,9 +693,12 @@ class MessageExchange(object):
1369 # The 'not self._exchanging' check below is currently untested.
1370 # It's a bit tricky to test as it is preventing rehooking 'exchange'
1371 # while there's a background thread doing the exchange itself.
1372- if (not self._exchanging and
1373- (force or self._exchange_id is None or
1374- urgent and not self._urgent_exchange)):
1375+ if not self._exchanging and (
1376+ force
1377+ or self._exchange_id is None
1378+ or urgent
1379+ and not self._urgent_exchange
1380+ ):
1381 if urgent:
1382 self._urgent_exchange = True
1383 if self._exchange_id:
1384@@ -680,18 +710,24 @@ class MessageExchange(object):
1385 interval = self._config.exchange_interval
1386 backoff_delay = self._backoff_counter.get_random_delay()
1387 if backoff_delay:
1388- logging.warning("Server is busy. Backing off client for {} "
1389- "seconds".format(backoff_delay))
1390+ logging.warning(
1391+ "Server is busy. Backing off client for {} "
1392+ "seconds".format(backoff_delay),
1393+ )
1394 interval += backoff_delay
1395
1396 if self._notification_id is not None:
1397 self._reactor.cancel_call(self._notification_id)
1398 notification_interval = interval - 10
1399 self._notification_id = self._reactor.call_later(
1400- notification_interval, self._notify_impending_exchange)
1401+ notification_interval,
1402+ self._notify_impending_exchange,
1403+ )
1404
1405 self._exchange_id = self._reactor.call_later(
1406- interval, self.exchange)
1407+ interval,
1408+ self.exchange,
1409+ )
1410
1411 def _get_exchange_token(self):
1412 """Get the token given us by the server at the last exchange.
1413@@ -743,13 +779,15 @@ class MessageExchange(object):
1414 del messages[i:]
1415 else:
1416 server_api = store.get_server_api()
1417- payload = {"server-api": server_api,
1418- "client-api": CLIENT_API,
1419- "sequence": store.get_sequence(),
1420- "accepted-types": accepted_types_digest,
1421- "messages": messages,
1422- "total-messages": total_messages,
1423- "next-expected-sequence": store.get_server_sequence()}
1424+ payload = {
1425+ "server-api": server_api,
1426+ "client-api": CLIENT_API,
1427+ "sequence": store.get_sequence(),
1428+ "accepted-types": accepted_types_digest,
1429+ "messages": messages,
1430+ "total-messages": total_messages,
1431+ "next-expected-sequence": store.get_server_sequence(),
1432+ }
1433 accepted_client_types = self.get_client_accepted_message_types()
1434 accepted_client_types_hash = self._hash_types(accepted_client_types)
1435 if accepted_client_types_hash != self._client_accepted_types_hash:
1436@@ -776,7 +814,8 @@ class MessageExchange(object):
1437 """
1438 message_store = self._message_store
1439 self._client_accepted_types_hash = result.get(
1440- "client-accepted-types-hash")
1441+ "client-accepted-types-hash",
1442+ )
1443 next_expected = result.get("next-expected-sequence")
1444 old_sequence = message_store.get_sequence()
1445 if next_expected is None:
1446@@ -793,8 +832,10 @@ class MessageExchange(object):
1447 # let's fire an event to tell all the plugins that they
1448 # ought to generate new messages so the server gets some
1449 # up-to-date data.
1450- logging.info("Server asked for ancient data: resynchronizing all "
1451- "state with the server.")
1452+ logging.info(
1453+ "Server asked for ancient data: resynchronizing all "
1454+ "state with the server.",
1455+ )
1456 self.send({"type": "resynchronize"})
1457 self._reactor.fire("resynchronize-clients")
1458
1459@@ -808,8 +849,9 @@ class MessageExchange(object):
1460 if new_uuid and isinstance(new_uuid, bytes):
1461 new_uuid = new_uuid.decode("ascii")
1462 if new_uuid != old_uuid:
1463- logging.info("Server UUID changed (old=%s, new=%s)."
1464- % (old_uuid, new_uuid))
1465+ logging.info(
1466+ f"Server UUID changed (old={old_uuid}, new={new_uuid}).",
1467+ )
1468 self._reactor.fire("server-uuid-changed", old_uuid, new_uuid)
1469 message_store.set_server_uuid(new_uuid)
1470
1471@@ -874,12 +916,14 @@ class MessageExchange(object):
1472 Any message handlers registered with L{register_message} will
1473 be called.
1474 """
1475- if 'operation-id' in message:
1476+ if "operation-id" in message:
1477 # This is a message that requires a response. Store the secure ID
1478 # so we can check for obsolete results later.
1479 self._exchange_store.add_message_context(
1480- message['operation-id'], self._registration_info.secure_id,
1481- message['type'])
1482+ message["operation-id"],
1483+ self._registration_info.secure_id,
1484+ message["type"],
1485+ )
1486
1487 self._reactor.fire("message", message)
1488 # This has plan interference! but whatever.
1489@@ -903,9 +947,9 @@ def get_accepted_types_diff(old_types, new_types):
1490 stable_types = old_types & new_types
1491 removed_types = old_types - new_types
1492 diff = []
1493- diff.extend(["+%s" % type for type in added_types])
1494- diff.extend(["%s" % type for type in stable_types])
1495- diff.extend(["-%s" % type for type in removed_types])
1496+ diff.extend([f"+{typ}" for typ in added_types])
1497+ diff.extend([f"{typ}" for typ in stable_types])
1498+ diff.extend([f"-{typ}" for typ in removed_types])
1499 return " ".join(diff)
1500
1501
1502diff --git a/landscape/client/broker/exchangestore.py b/landscape/client/broker/exchangestore.py
1503index aaa9192..4fb855a 100644
1504--- a/landscape/client/broker/exchangestore.py
1505+++ b/landscape/client/broker/exchangestore.py
1506@@ -9,7 +9,7 @@ except ImportError:
1507 from landscape.lib.store import with_cursor
1508
1509
1510-class MessageContext(object):
1511+class MessageContext:
1512 """Stores a context for incoming messages that require a response.
1513
1514 The context consists of
1515@@ -39,10 +39,11 @@ class MessageContext(object):
1516 def remove(self, cursor):
1517 cursor.execute(
1518 "DELETE FROM message_context WHERE operation_id=?",
1519- (self.operation_id,))
1520+ (self.operation_id,),
1521+ )
1522
1523
1524-class ExchangeStore(object):
1525+class ExchangeStore:
1526 """Message meta data required by the L{MessageExchange}.
1527
1528 The implementation uses a SQLite database as backend, with a single table
1529@@ -51,6 +52,7 @@ class ExchangeStore(object):
1530
1531 @param filename: The name of the file that contains the sqlite database.
1532 """
1533+
1534 _db = None
1535
1536 def __init__(self, filename):
1537@@ -61,13 +63,20 @@ class ExchangeStore(object):
1538
1539 @with_cursor
1540 def add_message_context(
1541- self, cursor, operation_id, secure_id, message_type):
1542+ self,
1543+ cursor,
1544+ operation_id,
1545+ secure_id,
1546+ message_type,
1547+ ):
1548 """Add a L{MessageContext} with the given data."""
1549 params = (operation_id, secure_id, message_type, time.time())
1550 cursor.execute(
1551 "INSERT INTO message_context "
1552 " (operation_id, secure_id, message_type, timestamp) "
1553- " VALUES (?,?,?,?)", params)
1554+ " VALUES (?,?,?,?)",
1555+ params,
1556+ )
1557 return MessageContext(self._db, *params)
1558
1559 @with_cursor
1560@@ -75,7 +84,9 @@ class ExchangeStore(object):
1561 """The L{MessageContext} for the given C{operation_id} or C{None}."""
1562 cursor.execute(
1563 "SELECT operation_id, secure_id, message_type, timestamp "
1564- "FROM message_context WHERE operation_id=?", (operation_id,))
1565+ "FROM message_context WHERE operation_id=?",
1566+ (operation_id,),
1567+ )
1568 row = cursor.fetchone()
1569 if row:
1570 return MessageContext(self._db, *row)
1571@@ -101,10 +112,12 @@ def ensure_exchange_schema(db):
1572 "CREATE TABLE message_context"
1573 " (id INTEGER PRIMARY KEY, timestamp TIMESTAMP, "
1574 " secure_id TEXT NOT NULL, operation_id INTEGER NOT NULL, "
1575- " message_type text NOT NULL)")
1576+ " message_type text NOT NULL)",
1577+ )
1578 cursor.execute(
1579 "CREATE UNIQUE INDEX msgctx_operationid_idx ON "
1580- "message_context(operation_id)")
1581+ "message_context(operation_id)",
1582+ )
1583 except (sqlite3.OperationalError, sqlite3.DatabaseError):
1584 cursor.close()
1585 db.rollback()
1586diff --git a/landscape/client/broker/ping.py b/landscape/client/broker/ping.py
1587index 5c8062e..153be3c 100644
1588--- a/landscape/client/broker/ping.py
1589+++ b/landscape/client/broker/ping.py
1590@@ -48,7 +48,7 @@ from landscape.lib.fetch import fetch
1591 from landscape.lib.log import log_failure
1592
1593
1594-class PingClient(object):
1595+class PingClient:
1596 """An HTTP client which knows how to talk to the ping server."""
1597
1598 def __init__(self, reactor, get_page=None):
1599@@ -74,10 +74,16 @@ class PingClient(object):
1600
1601 def errback(type, value, tb):
1602 page_deferred.errback(Failure(value, type, tb))
1603- self._reactor.call_in_thread(page_deferred.callback, errback,
1604- self.get_page, url,
1605- post=True, data=data,
1606- headers=headers)
1607+
1608+ self._reactor.call_in_thread(
1609+ page_deferred.callback,
1610+ errback,
1611+ self.get_page,
1612+ url,
1613+ post=True,
1614+ data=data,
1615+ headers=headers,
1616+ )
1617 page_deferred.addCallback(self._got_result)
1618 return page_deferred
1619 return defer.succeed(False)
1620@@ -92,7 +98,7 @@ class PingClient(object):
1621 return True
1622
1623
1624-class Pinger(object):
1625+class Pinger:
1626 """
1627 A plugin which pings the Landscape server with HTTP requests to
1628 see if a full exchange should be initiated.
1629@@ -107,8 +113,14 @@ class Pinger(object):
1630 scheduled ping.
1631 """
1632
1633- def __init__(self, reactor, identity, exchanger, config,
1634- ping_client_factory=PingClient):
1635+ def __init__(
1636+ self,
1637+ reactor,
1638+ identity,
1639+ exchanger,
1640+ config,
1641+ ping_client_factory=PingClient,
1642+ ):
1643 self._config = config
1644 self._identity = identity
1645 self._reactor = reactor
1646@@ -132,33 +144,42 @@ class Pinger(object):
1647 def ping(self):
1648 """Perform a ping; if there are messages, fire an exchange."""
1649 deferred = self._ping_client.ping(
1650- self._config.ping_url, self._identity.insecure_id)
1651+ self._config.ping_url,
1652+ self._identity.insecure_id,
1653+ )
1654 deferred.addCallback(self._got_result)
1655 deferred.addErrback(self._got_error)
1656 deferred.addBoth(lambda _: self._schedule())
1657
1658 def _got_result(self, exchange):
1659 if exchange:
1660- info("Ping indicates message available. "
1661- "Scheduling an urgent exchange.")
1662+ info(
1663+ "Ping indicates message available. "
1664+ "Scheduling an urgent exchange.",
1665+ )
1666 self._exchanger.schedule_exchange(urgent=True)
1667
1668 def _got_error(self, failure):
1669- log_failure(failure,
1670- "Error contacting ping server at %s" %
1671- (self._ping_client.url,))
1672+ log_failure(
1673+ failure,
1674+ f"Error contacting ping server at {self._ping_client.url}",
1675+ )
1676
1677 def _schedule(self):
1678 """Schedule a new ping using the current ping interval."""
1679- self._call_id = self._reactor.call_later(self._config.ping_interval,
1680- self.ping)
1681+ self._call_id = self._reactor.call_later(
1682+ self._config.ping_interval,
1683+ self.ping,
1684+ )
1685
1686 def _handle_set_intervals(self, message):
1687 if message["type"] == "set-intervals" and "ping" in message:
1688 self._config.ping_interval = message["ping"]
1689 self._config.write()
1690- info("Ping interval set to %d seconds." %
1691- self._config.ping_interval)
1692+ info(
1693+ f"Ping interval set to {self._config.ping_interval:d} "
1694+ "seconds.",
1695+ )
1696 if self._call_id is not None:
1697 self._reactor.cancel_call(self._call_id)
1698 self._schedule()
1699@@ -170,8 +191,7 @@ class Pinger(object):
1700 self._call_id = None
1701
1702
1703-class FakePinger(object):
1704-
1705+class FakePinger:
1706 def __init__(self, *args, **kwargs):
1707 pass
1708
1709diff --git a/landscape/client/broker/registration.py b/landscape/client/broker/registration.py
1710index 864ce99..c21186e 100644
1711--- a/landscape/client/broker/registration.py
1712+++ b/landscape/client/broker/registration.py
1713@@ -16,10 +16,11 @@ from twisted.internet.defer import Deferred
1714 from landscape.client.broker.exchange import maybe_bytes
1715 from landscape.client.monitor.ubuntuproinfo import get_ubuntu_pro_info
1716 from landscape.lib.juju import get_juju_info
1717-from landscape.lib.tag import is_valid_tag_list
1718 from landscape.lib.network import get_fqdn
1719-from landscape.lib.vm_info import get_vm_info, get_container_info
1720+from landscape.lib.tag import is_valid_tag_list
1721 from landscape.lib.versioning import is_version_higher
1722+from landscape.lib.vm_info import get_container_info
1723+from landscape.lib.vm_info import get_vm_info
1724
1725
1726 class RegistrationError(Exception):
1727@@ -31,7 +32,6 @@ class RegistrationError(Exception):
1728
1729
1730 def persist_property(name):
1731-
1732 def get(self):
1733 value = self._persist.get(name)
1734 try:
1735@@ -46,14 +46,13 @@ def persist_property(name):
1736
1737
1738 def config_property(name):
1739-
1740 def get(self):
1741 return getattr(self._config, name)
1742
1743 return property(get)
1744
1745
1746-class Identity(object):
1747+class Identity:
1748 """Maintains details about the identity of this Landscape client.
1749
1750 @ivar secure_id: A server-provided ID for secure message exchange.
1751@@ -83,7 +82,7 @@ class Identity(object):
1752 self._persist = persist.root_at("registration")
1753
1754
1755-class RegistrationHandler(object):
1756+class RegistrationHandler:
1757 """
1758 An object from which registration can be requested of the server,
1759 and which will handle forced ID changes from the server.
1760@@ -91,8 +90,16 @@ class RegistrationHandler(object):
1761 L{register} should be used to perform initial registration.
1762 """
1763
1764- def __init__(self, config, identity, reactor, exchange, pinger,
1765- message_store, fetch_async=None):
1766+ def __init__(
1767+ self,
1768+ config,
1769+ identity,
1770+ reactor,
1771+ exchange,
1772+ pinger,
1773+ message_store,
1774+ fetch_async=None,
1775+ ):
1776 self._config = config
1777 self._identity = identity
1778 self._reactor = reactor
1779@@ -104,8 +111,10 @@ class RegistrationHandler(object):
1780 self._reactor.call_on("exchange-done", self._handle_exchange_done)
1781 self._exchange.register_message("set-id", self._handle_set_id)
1782 self._exchange.register_message("unknown-id", self._handle_unknown_id)
1783- self._exchange.register_message("registration",
1784- self._handle_registration)
1785+ self._exchange.register_message(
1786+ "registration",
1787+ self._handle_registration,
1788+ )
1789 self._should_register = None
1790 self._fetch_async = fetch_async
1791 self._juju_data = None
1792@@ -116,9 +125,11 @@ class RegistrationHandler(object):
1793 if id.secure_id:
1794 return False
1795
1796- return bool(id.computer_title and
1797- id.account_name and
1798- self._message_store.accepts("register"))
1799+ return bool(
1800+ id.computer_title
1801+ and id.account_name
1802+ and self._message_store.accepts("register"),
1803+ )
1804
1805 def register(self):
1806 """
1807@@ -186,14 +197,16 @@ class RegistrationHandler(object):
1808 tags = None
1809 logging.error("Invalid tags provided for registration.")
1810
1811- message = {"type": "register",
1812- "hostname": get_fqdn(),
1813- "account_name": account_name,
1814- "computer_title": identity.computer_title,
1815- "registration_password": identity.registration_key,
1816- "tags": tags,
1817- "container-info": get_container_info(),
1818- "vm-info": get_vm_info()}
1819+ message = {
1820+ "type": "register",
1821+ "hostname": get_fqdn(),
1822+ "account_name": account_name,
1823+ "computer_title": identity.computer_title,
1824+ "registration_password": identity.registration_key,
1825+ "tags": tags,
1826+ "container-info": get_container_info(),
1827+ "vm-info": get_vm_info(),
1828+ }
1829
1830 if self._clone_secure_id:
1831 # We use the secure id here because the registration is encrypted
1832@@ -216,19 +229,20 @@ class RegistrationHandler(object):
1833 message["juju-info"] = {
1834 "environment-uuid": self._juju_data["environment-uuid"],
1835 "api-addresses": self._juju_data["api-addresses"],
1836- "machine-id": self._juju_data["machine-id"]}
1837+ "machine-id": self._juju_data["machine-id"],
1838+ }
1839
1840 # The computer is a normal computer, possibly a container.
1841 with_word = "with" if bool(registration_key) else "without"
1842- with_tags = "and tags %s " % tags if tags else ""
1843- with_group = "in access group '%s' " % group if group else ""
1844+ with_tags = f"and tags {tags} " if tags else ""
1845+ with_group = f"in access group '{group}' " if group else ""
1846
1847 message["ubuntu_pro_info"] = json.dumps(get_ubuntu_pro_info())
1848
1849 logging.info(
1850- u"Queueing message to register with account %r %s%s"
1851- "%s a password." % (
1852- account_name, with_group, with_tags, with_word))
1853+ f"Queueing message to register with account {account_name!r} "
1854+ f"{with_group}{with_tags}{with_word} a password.",
1855+ )
1856 self._exchange.send(message)
1857
1858 def _handle_set_id(self, message):
1859@@ -239,15 +253,18 @@ class RegistrationHandler(object):
1860
1861 Fire C{"registration-done"} and C{"resynchronize-clients"}.
1862 """
1863- id = self._identity
1864- if id.secure_id:
1865- logging.info("Overwriting secure_id with '%s'" % id.secure_id)
1866+ cid = self._identity
1867+ if cid.secure_id:
1868+ logging.info(f"Overwriting secure_id with '{cid.secure_id}'")
1869
1870- id.secure_id = message.get("id")
1871- id.insecure_id = message.get("insecure-id")
1872- logging.info("Using new secure-id ending with %s for account %s.",
1873- id.secure_id[-10:], id.account_name)
1874- logging.debug("Using new secure-id: %s", id.secure_id)
1875+ cid.secure_id = message.get("id")
1876+ cid.insecure_id = message.get("insecure-id")
1877+ logging.info(
1878+ "Using new secure-id ending with %s for account %s.",
1879+ cid.secure_id[-10:],
1880+ cid.account_name,
1881+ )
1882+ logging.debug("Using new secure-id: %s", cid.secure_id)
1883 self._reactor.fire("registration-done")
1884 self._reactor.fire("resynchronize-clients")
1885
1886@@ -257,19 +274,21 @@ class RegistrationHandler(object):
1887 self._reactor.fire("registration-failed", reason=message_info)
1888
1889 def _handle_unknown_id(self, message):
1890- id = self._identity
1891+ cid = self._identity
1892 clone = message.get("clone-of")
1893 if clone is None:
1894- logging.info("Client has unknown secure-id for account %s."
1895- % id.account_name)
1896+ logging.info(
1897+ "Client has unknown secure-id for account "
1898+ f"{cid.account_name}.",
1899+ )
1900 else: # Save the secure id as the clone, and clear it so it's renewed
1901- logging.info("Client is clone of computer %s" % clone)
1902- self._clone_secure_id = id.secure_id
1903- id.secure_id = None
1904- id.insecure_id = None
1905+ logging.info(f"Client is clone of computer {clone}")
1906+ self._clone_secure_id = cid.secure_id
1907+ cid.secure_id = None
1908+ cid.insecure_id = None
1909
1910
1911-class RegistrationResponse(object):
1912+class RegistrationResponse:
1913 """A helper for dealing with the response of a single registration request.
1914
1915 @ivar deferred: The L{Deferred} that will be fired as per
1916diff --git a/landscape/client/broker/server.py b/landscape/client/broker/server.py
1917index 1f8d708..766c6a0 100644
1918--- a/landscape/client/broker/server.py
1919+++ b/landscape/client/broker/server.py
1920@@ -42,15 +42,14 @@ Diagram::
1921 : exchange
1922
1923 """
1924-
1925 import logging
1926
1927 from twisted.internet.defer import Deferred
1928-from landscape.lib.compat import _PY3
1929
1930-from landscape.lib.twisted_util import gather_results
1931 from landscape.client.amp import remote
1932 from landscape.client.manager.manager import FAILED
1933+from landscape.lib.compat import _PY3
1934+from landscape.lib.twisted_util import gather_results
1935
1936
1937 def event(method):
1938@@ -71,7 +70,7 @@ def event(method):
1939 return broadcast_event
1940
1941
1942-class BrokerServer(object):
1943+class BrokerServer:
1944 """
1945 A broker server capable of handling messages from plugins connected using
1946 the L{BrokerProtocol}.
1947@@ -82,11 +81,20 @@ class BrokerServer(object):
1948 @param registration: The {RegistrationHandler}.
1949 @param message_store: The broker's L{MessageStore}.
1950 """
1951+
1952 name = "broker"
1953
1954- def __init__(self, config, reactor, exchange, registration,
1955- message_store, pinger):
1956+ def __init__(
1957+ self,
1958+ config,
1959+ reactor,
1960+ exchange,
1961+ registration,
1962+ message_store,
1963+ pinger,
1964+ ):
1965 from landscape.client.broker.amp import get_component_registry
1966+
1967 self.connectors_registry = get_component_registry()
1968 self._config = config
1969 self._reactor = reactor
1970@@ -99,8 +107,10 @@ class BrokerServer(object):
1971
1972 reactor.call_on("message", self.broadcast_message)
1973 reactor.call_on("impending-exchange", self.impending_exchange)
1974- reactor.call_on("message-type-acceptance-changed",
1975- self.message_type_acceptance_changed)
1976+ reactor.call_on(
1977+ "message-type-acceptance-changed",
1978+ self.message_type_acceptance_changed,
1979+ )
1980 reactor.call_on("server-uuid-changed", self.server_uuid_changed)
1981 reactor.call_on("package-data-changed", self.package_data_changed)
1982 reactor.call_on("resynchronize-clients", self.resynchronize)
1983@@ -201,7 +211,9 @@ class BrokerServer(object):
1984 message = {k.decode("ascii"): v for k, v in message.items()}
1985 message["type"] = message["type"].decode("ascii")
1986 if isinstance(session_id, bool) and message["type"] in (
1987- "operation-result", "change-packages-result"):
1988+ "operation-result",
1989+ "change-packages-result",
1990+ ):
1991 # XXX This means we're performing a Landscape-driven upgrade and
1992 # we're being invoked by a package-changer or release-upgrader
1993 # process that is running code which doesn't know about the
1994@@ -218,7 +230,8 @@ class BrokerServer(object):
1995
1996 if session_id is None:
1997 raise RuntimeError(
1998- "Session ID must be set before attempting to send a message")
1999+ "Session ID must be set before attempting to send a message",
2000+ )
2001 if self._message_store.is_valid_session_id(session_id):
2002 return self._exchanger.send(message, urgent=urgent)
2003
2004@@ -320,7 +333,6 @@ class BrokerServer(object):
2005 calls = []
2006
2007 def get_handler(event_type):
2008-
2009 def handler(**kwargs):
2010 for call in calls:
2011 self._reactor.cancel_call(call)
2012@@ -367,26 +379,30 @@ class BrokerServer(object):
2013 indicating as such.
2014 """
2015 opid = message.get("operation-id")
2016- if (True not in results and
2017- opid is not None and
2018- message["type"] != "resynchronize"
2019- ):
2020+ if (
2021+ True not in results
2022+ and opid is not None
2023+ and message["type"] != "resynchronize"
2024+ ):
2025
2026 mtype = message["type"]
2027- logging.error("Nobody handled the %s message." % (mtype,))
2028+ logging.error(f"Nobody handled the {mtype} message.")
2029
2030 result_text = """\
2031-Landscape client failed to handle this request (%s) because the
2032+Landscape client failed to handle this request ({}) because the
2033 plugin which should handle it isn't available. This could mean that the
2034 plugin has been intentionally disabled, or that the client isn't running
2035 properly, or you may be running an older version of the client that doesn't
2036 support this feature.
2037-""" % (mtype,)
2038+""".format(
2039+ mtype,
2040+ )
2041 response = {
2042 "type": "operation-result",
2043 "status": FAILED,
2044 "result-text": result_text,
2045- "operation-id": opid}
2046+ "operation-id": opid,
2047+ }
2048 self._exchanger.send(response, urgent=True)
2049
2050 @remote
2051diff --git a/landscape/client/broker/service.py b/landscape/client/broker/service.py
2052index 7e36149..9c86267 100644
2053--- a/landscape/client/broker/service.py
2054+++ b/landscape/client/broker/service.py
2055@@ -1,17 +1,19 @@
2056 """Deployment code for the monitor."""
2057-
2058 import os
2059
2060-from landscape.client.service import LandscapeService, run_landscape_service
2061 from landscape.client.amp import ComponentPublisher
2062-from landscape.client.broker.registration import RegistrationHandler, Identity
2063 from landscape.client.broker.config import BrokerConfiguration
2064-from landscape.client.broker.transport import HTTPTransport
2065 from landscape.client.broker.exchange import MessageExchange
2066 from landscape.client.broker.exchangestore import ExchangeStore
2067 from landscape.client.broker.ping import Pinger
2068-from landscape.client.broker.store import get_default_message_store
2069+from landscape.client.broker.registration import Identity
2070+from landscape.client.broker.registration import RegistrationHandler
2071 from landscape.client.broker.server import BrokerServer
2072+from landscape.client.broker.store import get_default_message_store
2073+from landscape.client.broker.transport import HTTPTransport
2074+from landscape.client.service import LandscapeService
2075+from landscape.client.service import run_landscape_service
2076+from landscape.client.watchdog import bootstrap_list
2077
2078
2079 class BrokerService(LandscapeService):
2080@@ -44,48 +46,82 @@ class BrokerService(LandscapeService):
2081 service_name = BrokerServer.name
2082
2083 def __init__(self, config):
2084+ self._config = config
2085 self.persist_filename = os.path.join(
2086- config.data_path, "%s.bpickle" % (self.service_name,))
2087- super(BrokerService, self).__init__(config)
2088+ config.data_path,
2089+ f"{self.service_name}.bpickle",
2090+ )
2091+ super().__init__(config)
2092
2093 self.transport = self.transport_factory(
2094- self.reactor, config.url, config.ssl_public_key)
2095+ self.reactor,
2096+ config.url,
2097+ config.ssl_public_key,
2098+ )
2099 self.message_store = get_default_message_store(
2100- self.persist, config.message_store_path)
2101+ self.persist,
2102+ config.message_store_path,
2103+ )
2104 self.identity = Identity(self.config, self.persist)
2105 exchange_store = ExchangeStore(self.config.exchange_store_path)
2106 self.exchanger = MessageExchange(
2107- self.reactor, self.message_store, self.transport, self.identity,
2108- exchange_store, config)
2109+ self.reactor,
2110+ self.message_store,
2111+ self.transport,
2112+ self.identity,
2113+ exchange_store,
2114+ config,
2115+ )
2116 self.pinger = self.pinger_factory(
2117- self.reactor, self.identity, self.exchanger, config)
2118+ self.reactor,
2119+ self.identity,
2120+ self.exchanger,
2121+ config,
2122+ )
2123 self.registration = RegistrationHandler(
2124- config, self.identity, self.reactor, self.exchanger, self.pinger,
2125- self.message_store)
2126- self.broker = BrokerServer(self.config, self.reactor, self.exchanger,
2127- self.registration, self.message_store,
2128- self.pinger)
2129- self.publisher = ComponentPublisher(self.broker, self.reactor,
2130- self.config)
2131-
2132- def startService(self):
2133+ config,
2134+ self.identity,
2135+ self.reactor,
2136+ self.exchanger,
2137+ self.pinger,
2138+ self.message_store,
2139+ )
2140+ self.broker = BrokerServer(
2141+ self.config,
2142+ self.reactor,
2143+ self.exchanger,
2144+ self.registration,
2145+ self.message_store,
2146+ self.pinger,
2147+ )
2148+ self.publisher = ComponentPublisher(
2149+ self.broker,
2150+ self.reactor,
2151+ self.config,
2152+ )
2153+
2154+ def startService(self): # noqa: N802
2155 """Start the broker.
2156
2157 Create a L{BrokerServer} listening on C{broker_socket_path} for clients
2158 connecting with the L{BrokerServerConnector}, and start the
2159 L{MessageExchange} and L{Pinger} services.
2160 """
2161- super(BrokerService, self).startService()
2162+ super().startService()
2163+ bootstrap_list.bootstrap(
2164+ data_path=self._config.data_path,
2165+ log_dir=self._config.log_dir,
2166+ )
2167 self.publisher.start()
2168 self.exchanger.start()
2169 self.pinger.start()
2170
2171- def stopService(self):
2172+ def stopService(self): # noqa: N802
2173 """Stop the broker."""
2174 deferred = self.publisher.stop()
2175 self.exchanger.stop()
2176 self.pinger.stop()
2177- super(BrokerService, self).stopService()
2178+ super().stopService()
2179 return deferred
2180
2181
2182diff --git a/landscape/client/broker/store.py b/landscape/client/broker/store.py
2183index ebab5cf..7557d45 100644
2184--- a/landscape/client/broker/store.py
2185+++ b/landscape/client/broker/store.py
2186@@ -91,25 +91,28 @@ See L{MessageStore} for details about how messages are stored on the file
2187 system and L{landscape.lib.message.got_next_expected} to check how the
2188 strategy for updating the pending offset and the sequence is implemented.
2189 """
2190-
2191 import itertools
2192 import logging
2193 import os
2194+import shutil
2195+import traceback
2196 import uuid
2197
2198 from twisted.python.compat import iteritems
2199
2200 from landscape import DEFAULT_SERVER_API
2201 from landscape.lib import bpickle
2202-from landscape.lib.fs import create_binary_file, read_binary_file
2203-from landscape.lib.versioning import sort_versions, is_version_higher
2204+from landscape.lib.fs import create_binary_file
2205+from landscape.lib.fs import read_binary_file
2206+from landscape.lib.versioning import is_version_higher
2207+from landscape.lib.versioning import sort_versions
2208
2209
2210 HELD = "h"
2211 BROKEN = "b"
2212
2213
2214-class MessageStore(object):
2215+class MessageStore:
2216 """A message store which stores its messages in a file system hierarchy.
2217
2218 Beside the "sequence" and the "pending offset" values described in the
2219@@ -134,9 +137,12 @@ class MessageStore(object):
2220 # in case the server supports it.
2221 _api = DEFAULT_SERVER_API
2222
2223- def __init__(self, persist, directory, directory_size=1000):
2224+ def __init__(self, persist, directory, directory_size=1000, max_dirs=4,
2225+ max_size_mb=400):
2226 self._directory = directory
2227 self._directory_size = directory_size
2228+ self._max_dirs = max_dirs # Maximum number of directories in store
2229+ self._max_size_mb = max_size_mb # Maximum size of message store
2230 self._schemas = {}
2231 self._original_persist = persist
2232 self._persist = persist.root_at("message-store")
2233@@ -273,7 +279,7 @@ class MessageStore(object):
2234 logging.exception(e)
2235 self._add_flags(filename, BROKEN)
2236 else:
2237- if u"type" not in message:
2238+ if "type" not in message:
2239 # Special case to decode keys for messages which were
2240 # serialized by py27 prior to py3 upgrade, and having
2241 # implicit byte message keys. Message may still get
2242@@ -281,8 +287,9 @@ class MessageStore(object):
2243 # broker. (lp: #1718689)
2244 message = {
2245 (k if isinstance(k, str) else k.decode("ascii")): v
2246- for k, v in message.items()}
2247- message[u"type"] = message[u"type"].decode("ascii")
2248+ for k, v in message.items()
2249+ }
2250+ message["type"] = message["type"].decode("ascii")
2251
2252 unknown_type = message["type"] not in accepted_types
2253 unknown_api = not is_version_higher(server_api, message["api"])
2254@@ -292,10 +299,53 @@ class MessageStore(object):
2255 messages.append(message)
2256 return messages
2257
2258+ def get_messages_total_size(self):
2259+ """Get total size of messages directory"""
2260+ sizes = []
2261+ for dirname in os.listdir(self._directory):
2262+ dirpath = os.path.join(self._directory, dirname)
2263+ dirsize = sum(file.stat().st_size for file in os.scandir(dirpath))
2264+ sizes.append(dirsize)
2265+ return sum(sizes)
2266+
2267+ def delete_messages_over_limit(self):
2268+ """
2269+ Delete messages dirs if there's any over the max, which happens if
2270+ messages are queued up but not able to be sent
2271+ """
2272+
2273+ cur_dirs = os.listdir(self._directory)
2274+ cur_dirs.sort(key=int) # Since you could have 0, .., 9, 10
2275+ num_dirs = len(cur_dirs)
2276+
2277+ num_dirs_to_delete = max(0, num_dirs - self._max_dirs) # No negatives
2278+ dirs_to_delete = cur_dirs[:num_dirs_to_delete] # Chop off beginning
2279+
2280+ for dirname in dirs_to_delete:
2281+ dirpath = os.path.join(self._directory, dirname)
2282+ try:
2283+ logging.debug(f"Trimming message store: {dirpath}")
2284+ shutil.rmtree(dirpath)
2285+ except Exception: # We want to continue like normal if any error
2286+ logging.warning(traceback.format_exc())
2287+ logging.warning("Unable to delete message directory!")
2288+ logging.warning(dirpath)
2289+
2290+ # Something is wrong if after deleting a bunch of files, we are still
2291+ # using too much space. Rather then look around for big files, we just
2292+ # start over.
2293+ num_bytes = self.get_messages_total_size()
2294+ num_mb = num_bytes / 1e6
2295+ if num_mb > self._max_size_mb:
2296+ logging.warning("Messages too large! Clearing all messages!")
2297+ self.delete_all_messages()
2298+
2299 def delete_old_messages(self):
2300 """Delete messages which are unlikely to be needed in the future."""
2301- for fn in itertools.islice(self._walk_messages(exclude=HELD + BROKEN),
2302- self.get_pending_offset()):
2303+ for fn in itertools.islice(
2304+ self._walk_messages(exclude=HELD + BROKEN),
2305+ self.get_pending_offset(),
2306+ ):
2307 os.unlink(fn)
2308 containing_dir = os.path.split(fn)[0]
2309 if not os.listdir(containing_dir):
2310@@ -326,9 +376,9 @@ class MessageStore(object):
2311 pending_offset = self.get_pending_offset()
2312 for filename in self._walk_messages(exclude=BROKEN):
2313 flags = self._get_flags(filename)
2314- if ((HELD in flags or i >= pending_offset) and
2315- os.stat(filename).st_ino == message_id
2316- ):
2317+ if (HELD in flags or i >= pending_offset) and os.stat(
2318+ filename,
2319+ ).st_ino == message_id:
2320 return True
2321 if BROKEN not in flags and HELD not in flags:
2322 i += 1
2323@@ -347,7 +397,8 @@ class MessageStore(object):
2324 if not self._persist.has("first-failure-time"):
2325 self._persist.set("first-failure-time", timestamp)
2326 continued_failure_time = timestamp - self._persist.get(
2327- "first-failure-time")
2328+ "first-failure-time",
2329+ )
2330 if self._persist.get("blackhole-messages"):
2331 # Already added the resync message
2332 return
2333@@ -357,7 +408,8 @@ class MessageStore(object):
2334 self._persist.set("blackhole-messages", True)
2335 logging.warning(
2336 "Unable to succesfully communicate with Landscape server "
2337- "for more than a week. Waiting for resync.")
2338+ "for more than a week. Waiting for resync.",
2339+ )
2340
2341 def add(self, message):
2342 """Queue a message for delivery.
2343@@ -373,6 +425,8 @@ class MessageStore(object):
2344 logging.debug("Dropped message, awaiting resync.")
2345 return
2346
2347+ self.delete_messages_over_limit()
2348+
2349 server_api = self.get_server_api()
2350
2351 if "api" not in message:
2352@@ -432,7 +486,8 @@ class MessageStore(object):
2353 """Walk the files which are definitely pending."""
2354 pending_offset = self.get_pending_offset()
2355 for i, filename in enumerate(
2356- self._walk_messages(exclude=HELD + BROKEN)):
2357+ self._walk_messages(exclude=HELD + BROKEN),
2358+ ):
2359 if i >= pending_offset:
2360 yield filename
2361
2362@@ -443,12 +498,15 @@ class MessageStore(object):
2363 for message_dir in message_dirs:
2364 for filename in self._get_sorted_filenames(message_dir):
2365 flags = set(self._get_flags(filename))
2366- if (not exclude or not exclude & flags):
2367+ if not exclude or not exclude & flags:
2368 yield self._message_dir(message_dir, filename)
2369
2370 def _get_sorted_filenames(self, dir=""):
2371- message_files = [x for x in os.listdir(self._message_dir(dir))
2372- if not x.endswith(".tmp")]
2373+ message_files = [
2374+ x
2375+ for x in os.listdir(self._message_dir(dir))
2376+ if not x.endswith(".tmp")
2377+ ]
2378 message_files.sort(key=lambda x: int(x.split("_")[0]))
2379 return message_files
2380
2381@@ -549,6 +607,7 @@ def get_default_message_store(*args, **kwargs):
2382 Get a L{MessageStore} object with all Landscape message schemas added.
2383 """
2384 from landscape.message_schemas.server_bound import message_schemas
2385+
2386 store = MessageStore(*args, **kwargs)
2387 for schema in message_schemas:
2388 store.add_schema(schema)
2389diff --git a/landscape/client/broker/tests/helpers.py b/landscape/client/broker/tests/helpers.py
2390index 367a03f..2ce52b0 100644
2391--- a/landscape/client/broker/tests/helpers.py
2392+++ b/landscape/client/broker/tests/helpers.py
2393@@ -7,23 +7,24 @@ connected to remote test L{BrokerClient}.
2394 """
2395 import os
2396
2397-from landscape.lib.persist import Persist
2398-from landscape.lib.testing import FakeReactor
2399-from landscape.client.watchdog import bootstrap_list
2400 from landscape.client.amp import ComponentPublisher
2401-from landscape.client.broker.transport import FakeTransport
2402+from landscape.client.broker.amp import RemoteBrokerConnector
2403+from landscape.client.broker.client import BrokerClient
2404+from landscape.client.broker.config import BrokerConfiguration
2405 from landscape.client.broker.exchange import MessageExchange
2406 from landscape.client.broker.exchangestore import ExchangeStore
2407-from landscape.client.broker.store import get_default_message_store
2408-from landscape.client.broker.registration import Identity, RegistrationHandler
2409 from landscape.client.broker.ping import Pinger
2410-from landscape.client.broker.config import BrokerConfiguration
2411+from landscape.client.broker.registration import Identity
2412+from landscape.client.broker.registration import RegistrationHandler
2413 from landscape.client.broker.server import BrokerServer
2414-from landscape.client.broker.amp import RemoteBrokerConnector
2415-from landscape.client.broker.client import BrokerClient
2416+from landscape.client.broker.store import get_default_message_store
2417+from landscape.client.broker.transport import FakeTransport
2418+from landscape.client.watchdog import bootstrap_list
2419+from landscape.lib.persist import Persist
2420+from landscape.lib.testing import FakeReactor
2421
2422
2423-class BrokerConfigurationHelper(object):
2424+class BrokerConfigurationHelper:
2425 """Setup a L{BrokerConfiguration} instance with some test config values.
2426
2427 The following attributes will be set on your test case:
2428@@ -37,8 +38,10 @@ class BrokerConfigurationHelper(object):
2429 def set_up(self, test_case):
2430 data_path = test_case.makeDir()
2431 log_dir = test_case.makeDir()
2432- test_case.config_filename = os.path.join(test_case.makeDir(),
2433- "client.conf")
2434+ test_case.config_filename = os.path.join(
2435+ test_case.makeDir(),
2436+ "client.conf",
2437+ )
2438
2439 with open(test_case.config_filename, "w") as fh:
2440 fh.write(
2441@@ -47,8 +50,9 @@ class BrokerConfigurationHelper(object):
2442 "computer_title = Some Computer\n"
2443 "account_name = some_account\n"
2444 "ping_url = http://localhost:91910\n"
2445- "data_path = %s\n"
2446- "log_dir = %s\n" % (data_path, log_dir))
2447+ f"data_path = {data_path}\n"
2448+ f"log_dir = {log_dir}\n",
2449+ )
2450
2451 bootstrap_list.bootstrap(data_path=data_path, log_dir=log_dir)
2452
2453@@ -87,20 +91,31 @@ class ExchangeHelper(BrokerConfigurationHelper):
2454 """
2455
2456 def set_up(self, test_case):
2457- super(ExchangeHelper, self).set_up(test_case)
2458+ super().set_up(test_case)
2459 test_case.persist_filename = test_case.makePersistFile()
2460 test_case.persist = Persist(filename=test_case.persist_filename)
2461 test_case.mstore = get_default_message_store(
2462- test_case.persist, test_case.config.message_store_path)
2463+ test_case.persist,
2464+ test_case.config.message_store_path,
2465+ )
2466 test_case.identity = Identity(test_case.config, test_case.persist)
2467- test_case.transport = FakeTransport(None, test_case.config.url,
2468- test_case.config.ssl_public_key)
2469+ test_case.transport = FakeTransport(
2470+ None,
2471+ test_case.config.url,
2472+ test_case.config.ssl_public_key,
2473+ )
2474 test_case.reactor = FakeReactor()
2475 test_case.exchange_store = ExchangeStore(
2476- test_case.config.exchange_store_path)
2477+ test_case.config.exchange_store_path,
2478+ )
2479 test_case.exchanger = MessageExchange(
2480- test_case.reactor, test_case.mstore, test_case.transport,
2481- test_case.identity, test_case.exchange_store, test_case.config)
2482+ test_case.reactor,
2483+ test_case.mstore,
2484+ test_case.transport,
2485+ test_case.identity,
2486+ test_case.exchange_store,
2487+ test_case.config,
2488+ )
2489
2490
2491 class RegistrationHelper(ExchangeHelper):
2492@@ -116,16 +131,27 @@ class RegistrationHelper(ExchangeHelper):
2493 """
2494
2495 def set_up(self, test_case):
2496- super(RegistrationHelper, self).set_up(test_case)
2497- test_case.pinger = Pinger(test_case.reactor, test_case.identity,
2498- test_case.exchanger, test_case.config)
2499+ super().set_up(test_case)
2500+ test_case.pinger = Pinger(
2501+ test_case.reactor,
2502+ test_case.identity,
2503+ test_case.exchanger,
2504+ test_case.config,
2505+ )
2506 test_case.config.cloud = getattr(test_case, "cloud", False)
2507 if hasattr(test_case, "juju_contents"):
2508 test_case.makeFile(
2509- test_case.juju_contents, path=test_case.config.juju_filename)
2510+ test_case.juju_contents,
2511+ path=test_case.config.juju_filename,
2512+ )
2513 test_case.handler = RegistrationHandler(
2514- test_case.config, test_case.identity, test_case.reactor,
2515- test_case.exchanger, test_case.pinger, test_case.mstore)
2516+ test_case.config,
2517+ test_case.identity,
2518+ test_case.reactor,
2519+ test_case.exchanger,
2520+ test_case.pinger,
2521+ test_case.mstore,
2522+ )
2523
2524
2525 class BrokerServerHelper(RegistrationHelper):
2526@@ -139,10 +165,15 @@ class BrokerServerHelper(RegistrationHelper):
2527 """
2528
2529 def set_up(self, test_case):
2530- super(BrokerServerHelper, self).set_up(test_case)
2531- test_case.broker = BrokerServer(test_case.config, test_case.reactor,
2532- test_case.exchanger, test_case.handler,
2533- test_case.mstore, test_case.pinger)
2534+ super().set_up(test_case)
2535+ test_case.broker = BrokerServer(
2536+ test_case.config,
2537+ test_case.reactor,
2538+ test_case.exchanger,
2539+ test_case.handler,
2540+ test_case.mstore,
2541+ test_case.pinger,
2542+ )
2543
2544
2545 class RemoteBrokerHelper(BrokerServerHelper):
2546@@ -168,13 +199,17 @@ class RemoteBrokerHelper(BrokerServerHelper):
2547 """
2548
2549 def set_up(self, test_case):
2550- super(RemoteBrokerHelper, self).set_up(test_case)
2551-
2552- self._publisher = ComponentPublisher(test_case.broker,
2553- test_case.reactor,
2554- test_case.config)
2555- self._connector = RemoteBrokerConnector(test_case.reactor,
2556- test_case.config)
2557+ super().set_up(test_case)
2558+
2559+ self._publisher = ComponentPublisher(
2560+ test_case.broker,
2561+ test_case.reactor,
2562+ test_case.config,
2563+ )
2564+ self._connector = RemoteBrokerConnector(
2565+ test_case.reactor,
2566+ test_case.config,
2567+ )
2568
2569 self._publisher.start()
2570 deferred = self._connector.connect()
2571@@ -183,7 +218,7 @@ class RemoteBrokerHelper(BrokerServerHelper):
2572 def tear_down(self, test_case):
2573 self._connector.disconnect()
2574 self._publisher.stop()
2575- super(RemoteBrokerHelper, self).tear_down(test_case)
2576+ super().tear_down(test_case)
2577
2578
2579 class BrokerClientHelper(RemoteBrokerHelper):
2580@@ -204,7 +239,7 @@ class BrokerClientHelper(RemoteBrokerHelper):
2581 """
2582
2583 def set_up(self, test_case):
2584- super(BrokerClientHelper, self).set_up(test_case)
2585+ super().set_up(test_case)
2586 # The client needs its own reactor to avoid infinite loops
2587 # when the broker broadcasts and event
2588 test_case.client_reactor = FakeReactor()
2589@@ -227,10 +262,12 @@ class RemoteClientHelper(BrokerClientHelper):
2590 """
2591
2592 def set_up(self, test_case):
2593- super(RemoteClientHelper, self).set_up(test_case)
2594- self._client_publisher = ComponentPublisher(test_case.client,
2595- test_case.reactor,
2596- test_case.config)
2597+ super().set_up(test_case)
2598+ self._client_publisher = ComponentPublisher(
2599+ test_case.client,
2600+ test_case.reactor,
2601+ test_case.config,
2602+ )
2603 self._client_publisher.start()
2604 test_case.remote.register_client("client")
2605 test_case.remote_client = test_case.broker.get_client("client")
2606@@ -239,4 +276,4 @@ class RemoteClientHelper(BrokerClientHelper):
2607 def tear_down(self, test_case):
2608 self._client_connector.disconnect()
2609 self._client_publisher.stop()
2610- super(RemoteClientHelper, self).tear_down(test_case)
2611+ super().tear_down(test_case)
2612diff --git a/landscape/client/broker/tests/test_amp.py b/landscape/client/broker/tests/test_amp.py
2613index a017dc1..bc992b4 100644
2614--- a/landscape/client/broker/tests/test_amp.py
2615+++ b/landscape/client/broker/tests/test_amp.py
2616@@ -1,10 +1,10 @@
2617-import mock
2618+from unittest import mock
2619
2620+from landscape.client.broker.tests.helpers import RemoteBrokerHelper
2621+from landscape.client.broker.tests.helpers import RemoteClientHelper
2622+from landscape.client.tests.helpers import DEFAULT_ACCEPTED_TYPES
2623+from landscape.client.tests.helpers import LandscapeTest
2624 from landscape.lib.amp import MethodCallError
2625-from landscape.client.tests.helpers import (
2626- LandscapeTest, DEFAULT_ACCEPTED_TYPES)
2627-from landscape.client.broker.tests.helpers import (
2628- RemoteBrokerHelper, RemoteClientHelper)
2629
2630
2631 class RemoteBrokerTest(LandscapeTest):
2632@@ -41,13 +41,13 @@ class RemoteBrokerTest(LandscapeTest):
2633
2634 session_id = self.successResultOf(self.remote.get_session_id())
2635 message_id = self.successResultOf(
2636- self.remote.send_message(message, session_id))
2637+ self.remote.send_message(message, session_id),
2638+ )
2639
2640 self.assertTrue(isinstance(message_id, int))
2641 self.assertTrue(self.mstore.is_pending(message_id))
2642 self.assertFalse(self.exchanger.is_urgent())
2643- self.assertMessages(self.mstore.get_pending_messages(),
2644- [message])
2645+ self.assertMessages(self.mstore.get_pending_messages(), [message])
2646
2647 def test_send_message_with_urgent(self):
2648 """
2649@@ -56,8 +56,9 @@ class RemoteBrokerTest(LandscapeTest):
2650 message = {"type": "test"}
2651 self.mstore.set_accepted_types(["test"])
2652 session_id = self.successResultOf(self.remote.get_session_id())
2653- message_id = self.successResultOf(self.remote.send_message(
2654- message, session_id, urgent=True))
2655+ message_id = self.successResultOf(
2656+ self.remote.send_message(message, session_id, urgent=True),
2657+ )
2658 self.assertTrue(isinstance(message_id, int))
2659 self.assertTrue(self.exchanger.is_urgent())
2660
2661@@ -95,8 +96,9 @@ class RemoteBrokerTest(LandscapeTest):
2662 a L{Deferred}.
2663 """
2664 # This should make the registration succeed
2665- self.transport.responses.append([{"type": "set-id", "id": "abc",
2666- "insecure-id": "def"}])
2667+ self.transport.responses.append(
2668+ [{"type": "set-id", "id": "abc", "insecure-id": "def"}],
2669+ )
2670 result = self.remote.register()
2671 return self.assertSuccess(result, None)
2672
2673@@ -130,7 +132,8 @@ class RemoteBrokerTest(LandscapeTest):
2674 self.assertEqual(response, None)
2675 self.assertEqual(
2676 self.exchanger.get_client_accepted_message_types(),
2677- sorted(["type"] + DEFAULT_ACCEPTED_TYPES))
2678+ sorted(["type"] + DEFAULT_ACCEPTED_TYPES),
2679+ )
2680
2681 result = self.remote.register_client_accepted_message_type("type")
2682 return result.addCallback(assert_response)
2683@@ -159,8 +162,11 @@ class RemoteBrokerTest(LandscapeTest):
2684 The L{RemoteBroker.call_if_accepted} method doesn't do anything if the
2685 given message type is not accepted.
2686 """
2687- function = (lambda: 1 / 0)
2688- result = self.remote.call_if_accepted("test", function)
2689+
2690+ def division_by_zero():
2691+ raise ZeroDivisionError()
2692+
2693+ result = self.remote.call_if_accepted("test", division_by_zero)
2694 return self.assertSuccess(result, None)
2695
2696 def test_listen_events(self):
2697@@ -181,8 +187,9 @@ class RemoteBrokerTest(LandscapeTest):
2698 """
2699 callback1 = mock.Mock()
2700 callback2 = mock.Mock(return_value=123)
2701- deferred = self.remote.call_on_event({"event1": callback1,
2702- "event2": callback2})
2703+ deferred = self.remote.call_on_event(
2704+ {"event1": callback1, "event2": callback2},
2705+ )
2706 self.reactor.call_later(0.05, self.reactor.fire, "event2")
2707 self.reactor.advance(0.05)
2708 self.remote._factory.fake_connection.flush()
2709@@ -228,8 +235,10 @@ class RemoteClientTest(LandscapeTest):
2710 a L{Deferred}.
2711 """
2712 handler = mock.Mock()
2713- with mock.patch.object(self.client.broker,
2714- "register_client_accepted_message_type") as m:
2715+ with mock.patch.object(
2716+ self.client.broker,
2717+ "register_client_accepted_message_type",
2718+ ) as m:
2719 # We need to register a test message handler to let the dispatch
2720 # message method call succeed
2721 self.client.register_message("test", handler)
2722diff --git a/landscape/client/broker/tests/test_client.py b/landscape/client/broker/tests/test_client.py
2723index 3295b02..219cc0c 100644
2724--- a/landscape/client/broker/tests/test_client.py
2725+++ b/landscape/client/broker/tests/test_client.py
2726@@ -1,14 +1,14 @@
2727-import mock
2728+from unittest import mock
2729
2730 from twisted.internet import reactor
2731 from twisted.internet.defer import Deferred
2732
2733-from landscape.lib.twisted_util import gather_results
2734-from landscape.client.tests.helpers import (
2735- LandscapeTest, DEFAULT_ACCEPTED_TYPES)
2736+from landscape.client.broker.client import BrokerClientPlugin
2737+from landscape.client.broker.client import HandlerNotFoundError
2738 from landscape.client.broker.tests.helpers import BrokerClientHelper
2739-from landscape.client.broker.client import (
2740- BrokerClientPlugin, HandlerNotFoundError)
2741+from landscape.client.tests.helpers import DEFAULT_ACCEPTED_TYPES
2742+from landscape.client.tests.helpers import LandscapeTest
2743+from landscape.lib.twisted_util import gather_results
2744
2745
2746 class BrokerClientTest(LandscapeTest):
2747@@ -45,7 +45,8 @@ class BrokerClientTest(LandscapeTest):
2748 getting a session id.
2749 """
2750 test_session_id = self.successResultOf(
2751- self.client.broker.get_session_id(scope="test"))
2752+ self.client.broker.get_session_id(scope="test"),
2753+ )
2754 plugin = BrokerClientPlugin()
2755 plugin.scope = "test"
2756 self.client.add(plugin)
2757@@ -130,8 +131,10 @@ class BrokerClientTest(LandscapeTest):
2758 If a plugin has a run method, the reactor will call it every
2759 run_interval, but will stop and log if it raises unhandled exceptions.
2760 """
2761+
2762 class RunFailure(Exception):
2763 pass
2764+
2765 # log helper should not complain on the error we're testing
2766 self.log_helper.ignore_errors("BrokerClientPlugin.*")
2767 plugin = BrokerClientPlugin()
2768@@ -146,7 +149,8 @@ class BrokerClientTest(LandscapeTest):
2769 # message entry that would be present on a live client.
2770 self.assertIn(
2771 "ERROR: BrokerClientPlugin raised an uncaught exception",
2772- self.logfile.getvalue())
2773+ self.logfile.getvalue(),
2774+ )
2775
2776 def test_run_interval_blocked_during_resynch(self):
2777 """
2778@@ -220,7 +224,8 @@ class BrokerClientTest(LandscapeTest):
2779 def got_result(result):
2780 self.assertEqual(
2781 self.exchanger.get_client_accepted_message_types(),
2782- sorted(["bar", "foo"] + DEFAULT_ACCEPTED_TYPES))
2783+ sorted(["bar", "foo"] + DEFAULT_ACCEPTED_TYPES),
2784+ )
2785
2786 return gather_results([result1, result2]).addCallback(got_result)
2787
2788@@ -251,8 +256,10 @@ class BrokerClientTest(LandscapeTest):
2789
2790 def dispatch_message(result):
2791 self.assertIs(self.client.dispatch_message(message), None)
2792- self.assertTrue("Error running message handler for type 'foo'" in
2793- self.logfile.getvalue())
2794+ self.assertTrue(
2795+ "Error running message handler for type 'foo'"
2796+ in self.logfile.getvalue(),
2797+ )
2798 handle_message.assert_called_once_with(message)
2799
2800 result = self.client.register_message("foo", handle_message)
2801@@ -263,8 +270,11 @@ class BrokerClientTest(LandscapeTest):
2802 L{BrokerClient.dispatch_message} raises an error if no handler was
2803 found for the given message.
2804 """
2805- error = self.assertRaises(HandlerNotFoundError,
2806- self.client.dispatch_message, {"type": "x"})
2807+ error = self.assertRaises(
2808+ HandlerNotFoundError,
2809+ self.client.dispatch_message,
2810+ {"type": "x"},
2811+ )
2812 self.assertEqual(str(error), "x")
2813
2814 def test_message(self):
2815@@ -326,8 +336,9 @@ class BrokerClientTest(LandscapeTest):
2816 self.client.add(plugin1)
2817 self.client.add(plugin2)
2818 self.client.exchange()
2819- self.assertTrue("Error during plugin exchange" in
2820- self.logfile.getvalue())
2821+ self.assertTrue(
2822+ "Error during plugin exchange" in self.logfile.getvalue(),
2823+ )
2824 self.assertTrue("ZeroDivisionError" in self.logfile.getvalue())
2825 plugin1.exchange.assert_called_once_with()
2826 plugin2.exchange.assert_called_once_with()
2827@@ -342,8 +353,10 @@ class BrokerClientTest(LandscapeTest):
2828 plugin.exchange = mock.Mock()
2829 self.client.add(plugin)
2830 self.client_reactor.fire("impending-exchange")
2831- self.assertTrue("Got notification of impending exchange. "
2832- "Notifying all plugins." in self.logfile.getvalue())
2833+ self.assertTrue(
2834+ "Got notification of impending exchange. "
2835+ "Notifying all plugins." in self.logfile.getvalue(),
2836+ )
2837 plugin.exchange.assert_called_once_with()
2838
2839 def test_fire_event(self):
2840@@ -415,7 +428,9 @@ class BrokerClientTest(LandscapeTest):
2841 calls = [mock.call("bar"), mock.call("foo")]
2842
2843 broker.register_client_accepted_message_type.assert_has_calls(
2844- calls, any_order=True)
2845+ calls,
2846+ any_order=True,
2847+ )
2848 broker.register_client.assert_called_once_with("client")
2849
2850 return gather_results([result1, result2]).addCallback(got_result)
2851diff --git a/landscape/client/broker/tests/test_config.py b/landscape/client/broker/tests/test_config.py
2852index 0b60fc4..b5236ae 100644
2853--- a/landscape/client/broker/tests/test_config.py
2854+++ b/landscape/client/broker/tests/test_config.py
2855@@ -1,8 +1,8 @@
2856 import os
2857
2858 from landscape.client.broker.config import BrokerConfiguration
2859-from landscape.lib.testing import EnvironSaverHelper
2860 from landscape.client.tests.helpers import LandscapeTest
2861+from landscape.lib.testing import EnvironSaverHelper
2862
2863
2864 class ConfigurationTests(LandscapeTest):
2865@@ -20,9 +20,16 @@ class ConfigurationTests(LandscapeTest):
2866 del os.environ["https_proxy"]
2867
2868 configuration = BrokerConfiguration()
2869- configuration.load(["--http-proxy", "foo",
2870- "--https-proxy", "bar",
2871- "--url", "whatever"])
2872+ configuration.load(
2873+ [
2874+ "--http-proxy",
2875+ "foo",
2876+ "--https-proxy",
2877+ "bar",
2878+ "--url",
2879+ "whatever",
2880+ ],
2881+ )
2882 self.assertEqual(os.environ["http_proxy"], "foo")
2883 self.assertEqual(os.environ["https_proxy"], "bar")
2884
2885@@ -53,9 +60,9 @@ class ConfigurationTests(LandscapeTest):
2886 os.environ["https_proxy"] = "originals"
2887
2888 configuration = BrokerConfiguration()
2889- configuration.load(["--http-proxy", "x",
2890- "--https-proxy", "y",
2891- "--url", "whatever"])
2892+ configuration.load(
2893+ ["--http-proxy", "x", "--https-proxy", "y", "--url", "whatever"],
2894+ )
2895 self.assertEqual(os.environ["http_proxy"], "x")
2896 self.assertEqual(os.environ["https_proxy"], "y")
2897
2898@@ -74,10 +81,12 @@ class ConfigurationTests(LandscapeTest):
2899 The 'urgent_exchange_interval, 'exchange_interval' and 'ping_interval'
2900 values specified in the configuration file are converted to integers.
2901 """
2902- filename = self.makeFile("[client]\n"
2903- "urgent_exchange_interval = 12\n"
2904- "exchange_interval = 34\n"
2905- "ping_interval = 6\n")
2906+ filename = self.makeFile(
2907+ "[client]\n"
2908+ "urgent_exchange_interval = 12\n"
2909+ "exchange_interval = 34\n"
2910+ "ping_interval = 6\n",
2911+ )
2912
2913 configuration = BrokerConfiguration()
2914 configuration.load(["--config", filename, "--url", "whatever"])
2915@@ -91,8 +100,9 @@ class ConfigurationTests(LandscapeTest):
2916 The 'tags' value specified in the configuration file is not converted
2917 to a list (it must be a string). See bug #1228301.
2918 """
2919- filename = self.makeFile("[client]\n"
2920- "tags = check,linode,profile-test")
2921+ filename = self.makeFile(
2922+ "[client]\ntags = check,linode,profile-test",
2923+ )
2924
2925 configuration = BrokerConfiguration()
2926 configuration.load(["--config", filename, "--url", "whatever"])
2927@@ -104,8 +114,7 @@ class ConfigurationTests(LandscapeTest):
2928 The 'access_group' value specified in the configuration file is
2929 passed through.
2930 """
2931- filename = self.makeFile("[client]\n"
2932- "access_group = webserver")
2933+ filename = self.makeFile("[client]\naccess_group = webserver")
2934
2935 configuration = BrokerConfiguration()
2936 configuration.load(["--config", filename, "--url", "whatever"])
2937@@ -122,5 +131,7 @@ class ConfigurationTests(LandscapeTest):
2938 configuration = BrokerConfiguration()
2939 configuration.load(["--config", filename])
2940
2941- self.assertEqual(configuration.url,
2942- "https://landscape.canonical.com/message-system")
2943+ self.assertEqual(
2944+ configuration.url,
2945+ "https://landscape.canonical.com/message-system",
2946+ )
2947diff --git a/landscape/client/broker/tests/test_exchange.py b/landscape/client/broker/tests/test_exchange.py
2948index 3736bd4..bc7fb5f 100644
2949--- a/landscape/client/broker/tests/test_exchange.py
2950+++ b/landscape/client/broker/tests/test_exchange.py
2951@@ -1,22 +1,23 @@
2952-import mock
2953+from unittest import mock
2954
2955 from landscape import CLIENT_API
2956-from landscape.lib.persist import Persist
2957-from landscape.lib.fetch import HTTPCodeError, PyCurlError
2958-from landscape.lib.hashlib import md5
2959-from landscape.lib.schema import Int
2960-from landscape.message_schemas.message import Message
2961 from landscape.client.broker.config import BrokerConfiguration
2962-from landscape.client.broker.exchange import (
2963- get_accepted_types_diff, MessageExchange)
2964-from landscape.client.broker.transport import FakeTransport
2965-from landscape.client.broker.store import MessageStore
2966+from landscape.client.broker.exchange import get_accepted_types_diff
2967+from landscape.client.broker.exchange import MessageExchange
2968 from landscape.client.broker.ping import Pinger
2969 from landscape.client.broker.registration import RegistrationHandler
2970-from landscape.client.tests.helpers import (
2971- LandscapeTest, DEFAULT_ACCEPTED_TYPES)
2972-from landscape.client.broker.tests.helpers import ExchangeHelper
2973 from landscape.client.broker.server import BrokerServer
2974+from landscape.client.broker.store import MessageStore
2975+from landscape.client.broker.tests.helpers import ExchangeHelper
2976+from landscape.client.broker.transport import FakeTransport
2977+from landscape.client.tests.helpers import DEFAULT_ACCEPTED_TYPES
2978+from landscape.client.tests.helpers import LandscapeTest
2979+from landscape.lib.fetch import HTTPCodeError
2980+from landscape.lib.fetch import PyCurlError
2981+from landscape.lib.hashlib import md5
2982+from landscape.lib.persist import Persist
2983+from landscape.lib.schema import Int
2984+from landscape.message_schemas.message import Message
2985
2986
2987 class MessageExchangeTest(LandscapeTest):
2988@@ -24,11 +25,11 @@ class MessageExchangeTest(LandscapeTest):
2989 helpers = [ExchangeHelper]
2990
2991 def setUp(self):
2992- super(MessageExchangeTest, self).setUp()
2993+ super().setUp()
2994 self.mstore.add_schema(Message("empty", {}))
2995 self.mstore.add_schema(Message("data", {"data": Int()}))
2996 self.mstore.add_schema(Message("holdme", {}))
2997- self.identity.secure_id = 'needs-to-be-set-for-tests-to-pass'
2998+ self.identity.secure_id = "needs-to-be-set-for-tests-to-pass"
2999
3000 def wait_for_exchange(self, urgent=False, factor=1, delta=0):
3001 if urgent:
3002@@ -52,9 +53,14 @@ class MessageExchangeTest(LandscapeTest):
3003 session IDs are expired, so any new messages being sent with those IDs
3004 will be discarded.
3005 """
3006- broker = BrokerServer(self.config, self.reactor,
3007- self.exchanger, None,
3008- self.mstore, None)
3009+ broker = BrokerServer(
3010+ self.config,
3011+ self.reactor,
3012+ self.exchanger,
3013+ None,
3014+ self.mstore,
3015+ None,
3016+ )
3017
3018 disk_session_id = self.mstore.get_session_id(scope="disk")
3019 package_session_id = self.mstore.get_session_id(scope="package")
3020@@ -72,9 +78,14 @@ class MessageExchangeTest(LandscapeTest):
3021 When a resynchronisation event occurs with a scope existing session IDs
3022 for that scope are expired, all other session IDs are unaffected.
3023 """
3024- broker = BrokerServer(self.config, self.reactor,
3025- self.exchanger, None,
3026- self.mstore, None)
3027+ broker = BrokerServer(
3028+ self.config,
3029+ self.reactor,
3030+ self.exchanger,
3031+ None,
3032+ self.mstore,
3033+ None,
3034+ )
3035
3036 disk_session_id = self.mstore.get_session_id(scope="disk")
3037 package_session_id = self.mstore.get_session_id(scope="package")
3038@@ -105,9 +116,10 @@ class MessageExchangeTest(LandscapeTest):
3039 self.exchanger.exchange()
3040 self.assertEqual(len(self.transport.payloads), 1)
3041 messages = self.transport.payloads[0]["messages"]
3042- self.assertEqual(messages, [{"type": "empty",
3043- "timestamp": 0,
3044- "api": b"3.2"}])
3045+ self.assertEqual(
3046+ messages,
3047+ [{"type": "empty", "timestamp": 0, "api": b"3.2"}],
3048+ )
3049
3050 def test_send_urgent(self):
3051 """
3052@@ -118,8 +130,10 @@ class MessageExchangeTest(LandscapeTest):
3053 self.exchanger.send({"type": "empty"}, urgent=True)
3054 self.wait_for_exchange(urgent=True)
3055 self.assertEqual(len(self.transport.payloads), 1)
3056- self.assertMessages(self.transport.payloads[0]["messages"],
3057- [{"type": "empty"}])
3058+ self.assertMessages(
3059+ self.transport.payloads[0]["messages"],
3060+ [{"type": "empty"}],
3061+ )
3062
3063 def test_send_urgent_wont_reschedule(self):
3064 """
3065@@ -132,8 +146,10 @@ class MessageExchangeTest(LandscapeTest):
3066 self.exchanger.send({"type": "empty"}, urgent=True)
3067 self.wait_for_exchange(urgent=True, factor=0.5)
3068 self.assertEqual(len(self.transport.payloads), 1)
3069- self.assertMessages(self.transport.payloads[0]["messages"],
3070- [{"type": "empty"}, {"type": "empty"}])
3071+ self.assertMessages(
3072+ self.transport.payloads[0]["messages"],
3073+ [{"type": "empty"}, {"type": "empty"}],
3074+ )
3075
3076 def test_send_returns_message_id(self):
3077 """
3078@@ -151,14 +167,15 @@ class MessageExchangeTest(LandscapeTest):
3079 """
3080 self.mstore.set_accepted_types(["package-reporter-result"])
3081 self.exchanger._max_log_text_bytes = 5
3082- self.exchanger.send({"type": "package-reporter-result", "err": "E"*10,
3083- "code": 0})
3084+ self.exchanger.send(
3085+ {"type": "package-reporter-result", "err": "E" * 10, "code": 0},
3086+ )
3087 self.exchanger.exchange()
3088 self.assertEqual(len(self.transport.payloads), 1)
3089 messages = self.transport.payloads[0]["messages"]
3090- self.assertIn('TRUNCATED', messages[0]['err'])
3091- self.assertIn('EEEEE', messages[0]['err'])
3092- self.assertNotIn('EEEEEE', messages[0]['err'])
3093+ self.assertIn("TRUNCATED", messages[0]["err"])
3094+ self.assertIn("EEEEE", messages[0]["err"])
3095+ self.assertNotIn("EEEEEE", messages[0]["err"])
3096
3097 def test_send_big_message_trimmed_result(self):
3098 """
3099@@ -166,14 +183,21 @@ class MessageExchangeTest(LandscapeTest):
3100 """
3101 self.mstore.set_accepted_types(["operation-result"])
3102 self.exchanger._max_log_text_bytes = 5
3103- self.exchanger.send({"type": "operation-result", "result-text": "E"*10,
3104- "code": 0, "status": 0, "operation-id": 0})
3105+ self.exchanger.send(
3106+ {
3107+ "type": "operation-result",
3108+ "result-text": "E" * 10,
3109+ "code": 0,
3110+ "status": 0,
3111+ "operation-id": 0,
3112+ },
3113+ )
3114 self.exchanger.exchange()
3115 self.assertEqual(len(self.transport.payloads), 1)
3116 messages = self.transport.payloads[0]["messages"]
3117- self.assertIn('TRUNCATED', messages[0]['result-text'])
3118- self.assertIn('EEEEE', messages[0]['result-text'])
3119- self.assertNotIn('EEEEEE', messages[0]['result-text'])
3120+ self.assertIn("TRUNCATED", messages[0]["result-text"])
3121+ self.assertIn("EEEEE", messages[0]["result-text"])
3122+ self.assertNotIn("EEEEEE", messages[0]["result-text"])
3123
3124 def test_send_small_message_not_trimmed(self):
3125 """
3126@@ -181,13 +205,14 @@ class MessageExchangeTest(LandscapeTest):
3127 """
3128 self.mstore.set_accepted_types(["package-reporter-result"])
3129 self.exchanger._max_log_text_bytes = 4
3130- self.exchanger.send({"type": "package-reporter-result", "err": "E"*4,
3131- "code": 0})
3132+ self.exchanger.send(
3133+ {"type": "package-reporter-result", "err": "E" * 4, "code": 0},
3134+ )
3135 self.exchanger.exchange()
3136 self.assertEqual(len(self.transport.payloads), 1)
3137 messages = self.transport.payloads[0]["messages"]
3138- self.assertNotIn('TRUNCATED', messages[0]['err'])
3139- self.assertIn('EEEE', messages[0]['err'])
3140+ self.assertNotIn("TRUNCATED", messages[0]["err"])
3141+ self.assertIn("EEEE", messages[0]["err"])
3142
3143 def test_wb_include_accepted_types(self):
3144 """
3145@@ -204,7 +229,8 @@ class MessageExchangeTest(LandscapeTest):
3146 types.
3147 """
3148 self.exchanger.handle_message(
3149- {"type": "accepted-types", "types": ["foo"]})
3150+ {"type": "accepted-types", "types": ["foo"]},
3151+ )
3152 self.assertEqual(self.mstore.get_accepted_types(), ["foo"])
3153
3154 def test_message_type_acceptance_changed_event(self):
3155@@ -212,13 +238,18 @@ class MessageExchangeTest(LandscapeTest):
3156
3157 def callback(type, accepted):
3158 stash.append((type, accepted))
3159+
3160 self.reactor.call_on("message-type-acceptance-changed", callback)
3161 self.exchanger.handle_message(
3162- {"type": "accepted-types", "types": ["a", "b"]})
3163+ {"type": "accepted-types", "types": ["a", "b"]},
3164+ )
3165 self.exchanger.handle_message(
3166- {"type": "accepted-types", "types": ["b", "c"]})
3167- self.assertCountEqual(stash, [("a", True), ("b", True),
3168- ("a", False), ("c", True)])
3169+ {"type": "accepted-types", "types": ["b", "c"]},
3170+ )
3171+ self.assertCountEqual(
3172+ stash,
3173+ [("a", True), ("b", True), ("a", False), ("c", True)],
3174+ )
3175
3176 def test_wb_accepted_types_roundtrip(self):
3177 """
3178@@ -226,11 +257,11 @@ class MessageExchangeTest(LandscapeTest):
3179 should affect its future payloads.
3180 """
3181 self.exchanger.handle_message(
3182- {"type": "accepted-types", "types": ["ack", "bar"]})
3183+ {"type": "accepted-types", "types": ["ack", "bar"]},
3184+ )
3185 payload = self.exchanger._make_payload()
3186 self.assertIn("accepted-types", payload)
3187- self.assertEqual(payload["accepted-types"],
3188- md5(b"ack;bar").digest())
3189+ self.assertEqual(payload["accepted-types"], md5(b"ack;bar").digest())
3190
3191 def test_accepted_types_causes_urgent_if_held_messages_exist(self):
3192 """
3193@@ -240,11 +271,14 @@ class MessageExchangeTest(LandscapeTest):
3194 self.exchanger.send({"type": "holdme"})
3195 self.assertEqual(self.mstore.get_pending_messages(), [])
3196 self.exchanger.handle_message(
3197- {"type": "accepted-types", "types": ["holdme"]})
3198+ {"type": "accepted-types", "types": ["holdme"]},
3199+ )
3200 self.wait_for_exchange(urgent=True)
3201 self.assertEqual(len(self.transport.payloads), 1)
3202- self.assertMessages(self.transport.payloads[0]["messages"],
3203- [{"type": "holdme"}])
3204+ self.assertMessages(
3205+ self.transport.payloads[0]["messages"],
3206+ [{"type": "holdme"}],
3207+ )
3208
3209 def test_accepted_types_no_urgent_without_held(self):
3210 """
3211@@ -253,8 +287,10 @@ class MessageExchangeTest(LandscapeTest):
3212 """
3213 self.exchanger.send({"type": "holdme"})
3214 self.assertEqual(self.transport.payloads, [])
3215- self.reactor.fire("message",
3216- {"type": "accepted-types", "types": ["irrelevant"]})
3217+ self.reactor.fire(
3218+ "message",
3219+ {"type": "accepted-types", "types": ["irrelevant"]},
3220+ )
3221 self.assertEqual(len(self.transport.payloads), 0)
3222
3223 def test_sequence_is_committed_immediately(self):
3224@@ -294,8 +330,7 @@ class MessageExchangeTest(LandscapeTest):
3225 def handler(message):
3226 Persist(filename=self.persist_filename)
3227 store = MessageStore(self.persist, self.config.message_store_path)
3228- self.assertEqual(store.get_server_sequence(),
3229- self.message_counter)
3230+ self.assertEqual(store.get_server_sequence(), self.message_counter)
3231 self.message_counter += 1
3232 handled.append(True)
3233
3234@@ -324,8 +359,10 @@ class MessageExchangeTest(LandscapeTest):
3235 self.wait_for_exchange(urgent=True)
3236
3237 self.assertEqual(len(self.transport.payloads), 2)
3238- self.assertMessages(self.transport.payloads[1]["messages"],
3239- [{"type": "empty"}])
3240+ self.assertMessages(
3241+ self.transport.payloads[1]["messages"],
3242+ [{"type": "empty"}],
3243+ )
3244
3245 def test_server_expects_older_messages(self):
3246 """
3247@@ -361,22 +398,29 @@ class MessageExchangeTest(LandscapeTest):
3248 self.assertEqual(exchanged, [True])
3249
3250 payload = self.transport.payloads[-1]
3251- self.assertMessages(payload["messages"],
3252- [{"type": "data", "data": 1},
3253- {"type": "data", "data": 2},
3254- {"type": "data", "data": 3}])
3255+ self.assertMessages(
3256+ payload["messages"],
3257+ [
3258+ {"type": "data", "data": 1},
3259+ {"type": "data", "data": 2},
3260+ {"type": "data", "data": 3},
3261+ ],
3262+ )
3263 self.assertEqual(payload["sequence"], 1)
3264 self.assertEqual(payload["next-expected-sequence"], 0)
3265
3266- @mock.patch("landscape.client.broker.store.MessageStore"
3267- ".delete_old_messages")
3268- def test_pending_offset_when_next_expected_too_high(self,
3269- mock_rm_all_messages):
3270- '''
3271+ @mock.patch(
3272+ "landscape.client.broker.store.MessageStore.delete_old_messages",
3273+ )
3274+ def test_pending_offset_when_next_expected_too_high(
3275+ self,
3276+ mock_rm_all_messages,
3277+ ):
3278+ """
3279 When next expected sequence received from server is too high, then the
3280 pending offset should reset to zero. This will cause the client to
3281 resend the pending messages.
3282- '''
3283+ """
3284
3285 self.mstore.set_accepted_types(["data"])
3286 self.mstore.add({"type": "data", "data": 0})
3287@@ -399,12 +443,12 @@ class MessageExchangeTest(LandscapeTest):
3288 self.assertTrue(mock_rm_all_messages.called)
3289
3290 def test_payloads_when_next_expected_too_high(self):
3291- '''
3292+ """
3293 When next expected sequence received from server is too high, then the
3294 current messages should get sent again since we don't have confirmation
3295 that the server received it. Also previous messages should not get
3296 repeated.
3297- '''
3298+ """
3299
3300 self.mstore.set_accepted_types(["data"])
3301
3302@@ -426,17 +470,16 @@ class MessageExchangeTest(LandscapeTest):
3303 self.assertTrue(last_messages)
3304
3305 # Confirm earlier messages are not resent
3306- self.assertNotIn(message0["data"],
3307- [m["data"] for m in last_messages])
3308+ self.assertNotIn(message0["data"], [m["data"] for m in last_messages])
3309
3310 # Confirm contents of payload
3311 self.assertEqual([message1, message2], last_messages)
3312
3313 def test_resync_when_next_expected_too_high(self):
3314- '''
3315+ """
3316 When next expected sequence received from the server is too high, then
3317 a resynchronize should happen
3318- '''
3319+ """
3320
3321 self.mstore.set_accepted_types(["empty", "resynchronize"])
3322 self.mstore.add({"type": "empty"})
3323@@ -447,17 +490,24 @@ class MessageExchangeTest(LandscapeTest):
3324 self.reactor.call_on("resynchronize-clients", lambda scope=None: None)
3325
3326 self.exchanger.exchange()
3327- self.assertMessage(self.mstore.get_pending_messages()[-1],
3328- {"type": "resynchronize"})
3329+ self.assertMessage(
3330+ self.mstore.get_pending_messages()[-1],
3331+ {"type": "resynchronize"},
3332+ )
3333
3334 def test_start_with_urgent_exchange(self):
3335 """
3336 Immediately after registration, an urgent exchange should be scheduled.
3337 """
3338 transport = FakeTransport()
3339- exchanger = MessageExchange(self.reactor, self.mstore, transport,
3340- self.identity, self.exchange_store,
3341- self.config)
3342+ exchanger = MessageExchange(
3343+ self.reactor,
3344+ self.mstore,
3345+ transport,
3346+ self.identity,
3347+ self.exchange_store,
3348+ self.config,
3349+ )
3350 exchanger.start()
3351 self.wait_for_exchange(urgent=True)
3352 self.assertEqual(len(transport.payloads), 1)
3353@@ -499,13 +549,17 @@ class MessageExchangeTest(LandscapeTest):
3354 self.mstore.record_success = mock_record_success
3355
3356 exchanger = MessageExchange(
3357- self.reactor, self.mstore, self.transport,
3358- self.identity, self.exchange_store, self.config)
3359+ self.reactor,
3360+ self.mstore,
3361+ self.transport,
3362+ self.identity,
3363+ self.exchange_store,
3364+ self.config,
3365+ )
3366 exchanger.exchange()
3367
3368 mock_record_success.assert_called_with(mock.ANY)
3369- self.assertTrue(
3370- type(mock_record_success.call_args[0][0]) is int)
3371+ self.assertTrue(type(mock_record_success.call_args[0][0]) is int)
3372
3373 def test_ancient_causes_resynchronize(self):
3374 """
3375@@ -530,15 +584,20 @@ class MessageExchangeTest(LandscapeTest):
3376 # should come AFTER the "resynchronize" message that is generated
3377 # by the exchange code itself.
3378 self.mstore.add({"type": "data", "data": 999})
3379+
3380 self.reactor.call_on("resynchronize-clients", resynchronize)
3381
3382 # This exchange call will notice the server is asking for an old
3383 # message and fire the event:
3384 self.exchanger.exchange()
3385- self.assertMessages(self.mstore.get_pending_messages(),
3386- [{"type": "empty"},
3387- {"type": "resynchronize"},
3388- {"type": "data", "data": 999}])
3389+ self.assertMessages(
3390+ self.mstore.get_pending_messages(),
3391+ [
3392+ {"type": "empty"},
3393+ {"type": "resynchronize"},
3394+ {"type": "data", "data": 999},
3395+ ],
3396+ )
3397
3398 def test_resynchronize_msg_causes_resynchronize_response_then_event(self):
3399 """
3400@@ -551,15 +610,20 @@ class MessageExchangeTest(LandscapeTest):
3401
3402 def resynchronized(scopes=None):
3403 self.mstore.add({"type": "empty"})
3404+
3405 self.reactor.call_on("resynchronize-clients", resynchronized)
3406
3407- self.transport.responses.append([{"type": "resynchronize",
3408- "operation-id": 123}])
3409+ self.transport.responses.append(
3410+ [{"type": "resynchronize", "operation-id": 123}],
3411+ )
3412 self.exchanger.exchange()
3413- self.assertMessages(self.mstore.get_pending_messages(),
3414- [{"type": "resynchronize",
3415- "operation-id": 123},
3416- {"type": "empty"}])
3417+ self.assertMessages(
3418+ self.mstore.get_pending_messages(),
3419+ [
3420+ {"type": "resynchronize", "operation-id": 123},
3421+ {"type": "empty"},
3422+ ],
3423+ )
3424
3425 def test_scopes_are_copied_from_incoming_resynchronize_messages(self):
3426 """
3427@@ -574,9 +638,15 @@ class MessageExchangeTest(LandscapeTest):
3428
3429 self.reactor.call_on("resynchronize-clients", resynchronized)
3430
3431- self.transport.responses.append([{"type": "resynchronize",
3432- "operation-id": 123,
3433- "scopes": ["disk", "users"]}])
3434+ self.transport.responses.append(
3435+ [
3436+ {
3437+ "type": "resynchronize",
3438+ "operation-id": 123,
3439+ "scopes": ["disk", "users"],
3440+ },
3441+ ],
3442+ )
3443 self.exchanger.exchange()
3444 self.assertEqual(["disk", "users"], fired_scopes)
3445
3446@@ -622,8 +692,10 @@ class MessageExchangeTest(LandscapeTest):
3447
3448 def test_old_sequence_id_does_not_cause_resynchronize(self):
3449 resynchronized = []
3450- self.reactor.call_on("resynchronize",
3451- lambda: resynchronized.append(True))
3452+ self.reactor.call_on(
3453+ "resynchronize",
3454+ lambda: resynchronized.append(True),
3455+ )
3456
3457 self.mstore.set_accepted_types(["empty"])
3458 self.mstore.add({"type": "empty"})
3459@@ -663,9 +735,10 @@ class MessageExchangeTest(LandscapeTest):
3460 self.exchanger.exchange()
3461
3462 payload = self.transport.payloads[-1]
3463- self.assertMessages(payload["messages"],
3464- [{"type": "a", "api": b"1.0"},
3465- {"type": "b", "api": b"1.0"}])
3466+ self.assertMessages(
3467+ payload["messages"],
3468+ [{"type": "a", "api": b"1.0"}, {"type": "b", "api": b"1.0"}],
3469+ )
3470 self.assertEqual(payload.get("client-api"), CLIENT_API)
3471 self.assertEqual(payload.get("server-api"), b"1.0")
3472 self.assertEqual(self.transport.message_api, b"1.0")
3473@@ -673,9 +746,10 @@ class MessageExchangeTest(LandscapeTest):
3474 self.exchanger.exchange()
3475
3476 payload = self.transport.payloads[-1]
3477- self.assertMessages(payload["messages"],
3478- [{"type": "c", "api": b"1.1"},
3479- {"type": "d", "api": b"1.1"}])
3480+ self.assertMessages(
3481+ payload["messages"],
3482+ [{"type": "c", "api": b"1.1"}, {"type": "d", "api": b"1.1"}],
3483+ )
3484 self.assertEqual(payload.get("client-api"), CLIENT_API)
3485 self.assertEqual(payload.get("server-api"), b"1.1")
3486 self.assertEqual(self.transport.message_api, b"1.1")
3487@@ -732,9 +806,15 @@ class MessageExchangeTest(LandscapeTest):
3488 the total-messages is equivalent to the total number of messages
3489 pending.
3490 """
3491- exchanger = MessageExchange(self.reactor, self.mstore, self.transport,
3492- self.identity, self.exchange_store,
3493- self.config, max_messages=1)
3494+ exchanger = MessageExchange(
3495+ self.reactor,
3496+ self.mstore,
3497+ self.transport,
3498+ self.identity,
3499+ self.exchange_store,
3500+ self.config,
3501+ max_messages=1,
3502+ )
3503 self.mstore.set_accepted_types(["empty"])
3504 self.mstore.add({"type": "empty"})
3505 self.mstore.add({"type": "empty"})
3506@@ -763,9 +843,14 @@ class MessageExchangeTest(LandscapeTest):
3507 # fixture has an urgent exchange interval of 10 seconds, which makes
3508 # testing this awkward.
3509 self.config.urgent_exchange_interval = 20
3510- exchanger = MessageExchange(self.reactor, self.mstore, self.transport,
3511- self.identity, self.exchange_store,
3512- self.config)
3513+ exchanger = MessageExchange(
3514+ self.reactor,
3515+ self.mstore,
3516+ self.transport,
3517+ self.identity,
3518+ self.exchange_store,
3519+ self.config,
3520+ )
3521 exchanger.schedule_exchange(urgent=True)
3522 events = []
3523 self.reactor.call_on("impending-exchange", lambda: events.append(True))
3524@@ -783,9 +868,14 @@ class MessageExchangeTest(LandscapeTest):
3525 """
3526 self.config.exchange_interval = 60 * 60
3527 self.config.urgent_exchange_interval = 20
3528- exchanger = MessageExchange(self.reactor, self.mstore, self.transport,
3529- self.identity, self.exchange_store,
3530- self.config)
3531+ exchanger = MessageExchange(
3532+ self.reactor,
3533+ self.mstore,
3534+ self.transport,
3535+ self.identity,
3536+ self.exchange_store,
3537+ self.config,
3538+ )
3539 events = []
3540 self.reactor.call_on("impending-exchange", lambda: events.append(True))
3541 # This call will:
3542@@ -806,11 +896,12 @@ class MessageExchangeTest(LandscapeTest):
3543 # schedule a regular exchange.
3544 # Let's make sure that that *original* impending-exchange event has
3545 # been cancelled:
3546- TIME_UNTIL_EXCHANGE = 60 * 60
3547- TIME_UNTIL_NOTIFY = 10
3548- TIME_ADVANCED = 20 # time that we've already advanced
3549- self.reactor.advance(TIME_UNTIL_EXCHANGE -
3550- (TIME_UNTIL_NOTIFY + TIME_ADVANCED))
3551+ time_until_exchange = 60 * 60
3552+ time_until_notify = 10
3553+ time_advanced = 20
3554+ self.reactor.advance(
3555+ time_until_exchange - (time_until_notify + time_advanced),
3556+ )
3557 self.assertEqual(events, [True])
3558 # Ok, so no new events means that the original call was
3559 # cancelled. great.
3560@@ -877,8 +968,13 @@ class MessageExchangeTest(LandscapeTest):
3561 the L{MessageExchange} are changed, the configuration values as well,
3562 and the configuration is written to disk to be persisted.
3563 """
3564- server_message = [{"type": "set-intervals",
3565- "urgent-exchange": 1234, "exchange": 5678}]
3566+ server_message = [
3567+ {
3568+ "type": "set-intervals",
3569+ "urgent-exchange": 1234,
3570+ "exchange": 5678,
3571+ },
3572+ ]
3573 self.transport.responses.append(server_message)
3574
3575 self.exchanger.exchange()
3576@@ -993,6 +1089,7 @@ class MessageExchangeTest(LandscapeTest):
3577
3578 def server_uuid_changed(old_uuid, new_uuid):
3579 called.append((old_uuid, new_uuid))
3580+
3581 self.reactor.call_on("server-uuid-changed", server_uuid_changed)
3582
3583 # Set it for the first time, and it should emit the event
3584@@ -1028,6 +1125,7 @@ class MessageExchangeTest(LandscapeTest):
3585
3586 def server_uuid_changed(old_uuid, new_uuid):
3587 called.append((old_uuid, new_uuid))
3588+
3589 self.reactor.call_on("server-uuid-changed", server_uuid_changed)
3590
3591 self.mstore.set_server_uuid("the-uuid")
3592@@ -1039,8 +1137,10 @@ class MessageExchangeTest(LandscapeTest):
3593 self.transport.extra["server-uuid"] = "the-uuid"
3594 self.exchanger.exchange()
3595
3596- self.assertIn("INFO: Server UUID changed (old=None, new=the-uuid).",
3597- self.logfile.getvalue())
3598+ self.assertIn(
3599+ "INFO: Server UUID changed (old=None, new=the-uuid).",
3600+ self.logfile.getvalue(),
3601+ )
3602
3603 # An exchange with the same UUID shouldn't be logged.
3604 self.logfile.truncate(0)
3605@@ -1063,9 +1163,11 @@ class MessageExchangeTest(LandscapeTest):
3606 [message] = messages
3607 self.assertIsNot(
3608 None,
3609- self.exchange_store.get_message_context(message['operation-id']))
3610+ self.exchange_store.get_message_context(message["operation-id"]),
3611+ )
3612 message_context = self.exchange_store.get_message_context(
3613- message['operation-id'])
3614+ message["operation-id"],
3615+ )
3616 self.assertEqual(message_context.operation_id, 123456)
3617 self.assertEqual(message_context.message_type, "type-R")
3618
3619@@ -1099,12 +1201,13 @@ class MessageExchangeTest(LandscapeTest):
3620 self.exchanger.exchange()
3621
3622 # Change the secure ID so that the response message gets discarded.
3623- self.identity.secure_id = 'brand-new'
3624+ self.identity.secure_id = "brand-new"
3625 ids_before = self.exchange_store.all_operation_ids()
3626
3627 self.mstore.set_accepted_types(["resynchronize"])
3628 message_id = self.exchanger.send(
3629- {"type": "resynchronize", "operation-id": 234567})
3630+ {"type": "resynchronize", "operation-id": 234567},
3631+ )
3632 self.exchanger.exchange()
3633 self.assertEqual(2, len(self.transport.payloads))
3634 messages = self.transport.payloads[1]["messages"]
3635@@ -1112,13 +1215,14 @@ class MessageExchangeTest(LandscapeTest):
3636 self.assertIs(None, message_id)
3637 expected_log_entry = (
3638 "Response message with operation-id 234567 was discarded because "
3639- "the client's secure ID has changed in the meantime")
3640+ "the client's secure ID has changed in the meantime"
3641+ )
3642 self.assertIn(expected_log_entry, self.logfile.getvalue())
3643
3644 # The MessageContext was removed after utilisation.
3645 ids_after = self.exchange_store.all_operation_ids()
3646 self.assertEqual(len(ids_after), len(ids_before) - 1)
3647- self.assertNotIn('234567', ids_after)
3648+ self.assertNotIn("234567", ids_after)
3649
3650 def test_error_exchanging_causes_failed_exchange(self):
3651 """
3652@@ -1135,13 +1239,14 @@ class MessageExchangeTest(LandscapeTest):
3653 self.exchanger.exchange()
3654 self.assertEqual([None], events)
3655
3656- def test_SSL_error_exchanging_causes_failed_exchange(self):
3657+ def test_SSL_error_exchanging_causes_failed_exchange(self): # noqa: N802
3658 """
3659 If an SSL error occurs when exchanging, the 'exchange-failed'
3660 event should be fired with the optional "ssl_error" flag set to True.
3661 """
3662- self.log_helper.ignore_errors("Message exchange failed: Failed to "
3663- "communicate.")
3664+ self.log_helper.ignore_errors(
3665+ "Message exchange failed: Failed to communicate.",
3666+ )
3667 events = []
3668
3669 def failed_exchange(ssl_error):
3670@@ -1149,8 +1254,9 @@ class MessageExchangeTest(LandscapeTest):
3671 events.append(None)
3672
3673 self.reactor.call_on("exchange-failed", failed_exchange)
3674- self.transport.responses.append(PyCurlError(60,
3675- "Failed to communicate."))
3676+ self.transport.responses.append(
3677+ PyCurlError(60, "Failed to communicate."),
3678+ )
3679 self.exchanger.exchange()
3680 self.assertEqual([None], events)
3681
3682@@ -1167,8 +1273,9 @@ class MessageExchangeTest(LandscapeTest):
3683 events.append(None)
3684
3685 self.reactor.call_on("exchange-failed", failed_exchange)
3686- self.transport.responses.append(PyCurlError(10, # Not 60
3687- "Failed to communicate."))
3688+ self.transport.responses.append(
3689+ PyCurlError(10, "Failed to communicate."), # Not 60
3690+ )
3691 self.exchanger.exchange()
3692 self.assertEqual([None], events)
3693
3694@@ -1278,14 +1385,23 @@ class AcceptedTypesMessageExchangeTest(LandscapeTest):
3695 helpers = [ExchangeHelper]
3696
3697 def setUp(self):
3698- super(AcceptedTypesMessageExchangeTest, self).setUp()
3699- self.pinger = Pinger(self.reactor, self.identity, self.exchanger,
3700- self.config)
3701+ super().setUp()
3702+ self.pinger = Pinger(
3703+ self.reactor,
3704+ self.identity,
3705+ self.exchanger,
3706+ self.config,
3707+ )
3708 # The __init__ method of RegistrationHandler registers a few default
3709 # message types that we want to catch as well
3710 self.handler = RegistrationHandler(
3711- self.config, self.identity, self.reactor, self.exchanger,
3712- self.pinger, self.mstore)
3713+ self.config,
3714+ self.identity,
3715+ self.reactor,
3716+ self.exchanger,
3717+ self.pinger,
3718+ self.mstore,
3719+ )
3720
3721 def test_register_accepted_message_type(self):
3722 self.exchanger.register_client_accepted_message_type("type-B")
3723@@ -1293,9 +1409,10 @@ class AcceptedTypesMessageExchangeTest(LandscapeTest):
3724 self.exchanger.register_client_accepted_message_type("type-C")
3725 self.exchanger.register_client_accepted_message_type("type-A")
3726 types = self.exchanger.get_client_accepted_message_types()
3727- self.assertEqual(types,
3728- sorted(["type-A", "type-B", "type-C"] +
3729- DEFAULT_ACCEPTED_TYPES))
3730+ self.assertEqual(
3731+ types,
3732+ sorted(["type-A", "type-B", "type-C"] + DEFAULT_ACCEPTED_TYPES),
3733+ )
3734
3735 def test_exchange_sends_message_type_when_no_hash(self):
3736 self.exchanger.register_client_accepted_message_type("type-A")
3737@@ -1303,15 +1420,17 @@ class AcceptedTypesMessageExchangeTest(LandscapeTest):
3738 self.exchanger.exchange()
3739 self.assertEqual(
3740 self.transport.payloads[0]["client-accepted-types"],
3741- sorted(["type-A", "type-B"] + DEFAULT_ACCEPTED_TYPES))
3742+ sorted(["type-A", "type-B"] + DEFAULT_ACCEPTED_TYPES),
3743+ )
3744
3745 def test_exchange_does_not_send_message_types_when_hash_matches(self):
3746 self.exchanger.register_client_accepted_message_type("type-A")
3747 self.exchanger.register_client_accepted_message_type("type-B")
3748 types = sorted(["type-A", "type-B"] + DEFAULT_ACCEPTED_TYPES)
3749 accepted_types_digest = md5(";".join(types).encode("ascii")).digest()
3750- self.transport.extra["client-accepted-types-hash"] = \
3751- accepted_types_digest
3752+ self.transport.extra[
3753+ "client-accepted-types-hash"
3754+ ] = accepted_types_digest
3755 self.exchanger.exchange()
3756 self.exchanger.exchange()
3757 self.assertNotIn("client-accepted-types", self.transport.payloads[1])
3758@@ -1327,7 +1446,8 @@ class AcceptedTypesMessageExchangeTest(LandscapeTest):
3759 self.exchanger.exchange()
3760 self.assertEqual(
3761 self.transport.payloads[1]["client-accepted-types"],
3762- sorted(["type-A", "type-B"] + DEFAULT_ACCEPTED_TYPES))
3763+ sorted(["type-A", "type-B"] + DEFAULT_ACCEPTED_TYPES),
3764+ )
3765
3766 def test_exchange_sends_new_accepted_types_hash(self):
3767 """
3768@@ -1342,7 +1462,8 @@ class AcceptedTypesMessageExchangeTest(LandscapeTest):
3769 self.exchanger.exchange()
3770 self.assertEqual(
3771 self.transport.payloads[1]["client-accepted-types"],
3772- sorted(["type-A", "type-B"] + DEFAULT_ACCEPTED_TYPES))
3773+ sorted(["type-A", "type-B"] + DEFAULT_ACCEPTED_TYPES),
3774+ )
3775
3776 def test_exchange_sends_new_types_when_server_screws_up(self):
3777 """
3778@@ -1359,7 +1480,8 @@ class AcceptedTypesMessageExchangeTest(LandscapeTest):
3779 self.exchanger.exchange()
3780 self.assertEqual(
3781 self.transport.payloads[2]["client-accepted-types"],
3782- sorted(["type-A"] + DEFAULT_ACCEPTED_TYPES))
3783+ sorted(["type-A"] + DEFAULT_ACCEPTED_TYPES),
3784+ )
3785
3786 def test_register_message_adds_accepted_type(self):
3787 """
3788@@ -1373,7 +1495,6 @@ class AcceptedTypesMessageExchangeTest(LandscapeTest):
3789
3790
3791 class GetAcceptedTypesDiffTest(LandscapeTest):
3792-
3793 def test_diff_empty(self):
3794 self.assertEqual(get_accepted_types_diff([], []), "")
3795
3796@@ -1387,6 +1508,7 @@ class GetAcceptedTypesDiffTest(LandscapeTest):
3797 self.assertEqual(get_accepted_types_diff(["ooga"], ["ooga"]), "ooga")
3798
3799 def test_diff_complex(self):
3800- self.assertEqual(get_accepted_types_diff(["foo", "bar"],
3801- ["foo", "ooga"]),
3802- "+ooga foo -bar")
3803+ self.assertEqual(
3804+ get_accepted_types_diff(["foo", "bar"], ["foo", "ooga"]),
3805+ "+ooga foo -bar",
3806+ )
3807diff --git a/landscape/client/broker/tests/test_exchangestore.py b/landscape/client/broker/tests/test_exchangestore.py
3808index 45354db..76a7d9f 100644
3809--- a/landscape/client/broker/tests/test_exchangestore.py
3810+++ b/landscape/client/broker/tests/test_exchangestore.py
3811@@ -14,7 +14,7 @@ class ExchangeStoreTest(LandscapeTest):
3812 """Unit tests for the C{ExchangeStore}."""
3813
3814 def setUp(self):
3815- super(ExchangeStoreTest, self).setUp()
3816+ super().setUp()
3817
3818 self.filename = self.makeFile()
3819 self.store1 = ExchangeStore(self.filename)
3820@@ -23,19 +23,21 @@ class ExchangeStoreTest(LandscapeTest):
3821 def test_add_message_context(self):
3822 """Adding a message context works correctly."""
3823 now = time.time()
3824- self.store1.add_message_context(123, 'abc', 'change-packages')
3825+ self.store1.add_message_context(123, "abc", "change-packages")
3826
3827 db = sqlite3.connect(self.store2._filename)
3828 cursor = db.cursor()
3829 cursor.execute(
3830 "SELECT operation_id, secure_id, message_type, timestamp "
3831- "FROM message_context WHERE operation_id=?", (123,))
3832+ "FROM message_context WHERE operation_id=?",
3833+ (123,),
3834+ )
3835 results = cursor.fetchall()
3836 self.assertEqual(1, len(results))
3837 [row] = results
3838 self.assertEqual(123, row[0])
3839- self.assertEqual('abc', row[1])
3840- self.assertEqual('change-packages', row[2])
3841+ self.assertEqual("abc", row[1])
3842+ self.assertEqual("change-packages", row[2])
3843 self.assertTrue(row[3] > now)
3844
3845 def test_add_message_context_with_duplicate_operation_id(self):
3846@@ -43,18 +45,22 @@ class ExchangeStoreTest(LandscapeTest):
3847 self.store1.add_message_context(123, "abc", "change-packages")
3848 self.assertRaises(
3849 (sqlite3.IntegrityError, sqlite3.OperationalError),
3850- self.store1.add_message_context, 123, "def", "change-packages")
3851+ self.store1.add_message_context,
3852+ 123,
3853+ "def",
3854+ "change-packages",
3855+ )
3856
3857 def test_get_message_context(self):
3858 """
3859 Accessing a C{MessageContext} with an existing C{operation-id} works.
3860 """
3861 now = time.time()
3862- self.store1.add_message_context(234, 'bcd', 'change-packages')
3863+ self.store1.add_message_context(234, "bcd", "change-packages")
3864 context = self.store2.get_message_context(234)
3865 self.assertEqual(234, context.operation_id)
3866- self.assertEqual('bcd', context.secure_id)
3867- self.assertEqual('change-packages', context.message_type)
3868+ self.assertEqual("bcd", context.secure_id)
3869+ self.assertEqual("change-packages", context.message_type)
3870 self.assertTrue(context.timestamp > now)
3871
3872 def test_get_message_context_with_nonexistent_operation_id(self):
3873@@ -65,7 +71,10 @@ class ExchangeStoreTest(LandscapeTest):
3874 def test_message_context_remove(self):
3875 """C{MessageContext}s are deleted correctly."""
3876 context = self.store1.add_message_context(
3877- 345, 'opq', 'change-packages')
3878+ 345,
3879+ "opq",
3880+ "change-packages",
3881+ )
3882 context.remove()
3883 self.assertIs(None, self.store1.get_message_context(345))
3884
3885@@ -78,7 +87,7 @@ class ExchangeStoreTest(LandscapeTest):
3886
3887 def test_all_operation_ids(self):
3888 """C{all_operation_ids} works correctly."""
3889- self.store1.add_message_context(456, 'cde', 'change-packages')
3890+ self.store1.add_message_context(456, "cde", "change-packages")
3891 self.assertEqual([456], self.store2.all_operation_ids())
3892- self.store2.add_message_context(567, 'def', 'change-packages')
3893+ self.store2.add_message_context(567, "def", "change-packages")
3894 self.assertEqual([456, 567], self.store1.all_operation_ids())
3895diff --git a/landscape/client/broker/tests/test_ping.py b/landscape/client/broker/tests/test_ping.py
3896index b095b5a..843b984 100644
3897--- a/landscape/client/broker/tests/test_ping.py
3898+++ b/landscape/client/broker/tests/test_ping.py
3899@@ -1,15 +1,15 @@
3900-from landscape.client.tests.helpers import LandscapeTest
3901-
3902 from twisted.internet.defer import fail
3903
3904+from landscape.client.broker.ping import PingClient
3905+from landscape.client.broker.ping import Pinger
3906+from landscape.client.broker.tests.helpers import ExchangeHelper
3907+from landscape.client.tests.helpers import LandscapeTest
3908 from landscape.lib import bpickle
3909 from landscape.lib.fetch import fetch
3910 from landscape.lib.testing import FakeReactor
3911-from landscape.client.broker.ping import PingClient, Pinger
3912-from landscape.client.broker.tests.helpers import ExchangeHelper
3913
3914
3915-class FakePageGetter(object):
3916+class FakePageGetter:
3917 """An fake web client."""
3918
3919 def __init__(self, response):
3920@@ -39,9 +39,8 @@ class FakePageGetter(object):
3921
3922
3923 class PingClientTest(LandscapeTest):
3924-
3925 def setUp(self):
3926- super(PingClientTest, self).setUp()
3927+ super().setUp()
3928 self.reactor = FakeReactor()
3929
3930 def test_default_get_page(self):
3931@@ -64,8 +63,15 @@ class PingClientTest(LandscapeTest):
3932 pinger.ping(url, insecure_id)
3933 self.assertEqual(
3934 client.fetches,
3935- [(url, True, {"Content-Type": "application/x-www-form-urlencoded"},
3936- "insecure_id=10")])
3937+ [
3938+ (
3939+ url,
3940+ True,
3941+ {"Content-Type": "application/x-www-form-urlencoded"},
3942+ "insecure_id=10",
3943+ ),
3944+ ],
3945+ )
3946
3947 def test_ping_no_insecure_id(self):
3948 """
3949@@ -100,6 +106,7 @@ class PingClientTest(LandscapeTest):
3950
3951 def errback(failure):
3952 failures.append(failure)
3953+
3954 d.addErrback(errback)
3955 self.assertEqual(len(failures), 1)
3956 self.assertEqual(failures[0].getErrorMessage(), "That's a failure!")
3957@@ -116,7 +123,7 @@ class PingerTest(LandscapeTest):
3958 install_exchanger = False
3959
3960 def setUp(self):
3961- super(PingerTest, self).setUp()
3962+ super().setUp()
3963 self.page_getter = FakePageGetter(None)
3964
3965 def factory(reactor):
3966@@ -125,21 +132,25 @@ class PingerTest(LandscapeTest):
3967 self.config.ping_url = "http://localhost:8081/whatever"
3968 self.config.ping_interval = 10
3969
3970- self.pinger = Pinger(self.reactor,
3971- self.identity,
3972- self.exchanger,
3973- self.config,
3974- ping_client_factory=factory)
3975+ self.pinger = Pinger(
3976+ self.reactor,
3977+ self.identity,
3978+ self.exchanger,
3979+ self.config,
3980+ ping_client_factory=factory,
3981+ )
3982
3983 def test_default_ping_client(self):
3984 """
3985 The C{ping_client_factory} argument to L{Pinger} should be optional,
3986 and default to L{PingClient}.
3987 """
3988- pinger = Pinger(self.reactor,
3989- self.identity,
3990- self.exchanger,
3991- self.config)
3992+ pinger = Pinger(
3993+ self.reactor,
3994+ self.identity,
3995+ self.exchanger,
3996+ self.config,
3997+ )
3998 self.assertEqual(pinger.ping_client_factory, PingClient)
3999
4000 def test_occasional_ping(self):
4001@@ -195,7 +206,7 @@ class PingerTest(LandscapeTest):
4002 self.log_helper.ignore_errors(ZeroDivisionError)
4003 self.identity.insecure_id = 42
4004
4005- class BadPingClient(object):
4006+ class BadPingClient:
4007 def __init__(self, *args, **kwargs):
4008 pass
4009
4010@@ -204,11 +215,13 @@ class PingerTest(LandscapeTest):
4011 return fail(ZeroDivisionError("Couldn't fetch page"))
4012
4013 self.config.ping_url = "http://foo.com/"
4014- pinger = Pinger(self.reactor,
4015- self.identity,
4016- self.exchanger,
4017- self.config,
4018- ping_client_factory=BadPingClient)
4019+ pinger = Pinger(
4020+ self.reactor,
4021+ self.identity,
4022+ self.exchanger,
4023+ self.config,
4024+ ping_client_factory=BadPingClient,
4025+ )
4026 pinger.start()
4027
4028 self.reactor.advance(30)
4029@@ -238,8 +251,10 @@ class PingerTest(LandscapeTest):
4030 self.assertEqual(len(self.page_getter.fetches), 1)
4031
4032 def test_get_url(self):
4033- self.assertEqual(self.pinger.get_url(),
4034- "http://localhost:8081/whatever")
4035+ self.assertEqual(
4036+ self.pinger.get_url(),
4037+ "http://localhost:8081/whatever",
4038+ )
4039
4040 def test_config_url(self):
4041 """
4042diff --git a/landscape/client/broker/tests/test_registration.py b/landscape/client/broker/tests/test_registration.py
4043index 8ca0cb4..fe1d256 100644
4044--- a/landscape/client/broker/tests/test_registration.py
4045+++ b/landscape/client/broker/tests/test_registration.py
4046@@ -1,14 +1,14 @@
4047 import json
4048 import logging
4049 import socket
4050-import mock
4051+from unittest import mock
4052
4053-from landscape.lib.compat import _PY3
4054-
4055-from landscape.client.broker.registration import RegistrationError, Identity
4056+from landscape.client.broker.registration import Identity
4057+from landscape.client.broker.registration import RegistrationError
4058+from landscape.client.broker.tests.helpers import BrokerConfigurationHelper
4059+from landscape.client.broker.tests.helpers import RegistrationHelper
4060 from landscape.client.tests.helpers import LandscapeTest
4061-from landscape.client.broker.tests.helpers import (
4062- BrokerConfigurationHelper, RegistrationHelper)
4063+from landscape.lib.compat import _PY3
4064 from landscape.lib.persist import Persist
4065
4066
4067@@ -17,33 +17,43 @@ class IdentityTest(LandscapeTest):
4068 helpers = [BrokerConfigurationHelper]
4069
4070 def setUp(self):
4071- super(IdentityTest, self).setUp()
4072+ super().setUp()
4073 self.persist = Persist(filename=self.makePersistFile())
4074 self.identity = Identity(self.config, self.persist)
4075
4076 def check_persist_property(self, attr, persist_name):
4077 value = "VALUE"
4078- self.assertEqual(getattr(self.identity, attr), None,
4079- "%r attribute should default to None, not %r" %
4080- (attr, getattr(self.identity, attr)))
4081+ self.assertEqual(
4082+ getattr(self.identity, attr),
4083+ None,
4084+ f"{attr!r} attribute should default to None, "
4085+ f"not {getattr(self.identity, attr)!r}",
4086+ )
4087 setattr(self.identity, attr, value)
4088- self.assertEqual(getattr(self.identity, attr), value,
4089- "%r attribute should be %r, not %r" %
4090- (attr, value, getattr(self.identity, attr)))
4091 self.assertEqual(
4092- self.persist.get(persist_name), value,
4093- "%r not set to %r in persist" % (persist_name, value))
4094+ getattr(self.identity, attr),
4095+ value,
4096+ f"{attr!r} attribute should be {value!r}, "
4097+ f"not {getattr(self.identity, attr)!r}",
4098+ )
4099+ self.assertEqual(
4100+ self.persist.get(persist_name),
4101+ value,
4102+ f"{persist_name!r} not set to {value!r} in persist",
4103+ )
4104
4105 def check_config_property(self, attr):
4106 value = "VALUE"
4107 setattr(self.config, attr, value)
4108- self.assertEqual(getattr(self.identity, attr), value,
4109- "%r attribute should be %r, not %r" %
4110- (attr, value, getattr(self.identity, attr)))
4111+ self.assertEqual(
4112+ getattr(self.identity, attr),
4113+ value,
4114+ f"{attr!r} attribute should be {value!r}, "
4115+ f"not {getattr(self.identity, attr)!r}",
4116+ )
4117
4118 def test_secure_id(self):
4119- self.check_persist_property("secure_id",
4120- "registration.secure-id")
4121+ self.check_persist_property("secure_id", "registration.secure-id")
4122
4123 def test_secure_id_as_unicode(self):
4124 """secure-id is expected to be retrieved as unicode."""
4125@@ -51,8 +61,7 @@ class IdentityTest(LandscapeTest):
4126 self.assertEqual(self.identity.secure_id, "spam")
4127
4128 def test_insecure_id(self):
4129- self.check_persist_property("insecure_id",
4130- "registration.insecure-id")
4131+ self.check_persist_property("insecure_id", "registration.insecure-id")
4132
4133 def test_computer_title(self):
4134 self.check_config_property("computer_title")
4135@@ -75,7 +84,7 @@ class RegistrationHandlerTestBase(LandscapeTest):
4136 helpers = [RegistrationHelper]
4137
4138 def setUp(self):
4139- super(RegistrationHandlerTestBase, self).setUp()
4140+ super().setUp()
4141 logging.getLogger().setLevel(logging.INFO)
4142 self.hostname = "ooga.local"
4143 self.addCleanup(setattr, socket, "getfqdn", socket.getfqdn)
4144@@ -83,14 +92,14 @@ class RegistrationHandlerTestBase(LandscapeTest):
4145
4146
4147 class RegistrationHandlerTest(RegistrationHandlerTestBase):
4148-
4149 def test_server_initiated_id_changing(self):
4150 """
4151 The server must be able to ask a client to change its secure
4152 and insecure ids even if no requests were sent.
4153 """
4154 self.exchanger.handle_message(
4155- {"type": b"set-id", "id": b"abc", "insecure-id": b"def"})
4156+ {"type": b"set-id", "id": b"abc", "insecure-id": b"def"},
4157+ )
4158 self.assertEqual(self.identity.secure_id, "abc")
4159 self.assertEqual(self.identity.insecure_id, "def")
4160
4161@@ -101,7 +110,8 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4162 """
4163 reactor_fire_mock = self.reactor.fire = mock.Mock()
4164 self.exchanger.handle_message(
4165- {"type": b"set-id", "id": b"abc", "insecure-id": b"def"})
4166+ {"type": b"set-id", "id": b"abc", "insecure-id": b"def"},
4167+ )
4168 reactor_fire_mock.assert_any_call("registration-done")
4169
4170 def test_unknown_id(self):
4171@@ -120,9 +130,12 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4172 self.config.computer_title = "Wu"
4173 self.mstore.set_accepted_types(["register"])
4174 self.exchanger.handle_message(
4175- {"type": b"unknown-id", "clone-of": "Wu"})
4176- self.assertIn("Client is clone of computer Wu",
4177- self.logfile.getvalue())
4178+ {"type": b"unknown-id", "clone-of": "Wu"},
4179+ )
4180+ self.assertIn(
4181+ "Client is clone of computer Wu",
4182+ self.logfile.getvalue(),
4183+ )
4184
4185 def test_clone_secure_id_saved(self):
4186 """
4187@@ -134,7 +147,8 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4188 self.config.computer_title = "Wu"
4189 self.mstore.set_accepted_types(["register"])
4190 self.exchanger.handle_message(
4191- {"type": b"unknown-id", "clone-of": "Wu"})
4192+ {"type": b"unknown-id", "clone-of": "Wu"},
4193+ )
4194 self.assertEqual(self.handler._clone_secure_id, secure_id)
4195 self.assertIsNone(self.identity.secure_id)
4196
4197@@ -148,7 +162,8 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4198 self.mstore.set_accepted_types(["register"])
4199 self.mstore.set_server_api(b"3.3") # Note this is only for later api
4200 self.exchanger.handle_message(
4201- {"type": b"unknown-id", "clone-of": "Wu"})
4202+ {"type": b"unknown-id", "clone-of": "Wu"},
4203+ )
4204 self.reactor.fire("pre-exchange")
4205 messages = self.mstore.get_pending_messages()
4206 self.assertEqual(messages[0]["clone_secure_id"], secure_id)
4207@@ -192,9 +207,11 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4208 messages = self.mstore.get_pending_messages()
4209 self.assertEqual(1, len(messages))
4210 self.assertEqual("register", messages[0]["type"])
4211- self.assertEqual(self.logfile.getvalue().strip(),
4212- "INFO: Queueing message to register with account "
4213- "'account_name' without a password.")
4214+ self.assertEqual(
4215+ self.logfile.getvalue().strip(),
4216+ "INFO: Queueing message to register with account "
4217+ "'account_name' without a password.",
4218+ )
4219
4220 @mock.patch("landscape.client.broker.registration.get_vm_info")
4221 def test_queue_message_on_exchange_with_vm_info(self, get_vm_info_mock):
4222@@ -210,14 +227,18 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4223 self.reactor.fire("pre-exchange")
4224 messages = self.mstore.get_pending_messages()
4225 self.assertEqual(b"vmware", messages[0]["vm-info"])
4226- self.assertEqual(self.logfile.getvalue().strip(),
4227- "INFO: Queueing message to register with account "
4228- "'account_name' without a password.")
4229+ self.assertEqual(
4230+ self.logfile.getvalue().strip(),
4231+ "INFO: Queueing message to register with account "
4232+ "'account_name' without a password.",
4233+ )
4234 get_vm_info_mock.assert_called_once_with()
4235
4236 @mock.patch("landscape.client.broker.registration.get_container_info")
4237 def test_queue_message_on_exchange_with_lxc_container(
4238- self, get_container_info_mock):
4239+ self,
4240+ get_container_info_mock,
4241+ ):
4242 """
4243 If the client is running in an LXC container, the information is
4244 included in the registration message.
4245@@ -241,9 +262,11 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4246 messages = self.mstore.get_pending_messages()
4247 password = messages[0]["registration_password"]
4248 self.assertEqual("SEKRET", password)
4249- self.assertEqual(self.logfile.getvalue().strip(),
4250- "INFO: Queueing message to register with account "
4251- "'account_name' with a password.")
4252+ self.assertEqual(
4253+ self.logfile.getvalue().strip(),
4254+ "INFO: Queueing message to register with account "
4255+ "'account_name' with a password.",
4256+ )
4257
4258 def test_queue_message_on_exchange_with_tags(self):
4259 """
4260@@ -254,14 +277,16 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4261 self.config.computer_title = "Computer Title"
4262 self.config.account_name = "account_name"
4263 self.config.registration_key = "SEKRET"
4264- self.config.tags = u"computer,tag"
4265+ self.config.tags = "computer,tag"
4266 self.reactor.fire("pre-exchange")
4267 messages = self.mstore.get_pending_messages()
4268 self.assertEqual("computer,tag", messages[0]["tags"])
4269- self.assertEqual(self.logfile.getvalue().strip(),
4270- "INFO: Queueing message to register with account "
4271- "'account_name' and tags computer,tag with a "
4272- "password.")
4273+ self.assertEqual(
4274+ self.logfile.getvalue().strip(),
4275+ "INFO: Queueing message to register with account "
4276+ "'account_name' and tags computer,tag with a "
4277+ "password.",
4278+ )
4279
4280 def test_queue_message_on_exchange_with_invalid_tags(self):
4281 """
4282@@ -273,14 +298,16 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4283 self.config.computer_title = "Computer Title"
4284 self.config.account_name = "account_name"
4285 self.config.registration_key = "SEKRET"
4286- self.config.tags = u"<script>alert()</script>"
4287+ self.config.tags = "<script>alert()</script>"
4288 self.reactor.fire("pre-exchange")
4289 messages = self.mstore.get_pending_messages()
4290 self.assertIs(None, messages[0]["tags"])
4291- self.assertEqual(self.logfile.getvalue().strip(),
4292- "ERROR: Invalid tags provided for registration.\n "
4293- "INFO: Queueing message to register with account "
4294- "'account_name' with a password.")
4295+ self.assertEqual(
4296+ self.logfile.getvalue().strip(),
4297+ "ERROR: Invalid tags provided for registration.\n "
4298+ "INFO: Queueing message to register with account "
4299+ "'account_name' with a password.",
4300+ )
4301
4302 def test_queue_message_on_exchange_with_unicode_tags(self):
4303 """
4304@@ -291,10 +318,10 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4305 self.config.computer_title = "Computer Title"
4306 self.config.account_name = "account_name"
4307 self.config.registration_key = "SEKRET"
4308- self.config.tags = u"prova\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}o"
4309+ self.config.tags = "prova\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}o"
4310 self.reactor.fire("pre-exchange")
4311 messages = self.mstore.get_pending_messages()
4312- expected = u"prova\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}o"
4313+ expected = "prova\N{LATIN SMALL LETTER J WITH CIRCUMFLEX}o"
4314 self.assertEqual(expected, messages[0]["tags"])
4315
4316 logs = self.logfile.getvalue().strip()
4317@@ -306,10 +333,12 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4318 # here, to circumvent that problem.
4319 if _PY3:
4320 logs = logs.encode("utf-8")
4321- self.assertEqual(logs,
4322- b"INFO: Queueing message to register with account "
4323- b"'account_name' and tags prova\xc4\xb5o "
4324- b"with a password.")
4325+ self.assertEqual(
4326+ logs,
4327+ b"INFO: Queueing message to register with account "
4328+ b"'account_name' and tags prova\xc4\xb5o "
4329+ b"with a password.",
4330+ )
4331
4332 def test_queue_message_on_exchange_with_access_group(self):
4333 """
4334@@ -318,15 +347,17 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4335 """
4336 self.mstore.set_accepted_types(["register"])
4337 self.config.account_name = "account_name"
4338- self.config.access_group = u"dinosaurs"
4339- self.config.tags = u"server,london"
4340+ self.config.access_group = "dinosaurs"
4341+ self.config.tags = "server,london"
4342 self.reactor.fire("pre-exchange")
4343 messages = self.mstore.get_pending_messages()
4344 self.assertEqual("dinosaurs", messages[0]["access_group"])
4345- self.assertEqual(self.logfile.getvalue().strip(),
4346- "INFO: Queueing message to register with account "
4347- "'account_name' in access group 'dinosaurs' and "
4348- "tags server,london without a password.")
4349+ self.assertEqual(
4350+ self.logfile.getvalue().strip(),
4351+ "INFO: Queueing message to register with account "
4352+ "'account_name' in access group 'dinosaurs' and "
4353+ "tags server,london without a password.",
4354+ )
4355
4356 def test_queue_message_on_exchange_with_empty_access_group(self):
4357 """
4358@@ -334,7 +365,7 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4359 an "access_group" key.
4360 """
4361 self.mstore.set_accepted_types(["register"])
4362- self.config.access_group = u""
4363+ self.config.access_group = ""
4364 self.reactor.fire("pre-exchange")
4365 messages = self.mstore.get_pending_messages()
4366 # Make sure the key does not appear in the outgoing message.
4367@@ -368,8 +399,7 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4368 self.assertEqual(messages[0]["type"], "register")
4369
4370 def test_no_message_when_should_register_is_false(self):
4371- """If we already have a secure id, do not queue a register message.
4372- """
4373+ """If we already have a secure id, do not queue a register message."""
4374 self.mstore.set_accepted_types(["register"])
4375 self.config.computer_title = "Computer Title"
4376 self.config.account_name = "account_name"
4377@@ -395,9 +425,12 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4378 """
4379 reactor_fire_mock = self.reactor.fire = mock.Mock()
4380 self.exchanger.handle_message(
4381- {"type": b"registration", "info": b"unknown-account"})
4382+ {"type": b"registration", "info": b"unknown-account"},
4383+ )
4384 reactor_fire_mock.assert_called_with(
4385- "registration-failed", reason="unknown-account")
4386+ "registration-failed",
4387+ reason="unknown-account",
4388+ )
4389
4390 def test_registration_failed_event_max_pending_computers(self):
4391 """
4392@@ -407,9 +440,12 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4393 """
4394 reactor_fire_mock = self.reactor.fire = mock.Mock()
4395 self.exchanger.handle_message(
4396- {"type": b"registration", "info": b"max-pending-computers"})
4397+ {"type": b"registration", "info": b"max-pending-computers"},
4398+ )
4399 reactor_fire_mock.assert_called_with(
4400- "registration-failed", reason="max-pending-computers")
4401+ "registration-failed",
4402+ reason="max-pending-computers",
4403+ )
4404
4405 def test_registration_failed_event_not_fired_when_uncertain(self):
4406 """
4407@@ -418,7 +454,8 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4408 """
4409 reactor_fire_mock = self.reactor.fire = mock.Mock()
4410 self.exchanger.handle_message(
4411- {"type": b"registration", "info": b"blah-blah"})
4412+ {"type": b"registration", "info": b"blah-blah"},
4413+ )
4414 for name, args, kwargs in reactor_fire_mock.mock_calls:
4415 self.assertNotEquals("registration-failed", args[0])
4416
4417@@ -449,13 +486,15 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4418
4419 # This should somehow callback the deferred.
4420 self.exchanger.handle_message(
4421- {"type": b"set-id", "id": b"abc", "insecure-id": b"def"})
4422+ {"type": b"set-id", "id": b"abc", "insecure-id": b"def"},
4423+ )
4424
4425 self.assertEqual(calls, [1])
4426
4427 # Doing it again to ensure that the deferred isn't called twice.
4428 self.exchanger.handle_message(
4429- {"type": b"set-id", "id": b"abc", "insecure-id": b"def"})
4430+ {"type": b"set-id", "id": b"abc", "insecure-id": b"def"},
4431+ )
4432
4433 self.assertEqual(calls, [1])
4434
4435@@ -477,7 +516,8 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4436
4437 # This should somehow callback the deferred.
4438 self.exchanger.handle_message(
4439- {"type": b"set-id", "id": b"abc", "insecure-id": b"def"})
4440+ {"type": b"set-id", "id": b"abc", "insecure-id": b"def"},
4441+ )
4442
4443 self.assertEqual(results, [None])
4444
4445@@ -502,13 +542,15 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4446
4447 # This should somehow callback the deferred.
4448 self.exchanger.handle_message(
4449- {"type": b"registration", "info": b"unknown-account"})
4450+ {"type": b"registration", "info": b"unknown-account"},
4451+ )
4452
4453 self.assertEqual(calls, [True])
4454
4455 # Doing it again to ensure that the deferred isn't called twice.
4456 self.exchanger.handle_message(
4457- {"type": b"registration", "info": b"unknown-account"})
4458+ {"type": b"registration", "info": b"unknown-account"},
4459+ )
4460
4461 self.assertEqual(calls, [True])
4462
4463@@ -534,13 +576,15 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4464 d.addErrback(add_call)
4465
4466 self.exchanger.handle_message(
4467- {"type": b"registration", "info": b"max-pending-computers"})
4468+ {"type": b"registration", "info": b"max-pending-computers"},
4469+ )
4470
4471 self.assertEqual(calls, [True])
4472
4473 # Doing it again to ensure that the deferred isn't called twice.
4474 self.exchanger.handle_message(
4475- {"type": b"registration", "info": b"max-pending-computers"})
4476+ {"type": b"registration", "info": b"max-pending-computers"},
4477+ )
4478
4479 self.assertEqual(calls, [True])
4480
4481@@ -575,9 +619,13 @@ class RegistrationHandlerTest(RegistrationHandlerTestBase):
4482
4483 class JujuRegistrationHandlerTest(RegistrationHandlerTestBase):
4484
4485- juju_contents = json.dumps({"environment-uuid": "DEAD-BEEF",
4486- "machine-id": "1",
4487- "api-addresses": "10.0.3.1:17070"})
4488+ juju_contents = json.dumps(
4489+ {
4490+ "environment-uuid": "DEAD-BEEF",
4491+ "machine-id": "1",
4492+ "api-addresses": "10.0.3.1:17070",
4493+ },
4494+ )
4495
4496 def test_juju_info_added_when_present(self):
4497 """
4498@@ -593,10 +641,13 @@ class JujuRegistrationHandlerTest(RegistrationHandlerTestBase):
4499
4500 messages = self.mstore.get_pending_messages()
4501 self.assertEqual(
4502- {"environment-uuid": "DEAD-BEEF",
4503- "machine-id": "1",
4504- "api-addresses": ["10.0.3.1:17070"]},
4505- messages[0]["juju-info"])
4506+ {
4507+ "environment-uuid": "DEAD-BEEF",
4508+ "machine-id": "1",
4509+ "api-addresses": ["10.0.3.1:17070"],
4510+ },
4511+ messages[0]["juju-info"],
4512+ )
4513
4514 def test_juju_info_skipped_with_old_server(self):
4515 """
4516diff --git a/landscape/client/broker/tests/test_server.py b/landscape/client/broker/tests/test_server.py
4517index 99fa65a..13461eb 100644
4518--- a/landscape/client/broker/tests/test_server.py
4519+++ b/landscape/client/broker/tests/test_server.py
4520@@ -1,23 +1,23 @@
4521 import random
4522+from unittest.mock import Mock
4523
4524 from configobj import ConfigObj
4525-from mock import Mock
4526-from twisted.internet.defer import succeed, fail
4527+from twisted.internet.defer import fail
4528+from twisted.internet.defer import succeed
4529
4530-from landscape.client.manager.manager import FAILED
4531-from landscape.client.tests.helpers import (
4532- LandscapeTest, DEFAULT_ACCEPTED_TYPES)
4533-from landscape.client.broker.tests.helpers import (
4534- BrokerServerHelper, RemoteClientHelper)
4535+from landscape.client.broker.tests.helpers import BrokerServerHelper
4536+from landscape.client.broker.tests.helpers import RemoteClientHelper
4537 from landscape.client.broker.tests.test_ping import FakePageGetter
4538+from landscape.client.manager.manager import FAILED
4539+from landscape.client.tests.helpers import DEFAULT_ACCEPTED_TYPES
4540+from landscape.client.tests.helpers import LandscapeTest
4541
4542
4543-class FakeClient(object):
4544+class FakeClient:
4545 pass
4546
4547
4548-class FakeCreator(object):
4549-
4550+class FakeCreator:
4551 def __init__(self, reactor, config):
4552 pass
4553
4554@@ -105,7 +105,11 @@ class BrokerServerTest(LandscapeTest):
4555 message = {"type": "test"}
4556 self.mstore.set_accepted_types(["test"])
4557 self.assertRaises(
4558- RuntimeError, self.broker.send_message, message, None)
4559+ RuntimeError,
4560+ self.broker.send_message,
4561+ message,
4562+ None,
4563+ )
4564
4565 def test_send_message_with_old_release_upgrader(self):
4566 """
4567@@ -123,8 +127,11 @@ class BrokerServerTest(LandscapeTest):
4568 If we receive a message from an old package-changer process that
4569 doesn't know about session IDs, we just let the message in.
4570 """
4571- message = {"type": "change-packages-result", "operation-id": 99,
4572- "result-code": 123}
4573+ message = {
4574+ "type": "change-packages-result",
4575+ "operation-id": 99,
4576+ "result-code": 123,
4577+ }
4578 self.mstore.set_accepted_types(["change-packages-result"])
4579 self.broker.send_message(message, True)
4580 self.assertMessages(self.mstore.get_pending_messages(), [message])
4581@@ -138,14 +145,17 @@ class BrokerServerTest(LandscapeTest):
4582 legacy_message = {
4583 b"type": b"change-packages-result",
4584 b"operation-id": 99,
4585- b"result-code": 123}
4586+ b"result-code": 123,
4587+ }
4588 self.mstore.set_accepted_types(["change-packages-result"])
4589 self.broker.send_message(legacy_message, True)
4590- expected = [{
4591- "type": "change-packages-result",
4592- "operation-id": 99,
4593- "result-code": 123
4594- }]
4595+ expected = [
4596+ {
4597+ "type": "change-packages-result",
4598+ "operation-id": 99,
4599+ "result-code": 123,
4600+ },
4601+ ]
4602 self.assertMessages(self.mstore.get_pending_messages(), expected)
4603 self.assertTrue(self.exchanger.is_urgent())
4604
4605@@ -176,9 +186,11 @@ class BrokerServerTest(LandscapeTest):
4606 self.assertEqual(len(self.broker.get_clients()), 1)
4607 self.assertEqual(len(self.broker.get_connectors()), 1)
4608 self.assertTrue(
4609- isinstance(self.broker.get_client("test"), FakeClient))
4610+ isinstance(self.broker.get_client("test"), FakeClient),
4611+ )
4612 self.assertTrue(
4613- isinstance(self.broker.get_connector("test"), FakeCreator))
4614+ isinstance(self.broker.get_connector("test"), FakeCreator),
4615+ )
4616
4617 self.broker.connectors_registry = {"test": FakeCreator}
4618 result = self.broker.register_client("test")
4619@@ -190,8 +202,10 @@ class BrokerServerTest(LandscapeTest):
4620 of each registered client, and returns a deferred resulting in C{None}
4621 if all C{exit} calls were successful.
4622 """
4623- self.broker.connectors_registry = {"foo": FakeCreator,
4624- "bar": FakeCreator}
4625+ self.broker.connectors_registry = {
4626+ "foo": FakeCreator,
4627+ "bar": FakeCreator,
4628+ }
4629 self.broker.register_client("foo")
4630 self.broker.register_client("bar")
4631 for client in self.broker.get_clients():
4632@@ -203,8 +217,10 @@ class BrokerServerTest(LandscapeTest):
4633 The L{BrokerServer.stop_clients} method calls the C{exit} method of
4634 each registered client, and raises an exception if any calls fail.
4635 """
4636- self.broker.connectors_registry = {"foo": FakeCreator,
4637- "bar": FakeCreator}
4638+ self.broker.connectors_registry = {
4639+ "foo": FakeCreator,
4640+ "bar": FakeCreator,
4641+ }
4642 self.broker.register_client("foo")
4643 self.broker.register_client("bar")
4644 [client1, client2] = self.broker.get_clients()
4645@@ -221,8 +237,12 @@ class BrokerServerTest(LandscapeTest):
4646 config_obj["client"]["computer_title"] = "New Title"
4647 config_obj.write()
4648 result = self.broker.reload_configuration()
4649- result.addCallback(lambda x: self.assertEqual(
4650- self.config.computer_title, "New Title"))
4651+ result.addCallback(
4652+ lambda x: self.assertEqual(
4653+ self.config.computer_title,
4654+ "New Title",
4655+ ),
4656+ )
4657 return result
4658
4659 def test_reload_configuration_stops_clients(self):
4660@@ -230,8 +250,10 @@ class BrokerServerTest(LandscapeTest):
4661 The L{BrokerServer.reload_configuration} method forces the config
4662 file associated with the broker server to be reloaded.
4663 """
4664- self.broker.connectors_registry = {"foo": FakeCreator,
4665- "bar": FakeCreator}
4666+ self.broker.connectors_registry = {
4667+ "foo": FakeCreator,
4668+ "bar": FakeCreator,
4669+ }
4670 self.broker.register_client("foo")
4671 self.broker.register_client("bar")
4672 for client in self.broker.get_clients():
4673@@ -245,8 +267,9 @@ class BrokerServerTest(LandscapeTest):
4674 """
4675 registered = self.broker.register()
4676 # This should callback the deferred.
4677- self.exchanger.handle_message({"type": "set-id", "id": "abc",
4678- "insecure-id": "def"})
4679+ self.exchanger.handle_message(
4680+ {"type": "set-id", "id": "abc", "insecure-id": "def"},
4681+ )
4682 return self.assertSuccess(registered)
4683
4684 def test_get_accepted_types_empty(self):
4685@@ -263,8 +286,10 @@ class BrokerServerTest(LandscapeTest):
4686 message types accepted by the Landscape server.
4687 """
4688 self.mstore.set_accepted_types(["foo", "bar"])
4689- self.assertEqual(sorted(self.broker.get_accepted_message_types()),
4690- ["bar", "foo"])
4691+ self.assertEqual(
4692+ sorted(self.broker.get_accepted_message_types()),
4693+ ["bar", "foo"],
4694+ )
4695
4696 def test_get_server_uuid_with_unset_uuid(self):
4697 """
4698@@ -288,8 +313,10 @@ class BrokerServerTest(LandscapeTest):
4699 """
4700 self.broker.register_client_accepted_message_type("type1")
4701 self.broker.register_client_accepted_message_type("type2")
4702- self.assertEqual(self.exchanger.get_client_accepted_message_types(),
4703- sorted(["type1", "type2"] + DEFAULT_ACCEPTED_TYPES))
4704+ self.assertEqual(
4705+ self.exchanger.get_client_accepted_message_types(),
4706+ sorted(["type1", "type2"] + DEFAULT_ACCEPTED_TYPES),
4707+ )
4708
4709 def test_fire_event(self):
4710 """
4711@@ -304,8 +331,10 @@ class BrokerServerTest(LandscapeTest):
4712 """
4713 The L{BrokerServer.exit} method stops all registered clients.
4714 """
4715- self.broker.connectors_registry = {"foo": FakeCreator,
4716- "bar": FakeCreator}
4717+ self.broker.connectors_registry = {
4718+ "foo": FakeCreator,
4719+ "bar": FakeCreator,
4720+ }
4721 self.broker.register_client("foo")
4722 self.broker.register_client("bar")
4723 for client in self.broker.get_clients():
4724@@ -430,8 +459,10 @@ class EventTest(LandscapeTest):
4725 """
4726 callback = Mock(return_value="foo")
4727 self.client_reactor.call_on("resynchronize", callback)
4728- return self.assertSuccess(self.broker.resynchronize(["foo"]),
4729- [["foo"]])
4730+ return self.assertSuccess(
4731+ self.broker.resynchronize(["foo"]),
4732+ [["foo"]],
4733+ )
4734
4735 def test_impending_exchange(self):
4736 """
4737@@ -448,7 +479,9 @@ class EventTest(LandscapeTest):
4738 plugin.exchange.assert_called_once_with()
4739
4740 deferred = self.assertSuccess(
4741- self.broker.impending_exchange(), [[None]])
4742+ self.broker.impending_exchange(),
4743+ [[None]],
4744+ )
4745 deferred.addCallback(assert_called)
4746 return deferred
4747
4748@@ -464,12 +497,15 @@ class EventTest(LandscapeTest):
4749 self.remote.register_client = Mock()
4750
4751 def assert_called_made(ignored):
4752- self.remote.register_client_accepted_message_type\
4753- .assert_called_once_with("type")
4754+ self.remote.register_client_accepted_message_type.assert_called_once_with( # noqa: E501
4755+ "type",
4756+ )
4757 self.remote.register_client.assert_called_once_with("client")
4758
4759 deferred = self.assertSuccess(
4760- self.broker.broker_reconnect(), [[None]])
4761+ self.broker.broker_reconnect(),
4762+ [[None]],
4763+ )
4764 return deferred.addCallback(assert_called_made)
4765
4766 registered = self.client.register_message("type", lambda x: None)
4767@@ -488,7 +524,9 @@ class EventTest(LandscapeTest):
4768
4769 self.client_reactor.call_on("server-uuid-changed", callback)
4770 deferred = self.assertSuccess(
4771- self.broker.server_uuid_changed(None, "abc"), [[return_value]])
4772+ self.broker.server_uuid_changed(None, "abc"),
4773+ [[return_value]],
4774+ )
4775 return deferred.addCallback(assert_called)
4776
4777 def test_message_type_acceptance_changed(self):
4778@@ -499,7 +537,9 @@ class EventTest(LandscapeTest):
4779 return_value = random.randint(1, 100)
4780 callback = Mock(return_value=return_value)
4781 self.client_reactor.call_on(
4782- ("message-type-acceptance-changed", "type"), callback)
4783+ ("message-type-acceptance-changed", "type"),
4784+ callback,
4785+ )
4786 result = self.broker.message_type_acceptance_changed("type", True)
4787 return self.assertSuccess(result, [[return_value]])
4788
4789@@ -512,7 +552,9 @@ class EventTest(LandscapeTest):
4790 callback = Mock(return_value=return_value)
4791 self.client_reactor.call_on("package-data-changed", callback)
4792 return self.assertSuccess(
4793- self.broker.package_data_changed(), [[return_value]])
4794+ self.broker.package_data_changed(),
4795+ [[return_value]],
4796+ )
4797
4798
4799 class HandlersTest(LandscapeTest):
4800@@ -520,7 +562,7 @@ class HandlersTest(LandscapeTest):
4801 helpers = [BrokerServerHelper]
4802
4803 def setUp(self):
4804- super(HandlersTest, self).setUp()
4805+ super().setUp()
4806 self.broker.connectors_registry = {"test": FakeCreator}
4807 self.broker.register_client("test")
4808 self.client = self.broker.get_client("test")
4809@@ -549,18 +591,25 @@ class HandlersTest(LandscapeTest):
4810 result = self.reactor.fire("message", message)
4811 result = [i for i in result if i is not None][0]
4812
4813- class StartsWith(object):
4814-
4815+ class StartsWith:
4816 def __eq__(self, other):
4817 return other.startswith(
4818- "Landscape client failed to handle this request (foobar)")
4819+ "Landscape client failed to handle this request (foobar)",
4820+ )
4821
4822 def broadcasted(ignored):
4823 self.client.message.assert_called_once_with(message)
4824 self.assertMessages(
4825 self.mstore.get_pending_messages(),
4826- [{"type": "operation-result", "status": FAILED,
4827- "result-text": StartsWith(), "operation-id": 4}])
4828+ [
4829+ {
4830+ "type": "operation-result",
4831+ "status": FAILED,
4832+ "result-text": StartsWith(),
4833+ "operation-id": 4,
4834+ },
4835+ ],
4836+ )
4837
4838 result.addCallback(broadcasted)
4839 return result
4840@@ -582,7 +631,10 @@ class HandlersTest(LandscapeTest):
4841 self.client.fire_event = Mock(return_value=succeed(None))
4842 self.reactor.fire("message-type-acceptance-changed", "test", True)
4843 self.client.fire_event.assert_called_once_with(
4844- "message-type-acceptance-changed", "test", True)
4845+ "message-type-acceptance-changed",
4846+ "test",
4847+ True,
4848+ )
4849
4850 def test_server_uuid_changed(self):
4851 """
4852@@ -592,7 +644,10 @@ class HandlersTest(LandscapeTest):
4853 self.client.fire_event = Mock(return_value=succeed(None))
4854 self.reactor.fire("server-uuid-changed", None, 123)
4855 self.client.fire_event.assert_called_once_with(
4856- "server-uuid-changed", None, 123)
4857+ "server-uuid-changed",
4858+ None,
4859+ 123,
4860+ )
4861
4862 def test_package_data_changed(self):
4863 """
4864diff --git a/landscape/client/broker/tests/test_service.py b/landscape/client/broker/tests/test_service.py
4865index 580f64e..e99f9ac 100644
4866--- a/landscape/client/broker/tests/test_service.py
4867+++ b/landscape/client/broker/tests/test_service.py
4868@@ -1,12 +1,11 @@
4869 import os
4870+from unittest.mock import Mock
4871
4872-from mock import Mock
4873-
4874-from landscape.client.tests.helpers import LandscapeTest
4875-from landscape.client.broker.tests.helpers import BrokerConfigurationHelper
4876+from landscape.client.broker.amp import RemoteBrokerConnector
4877 from landscape.client.broker.service import BrokerService
4878+from landscape.client.broker.tests.helpers import BrokerConfigurationHelper
4879 from landscape.client.broker.transport import HTTPTransport
4880-from landscape.client.broker.amp import RemoteBrokerConnector
4881+from landscape.client.tests.helpers import LandscapeTest
4882 from landscape.lib.testing import FakeReactor
4883
4884
4885@@ -15,7 +14,7 @@ class BrokerServiceTest(LandscapeTest):
4886 helpers = [BrokerConfigurationHelper]
4887
4888 def setUp(self):
4889- super(BrokerServiceTest, self).setUp()
4890+ super().setUp()
4891
4892 class FakeBrokerService(BrokerService):
4893 reactor_factory = FakeReactor
4894@@ -28,7 +27,8 @@ class BrokerServiceTest(LandscapeTest):
4895 """
4896 self.assertEqual(
4897 self.service.persist.filename,
4898- os.path.join(self.config.data_path, "broker.bpickle"))
4899+ os.path.join(self.config.data_path, "broker.bpickle"),
4900+ )
4901
4902 def test_transport(self):
4903 """
4904diff --git a/landscape/client/broker/tests/test_store.py b/landscape/client/broker/tests/test_store.py
4905index 208c75c..b61edf3 100644
4906--- a/landscape/client/broker/tests/test_store.py
4907+++ b/landscape/client/broker/tests/test_store.py
4908@@ -1,22 +1,22 @@
4909 import os
4910-import mock
4911-
4912+from unittest import mock
4913
4914 from twisted.python.compat import intToBytes
4915
4916+from landscape.client.broker.store import MessageStore
4917+from landscape.client.tests.helpers import LandscapeTest
4918 from landscape.lib.bpickle import dumps
4919 from landscape.lib.persist import Persist
4920-from landscape.lib.schema import InvalidError, Int, Bytes, Unicode
4921+from landscape.lib.schema import Bytes
4922+from landscape.lib.schema import Int
4923+from landscape.lib.schema import InvalidError
4924+from landscape.lib.schema import Unicode
4925 from landscape.message_schemas.message import Message
4926-from landscape.client.broker.store import MessageStore
4927-
4928-from landscape.client.tests.helpers import LandscapeTest
4929
4930
4931 class MessageStoreTest(LandscapeTest):
4932-
4933 def setUp(self):
4934- super(MessageStoreTest, self).setUp()
4935+ super().setUp()
4936 self.temp_dir = self.makeDir()
4937 self.persist_filename = self.makeFile()
4938 self.store = self.create_store()
4939@@ -137,13 +137,87 @@ class MessageStoreTest(LandscapeTest):
4940 self.assertEqual(self.store.get_pending_offset(), 0)
4941 self.assertEqual(self.store.get_pending_messages(), [])
4942
4943+ def test_messages_over_limit(self):
4944+ """
4945+ Create six messages, two per directory. Since there is a limit of
4946+ one directory then only the last 2 messages should be in the queue
4947+ """
4948+
4949+ self.store._directory_size = 2
4950+ self.store._max_dirs = 1
4951+ for num in range(6): # 0,1 2,3 4,5
4952+ message = {"type": "data", "data": f"{num}".encode()}
4953+ self.store.add(message)
4954+ messages = self.store.get_pending_messages(200)
4955+ self.assertMessages(
4956+ messages,
4957+ [{"type": "data", "data": b"4"}, {"type": "data", "data": b"5"}],
4958+ )
4959+
4960+ def test_messages_under_limit(self):
4961+ """
4962+ Create six messages, all of which should be in the queue, since 3
4963+ directories are active
4964+ """
4965+
4966+ self.store._directory_size = 2
4967+ self.store._max_dirs = 3
4968+ messages_sent = []
4969+ for num in range(6): # 0,1 2,3 4,5
4970+ message = {"type": "data", "data": f"{num}".encode()}
4971+ messages_sent.append(message)
4972+ self.store.add(message)
4973+ messages = self.store.get_pending_messages(200)
4974+ self.assertMessages(messages, messages_sent)
4975+
4976+ def test_messages_over_mb(self):
4977+ """
4978+ Create three messages with the second one being very large. The
4979+ max size should get triggered, so all should be cleared except for the
4980+ last one.
4981+ """
4982+
4983+ self.store._directory_size = 2
4984+ self.store._max_size_mb = 0.01
4985+ self.store.add({"type": "data", "data": b"a"})
4986+ self.store.add({"type": "data", "data": b"b" * 15000})
4987+ self.store.add({"type": "data", "data": b"c"})
4988+ messages = self.store.get_pending_messages(200)
4989+ self.assertMessages(
4990+ messages,
4991+ [{"type": "data", "data": b"c"}],
4992+ )
4993+
4994+ @mock.patch("shutil.rmtree")
4995+ def test_exception_on_message_limit(self, rmtree_mock):
4996+ """
4997+ If an exception occurs while deleting it shouldn't affect the next
4998+ message sent
4999+ """
5000+ rmtree_mock.side_effect = IOError("Error!")
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches