368 lines
17 KiB
Go
368 lines
17 KiB
Go
/*
|
|
Copyright 2024 The Karmada Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/onsi/ginkgo/v2"
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/util/rand"
|
|
"k8s.io/utils/pointer"
|
|
|
|
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
|
|
"github.com/karmada-io/karmada/pkg/util"
|
|
"github.com/karmada-io/karmada/test/e2e/framework"
|
|
testhelper "github.com/karmada-io/karmada/test/helper"
|
|
)
|
|
|
|
const waitIntervalForLazyPolicyTest = 3 * time.Second
|
|
|
|
// e2e test for https://github.com/karmada-io/karmada/blob/release-1.9/docs/proposals/scheduling/activation-preference/lazy-activation-preference.md#test-plan
|
|
var _ = ginkgo.Describe("Lazy activation policy testing", func() {
|
|
var namespace string
|
|
var deploymentName, configMapName, policyName, policyHigherPriorityName string
|
|
var originalCluster, modifiedCluster string
|
|
var deployment *appsv1.Deployment
|
|
var configMap *corev1.ConfigMap
|
|
var policy, policyHigherPriority *policyv1alpha1.PropagationPolicy
|
|
|
|
ginkgo.BeforeEach(func() {
|
|
namespace = testNamespace
|
|
deploymentName = deploymentNamePrefix + rand.String(RandomStrLength)
|
|
configMapName = deploymentName
|
|
policyName = deploymentName
|
|
policyHigherPriorityName = deploymentName + "higherpriority"
|
|
originalCluster = framework.ClusterNames()[0]
|
|
modifiedCluster = framework.ClusterNames()[1]
|
|
|
|
deployment = testhelper.NewDeployment(namespace, deploymentName)
|
|
configMap = testhelper.NewConfigMap(namespace, configMapName, map[string]string{"test": "test"})
|
|
policy = testhelper.NewLazyPropagationPolicy(namespace, policyName, []policyv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: deployment.APIVersion,
|
|
Kind: deployment.Kind,
|
|
Name: deploymentName,
|
|
}}, policyv1alpha1.Placement{
|
|
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{originalCluster},
|
|
},
|
|
})
|
|
policyHigherPriority = testhelper.NewLazyPropagationPolicy(namespace, policyHigherPriorityName, []policyv1alpha1.ResourceSelector{
|
|
{
|
|
APIVersion: deployment.APIVersion,
|
|
Kind: deployment.Kind,
|
|
Name: deploymentName,
|
|
}}, policyv1alpha1.Placement{
|
|
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
|
ClusterNames: []string{modifiedCluster},
|
|
},
|
|
})
|
|
policyHigherPriority.Spec.Priority = pointer.Int32(2)
|
|
policyHigherPriority.Spec.Preemption = policyv1alpha1.PreemptAlways
|
|
})
|
|
|
|
ginkgo.Context("1. Policy created before resource", func() {
|
|
ginkgo.JustBeforeEach(func() {
|
|
framework.CreatePropagationPolicy(karmadaClient, policy)
|
|
waitPropagatePolicyReconciled(namespace, policyName)
|
|
framework.CreateDeployment(kubeClient, deployment)
|
|
|
|
ginkgo.DeferCleanup(func() {
|
|
framework.RemovePropagationPolicyIfExist(karmadaClient, namespace, policyName)
|
|
framework.RemoveDeployment(kubeClient, namespace, deploymentName)
|
|
framework.WaitDeploymentDisappearOnCluster(originalCluster, namespace, deploymentName)
|
|
framework.WaitDeploymentDisappearOnCluster(modifiedCluster, namespace, deploymentName)
|
|
})
|
|
})
|
|
|
|
// Simple Case 1 (Policy created before resource)
|
|
// refer: https://github.com/karmada-io/karmada/blob/release-1.9/docs/proposals/scheduling/activation-preference/lazy-activation-preference.md#simple-case-1-policy-created-before-resource
|
|
ginkgo.It("Simple Case 1 (Policy created before resource)", func() {
|
|
ginkgo.By("step 1: deployment propagate success when policy created before it", func() {
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
|
|
ginkgo.By("step 2: after policy deleted, deployment still keep previous propagation states", func() {
|
|
framework.RemovePropagationPolicy(karmadaClient, namespace, policyName)
|
|
// wait to distinguish whether the state will not change or have no time to change
|
|
time.Sleep(waitIntervalForLazyPolicyTest)
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
})
|
|
|
|
// Simple Case 3 (Lazy to immediate)
|
|
// refer: https://github.com/karmada-io/karmada/blob/release-1.9/docs/proposals/scheduling/activation-preference/lazy-activation-preference.md#simple-case-3-lazy-to-immediate
|
|
ginkgo.It("Simple Case 3 (Lazy to immediate)", func() {
|
|
ginkgo.By("step 1: deployment propagate success when policy created before it", func() {
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
|
|
ginkgo.By("step 2: after policy updated (cluster=member2, remove lazy activationPreference field), the propagation of deployment changed", func() {
|
|
// 1. remove lazy activationPreference field
|
|
policy.Spec.ActivationPreference = ""
|
|
// 2. update policy placement with clusterAffinities
|
|
changePlacementTargetCluster(policy, modifiedCluster)
|
|
// 3. the propagation of deployment changed
|
|
framework.WaitDeploymentDisappearOnCluster(originalCluster, namespace, deploymentName)
|
|
waitDeploymentPresentOnCluster(modifiedCluster, namespace, deploymentName)
|
|
})
|
|
})
|
|
|
|
ginkgo.Context("Immediate to lazy", func() {
|
|
ginkgo.BeforeEach(func() {
|
|
// remove lazy activationPreference field
|
|
policy.Spec.ActivationPreference = ""
|
|
})
|
|
|
|
// Simple Case 4 (Immediate to lazy)
|
|
// refer: https://github.com/karmada-io/karmada/blob/release-1.9/docs/proposals/scheduling/activation-preference/lazy-activation-preference.md#simple-case-4-immediate-to-lazy
|
|
ginkgo.It("Simple Case 4 (Immediate to lazy)", func() {
|
|
ginkgo.By("step 1: deployment propagate success", func() {
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
|
|
ginkgo.By("step 2: after policy updated (cluster=member2, activationPreference=lazy), the propagation of deployment unchanged", func() {
|
|
// 1. activationPreference=lazy
|
|
policy.Spec.ActivationPreference = policyv1alpha1.LazyActivation
|
|
// 2. update policy placement with clusterAffinities
|
|
changePlacementTargetCluster(policy, modifiedCluster)
|
|
// 3. wait to distinguish whether the state will not change or have no time to change
|
|
time.Sleep(waitIntervalForLazyPolicyTest)
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
framework.WaitDeploymentDisappearOnCluster(modifiedCluster, namespace, deploymentName)
|
|
})
|
|
|
|
ginkgo.By("step3: resource would propagate when itself updated", func() {
|
|
// 1. update annotation of the deployment
|
|
updateDeploymentManually(deployment)
|
|
// 2. the propagation of deployment changed
|
|
framework.WaitDeploymentDisappearOnCluster(originalCluster, namespace, deploymentName)
|
|
waitDeploymentPresentOnCluster(modifiedCluster, namespace, deploymentName)
|
|
})
|
|
})
|
|
})
|
|
|
|
// Combined Case 4 (Policy preemption)
|
|
// refer: https://github.com/karmada-io/karmada/blob/release-1.9/docs/proposals/scheduling/activation-preference/lazy-activation-preference.md#combined-case-4-policy-preemption
|
|
ginkgo.It("Policy preemption", func() {
|
|
ginkgo.By("step 1: deployment propagate success when policy created before it", func() {
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
|
|
ginkgo.By("step 2: create PP2 (match nginx, cluster=member2, lazy, priority=2, preemption=true)", func() {
|
|
framework.CreatePropagationPolicy(karmadaClient, policyHigherPriority)
|
|
// 1. annotation of policy name changed
|
|
framework.WaitDeploymentFitWith(kubeClient, namespace, deploymentName, func(deployment *appsv1.Deployment) bool {
|
|
policyNameAnnotation := util.GetAnnotationValue(deployment.GetAnnotations(), policyv1alpha1.PropagationPolicyNameAnnotation)
|
|
return policyNameAnnotation == policyHigherPriorityName
|
|
})
|
|
// 2. wait to distinguish whether the state will not change or have no time to change
|
|
time.Sleep(waitIntervalForLazyPolicyTest)
|
|
// propagation unchanged
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
framework.WaitDeploymentDisappearOnCluster(modifiedCluster, namespace, deploymentName)
|
|
})
|
|
|
|
ginkgo.By("step 3: update deployment", func() {
|
|
// 1. update annotation of the deployment
|
|
updateDeploymentManually(deployment)
|
|
// 2. the propagation of deployment changed
|
|
waitDeploymentPresentOnCluster(modifiedCluster, namespace, deploymentName)
|
|
framework.WaitDeploymentDisappearOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
|
|
ginkgo.By("step 4: clean up", func() {
|
|
framework.RemovePropagationPolicyIfExist(karmadaClient, namespace, policyHigherPriorityName)
|
|
})
|
|
})
|
|
|
|
ginkgo.Context("Propagate dependencies", func() {
|
|
ginkgo.BeforeEach(func() {
|
|
policy.Spec.PropagateDeps = true
|
|
mountConfigMapToDeployment(deployment, configMapName)
|
|
})
|
|
|
|
ginkgo.JustBeforeEach(func() {
|
|
framework.CreateConfigMap(kubeClient, configMap)
|
|
ginkgo.DeferCleanup(func() {
|
|
framework.RemoveConfigMap(kubeClient, namespace, configMapName)
|
|
})
|
|
})
|
|
|
|
// Combined Case 5 (Propagate dependencies)
|
|
// refer: https://github.com/karmada-io/karmada/blob/release-1.9/docs/proposals/scheduling/activation-preference/lazy-activation-preference.md#combined-case-5-propagate-dependencies
|
|
ginkgo.It("Combined Case 5 (Propagate dependencies)", func() {
|
|
ginkgo.By("step 1: deployment and its dependencies could propagate success.", func() {
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
waitConfigMapPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
|
|
ginkgo.By("step 2: change of lazy policy will not take effect", func() {
|
|
changePlacementTargetCluster(policy, modifiedCluster)
|
|
// wait to distinguish whether the policy will not take effect or have no time to take effect
|
|
time.Sleep(waitIntervalForLazyPolicyTest)
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
waitConfigMapPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
|
|
ginkgo.By("step 3: lazy policy take effect when deployment updated, dependencies can also been propagated.", func() {
|
|
updateDeploymentManually(deployment)
|
|
waitDeploymentPresentOnCluster(modifiedCluster, namespace, deploymentName)
|
|
waitConfigMapPresentOnCluster(modifiedCluster, namespace, deploymentName)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
ginkgo.Context("2. Policy created after resource", func() {
|
|
ginkgo.JustBeforeEach(func() {
|
|
framework.CreateDeployment(kubeClient, deployment)
|
|
waitDeploymentReconciled(namespace, deploymentName)
|
|
framework.CreatePropagationPolicy(karmadaClient, policy)
|
|
|
|
ginkgo.DeferCleanup(func() {
|
|
framework.RemovePropagationPolicy(karmadaClient, namespace, policyName)
|
|
framework.RemoveDeployment(kubeClient, namespace, deploymentName)
|
|
framework.WaitDeploymentDisappearOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
})
|
|
|
|
// Simple Case 2 (Policy created after resource)
|
|
// refer: https://github.com/karmada-io/karmada/blob/release-1.9/docs/proposals/scheduling/activation-preference/lazy-activation-preference.md#simple-case-2-policy-created-after-resource
|
|
ginkgo.It("Simple Case 2 (Policy created after resource)", func() {
|
|
ginkgo.By("step1: deployment would not propagate when lazy policy created after deployment", func() {
|
|
// wait to distinguish whether the deployment will not propagate or have no time to propagate
|
|
time.Sleep(waitIntervalForLazyPolicyTest)
|
|
framework.WaitDeploymentDisappearOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
|
|
ginkgo.By("step2: resource would propagate when itself updated", func() {
|
|
updateDeploymentManually(deployment)
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
})
|
|
|
|
ginkgo.Context("Propagate dependencies", func() {
|
|
ginkgo.BeforeEach(func() {
|
|
mountConfigMapToDeployment(deployment, configMapName)
|
|
framework.CreateConfigMap(kubeClient, configMap)
|
|
ginkgo.DeferCleanup(func() {
|
|
framework.RemoveConfigMap(kubeClient, namespace, configMapName)
|
|
})
|
|
})
|
|
|
|
// Combined Case 6 (Propagate dependencies)
|
|
// refer: https://github.com/karmada-io/karmada/blob/release-1.9/docs/proposals/scheduling/activation-preference/lazy-activation-preference.md#combined-case-6-propagate-dependencies
|
|
ginkgo.It("Combined Case 6 (Propagate dependencies)", func() {
|
|
ginkgo.By("step 1: resources would not propagate when lazy policy created after resources", func() {
|
|
// wait to distinguish whether the resource will not propagate or have no time to propagate
|
|
time.Sleep(waitIntervalForLazyPolicyTest)
|
|
framework.WaitDeploymentDisappearOnCluster(originalCluster, namespace, deploymentName)
|
|
framework.WaitConfigMapDisappearOnCluster(originalCluster, namespace, configMapName)
|
|
})
|
|
|
|
ginkgo.By("step 2: configMap not propagate with deployment since policy PropagateDeps unset", func() {
|
|
updateDeploymentManually(deployment)
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
framework.WaitConfigMapDisappearOnCluster(originalCluster, namespace, configMapName)
|
|
})
|
|
|
|
ginkgo.By("step 3: set PropagateDeps of a lazy policy would not take effect immediately", func() {
|
|
setPolicyPropagateDeps(policy)
|
|
// wait to distinguish whether the policy will not take effect or have no time to take effect
|
|
time.Sleep(waitIntervalForLazyPolicyTest)
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
framework.WaitConfigMapDisappearOnCluster(originalCluster, namespace, configMapName)
|
|
})
|
|
|
|
ginkgo.By("step 4: set PropagateDeps of a lazy policy take effect when deployment itself updated", func() {
|
|
updateDeploymentManually(deployment)
|
|
waitDeploymentPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
waitConfigMapPresentOnCluster(originalCluster, namespace, deploymentName)
|
|
})
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
// updateDeploymentManually manually update deployment
|
|
func updateDeploymentManually(deployment *appsv1.Deployment) {
|
|
framework.AppendDeploymentAnnotations(kubeClient, deployment, map[string]string{"reconcileAt": time.Now().Format(time.RFC3339)})
|
|
}
|
|
|
|
// waitDeploymentPresentOnCluster wait deployment present on cluster
|
|
func waitDeploymentPresentOnCluster(cluster, namespace, name string) {
|
|
framework.WaitDeploymentPresentOnClusterFitWith(cluster, namespace, name, func(_ *appsv1.Deployment) bool {
|
|
return true
|
|
})
|
|
}
|
|
|
|
// waitDeploymentReconciled wait reconciliation of deployment finished
|
|
func waitDeploymentReconciled(namespace, name string) {
|
|
framework.WaitDeploymentFitWith(kubeClient, namespace, name, func(_ *appsv1.Deployment) bool {
|
|
// when applying deployment and policy sequentially, we expect deployment to perform the reconcile process before policy,
|
|
// but the order is actually uncertain, so we sleep a while to wait reconciliation of deployment finished.
|
|
time.Sleep(waitIntervalForLazyPolicyTest)
|
|
return true
|
|
})
|
|
}
|
|
|
|
// waitPropagatePolicyReconciled wait reconciliation of PropagatePolicy finished
|
|
func waitPropagatePolicyReconciled(namespace, name string) {
|
|
framework.WaitPropagationPolicyFitWith(karmadaClient, namespace, name, func(_ *policyv1alpha1.PropagationPolicy) bool {
|
|
// when applying policy and deployment sequentially, we expect policy to perform the reconcile process before deployment,
|
|
// but the order is actually uncertain, so we sleep a while to wait reconciliation of policy finished.
|
|
time.Sleep(waitIntervalForLazyPolicyTest)
|
|
return true
|
|
})
|
|
}
|
|
|
|
// waitConfigMapPresentOnCluster wait configmap present on cluster
|
|
func waitConfigMapPresentOnCluster(cluster, namespace, name string) {
|
|
framework.WaitConfigMapPresentOnClusterFitWith(cluster, namespace, name, func(_ *corev1.ConfigMap) bool {
|
|
return true
|
|
})
|
|
}
|
|
|
|
// mountConfigMapToDeployment mount ConfigMap to Deployment
|
|
func mountConfigMapToDeployment(deployment *appsv1.Deployment, configMapName string) {
|
|
volumes := []corev1.Volume{{
|
|
Name: "vol-configmap",
|
|
VolumeSource: corev1.VolumeSource{
|
|
ConfigMap: &corev1.ConfigMapVolumeSource{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: configMapName,
|
|
}}}}}
|
|
deployment.Spec.Template.Spec.Volumes = volumes
|
|
}
|
|
|
|
// changePlacementTargetCluster change policy target cluster to @modifiedCluster
|
|
func changePlacementTargetCluster(policy *policyv1alpha1.PropagationPolicy, modifiedCluster string) {
|
|
policySpec := policy.Spec
|
|
policySpec.Placement.ClusterAffinity = &policyv1alpha1.ClusterAffinity{ClusterNames: []string{modifiedCluster}}
|
|
framework.UpdatePropagationPolicyWithSpec(karmadaClient, policy.Namespace, policy.Name, policySpec)
|
|
}
|
|
|
|
// setPolicyPropagateDeps set PropagateDeps of policy to true
|
|
func setPolicyPropagateDeps(policy *policyv1alpha1.PropagationPolicy) {
|
|
policySpec := policy.Spec
|
|
policySpec.PropagateDeps = true
|
|
framework.UpdatePropagationPolicyWithSpec(karmadaClient, policy.Namespace, policy.Name, policySpec)
|
|
}
|