Skip to content

Commit

Permalink
feat: add a dynamic, relation-driven sidebar (#118)
Browse files Browse the repository at this point in the history
Changes include:
* adding KubeflowDashboardSidebarRequirer
* adding KubeflowDashboardSidebarProvider
* adding SidebarItem
* adding associated tests, including a Requires side tester for the new relation
* adding the sidebar relation and using the KubeflowDashboardSidebarProvider to manage it
* refactoring computation of k8s context until it is needed, instead of in `__init__`, to make testing easier
  • Loading branch information
ca-scribner authored Jun 28, 2023
1 parent 3224ba1 commit 97d1929
Show file tree
Hide file tree
Showing 17 changed files with 989 additions and 177 deletions.
23 changes: 4 additions & 19 deletions .github/workflows/integrate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,29 +77,14 @@ jobs:
run: |
sg microk8s -c "tox -e integration -- --model kubeflow"
- name: Get all
run: kubectl get all -A
if: failure()

- name: Get configmaps
run: kubectl get cm -A
if: failure()

- name: Get juju status
run: juju status
if: failure()

- name: Get kubeflow-profiles workload logs
run: kubectl logs --tail 100 -nkubeflow -ljuju-app=kubeflow-dashboard
if: failure()

- name: Get kubeflow-kfam workload logs
run: kubectl logs --tail 100 -nkubeflow -ljuju-app=kubeflow-dashboard
if: failure()

- name: Get operator logs
run: kubectl logs --tail 100 -nkubeflow -loperator.juju.is/name
if: failure()
- uses: canonical/kubeflow-ci/actions/dump-charm-debug-artifacts@main
# always() if you want this to run on every run, regardless of failure.
# more details: https://docs.github.com/en/actions/learn-github-actions/expressions#status-check-functions
if: always()

- name: Upload selenium screenshots
uses: actions/upload-artifact@v3
Expand Down
86 changes: 86 additions & 0 deletions lib/charms/harness_extensions/v0/capture_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'''This is a library providing a utility for unittesting events fired on a
Harness-ed Charm.
Example usage:
>>> from charms.harness_extensions.v0.capture_events import capture
>>> with capture(RelationEvent) as captured:
>>> harness.add_relation('foo', 'remote')
>>> assert captured.event.unit.name == 'remote'
'''

# The unique Charmhub library identifier, never change it
LIBID = "9fcdab70e26d4eee9797c0e542ab397a"

# Increment this major API version when introducing breaking changes
LIBAPI = 0

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

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

from contextlib import contextmanager
from typing import Generic, Iterator, Optional, Type, TypeVar

from ops.charm import CharmBase
from ops.framework import EventBase

_T = TypeVar("_T", bound=EventBase)


@contextmanager
def capture_events(charm: CharmBase, *types: Type[EventBase]):
"""Capture all events of type `*types` (using instance checks)."""
allowed_types = types or (EventBase,)

captured = []
_real_emit = charm.framework._emit

def _wrapped_emit(evt):
if isinstance(evt, allowed_types):
captured.append(evt)
return _real_emit(evt)

charm.framework._emit = _wrapped_emit # type: ignore # noqa # ugly

yield captured

charm.framework._emit = _real_emit # type: ignore # noqa # ugly


class Captured(Generic[_T]):
"""Object to type and expose return value of capture()."""

_event = None

@property
def event(self) -> Optional[_T]:
"""Return the captured event."""
return self._event

@event.setter
def event(self, val: _T):
self._event = val


@contextmanager
def capture(charm: CharmBase, typ_: Type[_T] = EventBase) -> Iterator[Captured[_T]]:
"""Capture exactly 1 event of type `typ_`.
Will raise if more/less events have been fired, or if the returned event
does not pass an instance check.
"""
result = Captured()
with capture_events(charm, typ_) as captured:
if not captured:
yield result

assert len(captured) <= 1, f"too many events captured: {captured}"
assert len(captured) >= 1, f"no event of type {typ_} emitted."
event = captured[0]
assert isinstance(event, typ_), f"expected {typ_}, not {type(event)}"
result.event = event

Loading

0 comments on commit 97d1929

Please sign in to comment.