New upstream release
Jonathan Carter
3 years ago
0 | # 4.2.1 | |
1 | ||
2 | 2020-10-15 | |
3 | ||
4 | * fixed unintended Fault evaluation in metadata collision error message | |
5 | * fixed sorting of Faults with other types | |
6 | * fixed display of paged output on large macOS terminals | |
7 | * fixed svc_openbsd being applied concurrently | |
8 | * fixed services being reloaded and restarted at the same time | |
9 | * fixed possible mangling of group metadata from items.py | |
10 | ||
11 | ||
0 | 12 | # 4.2.0 |
1 | 13 | |
2 | 14 | 2020-09-21 |
27 | 27 | target_nodes = get_target_nodes(repo, args['targets']) |
28 | 28 | pending_nodes = target_nodes.copy() |
29 | 29 | |
30 | io.progress_set_total(count_items(pending_nodes)) | |
31 | ||
32 | 30 | try: |
33 | 31 | repo.hooks.apply_start( |
34 | 32 | repo, |
42 | 40 | x=red("!!!"), |
43 | 41 | )) |
44 | 42 | exit(1) |
43 | ||
44 | io.progress_set_total(count_items(pending_nodes)) | |
45 | 45 | |
46 | 46 | start_time = datetime.now() |
47 | 47 | results = [] |
30 | 30 | |
31 | 31 | def apply(self, *args, **kwargs): |
32 | 32 | return (Item.STATUS_OK, []) |
33 | ||
34 | def get_canned_actions(self): | |
35 | return {} | |
33 | 36 | |
34 | 37 | def test(self): |
35 | 38 | pass |
205 | 208 | |
206 | 209 | def _inject_canned_actions(items): |
207 | 210 | """ |
208 | Looks for canned actions like "svc_upstart:mysql:reload" in item | |
209 | triggers and adds them to the list of items. | |
210 | """ | |
211 | added_actions = {} | |
212 | for item in items.values(): | |
213 | for triggered_item_id in item.triggers: | |
214 | if triggered_item_id in added_actions: | |
215 | # action has already been triggered | |
216 | continue | |
217 | ||
218 | try: | |
219 | type_name, item_name, action_name = triggered_item_id.split(":") | |
220 | except ValueError: | |
221 | # not a canned action | |
222 | continue | |
223 | ||
224 | target_item_id = "{}:{}".format(type_name, item_name) | |
225 | ||
226 | try: | |
227 | target_item = items[target_item_id] | |
228 | except KeyError: | |
229 | raise BundleError(_( | |
230 | "{item} in bundle '{bundle}' triggers unknown item '{target_item}'" | |
231 | ).format( | |
232 | bundle=item.bundle.name, | |
233 | item=item.id, | |
234 | target_item=target_item_id, | |
235 | )) | |
236 | ||
237 | try: | |
238 | action_attrs = target_item.get_canned_actions()[action_name] | |
239 | except KeyError: | |
240 | raise BundleError(_( | |
241 | "{item} in bundle '{bundle}' triggers unknown " | |
242 | "canned action '{action}' on {target_item}" | |
243 | ).format( | |
244 | action=action_name, | |
245 | bundle=item.bundle.name, | |
246 | item=item.id, | |
247 | target_item=target_item_id, | |
248 | )) | |
249 | ||
250 | action_attrs.update({'triggered': True}) | |
211 | Looks for canned actions like "svc_upstart:mysql:reload" in items, | |
212 | created actions for them and add those to the list of items. | |
213 | """ | |
214 | for item in list(items.values()): | |
215 | for canned_action_name, canned_action_attrs in item.get_canned_actions().items(): | |
216 | canned_action_id = f"{item.id}:{canned_action_name}" | |
217 | canned_action_attrs.update({'triggered': True}) | |
251 | 218 | action = Action( |
252 | 219 | item.bundle, |
253 | triggered_item_id, | |
254 | action_attrs, | |
220 | canned_action_id, | |
221 | canned_action_attrs, | |
255 | 222 | skip_name_validation=True, |
256 | 223 | ) |
257 | 224 | action._prepare_deps(items) |
258 | added_actions[triggered_item_id] = action | |
259 | ||
260 | items.update({item.id: item for item in added_actions.values()}) | |
225 | items[canned_action_id] = action | |
261 | 226 | return items |
262 | 227 | |
263 | 228 |
44 | 44 | } |
45 | 45 | ITEM_TYPE_NAME = "svc_openbsd" |
46 | 46 | |
47 | @classmethod | |
48 | def block_concurrent(cls, node_os, node_os_version): | |
49 | # https://github.com/bundlewrap/bundlewrap/issues/554 | |
50 | return [cls.ITEM_TYPE_NAME] | |
51 | ||
47 | 52 | def __repr__(self): |
48 | 53 | return "<SvcOpenBSD name:{} running:{} enabled:{}>".format( |
49 | 54 | self.name, |
67 | 72 | return { |
68 | 73 | 'restart': { |
69 | 74 | 'command': "/etc/rc.d/{} restart".format(self.name), |
70 | 'needs': [self.id], | |
75 | # make sure we don't restart and stopstart simultaneously | |
76 | 'needs': [f"{self.id}:stopstart"], | |
71 | 77 | }, |
72 | 78 | 'stopstart': { |
73 | 79 | 'command': "/etc/rc.d/{0} stop && /etc/rc.d/{0} start".format(self.name), |
81 | 81 | return { |
82 | 82 | 'reload': { |
83 | 83 | 'command': "systemctl reload -- {}".format(self.name), |
84 | 'needs': [self.id], | |
84 | # make sure we don't reload and restart simultaneously | |
85 | 'needs': [f"{self.id}:restart"], | |
85 | 86 | }, |
86 | 87 | 'restart': { |
87 | 88 | 'command': "systemctl restart -- {}".format(self.name), |
46 | 46 | return { |
47 | 47 | 'reload': { |
48 | 48 | 'command': "/etc/init.d/{} reload".format(self.name), |
49 | 'needs': [self.id], | |
49 | # make sure we don't reload and restart simultaneously | |
50 | 'needs': [f"{self.id}:restart"], | |
50 | 51 | }, |
51 | 52 | 'restart': { |
52 | 53 | 'command': "/etc/init.d/{} restart".format(self.name), |
45 | 45 | return { |
46 | 46 | 'reload': { |
47 | 47 | 'command': "reload {}".format(self.name), |
48 | 'needs': [self.id], | |
48 | # make sure we don't reload and restart simultaneously | |
49 | 'needs': [f"{self.id}:restart"], | |
49 | 50 | }, |
50 | 51 | 'restart': { |
51 | 52 | 'command': "restart {}".format(self.name), |
52 | 'needs': [self.id], | |
53 | # make sure we don't restart and stopstart simultaneously | |
54 | 'needs': [f"{self.id}:stopstart"], | |
53 | 55 | }, |
54 | 56 | 'stopstart': { |
55 | 57 | 'command': "stop {0} && start {0}".format(self.name), |
143 | 143 | elif value != prev_value: |
144 | 144 | raise ValueError(_( |
145 | 145 | "{node}: {a} and {b} are clashing over this key path: {path} " |
146 | "(\"{val_a}\" vs. \"{val_b}\")" | |
146 | "({val_a} vs. {val_b})" | |
147 | 147 | ).format( |
148 | 148 | a=identifier, |
149 | 149 | b=prev_identifier, |
150 | 150 | node=node.name, |
151 | 151 | path="/".join(path), |
152 | val_a=value, | |
153 | val_b=prev_value, | |
152 | val_a=repr(value), | |
153 | val_b=repr(prev_value), | |
154 | 154 | )) |
155 | 155 | |
156 | 156 |
154 | 154 | return len(self.value) |
155 | 155 | |
156 | 156 | def __lt__(self, other): |
157 | return self.value < other.value | |
157 | if isinstance(other, Fault): | |
158 | return self.value < other.value | |
159 | else: | |
160 | return self.value < other | |
161 | ||
162 | def __gt__(self, other): | |
163 | if isinstance(other, Fault): | |
164 | return self.value > other.value | |
165 | else: | |
166 | return self.value > other | |
167 | ||
168 | def __repr__(self): | |
169 | return f"<Fault: {self.id_list}>" | |
158 | 170 | |
159 | 171 | def __str__(self): |
160 | 172 | return str(self.value) |
0 | from copy import copy | |
0 | 1 | from difflib import unified_diff |
1 | 2 | from hashlib import sha1 |
2 | 3 | from json import dumps, JSONEncoder |
248 | 249 | ): |
249 | 250 | merged[key] = base[key].union(set(value)) |
250 | 251 | else: |
251 | merged[key] = value | |
252 | # If we don't copy here, we end up with dicts from groups in | |
253 | # node metadata. Not an issue per se, but a nasty pitfall | |
254 | # when users do things like this in items.py: | |
255 | # | |
256 | # my_dict = node.metadata.get('foo', {}) | |
257 | # my_dict['bar'] = 'baz' | |
258 | # | |
259 | # The expectation here is to be able to mangle my_dict | |
260 | # because it is only relevant for the current node. However, | |
261 | # if 'foo' has only been defined in a group, we end up | |
262 | # mangling that dict for every node in the group. | |
263 | # Since we can't really force users to .copy() in this case | |
264 | # (although they should!), we have to do it here. | |
265 | merged[key] = copy(value) | |
252 | 266 | |
253 | 267 | return merged |
254 | 268 |
105 | 105 | View the given list of Unicode lines in a pager (e.g. `less`). |
106 | 106 | """ |
107 | 107 | lines = list(lines) |
108 | if TTY: | |
108 | line_width = max([len(ansi_clean(line)) for line in lines]) | |
109 | if TTY and line_width > get_terminal_size().columns: | |
109 | 110 | write_to_stream(STDOUT_WRITER, SHOW_CURSOR) |
110 | 111 | env = environ.copy() |
111 | env["LESS"] = env.get("LESS", "") + " -FR" | |
112 | env["LESS"] = env.get("LESS", "") + " -R" | |
112 | 113 | pager = Popen( |
113 | 114 | [environ.get("PAGER", "/usr/bin/less")], |
114 | 115 | env=env, |
0 | bundlewrap (4.2.1-1) unstable; urgency=medium | |
1 | ||
2 | * New upstream release | |
3 | ||
4 | -- Jonathan Carter <jcc@debian.org> Sun, 18 Oct 2020 17:04:59 +0200 | |
5 | ||
0 | 6 | bundlewrap (4.2.0-1) unstable; urgency=medium |
1 | 7 | |
2 | 8 | * New upstream release |
9 | * Update VCS field to new unified Python team | |
10 | * Update uploader field to new unified Python team | |
3 | 11 | |
4 | 12 | -- Jonathan Carter <jcc@debian.org> Tue, 22 Sep 2020 20:21:56 +0200 |
5 | 13 |
1 | 1 | Section: python |
2 | 2 | Priority: optional |
3 | 3 | Maintainer: Jonathan Carter <jcc@debian.org> |
4 | Uploaders: Python Applications Packaging Team <python-apps-team@lists.alioth.debian.org> | |
4 | Uploaders: Debian Python Team <team+python@tracker.debian.org> | |
5 | 5 | Build-Depends: debhelper-compat (= 13), |
6 | 6 | dh-python, |
7 | 7 | python3, |
2 | 2 | |
3 | 3 | setup( |
4 | 4 | name="bundlewrap", |
5 | version="4.2.0", | |
5 | version="4.2.1", | |
6 | 6 | description="Config management with Python", |
7 | 7 | long_description=( |
8 | 8 | "By allowing for easy and low-overhead config management, BundleWrap fills the gap between complex deployments using Chef or Puppet and old school system administration over SSH.\n" |
0 | 0 | from bundlewrap.utils import Fault |
1 | ||
2 | from pytest import raises | |
1 | 3 | |
2 | 4 | |
3 | 5 | def test_basic_resolve(): |
369 | 371 | hash2 = hash(a) |
370 | 372 | |
371 | 373 | assert hash1 == hash2 |
374 | ||
375 | ||
376 | def test_sort(): | |
377 | def one(): | |
378 | return 1 | |
379 | ||
380 | def three(): | |
381 | return 3 | |
382 | ||
383 | f1 = Fault("1", one) | |
384 | f3 = Fault("3", three) | |
385 | ||
386 | assert sorted([2, f3, f1]) == [f1, 2, f3] | |
387 | ||
388 | ||
389 | def test_sort_typeerror(): | |
390 | def one(): | |
391 | return 1 | |
392 | ||
393 | def three(): | |
394 | return 3 | |
395 | ||
396 | f1 = Fault("1", one) | |
397 | f3 = Fault("3", three) | |
398 | ||
399 | with raises(TypeError): | |
400 | sorted(["2", f3, f1]) | |
401 | ||
402 | ||
403 | def test_sort_typeerror_from_fault(): | |
404 | def one(): | |
405 | return 1 | |
406 | ||
407 | def three(): | |
408 | return "3" | |
409 | ||
410 | f1 = Fault("1", one) | |
411 | f3 = Fault("3", three) | |
412 | ||
413 | with raises(TypeError): | |
414 | sorted([2, f3, f1]) |