Skip to content

Commit

Permalink
Annotate nodes with cluster and owner info
Browse files Browse the repository at this point in the history
  • Loading branch information
Cecile Robert-Michon committed Mar 26, 2021
1 parent ee72431 commit 27b750a
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 0 deletions.
15 changes: 15 additions & 0 deletions api/v1alpha3/common_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ const (
// tool uses this label for implementing provider's lifecycle operations.
ProviderLabelName = "cluster.x-k8s.io/provider"

// ClusterNameAnnotation is the annotation set on nodes identifying the name of the cluster the node belongs to.
ClusterNameAnnotation = "cluster.x-k8s.io/cluster-name"

// ClusterNamespaceAnnotation is the annotation set on nodes identifying the namespace of the cluster the node belongs to.
ClusterNamespaceAnnotation = "cluster.x-k8s.io/cluster-namespace"

// MachineAnnotation is the annotation set on nodes identifying the machine the node belongs to.
MachineAnnotation = "cluster.x-k8s.io/machine"

// OwnerKindAnnotation is the annotation set on nodes identifying the owner kind.
OwnerKindAnnotation = "cluster.x-k8s.io/owner-kind"

// OwnerNameAnnotation is the annotation set on nodes identifying the owner name.
OwnerNameAnnotation = "cluster.x-k8s.io/owner-name"

// PausedAnnotation is an annotation that can be applied to any Cluster API
// object to prevent a controller from processing a resource.
//
Expand Down
24 changes: 24 additions & 0 deletions controllers/machine_controller_noderef.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ package controllers
import (
"context"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/cluster-api/util/annotations"
"sigs.k8s.io/cluster-api/util/patch"

"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -84,6 +87,27 @@ func (r *MachineReconciler) reconcileNode(ctx context.Context, cluster *clusterv
r.recorder.Event(machine, corev1.EventTypeNormal, "SuccessfulSetNodeRef", machine.Status.NodeRef.Name)
}

// Reconcile node annotations.
patchHelper, err := patch.NewHelper(node, remoteClient)
if err != nil {
return ctrl.Result{}, err
}
desired := map[string]string{
clusterv1.ClusterNameAnnotation: machine.Spec.ClusterName,
clusterv1.ClusterNamespaceAnnotation: machine.GetNamespace(),
clusterv1.MachineAnnotation: machine.Name,
}
if owner := metav1.GetControllerOfNoCopy(machine); owner != nil {
desired[clusterv1.OwnerKindAnnotation] = owner.Kind
desired[clusterv1.OwnerNameAnnotation] = owner.Name
}
if annotations.AddAnnotations(node, desired) {
if err := patchHelper.Patch(ctx, node); err != nil {
logger.V(2).Info("Failed patch node to set annotations", "err", err, "node name", node.Name)
return ctrl.Result{}, err
}
}

// 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 Down
27 changes: 27 additions & 0 deletions exp/controllers/machinepool_controller_noderef.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package controllers
import (
"context"
"fmt"
"sigs.k8s.io/cluster-api/util/annotations"
"sigs.k8s.io/cluster-api/util/patch"
"time"

ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -100,6 +102,31 @@ func (r *MachinePoolReconciler) reconcileNodeRefs(ctx context.Context, cluster *
logger.Info("Set MachinePools's NodeRefs", "noderefs", mp.Status.NodeRefs)
r.recorder.Event(mp, apicorev1.EventTypeNormal, "SuccessfulSetNodeRefs", fmt.Sprintf("%+v", mp.Status.NodeRefs))

// Reconcile node annotations.
for _, nodeRef := range nodeRefsResult.references {
node := &corev1.Node{}
if err := clusterClient.Get(ctx, client.ObjectKey{Name: nodeRef.Name}, node); err != nil {
logger.V(2).Info("Failed to get Node, skipping setting annotations", "err", err, "nodeRef.Name", nodeRef.Name)
continue
}
patchHelper, err := patch.NewHelper(node, clusterClient)
if err != nil {
return ctrl.Result{}, err
}
desired := map[string]string{
clusterv1.ClusterNameAnnotation: mp.Spec.ClusterName,
clusterv1.ClusterNamespaceAnnotation: mp.GetNamespace(),
clusterv1.OwnerKindAnnotation: mp.Kind,
clusterv1.OwnerNameAnnotation: mp.Name,
}
if annotations.AddAnnotations(node, desired) {
if err := patchHelper.Patch(ctx, node); err != nil {
logger.V(2).Info("Failed patch node to set annotations", "err", err, "node name", node.Name)
return ctrl.Result{}, err
}
}
}

if mp.Status.Replicas != mp.Status.ReadyReplicas || len(nodeRefsResult.references) != int(mp.Status.ReadyReplicas) {
r.Log.Info("NodeRefs != ReadyReplicas", "NodeRefs", len(nodeRefsResult.references), "ReadyReplicas", mp.Status.ReadyReplicas)
conditions.MarkFalse(mp, expv1.ReplicasReadyCondition, expv1.WaitingForReplicasReadyReason, clusterv1.ConditionSeverityInfo, "")
Expand Down
122 changes: 122 additions & 0 deletions util/annotations/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package annotations

import (
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"testing"
)

func TestAddAnnotations(t *testing.T) {
g := NewWithT(t)

var testcases = []struct {
name string
obj metav1.Object
input map[string]string
expected map[string]string
changed bool
}{
{
name: "should return false if no changes are made",
obj: &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"foo": "bar",
},
},
Spec: corev1.NodeSpec{},
Status: corev1.NodeStatus{},
},
input: map[string]string{
"foo": "bar",
},
expected: map[string]string{
"foo": "bar",
},
changed: false,
},
{
name: "should do nothing if no annotations are provided",
obj: &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"foo": "bar",
},
},
Spec: corev1.NodeSpec{},
Status: corev1.NodeStatus{},
},
input: map[string]string{},
expected: map[string]string{
"foo": "bar",
},
changed: false,
},
{
name: "should return true if annotations are added",
obj: &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"foo": "bar",
},
},
Spec: corev1.NodeSpec{},
Status: corev1.NodeStatus{},
},
input: map[string]string{
"thing1": "thing2",
"buzz": "blah",
},
expected: map[string]string{
"foo": "bar",
"thing1": "thing2",
"buzz": "blah",
},
changed: true,
},
{
name: "should return true if annotations are changed",
obj: &corev1.Node{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
"foo": "bar",
},
},
Spec: corev1.NodeSpec{},
Status: corev1.NodeStatus{},
},
input: map[string]string{
"foo": "buzz",
},
expected: map[string]string{
"foo": "buzz",
},
changed: true,
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
res := AddAnnotations(tc.obj, tc.input)
g.Expect(res).To(Equal(tc.changed))
g.Expect(tc.obj.GetAnnotations()).To(Equal(tc.expected))
})
}
}

0 comments on commit 27b750a

Please sign in to comment.