Skip to content

Commit 37beaca

Browse files
committed
Implement periodic resync scheduling with CLI flag
Add --default-resync-period CLI flag to the manager for setting a global default resync period. Implement DetermineResyncPeriod to resolve the effective period using per-resource then global default precedence. Create resync scheduler package using wait.Jitter for jittered duration calculation. Modify shouldReconcile to support periodic resync by checking lastSyncTime against the effective resync period. Integrate resync requeue scheduling into reconcileNormal and update the status writer to set lastSyncTime on successful reconciliation. Wire the DefaultResyncPeriod from manager options through to all controllers. Add typed ExternallyDeleted signal on ReconcileStatus for safe detection of externally deleted OpenStack resources.
1 parent 7e6f71c commit 37beaca

36 files changed

Lines changed: 1508 additions & 107 deletions

File tree

cmd/manager/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ func main() {
8080
flag.IntVar(&orcOpts.ScopeCacheMaxSize, "scope-cache-max-size", 10,
8181
"The maximum credentials count the operator should keep in cache. "+
8282
"Setting this value to 0 means no cache.")
83+
flag.DurationVar(&orcOpts.DefaultResyncPeriod, "default-resync-period", 0,
84+
"Default resync period for all resources. Set to 0 to disable. "+
85+
"Can be overridden per-resource via spec.resyncPeriod.")
8386
flag.StringVar(&defaultCACertsPath, "default-ca-certs", "",
8487
"The path to a PEM-encoded CA Certificate file to supply as default for OpenStack API requests.")
8588
flag.Func("namespace", "A namespace that the controller watches to reconcile ORC objects. "+

internal/controllers/addressscope/controller.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package addressscope
1919
import (
2020
"context"
2121
"errors"
22+
"time"
2223

2324
ctrl "sigs.k8s.io/controller-runtime"
2425
"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -40,17 +41,22 @@ const controllerName = "addressscope"
4041
// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=addressscopes/status,verbs=get;update;patch
4142

4243
type addressscopeReconcilerConstructor struct {
43-
scopeFactory scope.Factory
44+
scopeFactory scope.Factory
45+
defaultResyncPeriod time.Duration
4446
}
4547

4648
func New(scopeFactory scope.Factory) interfaces.Controller {
47-
return addressscopeReconcilerConstructor{scopeFactory: scopeFactory}
49+
return &addressscopeReconcilerConstructor{scopeFactory: scopeFactory}
4850
}
4951

5052
func (addressscopeReconcilerConstructor) GetName() string {
5153
return controllerName
5254
}
5355

56+
func (c *addressscopeReconcilerConstructor) SetDefaultResyncPeriod(d time.Duration) {
57+
c.defaultResyncPeriod = d
58+
}
59+
5460
var projectDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.AddressScopeList, *orcv1alpha1.Project](
5561
"spec.resource.projectRef",
5662
func(addressscope *orcv1alpha1.AddressScope) []string {
@@ -75,7 +81,7 @@ var projectImportDependency = dependency.NewDependency[*orcv1alpha1.AddressScope
7581
)
7682

7783
// SetupWithManager sets up the controller with the Manager.
78-
func (c addressscopeReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
84+
func (c *addressscopeReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
7985
log := ctrl.LoggerFrom(ctx)
8086
k8sClient := mgr.GetClient()
8187

@@ -109,6 +115,6 @@ func (c addressscopeReconcilerConstructor) SetupWithManager(ctx context.Context,
109115
return err
110116
}
111117

112-
r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, addressscopeHelperFactory{}, addressscopeStatusWriter{})
118+
r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, addressscopeHelperFactory{}, addressscopeStatusWriter{}, c.defaultResyncPeriod)
113119
return builder.Complete(&r)
114120
}

internal/controllers/applicationcredential/controller.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package applicationcredential
1919
import (
2020
"context"
2121
"errors"
22+
"time"
2223

2324
corev1 "k8s.io/api/core/v1"
2425
ctrl "sigs.k8s.io/controller-runtime"
@@ -94,17 +95,22 @@ var (
9495
)
9596

9697
type applicationcredentialReconcilerConstructor struct {
97-
scopeFactory scope.Factory
98+
scopeFactory scope.Factory
99+
defaultResyncPeriod time.Duration
98100
}
99101

100102
func New(scopeFactory scope.Factory) interfaces.Controller {
101-
return applicationcredentialReconcilerConstructor{scopeFactory: scopeFactory}
103+
return &applicationcredentialReconcilerConstructor{scopeFactory: scopeFactory}
102104
}
103105

104106
func (applicationcredentialReconcilerConstructor) GetName() string {
105107
return controllerName
106108
}
107109

110+
func (c *applicationcredentialReconcilerConstructor) SetDefaultResyncPeriod(d time.Duration) {
111+
c.defaultResyncPeriod = d
112+
}
113+
108114
var userDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.ApplicationCredentialList, *orcv1alpha1.User](
109115
"spec.resource.userRef",
110116
func(applicationcredential *orcv1alpha1.ApplicationCredential) []string {
@@ -129,7 +135,7 @@ var userImportDependency = dependency.NewDependency[*orcv1alpha1.ApplicationCred
129135
)
130136

131137
// SetupWithManager sets up the controller with the Manager.
132-
func (c applicationcredentialReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
138+
func (c *applicationcredentialReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
133139
log := ctrl.LoggerFrom(ctx)
134140
k8sClient := mgr.GetClient()
135141

@@ -195,6 +201,6 @@ func (c applicationcredentialReconcilerConstructor) SetupWithManager(ctx context
195201
return err
196202
}
197203

198-
r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, applicationcredentialHelperFactory{}, applicationcredentialStatusWriter{})
204+
r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, applicationcredentialHelperFactory{}, applicationcredentialStatusWriter{}, c.defaultResyncPeriod)
199205
return builder.Complete(&r)
200206
}

internal/controllers/domain/controller.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package domain
1919
import (
2020
"context"
2121
"errors"
22+
"time"
2223

2324
ctrl "sigs.k8s.io/controller-runtime"
2425
"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -37,19 +38,24 @@ const controllerName = "domain"
3738
// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=domains/status,verbs=get;update;patch
3839

3940
type domainReconcilerConstructor struct {
40-
scopeFactory scope.Factory
41+
scopeFactory scope.Factory
42+
defaultResyncPeriod time.Duration
4143
}
4244

4345
func New(scopeFactory scope.Factory) interfaces.Controller {
44-
return domainReconcilerConstructor{scopeFactory: scopeFactory}
46+
return &domainReconcilerConstructor{scopeFactory: scopeFactory}
4547
}
4648

4749
func (domainReconcilerConstructor) GetName() string {
4850
return controllerName
4951
}
5052

53+
func (c *domainReconcilerConstructor) SetDefaultResyncPeriod(d time.Duration) {
54+
c.defaultResyncPeriod = d
55+
}
56+
5157
// SetupWithManager sets up the controller with the Manager.
52-
func (c domainReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
58+
func (c *domainReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
5359
log := ctrl.LoggerFrom(ctx)
5460

5561
builder := ctrl.NewControllerManagedBy(mgr).
@@ -63,6 +69,6 @@ func (c domainReconcilerConstructor) SetupWithManager(ctx context.Context, mgr c
6369
return err
6470
}
6571

66-
r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, domainHelperFactory{}, domainStatusWriter{})
72+
r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, domainHelperFactory{}, domainStatusWriter{}, c.defaultResyncPeriod)
6773
return builder.Complete(&r)
6874
}

internal/controllers/endpoint/controller.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package endpoint
1919
import (
2020
"context"
2121
"errors"
22+
"time"
2223

2324
ctrl "sigs.k8s.io/controller-runtime"
2425
"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -40,17 +41,22 @@ const controllerName = "endpoint"
4041
// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=endpoints/status,verbs=get;update;patch
4142

4243
type endpointReconcilerConstructor struct {
43-
scopeFactory scope.Factory
44+
scopeFactory scope.Factory
45+
defaultResyncPeriod time.Duration
4446
}
4547

4648
func New(scopeFactory scope.Factory) interfaces.Controller {
47-
return endpointReconcilerConstructor{scopeFactory: scopeFactory}
49+
return &endpointReconcilerConstructor{scopeFactory: scopeFactory}
4850
}
4951

5052
func (endpointReconcilerConstructor) GetName() string {
5153
return controllerName
5254
}
5355

56+
func (c *endpointReconcilerConstructor) SetDefaultResyncPeriod(d time.Duration) {
57+
c.defaultResyncPeriod = d
58+
}
59+
5460
var serviceDependency = dependency.NewDeletionGuardDependency[*orcv1alpha1.EndpointList, *orcv1alpha1.Service](
5561
"spec.resource.serviceRef",
5662
func(endpoint *orcv1alpha1.Endpoint) []string {
@@ -75,7 +81,7 @@ var serviceImportDependency = dependency.NewDependency[*orcv1alpha1.EndpointList
7581
)
7682

7783
// SetupWithManager sets up the controller with the Manager.
78-
func (c endpointReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
84+
func (c *endpointReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
7985
log := ctrl.LoggerFrom(ctx)
8086
k8sClient := mgr.GetClient()
8187

@@ -109,6 +115,6 @@ func (c endpointReconcilerConstructor) SetupWithManager(ctx context.Context, mgr
109115
return err
110116
}
111117

112-
r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, endpointHelperFactory{}, endpointStatusWriter{})
118+
r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, endpointHelperFactory{}, endpointStatusWriter{}, c.defaultResyncPeriod)
113119
return builder.Complete(&r)
114120
}

internal/controllers/flavor/controller.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package flavor
1919
import (
2020
"context"
2121
"errors"
22+
"time"
2223

2324
ctrl "sigs.k8s.io/controller-runtime"
2425
"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -37,19 +38,24 @@ const controllerName = "flavor"
3738
// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=flavors/status,verbs=get;update;patch
3839

3940
type flavorReconcilerConstructor struct {
40-
scopeFactory scope.Factory
41+
scopeFactory scope.Factory
42+
defaultResyncPeriod time.Duration
4143
}
4244

4345
func New(scopeFactory scope.Factory) interfaces.Controller {
44-
return flavorReconcilerConstructor{scopeFactory: scopeFactory}
46+
return &flavorReconcilerConstructor{scopeFactory: scopeFactory}
4547
}
4648

4749
func (flavorReconcilerConstructor) GetName() string {
4850
return controllerName
4951
}
5052

53+
func (c *flavorReconcilerConstructor) SetDefaultResyncPeriod(d time.Duration) {
54+
c.defaultResyncPeriod = d
55+
}
56+
5157
// SetupWithManager sets up the controller with the Manager.
52-
func (c flavorReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
58+
func (c *flavorReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
5359
log := ctrl.LoggerFrom(ctx)
5460

5561
builder := ctrl.NewControllerManagedBy(mgr).
@@ -63,6 +69,6 @@ func (c flavorReconcilerConstructor) SetupWithManager(ctx context.Context, mgr c
6369
return err
6470
}
6571

66-
r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, flavorHelperFactory{}, flavorStatusWriter{})
72+
r := reconciler.NewController(controllerName, mgr.GetClient(), c.scopeFactory, flavorHelperFactory{}, flavorStatusWriter{}, c.defaultResyncPeriod)
6773
return builder.Complete(&r)
6874
}

internal/controllers/floatingip/controller.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package floatingip
1919
import (
2020
"context"
2121
"errors"
22+
"time"
2223

2324
"k8s.io/utils/ptr"
2425
ctrl "sigs.k8s.io/controller-runtime"
@@ -39,17 +40,22 @@ import (
3940
// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=floatingips/status,verbs=get;update;patch
4041

4142
type floatingipReconcilerConstructor struct {
42-
scopeFactory scope.Factory
43+
scopeFactory scope.Factory
44+
defaultResyncPeriod time.Duration
4345
}
4446

4547
func New(scopeFactory scope.Factory) interfaces.Controller {
46-
return floatingipReconcilerConstructor{scopeFactory: scopeFactory}
48+
return &floatingipReconcilerConstructor{scopeFactory: scopeFactory}
4749
}
4850

4951
func (floatingipReconcilerConstructor) GetName() string {
5052
return controllerName
5153
}
5254

55+
func (c *floatingipReconcilerConstructor) SetDefaultResyncPeriod(d time.Duration) {
56+
c.defaultResyncPeriod = d
57+
}
58+
5359
const controllerName = "floatingip"
5460

5561
var (
@@ -136,7 +142,7 @@ var (
136142
)
137143

138144
// SetupWithManager sets up the controller with the Manager.
139-
func (c floatingipReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
145+
func (c *floatingipReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
140146
log := mgr.GetLogger().WithValues("controller", controllerName)
141147
k8sClient := mgr.GetClient()
142148

@@ -217,6 +223,6 @@ func (c floatingipReconcilerConstructor) SetupWithManager(ctx context.Context, m
217223
return err
218224
}
219225

220-
r := reconciler.NewController(controllerName, k8sClient, c.scopeFactory, floatingipHelperFactory{}, floatingipStatusWriter{})
226+
r := reconciler.NewController(controllerName, k8sClient, c.scopeFactory, floatingipHelperFactory{}, floatingipStatusWriter{}, c.defaultResyncPeriod)
221227
return builder.Complete(&r)
222228
}

internal/controllers/generic/interfaces/controller.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package interfaces
1818

1919
import (
2020
"context"
21+
"time"
2122

2223
ctrl "sigs.k8s.io/controller-runtime"
2324
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -31,6 +32,14 @@ type Controller interface {
3132
GetName() string
3233
}
3334

35+
// ResyncConfigurable is an optional interface that Controller implementations
36+
// may satisfy to receive the operator-level default resync period. The manager
37+
// calls SetDefaultResyncPeriod before SetupWithManager so that the value is
38+
// available when the controller is built.
39+
type ResyncConfigurable interface {
40+
SetDefaultResyncPeriod(time.Duration)
41+
}
42+
3443
type ResourceController interface {
3544
GetName() string
3645

internal/controllers/generic/interfaces/status.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ type ORCApplyConfig[objectApplyPT any, statusApplyPT ORCStatusApplyConfig[status
3535
}
3636

3737
// ORCStatusApplyConfig is an interface implemented by the status of any apply
38-
// configuration for an ORC API object. It has Conditions and an ID field.
38+
// configuration for an ORC API object. It has Conditions, an ID field, and a
39+
// LastSyncTime field.
3940
type ORCStatusApplyConfig[statusApplyPT any] interface {
4041
WithConditions(...*applyconfigv1.ConditionApplyConfiguration) statusApplyPT
4142
WithID(id string) statusApplyPT
43+
WithLastSyncTime(metav1.Time) statusApplyPT
4244
}
4345

4446
// ResourceStatusWriter defines methods for writing an ORC object status

internal/controllers/generic/progress/reconcile_status.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ type reconcileStatus struct {
4242
requeue time.Duration
4343

4444
err error
45+
46+
externallyDeleted bool
4547
}
4648

4749
// NewReconcileStatus returns an empty ReconcileStatus
@@ -177,9 +179,38 @@ func (r ReconcileStatus) WithReconcileStatus(o ReconcileStatus) ReconcileStatus
177179
return o
178180
}
179181

180-
return r.WithProgressMessage(o.GetProgressMessages()...).
182+
r = r.WithProgressMessage(o.GetProgressMessages()...).
181183
WithRequeue(o.GetRequeue()).
182184
WithError(o.GetError())
185+
if o != nil && o.externallyDeleted {
186+
r.externallyDeleted = true
187+
}
188+
return r
189+
}
190+
191+
// ExternallyDeleted returns a ReconcileStatus indicating that the OpenStack
192+
// resource referenced by status.id has been deleted outside of ORC. The caller
193+
// is expected to clear status.id and add an appropriate progress message.
194+
func (r ReconcileStatus) ExternallyDeleted() ReconcileStatus {
195+
if r == nil {
196+
r = &reconcileStatus{}
197+
}
198+
r.externallyDeleted = true
199+
return r
200+
}
201+
202+
// ExternallyDeleted is a convenience method which returns a new ReconcileStatus with ExternallyDeleted.
203+
func ExternallyDeleted() ReconcileStatus {
204+
return NewReconcileStatus().ExternallyDeleted()
205+
}
206+
207+
// IsExternallyDeleted returns true if the ReconcileStatus indicates that the
208+
// OpenStack resource was deleted externally.
209+
func (r ReconcileStatus) IsExternallyDeleted() bool {
210+
if r == nil {
211+
return false
212+
}
213+
return r.externallyDeleted
183214
}
184215

185216
// WaitingOnEvent represents the type of event we are waiting on

0 commit comments

Comments
 (0)