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

Support kube-control v2 schema #5

Merged
merged 3 commits into from
Nov 20, 2024
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: 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
Loading