vitess.io/vitess@v0.16.2/go/cmd/vtctldclient/command/doc.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 /* 18 Package command contains the commands used by vtctldclient. It is intended only 19 for use in vtctldclient's main package and entrypoint. The rest of this 20 documentation is intended for maintainers. 21 22 Commands are grouped into files by the types of resources they interact with ( 23 e.g. GetTablet, CreateTablet, DeleteTablet, GetTablets) or by what they do (e.g. 24 PlannedReparentShard, EmergencyReparentShard, InitShardPrimary). Please add the 25 command to the appropriate existing file, alphabetically, or create a new 26 grouping if one does not exist. 27 28 The root command lives in root.go, and commands must attach themselves to this 29 during an init function in order to be reachable from the CLI. root.go also 30 contains the global variables available to any subcommand that are managed by 31 the root command's pre- and post-run functions. Commands must not attempt to 32 manage these, as that may conflict with Root's post-run cleanup actions. All 33 commands should, at a minimum, use the commandCtx rather than creating their own 34 context.Background to start, as it contains root tracing spans that would be 35 lost. 36 37 Commands should not keep their logic in an anonymous function on the 38 cobra.Command struct, but instead in a separate function that is assigned to 39 RunE. Commands should strive to keep declaration, function definition, and flag 40 initialization located as closely together as possible, to make the code easier 41 to follow and understand (the global variables declared near Root are the 42 exception here, not the rule). Commands should also prevent individual flag 43 names from polluting the package namespace. 44 45 A good pattern we have found is to do the following: 46 47 package command 48 49 // (imports ...) 50 51 var ( 52 CreateTablet = &cobra.Command{ 53 Use: "CreateTablet [options] --keyspace=<keyspace> --shard=<shard-range> <tablet-alias> <tablet-type>", 54 Args: cobra.ExactArgs(2), 55 RunE: commandCreateTablet, 56 } 57 GetTablet = &cobra.Command{ 58 Use: "GetTablet <tablet-alias>", 59 Args: cobra.ExactArgs(1), 60 RunE: commandGetTablet, 61 } 62 ) 63 64 var createTabletOptions = struct { 65 Opt1 string 66 Opt2 bool 67 Keyspace string 68 Shard string 69 }{} 70 71 func commandCreateTablet(cmd *cobra.Command, args []string) error { 72 aliasStr := cmd.Flags().Args(0) 73 tabletTypeStr := cmd.Flags().Args(1) 74 75 // do stuff with: 76 // - client 77 // - commandCtx 78 // - createTabletOptions 79 // - aliasStr 80 // - tabletTypeStr 81 82 return nil 83 } 84 85 // GetTablet takes no flags, so it needs no anonymous struct to store them 86 func commandGetTablet(cmd *cobra.Command, args []string) error { 87 aliasStr := cmd.Flags().Arg(0) 88 89 // do stuff with: 90 // - client 91 // - commandCtx 92 // - aliasStr 93 94 return nil 95 } 96 97 // finally, hook up all the commands in this file to Root, and add any flags 98 // to each of those commands 99 100 func init() { 101 CreateTablet.Flags().StringVar(&createTabletOptions.Opt1, "opt1", "default", "help") 102 CreateTablet.Flags().BoolVar(&createTabletOptions.Opt2, "opt2", false, "help") 103 CreateTablet.Flags().StringVarP(&createTabletOptions.Keyspace, "keyspace", "k", "keyspace of tablet") 104 CreateTablet.MarkFlagRequired("keyspace") 105 CreateTablet.Flags().StringVarP(&createTabletOptions.Shard, "shard", "s", "shard range of tablet") 106 CreateTablet.MarkFlagRequired("shard") 107 Root.AddCommand(CreateTablet) 108 109 Root.AddCommand(GetTablet) 110 } 111 112 A note on RunE and SilenceUsage: 113 114 We prefer using RunE over Run for the entrypoint to our subcommands, because it 115 allows us return errors back up to the vtctldclient main function and do error 116 handling, logging, and exit-code management once, in one place, rather than on a 117 per-command basis. However, cobra treats errors returned from a command's RunE 118 as usage errors, and therefore will print the command's full usage text to 119 stderr when RunE returns non-nil, in addition to propagating that error back up 120 to the result of the root command's Execute() method. This is decidedly not what 121 we want. There is no plan to address this in cobra v1. [1] 122 123 The suggested workaround for this issue is to set SilenceUsage: true, either on 124 the root command or on every subcommand individually. This also does not work 125 for vtctldclient, because not every flag can be parsed during pflag.Parse time, 126 and for certain flags (mutually exclusive options, optional flags that require 127 other flags to be set with them, etc) we do additional parsing and validation of 128 flags in an individual subcommand. We want errors during this phase to be 129 treated as usage errors, so setting SilenceUsage=true before this point would 130 not cause usage text to be printed for us. 131 132 So, for us, we want to individually set cmd.SilenceUsage = true at *particular 133 points* in each command, dependending on whether that command needs to do 134 an additional parse & validation pass. In most cases, the command does not need 135 to post-validate its options, and can set cmd.SilencUsage = true as their first 136 line. We feel, though, that a line that reads "SilenceUsage = true" to be 137 potentially confusing in how it reads. A maintainer without sufficient context 138 may read this and say "Silence usage? We don't want that" and remove the lines, 139 so we provide a wrapper function that communicates intent, cli.FinishedParsing, 140 that each subcommand should call when they have transitioned from the parsing & 141 validation phase of their entrypoint to the actual logic. 142 143 [1]: https://github.com/spf13/cobra/issues/340 144 */ 145 package command