Add drift detection and periodic resync for OpenStack resources#759
Conversation
5072ca0 to
3ebabf9
Compare
|
Adding the |
9138a1d to
da9fb9b
Compare
|
@mandre I revised the PR myself removing any indication of internal references I think it is ready for review |
2f59e67 to
03223a3
Compare
03223a3 to
e29ca26
Compare
dlaw4608
left a comment
There was a problem hiding this comment.
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?
winiciusallan
left a comment
There was a problem hiding this comment.
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.
|
|
||
| // 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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
I don't see any specific reason or use case to make this configurable, any specific use case in mind for adding this?
There was a problem hiding this comment.
I don't see why one would want to configure this. Let's reconsider if someone has a use case.
|
Failed to assess the semver bump. See logs for details. |
bf78f06 to
00de750
Compare
mandre
left a comment
There was a problem hiding this comment.
Just a first review for now, I'll need another pass.
| 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 |
There was a problem hiding this comment.
It would be interesting to verify that the interval between init and current is different for each resource.
There was a problem hiding this comment.
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.
| 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)) |
There was a problem hiding this comment.
Is this fixing an existing bug?
There was a problem hiding this comment.
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.
2b53bfb to
da6353f
Compare
| // 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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
mandre
left a comment
There was a problem hiding this comment.
Good. Let's now squash the commits into logical chunks.
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.
d0fc696 to
3e115ae
Compare
3e115ae to
a8901c5
Compare
|
Thanks @eshulman2, this is an important feature and I'm glad it's finally merging 🎉 |
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
resyncPeriodfield to the code generator spec template for per-resource resync configurationlastSyncTimefield to the code generator status template to track successful reconciliation timestampsGetResyncPeriod(),GetLastSyncTime(), andIsImported()methods toAPIObjectAdapterinterfaceCLI & Manager
--default-resync-periodCLI flag with default value of 0 (disabled)ResyncConfigurableinterface to propagate global default to all controllersCore Resync Logic
internal/controllers/generic/resync/package with:DetermineResyncPeriod(): resolves effective period (per-resource → global → disabled)CalculateJitteredDuration(): wrapswait.Jitterwith 20% positive-only jitterShouldScheduleResync(): guards against scheduling for disabled/terminal/pending-requeue statesshouldReconcile()to trigger reconciliation when resync period has elapsedreconcileNormal()to schedule jittered requeue after successful reconciliationExternal Deletion Handling
GetOrCreateOSResource()to detect 404s when fetching bystatus.idExternallyDeletedsignal viaReconcileStatusfor managed non-imported resourcesClearStatusID()to status writer for clearing ID before recreationStatus Updates
UpdateStatus()to setlastSyncTimeonly on successful reconciliationAvailablecondition: terminal errors now setFalseinstead ofUnknownCode Generation
api.templateandadapter.templatewith new fields and methodsDocumentation
website/docs/user-guide/drift-detection.mdenhancements/drift-detection.mdwith implementation statusCommit structure
Design decisions
(nil, nil)return fromGetOrCreateOSResourceto signal external deletion. This was replaced with a typedExternallyDeletedflag onReconcileStatus, which avoids ambiguity with actuator bugs that might also return nil and restores a safety guard for unexpected nil returns fromGetOSResourceByID.wait.Jitterreuse:CalculateJitteredDurationwrapsk8s.io/apimachinery/pkg/util/wait.Jitterrather than reimplementing the same [base, base*1.2) jitter range.CommonStatus/CommonOptionstypes: These types were declared but never embedded — the code generator inlines the fields directly. They were removed to avoid misleading readers.Testing
go build,go vet,make lintall pass