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

bump version 1.7 -> 1.8 for CKF release 1.8 #115

Merged
merged 10 commits into from
Sep 19, 2023
Merged
2 changes: 1 addition & 1 deletion .github/workflows/integrate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
# Requires the model to be called kubeflow due to this bug:
# https://github.com/kubeflow/kubeflow/issues/6136
juju add-model kubeflow
sg snap_microk8s -c "tox -e integration -- --model kubeflow"
sg snap_microk8s -c "tox -e integration"

- run: sg snap_microk8s -c "microk8s.kubectl get all -A"
if: failure()
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ build/
.tox/
__pycache__
*.charm
.vscode
4 changes: 4 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ options:
type: boolean
default: false
description: Whether cookies should require HTTPS
volume-viewer-image:
type: string
default: filebrowser/filebrowser:latest
description: Volume Viewer OCI Image (PVCViewer)
2 changes: 1 addition & 1 deletion metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ resources:
type: oci-image
description: 'Backing OCI image'
auto-fetch: true
upstream-source: docker.io/kubeflownotebookswg/volumes-web-app:v1.7.0
upstream-source: docker.io/kubeflownotebookswg/volumes-web-app:v1.8.0-rc.0
requires:
ingress:
interface: ingress
Expand Down
4 changes: 4 additions & 0 deletions requirements-unit.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ importlib-resources==6.0.1
# via jsonschema
iniconfig==2.0.0
# via pytest
jinja2==3.1.2
# via -r requirements.in
jsonschema==4.17.3
# via serialized-data-interface
markupsafe==2.1.3
# via jinja2
oci-image==1.0.0
# via -r requirements.in
ops==2.6.0
Expand Down
1 change: 1 addition & 0 deletions requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
ops
oci-image
serialized-data-interface
jinja2
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ idna==3.4
# via requests
importlib-resources==6.0.1
# via jsonschema
jinja2==3.1.2
# via -r requirements.in
jsonschema==4.17.3
# via serialized-data-interface
markupsafe==2.1.3
# via jinja2
oci-image==1.0.0
# via -r requirements.in
ops==2.6.0
Expand Down
52 changes: 52 additions & 0 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,41 @@
# See LICENSE file for licensing details.

import logging
from pathlib import Path
from typing import Dict

from charms.kubeflow_dashboard.v0.kubeflow_dashboard_links import (
DashboardLink,
KubeflowDashboardLinksRequirer,
)
from jinja2 import Template
from oci_image import OCIImageResource, OCIImageResourceError
from ops.charm import CharmBase
from ops.main import main
from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus, WaitingStatus
from serialized_data_interface import NoCompatibleVersions, NoVersionsListed, get_interfaces


def render_template(template_path: str, context: Dict) -> str:
"""
Render a Jinja2 template.

This function takes the file path of a Jinja2 template and a context dictionary
containing the variables for template rendering. It loads the template,
substitutes the variables in the context, and returns the rendered content.

Args:
template_path (str): The file path of the Jinja2 template.
context (Dict): A dictionary containing the variables for template rendering.

Returns:
str: The rendered template content.
"""
template = Template(Path(template_path).read_text())
rendered_template = template.render(**context)
return rendered_template


class CheckFailed(Exception):
"""Raise this exception if one of the checks in main fails."""

Expand Down Expand Up @@ -120,6 +143,11 @@ def main(self, event):
"resources": ["notebooks"],
"verbs": ["list"],
},
{
"apiGroups": ["kubeflow.org"],
"resources": ["pvcviewers"],
"verbs": ["get", "list", "create", "delete"],
},
],
}
]
Expand All @@ -134,11 +162,35 @@ def main(self, event):
"APP_SECURE_COOKIES": str(config["secure-cookies"]).lower(),
"BACKEND_MODE": config["backend-mode"],
"APP_PREFIX": "/volumes",
"VOLUME_VIEWER_IMAGE": config["volume-viewer-image"],
},
"ports": [{"name": "http", "containerPort": config["port"]}],
"volumeConfig": [
{
"name": "viewer-spec",
"mountPath": "/etc/config/", # xwris to .yaml?
"files": [
{
"path": "viewer-spec.yaml",
"content": render_template(
"src/templates/viewer-spec.yaml.j2", {}
),
}
],
},
],
}
],
},
k8s_resources={
"configMaps": {
"volumes-web-app-viewer-spec-ck6bhh4bdm": {
DnPlas marked this conversation as resolved.
Show resolved Hide resolved
"viewer-spec.yaml": render_template(
"src/templates/viewer-spec.yaml.j2", {}
),
},
},
},
)

self.model.unit.status = ActiveStatus()
Expand Down
38 changes: 38 additions & 0 deletions src/templates/viewer-spec.yaml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Source: manifests/apps/volumes-web-app/upstream/base/viewer-spec.yaml
# Note: the volumes-web-app allows expanding strings using ${VAR_NAME}
# You may use any environment variable. This lets us e.g. specify images that can be modified using kustomize's image transformer.
# Additionally, 'PVC_NAME', 'NAME' and 'NAMESPACE' are defined
DnPlas marked this conversation as resolved.
Show resolved Hide resolved
# Name of the pvc is set by the volumes web app
pvc: $NAME
podTemplate:
containers:
- name: main
image: $VOLUME_VIEWER_IMAGE
DnPlas marked this conversation as resolved.
Show resolved Hide resolved
env:
- name: FB_ADDRESS
value: "0.0.0.0"
- name: FB_PORT
value: "8080"
- name: FB_DATABASE
value: /tmp/filebrowser.db
- name: FB_NOAUTH
value: "true"
- name: FB_BASEURL
value: /pvcviewers/$NAMESPACE/$NAME/
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 2
periodSeconds: 10
# viewer-volume is provided automatically by the volumes web app
volumeMounts:
- name: viewer-volume
mountPath: /data
workingDir: /data
serviceAccountName: default-editor
networking:
targetPort: 8080
basePrefix: "/pvcviewers"
rewrite: "/"
timeout: 30s
rwoScheduling: true
32 changes: 26 additions & 6 deletions tests/integration/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import logging
from pathlib import Path

# from lightkube import Client
# from lightkube.resources.core_v1 import Service
import pytest
import yaml
from lightkube import Client
from lightkube.resources.core_v1 import ConfigMap
from pytest_operator.plugin import OpsTest

# from random import choices
Expand All @@ -24,26 +24,43 @@
log = logging.getLogger(__name__)

METADATA = yaml.safe_load(Path("./metadata.yaml").read_text())
KATIB_CONFIG = "volumes-web-app-viewer-spec-ck6bhh4bdm"
orfeas-k marked this conversation as resolved.
Show resolved Hide resolved
CHARM_NAME = METADATA["name"]
EXPECTED_CONFIG_MAP = {
DnPlas marked this conversation as resolved.
Show resolved Hide resolved
"viewer-spec.yaml": '# Source: manifests/apps/volumes-web-app/upstream/base/viewer-spec.yaml\n# Note: the volumes-web-app allows expanding strings using ${VAR_NAME}\n# You may use any environment variable. This lets us e.g. specify images that can be modified using kustomize\'s image transformer.\n# Additionally, \'PVC_NAME\', \'NAME\' and \'NAMESPACE\' are defined\n# Name of the pvc is set by the volumes web app\npvc: $NAME\npodTemplate:\n containers:\n - name: main\n image: $VOLUME_VIEWER_IMAGE\n env:\n - name: FB_ADDRESS\n value: "0.0.0.0"\n - name: FB_PORT\n value: "8080"\n - name: FB_DATABASE\n value: /tmp/filebrowser.db\n - name: FB_NOAUTH\n value: "true"\n - name: FB_BASEURL\n value: /pvcviewers/$NAMESPACE/$NAME/\n readinessProbe:\n tcpSocket:\n port: 8080\n initialDelaySeconds: 2\n periodSeconds: 10\n # viewer-volume is provided automatically by the volumes web app\n volumeMounts:\n - name: viewer-volume\n mountPath: /data\n workingDir: /data\n serviceAccountName: default-editor\nnetworking:\n targetPort: 8080\n basePrefix: "/pvcviewers"\n rewrite: "/"\n timeout: 30s\nrwoScheduling: true', # noqa: E501
}


@pytest.fixture(scope="session")
def lightkube_client() -> Client:
client = Client(field_manager=CHARM_NAME)
return client


@pytest.mark.abort_on_fail
async def test_build_and_deploy(ops_test: OpsTest):
charm_name = METADATA["name"]

my_charm = await ops_test.build_charm(".")
image_path = METADATA["resources"]["oci-image"]["upstream-source"]

await ops_test.model.deploy(my_charm, resources={"oci-image": image_path})

await ops_test.model.wait_for_idle(
[charm_name],
[CHARM_NAME],
wait_for_active=True,
raise_on_blocked=True,
raise_on_error=True,
timeout=300,
)


@pytest.mark.abort_on_fail
async def test_configmap_created(lightkube_client: Client, ops_test: OpsTest):
"""Test configmaps contents with default config."""
config_map = lightkube_client.get(ConfigMap, KATIB_CONFIG, namespace=ops_test.model_name)

assert config_map.data == EXPECTED_CONFIG_MAP


@pytest.mark.abort_on_fail
async def test_relate_dependencies(ops_test: OpsTest):
await ops_test.model.deploy(
Expand Down Expand Up @@ -74,8 +91,10 @@ async def test_relate_dependencies(ops_test: OpsTest):
await ops_test.model.add_relation("kubeflow-dashboard", "kubeflow-profiles")
await ops_test.model.add_relation("istio-pilot:ingress", "kubeflow-dashboard:ingress")
await ops_test.model.add_relation("istio-pilot", "kubeflow-volumes")
# raise_on_blocked=False to avoid flakiness due to kubeflow-dashboard going to
# Blocked((install) Add required relation to kubeflow-profiles) although it has been added
await ops_test.model.wait_for_idle(
raise_on_blocked=True,
raise_on_blocked=False,
raise_on_error=True,
timeout=300,
)
Expand All @@ -102,6 +121,7 @@ async def test_relate_dependencies(ops_test: OpsTest):


# Disabled until we re-enable the selenium tests below
# When reenabling, we should add Service to "from lightkube.resources.core_v1 import"
# @pytest.fixture()
# def driver(request, ops_test, profile):
# profile_name = profile
Expand Down
1 change: 1 addition & 0 deletions tools/get-images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
# dynamic list
IMAGE_LIST=()
IMAGE_LIST+=($(find -type f -name metadata.yaml -exec yq '.resources | to_entries | .[] | .value | ."upstream-source"' {} \;))
IMAGE_LIST+=($(find -type f -name config.yaml -exec yq '.options | ."volume-viewer-image" | .default' {} \;))
printf "%s\n" "${IMAGE_LIST[@]}"
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ deps =
description = Run unit tests

[testenv:integration]
commands = pytest -v --tb native --asyncio-mode=auto {[vars]tst_path}integration --log-cli-level=INFO -s {posargs}
commands = pytest -v --tb native --asyncio-mode=auto {[vars]tst_path}integration --log-cli-level=INFO -s {posargs} --model kubeflow
DnPlas marked this conversation as resolved.
Show resolved Hide resolved
deps =
-r requirements-integration.txt
description = Run integration tests