vitess.io/vitess@v0.16.2/test/ci_workflow_gen.go (about) 1 /* 2 Copyright 2021 The Vitess Authors. 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 main 18 19 import ( 20 "bytes" 21 "fmt" 22 "log" 23 "os" 24 "path" 25 "strings" 26 "text/template" 27 ) 28 29 type mysqlVersion string 30 31 const ( 32 mysql57 mysqlVersion = "mysql57" 33 mysql80 mysqlVersion = "mysql80" 34 35 defaultMySQLVersion = mysql80 36 ) 37 38 type mysqlVersions []mysqlVersion 39 40 var ( 41 defaultMySQLVersions = []mysqlVersion{defaultMySQLVersion} 42 allMySQLVersions = []mysqlVersion{mysql57, mysql80} 43 ) 44 45 var ( 46 unitTestDatabases = []mysqlVersion{mysql57, mysql80} 47 ) 48 49 const ( 50 workflowConfigDir = "../.github/workflows" 51 52 unitTestTemplate = "templates/unit_test.tpl" 53 54 // An empty string will cause the default non platform specific template 55 // to be used. 56 clusterTestTemplate = "templates/cluster_endtoend_test%s.tpl" 57 58 unitTestSelfHostedTemplate = "templates/unit_test_self_hosted.tpl" 59 unitTestSelfHostedDatabases = "" 60 dockerFileTemplate = "templates/dockerfile.tpl" 61 clusterTestSelfHostedTemplate = "templates/cluster_endtoend_test_self_hosted.tpl" 62 clusterTestDockerTemplate = "templates/cluster_endtoend_test_docker.tpl" 63 ) 64 65 var ( 66 // Clusters 10, 25 are executed on docker, using the docker_test_cluster 10, 25 workflows. 67 // Hence, they are not listed in the list below. 68 clusterList = []string{ 69 "vtctlbackup_sharded_clustertest_heavy", 70 "12", 71 "13", 72 "ers_prs_newfeatures_heavy", 73 "15", 74 "vtgate_general_heavy", 75 "vtbackup", 76 "18", 77 "xb_backup", 78 "backup_pitr", 79 "21", 80 "22", 81 "mysql_server_vault", 82 "vstream_failover", 83 "vstream_stoponreshard_true", 84 "vstream_stoponreshard_false", 85 "vstream_with_keyspaces_to_watch", 86 "onlineddl_ghost", 87 "onlineddl_vrepl", 88 "onlineddl_vrepl_stress", 89 "onlineddl_vrepl_stress_suite", 90 "onlineddl_vrepl_suite", 91 "vreplication_migrate_vdiff2_convert_tz", 92 "onlineddl_revert", 93 "onlineddl_scheduler", 94 "tabletmanager_throttler", 95 "tabletmanager_throttler_topo", 96 "tabletmanager_throttler_custom_config", 97 "tabletmanager_tablegc", 98 "tabletmanager_consul", 99 "vtgate_concurrentdml", 100 "vtgate_godriver", 101 "vtgate_gen4", 102 "vtgate_readafterwrite", 103 "vtgate_reservedconn", 104 "vtgate_schema", 105 "vtgate_tablet_healthcheck_cache", 106 "vtgate_topo", 107 "vtgate_topo_consul", 108 "vtgate_topo_etcd", 109 "vtgate_transaction", 110 "vtgate_unsharded", 111 "vtgate_vindex_heavy", 112 "vtgate_vschema", 113 "vtgate_queries", 114 "vtgate_schema_tracker", 115 "vtorc", 116 "xb_recovery", 117 "mysql80", 118 "vreplication_across_db_versions", 119 "vreplication_multicell", 120 "vreplication_cellalias", 121 "vreplication_basic", 122 "vreplication_v2", 123 "schemadiff_vrepl", 124 "topo_connection_cache", 125 "vtgate_partial_keyspace", 126 "vttablet_prscomplex", 127 } 128 129 clusterSelfHostedList = []string{} 130 clusterDockerList = []string{} 131 clustersRequiringXtraBackup = []string{ 132 "xb_backup", 133 "xb_recovery", 134 } 135 clustersRequiringMakeTools = []string{ 136 "18", 137 "mysql_server_vault", 138 "vtgate_topo_consul", 139 "tabletmanager_consul", 140 } 141 ) 142 143 type unitTest struct { 144 Name, Platform, FileName string 145 } 146 147 type clusterTest struct { 148 Name, Shard, Platform string 149 FileName string 150 MakeTools, InstallXtraBackup bool 151 Docker bool 152 LimitResourceUsage bool 153 PartialKeyspace bool 154 } 155 156 type selfHostedTest struct { 157 Name, Platform, Dockerfile, Shard, ImageName, directoryName string 158 FileName string 159 MakeTools, InstallXtraBackup, Docker bool 160 } 161 162 // clusterMySQLVersions return list of mysql versions (one or more) that this cluster needs to test against 163 func clusterMySQLVersions(clusterName string) mysqlVersions { 164 switch { 165 case strings.HasPrefix(clusterName, "onlineddl_"): 166 return allMySQLVersions 167 case clusterName == "schemadiff_vrepl": 168 return allMySQLVersions 169 case clusterName == "backup_pitr": 170 return allMySQLVersions 171 case clusterName == "tabletmanager_tablegc": 172 return allMySQLVersions 173 case clusterName == "vtorc": 174 return allMySQLVersions 175 case clusterName == "xb_backup": 176 return allMySQLVersions 177 case clusterName == "xb_recovery": 178 return allMySQLVersions 179 default: 180 return defaultMySQLVersions 181 } 182 } 183 184 func mergeBlankLines(buf *bytes.Buffer) string { 185 var out []string 186 in := strings.Split(buf.String(), "\n") 187 lastWasBlank := false 188 for _, line := range in { 189 if strings.TrimSpace(line) == "" { 190 if lastWasBlank { 191 continue 192 } 193 lastWasBlank = true 194 } else { 195 lastWasBlank = false 196 } 197 198 out = append(out, line) 199 } 200 return strings.Join(out, "\n") 201 } 202 203 func main() { 204 generateUnitTestWorkflows() 205 generateClusterWorkflows(clusterList, clusterTestTemplate) 206 generateClusterWorkflows(clusterDockerList, clusterTestDockerTemplate) 207 208 // tests that will use self-hosted runners 209 err := generateSelfHostedUnitTestWorkflows() 210 if err != nil { 211 log.Fatal(err) 212 } 213 err = generateSelfHostedClusterWorkflows() 214 if err != nil { 215 log.Fatal(err) 216 } 217 } 218 219 func canonnizeList(list []string) []string { 220 var output []string 221 for _, item := range list { 222 if item := strings.TrimSpace(item); item != "" { 223 output = append(output, item) 224 } 225 } 226 return output 227 } 228 229 func parseList(csvList string) []string { 230 var list []string 231 for _, item := range strings.Split(csvList, ",") { 232 if item != "" { 233 list = append(list, strings.TrimSpace(item)) 234 } 235 } 236 return list 237 } 238 239 func generateSelfHostedUnitTestWorkflows() error { 240 platforms := parseList(unitTestSelfHostedDatabases) 241 for _, platform := range platforms { 242 directoryName := fmt.Sprintf("unit_test_%s", platform) 243 test := &selfHostedTest{ 244 Name: fmt.Sprintf("Unit Test (%s)", platform), 245 ImageName: fmt.Sprintf("unit_test_%s", platform), 246 Platform: platform, 247 directoryName: directoryName, 248 Dockerfile: fmt.Sprintf("./.github/docker/%s/Dockerfile", directoryName), 249 MakeTools: true, 250 InstallXtraBackup: false, 251 } 252 err := setupTestDockerFile(test) 253 if err != nil { 254 return err 255 } 256 test.FileName = fmt.Sprintf("unit_test_%s.yml", platform) 257 filePath := fmt.Sprintf("%s/%s", workflowConfigDir, test.FileName) 258 err = writeFileFromTemplate(unitTestSelfHostedTemplate, filePath, test) 259 if err != nil { 260 log.Print(err) 261 } 262 } 263 return nil 264 } 265 266 func generateSelfHostedClusterWorkflows() error { 267 clusters := canonnizeList(clusterSelfHostedList) 268 for _, cluster := range clusters { 269 for _, mysqlVersion := range clusterMySQLVersions(cluster) { 270 // check mysqlversion 271 mysqlVersionIndicator := "" 272 if mysqlVersion != defaultMySQLVersion && len(clusterMySQLVersions(cluster)) > 1 { 273 mysqlVersionIndicator = "_" + string(mysqlVersion) 274 } 275 276 directoryName := fmt.Sprintf("cluster_test_%s%s", cluster, mysqlVersionIndicator) 277 test := &selfHostedTest{ 278 Name: fmt.Sprintf("Cluster (%s)(%s)", cluster, mysqlVersion), 279 ImageName: fmt.Sprintf("cluster_test_%s%s", cluster, mysqlVersionIndicator), 280 Platform: "mysql80", 281 directoryName: directoryName, 282 Dockerfile: fmt.Sprintf("./.github/docker/%s/Dockerfile", directoryName), 283 Shard: cluster, 284 MakeTools: false, 285 InstallXtraBackup: false, 286 } 287 makeToolClusters := canonnizeList(clustersRequiringMakeTools) 288 for _, makeToolCluster := range makeToolClusters { 289 if makeToolCluster == cluster { 290 test.MakeTools = true 291 break 292 } 293 } 294 xtraBackupClusters := canonnizeList(clustersRequiringXtraBackup) 295 for _, xtraBackupCluster := range xtraBackupClusters { 296 if xtraBackupCluster == cluster { 297 test.InstallXtraBackup = true 298 break 299 } 300 } 301 if mysqlVersion == mysql57 { 302 test.Platform = string(mysql57) 303 } 304 305 err := setupTestDockerFile(test) 306 if err != nil { 307 return err 308 } 309 310 test.FileName = fmt.Sprintf("cluster_endtoend_%s%s.yml", cluster, mysqlVersionIndicator) 311 filePath := fmt.Sprintf("%s/%s", workflowConfigDir, test.FileName) 312 err = writeFileFromTemplate(clusterTestSelfHostedTemplate, filePath, test) 313 if err != nil { 314 log.Print(err) 315 } 316 } 317 } 318 return nil 319 } 320 321 func generateClusterWorkflows(list []string, tpl string) { 322 clusters := canonnizeList(list) 323 for _, cluster := range clusters { 324 for _, mysqlVersion := range clusterMySQLVersions(cluster) { 325 test := &clusterTest{ 326 Name: fmt.Sprintf("Cluster (%s)", cluster), 327 Shard: cluster, 328 } 329 makeToolClusters := canonnizeList(clustersRequiringMakeTools) 330 for _, makeToolCluster := range makeToolClusters { 331 if makeToolCluster == cluster { 332 test.MakeTools = true 333 break 334 } 335 } 336 xtraBackupClusters := canonnizeList(clustersRequiringXtraBackup) 337 for _, xtraBackupCluster := range xtraBackupClusters { 338 if xtraBackupCluster == cluster { 339 test.InstallXtraBackup = true 340 break 341 } 342 } 343 if mysqlVersion == mysql57 { 344 test.Platform = string(mysql57) 345 } 346 if strings.HasPrefix(cluster, "vreplication") || strings.HasSuffix(cluster, "heavy") { 347 test.LimitResourceUsage = true 348 } 349 mysqlVersionIndicator := "" 350 if mysqlVersion != defaultMySQLVersion && len(clusterMySQLVersions(cluster)) > 1 { 351 mysqlVersionIndicator = "_" + string(mysqlVersion) 352 test.Name = test.Name + " " + string(mysqlVersion) 353 } 354 if strings.Contains(test.Shard, "partial_keyspace") { 355 test.PartialKeyspace = true 356 } 357 358 workflowPath := fmt.Sprintf("%s/cluster_endtoend_%s%s.yml", workflowConfigDir, cluster, mysqlVersionIndicator) 359 templateFileName := tpl 360 if test.Platform != "" { 361 templateFileName = fmt.Sprintf(tpl, "_"+test.Platform) 362 } else if strings.Contains(templateFileName, "%s") { 363 templateFileName = fmt.Sprintf(tpl, "") 364 } 365 test.FileName = fmt.Sprintf("cluster_endtoend_%s%s.yml", cluster, mysqlVersionIndicator) 366 err := writeFileFromTemplate(templateFileName, workflowPath, test) 367 if err != nil { 368 log.Print(err) 369 } 370 } 371 } 372 } 373 374 func generateUnitTestWorkflows() { 375 for _, platform := range unitTestDatabases { 376 test := &unitTest{ 377 Name: fmt.Sprintf("Unit Test (%s)", platform), 378 Platform: string(platform), 379 } 380 test.FileName = fmt.Sprintf("unit_test_%s.yml", platform) 381 path := fmt.Sprintf("%s/%s", workflowConfigDir, test.FileName) 382 err := writeFileFromTemplate(unitTestTemplate, path, test) 383 if err != nil { 384 log.Print(err) 385 } 386 } 387 } 388 389 func setupTestDockerFile(test *selfHostedTest) error { 390 // remove the directory 391 relDirectoryName := fmt.Sprintf("../.github/docker/%s", test.directoryName) 392 err := os.RemoveAll(relDirectoryName) 393 if err != nil { 394 return err 395 } 396 // create the directory 397 err = os.MkdirAll(relDirectoryName, 0755) 398 if err != nil { 399 return err 400 } 401 402 // generate the docker file 403 dockerFilePath := path.Join(relDirectoryName, "Dockerfile") 404 err = writeFileFromTemplate(dockerFileTemplate, dockerFilePath, test) 405 if err != nil { 406 return err 407 } 408 409 return nil 410 } 411 412 func writeFileFromTemplate(templateFile, filePath string, test any) error { 413 tpl := template.New(path.Base(templateFile)) 414 tpl.Funcs(template.FuncMap{ 415 "contains": strings.Contains, 416 }) 417 tpl, err := tpl.ParseFiles(templateFile) 418 if err != nil { 419 return fmt.Errorf("Error: %s\n", err) 420 } 421 422 buf := &bytes.Buffer{} 423 err = tpl.Execute(buf, test) 424 if err != nil { 425 return fmt.Errorf("Error: %s\n", err) 426 } 427 428 f, err := os.Create(filePath) 429 if err != nil { 430 return fmt.Errorf("Error creating file: %s\n", err) 431 } 432 if _, err := f.WriteString("# DO NOT MODIFY: THIS FILE IS GENERATED USING \"make generate_ci_workflows\"\n\n"); err != nil { 433 return err 434 } 435 if _, err := f.WriteString(mergeBlankLines(buf)); err != nil { 436 return err 437 } 438 fmt.Printf("Generated %s\n", filePath) 439 return nil 440 }