daemon: prevent invalid swarm events from unknown action kinds
- Replace clusterEventAction map with switch for exhaustive checking - Make eventTimestamp() switch exhaustive with explicit Unknown handling - Use early returns instead of fallthrough in eventTimestamp - Add test cases for unknown and invalid action kinds - Rename test field 'name' to 'doc' and loop variable 'tt' to 'tc' This change ensures that: - Invalid action kinds are filtered out before publishing events - The linter can catch missing action kinds - All code paths use valid timestamps Signed-off-by: hiroto.toyoda <hiroto.toyoda@dena.com>
This commit is contained in:
1 parent
f222c1ed05
commit
2d0676b8bb
2 files changed
+99
-13
No files matched your search
+26
-13
@@ -241,15 +241,25 @@ func (daemon *Daemon) logServiceEvent(action swarmapi.WatchActionKind, service *
|
||||
daemon.logClusterEvent(action, service.ID, events.ServiceEventType, eventTime, attributes)
|
||||
}
|
||||
|
||||
var clusterEventAction = map[swarmapi.WatchActionKind]events.Action{
|
||||
swarmapi.WatchActionKindCreate: events.ActionCreate,
|
||||
swarmapi.WatchActionKindUpdate: events.ActionUpdate,
|
||||
swarmapi.WatchActionKindRemove: events.ActionRemove,
|
||||
}
|
||||
|
||||
func (daemon *Daemon) logClusterEvent(action swarmapi.WatchActionKind, id string, eventType events.Type, eventTime time.Time, attributes map[string]string) {
|
||||
var eventAction events.Action
|
||||
switch action {
|
||||
case swarmapi.WatchActionKindCreate:
|
||||
eventAction = events.ActionCreate
|
||||
case swarmapi.WatchActionKindUpdate:
|
||||
eventAction = events.ActionUpdate
|
||||
case swarmapi.WatchActionKindRemove:
|
||||
eventAction = events.ActionRemove
|
||||
case swarmapi.WatchActionKindUnknown:
|
||||
// Unknown action kind - skip publishing invalid event
|
||||
return
|
||||
default:
|
||||
// Unknown action kind - skip publishing invalid event
|
||||
return
|
||||
}
|
||||
|
||||
daemon.EventsService.PublishMessage(events.Message{
|
||||
Action: clusterEventAction[action],
|
||||
Action: eventAction,
|
||||
Type: eventType,
|
||||
Actor: events.Actor{
|
||||
ID: id,
|
||||
@@ -262,18 +272,21 @@ func (daemon *Daemon) logClusterEvent(action swarmapi.WatchActionKind, id string
|
||||
}
|
||||
|
||||
func eventTimestamp(meta swarmapi.Meta, action swarmapi.WatchActionKind) time.Time {
|
||||
var eventTime time.Time
|
||||
switch action {
|
||||
case swarmapi.WatchActionKindCreate:
|
||||
eventTime, _ = gogotypes.TimestampFromProto(meta.CreatedAt)
|
||||
eventTime, _ := gogotypes.TimestampFromProto(meta.CreatedAt)
|
||||
return eventTime
|
||||
case swarmapi.WatchActionKindUpdate:
|
||||
eventTime, _ = gogotypes.TimestampFromProto(meta.UpdatedAt)
|
||||
eventTime, _ := gogotypes.TimestampFromProto(meta.UpdatedAt)
|
||||
return eventTime
|
||||
case swarmapi.WatchActionKindRemove:
|
||||
// There is no timestamp from store message for remove operations.
|
||||
// Use current time.
|
||||
eventTime = time.Now()
|
||||
return time.Now()
|
||||
case swarmapi.WatchActionKindUnknown:
|
||||
return time.Now()
|
||||
default:
|
||||
// TODO(thaJeztah): make switch exhaustive: anything to do for swarmapi.WatchActionKindUnknown or "other" ?
|
||||
// For any unexpected action kinds, use current time as fallback.
|
||||
return time.Now()
|
||||
}
|
||||
return eventTime
|
||||
}
|
||||
@@ -4,10 +4,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
containertypes "github.com/moby/moby/api/types/container"
|
||||
eventtypes "github.com/moby/moby/api/types/events"
|
||||
"github.com/moby/moby/v2/daemon/container"
|
||||
"github.com/moby/moby/v2/daemon/events"
|
||||
swarmapi "github.com/moby/swarmkit/v2/api"
|
||||
)
|
||||
|
||||
func TestLogContainerEventCopyLabels(t *testing.T) {
|
||||
@@ -87,3 +89,74 @@ func validateTestAttributes(t *testing.T, l chan any, expectedAttributesToTest m
|
||||
t.Fatal("LogEvent test timed out")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventTimestamp(t *testing.T) {
|
||||
now := time.Now()
|
||||
createdAt := &gogotypes.Timestamp{Seconds: now.Unix(), Nanos: int32(now.Nanosecond())}
|
||||
updatedAt := &gogotypes.Timestamp{Seconds: now.Add(time.Hour).Unix(), Nanos: int32(now.Add(time.Hour).Nanosecond())}
|
||||
|
||||
tests := []struct {
|
||||
doc string
|
||||
meta swarmapi.Meta
|
||||
action swarmapi.WatchActionKind
|
||||
check func(t *testing.T, result time.Time)
|
||||
}{
|
||||
{
|
||||
doc: "Create action uses CreatedAt timestamp",
|
||||
meta: swarmapi.Meta{CreatedAt: createdAt},
|
||||
action: swarmapi.WatchActionKindCreate,
|
||||
check: func(t *testing.T, result time.Time) {
|
||||
if result.Unix() != now.Unix() {
|
||||
t.Errorf("expected CreatedAt timestamp, got %v", result)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "Update action uses UpdatedAt timestamp",
|
||||
meta: swarmapi.Meta{UpdatedAt: updatedAt},
|
||||
action: swarmapi.WatchActionKindUpdate,
|
||||
check: func(t *testing.T, result time.Time) {
|
||||
if result.Unix() != now.Add(time.Hour).Unix() {
|
||||
t.Errorf("expected UpdatedAt timestamp, got %v", result)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "Remove action uses current time",
|
||||
meta: swarmapi.Meta{},
|
||||
action: swarmapi.WatchActionKindRemove,
|
||||
check: func(t *testing.T, result time.Time) {
|
||||
if result.IsZero() {
|
||||
t.Error("expected non-zero timestamp for Remove action")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "Unknown action returns valid timestamp",
|
||||
meta: swarmapi.Meta{},
|
||||
action: swarmapi.WatchActionKindUnknown,
|
||||
check: func(t *testing.T, result time.Time) {
|
||||
if result.IsZero() {
|
||||
t.Error("expected non-zero timestamp for Unknown action")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "Invalid action falls back to current time",
|
||||
meta: swarmapi.Meta{},
|
||||
action: swarmapi.WatchActionKind(123456789),
|
||||
check: func(t *testing.T, result time.Time) {
|
||||
if result.IsZero() {
|
||||
t.Error("expected non-zero timestamp for invalid action")
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
result := eventTimestamp(tc.meta, tc.action)
|
||||
tc.check(t, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user