Skip to content

Add drift detection and periodic resync for OpenStack resources#759

Merged
mandre merged 6 commits into
k-orc:mainfrom
eshulman2:forge/aisos-376
Jul 8, 2026
Merged

Add drift detection and periodic resync for OpenStack resources#759
mandre merged 6 commits into
k-orc:mainfrom
eshulman2:forge/aisos-376

Conversation

@eshulman2

@eshulman2 eshulman2 commented Apr 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements periodic resync and external deletion handling for the OpenStack Resource Controller. When enabled, ORC periodically reconciles resources with OpenStack to detect configuration drift and external deletions, automatically recreating managed resources that were deleted outside of ORC while surfacing terminal errors for imported resources.

Changes

API Changes

  • Added resyncPeriod field to the code generator spec template for per-resource resync configuration
  • Added lastSyncTime field to the code generator status template to track successful reconciliation timestamps
  • Added GetResyncPeriod(), GetLastSyncTime(), and IsImported() methods to APIObjectAdapter interface

CLI & Manager

  • Added --default-resync-period CLI flag with default value of 0 (disabled)
  • Added ResyncConfigurable interface to propagate global default to all controllers

Core Resync Logic

  • Created internal/controllers/generic/resync/ package with:
    • DetermineResyncPeriod(): resolves effective period (per-resource → global → disabled)
    • CalculateJitteredDuration(): wraps wait.Jitter with 20% positive-only jitter
    • ShouldScheduleResync(): guards against scheduling for disabled/terminal/pending-requeue states
  • Modified shouldReconcile() to trigger reconciliation when resync period has elapsed
  • Modified reconcileNormal() to schedule jittered requeue after successful reconciliation

External Deletion Handling

  • Modified GetOrCreateOSResource() to detect 404s when fetching by status.id
  • Returns typed ExternallyDeleted signal via ReconcileStatus for managed non-imported resources
  • Restored safety guard for unexpected nil returns from actuators
  • Imported/unmanaged resources return terminal error
  • Added ClearStatusID() to status writer for clearing ID before recreation

Status Updates

  • Modified UpdateStatus() to set lastSyncTime only on successful reconciliation
  • Fixed Available condition: terminal errors now set False instead of Unknown

Code Generation

  • Updated api.template and adapter.template with new fields and methods
  • Regenerated all resources, CRDs, deep copy, and client code

Documentation

  • Created website/docs/user-guide/drift-detection.md
  • Updated enhancements/drift-detection.md with implementation status

Commit structure

  1. API fields + templates — hand-written type and template changes
  2. Regenerate — all generated code (CRDs, adapters, apply configs, OpenAPI, docs)
  3. Resync scheduling — scheduler, controller integration, CLI flag, all controller wiring
  4. E2E tests for resync — kuttl test suites + integration tests
  5. External deletion — detection, recreation, condition fix, unit + E2E tests
  6. Documentation — user guide and enhancement doc
  7. Design update — update enhancement proposal with typed ExternallyDeleted signal

Design decisions

  • Typed ExternallyDeleted signal: The original proposal used a (nil, nil) return from GetOrCreateOSResource to signal external deletion. This was replaced with a typed ExternallyDeleted flag on ReconcileStatus, which avoids ambiguity with actuator bugs that might also return nil and restores a safety guard for unexpected nil returns from GetOSResourceByID.
  • wait.Jitter reuse: CalculateJitteredDuration wraps k8s.io/apimachinery/pkg/util/wait.Jitter rather than reimplementing the same [base, base*1.2) jitter range.
  • Removed dead CommonStatus/CommonOptions types: These types were declared but never embedded — the code generator inlines the fields directly. They were removed to avoid misleading readers.

Testing

  • Unit tests for resync package, shouldReconcile, status updates, external deletion (all policy/import combinations)
  • 6 kuttl E2E test suites: resync-period, resync-disabled, resync-terminal-error, resync-jitter, external-deletion, external-deletion-import
  • go build, go vet, make lint all pass

@github-actions github-actions Bot added the semver:minor Backwards-compatible change label Apr 19, 2026
@eshulman2 eshulman2 changed the title [AISOS-376] Implementation for AISOS-376 [AISOS-376] Add drift detection and periodic resync for OpenStack resources Apr 20, 2026

@eshulman2 eshulman2 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are some issues especially with docs

Comment thread internal/controllers/generic/reconciler/controller.go Outdated
Comment thread internal/controllers/generic/reconciler/resource_actions.go Outdated
Comment thread internal/controllers/generic/reconciler/controller.go Outdated
Comment thread api/v1alpha1/controller_options.go Outdated
Comment thread internal/controllers/generic/status/status_test.go Outdated
Comment thread internal/controllers/generic/resync/scheduler_test.go Outdated
Comment thread enhancements/drift-detection.md Outdated
@mandre mandre added the hold Do not merge label Apr 23, 2026
@mandre

mandre commented Apr 23, 2026

Copy link
Copy Markdown
Collaborator

Adding the hold label because this is an experiment.

@eshulman2 eshulman2 changed the title [AISOS-376] Add drift detection and periodic resync for OpenStack resources Add drift detection and periodic resync for OpenStack resources May 25, 2026
@eshulman2 eshulman2 force-pushed the forge/aisos-376 branch 4 times, most recently from 9138a1d to da9fb9b Compare May 31, 2026 08:28
@eshulman2

Copy link
Copy Markdown
Contributor Author

@mandre I revised the PR myself removing any indication of internal references I think it is ready for review

Comment thread .gitignore Outdated
@eshulman2 eshulman2 requested a review from dlaw4608 June 9, 2026 06:42

@dlaw4608 dlaw4608 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @eshulman2 , I ran it locally following the drift-detection.md and it works well, also ran some of the E2E tests, I left a few comments, please let me know what you think?

Comment thread website/docs/user-guide/drift-detection.md Outdated
Comment thread internal/controllers/generic/reconciler/resource_actions.go Outdated
Comment thread internal/controllers/generic/reconciler/resource_actions_test.go Outdated
Comment thread internal/controllers/generic/reconciler/resource_actions_test.go Outdated
Comment thread internal/controllers/generic/resync/scheduler.go Outdated
@eshulman2 eshulman2 requested a review from dlaw4608 June 10, 2026 13:24
Comment thread website/docs/user-guide/drift-detection.md Outdated
@eshulman2 eshulman2 requested a review from dlaw4608 June 11, 2026 12:10

@winiciusallan winiciusallan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @eshulman2, thanks for working on this. This is such a nice feature to have in ORC.

I've left a few comments along all the changes. Let me know what you think.

Comment thread website/docs/user-guide/drift-detection.md Outdated
Comment thread internal/controllers/floatingip/controller.go
Comment thread internal/controllers/flavor/adapter_test.go Outdated
Comment thread enhancements/drift-detection.md Outdated
Comment thread internal/controllers/generic/reconciler/controller.go Outdated

// Schedule a resync requeue when the effective resync period is configured,
// there is no terminal error, and no other requeue is already pending.
// Positive-only jitter of [0%, +20%] is applied to spread load across

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be another effort, but I would like to leave the idea here: Would it be good to allow the operator/user to configure the jitter interval?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any specific reason or use case to make this configurable, any specific use case in mind for adding this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mandre any strong opinions on that?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why one would want to configure this. Let's reconsider if someone has a use case.

Comment thread internal/controllers/generic/resync/config.go Outdated
Comment thread internal/controllers/network/tests/network-external-deletion/00-assert.yaml Outdated
Comment thread internal/controllers/network/tests/network-resync-jitter/README.md
@eshulman2 eshulman2 requested a review from winiciusallan June 24, 2026 12:06
Comment thread internal/controllers/generic/reconciler/resource_actions.go Outdated
Comment thread internal/controllers/generic/reconciler/resource_actions_test.go Outdated
@github-actions github-actions Bot removed the semver:minor Backwards-compatible change label Jun 28, 2026
@github-actions

Copy link
Copy Markdown

Failed to assess the semver bump. See logs for details.

@github-actions github-actions Bot added the semver:minor Backwards-compatible change label Jun 28, 2026
@eshulman2 eshulman2 requested a review from winiciusallan June 30, 2026 09:43

@mandre mandre left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a first review for now, I'll need another pass.

Comment thread internal/controllers/generic/progress/reconcile_status.go Outdated
CUR3=$(kubectl get network.openstack.k-orc.cloud network-resync-jitter-3 \
-n ${NAMESPACE} -o jsonpath='{.status.lastSyncTime}')

# All three must have updated their lastSyncTime, demonstrating that

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be interesting to verify that the interval between init and current is different for each resource.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree this would be useful to check, but I’m concerned that requiring all intervals to differ could be flaky because the jitter distribution is probabilistic and two resources may legitimately get the same value. I updated the test to verify that all three resources resync and that at least one of the three observed intervals differs. There is still a statistical chance this could flake if all three land on the same observed interval, but it is much lower, and the stricter jitter behavior is covered by the scheduler unit tests.

Comment thread internal/controllers/network/tests/network-resync-period/README.md Outdated
Comment thread internal/controllers/generic/reconciler/resource_actions.go Outdated
if osResource != nil {
log.V(logging.Verbose).Info("Got existing OpenStack resource", "ID", actuator.GetResourceID(osResource))
if osResource == nil {
return nil, progress.WrapError(fmt.Errorf("GetOSResourceByID returned nil resource with no error for ID %q", *resourceID))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this fixing an existing bug?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s not fixing a known bug. It is a defensive check for an invalid actuator response: GetOSResourceByID should not return nil with no error for a concrete status ID. I’m fine dropping it if we want to keep this PR scoped to external deletion/resync behavior.

Comment thread enhancements/drift-detection.md Outdated
Comment thread enhancements/drift-detection.md Outdated
Comment thread enhancements/drift-detection.md Outdated
@eshulman2 eshulman2 requested a review from mandre July 6, 2026 08:40
// string, e.g. "10m", "1h". Set to "0s" to disable periodic resync for
// this resource.
// +optional
ResyncPeriod *metav1.Duration `json:"resyncPeriod,omitempty"` //nolint:kubeapilinter // metav1.Duration is appropriate for user-facing duration config

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was initially on the fence to add a ResyncPeriod for roleassignment since the roleassignment is a relationship resource, it doesn't have an ID, but on second thought it's useful to re-create the role assignment in case it's deleted out of band.

Now that raises a new question, should we do the same for router interface?

@eshulman2 eshulman2 Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree we should probably add it for RouterInterface too. It has the same relationship-resource problem as RoleAssignment: if the OpenStack router interface is deleted out of band, resync is what lets ORC notice and recreate it. The main complication is that RouterInterface currently marks the whole spec immutable. To support per-object resync configuration properly, we would need to change that validation so the actual relationship fields remain immutable, while spec.resyncPeriod can be changed like on the generated resources. I don’t see a strong technical reason not to do that, but it is an API behavior change, but as this is a large code/behavioral change I think we can justify it as part of a new version if must.

I'm going to add it.

Comment thread internal/controllers/roleassignment/controller.go Outdated
Comment thread internal/controllers/sharenetwork/controller.go Outdated
Comment thread cmd/resource-generator/data/api.template
Comment thread internal/controllers/roleassignment/reconciler.go Outdated
Comment thread internal/controllers/generic/reconciler/controller.go
Comment thread internal/controllers/roleassignment/reconciler.go
@eshulman2 eshulman2 requested a review from mandre July 7, 2026 16:26
Comment thread internal/controllers/roleassignment/reconciler.go Outdated
Comment thread internal/controllers/routerinterface/reconcile.go Outdated
Comment thread website/docs/user-guide/drift-detection.md Outdated
Comment thread website/docs/user-guide/drift-detection.md Outdated
Comment thread website/docs/user-guide/drift-detection.md Outdated
Comment thread website/docs/user-guide/drift-detection.md Outdated
@eshulman2 eshulman2 requested a review from mandre July 8, 2026 12:36

@mandre mandre left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good. Let's now squash the commits into logical chunks.

eshulman2 added 6 commits July 8, 2026 17:18
Introduce per-resource resync configuration and last sync status across the API surface. The new spec field lets users opt individual resources into periodic reconciliation while status.lastSyncTime records the last successful OpenStack sync.

Regenerate CRDs, OpenAPI, apply configurations, and controller adapters so the new fields are available consistently to controllers, clients, and generated documentation.
Wire the manager-level default resync period into every generated controller and make it part of the controller contract. Controllers resolve the effective period from the per-resource value first, then the global default, with zero disabling periodic resync.

Add the generic scheduler and status update logic that records successful syncs, preserves pending timers after spurious events, applies positive-only jitter to spread load, and warns when the global default is set very low.
Represent externally deleted OpenStack resources with a typed ReconcileStatus instead of overloading ordinary errors. Managed resources can then clear status.id and re-enter the normal creation path, while unmanaged or imported resources keep terminal error behavior.

Update the generic get-or-create and condition handling paths so external deletion is explicit, status updates remain coherent, and invalid actuator responses are surfaced as errors.
Extend the resync behavior to custom relationship controllers that do not follow the generated generic controller path. RoleAssignment can now periodically verify and recreate missing assignments, and RouterInterface gains matching resync and lastSyncTime handling.

Reuse the generic ShouldReconcile predicate to keep skip/resync decisions consistent across generic controllers, RoleAssignment, and RouterInterface while preserving relationship-specific reconciliation flow.
Add focused unit coverage for resync period resolution, jittered scheduling, lastSyncTime updates, external deletion state propagation, and generic reconcile skip behavior.

Add API validation and KUTTL scenarios covering RouterInterface resync mutability, network resync scheduling, disabled resync behavior, terminal-error exclusion, and managed/imported external deletion outcomes.
Update the enhancement proposal and user guide to describe periodic drift detection, global and per-resource configuration, jitter, terminal-error behavior, and external deletion semantics.

Refresh generated godoc and navigation so the new reconcile status and generic controller interfaces are reflected in the development documentation.
@mandre mandre removed the hold Do not merge label Jul 8, 2026
@mandre

mandre commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Thanks @eshulman2, this is an important feature and I'm glad it's finally merging 🎉

@mandre mandre enabled auto-merge July 8, 2026 15:09
@mandre mandre added this pull request to the merge queue Jul 8, 2026
Merged via the queue into k-orc:main with commit ef6119d Jul 8, 2026
10 of 11 checks passed
@eshulman2 eshulman2 deleted the forge/aisos-376 branch July 9, 2026 06:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:minor Backwards-compatible change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants