Preservation bugfix to put PreserveExpiryTime whenever machine transitions to Failed phase - #1147
Preservation bugfix to put PreserveExpiryTime whenever machine transitions to Failed phase#1147r4mek wants to merge 3 commits into
Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
/kind bug |
thiyyakat
left a comment
There was a problem hiding this comment.
Thanks for the changes and the refactoring! I haven't looked at the full PR yet. I had one case in mind for which I thought the new code may not behave as expected. Once you address that, I will look at the rest as well.
| defer trackers.Stop() | ||
| waitForCacheSync(stop, c) | ||
| err := c.updateMachineAndMachineDeploymentDeletionAnnotations(context.TODO(), testMachineDeployment) | ||
| err := func() error { |
There was a problem hiding this comment.
This may be a stupid question, but what is the point of the closure here? Can you not simply have just L2361?
| node.Annotations[PreserveMachineAnnotationKey]; ok && | ||
| AllowedPreserveAnnotationValues.Has(val) { | ||
| return val, true |
There was a problem hiding this comment.
Please print a warning here if the value is not valid.
| if val, ok := | ||
| machine.Annotations[PreserveMachineAnnotationKey]; ok && | ||
| AllowedPreserveAnnotationValues.Has(val) { | ||
| return val, true |
There was a problem hiding this comment.
Please log a warning if the value is not valid.
| preserveAnnotationValue, exists := machineutils.GetPreserveAnnotationValue(node, machine) | ||
| if !exists { | ||
| return | ||
| } else if getErr != nil { | ||
| if !apierrors.IsNotFound(getErr) { | ||
| err = getErr | ||
| return | ||
| } | ||
| klog.Warningf("Couldn't find node %q for machine %q", nodeName, machine.Name) | ||
| err = nil | ||
| } |
There was a problem hiding this comment.
Please correct me if I'm wrong, but what about the case where preservation was being manipulated by only the machine object's annotations? If a machine was preserved, and the annotation was deleted to indicate that preservation should be stopped, you will end up returning early here instead of stopping preservation.
There was a problem hiding this comment.
Good catch! Addressed in the latest commit ab6bf44
gagan16k
left a comment
There was a problem hiding this comment.
Thanks for the changes! Have some comments, PTAL
| machineDeepCopy.Annotations[machineutils.MarkedForDeletionTime] = tgd.markedMachineDeletionTimes[i] | ||
| } | ||
| _, err = dc.controlMachineClient.Machines(machine.Namespace).Update(ctx, machineDeepCopy, metav1.UpdateOptions{}) | ||
| deletionTime := tgd.markedMachineDeletionTimes[i] |
There was a problem hiding this comment.
Do we need this variable deletionTime?
| return mcd, err | ||
| } | ||
| // TODO: not neat. refactor later. | ||
| for _, machineList := range machineMap { |
There was a problem hiding this comment.
Can we key the machineMap by the UID of the machineSet here, might save a loop
Something like
if controllerRef := metav1.GetControllerOf(updatedMachine); controllerRef != nil {
if machineList, ok := machineMap[controllerRef.UID]; ok {
for i := range machineList.Items {
| m.Status.CurrentStatus.PreserveExpiryTime = &metav1.Time{Time: metav1.Now().Add(c.safetyOptions.MachinePreserveTimeout.Duration)} | ||
| } | ||
| return nil | ||
| }, true) |
There was a problem hiding this comment.
As per docs
The PUT and POST verbs on objects MUST ignore the status values, to avoid accidentally overwriting the status in read-modify-write scenarios. A /status subresource MUST be provided to enable system components to update statuses of resources they manage.
On annotating machine (with mcm scaled to zero to avoid it reconciling) with these keys and dummy values
(⎈|garden--aws-ha-external:garden shoot--xxx--demo)➜ machine-controller-manager git:(pr/r4mek/1147) ✗ k patch mc shoot--xxx--demo-worker-cpu-z1-58c68-qjrl2 --type=merge -p '{
"metadata":{"annotations":{"node.machine.sapcloud.io/preserve":"auto-preserved"}},
"status":{"currentStatus":{"preserveExpiryTime":"2030-01-01T00:00:00Z"}}
}'
machine.machine.sapcloud.io/shoot--xxx--demo-worker-cpu-z1-58c68-qjrl2 patched
(⎈|garden--aws-ha-external:garden shoot--xxx--demo)➜ machine-controller-manager git:(pr/r4mek/1147) ✗ k get mc shoot--xxx--demo-worker-cpu-z1-58c68-qjrl2 -oyaml | grep "annotations" -A 2
annotations:
machinepriority.machine.sapcloud.io: "3"
node.machine.sapcloud.io/preserve: auto-preserved
(⎈|garden--aws-ha-external:garden shoot--xxx--demo)➜ machine-controller-manager git:(pr/r4mek/1147) ✗ k get mc shoot--xxx--demo-worker-cpu-z1-58c68-qjrl2 -oyaml | grep "preserveExpiryTime"
(⎈|garden--aws-ha-external:garden shoot--xxx--demo)➜ machine-controller-manager git:(pr/r4mek/1147) ✗
As the "/status" subresource is not specified here, it drops the change on it. Maybe we would need two different patches for each change as they target different resource endpoints?.
| }) | ||
| updatedMachine, err := machineutils.PatchMachine(ctx, c.Machines(machine.Namespace), &machine, func(m *v1alpha1.Machine) error { | ||
| m.Labels = labelsutil.AddLabel(m.Labels, v1alpha1.DefaultMachineDeploymentUniqueLabelKey, hash) | ||
| return nil |
There was a problem hiding this comment.
This will force a reconcile instead of retry on returning 409 errors, so the unit test for LDRCBST could flake even more now. Might need to find another way to correct that.
| // Deep-copy to avoid mutating cache objects downstream. | ||
| for i, m := range filteredMachines { | ||
| filteredMachines[i] = m.DeepCopy() | ||
| } | ||
|
|
There was a problem hiding this comment.
Great change, but I was thinking if we could apply this while creating the filteredMachines slices, in claimMachines().
Argument for: Saves extra work on the slice, and does it in the same loop while the slice is getting constructed.
Argument against: The other callers for the function, do not seem to write to the slice like this reconciler does, and thus do not really need a deep copy. WDYT?
| machine.Name, | ||
| machineutils.PreserveMachineAnnotationKey, | ||
| machine.Annotations[machineutils.PreserveMachineAnnotationKey], | ||
| machine.Annotations[v1alpha1.NodeLabelKey], |
There was a problem hiding this comment.
| machine.Annotations[v1alpha1.NodeLabelKey], | |
| machine.Labels[v1alpha1.NodeLabelKey], |
| return nil | ||
| } | ||
|
|
||
| func (c *controller) findEffectivePreserveValue(machine *v1alpha1.Machine) (string, error) { |
There was a problem hiding this comment.
This function is redundant with the introduction of GetPreserveAnnotationValue()
| if val, ok := | ||
| node.Annotations[PreserveMachineAnnotationKey]; ok && | ||
| AllowedPreserveAnnotationValues.Has(val) { |
There was a problem hiding this comment.
| if val, ok := | |
| node.Annotations[PreserveMachineAnnotationKey]; ok && | |
| AllowedPreserveAnnotationValues.Has(val) { | |
| if val, ok := node.Annotations[PreserveMachineAnnotationKey]; ok && AllowedPreserveAnnotationValues.Has(val) { |
Not a strong opinion, but I found this kind of confusing to read the first time. But that could be just me, feel free to ignore this comment.
| klog.V(4).Infof("Machine %s precondition doesn't hold, skip updating it.", name) | ||
| retryErr = nil | ||
| } | ||
| return machineClient.Patch(ctx, machine.Name, types.MergePatchType, patch, metav1.PatchOptions{}, subresources...) |
There was a problem hiding this comment.
| return machineClient.Patch(ctx, machine.Name, types.MergePatchType, patch, metav1.PatchOptions{}, subresources...) | |
| return machineClient.Patch(ctx, machine.Name, types.StrategicMergePatchType, patch, metav1.PatchOptions{}, subresources...) |
| patch, err := strategicpatch.CreateTwoWayMergePatch(base, modifiedJSON, v1alpha1.Machine{}) | ||
| if err != nil { | ||
| return nil, err | ||
| } |
There was a problem hiding this comment.
| } | |
| } | |
| if string(patch) == "{}" { | |
| return machine, nil | |
| } |
We could short-circuit if the patch is empty for whatever reason, as the request will still be considered valid by the API server
What this PR does / why we need it:
PreserveExpiryTimeon the machine if the machine has annotation:node.machine.sapcloud.io/preserve: "when-failed"and it transitions toFailedphase.PreserveExpiryTimeon the machine if the failed machine gets the annotation:node.machine.sapcloud.io/preserve: "auto-preserved"UpdateMachineWithRetries()from the lister cache, which may return a stale object.Which issue(s) this PR fixes:
Fixes #
Special notes for your reviewer:
Release note: