Skip to content

Commit

Permalink
Peer relation for more than local.app, bugfix on delete
Browse files Browse the repository at this point in the history
  • Loading branch information
juditnovak committed Feb 6, 2024
1 parent 3ffd54a commit 19b660f
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 21 deletions.
69 changes: 54 additions & 15 deletions lib/charms/data_platform_libs/v0/data_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,18 @@ def get_info(self) -> Optional[SecretInfo]:
if self.meta:
return self.meta.get_info()

def remove(self) -> None:
"""Remove secret."""
if not self.meta:
raise SecretsUnavailableError("Non-existent secret was attempted to be removed.")
try:
self.meta.remove_all_revisions()
except SecretNotFoundError:
pass
self._secret_content = {}
self._secret_meta = None
self._secret_uri = None


class SecretCache:
"""A data structure storing CachedSecret objects."""
Expand Down Expand Up @@ -603,8 +615,12 @@ class DataRelation(Object, ABC):
"tls-ca": SecretGroup.TLS,
}

def __init__(self, charm: CharmBase, relation_name: str) -> None:
super().__init__(charm, relation_name)
def __init__(
self, charm: CharmBase, relation_name: str, unique_key: Optional[str] = None
) -> None:
if not unique_key:
unique_key = relation_name
super().__init__(charm, unique_key)
self.charm = charm
self.local_app = self.charm.model.app
self.local_unit = self.charm.unit
Expand Down Expand Up @@ -799,7 +815,7 @@ def _process_secret_fields(
# self.local_app is sufficient to check (ignored if Requires, never has secrets -- works if Provides)
fallback_to_databag = (
req_secret_fields
and self.local_unit.is_leader()
and (self.local_unit == self.charm.unit and self.local_unit.is_leader())
and set(req_secret_fields) & set(relation.data[self.component])
)

Expand Down Expand Up @@ -860,16 +876,19 @@ def _fetch_relation_data_with_secrets(
normal_fields = []

if not fields:
if component not in relation.data or not relation.data[component]:
if component not in relation.data:
return {}

all_fields = list(relation.data[component].keys())
normal_fields = [field for field in all_fields if not self._is_secret_field(field)]

# There must have been secrets there
if all_fields != normal_fields and req_secret_fields:
# So we assemble the full fields list (without 'secret-<X>' fields)
fields = normal_fields + req_secret_fields
#
# # There must have been secrets there
# if all_fields != normal_fields and req_secret_fields:
# # So we assemble the full fields list (without 'secret-<X>' fields)
# fields = normal_fields + req_secret_fields
fields = normal_fields
if req_secret_fields:
fields += req_secret_fields

if fields:
result, normal_fields = self._process_secret_fields(
Expand Down Expand Up @@ -1029,8 +1048,10 @@ def delete_relation_data(self, relation_id: int, fields: List[str]) -> None:
class DataProvides(DataRelation):
"""Base provides-side of the data products relation."""

def __init__(self, charm: CharmBase, relation_name: str) -> None:
super().__init__(charm, relation_name)
def __init__(
self, charm: CharmBase, relation_name: str, unique_key: Optional[str] = None
) -> None:
super().__init__(charm, relation_name, unique_key)

def _diff(self, event: RelationChangedEvent) -> Diff:
"""Retrieves the diff of the data in the relation changed databag.
Expand Down Expand Up @@ -1136,15 +1157,16 @@ def _delete_relation_secret(
)
return False

secret.set_content(new_content)

# Remove secret from the relation if it's fully gone
if not new_content:
field = self._generate_secret_field_name(group)
try:
relation.data[self.component].pop(field)
except KeyError:
pass
secret.remove()
else:
secret.set_content(new_content)

# Return the content that was removed
return True
Expand Down Expand Up @@ -1276,9 +1298,10 @@ def __init__(
relation_name: str,
extra_user_roles: Optional[str] = None,
additional_secret_fields: Optional[List[str]] = [],
unique_key: Optional[str] = None,
):
"""Manager of base client relations."""
super().__init__(charm, relation_name)
super().__init__(charm, relation_name, unique_key)
self.extra_user_roles = extra_user_roles
self._secret_fields = list(self.SECRET_FIELDS)
if additional_secret_fields:
Expand Down Expand Up @@ -1481,10 +1504,16 @@ def __init__(
additional_secret_fields: Optional[List[str]] = [],
secret_field_name: Optional[str] = None,
deleted_label: Optional[str] = None,
unique_key: Optional[str] = None,
):
"""Manager of base client relations."""
DataRequires.__init__(
self, charm, relation_name, extra_user_roles, additional_secret_fields
self,
charm,
relation_name,
extra_user_roles,
additional_secret_fields,
unique_key=unique_key,
)
self.secret_field_name = secret_field_name if secret_field_name else self.SECRET_FIELD_NAME
self.deleted_label = deleted_label
Expand Down Expand Up @@ -1672,6 +1701,16 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)


class DataPeerOtherUnit(DataPeerUnit):
"""Unit databag representation for another unit than the executor."""

def __init__(self, unit: Unit, relation_name: str, *args, **kwargs):
unique_key = f"{relation_name}-{unit.name}"
super().__init__(unique_key=unique_key, relation_name=relation_name, *args, **kwargs)
self.local_unit = unit
self.component = unit


# General events


Expand Down
1 change: 1 addition & 0 deletions lib/charms/data_platform_libs/v0/data_secrets.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Secrets related helper classes/functions."""

# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.

Expand Down
12 changes: 6 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ def juju_has_secrets(mocker: MockerFixture):
juju_version = version("juju")

if juju_version < "3":
mocker.patch.object(
JujuVersion, "has_secrets", new_callable=PropertyMock
).return_value = False
mocker.patch.object(JujuVersion, "has_secrets", new_callable=PropertyMock).return_value = (
False
)
return False
else:
mocker.patch.object(
JujuVersion, "has_secrets", new_callable=PropertyMock
).return_value = True
mocker.patch.object(JujuVersion, "has_secrets", new_callable=PropertyMock).return_value = (
True
)
return True


Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Secrets related helper classes/functions."""

# Copyright 2023 Canonical Ltd.
# See LICENSE file for licensing details.

Expand Down

0 comments on commit 19b660f

Please sign in to comment.