Skip to content

Commit 4ec8620

Browse files
Update only affected replicated policies
Signed-off-by: Yi Rae Kim <[email protected]>
1 parent df22cc2 commit 4ec8620

15 files changed

+1336
-164
lines changed

controllers/common/common.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,74 @@ func FullNameForPolicy(plc *policiesv1.Policy) string {
245245
return plc.GetNamespace() + "." + plc.GetName()
246246
}
247247

248+
// GetRepPoliciesInPlacementBinding returns a list of the RepPolicies that are either direct subjects of
249+
// the given PlacementBinding, or are in PolicySets that are subjects of the PlacementBinding.
250+
// The list items are guaranteed to be unique (for example if a policy is in multiple sets).
251+
func GetRepPoliciesInPlacementBinding(
252+
ctx context.Context, c client.Client, pb *policiesv1.PlacementBinding,
253+
) []reconcile.Request {
254+
result := make([]reconcile.Request, 0)
255+
256+
decisions, err := GetDecisions(c, pb) //nolint:contextcheck
257+
if err != nil {
258+
return result
259+
}
260+
261+
for _, subject := range pb.Subjects {
262+
if subject.APIGroup != policiesv1.SchemeGroupVersion.Group {
263+
continue
264+
}
265+
266+
switch subject.Kind {
267+
case policiesv1.Kind:
268+
for _, pd := range decisions {
269+
result = append(result, reconcile.Request{NamespacedName: types.NamespacedName{
270+
Name: pb.GetNamespace() + "." + subject.Name,
271+
Namespace: pd.ClusterName,
272+
}})
273+
}
274+
275+
case policiesv1.PolicySetKind:
276+
setNN := types.NamespacedName{
277+
Name: subject.Name,
278+
Namespace: pb.GetNamespace(),
279+
}
280+
281+
policySet := policiesv1beta1.PolicySet{}
282+
if err := c.Get(ctx, setNN, &policySet); err != nil {
283+
continue
284+
}
285+
286+
for _, plc := range policySet.Spec.Policies {
287+
for _, pd := range decisions {
288+
result = append(result, reconcile.Request{NamespacedName: types.NamespacedName{
289+
Name: pb.GetNamespace() + "." + string(plc),
290+
Namespace: pd.ClusterName,
291+
}})
292+
}
293+
}
294+
}
295+
}
296+
297+
var unique []reconcile.Request
298+
299+
table := map[reconcile.Request]bool{}
300+
// Remove duplicated policies
301+
if len(result) != 0 {
302+
for _, r := range result {
303+
if !table[r] {
304+
table[r] = true
305+
306+
unique = append(unique, r)
307+
}
308+
}
309+
310+
result = unique
311+
}
312+
313+
return result
314+
}
315+
248316
// TypeConverter is a helper function to converter type struct a to b
249317
func TypeConverter(a, b interface{}) error {
250318
js, err := json.Marshal(a)
@@ -254,3 +322,26 @@ func TypeConverter(a, b interface{}) error {
254322

255323
return json.Unmarshal(js, b)
256324
}
325+
326+
// Select objects that are deleted or created
327+
func GetAffectedOjbs[T comparable](oldObjs []T, newObjs []T) []T {
328+
table := make(map[T]int)
329+
330+
for _, oldObj := range oldObjs {
331+
table[oldObj] = 1
332+
}
333+
334+
for _, newObj := range newObjs {
335+
table[newObj]++
336+
}
337+
338+
result := []T{}
339+
340+
for key, val := range table {
341+
if val == 1 {
342+
result = append(result, key)
343+
}
344+
}
345+
346+
return result
347+
}

controllers/common/common_test.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
package common
22

3-
import "testing"
3+
import (
4+
"testing"
5+
6+
"github.com/google/go-cmp/cmp"
7+
"k8s.io/apimachinery/pkg/types"
8+
clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
9+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
10+
)
411

512
func TestParseRootPolicyLabel(t *testing.T) {
613
tests := map[string]struct {
@@ -28,3 +35,43 @@ func TestParseRootPolicyLabel(t *testing.T) {
2835
})
2936
}
3037
}
38+
39+
func TestGetAffectedOjbsWithDecision(t *testing.T) {
40+
newOjbs := []clusterv1beta1.ClusterDecision{
41+
{ClusterName: "managed1", Reason: "test11"},
42+
{ClusterName: "managed2", Reason: "test11"},
43+
}
44+
oldObjs := []clusterv1beta1.ClusterDecision{
45+
{ClusterName: "managed1", Reason: "test11"},
46+
{ClusterName: "managed3", Reason: "test11"},
47+
}
48+
expectedResult := []clusterv1beta1.ClusterDecision{
49+
{ClusterName: "managed2", Reason: "test11"},
50+
{ClusterName: "managed3", Reason: "test11"},
51+
}
52+
53+
result := GetAffectedOjbs(newOjbs, oldObjs)
54+
if !cmp.Equal(result, expectedResult) {
55+
t.Fatalf(`GetAffectedOjbs test failed expected: %+v but result is %+v`, expectedResult, result)
56+
}
57+
}
58+
59+
func TestGetAffectedOjbsWithRequest(t *testing.T) {
60+
newOjbs := []reconcile.Request{
61+
{NamespacedName: types.NamespacedName{Namespace: "test1", Name: "test1"}},
62+
{NamespacedName: types.NamespacedName{Namespace: "test2", Name: "test2"}},
63+
}
64+
oldOjbs := []reconcile.Request{
65+
{NamespacedName: types.NamespacedName{Namespace: "test1", Name: "test1"}},
66+
{NamespacedName: types.NamespacedName{Namespace: "test3", Name: "test3"}},
67+
}
68+
expectedResult := []reconcile.Request{
69+
{NamespacedName: types.NamespacedName{Namespace: "test2", Name: "test2"}},
70+
{NamespacedName: types.NamespacedName{Namespace: "test3", Name: "test3"}},
71+
}
72+
73+
result := GetAffectedOjbs(newOjbs, oldOjbs)
74+
if !cmp.Equal(result, expectedResult) {
75+
t.Fatalf(`GetAffectedOjbs test failed expected: %+v but result is %+v`, expectedResult, result)
76+
}
77+
}

controllers/propagator/replicatedpolicy_controller.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"sigs.k8s.io/controller-runtime/pkg/reconcile"
1818

1919
policiesv1 "open-cluster-management.io/governance-policy-propagator/api/v1"
20+
policyv1beta1 "open-cluster-management.io/governance-policy-propagator/api/v1beta1"
2021
"open-cluster-management.io/governance-policy-propagator/controllers/common"
2122
)
2223

@@ -126,6 +127,22 @@ func (r *ReplicatedPolicyReconciler) Reconcile(ctx context.Context, request ctrl
126127
return reconcile.Result{}, nil
127128
}
128129

130+
// err = r.rootStatusUpdate(rootPolicy)
131+
// if err != nil {
132+
// return reconcile.Result{}, err
133+
// }
134+
// Handle replicated policy, which is related to policyset
135+
isHandled, err := r.handlePolicySet(ctx, rootPolicy, replicatedPolicy)
136+
if err == nil && isHandled {
137+
return reconcile.Result{}, nil
138+
}
139+
140+
if err != nil && !isHandled {
141+
log.Error(err, "Failed to remove the replicated policy for PolicySet, requeueing")
142+
143+
return reconcile.Result{}, err
144+
}
145+
129146
if rootPolicy.Spec.Disabled {
130147
if replicatedExists {
131148
if err := r.cleanUpReplicated(ctx, replicatedPolicy); err != nil {
@@ -511,3 +528,61 @@ func (r *ReplicatedPolicyReconciler) isSingleClusterInDecisions(
511528

512529
return false, nil
513530
}
531+
532+
// Check this replicated policy related to policyset and
533+
// Check policyset existing and delete policies deteched from policyset
534+
func (r *ReplicatedPolicyReconciler) handlePolicySet(
535+
ctx context.Context, rootPlc *policiesv1.Policy, replicatedPolicy *policiesv1.Policy,
536+
) (isHandled bool, err error) {
537+
// Find PlacementBinding
538+
pbList := &policiesv1.PlacementBindingList{}
539+
540+
err = r.List(ctx, pbList, &client.ListOptions{Namespace: rootPlc.GetNamespace()})
541+
if err != nil {
542+
return false, err
543+
}
544+
545+
for _, pb := range pbList.Items {
546+
for _, sub := range pb.Subjects {
547+
policySet := &policyv1beta1.PolicySet{}
548+
if sub.Kind == "PolicySet" && policyv1beta1.GroupVersion.Group == sub.APIGroup {
549+
if err := r.Get(ctx, types.NamespacedName{
550+
Namespace: pb.Namespace,
551+
Name: sub.Name,
552+
}, policySet); err != nil {
553+
isCorrectPolicyset := false
554+
555+
for _, plc := range rootPlc.Status.Placement {
556+
if plc.PolicySet == sub.Name {
557+
isCorrectPolicyset = true
558+
}
559+
}
560+
561+
if !isCorrectPolicyset {
562+
log.V(2).Info("Incorrect PolicySet", "policySetName", sub.Name)
563+
564+
return false, nil
565+
}
566+
567+
if k8serrors.IsNotFound(err) {
568+
log.Info("PolicySet deleted so delete related replicated policy")
569+
570+
if err := r.cleanUpReplicated(ctx, replicatedPolicy); err != nil {
571+
if !k8serrors.IsNotFound(err) {
572+
return false, err
573+
}
574+
}
575+
576+
return true, nil
577+
}
578+
579+
log.Error(err, "Error to get policySet")
580+
581+
return false, err
582+
}
583+
}
584+
}
585+
}
586+
587+
return false, nil
588+
}

controllers/propagator/replicatedpolicy_setup.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,26 @@ package propagator
22

33
import (
44
"sync"
5+
"time"
56

67
"k8s.io/apimachinery/pkg/api/equality"
8+
clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
9+
appsv1 "open-cluster-management.io/multicloud-operators-subscription/pkg/apis/apps/placementrule/v1"
710
ctrl "sigs.k8s.io/controller-runtime"
811
"sigs.k8s.io/controller-runtime/pkg/builder"
912
"sigs.k8s.io/controller-runtime/pkg/controller"
1013
"sigs.k8s.io/controller-runtime/pkg/event"
1114
"sigs.k8s.io/controller-runtime/pkg/handler"
1215
"sigs.k8s.io/controller-runtime/pkg/predicate"
16+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
1317
"sigs.k8s.io/controller-runtime/pkg/source"
1418

1519
policiesv1 "open-cluster-management.io/governance-policy-propagator/api/v1"
1620
"open-cluster-management.io/governance-policy-propagator/controllers/common"
1721
)
1822

23+
var emptyRequest = []reconcile.Request{}
24+
1925
func (r *ReplicatedPolicyReconciler) SetupWithManager(
2026
mgr ctrl.Manager,
2127
maxConcurrentReconciles uint,
@@ -32,6 +38,30 @@ func (r *ReplicatedPolicyReconciler) SetupWithManager(
3238
WatchesRawSource(dependenciesSource, &handler.EnqueueRequestForObject{}).
3339
WatchesRawSource(updateSrc, &handler.EnqueueRequestForObject{}).
3440
WatchesRawSource(templateSrc, &handler.EnqueueRequestForObject{}).
41+
Watches(
42+
&clusterv1beta1.PlacementDecision{},
43+
EnqueueReqsFromMapFuncByDecision(mgr.GetClient()),
44+
).
45+
Watches(
46+
&policiesv1.PlacementBinding{},
47+
EnqueueReqsFromMapFuncByBinding(mgr.GetClient()),
48+
).
49+
Watches(
50+
&appsv1.PlacementRule{},
51+
EnqueueReqsFromMapFuncByRule(mgr.GetClient()),
52+
).
53+
WatchesRawSource(
54+
dynWatcherSrc,
55+
// The dependency-watcher could create an event before the same sort of watch in the
56+
// controller-runtime triggers an update in the cache. This tries to ensure the cache is
57+
// updated before the reconcile is triggered.
58+
&delayGeneric{
59+
EventHandler: &handler.EnqueueRequestForObject{},
60+
delay: time.Second * 3,
61+
}).
62+
WatchesRawSource(
63+
updateSrc,
64+
&handler.EnqueueRequestForObject{}).
3565
Complete(r)
3666
}
3767

0 commit comments

Comments
 (0)