[node operator] case insensitive equality checks for image and scaling group references

This commit is contained in:
Malte Poll 2022-08-05 12:17:53 +02:00 committed by Malte Poll
parent 80ebfab164
commit fb4bc1545f
3 changed files with 49 additions and 14 deletions

View File

@ -4,6 +4,7 @@ package controllers
import ( import (
"context" "context"
"reflect" "reflect"
"strings"
"time" "time"
nodeutil "github.com/edgelesssys/constellation/operators/constellation-node-operator/internal/node" nodeutil "github.com/edgelesssys/constellation/operators/constellation-node-operator/internal/node"
@ -28,9 +29,9 @@ import (
const ( const (
// nodeOverprovisionLimit is the maximum number of extra nodes created during the update procedure at any point in time. // nodeOverprovisionLimit is the maximum number of extra nodes created during the update procedure at any point in time.
nodeOverprovisionLimit = 4 nodeOverprovisionLimit = 1
// nodeJoinTimeout is the time limit pending nodes have to join the cluster before being terminated. // nodeJoinTimeout is the time limit pending nodes have to join the cluster before being terminated.
nodeJoinTimeout = time.Minute * 15 nodeJoinTimeout = time.Minute * 30
// nodeLeaveTimeout is the time limit pending nodes have to leave the cluster and being terminated. // nodeLeaveTimeout is the time limit pending nodes have to leave the cluster and being terminated.
nodeLeaveTimeout = time.Minute nodeLeaveTimeout = time.Minute
donorAnnotation = "constellation.edgeless.systems/donor" donorAnnotation = "constellation.edgeless.systems/donor"
@ -44,7 +45,7 @@ const (
conditionNodeImageOutOfDateMessage = "Some node images are out of date" conditionNodeImageOutOfDateMessage = "Some node images are out of date"
) )
// NodeImageReconciler reconciles a NodeImage object // NodeImageReconciler reconciles a NodeImage object.
type NodeImageReconciler struct { type NodeImageReconciler struct {
nodeReplacer nodeReplacer
etcdRemover etcdRemover
@ -113,7 +114,7 @@ func (r *NodeImageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
} }
scalingGroupByID := make(map[string]updatev1alpha1.ScalingGroup, len(scalingGroupList.Items)) scalingGroupByID := make(map[string]updatev1alpha1.ScalingGroup, len(scalingGroupList.Items))
for _, scalingGroup := range scalingGroupList.Items { for _, scalingGroup := range scalingGroupList.Items {
scalingGroupByID[scalingGroup.Spec.GroupID] = scalingGroup scalingGroupByID[strings.ToLower(scalingGroup.Spec.GroupID)] = scalingGroup
} }
annotatedNodes, invalidNodes := r.annotateNodes(ctx, nodeList.Items) annotatedNodes, invalidNodes := r.annotateNodes(ctx, nodeList.Items)
groups := groupNodes(annotatedNodes, pendingNodeList.Items, desiredNodeImage.Spec.ImageReference) groups := groupNodes(annotatedNodes, pendingNodeList.Items, desiredNodeImage.Spec.ImageReference)
@ -272,7 +273,7 @@ func (r *NodeImageReconciler) pairDonorsAndHeirs(ctx context.Context, controller
// find outdated node in the same group // find outdated node in the same group
for i := range outdatedNodes { for i := range outdatedNodes {
outdatedNode := &outdatedNodes[i] outdatedNode := &outdatedNodes[i]
if outdatedNode.Annotations[scalingGroupAnnotation] != mintNode.pendingNode.Spec.ScalingGroupID || len(outdatedNode.Annotations[heirAnnotation]) != 0 { if !strings.EqualFold(outdatedNode.Annotations[scalingGroupAnnotation], mintNode.pendingNode.Spec.ScalingGroupID) || len(outdatedNode.Annotations[heirAnnotation]) != 0 {
continue continue
} }
// mark as donor <-> heir pair and delete "pending node" resource // mark as donor <-> heir pair and delete "pending node" resource
@ -302,6 +303,7 @@ func (r *NodeImageReconciler) pairDonorsAndHeirs(ctx context.Context, controller
break break
} }
if !foundReplacement { if !foundReplacement {
logr.Info("No replacement found for mint node. Marking as outdated.", "mintNode", mintNode.node.Name, "scalingGroupID", mintNode.pendingNode.Spec.ScalingGroupID)
// mint node was not needed as heir. Cleanup obsolete resources. // mint node was not needed as heir. Cleanup obsolete resources.
if err := r.Delete(ctx, &mintNode.pendingNode); err != nil { if err := r.Delete(ctx, &mintNode.pendingNode); err != nil {
logr.Error(err, "Unable to delete pending node resource", "pendingNode", mintNode.pendingNode.Name) logr.Error(err, "Unable to delete pending node resource", "pendingNode", mintNode.pendingNode.Name)
@ -503,7 +505,7 @@ func (r *NodeImageReconciler) createNewNodes(
if len(node.Annotations[heirAnnotation]) != 0 { if len(node.Annotations[heirAnnotation]) != 0 {
continue continue
} }
outdatedNodesPerScalingGroup[node.Annotations[scalingGroupAnnotation]]++ outdatedNodesPerScalingGroup[strings.ToLower(node.Annotations[scalingGroupAnnotation])]++
} }
pendingJoiningNodesPerScalingGroup := make(map[string]int) pendingJoiningNodesPerScalingGroup := make(map[string]int)
for _, pendingNode := range pendingNodes { for _, pendingNode := range pendingNodes {
@ -511,10 +513,11 @@ func (r *NodeImageReconciler) createNewNodes(
if pendingNode.Spec.Goal != updatev1alpha1.NodeGoalJoin { if pendingNode.Spec.Goal != updatev1alpha1.NodeGoalJoin {
continue continue
} }
pendingJoiningNodesPerScalingGroup[pendingNode.Spec.ScalingGroupID]++ pendingJoiningNodesPerScalingGroup[strings.ToLower(pendingNode.Spec.ScalingGroupID)]++
} }
requiredNodesPerScalingGroup := make(map[string]int, len(outdatedNodesPerScalingGroup)) requiredNodesPerScalingGroup := make(map[string]int, len(outdatedNodesPerScalingGroup))
for scalingGroupID := range outdatedNodesPerScalingGroup { for scalingGroupID := range outdatedNodesPerScalingGroup {
scalingGroupID := strings.ToLower(scalingGroupID)
if pendingJoiningNodesPerScalingGroup[scalingGroupID] < outdatedNodesPerScalingGroup[scalingGroupID] { if pendingJoiningNodesPerScalingGroup[scalingGroupID] < outdatedNodesPerScalingGroup[scalingGroupID] {
requiredNodesPerScalingGroup[scalingGroupID] = outdatedNodesPerScalingGroup[scalingGroupID] - pendingJoiningNodesPerScalingGroup[scalingGroupID] requiredNodesPerScalingGroup[scalingGroupID] = outdatedNodesPerScalingGroup[scalingGroupID] - pendingJoiningNodesPerScalingGroup[scalingGroupID]
} }
@ -522,10 +525,10 @@ func (r *NodeImageReconciler) createNewNodes(
for scalingGroupID := range requiredNodesPerScalingGroup { for scalingGroupID := range requiredNodesPerScalingGroup {
scalingGroup, ok := scalingGroupByID[scalingGroupID] scalingGroup, ok := scalingGroupByID[scalingGroupID]
if !ok { if !ok {
logr.Info("Scaling group does not have matching resource", "scalingGroup", scalingGroupID) logr.Info("Scaling group does not have matching resource", "scalingGroup", scalingGroupID, "scalingGroups", scalingGroupByID)
continue continue
} }
if scalingGroup.Status.ImageReference != desiredNodeImage.Spec.ImageReference { if !strings.EqualFold(scalingGroup.Status.ImageReference, desiredNodeImage.Spec.ImageReference) {
logr.Info("Scaling group does not use latest image", "scalingGroup", scalingGroupID, "usedImage", scalingGroup.Status.ImageReference, "wantedImage", desiredNodeImage.Spec.ImageReference) logr.Info("Scaling group does not use latest image", "scalingGroup", scalingGroupID, "usedImage", scalingGroup.Status.ImageReference, "wantedImage", desiredNodeImage.Spec.ImageReference)
continue continue
} }
@ -540,7 +543,7 @@ func (r *NodeImageReconciler) createNewNodes(
break break
} }
logr.Info("Creating new node", "scalingGroup", scalingGroupID) logr.Info("Creating new node", "scalingGroup", scalingGroupID)
nodeName, providerID, err := r.CreateNode(ctx, scalingGroupID) nodeName, providerID, err := r.CreateNode(ctx, scalingGroup.Spec.GroupID)
if err != nil { if err != nil {
return err return err
} }
@ -549,7 +552,7 @@ func (r *NodeImageReconciler) createNewNodes(
ObjectMeta: metav1.ObjectMeta{Name: nodeName}, ObjectMeta: metav1.ObjectMeta{Name: nodeName},
Spec: updatev1alpha1.PendingNodeSpec{ Spec: updatev1alpha1.PendingNodeSpec{
ProviderID: providerID, ProviderID: providerID,
ScalingGroupID: scalingGroupID, ScalingGroupID: scalingGroup.Spec.GroupID,
NodeName: nodeName, NodeName: nodeName,
Goal: updatev1alpha1.NodeGoalJoin, Goal: updatev1alpha1.NodeGoalJoin,
Deadline: &deadline, Deadline: &deadline,
@ -757,7 +760,7 @@ func groupNodes(nodes []corev1.Node, pendingNodes []updatev1alpha1.PendingNode,
groups.Obsolete = append(groups.Obsolete, node) groups.Obsolete = append(groups.Obsolete, node)
continue continue
} }
if node.Annotations[nodeImageAnnotation] != latestImageReference { if !strings.EqualFold(node.Annotations[nodeImageAnnotation], latestImageReference) {
if heir := node.Annotations[heirAnnotation]; heir != "" { if heir := node.Annotations[heirAnnotation]; heir != "" {
groups.Donors = append(groups.Donors, node) groups.Donors = append(groups.Donors, node)
} else { } else {

View File

@ -330,6 +330,9 @@ func TestCreateNewNodes(t *testing.T) {
"no outdated nodes": { "no outdated nodes": {
scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{ scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{
"scaling-group": { "scaling-group": {
Spec: updatev1alpha1.ScalingGroupSpec{
GroupID: "scaling-group",
},
Status: updatev1alpha1.ScalingGroupStatus{ Status: updatev1alpha1.ScalingGroupStatus{
ImageReference: "image", ImageReference: "image",
}, },
@ -350,6 +353,9 @@ func TestCreateNewNodes(t *testing.T) {
}, },
scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{ scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{
"scaling-group": { "scaling-group": {
Spec: updatev1alpha1.ScalingGroupSpec{
GroupID: "scaling-group",
},
Status: updatev1alpha1.ScalingGroupStatus{ Status: updatev1alpha1.ScalingGroupStatus{
ImageReference: "image", ImageReference: "image",
}, },
@ -371,6 +377,9 @@ func TestCreateNewNodes(t *testing.T) {
}, },
scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{ scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{
"scaling-group": { "scaling-group": {
Spec: updatev1alpha1.ScalingGroupSpec{
GroupID: "scaling-group",
},
Status: updatev1alpha1.ScalingGroupStatus{ Status: updatev1alpha1.ScalingGroupStatus{
ImageReference: "image", ImageReference: "image",
}, },
@ -392,6 +401,9 @@ func TestCreateNewNodes(t *testing.T) {
}, },
scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{ scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{
"scaling-group": { "scaling-group": {
Spec: updatev1alpha1.ScalingGroupSpec{
GroupID: "scaling-group",
},
Status: updatev1alpha1.ScalingGroupStatus{ Status: updatev1alpha1.ScalingGroupStatus{
ImageReference: "image", ImageReference: "image",
}, },
@ -411,6 +423,9 @@ func TestCreateNewNodes(t *testing.T) {
}, },
scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{ scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{
"scaling-group": { "scaling-group": {
Spec: updatev1alpha1.ScalingGroupSpec{
GroupID: "scaling-group",
},
Status: updatev1alpha1.ScalingGroupStatus{ Status: updatev1alpha1.ScalingGroupStatus{
ImageReference: "outdated-image", ImageReference: "outdated-image",
}, },
@ -439,6 +454,9 @@ func TestCreateNewNodes(t *testing.T) {
}, },
scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{ scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{
"scaling-group": { "scaling-group": {
Spec: updatev1alpha1.ScalingGroupSpec{
GroupID: "scaling-group",
},
Status: updatev1alpha1.ScalingGroupStatus{ Status: updatev1alpha1.ScalingGroupStatus{
ImageReference: "image", ImageReference: "image",
}, },
@ -467,6 +485,9 @@ func TestCreateNewNodes(t *testing.T) {
}, },
scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{ scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{
"scaling-group": { "scaling-group": {
Spec: updatev1alpha1.ScalingGroupSpec{
GroupID: "scaling-group",
},
Status: updatev1alpha1.ScalingGroupStatus{ Status: updatev1alpha1.ScalingGroupStatus{
ImageReference: "image", ImageReference: "image",
}, },
@ -489,6 +510,9 @@ func TestCreateNewNodes(t *testing.T) {
}, },
scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{ scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{
"scaling-group": { "scaling-group": {
Spec: updatev1alpha1.ScalingGroupSpec{
GroupID: "scaling-group",
},
Status: updatev1alpha1.ScalingGroupStatus{ Status: updatev1alpha1.ScalingGroupStatus{
ImageReference: "image", ImageReference: "image",
}, },
@ -509,11 +533,17 @@ func TestCreateNewNodes(t *testing.T) {
}, },
scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{ scalingGroupByID: map[string]updatev1alpha1.ScalingGroup{
"scaling-group": { "scaling-group": {
Spec: updatev1alpha1.ScalingGroupSpec{
GroupID: "scaling-group",
},
Status: updatev1alpha1.ScalingGroupStatus{ Status: updatev1alpha1.ScalingGroupStatus{
ImageReference: "image", ImageReference: "image",
}, },
}, },
"other-scaling-group": { "other-scaling-group": {
Spec: updatev1alpha1.ScalingGroupSpec{
GroupID: "other-scaling-group",
},
Status: updatev1alpha1.ScalingGroupStatus{ Status: updatev1alpha1.ScalingGroupStatus{
ImageReference: "image", ImageReference: "image",
}, },

View File

@ -3,6 +3,7 @@ package controllers
import ( import (
"context" "context"
"strings"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -75,7 +76,8 @@ func (r *ScalingGroupReconciler) Reconcile(ctx context.Context, req ctrl.Request
outdatedCondition := metav1.Condition{ outdatedCondition := metav1.Condition{
Type: updatev1alpha1.ConditionOutdated, Type: updatev1alpha1.ConditionOutdated,
} }
if nodeImage == desiredNodeImage.Spec.ImageReference { imagesMatch := strings.EqualFold(nodeImage, desiredNodeImage.Spec.ImageReference)
if imagesMatch {
outdatedCondition.Status = metav1.ConditionFalse outdatedCondition.Status = metav1.ConditionFalse
outdatedCondition.Reason = conditionScalingGroupUpToDateReason outdatedCondition.Reason = conditionScalingGroupUpToDateReason
outdatedCondition.Message = conditionScalingGroupUpToDateMessage outdatedCondition.Message = conditionScalingGroupUpToDateMessage
@ -90,7 +92,7 @@ func (r *ScalingGroupReconciler) Reconcile(ctx context.Context, req ctrl.Request
return ctrl.Result{}, err return ctrl.Result{}, err
} }
if nodeImage != desiredNodeImage.Spec.ImageReference { if !imagesMatch {
logr.Info("ScalingGroup NodeImage is out of date") logr.Info("ScalingGroup NodeImage is out of date")
if err := r.scalingGroupUpdater.SetScalingGroupImage(ctx, desiredScalingGroup.Spec.GroupID, desiredNodeImage.Spec.ImageReference); err != nil { if err := r.scalingGroupUpdater.SetScalingGroupImage(ctx, desiredScalingGroup.Spec.GroupID, desiredNodeImage.Spec.ImageReference); err != nil {
logr.Error(err, "Unable to set ScalingGroup NodeImage") logr.Error(err, "Unable to set ScalingGroup NodeImage")