Skip to content

Commit

Permalink
add template_node node attribute
Browse files Browse the repository at this point in the history
closes #399
  • Loading branch information
trehn committed May 28, 2018
1 parent 4d96006 commit ba643f0
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 11 deletions.
19 changes: 19 additions & 0 deletions bundlewrap/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ def __init__(self, name, attributes=None):
self._node_metadata = attributes.get('metadata', {})
self._ssh_conn_established = False
self._ssh_first_conn_lock = Lock()
self._template_node_name = attributes.get('template_node')
self.hostname = attributes.get('hostname', name)
self.name = name

Expand Down Expand Up @@ -731,6 +732,20 @@ def log_function(msg):
wrapper_outer=self.cmd_wrapper_outer,
)

@property
def template_node(self):
if not self._template_node_name:
return None
else:
target_node = self.repo.get_node(self._template_node_name)
if target_node._template_node_name:
raise RepositoryError(_(
"{template_node} cannot use template_node because {node} uses {template_node} "
"as template_node"
).format(node=self.name, template_node=target_node.name))
else:
return target_node

def upload(self, local_path, remote_path, mode=None, owner="", group="", may_fail=False):
assert self.os in self.OS_FAMILY_UNIX
return operations.upload(
Expand Down Expand Up @@ -777,6 +792,10 @@ def method(self):
attr_source = "group:{}".format(group.name)
attr_value = getattr(group, attr)

if self.template_node:
attr_source = "template_node"
attr_value = getattr(self.template_node, attr)

if getattr(self, "_{}".format(attr)) is not None:
attr_source = "node"
attr_value = getattr(self, "_{}".format(attr))
Expand Down
25 changes: 14 additions & 11 deletions bundlewrap/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,18 +513,21 @@ def _build_node_metadata(self, blame=False):
# start messing with these objects in metadata processors. Every
# time we would edit one of these objects, the changes would be
# shared amongst multiple nodes.
new_metadata = deepcopy_metadata(merge_dict(
self._node_metadata_partial[node.name],
node._node_metadata,
))
if blame:
blame_changed_paths(
for source_node in (node.template_node, node):
if not source_node: # template_node might be None
continue
new_metadata = deepcopy_metadata(merge_dict(
self._node_metadata_partial[node.name],
new_metadata,
node_blame,
"node:{}".format(node.name),
)
self._node_metadata_partial[node.name] = new_metadata
source_node._node_metadata,
))
if blame:
blame_changed_paths(
self._node_metadata_partial[node.name],
new_metadata,
node_blame,
"node:{}".format(source_node.name),
)
self._node_metadata_partial[node.name] = new_metadata

# Now for the interesting part: We run all metadata processors
# until none of them return DONE anymore (indicating that they're
Expand Down
8 changes: 8 additions & 0 deletions docs/content/repo/nodes.py.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ Tuples of integers can be used for easy comparison of versions: `(12, 4) < (16,

<br>

### template_node

Copy all attributes and merge all metadata from this node. This is useful for temporary clones of single specific nodes, where you don't want to create a group to deduplicate all the node-level configuration.

Cannot be set at group level.

<br>

## OS compatibility overrides

### cmd_wrapper_outer
Expand Down
59 changes: 59 additions & 0 deletions tests/integration/bw_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,65 @@ def test_merge(tmpdir):
assert rcode == 0


def test_template_node(tmpdir):
make_repo(
tmpdir,
nodes={
"node1": {
'template_node': "node2",
},
"node2": {
'metadata': {
"foo": 2,
},
},
},
groups={
"group1": {
'members': ["node1"],
'metadata': {
"foo": 3,
},
},
},
)
stdout, stderr, rcode = run("bw metadata node1", path=str(tmpdir))
assert loads(stdout.decode()) == {"foo": 2}
assert stderr == b""
assert rcode == 0


def test_template_node_override(tmpdir):
make_repo(
tmpdir,
nodes={
"node1": {
'metadata': {
"foo": 1,
},
'template_node': "node2",
},
"node2": {
'metadata': {
"foo": 2,
},
},
},
groups={
"group1": {
'members': ["node1"],
'metadata': {
"foo": 3,
},
},
},
)
stdout, stderr, rcode = run("bw metadata node1", path=str(tmpdir))
assert loads(stdout.decode()) == {"foo": 1}
assert stderr == b""
assert rcode == 0


def test_metadatapy(tmpdir):
make_repo(
tmpdir,
Expand Down
26 changes: 26 additions & 0 deletions tests/integration/bw_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,29 @@ def test_bundles(tmpdir):
assert stdout.decode().strip().split("\n") == ["bundle1", "bundle2"]
assert stderr == b""
assert rcode == 0


def test_template_node(tmpdir):
make_repo(
tmpdir,
nodes={
"node1": {'template_node': "node2"},
"node2": {'dummy': True},
},
)
stdout, stderr, rcode = run("BW_TABLE_STYLE=grep bw nodes node1 dummy | grep node1 | cut -f 2", path=str(tmpdir))
assert stdout.decode().strip() == "True"
assert stderr == b""
assert rcode == 0


def test_template_node_cascade(tmpdir):
make_repo(
tmpdir,
nodes={
"node1": {'template_node': "node2"},
"node2": {'template_node': "node1"},
},
)
stdout, stderr, rcode = run("BW_TABLE_STYLE=grep bw nodes node1 dummy", path=str(tmpdir))
assert rcode == 1

0 comments on commit ba643f0

Please sign in to comment.