Charmed-Kubernetes/kubeapi-load-balancer/tests/unit/test_k8s_common.py

216 lines
7.3 KiB
Python

import json
import string
from subprocess import CalledProcessError
from pathlib import Path
import pytest
from unittest.mock import Mock, call, patch
from charms.reactive import endpoint_from_flag
from charms.layer import kubernetes_common as kc
@pytest.mark.parametrize(
"taint, key, value, effect",
[
(
"kubernetes.io/uninitialized=true:NoSchedule",
"kubernetes.io/uninitialized",
"true",
"NoSchedule",
),
(
"kubernetes.io/uninitialized:NoExecute",
"kubernetes.io/uninitialized",
None,
"NoExecute",
),
],
)
def test_v1_taints_from_string_success(taint, key, value, effect):
obj = kc.v1_taint_from_string(taint)
assert obj.get("key") == key
assert obj.get("value") == value
assert obj.get("effect") == effect
@pytest.mark.parametrize(
"taint, issue",
[
("kubernetes.io/uninitialized=true:BadEffect", "effect"),
("kubernetes.io/uninitialized=NoExecute", "colon"),
("kubernetes.io=uninitialized=true:NoExecute", "equals"),
],
)
def test_v1_taints_from_string_failure(taint, issue):
with pytest.raises(ValueError) as ie:
kc.v1_taint_from_string(taint)
assert issue in str(ie.value)
def test_token_generator():
alphanum = string.ascii_letters + string.digits
token = kc.token_generator(10)
assert len(token) == 10
unknown_chars = set(token) - set(alphanum)
assert not unknown_chars
def test_get_secret_names(monkeypatch):
monkeypatch.setattr(kc, "kubectl", Mock())
kc.kubectl.side_effect = [
CalledProcessError(1, "none"),
FileNotFoundError,
"{}".encode("utf8"),
json.dumps(
{
"items": [
{
"metadata": {"name": "secret-id"},
"data": {"username": "dXNlcg=="},
},
],
}
).encode("utf8"),
]
assert kc.get_secret_names() == {}
assert kc.get_secret_names() == {}
assert kc.get_secret_names() == {}
assert kc.get_secret_names() == {"user": "secret-id"}
def test_generate_rfc1123():
alphanum = string.ascii_letters + string.digits
token = kc.generate_rfc1123(1000)
assert len(token) == 253
unknown_chars = set(token) - set(alphanum)
assert not unknown_chars
def test_create_secret(monkeypatch):
monkeypatch.setattr(kc, "render", Mock())
monkeypatch.setattr(kc, "kubectl_manifest", Mock())
monkeypatch.setattr(kc, "get_secret_names", Mock())
monkeypatch.setattr(kc, "generate_rfc1123", Mock())
kc.kubectl_manifest.side_effect = [True, False]
kc.get_secret_names.side_effect = [{"username": "secret-id"}, {}]
kc.generate_rfc1123.return_value = "foo"
assert kc.create_secret("token", "username", "user", "groups")
assert kc.render.call_args[1]["context"] == {
"groups": "Z3JvdXBz",
"password": "dXNlcjo6dG9rZW4=",
"secret_name": "secret-id",
"secret_namespace": "kube-system",
"type": "juju.is/token-auth",
"user": "dXNlcg==",
"username": "dXNlcm5hbWU=",
}
assert not kc.create_secret("token", "username", "user", "groups")
assert kc.render.call_args[1]["context"] == {
"groups": "Z3JvdXBz",
"password": "dXNlcjo6dG9rZW4=",
"secret_name": "auth-user-foo",
"secret_namespace": "kube-system",
"type": "juju.is/token-auth",
"user": "dXNlcg==",
"username": "dXNlcm5hbWU=",
}
def test_get_secret_password(monkeypatch):
monkeypatch.setattr(kc, "kubectl", Mock())
monkeypatch.setattr(kc, "Path", Mock())
monkeypatch.setattr(kc, "yaml", Mock())
kc.kubectl.side_effect = [
CalledProcessError(1, "none"),
CalledProcessError(1, "none"),
CalledProcessError(1, "none"),
CalledProcessError(1, "none"),
CalledProcessError(1, "none"),
CalledProcessError(1, "none"),
FileNotFoundError,
json.dumps({}).encode("utf8"),
json.dumps({"items": []}).encode("utf8"),
json.dumps({"items": []}).encode("utf8"),
json.dumps({"items": [{}]}).encode("utf8"),
json.dumps({"items": [{"data": {}}]}).encode("utf8"),
json.dumps(
{"items": [{"data": {"username": "Ym9i", "password": "c2VjcmV0"}}]}
).encode("utf8"),
json.dumps(
{"items": [{"data": {"username": "dXNlcm5hbWU=", "password": "c2VjcmV0"}}]}
).encode("utf8"),
]
kc.yaml.safe_load.side_effect = [
{},
{"users": None},
{"users": []},
{"users": [{"user": {}}]},
{"users": [{"user": {"token": "secret"}}]},
]
assert kc.get_secret_password("username") is None
assert kc.get_secret_password("admin") is None
assert kc.get_secret_password("admin") is None
assert kc.get_secret_password("admin") is None
assert kc.get_secret_password("admin") is None
assert kc.get_secret_password("admin") == "secret"
assert kc.get_secret_password("username") is None
assert kc.get_secret_password("username") is None
assert kc.get_secret_password("username") is None
assert kc.get_secret_password("username") is None
assert kc.get_secret_password("username") is None
assert kc.get_secret_password("username") is None
assert kc.get_secret_password("username") is None
assert kc.get_secret_password("username") == "secret"
@patch("os.listdir")
@patch("os.remove")
@patch("os.symlink")
def test_configure_default_cni(os_symlink, os_remove, os_listdir):
os_listdir.return_value = ["01-default.conflist", "10-cni.conflist"]
cni = endpoint_from_flag("cni.available")
cni.get_config.return_value = {
"cidr": "192.168.0.0/24",
"cni-conf-file": "10-cni.conflist",
}
kc.configure_default_cni("test-cni")
os_remove.assert_called_once_with("/etc/cni/net.d/01-default.conflist")
os_symlink.assert_called_once_with(
"10-cni.conflist", "/etc/cni/net.d/01-default.conflist"
)
def test_get_bind_addrs():
response = Path("tests", "data", "ip_addr_json").read_bytes()
with patch.object(kc, "check_output", return_value=response):
addrs = kc.get_bind_addrs()
assert addrs == ["10.246.154.77"]
@patch.object(kc, "get_version")
@patch.object(kc, "get_node_ip", Mock(return_value="10.1.1.1"))
@patch.object(kc, "workaround_lxd_kernel_params", Mock())
@patch.object(kc, "configure_kubernetes_service")
@patch.object(kc, "hookenv")
@patch("os.makedirs", Mock())
@patch.object(kc, "open")
@pytest.mark.parametrize("version", [(1, 27, 0), (1, 26, 0)], ids=["1.27.0", "1.26.0"])
@pytest.mark.parametrize("runtime", ["remote", "local"])
def test_configure_kubelet(
f_open, hookenv, conf_service, get_version, version, runtime, tmp_path
):
get_version.return_value = version
endpoint_from_flag(
"endpoint.container-runtime.available"
).get_runtime.return_value = runtime
hookenv.config.return_value = "{}"
kc.configure_kubelet(".test.domain", "10.10.10.10", "registry.k8s.io")
f_open.assert_called_once_with("/root/cdk/kubelet/config.yaml", "w")
with f_open() as f:
first_call = call("# Generated by kubernetes-common library, do not edit\n")
f.write.assert_has_calls([first_call])
conf_service.assert_called_once()