github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/acceptance/util_cluster.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package acceptance 12 13 import ( 14 "context" 15 "path/filepath" 16 "regexp" 17 "strings" 18 "testing" 19 "time" 20 21 "github.com/cockroachdb/cockroach/pkg/acceptance/cluster" 22 "github.com/cockroachdb/cockroach/pkg/testutils" 23 "github.com/cockroachdb/cockroach/pkg/util/log" 24 "github.com/cockroachdb/cockroach/pkg/util/stop" 25 "github.com/cockroachdb/errors" 26 ) 27 28 const ( 29 dockerTest = "runMode=docker" 30 ) 31 32 var stopper = stop.NewStopper() 33 34 // RunDocker runs the given acceptance test using a Docker cluster. 35 func RunDocker(t *testing.T, testee func(t *testing.T)) { 36 t.Run(dockerTest, testee) 37 } 38 39 // turns someTest#123 into someTest when invoked with ReplicaAllLiteralString. 40 // This is useful because the go test harness automatically disambiguates 41 // subtests in that way when they are invoked multiple times with the same name, 42 // and we sometimes call RunDocker multiple times in tests. 43 var reStripTestEnumeration = regexp.MustCompile(`#\d+$`) 44 45 // StartCluster starts a cluster from the relevant flags. All test clusters 46 // should be created through this command since it sets up the logging in a 47 // unified way. 48 func StartCluster(ctx context.Context, t *testing.T, cfg cluster.TestConfig) (c cluster.Cluster) { 49 var completed bool 50 defer func() { 51 if !completed && c != nil { 52 c.AssertAndStop(ctx, t) 53 } 54 }() 55 56 parts := strings.Split(t.Name(), "/") 57 if len(parts) < 2 { 58 t.Fatal("must invoke RunDocker") 59 } 60 61 var runMode string 62 for _, part := range parts[1:] { 63 part = reStripTestEnumeration.ReplaceAllLiteralString(part, "") 64 switch part { 65 case dockerTest: 66 if runMode != "" { 67 t.Fatalf("test has more than one run mode: %s and %s", runMode, part) 68 } 69 runMode = part 70 } 71 } 72 73 switch runMode { 74 case dockerTest: 75 logDir := *flagLogDir 76 if logDir != "" { 77 logDir = filepath.Join(logDir, filepath.Clean(t.Name())) 78 } 79 l := cluster.CreateDocker(ctx, cfg, logDir, stopper) 80 l.Start(ctx) 81 c = l 82 83 default: 84 t.Fatalf("unable to run in mode %q, use RunDocker", runMode) 85 } 86 87 // Don't wait for replication unless requested (usually it is). 88 if !cfg.NoWait && cfg.InitMode != cluster.INIT_NONE { 89 wantedReplicas := 3 90 if numNodes := c.NumNodes(); numNodes < wantedReplicas { 91 wantedReplicas = numNodes 92 } 93 94 // Looks silly, but we actually start zero-node clusters in the 95 // reference tests. 96 if wantedReplicas > 0 { 97 log.Infof(ctx, "waiting for first range to have %d replicas", wantedReplicas) 98 99 testutils.SucceedsSoon(t, func() error { 100 select { 101 case <-stopper.ShouldStop(): 102 t.Fatal("interrupted") 103 case <-time.After(time.Second): 104 } 105 106 // Always talk to node 0 because it's guaranteed to exist. 107 db, err := c.NewDB(ctx, 0) 108 if err != nil { 109 t.Fatal(err) 110 } 111 rows, err := db.Query(`SELECT array_length(replicas, 1) FROM crdb_internal.ranges LIMIT 1`) 112 if err != nil { 113 // Versions <= 1.1 do not contain the crdb_internal table, which is what's used 114 // to determine whether a cluster has up-replicated. This is relevant for the 115 // version upgrade acceptance test. Just skip the replication check for this case. 116 if testutils.IsError(err, "(table|relation) \"crdb_internal.ranges\" does not exist") { 117 return nil 118 } 119 t.Fatal(err) 120 } 121 defer rows.Close() 122 var foundReplicas int 123 if rows.Next() { 124 if err = rows.Scan(&foundReplicas); err != nil { 125 t.Fatalf("unable to scan for length of replicas array: %s", err) 126 } 127 if log.V(1) { 128 log.Infof(ctx, "found %d replicas", foundReplicas) 129 } 130 } else { 131 return errors.Errorf("no ranges listed") 132 } 133 134 if foundReplicas < wantedReplicas { 135 return errors.Errorf("expected %d replicas, only found %d", wantedReplicas, foundReplicas) 136 } 137 return nil 138 }) 139 } 140 141 // Ensure that all nodes are serving SQL by making sure a simple 142 // read-only query succeeds. 143 for i := 0; i < c.NumNodes(); i++ { 144 testutils.SucceedsSoon(t, func() error { 145 db, err := c.NewDB(ctx, i) 146 if err != nil { 147 return err 148 } 149 if _, err := db.Exec("SHOW DATABASES"); err != nil { 150 return err 151 } 152 return nil 153 }) 154 } 155 } 156 157 completed = true 158 return c 159 }