From d99ea235c25c78fe487aaf1b5fc6894637338320 Mon Sep 17 00:00:00 2001 From: "antoine.choimet" <12182686+achoimet@users.noreply.github.com.> Date: Thu, 6 Aug 2026 08:32:19 +0200 Subject: [PATCH] fix(netfault): report repeated tc batch errors only once tc -batch reports one error per failed batch line. Actions installing many rules (e.g. network delay) rendered the same error message dozens of times in the user-facing details. Deduplicate identical messages when rendering batchErrors, keeping the first occurrence and appending the number of further occurrences. --- .../network/netfault/batch_error.go | 13 ++++++ .../network/netfault/batch_error_test.go | 42 ++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/go/action_kit_commons/network/netfault/batch_error.go b/go/action_kit_commons/network/netfault/batch_error.go index 9de48308..80c6ba44 100644 --- a/go/action_kit_commons/network/netfault/batch_error.go +++ b/go/action_kit_commons/network/netfault/batch_error.go @@ -48,10 +48,23 @@ func (t *batchError) Error() string { } func (t *batchErrors) Error() string { + occurrences := make(map[string]int) + for _, err := range t.Errors { + occurrences[err.Msg]++ + } + var sb strings.Builder sb.WriteString(fmt.Sprintf("Command failed %s\n", strings.Join(t.Cmd, " "))) + reported := make(map[string]bool) for _, err := range t.Errors { + if reported[err.Msg] { + continue + } + reported[err.Msg] = true sb.WriteString(err.Error()) + if count := occurrences[err.Msg]; count > 1 { + sb.WriteString(fmt.Sprintf(" (and %d more)", count-1)) + } sb.WriteString("\n") } return sb.String() diff --git a/go/action_kit_commons/network/netfault/batch_error_test.go b/go/action_kit_commons/network/netfault/batch_error_test.go index e8a10460..f4bb9288 100644 --- a/go/action_kit_commons/network/netfault/batch_error_test.go +++ b/go/action_kit_commons/network/netfault/batch_error_test.go @@ -31,6 +31,23 @@ Command failed -:2 Error: Parent Qdisc doesn't exists. We have an error talking to the kernel Command failed -:3 +` + exampleErrorRepeated := `Error: NLM_F_REPLACE needed to override. +Command failed -:1 +Error: Failed to find specified qdisc. +Command failed -:2 +Error: Parent Qdisc doesn't exists. +We have an error talking to the kernel +Command failed -:3 +Error: Parent Qdisc doesn't exists. +We have an error talking to the kernel +Command failed -:4 +Error: Parent Qdisc doesn't exists. +We have an error talking to the kernel +Command failed -:5 +Error: Parent Qdisc doesn't exists. +We have an error talking to the kernel +Command failed -:6 ` tests := []struct { @@ -56,12 +73,35 @@ Command failed -:3 }, assert: func(t assert.TestingT, err error, message string) { assert.Equal(t, 3, len(err.(*batchErrors).Errors)) - assert.Equal(t, exampleError, strings.TrimPrefix(err.Error(), "Command failed test -b -\n")) + assert.Equal(t, `Error: Exclusivity flag on, cannot modify. +Command failed -:1 (and 1 more) +RTNETLINK answers: File exists +Command failed -:2 +`, strings.TrimPrefix(err.Error(), "Command failed test -b -\n")) assert.Equal(t, "Error: Exclusivity flag on, cannot modify.", err.(*batchErrors).Errors[0].Msg) assert.Equal(t, "RTNETLINK answers: File exists", err.(*batchErrors).Errors[1].Msg) assert.Equal(t, "Error: Exclusivity flag on, cannot modify.", err.(*batchErrors).Errors[2].Msg) }, }, + { + name: "should report repeated errors only once", + args: args{ + cmd: []string{"tc", "-force", "-batch", "-"}, + r: strings.NewReader(exampleErrorRepeated), + }, + assert: func(t assert.TestingT, err error, message string) { + assert.Equal(t, 6, len(err.(*batchErrors).Errors)) + assert.Equal(t, `Command failed tc -force -batch - +Error: NLM_F_REPLACE needed to override. +Command failed -:1 +Error: Failed to find specified qdisc. +Command failed -:2 +Error: Parent Qdisc doesn't exists. +We have an error talking to the kernel +Command failed -:3 (and 3 more) +`, err.Error()) + }, + }, { name: "should add kernel module hint", args: args{