Skip to content

Commit

Permalink
Merge pull request #363 from bundlewrap/3
Browse files Browse the repository at this point in the history
3.0
  • Loading branch information
trehn committed Sep 24, 2017
2 parents 23d28e7 + 2e6f253 commit 0c39bc0
Show file tree
Hide file tree
Showing 50 changed files with 1,084 additions and 901 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# 3.0.0

unreleased

* new metadata processor API and options (BACKWARDS INCOMPATIBLE)
* files, directories, and symlinks now have defaults for owner, group, and mode (BACKWARDS INCOMPATIBLE)
* overhauled options and output of `bw groups` (BACKWARDS INCOMPATIBLE)
* overhauled options and output of `bw nodes` (BACKWARDS INCOMPATIBLE)
* overhauled options and output of `bw run` (BACKWARDS INCOMPATIBLE)
* overhauled options of `bw test` (BACKWARDS INCOMPATIBLE)
* svc_systemd services are now 'enabled' by default (BACKWARDS INCOMPATIBLE)
* `bw items --file-preview` no longer uses a separate file path argument (BACKWARDS INCOMPATIBLE)
* removed `bw apply --profiling` (BACKWARDS INCOMPATIBLE)
* removed `Item.display_keys()` (BACKWARDS INCOMPATIBLE)
* changed return value of `Item.display_dicts()` (BACKWARDS INCOMPATIBLE)
* changed `Item.BLOCK_CONCURRENT` into a class method (BACKWARDS INCOMPATIBLE)
* removed `repo.vault.format()` (BACKWARDS INCOMPATIBLE)
* removed env vars: BWADDHOSTKEYS, BWCOLORS, BWITEMWORKERS, BWNODEWORKERS (BACKWARDS INCOMPATIBLE)


# 2.20.1

2017-09-21
Expand Down
19 changes: 16 additions & 3 deletions bundlewrap/bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from os.path import exists, join

from .exceptions import NoSuchBundle, RepositoryError
from .metadata import DEFAULTS, DONE, RUN_ME_AGAIN, OVERWRITE
from .utils import cached_property, get_all_attrs_from_file
from .utils.text import mark_for_translation as _
from .utils.text import validate_name
Expand All @@ -14,6 +15,14 @@
FILENAME_METADATA = "metadata.py"


def metadata_processor(func):
"""
Decorator that tags metadata processors.
"""
func.__is_a_metadata_processor = True
return func


class Bundle(object):
"""
A collection of config items, bound to a node.
Expand Down Expand Up @@ -92,11 +101,15 @@ def metadata_processors(self):
for name, attr in get_all_attrs_from_file(
self.metadata_file,
base_env={
'DEFAULTS': DEFAULTS,
'DONE': DONE,
'RUN_ME_AGAIN': RUN_ME_AGAIN,
'OVERWRITE': OVERWRITE,
'metadata_processor': metadata_processor,
'node': self.node,
'repo': self.repo,
},
).items():
if name.startswith("_") or not callable(attr):
continue
result.append(attr)
if getattr(attr, '__is_a_metadata_processor', False):
result.append(attr)
return result
16 changes: 7 additions & 9 deletions bundlewrap/cmdline/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from cProfile import Profile
from functools import wraps
from os import environ
from os.path import abspath, dirname
Expand Down Expand Up @@ -101,21 +102,15 @@ def main(*args, **kwargs):
if not hasattr(pargs, 'func'):
parser_bw.print_help()
exit(2)
if pargs.profile:
profile = Profile()
profile.enable()

path = abspath(pargs.repo_path)
io.debug_mode = pargs.debug
io.activate()
io.debug(_("invocation: {}").format(" ".join([force_text(arg) for arg in argv])))

if 'BWADDHOSTKEYS' in environ: # TODO remove in 3.0.0
environ.setdefault('BW_ADD_HOST_KEYS', environ['BWADDHOSTKEYS'])
if 'BWCOLORS' in environ: # TODO remove in 3.0.0
environ.setdefault('BW_COLORS', environ['BWCOLORS'])
if 'BWITEMWORKERS' in environ: # TODO remove in 3.0.0
environ.setdefault('BW_ITEM_WORKERS', environ['BWITEMWORKERS'])
if 'BWNODEWORKERS' in environ: # TODO remove in 3.0.0
environ.setdefault('BW_NODE_WORKERS', environ['BWNODEWORKERS'])

environ.setdefault('BW_ADD_HOST_KEYS', "1" if pargs.add_ssh_host_keys else "0")

if len(text_args) >= 1 and (
Expand Down Expand Up @@ -152,3 +147,6 @@ def main(*args, **kwargs):
pargs.func(repo, text_pargs)
finally:
io.deactivate()
if pargs.profile:
profile.disable()
profile.dump_stats(pargs.profile)
21 changes: 2 additions & 19 deletions bundlewrap/cmdline/apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ def next_task():
'interactive': args['interactive'],
'skip_list': skip_list,
'workers': args['item_workers'],
'profiling': args['profiling'],
},
}

Expand All @@ -67,22 +66,6 @@ def handle_result(task_id, return_value, duration):
return
skip_list.add(task_id)
results.append(return_value)
if args['profiling']:
total_time = 0.0
io.stdout(_(" {}").format(bold(task_id)))
io.stdout(_(" {} BEGIN PROFILING DATA "
"(most expensive items first)").format(bold(task_id)))
io.stdout(_(" {} seconds item").format(bold(task_id)))
for time_elapsed, item_id in return_value.profiling_info:
io.stdout(" {} {:10.3f} {}".format(
bold(task_id),
time_elapsed.total_seconds(),
item_id,
))
total_time += time_elapsed.total_seconds()
io.stdout(_(" {} {:10.3f} (total)").format(bold(task_id), total_time))
io.stdout(_(" {} END PROFILING DATA").format(bold(task_id)))
io.stdout(_(" {}").format(bold(task_id)))

def handle_exception(task_id, exception, traceback):
if isinstance(exception, ItemDependencyLoop):
Expand Down Expand Up @@ -143,12 +126,12 @@ def stats_summary(results, total_duration):
], ROW_SEPARATOR]

for result in results:
totals['items'] += len(result.profiling_info)
totals['items'] += result.total
for metric in ('correct', 'fixed', 'skipped', 'failed'):
totals[metric] += getattr(result, metric)
rows.append([
result.node_name,
str(len(result.profiling_info)),
str(result.total),
str(result.correct),
green_unless_zero(result.fixed),
yellow_unless_zero(result.skipped),
Expand Down
33 changes: 27 additions & 6 deletions bundlewrap/cmdline/groups.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from ..utils import names
from ..group import GROUP_ATTR_DEFAULTS
from ..utils.text import bold, mark_for_translation as _
from ..utils.ui import io
from .nodes import _attribute_table


GROUP_ATTRS = sorted(list(GROUP_ATTR_DEFAULTS) + ['nodes'])
GROUP_ATTRS_LISTS = ('nodes',)


def bw_groups(repo, args):
for group in repo.groups:
line = group.name
if args['show_nodes']:
line += ": " + ", ".join(names(group.nodes))
io.stdout(line)
if not args['groups']:
for group in repo.groups:
io.stdout(group.name)
else:
groups = [repo.get_group(group.strip()) for group in args['groups'].split(",")]
if not args['attrs']:
subgroups = set(groups)
for group in groups:
subgroups = subgroups.union(group.subgroups)
for subgroup in sorted(subgroups):
io.stdout(subgroup.name)
else:
_attribute_table(
groups,
bold(_("group")),
args['attrs'],
GROUP_ATTRS,
GROUP_ATTRS_LISTS,
args['inline'],
)
68 changes: 41 additions & 27 deletions bundlewrap/cmdline/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,13 @@ def write_preview(file_item, base_path):

def bw_items(repo, args):
node = get_node(repo, args['node'], adhoc_nodes=args['adhoc_nodes'])
if args['file_preview']:
item = get_item(node, "file:{}".format(args['file_preview']))
if (
item.attributes['content_type'] in ('any', 'base64', 'binary') or
item.attributes['delete'] is True
):
io.stderr(_(
"{x} cannot preview {file} on {node} (unsuitable content_type or deleted)"
).format(x=red("!!!"), file=item.id, node=node.name))
exit(1)
else:
try:
io.stdout(item.content.decode(item.attributes['encoding']), append_newline=False)
except FaultUnavailable:
io.stderr(_(
"{x} skipped {path} (Fault unavailable)"
).format(x=yellow("»"), path=bold(item.name)))
exit(1)
if args['file_preview'] and not args['item']:
io.stderr(_("{x} no ITEM given for file preview").format(x=red("!!!")))
exit(1)
elif args['file_preview_path']:
if args['item']:
io.stderr(_("{x} use --file-preview to preview single files").format(x=red("!!!")))
exit(1)
if exists(args['file_preview_path']):
io.stderr(_(
"not writing to existing path: {path}"
Expand Down Expand Up @@ -88,17 +76,43 @@ def bw_items(repo, args):
))
elif args['item']:
item = get_item(node, args['item'])
if args['show_sdict']:
statedict = item.sdict()
else:
statedict = item.cdict()
if statedict is None:
io.stdout("REMOVE")
if args['file_preview']:
if item.ITEM_TYPE_NAME != 'file':
io.stderr(_(
"{x} cannot preview {item} on {node} (not a file)"
).format(x=red("!!!"), item=item.id, node=node.name))
exit(1)
if (
item.attributes['content_type'] in ('any', 'base64', 'binary') or
item.attributes['delete'] is True
):
io.stderr(_(
"{x} cannot preview {file} on {node} (unsuitable content_type or deleted)"
).format(x=red("!!!"), file=item.id, node=node.name))
exit(1)
else:
try:
io.stdout(
item.content.decode(item.attributes['encoding']),
append_newline=False,
)
except FaultUnavailable:
io.stderr(_(
"{x} skipped {path} (Fault unavailable)"
).format(x=yellow("»"), path=bold(item.name)))
exit(1)
else:
if args['attr']:
io.stdout(repr(statedict[args['attr']]))
if args['show_sdict']:
statedict = item.sdict()
else:
statedict = item.cdict()
if statedict is None:
io.stdout("REMOVE")
else:
io.stdout(statedict_to_json(statedict, pretty=True))
if args['attr']:
io.stdout(repr(statedict[args['attr']]))
else:
io.stdout(statedict_to_json(statedict, pretty=True))
else:
for item in sorted(node.items):
if args['show_repr']:
Expand Down

0 comments on commit 0c39bc0

Please sign in to comment.