diff --git a/api/v1alpha1/controller_options.go b/api/v1alpha1/controller_options.go
index d0908b10e..e38ad0d4d 100644
--- a/api/v1alpha1/controller_options.go
+++ b/api/v1alpha1/controller_options.go
@@ -16,6 +16,12 @@ limitations under the License.
package v1alpha1
+import (
+ "time"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
// +kubebuilder:validation:Enum:=managed;unmanaged
type ManagementPolicy string
@@ -53,6 +59,14 @@ type ManagedOptions struct {
// +kubebuilder:default:=delete
// +optional
OnDelete OnDelete `json:"onDelete,omitempty"`
+
+ // resyncPeriod specifies the interval after which a successfully
+ // reconciled resource will be reconciled again to detect drift from the
+ // desired state. Set to 0 to disable periodic resync. If not specified,
+ // the default is 10 hours.
+ // +kubebuilder:default:="10h"
+ // +optional
+ ResyncPeriod *metav1.Duration `json:"resyncPeriod,omitempty"` //nolint:kubeapilinter
}
// GetOnDelete returns the delete behaviour from ManagedOptions. If called on a
@@ -63,3 +77,15 @@ func (o *ManagedOptions) GetOnDelete() OnDelete {
}
return o.OnDelete
}
+
+// DefaultResyncPeriod is the default interval for periodic resync to detect drift (10 hours).
+const DefaultResyncPeriod = 10 * time.Hour
+
+// GetResyncPeriod returns the resync period from ManagedOptions. If called on a
+// nil receiver or if ResyncPeriod is not set, it returns the default of 10 hours.
+func (o *ManagedOptions) GetResyncPeriod() time.Duration {
+ if o == nil || o.ResyncPeriod == nil {
+ return DefaultResyncPeriod
+ }
+ return o.ResyncPeriod.Duration
+}
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 1024ea46a..edfc6f2d1 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -300,7 +300,7 @@ func (in *DomainSpec) DeepCopyInto(out *DomainSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -707,7 +707,7 @@ func (in *FlavorSpec) DeepCopyInto(out *FlavorSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -971,7 +971,7 @@ func (in *FloatingIPSpec) DeepCopyInto(out *FloatingIPSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -1188,7 +1188,7 @@ func (in *GroupSpec) DeepCopyInto(out *GroupSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -1700,7 +1700,7 @@ func (in *ImageSpec) DeepCopyInto(out *ImageSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -1928,7 +1928,7 @@ func (in *KeyPairSpec) DeepCopyInto(out *KeyPairSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -1978,6 +1978,11 @@ func (in *KeyPairStatus) DeepCopy() *KeyPairStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ManagedOptions) DeepCopyInto(out *ManagedOptions) {
*out = *in
+ if in.ResyncPeriod != nil {
+ in, out := &in.ResyncPeriod, &out.ResyncPeriod
+ *out = new(v1.Duration)
+ **out = **in
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagedOptions.
@@ -2257,7 +2262,7 @@ func (in *NetworkSpec) DeepCopyInto(out *NetworkSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -2608,7 +2613,7 @@ func (in *PortSpec) DeepCopyInto(out *PortSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -2836,7 +2841,7 @@ func (in *ProjectSpec) DeepCopyInto(out *ProjectSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -3073,7 +3078,7 @@ func (in *RoleSpec) DeepCopyInto(out *RoleSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -3447,7 +3452,7 @@ func (in *RouterSpec) DeepCopyInto(out *RouterSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -3760,7 +3765,7 @@ func (in *SecurityGroupSpec) DeepCopyInto(out *SecurityGroupSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -4055,7 +4060,7 @@ func (in *ServerGroupSpec) DeepCopyInto(out *ServerGroupSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -4366,7 +4371,7 @@ func (in *ServerSpec) DeepCopyInto(out *ServerSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -4623,7 +4628,7 @@ func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -4967,7 +4972,7 @@ func (in *SubnetSpec) DeepCopyInto(out *SubnetSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -5308,7 +5313,7 @@ func (in *VolumeSpec) DeepCopyInto(out *VolumeSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
@@ -5575,7 +5580,7 @@ func (in *VolumeTypeSpec) DeepCopyInto(out *VolumeTypeSpec) {
if in.ManagedOptions != nil {
in, out := &in.ManagedOptions, &out.ManagedOptions
*out = new(ManagedOptions)
- **out = **in
+ (*in).DeepCopyInto(*out)
}
out.CloudCredentialsRef = in.CloudCredentialsRef
}
diff --git a/cmd/models-schema/zz_generated.openapi.go b/cmd/models-schema/zz_generated.openapi.go
index 42f004b03..f6dcfeadb 100644
--- a/cmd/models-schema/zz_generated.openapi.go
+++ b/cmd/models-schema/zz_generated.openapi.go
@@ -3774,9 +3774,17 @@ func schema_openstack_resource_controller_v2_api_v1alpha1_ManagedOptions(ref com
Format: "",
},
},
+ "resyncPeriod": {
+ SchemaProps: spec.SchemaProps{
+ Description: "resyncPeriod specifies the interval after which a successfully reconciled resource will be reconciled again to detect drift from the desired state. Set to 0 to disable periodic resync. If not specified, the default is 10 hours.",
+ Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Duration"),
+ },
+ },
},
},
},
+ Dependencies: []string{
+ "k8s.io/apimachinery/pkg/apis/meta/v1.Duration"},
}
}
diff --git a/config/crd/bases/openstack.k-orc.cloud_domains.yaml b/config/crd/bases/openstack.k-orc.cloud_domains.yaml
index 893ff831c..f9acb41bb 100644
--- a/config/crd/bases/openstack.k-orc.cloud_domains.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_domains.yaml
@@ -125,6 +125,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_flavors.yaml b/config/crd/bases/openstack.k-orc.cloud_flavors.yaml
index d1c930aa2..ae3b474b8 100644
--- a/config/crd/bases/openstack.k-orc.cloud_flavors.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_flavors.yaml
@@ -137,6 +137,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_floatingips.yaml b/config/crd/bases/openstack.k-orc.cloud_floatingips.yaml
index 686b41c71..a0a5cbcb6 100644
--- a/config/crd/bases/openstack.k-orc.cloud_floatingips.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_floatingips.yaml
@@ -209,6 +209,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_groups.yaml b/config/crd/bases/openstack.k-orc.cloud_groups.yaml
index e62f33c1c..ebe56583d 100644
--- a/config/crd/bases/openstack.k-orc.cloud_groups.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_groups.yaml
@@ -126,6 +126,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_images.yaml b/config/crd/bases/openstack.k-orc.cloud_images.yaml
index 6b5dce9b9..109aa537e 100644
--- a/config/crd/bases/openstack.k-orc.cloud_images.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_images.yaml
@@ -139,6 +139,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_keypairs.yaml b/config/crd/bases/openstack.k-orc.cloud_keypairs.yaml
index 969748e90..b83c290f7 100644
--- a/config/crd/bases/openstack.k-orc.cloud_keypairs.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_keypairs.yaml
@@ -121,6 +121,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_networks.yaml b/config/crd/bases/openstack.k-orc.cloud_networks.yaml
index bba47e4c8..d431a45b5 100644
--- a/config/crd/bases/openstack.k-orc.cloud_networks.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_networks.yaml
@@ -195,6 +195,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_ports.yaml b/config/crd/bases/openstack.k-orc.cloud_ports.yaml
index c14173d71..c51acacd9 100644
--- a/config/crd/bases/openstack.k-orc.cloud_ports.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_ports.yaml
@@ -209,6 +209,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_projects.yaml b/config/crd/bases/openstack.k-orc.cloud_projects.yaml
index ab550af2b..470b3b4f4 100644
--- a/config/crd/bases/openstack.k-orc.cloud_projects.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_projects.yaml
@@ -165,6 +165,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_roles.yaml b/config/crd/bases/openstack.k-orc.cloud_roles.yaml
index 98cb4993d..7a43e41e7 100644
--- a/config/crd/bases/openstack.k-orc.cloud_roles.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_roles.yaml
@@ -126,6 +126,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_routers.yaml b/config/crd/bases/openstack.k-orc.cloud_routers.yaml
index 870aee9db..c19e2fb83 100644
--- a/config/crd/bases/openstack.k-orc.cloud_routers.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_routers.yaml
@@ -190,6 +190,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_securitygroups.yaml b/config/crd/bases/openstack.k-orc.cloud_securitygroups.yaml
index 13cef5e35..98bfee8a9 100644
--- a/config/crd/bases/openstack.k-orc.cloud_securitygroups.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_securitygroups.yaml
@@ -190,6 +190,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_servergroups.yaml b/config/crd/bases/openstack.k-orc.cloud_servergroups.yaml
index ad2eafd83..873f3bbd3 100644
--- a/config/crd/bases/openstack.k-orc.cloud_servergroups.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_servergroups.yaml
@@ -121,6 +121,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_servers.yaml b/config/crd/bases/openstack.k-orc.cloud_servers.yaml
index 2a882bad3..f7f06f67c 100644
--- a/config/crd/bases/openstack.k-orc.cloud_servers.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_servers.yaml
@@ -171,6 +171,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_services.yaml b/config/crd/bases/openstack.k-orc.cloud_services.yaml
index 9e6a16416..79b563306 100644
--- a/config/crd/bases/openstack.k-orc.cloud_services.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_services.yaml
@@ -126,6 +126,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_subnets.yaml b/config/crd/bases/openstack.k-orc.cloud_subnets.yaml
index e0445ee80..354dfcd4f 100644
--- a/config/crd/bases/openstack.k-orc.cloud_subnets.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_subnets.yaml
@@ -237,6 +237,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_volumes.yaml b/config/crd/bases/openstack.k-orc.cloud_volumes.yaml
index eeaf10a8b..12392695f 100644
--- a/config/crd/bases/openstack.k-orc.cloud_volumes.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_volumes.yaml
@@ -136,6 +136,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/config/crd/bases/openstack.k-orc.cloud_volumetypes.yaml b/config/crd/bases/openstack.k-orc.cloud_volumetypes.yaml
index c92df01fc..8080366f8 100644
--- a/config/crd/bases/openstack.k-orc.cloud_volumetypes.yaml
+++ b/config/crd/bases/openstack.k-orc.cloud_volumetypes.yaml
@@ -130,6 +130,14 @@ spec:
- delete
- detach
type: string
+ resyncPeriod:
+ default: 10h
+ description: |-
+ resyncPeriod specifies the interval after which a successfully
+ reconciled resource will be reconciled again to detect drift from the
+ desired state. Set to 0 to disable periodic resync. If not specified,
+ the default is 10 hours.
+ type: string
type: object
managementPolicy:
default: managed
diff --git a/internal/controllers/generic/reconciler/controller.go b/internal/controllers/generic/reconciler/controller.go
index 7571519cd..56ca8512f 100644
--- a/internal/controllers/generic/reconciler/controller.go
+++ b/internal/controllers/generic/reconciler/controller.go
@@ -19,6 +19,7 @@ package reconciler
import (
"context"
"fmt"
+ "time"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
@@ -135,15 +136,16 @@ func (c *Controller[
// status indicates that no further reconciliation is required.
//
// Specifically it looks at the Progressing condition. It has the following behaviour:
-// - Progressing condition is not present -> reconcile
-// - Progressing condition is present and True -> reconcile
-// - Progressing condition is present and False, but observedGeneration is old -> reconcile
-// - Progressing condition is false and observedGeneration is up to date -> do not reconcile
+// - Progressing condition is not present -> reconcile
+// - Progressing condition is present and True -> reconcile
+// - Progressing condition is present and False, but observedGeneration is old -> reconcile
+// - Progressing condition is false and observedGeneration is up to date -> do not reconcile
+// (unless periodic resync is enabled and enough time has passed since the last sync)
//
// If shouldReconcile is preventing an object from being reconciled which should
// be reconciled, consider if that object's actuator is correctly returning a
// ProgressStatus indicating that the reconciliation should continue.
-func shouldReconcile(obj orcv1alpha1.ObjectWithConditions) bool {
+func shouldReconcile(obj orcv1alpha1.ObjectWithConditions, resyncPeriod time.Duration) bool {
progressing := meta.FindStatusCondition(obj.GetConditions(), orcv1alpha1.ConditionProgressing)
if progressing == nil {
return true
@@ -153,7 +155,17 @@ func shouldReconcile(obj orcv1alpha1.ObjectWithConditions) bool {
return true
}
- return progressing.ObservedGeneration != obj.GetGeneration()
+ if progressing.ObservedGeneration != obj.GetGeneration() {
+ return true
+ }
+
+ // At this point, Progressing is False and generation is up to date.
+ // For periodic resync, check if enough time has passed since the last sync.
+ if resyncPeriod > 0 {
+ return time.Since(progressing.LastTransitionTime.Time) >= resyncPeriod
+ }
+
+ return false
}
func (c *Controller[
@@ -165,10 +177,13 @@ func (c *Controller[
]) reconcileNormal(ctx context.Context, objAdapter interfaces.APIObjectAdapter[orcObjectPT, resourceSpecT, filterT]) (reconcileStatus progress.ReconcileStatus) {
log := ctrl.LoggerFrom(ctx)
+ // Get the resync period from the object's managed options
+ resyncPeriod := objAdapter.GetManagedOptions().GetResyncPeriod()
+
// We do this here rather than in a predicate because predicates only cover
// a single watch. Doing it here means we cover all sources of
// reconciliation, including our dependencies.
- if !shouldReconcile(objAdapter.GetObject()) {
+ if !shouldReconcile(objAdapter.GetObject(), resyncPeriod) {
log.V(logging.Verbose).Info("Status is up to date: not reconciling")
return reconcileStatus
}
@@ -204,8 +219,10 @@ func (c *Controller[
return reconcileStatus.WithError(fmt.Errorf("oResource is not set, but no wait events or error"))
}
- if objAdapter.GetStatusID() == nil {
- resourceID := actuator.GetResourceID(osResource)
+ resourceID := actuator.GetResourceID(osResource)
+ statusID := objAdapter.GetStatusID()
+ if statusID == nil || *statusID != resourceID {
+ // Update status ID if not set, or if it differs (e.g., after recreation due to drift detection)
if err := status.SetStatusID(ctx, c, objAdapter.GetObject(), resourceID, c.statusWriter); err != nil {
return reconcileStatus.WithError(err)
}
@@ -229,6 +246,15 @@ func (c *Controller[
}
}
+ // If periodic resync is enabled and we're not already rescheduling for
+ // another reason, schedule the next resync to detect drift.
+ if resyncPeriod > 0 {
+ needsReschedule, _ := reconcileStatus.NeedsReschedule()
+ if !needsReschedule {
+ reconcileStatus = reconcileStatus.WithRequeue(resyncPeriod)
+ }
+ }
+
return reconcileStatus
}
diff --git a/internal/controllers/generic/reconciler/resource_actions.go b/internal/controllers/generic/reconciler/resource_actions.go
index 49f4598b1..6e1092843 100644
--- a/internal/controllers/generic/reconciler/resource_actions.go
+++ b/internal/controllers/generic/reconciler/resource_actions.go
@@ -70,17 +70,26 @@ func GetOrCreateOSResource[
osResource, reconcileStatus := actuator.GetOSResourceByID(ctx, *resourceID)
if needsReschedule, err := reconcileStatus.NeedsReschedule(); needsReschedule {
if orcerrors.IsNotFound(err) {
- // An OpenStack resource we previously referenced has been deleted unexpectedly. We can't recover from this.
- return osResource, progress.WrapError(
- orcerrors.Terminal(orcv1alpha1.ConditionReasonUnrecoverableError, "resource has been deleted from OpenStack"))
+ // An OpenStack resource we previously referenced has been deleted unexpectedly.
+ // For managed resources, we can recover by recreating the resource.
+ // For imported resources, this is an unrecoverable error.
+ if objAdapter.GetManagementPolicy() == orcv1alpha1.ManagementPolicyManaged && objAdapter.GetImportID() == nil && objAdapter.GetImportFilter() == nil {
+ log.V(logging.Info).Info("Resource has been deleted from OpenStack, will recreate", "ID", *resourceID)
+ // Fall through to creation by not returning here.
+ // The status ID will be updated after the new resource is created.
+ } else {
+ return osResource, progress.WrapError(
+ orcerrors.Terminal(orcv1alpha1.ConditionReasonUnrecoverableError, "resource has been deleted from OpenStack"))
+ }
} else {
return osResource, reconcileStatus
}
}
if osResource != nil {
log.V(logging.Verbose).Info("Got existing OpenStack resource", "ID", actuator.GetResourceID(osResource))
+ return osResource, nil
}
- return osResource, nil
+ // osResource is nil, fall through to creation
}
// Import by ID
diff --git a/pkg/clients/applyconfiguration/api/v1alpha1/managedoptions.go b/pkg/clients/applyconfiguration/api/v1alpha1/managedoptions.go
index 092ab7883..0c64d1b0d 100644
--- a/pkg/clients/applyconfiguration/api/v1alpha1/managedoptions.go
+++ b/pkg/clients/applyconfiguration/api/v1alpha1/managedoptions.go
@@ -20,12 +20,14 @@ package v1alpha1
import (
apiv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1"
+ v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ManagedOptionsApplyConfiguration represents a declarative configuration of the ManagedOptions type for use
// with apply.
type ManagedOptionsApplyConfiguration struct {
- OnDelete *apiv1alpha1.OnDelete `json:"onDelete,omitempty"`
+ OnDelete *apiv1alpha1.OnDelete `json:"onDelete,omitempty"`
+ ResyncPeriod *v1.Duration `json:"resyncPeriod,omitempty"`
}
// ManagedOptionsApplyConfiguration constructs a declarative configuration of the ManagedOptions type for use with
@@ -41,3 +43,11 @@ func (b *ManagedOptionsApplyConfiguration) WithOnDelete(value apiv1alpha1.OnDele
b.OnDelete = &value
return b
}
+
+// WithResyncPeriod sets the ResyncPeriod field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the ResyncPeriod field is set to the value of the last call.
+func (b *ManagedOptionsApplyConfiguration) WithResyncPeriod(value v1.Duration) *ManagedOptionsApplyConfiguration {
+ b.ResyncPeriod = &value
+ return b
+}
diff --git a/pkg/clients/applyconfiguration/internal/internal.go b/pkg/clients/applyconfiguration/internal/internal.go
index dfb83d47f..482c725bd 100644
--- a/pkg/clients/applyconfiguration/internal/internal.go
+++ b/pkg/clients/applyconfiguration/internal/internal.go
@@ -1004,6 +1004,9 @@ var schemaYAML = typed.YAMLObject(`types:
- name: onDelete
type:
scalar: string
+ - name: resyncPeriod
+ type:
+ namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Duration
- name: com.github.k-orc.openstack-resource-controller.v2.api.v1alpha1.Network
map:
fields:
@@ -3289,6 +3292,8 @@ var schemaYAML = typed.YAMLObject(`types:
type:
scalar: string
default: ""
+- name: io.k8s.apimachinery.pkg.apis.meta.v1.Duration
+ scalar: string
- name: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1
map:
elementType:
diff --git a/website/docs/crd-reference.md b/website/docs/crd-reference.md
index f1664a185..32416f124 100644
--- a/website/docs/crd-reference.md
+++ b/website/docs/crd-reference.md
@@ -1698,6 +1698,7 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `onDelete` _[OnDelete](#ondelete)_ | onDelete specifies the behaviour of the controller when the ORC
object is deleted. Options are `delete` - delete the OpenStack resource;
`detach` - do not delete the OpenStack resource. If not specified, the
default is `delete`. | delete | Enum: [delete detach]
|
+| `resyncPeriod` _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#duration-v1-meta)_ | resyncPeriod specifies the interval after which a successfully
reconciled resource will be reconciled again to detect drift from the
desired state. Set to 0 to disable periodic resync. If not specified,
the default is 10 hours. | 10h | |
#### ManagementPolicy
diff --git a/website/docs/user-guide/index.md b/website/docs/user-guide/index.md
index 4945fcdef..741a771c3 100644
--- a/website/docs/user-guide/index.md
+++ b/website/docs/user-guide/index.md
@@ -100,6 +100,47 @@ spec:
# ...
```
+### Drift Detection
+
+ORC periodically reconciles managed resources to detect and correct drift from the desired state. This ensures that if someone modifies an OpenStack resource outside of ORC (e.g., through the OpenStack CLI or dashboard), ORC will detect the change and restore the resource to match the Kubernetes specification.
+
+**By default, drift detection is enabled with a resync period of 10 hours (`10h`).** The `managedOptions.resyncPeriod` field controls how often ORC checks for drift using standard duration format (e.g., `1h`, `30m`, `24h`):
+
+| Value | Description |
+|-------|-------------|
+| `10h` | Check for drift every 10 hours. This is the default. |
+| `1h` | Check for drift every hour. |
+| `30m` | Check for drift every 30 minutes. |
+| `0` | Disable periodic drift detection. Resources are only reconciled when their spec changes. |
+
+```yaml
+spec:
+ managementPolicy: managed
+ managedOptions:
+ resyncPeriod: 1h # Check for drift every hour
+ resource:
+ # ...
+```
+
+!!! note
+
+ Drift detection only applies to managed resources. Unmanaged resources are never modified by ORC, so drift detection does not apply to them.
+
+!!! tip
+
+ For resources that are frequently modified outside of ORC, consider using a shorter resync period. For stable resources, the default of 10 hours is usually sufficient.
+
+!!! warning
+
+ Be aware of the side effects of drift detection, especially with a low `resyncPeriod`:
+
+ - **Increased OpenStack API load**: Each drift detection cycle queries the OpenStack API. A low resyncPeriod across many resources can generate significant API traffic and may trigger rate limiting.
+ - **Controller resource consumption**: Frequent reconciliation increases CPU and memory usage on the ORC controller.
+ - **Potential conflicts**: If resources are actively being modified by external systems (other controllers, automation scripts, or manual operations), frequent drift correction can cause conflicts or unexpected behavior.
+ - **Network overhead**: Each reconciliation involves network calls to OpenStack, which adds latency and bandwidth usage.
+
+ Consider your environment's scale and requirements when configuring resyncPeriod. For most use cases, the default of 10 hours provides a good balance between drift detection and resource efficiency.
+
### Resource References
ORC resources reference each other using `*Ref` fields. These references: