diff --git a/go/vt/topo/cell_info.go b/go/vt/topo/cell_info.go index c07b8db0b76..694429a9d6d 100644 --- a/go/vt/topo/cell_info.go +++ b/go/vt/topo/cell_info.go @@ -17,7 +17,6 @@ limitations under the License. package topo import ( - "fmt" "path" "github.com/golang/protobuf/proto" @@ -25,6 +24,7 @@ import ( "vitess.io/vitess/go/vt/vterrors" topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) // This file provides the utility methods to save / retrieve CellInfo @@ -135,18 +135,21 @@ func (ts *Server) UpdateCellInfoFields(ctx context.Context, cell string, update } // DeleteCellInfo deletes the specified CellInfo. -// We first make sure no Shard record points to the cell. -func (ts *Server) DeleteCellInfo(ctx context.Context, cell string) error { +// We first try to make sure no Shard record points to the cell, +// but we'll continue regardless if 'force' is true. +func (ts *Server) DeleteCellInfo(ctx context.Context, cell string, force bool) error { srvKeyspaces, err := ts.GetSrvKeyspaceNames(ctx, cell) switch { case err == nil: - if len(srvKeyspaces) != 0 { - return vterrors.Wrap(err, fmt.Sprintf("cell %v has serving keyspaces. Before deleting, delete keyspace with: DeleteKeyspace", cell)) + if len(srvKeyspaces) != 0 && !force { + return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "cell %v has serving keyspaces. Before deleting, delete keyspace with DeleteKeyspace, or use -force to continue anyway.", cell) } case IsErrType(err, NoNode): // Nothing to do. default: - return vterrors.Wrap(err, "GetSrvKeyspaceNames() failed") + if !force { + return vterrors.Wrap(err, "can't list SrvKeyspace entries in the cell; use -force flag to continue anyway (e.g. if cell-local topo was already permanently shut down)") + } } filePath := pathForCellInfo(cell) diff --git a/go/vt/topo/topotests/cell_info_test.go b/go/vt/topo/topotests/cell_info_test.go index 848e869fa35..44426b18ce0 100644 --- a/go/vt/topo/topotests/cell_info_test.go +++ b/go/vt/topo/topotests/cell_info_test.go @@ -99,11 +99,28 @@ func TestCellInfo(t *testing.T) { t.Fatalf("unexpected CellInfo: %v", ci) } - // Might as well test DeleteCellInfo. - if err := ts.DeleteCellInfo(ctx, newCell); err != nil { - t.Fatalf("DeleteCellInfo failed: %v", err) + // Add a record that should block CellInfo deletion for safety reasons. + if err := ts.UpdateSrvKeyspace(ctx, cell, "keyspace", &topodatapb.SrvKeyspace{}); err != nil { + t.Fatalf("UpdateSrvKeyspace failed: %v", err) } - if _, err := ts.GetCellInfo(ctx, newCell, true /*strongRead*/); !topo.IsErrType(err, topo.NoNode) { + srvKeyspaces, err := ts.GetSrvKeyspaceNames(ctx, cell) + if err != nil { + t.Fatalf("GetSrvKeyspaceNames failed: %v", err) + } + if len(srvKeyspaces) == 0 { + t.Fatalf("UpdateSrvKeyspace did not add SrvKeyspace.") + } + + // Try to delete without force; it should fail. + if err := ts.DeleteCellInfo(ctx, cell, false); err == nil { + t.Fatalf("DeleteCellInfo should have failed without -force") + } + + // Use the force. + if err := ts.DeleteCellInfo(ctx, cell, true); err != nil { + t.Fatalf("DeleteCellInfo failed even with -force: %v", err) + } + if _, err := ts.GetCellInfo(ctx, cell, true /*strongRead*/); !topo.IsErrType(err, topo.NoNode) { t.Fatalf("GetCellInfo(non-existing cell) failed: %v", err) } } diff --git a/go/vt/vtctl/cell_info.go b/go/vt/vtctl/cell_info.go index 7819cb8b45d..acee57789ee 100644 --- a/go/vt/vtctl/cell_info.go +++ b/go/vt/vtctl/cell_info.go @@ -51,7 +51,7 @@ func init() { addCommand(cellsGroupName, command{ "DeleteCellInfo", commandDeleteCellInfo, - "", + "[-force] ", "Deletes the CellInfo for the provided cell. The cell cannot be referenced by any Shard record."}) addCommand(cellsGroupName, command{ @@ -111,6 +111,7 @@ func commandUpdateCellInfo(ctx context.Context, wr *wrangler.Wrangler, subFlags } func commandDeleteCellInfo(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error { + force := subFlags.Bool("force", false, "Proceeds even if the cell's topology server cannot be reached. The assumption is that you turned down the entire cell, and just need to update the global topo data.") if err := subFlags.Parse(args); err != nil { return err } @@ -119,7 +120,7 @@ func commandDeleteCellInfo(ctx context.Context, wr *wrangler.Wrangler, subFlags } cell := subFlags.Arg(0) - return wr.TopoServer().DeleteCellInfo(ctx, cell) + return wr.TopoServer().DeleteCellInfo(ctx, cell, *force) } func commandGetCellInfoNames(ctx context.Context, wr *wrangler.Wrangler, subFlags *flag.FlagSet, args []string) error {