From 5fbbb8e069080ad3823639d0e8f4882e582ade7d Mon Sep 17 00:00:00 2001 From: "vitess-bot[bot]" <108069721+vitess-bot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:15:41 -0600 Subject: [PATCH] [release-21.0] Fix panic in vttablet when closing topo server twice (#17094) (#17122) Co-authored-by: vitess-bot[bot] <108069721+vitess-bot[bot]@users.noreply.github.com> --- go/cmd/vttablet/cli/cli.go | 2 -- go/cmd/vttablet/cli/cli_test.go | 58 +++++++++++++++++++++++++++++++++ go/vt/topo/server.go | 6 ++-- 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 go/cmd/vttablet/cli/cli_test.go diff --git a/go/cmd/vttablet/cli/cli.go b/go/cmd/vttablet/cli/cli.go index 3fb1e98877f..3b77b43d9cb 100644 --- a/go/cmd/vttablet/cli/cli.go +++ b/go/cmd/vttablet/cli/cli.go @@ -143,7 +143,6 @@ func run(cmd *cobra.Command, args []string) error { qsc, err := createTabletServer(ctx, env, config, ts, tabletAlias, srvTopoCounts) if err != nil { - ts.Close() return err } @@ -172,7 +171,6 @@ func run(cmd *cobra.Command, args []string) error { VDiffEngine: vdiff.NewEngine(ts, tablet, env.CollationEnv(), env.Parser()), } if err := tm.Start(tablet, config); err != nil { - ts.Close() return fmt.Errorf("failed to parse --tablet-path or initialize DB credentials: %w", err) } servenv.OnClose(func() { diff --git a/go/cmd/vttablet/cli/cli_test.go b/go/cmd/vttablet/cli/cli_test.go new file mode 100644 index 00000000000..c88ebd1b8ae --- /dev/null +++ b/go/cmd/vttablet/cli/cli_test.go @@ -0,0 +1,58 @@ +/* +Copyright 2024 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cli + +import ( + "context" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/memorytopo" +) + +// TestRunFailsToStartTabletManager tests the code path in 'run' where we fail to start the TabletManager +// this is done by starting vttablet without a cnf file but requesting it to restore from backup. +// When starting, the TabletManager checks if it needs to restore, in tm.handleRestore but this step will +// fail if we do not provide a cnf file and if the flag --restore_from_backup is provided. +func TestRunFailsToStartTabletManager(t *testing.T) { + ts, factory := memorytopo.NewServerAndFactory(context.Background(), "cell") + topo.RegisterFactory("test", factory) + + args := append([]string{}, os.Args...) + t.Cleanup(func() { + ts.Close() + tabletPath = "" + os.Args = append([]string{}, args...) + }) + + os.Args = []string{"vttablet", + "--topo_implementation", "test", "--topo_global_server_address", "localhost", "--topo_global_root", "cell", + "--db_host", "localhost", "--db_port", "3306", + "--tablet-path", "cell-1", "--init_keyspace", "ks", "--init_shard", "0", "--init_tablet_type", "replica", + "--restore_from_backup", + } + + // Creating and canceling the context so that pending tasks in tm_init gets canceled before we close the topo server + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + err := Main.ExecuteContext(ctx) + require.ErrorContains(t, err, "you cannot enable --restore_from_backup without a my.cnf file") +} diff --git a/go/vt/topo/server.go b/go/vt/topo/server.go index 23b7be6cfa1..a0dba42006d 100644 --- a/go/vt/topo/server.go +++ b/go/vt/topo/server.go @@ -343,8 +343,10 @@ func GetAliasByCell(ctx context.Context, ts *Server, cell string) string { // Close will close all connections to underlying topo Server. // It will nil all member variables, so any further access will panic. func (ts *Server) Close() { - ts.globalCell.Close() - if ts.globalReadOnlyCell != ts.globalCell { + if ts.globalCell != nil { + ts.globalCell.Close() + } + if ts.globalReadOnlyCell != nil && ts.globalReadOnlyCell != ts.globalCell { ts.globalReadOnlyCell.Close() } ts.globalCell = nil