Skip to content

Commit

Permalink
Merge pull request #62 from canonical/IAM-664-add-integration-tests
Browse files Browse the repository at this point in the history
Add integration tests for `auth-proxy`, `forward-auth` relations and app scaling
  • Loading branch information
natalian98 authored Feb 23, 2024
2 parents c9626b1 + 55cb580 commit 10ceff5
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 3 deletions.
1 change: 1 addition & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
channel: 1.28-strict/stable
juju-channel: 3.1
bootstrap-options: '--agent-version=3.1.0'
microk8s-addons: "dns hostpath-storage metallb:10.64.140.43-10.64.140.49"

- name: Run integration tests
# set a predictable model name so it can be consumed by charm-logdump-action
Expand Down
5 changes: 4 additions & 1 deletion src/config_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ def pop(self, keys: List[str]):
return

for key in keys:
cm.data.pop(key)
try:
cm.data.pop(key)
except KeyError:
logger.info(f"{key} not found in the ConfigMap")

self._client.replace(cm)

Expand Down
17 changes: 17 additions & 0 deletions tests/integration/auth-proxy-requirer/charmcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright 2024 Canonical Ltd.
# See LICENSE file for licensing details.
type: charm
bases:
- build-on:
- name: "ubuntu"
channel: "22.04"
run-on:
- name: "ubuntu"
channel: "22.04"
parts:
charm:
charm-binary-python-packages:
- jsonschema
- ops
build-packages:
- git
22 changes: 22 additions & 0 deletions tests/integration/auth-proxy-requirer/metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2024 Canonical Ltd.
# See LICENSE file for licensing details.
name: auth-proxy-requirer
display-name: auth-proxy-requirer
description: Auth Proxy Requirer Tester
summary: Auth Proxy Requirer Tester
assumes:
- k8s-api
containers:
httpbin:
resource: oci-image
resources:
oci-image:
type: oci-image
description: OCI image for IAP Tester container
upstream-source: kennethreitz/httpbin
requires:
auth-proxy:
interface: auth_proxy
limit: 1
ingress:
interface: ingress
2 changes: 2 additions & 0 deletions tests/integration/auth-proxy-requirer/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
jsonschema
ops
82 changes: 82 additions & 0 deletions tests/integration/auth-proxy-requirer/src/charm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
# Copyright 2024 Canonical Ltd.
# See LICENSE file for licensing details.
import logging

from charms.oathkeeper.v0.auth_proxy import AuthProxyConfig, AuthProxyRequirer
from charms.traefik_k8s.v2.ingress import IngressPerAppReadyEvent, IngressPerAppRequirer
from ops.charm import CharmBase, PebbleReadyEvent
from ops.main import main
from ops.model import ActiveStatus
from ops.pebble import Layer

AUTH_PROXY_ALLOWED_ENDPOINTS = ["anything/allowed"]
AUTH_PROXY_HEADERS = ["X-User"]
HTTPBIN_PORT = 80

logger = logging.getLogger(__name__)


class AuthProxyRequirerMock(CharmBase):
def __init__(self, framework):
super().__init__(framework)
self._container = self.unit.get_container("httpbin")
self._service_name = "httpbin"

self.ingress = IngressPerAppRequirer(
self,
host=f"{self.app.name}.{self.model.name}.svc.cluster.local",
relation_name="ingress",
port=HTTPBIN_PORT,
strip_prefix=True,
)

self.auth_proxy_relation_name = "auth-proxy"
self.auth_proxy = AuthProxyRequirer(
self, self._auth_proxy_config, self.auth_proxy_relation_name
)

self.framework.observe(self.on.httpbin_pebble_ready, self._on_httpbin_pebble_ready)
self.framework.observe(self.ingress.on.ready, self._on_ingress_ready)

@property
def _auth_proxy_config(self) -> AuthProxyConfig:
return AuthProxyConfig(
protected_urls=[
self.ingress.url if self.ingress.url is not None else "https://some-test-url.com"
],
headers=AUTH_PROXY_HEADERS,
allowed_endpoints=AUTH_PROXY_ALLOWED_ENDPOINTS,
)

@property
def _httpbin_pebble_layer(self) -> Layer:
layer_config = {
"summary": "auth-proxy-requirer-mock layer",
"description": "pebble config layer for auth-proxy-requirer-mock",
"services": {
self._service_name: {
"override": "replace",
"summary": "httpbin layer",
"command": "gunicorn -b 0.0.0.0:80 httpbin:app -k gevent",
"startup": "enabled",
}
},
}
return Layer(layer_config)

def _on_httpbin_pebble_ready(self, event: PebbleReadyEvent) -> None:
self._container.add_layer("httpbin", self._httpbin_pebble_layer, combine=True)
self._container.replan()
self.unit.open_port(protocol="tcp", port=HTTPBIN_PORT)

self.unit.status = ActiveStatus()

def _on_ingress_ready(self, event: IngressPerAppReadyEvent) -> None:
if self.unit.is_leader():
logger.info(f"This app's ingress URL: {event.url}")
self.auth_proxy.update_auth_proxy_config(auth_proxy_config=self._auth_proxy_config)


if __name__ == "__main__":
main(AuthProxyRequirerMock)
23 changes: 23 additions & 0 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,36 @@
# See LICENSE file for licensing details.

import os
import shutil

import pytest
from lightkube import Client, KubeConfig
from pytest_operator.plugin import OpsTest

KUBECONFIG = os.environ.get("TESTING_KUBECONFIG", "~/.kube/config")
AUTH_PROXY_REQUIRER = "auth-proxy-requirer"


@pytest.fixture(scope="module")
def client() -> Client:
return Client(config=KubeConfig.from_file(KUBECONFIG))


@pytest.fixture(scope="module")
def lightkube_client(ops_test: OpsTest) -> Client:
lightkube_client = Client(field_manager="oathkeeper", namespace=ops_test.model.name)
return lightkube_client


@pytest.fixture(scope="module")
def copy_libraries_into_tester_charm() -> None:
"""Ensure the tester charm has the required libraries."""
libraries = [
"traefik_k8s/v2/ingress.py",
"oathkeeper/v0/auth_proxy.py",
]

for lib in libraries:
install_path = f"tests/integration/{AUTH_PROXY_REQUIRER}/lib/charms/{lib}"
os.makedirs(os.path.dirname(install_path), exist_ok=True)
shutil.copyfile(f"lib/charms/{lib}", install_path)
Loading

0 comments on commit 10ceff5

Please sign in to comment.