diff --git a/docs/documents/apis.md b/docs/documents/apis.md index 9b2668d1d..1ed350d9e 100644 --- a/docs/documents/apis.md +++ b/docs/documents/apis.md @@ -2435,6 +2435,22 @@ Kubernetes core/v1.ResourceList +virtualCapacity + + + + +Kubernetes core/v1.ResourceList + + + + +(Optional) +

VirtualCapacity represents the expected Node ‘virtual’ capacity ie comprising virtual extended resources.

+ + + + instanceType diff --git a/kubernetes/crds/machine.sapcloud.io_machineclasses.yaml b/kubernetes/crds/machine.sapcloud.io_machineclasses.yaml index 4452c16c2..f0cd9d515 100644 --- a/kubernetes/crds/machine.sapcloud.io_machineclasses.yaml +++ b/kubernetes/crds/machine.sapcloud.io_machineclasses.yaml @@ -78,6 +78,16 @@ spec: region: description: Region of the expected node belonging to nodeGroup type: string + virtualCapacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: VirtualCapacity represents the expected Node 'virtual' + capacity ie comprising virtual extended resources. + type: object zone: description: Zone of the expected node belonging to nodeGroup type: string diff --git a/pkg/apis/machine/types.go b/pkg/apis/machine/types.go index 8ac47c2f0..0facf09ba 100644 --- a/pkg/apis/machine/types.go +++ b/pkg/apis/machine/types.go @@ -743,6 +743,10 @@ type NodeTemplate struct { // Capacity contains subfields to track all node resources required to scale nodegroup from zero Capacity corev1.ResourceList + // VirtualCapacity represents the expected Node 'virtual' capacity ie comprising virtual extended resources. + // +optional + VirtualCapacity corev1.ResourceList + // Instance type of the node belonging to nodeGroup InstanceType string diff --git a/pkg/apis/machine/v1alpha1/machineclass_types.go b/pkg/apis/machine/v1alpha1/machineclass_types.go index 59ee9e1e0..2db540a3f 100644 --- a/pkg/apis/machine/v1alpha1/machineclass_types.go +++ b/pkg/apis/machine/v1alpha1/machineclass_types.go @@ -66,6 +66,10 @@ type NodeTemplate struct { // Capacity contains subfields to track all node resources required to scale nodegroup from zero Capacity corev1.ResourceList `json:"capacity"` + // VirtualCapacity represents the expected Node 'virtual' capacity ie comprising virtual extended resources. + // +optional + VirtualCapacity corev1.ResourceList `json:"virtualCapacity,omitempty"` + // Instance type of the node belonging to nodeGroup InstanceType string `json:"instanceType"` diff --git a/pkg/apis/machine/v1alpha1/zz_generated.conversion.go b/pkg/apis/machine/v1alpha1/zz_generated.conversion.go index 1ef0f3aeb..7d5d1b485 100644 --- a/pkg/apis/machine/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/machine/v1alpha1/zz_generated.conversion.go @@ -1052,6 +1052,7 @@ func Convert_machine_MachineTemplateSpec_To_v1alpha1_MachineTemplateSpec(in *mac func autoConvert_v1alpha1_NodeTemplate_To_machine_NodeTemplate(in *NodeTemplate, out *machine.NodeTemplate, s conversion.Scope) error { out.Capacity = *(*v1.ResourceList)(unsafe.Pointer(&in.Capacity)) + out.VirtualCapacity = *(*v1.ResourceList)(unsafe.Pointer(&in.VirtualCapacity)) out.InstanceType = in.InstanceType out.Region = in.Region out.Zone = in.Zone @@ -1066,6 +1067,7 @@ func Convert_v1alpha1_NodeTemplate_To_machine_NodeTemplate(in *NodeTemplate, out func autoConvert_machine_NodeTemplate_To_v1alpha1_NodeTemplate(in *machine.NodeTemplate, out *NodeTemplate, s conversion.Scope) error { out.Capacity = *(*v1.ResourceList)(unsafe.Pointer(&in.Capacity)) + out.VirtualCapacity = *(*v1.ResourceList)(unsafe.Pointer(&in.VirtualCapacity)) out.InstanceType = in.InstanceType out.Region = in.Region out.Zone = in.Zone diff --git a/pkg/apis/machine/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/machine/v1alpha1/zz_generated.deepcopy.go index a5f4b0571..2691557c9 100644 --- a/pkg/apis/machine/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/machine/v1alpha1/zz_generated.deepcopy.go @@ -686,6 +686,13 @@ func (in *NodeTemplate) DeepCopyInto(out *NodeTemplate) { (*out)[key] = val.DeepCopy() } } + if in.VirtualCapacity != nil { + in, out := &in.VirtualCapacity, &out.VirtualCapacity + *out = make(v1.ResourceList, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } if in.Architecture != nil { in, out := &in.Architecture, &out.Architecture *out = new(string) diff --git a/pkg/apis/machine/zz_generated.deepcopy.go b/pkg/apis/machine/zz_generated.deepcopy.go index dd0487331..05c40b651 100644 --- a/pkg/apis/machine/zz_generated.deepcopy.go +++ b/pkg/apis/machine/zz_generated.deepcopy.go @@ -779,6 +779,13 @@ func (in *NodeTemplate) DeepCopyInto(out *NodeTemplate) { (*out)[key] = val.DeepCopy() } } + if in.VirtualCapacity != nil { + in, out := &in.VirtualCapacity, &out.VirtualCapacity + *out = make(v1.ResourceList, len(*in)) + for key, val := range *in { + (*out)[key] = val.DeepCopy() + } + } if in.Architecture != nil { in, out := &in.Architecture, &out.Architecture *out = new(string) diff --git a/pkg/openapi/openapi_generated.go b/pkg/openapi/openapi_generated.go index f602824f5..c4ee08c09 100644 --- a/pkg/openapi/openapi_generated.go +++ b/pkg/openapi/openapi_generated.go @@ -1648,6 +1648,20 @@ func schema_pkg_apis_machine_v1alpha1_NodeTemplate(ref common.ReferenceCallback) }, }, }, + "virtualCapacity": { + SchemaProps: spec.SchemaProps{ + Description: "VirtualCapacity represents the expected Node 'virtual' capacity ie comprising virtual extended resources.", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, "instanceType": { SchemaProps: spec.SchemaProps{ Description: "Instance type of the node belonging to nodeGroup", diff --git a/pkg/util/provider/machinecontroller/machine.go b/pkg/util/provider/machinecontroller/machine.go index a15710f10..53cca4460 100644 --- a/pkg/util/provider/machinecontroller/machine.go +++ b/pkg/util/provider/machinecontroller/machine.go @@ -220,7 +220,7 @@ func (c *controller) reconcileClusterMachine(ctx context.Context, machine *v1alp return retry, err } - retry, err = c.syncMachineNodeTemplates(ctx, machine) + retry, err = c.syncNodeTemplates(ctx, machine, machineClass) if err != nil { return retry, err } diff --git a/pkg/util/provider/machinecontroller/machine_util.go b/pkg/util/provider/machinecontroller/machine_util.go index 7e7e41430..7f76293ac 100644 --- a/pkg/util/provider/machinecontroller/machine_util.go +++ b/pkg/util/provider/machinecontroller/machine_util.go @@ -486,14 +486,17 @@ func (c *controller) updateMachineStatusAndNodeCondition(ctx context.Context, ma return machineutils.ShortRetry, err } -// syncMachineNodeTemplate syncs nodeTemplates between machine and corresponding node-object. -// It ensures, that any nodeTemplate element available on Machine should be available on node-object. +// syncNodeTemplates syncs nodeTemplates between machine, machineClass and corresponding node-object. +// It ensures that any nodeTemplate element available on Machine should be available on node-object. +// It ensures that MachineClass.NodeTemplate.VirtualCapacity is synced to the Node's Capacity. // Although there could be more elements already available on node-object which will not be touched. -func (c *controller) syncMachineNodeTemplates(ctx context.Context, machine *v1alpha1.Machine) (machineutils.RetryPeriod, error) { +func (c *controller) syncNodeTemplates(ctx context.Context, machine *v1alpha1.Machine, machineClass *v1alpha1.MachineClass) (machineutils.RetryPeriod, error) { var ( - initializedNodeAnnotation bool - currentlyAppliedALTJSONByte []byte - lastAppliedALT v1alpha1.NodeTemplateSpec + initializedNodeAnnotation bool + currentlyAppliedALTJSONByte []byte + lastAppliedALT v1alpha1.NodeTemplateSpec + currentlyAppliedVirtualCapacityJSONByte []byte + lastAppliedVirtualCapacity v1.ResourceList ) node, err := c.nodeLister.Get(getNodeName(machine)) @@ -524,10 +527,30 @@ func (c *controller) syncMachineNodeTemplates(ctx context.Context, machine *v1al } } + lastAppliedVirtualCapacityJSONString, exists := node.Annotations[machineutils.LastAppliedVirtualCapacityAnnotation] + if exists { + err = json.Unmarshal([]byte(lastAppliedVirtualCapacityJSONString), &lastAppliedVirtualCapacity) + if err != nil { + klog.Errorf("Error occurred while syncing node virtual capacity: %s", err) + return machineutils.ShortRetry, err + } + } + annotationsChanged := SyncMachineAnnotations(machine, nodeCopy, lastAppliedALT.Annotations) labelsChanged := SyncMachineLabels(machine, nodeCopy, lastAppliedALT.Labels) taintsChanged := SyncMachineTaints(machine, nodeCopy, lastAppliedALT.Spec.Taints) + var virtualCapacityChanged bool + var desiredVirtualCapacity v1.ResourceList + if machineClass != nil && machineClass.NodeTemplate != nil { + desiredVirtualCapacity = machineClass.NodeTemplate.VirtualCapacity + virtualCapacityChanged = SyncVirtualCapacity(desiredVirtualCapacity, nodeCopy, lastAppliedVirtualCapacity) + } + + if !initializedNodeAnnotation && !annotationsChanged && !labelsChanged && !taintsChanged && !virtualCapacityChanged { + return machineutils.LongRetry, nil + } + // Update node-object with latest nodeTemplate elements if elements have changed. if initializedNodeAnnotation || labelsChanged || annotationsChanged || taintsChanged { @@ -548,23 +571,44 @@ func (c *controller) syncMachineNodeTemplates(ctx context.Context, machine *v1al return machineutils.ShortRetry, err } nodeCopy.Annotations[machineutils.LastAppliedALTAnnotation] = string(currentlyAppliedALTJSONByte) + } - _, err := c.targetCoreClient.CoreV1().Nodes().Update(ctx, nodeCopy, metav1.UpdateOptions{}) + if virtualCapacityChanged { + klog.V(3).Infof("virtualCapacity changed, attempting UpdateStatus for node.Status.Capacity of node %q to %v", nodeCopy.Name, nodeCopy.Status.Capacity) + // must patch the Node’s status subresource, because capacity lives under status + nodeUpdated, err := c.targetCoreClient.CoreV1().Nodes().UpdateStatus(ctx, nodeCopy, metav1.UpdateOptions{}) if err != nil { - // Keep retrying until update goes through - klog.Errorf("Updated failed for node object of machine %q. Retrying, error: %q", machine.Name, err) + klog.Errorf("UpdateStatus failed for node %q of machine %q. error: %q", node.Name, machine.Name, err) + return machineutils.ShortRetry, err + } + klog.V(3).Infof("node.Status.Capacity of node %q updated to: %v", node.Name, nodeUpdated.Status.Capacity) + currentlyAppliedVirtualCapacityJSONByte, err = json.Marshal(desiredVirtualCapacity) + if err != nil { + klog.Errorf("Error occurred while syncing node virtual capacity of node %q: %v", node.Name, err) + return machineutils.ShortRetry, err + } + nodeCopy = nodeUpdated.DeepCopy() + if len(desiredVirtualCapacity) == 0 { + delete(nodeCopy.Annotations, machineutils.LastAppliedVirtualCapacityAnnotation) } else { - // Return error to continue in next reconcile - err = errSuccessfulALTsync + nodeCopy.Annotations[machineutils.LastAppliedVirtualCapacityAnnotation] = string(currentlyAppliedVirtualCapacityJSONByte) } + } - if apierrors.IsConflict(err) { - return machineutils.ConflictRetry, err - } - return machineutils.ShortRetry, err + _, err = c.targetCoreClient.CoreV1().Nodes().Update(ctx, nodeCopy, metav1.UpdateOptions{}) + if err != nil { + // Keep retrying until update goes through + klog.Errorf("Updated failed for node object of machine %q. Retrying, error: %q", machine.Name, err) + } else { + // Return error to continue in next reconcile + err = errSuccessfulALTsync } - return machineutils.LongRetry, nil + if apierrors.IsConflict(err) { + return machineutils.ConflictRetry, err + } + return machineutils.ShortRetry, err + } // SyncMachineAnnotations syncs the annotations of the machine with node-objects. @@ -719,6 +763,37 @@ func SyncMachineTaints( return toBeUpdated } +// SyncVirtualCapacity syncs the MachineClass.NodeTemplate.VirtualCapacity with the Node.Status.Capacity +// It returns true if update is needed else false. +func SyncVirtualCapacity(desiredVirtualCapacity v1.ResourceList, node *v1.Node, lastAppliedVirtualCapacity v1.ResourceList) bool { + toBeUpdated := false + + if node.Status.Capacity == nil { + node.Status.Capacity = v1.ResourceList{} + } + if desiredVirtualCapacity == nil { + desiredVirtualCapacity = v1.ResourceList{} + } + + // Delete any keys that existed in the past but has been deleted now + for prevKey := range lastAppliedVirtualCapacity { + if _, exists := desiredVirtualCapacity[prevKey]; !exists { + delete(node.Status.Capacity, prevKey) + toBeUpdated = true + } + } + + // Add/Update any key that doesn't exist or whose value as changed + for targKey, targQuant := range desiredVirtualCapacity { + if nodeQuant, exists := node.Status.Capacity[targKey]; !exists || !nodeQuant.Equal(targQuant) { + node.Status.Capacity[targKey] = targQuant + toBeUpdated = true + } + } + + return toBeUpdated +} + // machineCreateErrorHandler updates the machine status based on // CreateMachineResponse and the error during the machine creation func (c *controller) machineCreateErrorHandler(ctx context.Context, machine *v1alpha1.Machine, createMachineResponse *driver.CreateMachineResponse, err error) (machineutils.RetryPeriod, error) { diff --git a/pkg/util/provider/machinecontroller/machine_util_test.go b/pkg/util/provider/machinecontroller/machine_util_test.go index 26d928a9b..53e92a6ba 100644 --- a/pkg/util/provider/machinecontroller/machine_util_test.go +++ b/pkg/util/provider/machinecontroller/machine_util_test.go @@ -9,6 +9,7 @@ import ( "encoding/json" "errors" "fmt" + "k8s.io/klog/v2" "time" machinev1 "github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1" @@ -22,7 +23,6 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/klog/v2" "k8s.io/utils/ptr" ) @@ -35,9 +35,10 @@ const ( ) var _ = Describe("machine_util", func() { - Describe("#syncMachineNodeTemplates", func() { + Describe("#syncNodeTemplates", func() { type setup struct { - machine *machinev1.Machine + machine *machinev1.Machine + machineClass *machinev1.MachineClass } type action struct { node *corev1.Node @@ -61,6 +62,7 @@ var _ = Describe("machine_util", func() { coreObjects := []runtime.Object{} machineObject := data.setup.machine + machineClass := data.setup.machineClass nodeObject := data.action.node coreObjects = append(coreObjects, nodeObject) @@ -70,7 +72,7 @@ var _ = Describe("machine_util", func() { defer trackers.Stop() waitForCacheSync(stop, c) - _, err := c.syncMachineNodeTemplates(context.TODO(), machineObject) + _, err := c.syncNodeTemplates(context.TODO(), machineObject, machineClass) waitForCacheSync(stop, c) @@ -87,6 +89,7 @@ var _ = Describe("machine_util", func() { if data.expect.node != nil { Expect(updatedNodeObject.Spec.Taints).Should(ConsistOf(data.expect.node.Spec.Taints)) Expect(updatedNodeObject.Labels).Should(Equal(data.expect.node.Labels)) + Expect(updatedNodeObject.Status.Capacity).Should(Equal(data.expect.node.Status.Capacity)) // ignore LastAppliedALTAnnotataion delete(updatedNodeObject.Annotations, machineutils.LastAppliedALTAnnotation) @@ -132,6 +135,17 @@ var _ = Describe("machine_util", func() { }, &machinev1.MachineStatus{}, nil, nil, map[string]string{machinev1.NodeLabelKey: "test-node-0"}, true, metav1.Now()), + machineClass: &machinev1.MachineClass{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-machine-class", + }, + NodeTemplate: &machinev1.NodeTemplate{ + VirtualCapacity: corev1.ResourceList{ + "virtual.com/dongle": resource.MustParse("2"), + }, + }, + }, }, action: action{ node: &corev1.Node{ @@ -170,6 +184,7 @@ var _ = Describe("machine_util", func() { Namespace: testNamespace, Annotations: map[string]string{ "anno1": "anno1", + machineutils.LastAppliedVirtualCapacityAnnotation: "{\"virtual.com/dongle\":\"2\"}", }, Labels: map[string]string{ "key1": "value1", @@ -184,6 +199,11 @@ var _ = Describe("machine_util", func() { }, }, }, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + "virtual.com/dongle": resource.MustParse("2"), + }, + }, }, err: errSuccessfulALTsync, }, @@ -1842,6 +1862,243 @@ var _ = Describe("machine_util", func() { ) }) + Describe("#SyncVirtualCapacity", func() { + type action struct { + node *corev1.Node + desiredVirtualCapacity corev1.ResourceList + } + type expect struct { + node *corev1.Node + virtualCapacityChanged bool + } + type data struct { + action action + expect expect + } + + DescribeTable("##table", + func(data *data) error { + testNode := data.action.node.DeepCopy() + desiredVirtualCapacity := data.action.desiredVirtualCapacity + expectedNode := data.expect.node + + var lastAppliedVirtualCapacity corev1.ResourceList + lastAppliedVirtualCapacityJSONString, exists := testNode.Annotations[machineutils.LastAppliedVirtualCapacityAnnotation] + if exists { + err := json.Unmarshal([]byte(lastAppliedVirtualCapacityJSONString), &lastAppliedVirtualCapacity) + if err != nil { + return fmt.Errorf("cannot unmarshall %q annotation: %w", machineutils.LastAppliedVirtualCapacityAnnotation, err) + } + } + + virtualCapacityChanged := SyncVirtualCapacity(desiredVirtualCapacity, testNode, lastAppliedVirtualCapacity) + + Expect(virtualCapacityChanged).To(Equal(data.expect.virtualCapacityChanged)) + Expect(testNode.Status.Capacity).Should(Equal(expectedNode.Status.Capacity)) + + lastAppliedVirtualCapacityJSONString = expectedNode.Annotations[machineutils.LastAppliedVirtualCapacityAnnotation] + lastAppliedVirtualCapacity = corev1.ResourceList{} + err := json.Unmarshal([]byte(lastAppliedVirtualCapacityJSONString), &lastAppliedVirtualCapacity) + if err != nil { + return fmt.Errorf("cannot unmarshall %q annotation with value %q: %w", machineutils.LastAppliedVirtualCapacityAnnotation, lastAppliedVirtualCapacityJSONString, err) + } + Expect(lastAppliedVirtualCapacity).To(Equal(desiredVirtualCapacity)) + return nil + }, + + Entry("when node.status.capacity has not been changed", &data{ + action: action{ + desiredVirtualCapacity: corev1.ResourceList{ + "hc.hana.com/memory": resource.MustParse("1234567"), + }, + node: &corev1.Node{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Node", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-node-0", + Annotations: map[string]string{ + machineutils.LastAppliedVirtualCapacityAnnotation: "{\"hc.hana.com/memory\":\"1234567\"}", + }, + }, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1234567"), + "hc.hana.com/memory": resource.MustParse("1234567"), + }, + }, + }, + }, + expect: expect{ + node: &corev1.Node{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Node", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-node-0", + Annotations: map[string]string{ + machineutils.LastAppliedVirtualCapacityAnnotation: "{\"hc.hana.com/memory\":\"1234567\"}", + }, + }, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1234567"), + "hc.hana.com/memory": resource.MustParse("1234567"), + }, + }, + }, + virtualCapacityChanged: false, + }, + }), + + Entry("when virtual resource value is changed in virtualCapacity", &data{ + action: action{ + desiredVirtualCapacity: corev1.ResourceList{ + "hc.hana.com/memory": resource.MustParse("2234567"), + }, + node: &corev1.Node{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Node", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-node-0", + Annotations: map[string]string{ + machineutils.LastAppliedVirtualCapacityAnnotation: "{\"hc.hana.com/memory\":\"1234567\"}", + }, + }, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1234567"), + "hc.hana.com/memory": resource.MustParse("1234567"), + }, + }, + }, + }, + expect: expect{ + node: &corev1.Node{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Node", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-node-0", + Annotations: map[string]string{ + machineutils.LastAppliedVirtualCapacityAnnotation: "{\"hc.hana.com/memory\":\"2234567\"}", + }, + }, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1234567"), + "hc.hana.com/memory": resource.MustParse("2234567"), + }, + }, + }, + virtualCapacityChanged: true, + }, + }), + + Entry("when virtual resources are added in virtualCapacity", &data{ + action: action{ + desiredVirtualCapacity: corev1.ResourceList{ + "hc.hana.com/memory": resource.MustParse("1111111"), + "hc.hana.com/cpu": resource.MustParse("2"), + }, + node: &corev1.Node{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Node", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-node-0", + Annotations: map[string]string{ + machineutils.LastAppliedVirtualCapacityAnnotation: "{\"hc.hana.com/memory\":\"1111111\"}", + }, + }, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1234567"), + "hc.hana.com/memory": resource.MustParse("1111111"), + }, + }, + }, + }, + expect: expect{ + node: &corev1.Node{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Node", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-node-0", + Annotations: map[string]string{ + machineutils.LastAppliedVirtualCapacityAnnotation: "{\"hc.hana.com/cpu\":\"2\",\"hc.hana.com/memory\":\"1111111\"}", + }, + }, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1234567"), + "hc.hana.com/memory": resource.MustParse("1111111"), + "hc.hana.com/cpu": resource.MustParse("2"), + }, + }, + }, + virtualCapacityChanged: true, + }, + }), + + Entry("when virtual resources are deleted in virtualCapacity", &data{ + action: action{ + desiredVirtualCapacity: corev1.ResourceList{ + "hc.hana.com/cpu": resource.MustParse("2"), + }, + node: &corev1.Node{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Node", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-node-0", + Annotations: map[string]string{ + machineutils.LastAppliedVirtualCapacityAnnotation: "{\"hc.hana.com/cpu\":\"2\",\"hc.hana.com/memory\":\"1111111\"}", + }, + }, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1234567"), + "hc.hana.com/cpu": resource.MustParse("2"), + "hc.hana.com/memory": resource.MustParse("1111111"), + }, + }, + }, + }, + expect: expect{ + node: &corev1.Node{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Node", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-node-0", + Annotations: map[string]string{ + machineutils.LastAppliedVirtualCapacityAnnotation: "{\"hc.hana.com/cpu\":\"2\"}", + }, + }, + Status: corev1.NodeStatus{ + Capacity: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1234567"), + "hc.hana.com/cpu": resource.MustParse("2"), + }, + }, + }, + virtualCapacityChanged: true, + }, + }), + ) + }) + Describe("#isMachineStatusSimilar", func() { type setup struct { m1, m2 machinev1.MachineStatus diff --git a/pkg/util/provider/machinecontroller/machineclass.go b/pkg/util/provider/machinecontroller/machineclass.go index d4a8b2392..975dfce20 100644 --- a/pkg/util/provider/machinecontroller/machineclass.go +++ b/pkg/util/provider/machinecontroller/machineclass.go @@ -115,6 +115,7 @@ func (c *controller) reconcileClusterMachineClass(ctx context.Context, class *v1 if class.DeletionTimestamp == nil && len(machines) > 0 { // If deletionTimestamp is not set and at least one machine is referring this machineClass + var reason string if finalizers := sets.NewString(class.Finalizers...); !finalizers.Has(MCMFinalizerName) { // Add machineClassFinalizer as if doesn't exist err = c.addMCMFinalizerToMachineClass(ctx, class) @@ -124,11 +125,14 @@ func (c *controller) reconcileClusterMachineClass(ctx context.Context, class *v1 // Enqueue all machines once finalizer is added to machineClass // This is to allow processing of such machines - for _, machine := range machines { - c.enqueueMachine(machine, "finalizer placed on machineClass") - } + reason = "finalizer placed on machineClass" + } else { + reason = fmt.Sprintf("machine class %q was updated", class.Name) } + for _, m := range machines { + c.enqueueMachine(m, reason) + } return nil } diff --git a/pkg/util/provider/machineutils/utils.go b/pkg/util/provider/machineutils/utils.go index dd9dae817..5389aa96a 100644 --- a/pkg/util/provider/machineutils/utils.go +++ b/pkg/util/provider/machineutils/utils.go @@ -41,6 +41,9 @@ const ( // LastAppliedALTAnnotation contains the last configuration of annotations, labels & taints applied on the node object LastAppliedALTAnnotation = "node.machine.sapcloud.io/last-applied-anno-labels-taints" + // LastAppliedVirtualCapacityAnnotation contains the last configuration of MachineClass.NodeTemplate.VirtualCapacity applied on the node object + LastAppliedVirtualCapacityAnnotation = "node.machine.sapcloud.io/last-applied-virtual-capacity" + // MachinePriority is the annotation used to specify priority // associated with a machine while deleting it. The less its // priority the more likely it is to be deleted first