Skip to content

Commit cf04016

Browse files
[MISC] Restore Codecov and update libs (#342)
* Update codecov/codecov-action action to v4 * Try to revive code cov * Old codecov * Codecov configs * Bump libs * Fix unit test --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
1 parent 4a264f7 commit cf04016

File tree

7 files changed

+91
-59
lines changed

7 files changed

+91
-59
lines changed

.github/codecov.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github_checks:
2+
annotations: false
3+
coverage:
4+
status:
5+
project:
6+
default:
7+
target: 70%
8+
patch:
9+
default:
10+
target: 33%

lib/charms/data_platform_libs/v0/data_interfaces.py

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ def _on_topic_requested(self, event: TopicRequestedEvent):
320320

321321
# Increment this PATCH version before using `charmcraft publish-lib` or reset
322322
# to 0 if you are raising the major API version
323-
LIBPATCH = 26
323+
LIBPATCH = 27
324324

325325
PYDEPS = ["ops>=2.0.0"]
326326

@@ -422,15 +422,15 @@ def diff(event: RelationChangedEvent, bucket: Union[Unit, Application]) -> Diff:
422422
)
423423

424424
# These are the keys that were added to the databag and triggered this event.
425-
added = new_data.keys() - old_data.keys() # pyright: ignore [reportGeneralTypeIssues]
425+
added = new_data.keys() - old_data.keys() # pyright: ignore [reportAssignmentType]
426426
# These are the keys that were removed from the databag and triggered this event.
427-
deleted = old_data.keys() - new_data.keys() # pyright: ignore [reportGeneralTypeIssues]
427+
deleted = old_data.keys() - new_data.keys() # pyright: ignore [reportAssignmentType]
428428
# These are the keys that already existed in the databag,
429429
# but had their values changed.
430430
changed = {
431431
key
432-
for key in old_data.keys() & new_data.keys() # pyright: ignore [reportGeneralTypeIssues]
433-
if old_data[key] != new_data[key] # pyright: ignore [reportGeneralTypeIssues]
432+
for key in old_data.keys() & new_data.keys() # pyright: ignore [reportAssignmentType]
433+
if old_data[key] != new_data[key] # pyright: ignore [reportAssignmentType]
434434
}
435435
# Convert the new_data to a serializable format and save it for a next diff check.
436436
set_encoded_field(event.relation, bucket, "data", new_data)
@@ -1619,7 +1619,8 @@ def _delete_relation_data(self, relation: Relation, fields: List[str]) -> None:
16191619
current_data.get(relation.id, [])
16201620
):
16211621
logger.error(
1622-
"Non-existing secret %s was attempted to be removed.", non_existent
1622+
"Non-existing secret %s was attempted to be removed.",
1623+
", ".join(non_existent),
16231624
)
16241625

16251626
_, normal_fields = self._process_secret_fields(
@@ -1686,12 +1687,8 @@ def extra_user_roles(self) -> Optional[str]:
16861687
return self.relation.data[self.relation.app].get("extra-user-roles")
16871688

16881689

1689-
class AuthenticationEvent(RelationEvent):
1690-
"""Base class for authentication fields for events.
1691-
1692-
The amount of logic added here is not ideal -- but this was the only way to preserve
1693-
the interface when moving to Juju Secrets
1694-
"""
1690+
class RelationEventWithSecret(RelationEvent):
1691+
"""Base class for Relation Events that need to handle secrets."""
16951692

16961693
@property
16971694
def _secrets(self) -> dict:
@@ -1703,18 +1700,6 @@ def _secrets(self) -> dict:
17031700
self._cached_secrets = {}
17041701
return self._cached_secrets
17051702

1706-
@property
1707-
def _jujuversion(self) -> JujuVersion:
1708-
"""Caching jujuversion to avoid a Juju call on each field evaluation.
1709-
1710-
DON'T USE the encapsulated helper variable outside of this function
1711-
"""
1712-
if not hasattr(self, "_cached_jujuversion"):
1713-
self._cached_jujuversion = None
1714-
if not self._cached_jujuversion:
1715-
self._cached_jujuversion = JujuVersion.from_environ()
1716-
return self._cached_jujuversion
1717-
17181703
def _get_secret(self, group) -> Optional[Dict[str, str]]:
17191704
"""Retrieveing secrets."""
17201705
if not self.app:
@@ -1730,7 +1715,15 @@ def _get_secret(self, group) -> Optional[Dict[str, str]]:
17301715
@property
17311716
def secrets_enabled(self):
17321717
"""Is this Juju version allowing for Secrets usage?"""
1733-
return self._jujuversion.has_secrets
1718+
return JujuVersion.from_environ().has_secrets
1719+
1720+
1721+
class AuthenticationEvent(RelationEventWithSecret):
1722+
"""Base class for authentication fields for events.
1723+
1724+
The amount of logic added here is not ideal -- but this was the only way to preserve
1725+
the interface when moving to Juju Secrets
1726+
"""
17341727

17351728
@property
17361729
def username(self) -> Optional[str]:
@@ -1813,7 +1806,7 @@ class DatabaseProvidesEvents(CharmEvents):
18131806
database_requested = EventSource(DatabaseRequestedEvent)
18141807

18151808

1816-
class DatabaseRequiresEvent(RelationEvent):
1809+
class DatabaseRequiresEvent(RelationEventWithSecret):
18171810
"""Base class for database events."""
18181811

18191812
@property
@@ -1868,6 +1861,11 @@ def uris(self) -> Optional[str]:
18681861
if not self.relation.app:
18691862
return None
18701863

1864+
if self.secrets_enabled:
1865+
secret = self._get_secret("user")
1866+
if secret:
1867+
return secret.get("uris")
1868+
18711869
return self.relation.data[self.relation.app].get("uris")
18721870

18731871
@property
@@ -1911,7 +1909,7 @@ class DatabaseRequiresEvents(CharmEvents):
19111909
class DatabaseProvides(DataProvides):
19121910
"""Provider-side of the database relations."""
19131911

1914-
on = DatabaseProvidesEvents() # pyright: ignore [reportGeneralTypeIssues]
1912+
on = DatabaseProvidesEvents() # pyright: ignore [reportAssignmentType]
19151913

19161914
def __init__(self, charm: CharmBase, relation_name: str) -> None:
19171915
super().__init__(charm, relation_name)
@@ -2006,7 +2004,7 @@ def set_version(self, relation_id: int, version: str) -> None:
20062004
class DatabaseRequires(DataRequires):
20072005
"""Requires-side of the database relation."""
20082006

2009-
on = DatabaseRequiresEvents() # pyright: ignore [reportGeneralTypeIssues]
2007+
on = DatabaseRequiresEvents() # pyright: ignore [reportAssignmentType]
20102008

20112009
def __init__(
20122010
self,
@@ -2335,7 +2333,7 @@ class KafkaRequiresEvents(CharmEvents):
23352333
class KafkaProvides(DataProvides):
23362334
"""Provider-side of the Kafka relation."""
23372335

2338-
on = KafkaProvidesEvents() # pyright: ignore [reportGeneralTypeIssues]
2336+
on = KafkaProvidesEvents() # pyright: ignore [reportAssignmentType]
23392337

23402338
def __init__(self, charm: CharmBase, relation_name: str) -> None:
23412339
super().__init__(charm, relation_name)
@@ -2396,7 +2394,7 @@ def set_zookeeper_uris(self, relation_id: int, zookeeper_uris: str) -> None:
23962394
class KafkaRequires(DataRequires):
23972395
"""Requires-side of the Kafka relation."""
23982396

2399-
on = KafkaRequiresEvents() # pyright: ignore [reportGeneralTypeIssues]
2397+
on = KafkaRequiresEvents() # pyright: ignore [reportAssignmentType]
24002398

24012399
def __init__(
24022400
self,
@@ -2533,7 +2531,7 @@ class OpenSearchRequiresEvents(CharmEvents):
25332531
class OpenSearchProvides(DataProvides):
25342532
"""Provider-side of the OpenSearch relation."""
25352533

2536-
on = OpenSearchProvidesEvents() # pyright: ignore[reportGeneralTypeIssues]
2534+
on = OpenSearchProvidesEvents() # pyright: ignore[reportAssignmentType]
25372535

25382536
def __init__(self, charm: CharmBase, relation_name: str) -> None:
25392537
super().__init__(charm, relation_name)
@@ -2586,7 +2584,7 @@ def set_version(self, relation_id: int, version: str) -> None:
25862584
class OpenSearchRequires(DataRequires):
25872585
"""Requires-side of the OpenSearch relation."""
25882586

2589-
on = OpenSearchRequiresEvents() # pyright: ignore[reportGeneralTypeIssues]
2587+
on = OpenSearchRequiresEvents() # pyright: ignore[reportAssignmentType]
25902588

25912589
def __init__(
25922590
self,

lib/charms/postgresql_k8s/v0/postgresql.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@
1919
Any charm using this library should import the `psycopg2` or `psycopg2-binary` dependency.
2020
"""
2121
import logging
22+
from collections import OrderedDict
2223
from typing import Dict, List, Optional, Set, Tuple
2324

2425
import psycopg2
2526
from ops.model import Relation
2627
from psycopg2 import sql
2728
from psycopg2.sql import Composed
2829

29-
from collections import OrderedDict
30-
3130
# The unique Charmhub library identifier, never change it
3231
LIBID = "24ee217a54e840a598ff21a079c3e678"
3332

@@ -40,7 +39,6 @@
4039

4140
INVALID_EXTRA_USER_ROLE_BLOCKING_MESSAGE = "invalid role(s) for extra user roles"
4241

43-
4442
REQUIRED_PLUGINS = {
4543
"address_standardizer": ["postgis"],
4644
"address_standardizer_data_us": ["postgis"],
@@ -53,7 +51,6 @@
5351
for dependencies in REQUIRED_PLUGINS.values():
5452
DEPENDENCY_PLUGINS |= set(dependencies)
5553

56-
5754
logger = logging.getLogger(__name__)
5855

5956

@@ -304,18 +301,18 @@ def enable_disable_extensions(self, extensions: Dict[str, bool], database: str =
304301
cursor.execute("SELECT datname FROM pg_database WHERE NOT datistemplate;")
305302
databases = {database[0] for database in cursor.fetchall()}
306303

307-
orderedExtensions = OrderedDict()
304+
ordered_extensions = OrderedDict()
308305
for plugin in DEPENDENCY_PLUGINS:
309-
orderedExtensions[plugin] = extensions.get(plugin, False)
306+
ordered_extensions[plugin] = extensions.get(plugin, False)
310307
for extension, enable in extensions.items():
311-
orderedExtensions[extension] = enable
308+
ordered_extensions[extension] = enable
312309

313310
# Enable/disabled the extension in each database.
314311
for database in databases:
315312
with self._connect_to_database(
316313
database=database
317314
) as connection, connection.cursor() as cursor:
318-
for extension, enable in orderedExtensions.items():
315+
for extension, enable in ordered_extensions.items():
319316
cursor.execute(
320317
f"CREATE EXTENSION IF NOT EXISTS {extension};"
321318
if enable

lib/charms/rolling_ops/v0/rollingops.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def _on_trigger_restart(self, event):
7272
"""
7373
import logging
7474
from enum import Enum
75-
from typing import AnyStr, Callable
75+
from typing import AnyStr, Callable, Optional
7676

7777
from ops.charm import ActionEvent, CharmBase, RelationChangedEvent
7878
from ops.framework import EventBase, Object
@@ -88,7 +88,7 @@ def _on_trigger_restart(self, event):
8888

8989
# Increment this PATCH version before using `charmcraft publish-lib` or reset
9090
# to 0 if you are raising the major API version
91-
LIBPATCH = 2
91+
LIBPATCH = 5
9292

9393

9494
class LockNoRelationError(Exception):
@@ -261,7 +261,15 @@ class RunWithLock(EventBase):
261261
class AcquireLock(EventBase):
262262
"""Signals that this unit wants to acquire a lock."""
263263

264-
pass
264+
def __init__(self, handle, callback_override: Optional[str] = None):
265+
super().__init__(handle)
266+
self.callback_override = callback_override or ""
267+
268+
def snapshot(self):
269+
return {"callback_override": self.callback_override}
270+
271+
def restore(self, snapshot):
272+
self.callback_override = snapshot["callback_override"]
265273

266274

267275
class ProcessLocks(EventBase):
@@ -366,25 +374,42 @@ def _on_process_locks(self: CharmBase, event: ProcessLocks):
366374
self.charm.on[self.name].run_with_lock.emit()
367375
return
368376

369-
self.model.app.status = ActiveStatus()
377+
if self.model.app.status.message == f"Beginning rolling {self.name}":
378+
self.model.app.status = ActiveStatus()
370379

371380
def _on_acquire_lock(self: CharmBase, event: ActionEvent):
372381
"""Request a lock."""
373382
try:
374383
Lock(self).acquire() # Updates relation data
375384
# emit relation changed event in the edge case where aquire does not
376385
relation = self.model.get_relation(self.name)
377-
self.charm.on[self.name].relation_changed.emit(relation)
386+
387+
# persist callback override for eventual run
388+
relation.data[self.charm.unit].update({"callback_override": event.callback_override})
389+
self.charm.on[self.name].relation_changed.emit(relation, app=self.charm.app)
390+
378391
except LockNoRelationError:
379392
logger.debug("No {} peer relation yet. Delaying rolling op.".format(self.name))
380393
event.defer()
381394

382395
def _on_run_with_lock(self: CharmBase, event: RunWithLock):
383396
lock = Lock(self)
384397
self.model.unit.status = MaintenanceStatus("Executing {} operation".format(self.name))
385-
self._callback(event)
398+
relation = self.model.get_relation(self.name)
399+
400+
# default to instance callback if not set
401+
callback_name = relation.data[self.charm.unit].get(
402+
"callback_override", self._callback.__name__
403+
)
404+
callback = getattr(self.charm, callback_name)
405+
callback(event)
406+
386407
lock.release() # Updates relation data
387408
if lock.unit == self.model.unit:
388409
self.charm.on[self.name].process_locks.emit()
389410

390-
self.model.unit.status = ActiveStatus()
411+
# cleanup old callback overrides
412+
relation.data[self.charm.unit].update({"callback_override": ""})
413+
414+
if self.model.unit.status.message == f"Executing {self.name} operation":
415+
self.model.unit.status = ActiveStatus()

lib/charms/tls_certificates_interface/v2/tls_certificates.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,6 @@ def _on_all_certificates_invalidated(self, event: AllCertificatesInvalidatedEven
286286
from cryptography.hazmat.primitives import hashes, serialization
287287
from cryptography.hazmat.primitives.asymmetric import rsa
288288
from cryptography.hazmat.primitives.serialization import pkcs12
289-
from cryptography.x509.extensions import Extension, ExtensionNotFound
290289
from jsonschema import exceptions, validate # type: ignore[import-untyped]
291290
from ops.charm import (
292291
CharmBase,
@@ -308,7 +307,7 @@ def _on_all_certificates_invalidated(self, event: AllCertificatesInvalidatedEven
308307

309308
# Increment this PATCH version before using `charmcraft publish-lib` or reset
310309
# to 0 if you are raising the major API version
311-
LIBPATCH = 22
310+
LIBPATCH = 24
312311

313312
PYDEPS = ["cryptography", "jsonschema"]
314313

@@ -939,9 +938,11 @@ def generate_private_key(
939938
key_bytes = private_key.private_bytes(
940939
encoding=serialization.Encoding.PEM,
941940
format=serialization.PrivateFormat.TraditionalOpenSSL,
942-
encryption_algorithm=serialization.BestAvailableEncryption(password)
943-
if password
944-
else serialization.NoEncryption(),
941+
encryption_algorithm=(
942+
serialization.BestAvailableEncryption(password)
943+
if password
944+
else serialization.NoEncryption()
945+
),
945946
)
946947
return key_bytes
947948

@@ -1676,7 +1677,7 @@ def get_assigned_certificates(self) -> List[Dict[str, str]]:
16761677
"""
16771678
final_list = []
16781679
for csr in self.get_certificate_signing_requests(fulfilled_only=True):
1679-
assert type(csr["certificate_signing_request"]) == str
1680+
assert isinstance(csr["certificate_signing_request"], str)
16801681
if cert := self._find_certificate_in_relation_data(csr["certificate_signing_request"]):
16811682
final_list.append(cert)
16821683
return final_list
@@ -1699,7 +1700,7 @@ def get_expiring_certificates(self) -> List[Dict[str, str]]:
16991700
"""
17001701
final_list = []
17011702
for csr in self.get_certificate_signing_requests(fulfilled_only=True):
1702-
assert type(csr["certificate_signing_request"]) == str
1703+
assert isinstance(csr["certificate_signing_request"], str)
17031704
if cert := self._find_certificate_in_relation_data(csr["certificate_signing_request"]):
17041705
expiry_time = _get_certificate_expiry_time(cert["certificate"])
17051706
if not expiry_time:
@@ -1719,11 +1720,12 @@ def get_certificate_signing_requests(
17191720
"""Gets the list of CSR's that were sent to the provider.
17201721
17211722
You can choose to get only the CSR's that have a certificate assigned or only the CSR's
1722-
that don't.
1723+
that don't.
17231724
17241725
Args:
17251726
fulfilled_only (bool): This option will discard CSRs that don't have certificates yet.
17261727
unfulfilled_only (bool): This option will discard CSRs that have certificates signed.
1728+
17271729
Returns:
17281730
List of CSR dictionaries. For example:
17291731
[
@@ -1733,10 +1735,9 @@ def get_certificate_signing_requests(
17331735
}
17341736
]
17351737
"""
1736-
17371738
final_list = []
17381739
for csr in self._requirer_csrs:
1739-
assert type(csr["certificate_signing_request"]) == str
1740+
assert isinstance(csr["certificate_signing_request"], str)
17401741
cert = self._find_certificate_in_relation_data(csr["certificate_signing_request"])
17411742
if (unfulfilled_only and cert) or (fulfilled_only and not cert):
17421743
continue

0 commit comments

Comments
 (0)