Skip to content

Commit

Permalink
Add support to reconcile labels from Machines to Nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
enxebre committed Jan 2, 2023
1 parent 8799883 commit 45f1c26
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 0 deletions.
7 changes: 7 additions & 0 deletions api/v1beta1/machine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ const (
// This annotation can be set on BootstrapConfig or Machine objects. The value set on the Machine object takes precedence.
// This annotation can only be used on Control Plane Machines.
MachineCertificatesExpiryDateAnnotation = "machine.cluster.x-k8s.io/certificates-expiry"

// NodeRoleLabelPrefix is one of the CAPI managed Node label domains.
NodeRoleLabelPrefix = "node-role.kubernetes.io"
// NodeRestrictionLabelDomain is one of the CAPI managed Node label domains.
NodeRestrictionLabelDomain = "node-restriction.kubernetes.io"
// ManagedNodeLabelDomain is one of the CAPI managed domain Node label domains.
ManagedNodeLabelDomain = "node.cluster.x-k8s.io"
)

// ANCHOR: MachineSpec
Expand Down
44 changes: 44 additions & 0 deletions internal/controllers/machine/machine_controller_noderef.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ package machine
import (
"context"
"fmt"
"strings"

"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/klog/v2"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -116,6 +118,17 @@ func (r *Reconciler) reconcileNode(ctx context.Context, cluster *clusterv1.Clust
}
}

options := []client.PatchOption{
client.FieldOwner("capi-machine"),
client.ForceOwnership,
}
nodeCopy := node.DeepCopy()
nodeCopy.Labels = getManagedLabels(machine.Labels)
nodePatch := unstructuredNode(nodeCopy)
if err := remoteClient.Patch(ctx, nodePatch, client.Apply, options...); err != nil {
return ctrl.Result{}, errors.Wrap(err, "failed to apply patch label to the node")
}

// Do the remaining node health checks, then set the node health to true if all checks pass.
status, message := summarizeNodeConditions(node)
if status == corev1.ConditionFalse {
Expand All @@ -131,6 +144,37 @@ func (r *Reconciler) reconcileNode(ctx context.Context, cluster *clusterv1.Clust
return ctrl.Result{}, nil
}

// unstructuredNode returns a raw unstructured from a Node.
func unstructuredNode(node *corev1.Node) *unstructured.Unstructured {
obj := &unstructured.Unstructured{}
obj.SetAPIVersion(node.GroupVersionKind().GroupVersion().String())
obj.SetKind("Node")
obj.SetName(node.Name)
obj.SetUID(node.UID)
obj.SetLabels(node.Labels)
return obj
}

// getManagedLabels gets a map[string]string and returns another map[string]string
// filtering out labels not managed by CAPI.
func getManagedLabels(labels map[string]string) map[string]string {
managedLabels := make(map[string]string)
for key, value := range labels {
dnsSubdomainOrName := strings.Split(key, "/")[0]
if dnsSubdomainOrName == clusterv1.NodeRoleLabelPrefix {
managedLabels[key] = value
}
if dnsSubdomainOrName == clusterv1.NodeRestrictionLabelDomain || strings.HasSuffix(dnsSubdomainOrName, "."+clusterv1.NodeRestrictionLabelDomain) {
managedLabels[key] = value
}
if dnsSubdomainOrName == clusterv1.ManagedNodeLabelDomain || strings.HasSuffix(dnsSubdomainOrName, "."+clusterv1.ManagedNodeLabelDomain) {
managedLabels[key] = value
}
}

return managedLabels
}

// summarizeNodeConditions summarizes a Node's conditions and returns the summary of condition statuses and concatenate failed condition messages:
// if there is at least 1 semantically-negative condition, summarized status = False;
// if there is at least 1 semantically-positive condition when there is 0 semantically negative condition, summarized status = True;
Expand Down
26 changes: 26 additions & 0 deletions internal/controllers/machine/machine_controller_noderef_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,29 @@ func TestSummarizeNodeConditions(t *testing.T) {
})
}
}

func TestGetManagedLabels(t *testing.T) {
// Create managedLabels map from known managed prefixes.
managedLabels := map[string]string{}
managedLabels[clusterv1.ManagedNodeLabelDomain] = ""
managedLabels["custom-prefix."+clusterv1.NodeRestrictionLabelDomain] = ""
managedLabels["custom-prefix."+clusterv1.NodeRestrictionLabelDomain+"/anything"] = ""
managedLabels[clusterv1.NodeRoleLabelPrefix+"/anything"] = ""

// Append arbitrary labels.
allLabels := map[string]string{
"foo": "",
"bar": "",
"company.xyz/node.cluster.x-k8s.io": "not-managed",
"gpu-node.cluster.x-k8s.io": "not-managed",
"company.xyz/node-restriction.kubernetes.io": "not-managed",
"gpu-node-restriction.kubernetes.io": "not-managed",
}
for k, v := range managedLabels {
allLabels[k] = v
}

g := NewWithT(t)
got := getManagedLabels(allLabels)
g.Expect(got).To(BeEquivalentTo(managedLabels))
}

0 comments on commit 45f1c26

Please sign in to comment.