vitess.io/vitess@v0.16.2/go/cmd/vtctldclient/command/shards.go (about)

     1  /*
     2  Copyright 2021 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package command
    18  
    19  import (
    20  	"fmt"
    21  	"strconv"
    22  
    23  	"github.com/spf13/cobra"
    24  
    25  	"vitess.io/vitess/go/cmd/vtctldclient/cli"
    26  	"vitess.io/vitess/go/vt/key"
    27  	"vitess.io/vitess/go/vt/topo"
    28  	"vitess.io/vitess/go/vt/topo/topoproto"
    29  
    30  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    31  	vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata"
    32  )
    33  
    34  var (
    35  	// CreateShard makes a CreateShard gRPC request to a vtctld.
    36  	CreateShard = &cobra.Command{
    37  		Use:                   "CreateShard [--force|-f] [--include-parent|-p] <keyspace/shard>",
    38  		Short:                 "Creates the specified shard in the topology.",
    39  		DisableFlagsInUseLine: true,
    40  		Args:                  cobra.ExactArgs(1),
    41  		RunE:                  commandCreateShard,
    42  	}
    43  	// DeleteShards makes a DeleteShards gRPC request to a vtctld.
    44  	DeleteShards = &cobra.Command{
    45  		Use:   "DeleteShards [--recursive|-r] [--even-if-serving] [--force|-f] <keyspace/shard> [<keyspace/shard> ...]",
    46  		Short: "Deletes the specified shards from the topology.",
    47  		Long: `Deletes the specified shards from the topology.
    48  
    49  In recursive mode, it also deletes all tablets belonging to the shard.
    50  Otherwise, the shard must be empty (have no tablets) or returns an error for
    51  that shard.`,
    52  		DisableFlagsInUseLine: true,
    53  		Args:                  cobra.MinimumNArgs(1),
    54  		RunE:                  commandDeleteShards,
    55  	}
    56  	// GenerateShardRanges outputs a set of shard ranges assuming a (mostly)
    57  	// equal distribution of N shards.
    58  	GenerateShardRanges = &cobra.Command{
    59  		Use:                   "GenerateShardRanges <num_shards>",
    60  		Short:                 "Print a set of shard ranges assuming a keyspace with N shards.",
    61  		DisableFlagsInUseLine: true,
    62  		Args:                  cobra.ExactArgs(1),
    63  		RunE: func(cmd *cobra.Command, args []string) error {
    64  			n, err := strconv.ParseInt(cmd.Flags().Arg(0), 10, 64)
    65  			if err != nil {
    66  				return err
    67  			}
    68  
    69  			cli.FinishedParsing(cmd)
    70  
    71  			shards, err := key.GenerateShardRanges(int(n))
    72  			if err != nil {
    73  				return err
    74  			}
    75  
    76  			data, err := cli.MarshalJSON(shards)
    77  			if err != nil {
    78  				return err
    79  			}
    80  
    81  			fmt.Printf("%s\n", data)
    82  			return nil
    83  		},
    84  		Annotations: map[string]string{
    85  			skipClientCreationKey: "true",
    86  		},
    87  	}
    88  	// GetShard makes a GetShard gRPC request to a vtctld.
    89  	GetShard = &cobra.Command{
    90  		Use:                   "GetShard <keyspace/shard>",
    91  		Short:                 "Returns information about a shard in the topology.",
    92  		DisableFlagsInUseLine: true,
    93  		Args:                  cobra.ExactArgs(1),
    94  		RunE:                  commandGetShard,
    95  	}
    96  	// RemoveShardCell makes a RemoveShardCell gRPC request to a vtctld.
    97  	RemoveShardCell = &cobra.Command{
    98  		Use:                   "RemoveShardCell [--force|-f] [--recursive|-r] <keyspace/shard> <cell>",
    99  		Short:                 "Remove the specified cell from the specified shard's Cells list.",
   100  		DisableFlagsInUseLine: true,
   101  		Args:                  cobra.ExactArgs(2),
   102  		RunE:                  commandRemoveShardCell,
   103  	}
   104  	// SetShardIsPrimaryServing makes a SetShardIsPrimaryServing gRPC call to a
   105  	// vtctld.
   106  	SetShardIsPrimaryServing = &cobra.Command{
   107  		Use:                   "SetShardIsPrimaryServing <keyspace/shard> <true/false>",
   108  		Short:                 "Add or remove a shard from serving. This is meant as an emergency function. It does not rebuild any serving graphs; i.e. it does not run `RebuildKeyspaceGraph`.",
   109  		DisableFlagsInUseLine: true,
   110  		Args:                  cobra.ExactArgs(2),
   111  		RunE:                  commandSetShardIsPrimaryServing,
   112  	}
   113  	// SetShardTabletControl makes a SetShardTabletControl gRPC call to a vtctld.
   114  	SetShardTabletControl = &cobra.Command{
   115  		Use:   "SetShardTabletControl [--cells=c1,c2...] [--denied-tables=t1,t2,...] [--remove] [--disable-query-service[=0|false]] <keyspace/shard> <tablet_type>",
   116  		Short: "Sets the TabletControl record for a shard and tablet type. Only use this for an emergency fix or after a finished MoveTables.",
   117  		Long: `Sets the TabletControl record for a shard and tablet type.
   118  
   119  Only use this for an emergency fix or after a finished MoveTables.
   120  
   121  Always specify the denied-tables flag for MoveTables, but never for Reshard operations.
   122  
   123  To set the DisableQueryService flag, keep denied-tables empty, and set --disable-query-service
   124  to true or false. This is useful to fix Reshard operations gone wrong.
   125  
   126  To change the list of denied tables, specify the --denied-tables parameter with
   127  the new list. This is useful to fix tables that are being blocked after a
   128  MoveTables operation.
   129  
   130  To remove the ShardTabletControl record entirely, use the --remove flag. This is
   131  useful after a MoveTables has finished to remove serving restrictions.`,
   132  		DisableFlagsInUseLine: true,
   133  		Args:                  cobra.ExactArgs(2),
   134  		RunE:                  commandSetShardTabletControl,
   135  	}
   136  	// ShardReplicationAdd makse a ShardReplicationAdd gRPC request to a vtctld.
   137  	ShardReplicationAdd = &cobra.Command{
   138  		Use:                   "ShardReplicationAdd <keyspace/shard> <tablet alias>",
   139  		Short:                 "Adds an entry to the replication graph in the given cell.",
   140  		DisableFlagsInUseLine: true,
   141  		Args:                  cobra.ExactArgs(2),
   142  		RunE:                  commandShardReplicationAdd,
   143  		Hidden:                true,
   144  	}
   145  	// ShardReplicationFix makes a ShardReplicationFix gRPC request to a vtctld.
   146  	ShardReplicationFix = &cobra.Command{
   147  		Use:                   "ShardReplicationFix <cell> <keyspace/shard>",
   148  		Short:                 "Walks through a ShardReplication object and fixes the first error encountered.",
   149  		DisableFlagsInUseLine: true,
   150  		Args:                  cobra.ExactArgs(2),
   151  		RunE:                  commandShardReplicationFix,
   152  	}
   153  	// ShardReplicationPositions makes a ShardReplicationPositions gRPC request
   154  	// to a vtctld.
   155  	ShardReplicationPositions = &cobra.Command{
   156  		Use: "ShardReplicationPositions <keyspace/shard>",
   157  		Long: `Shows the replication status of each tablet in the shard graph.
   158  Output is sorted by tablet type, then replication position.
   159  Use ctrl-C to interrupt the command and see partial results if needed.`,
   160  		DisableFlagsInUseLine: true,
   161  		Args:                  cobra.ExactArgs(1),
   162  		RunE:                  commandShardReplicationPositions,
   163  	}
   164  	// ShardReplicationRemove makse a ShardReplicationRemove gRPC request to a vtctld.
   165  	ShardReplicationRemove = &cobra.Command{
   166  		Use:                   "ShardReplicationRemove <keyspace/shard> <tablet alias>",
   167  		Short:                 "Removes an entry from the replication graph in the given cell.",
   168  		DisableFlagsInUseLine: true,
   169  		Args:                  cobra.ExactArgs(2),
   170  		RunE:                  commandShardReplicationRemove,
   171  		Hidden:                true,
   172  	}
   173  	// SourceShardAdd makes a SourceShardAdd gRPC request to a vtctld.
   174  	SourceShardAdd = &cobra.Command{
   175  		Use:                   "SourceShardAdd [--key-range <keyrange>] [--tables <table1,table2,...> [--tables <table3,...>]...] <keyspace/shard> <uid> <source keyspace/shard>",
   176  		Short:                 "Adds the SourceShard record with the provided index for emergencies only. It does not call RefreshState for the shard primary.",
   177  		DisableFlagsInUseLine: true,
   178  		Args:                  cobra.ExactArgs(3),
   179  		RunE:                  commandSourceShardAdd,
   180  	}
   181  	// SourceShardDelete makes a SourceShardDelete gRPC request to a vtctld.
   182  	SourceShardDelete = &cobra.Command{
   183  		Use:                   "SourceShardDelete <keyspace/shard> <uid>",
   184  		Short:                 "Deletes the SourceShard record with the provided index. This should only be used for emergency cleanup. It does not call RefreshState for the shard primary.",
   185  		DisableFlagsInUseLine: true,
   186  		Args:                  cobra.ExactArgs(2),
   187  		RunE:                  commandSourceShardDelete,
   188  	}
   189  
   190  	// ValidateVersionShard makes a ValidateVersionShard gRPC request to a vtctld.
   191  	ValidateVersionShard = &cobra.Command{
   192  		Use:                   "ValidateVersionShard <keyspace/shard>",
   193  		Short:                 "Validates that the version on the primary matches all of the replicas.",
   194  		DisableFlagsInUseLine: true,
   195  		Args:                  cobra.ExactArgs(1),
   196  		RunE:                  commandValidateVersionShard,
   197  	}
   198  )
   199  
   200  var createShardOptions = struct {
   201  	Force         bool
   202  	IncludeParent bool
   203  }{}
   204  
   205  func commandCreateShard(cmd *cobra.Command, args []string) error {
   206  	keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   207  	if err != nil {
   208  		return err
   209  	}
   210  
   211  	cli.FinishedParsing(cmd)
   212  
   213  	resp, err := client.CreateShard(commandCtx, &vtctldatapb.CreateShardRequest{
   214  		Keyspace:      keyspace,
   215  		ShardName:     shard,
   216  		Force:         createShardOptions.Force,
   217  		IncludeParent: createShardOptions.IncludeParent,
   218  	})
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	data, err := cli.MarshalJSON(resp)
   224  	if err != nil {
   225  		return err
   226  	}
   227  
   228  	fmt.Printf("%s\n", data)
   229  
   230  	return nil
   231  }
   232  
   233  var deleteShardsOptions = struct {
   234  	Recursive     bool
   235  	EvenIfServing bool
   236  	Force         bool
   237  }{}
   238  
   239  func commandDeleteShards(cmd *cobra.Command, args []string) error {
   240  	shards, err := cli.ParseKeyspaceShards(cmd.Flags().Args())
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	cli.FinishedParsing(cmd)
   246  
   247  	_, err = client.DeleteShards(commandCtx, &vtctldatapb.DeleteShardsRequest{
   248  		Shards:        shards,
   249  		EvenIfServing: deleteShardsOptions.EvenIfServing,
   250  		Recursive:     deleteShardsOptions.Recursive,
   251  		Force:         deleteShardsOptions.Force,
   252  	})
   253  
   254  	if err != nil {
   255  		return fmt.Errorf("%w: while deleting %d shards; please inspect the topo", err, len(shards))
   256  	}
   257  
   258  	fmt.Printf("Successfully deleted %d shards\n", len(shards))
   259  
   260  	return nil
   261  }
   262  
   263  func commandGetShard(cmd *cobra.Command, args []string) error {
   264  	keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   265  	if err != nil {
   266  		return err
   267  	}
   268  
   269  	cli.FinishedParsing(cmd)
   270  
   271  	resp, err := client.GetShard(commandCtx, &vtctldatapb.GetShardRequest{
   272  		Keyspace:  keyspace,
   273  		ShardName: shard,
   274  	})
   275  	if err != nil {
   276  		return err
   277  	}
   278  
   279  	data, err := cli.MarshalJSON(resp.Shard)
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	fmt.Printf("%s\n", data)
   285  
   286  	return nil
   287  }
   288  
   289  var removeShardCellOptions = struct {
   290  	Force     bool
   291  	Recursive bool
   292  }{}
   293  
   294  func commandRemoveShardCell(cmd *cobra.Command, args []string) error {
   295  	keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   296  	if err != nil {
   297  		return err
   298  	}
   299  
   300  	cli.FinishedParsing(cmd)
   301  
   302  	cell := cmd.Flags().Arg(1)
   303  
   304  	_, err = client.RemoveShardCell(commandCtx, &vtctldatapb.RemoveShardCellRequest{
   305  		Keyspace:  keyspace,
   306  		ShardName: shard,
   307  		Cell:      cell,
   308  		Force:     removeShardCellOptions.Force,
   309  		Recursive: removeShardCellOptions.Recursive,
   310  	})
   311  
   312  	if err != nil {
   313  		return err
   314  	}
   315  
   316  	fmt.Printf("Successfully removed cell %v from shard %s/%s\n", cell, keyspace, shard)
   317  
   318  	return nil
   319  }
   320  
   321  func commandSetShardIsPrimaryServing(cmd *cobra.Command, args []string) error {
   322  	keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   323  	if err != nil {
   324  		return fmt.Errorf("cannot parse keyspace/shard: %w", err)
   325  	}
   326  
   327  	isServing, err := strconv.ParseBool(cmd.Flags().Arg(1))
   328  	if err != nil {
   329  		return fmt.Errorf("cannot parse is_serving as bool: %w", err)
   330  	}
   331  
   332  	cli.FinishedParsing(cmd)
   333  
   334  	resp, err := client.SetShardIsPrimaryServing(commandCtx, &vtctldatapb.SetShardIsPrimaryServingRequest{
   335  		Keyspace:  keyspace,
   336  		Shard:     shard,
   337  		IsServing: isServing,
   338  	})
   339  	if err != nil {
   340  		return err
   341  	}
   342  
   343  	data, err := cli.MarshalJSON(resp.Shard)
   344  	if err != nil {
   345  		return err
   346  	}
   347  
   348  	fmt.Printf("%s\n", data)
   349  	return nil
   350  }
   351  
   352  var setShardTabletControlOptions = struct {
   353  	Cells               []string
   354  	DeniedTables        []string
   355  	Remove              bool
   356  	DisableQueryService bool
   357  }{}
   358  
   359  func commandSetShardTabletControl(cmd *cobra.Command, args []string) error {
   360  	keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   361  	if err != nil {
   362  		return fmt.Errorf("cannot parse keyspace/shard: %w", err)
   363  	}
   364  
   365  	tabletType, err := topoproto.ParseTabletType(cmd.Flags().Arg(1))
   366  	if err != nil {
   367  		return fmt.Errorf("cannot parse tablet type: %w", err)
   368  	}
   369  
   370  	cli.FinishedParsing(cmd)
   371  
   372  	resp, err := client.SetShardTabletControl(commandCtx, &vtctldatapb.SetShardTabletControlRequest{
   373  		Keyspace:            keyspace,
   374  		Shard:               shard,
   375  		TabletType:          tabletType,
   376  		Cells:               setShardTabletControlOptions.Cells,
   377  		DeniedTables:        setShardTabletControlOptions.DeniedTables,
   378  		Remove:              setShardTabletControlOptions.Remove,
   379  		DisableQueryService: setShardTabletControlOptions.DisableQueryService,
   380  	})
   381  	if err != nil {
   382  		return err
   383  	}
   384  
   385  	data, err := cli.MarshalJSON(resp.Shard)
   386  	if err != nil {
   387  		return err
   388  	}
   389  
   390  	fmt.Printf("%s\n", data)
   391  	return nil
   392  }
   393  
   394  func commandShardReplicationAdd(cmd *cobra.Command, args []string) error {
   395  	keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   396  	if err != nil {
   397  		return err
   398  	}
   399  
   400  	tabletAlias, err := topoproto.ParseTabletAlias(cmd.Flags().Arg(1))
   401  	if err != nil {
   402  		return err
   403  	}
   404  
   405  	cli.FinishedParsing(cmd)
   406  
   407  	_, err = client.ShardReplicationAdd(commandCtx, &vtctldatapb.ShardReplicationAddRequest{
   408  		Keyspace:    keyspace,
   409  		Shard:       shard,
   410  		TabletAlias: tabletAlias,
   411  	})
   412  	return err
   413  }
   414  
   415  func commandShardReplicationFix(cmd *cobra.Command, args []string) error {
   416  	cell := cmd.Flags().Arg(0)
   417  	keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(1))
   418  	if err != nil {
   419  		return err
   420  	}
   421  
   422  	cli.FinishedParsing(cmd)
   423  
   424  	resp, err := client.ShardReplicationFix(commandCtx, &vtctldatapb.ShardReplicationFixRequest{
   425  		Keyspace: keyspace,
   426  		Shard:    shard,
   427  		Cell:     cell,
   428  	})
   429  	if err != nil {
   430  		return err
   431  	}
   432  
   433  	switch resp.Error {
   434  	case nil:
   435  		fmt.Println("All nodes in the replication graph are valid.")
   436  	default:
   437  		fmt.Printf("%s has been fixed for %s.\n", topoproto.ShardReplicationErrorTypeString(resp.Error.Type), topoproto.TabletAliasString(resp.Error.TabletAlias))
   438  	}
   439  
   440  	return nil
   441  }
   442  
   443  func commandShardReplicationPositions(cmd *cobra.Command, args []string) error {
   444  	keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   445  	if err != nil {
   446  		return err
   447  	}
   448  
   449  	cli.FinishedParsing(cmd)
   450  
   451  	resp, err := client.ShardReplicationPositions(commandCtx, &vtctldatapb.ShardReplicationPositionsRequest{
   452  		Keyspace: keyspace,
   453  		Shard:    shard,
   454  	})
   455  	if err != nil {
   456  		return err
   457  	}
   458  
   459  	for _, rt := range cli.SortedReplicatingTablets(resp.TabletMap, resp.ReplicationStatuses) {
   460  		var line string
   461  
   462  		switch rt.Status {
   463  		case nil:
   464  			line = cli.MarshalTabletAWK(rt.Tablet) + "<err> <err> <err>"
   465  		default:
   466  			line = cli.MarshalTabletAWK(rt.Tablet) + fmt.Sprintf(" %v %v", rt.Status.Position, rt.Status.ReplicationLagSeconds)
   467  		}
   468  
   469  		fmt.Println(line)
   470  	}
   471  
   472  	return nil
   473  }
   474  
   475  func commandShardReplicationRemove(cmd *cobra.Command, args []string) error {
   476  	keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   477  	if err != nil {
   478  		return err
   479  	}
   480  
   481  	tabletAlias, err := topoproto.ParseTabletAlias(cmd.Flags().Arg(1))
   482  	if err != nil {
   483  		return err
   484  	}
   485  
   486  	cli.FinishedParsing(cmd)
   487  
   488  	_, err = client.ShardReplicationRemove(commandCtx, &vtctldatapb.ShardReplicationRemoveRequest{
   489  		Keyspace:    keyspace,
   490  		Shard:       shard,
   491  		TabletAlias: tabletAlias,
   492  	})
   493  	return err
   494  }
   495  
   496  var sourceShardAddOptions = struct {
   497  	KeyRangeStr string
   498  	Tables      []string
   499  }{}
   500  
   501  func commandSourceShardAdd(cmd *cobra.Command, args []string) error {
   502  	ks, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   503  	if err != nil {
   504  		return err
   505  	}
   506  
   507  	uid, err := strconv.ParseUint(cmd.Flags().Arg(1), 10, 32)
   508  	if err != nil {
   509  		return fmt.Errorf("Failed to parse SourceShard uid: %w", err) // nolint
   510  	}
   511  
   512  	sks, sshard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(2))
   513  	if err != nil {
   514  		return err
   515  	}
   516  
   517  	var kr *topodatapb.KeyRange
   518  	if sourceShardAddOptions.KeyRangeStr != "" {
   519  		_, kr, err = topo.ValidateShardName(sourceShardAddOptions.KeyRangeStr)
   520  		if err != nil {
   521  			return fmt.Errorf("Invalid keyrange: %w", err)
   522  		}
   523  	}
   524  
   525  	cli.FinishedParsing(cmd)
   526  
   527  	resp, err := client.SourceShardAdd(commandCtx, &vtctldatapb.SourceShardAddRequest{
   528  		Keyspace:       ks,
   529  		Shard:          shard,
   530  		Uid:            uint32(uid),
   531  		SourceKeyspace: sks,
   532  		SourceShard:    sshard,
   533  		KeyRange:       kr,
   534  		Tables:         sourceShardAddOptions.Tables,
   535  	})
   536  	if err != nil {
   537  		return err
   538  	}
   539  
   540  	switch resp.Shard {
   541  	case nil:
   542  		fmt.Printf("SourceShard with uid %v already exists for %s/%s, not adding it.\n", uid, ks, shard)
   543  	default:
   544  		data, err := cli.MarshalJSON(resp.Shard)
   545  		if err != nil {
   546  			return err
   547  		}
   548  
   549  		fmt.Printf("Updated shard record:\n%s\n", data)
   550  	}
   551  
   552  	return nil
   553  }
   554  
   555  func commandSourceShardDelete(cmd *cobra.Command, args []string) error {
   556  	ks, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   557  	if err != nil {
   558  		return err
   559  	}
   560  
   561  	uid, err := strconv.ParseUint(cmd.Flags().Arg(1), 10, 32)
   562  	if err != nil {
   563  		return fmt.Errorf("Failed to parse SourceShard uid: %w", err) // nolint
   564  	}
   565  
   566  	cli.FinishedParsing(cmd)
   567  
   568  	resp, err := client.SourceShardDelete(commandCtx, &vtctldatapb.SourceShardDeleteRequest{
   569  		Keyspace: ks,
   570  		Shard:    shard,
   571  		Uid:      uint32(uid),
   572  	})
   573  	if err != nil {
   574  		return err
   575  	}
   576  
   577  	switch resp.Shard {
   578  	case nil:
   579  		fmt.Printf("No SourceShard with uid %v.\n", uid)
   580  	default:
   581  		data, err := cli.MarshalJSON(resp.Shard)
   582  		if err != nil {
   583  			return err
   584  		}
   585  
   586  		fmt.Printf("Updated shard record:\n%s\n", data)
   587  	}
   588  	return nil
   589  }
   590  
   591  func commandValidateVersionShard(cmd *cobra.Command, args []string) error {
   592  	keyspace, shard, err := topoproto.ParseKeyspaceShard(cmd.Flags().Arg(0))
   593  	if err != nil {
   594  		return err
   595  	}
   596  
   597  	cli.FinishedParsing(cmd)
   598  
   599  	resp, err := client.ValidateVersionShard(commandCtx, &vtctldatapb.ValidateVersionShardRequest{
   600  		Keyspace: keyspace,
   601  		Shard:    shard,
   602  	})
   603  	if err != nil {
   604  		return err
   605  	}
   606  
   607  	data, err := cli.MarshalJSON(resp)
   608  	if err != nil {
   609  		return err
   610  	}
   611  
   612  	fmt.Printf("%s\n", data)
   613  	return nil
   614  }
   615  
   616  func init() {
   617  	CreateShard.Flags().BoolVarP(&createShardOptions.Force, "force", "f", false, "Overwrite an existing shard record, if one exists.")
   618  	CreateShard.Flags().BoolVarP(&createShardOptions.IncludeParent, "include-parent", "p", false, "Creates the parent keyspace record if does not already exist.")
   619  	Root.AddCommand(CreateShard)
   620  
   621  	DeleteShards.Flags().BoolVarP(&deleteShardsOptions.Recursive, "recursive", "r", false, "Also delete all tablets belonging to the shard. This is required to delete a non-empty shard.")
   622  	DeleteShards.Flags().BoolVar(&deleteShardsOptions.EvenIfServing, "even-if-serving", false, "Remove the shard even if it is serving. Use with caution.")
   623  	DeleteShards.Flags().BoolVarP(&deleteShardsOptions.Force, "force", "f", false, "Remove the shard even if it cannot be locked; this should only be used for cleanup operations.")
   624  	Root.AddCommand(DeleteShards)
   625  
   626  	Root.AddCommand(GetShard)
   627  	Root.AddCommand(GenerateShardRanges)
   628  
   629  	RemoveShardCell.Flags().BoolVarP(&removeShardCellOptions.Force, "force", "f", false, "Proceed 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.")
   630  	RemoveShardCell.Flags().BoolVarP(&removeShardCellOptions.Recursive, "recursive", "r", false, "Also delete all tablets in that cell beloning to the specified shard.")
   631  	Root.AddCommand(RemoveShardCell)
   632  
   633  	Root.AddCommand(SetShardIsPrimaryServing)
   634  
   635  	SetShardTabletControl.Flags().StringSliceVarP(&setShardTabletControlOptions.Cells, "cells", "c", nil, "Specifies a comma-separated list of cells to update.")
   636  	SetShardTabletControl.Flags().StringSliceVar(&setShardTabletControlOptions.DeniedTables, "denied-tables", nil, "Specifies a comma-separated list of tables to add to the denylist (for MoveTables). Each table name is either an exact match, or a regular expression of the form '/regexp/'.")
   637  	SetShardTabletControl.Flags().BoolVarP(&setShardTabletControlOptions.Remove, "remove", "r", false, "Removes the specified cells for MoveTables operations.")
   638  	SetShardTabletControl.Flags().BoolVar(&setShardTabletControlOptions.DisableQueryService, "disable-query-service", false, "Sets the DisableQueryService flag in the specified cells. This flag requires --denied-tables and --remove to be unset; if either is set, this flag is ignored.")
   639  	Root.AddCommand(SetShardTabletControl)
   640  
   641  	Root.AddCommand(ShardReplicationAdd)
   642  	Root.AddCommand(ShardReplicationFix)
   643  	Root.AddCommand(ShardReplicationPositions)
   644  	Root.AddCommand(ShardReplicationRemove)
   645  	Root.AddCommand(ValidateVersionShard)
   646  
   647  	SourceShardAdd.Flags().StringVar(&sourceShardAddOptions.KeyRangeStr, "key-range", "", "Key range to use for the SourceShard.")
   648  	SourceShardAdd.Flags().StringSliceVar(&sourceShardAddOptions.Tables, "tables", nil, "Comma-separated lists of tables to replicate (for MoveTables). Each table name is either an exact match, or a regular expression of the form \"/regexp/\".")
   649  	Root.AddCommand(SourceShardAdd)
   650  
   651  	Root.AddCommand(SourceShardDelete)
   652  }