Tolerate sub-microsecond eventTime changes on update

This commit is contained in:
Jordan Liggitt 2022-09-01 14:54:17 -04:00
parent c870f1ddc7
commit 42bb7bb458
No known key found for this signature in database
2 changed files with 77 additions and 2 deletions

View File

@ -95,7 +95,18 @@ func ValidateEventUpdate(newEvent, oldEvent *core.Event, requestVersion schema.G
allErrs = append(allErrs, ValidateImmutableField(newEvent.Count, oldEvent.Count, field.NewPath("count"))...)
allErrs = append(allErrs, ValidateImmutableField(newEvent.Reason, oldEvent.Reason, field.NewPath("reason"))...)
allErrs = append(allErrs, ValidateImmutableField(newEvent.Type, oldEvent.Type, field.NewPath("type"))...)
// Disallow changes to eventTime greater than microsecond-level precision.
// Tolerating sub-microsecond changes is required to tolerate updates
// from clients that correctly truncate to microsecond-precision when serializing,
// or from clients built with incorrect nanosecond-precision protobuf serialization.
// See https://github.com/kubernetes/kubernetes/issues/111928
newTruncated := newEvent.EventTime.Truncate(time.Microsecond).UTC()
oldTruncated := oldEvent.EventTime.Truncate(time.Microsecond).UTC()
if newTruncated != oldTruncated {
allErrs = append(allErrs, ValidateImmutableField(newEvent.EventTime, oldEvent.EventTime, field.NewPath("eventTime"))...)
}
allErrs = append(allErrs, ValidateImmutableField(newEvent.Action, oldEvent.Action, field.NewPath("action"))...)
allErrs = append(allErrs, ValidateImmutableField(newEvent.Related, oldEvent.Related, field.NewPath("related"))...)
allErrs = append(allErrs, ValidateImmutableField(newEvent.ReportingController, oldEvent.ReportingController, field.NewPath("reportingController"))...)

View File

@ -20,7 +20,7 @@ import (
"testing"
"time"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
eventsv1 "k8s.io/api/events/v1"
eventsv1beta1 "k8s.io/api/events/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -1312,3 +1312,67 @@ func TestValidateEventUpdateForNewV1Events(t *testing.T) {
}
}
}
func TestEventV1EventTimeImmutability(t *testing.T) {
testcases := []struct {
Name string
Old metav1.MicroTime
New metav1.MicroTime
Valid bool
}{
{
Name: "noop microsecond precision",
Old: metav1.NewMicroTime(time.Unix(100, int64(5*time.Microsecond))),
New: metav1.NewMicroTime(time.Unix(100, int64(5*time.Microsecond))),
Valid: true,
},
{
Name: "noop nanosecond precision",
Old: metav1.NewMicroTime(time.Unix(100, int64(5*time.Nanosecond))),
New: metav1.NewMicroTime(time.Unix(100, int64(5*time.Nanosecond))),
Valid: true,
},
{
Name: "modify nanoseconds within the same microsecond",
Old: metav1.NewMicroTime(time.Unix(100, int64(5*time.Nanosecond))),
New: metav1.NewMicroTime(time.Unix(100, int64(6*time.Nanosecond))),
Valid: true,
},
{
Name: "modify microseconds",
Old: metav1.NewMicroTime(time.Unix(100, int64(5*time.Microsecond))),
New: metav1.NewMicroTime(time.Unix(100, int64(5*time.Microsecond-time.Nanosecond))),
Valid: false,
},
{
Name: "modify seconds",
Old: metav1.NewMicroTime(time.Unix(100, 0)),
New: metav1.NewMicroTime(time.Unix(101, 0)),
Valid: false,
},
}
for _, tc := range testcases {
t.Run(tc.Name, func(t *testing.T) {
oldEvent := &core.Event{
ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: metav1.NamespaceSystem, ResourceVersion: "2"},
InvolvedObject: core.ObjectReference{APIVersion: "v2", Kind: "Node"},
Series: &core.EventSeries{Count: 2, LastObservedTime: tc.Old},
EventTime: tc.Old,
ReportingController: "k8s.io/my-controller",
ReportingInstance: "node-xyz",
Action: "Do",
Reason: "Yeees",
Type: "Normal",
}
newEvent := oldEvent.DeepCopy()
newEvent.EventTime = tc.New
updateErrs := ValidateEventUpdate(newEvent, oldEvent, eventsv1.SchemeGroupVersion)
if e, a := tc.Valid, len(updateErrs) == 0; e != a {
t.Errorf("%v: expected valid=%v, got %v: %v", tc.Valid, e, a, updateErrs)
}
})
}
}