github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/test/integration/mysql_reconfigure_test.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package appstest 18 19 import ( 20 "fmt" 21 22 . "github.com/onsi/ginkgo/v2" 23 . "github.com/onsi/gomega" 24 25 corev1 "k8s.io/api/core/v1" 26 "k8s.io/apimachinery/pkg/types" 27 "sigs.k8s.io/controller-runtime/pkg/client" 28 29 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 30 "github.com/1aal/kubeblocks/pkg/common" 31 "github.com/1aal/kubeblocks/pkg/configuration/core" 32 "github.com/1aal/kubeblocks/pkg/generics" 33 testapps "github.com/1aal/kubeblocks/pkg/testutil/apps" 34 testk8s "github.com/1aal/kubeblocks/pkg/testutil/k8s" 35 "github.com/1aal/kubeblocks/test/testutils" 36 ) 37 38 var _ = Describe("MySQL Reconfigure function", func() { 39 const clusterDefName = "test-clusterdef" 40 const clusterVersionName = "test-clusterversion" 41 const clusterNamePrefix = "test-cluster" 42 43 const mysqlConfigTemplatePath = "resources/mysql-consensus-config-template.yaml" 44 const mysqlConfigConstraintPath = "resources/mysql-consensus-config-constraint.yaml" 45 const mysqlScriptsPath = "resources/mysql-consensus-scripts.yaml" 46 47 const leader = "leader" 48 const follower = "follower" 49 50 // ctx := context.Background() 51 52 // Cleanups 53 54 cleanEnv := func() { 55 // must wait until resources deleted and no longer exist before the testcases start, 56 // otherwise if later it needs to create some new resource objects with the same name, 57 // in race conditions, it will find the existence of old objects, resulting failure to 58 // create the new objects. 59 By("clean resources") 60 61 // delete cluster(and all dependent sub-resources), clusterversion and clusterdef 62 testapps.ClearClusterResources(&testCtx) 63 64 // delete rest configurations 65 inNS := client.InNamespace(testCtx.DefaultNamespace) 66 ml := client.HasLabels{testCtx.TestObjLabelKey} 67 // namespaced 68 testapps.ClearResources(&testCtx, generics.ConfigMapSignature, inNS, ml) 69 // non-namespaced 70 testapps.ClearResources(&testCtx, generics.ConfigConstraintSignature, ml) 71 testapps.ClearResources(&testCtx, generics.BackupPolicyTemplateSignature, ml) 72 } 73 74 BeforeEach(cleanEnv) 75 76 AfterEach(cleanEnv) 77 78 // Testcases 79 80 var ( 81 clusterDefObj *appsv1alpha1.ClusterDefinition 82 clusterVersionObj *appsv1alpha1.ClusterVersion 83 clusterObj *appsv1alpha1.Cluster 84 clusterKey types.NamespacedName 85 ) 86 87 newReconfigureRequest := func(clusterName string, componentName string, 88 configName string, configFile string, 89 parameterKey string, parameterValue *string) (opsRequest *appsv1alpha1.OpsRequest) { 90 randomOpsName := "reconfigure-ops-" + testCtx.GetRandomStr() 91 opsRequest = testapps.NewOpsRequestObj(randomOpsName, testCtx.DefaultNamespace, 92 clusterName, appsv1alpha1.ReconfiguringType) 93 opsRequest.Spec.Reconfigure = &appsv1alpha1.Reconfigure{ 94 Configurations: []appsv1alpha1.ConfigurationItem{{ 95 Name: configName, 96 Keys: []appsv1alpha1.ParameterConfig{{ 97 Key: configFile, 98 Parameters: []appsv1alpha1.ParameterPair{ 99 { 100 Key: parameterKey, 101 Value: parameterValue, 102 }, 103 }, 104 }}, 105 }}, 106 ComponentOps: appsv1alpha1.ComponentOps{ComponentName: componentName}, 107 } 108 return opsRequest 109 } 110 111 getClusterConfig := func(clusterObj *appsv1alpha1.Cluster) ( 112 componentName string, tpl *appsv1alpha1.ComponentConfigSpec, cmObj *corev1.ConfigMap) { 113 114 By("Get configuration information from cluster") 115 componentName = clusterObj.Spec.ComponentSpecs[0].ComponentDefRef 116 tpls, err := core.GetConfigTemplatesFromComponent(clusterObj.Spec.ComponentSpecs, 117 clusterDefObj.Spec.ComponentDefs, clusterVersionObj.Spec.ComponentVersions, componentName) 118 Expect(err).Should(BeNil()) 119 Expect(len(tpls) > 0).Should(BeTrue()) 120 121 By("Should have at least one valid config") 122 validTpls := make([]appsv1alpha1.ComponentConfigSpec, 0, len(tpls)) 123 for _, tpl := range tpls { 124 if len(tpl.ConfigConstraintRef) > 0 && len(tpl.TemplateRef) > 0 { 125 validTpls = append(validTpls, tpl) 126 } 127 } 128 Expect(len(validTpls) > 0).Should(BeTrue()) 129 130 cmObj = &corev1.ConfigMap{} 131 cmName := core.GetComponentCfgName(clusterObj.Name, componentName, tpls[0].Name) 132 err = testutils.GetResourceObjectFromGVR(testutils.ConfigmapGVR(), client.ObjectKey{ 133 Name: cmName, 134 Namespace: testCtx.DefaultNamespace, 135 }, dynamicClient, cmObj) 136 Expect(err).Should(BeNil()) 137 138 return componentName, &validTpls[0], cmObj 139 } 140 141 testReconfigureThreeReplicas := func() { 142 By("Create a cluster obj") 143 clusterName := testapps.GetRandomizedKey("", clusterNamePrefix).Name 144 clusterDefObj, clusterVersionObj, clusterObj = CreateSimpleConsensusMySQLClusterWithConfig( 145 testCtx, clusterDefName, clusterVersionName, clusterName, mysqlConfigTemplatePath, mysqlConfigConstraintPath, mysqlScriptsPath) 146 clusterKey = client.ObjectKeyFromObject(clusterObj) 147 fmt.Printf("ClusterDefinition:%s ClusterVersion:%s Cluster:%s \n", clusterDefObj.Name, clusterVersionObj.Name, clusterObj.Name) 148 149 By("Waiting the cluster is created") 150 Eventually(testapps.GetClusterPhase(&testCtx, clusterKey)).Should(Equal(appsv1alpha1.RunningClusterPhase)) 151 152 By("Checking pods' role label") 153 sts := testk8s.ListAndCheckStatefulSet(&testCtx, clusterKey).Items[0] 154 pods, err := common.GetPodListByStatefulSet(testCtx.Ctx, k8sClient, &sts) 155 Expect(err).To(Succeed()) 156 Expect(len(pods)).Should(Equal(3)) 157 158 // get role->count map 159 By("Checking the count of leader and followers, learners are ignored") 160 roleCountMap := GetConsensusRoleCountMap(testCtx, k8sClient, clusterObj) 161 Expect(roleCountMap[leader]).Should(Equal(1)) 162 Expect(roleCountMap[follower]).Should(Equal(2)) 163 164 By("Checking the cluster config") 165 componentName, tpl, cmObj := getClusterConfig(clusterObj) 166 configFile := "" 167 // get first config file 168 for k := range cmObj.Data { 169 configFile = k 170 break 171 } 172 173 By("Issue a restart load reconfigure OpsRequest - max_connections") 174 pKey := "max_connections" 175 pValue := "2000" 176 reconfigureOpsRequest := newReconfigureRequest(clusterObj.Name, componentName, 177 tpl.Name, configFile, pKey, &pValue) 178 Expect(testCtx.CreateObj(testCtx.Ctx, reconfigureOpsRequest)).Should(Succeed()) 179 180 By("Checking ReconfigureOpsRequest is running") 181 opsKey := types.NamespacedName{Name: reconfigureOpsRequest.Name, Namespace: testCtx.DefaultNamespace} 182 Eventually(testapps.GetOpsRequestPhase(&testCtx, opsKey)).Should(Equal(appsv1alpha1.OpsRunningPhase)) 183 184 By("Checking Cluster and changed component phase is Reconfiguring") 185 Eventually(testapps.CheckObj(&testCtx, clusterKey, func(g Gomega, cluster *appsv1alpha1.Cluster) { 186 g.Expect(cluster.Status.Phase).To(Equal(appsv1alpha1.UpdatingClusterPhase)) // appsv1alpha1.ReconfiguringPhase 187 g.Expect(cluster.Status.Components[componentName].Phase).To(Equal(appsv1alpha1.UpdatingClusterCompPhase)) // appsv1alpha1.ReconfiguringPhase 188 // TODO: add status condition check 189 })).Should(Succeed()) 190 191 By("Issue another reconfigure OpsRequest that will fail - innodb_read_io_threads") 192 pKey = "innodb_read_io_threads" 193 pValue = "2" 194 reconfigureOpsRequest = newReconfigureRequest(clusterObj.Name, componentName, 195 tpl.Name, configFile, pKey, &pValue) 196 Expect(testCtx.CreateObj(testCtx.Ctx, reconfigureOpsRequest)).ShouldNot(Succeed()) 197 } 198 199 // Scenarios 200 Context("with MySQL defined as Consensus type and three replicas", func() { 201 It("should update config with opsrequest in restart mode or dynamic loading mode", func() { 202 testReconfigureThreeReplicas() 203 }) 204 }) 205 })