Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli,server: add --virtualized[-empty] flags to init #120813

Merged
merged 1 commit into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions pkg/acceptance/generated_cli_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions pkg/cli/cliflags/flags_mt.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,13 @@ listeners, if the headers are allowed.`,
Name: "base-dir",
Description: "If set, the tenant processes will use it as a store location.",
}

Virtualized = FlagInfo{
Name: "virtualized",
Description: "If set, the cluster will be initialized as a virtualized cluster.",
}
VirtualizedEmpty = FlagInfo{
Name: "virtualized-empty",
Description: "If set, the cluster will be initialized as a virtualized cluster without an application cluster.",
}
)
6 changes: 6 additions & 0 deletions pkg/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,12 @@ func init() {
_ = f.MarkHidden(cliflags.ApplicationInternalRPCPortRange.Name)
}

{
f := initCmd.Flags()
cliflagcfg.BoolFlag(f, &initCmdOptions.virtualized, cliflags.Virtualized)
cliflagcfg.BoolFlag(f, &initCmdOptions.virtualizedEmpty, cliflags.VirtualizedEmpty)
}

// Multi-tenancy start-sql command flags.
{
f := mtStartSQLCmd.Flags()
Expand Down
20 changes: 19 additions & 1 deletion pkg/cli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,16 @@ you would use for the sql command).
RunE: clierrorplus.MaybeDecorateError(runInit),
}

var initCmdOptions = struct {
virtualized bool
virtualizedEmpty bool
}{}

func runInit(cmd *cobra.Command, args []string) error {
if initCmdOptions.virtualized && initCmdOptions.virtualizedEmpty {
return errors.Newf("only one of --virtualized and --virtualized-empty can be used")
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

Expand All @@ -52,9 +61,18 @@ func runInit(cmd *cobra.Command, args []string) error {
}
defer finish()

typ := serverpb.InitType_NONE
if initCmdOptions.virtualized {
typ = serverpb.InitType_VIRTUALIZED
} else if initCmdOptions.virtualizedEmpty {
typ = serverpb.InitType_VIRTUALIZED_EMPTY
}

// Actually perform cluster initialization.
c := serverpb.NewInitClient(conn)
if _, err = c.Bootstrap(ctx, &serverpb.BootstrapRequest{}); err != nil {
if _, err = c.Bootstrap(ctx, &serverpb.BootstrapRequest{
InitType: typ,
}); err != nil {
return err
}

Expand Down
28 changes: 28 additions & 0 deletions pkg/cli/interactive_tests/test_init_virtualized_command.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#! /usr/bin/env expect -f
#
source [file join [file dirnam $argv0] common.tcl]

# Start a server with a --join flag so the init command is required
# (even though we have no intention of starting a second node). Note
# that unlike other expect-based tests, this one doesn't use a fifo
# for --pid_file because we don't want reads from that fifo to change
# the outcome.
system "$argv start --insecure --pid-file=server_pid -s=path=logs/db --listen-addr=localhost --background --join=localhost:26258 >>logs/expect-cmd.log 2>&1"

start_test "Check that the server has informed us and the log file that it was ready before forking off in the background"
system "grep -q 'initial startup completed' logs/db/logs/cockroach.log"
system "grep -q 'will now attempt to join a running cluster, or wait' logs/db/logs/cockroach.log"
end_test

start_test "Check that init --virtualized creates an application virtual cluster"

system "$argv init --insecure --host=localhost --virtualized"

# Start a shell and expect that we end up inside a secondary virtual
# cluster.
spawn $argv sql --no-line-editor
send "SELECT crdb_internal.pretty_key(crdb_internal.table_span(1)\[1\], 0);\r"
eexpect "/3/Table/1"

stop_server $argv
end_test
4 changes: 3 additions & 1 deletion pkg/server/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ type initState struct {
initializedEngines []storage.Engine
uninitializedEngines []storage.Engine
initialSettingsKVs []roachpb.KeyValue
initType serverpb.InitType
}

// bootstrapped is a shorthand to check if there exists at least one initialized
Expand Down Expand Up @@ -336,7 +337,7 @@ var errInternalBootstrapError = errors.New("unable to bootstrap due to internal
// nodes. In that case, they end up with more than one cluster, and nodes
// panicking or refusing to connect to each other.
func (s *initServer) Bootstrap(
ctx context.Context, _ *serverpb.BootstrapRequest,
ctx context.Context, r *serverpb.BootstrapRequest,
) (*serverpb.BootstrapResponse, error) {
// Bootstrap() only responds once. Everyone else gets an error, either
// ErrClusterInitialized (in the success case) or errInternalBootstrapError.
Expand All @@ -358,6 +359,7 @@ func (s *initServer) Bootstrap(
s.mu.rejectErr = errInternalBootstrapError
return nil, s.mu.rejectErr
}
state.initType = r.InitType

// We've successfully bootstrapped (we've initialized at least one engine).
// We mark ourselves as bootstrapped to prevent future bootstrap attempts.
Expand Down
56 changes: 56 additions & 0 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2084,6 +2084,10 @@ func (s *topLevelServer) PreStart(ctx context.Context) error {
s.appRegistry,
)

if err := s.runIdempontentSQLForInitType(ctx, state.initType); err != nil {
return err
}

// Start the job scheduler now that the SQL Server and
// external storage is initialized.
if err := s.initJobScheduler(ctx); err != nil {
Expand Down Expand Up @@ -2229,6 +2233,58 @@ func (s *topLevelServer) initJobScheduler(ctx context.Context) error {
return nil
}

// runIdempontentSQLForInitType runs one-time initialization steps via
// SQL based on the given InitType.
func (s *topLevelServer) runIdempontentSQLForInitType(
ctx context.Context, typ serverpb.InitType,
) error {
if typ == serverpb.InitType_NONE || typ == serverpb.InitType_DEFAULT {
return nil
}

initAttempt := func() error {
const defaultApplicationClusterName = "application"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bikeshed: we can't use default since it doesn't work as a bare keyword in places where we use a tenant name, but is there anything shorter we could use?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main ?

switch typ {
case serverpb.InitType_VIRTUALIZED:
ie := s.sqlServer.execCfg.InternalDB.Executor()
_, err := ie.Exec(ctx, "init-create-app-tenant", nil, /* txn */
"CREATE VIRTUAL CLUSTER IF NOT EXISTS $1", defaultApplicationClusterName)
if err != nil {
return err
}
_, err = ie.Exec(ctx, "init-default-app-tenant", nil, /* txn */
"ALTER VIRTUAL CLUSTER $1 START SERVICE SHARED", defaultApplicationClusterName)
if err != nil {
return err
}
fallthrough
case serverpb.InitType_VIRTUALIZED_EMPTY:
ie := s.sqlServer.execCfg.InternalDB.Executor()
_, err := ie.Exec(ctx, "init-default-target-cluster-setting", nil, /* txn */
"SET CLUSTER SETTING server.controller.default_target_cluster = $1", defaultApplicationClusterName)
if err != nil {
return err
}
default:
return errors.Errorf("unknown bootstrap init type: %d", typ)
}
return nil
}

rOpts := retry.Options{
MaxBackoff: 10 * time.Second,
InitialBackoff: time.Second,
}
for r := retry.StartWithCtx(ctx, rOpts); r.Next(); {
if err := initAttempt(); err != nil {
log.Errorf(ctx, "cluster initialization attempt failed: %s", err.Error())
continue
}
return nil
}
return errors.Errorf("cluster initialization failed; cluster may need to be manually configured")
}

// AcceptClients starts listening for incoming SQL clients over the network.
// This mirrors the implementation of (*SQLServerWrapper).AcceptClients.
// TODO(knz): Find a way to implement this method only once for both.
Expand Down
11 changes: 10 additions & 1 deletion pkg/server/serverpb/init.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@ syntax = "proto3";
package cockroach.server.serverpb;
option go_package = "github.com/cockroachdb/cockroach/pkg/server/serverpb";

message BootstrapRequest { }
message BootstrapRequest {
InitType init_type = 1;
}

enum InitType {
DEFAULT = 0;
NONE = 1;
VIRTUALIZED_EMPTY = 2;
VIRTUALIZED = 3;
}
message BootstrapResponse { }

service Init {
Expand Down