Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,28 @@ func GetRootCmd() *cobra.Command {
// Cluster administration commands
AddAddClusterAdminCmd(rootCmd, opts)

silenceUsageOnRunErrors(rootCmd)

return rootCmd
}

// silenceUsageOnRunErrors prevents Cobra from printing command usage for
// operational errors returned by RunE. Argument, flag, and required-flag
// validation happens before RunE, so those errors still include usage.
func silenceUsageOnRunErrors(command *cobra.Command) {
if command.RunE != nil {
runE := command.RunE
command.RunE = func(cmd *cobra.Command, args []string) error {
cmd.Root().SilenceUsage = true
return runE(cmd, args)
}
}

for _, child := range command.Commands() {
silenceUsageOnRunErrors(child)
}
}

// Execute executes the root command. This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
//Disable printing timestamps on log lines
Expand Down
66 changes: 66 additions & 0 deletions cli/cmd/root_usage_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) Codesphere Inc.
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
"bytes"
"errors"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/spf13/cobra"
)

var _ = Describe("command usage on errors", func() {
execute := func(root *cobra.Command, args ...string) (string, error) {
var output bytes.Buffer
root.SetOut(&output)
root.SetErr(&output)
root.SetArgs(args)
err := root.Execute()
return output.String(), err
}

newCommandTree := func() (*cobra.Command, *cobra.Command) {
root := &cobra.Command{Use: "oms", Args: cobra.NoArgs}
child := &cobra.Command{
Use: "install",
Args: cobra.NoArgs,
RunE: func(*cobra.Command, []string) error {
return errors.New("installation failed")
},
}
child.Flags().String("config", "", "installation config")
root.AddCommand(child)
silenceUsageOnRunErrors(root)
return root, child
}

It("does not print usage for an operational RunE error", func() {
root, _ := newCommandTree()

output, err := execute(root, "install")

Expect(err).To(MatchError("installation failed"))
Expect(output).To(ContainSubstring("Error: installation failed"))
Expect(output).NotTo(ContainSubstring("Usage:"))
})

DescribeTable("prints usage for invalid command input",
func(configure func(*cobra.Command), args []string) {
root, child := newCommandTree()
configure(child)

output, err := execute(root, args...)

Expect(err).To(HaveOccurred())
Expect(output).To(ContainSubstring("Usage:"))
},
Entry("unexpected positional arguments", func(*cobra.Command) {}, []string{"install", "unexpected"}),
Entry("unknown flags", func(*cobra.Command) {}, []string{"install", "--unknown"}),
Entry("missing required flags", func(command *cobra.Command) {
Expect(command.MarkFlagRequired("config")).To(Succeed())
}, []string{"install"}),
)
})
Loading