istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/cmd/root.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cmd 16 17 import ( 18 "errors" 19 "fmt" 20 "path/filepath" 21 "strings" 22 23 "github.com/spf13/cobra" 24 "github.com/spf13/viper" 25 26 "istio.io/istio/istioctl/pkg/admin" 27 "istio.io/istio/istioctl/pkg/analyze" 28 "istio.io/istio/istioctl/pkg/authz" 29 "istio.io/istio/istioctl/pkg/checkinject" 30 "istio.io/istio/istioctl/pkg/cli" 31 "istio.io/istio/istioctl/pkg/completion" 32 "istio.io/istio/istioctl/pkg/config" 33 "istio.io/istio/istioctl/pkg/dashboard" 34 "istio.io/istio/istioctl/pkg/describe" 35 "istio.io/istio/istioctl/pkg/injector" 36 "istio.io/istio/istioctl/pkg/internaldebug" 37 "istio.io/istio/istioctl/pkg/kubeinject" 38 "istio.io/istio/istioctl/pkg/metrics" 39 "istio.io/istio/istioctl/pkg/multicluster" 40 "istio.io/istio/istioctl/pkg/precheck" 41 "istio.io/istio/istioctl/pkg/proxyconfig" 42 "istio.io/istio/istioctl/pkg/proxystatus" 43 "istio.io/istio/istioctl/pkg/root" 44 "istio.io/istio/istioctl/pkg/tag" 45 "istio.io/istio/istioctl/pkg/util" 46 "istio.io/istio/istioctl/pkg/validate" 47 "istio.io/istio/istioctl/pkg/version" 48 "istio.io/istio/istioctl/pkg/wait" 49 "istio.io/istio/istioctl/pkg/waypoint" 50 "istio.io/istio/istioctl/pkg/workload" 51 "istio.io/istio/istioctl/pkg/ztunnelconfig" 52 "istio.io/istio/operator/cmd/mesh" 53 "istio.io/istio/pkg/cmd" 54 "istio.io/istio/pkg/collateral" 55 "istio.io/istio/pkg/config/constants" 56 "istio.io/istio/pkg/log" 57 "istio.io/istio/tools/bug-report/pkg/bugreport" 58 ) 59 60 const ( 61 // Location to read istioctl defaults from 62 defaultIstioctlConfig = "$HOME/.istioctl/config.yaml" 63 ) 64 65 const ( 66 FlagCharts = "charts" 67 ) 68 69 // ConfigAndEnvProcessing uses spf13/viper for overriding CLI parameters 70 func ConfigAndEnvProcessing() error { 71 configPath := filepath.Dir(root.IstioConfig) 72 baseName := filepath.Base(root.IstioConfig) 73 configType := filepath.Ext(root.IstioConfig) 74 configName := baseName[0 : len(baseName)-len(configType)] 75 if configType != "" { 76 configType = configType[1:] 77 } 78 79 // Allow users to override some variables through $HOME/.istioctl/config.yaml 80 // and environment variables. 81 viper.SetEnvPrefix("ISTIOCTL") 82 viper.AutomaticEnv() 83 viper.AllowEmptyEnv(true) // So we can say ISTIOCTL_CERT_DIR="" to suppress certs 84 viper.SetConfigName(configName) 85 viper.SetConfigType(configType) 86 viper.AddConfigPath(configPath) 87 viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) 88 err := viper.ReadInConfig() 89 // Ignore errors reading the configuration unless the file is explicitly customized 90 if root.IstioConfig != defaultIstioctlConfig { 91 return err 92 } 93 94 return nil 95 } 96 97 func init() { 98 viper.SetDefault("istioNamespace", constants.IstioSystemNamespace) 99 viper.SetDefault("xds-port", 15012) 100 } 101 102 // GetRootCmd returns the root of the cobra command-tree. 103 func GetRootCmd(args []string) *cobra.Command { 104 rootCmd := &cobra.Command{ 105 Use: "istioctl", 106 Short: "Istio control interface.", 107 SilenceUsage: true, 108 DisableAutoGenTag: true, 109 PersistentPreRunE: ConfigureLogging, 110 Long: `Istio configuration command line utility for service operators to 111 debug and diagnose their Istio mesh. 112 `, 113 } 114 115 rootCmd.SetArgs(args) 116 117 flags := rootCmd.PersistentFlags() 118 rootOptions := cli.AddRootFlags(flags) 119 120 ctx := cli.NewCLIContext(rootOptions) 121 122 _ = rootCmd.RegisterFlagCompletionFunc(cli.FlagIstioNamespace, func( 123 cmd *cobra.Command, args []string, toComplete string, 124 ) ([]string, cobra.ShellCompDirective) { 125 return completion.ValidNamespaceArgs(cmd, ctx, args, toComplete) 126 }) 127 _ = rootCmd.RegisterFlagCompletionFunc(cli.FlagNamespace, func( 128 cmd *cobra.Command, args []string, toComplete string, 129 ) ([]string, cobra.ShellCompDirective) { 130 return completion.ValidNamespaceArgs(cmd, ctx, args, toComplete) 131 }) 132 133 // Attach the Istio logging options to the command. 134 root.LoggingOptions.AttachCobraFlags(rootCmd) 135 hiddenFlags := []string{ 136 "log_as_json", "log_rotate", "log_rotate_max_age", "log_rotate_max_backups", 137 "log_rotate_max_size", "log_stacktrace_level", "log_target", "log_caller", "log_output_level", 138 } 139 for _, opt := range hiddenFlags { 140 _ = rootCmd.PersistentFlags().MarkHidden(opt) 141 } 142 143 cmd.AddFlags(rootCmd) 144 145 kubeInjectCmd := kubeinject.InjectCommand(ctx) 146 hideInheritedFlags(kubeInjectCmd, cli.FlagNamespace) 147 rootCmd.AddCommand(kubeInjectCmd) 148 149 experimentalCmd := &cobra.Command{ 150 Use: "experimental", 151 Aliases: []string{"x", "exp"}, 152 Short: "Experimental commands that may be modified or deprecated", 153 } 154 155 xdsBasedTroubleshooting := []*cobra.Command{ 156 // TODO(hanxiaop): I think experimental version still has issues, so we keep the old version for now. 157 version.XdsVersionCommand(ctx), 158 // TODO(hanxiaop): this is kept for some releases in case someone is using it. 159 proxystatus.XdsStatusCommand(ctx), 160 } 161 troubleshootingCommands := []*cobra.Command{ 162 version.NewVersionCommand(ctx), 163 proxystatus.StableXdsStatusCommand(ctx), 164 } 165 var debugCmdAttachmentPoint *cobra.Command 166 if viper.GetBool("PREFER-EXPERIMENTAL") { 167 legacyCmd := &cobra.Command{ 168 Use: "legacy", 169 Short: "Legacy command variants", 170 } 171 rootCmd.AddCommand(legacyCmd) 172 for _, c := range xdsBasedTroubleshooting { 173 rootCmd.AddCommand(c) 174 } 175 debugCmdAttachmentPoint = legacyCmd 176 } else { 177 debugCmdAttachmentPoint = rootCmd 178 } 179 for _, c := range xdsBasedTroubleshooting { 180 experimentalCmd.AddCommand(c) 181 } 182 for _, c := range troubleshootingCommands { 183 debugCmdAttachmentPoint.AddCommand(c) 184 } 185 186 rootCmd.AddCommand(experimentalCmd) 187 rootCmd.AddCommand(proxyconfig.ProxyConfig(ctx)) 188 rootCmd.AddCommand(admin.Cmd(ctx)) 189 experimentalCmd.AddCommand(injector.Cmd(ctx)) 190 191 rootCmd.AddCommand(mesh.NewVerifyCommand(ctx)) 192 rootCmd.AddCommand(mesh.UninstallCmd(ctx)) 193 194 experimentalCmd.AddCommand(authz.AuthZ(ctx)) 195 rootCmd.AddCommand(seeExperimentalCmd("authz")) 196 experimentalCmd.AddCommand(metrics.Cmd(ctx)) 197 experimentalCmd.AddCommand(describe.Cmd(ctx)) 198 experimentalCmd.AddCommand(wait.Cmd(ctx)) 199 experimentalCmd.AddCommand(config.Cmd()) 200 experimentalCmd.AddCommand(workload.Cmd(ctx)) 201 experimentalCmd.AddCommand(internaldebug.DebugCommand(ctx)) 202 experimentalCmd.AddCommand(precheck.Cmd(ctx)) 203 experimentalCmd.AddCommand(proxyconfig.StatsConfigCmd(ctx)) 204 experimentalCmd.AddCommand(checkinject.Cmd(ctx)) 205 experimentalCmd.AddCommand(waypoint.Cmd(ctx)) 206 experimentalCmd.AddCommand(ztunnelconfig.ZtunnelConfig(ctx)) 207 208 analyzeCmd := analyze.Analyze(ctx) 209 hideInheritedFlags(analyzeCmd, cli.FlagIstioNamespace) 210 rootCmd.AddCommand(analyzeCmd) 211 212 dashboardCmd := dashboard.Dashboard(ctx) 213 hideInheritedFlags(dashboardCmd, cli.FlagNamespace, cli.FlagIstioNamespace) 214 rootCmd.AddCommand(dashboardCmd) 215 216 manifestCmd := mesh.ManifestCmd(ctx) 217 hideInheritedFlags(manifestCmd, cli.FlagNamespace, cli.FlagIstioNamespace, FlagCharts) 218 rootCmd.AddCommand(manifestCmd) 219 220 operatorCmd := mesh.OperatorCmd(ctx) 221 hideInheritedFlags(operatorCmd, cli.FlagNamespace, cli.FlagIstioNamespace, FlagCharts) 222 rootCmd.AddCommand(operatorCmd) 223 224 installCmd := mesh.InstallCmd(ctx) 225 hideInheritedFlags(installCmd, cli.FlagNamespace, cli.FlagIstioNamespace, FlagCharts) 226 rootCmd.AddCommand(installCmd) 227 228 profileCmd := mesh.ProfileCmd(ctx) 229 hideInheritedFlags(profileCmd, cli.FlagNamespace, cli.FlagIstioNamespace, FlagCharts) 230 rootCmd.AddCommand(profileCmd) 231 232 upgradeCmd := mesh.UpgradeCmd(ctx) 233 hideInheritedFlags(upgradeCmd, cli.FlagNamespace, cli.FlagIstioNamespace, FlagCharts) 234 rootCmd.AddCommand(upgradeCmd) 235 236 bugReportCmd := bugreport.Cmd(ctx, root.LoggingOptions) 237 hideInheritedFlags(bugReportCmd, cli.FlagNamespace, cli.FlagIstioNamespace) 238 rootCmd.AddCommand(bugReportCmd) 239 240 tagCmd := tag.TagCommand(ctx) 241 hideInheritedFlags(tag.TagCommand(ctx), cli.FlagNamespace, cli.FlagIstioNamespace, FlagCharts) 242 rootCmd.AddCommand(tagCmd) 243 244 // leave the multicluster commands in x for backwards compat 245 rootCmd.AddCommand(multicluster.NewCreateRemoteSecretCommand(ctx)) 246 rootCmd.AddCommand(proxyconfig.ClustersCommand(ctx)) 247 248 rootCmd.AddCommand(collateral.CobraCommand(rootCmd, collateral.Metadata{ 249 Title: "Istio Control", 250 Section: "istioctl CLI", 251 Manual: "Istio Control", 252 })) 253 254 validateCmd := validate.NewValidateCommand(ctx) 255 hideInheritedFlags(validateCmd, "kubeconfig") 256 rootCmd.AddCommand(validateCmd) 257 258 rootCmd.AddCommand(optionsCommand(rootCmd)) 259 260 // BFS applies the flag error function to all subcommands 261 seenCommands := make(map[*cobra.Command]bool) 262 var commandStack []*cobra.Command 263 264 commandStack = append(commandStack, rootCmd) 265 266 for len(commandStack) > 0 { 267 n := len(commandStack) - 1 268 curCmd := commandStack[n] 269 commandStack = commandStack[:n] 270 seenCommands[curCmd] = true 271 for _, command := range curCmd.Commands() { 272 if !seenCommands[command] { 273 commandStack = append(commandStack, command) 274 } 275 } 276 curCmd.SetFlagErrorFunc(func(_ *cobra.Command, e error) error { 277 return util.CommandParseError{Err: e} 278 }) 279 } 280 281 return rootCmd 282 } 283 284 func hideInheritedFlags(orig *cobra.Command, hidden ...string) { 285 orig.SetHelpFunc(func(cmd *cobra.Command, args []string) { 286 for _, hidden := range hidden { 287 _ = cmd.Flags().MarkHidden(hidden) // nolint: errcheck 288 } 289 290 orig.SetHelpFunc(nil) 291 orig.HelpFunc()(cmd, args) 292 }) 293 } 294 295 func ConfigureLogging(_ *cobra.Command, _ []string) error { 296 return log.Configure(root.LoggingOptions) 297 } 298 299 // seeExperimentalCmd is used for commands that have been around for a release but not graduated from 300 // Other alternative 301 // for graduatedCmd see https://github.com/istio/istio/pull/26408 302 // for softGraduatedCmd see https://github.com/istio/istio/pull/26563 303 func seeExperimentalCmd(name string) *cobra.Command { 304 msg := fmt.Sprintf("(%s is experimental. Use `istioctl experimental %s`)", name, name) 305 return &cobra.Command{ 306 Use: name, 307 Short: msg, 308 RunE: func(_ *cobra.Command, _ []string) error { 309 return errors.New(msg) 310 }, 311 } 312 }