diff --git a/istioctl/cmd/dashboard_test.go b/istioctl/cmd/dashboard_test.go index 3efccd429a68..99996084bea3 100644 --- a/istioctl/cmd/dashboard_test.go +++ b/istioctl/cmd/dashboard_test.go @@ -28,59 +28,64 @@ func TestDashboard(t *testing.T) { cases := []testCase{ { // case 0 - args: strings.Split("experimental dashboard", " "), + args: strings.Split("dashboard", " "), expectedRegexp: regexp.MustCompile("Access to Istio web UIs"), }, { // case 1 - args: strings.Split("experimental dashboard invalid", " "), + args: strings.Split("dashboard invalid", " "), expectedRegexp: regexp.MustCompile(`unknown dashboard "invalid"`), wantException: true, }, { // case 2 - args: strings.Split("experimental dashboard controlz", " "), + args: strings.Split("dashboard controlz", " "), expectedRegexp: regexp.MustCompile(".*Error: specify a pod"), wantException: true, }, { // case 3 - args: strings.Split("experimental dashboard controlz pod-123456-7890", " "), + args: strings.Split("dashboard controlz pod-123456-7890", " "), expectedRegexp: regexp.MustCompile(".*mock k8s does not forward"), wantException: true, }, { // case 4 - args: strings.Split("experimental dashboard envoy", " "), + args: strings.Split("dashboard envoy", " "), expectedRegexp: regexp.MustCompile(".*Error: specify a pod"), wantException: true, }, { // case 5 - args: strings.Split("experimental dashboard envoy pod-123456-7890", " "), + args: strings.Split("dashboard envoy pod-123456-7890", " "), expectedRegexp: regexp.MustCompile(".*mock k8s does not forward"), wantException: true, }, { // case 6 - args: strings.Split("experimental dashboard grafana", " "), + args: strings.Split("dashboard grafana", " "), expectedOutput: "Error: no Grafana pods found\n", wantException: true, }, { // case 7 - args: strings.Split("experimental dashboard jaeger", " "), + args: strings.Split("dashboard jaeger", " "), expectedOutput: "Error: no Jaeger pods found\n", wantException: true, }, { // case 8 - args: strings.Split("experimental dashboard kiali", " "), + args: strings.Split("dashboard kiali", " "), expectedOutput: "Error: no Kiali pods found\n", wantException: true, }, { // case 9 - args: strings.Split("experimental dashboard prometheus", " "), + args: strings.Split("dashboard prometheus", " "), expectedOutput: "Error: no Prometheus pods found\n", wantException: true, }, { // case 10 - args: strings.Split("experimental dashboard zipkin", " "), + args: strings.Split("dashboard zipkin", " "), expectedOutput: "Error: no Zipkin pods found\n", wantException: true, }, + { // case 11 + args: strings.Split("experimental dashboard", " "), + expectedOutput: "Error: (dashboard has graduated. Use `istioctl dashboard`)\n", + wantException: true, + }, } for i, c := range cases { diff --git a/istioctl/cmd/metrics_test.go b/istioctl/cmd/metrics_test.go index e18531686c6e..65ce3bb17255 100644 --- a/istioctl/cmd/metrics_test.go +++ b/istioctl/cmd/metrics_test.go @@ -48,15 +48,20 @@ func TestMetricsNoPrometheus(t *testing.T) { cases := []testCase{ { // case 0 - args: strings.Split("experimental metrics", " "), + args: strings.Split("metrics", " "), expectedRegexp: regexp.MustCompile("Error: metrics requires workload name\n"), wantException: true, }, { // case 1 - args: strings.Split("experimental metrics details", " "), + args: strings.Split("metrics details", " "), expectedOutput: "Error: no Prometheus pods found\n", wantException: true, }, + { // case 2 + args: strings.Split("experimental metrics details", " "), + expectedOutput: "Error: (metrics has graduated. Use `istioctl metrics`)\n", + wantException: true, + }, } for i, c := range cases { diff --git a/istioctl/cmd/root.go b/istioctl/cmd/root.go index d7bc15b72650..49f3f1051a69 100644 --- a/istioctl/cmd/root.go +++ b/istioctl/cmd/root.go @@ -15,6 +15,9 @@ package cmd import ( + "errors" + "fmt" + "github.com/spf13/cobra" "github.com/spf13/cobra/doc" @@ -103,13 +106,17 @@ debug and diagnose their Istio mesh. rootCmd.AddCommand(experimentalCmd) rootCmd.AddCommand(proxyConfig()) rootCmd.AddCommand(statusCmd) + rootCmd.AddCommand(convertIngress()) + rootCmd.AddCommand(dashboard()) + rootCmd.AddCommand(metricsCmd) rootCmd.AddCommand(install.NewVerifyCommand()) experimentalCmd.AddCommand(Auth()) - experimentalCmd.AddCommand(convertIngress()) - experimentalCmd.AddCommand(dashboard()) + rootCmd.AddCommand(seeExperimentalCmd("auth")) + experimentalCmd.AddCommand(graduatedCmd("convert-ingress")) + experimentalCmd.AddCommand(graduatedCmd("dashboard")) experimentalCmd.AddCommand(uninjectCommand()) - experimentalCmd.AddCommand(metricsCmd) + experimentalCmd.AddCommand(graduatedCmd("metrics")) experimentalCmd.AddCommand(describe()) experimentalCmd.AddCommand(addToMeshCmd()) experimentalCmd.AddCommand(removeFromMeshCmd()) @@ -183,3 +190,27 @@ func getDefaultNamespace(kubeconfig string) string { } return context.Namespace } + +// graduatedCmd is used for commands that have graduated +func graduatedCmd(name string) *cobra.Command { + msg := fmt.Sprintf("(%s has graduated. Use `istioctl %s`)", name, name) + return &cobra.Command{ + Use: name, + Short: msg, + RunE: func(_ *cobra.Command, _ []string) error { + return errors.New(msg) + }, + } +} + +// seeExperimentalCmd is used for commands that have been around for a release but not graduated +func seeExperimentalCmd(name string) *cobra.Command { + msg := fmt.Sprintf("(%s is experimental. Use `istioctl experimental %s`)", name, name) + return &cobra.Command{ + Use: name, + Short: msg, + RunE: func(_ *cobra.Command, _ []string) error { + return errors.New(msg) + }, + } +}