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  }