Skip to content

Commit

Permalink
Add test and ported knative#910 over.
Browse files Browse the repository at this point in the history
  • Loading branch information
rhuss committed Jul 14, 2020
1 parent 0f32b9b commit e157a02
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 37 deletions.
40 changes: 26 additions & 14 deletions cmd/kn/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,59 +35,68 @@ func init() {
}

func main() {
err := run(os.Args[1:])
if err != nil && len(os.Args) > 1 {
printError(err)
runError := run(os.Args[1:])
if runError.err != nil && len(os.Args) > 1 {
printError(runError)
// This is the only point from where to exit when an error occurs
os.Exit(1)
}
}

// Type for not printing a help message
type runError struct {
// Return encapsulate error
err error

// Whether to show a help message or not
showUsageHint bool
}

// Run the main program. Args are the args as given on the command line (excluding the program name itself)
func run(args []string) error {
func run(args []string) runError {
// Parse config & plugin flags early to read in configuration file
// and bind to viper. After that you can access all configuration and
// global options via methods on config.GlobalConfig
err := config.BootstrapConfig()
if err != nil {
return err
return runError{err, true}
}

// Strip of all flags to get the non-flag commands only
commands, err := stripFlags(args)
if err != nil {
return err
return runError{err, true}
}

// Find plugin with the commands arguments
pluginManager := plugin.NewManager(config.GlobalConfig.PluginsDir(), config.GlobalConfig.LookupPluginsInPath())
plugin, err := pluginManager.FindPlugin(commands)
if err != nil {
return err
return runError{err, true}
}

// Create kn root command and all sub-commands
rootCmd, err := root.NewRootCommand(pluginManager.HelpTemplateFuncs())
if err != nil {
return err
return runError{err, true}
}

if plugin != nil {
// Validate & Execute plugin
err = validatePlugin(rootCmd, plugin)
if err != nil {
return err
return runError{err, true}
}

return plugin.Execute(argsWithoutCommands(args, plugin.CommandParts()))
return runError{err, false}
} else {
// Validate args for root command
err = validateRootCommand(rootCmd)
if err != nil {
return err
return runError{err, true}
}
// Execute kn root command, args are taken from os.Args directly
return rootCmd.Execute()
return runError{rootCmd.Execute(), true}
}
}

Expand Down Expand Up @@ -188,9 +197,12 @@ func validateRootCommand(cmd *cobra.Command) error {
}

// printError prints out any given error
func printError(err error) {
func printError(runErr runError) {
err := runErr.err
fmt.Fprintf(os.Stderr, "Error: %s\n", cleanupErrorMessage(err.Error()))
fmt.Fprintf(os.Stderr, "Run '%s --help' for usage\n", extractCommandPathFromErrorMessage(err.Error(), os.Args[0]))
if runErr.showUsageHint {
fmt.Fprintf(os.Stderr, "Run '%s --help' for usage\n", extractCommandPathFromErrorMessage(err.Error(), os.Args[0]))
}
}

// extractCommandPathFromErrorMessage tries to extract the command name from an error message
Expand Down
27 changes: 25 additions & 2 deletions cmd/kn/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func TestRunWithError(t *testing.T) {
}
for _, d := range data {
capture := test.CaptureOutput(t)
printError(errors.New(d.given))
printError(runError{errors.New(d.given), true})
stdOut, errOut := capture.Close()

assert.Equal(t, stdOut, "")
Expand All @@ -253,6 +253,29 @@ func TestRunWithError(t *testing.T) {
}
}

func TestRunWithPluginError(t *testing.T) {
data := []struct {
given string
expected string
}{
{
"exit status 1",
"Error: exit status 1",
},
}
for _, d := range data {
capture := test.CaptureOutput(t)
// displayHelp argument is false for plugin error
printError(runError{errors.New(d.given), false})
stdOut, errOut := capture.Close()

assert.Equal(t, stdOut, "")
assert.Assert(t, strings.Contains(errOut, d.expected))
// check that --help message isn't displayed
assert.Assert(t, util.ContainsNone(errOut, "Run", "--help", "usage"))
}
}

// Smoke test
func TestRun(t *testing.T) {
oldArgs := os.Args
Expand All @@ -265,6 +288,6 @@ func TestRun(t *testing.T) {
err := run(os.Args[1:])
out, _ := capture.Close()

assert.NilError(t, err)
assert.NilError(t, err.err)
assert.Assert(t, util.ContainsAllIgnoreCase(out, "version", "build", "git"))
}
5 changes: 3 additions & 2 deletions pkg/kn/plugin/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,9 @@ func (plugin *plugin) Execute(args []string) error {
// Return a description of the plugin (if support by the plugin binary)
func (plugin *plugin) Description() (string, error) {
// TODO: Call out to the plugin to find a description.
// For now just use the plugin name
return strings.Join(plugin.commandParts, "-"), nil
// For now just use the path to the plugin
return plugin.path, nil
// return strings.Join(plugin.commandParts, "-"), nil
}

// The the command path leading to this plugin.
Expand Down
52 changes: 52 additions & 0 deletions pkg/kn/plugin/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"runtime"
"testing"

"github.com/spf13/cobra"
"gotest.tools/assert"
)

Expand Down Expand Up @@ -110,6 +112,56 @@ func TestPluginExecute(t *testing.T) {
assert.Equal(t, out, "OK arg1 arg2\n")
}

func TestPluginListForCommandGroup(t *testing.T) {
ctx := setup(t)
defer cleanup(t, ctx)
createTestPlugin(t, "kn-service-log_2", ctx)

pluginList, err := ctx.pluginManager.ListPluginsForCommandGroup([]string{"service"})
assert.NilError(t, err)
assert.Assert(t, pluginList.Len() == 1)
assert.Equal(t, pluginList[0].Name(), "kn-service-log_2")
pluginList, err = ctx.pluginManager.ListPluginsForCommandGroup([]string{})
assert.NilError(t, err)
assert.Assert(t, pluginList.Len() == 0)
}

func TestPluginHelpMessage(t *testing.T) {
ctx := setup(t)
defer cleanup(t, ctx)
createTestPlugin(t, "kn-service-log_2", ctx)
createTestPlugin(t, "kn-admin", ctx)

funcs := *ctx.pluginManager.HelpTemplateFuncs()
f := funcs["listPlugins"]
assert.Assert(t, f != nil)
listPluginsFunc := ctx.pluginManager.listPluginsHelpMessage()

root := &cobra.Command{
Use: "kn",
}
serviceCmd := &cobra.Command{
Use: "service",
}
serviceCreateCmd := &cobra.Command{
Use: "create",
}
serviceCmd.AddCommand(serviceCreateCmd)
root.AddCommand(serviceCmd)

helpRoot := listPluginsFunc(root)
re := regexp.MustCompile("^\\s*admin\\s.*admin")
assert.Assert(t, re.MatchString(helpRoot))

helpService := listPluginsFunc(serviceCmd)
println(helpService)
re = regexp.MustCompile("^\\s*log-2\\s.*kn-service-log_2")
assert.Assert(t, re.MatchString(helpService))

helpServiceCreate := listPluginsFunc(serviceCreateCmd)
assert.Assert(t, len(helpServiceCreate) == 0)
}

func TestPluginList(t *testing.T) {
ctx := setup(t)
defer cleanup(t, ctx)
Expand Down
60 changes: 41 additions & 19 deletions test/e2e/plugins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"

Expand All @@ -38,11 +37,14 @@ const (
echo "Hello Knative, I'm a Kn plugin"
echo " My plugin file is $0"
echo " I received arguments: $1 $2 $3 $4"`

TestPluginCodeErr string = `#!/bin/bash
exit 1`
)

type pluginTestConfig struct {
knConfigDir, knPluginsDir, knPluginsDir2 string
knConfigPath, knPluginPath, knPluginPath2 string
knConfigDir, knPluginsDir, knPluginsDir2, knPluginsDir3 string
knConfigPath, knPluginPath, knPluginPath2, knPluginsPath3 string
}

func (pc *pluginTestConfig) setup() error {
Expand Down Expand Up @@ -108,17 +110,18 @@ func TestPluginWithoutLookup(t *testing.T) {
listPlugin(r, knFlags, []string{pc.knPluginPath}, []string{pc.knPluginPath2})
}

func execute(command string, args ...string) string {
cmd := exec.Command(command, args...)
r, w, _ := os.Pipe()
cmd.Stdout = w
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Env = os.Environ()
cmd.Run()
w.Close()
ret, _ := ioutil.ReadAll(r)
return string(ret)
func TestPluginInHelpMessage(t *testing.T) {
pc := pluginTestConfig{}
assert.NilError(t, pc.setup())
defer pc.teardown()

result := test.Kn{}.Run("--plugins-dir", pc.knPluginPath, "--help")
assert.NilError(t, result.Error)
assert.Assert(t, util.ContainsAll(result.Stdout, "Plugins:", "helloe2e", "kn-helloe2e"))

result = test.Kn{}.Run("--plugins-dir", pc.knPluginPath, "service", "--help")
assert.NilError(t, result.Error)
assert.Assert(t, util.ContainsNone(result.Stdout, "Plugins:", "helloe2e", "kn-helloe2e"))
}

func TestPluginWithLookup(t *testing.T) {
Expand Down Expand Up @@ -172,14 +175,33 @@ func TestExecutePluginInPath(t *testing.T) {
runPlugin(r, knFlags, "hello2e2e", []string{}, []string{"Hello Knative, I'm a Kn plugin"})
}

// Private
func TestExecutePluginInPathWithError(t *testing.T) {
it, err := test.NewKnTest()
assert.NilError(t, err)

func createPluginFile(fileName, fileContent, filePath string, fileMode os.FileMode) (string, error) {
file := filepath.Join(filePath, fileName)
err := ioutil.WriteFile(file, []byte(fileContent), fileMode)
return file, err
r := test.NewKnRunResultCollector(t, it)
defer r.DumpIfFailed()

pc := pluginTestConfig{}
assert.NilError(t, pc.setup())
oldPath := os.Getenv("PATH")

t.Log("execute plugin in $PATH that returns error")
pluginsDir := filepath.Join(pc.knConfigDir, "plugins3")
err = os.MkdirAll(pc.knPluginsDir3, test.FileModeExecutable)
assert.NilError(t, err)
_, err = test.CreateFile("kn-hello3e2e", TestPluginCodeErr, pluginsDir, test.FileModeExecutable)
assert.NilError(t, err)
assert.NilError(t, os.Setenv("PATH", fmt.Sprintf("%s:%s", oldPath, pluginsDir)))
defer tearDownWithPath(pc, oldPath)

out := test.Kn{}.Run("--lookup-plugins=true", "hello3e2e")
r.AssertError(out)
assert.Check(r.T(), util.ContainsAll(out.Stdout, "Error: exit status 1"))
assert.Check(r.T(), util.ContainsNone(out.Stderr, "Run", "kn --help", "usage"))
}

// Private
func setupPluginTestConfigWithNewPath(t *testing.T) (pluginTestConfig, string) {
pc := pluginTestConfig{}
assert.NilError(t, pc.setup())
Expand Down

0 comments on commit e157a02

Please sign in to comment.