Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Truncate status in reporter #524

Merged
merged 5 commits into from
Sep 28, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add truncation for status reporting
Kevin Dorosh committed Sep 28, 2022
commit 4b70a321b2b5ac44ec6631b0b1747b3c22e7a680
32 changes: 32 additions & 0 deletions pkg/api/v2/reporter/reporter.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package reporter

import (
"context"
"sort"
"strings"

"k8s.io/client-go/util/retry"
@@ -261,6 +262,7 @@ func (r *reporter) WriteReports(ctx context.Context, resourceErrs ResourceReport
return errors.Errorf("reporter: was passed resource of kind %v but no client to support it", kind)
}
status := r.StatusFromReport(report, subresourceStatuses)
status = trimStatus(status)
resourceStatus := r.statusClient.GetStatus(resource)

if status.Equal(resourceStatus) {
@@ -339,3 +341,33 @@ func (r *reporter) StatusFromReport(report Report, subresourceStatuses map[strin
Messages: messages,
}
}

func trimStatus(status *core.Status) *core.Status {
elcasteel marked this conversation as resolved.
Show resolved Hide resolved
if len(status.Reason) > 1024 {
// truncate status reason to a kilobyte
status.Reason = status.Reason[:1024]
}

// keep at most max 100 keys
if len(status.SubresourceStatuses) > 100 {
// sort for idempotency
keys := make([]string, 0, len(status.SubresourceStatuses))
for key := range status.SubresourceStatuses {
keys = append(keys, key)
}
sort.Strings(keys)
trimmedSubresourceStatuses := make(map[string]*core.Status, 100)
for _, key := range keys[:100] {
trimmedSubresourceStatuses[key] = status.SubresourceStatuses[key]
}
status.SubresourceStatuses = trimmedSubresourceStatuses
}

for k, v := range status.SubresourceStatuses {
// truncate subresources to a kilobyte per value
if len(v.Reason) > 1024 {
status.SubresourceStatuses[k].Reason = v.Reason[:1024]
}
}
return status
}
62 changes: 62 additions & 0 deletions pkg/api/v2/reporter/reporter_test.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package reporter_test
import (
"context"
"fmt"
"strings"

"github.com/solo-io/go-utils/contextutils"

@@ -36,6 +37,7 @@ var _ = Describe("Reporter", func() {
fakeResourceClient = memory.NewResourceClient(memory.NewInMemoryResourceCache(), &v1.FakeResource{})
reporter = rep.NewReporter("test", statusClient, mockResourceClient, fakeResourceClient)
})

It("reports errors for resources", func() {
r1, err := mockResourceClient.Write(v1.NewMockResource("", "mocky"), clients.WriteOpts{})
Expect(err).NotTo(HaveOccurred())
@@ -96,6 +98,66 @@ var _ = Describe("Reporter", func() {
}))
})

It("truncates large errors", func() {
r1, err := mockResourceClient.Write(v1.NewMockResource("", "mocky"), clients.WriteOpts{})
Expect(err).NotTo(HaveOccurred())

var sb strings.Builder
for i := 0; i < 10000; i++ {
sb.WriteString("a")
}

// 10000 chars = 10000 bytes
veryLargeError := sb.String()

// 1024 chars = 1kb
trimmedErr := veryLargeError[:1024] // we expect to trim this to 1kb

subresourceStatuses := map[string]*core.Status{}
for i := 0; i < 1000; i++ { // we have numerous keys, and expect to trim to 100 keys
var sb strings.Builder
for j := 0; j < i; j++ {
sb.WriteString("a")
}
subresourceStatuses[fmt.Sprintf("subresource-%s", sb.String())] = &core.Status{
State: core.Status_Warning,
Reason: veryLargeError,
ReportedBy: "test",
}
}

trimmedSubresourceStatuses := map[string]*core.Status{}
for i := 0; i < 100; i++ { // we expect a max of 100 keys
var sb strings.Builder
for j := 0; j < i; j++ {
sb.WriteString("a")
}
trimmedSubresourceStatuses[fmt.Sprintf("subresource-%s", sb.String())] = &core.Status{
State: core.Status_Warning,
Reason: trimmedErr,
ReportedBy: "test",
}
}

resourceErrs := rep.ResourceReports{
r1.(*v1.MockResource): rep.Report{Errors: fmt.Errorf(veryLargeError)},
}
err = reporter.WriteReports(context.TODO(), resourceErrs, subresourceStatuses)
Expect(err).NotTo(HaveOccurred())

r1, err = mockResourceClient.Read(r1.GetMetadata().Namespace, r1.GetMetadata().Name, clients.ReadOpts{})
Expect(err).NotTo(HaveOccurred())

status := statusClient.GetStatus(r1.(*v1.MockResource))
Expect(status).To(Equal(&core.Status{
State: 2,
Reason: trimmedErr,
ReportedBy: "test",
Messages: nil,
SubresourceStatuses: trimmedSubresourceStatuses,
}))
})

It("handles conflict", func() {
r1, err := mockResourceClient.Write(v1.NewMockResource("", "mocky"), clients.WriteOpts{})
Expect(err).NotTo(HaveOccurred())