Skip to content

Commit

Permalink
Merge pull request #82 from canonical/fix-secret-id-canonical
Browse files Browse the repository at this point in the history
fix secret id canonicalization
  • Loading branch information
PietroPasotti authored Nov 17, 2023
2 parents f5ac9c8 + e0019f9 commit 31ab9dd
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 7 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "ops-scenario"

version = "5.6.1"
version = "5.6.2"

authors = [
{ name = "Pietro Pasotti", email = "pietro.pasotti@canonical.com" }
Expand Down
18 changes: 12 additions & 6 deletions scenario/mocking.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Set, Tuple, Union

from ops import JujuVersion, pebble
from ops.model import ModelError, RelationNotFoundError
from ops.model import Secret as Secret_Ops # lol
from ops.model import (
ModelError,
RelationNotFoundError,
SecretInfo,
SecretNotFoundError,
SecretRotate,
Expand Down Expand Up @@ -138,13 +138,19 @@ def _get_relation_by_id(
raise RelationNotFoundError()

def _get_secret(self, id=None, label=None):
# cleanup id:
if id and id.startswith("secret:"):
id = id[7:]
canonicalize_id = Secret_Ops._canonicalize_id

if id:
# in scenario, you can create Secret(id="foo"),
# but ops.Secret will prepend a "secret:" prefix to that ID.
# we allow getting secret by either version.
try:
return next(filter(lambda s: s.id == id, self._state.secrets))
return next(
filter(
lambda s: canonicalize_id(s.id) == canonicalize_id(id),
self._state.secrets,
),
)
except StopIteration:
raise SecretNotFoundError()
elif label:
Expand Down
4 changes: 4 additions & 0 deletions scenario/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ def copy(self) -> "Self":
@dataclasses.dataclass(frozen=True)
class Secret(_DCBase):
id: str
# CAUTION: ops-created Secrets (via .add_secret()) will have a canonicalized
# secret id (`secret:` prefix)
# but user-created ones will not. Using post-init to patch it in feels bad, but requiring the user to
# add the prefix manually every time seems painful as well.

# mapping from revision IDs to each revision's contents
contents: Dict[int, "RawSecretRevisionContents"]
Expand Down
19 changes: 19 additions & 0 deletions tests/test_e2e/test_secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from ops.framework import Framework
from ops.model import SecretNotFoundError, SecretRotate

from scenario import Context
from scenario.state import Relation, Secret, State
from tests.helpers import trigger

Expand Down Expand Up @@ -278,3 +279,21 @@ def post_event(charm: CharmBase):
meta={"name": "local", "requires": {"foo": {"interface": "bar"}}},
post_event=post_event,
)


class GrantingCharm(CharmBase):
def __init__(self, *args):
super().__init__(*args)
self.framework.observe(self.on.start, self._on_start)

def _on_start(self, _):
secret = self.app.add_secret({"foo": "bar"})
secret.grant(self.model.relations["bar"][0])


def test_grant_after_add():
context = Context(
GrantingCharm, meta={"name": "foo", "provides": {"bar": {"interface": "bar"}}}
)
state = State(relations=[Relation("bar")])
context.run("start", state)

0 comments on commit 31ab9dd

Please sign in to comment.