Skip to content

Commit

Permalink
Remove explicit "init" step to start a cluster
Browse files Browse the repository at this point in the history
Previously, you had to create a cluster by init'ing and starting first node:

./cockroach init --stores=...
./cockroach start --stores=... --gossip=self=

  and add new nodes with:

./cockroach start --stores=... --gossip=<node[,node,...]>

Now, a cluster is created just by running start:

./cockroach start --stores=...

  and add new nodes with:

./cockroach start --stores=... --join=<node[,node,...]>

Also cleaned up the gossip address storage code. It had gotten out of
whack somehow and wasn't storing addresses in the event that a node
had no bootstrapped stores on start.
  • Loading branch information
spencerkimball committed Feb 2, 2016
1 parent b3ca6e4 commit dac13d8
Show file tree
Hide file tree
Showing 24 changed files with 240 additions and 307 deletions.
20 changes: 6 additions & 14 deletions acceptance/cluster/localcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,17 +233,10 @@ func (l *LocalCluster) initCluster() {
}
}
create := func() (*Container, error) {
var entrypoint []string
if *cockroachImage == builderImage {
entrypoint = append(entrypoint, "/"+filepath.Base(*cockroachBinary))
} else if *cockroachEntry != "" {
entrypoint = append(entrypoint, *cockroachEntry)
}
return createContainer(l, dockerclient.ContainerConfig{
Image: *cockroachImage,
Volumes: vols,
Entrypoint: entrypoint,
Cmd: []string{"init", "--stores=ssd=" + dataStr(0, 0)},
Entrypoint: []string{"/bin/true"},
})
}
c, err := create()
Expand Down Expand Up @@ -351,11 +344,6 @@ func (l *LocalCluster) createNodeCerts() {
}

func (l *LocalCluster) startNode(i int) *Container {
gossipNodes := []string{}
for j := 0; j < l.numLocal; j++ {
gossipNodes = append(gossipNodes, fmt.Sprintf("%s:%d", nodeStr(j), cockroachPort))
}

var stores = "ssd=" + dataStr(i, 0)
for j := 1; j < l.numStores; j++ {
stores += ",ssd=" + dataStr(i, j)
Expand All @@ -367,9 +355,13 @@ func (l *LocalCluster) startNode(i int) *Container {
"--certs=/certs",
"--host=" + nodeStr(i),
"--port=" + fmt.Sprintf("%d", cockroachPort),
"--gossip=" + strings.Join(gossipNodes, ","),
"--scan-max-idle-time=200ms", // set low to speed up tests
}
// Append --join flag for all nodes except first.
if i > 0 {
cmd = append(cmd, fmt.Sprintf("--join=%s:%d", nodeStr(0), cockroachPort))
}

var locallogDir string
if len(l.logDir) > 0 {
dockerlogDir := "/logs/" + nodeStr(i)
Expand Down
9 changes: 5 additions & 4 deletions cli/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
package cli

import (
"fmt"

"github.com/cockroachdb/cockroach/security"
"github.com/cockroachdb/cockroach/util"

"github.com/spf13/cobra"
)
Expand All @@ -44,7 +45,7 @@ individual files in the directory specified by --certs (required).
// to their corresponding files.
func runCreateCACert(cmd *cobra.Command, args []string) error {
if err := security.RunCreateCACert(context.Certs, keySize); err != nil {
return util.Errorf("failed to generate CA certificate: %s", err)
return fmt.Errorf("failed to generate CA certificate: %s", err)
}
return nil
}
Expand All @@ -68,7 +69,7 @@ At least one host should be passed in (either IP address of dns name).
// to their corresponding files.
func runCreateNodeCert(cmd *cobra.Command, args []string) error {
if err := security.RunCreateNodeCert(context.Certs, keySize, args); err != nil {
return util.Errorf("failed to generate node certificate: %s", err)
return fmt.Errorf("failed to generate node certificate: %s", err)
}
return nil
}
Expand All @@ -95,7 +96,7 @@ func runCreateClientCert(cmd *cobra.Command, args []string) error {
return errMissingParams
}
if err := security.RunCreateClientCert(context.Certs, keySize, args[0]); err != nil {
return util.Errorf("failed to generate clent certificate: %s", err)
return fmt.Errorf("failed to generate clent certificate: %s", err)
}
return nil
}
Expand Down
1 change: 0 additions & 1 deletion cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ var cockroachCmd = &cobra.Command{

func init() {
cockroachCmd.AddCommand(
initCmd,
startCmd,
certCmd,
exterminateCmd,
Expand Down
3 changes: 1 addition & 2 deletions cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,8 +654,7 @@ func TestFlagUsage(t *testing.T) {
cockroach [command]
Available Commands:
init init new Cockroach cluster
start start a node by joining the gossip network
start start a node
cert create ca, node, and client certs
exterminate destroy all data held by the node
quit drain and shutdown node
Expand Down
34 changes: 10 additions & 24 deletions cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,16 @@ error occurs in any statement, the command exits with a
non-zero status code and further statements are not
executed. Only the results of the first SQL statement in each
positional argument are printed on the standard output.`),
"gossip": wrapText(`
A comma-separated list of gossip addresses or resolvers for gossip
bootstrap. Each item in the list has an optional type:
[type=]<address>. An unspecified type means ip address or dns.
Type is one of:`) + `
"join": wrapText(`
A comma-separated list of addresses to use when a new node is joining
an existing cluster. For the first node in a cluster, --join should
NOT be specified. Each address in the list has an optional type:
[type=]<address>. An unspecified type means ip address or dns. Type
is one of:
- tcp: (default if type is omitted): plain ip address or hostname.
- unix: unix socket
- lb: RPC load balancer forwarding to an arbitrary node
- http-lb: HTTP load balancer: we query
http(s)://<address>/_status/details/local
- self: for single node systems, specify --gossip=self (the
<address> is omitted).
`,
http(s)://<address>/_status/details/local
`),
"host": wrapText(`
Database server host.`),
"insecure": wrapText(`
Expand Down Expand Up @@ -193,14 +190,6 @@ func initFlags(ctx *Context) {
pf.Var(pflagValue{f.Value}, normalizeStdFlagName(f.Name), f.Usage)
})

{
f := initCmd.Flags()
f.StringVar(&ctx.Stores, "stores", ctx.Stores, usage("stores"))
if err := initCmd.MarkFlagRequired("stores"); err != nil {
panic(err)
}
}

{
f := startCmd.Flags()
f.BoolVar(&ctx.EphemeralSingleNode, "dev", ctx.EphemeralSingleNode, usage("dev"))
Expand All @@ -219,8 +208,8 @@ func initFlags(ctx *Context) {
f.StringVar(&ctx.Certs, "certs", ctx.Certs, usage("certs"))
f.BoolVar(&ctx.Insecure, "insecure", ctx.Insecure, usage("insecure"))

// Gossip flags.
f.StringVar(&ctx.GossipBootstrap, "gossip", ctx.GossipBootstrap, usage("gossip"))
// Cluster joining flags.
f.StringVar(&ctx.JoinUsing, "join", ctx.JoinUsing, usage("join"))

// KV flags.
f.BoolVar(&ctx.Linearizable, "linearizable", ctx.Linearizable, usage("linearizable"))
Expand All @@ -232,9 +221,6 @@ func initFlags(ctx *Context) {
f.DurationVar(&ctx.ScanMaxIdleTime, "scan-max-idle-time", ctx.ScanMaxIdleTime, usage("scan-max-idle-time"))
f.DurationVar(&ctx.TimeUntilStoreDead, "time-until-store-dead", ctx.TimeUntilStoreDead, usage("time-until-store-dead"))

if err := startCmd.MarkFlagRequired("gossip"); err != nil {
panic(err)
}
if err := startCmd.MarkFlagRequired("stores"); err != nil {
panic(err)
}
Expand Down
98 changes: 19 additions & 79 deletions cli/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import (
"github.com/cockroachdb/cockroach/util"
"github.com/cockroachdb/cockroach/util/log"
"github.com/cockroachdb/cockroach/util/stop"
"github.com/cockroachdb/cockroach/util/uuid"

"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -77,77 +76,29 @@ func getJSON(hostport, path string, v interface{}) error {
return util.GetJSON(httpClient, context.HTTPRequestScheme(), hostport, path, v)
}

// initCmd command initializes a new Cockroach cluster.
var initCmd = &cobra.Command{
Use: "init --stores=...",
Short: "init new Cockroach cluster",
Long: `
Initialize a new Cockroach cluster using the --stores flag to specify one or
more storage locations. The first of these storage locations is used to
bootstrap the first replica of the first range. If any of the storage locations
are already part of a pre-existing cluster, the bootstrap will fail.
`,
Example: ` cockroach init --stores=ssd=/mnt/ssd1,ssd=/mnt/ssd2`,
SilenceUsage: true,
RunE: runInit,
}

// runInit initializes the engine based on the first
// store. The bootstrap engine may not be an in-memory type.
func runInit(_ *cobra.Command, _ []string) error {
stopper := stop.NewStopper()
defer stopper.Stop()

// Default user for servers.
context.User = security.NodeUser

if err := context.InitStores(stopper); err != nil {
return util.Errorf("failed to initialize stores: %s", err)
}
if len(context.Engines) > 1 {
return util.Errorf("cannot bootstrap to more than one store")
}

return initCluster(stopper)
}

func initCluster(stopper *stop.Stopper) error {
// Generate a new UUID for cluster ID and bootstrap the cluster.
clusterID := uuid.NewUUID4().String()
if _, err := server.BootstrapCluster(clusterID, context.Engines, stopper); err != nil {
return util.Errorf("unable to bootstrap cluster: %s", err)
}

log.Infof("cockroach cluster %s has been initialized", clusterID)
return nil
}

// startCmd command starts nodes by joining the gossip network.
// startCmd starts a node by initializing the stores and joining
// the cluster.
var startCmd = &cobra.Command{
Use: "start",
Short: "start a node by joining the gossip network",
Short: "start a node",
Long: `
Start a Cockroach node by joining the gossip network and exporting key ranges
stored on physical device(s). The gossip network is joined by contacting one or
more well-known hosts specified by the --gossip flag. Every node should be run
with the same list of bootstrap hosts to guarantee a connected network. An
alternate approach is to use a single host for --gossip and round-robin DNS.
Start a CockroachDB node, which will export data from one or more
storage devices, specified via the --stores flag.
Each node exports data from one or more physical devices. These devices are
specified via the --stores flag. This is a comma-separated list of paths to
storage directories or for in-memory stores, the number of bytes. Although the
paths should be specified to correspond uniquely to physical devices, this
requirement isn't strictly enforced. See the --stores flag help description for
additional details.`,
Example: ` cockroach start --certs=<dir> --gossip=host1:port1[,...] --stores=ssd=/mnt/ssd1,...`,
If no cluster exists yet and this is the first node, no additional
flags are required. If the cluster already exists, and this node is
uninitialized, specify the --join flag to point to any healthy node
(or list of nodes) already part of the cluster.
`,
Example: ` cockroach start --certs=<dir> --stores=ssd=/mnt/ssd1,... [--join=host:port,[host:port]]`,
SilenceUsage: true,
RunE: runStart,
}

// runStart starts the cockroach node using --stores as the list of
// storage devices ("stores") on this machine and --gossip as the list
// of "well-known" hosts used to join this node to the cockroach
// cluster via the gossip network.
// storage devices ("stores") on this machine and --join as the list
// of other active nodes used to join this node to the cockroach
// cluster, if this is its first time connecting.
func runStart(_ *cobra.Command, _ []string) error {
info := util.GetBuildInfo()
log.Infof("[build] %s @ %s (%s)", info.Tag, info.Time, info.Vers)
Expand All @@ -160,36 +111,25 @@ func runStart(_ *cobra.Command, _ []string) error {
// for the default cluster-wide zone config.
config.DefaultZoneConfig.ReplicaAttrs = []roachpb.Attributes{{}}
context.Stores = "mem=1073741824"
context.GossipBootstrap = server.SelfGossipAddr
}

stopper := stop.NewStopper()
if err := context.InitStores(stopper); err != nil {
return util.Errorf("failed to initialize stores: %s", err)
}

if context.EphemeralSingleNode {
// A separate stopper for bootstrapping so that we can properly shutdown
// all of the stores.
initStopper := stop.NewStopper()
if err := initCluster(initStopper); err != nil {
return err
}
initStopper.Stop()
return fmt.Errorf("failed to initialize stores: %s", err)
}

if err := context.InitNode(); err != nil {
return util.Errorf("failed to initialize node: %s", err)
return fmt.Errorf("failed to initialize node: %s", err)
}

log.Info("starting cockroach node")
s, err := server.NewServer(&context.Context, stopper)
if err != nil {
return util.Errorf("failed to start Cockroach server: %s", err)
return fmt.Errorf("failed to start Cockroach server: %s", err)
}

if err := s.Start(false); err != nil {
return util.Errorf("cockroach server exited with error: %s", err)
if err := s.Start(); err != nil {
return fmt.Errorf("cockroach server exited with error: %s", err)
}

signalCh := make(chan os.Signal, 1)
Expand Down
1 change: 0 additions & 1 deletion client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,6 @@ func setupClientBenchData(useSSL bool, numVersions, numKeys int, b *testing.B) (

s := &server.TestServer{}
s.Ctx = server.NewTestContext()
s.SkipBootstrap = exists
if !useSSL {
s.Ctx.Insecure = true
}
Expand Down
2 changes: 1 addition & 1 deletion gossip/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func (c *client) handleGossip(g *Gossip, call *netrpc.Call) error {
return util.Errorf("received forward from node %d to %d (%s)", reply.NodeID, reply.AlternateNodeID, reply.AlternateAddr)
}

// If we have the sentinel gossip, we're considered connected.
// If we have the sentinel gossip we're considered connected.
g.checkHasConnected()

// Check whether this outgoing client is duplicating work already
Expand Down
Loading

0 comments on commit dac13d8

Please sign in to comment.