diff --git a/istioctl/cmd/root.go b/istioctl/cmd/root.go index e3d1f3fa33dc..b1c072c1da9a 100644 --- a/istioctl/cmd/root.go +++ b/istioctl/cmd/root.go @@ -113,8 +113,13 @@ debug and diagnose their Istio mesh. experimentalCmd.AddCommand(describe()) experimentalCmd.AddCommand(addToMeshCmd()) - experimentalCmd.AddCommand(mesh.ManifestCmd()) - experimentalCmd.AddCommand(mesh.ProfileCmd()) + manifestCmd := mesh.ManifestCmd() + hideInheritedFlags(manifestCmd, "namespace", "istioNamespace") + experimentalCmd.AddCommand(manifestCmd) + + profileCmd := mesh.ProfileCmd() + hideInheritedFlags(profileCmd, "namespace", "istioNamespace") + experimentalCmd.AddCommand(profileCmd) rootCmd.AddCommand(collateral.CobraCommand(rootCmd, &doc.GenManHeader{ Title: "Istio Control", @@ -134,6 +139,17 @@ debug and diagnose their Istio mesh. return rootCmd } +func hideInheritedFlags(orig *cobra.Command, hidden ...string) { + orig.SetHelpFunc(func(cmd *cobra.Command, args []string) { + for _, hidden := range hidden { + cmd.Flags().MarkHidden(hidden) // nolint: errcheck + } + + orig.SetHelpFunc(nil) + orig.HelpFunc()(cmd, args) + }) +} + func istioPersistentPreRunE(_ *cobra.Command, _ []string) error { if err := log.Configure(loggingOptions); err != nil { return err diff --git a/istioctl/cmd/root_test.go b/istioctl/cmd/root_test.go new file mode 100644 index 000000000000..ec37fc4f9110 --- /dev/null +++ b/istioctl/cmd/root_test.go @@ -0,0 +1,81 @@ +// Copyright 2019 Istio 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 cmd + +import ( + "bytes" + "strings" + "testing" + + "github.com/spf13/cobra" +) + +func checkHelpForFlag(t *testing.T, gotHelpText, flag string, wantExists bool) { + t.Helper() + + if strings.Contains(gotHelpText, flag) != wantExists { + if wantExists { + t.Errorf("%q flag was expected but not found in help text", flag) + } else { + t.Errorf("%q flag was found in help text but not expected", flag) + } + } +} + +func TestHideInheritedFlags(t *testing.T) { + const ( + parentFlag0 = "parent-flag0" + parentFlag1 = "parent-flag1" + parentFlag2 = "parent-flag2" + childFlag2 = "child-flag2" + ) + parent := &cobra.Command{Use: "parent"} + _ = parent.PersistentFlags().String(parentFlag0, "", parentFlag0) + _ = parent.PersistentFlags().String(parentFlag1, "", parentFlag1) + _ = parent.PersistentFlags().String(parentFlag2, "", parentFlag2) + var out bytes.Buffer + parent.SetOutput(&out) + + child := &cobra.Command{ + Use: "child", + Run: func(c *cobra.Command, args []string) {}, + } + _ = parent.PersistentFlags().String(childFlag2, "", childFlag2) + parent.AddCommand(child) + + // verify both parent flags and the child flag are visible by default + parent.SetArgs([]string{"child", "--help"}) + if err := parent.Execute(); err != nil { + t.Fatal(err) + } + got := out.String() + out.Reset() + checkHelpForFlag(t, got, parentFlag0, true) + checkHelpForFlag(t, got, parentFlag1, true) + checkHelpForFlag(t, got, parentFlag2, true) + checkHelpForFlag(t, got, childFlag2, true) + + // verify the hidden parent flag is not visible in help text + hideInheritedFlags(child, parentFlag1, parentFlag2) + if err := parent.Execute(); err != nil { + t.Fatal(err) + } + got = out.String() + out.Reset() + checkHelpForFlag(t, got, parentFlag0, true) + checkHelpForFlag(t, got, parentFlag1, false) + checkHelpForFlag(t, got, parentFlag1, false) + checkHelpForFlag(t, got, childFlag2, true) +}