Skip to content

Commit

Permalink
Merge branch 'main' into chore/bump-to-3.1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
arturo-seijas authored Jan 11, 2024
2 parents 8d16363 + a33a9f1 commit 04f5684
Showing 1 changed file with 44 additions and 26 deletions.
70 changes: 44 additions & 26 deletions lib/charms/data_platform_libs/v0/data_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ def _on_topic_requested(self, event: TopicRequestedEvent):

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 25
LIBPATCH = 26

PYDEPS = ["ops>=2.0.0"]

Expand Down Expand Up @@ -477,12 +477,19 @@ class CachedSecret:
The data structure is precisely re-using/simulating as in the actual Secret Storage
"""

def __init__(self, charm: CharmBase, label: str, secret_uri: Optional[str] = None):
def __init__(
self,
charm: CharmBase,
component: Union[Application, Unit],
label: str,
secret_uri: Optional[str] = None,
):
self._secret_meta = None
self._secret_content = {}
self._secret_uri = secret_uri
self.label = label
self.charm = charm
self.component = component

def add_secret(self, content: Dict[str, str], relation: Relation) -> Secret:
"""Create a new secret."""
Expand All @@ -491,7 +498,7 @@ def add_secret(self, content: Dict[str, str], relation: Relation) -> Secret:
"Secret is already defined with uri %s", self._secret_uri
)

secret = self.charm.app.add_secret(content, label=self.label)
secret = self.component.add_secret(content, label=self.label)
if relation.app != self.charm.app:
# If it's not a peer relation, grant is to be applied
secret.grant(relation)
Expand Down Expand Up @@ -523,8 +530,13 @@ def get_content(self) -> Dict[str, str]:
except (ValueError, ModelError) as err:
# https://bugs.launchpad.net/juju/+bug/2042596
# Only triggered when 'refresh' is set
msg = "ERROR either URI or label should be used for getting an owned secret but not both"
if isinstance(err, ModelError) and msg not in str(err):
known_model_errors = [
"ERROR either URI or label should be used for getting an owned secret but not both",
"ERROR secret owner cannot use --refresh",
]
if isinstance(err, ModelError) and not any(
msg in str(err) for msg in known_model_errors
):
raise
# Due to: ValueError: Secret owner cannot use refresh=True
self._secret_content = self.meta.get_content()
Expand All @@ -550,14 +562,15 @@ def get_info(self) -> Optional[SecretInfo]:
class SecretCache:
"""A data structure storing CachedSecret objects."""

def __init__(self, charm):
def __init__(self, charm: CharmBase, component: Union[Application, Unit]):
self.charm = charm
self.component = component
self._secrets: Dict[str, CachedSecret] = {}

def get(self, label: str, uri: Optional[str] = None) -> Optional[CachedSecret]:
"""Getting a secret from Juju Secret store or cache."""
if not self._secrets.get(label):
secret = CachedSecret(self.charm, label, uri)
secret = CachedSecret(self.charm, self.component, label, uri)
if secret.meta:
self._secrets[label] = secret
return self._secrets.get(label)
Expand All @@ -567,7 +580,7 @@ def add(self, label: str, content: Dict[str, str], relation: Relation) -> Cached
if self._secrets.get(label):
raise SecretAlreadyExistsError(f"Secret {label} already exists")

secret = CachedSecret(self.charm, label)
secret = CachedSecret(self.charm, self.component, label)
secret.add_secret(content, relation)
self._secrets[label] = secret
return self._secrets[label]
Expand All @@ -579,6 +592,8 @@ def add(self, label: str, content: Dict[str, str], relation: Relation) -> Cached
class DataRelation(Object, ABC):
"""Base relation data mainpulation (abstract) class."""

SCOPE = Scope.APP

# Local map to associate mappings with secrets potentially as a group
SECRET_LABEL_MAP = {
"username": SecretGroup.USER,
Expand All @@ -599,8 +614,8 @@ def __init__(self, charm: CharmBase, relation_name: str) -> None:
self._on_relation_changed_event,
)
self._jujuversion = None
self.secrets = SecretCache(self.charm)
self.component = self.local_app
self.component = self.local_app if self.SCOPE == Scope.APP else self.local_unit
self.secrets = SecretCache(self.charm, self.component)

@property
def relations(self) -> List[Relation]:
Expand Down Expand Up @@ -808,7 +823,7 @@ def _process_secret_fields(
return (result, normal_fields)

def _fetch_relation_data_without_secrets(
self, app: Union[Application, Unit], relation: Relation, fields: Optional[List[str]]
self, component: Union[Application, Unit], relation: Relation, fields: Optional[List[str]]
) -> Dict[str, str]:
"""Fetching databag contents when no secrets are involved.
Expand All @@ -817,17 +832,19 @@ def _fetch_relation_data_without_secrets(
This is used typically when the Provides side wants to read the Requires side's data,
or when the Requires side may want to read its own data.
"""
if app not in relation.data or not relation.data[app]:
if component not in relation.data or not relation.data[component]:
return {}

if fields:
return {k: relation.data[app][k] for k in fields if k in relation.data[app]}
return {
k: relation.data[component][k] for k in fields if k in relation.data[component]
}
else:
return dict(relation.data[app])
return dict(relation.data[component])

def _fetch_relation_data_with_secrets(
self,
app: Union[Application, Unit],
component: Union[Application, Unit],
req_secret_fields: Optional[List[str]],
relation: Relation,
fields: Optional[List[str]] = None,
Expand All @@ -843,10 +860,10 @@ def _fetch_relation_data_with_secrets(
normal_fields = []

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

all_fields = list(relation.data[app].keys())
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
Expand All @@ -863,30 +880,30 @@ def _fetch_relation_data_with_secrets(
# (Typically when Juju3 Requires meets Juju2 Provides)
if normal_fields:
result.update(
self._fetch_relation_data_without_secrets(app, relation, list(normal_fields))
self._fetch_relation_data_without_secrets(component, relation, list(normal_fields))
)
return result

def _update_relation_data_without_secrets(
self, app: Union[Application, Unit], relation: Relation, data: Dict[str, str]
self, component: Union[Application, Unit], relation: Relation, data: Dict[str, str]
) -> None:
"""Updating databag contents when no secrets are involved."""
if app not in relation.data or relation.data[app] is None:
if component not in relation.data or relation.data[component] is None:
return

if relation:
relation.data[app].update(data)
relation.data[component].update(data)

def _delete_relation_data_without_secrets(
self, app: Union[Application, Unit], relation: Relation, fields: List[str]
self, component: Union[Application, Unit], relation: Relation, fields: List[str]
) -> None:
"""Remove databag fields 'fields' from Relation."""
if app not in relation.data or relation.data[app] is None:
if component not in relation.data or relation.data[component] is None:
return

for field in fields:
try:
relation.data[app].pop(field)
relation.data[component].pop(field)
except KeyError:
logger.error(
"Non-existing field '%s' was attempted to be removed from the databag (relation ID: %s)",
Expand Down Expand Up @@ -1311,7 +1328,7 @@ def _register_secret_to_relation(
label = self._generate_secret_label(relation_name, relation_id, group)

# Fetchin the Secret's meta information ensuring that it's locally getting registered with
CachedSecret(self.charm, label, secret_id).meta
CachedSecret(self.charm, self.component, label, secret_id).meta

def _register_secrets_to_relation(self, relation: Relation, params_name_list: List[str]):
"""Make sure that secrets of the provided list are locally 'registered' from the databag.
Expand Down Expand Up @@ -1648,9 +1665,10 @@ def fetch_relation_field(
class DataPeerUnit(DataPeer):
"""Unit databag representation."""

SCOPE = Scope.UNIT

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.component = self.local_unit


# General events
Expand Down

0 comments on commit 04f5684

Please sign in to comment.