Skip to content

Commit

Permalink
🤖 Sync from open-cluster-management-io/governance-policy-propagator: #…
Browse files Browse the repository at this point in the history
…136 (#470)

* Update only affected replicated policies

Signed-off-by: Yi Rae Kim <[email protected]>
(cherry picked from commit a0c3284)

* Refactor rootStatusPolicy to make it as common function

Signed-off-by: Yi Rae Kim <[email protected]>
(cherry picked from commit a15f6ac)

* Filter replicated policy in rootstatus ctlr

Signed-off-by: Yi Rae Kim <[email protected]>

---------

Signed-off-by: Yi Rae Kim <[email protected]>
Co-authored-by: Yi Rae Kim <[email protected]>
  • Loading branch information
magic-mirror-bot[bot] and yiraeChristineKim authored Oct 23, 2023
1 parent 90b33f5 commit 67595c3
Show file tree
Hide file tree
Showing 31 changed files with 1,433 additions and 1,045 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Contributors to the Open Cluster Management project

package common
package automation

import (
"context"
Expand All @@ -15,21 +15,17 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
ctrl "sigs.k8s.io/controller-runtime"

policyv1beta1 "open-cluster-management.io/governance-policy-propagator/api/v1beta1"
)

const (
ControllerName string = "policy-automation"
PolicyAutomationLabel string = "policy.open-cluster-management.io/policyautomation-name"
PolicyAutomationGeneration string = "policy.open-cluster-management.io/policyautomation-generation"
// policyautomation-ResouceVersion
PolicyAutomationResouceV string = "policy.open-cluster-management.io/policyautomation-resource-version"
)

var log = ctrl.Log.WithName(ControllerName)

var ansibleJobRes = schema.GroupVersionResource{
Group: "tower.ansible.com", Version: "v1alpha1",
Resource: "ansiblejobs",
Expand Down
12 changes: 6 additions & 6 deletions controllers/automation/policyautomation_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ func (r *PolicyAutomationReconciler) Reconcile(
}

if policyAutomation.Annotations["policy.open-cluster-management.io/rerun"] == "true" {
AjExist, err := common.MatchPAResouceV(policyAutomation,
AjExist, err := MatchPAResouceV(policyAutomation,
r.DynamicClient, policyAutomation.GetResourceVersion())
if err != nil {
log.Error(err, "Failed to compare Ansible job's resourceVersion")
Expand All @@ -323,7 +323,7 @@ func (r *PolicyAutomationReconciler) Reconcile(

violationContext, _ := r.getViolationContext(ctx, policy, targetList, policyAutomation)

err = common.CreateAnsibleJob(
err = CreateAnsibleJob(
policyAutomation,
r.DynamicClient,
"manual",
Expand Down Expand Up @@ -373,7 +373,7 @@ func (r *PolicyAutomationReconciler) Reconcile(
if len(targetList) > 0 {
log.Info("Creating An Ansible job", "targetList", targetList)
violationContext, _ := r.getViolationContext(ctx, policy, targetList, policyAutomation)
err = common.CreateAnsibleJob(policyAutomation, r.DynamicClient, "scan",
err = CreateAnsibleJob(policyAutomation, r.DynamicClient, "scan",
violationContext)
if err != nil {
return reconcile.Result{RequeueAfter: requeueAfter}, err
Expand All @@ -395,7 +395,7 @@ func (r *PolicyAutomationReconciler) Reconcile(
if len(targetList) > 0 {
log.Info("Creating an Ansible job", "targetList", targetList)

AjExist, err := common.MatchPAGeneration(policyAutomation,
AjExist, err := MatchPAGeneration(policyAutomation,
r.DynamicClient, policyAutomation.GetGeneration())
if err != nil {
log.Error(err, "Failed to get Ansible job's generation")
Expand All @@ -406,7 +406,7 @@ func (r *PolicyAutomationReconciler) Reconcile(
return reconcile.Result{}, nil
}
violationContext, _ := r.getViolationContext(ctx, policy, targetList, policyAutomation)
err = common.CreateAnsibleJob(
err = CreateAnsibleJob(
policyAutomation,
r.DynamicClient,
string(policyv1beta1.Once),
Expand Down Expand Up @@ -517,7 +517,7 @@ func (r *PolicyAutomationReconciler) Reconcile(
}
log.Info("Creating An Ansible job", "trimmedTargetList", trimmedTargetList)
violationContext, _ := r.getViolationContext(ctx, policy, trimmedTargetList, policyAutomation)
err = common.CreateAnsibleJob(
err = CreateAnsibleJob(
policyAutomation,
r.DynamicClient,
string(policyv1beta1.EveryEvent),
Expand Down
138 changes: 123 additions & 15 deletions controllers/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
clusterv1 "open-cluster-management.io/api/cluster/v1"
clusterv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
appsv1 "open-cluster-management.io/multicloud-operators-subscription/pkg/apis/apps/placementrule/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

Expand All @@ -29,13 +30,16 @@ const (
RootPolicyLabel string = APIGroup + "/root-policy"
)

var ErrInvalidLabelValue = errors.New("unexpected format of label value")
var (
log = ctrl.Log.WithName("common")
ErrInvalidLabelValue = errors.New("unexpected format of label value")
)

// IsInClusterNamespace check if policy is in cluster namespace
func IsInClusterNamespace(c client.Client, ns string) (bool, error) {
func IsInClusterNamespace(ctx context.Context, c client.Client, ns string) (bool, error) {
cluster := &clusterv1.ManagedCluster{}

err := c.Get(context.TODO(), types.NamespacedName{Name: ns}, cluster)
err := c.Get(ctx, types.NamespacedName{Name: ns}, cluster)
if k8serrors.IsNotFound(err) {
return false, nil
}
Expand All @@ -47,7 +51,7 @@ func IsInClusterNamespace(c client.Client, ns string) (bool, error) {
return true, nil
}

func IsReplicatedPolicy(c client.Client, policy client.Object) (bool, error) {
func IsReplicatedPolicy(ctx context.Context, c client.Client, policy client.Object) (bool, error) {
rootPlcName := policy.GetLabels()[RootPolicyLabel]
if rootPlcName == "" {
return false, nil
Expand All @@ -58,7 +62,7 @@ func IsReplicatedPolicy(c client.Client, policy client.Object) (bool, error) {
return false, fmt.Errorf("invalid value set in %s: %w", RootPolicyLabel, err)
}

return IsInClusterNamespace(c, policy.GetNamespace())
return IsInClusterNamespace(ctx, c, policy.GetNamespace())
}

// IsForPolicyOrPolicySet returns true if any of the subjects of the PlacementBinding are Policies
Expand Down Expand Up @@ -96,11 +100,11 @@ func IsPbForPolicySet(pb *policiesv1.PlacementBinding) bool {

// GetPoliciesInPlacementBinding returns a list of the Policies that are either direct subjects of
// the given PlacementBinding, or are in PolicySets that are subjects of the PlacementBinding.
// The list items are not guaranteed to be unique (for example if a policy is in multiple sets).
// The list items are guaranteed to be unique (for example if a policy is in multiple sets).
func GetPoliciesInPlacementBinding(
ctx context.Context, c client.Client, pb *policiesv1.PlacementBinding,
) []reconcile.Request {
result := make([]reconcile.Request, 0)
table := map[reconcile.Request]bool{}

for _, subject := range pb.Subjects {
if subject.APIGroup != policiesv1.SchemeGroupVersion.Group {
Expand All @@ -109,10 +113,12 @@ func GetPoliciesInPlacementBinding(

switch subject.Kind {
case policiesv1.Kind:
result = append(result, reconcile.Request{NamespacedName: types.NamespacedName{
req := reconcile.Request{NamespacedName: types.NamespacedName{
Name: subject.Name,
Namespace: pb.GetNamespace(),
}})
}}

table[req] = true
case policiesv1.PolicySetKind:
setNN := types.NamespacedName{
Name: subject.Name,
Expand All @@ -125,14 +131,22 @@ func GetPoliciesInPlacementBinding(
}

for _, plc := range policySet.Spec.Policies {
result = append(result, reconcile.Request{NamespacedName: types.NamespacedName{
req := reconcile.Request{NamespacedName: types.NamespacedName{
Name: string(plc),
Namespace: pb.GetNamespace(),
}})
}}

table[req] = true
}
}
}

result := make([]reconcile.Request, 0, len(table))

for k := range table {
result = append(result, k)
}

return result
}

Expand Down Expand Up @@ -162,7 +176,9 @@ func HasValidPlacementRef(pb *policiesv1.PlacementBinding) bool {

// GetDecisions returns the placement decisions from the Placement or PlacementRule referred to by
// the PlacementBinding
func GetDecisions(c client.Client, pb *policiesv1.PlacementBinding) ([]appsv1.PlacementDecision, error) {
func GetDecisions(
ctx context.Context, c client.Client, pb *policiesv1.PlacementBinding,
) ([]appsv1.PlacementDecision, error) {
if !HasValidPlacementRef(pb) {
return nil, fmt.Errorf("placement binding %s/%s reference is not valid", pb.Name, pb.Namespace)
}
Expand All @@ -176,7 +192,7 @@ func GetDecisions(c client.Client, pb *policiesv1.PlacementBinding) ([]appsv1.Pl
case "Placement":
pl := &clusterv1beta1.Placement{}

err := c.Get(context.TODO(), refNN, pl)
err := c.Get(ctx, refNN, pl)
if err != nil && !k8serrors.IsNotFound(err) {
return nil, fmt.Errorf("failed to get Placement '%v': %w", pb.PlacementRef.Name, err)
}
Expand All @@ -191,7 +207,7 @@ func GetDecisions(c client.Client, pb *policiesv1.PlacementBinding) ([]appsv1.Pl
opts := client.MatchingLabels{"cluster.open-cluster-management.io/placement": pl.GetName()}
opts.ApplyToList(lopts)

err = c.List(context.TODO(), list, lopts)
err = c.List(ctx, list, lopts)
if err != nil && !k8serrors.IsNotFound(err) {
return nil, fmt.Errorf("failed to list the PlacementDecisions for '%v', %w", pb.PlacementRef.Name, err)
}
Expand All @@ -210,7 +226,7 @@ func GetDecisions(c client.Client, pb *policiesv1.PlacementBinding) ([]appsv1.Pl
return decisions, nil
case "PlacementRule":
plr := &appsv1.PlacementRule{}
if err := c.Get(context.TODO(), refNN, plr); err != nil && !k8serrors.IsNotFound(err) {
if err := c.Get(ctx, refNN, plr); err != nil && !k8serrors.IsNotFound(err) {
return nil, fmt.Errorf("failed to get PlacementRule '%v': %w", pb.PlacementRef.Name, err)
}

Expand Down Expand Up @@ -245,6 +261,33 @@ func FullNameForPolicy(plc *policiesv1.Policy) string {
return plc.GetNamespace() + "." + plc.GetName()
}

// GetRepPoliciesInPlacementBinding returns a list of the replicated policies that are either direct subjects of
// the given PlacementBinding, or are in PolicySets that are subjects of the PlacementBinding.
// The list items are guaranteed to be unique (for example if a policy is in multiple sets).
func GetRepPoliciesInPlacementBinding(
ctx context.Context, c client.Client, pb *policiesv1.PlacementBinding,
) []reconcile.Request {
decisions, err := GetDecisions(ctx, c, pb)
if err != nil {
return []reconcile.Request{}
}
// Use this for removing duplicated policies
rootPolicyRequest := GetPoliciesInPlacementBinding(ctx, c, pb)

result := make([]reconcile.Request, 0, len(rootPolicyRequest)*len(decisions))

for _, rp := range rootPolicyRequest {
for _, pd := range decisions {
result = append(result, reconcile.Request{NamespacedName: types.NamespacedName{
Name: rp.Namespace + "." + rp.Name,
Namespace: pd.ClusterName,
}})
}
}

return result
}

// TypeConverter is a helper function to converter type struct a to b
func TypeConverter(a, b interface{}) error {
js, err := json.Marshal(a)
Expand All @@ -254,3 +297,68 @@ func TypeConverter(a, b interface{}) error {

return json.Unmarshal(js, b)
}

// Select objects that are deleted or created
func GetAffectedObjs[T comparable](oldObjs []T, newObjs []T) []T {
table := make(map[T]int)

for _, oldObj := range oldObjs {
table[oldObj] = 1
}

for _, newObj := range newObjs {
table[newObj]++
}

result := []T{}

for key, val := range table {
if val == 1 {
result = append(result, key)
}
}

return result
}

type PlacementRefKinds string

const (
Placement PlacementRefKinds = "Placement"
PlacementRule PlacementRefKinds = "PlacementRule"
)

// GetRootPolicyRequests find and filter placementbindings which have namespace and placementRef.name.
// Gather all root policies under placementbindings
func GetRootPolicyRequests(ctx context.Context, c client.Client,
namespace, placementRefName string, refKind PlacementRefKinds,
) ([]reconcile.Request, error) {
kindGroupMap := map[PlacementRefKinds]string{
Placement: clusterv1beta1.SchemeGroupVersion.Group,
PlacementRule: appsv1.SchemeGroupVersion.Group,
}

pbList := &policiesv1.PlacementBindingList{}
// Find pb in the same namespace of placementrule
lopts := &client.ListOptions{Namespace: namespace}
opts := client.MatchingFields{"placementRef.name": placementRefName}
opts.ApplyToList(lopts)

err := c.List(ctx, pbList, lopts)
if err != nil {
return nil, err
}
var rootPolicyResults []reconcile.Request

for i, pb := range pbList.Items {
if pb.PlacementRef.APIGroup != kindGroupMap[refKind] ||
pb.PlacementRef.Kind != string(refKind) || pb.PlacementRef.Name != placementRefName {
continue
}
// GetPoliciesInPlacementBinding only pick root-policy name
rootPolicyResults = append(rootPolicyResults,
GetPoliciesInPlacementBinding(ctx, c, &pbList.Items[i])...)
}

return rootPolicyResults, nil
}
Loading

0 comments on commit 67595c3

Please sign in to comment.