Skip to content

Commit de1345a

Browse files
mprahlopenshift-merge-bot[bot]
authored andcommitted
Trigger status-sync reconciles when spec-sync sees mismatching status
Since status-sync watches the managed cluster's replicated policies on the managed cluster (not the hub), it has a blind spot for when the hub's status doesn't match and nothing triggers a status-sync reconcile (e.g. new compliance event or policy update). Since spec-sync is already watching the managed cluster's replicated policies on the hub, it can do the work of detecting the status mismatch on the hub and managed cluster, and trigger a status-sync reconcile to have the status-sync controller fix it. Relates: https://issues.redhat.com/browse/ACM-12447 Signed-off-by: mprahl <[email protected]>
1 parent c2681fe commit de1345a

File tree

4 files changed

+55
-8
lines changed

4 files changed

+55
-8
lines changed

controllers/specsync/policy_spec_sync.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"time"
1010

11+
"k8s.io/apimachinery/pkg/api/equality"
1112
"k8s.io/apimachinery/pkg/api/errors"
1213
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1314
"k8s.io/apimachinery/pkg/runtime"
@@ -18,6 +19,7 @@ import (
1819
ctrl "sigs.k8s.io/controller-runtime"
1920
"sigs.k8s.io/controller-runtime/pkg/client"
2021
"sigs.k8s.io/controller-runtime/pkg/controller"
22+
"sigs.k8s.io/controller-runtime/pkg/event"
2123
"sigs.k8s.io/controller-runtime/pkg/handler"
2224
logf "sigs.k8s.io/controller-runtime/pkg/log"
2325
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -59,6 +61,8 @@ type PolicyReconciler struct {
5961
// The namespace that the replicated policies should be synced to.
6062
TargetNamespace string
6163
ConcurrentReconciles int
64+
// StatusSyncRequests triggers status-sync controller reconciles based on what is observed on the hub
65+
StatusSyncRequests chan<- event.GenericEvent
6266
}
6367

6468
//+kubebuilder:rbac:groups=policy.open-cluster-management.io,resources=policies,verbs=create;delete;get;list;patch;update;watch
@@ -173,6 +177,10 @@ func (r *PolicyReconciler) Reconcile(ctx context.Context, request reconcile.Requ
173177
r.ManagedRecorder.Event(managedPlc, "Normal", "PolicySpecSync",
174178
fmt.Sprintf("Policy %s was updated in cluster namespace %s", instance.GetName(),
175179
r.TargetNamespace))
180+
} else if !equality.Semantic.DeepEqual(instance.Status, managedPlc.Status) {
181+
reqLogger.Info("Policy status does not match on the hub. Triggering the status-sync to update it.")
182+
183+
r.StatusSyncRequests <- event.GenericEvent{Object: managedPlc}
176184
}
177185

178186
reqLogger.V(2).Info("Reconciliation complete.")

controllers/statussync/policy_status_sync.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"sigs.k8s.io/controller-runtime/pkg/event"
4242
"sigs.k8s.io/controller-runtime/pkg/handler"
4343
"sigs.k8s.io/controller-runtime/pkg/reconcile"
44+
"sigs.k8s.io/controller-runtime/pkg/source"
4445

4546
"open-cluster-management.io/governance-policy-framework-addon/controllers/uninstall"
4647
"open-cluster-management.io/governance-policy-framework-addon/controllers/utils"
@@ -61,17 +62,22 @@ var (
6162
)
6263

6364
// SetupWithManager sets up the controller with the Manager.
64-
func (r *PolicyReconciler) SetupWithManager(mgr ctrl.Manager) error {
65-
return ctrl.NewControllerManagedBy(mgr).
65+
func (r *PolicyReconciler) SetupWithManager(mgr ctrl.Manager, additionalSource *source.Channel) error {
66+
builder := ctrl.NewControllerManagedBy(mgr).
6667
For(&policiesv1.Policy{}).
6768
Watches(
6869
&corev1.Event{},
6970
handler.EnqueueRequestsFromMapFunc(eventMapper),
7071
builder.WithPredicates(eventPredicateFuncs),
7172
).
7273
WithOptions(controller.Options{MaxConcurrentReconciles: r.ConcurrentReconciles}).
73-
Named(ControllerName).
74-
Complete(r)
74+
Named(ControllerName)
75+
76+
if additionalSource != nil {
77+
builder = builder.WatchesRawSource(additionalSource, &handler.EnqueueRequestForObject{})
78+
}
79+
80+
return builder.Complete(r)
7581
}
7682

7783
// blank assignment to verify that ReconcilePolicy implements reconcile.Reconciler

main.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,8 @@ func addControllers(
780780
var hubClient client.Client
781781
var specSyncRequests chan event.GenericEvent
782782
var specSyncRequestsSource *source.Channel
783+
var statusSyncRequests chan event.GenericEvent
784+
var statusSyncRequestsSource *source.Channel
783785

784786
if hubMgr == nil {
785787
hubCache, err := cache.New(hubCfg,
@@ -830,6 +832,12 @@ func addControllers(
830832
DestBufferSize: bufferSize,
831833
}
832834

835+
statusSyncRequests = make(chan event.GenericEvent, bufferSize)
836+
statusSyncRequestsSource = &source.Channel{
837+
Source: statusSyncRequests,
838+
DestBufferSize: bufferSize,
839+
}
840+
833841
hubClient = hubMgr.GetClient()
834842
}
835843

@@ -852,7 +860,7 @@ func addControllers(
852860
ConcurrentReconciles: int(tool.Options.EvaluationConcurrency),
853861
EventsQueue: queue,
854862
SpecSyncRequests: specSyncRequests,
855-
}).SetupWithManager(managedMgr); err != nil {
863+
}).SetupWithManager(managedMgr, statusSyncRequestsSource); err != nil {
856864
log.Error(err, "unable to create controller", "controller", "Policy")
857865
os.Exit(1)
858866
}
@@ -917,6 +925,7 @@ func addControllers(
917925
Scheme: hubMgr.GetScheme(),
918926
TargetNamespace: tool.Options.ClusterNamespace,
919927
ConcurrentReconciles: int(tool.Options.EvaluationConcurrency),
928+
StatusSyncRequests: statusSyncRequests,
920929
}).SetupWithManager(hubMgr, specSyncRequestsSource); err != nil {
921930
log.Error(err, "Unable to create the controller", "controller", specsync.ControllerName)
922931
os.Exit(1)

test/e2e/case2_status_sync_test.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
. "github.com/onsi/ginkgo/v2"
1010
. "github.com/onsi/gomega"
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1213
"k8s.io/apimachinery/pkg/runtime"
1314
policiesv1 "open-cluster-management.io/governance-policy-propagator/api/v1"
1415
"open-cluster-management.io/governance-policy-propagator/test/utils"
@@ -117,7 +118,7 @@ var _ = Describe("Test status sync", func() {
117118
return hubPlc.Object["status"]
118119
}, defaultTimeoutSeconds, 1).Should(Equal(managedPlc.Object["status"]))
119120
})
120-
It("Should set status to Compliant again", func() {
121+
It("Should set status to Compliant again", func(ctx SpecContext) {
121122
By("Generating an compliant event on the policy")
122123
managedPlc := utils.GetWithTimeout(
123124
clientManagedDynamic,
@@ -150,9 +151,12 @@ var _ = Describe("Test status sync", func() {
150151
Expect((plc.Status.Details)).To(HaveLen(1))
151152
Expect((plc.Status.Details[0].History)).To(HaveLen(1))
152153
Expect(plc.Status.Details[0].TemplateMeta.GetName()).To(Equal("case2-test-policy-configurationpolicy"))
153-
By("Checking if hub policy status is in sync")
154+
155+
By("Checking if the hub policy status is in sync")
156+
var hubPlc *unstructured.Unstructured
157+
154158
Eventually(func() interface{} {
155-
hubPlc := utils.GetWithTimeout(
159+
hubPlc = utils.GetWithTimeout(
156160
clientHubDynamic,
157161
gvrPolicy,
158162
case2PolicyName,
@@ -162,6 +166,26 @@ var _ = Describe("Test status sync", func() {
162166

163167
return hubPlc.Object["status"]
164168
}, defaultTimeoutSeconds, 1).Should(Equal(managedPlc.Object["status"]))
169+
170+
By("Clearing the hub status to verify it gets recovered")
171+
delete(hubPlc.Object, "status")
172+
_, err = clientHubDynamic.Resource(gvrPolicy).Namespace(clusterNamespaceOnHub).UpdateStatus(
173+
ctx, hubPlc, metav1.UpdateOptions{},
174+
)
175+
Expect(err).ShouldNot(HaveOccurred())
176+
177+
Eventually(func() interface{} {
178+
hubPlc = utils.GetWithTimeout(
179+
clientHubDynamic,
180+
gvrPolicy,
181+
case2PolicyName,
182+
clusterNamespaceOnHub,
183+
true,
184+
defaultTimeoutSeconds)
185+
186+
return hubPlc.Object["status"]
187+
}, defaultTimeoutSeconds, 1).Should(Equal(managedPlc.Object["status"]))
188+
165189
By("clean up all events")
166190
_, err = kubectlManaged("delete", "events", "-n", clusterNamespace, "--all")
167191
Expect(err).ShouldNot(HaveOccurred())

0 commit comments

Comments
 (0)