// PGUrl returns a postgres connection url which connects to this server with // the given user. Returns a connection string and a cleanup function which must // be called after any connection created using the string has been closed. // // In order to connect securely using postgres, this method will create // temporary on-disk copies of certain embedded security certificates. The // certificates will be created as temporary files in the provided directory, // and their filenames will have the provided prefix. The returned cleanup // function will delete these temporary files. func PGUrl(t util.Tester, ts *server.TestServer, user, tempDir, prefix string) (url.URL, func()) { host, port, err := net.SplitHostPort(ts.PGAddr()) if err != nil { t.Fatal(err) } caPath := filepath.Join(security.EmbeddedCertsDir, "ca.crt") certPath := security.ClientCertPath(security.EmbeddedCertsDir, user) keyPath := security.ClientKeyPath(security.EmbeddedCertsDir, user) // Copy these assets to disk from embedded strings, so this test can // run from a standalone binary. tempCAPath, tempCACleanup := securitytest.TempRestrictedCopy(t, caPath, tempDir, "TestLogic_ca") tempCertPath, tempCertCleanup := securitytest.TempRestrictedCopy(t, certPath, tempDir, "TestLogic_cert") tempKeyPath, tempKeyCleanup := securitytest.TempRestrictedCopy(t, keyPath, tempDir, "TestLogic_key") return url.URL{ Scheme: "postgres", User: url.User(user), Host: net.JoinHostPort(host, port), RawQuery: fmt.Sprintf("sslmode=verify-full&sslrootcert=%s&sslcert=%s&sslkey=%s", url.QueryEscape(tempCAPath), url.QueryEscape(tempCertPath), url.QueryEscape(tempKeyPath), ), }, func() { tempCACleanup() tempCertCleanup() tempKeyCleanup() } }
// Start starts the test cluster by bootstrapping an in-memory store // (defaults to maximum of 50M). The server is started, launching the // node RPC server and all HTTP endpoints. Use the value of // TestServer.Addr after Start() for client connections. Use Stop() // to shutdown the server after the test completes. func (ltc *LocalTestCluster) Start(t util.Tester) { ltc.Manual = hlc.NewManualClock(0) ltc.Clock = hlc.NewClock(ltc.Manual.UnixNano) ltc.Stopper = stop.NewStopper() rpcContext := rpc.NewContext(testutils.NewRootTestBaseContext(), ltc.Clock, ltc.Stopper) ltc.Gossip = gossip.New(rpcContext, gossip.TestInterval, gossip.TestBootstrap) ltc.Eng = engine.NewInMem(proto.Attributes{}, 50<<20) ltc.lSender = newRetryableLocalSender(NewLocalSender()) ltc.Sender = NewTxnCoordSender(ltc.lSender, ltc.Clock, false, nil, ltc.Stopper) var err error if ltc.DB, err = client.Open("//root@", client.SenderOpt(ltc.Sender)); err != nil { t.Fatal(err) } transport := multiraft.NewLocalRPCTransport(ltc.Stopper) ltc.Stopper.AddCloser(transport) ctx := storage.TestStoreContext ctx.Clock = ltc.Clock ctx.DB = ltc.DB ctx.Gossip = ltc.Gossip ctx.Transport = transport ltc.Store = storage.NewStore(ctx, ltc.Eng, &proto.NodeDescriptor{NodeID: 1}) if err := ltc.Store.Bootstrap(proto.StoreIdent{NodeID: 1, StoreID: 1}, ltc.Stopper); err != nil { t.Fatalf("unable to start local test cluster: %s", err) } ltc.lSender.AddStore(ltc.Store) if err := ltc.Store.BootstrapRange(nil); err != nil { t.Fatalf("unable to start local test cluster: %s", err) } if err := ltc.Store.Start(ltc.Stopper); err != nil { t.Fatalf("unable to start local test cluster: %s", err) } }
// TempRestrictedCopy creates a temporary on-disk copy of the embedded security asset // with the provided path. The copy will be created as a temporary file in the // provided directory; its filename will have the provided prefix. Returns the // path of the temporary file and a cleanup function that will delete the // temporary file. // // The temporary file will have restrictive file permissions (0600), making it // appropriate for usage by libraries that require security assets to have such // restrictive permissions. func TempRestrictedCopy(t util.Tester, path, tempdir, prefix string) (string, func()) { contents, err := Asset(path) if err != nil { t.Fatal(err) } return util.CreateTempRestrictedFile(t, contents, tempdir, prefix) }
// RestrictedCopy creates an on-disk copy of the embedded security asset // with the provided path. The copy will be created in the provided directory. // Returns the path of the file and a cleanup function that will delete the file. // // The file will have restrictive file permissions (0600), making it // appropriate for usage by libraries that require security assets to have such // restrictive permissions. func RestrictedCopy(t util.Tester, path, tempdir, name string) string { contents, err := Asset(path) if err != nil { if t == nil { log.Fatal(err) } else { t.Fatal(err) } } return util.CreateRestrictedFile(t, contents, tempdir, name) }
func makeClient(t util.Tester, str string) (*client.DB, *stop.Stopper) { stopper := stop.NewStopper() db, err := client.Open(stopper, str) if err != nil { t.Fatal(err) } return db, stopper }
// MakeClient creates a DB client for node 'i' using the cluster certs dir. func (l *LocalCluster) MakeClient(t util.Tester, node int) (*client.DB, *stop.Stopper) { stopper := stop.NewStopper() db, err := client.Open(stopper, "rpcs://"+security.NodeUser+"@"+ l.Nodes[node].Addr("").String()+ "?certs="+l.CertsDir) if err != nil { t.Fatal(err) } return db, stopper }
// MakeClient returns a client which is pointing at the node with the given // index. The given integer must be in the range [0,NumNodes()-1]. func (r *RemoteCluster) MakeClient(t util.Tester, i int) (*client.DB, *stop.Stopper) { stopper := stop.NewStopper() // TODO(tschottdorf,mberhault): TLS all the things! db, err := client.Open(stopper, "rpc://"+"root"+"@"+ util.EnsureHostPort(r.nodes[i].Addr)+ "?certs="+"certswhocares") if err != nil { t.Fatal(err) } return db, stopper }
// makeDBClientForUser creates a DB client for node 'i' and user 'user'. func makeDBClientForUser(t util.Tester, lc *LocalCluster, user string, node int) (*client.DB, *stop.Stopper) { stopper := stop.NewStopper() // We need to run with "InsecureSkipVerify" (set when Certs="" inside the http sender). // This is due to the fact that we're running outside docker, so we cannot use a fixed hostname // to reach the cluster. This in turn means that we do not have a verified server name in the certs. db, err := client.Open(stopper, "rpcs://"+user+"@"+ lc.Nodes[node].Addr("").String()+ "?certs="+lc.CertsDir) if err != nil { t.Fatal(err) } return db, stopper }
// Assert verifies that the cluster state is as expected (i.e. no unexpected // restarts or node deaths occurred). Tests can call this periodically to // ascertain cluster health. // TODO(tschottdorf): unimplemented when nodes are expected down. func (f *Farmer) Assert(t util.Tester) { for _, item := range []struct { typ string hosts []string }{ {"cockroach", f.Nodes()}, {"block_writer", f.Writers()}, } { for i, host := range item.hosts { out, _, err := f.execSupervisor(host, "status "+item.typ) if err != nil { t.Fatal(err) } if !strings.Contains(out, "RUNNING") { t.Fatalf("%s %d (%s) is down:\n%s", item.typ, i, host, out) } } } }
// AssertAndStop performs the same test as Assert but then proceeds to // dismantle the cluster. func (f *Farmer) AssertAndStop(t util.Tester) { if err := f.Destroy(); err != nil { t.Fatal(err) } }