Merge pull request #2562 from XiShanYongYe-Chang/allow-resourceselector-edit
allow to update the resourceSelector of PropagationPolicy/ClusterPropagationPolicy
This commit is contained in:
commit
6aa0336c9f
|
@ -734,7 +734,13 @@ func (d *ResourceDetector) OnPropagationPolicyAdd(obj interface{}) {
|
||||||
|
|
||||||
// OnPropagationPolicyUpdate handles object update event and push the object to queue.
|
// OnPropagationPolicyUpdate handles object update event and push the object to queue.
|
||||||
func (d *ResourceDetector) OnPropagationPolicyUpdate(oldObj, newObj interface{}) {
|
func (d *ResourceDetector) OnPropagationPolicyUpdate(oldObj, newObj interface{}) {
|
||||||
// currently do nothing, since a policy's resource selector can not be updated.
|
key, err := ClusterWideKeyFunc(newObj)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.V(2).Infof("Update PropagationPolicy(%s) resourceSelectors", key)
|
||||||
|
d.policyReconcileWorker.Add(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPropagationPolicyDelete handles object delete event and push the object to queue.
|
// OnPropagationPolicyDelete handles object delete event and push the object to queue.
|
||||||
|
@ -776,7 +782,7 @@ func (d *ResourceDetector) ReconcilePropagationPolicy(key util.QueueKey) error {
|
||||||
klog.Errorf("Failed to convert PropagationPolicy(%s) from unstructured object: %v", ckey.NamespaceKey(), err)
|
klog.Errorf("Failed to convert PropagationPolicy(%s) from unstructured object: %v", ckey.NamespaceKey(), err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return d.HandlePropagationPolicyCreation(propagationObject)
|
return d.HandlePropagationPolicyCreationOrUpdate(propagationObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnClusterPropagationPolicyAdd handles object add event and push the object to queue.
|
// OnClusterPropagationPolicyAdd handles object add event and push the object to queue.
|
||||||
|
@ -792,7 +798,13 @@ func (d *ResourceDetector) OnClusterPropagationPolicyAdd(obj interface{}) {
|
||||||
|
|
||||||
// OnClusterPropagationPolicyUpdate handles object update event and push the object to queue.
|
// OnClusterPropagationPolicyUpdate handles object update event and push the object to queue.
|
||||||
func (d *ResourceDetector) OnClusterPropagationPolicyUpdate(oldObj, newObj interface{}) {
|
func (d *ResourceDetector) OnClusterPropagationPolicyUpdate(oldObj, newObj interface{}) {
|
||||||
// currently do nothing, since a policy's resource selector can not be updated.
|
key, err := ClusterWideKeyFunc(newObj)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
klog.V(2).Infof("Update ClusterPropagationPolicy(%s) resourceSelectors", key)
|
||||||
|
d.clusterPolicyReconcileWorker.Add(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnClusterPropagationPolicyDelete handles object delete event and push the object to queue.
|
// OnClusterPropagationPolicyDelete handles object delete event and push the object to queue.
|
||||||
|
@ -835,7 +847,7 @@ func (d *ResourceDetector) ReconcileClusterPropagationPolicy(key util.QueueKey)
|
||||||
klog.Errorf("Failed to convert ClusterPropagationPolicy(%s) from unstructured object: %v", ckey.NamespaceKey(), err)
|
klog.Errorf("Failed to convert ClusterPropagationPolicy(%s) from unstructured object: %v", ckey.NamespaceKey(), err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return d.HandleClusterPropagationPolicyCreation(propagationObject)
|
return d.HandleClusterPropagationPolicyCreationOrUpdate(propagationObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandlePropagationPolicyDeletion handles PropagationPolicy delete event.
|
// HandlePropagationPolicyDeletion handles PropagationPolicy delete event.
|
||||||
|
@ -934,10 +946,17 @@ func (d *ResourceDetector) HandleClusterPropagationPolicyDeletion(policyName str
|
||||||
return errors.NewAggregate(errs)
|
return errors.NewAggregate(errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandlePropagationPolicyCreation handles PropagationPolicy add event.
|
// HandlePropagationPolicyCreationOrUpdate handles PropagationPolicy add and update event.
|
||||||
// When a new policy arrives, should check if object in waiting list matches the policy, if yes remove the object
|
// When a new policy arrives, should first check whether existing objects are no longer
|
||||||
|
// matched by the current policy, if yes, clean the labels on the object.
|
||||||
|
// And then check if object in waiting list matches the policy, if yes remove the object
|
||||||
// from waiting list and throw the object to it's reconcile queue. If not, do nothing.
|
// from waiting list and throw the object to it's reconcile queue. If not, do nothing.
|
||||||
func (d *ResourceDetector) HandlePropagationPolicyCreation(policy *policyv1alpha1.PropagationPolicy) error {
|
func (d *ResourceDetector) HandlePropagationPolicyCreationOrUpdate(policy *policyv1alpha1.PropagationPolicy) error {
|
||||||
|
err := d.cleanUnmatchedResourceBindings(policy.Namespace, policy.Name, policy.Spec.ResourceSelectors)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
matchedKeys := d.GetMatching(policy.Spec.ResourceSelectors)
|
matchedKeys := d.GetMatching(policy.Spec.ResourceSelectors)
|
||||||
klog.Infof("Matched %d resources by policy(%s/%s)", len(matchedKeys), policy.Namespace, policy.Name)
|
klog.Infof("Matched %d resources by policy(%s/%s)", len(matchedKeys), policy.Namespace, policy.Name)
|
||||||
|
|
||||||
|
@ -958,10 +977,22 @@ func (d *ResourceDetector) HandlePropagationPolicyCreation(policy *policyv1alpha
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleClusterPropagationPolicyCreation handles ClusterPropagationPolicy add event.
|
// HandleClusterPropagationPolicyCreationOrUpdate handles ClusterPropagationPolicy add and update event.
|
||||||
// When a new policy arrives, should check if object in waiting list matches the policy, if yes remove the object
|
// When a new policy arrives, should first check whether existing objects are no longer
|
||||||
|
// matched by the current policy, if yes, clean the labels on the object.
|
||||||
|
// And then check if object in waiting list matches the policy, if yes remove the object
|
||||||
// from waiting list and throw the object to it's reconcile queue. If not, do nothing.
|
// from waiting list and throw the object to it's reconcile queue. If not, do nothing.
|
||||||
func (d *ResourceDetector) HandleClusterPropagationPolicyCreation(policy *policyv1alpha1.ClusterPropagationPolicy) error {
|
func (d *ResourceDetector) HandleClusterPropagationPolicyCreationOrUpdate(policy *policyv1alpha1.ClusterPropagationPolicy) error {
|
||||||
|
err := d.cleanUnmatchedResourceBindings("", policy.Name, policy.Spec.ResourceSelectors)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.cleanUnmatchedClusterResourceBinding(policy.Name, policy.Spec.ResourceSelectors)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
matchedKeys := d.GetMatching(policy.Spec.ResourceSelectors)
|
matchedKeys := d.GetMatching(policy.Spec.ResourceSelectors)
|
||||||
klog.Infof("Matched %d resources by policy(%s)", len(matchedKeys), policy.Name)
|
klog.Infof("Matched %d resources by policy(%s)", len(matchedKeys), policy.Name)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
package detector
|
package detector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/util/errors"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
||||||
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
||||||
|
@ -118,3 +123,122 @@ func (d *ResourceDetector) getAndApplyClusterPolicy(object *unstructured.Unstruc
|
||||||
|
|
||||||
return d.ApplyClusterPolicy(object, objectKey, matchedClusterPropagationPolicy)
|
return d.ApplyClusterPolicy(object, objectKey, matchedClusterPropagationPolicy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *ResourceDetector) cleanUnmatchedResourceBindings(policyNamespace, policyName string, selectors []policyv1alpha1.ResourceSelector) error {
|
||||||
|
var ls labels.Set
|
||||||
|
var removeLabels []string
|
||||||
|
if len(policyNamespace) == 0 {
|
||||||
|
ls = labels.Set{policyv1alpha1.ClusterPropagationPolicyLabel: policyName}
|
||||||
|
removeLabels = []string{policyv1alpha1.ClusterPropagationPolicyLabel}
|
||||||
|
} else {
|
||||||
|
ls = labels.Set{
|
||||||
|
policyv1alpha1.PropagationPolicyNamespaceLabel: policyNamespace,
|
||||||
|
policyv1alpha1.PropagationPolicyNameLabel: policyName,
|
||||||
|
}
|
||||||
|
removeLabels = []string{
|
||||||
|
policyv1alpha1.PropagationPolicyNamespaceLabel,
|
||||||
|
policyv1alpha1.PropagationPolicyNameLabel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bindings := &workv1alpha2.ResourceBindingList{}
|
||||||
|
listOpt := &client.ListOptions{LabelSelector: labels.SelectorFromSet(ls)}
|
||||||
|
err := d.Client.List(context.TODO(), bindings, listOpt)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to list ResourceBinding with policy(%s/%s), error: %v", policyNamespace, policyName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
for _, binding := range bindings.Items {
|
||||||
|
removed, err := d.removeResourceLabelsIfNotMatch(binding.Spec.Resource, selectors, removeLabels...)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to remove resource labels when resource not match with policy selectors, err: %v", err)
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !removed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bindingCopy := binding.DeepCopy()
|
||||||
|
for _, l := range removeLabels {
|
||||||
|
delete(bindingCopy.Labels, l)
|
||||||
|
}
|
||||||
|
err = d.Client.Update(context.TODO(), bindingCopy)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to update resourceBinding(%s/%s), err: %v", binding.Namespace, binding.Name, err)
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.NewAggregate(errs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ResourceDetector) cleanUnmatchedClusterResourceBinding(policyName string, selectors []policyv1alpha1.ResourceSelector) error {
|
||||||
|
bindings := &workv1alpha2.ClusterResourceBindingList{}
|
||||||
|
listOpt := &client.ListOptions{
|
||||||
|
LabelSelector: labels.SelectorFromSet(labels.Set{
|
||||||
|
policyv1alpha1.ClusterPropagationPolicyLabel: policyName,
|
||||||
|
})}
|
||||||
|
err := d.Client.List(context.TODO(), bindings, listOpt)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to list ClusterResourceBinding with policy(%s), error: %v", policyName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
for _, binding := range bindings.Items {
|
||||||
|
removed, err := d.removeResourceLabelsIfNotMatch(binding.Spec.Resource, selectors, []string{policyv1alpha1.ClusterPropagationPolicyLabel}...)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to remove resource labels when resource not match with policy selectors, err: %v", err)
|
||||||
|
errs = append(errs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !removed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bindingCopy := binding.DeepCopy()
|
||||||
|
delete(bindingCopy.Labels, policyv1alpha1.ClusterPropagationPolicyLabel)
|
||||||
|
err = d.Client.Update(context.TODO(), bindingCopy)
|
||||||
|
if err != nil {
|
||||||
|
klog.Errorf("Failed to update clusterResourceBinding(%s), err: %v", binding.Name, err)
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errors.NewAggregate(errs)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *ResourceDetector) removeResourceLabelsIfNotMatch(objectReference workv1alpha2.ObjectReference, selectors []policyv1alpha1.ResourceSelector, labelKeys ...string) (bool, error) {
|
||||||
|
objectKey, err := helper.ConstructClusterWideKey(objectReference)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
object, err := d.GetUnstructuredObject(objectKey)
|
||||||
|
if err != nil {
|
||||||
|
if apierrors.IsNotFound(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if util.ResourceMatchSelectors(object, selectors...) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, labelKey := range labelKeys {
|
||||||
|
util.RemoveLabel(object, labelKey)
|
||||||
|
}
|
||||||
|
err = d.Client.Update(context.TODO(), object)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
"github.com/karmada-io/karmada/pkg/util"
|
||||||
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
||||||
|
"github.com/karmada-io/karmada/pkg/util/fedinformer/keys"
|
||||||
"github.com/karmada-io/karmada/pkg/util/names"
|
"github.com/karmada-io/karmada/pkg/util/names"
|
||||||
"github.com/karmada-io/karmada/pkg/util/restmapper"
|
"github.com/karmada-io/karmada/pkg/util/restmapper"
|
||||||
)
|
)
|
||||||
|
@ -246,3 +247,18 @@ func GenerateReplicaRequirements(podTemplate *corev1.PodTemplateSpec) *workv1alp
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConstructClusterWideKey construct resource ClusterWideKey from binding's objectReference.
|
||||||
|
func ConstructClusterWideKey(resource workv1alpha2.ObjectReference) (keys.ClusterWideKey, error) {
|
||||||
|
gv, err := schema.ParseGroupVersion(resource.APIVersion)
|
||||||
|
if err != nil {
|
||||||
|
return keys.ClusterWideKey{}, err
|
||||||
|
}
|
||||||
|
return keys.ClusterWideKey{
|
||||||
|
Group: gv.Group,
|
||||||
|
Version: gv.Version,
|
||||||
|
Kind: resource.Kind,
|
||||||
|
Namespace: resource.Namespace,
|
||||||
|
Name: resource.Name,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
|
workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1"
|
||||||
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
||||||
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
||||||
|
"github.com/karmada-io/karmada/pkg/util/fedinformer/keys"
|
||||||
"github.com/karmada-io/karmada/pkg/util/gclient"
|
"github.com/karmada-io/karmada/pkg/util/gclient"
|
||||||
"github.com/karmada-io/karmada/pkg/util/names"
|
"github.com/karmada-io/karmada/pkg/util/names"
|
||||||
)
|
)
|
||||||
|
@ -899,3 +900,72 @@ func TestGenerateReplicaRequirements(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConstructClusterWideKey(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
resource workv1alpha2.ObjectReference
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want keys.ClusterWideKey
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "wrong APIVersion",
|
||||||
|
args: args{resource: workv1alpha2.ObjectReference{
|
||||||
|
APIVersion: "a/b/c",
|
||||||
|
Kind: "Foo",
|
||||||
|
Namespace: "test",
|
||||||
|
Name: "foo",
|
||||||
|
}},
|
||||||
|
want: keys.ClusterWideKey{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "APIVersion: v1",
|
||||||
|
args: args{resource: workv1alpha2.ObjectReference{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "Foo",
|
||||||
|
Namespace: "test",
|
||||||
|
Name: "foo",
|
||||||
|
}},
|
||||||
|
want: keys.ClusterWideKey{
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Foo",
|
||||||
|
Namespace: "test",
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "APIVersion: test/v1",
|
||||||
|
args: args{resource: workv1alpha2.ObjectReference{
|
||||||
|
APIVersion: "test/v1",
|
||||||
|
Kind: "Foo",
|
||||||
|
Namespace: "test",
|
||||||
|
Name: "foo",
|
||||||
|
}},
|
||||||
|
want: keys.ClusterWideKey{
|
||||||
|
Group: "test",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Foo",
|
||||||
|
Namespace: "test",
|
||||||
|
Name: "foo",
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := ConstructClusterWideKey(tt.args.resource)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("ConstructClusterWideKey() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("ConstructClusterWideKey() got = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,9 +15,6 @@ import (
|
||||||
"github.com/karmada-io/karmada/pkg/util/names"
|
"github.com/karmada-io/karmada/pkg/util/names"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DenyReasonResourceSelectorsModify constructs a reason indicating that modify ResourceSelectors is not allowed.
|
|
||||||
const DenyReasonResourceSelectorsModify = "modify ResourceSelectors is forbidden"
|
|
||||||
|
|
||||||
// SetDefaultSpreadConstraints set default spread constraints if both 'SpreadByField' and 'SpreadByLabel' not set.
|
// SetDefaultSpreadConstraints set default spread constraints if both 'SpreadByField' and 'SpreadByLabel' not set.
|
||||||
func SetDefaultSpreadConstraints(spreadConstraints []policyv1alpha1.SpreadConstraint) {
|
func SetDefaultSpreadConstraints(spreadConstraints []policyv1alpha1.SpreadConstraint) {
|
||||||
for i := range spreadConstraints {
|
for i := range spreadConstraints {
|
||||||
|
|
|
@ -15,12 +15,19 @@ func GetLabelValue(labels map[string]string, labelKey string) string {
|
||||||
|
|
||||||
// MergeLabel adds label for the given object.
|
// MergeLabel adds label for the given object.
|
||||||
func MergeLabel(obj *unstructured.Unstructured, labelKey string, labelValue string) {
|
func MergeLabel(obj *unstructured.Unstructured, labelKey string, labelValue string) {
|
||||||
workloadLabel := obj.GetLabels()
|
labels := obj.GetLabels()
|
||||||
if workloadLabel == nil {
|
if labels == nil {
|
||||||
workloadLabel = make(map[string]string, 1)
|
labels = make(map[string]string, 1)
|
||||||
}
|
}
|
||||||
workloadLabel[labelKey] = labelValue
|
labels[labelKey] = labelValue
|
||||||
obj.SetLabels(workloadLabel)
|
obj.SetLabels(labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveLabel removes the label from the given object.
|
||||||
|
func RemoveLabel(obj *unstructured.Unstructured, labelKey string) {
|
||||||
|
labels := obj.GetLabels()
|
||||||
|
delete(labels, labelKey)
|
||||||
|
obj.SetLabels(labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DedupeAndMergeLabels merges the new labels into exist labels.
|
// DedupeAndMergeLabels merges the new labels into exist labels.
|
||||||
|
|
|
@ -208,3 +208,106 @@ func TestDedupeAndMergeLabels(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRemoveLabel(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
obj *unstructured.Unstructured
|
||||||
|
labelKey string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
expected *unstructured.Unstructured
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil object labels",
|
||||||
|
args: args{
|
||||||
|
obj: &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "demo-deployment",
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"replicas": 2,
|
||||||
|
}}},
|
||||||
|
labelKey: "foo",
|
||||||
|
},
|
||||||
|
expected: &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "demo-deployment",
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"replicas": 2,
|
||||||
|
}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "same labelKey",
|
||||||
|
args: args{
|
||||||
|
obj: &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "demo-deployment",
|
||||||
|
"labels": map[string]interface{}{"foo": "bar"},
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"replicas": 2,
|
||||||
|
}}},
|
||||||
|
labelKey: "foo",
|
||||||
|
},
|
||||||
|
expected: &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "demo-deployment",
|
||||||
|
"labels": map[string]interface{}{},
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"replicas": 2,
|
||||||
|
}}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "different labelKey",
|
||||||
|
args: args{
|
||||||
|
obj: &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "demo-deployment",
|
||||||
|
"labels": map[string]interface{}{"foo": "bar"},
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"replicas": 2,
|
||||||
|
}}},
|
||||||
|
labelKey: "foo1",
|
||||||
|
},
|
||||||
|
expected: &unstructured.Unstructured{
|
||||||
|
Object: map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "demo-deployment",
|
||||||
|
"labels": map[string]interface{}{"foo": "bar"},
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"replicas": 2,
|
||||||
|
}}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
RemoveLabel(tt.args.obj, tt.args.labelKey)
|
||||||
|
if !reflect.DeepEqual(tt.args.obj, tt.expected) {
|
||||||
|
t.Errorf("RemoveLabel() = %v, want %v", tt.args.obj, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
admissionv1 "k8s.io/api/admission/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||||
|
|
||||||
|
@ -34,18 +32,6 @@ func (v *ValidatingAdmission) Handle(ctx context.Context, req admission.Request)
|
||||||
}
|
}
|
||||||
klog.V(2).Infof("Validating ClusterPropagationPolicy(%s) for request: %s", policy.Name, req.Operation)
|
klog.V(2).Infof("Validating ClusterPropagationPolicy(%s) for request: %s", policy.Name, req.Operation)
|
||||||
|
|
||||||
if req.Operation == admissionv1.Update {
|
|
||||||
oldPolicy := &policyv1alpha1.ClusterPropagationPolicy{}
|
|
||||||
err := v.decoder.DecodeRaw(req.OldObject, oldPolicy)
|
|
||||||
if err != nil {
|
|
||||||
return admission.Errored(http.StatusBadRequest, err)
|
|
||||||
}
|
|
||||||
if !equality.Semantic.DeepEqual(policy.Spec.ResourceSelectors, oldPolicy.Spec.ResourceSelectors) {
|
|
||||||
klog.Error(helper.DenyReasonResourceSelectorsModify)
|
|
||||||
return admission.Denied(helper.DenyReasonResourceSelectorsModify)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := helper.ValidateSpreadConstraint(policy.Spec.Placement.SpreadConstraints); err != nil {
|
if err := helper.ValidateSpreadConstraint(policy.Spec.Placement.SpreadConstraints); err != nil {
|
||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
return admission.Denied(err.Error())
|
return admission.Denied(err.Error())
|
||||||
|
|
|
@ -4,8 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
admissionv1 "k8s.io/api/admission/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/equality"
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||||
|
|
||||||
|
@ -34,18 +32,6 @@ func (v *ValidatingAdmission) Handle(ctx context.Context, req admission.Request)
|
||||||
}
|
}
|
||||||
klog.V(2).Infof("Validating PropagationPolicy(%s/%s) for request: %s", policy.Namespace, policy.Name, req.Operation)
|
klog.V(2).Infof("Validating PropagationPolicy(%s/%s) for request: %s", policy.Namespace, policy.Name, req.Operation)
|
||||||
|
|
||||||
if req.Operation == admissionv1.Update {
|
|
||||||
oldPolicy := &policyv1alpha1.PropagationPolicy{}
|
|
||||||
err := v.decoder.DecodeRaw(req.OldObject, oldPolicy)
|
|
||||||
if err != nil {
|
|
||||||
return admission.Errored(http.StatusBadRequest, err)
|
|
||||||
}
|
|
||||||
if !equality.Semantic.DeepEqual(policy.Spec.ResourceSelectors, oldPolicy.Spec.ResourceSelectors) {
|
|
||||||
klog.Info(helper.DenyReasonResourceSelectorsModify)
|
|
||||||
return admission.Denied(helper.DenyReasonResourceSelectorsModify)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := helper.ValidateSpreadConstraint(policy.Spec.Placement.SpreadConstraints); err != nil {
|
if err := helper.ValidateSpreadConstraint(policy.Spec.Placement.SpreadConstraints); err != nil {
|
||||||
klog.Error(err)
|
klog.Error(err)
|
||||||
return admission.Denied(err.Error())
|
return admission.Denied(err.Error())
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/rand"
|
"k8s.io/apimachinery/pkg/util/rand"
|
||||||
|
@ -13,7 +14,7 @@ import (
|
||||||
testhelper "github.com/karmada-io/karmada/test/helper"
|
testhelper "github.com/karmada-io/karmada/test/helper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = ginkgo.Describe("[BasicClusterPropagation] basic cluster propagation testing", func() {
|
var _ = ginkgo.Describe("[BasicClusterPropagation] propagation testing", func() {
|
||||||
ginkgo.Context("CustomResourceDefinition propagation testing", func() {
|
ginkgo.Context("CustomResourceDefinition propagation testing", func() {
|
||||||
var crdGroup string
|
var crdGroup string
|
||||||
var randStr string
|
var randStr string
|
||||||
|
@ -149,3 +150,171 @@ var _ = ginkgo.Describe("[BasicClusterPropagation] basic cluster propagation tes
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var _ = ginkgo.Describe("[AdvancedClusterPropagation] propagation testing", func() {
|
||||||
|
ginkgo.Context("Edit ClusterPropagationPolicy ResourceSelectors", func() {
|
||||||
|
ginkgo.When("propagate namespace scope resource", func() {
|
||||||
|
var policy *policyv1alpha1.ClusterPropagationPolicy
|
||||||
|
var deployment01, deployment02 *appsv1.Deployment
|
||||||
|
var targetMember string
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
targetMember = framework.ClusterNames()[0]
|
||||||
|
policyName := deploymentNamePrefix + rand.String(RandomStrLength)
|
||||||
|
|
||||||
|
deployment01 = testhelper.NewDeployment(testNamespace, policyName+"01")
|
||||||
|
deployment02 = testhelper.NewDeployment(testNamespace, policyName+"02")
|
||||||
|
|
||||||
|
policy = testhelper.NewClusterPropagationPolicy(policyName, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: deployment01.APIVersion,
|
||||||
|
Kind: deployment01.Kind,
|
||||||
|
Name: deployment01.Name,
|
||||||
|
}}, policyv1alpha1.Placement{
|
||||||
|
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
||||||
|
ClusterNames: []string{targetMember},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
framework.CreateClusterPropagationPolicy(karmadaClient, policy)
|
||||||
|
framework.CreateDeployment(kubeClient, deployment01)
|
||||||
|
framework.CreateDeployment(kubeClient, deployment02)
|
||||||
|
ginkgo.DeferCleanup(func() {
|
||||||
|
framework.RemoveClusterPropagationPolicy(karmadaClient, policy.Name)
|
||||||
|
framework.RemoveDeployment(kubeClient, deployment01.Namespace, deployment01.Name)
|
||||||
|
framework.RemoveDeployment(kubeClient, deployment02.Namespace, deployment02.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
framework.WaitDeploymentPresentOnClusterFitWith(targetMember, deployment01.Namespace, deployment01.Name,
|
||||||
|
func(deployment *appsv1.Deployment) bool { return true })
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("add resourceSelectors item", func() {
|
||||||
|
framework.UpdateClusterPropagationPolicy(karmadaClient, policy.Name, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: deployment01.APIVersion,
|
||||||
|
Kind: deployment01.Kind,
|
||||||
|
Name: deployment01.Name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
APIVersion: deployment02.APIVersion,
|
||||||
|
Kind: deployment02.Kind,
|
||||||
|
Name: deployment02.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
framework.WaitDeploymentPresentOnClusterFitWith(targetMember, deployment02.Namespace, deployment02.Name,
|
||||||
|
func(deployment *appsv1.Deployment) bool { return true })
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("update resourceSelectors item", func() {
|
||||||
|
framework.UpdateClusterPropagationPolicy(karmadaClient, policy.Name, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: deployment02.APIVersion,
|
||||||
|
Kind: deployment02.Kind,
|
||||||
|
Name: deployment02.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
framework.WaitDeploymentPresentOnClusterFitWith(targetMember, deployment02.Namespace, deployment02.Name,
|
||||||
|
func(deployment *appsv1.Deployment) bool { return true })
|
||||||
|
framework.WaitDeploymentGetByClientFitWith(kubeClient, deployment01.Namespace, deployment01.Name,
|
||||||
|
func(deployment *appsv1.Deployment) bool {
|
||||||
|
if deployment.Labels == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_, exist := deployment.Labels[policyv1alpha1.ClusterPropagationPolicyLabel]
|
||||||
|
return !exist
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.When("propagate cluster scope resource", func() {
|
||||||
|
var policy *policyv1alpha1.ClusterPropagationPolicy
|
||||||
|
var clusterRole01, clusterRole02 *rbacv1.ClusterRole
|
||||||
|
var targetMember string
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
policyName := clusterRoleNamePrefix + rand.String(RandomStrLength)
|
||||||
|
targetMember = framework.ClusterNames()[0]
|
||||||
|
|
||||||
|
clusterRole01 = testhelper.NewClusterRole(fmt.Sprintf("system:test-%s-01", policyName), nil)
|
||||||
|
clusterRole02 = testhelper.NewClusterRole(fmt.Sprintf("system:test-%s-02", policyName), nil)
|
||||||
|
policy = testhelper.NewClusterPropagationPolicy(policyName, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: clusterRole01.APIVersion,
|
||||||
|
Kind: clusterRole01.Kind,
|
||||||
|
Name: clusterRole01.Name,
|
||||||
|
},
|
||||||
|
}, policyv1alpha1.Placement{
|
||||||
|
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
||||||
|
ClusterNames: []string{targetMember},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
framework.CreateClusterPropagationPolicy(karmadaClient, policy)
|
||||||
|
framework.CreateClusterRole(kubeClient, clusterRole01)
|
||||||
|
framework.CreateClusterRole(kubeClient, clusterRole02)
|
||||||
|
ginkgo.DeferCleanup(func() {
|
||||||
|
framework.RemoveClusterPropagationPolicy(karmadaClient, policy.Name)
|
||||||
|
framework.RemoveClusterRole(kubeClient, clusterRole01.Name)
|
||||||
|
framework.RemoveClusterRole(kubeClient, clusterRole02.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
framework.WaitClusterRolePresentOnClusterFitWith(targetMember, clusterRole01.Name,
|
||||||
|
func(role *rbacv1.ClusterRole) bool {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("add resourceSelectors item", func() {
|
||||||
|
framework.UpdateClusterPropagationPolicy(karmadaClient, policy.Name, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: clusterRole01.APIVersion,
|
||||||
|
Kind: clusterRole01.Kind,
|
||||||
|
Name: clusterRole01.Name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
APIVersion: clusterRole02.APIVersion,
|
||||||
|
Kind: clusterRole02.Kind,
|
||||||
|
Name: clusterRole02.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
framework.WaitClusterRolePresentOnClusterFitWith(targetMember, clusterRole02.Name,
|
||||||
|
func(role *rbacv1.ClusterRole) bool {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("update resourceSelectors item", func() {
|
||||||
|
framework.UpdateClusterPropagationPolicy(karmadaClient, policy.Name, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: clusterRole02.APIVersion,
|
||||||
|
Kind: clusterRole02.Kind,
|
||||||
|
Name: clusterRole02.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
framework.WaitClusterRolePresentOnClusterFitWith(targetMember, clusterRole02.Name,
|
||||||
|
func(role *rbacv1.ClusterRole) bool {
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
framework.WaitClusterRoleGetByClientFitWith(kubeClient, clusterRole01.Name,
|
||||||
|
func(clusterRole *rbacv1.ClusterRole) bool {
|
||||||
|
if clusterRole.Labels == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_, exist := clusterRole.Labels[policyv1alpha1.ClusterPropagationPolicyLabel]
|
||||||
|
return !exist
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -27,3 +27,15 @@ func RemoveClusterPropagationPolicy(client karmada.Interface, name string) {
|
||||||
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateClusterPropagationPolicy update ClusterPropagationPolicy resourceSelectors with karmada client.
|
||||||
|
func UpdateClusterPropagationPolicy(client karmada.Interface, name string, resourceSelectors []policyv1alpha1.ResourceSelector) {
|
||||||
|
ginkgo.By(fmt.Sprintf("Updating ClusterPropagationPolicy(%s)", name), func() {
|
||||||
|
newPolicy, err := client.PolicyV1alpha1().ClusterPropagationPolicies().Get(context.TODO(), name, metav1.GetOptions{})
|
||||||
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
||||||
|
|
||||||
|
newPolicy.Spec.ResourceSelectors = resourceSelectors
|
||||||
|
_, err = client.PolicyV1alpha1().ClusterPropagationPolicies().Update(context.TODO(), newPolicy, metav1.UpdateOptions{})
|
||||||
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -166,3 +166,16 @@ func CheckDeploymentReadyStatus(deployment *appsv1.Deployment, wantedReplicas in
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WaitDeploymentGetByClientFitWith wait deployment get by client fit with func.
|
||||||
|
func WaitDeploymentGetByClientFitWith(client kubernetes.Interface, namespace, name string, fit func(deployment *appsv1.Deployment) bool) {
|
||||||
|
ginkgo.By(fmt.Sprintf("Check deployment(%s/%s) labels fit with function", namespace, name), func() {
|
||||||
|
gomega.Eventually(func() bool {
|
||||||
|
dep, err := client.AppsV1().Deployments(namespace).Get(context.TODO(), name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fit(dep)
|
||||||
|
}, pollTimeout, pollInterval).Should(gomega.Equal(true))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -40,3 +40,15 @@ func PatchPropagationPolicy(client karmada.Interface, namespace, name string, pa
|
||||||
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdatePropagationPolicy update PropagationPolicy resourceSelectors with karmada client.
|
||||||
|
func UpdatePropagationPolicy(client karmada.Interface, namespace, name string, resourceSelectors []policyv1alpha1.ResourceSelector) {
|
||||||
|
ginkgo.By(fmt.Sprintf("Updating PropagationPolicy(%s/%s)", namespace, name), func() {
|
||||||
|
newPolicy, err := client.PolicyV1alpha1().PropagationPolicies(namespace).Get(context.TODO(), name, metav1.GetOptions{})
|
||||||
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
||||||
|
|
||||||
|
newPolicy.Spec.ResourceSelectors = resourceSelectors
|
||||||
|
_, err = client.PolicyV1alpha1().PropagationPolicies(namespace).Update(context.TODO(), newPolicy, metav1.UpdateOptions{})
|
||||||
|
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -158,6 +158,19 @@ func WaitClusterRoleDisappearOnCluster(cluster, name string) {
|
||||||
}, pollTimeout, pollInterval).Should(gomega.Equal(true))
|
}, pollTimeout, pollInterval).Should(gomega.Equal(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WaitClusterRoleGetByClientFitWith wait clusterRole get by client fit with func.
|
||||||
|
func WaitClusterRoleGetByClientFitWith(client kubernetes.Interface, name string, fit func(clusterRole *rbacv1.ClusterRole) bool) {
|
||||||
|
ginkgo.By(fmt.Sprintf("Check clusterRole(%s) labels fit with function", name), func() {
|
||||||
|
gomega.Eventually(func() bool {
|
||||||
|
clusterRole, err := client.RbacV1().ClusterRoles().Get(context.TODO(), name, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return fit(clusterRole)
|
||||||
|
}, pollTimeout, pollInterval).Should(gomega.Equal(true))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// CreateRoleBinding create roleBinding.
|
// CreateRoleBinding create roleBinding.
|
||||||
func CreateRoleBinding(client kubernetes.Interface, roleBinding *rbacv1.RoleBinding) {
|
func CreateRoleBinding(client kubernetes.Interface, roleBinding *rbacv1.RoleBinding) {
|
||||||
ginkgo.By(fmt.Sprintf("Creating RoleBinding(%s/%s)", roleBinding.Namespace, roleBinding.Name), func() {
|
ginkgo.By(fmt.Sprintf("Creating RoleBinding(%s/%s)", roleBinding.Namespace, roleBinding.Name), func() {
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// BasicPropagation focus on basic propagation functionality testing.
|
// BasicPropagation focus on basic propagation functionality testing.
|
||||||
var _ = ginkgo.Describe("[BasicPropagation] basic propagation testing", func() {
|
var _ = ginkgo.Describe("[BasicPropagation] propagation testing", func() {
|
||||||
ginkgo.Context("Deployment propagation testing", func() {
|
ginkgo.Context("Deployment propagation testing", func() {
|
||||||
var policyNamespace, policyName string
|
var policyNamespace, policyName string
|
||||||
var deploymentNamespace, deploymentName string
|
var deploymentNamespace, deploymentName string
|
||||||
|
@ -495,7 +495,7 @@ var _ = ginkgo.Describe("[BasicPropagation] basic propagation testing", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
// ImplicitPriority more than one PP matches the object, we should choose the most suitable one.
|
// ImplicitPriority more than one PP matches the object, we should choose the most suitable one.
|
||||||
var _ = ginkgo.Describe("[ImplicitPriority] basic propagation testing", func() {
|
var _ = ginkgo.Describe("[ImplicitPriority] propagation testing", func() {
|
||||||
ginkgo.Context("priorityMatchName propagation testing", func() {
|
ginkgo.Context("priorityMatchName propagation testing", func() {
|
||||||
var policyNamespace, priorityMatchName, priorityMatchLabelSelector, priorityMatchAll string
|
var policyNamespace, priorityMatchName, priorityMatchLabelSelector, priorityMatchAll string
|
||||||
var deploymentNamespace, deploymentName string
|
var deploymentNamespace, deploymentName string
|
||||||
|
@ -573,6 +573,7 @@ var _ = ginkgo.Describe("[ImplicitPriority] basic propagation testing", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.Context("policyMatchLabelSelector propagation testing", func() {
|
ginkgo.Context("policyMatchLabelSelector propagation testing", func() {
|
||||||
var policyNamespace, priorityMatchLabelSelector, priorityMatchAll string
|
var policyNamespace, priorityMatchLabelSelector, priorityMatchAll string
|
||||||
var deploymentNamespace, deploymentName string
|
var deploymentNamespace, deploymentName string
|
||||||
|
@ -638,6 +639,7 @@ var _ = ginkgo.Describe("[ImplicitPriority] basic propagation testing", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.Context("priorityMatchAll propagation testing", func() {
|
ginkgo.Context("priorityMatchAll propagation testing", func() {
|
||||||
var policyNamespace, priorityMatchAll string
|
var policyNamespace, priorityMatchAll string
|
||||||
var deploymentNamespace, deploymentName string
|
var deploymentNamespace, deploymentName string
|
||||||
|
@ -688,3 +690,90 @@ var _ = ginkgo.Describe("[ImplicitPriority] basic propagation testing", func() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// AdvancedPropagation focus on some advanced propagation testing.
|
||||||
|
var _ = ginkgo.Describe("[AdvancedPropagation] propagation testing", func() {
|
||||||
|
ginkgo.Context("Edit PropagationPolicy ResourceSelectors", func() {
|
||||||
|
var policy *policyv1alpha1.PropagationPolicy
|
||||||
|
var deployment01, deployment02 *appsv1.Deployment
|
||||||
|
var targetMember string
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
targetMember = framework.ClusterNames()[0]
|
||||||
|
policyNamespace := testNamespace
|
||||||
|
policyName := deploymentNamePrefix + rand.String(RandomStrLength)
|
||||||
|
|
||||||
|
deployment01 = testhelper.NewDeployment(testNamespace, policyName+"01")
|
||||||
|
deployment02 = testhelper.NewDeployment(testNamespace, policyName+"02")
|
||||||
|
|
||||||
|
policy = testhelper.NewPropagationPolicy(policyNamespace, policyName, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: deployment01.APIVersion,
|
||||||
|
Kind: deployment01.Kind,
|
||||||
|
Name: deployment01.Name,
|
||||||
|
}}, policyv1alpha1.Placement{
|
||||||
|
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
||||||
|
ClusterNames: []string{targetMember},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.BeforeEach(func() {
|
||||||
|
framework.CreatePropagationPolicy(karmadaClient, policy)
|
||||||
|
framework.CreateDeployment(kubeClient, deployment01)
|
||||||
|
framework.CreateDeployment(kubeClient, deployment02)
|
||||||
|
ginkgo.DeferCleanup(func() {
|
||||||
|
framework.RemovePropagationPolicy(karmadaClient, policy.Namespace, policy.Name)
|
||||||
|
framework.RemoveDeployment(kubeClient, deployment01.Namespace, deployment01.Name)
|
||||||
|
framework.RemoveDeployment(kubeClient, deployment02.Namespace, deployment02.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
framework.WaitDeploymentPresentOnClusterFitWith(targetMember, deployment01.Namespace, deployment01.Name,
|
||||||
|
func(deployment *appsv1.Deployment) bool { return true })
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("add resourceSelectors item", func() {
|
||||||
|
framework.UpdatePropagationPolicy(karmadaClient, policy.Namespace, policy.Name, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: deployment01.APIVersion,
|
||||||
|
Kind: deployment01.Kind,
|
||||||
|
Name: deployment01.Name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
APIVersion: deployment02.APIVersion,
|
||||||
|
Kind: deployment02.Kind,
|
||||||
|
Name: deployment02.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
framework.WaitDeploymentPresentOnClusterFitWith(targetMember, deployment02.Namespace, deployment02.Name,
|
||||||
|
func(deployment *appsv1.Deployment) bool { return true })
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("update resourceSelectors item", func() {
|
||||||
|
framework.UpdatePropagationPolicy(karmadaClient, policy.Namespace, policy.Name, []policyv1alpha1.ResourceSelector{
|
||||||
|
{
|
||||||
|
APIVersion: deployment02.APIVersion,
|
||||||
|
Kind: deployment02.Kind,
|
||||||
|
Name: deployment02.Name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
framework.WaitDeploymentPresentOnClusterFitWith(targetMember, deployment02.Namespace, deployment02.Name,
|
||||||
|
func(deployment *appsv1.Deployment) bool { return true })
|
||||||
|
framework.WaitDeploymentGetByClientFitWith(kubeClient, deployment01.Namespace, deployment01.Name,
|
||||||
|
func(deployment *appsv1.Deployment) bool {
|
||||||
|
if deployment.Labels == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_, namespaceExist := deployment.Labels[policyv1alpha1.PropagationPolicyNamespaceLabel]
|
||||||
|
_, nameExist := deployment.Labels[policyv1alpha1.PropagationPolicyNameLabel]
|
||||||
|
if namespaceExist || nameExist {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
Loading…
Reference in New Issue