github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/controller/component/affinity_utils_test.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package component
    21  
    22  import (
    23  	"fmt"
    24  
    25  	. "github.com/onsi/ginkgo/v2"
    26  	. "github.com/onsi/gomega"
    27  
    28  	corev1 "k8s.io/api/core/v1"
    29  
    30  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    31  	"github.com/1aal/kubeblocks/pkg/constant"
    32  	intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil"
    33  	testapps "github.com/1aal/kubeblocks/pkg/testutil/apps"
    34  	viper "github.com/1aal/kubeblocks/pkg/viperx"
    35  )
    36  
    37  var _ = Describe("affinity utils", func() {
    38  	const (
    39  		clusterDefName     = "test-clusterdef"
    40  		clusterVersionName = "test-clusterversion"
    41  		clusterName        = "test-cluster"
    42  		mysqlCompDefName   = "replicasets"
    43  		mysqlCompName      = "mysql"
    44  
    45  		clusterTolerationKey = "testClusterTolerationKey"
    46  		topologyKey          = "testTopologyKey"
    47  		labelKey             = "testNodeLabelKey"
    48  		labelValue           = "testLabelValue"
    49  		nodeKey              = "testNodeKey"
    50  	)
    51  
    52  	var (
    53  		clusterObj *appsv1alpha1.Cluster
    54  		component  *SynthesizedComponent
    55  
    56  		buildObjs = func(podAntiAffinity appsv1alpha1.PodAntiAffinity) {
    57  			clusterDefObj := testapps.NewClusterDefFactory(clusterDefName).
    58  				AddComponentDef(testapps.StatefulMySQLComponent, mysqlCompDefName).
    59  				GetObject()
    60  
    61  			clusterVersionObj := testapps.NewClusterVersionFactory(clusterVersionName, clusterDefObj.Name).
    62  				AddComponentVersion(mysqlCompDefName).AddContainerShort("mysql", testapps.ApeCloudMySQLImage).
    63  				GetObject()
    64  
    65  			affinity := &appsv1alpha1.Affinity{
    66  				PodAntiAffinity: podAntiAffinity,
    67  				TopologyKeys:    []string{topologyKey},
    68  				NodeLabels: map[string]string{
    69  					labelKey: labelValue,
    70  				},
    71  			}
    72  
    73  			toleration := corev1.Toleration{
    74  				Key:      clusterTolerationKey,
    75  				Operator: corev1.TolerationOpExists,
    76  				Effect:   corev1.TaintEffectNoExecute,
    77  			}
    78  
    79  			clusterObj = testapps.NewClusterFactory(testCtx.DefaultNamespace, clusterName,
    80  				clusterDefObj.Name, clusterVersionObj.Name).
    81  				AddComponent(mysqlCompName, mysqlCompDefName).
    82  				SetClusterAffinity(affinity).
    83  				AddClusterToleration(toleration).
    84  				GetObject()
    85  
    86  			reqCtx := intctrlutil.RequestCtx{
    87  				Ctx: ctx,
    88  				Log: tlog,
    89  			}
    90  			component, _ = BuildComponent(
    91  				reqCtx,
    92  				nil,
    93  				clusterObj,
    94  				clusterDefObj,
    95  				&clusterDefObj.Spec.ComponentDefs[0],
    96  				&clusterObj.Spec.ComponentSpecs[0],
    97  				nil,
    98  				&clusterVersionObj.Spec.ComponentVersions[0],
    99  			)
   100  		}
   101  	)
   102  
   103  	Context("with PodAntiAffinity set to Required", func() {
   104  		BeforeEach(func() {
   105  			buildObjs(appsv1alpha1.Required)
   106  			Expect(component).ShouldNot(BeNil())
   107  		})
   108  
   109  		It("should have correct Affinity and TopologySpreadConstraints", func() {
   110  			affinity, err := BuildPodAffinity(clusterObj, clusterObj.Spec.Affinity, component)
   111  			Expect(err).Should(Succeed())
   112  			Expect(affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Key).Should(Equal(labelKey))
   113  			Expect(affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].TopologyKey).Should(Equal(topologyKey))
   114  			Expect(affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution).Should(BeEmpty())
   115  			Expect(affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution).Should(BeEmpty())
   116  
   117  			topologySpreadConstraints := BuildPodTopologySpreadConstraints(clusterObj, clusterObj.Spec.Affinity, component)
   118  			Expect(topologySpreadConstraints[0].WhenUnsatisfiable).Should(Equal(corev1.DoNotSchedule))
   119  			Expect(topologySpreadConstraints[0].TopologyKey).Should(Equal(topologyKey))
   120  		})
   121  
   122  		It("when data plane affinity is set, should have correct Affinity and TopologySpreadConstraints", func() {
   123  			viper.Set(constant.CfgKeyDataPlaneAffinity,
   124  				fmt.Sprintf("{\"nodeAffinity\":{\"preferredDuringSchedulingIgnoredDuringExecution\":[{\"preference\":{\"matchExpressions\":[{\"key\":\"%s\",\"operator\":\"In\",\"values\":[\"true\"]}]},\"weight\":100}]}}", nodeKey))
   125  			defer viper.Set(constant.CfgKeyDataPlaneAffinity, "")
   126  
   127  			affinity, err := BuildPodAffinity(clusterObj, clusterObj.Spec.Affinity, component)
   128  			Expect(err).Should(Succeed())
   129  			Expect(affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Key).Should(Equal(labelKey))
   130  			Expect(affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].TopologyKey).Should(Equal(topologyKey))
   131  			Expect(affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution).Should(BeEmpty())
   132  			Expect(affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].Preference.MatchExpressions[0].Key).Should(Equal(nodeKey))
   133  
   134  			topologySpreadConstraints := BuildPodTopologySpreadConstraints(clusterObj, clusterObj.Spec.Affinity, component)
   135  			Expect(topologySpreadConstraints[0].WhenUnsatisfiable).Should(Equal(corev1.DoNotSchedule))
   136  			Expect(topologySpreadConstraints[0].TopologyKey).Should(Equal(topologyKey))
   137  		})
   138  	})
   139  
   140  	Context("with tolerations", func() {
   141  		BeforeEach(func() {
   142  			buildObjs(appsv1alpha1.Required)
   143  		})
   144  
   145  		It("should have correct tolerations", func() {
   146  			tolerations, err := BuildTolerations(clusterObj, &clusterObj.Spec.ComponentSpecs[0])
   147  			Expect(err).Should(Succeed())
   148  			Expect(tolerations).ShouldNot(BeEmpty())
   149  			Expect(tolerations[0].Key).Should(Equal(clusterTolerationKey))
   150  		})
   151  
   152  		It("when data plane tolerations is set, should have correct tolerations", func() {
   153  			const dpTolerationKey = "dataPlaneTolerationKey"
   154  			viper.Set(constant.CfgKeyDataPlaneTolerations, fmt.Sprintf("[{\"key\":\"%s\", \"operator\": \"Exists\", \"effect\": \"NoSchedule\"}]", dpTolerationKey))
   155  			defer viper.Set(constant.CfgKeyDataPlaneTolerations, "")
   156  			tolerations, err := BuildTolerations(clusterObj, &clusterObj.Spec.ComponentSpecs[0])
   157  			Expect(err).Should(Succeed())
   158  			Expect(tolerations).Should(HaveLen(2))
   159  			Expect(tolerations[0].Key).Should(Equal(clusterTolerationKey))
   160  			Expect(tolerations[1].Key).Should(Equal(dpTolerationKey))
   161  		})
   162  	})
   163  
   164  	Context("with PodAntiAffinity set to Preferred", func() {
   165  		BeforeEach(func() {
   166  			buildObjs(appsv1alpha1.Preferred)
   167  			Expect(component).ShouldNot(BeNil())
   168  		})
   169  
   170  		It("should have correct Affinity and TopologySpreadConstraints", func() {
   171  			affinity, err := BuildPodAffinity(clusterObj, clusterObj.Spec.Affinity, component)
   172  			Expect(err).Should(Succeed())
   173  			Expect(affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Key).Should(Equal(labelKey))
   174  			Expect(affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution).Should(BeEmpty())
   175  			Expect(affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].Weight).ShouldNot(BeNil())
   176  			Expect(affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution[0].PodAffinityTerm.TopologyKey).Should(Equal(topologyKey))
   177  
   178  			topologySpreadConstraints := BuildPodTopologySpreadConstraints(clusterObj, clusterObj.Spec.Affinity, component)
   179  			Expect(topologySpreadConstraints[0].WhenUnsatisfiable).Should(Equal(corev1.ScheduleAnyway))
   180  			Expect(topologySpreadConstraints[0].TopologyKey).Should(Equal(topologyKey))
   181  		})
   182  	})
   183  })