diff --git a/lib/autoupdate/rolloutcontroller/client_test.go b/lib/autoupdate/rolloutcontroller/client_test.go index ba204ffb77db3..47f92a087bf5e 100644 --- a/lib/autoupdate/rolloutcontroller/client_test.go +++ b/lib/autoupdate/rolloutcontroller/client_test.go @@ -29,7 +29,7 @@ import ( ) // mockClient is a mock implementation if the Client interface for testing purposes. -// This is used to precisely check which calls are made by the Reconciler during tests. +// This is used to precisely check which calls are made by the reconciler during tests. // Use newMockClient to create one from stubs. Once the test is over, you must call // mockClient.checkIfEmpty to validate all expected calls were made. type mockClient struct { diff --git a/lib/autoupdate/rolloutcontroller/controller.go b/lib/autoupdate/rolloutcontroller/controller.go index b9e7e82e02c56..2d1df6fab2674 100644 --- a/lib/autoupdate/rolloutcontroller/controller.go +++ b/lib/autoupdate/rolloutcontroller/controller.go @@ -15,23 +15,29 @@ const ( reconcilerPeriod = time.Minute ) +// Controller wakes up every minute to reconcile the autoupdate_agent_rollout resource. +// See the reconciler godoc for more details about the reconciliation process. +// We currently wake up every minute, in the future we might decide to also watch for events +// (from autoupdate_config and autoupdate_version changefeed) to react faster. type Controller struct { - reconciler Reconciler + reconciler reconciler clock clockwork.Clock log *slog.Logger } +// New creates a new Controller for the autoupdate_agent_rollout kind. func New(client Client, log *slog.Logger, clock clockwork.Clock) *Controller { return &Controller{ clock: clock, log: log, - reconciler: Reconciler{ + reconciler: reconciler{ clt: client, log: log, }, } } +// Run the autoupdate_agent_rollout controller. This function returns only when its context is cancelled. func (c *Controller) Run(ctx context.Context) error { config := interval.Config{ Duration: reconcilerPeriod, @@ -47,8 +53,8 @@ func (c *Controller) Run(ctx context.Context) error { c.log.InfoContext(ctx, "Stopping autoupdate_agent_rollout controller", "reason", ctx.Err()) return ctx.Err() case <-ticker.Next(): - c.log.DebugContext(ctx, "Running autoupdate_agent_rollout reconciler") - err := c.reconciler.Reconcile(ctx) + c.log.DebugContext(ctx, "Reconciling autoupdate_agent_rollout") + err := c.reconciler.reconcile(ctx) if err != nil { c.log.ErrorContext(ctx, "Failed to reconcile autoudpate_agent_controller", "error", err) } diff --git a/lib/autoupdate/rolloutcontroller/reconciler.go b/lib/autoupdate/rolloutcontroller/reconciler.go index 78989c4ec4a6b..b1f6921ec0a6e 100644 --- a/lib/autoupdate/rolloutcontroller/reconciler.go +++ b/lib/autoupdate/rolloutcontroller/reconciler.go @@ -37,11 +37,11 @@ const ( maxConflictRetry = 3 ) -// Reconciler reconciles the AutoUpdateAgentRollout singleton based on the content of the AutoUpdateVersion and +// reconciler reconciles the AutoUpdateAgentRollout singleton based on the content of the AutoUpdateVersion and // AutoUpdateConfig singletons. This reconciler is not based on the services.GenericReconciler because: // - we reconcile 2 resources with one // - both input and output are singletons, we don't need the multi resource logic nor stream/paginated APIs -type Reconciler struct { +type reconciler struct { clt Client log *slog.Logger @@ -49,9 +49,9 @@ type Reconciler struct { mutex sync.Mutex } -// Reconcile the AutoUpdateAgentRollout singleton. The reconciliation can fail because of a conflict (multiple auths +// reconcile the AutoUpdateAgentRollout singleton. The reconciliation can fail because of a conflict (multiple auths // are racing), in this case we retry the reconciliation immediately. -func (r *Reconciler) Reconcile(ctx context.Context) error { +func (r *reconciler) reconcile(ctx context.Context) error { r.mutex.Lock() defer r.mutex.Unlock() @@ -88,7 +88,7 @@ func (r *Reconciler) Reconcile(ctx context.Context) error { // The creation/update/deletion can fail with a trace.CompareFailedError or trace.NotFoundError // if the resource change while we were computing it. // The caller must handle those error and retry the reconciliation. -func (r *Reconciler) tryReconcile(ctx context.Context) error { +func (r *reconciler) tryReconcile(ctx context.Context) error { // get autoupdate_config var config *autoupdate.AutoUpdateConfig if c, err := r.clt.GetAutoUpdateConfig(ctx); err == nil { @@ -171,7 +171,7 @@ func (r *Reconciler) tryReconcile(ctx context.Context) error { return trace.Wrap(err, "updating rollout") } -func (r *Reconciler) buildRolloutSpec(config *autoupdate.AutoUpdateConfigSpecAgents, version *autoupdate.AutoUpdateVersionSpecAgents) (*autoupdate.AutoUpdateAgentRolloutSpec, error) { +func (r *reconciler) buildRolloutSpec(config *autoupdate.AutoUpdateConfigSpecAgents, version *autoupdate.AutoUpdateVersionSpecAgents) (*autoupdate.AutoUpdateAgentRolloutSpec, error) { // reconcile mode mode, err := getMode(config.GetMode(), version.GetMode()) if err != nil { diff --git a/lib/autoupdate/rolloutcontroller/reconciler_test.go b/lib/autoupdate/rolloutcontroller/reconciler_test.go index 340451d8da46d..4888fa6ee3f3c 100644 --- a/lib/autoupdate/rolloutcontroller/reconciler_test.go +++ b/lib/autoupdate/rolloutcontroller/reconciler_test.go @@ -307,7 +307,7 @@ func TestTryReconcile(t *testing.T) { // Test execution: Running the reconciliation - reconciler := &Reconciler{ + reconciler := &reconciler{ clt: client, log: log, } @@ -375,13 +375,13 @@ func TestReconciler_Reconcile(t *testing.T) { } client := newMockClient(t, stubs) - reconciler := &Reconciler{ + reconciler := &reconciler{ clt: client, log: log, } // Test execution: run the reconciliation loop - require.NoError(t, reconciler.Reconcile(ctx)) + require.NoError(t, reconciler.reconcile(ctx)) // Test validation: check that all the expected calls were received client.checkIfEmpty(t) @@ -397,13 +397,13 @@ func TestReconciler_Reconcile(t *testing.T) { } client := newMockClient(t, stubs) - reconciler := &Reconciler{ + reconciler := &reconciler{ clt: client, log: log, } // Test execution: run the reconciliation loop - require.NoError(t, reconciler.Reconcile(ctx)) + require.NoError(t, reconciler.reconcile(ctx)) // Test validation: check that all the expected calls were received client.checkIfEmpty(t) @@ -421,13 +421,13 @@ func TestReconciler_Reconcile(t *testing.T) { } client := newMockClient(t, stubs) - reconciler := &Reconciler{ + reconciler := &reconciler{ clt: client, log: log, } // Test execution: run the reconciliation loop - require.NoError(t, reconciler.Reconcile(ctx)) + require.NoError(t, reconciler.reconcile(ctx)) // Test validation: check that all the expected calls were received client.checkIfEmpty(t) @@ -461,13 +461,13 @@ func TestReconciler_Reconcile(t *testing.T) { } client := newMockClient(t, stubs) - reconciler := &Reconciler{ + reconciler := &reconciler{ clt: client, log: log, } // Test execution: run the reconciliation loop - require.NoError(t, reconciler.Reconcile(ctx)) + require.NoError(t, reconciler.reconcile(ctx)) // Test validation: check that all the expected calls were received client.checkIfEmpty(t) @@ -499,13 +499,13 @@ func TestReconciler_Reconcile(t *testing.T) { } client := newMockClient(t, stubs) - reconciler := &Reconciler{ + reconciler := &reconciler{ clt: client, log: log, } // Test execution: run the reconciliation loop - require.NoError(t, reconciler.Reconcile(ctx)) + require.NoError(t, reconciler.reconcile(ctx)) // Test validation: check that all the expected calls were received client.checkIfEmpty(t) @@ -523,13 +523,13 @@ func TestReconciler_Reconcile(t *testing.T) { } client := newMockClient(t, stubs) - reconciler := &Reconciler{ + reconciler := &reconciler{ clt: client, log: log, } // Test execution: run the reconciliation loop - require.ErrorContains(t, reconciler.Reconcile(ctx), "the DB fell on the floor") + require.ErrorContains(t, reconciler.reconcile(ctx), "the DB fell on the floor") // Test validation: check that all the expected calls were received client.checkIfEmpty(t) @@ -553,13 +553,13 @@ func TestReconciler_Reconcile(t *testing.T) { } client := newMockClient(t, stubs) - reconciler := &Reconciler{ + reconciler := &reconciler{ clt: client, log: log, } // Test execution: run the reconciliation loop - require.ErrorContains(t, reconciler.Reconcile(cancelableCtx), "canceled") + require.ErrorContains(t, reconciler.reconcile(cancelableCtx), "canceled") // Test validation: check that all the expected calls were received client.checkIfEmpty(t)