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

Minor changes required for new-style Charm Design usage #138

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
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
Loading