Skip to content

Commit

Permalink
Support kube-control v2 schema (#5)
Browse files Browse the repository at this point in the history
* Support kube-control v2 schema

* Fetch kubeconfig via action for integration tests

* Update requirements.txt
  • Loading branch information
addyess authored Nov 20, 2024
1 parent 084b17c commit bc71c22
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 25 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ on:
jobs:
call-inclusive-naming-check:
name: Inclusive Naming
uses: canonical-web-and-design/Inclusive-naming/.github/workflows/woke.yaml@main
uses: canonical/inclusive-naming/.github/workflows/woke.yaml@main
with:
fail-on-error: "true"

lint-unit:
name: Lint Unit
uses: charmed-kubernetes/workflows/.github/workflows/lint-unit.yaml@main
with:
python: "['3.8', '3.9', '3.10', '3.11']"
python: "['3.8', '3.10', '3.12']"
needs:
- call-inclusive-naming-check

Expand Down
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
backports.cached-property
ops>=1.3.0,<2.0.0
ops
lightkube>=0.10.1,<1.0.0
pyyaml
pydantic==1.*
ops.manifest>=1.1.0,<2.0.0
git+https://github.com/charmed-kubernetes/interface-kube-control.git@6dd289d1c795fdeda1bed17873b8d6562227c829#subdirectory=ops
git+https://github.com/charmed-kubernetes/interface-tls-certificates.git@339efe3823b9728d16cdf5bcd1fc3b5de4e68923#subdirectory=ops
ops.interface-kube-control @ git+https://github.com/charmed-kubernetes/interface-kube-control.git@edc07bce7ea4c25d472fa4d95834602a7ebce5cd#subdirectory=ops
ops.interface-tls-certificates @ git+https://github.com/charmed-kubernetes/interface-tls-certificates.git@4a1081da098154b96337a09c8e9c40acff2d330e#subdirectory=ops
14 changes: 9 additions & 5 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
import logging
from pathlib import Path

import ops
from ops.charm import CharmBase
from ops.framework import StoredState
from ops.interface_kube_control import KubeControlRequirer
from ops.interface_tls_certificates import CertificatesRequires
from ops.main import main
from ops.manifests import Collector, ManifestClientError
from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus, WaitingStatus

Expand All @@ -31,7 +31,7 @@ def __init__(self, *args):
super().__init__(*args)

# Relation Validator and datastore
self.kube_control = KubeControlRequirer(self)
self.kube_control = KubeControlRequirer(self, schemas="0,1")
self.certificates = CertificatesRequires(self)
# Config Validator and datastore
self.charm_config = CharmConfig(self)
Expand Down Expand Up @@ -113,7 +113,7 @@ def _update_status(self, _):
self.app.status = ActiveStatus(self.collector.long_version)

def _kube_control(self, event):
self.kube_control.set_auth_request(self.unit.name)
self.kube_control.set_auth_request(self.unit.name, "system:masters")
return self._merge_config(event)

def _check_kube_control(self, event):
Expand All @@ -137,6 +137,10 @@ def _check_kube_control(self, event):
return True

def _check_certificates(self, event):
if self.kube_control.get_ca_certificate():
log.info("CA Certificate is available from kube-control.")
return True

self.unit.status = MaintenanceStatus("Evaluating certificates.")
evaluation = self.certificates.evaluate_relation(event)
if evaluation:
Expand Down Expand Up @@ -192,7 +196,7 @@ def _install_or_upgrade(self, event, config_hash=None):
controller.apply_manifests()
except ManifestClientError as e:
self.unit.status = WaitingStatus("Waiting for kube-apiserver")
log.warn(f"Encountered retryable installation error: {e}")
log.warning("Encountered retryable installation error: %s", e)
event.defer()
return False
return True
Expand All @@ -211,4 +215,4 @@ def _cleanup(self, event):


if __name__ == "__main__":
main(AwsCloudProviderCharm)
ops.main(AwsCloudProviderCharm)
46 changes: 34 additions & 12 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright 2022 Canonical Ltd.
# See LICENSE file for licensing details.
import asyncio
import logging
import random
import string
Expand All @@ -9,6 +10,7 @@
from lightkube import AsyncClient, KubeConfig
from lightkube.models.meta_v1 import ObjectMeta
from lightkube.resources.core_v1 import Namespace
from pytest_operator.plugin import OpsTest

log = logging.getLogger(__name__)

Expand All @@ -18,20 +20,40 @@ def module_name(request):
return request.module.__name__.replace("_", "-")


async def get_leader(app):
"""Find leader unit of an application.
Args:
app: Juju application
Returns:
int: index to leader unit
"""
is_leader = await asyncio.gather(*(u.is_leader_from_status() for u in app.units))
for idx, flag in enumerate(is_leader):
if flag:
return idx


@pytest.fixture()
async def kubeconfig(ops_test):
async def kubeconfig(ops_test: OpsTest):
for choice in ["kubernetes-control-plane", "k8s"]:
if app := ops_test.model.applications.get(choice):
break
else:
pytest.fail("No kubernetes-control-plane or k8s application found")
leader_idx = await get_leader(app)
leader = app.units[leader_idx]

kubeconfig_path = ops_test.tmp_path / "kubeconfig"
retcode, stdout, stderr = await ops_test.run(
"juju",
"scp",
"kubernetes-control-plane/leader:/home/ubuntu/config",
kubeconfig_path,
)
action = await leader.run_action("get-kubeconfig")
data = await action.wait()
retcode, kubeconfig = (data.results.get(key, {}) for key in ["return-code", "kubeconfig"])

if retcode != 0:
log.error(f"retcode: {retcode}")
log.error(f"stdout:\n{stdout.strip()}")
log.error(f"stderr:\n{stderr.strip()}")
pytest.fail("Failed to copy kubeconfig from kubernetes-control-plane")
log.error("Failed to copy kubeconfig from %s (%s)", app.name, data.results)
pytest.fail(f"Failed to copy kubeconfig from {app.name}")
kubeconfig_path.write_text(kubeconfig)
assert Path(kubeconfig_path).stat().st_size, "kubeconfig file is 0 bytes"
yield kubeconfig_path

Expand All @@ -42,7 +64,7 @@ async def kubernetes(kubeconfig, module_name):
namespace = f"{module_name}-{rand_str}"
config = KubeConfig.from_file(kubeconfig)
client = AsyncClient(
config=config.get(context_name="juju-context"),
config=config.get(context_name=config.current_context),
namespace=namespace,
trust_env=False,
)
Expand Down
13 changes: 11 additions & 2 deletions tests/unit/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,20 @@ def kube_control():
kube_control.get_controller_labels.return_value = []
kube_control.get_cluster_tag.return_value = "kubernetes-4ypskxahbu3rnfgsds3pksvwe3uh0lxt"
kube_control.get_cluster_cidr.return_value = ip_network("192.168.0.0/16")
kube_control.get_ca_certificate.return_value = None
kube_control.relation.app.name = "kubernetes-control-plane"
kube_control.relation.units = [f"kubernetes-control-plane/{_}" for _ in range(2)]
yield kube_control


def test_waits_for_relations(harness):
harness.begin_with_initial_hooks()
charm = harness.charm
assert isinstance(charm.unit.status, BlockedStatus)
assert charm.unit.status.message == "Missing required certificates"


@pytest.mark.usefixtures("kube_control")
def test_waits_for_certificates(harness):
harness.begin_with_initial_hooks()
charm = harness.charm
Expand All @@ -80,8 +89,8 @@ def test_waits_for_certificates(harness):
"easyrsa/0",
yaml.safe_load(Path("tests/data/certificates_data.yaml").read_text()),
)
assert isinstance(charm.unit.status, BlockedStatus)
assert charm.unit.status.message == "Missing required kube-control relation"
assert isinstance(charm.unit.status, MaintenanceStatus)
assert charm.unit.status.message == "Deploying AWS Cloud Provider"


@mock.patch("ops.interface_kube_control.KubeControlRequirer.create_kubeconfig")
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ commands =
description = Check code against coding style standards
deps =
black
flake8 < 5.0 # TODO: https://github.com/csachs/pyproject-flake8/issues/13
flake8
flake8-docstrings
flake8-copyright
flake8-builtins
Expand Down

0 comments on commit bc71c22

Please sign in to comment.