Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 7 additions & 0 deletions runtime/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type Reconciler interface {
type ReconcileResult struct {
Err error
Retrigger time.Time
Warnings []string
}

// ReconcilerInitializer is a function that initializes a new reconciler for a specific controller
Expand Down Expand Up @@ -1392,6 +1393,12 @@ func (c *Controller) processCompletedInvocation(inv *invocation) error {
c.Logger.Info("Reconciled resource", logArgs...)
}

if len(inv.result.Warnings) > 0 {
for _, w := range inv.result.Warnings {
c.Logger.Warn("Reconcile warning", append(logArgs, zap.String("warning", w))...)
}
}

commonDims := []attribute.KeyValue{
attribute.String("resource_name", inv.name.Name),
attribute.String("resource_type", PrettifyResourceKind(inv.name.Kind)),
Expand Down
31 changes: 10 additions & 21 deletions runtime/reconcilers/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,15 +272,15 @@ func (r *ModelReconciler) Reconcile(ctx context.Context, n *runtimev1.ResourceNa
}
}

// Show if any partitions errored
// Surface warnings (non-blocking) for partitions and tests
var warnings []string
if model.State.PartitionsHaveErrors {
return runtime.ReconcileResult{Err: errPartitionsHaveErrors, Retrigger: refreshOn}
warnings = append(warnings, errPartitionsHaveErrors.Error())
}
// Show if any model tests failed
if len(model.State.TestErrors) > 0 {
return runtime.ReconcileResult{Err: newTestsError(model.State.TestErrors), Retrigger: refreshOn}
warnings = append(warnings, model.State.TestErrors...)
}
return runtime.ReconcileResult{Retrigger: refreshOn}
return runtime.ReconcileResult{Retrigger: refreshOn, Warnings: warnings}
}

// Acquire the execution semaphore for the remainder of the function.
Expand Down Expand Up @@ -403,18 +403,15 @@ func (r *ModelReconciler) Reconcile(ctx context.Context, n *runtimev1.ResourceNa
return runtime.ReconcileResult{Err: execErr, Retrigger: refreshOn}
}

// Show if any partitions errored
// Return the next refresh time with warnings as non-blocking signals
var warnings []string
if model.State.PartitionsHaveErrors {
return runtime.ReconcileResult{Err: errPartitionsHaveErrors, Retrigger: refreshOn}
warnings = append(warnings, errPartitionsHaveErrors.Error())
}

// Show if the model has tests that failed
if len(model.State.TestErrors) > 0 {
return runtime.ReconcileResult{Err: newTestsError(model.State.TestErrors), Retrigger: refreshOn}
warnings = append(warnings, model.State.TestErrors...)
}

// Return the next refresh time
return runtime.ReconcileResult{Retrigger: refreshOn}
return runtime.ReconcileResult{Retrigger: refreshOn, Warnings: warnings}
}

func (r *ModelReconciler) ResolveTransitiveAccess(ctx context.Context, claims *runtime.SecurityClaims, res *runtimev1.Resource) ([]*runtimev1.SecurityRule, error) {
Expand Down Expand Up @@ -1793,14 +1790,6 @@ func (r *ModelReconciler) execModelTest(ctx context.Context, test *runtimev1.Mod
return fmt.Sprintf("%s: test did not pass", test.Name), nil
}

// newTestsError creates a new error that summarizes the messages returned from runModelTests.
func newTestsError(msgs []string) error {
if len(msgs) == 0 {
return nil // No errors
}
return fmt.Errorf("tests failed:\n%s", strings.Join(msgs, "\n"))
}

// hashWriteMapOrdered writes the keys and values of a map to the writer in a deterministic order.
func hashWriteMapOrdered(w io.Writer, m map[string]string) error {
keys := make([]string, 0, len(m))
Expand Down