Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the charm to set extra-labels it wishes to manage not set by user #13

Merged
merged 1 commit into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions ops/charms/node_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __init__(
kubectl: Optional[PathLike] = "/snap/bin/kubectl",
user_label_key: str = "labels",
timeout: Optional[PositiveInt] = None,
raise_invalid_label: bool = False,
) -> None:
"""Initialize the LabelMaker.

Expand All @@ -71,6 +72,7 @@ def __init__(
kubectl (Optional[PathLike], optional): Path to the kubectl binary. Defaults to "/snap/bin/kubectl".
user_label_key (str, optional): The key in the charm config where the user labels are stored. Defaults to "labels".
timeout (Optional[PositiveInt], optional): Number of seconds to retry a command. Defaults to None.
raise_invalid_label (bool, optional): Whether to raise an exception when an invalid label is found. Defaults to False.
"""
super().__init__(parent=charm, key="NodeBase")
self.charm = charm
Expand All @@ -79,6 +81,7 @@ def __init__(
self.user_labels_key = user_label_key
self.timeout = DEFAULT_TIMEOUT if timeout is None else timeout
self._stored.set_default(current_labels=dict())
self._raise_invalid_label = raise_invalid_label

def _retried_call(
self, cmd: List[str], retry_msg: str, timeout: Optional[int] = None
Expand Down Expand Up @@ -185,7 +188,9 @@ def user_labels(self) -> Mapping[str, str]:
try:
key, val = item.split("=")
except ValueError:
log.info(f"Skipping malformed option: {item}.")
if self._raise_invalid_label:
raise self.NodeLabelError(f"Malformed label: {item}.")
log.error(f"Skipping Malformed label: {item}.")
else:
user_labels[key] = val
return user_labels
Expand All @@ -210,8 +215,12 @@ def apply_node_labels(self) -> None:

# Add any new labels.
for key, val in user_labels.items():
self.set_label(key, val)
self._stored.current_labels[key] = val
if val.endswith("-"):
# Remove the label if the value ends with a dash.
self.remove_label(key)
else:
self.set_label(key, val)
self._stored.current_labels[key] = val

# Set the juju-application and juju-charm labels.
self.set_label("juju-application", self.charm.model.app.name)
Expand Down
22 changes: 18 additions & 4 deletions ops/tests/unit/test_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,19 @@ def test_active_labels_apply_layers_from_config(
subprocess_run, harness, label_maker, caplog
):
harness.update_config(
{"my-labels": "node-role.kubernetes.io/control-plane= invalid"}
{
"my-labels": "node-role.kubernetes.io/control-plane= invalid extra-label.removable-"
}
)
subprocess_run.return_value = RunResponse(0)
label_maker._stored.current_labels = {"node-role.kubernetes.io/worker": ""}
label_maker._stored.current_labels = {
"node-role.kubernetes.io/worker": "",
"extra-label.removable": "",
}
label_maker.apply_node_labels()
assert "Skipping malformed option: invalid." in caplog.messages
assert "Skipping Malformed label: invalid." in caplog.messages
assert label_maker._stored.current_labels == {
"node-role.kubernetes.io/control-plane": ""
"node-role.kubernetes.io/control-plane": "",
}
subprocess_run.assert_has_calls(
[
Expand All @@ -151,10 +156,19 @@ def test_active_labels_apply_layers_from_config(
)
for label_args in [
("node-role.kubernetes.io/worker-",),
("extra-label.removable-",),
("node-role.kubernetes.io/control-plane=", "--overwrite"),
("juju-application=test-charm", "--overwrite"),
("juju-charm=test-charm", "--overwrite"),
("juju.io/cloud-",),
]
],
)


def test_raise_invalid_label(subprocess_run, harness, label_maker):
harness.update_config({"my-labels": "this=isn't=valid"})
subprocess_run.return_value = RunResponse(0)
label_maker._raise_invalid_label = True
with pytest.raises(node_base.LabelMaker.NodeLabelError):
label_maker.apply_node_labels()