-
Notifications
You must be signed in to change notification settings - Fork 10
Hyperfleet-361 : Use updated broker with logger context inheritance #45
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,7 @@ require ( | |
| github.com/cloudevents/sdk-go/v2 v2.16.2 | ||
| github.com/google/uuid v1.6.0 | ||
| github.com/oapi-codegen/runtime v1.1.2 | ||
| github.com/openshift-hyperfleet/hyperfleet-broker v1.0.0 | ||
| github.com/openshift-hyperfleet/hyperfleet-broker v1.0.1 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: In hyperfleet-broker v1.0.1 (released Jan 8, 2026), the release notes list one main change: “Implement logger standardization and structured logging improvements”. [1] API changes called out in the v1.0.1 changelog/commit message
Sources: GitHub release notes for v1.0.1 [1], tag comparison notes v1.0.0…v1.0.1 [2], and the main v1.0.1 implementation commit message [3]. 🏁 Script executed: # Find all Go files and check imports/usage of hyperfleet-broker
rg "hyperfleet-broker|NewPublisher|Publish" --type go -iRepository: openshift-hyperfleet/hyperfleet-sentinel Length of output: 21331 🏁 Script executed: # Check the go.mod file to see the dependency
cat -n go.mod | head -20Repository: openshift-hyperfleet/hyperfleet-sentinel Length of output: 835 API refactoring in v1.0.1 requires code updates—NewPublisher signature changed to options pattern. v1.0.1 (released Jan 8, 2026) refactored the constructor to use an options pattern with helpers like 🤖 Prompt for AI Agents |
||
| github.com/prometheus/client_golang v1.23.2 | ||
| github.com/segmentio/ksuid v1.0.4 | ||
| github.com/spf13/cobra v1.8.0 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import ( | |
| "context" | ||
| "encoding/json" | ||
| "errors" | ||
| "fmt" | ||
| "net/http" | ||
| "net/http/httptest" | ||
| "testing" | ||
|
|
@@ -81,7 +82,7 @@ type MockPublisher struct { | |
| publishError error | ||
| } | ||
|
|
||
| func (m *MockPublisher) Publish(topic string, event *cloudevents.Event) error { | ||
| func (m *MockPublisher) Publish(ctx context.Context, topic string, event *cloudevents.Event) error { | ||
| if m.publishError != nil { | ||
| return m.publishError | ||
| } | ||
|
|
@@ -94,6 +95,18 @@ func (m *MockPublisher) Close() error { | |
| return nil | ||
| } | ||
|
|
||
| type MockPublisherWithLogger struct { | ||
| mockLogger *logger.MockLoggerWithContext | ||
| } | ||
|
|
||
| func (m *MockPublisherWithLogger) Publish(ctx context.Context, topic string, event *cloudevents.Event) error { | ||
| // Simulate what broker does - log with the provided context | ||
| m.mockLogger.Info(ctx, fmt.Sprintf("broker publishing event to topic %s", topic)) | ||
| return nil | ||
| } | ||
|
|
||
| func (m *MockPublisherWithLogger) Close() error { return nil } | ||
|
|
||
| // TestTrigger_Success tests successful event publishing | ||
| func TestTrigger_Success(t *testing.T) { | ||
| ctx := context.Background() | ||
|
|
@@ -305,10 +318,10 @@ func TestTrigger_MixedResources(t *testing.T) { | |
| // Create mock server | ||
| server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| clusters := []map[string]interface{}{ | ||
| createMockCluster("cluster-1", 2, 2, true, now.Add(-31*time.Minute)), // Should publish (max age exceeded) | ||
| createMockCluster("cluster-2", 1, 1, true, now.Add(-5*time.Minute)), // Should skip (within max age) | ||
| createMockCluster("cluster-3", 3, 3, false, now.Add(-1*time.Minute)), // Should publish (not ready max age exceeded: 1min > 10s) | ||
| createMockCluster("cluster-4", 5, 4, true, now.Add(-5*time.Minute)), // Should publish (generation changed) | ||
| createMockCluster("cluster-1", 2, 2, true, now.Add(-31*time.Minute)), // Should publish (max age exceeded) | ||
| createMockCluster("cluster-2", 1, 1, true, now.Add(-5*time.Minute)), // Should skip (within max age) | ||
| createMockCluster("cluster-3", 3, 3, false, now.Add(-1*time.Minute)), // Should publish (not ready max age exceeded: 1min > 10s) | ||
| createMockCluster("cluster-4", 5, 4, true, now.Add(-5*time.Minute)), // Should publish (generation changed) | ||
| } | ||
| response := createMockClusterList(clusters) | ||
| w.Header().Set("Content-Type", "application/json") | ||
|
|
@@ -359,3 +372,55 @@ func TestTrigger_MixedResources(t *testing.T) { | |
| } | ||
| } | ||
| } | ||
|
|
||
| func TestTrigger_ContextPropagationToBroker(t *testing.T) { | ||
| var capturedLogs []string | ||
| var capturedContexts []context.Context | ||
|
|
||
| mockLogger := &logger.MockLoggerWithContext{ | ||
| CapturedLogs: &capturedLogs, | ||
| CapturedContexts: &capturedContexts, | ||
| } | ||
|
|
||
| // Create mock publisher that uses our mock logger | ||
| mockPublisherWithLogger := &MockPublisherWithLogger{ | ||
| mockLogger: mockLogger, | ||
| } | ||
|
|
||
| ctx := context.Background() | ||
| ctx = logger.WithDecisionReason(ctx, "max_age_exceeded") | ||
| ctx = logger.WithTopic(ctx, "test-topic") | ||
| ctx = logger.WithSubset(ctx, "clusters") | ||
| ctx = logger.WithTraceID(ctx, "trace-123") | ||
| ctx = logger.WithSpanID(ctx, "span-456") | ||
|
|
||
| event := cloudevents.NewEvent() | ||
| event.SetSpecVersion(cloudevents.VersionV1) | ||
| event.SetType("com.redhat.hyperfleet.cluster.reconcile") | ||
| event.SetSource("hyperfleet-sentinel") | ||
| event.SetID("test-id") | ||
|
|
||
| err := mockPublisherWithLogger.Publish(ctx, "test-topic", &event) | ||
| if err != nil { | ||
| t.Fatalf("publish failed: %v", err) | ||
| } | ||
|
|
||
| if len(capturedContexts) == 0 { | ||
| t.Fatal("no context captured by broker logger") | ||
| } | ||
|
|
||
| brokerCtx := capturedContexts[0] | ||
|
|
||
| // Test context values propagated to broker | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also be testing span_id propagation, we are doing traceID might be worth doing the span_id also |
||
| if reason, ok := brokerCtx.Value(logger.DecisionReasonCtxKey).(string); !ok || reason != "max_age_exceeded" { | ||
| t.Errorf("decision_reason not propagated: got %v", reason) | ||
| } | ||
|
|
||
| if topic, ok := brokerCtx.Value(logger.TopicCtxKey).(string); !ok || topic != "test-topic" { | ||
| t.Errorf("topic not propagated: got %v", topic) | ||
| } | ||
|
|
||
| if traceID, ok := brokerCtx.Value(logger.TraceIDCtxKey).(string); !ok || traceID != "trace-123" { | ||
| t.Errorf("trace_id not propagated: got %v", traceID) | ||
| } | ||
| } | ||
|
Comment on lines
+412
to
+426
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# First, find the test file and read the full test function (lines 376-426)
fd sentinel_test.go internal/sentinel/ -x cat -n {} | sed -n '376,426p'Repository: openshift-hyperfleet/hyperfleet-sentinel Length of output: 2064 🏁 Script executed: #!/bin/bash
# Check if SubsetCtxKey and SpanIDCtxKey are exported in the logger package
find . -path "*/pkg/logger*" -name "*.go" -type f | xargs rg -n "SubsetCtxKey|SpanIDCtxKey"Repository: openshift-hyperfleet/hyperfleet-sentinel Length of output: 630 🏁 Script executed: #!/bin/bash
# Search for where context keys are defined
find . -path "*/pkg/logger*" -name "*.go" -type f | xargs rg -n "CtxKey.*=" | head -20Repository: openshift-hyperfleet/hyperfleet-sentinel Length of output: 559 Add missing assertions for The test sets Proposed fix to add missing assertions if traceID, ok := brokerCtx.Value(logger.TraceIDCtxKey).(string); !ok || traceID != "trace-123" {
t.Errorf("trace_id not propagated: got %v", traceID)
}
+
+ if subset, ok := brokerCtx.Value(logger.SubsetCtxKey).(string); !ok || subset != "clusters" {
+ t.Errorf("subset not propagated: got %v", subset)
+ }
+
+ if spanID, ok := brokerCtx.Value(logger.SpanIDCtxKey).(string); !ok || spanID != "span-456" {
+ t.Errorf("span_id not propagated: got %v", spanID)
+ }
}🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,8 +59,8 @@ type HyperFleetLogger interface { | |
| Debugf(ctx context.Context, format string, args ...interface{}) | ||
| Info(ctx context.Context, message string) | ||
| Infof(ctx context.Context, format string, args ...interface{}) | ||
| Warning(ctx context.Context, message string) | ||
| Warningf(ctx context.Context, format string, args ...interface{}) | ||
| Warn(ctx context.Context, message string) | ||
| Warnf(ctx context.Context, format string, args ...interface{}) | ||
| Error(ctx context.Context, message string) | ||
| Errorf(ctx context.Context, format string, args ...interface{}) | ||
| Fatal(ctx context.Context, message string) | ||
|
|
@@ -457,11 +457,11 @@ func (l *logger) Infof(ctx context.Context, format string, args ...interface{}) | |
| l.log(ctx, LevelInfo, fmt.Sprintf(format, args...)) | ||
| } | ||
|
|
||
| func (l *logger) Warning(ctx context.Context, message string) { | ||
| func (l *logger) Warn(ctx context.Context, message string) { | ||
| l.log(ctx, LevelWarn, message) | ||
| } | ||
|
|
||
| func (l *logger) Warningf(ctx context.Context, format string, args ...interface{}) { | ||
| func (l *logger) Warnf(ctx context.Context, format string, args ...interface{}) { | ||
| l.log(ctx, LevelWarn, fmt.Sprintf(format, args...)) | ||
| } | ||
|
|
||
|
|
@@ -522,14 +522,14 @@ func (l *logger) Extra(key string, value interface{}) HyperFleetLogger { | |
| // noopLogger is a logger that does nothing (used for verbosity filtering) | ||
| type noopLogger struct{} | ||
|
|
||
| func (n *noopLogger) Debug(ctx context.Context, message string) {} | ||
| func (n *noopLogger) Debugf(ctx context.Context, format string, args ...interface{}) {} | ||
| func (n *noopLogger) Info(ctx context.Context, message string) {} | ||
| func (n *noopLogger) Infof(ctx context.Context, format string, args ...interface{}) {} | ||
| func (n *noopLogger) Warning(ctx context.Context, message string) {} | ||
| func (n *noopLogger) Warningf(ctx context.Context, format string, args ...interface{}) {} | ||
| func (n *noopLogger) Error(ctx context.Context, message string) {} | ||
| func (n *noopLogger) Errorf(ctx context.Context, format string, args ...interface{}) {} | ||
| func (n *noopLogger) Debug(ctx context.Context, message string) {} | ||
| func (n *noopLogger) Debugf(ctx context.Context, format string, args ...interface{}) {} | ||
| func (n *noopLogger) Info(ctx context.Context, message string) {} | ||
| func (n *noopLogger) Infof(ctx context.Context, format string, args ...interface{}) {} | ||
| func (n *noopLogger) Warn(ctx context.Context, message string) {} | ||
| func (n *noopLogger) Warnf(ctx context.Context, format string, args ...interface{}) {} | ||
| func (n *noopLogger) Error(ctx context.Context, message string) {} | ||
| func (n *noopLogger) Errorf(ctx context.Context, format string, args ...interface{}) {} | ||
| func (n *noopLogger) Fatal(ctx context.Context, message string) { | ||
| fmt.Fprintf(os.Stderr, "FATAL: %s\n", message) | ||
| os.Exit(1) | ||
|
|
@@ -540,3 +540,63 @@ func (n *noopLogger) Fatalf(ctx context.Context, format string, args ...interfac | |
| } | ||
| func (n *noopLogger) V(level int32) HyperFleetLogger { return n } | ||
| func (n *noopLogger) Extra(key string, value interface{}) HyperFleetLogger { return n } | ||
|
|
||
| type MockLoggerWithContext struct { | ||
| CapturedLogs *[]string | ||
| CapturedContexts *[]context.Context | ||
| } | ||
|
|
||
| func (m *MockLoggerWithContext) capture(ctx context.Context, msg string) { | ||
| if m.CapturedLogs == nil { | ||
| m.CapturedLogs = &[]string{} | ||
| } | ||
| if m.CapturedContexts == nil { | ||
| m.CapturedContexts = &[]context.Context{} | ||
| } | ||
| *m.CapturedLogs = append(*m.CapturedLogs, msg) | ||
| *m.CapturedContexts = append(*m.CapturedContexts, ctx) | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we missing the V() and Extra() funcs in this MockLoggerWithContext |
||
| func (m *MockLoggerWithContext) Debug(ctx context.Context, message string) { | ||
| m.capture(ctx, message) | ||
| } | ||
|
|
||
| func (m *MockLoggerWithContext) Debugf(ctx context.Context, format string, args ...interface{}) { | ||
| m.capture(ctx, fmt.Sprintf(format, args...)) | ||
| } | ||
|
|
||
| func (m *MockLoggerWithContext) Info(ctx context.Context, message string) { | ||
| m.capture(ctx, message) | ||
| } | ||
|
|
||
| func (m *MockLoggerWithContext) Infof(ctx context.Context, format string, args ...interface{}) { | ||
| m.capture(ctx, fmt.Sprintf(format, args...)) | ||
| } | ||
|
|
||
| func (m *MockLoggerWithContext) Warn(ctx context.Context, message string) { | ||
| m.capture(ctx, message) | ||
| } | ||
|
|
||
| func (m *MockLoggerWithContext) Warnf(ctx context.Context, format string, args ...interface{}) { | ||
| m.capture(ctx, fmt.Sprintf(format, args...)) | ||
| } | ||
|
|
||
| func (m *MockLoggerWithContext) Error(ctx context.Context, message string) { | ||
| m.capture(ctx, message) | ||
| } | ||
|
|
||
| func (m *MockLoggerWithContext) Errorf(ctx context.Context, format string, args ...interface{}) { | ||
| m.capture(ctx, fmt.Sprintf(format, args...)) | ||
| } | ||
|
|
||
| func (m *MockLoggerWithContext) Fatal(ctx context.Context, message string) { | ||
| m.capture(ctx, message) | ||
| fmt.Fprintf(os.Stderr, "FATAL: %s\n", message) | ||
| os.Exit(1) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to os.Exit on a mock logger? Will crash tests that are using it? |
||
| } | ||
|
|
||
| func (m *MockLoggerWithContext) Fatalf(ctx context.Context, format string, args ...interface{}) { | ||
| m.capture(ctx, fmt.Sprintf(format, args...)) | ||
| fmt.Fprintf(os.Stderr, "FATAL: %s\n", fmt.Sprintf(format, args...)) | ||
| os.Exit(1) | ||
tirthct marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -222,7 +222,7 @@ func TestLoggerLevelFiltering(t *testing.T) { | |
| case "info": | ||
| log.Info(ctx, testMessage) | ||
| case "warn": | ||
| log.Warning(ctx, testMessage) | ||
| log.Warn(ctx, testMessage) | ||
| case "error": | ||
| log.Error(ctx, testMessage) | ||
| } | ||
|
|
@@ -505,6 +505,10 @@ func TestLoggerSentinelFields(t *testing.T) { | |
| if entry.Subset != "clusters" { | ||
| t.Errorf("expected subset 'clusters', got %q", entry.Subset) | ||
| } | ||
|
|
||
| if entry.Component != "sentinel" { | ||
| t.Errorf("expected component 'sentinel', got %q", entry.Component) | ||
| } | ||
| } | ||
|
|
||
| func TestLoggerCorrelationFields(t *testing.T) { | ||
|
|
@@ -641,8 +645,8 @@ func TestNoopLogger(t *testing.T) { | |
| noop.Debugf(ctx, "test %s", "arg") | ||
| noop.Info(ctx, "test") | ||
| noop.Infof(ctx, "test %s", "arg") | ||
| noop.Warning(ctx, "test") | ||
| noop.Warningf(ctx, "test %s", "arg") | ||
| noop.Warn(ctx, "test") | ||
| noop.Warnf(ctx, "test %s", "arg") | ||
| noop.Error(ctx, "test") | ||
| noop.Errorf(ctx, "test %s", "arg") | ||
|
|
||
|
|
@@ -677,7 +681,7 @@ func TestLoggerFormattedMethods(t *testing.T) { | |
| }{ | ||
| {"Debugf", func() { log.Debugf(ctx, "debug %s %d", "test", 123) }, "debug test 123"}, | ||
| {"Infof", func() { log.Infof(ctx, "info %s %d", "test", 456) }, "info test 456"}, | ||
| {"Warningf", func() { log.Warningf(ctx, "warn %s %d", "test", 789) }, "warn test 789"}, | ||
| {"Warningf", func() { log.Warnf(ctx, "warn %s %d", "test", 789) }, "warn test 789"}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be Warnf as that is what we are testing here? |
||
| {"Errorf", func() { log.Errorf(ctx, "error %s %d", "test", 101) }, "error test 101"}, | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NP : think this got updated by mistake, should still be Warning