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 })