Skip to content

Commit

Permalink
Testing for cloud integrations
Browse files Browse the repository at this point in the history
  • Loading branch information
addyess committed Nov 19, 2024
1 parent 23053a0 commit e2eba04
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 1 deletion.
2 changes: 1 addition & 1 deletion charms/worker/k8s/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ charm-lib-contextual-status @ git+https://github.com/charmed-kubernetes/charm-li
charm-lib-interface-external-cloud-provider @ git+https://github.com/charmed-kubernetes/charm-lib-interface-external-cloud-provider@e1c5fc69e98100a7d43c0ad5a7969bba1ecbcd40
charm-lib-node-base @ git+https://github.com/charmed-kubernetes/layer-kubernetes-node-base@9b212854e768f13c26cc907bed51444e97e51b50#subdirectory=ops
charm-lib-reconciler @ git+https://github.com/charmed-kubernetes/charm-lib-reconciler@f818cc30d1a22be43ffdfecf7fbd9c3fd2967502
ops-interface-kube-control @ git+https://github.com/charmed-kubernetes/interface-kube-control.git@KU-1998/use-secrets#subdirectory=ops
ops-interface-kube-control @ git+https://github.com/charmed-kubernetes/interface-kube-control.git@main#subdirectory=ops
ops.interface_aws @ git+https://github.com/charmed-kubernetes/interface-aws-integration@main#subdirectory=ops
ops.interface_gcp @ git+https://github.com/charmed-kubernetes/interface-gcp-integration@main#subdirectory=ops
ops.interface_azure @ git+https://github.com/charmed-kubernetes/interface-azure-integration@main#subdirectory=ops
Expand Down
4 changes: 4 additions & 0 deletions charms/worker/k8s/src/kube_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,7 @@ def configure(charm: K8sCharmProtocol):
kubelet_token=str(),
proxy_token=str(),
)

for user, _ in charm.kube_control.closed_auth_creds():
log.info("TODO: Revoke auth-token for '%s'", user)
# charm.api_manager.remove_auth_token(cred.client_token.get_secret_value())
196 changes: 196 additions & 0 deletions charms/worker/k8s/tests/unit/test_cloud_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import unittest.mock as mock
from pathlib import Path
from unittest import mock

import ops
import ops.testing
import pytest
from charm import K8sCharm
from ops.interface_aws.requires import AWSIntegrationRequires
from ops.interface_azure.requires import AzureIntegrationRequires
from ops.interface_gcp.requires import GCPIntegrationRequires


@pytest.fixture(autouse=True)
def vendor_name():
with mock.patch(
"charms.interface_external_cloud_provider.ExternalCloudProvider.name",
new_callable=mock.PropertyMock,
) as mock_vendor_name:
mock_vendor_name.return_value = "aws"
yield mock_vendor_name


@pytest.fixture(params=["worker", "control-plane"])
def harness(request):
"""Craft a ops test harness.
Args:
request: pytest request object
"""
meta = Path(__file__).parent / "../../charmcraft.yaml"
if request.param == "worker":
meta = Path(__file__).parent / "../../../charmcraft.yaml"
harness = ops.testing.Harness(K8sCharm, meta=meta.read_text())
harness.begin()
harness.charm.is_worker = request.param == "worker"
with mock.patch.object(harness.charm, "get_cloud_name"):
with mock.patch.object(harness.charm, "get_cluster_name", return_value="my-cluster"):
yield harness
harness.cleanup()


@pytest.mark.parametrize(
"cloud_name, cloud_relation",
[
("aws", "aws"),
("gce", "gcp"),
("azure", "azure"),
("unknown", None),
],
ids=["aws", "gce", "azure", "unknown"],
)
def test_cloud_detection(harness, cloud_name, cloud_relation, vendor_name):
# Test that the cloud property returns the correct integration requires object
harness.charm.get_cloud_name.return_value = cloud_name
integration = harness.charm.cloud_integration
assert integration.cloud is None
if cloud_name != "unknown":
harness.add_relation(cloud_relation, "cloud-integrator")
assert integration.cloud


def test_cloud_aws(harness):
# Test that the cloud property returns the correct integration requires object
harness.charm.get_cloud_name.return_value = "aws"
# with mock.patch.object(harness.charm.cloud_integration, "cloud", callable=mock.PropertyMock) as mock_cloud:
with mock.patch(
"cloud_integration.CloudIntegration.cloud",
new_callable=mock.PropertyMock,
return_value=mock.create_autospec(AWSIntegrationRequires),
) as mock_property:
mock_cloud = mock_property()
mock_cloud.evaluate_relation.return_value = None
event = mock.MagicMock()
harness.charm.cloud_integration.integrate(event)
if harness.charm.is_worker:
mock_cloud.tag_instance.assert_called_once_with(
{"kubernetes.io/cluster/my-cluster": "owned"}
)
else:
mock_cloud.tag_instance.assert_called_once_with(
{
"kubernetes.io/cluster/my-cluster": "owned",
"k8s.io/role/master": "true", # wokeignore:rule=master
}
)
mock_cloud.tag_instance_security_group.assert_called_once_with(
{"kubernetes.io/cluster/my-cluster": "owned"}
)
mock_cloud.tag_instance_subnet.assert_called_once_with(
{"kubernetes.io/cluster/my-cluster": "owned"}
)
mock_cloud.enable_object_storage_management.assert_called_once_with(["kubernetes-*"])
if harness.charm.is_worker:
mock_cloud.enable_load_balancer_management.assert_not_called()
mock_cloud.enable_autoscaling_readonly.assert_not_called()
mock_cloud.enable_instance_modification.assert_not_called()
mock_cloud.enable_region_readonly.assert_not_called()
mock_cloud.enable_network_management.assert_not_called()
mock_cloud.enable_block_storage_management.assert_not_called()
else:
mock_cloud.enable_load_balancer_management.assert_called_once()
mock_cloud.enable_autoscaling_readonly.assert_called_once()
mock_cloud.enable_instance_modification.assert_called_once()
mock_cloud.enable_region_readonly.assert_called_once()
mock_cloud.enable_network_management.assert_called_once()
mock_cloud.enable_block_storage_management.assert_called_once()
mock_cloud.enable_instance_inspection.assert_called_once()
mock_cloud.enable_dns_management.assert_called_once()
mock_cloud.evaluate_relation.assert_called_once_with(event)


def test_cloud_gce(harness):
# Test that the cloud property returns the correct integration requires object
harness.charm.get_cloud_name.return_value = "gce"
with mock.patch(
"cloud_integration.CloudIntegration.cloud",
new_callable=mock.PropertyMock,
return_value=mock.create_autospec(GCPIntegrationRequires),
) as mock_property:
mock_cloud = mock_property()
mock_cloud.evaluate_relation.return_value = None
event = mock.MagicMock()
harness.charm.cloud_integration.integrate(event)

if harness.charm.is_worker:
mock_cloud.tag_instance.assert_called_once_with({"k8s-io-cluster-name": "my-cluster"})
else:
mock_cloud.tag_instance.assert_called_once_with(
{
"k8s-io-cluster-name": "my-cluster",
"k8s-io-role-master": "master", # wokeignore:rule=master
}
)
mock_cloud.enable_object_storage_management.assert_called_once()
if harness.charm.is_worker:
mock_cloud.enable_security_management.assert_not_called()
mock_cloud.enable_network_management.assert_not_called()
mock_cloud.enable_block_storage_management.assert_not_called()
else:
mock_cloud.enable_security_management.assert_called_once()
mock_cloud.enable_network_management.assert_called_once()
mock_cloud.enable_block_storage_management.assert_called_once()
mock_cloud.enable_instance_inspection.assert_called_once()
mock_cloud.enable_dns_management.assert_called_once()
mock_cloud.evaluate_relation.assert_called_once_with(event)


def test_cloud_azure(harness):
# Test that the cloud property returns the correct integration requires object
harness.charm.get_cloud_name.return_value = "azure"
with mock.patch(
"cloud_integration.CloudIntegration.cloud",
new_callable=mock.PropertyMock,
return_value=mock.create_autospec(AzureIntegrationRequires),
) as mock_property:
mock_cloud = mock_property()
mock_cloud.evaluate_relation.return_value = None
event = mock.MagicMock()
harness.charm.cloud_integration.integrate(event)
if harness.charm.is_worker:
mock_cloud.tag_instance.assert_called_once_with({"k8s-io-cluster-name": "my-cluster"})
else:
mock_cloud.tag_instance.assert_called_once_with(
{
"k8s-io-cluster-name": "my-cluster",
"k8s-io-role-master": "master", # wokeignore:rule=master
}
)
mock_cloud.enable_object_storage_management.assert_called_once()
if harness.charm.is_worker:
mock_cloud.enable_security_management.assert_not_called()
mock_cloud.enable_loadbalancer_management.assert_not_called()
mock_cloud.enable_network_management.assert_not_called()
mock_cloud.enable_block_storage_management.assert_not_called()
else:
mock_cloud.enable_security_management.assert_called_once()
mock_cloud.enable_loadbalancer_management.assert_called_once()
mock_cloud.enable_network_management.assert_called_once()
mock_cloud.enable_block_storage_management.assert_called_once()
mock_cloud.enable_dns_management.assert_called_once()
mock_cloud.enable_instance_inspection.assert_called_once()
mock_cloud.evaluate_relation.assert_called_once_with(event)


def test_cloud_unknown(harness):
# Test that the cloud property returns the correct integration requires object
harness.charm.get_cloud_name.return_value = "unknown"
with mock.patch(
"cloud_integration.CloudIntegration.cloud",
new_callable=mock.PropertyMock,
return_value=None,
) as mock_property:
event = mock.MagicMock()
harness.charm.cloud_integration.integrate(event)
assert mock_property.called

0 comments on commit e2eba04

Please sign in to comment.