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

fix: Add Persistence Agent ClusterRole and Binding #324

Merged
merged 6 commits into from
Sep 21, 2023
Merged
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
4 changes: 3 additions & 1 deletion charms/kfp-persistence/requirements-unit.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ jinja2==3.1.2
jsonschema==4.17.3
# via serialized-data-interface
lightkube==0.14.0
# via charmed-kubeflow-chisme
# via
# -r requirements.in
# charmed-kubeflow-chisme
lightkube-models==1.27.1.4
# via lightkube
markupsafe==2.1.3
Expand Down
1 change: 1 addition & 0 deletions charms/kfp-persistence/requirements.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
charmed-kubeflow-chisme
lightkube
ops
serialized-data-interface
4 changes: 3 additions & 1 deletion charms/kfp-persistence/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ jinja2==3.1.2
jsonschema==4.17.3
# via serialized-data-interface
lightkube==0.14.0
# via charmed-kubeflow-chisme
# via
# -r requirements.in
# charmed-kubeflow-chisme
lightkube-models==1.27.1.4
# via lightkube
markupsafe==2.1.3
Expand Down
24 changes: 23 additions & 1 deletion charms/kfp-persistence/src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@

import logging

import lightkube
from charmed_kubeflow_chisme.components.charm_reconciler import CharmReconciler
from charmed_kubeflow_chisme.components.kubernetes_component import KubernetesComponent
from charmed_kubeflow_chisme.components.leadership_gate_component import LeadershipGateComponent
from charmed_kubeflow_chisme.components.serialised_data_interface_components import (
SdiRelationDataReceiverComponent,
)
from charmed_kubeflow_chisme.kubernetes import create_charm_default_labels
from lightkube.resources.core_v1 import ServiceAccount
from lightkube.resources.rbac_authorization_v1 import ClusterRole, ClusterRoleBinding
from ops import CharmBase, main

from components.pebble_components import (
Expand All @@ -23,6 +28,8 @@

log = logging.getLogger()

K8S_RESOURCE_FILES = ["src/templates/auth_manifests.yaml.j2"]


class KfpPersistenceOperator(CharmBase):
"""Charm for the data persistence application of Kubeflow Pipelines."""
Expand All @@ -46,6 +53,21 @@ def __init__(self, *args, **kwargs):
depends_on=[self.leadership_gate],
)

self.kubernetes_resources = self.charm_reconciler.add(
component=KubernetesComponent(
charm=self,
name="kubernetes:auth",
resource_templates=K8S_RESOURCE_FILES,
krh_resource_types={ClusterRole, ClusterRoleBinding, ServiceAccount},
krh_labels=create_charm_default_labels(
self.app.name, self.model.name, scope="auth"
),
context_callable=lambda: {"app_name": self.app.name, "namespace": self.model.name},
lightkube_client=lightkube.Client(),
),
depends_on=[self.leadership_gate],
)

self.persistenceagent_container = self.charm_reconciler.add(
component=PersistenceAgentPebbleService(
charm=self,
Expand All @@ -67,7 +89,7 @@ def __init__(self, *args, **kwargs):
],
),
),
depends_on=[self.leadership_gate, self.kfp_api_relation],
depends_on=[self.leadership_gate, self.kfp_api_relation, self.kubernetes_resources],
)

self.charm_reconciler.install_default_event_handlers()
Expand Down
58 changes: 58 additions & 0 deletions charms/kfp-persistence/src/templates/auth_manifests.yaml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Source manifests/apps/pipeline/upstream/base/installs/multi-user/persistence-agent/
# These manifest files have been modified to suit the needs of the charm; the app label, metadata name,
# and namespace fields will be rendered with information from the application and the model.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app: {{ app_name }}
name: {{ app_name }}-role
rules:
- apiGroups:
- argoproj.io
resources:
- workflows
verbs:
- get
- list
- watch
- apiGroups:
- kubeflow.org
resources:
- scheduledworkflows
verbs:
- get
- list
- watch
- apiGroups:
- pipelines.kubeflow.org
resources:
- scheduledworkflows
- workflows
verbs:
- report
- apiGroups:
- ''
resources:
- namespaces
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ app_name }}-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ app_name }}-role
subjects:
- kind: ServiceAccount
name: {{ app_name }}-sa
namespace: {{ namespace }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ app_name }}-sa
namespace: {{ namespace }}
20 changes: 13 additions & 7 deletions charms/kfp-persistence/tests/unit/test_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@ def harness():
return harness


def test_not_leader(
harness, # noqa F811
):
@pytest.fixture()
DnPlas marked this conversation as resolved.
Show resolved Hide resolved
def mocked_lightkube_client(mocker):
"""Mocks the Lightkube Client in charm.py, returning a mock instead."""
mocked_lightkube_client = MagicMock()
mocker.patch("charm.lightkube.Client", return_value=mocked_lightkube_client)
yield mocked_lightkube_client


def test_not_leader(harness, mocked_lightkube_client):
"""Test not a leader scenario."""
harness.begin_with_initial_hooks()
assert harness.charm.model.unit.status == WaitingStatus(
Expand All @@ -35,7 +41,7 @@ def test_not_leader(
OTHER_APP_NAME = "kfp-api-provider"


def test_kfp_api_relation_with_data(harness):
def test_kfp_api_relation_with_data(harness, mocked_lightkube_client):
"""Test that if Leadership is Active, the kfp-api relation operates as expected."""
# Arrange
harness.begin()
Expand All @@ -51,7 +57,7 @@ def test_kfp_api_relation_with_data(harness):
assert isinstance(harness.charm.kfp_api_relation.status, ActiveStatus)


def test_kfp_api_relation_without_data(harness):
def test_kfp_api_relation_without_data(harness, mocked_lightkube_client):
"""Test that the kfp-api relation goes Blocked if no data is available."""
# Arrange
harness.begin()
Expand All @@ -67,7 +73,7 @@ def test_kfp_api_relation_without_data(harness):
assert isinstance(harness.charm.kfp_api_relation.status, BlockedStatus)


def test_kfp_api_relation_without_relation(harness):
def test_kfp_api_relation_without_relation(harness, mocked_lightkube_client):
"""Test that the kfp-api relation goes Blocked if no relation is established."""
# Arrange
harness.begin()
Expand All @@ -83,7 +89,7 @@ def test_kfp_api_relation_without_relation(harness):
assert isinstance(harness.charm.kfp_api_relation.status, BlockedStatus)


def test_pebble_services_running(harness):
def test_pebble_services_running(harness, mocked_lightkube_client):
"""Test that if the Kubernetes Component is Active, the pebble services successfully start."""
# Arrange
harness.begin()
Expand Down