Skip to content

Commit

Permalink
Copy control-plane taint from bootstrap config (#425)
Browse files Browse the repository at this point in the history
* Copy control-plane taint from bootstrap config
* Add tests for ControlPlaneTaints
* Empty control plane taints slices can be ignored
  • Loading branch information
addyess authored May 22, 2024
1 parent 0bd5c82 commit f4fd0e3
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 6 deletions.
3 changes: 3 additions & 0 deletions src/k8s/pkg/k8sd/types/cluster_config_convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ func ClusterConfigFromBootstrapConfig(b apiv1.BootstrapConfig) (ClusterConfig, e

// Kubelet
config.Kubelet.CloudProvider = b.ClusterConfig.CloudProvider
if len(b.ControlPlaneTaints) != 0 {
config.Kubelet.ControlPlaneTaints = utils.Pointer(b.ControlPlaneTaints)
}

return config, nil
}
Expand Down
17 changes: 17 additions & 0 deletions src/k8s/pkg/k8sd/types/cluster_config_convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,23 @@ func TestClusterConfigFromBootstrapConfig(t *testing.T) {
},
},
},
{
name: "ControlPlainTaints",
bootstrap: apiv1.BootstrapConfig{
ControlPlaneTaints: []string{"node-role.kubernetes.io/control-plane:NoSchedule"},
},
expectConfig: types.ClusterConfig{
APIServer: types.APIServer{
AuthorizationMode: utils.Pointer("Node,RBAC"),
},
Datastore: types.Datastore{
Type: utils.Pointer("k8s-dqlite"),
},
Kubelet: types.Kubelet{
ControlPlaneTaints: utils.Pointer([]string{"node-role.kubernetes.io/control-plane:NoSchedule"}),
},
},
},
} {
t.Run(tc.name, func(t *testing.T) {
g := NewWithT(t)
Expand Down
42 changes: 42 additions & 0 deletions tests/integration/tests/test_control_plane_taints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#
# Copyright 2024 Canonical, Ltd.
#
import logging
import time
from typing import List

import pytest
import yaml
from test_util import harness, util

LOG = logging.getLogger(__name__)


@pytest.mark.node_count(1)
@pytest.mark.disable_k8s_bootstrapping()
def test_control_plane_taints(instances: List[harness.Instance]):
k8s_instance = instances[0]

bootstrap_conf = yaml.safe_dump(
{"control-plane-taints": ["node-role.kubernetes.io/control-plane:NoSchedule"]}
)

k8s_instance.exec(
["dd", "of=/root/config.yaml"],
input=str.encode(bootstrap_conf),
)

k8s_instance.exec(["k8s", "bootstrap", "--file", "/root/config.yaml"])
retries = 10
while retries and not (nodes := util.get_nodes(k8s_instance)):
LOG.info("Waiting for Nodes")
time.sleep(3)
retries -= 1
assert len(nodes) == 1, "Should have found one node in 30 sec"
assert all(
[
t["effect"] == "NoSchedule"
for t in nodes[0]["spec"]["taints"]
if t["key"] == "node-role.kubernetes.io/control-plane"
]
)
23 changes: 17 additions & 6 deletions tests/integration/tests/test_util/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ def get_local_node_status(instance: harness.Instance) -> str:
return resp.stdout.decode().strip()


def ready_nodes(control_node: harness.Instance) -> List[Any]:
"""Get a list of the ready nodes.
def get_nodes(control_node: harness.Instance) -> List[Any]:
"""Get a list of existing nodes.
Args:
control_node: instance on which to execute check
Expand All @@ -239,21 +239,32 @@ def ready_nodes(control_node: harness.Instance) -> List[Any]:
list of nodes
"""
result = control_node.exec(
"k8s kubectl get nodes -o json".split(" "), capture_output=True
["k8s", "kubectl", "get", "nodes", "-o", "json"], capture_output=True
)
assert result.returncode == 0, "Failed to get nodes with kubectl"
node_list = json.loads(result.stdout.decode())
assert node_list["kind"] == "List", "Should have found a list of nodes"
nodes = [
return [node for node in node_list["items"]]


def ready_nodes(control_node: harness.Instance) -> List[Any]:
"""Get a list of the ready nodes.
Args:
control_node: instance on which to execute check
Returns:
list of nodes
"""
return [
node
for node in node_list["items"]
for node in get_nodes(control_node)
if all(
condition["status"] == "False"
for condition in node["status"]["conditions"]
if condition["type"] != "Ready"
)
]
return nodes


# Create a token to join a node to an existing cluster
Expand Down

0 comments on commit f4fd0e3

Please sign in to comment.