diff --git a/pkg/apis/config/v1alpha4/types.go b/pkg/apis/config/v1alpha4/types.go index bc31b263c0..1916b56250 100644 --- a/pkg/apis/config/v1alpha4/types.go +++ b/pkg/apis/config/v1alpha4/types.go @@ -104,6 +104,9 @@ type Node struct { // If unset a default image will be used, see defaults.Image Image string `yaml:"image,omitempty"` + // Labels are the labels with which the respective node will be labeled + Labels map[string]string `yaml:"labels,omitempty"` + /* Advanced fields */ // TODO: cri-like types should be inline instead diff --git a/pkg/apis/config/v1alpha4/zz_generated.deepcopy.go b/pkg/apis/config/v1alpha4/zz_generated.deepcopy.go index 6fa61d4e34..8aab0fa122 100644 --- a/pkg/apis/config/v1alpha4/zz_generated.deepcopy.go +++ b/pkg/apis/config/v1alpha4/zz_generated.deepcopy.go @@ -114,6 +114,13 @@ func (in *Networking) DeepCopy() *Networking { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Node) DeepCopyInto(out *Node) { *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.ExtraMounts != nil { in, out := &in.ExtraMounts, &out.ExtraMounts *out = make([]Mount, len(*in)) diff --git a/pkg/cluster/internal/create/actions/config/config.go b/pkg/cluster/internal/create/actions/config/config.go index 638261be47..64beac53cc 100644 --- a/pkg/cluster/internal/create/actions/config/config.go +++ b/pkg/cluster/internal/create/actions/config/config.go @@ -98,15 +98,42 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { } } + // Populate the list of control-plane node labels and the list of worker node labels respectively. + // controlPlaneLabels is an array of maps (labels, read from config) associated with all the control-plane nodes. + // workerLabels is an array of maps (labels, read from config) associated with all the worker nodes. + controlPlaneLabels := []map[string]string{} + workerLabels := []map[string]string{} + for _, node := range ctx.Config.Nodes { + if node.Role == config.ControlPlaneRole { + controlPlaneLabels = append(controlPlaneLabels, node.Labels) + } else if node.Role == config.WorkerRole { + workerLabels = append(workerLabels, node.Labels) + } else { + continue + } + } + + // hashMapLabelsToCommaSeparatedLabels converts labels in hashmap form to labels in a comma-separated string form like "key1=value1,key2=value2" + hashMapLabelsToCommaSeparatedLabels := func(labels map[string]string) string { + output := "" + for key, value := range labels { + output += fmt.Sprintf("%s=%s,", key, value) + } + return strings.TrimSuffix(output, ",") // remove the last character (comma) in the output string + } + // create the kubeadm join configuration for control plane nodes controlPlanes, err := nodeutils.ControlPlaneNodes(allNodes) if err != nil { return err } - for _, node := range controlPlanes { + for i, node := range controlPlanes { node := node // capture loop variable configData := configData // copy config data + if len(controlPlaneLabels[i]) > 0 { + configData.NodeLabels = hashMapLabelsToCommaSeparatedLabels(controlPlaneLabels[i]) // updating the config with the respective labels to be written over the current control-plane node in consideration + } fns = append(fns, kubeadmConfigPlusPatches(node, configData)) } @@ -117,10 +144,13 @@ func (a *Action) Execute(ctx *actions.ActionContext) error { } if len(workers) > 0 { // create the workers concurrently - for _, node := range workers { + for i, node := range workers { node := node // capture loop variable configData := configData // copy config data configData.ControlPlane = false + if len(workerLabels[i]) > 0 { + configData.NodeLabels = hashMapLabelsToCommaSeparatedLabels(workerLabels[i]) // updating the config with the respective labels to be written over the current worker node in consideration + } fns = append(fns, kubeadmConfigPlusPatches(node, configData)) } } diff --git a/pkg/cluster/internal/kubeadm/config.go b/pkg/cluster/internal/kubeadm/config.go index 6fe4df1cad..82f97e8556 100644 --- a/pkg/cluster/internal/kubeadm/config.go +++ b/pkg/cluster/internal/kubeadm/config.go @@ -70,6 +70,9 @@ type ConfigData struct { // IPv4 values take precedence over IPv6 by default, if true set IPv6 default values IPv6 bool + // Labels are the labels, in the format "key1=val1,key2=val2", with which the respective node will be labeled + NodeLabels string + // DerivedConfigData is populated by Derive() // These auto-generated fields are available to Config templates, // but not meant to be set by hand @@ -326,6 +329,7 @@ nodeRegistration: fail-swap-on: "false" node-ip: "{{ .NodeAddress }}" provider-id: "kind://{{.NodeProvider}}/{{.ClusterName}}/{{.NodeName}}" + node-labels: "{{ .NodeLabels }}" --- # no-op entry that exists solely so it can be patched apiVersion: kubeadm.k8s.io/v1beta2 @@ -344,6 +348,7 @@ nodeRegistration: fail-swap-on: "false" node-ip: "{{ .NodeAddress }}" provider-id: "kind://{{.NodeProvider}}/{{.ClusterName}}/{{.NodeName}}" + node-labels: "{{ .NodeLabels }}" discovery: bootstrapToken: apiServerEndpoint: "{{ .ControlPlaneEndpoint }}" diff --git a/pkg/internal/apis/config/convert_v1alpha4.go b/pkg/internal/apis/config/convert_v1alpha4.go index 96e7aa8d4c..f37fe6c16e 100644 --- a/pkg/internal/apis/config/convert_v1alpha4.go +++ b/pkg/internal/apis/config/convert_v1alpha4.go @@ -51,6 +51,7 @@ func convertv1alpha4Node(in *v1alpha4.Node, out *Node) { out.Role = NodeRole(in.Role) out.Image = in.Image + out.Labels = in.Labels out.KubeadmConfigPatches = in.KubeadmConfigPatches out.ExtraMounts = make([]Mount, len(in.ExtraMounts)) out.ExtraPortMappings = make([]PortMapping, len(in.ExtraPortMappings)) diff --git a/pkg/internal/apis/config/types.go b/pkg/internal/apis/config/types.go index a4884e82b1..7c9786b1ab 100644 --- a/pkg/internal/apis/config/types.go +++ b/pkg/internal/apis/config/types.go @@ -85,6 +85,9 @@ type Node struct { // If unset a default image will be used, see defaults.Image Image string + // Labels are the labels with which the respective node will be labeled + Labels map[string]string + /* Advanced fields */ // ExtraMounts describes additional mount points for the node container diff --git a/pkg/internal/apis/config/zz_generated.deepcopy.go b/pkg/internal/apis/config/zz_generated.deepcopy.go index 74e0c9d5f5..3595f0ae36 100644 --- a/pkg/internal/apis/config/zz_generated.deepcopy.go +++ b/pkg/internal/apis/config/zz_generated.deepcopy.go @@ -113,6 +113,13 @@ func (in *Networking) DeepCopy() *Networking { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Node) DeepCopyInto(out *Node) { *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.ExtraMounts != nil { in, out := &in.ExtraMounts, &out.ExtraMounts *out = make([]Mount, len(*in))