diff --git a/examples/common/scripts/etcd-up.sh b/examples/common/scripts/etcd-up.sh index ac81c1fbd28..b0c0aba65a4 100755 --- a/examples/common/scripts/etcd-up.sh +++ b/examples/common/scripts/etcd-up.sh @@ -32,13 +32,13 @@ sleep 5 # And also add the CellInfo description for the cell. # If the node already exists, it's fine, means we used existing data. -echo "add $cell CellInfo" +echo "add ${cell} CellInfo" set +e -# shellcheck disable=SC2086 -vtctl $TOPOLOGY_FLAGS VtctldCommand AddCellInfo \ - --root /vitess/$cell \ +command vtctldclient --server bundled AddCellInfo \ + --bootstrap --topology-servers "${ETCD_SERVER}" \ + --root "/vitess/${cell}" \ --server-address "${ETCD_SERVER}" \ - $cell + "${cell}" set -e echo "etcd is running!" diff --git a/go/cmd/vtctldclient/command/cells.go b/go/cmd/vtctldclient/command/cells.go index 0a1e7ec727d..812f505a39a 100644 --- a/go/cmd/vtctldclient/command/cells.go +++ b/go/cmd/vtctldclient/command/cells.go @@ -17,12 +17,23 @@ limitations under the License. package command import ( + "context" "fmt" + "net" "strings" "github.com/spf13/cobra" "vitess.io/vitess/go/cmd/vtctldclient/cli" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver" + "vitess.io/vitess/go/vt/vtctl/localvtctldclient" + "vitess.io/vitess/go/vt/vtctl/vtctldclient" + "vitess.io/vitess/go/vt/vttablet/tmclient" + + _ "vitess.io/vitess/go/vt/topo/consultopo" + _ "vitess.io/vitess/go/vt/topo/etcd2topo" + _ "vitess.io/vitess/go/vt/topo/zk2topo" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" @@ -33,13 +44,20 @@ var ( AddCellInfo = &cobra.Command{ Use: "AddCellInfo --root [--server-address ] ", Short: "Registers a local topology service in a new cell by creating the CellInfo.", - Long: `Registers a local topology service in a new cell by creating the CellInfo + Long: fmt.Sprintf(`Registers a local topology service in a new cell by creating the CellInfo with the provided parameters. The address will be used to connect to the topology service, and Vitess data will -be stored starting at the provided root.`, +be stored starting at the provided root. + +If the --boostrap flag is specified then you must specify a value of '%s' for --server +and also provide at least one topology server endpoint using --topology-servers so that +we do not attempt to connect to a remote vtctld server and vtctldclient can instead +connect directly to the topology server in order to create the cell that you can then +start vtctld in.`, useBundledVtctld), DisableFlagsInUseLine: true, Args: cobra.ExactArgs(1), + PreRunE: validateAddCellInfoFlags, RunE: commandAddCellInfo, } // AddCellsAlias makes an AddCellsAlias gRPC call to a vtctld. @@ -119,21 +137,72 @@ If a value is empty, it is ignored.`, } ) -var addCellInfoOptions topodatapb.CellInfo +var addCellInfoOptions = struct { + cellInfo topodatapb.CellInfo + bootstrap bool + topoImpl string + topoRoot string + topoServers []string +}{} + +func validateAddCellInfoFlags(cmd *cobra.Command, args []string) error { + if addCellInfoOptions.bootstrap { + // You must at least specify one topology server to bootstrap the cluster. + if len(addCellInfoOptions.topoServers) == 0 { + return fmt.Errorf("you must specify at least one topology server to bootstrap the cluster") + } + for _, topoServer := range addCellInfoOptions.topoServers { + if _, _, err := net.SplitHostPort(topoServer); err != nil { + return fmt.Errorf("invalid topology server address (%s): %v", topoServer, err) + } + } + } + + return nil +} func commandAddCellInfo(cmd *cobra.Command, args []string) error { cli.FinishedParsing(cmd) - cell := cmd.Flags().Arg(0) - _, err := client.AddCellInfo(commandCtx, &vtctldatapb.AddCellInfoRequest{ + var ( + cell = cmd.Flags().Arg(0) + ctx = commandCtx + cancel context.CancelFunc + ) + + if addCellInfoOptions.bootstrap { + ts, err := topo.OpenServer(addCellInfoOptions.topoImpl, strings.Join(addCellInfoOptions.topoServers, ","), addCellInfoOptions.topoRoot) + if err != nil { + return fmt.Errorf("failed to connect to the topology server: %v", err) + } + defer ts.Close() + + // Use internal vtcltd server implementation. + // Register a nil grpc handler -- we will not use tmclient at all. + tmclient.RegisterTabletManagerClientFactory("grpc", func() tmclient.TabletManagerClient { + return nil + }) + vtctld := grpcvtctldserver.NewVtctldServer(ts) + localvtctldclient.SetServer(vtctld) + VtctldClientProtocol = "local" + client, err = vtctldclient.New(VtctldClientProtocol, "") + if err != nil { + return fmt.Errorf("failed to setup internal vtctld server: %v", err) + } + + ctx, cancel = context.WithTimeout(commandCtx, topo.RemoteOperationTimeout) + defer cancel() + } + + _, err := client.AddCellInfo(ctx, &vtctldatapb.AddCellInfoRequest{ Name: cell, - CellInfo: &addCellInfoOptions, + CellInfo: &addCellInfoOptions.cellInfo, }) if err != nil { return err } - fmt.Printf("Created cell: %s\n", cell) + return nil } @@ -288,8 +357,12 @@ func commandUpdateCellsAlias(cmd *cobra.Command, args []string) error { } func init() { - AddCellInfo.Flags().StringVarP(&addCellInfoOptions.ServerAddress, "server-address", "a", "", "The address the topology server will connect to for this cell.") - AddCellInfo.Flags().StringVarP(&addCellInfoOptions.Root, "root", "r", "", "The root path the topology server will use for this cell.") + AddCellInfo.Flags().StringVarP(&addCellInfoOptions.cellInfo.ServerAddress, "server-address", "a", "", "The address the topology server will connect to for this cell.") + AddCellInfo.Flags().StringVarP(&addCellInfoOptions.cellInfo.Root, "root", "r", "", "The root path the topology server will use for this cell.") + AddCellInfo.Flags().BoolVar(&addCellInfoOptions.bootstrap, "bootstrap", false, fmt.Sprintf("Should we create the cell directly in the topology server(s) to bootstrap the cluster so that we can then start a vtctld process in this cell. Note: you will also need to specify a value of '%s' for --server when using this flag.", useBundledVtctld)) + AddCellInfo.Flags().StringVar(&addCellInfoOptions.topoImpl, "topology-implementation", "etcd2", "The topology server implementation used.") + AddCellInfo.Flags().StringSliceVar(&addCellInfoOptions.topoServers, "topology-servers", nil, "The endpoints (host and port) to use when connecting directly to the topology server(s) when boostrapping a cluster.") + AddCellInfo.Flags().StringVar(&addCellInfoOptions.topoRoot, "topology-global-root", "/vitess/global", "The topology server root path to use.") AddCellInfo.MarkFlagRequired("root") Root.AddCommand(AddCellInfo) diff --git a/go/cmd/vtctldclient/command/root.go b/go/cmd/vtctldclient/command/root.go index 1194b49ec8f..8f28fba0911 100644 --- a/go/cmd/vtctldclient/command/root.go +++ b/go/cmd/vtctldclient/command/root.go @@ -44,6 +44,10 @@ import ( _ "vitess.io/vitess/go/cmd/vtctldclient/command/vreplication/workflow" ) +// The --server value if you want to use a "local" +// vtctld server. +const useBundledVtctld = "bundled" + var ( // VtctldClientProtocol is the protocol to use when creating the vtctldclient.VtctldClient. VtctldClientProtocol = "grpc" @@ -130,6 +134,9 @@ const skipClientCreationKey = "skip_client_creation" // getClientForCommand returns a vtctldclient.VtctldClient for a given command. // It validates that --server was passed to the CLI for commands that need it. func getClientForCommand(cmd *cobra.Command) (vtctldclient.VtctldClient, error) { + if server == useBundledVtctld { + return nil, nil // The command will need to later setup a local vtctld server and client. + } if skipStr, ok := cmd.Annotations[skipClientCreationKey]; ok { skipClientCreation, err := strconv.ParseBool(skipStr) if err != nil {