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

Test configure_apiserver while addressing issue with authorization_modes #25

Merged
merged 1 commit into from
Jun 28, 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
22 changes: 11 additions & 11 deletions charms/kubernetes_snaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def configure_apiserver(
"Authorization mode includes 'Webhook' but no webhook config file is present."
"'Webhook' must be removed from authorization mode."
)
authorization_modes = authorization_modes.remove("Webhook")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the original thrust of the bug. Python's list.remove returns None. causing the following stack track:

  File "/var/lib/juju/agents/unit-kubernetes-control-plane-0/charm/./src/charm.py", line 541, in reconcile
    self.configure_apiserver()
  File "/var/lib/juju/agents/unit-kubernetes-control-plane-0/charm/./src/charm.py", line 141, in configure_apiserver
    kubernetes_snaps.configure_apiserver(
  File "/var/lib/juju/agents/unit-kubernetes-control-plane-0/charm/venv/charms/kubernetes_snaps.py", line 145, in configure_apiserver
    api_opts["authorization-mode"] = ",".join(authorization_modes)
TypeError: can only join an iterable

authorization_modes.remove("Webhook")
elif has_authz_webhook_file:
log.warning(
"Authorization mode doesn't include 'Webhook' but a webhook config file is present."
Expand Down Expand Up @@ -163,28 +163,28 @@ def configure_apiserver(

api_opts["feature-gates"] = ",".join(feature_gates)

audit_root = "/root/cdk/audit"
audit_log_path = audit_root + "/audit.log"
audit_policy_path = audit_root + "/audit-policy.yaml"
audit_webhook_conf_path = audit_root + "/audit-webhook-config.yaml"
os.makedirs(audit_root, exist_ok=True)
audit_root = Path("/root/cdk/audit")
audit_log_path = audit_root / "audit.log"
audit_policy_path = audit_root / "audit-policy.yaml"
audit_webhook_conf_path = audit_root / "audit-webhook-config.yaml"
audit_root.mkdir(exist_ok=True)

api_opts["audit-log-path"] = audit_log_path
api_opts["audit-log-path"] = str(audit_log_path)
api_opts["audit-log-maxage"] = "30"
Comment on lines -166 to 173
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These make the method more testable

api_opts["audit-log-maxsize"] = "100"
api_opts["audit-log-maxbackup"] = "10"

if audit_policy:
with open(audit_policy_path, "w") as f:
with audit_policy_path.open("w") as f:
f.write(audit_policy)
api_opts["audit-policy-file"] = audit_policy_path
api_opts["audit-policy-file"] = str(audit_policy_path)
else:
remove_if_exists(audit_policy_path)

if audit_webhook_conf:
with open(audit_webhook_conf_path, "w") as f:
with audit_webhook_conf_path.open("w") as f:
f.write(audit_webhook_conf)
api_opts["audit-webhook-config-file"] = audit_webhook_conf_path
api_opts["audit-webhook-config-file"] = str(audit_webhook_conf_path)
else:
Comment on lines -178 to 188
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again -- more test-ability -- only need to mock out Path

remove_if_exists(audit_webhook_conf_path)

Expand Down
73 changes: 72 additions & 1 deletion tests/unit/test_kubernetes_snaps.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import pytest
import unittest.mock as mock


from charms import kubernetes_snaps


Expand Down Expand Up @@ -138,3 +137,75 @@ def test_configure_kubelet(
if external_cloud.has_xcp:
expected_args["cloud-provider"] = "external"
assert expected_args == args


@mock.patch("charms.kubernetes_snaps.configure_kubernetes_service")
@mock.patch("charms.kubernetes_snaps.Path")
def test_configure_apiserver(mock_path, configure_kubernetes_service, external_cloud):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Useful test.

mock_path().__truediv__().__str__.return_value = "/some/path"
kubernetes_snaps.configure_apiserver(
"advertise_address",
"audit_policy",
"audit_webhook_conf",
"auth_webhook_conf",
"Node,RBAC,Webhook",
"ignored",
"https://1.1.1.1,https://1.1.1.2",
{},
"false",
"10.10.10.0/24",
external_cloud,
None,
)
configure_kubernetes_service.assert_called_once()
service, args, extra = configure_kubernetes_service.call_args[0]
assert service == "kube-apiserver"
assert extra == {}
expected_args = {
"allow-privileged": "true",
"service-cluster-ip-range": "10.10.10.0/24",
"min-request-timeout": "300",
"v": "4",
"tls-cert-file": "/root/cdk/server.crt",
"tls-private-key-file": "/root/cdk/server.key",
"tls-cipher-suites": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
"kubelet-certificate-authority": "/root/cdk/ca.crt",
"kubelet-client-certificate": "/root/cdk/client.crt",
"kubelet-client-key": "/root/cdk/client.key",
"storage-backend": "etcd3",
"profiling": "false",
"anonymous-auth": "false",
"authentication-token-webhook-cache-ttl": "1m0s",
"authentication-token-webhook-config-file": "auth_webhook_conf",
"service-account-issuer": "https://kubernetes.default.svc",
"service-account-signing-key-file": "/root/cdk/serviceaccount.key",
"service-account-key-file": "/root/cdk/serviceaccount.key",
"kubelet-preferred-address-types": "InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP",
"encryption-provider-config": "/some/path",
"advertise-address": "advertise_address",
"etcd-cafile": "/root/cdk/etcd/client-ca.pem",
"etcd-keyfile": "/root/cdk/etcd/client-key.pem",
"etcd-certfile": "/root/cdk/etcd/client-cert.pem",
"etcd-servers": "https://1.1.1.1,https://1.1.1.2",
"authorization-mode": "Node,RBAC",
"enable-admission-plugins": "PersistentVolumeLabel,NodeRestriction",
"requestheader-client-ca-file": "/root/cdk/ca.crt",
"requestheader-allowed-names": "system:kube-apiserver,client",
"requestheader-extra-headers-prefix": "X-Remote-Extra-",
"requestheader-group-headers": "X-Remote-Group",
"requestheader-username-headers": "X-Remote-User",
"proxy-client-cert-file": "/root/cdk/client.crt",
"proxy-client-key-file": "/root/cdk/client.key",
"enable-aggregator-routing": "true",
"client-ca-file": "/root/cdk/ca.crt",
"feature-gates": "",
"audit-log-path": "/some/path",
"audit-log-maxage": "30",
"audit-log-maxsize": "100",
"audit-log-maxbackup": "10",
"audit-policy-file": "/some/path",
"audit-webhook-config-file": "/some/path",
}
if external_cloud.has_xcp:
expected_args["cloud-provider"] = "external"
assert expected_args == args