From 80bd865c5e5973400262c082d709be12c6c33ff8 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Wed, 19 Aug 2015 15:00:29 +0200 Subject: [PATCH 01/17] Testing 'scw help' --- .travis.yml | 1 + Makefile | 11 +++ integration-cli/custom_assert_test.go | 10 ++ integration-cli/scw_cli_help_test.go | 98 +++++++++++++++++++ integration-cli/utils_test.go | 37 +++++++ integration-cli/vars_test.go | 25 +++++ .../docker/docker/pkg/archive/archive.go | 8 +- .../docker/docker/pkg/archive/archive_unix.go | 15 +++ .../docker/pkg/archive/archive_windows.go | 16 +++ 9 files changed, 218 insertions(+), 3 deletions(-) create mode 100644 integration-cli/custom_assert_test.go create mode 100644 integration-cli/scw_cli_help_test.go create mode 100644 integration-cli/utils_test.go create mode 100644 integration-cli/vars_test.go diff --git a/.travis.yml b/.travis.yml index 17fe1dc8be..264069f629 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,3 +37,4 @@ script: - make travis_run - make cover - goveralls -service=travis-ci -v -coverprofile=profile.out + - make integration-cli diff --git a/Makefile b/Makefile index 30b24dc0a8..ebdcc2fa53 100644 --- a/Makefile +++ b/Makefile @@ -152,3 +152,14 @@ party: convey: go get github.com/smartystreets/goconvey goconvey -cover -port=9042 -workDir="$(realpath .)/pkg" -depth=-1 + + +.PHONY: integration-cli +integration-cli: + go test -v ./integration-cli + + +.PHONY: integration-cli +convey-integration-cli: + go get github.com/smartystreets/goconvey + goconvey -cover -port=9043 -workDir="$(realpath .)/integration-cli" -depth=-1 diff --git a/integration-cli/custom_assert_test.go b/integration-cli/custom_assert_test.go new file mode 100644 index 0000000000..d93e5949f4 --- /dev/null +++ b/integration-cli/custom_assert_test.go @@ -0,0 +1,10 @@ +package integrationcli + +import "fmt" + +func shouldFitInTerminal(actual interface{}, expected ...interface{}) string { + if len(actual.(string)) < 80 { + return "" + } + return fmt.Sprintf("len(%q)\n -> %d chars (> 80 chars)", actual, len(actual.(string))) +} diff --git a/integration-cli/scw_cli_help_test.go b/integration-cli/scw_cli_help_test.go new file mode 100644 index 0000000000..3cd5203e09 --- /dev/null +++ b/integration-cli/scw_cli_help_test.go @@ -0,0 +1,98 @@ +package integrationcli + +import ( + "fmt" + "os/exec" + "strings" + "testing" + + . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" +) + +func TestHelp(t *testing.T) { + Convey("Testing 'scw help'", t, func() { + Convey("scw help", func() { + cmd := exec.Command(scwcli, "help") + out, ec, err := runCommandWithOutput(cmd) + So(ec, ShouldEqual, 0) + So(err, ShouldBeNil) + + // headers & footers + So(out, ShouldContainSubstring, "Usage: scw [OPTIONS] COMMAND [arg...]") + So(out, ShouldContainSubstring, "Interact with Scaleway from the command line.") + So(out, ShouldContainSubstring, "Run 'scw COMMAND --help' for more information on a command.") + + // options + So(out, ShouldContainSubstring, "Options:") + for _, option := range publicOptions { + So(out, ShouldContainSubstring, " "+option) + } + + // public commands + So(out, ShouldContainSubstring, "Commands:") + for _, command := range publicCommands { + So(out, ShouldContainSubstring, " "+command) + } + + // secret commands + for _, command := range secretCommands { + So(out, ShouldNotContainSubstring, " "+command) + } + + // :lipstick: + for _, line := range strings.Split(out, "\n") { + if false { // FIXME: disabled for now + So(line, shouldFitInTerminal) + } + } + + // FIXME: count amount of options/commands, and panic if amount is different + }) + + commands := append(publicCommands, secretCommands...) + for _, command := range commands { + if command == "help" { + // FIXME: this should not be a special case + continue + } + Convey(fmt.Sprintf("scw --help %s", command), func() { + cmd := exec.Command(scwcli, command, "--help") + _, ec, err := runCommandWithOutput(cmd) + + // exit code + So(ec, ShouldEqual, 1) + So(fmt.Sprintf("%s", err), ShouldEqual, "exit status 1") + + // FIXME: disabled for now because it depends of 'scw login' + /* + // Header + So(out, ShouldContainSubstring, fmt.Sprintf("Usage: scw %s", command)) + // FIXME: test description + // FIXME: test parameters + + // Options + So(out, ShouldContainSubstring, "Options:") + So(out, ShouldContainSubstring, " -h, --help=false") + // FIXME: test other options + + // Examples + // FIXME: todo + //So(out, ShouldContainSubstring, "Examples:") + + secondCmd := exec.Command(scwcli, "help", command) + secondOut, secondEc, secondErr := runCommandWithOutput(secondCmd) + So(out, ShouldEqual, secondOut) + So(ec, ShouldEqual, secondEc) + So(fmt.Sprintf("%v", err), ShouldEqual, fmt.Sprintf("%v", secondErr)) + */ + }) + } + Convey("Unknown command", func() { + cmd := exec.Command(scwcli, "boogie") + out, ec, err := runCommandWithOutput(cmd) + So(out, ShouldContainSubstring, "scw: unknown subcommand boogie") + So(ec, ShouldEqual, 1) + So(fmt.Sprintf("%s", err), ShouldEqual, "exit status 1") + }) + }) +} diff --git a/integration-cli/utils_test.go b/integration-cli/utils_test.go new file mode 100644 index 0000000000..9f60449a76 --- /dev/null +++ b/integration-cli/utils_test.go @@ -0,0 +1,37 @@ +package integrationcli + +import ( + "fmt" + "os/exec" + "syscall" +) + +func getExitCode(err error) (int, error) { + exitCode := 0 + if exiterr, ok := err.(*exec.ExitError); ok { + if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok { + return procExit.ExitStatus(), nil + } + } + return exitCode, fmt.Errorf("failed to get exit code") +} + +func processExitCode(err error) (exitCode int) { + if err != nil { + var exiterr error + if exitCode, exiterr = getExitCode(err); exiterr != nil { + // TODO: Fix this so we check the error's text. + // we've failed to retrieve exit code, so we set it to 127 + exitCode = 127 + } + } + return +} + +func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) { + exitCode = 0 + out, err := cmd.CombinedOutput() + exitCode = processExitCode(err) + output = string(out) + return +} diff --git a/integration-cli/vars_test.go b/integration-cli/vars_test.go new file mode 100644 index 0000000000..d8cc799a36 --- /dev/null +++ b/integration-cli/vars_test.go @@ -0,0 +1,25 @@ +package integrationcli + +var ( + scwcli string = "../scw" + publicCommands []string = []string{ + "help", "attach", "commit", "cp", "create", + "events", "exec", "history", "images", "info", + "inspect", "kill", "login", "logout", "logs", + "port", "ps", "rename", "restart", "rm", "rmi", + "run", "search", "start", "stop", "tag", "top", + "version", "wait", + } + secretCommands []string = []string{ + "_patch", "_completion", "_flush-cache", + } + publicOptions []string = []string{ + "-h, --help=false", + "-D, --debug=false", + "-V, --verbose=false", + "-q, --quiet=false", + "--api-endpoint=APIEndPoint", + "--sensitive=false", + "-v, --version=false", + } +) diff --git a/vendor/github.com/docker/docker/pkg/archive/archive.go b/vendor/github.com/docker/docker/pkg/archive/archive.go index c4b80b5c81..e4bdc08aec 100644 --- a/vendor/github.com/docker/docker/pkg/archive/archive.go +++ b/vendor/github.com/docker/docker/pkg/archive/archive.go @@ -405,6 +405,10 @@ func Tar(path string, compression Compression) (io.ReadCloser, error) { // paths are included in `options.IncludeFiles` (if non-nil) or not in `options.ExcludePatterns`. func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) { + // Fix the source path to work with long path names. This is a no-op + // on platforms other than Windows. + srcPath = fixVolumePathPrefix(srcPath) + patterns, patDirs, exceptions, err := fileutils.CleanPatterns(options.ExcludePatterns) if err != nil { @@ -474,9 +478,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error) for _, include := range options.IncludeFiles { rebaseName := options.RebaseNames[include] - // We can't use filepath.Join(srcPath, include) because this will - // clean away a trailing "." or "/" which may be important. - walkRoot := strings.Join([]string{srcPath, include}, string(filepath.Separator)) + walkRoot := getWalkRoot(srcPath, include) filepath.Walk(walkRoot, func(filePath string, f os.FileInfo, err error) error { if err != nil { logrus.Debugf("Tar: Can't stat file %s to tar: %s", srcPath, err) diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_unix.go b/vendor/github.com/docker/docker/pkg/archive/archive_unix.go index a3ec8e793c..8405479f7b 100644 --- a/vendor/github.com/docker/docker/pkg/archive/archive_unix.go +++ b/vendor/github.com/docker/docker/pkg/archive/archive_unix.go @@ -6,11 +6,26 @@ import ( "archive/tar" "errors" "os" + "path/filepath" "syscall" "github.com/scaleway/scaleway-cli/vendor/github.com/docker/docker/pkg/system" ) +// fixVolumePathPrefix does platform specific processing to ensure that if +// the path being passed in is not in a volume path format, convert it to one. +func fixVolumePathPrefix(srcPath string) string { + return srcPath +} + +// getWalkRoot calculates the root path when performing a TarWithOptions. +// We use a seperate function as this is platform specific. On Linux, we +// can't use filepath.Join(srcPath,include) because this will clean away +// a trailing "." or "/" which may be important. +func getWalkRoot(srcPath string, include string) string { + return srcPath + string(filepath.Separator) + include +} + // CanonicalTarNameForPath returns platform-specific filepath // to canonical posix-style path for tar archival. p is relative // path. diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_windows.go b/vendor/github.com/docker/docker/pkg/archive/archive_windows.go index 10db4bd00e..2500785667 100644 --- a/vendor/github.com/docker/docker/pkg/archive/archive_windows.go +++ b/vendor/github.com/docker/docker/pkg/archive/archive_windows.go @@ -6,9 +6,25 @@ import ( "archive/tar" "fmt" "os" + "path/filepath" "strings" ) +// fixVolumePathPrefix does platform specific processing to ensure that if +// the path being passed in is not in a volume path format, convert it to one. +func fixVolumePathPrefix(srcPath string) string { + if !strings.HasPrefix(srcPath, `\\?\`) { + srcPath = `\\?\` + srcPath + } + return srcPath +} + +// getWalkRoot calculates the root path when performing a TarWithOptions. +// We use a seperate function as this is platform specific. +func getWalkRoot(srcPath string, include string) string { + return filepath.Join(srcPath, include) +} + // canonicalTarNameForPath returns platform-specific filepath // to canonical posix-style path for tar archival. p is relative // path. From 521345c1c68051a9079ffa05710ae3c0806e7f1e Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Wed, 19 Aug 2015 19:18:28 +0200 Subject: [PATCH 02/17] Refactored pkg/cli and cmd/scw to allow cli testing --- .travis.yml | 1 - Makefile | 11 -- cmd/scw/main.go | 190 +------------------------ integration-cli/custom_assert_test.go | 10 -- integration-cli/scw_cli_help_test.go | 98 ------------- integration-cli/utils_test.go | 37 ----- integration-cli/vars_test.go | 25 ---- pkg/cli/{attach.go => cmd_attach.go} | 4 +- pkg/cli/{commit.go => cmd_commit.go} | 4 +- pkg/cli/{cp.go => cmd_cp.go} | 4 +- pkg/cli/{create.go => cmd_create.go} | 4 +- pkg/cli/{events.go => cmd_events.go} | 4 +- pkg/cli/{exec.go => cmd_exec.go} | 4 +- pkg/cli/{help.go => cmd_help.go} | 20 ++- pkg/cli/{history.go => cmd_history.go} | 4 +- pkg/cli/{images.go => cmd_images.go} | 4 +- pkg/cli/{info.go => cmd_info.go} | 4 +- pkg/cli/{inspect.go => cmd_inspect.go} | 4 +- pkg/cli/{kill.go => cmd_kill.go} | 4 +- pkg/cli/{login.go => cmd_login.go} | 4 +- pkg/cli/{logout.go => cmd_logout.go} | 4 +- pkg/cli/{logs.go => cmd_logs.go} | 4 +- pkg/cli/{port.go => cmd_port.go} | 4 +- pkg/cli/{ps.go => cmd_ps.go} | 4 +- pkg/cli/{rename.go => cmd_rename.go} | 4 +- pkg/cli/{restart.go => cmd_restart.go} | 4 +- pkg/cli/{rm.go => cmd_rm.go} | 4 +- pkg/cli/{rmi.go => cmd_rmi.go} | 4 +- pkg/cli/{run.go => cmd_run.go} | 4 +- pkg/cli/{search.go => cmd_search.go} | 4 +- pkg/cli/{start.go => cmd_start.go} | 4 +- pkg/cli/{stop.go => cmd_stop.go} | 4 +- pkg/cli/{tag.go => cmd_tag.go} | 4 +- pkg/cli/{top.go => cmd_top.go} | 4 +- pkg/cli/{version.go => cmd_version.go} | 4 +- pkg/cli/{wait.go => cmd_wait.go} | 4 +- pkg/cli/command.go | 54 +++++-- pkg/cli/flags.go | 31 ++++ pkg/cli/main.go | 188 ++++++++++++++++++++++++ pkg/cli/main_test.go | 187 ++++++++++++++++++++++++ pkg/cli/test.go | 68 +++++++++ pkg/cli/x_completion.go | 4 +- pkg/cli/x_flushcache.go | 4 +- pkg/cli/x_patch.go | 4 +- pkg/commands/command.go | 12 +- pkg/commands/command_test.go | 8 +- 46 files changed, 606 insertions(+), 458 deletions(-) delete mode 100644 integration-cli/custom_assert_test.go delete mode 100644 integration-cli/scw_cli_help_test.go delete mode 100644 integration-cli/utils_test.go delete mode 100644 integration-cli/vars_test.go rename pkg/cli/{attach.go => cmd_attach.go} (95%) rename pkg/cli/{commit.go => cmd_commit.go} (95%) rename pkg/cli/{cp.go => cmd_cp.go} (97%) rename pkg/cli/{create.go => cmd_create.go} (97%) rename pkg/cli/{events.go => cmd_events.go} (93%) rename pkg/cli/{exec.go => cmd_exec.go} (96%) rename pkg/cli/{help.go => cmd_help.go} (86%) rename pkg/cli/{history.go => cmd_history.go} (95%) rename pkg/cli/{images.go => cmd_images.go} (95%) rename pkg/cli/{info.go => cmd_info.go} (92%) rename pkg/cli/{inspect.go => cmd_inspect.go} (97%) rename pkg/cli/{kill.go => cmd_kill.go} (94%) rename pkg/cli/{login.go => cmd_login.go} (95%) rename pkg/cli/{logout.go => cmd_logout.go} (93%) rename pkg/cli/{logs.go => cmd_logs.go} (94%) rename pkg/cli/{port.go => cmd_port.go} (94%) rename pkg/cli/{ps.go => cmd_ps.go} (96%) rename pkg/cli/{rename.go => cmd_rename.go} (93%) rename pkg/cli/{restart.go => cmd_restart.go} (93%) rename pkg/cli/{rm.go => cmd_rm.go} (94%) rename pkg/cli/{rmi.go => cmd_rmi.go} (93%) rename pkg/cli/{run.go => cmd_run.go} (98%) rename pkg/cli/{search.go => cmd_search.go} (94%) rename pkg/cli/{start.go => cmd_start.go} (95%) rename pkg/cli/{stop.go => cmd_stop.go} (96%) rename pkg/cli/{tag.go => cmd_tag.go} (93%) rename pkg/cli/{top.go => cmd_top.go} (94%) rename pkg/cli/{version.go => cmd_version.go} (93%) rename pkg/cli/{wait.go => cmd_wait.go} (93%) create mode 100644 pkg/cli/flags.go create mode 100644 pkg/cli/main.go create mode 100644 pkg/cli/main_test.go create mode 100644 pkg/cli/test.go diff --git a/.travis.yml b/.travis.yml index 264069f629..17fe1dc8be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,4 +37,3 @@ script: - make travis_run - make cover - goveralls -service=travis-ci -v -coverprofile=profile.out - - make integration-cli diff --git a/Makefile b/Makefile index ebdcc2fa53..30b24dc0a8 100644 --- a/Makefile +++ b/Makefile @@ -152,14 +152,3 @@ party: convey: go get github.com/smartystreets/goconvey goconvey -cover -port=9042 -workDir="$(realpath .)/pkg" -depth=-1 - - -.PHONY: integration-cli -integration-cli: - go test -v ./integration-cli - - -.PHONY: integration-cli -convey-integration-cli: - go get github.com/smartystreets/goconvey - goconvey -cover -port=9043 -workDir="$(realpath .)/integration-cli" -depth=-1 diff --git a/cmd/scw/main.go b/cmd/scw/main.go index 53502aa334..7b89d9c56a 100644 --- a/cmd/scw/main.go +++ b/cmd/scw/main.go @@ -6,196 +6,16 @@ package main import ( - "encoding/json" - "fmt" - "io/ioutil" "os" - log "github.com/scaleway/scaleway-cli/vendor/github.com/Sirupsen/logrus" - flag "github.com/scaleway/scaleway-cli/vendor/github.com/docker/docker/pkg/mflag" - - "github.com/scaleway/scaleway-cli/pkg/api" - cmds "github.com/scaleway/scaleway-cli/pkg/cli" - "github.com/scaleway/scaleway-cli/pkg/scwversion" - "github.com/scaleway/scaleway-cli/pkg/utils" -) - -// CommandListOpts holds a list of parameters -type CommandListOpts struct { - Values *[]string -} - -// NewListOpts create an empty CommandListOpts -func NewListOpts() CommandListOpts { - var values []string - return CommandListOpts{ - Values: &values, - } -} - -// String returns a string representation of a CommandListOpts object -func (opts *CommandListOpts) String() string { - return fmt.Sprintf("%v", []string((*opts.Values))) -} - -// Set appends a new value to a CommandListOpts -func (opts *CommandListOpts) Set(value string) error { - (*opts.Values) = append((*opts.Values), value) - return nil -} - -func commandUsage(name string) { -} - -var ( - flAPIEndPoint *string - flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode") - flVerbose = flag.Bool([]string{"V", "-verbose"}, false, "Enable verbose mode") - flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit") - flQuiet = flag.Bool([]string{"q", "-quiet"}, false, "Enable quiet mode") - flSensitive = flag.Bool([]string{"-sensitive"}, false, "Show sensitive data in outputs, i.e. API Token/Organization") + "github.com/scaleway/scaleway-cli/pkg/cli" + "github.com/scaleway/scaleway-cli/vendor/github.com/Sirupsen/logrus" ) func main() { - config, cfgErr := getConfig() - if cfgErr != nil && !os.IsNotExist(cfgErr) { - log.Fatalf("Unable to open .scwrc config file: %v", cfgErr) - } - - if config != nil { - defaultComputeAPI := os.Getenv("scaleway_api_endpoint") - if defaultComputeAPI == "" { - defaultComputeAPI = config.ComputeAPI - } - flAPIEndPoint = flag.String([]string{"-api-endpoint"}, defaultComputeAPI, "Set the API endpoint") - } - flag.Parse() - - if *flVersion { - showVersion() - return - } - - if flAPIEndPoint != nil { - os.Setenv("scaleway_api_endpoint", *flAPIEndPoint) - } - - if *flSensitive { - os.Setenv("SCW_SENSITIVE", "1") - } - - if *flDebug { - os.Setenv("DEBUG", "1") - } - - utils.Quiet(*flQuiet) - initLogging(os.Getenv("DEBUG") != "", *flVerbose) - - args := flag.Args() - if len(args) < 1 { - usage() - } - name := args[0] - - args = args[1:] - - for _, cmd := range cmds.Commands { - if cmd.Name() == name { - cmd.Flag.SetOutput(ioutil.Discard) - err := cmd.Flag.Parse(args) - if err != nil { - log.Fatalf("usage: scw %s", cmd.UsageLine) - } - if cmd.Name() != "login" && cmd.Name() != "help" && cmd.Name() != "version" { - if cfgErr != nil { - if name != "login" && config == nil { - log.Debugf("cfgErr: %v", cfgErr) - fmt.Fprintf(os.Stderr, "You need to login first: 'scw login'\n") - os.Exit(1) - } - } - api, err := getScalewayAPI() - if err != nil { - log.Fatalf("unable to initialize scw api: %s", err) - } - cmd.API = api - } - err = cmd.Exec(cmd, cmd.Flag.Args()) - if err != nil { - log.Fatalf("Cannot execute '%s': %v", cmd.Name(), err) - } - if cmd.API != nil { - cmd.API.Sync() - } - os.Exit(0) - } - } - - log.Fatalf("scw: unknown subcommand %s\nRun 'scw help' for usage.", name) -} - -func usage() { - cmds.CmdHelp.Exec(cmds.CmdHelp, []string{}) - os.Exit(1) -} - -// getConfig returns the Scaleway CLI config file for the current user -func getConfig() (*api.Config, error) { - scwrcPath, err := utils.GetConfigFilePath() - if err != nil { - return nil, err - } - - stat, err := os.Stat(scwrcPath) - // we don't care if it fails, the user just won't see the warning - if err == nil { - mode := stat.Mode() - if mode&0066 != 0 { - log.Fatalf("Permissions %#o for .scwrc are too open.", mode) - } - } - - file, err := ioutil.ReadFile(scwrcPath) + ec, err := cli.Start(os.Args[1:], nil) if err != nil { - return nil, err - } - var config api.Config - err = json.Unmarshal(file, &config) - if err != nil { - return nil, err - } - // check if he has an old scwrc version - if config.AccountAPI == "" { - config.AccountAPI = "https://account.scaleway.com" - config.Save() - } - if os.Getenv("scaleway_api_endpoint") == "" { - os.Setenv("scaleway_api_endpoint", config.ComputeAPI) - } - return &config, nil -} - -// getScalewayAPI returns a ScalewayAPI using the user config file -func getScalewayAPI() (*api.ScalewayAPI, error) { - // We already get config globally, but whis way we can get explicit error when trying to create a ScalewayAPI object - config, err := getConfig() - if err != nil { - return nil, err - } - return api.NewScalewayAPI(os.Getenv("scaleway_api_endpoint"), config.AccountAPI, config.Organization, config.Token) -} - -func showVersion() { - fmt.Printf("scw version %s, build %s\n", scwversion.VERSION, scwversion.GITCOMMIT) -} - -func initLogging(debug bool, verbose bool) { - log.SetOutput(os.Stderr) - if debug { - log.SetLevel(log.DebugLevel) - } else if verbose { - log.SetLevel(log.InfoLevel) - } else { - log.SetLevel(log.WarnLevel) + logrus.Fatalf("%s", err) } + os.Exit(ec) } diff --git a/integration-cli/custom_assert_test.go b/integration-cli/custom_assert_test.go deleted file mode 100644 index d93e5949f4..0000000000 --- a/integration-cli/custom_assert_test.go +++ /dev/null @@ -1,10 +0,0 @@ -package integrationcli - -import "fmt" - -func shouldFitInTerminal(actual interface{}, expected ...interface{}) string { - if len(actual.(string)) < 80 { - return "" - } - return fmt.Sprintf("len(%q)\n -> %d chars (> 80 chars)", actual, len(actual.(string))) -} diff --git a/integration-cli/scw_cli_help_test.go b/integration-cli/scw_cli_help_test.go deleted file mode 100644 index 3cd5203e09..0000000000 --- a/integration-cli/scw_cli_help_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package integrationcli - -import ( - "fmt" - "os/exec" - "strings" - "testing" - - . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" -) - -func TestHelp(t *testing.T) { - Convey("Testing 'scw help'", t, func() { - Convey("scw help", func() { - cmd := exec.Command(scwcli, "help") - out, ec, err := runCommandWithOutput(cmd) - So(ec, ShouldEqual, 0) - So(err, ShouldBeNil) - - // headers & footers - So(out, ShouldContainSubstring, "Usage: scw [OPTIONS] COMMAND [arg...]") - So(out, ShouldContainSubstring, "Interact with Scaleway from the command line.") - So(out, ShouldContainSubstring, "Run 'scw COMMAND --help' for more information on a command.") - - // options - So(out, ShouldContainSubstring, "Options:") - for _, option := range publicOptions { - So(out, ShouldContainSubstring, " "+option) - } - - // public commands - So(out, ShouldContainSubstring, "Commands:") - for _, command := range publicCommands { - So(out, ShouldContainSubstring, " "+command) - } - - // secret commands - for _, command := range secretCommands { - So(out, ShouldNotContainSubstring, " "+command) - } - - // :lipstick: - for _, line := range strings.Split(out, "\n") { - if false { // FIXME: disabled for now - So(line, shouldFitInTerminal) - } - } - - // FIXME: count amount of options/commands, and panic if amount is different - }) - - commands := append(publicCommands, secretCommands...) - for _, command := range commands { - if command == "help" { - // FIXME: this should not be a special case - continue - } - Convey(fmt.Sprintf("scw --help %s", command), func() { - cmd := exec.Command(scwcli, command, "--help") - _, ec, err := runCommandWithOutput(cmd) - - // exit code - So(ec, ShouldEqual, 1) - So(fmt.Sprintf("%s", err), ShouldEqual, "exit status 1") - - // FIXME: disabled for now because it depends of 'scw login' - /* - // Header - So(out, ShouldContainSubstring, fmt.Sprintf("Usage: scw %s", command)) - // FIXME: test description - // FIXME: test parameters - - // Options - So(out, ShouldContainSubstring, "Options:") - So(out, ShouldContainSubstring, " -h, --help=false") - // FIXME: test other options - - // Examples - // FIXME: todo - //So(out, ShouldContainSubstring, "Examples:") - - secondCmd := exec.Command(scwcli, "help", command) - secondOut, secondEc, secondErr := runCommandWithOutput(secondCmd) - So(out, ShouldEqual, secondOut) - So(ec, ShouldEqual, secondEc) - So(fmt.Sprintf("%v", err), ShouldEqual, fmt.Sprintf("%v", secondErr)) - */ - }) - } - Convey("Unknown command", func() { - cmd := exec.Command(scwcli, "boogie") - out, ec, err := runCommandWithOutput(cmd) - So(out, ShouldContainSubstring, "scw: unknown subcommand boogie") - So(ec, ShouldEqual, 1) - So(fmt.Sprintf("%s", err), ShouldEqual, "exit status 1") - }) - }) -} diff --git a/integration-cli/utils_test.go b/integration-cli/utils_test.go deleted file mode 100644 index 9f60449a76..0000000000 --- a/integration-cli/utils_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package integrationcli - -import ( - "fmt" - "os/exec" - "syscall" -) - -func getExitCode(err error) (int, error) { - exitCode := 0 - if exiterr, ok := err.(*exec.ExitError); ok { - if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok { - return procExit.ExitStatus(), nil - } - } - return exitCode, fmt.Errorf("failed to get exit code") -} - -func processExitCode(err error) (exitCode int) { - if err != nil { - var exiterr error - if exitCode, exiterr = getExitCode(err); exiterr != nil { - // TODO: Fix this so we check the error's text. - // we've failed to retrieve exit code, so we set it to 127 - exitCode = 127 - } - } - return -} - -func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) { - exitCode = 0 - out, err := cmd.CombinedOutput() - exitCode = processExitCode(err) - output = string(out) - return -} diff --git a/integration-cli/vars_test.go b/integration-cli/vars_test.go deleted file mode 100644 index d8cc799a36..0000000000 --- a/integration-cli/vars_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package integrationcli - -var ( - scwcli string = "../scw" - publicCommands []string = []string{ - "help", "attach", "commit", "cp", "create", - "events", "exec", "history", "images", "info", - "inspect", "kill", "login", "logout", "logs", - "port", "ps", "rename", "restart", "rm", "rmi", - "run", "search", "start", "stop", "tag", "top", - "version", "wait", - } - secretCommands []string = []string{ - "_patch", "_completion", "_flush-cache", - } - publicOptions []string = []string{ - "-h, --help=false", - "-D, --debug=false", - "-V, --verbose=false", - "-q, --quiet=false", - "--api-endpoint=APIEndPoint", - "--sensitive=false", - "-v, --version=false", - } -) diff --git a/pkg/cli/attach.go b/pkg/cli/cmd_attach.go similarity index 95% rename from pkg/cli/attach.go rename to pkg/cli/cmd_attach.go index fdcfc2205f..d484872995 100644 --- a/pkg/cli/attach.go +++ b/pkg/cli/cmd_attach.go @@ -29,10 +29,10 @@ var attachNoStdin bool // --no-stdin flag func runAttach(cmd *Command, rawArgs []string) error { if attachHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.AttachArgs{ diff --git a/pkg/cli/commit.go b/pkg/cli/cmd_commit.go similarity index 95% rename from pkg/cli/commit.go rename to pkg/cli/cmd_commit.go index a1c4a96e33..d469479570 100644 --- a/pkg/cli/commit.go +++ b/pkg/cli/cmd_commit.go @@ -28,10 +28,10 @@ var commitHelp bool // -h, --help flag func runCommit(cmd *Command, rawArgs []string) error { if commitHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.CommitArgs{ diff --git a/pkg/cli/cp.go b/pkg/cli/cmd_cp.go similarity index 97% rename from pkg/cli/cp.go rename to pkg/cli/cmd_cp.go index 3dc226f2b8..940f2ab613 100644 --- a/pkg/cli/cp.go +++ b/pkg/cli/cmd_cp.go @@ -39,10 +39,10 @@ var cpGateway string // -g, --gateway flag func runCp(cmd *Command, rawArgs []string) error { if cpHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 2 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.CpArgs{ diff --git a/pkg/cli/create.go b/pkg/cli/cmd_create.go similarity index 97% rename from pkg/cli/create.go rename to pkg/cli/cmd_create.go index 65dd30a2c4..5bf16a2e3f 100644 --- a/pkg/cli/create.go +++ b/pkg/cli/cmd_create.go @@ -44,10 +44,10 @@ var createTmpSSHKey bool // --tmp-ssh-key flag func runCreate(cmd *Command, rawArgs []string) error { if createHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.CreateArgs{ diff --git a/pkg/cli/events.go b/pkg/cli/cmd_events.go similarity index 93% rename from pkg/cli/events.go rename to pkg/cli/cmd_events.go index 11c5719476..f6655fecf7 100644 --- a/pkg/cli/events.go +++ b/pkg/cli/cmd_events.go @@ -22,10 +22,10 @@ var eventsHelp bool // -h, --help flag func runEvents(cmd *Command, rawArgs []string) error { if eventsHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 0 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.EventsArgs{} diff --git a/pkg/cli/exec.go b/pkg/cli/cmd_exec.go similarity index 96% rename from pkg/cli/exec.go rename to pkg/cli/cmd_exec.go index d93bd6ef2f..90cd6bf075 100644 --- a/pkg/cli/exec.go +++ b/pkg/cli/cmd_exec.go @@ -40,10 +40,10 @@ var execGateway string // -g, --gateway flag func runExec(cmd *Command, rawArgs []string) error { if execHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.ExecArgs{ diff --git a/pkg/cli/help.go b/pkg/cli/cmd_help.go similarity index 86% rename from pkg/cli/help.go rename to pkg/cli/cmd_help.go index c6494e09aa..c0d4659e8f 100644 --- a/pkg/cli/help.go +++ b/pkg/cli/cmd_help.go @@ -5,7 +5,6 @@ package cli import ( - "os" "text/template" "github.com/scaleway/scaleway-cli/vendor/github.com/Sirupsen/logrus" @@ -53,28 +52,27 @@ Commands: Run 'scw COMMAND --help' for more information on a command. ` -func runHelp(cmd *Command, args []string) error { +func runHelp(cmd *Command, rawArgs []string) error { if waitHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } - if len(args) > 1 { - cmd.PrintShortUsage() + if len(rawArgs) > 1 { + return cmd.PrintShortUsage() } - if len(args) == 1 { - name := args[0] + if len(rawArgs) == 1 { + name := rawArgs[0] for _, command := range Commands { if command.Name() == name { - command.PrintUsage() + return command.PrintUsage() } } logrus.Fatalf("Unknown help topic `%s`. Run 'scw help'.", name) } else { t := template.New("top") template.Must(t.Parse(helpTemplate)) - if err := t.Execute(os.Stdout, Commands); err != nil { - return err - } + ctx := cmd.GetContext(rawArgs) + return t.Execute(ctx.Stdout, Commands) } return nil } diff --git a/pkg/cli/history.go b/pkg/cli/cmd_history.go similarity index 95% rename from pkg/cli/history.go rename to pkg/cli/cmd_history.go index 2e752eb309..e9126cd3b6 100644 --- a/pkg/cli/history.go +++ b/pkg/cli/cmd_history.go @@ -26,10 +26,10 @@ var historyHelp bool // -h, --help flag func runHistory(cmd *Command, rawArgs []string) error { if historyHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.HistoryArgs{ diff --git a/pkg/cli/images.go b/pkg/cli/cmd_images.go similarity index 95% rename from pkg/cli/images.go rename to pkg/cli/cmd_images.go index 45859c694a..8a3647307b 100644 --- a/pkg/cli/images.go +++ b/pkg/cli/cmd_images.go @@ -28,10 +28,10 @@ var imagesHelp bool // -h, --help flag func runImages(cmd *Command, rawArgs []string) error { if imagesHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 0 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.ImagesArgs{ diff --git a/pkg/cli/info.go b/pkg/cli/cmd_info.go similarity index 92% rename from pkg/cli/info.go rename to pkg/cli/cmd_info.go index db3a38785a..2612677168 100644 --- a/pkg/cli/info.go +++ b/pkg/cli/cmd_info.go @@ -22,10 +22,10 @@ var infoHelp bool // -h, --help flag func runInfo(cmd *Command, rawArgs []string) error { if infoHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 0 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.InfoArgs{} diff --git a/pkg/cli/inspect.go b/pkg/cli/cmd_inspect.go similarity index 97% rename from pkg/cli/inspect.go rename to pkg/cli/cmd_inspect.go index 14c8de0745..ecaf54e85a 100644 --- a/pkg/cli/inspect.go +++ b/pkg/cli/cmd_inspect.go @@ -43,10 +43,10 @@ var inspectHelp bool // -h, --help flag func runInspect(cmd *Command, rawArgs []string) error { if inspectHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.InspectArgs{ diff --git a/pkg/cli/kill.go b/pkg/cli/cmd_kill.go similarity index 94% rename from pkg/cli/kill.go rename to pkg/cli/cmd_kill.go index a4c1fb441f..8e8898e815 100644 --- a/pkg/cli/kill.go +++ b/pkg/cli/cmd_kill.go @@ -25,10 +25,10 @@ var killGateway string // -g, --gateway flag func runKill(cmd *Command, rawArgs []string) error { if killHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.KillArgs{ diff --git a/pkg/cli/login.go b/pkg/cli/cmd_login.go similarity index 95% rename from pkg/cli/login.go rename to pkg/cli/cmd_login.go index bf76d93f00..6a0c7ca0e0 100644 --- a/pkg/cli/login.go +++ b/pkg/cli/cmd_login.go @@ -31,10 +31,10 @@ var loginHelp bool // -h, --help flag func runLogin(cmd *Command, rawArgs []string) error { if loginHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 0 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.LoginArgs{ diff --git a/pkg/cli/logout.go b/pkg/cli/cmd_logout.go similarity index 93% rename from pkg/cli/logout.go rename to pkg/cli/cmd_logout.go index 4f045bd212..23b5b0ecf2 100644 --- a/pkg/cli/logout.go +++ b/pkg/cli/cmd_logout.go @@ -22,10 +22,10 @@ var logoutHelp bool // -h, --help flag func runLogout(cmd *Command, rawArgs []string) error { if logoutHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 0 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.LogoutArgs{} diff --git a/pkg/cli/logs.go b/pkg/cli/cmd_logs.go similarity index 94% rename from pkg/cli/logs.go rename to pkg/cli/cmd_logs.go index c18e657cb1..d55c5125e6 100644 --- a/pkg/cli/logs.go +++ b/pkg/cli/cmd_logs.go @@ -24,10 +24,10 @@ var logsGateway string // -g, --gateway flag func runLogs(cmd *Command, rawArgs []string) error { if logsHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.LogsArgs{ diff --git a/pkg/cli/port.go b/pkg/cli/cmd_port.go similarity index 94% rename from pkg/cli/port.go rename to pkg/cli/cmd_port.go index dd5c18f1c1..6fa379a25f 100644 --- a/pkg/cli/port.go +++ b/pkg/cli/cmd_port.go @@ -24,10 +24,10 @@ var portGateway string // -g, --gateway flag func runPort(cmd *Command, rawArgs []string) error { if portHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.PortArgs{ diff --git a/pkg/cli/ps.go b/pkg/cli/cmd_ps.go similarity index 96% rename from pkg/cli/ps.go rename to pkg/cli/cmd_ps.go index 396ef331cc..c094ec7237 100644 --- a/pkg/cli/ps.go +++ b/pkg/cli/cmd_ps.go @@ -32,10 +32,10 @@ var psHelp bool // -h, --help flag func runPs(cmd *Command, rawArgs []string) error { if psHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 0 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.PsArgs{ diff --git a/pkg/cli/rename.go b/pkg/cli/cmd_rename.go similarity index 93% rename from pkg/cli/rename.go rename to pkg/cli/cmd_rename.go index 49c0280cb3..b75ff09df9 100644 --- a/pkg/cli/rename.go +++ b/pkg/cli/cmd_rename.go @@ -22,10 +22,10 @@ var renameHelp bool // -h, --help flag func runRename(cmd *Command, rawArgs []string) error { if renameHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 2 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.RenameArgs{ diff --git a/pkg/cli/restart.go b/pkg/cli/cmd_restart.go similarity index 93% rename from pkg/cli/restart.go rename to pkg/cli/cmd_restart.go index 4c100932ff..a9075f77cb 100644 --- a/pkg/cli/restart.go +++ b/pkg/cli/cmd_restart.go @@ -22,10 +22,10 @@ var restartHelp bool // -h, --help flag func runRestart(cmd *Command, rawArgs []string) error { if restartHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.RestartArgs{ diff --git a/pkg/cli/rm.go b/pkg/cli/cmd_rm.go similarity index 94% rename from pkg/cli/rm.go rename to pkg/cli/cmd_rm.go index 9375c17e4e..1bb3be011a 100644 --- a/pkg/cli/rm.go +++ b/pkg/cli/cmd_rm.go @@ -27,10 +27,10 @@ var rmHelp bool // -h, --help flag func runRm(cmd *Command, rawArgs []string) error { if rmHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.RmArgs{ diff --git a/pkg/cli/rmi.go b/pkg/cli/cmd_rmi.go similarity index 93% rename from pkg/cli/rmi.go rename to pkg/cli/cmd_rmi.go index 6c0f04e784..0e76e1bc01 100644 --- a/pkg/cli/rmi.go +++ b/pkg/cli/cmd_rmi.go @@ -26,10 +26,10 @@ var rmiHelp bool // -h, --help flag func runRmi(cmd *Command, rawArgs []string) error { if rmiHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.RmiArgs{ diff --git a/pkg/cli/run.go b/pkg/cli/cmd_run.go similarity index 98% rename from pkg/cli/run.go rename to pkg/cli/cmd_run.go index 4966990f7d..27657a2100 100644 --- a/pkg/cli/run.go +++ b/pkg/cli/cmd_run.go @@ -58,10 +58,10 @@ var runTmpSSHKey bool // --tmp-ssh-key flag func runRun(cmd *Command, rawArgs []string) error { if runHelpFlag { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } if runAttachFlag && len(rawArgs) > 1 { return fmt.Errorf("conflicting options: -a and COMMAND") diff --git a/pkg/cli/search.go b/pkg/cli/cmd_search.go similarity index 94% rename from pkg/cli/search.go rename to pkg/cli/cmd_search.go index a3502df9e7..3c498140a6 100644 --- a/pkg/cli/search.go +++ b/pkg/cli/cmd_search.go @@ -24,10 +24,10 @@ var searchHelp bool // -h, --help flag func runSearch(cmd *Command, rawArgs []string) error { if searchHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.SearchArgs{ diff --git a/pkg/cli/start.go b/pkg/cli/cmd_start.go similarity index 95% rename from pkg/cli/start.go rename to pkg/cli/cmd_start.go index 6f4a9c0e59..75ce87af24 100644 --- a/pkg/cli/start.go +++ b/pkg/cli/cmd_start.go @@ -26,10 +26,10 @@ var startHelp bool // -h, --help flag func runStart(cmd *Command, rawArgs []string) error { if startHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.StartArgs{ diff --git a/pkg/cli/stop.go b/pkg/cli/cmd_stop.go similarity index 96% rename from pkg/cli/stop.go rename to pkg/cli/cmd_stop.go index 2e8c896e01..613902b7c4 100644 --- a/pkg/cli/stop.go +++ b/pkg/cli/cmd_stop.go @@ -34,10 +34,10 @@ var stopW bool // -w, --wait flat func runStop(cmd *Command, rawArgs []string) error { if stopHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.StopArgs{ diff --git a/pkg/cli/tag.go b/pkg/cli/cmd_tag.go similarity index 93% rename from pkg/cli/tag.go rename to pkg/cli/cmd_tag.go index 1be7732edb..bc779935a3 100644 --- a/pkg/cli/tag.go +++ b/pkg/cli/cmd_tag.go @@ -22,10 +22,10 @@ var tagHelp bool // -h, --help flag func runTag(cmd *Command, rawArgs []string) error { if tagHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 2 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.TagArgs{ diff --git a/pkg/cli/top.go b/pkg/cli/cmd_top.go similarity index 94% rename from pkg/cli/top.go rename to pkg/cli/cmd_top.go index 849a327456..adbffde3a8 100644 --- a/pkg/cli/top.go +++ b/pkg/cli/cmd_top.go @@ -24,10 +24,10 @@ var topGateway string // -g, --gateway flag func runTop(cmd *Command, rawArgs []string) error { if topHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.TopArgs{ diff --git a/pkg/cli/version.go b/pkg/cli/cmd_version.go similarity index 93% rename from pkg/cli/version.go rename to pkg/cli/cmd_version.go index 3b1e5e579e..785e8e9527 100644 --- a/pkg/cli/version.go +++ b/pkg/cli/cmd_version.go @@ -22,10 +22,10 @@ var versionHelp bool // -h, --help flag func runVersion(cmd *Command, rawArgs []string) error { if versionHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) != 0 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.VersionArgs{} diff --git a/pkg/cli/wait.go b/pkg/cli/cmd_wait.go similarity index 93% rename from pkg/cli/wait.go rename to pkg/cli/cmd_wait.go index e2e5f94eeb..2849d79ce8 100644 --- a/pkg/cli/wait.go +++ b/pkg/cli/cmd_wait.go @@ -22,10 +22,10 @@ var waitHelp bool // -h, --help flag func runWait(cmd *Command, rawArgs []string) error { if waitHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(rawArgs) < 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } args := commands.WaitArgs{ diff --git a/pkg/cli/command.go b/pkg/cli/command.go index 13e5d8c318..584ad2a15d 100644 --- a/pkg/cli/command.go +++ b/pkg/cli/command.go @@ -7,6 +7,7 @@ package cli // Command is a Scaleway command import ( "bytes" + "errors" "fmt" "os" "strings" @@ -19,6 +20,12 @@ import ( "github.com/scaleway/scaleway-cli/pkg/commands" ) +// errors +var ( + ErrExitFailure = errors.New("exit 1") + ErrExitSuccess = errors.New("exit 0") +) + // Command contains everything needed by the cli main loop to calls the workflow, display help and usage, and the context type Command struct { // Exec executes the command @@ -44,18 +51,41 @@ type Command struct { // API is the interface used to communicate with Scaleway's API API *api.ScalewayAPI + + streams *commands.Streams } // GetContext returns a standard context, with real stdin, stdout, stderr, a configured API and raw arguments func (c *Command) GetContext(rawArgs []string) commands.CommandContext { - return commands.CommandContext{ - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, + ctx := commands.CommandContext{ Env: os.Environ(), RawArgs: rawArgs, API: c.API, } + + if c.streams != nil { + ctx.Streams = *c.streams + } else { + ctx.Streams = commands.Streams{ + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + } + } + + return ctx +} + +func (c *Command) Streams() *commands.Streams { + if c.streams != nil { + return c.streams + } + return &commands.Streams{ + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + } + } // Name returns the command's name @@ -68,20 +98,20 @@ func (c *Command) Name() string { return name } -// PrintUsage prints a full command usage and exits -func (c *Command) PrintUsage() { +// PrintUsage prints a full command usage +func (c *Command) PrintUsage() error { helpMessage, err := commandHelpMessage(c) if err != nil { logrus.Fatalf("%v", err) } - fmt.Fprintf(os.Stderr, "%s\n", helpMessage) - os.Exit(1) + fmt.Fprintf(c.Streams().Stdout, "%s\n", helpMessage) + return ErrExitFailure } -// PrintShortUsage prints a short command usage and exits -func (c *Command) PrintShortUsage() { - fmt.Fprintf(os.Stderr, "usage: scw %s. See 'scw %s --help'.\n", c.UsageLine, c.Name()) - os.Exit(1) +// PrintShortUsage prints a short command usage +func (c *Command) PrintShortUsage() error { + fmt.Fprintf(c.Streams().Stderr, "usage: scw %s. See 'scw %s --help'.\n", c.UsageLine, c.Name()) + return ErrExitFailure } // Options returns a string describing options of the command diff --git a/pkg/cli/flags.go b/pkg/cli/flags.go new file mode 100644 index 0000000000..663e02f231 --- /dev/null +++ b/pkg/cli/flags.go @@ -0,0 +1,31 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package cli + +import "fmt" + +// CommandListOpts holds a list of parameters +type CommandListOpts struct { + Values *[]string +} + +// NewListOpts create an empty CommandListOpts +func NewListOpts() CommandListOpts { + var values []string + return CommandListOpts{ + Values: &values, + } +} + +// String returns a string representation of a CommandListOpts object +func (opts *CommandListOpts) String() string { + return fmt.Sprintf("%v", []string((*opts.Values))) +} + +// Set appends a new value to a CommandListOpts +func (opts *CommandListOpts) Set(value string) error { + (*opts.Values) = append((*opts.Values), value) + return nil +} diff --git a/pkg/cli/main.go b/pkg/cli/main.go new file mode 100644 index 0000000000..a0898ae591 --- /dev/null +++ b/pkg/cli/main.go @@ -0,0 +1,188 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package cli + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + + "github.com/scaleway/scaleway-cli/vendor/github.com/Sirupsen/logrus" + flag "github.com/scaleway/scaleway-cli/vendor/github.com/docker/docker/pkg/mflag" + + "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-cli/pkg/commands" + "github.com/scaleway/scaleway-cli/pkg/scwversion" + "github.com/scaleway/scaleway-cli/pkg/utils" +) + +// global options +var ( + flAPIEndPoint *string + flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode") + flVerbose = flag.Bool([]string{"V", "-verbose"}, false, "Enable verbose mode") + flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit") + flQuiet = flag.Bool([]string{"q", "-quiet"}, false, "Enable quiet mode") + flSensitive = flag.Bool([]string{"-sensitive"}, false, "Show sensitive data in outputs, i.e. API Token/Organization") +) + +func Start(rawArgs []string, streams *commands.Streams) (int, error) { + if streams == nil { + streams = &commands.Streams{ + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + } + } + + flag.CommandLine.Parse(rawArgs) + + config, cfgErr := getConfig() + if cfgErr != nil && !os.IsNotExist(cfgErr) { + return 1, fmt.Errorf("unable to open .scwrc config file: %v", cfgErr) + } + + if config != nil { + defaultComputeAPI := os.Getenv("scaleway_api_endpoint") + if defaultComputeAPI == "" { + defaultComputeAPI = config.ComputeAPI + } + if flAPIEndPoint == nil { + flAPIEndPoint = flag.String([]string{"-api-endpoint"}, defaultComputeAPI, "Set the API endpoint") + } + } + + if *flVersion { + fmt.Fprintf(streams.Stderr, "scw version %s, build %s\n", scwversion.VERSION, scwversion.GITCOMMIT) + return 0, nil + } + + if flAPIEndPoint != nil { + os.Setenv("scaleway_api_endpoint", *flAPIEndPoint) + } + + if *flSensitive { + os.Setenv("SCW_SENSITIVE", "1") + } + + if *flDebug { + os.Setenv("DEBUG", "1") + } + + utils.Quiet(*flQuiet) + initLogging(os.Getenv("DEBUG") != "", *flVerbose, streams) + + args := flag.Args() + if len(args) < 1 { + CmdHelp.Exec(CmdHelp, []string{}) + return 1, nil + } + name := args[0] + + args = args[1:] + + // Apply default values + for _, cmd := range Commands { + cmd.streams = streams + } + + for _, cmd := range Commands { + if cmd.Name() == name { + cmd.Flag.SetOutput(ioutil.Discard) + err := cmd.Flag.Parse(args) + if err != nil { + return 1, fmt.Errorf("usage: scw %s", cmd.UsageLine) + } + if cmd.Name() != "login" && cmd.Name() != "help" && cmd.Name() != "version" { + if cfgErr != nil { + if name != "login" && config == nil { + logrus.Debugf("cfgErr: %v", cfgErr) + fmt.Fprintf(streams.Stderr, "You need to login first: 'scw login'\n") + return 1, nil + } + } + api, err := getScalewayAPI() + if err != nil { + return 1, fmt.Errorf("unable to initialize scw api: %s", err) + } + cmd.API = api + } + err = cmd.Exec(cmd, cmd.Flag.Args()) + switch err { + case nil: + case ErrExitFailure: + return 1, nil + case ErrExitSuccess: + return 0, nil + default: + return 1, fmt.Errorf("cannot execute '%s': %v", cmd.Name(), err) + } + if cmd.API != nil { + cmd.API.Sync() + } + return 0, nil + } + } + + return 1, fmt.Errorf("scw: unknown subcommand %s\nRun 'scw help' for usage.", name) +} + +// getConfig returns the Scaleway CLI config file for the current user +func getConfig() (*api.Config, error) { + scwrcPath, err := utils.GetConfigFilePath() + if err != nil { + return nil, err + } + + stat, err := os.Stat(scwrcPath) + // we don't care if it fails, the user just won't see the warning + if err == nil { + mode := stat.Mode() + if mode&0066 != 0 { + return nil, fmt.Errorf("permissions %#o for .scwrc are too open.", mode) + } + } + + file, err := ioutil.ReadFile(scwrcPath) + if err != nil { + return nil, err + } + var config api.Config + err = json.Unmarshal(file, &config) + if err != nil { + return nil, err + } + // check if he has an old scwrc version + if config.AccountAPI == "" { + config.AccountAPI = "https://account.scaleway.com" + config.Save() + } + if os.Getenv("scaleway_api_endpoint") == "" { + os.Setenv("scaleway_api_endpoint", config.ComputeAPI) + } + return &config, nil +} + +// getScalewayAPI returns a ScalewayAPI using the user config file +func getScalewayAPI() (*api.ScalewayAPI, error) { + // We already get config globally, but whis way we can get explicit error when trying to create a ScalewayAPI object + config, err := getConfig() + if err != nil { + return nil, err + } + return api.NewScalewayAPI(os.Getenv("scaleway_api_endpoint"), config.AccountAPI, config.Organization, config.Token) +} + +func initLogging(debug bool, verbose bool, streams *commands.Streams) { + logrus.SetOutput(streams.Stderr) + if debug { + logrus.SetLevel(logrus.DebugLevel) + } else if verbose { + logrus.SetLevel(logrus.InfoLevel) + } else { + logrus.SetLevel(logrus.WarnLevel) + } +} diff --git a/pkg/cli/main_test.go b/pkg/cli/main_test.go new file mode 100644 index 0000000000..49aad280ad --- /dev/null +++ b/pkg/cli/main_test.go @@ -0,0 +1,187 @@ +package cli + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "testing" + + "github.com/scaleway/scaleway-cli/pkg/commands" + . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" +) + +func testHelpOutput(out string, err string) { + // headers & footers + So(out, ShouldContainSubstring, "Usage: scw [OPTIONS] COMMAND [arg...]") + So(out, ShouldContainSubstring, "Interact with Scaleway from the command line.") + So(out, ShouldContainSubstring, "Run 'scw COMMAND --help' for more information on a command.") + + // options + So(out, ShouldContainSubstring, "Options:") + for _, option := range publicOptions { + So(out, ShouldContainSubstring, " "+option) + } + + // public commands + So(out, ShouldContainSubstring, "Commands:") + for _, command := range publicCommands { + So(out, ShouldContainSubstring, " "+command) + } + + // secret commands + for _, command := range secretCommands { + So(out, ShouldNotContainSubstring, " "+command) + } + + // :lipstick: + /* + for _, line := range strings.Split(out, "\n") { + So(line, shouldFitInTerminal) + } + */ + + // FIXME: count amount of options/commands, and panic if amount is different + + // Testing stderr + So(err, ShouldEqual, "") +} + +func testHelpCommandOutput(command string, out string, err string) { + // Header + So(out, ShouldContainSubstring, fmt.Sprintf("Usage: scw %s", command)) + // FIXME: test description + // FIXME: test parameters + + // Options + So(out, ShouldContainSubstring, "Options:") + So(out, ShouldContainSubstring, " -h, --help=false") + // FIXME: test other options + + // Examples + // FIXME: todo + //So(out, ShouldContainSubstring, "Examples:") + + // :lipstick: + /* + for _, line := range strings.Split(out, "\n") { + So(line, shouldFitInTerminal) + } + */ + +} + +func TestHelp(t *testing.T) { + Convey("Testing golang' `start(\"help\", ...)`", t, func() { + Convey("start(\"help\")", func() { + stdout := bytes.Buffer{} + stderr := bytes.Buffer{} + streams := commands.Streams{ + Stdin: os.Stdin, + Stdout: &stdout, + Stderr: &stderr, + } + ec, err := Start([]string{"help"}, &streams) + + So(ec, ShouldEqual, 0) + So(err, ShouldBeNil) + testHelpOutput(stdout.String(), stderr.String()) + }) + + cmds := append(publicCommands, secretCommands...) + for _, command := range cmds { + // FIXME: test 'start(COMMAND, "--help")' + if command == "help" { + continue + } + + Convey(fmt.Sprintf("start(\"help\", \"%s\")", command), func() { + stdout := bytes.Buffer{} + stderr := bytes.Buffer{} + streams := commands.Streams{ + Stdin: os.Stdin, + Stdout: &stdout, + Stderr: &stderr, + } + ec, err := Start([]string{"help", command}, &streams) + + So(ec, ShouldEqual, 1) + So(err, ShouldBeNil) + testHelpCommandOutput(command, stdout.String(), stderr.String()) + + // secondary help usage + // FIXME: should check for 'scw login' first + /* + if command != "help" { + // FIXME: this should works for "help" too + secondaryStdout := bytes.Buffer{} + secondaryStderr := bytes.Buffer{} + secondaryStreams := commands.Streams{ + Stdin: os.Stdin, + Stdout: &secondaryStdout, + Stderr: &secondaryStderr, + } + secondEc, secondErr := Start([]string{command, "--help"}, &secondaryStreams) + So(ec, ShouldEqual, secondEc) + //So(outStdout, ShouldEqual, secondOut) + So(fmt.Sprintf("%v", err), ShouldEqual, fmt.Sprintf("%v", secondErr)) + } + */ + + }) + } + }) + + Convey("Testing shell' `scw help`", t, func() { + Convey("scw help", func() { + cmd := exec.Command(scwcli, "help") + out, ec, err := runCommandWithOutput(cmd) + stderr := "" // FIXME: get real stderr + + // exit code + So(ec, ShouldEqual, 0) + So(err, ShouldBeNil) + + // streams + testHelpOutput(out, stderr) + }) + + cmds := append(publicCommands, secretCommands...) + for _, command := range cmds { + // FIXME: test 'scw COMMAND --help' + + Convey(fmt.Sprintf("scw help %s", command), func() { + cmd := exec.Command(scwcli, "help", command) + out, ec, err := runCommandWithOutput(cmd) + stderr := "" // FIXME: get real stderr + + // exit code + So(ec, ShouldEqual, 1) + So(fmt.Sprintf("%s", err), ShouldEqual, "exit status 1") + + // streams + testHelpCommandOutput(command, out, stderr) + + // secondary help usage + // FIXME: should check for 'scw login' first + /* + if command != "help" { + // FIXME: this should works for "help" too + secondCmd := exec.Command(scwcli, command, "--help") + secondOut, secondEc, secondErr := runCommandWithOutput(secondCmd) + So(out, ShouldEqual, secondOut) + So(ec, ShouldEqual, secondEc) + So(fmt.Sprintf("%v", err), ShouldEqual, fmt.Sprintf("%v", secondErr)) + } + */ + }) + } + Convey("Unknown command", func() { + cmd := exec.Command(scwcli, "boogie") + out, ec, err := runCommandWithOutput(cmd) + So(out, ShouldContainSubstring, "scw: unknown subcommand boogie") + So(ec, ShouldEqual, 1) + So(fmt.Sprintf("%s", err), ShouldEqual, "exit status 1") + }) + }) +} diff --git a/pkg/cli/test.go b/pkg/cli/test.go new file mode 100644 index 0000000000..f94cbf8a38 --- /dev/null +++ b/pkg/cli/test.go @@ -0,0 +1,68 @@ +package cli + +import ( + "fmt" + "os/exec" + "syscall" +) + +var ( + scwcli string = "../../scw" + publicCommands []string = []string{ + "help", "attach", "commit", "cp", "create", + "events", "exec", "history", "images", "info", + "inspect", "kill", "login", "logout", "logs", + "port", "ps", "rename", "restart", "rm", "rmi", + "run", "search", "start", "stop", "tag", "top", + "version", "wait", + } + secretCommands []string = []string{ + "_patch", "_completion", "_flush-cache", + } + publicOptions []string = []string{ + "-h, --help=false", + "-D, --debug=false", + "-V, --verbose=false", + "-q, --quiet=false", + "--api-endpoint=APIEndPoint", + "--sensitive=false", + "-v, --version=false", + } +) + +func shouldFitInTerminal(actual interface{}, expected ...interface{}) string { + if len(actual.(string)) < 80 { + return "" + } + return fmt.Sprintf("len(%q)\n -> %d chars (> 80 chars)", actual, len(actual.(string))) +} + +func getExitCode(err error) (int, error) { + exitCode := 0 + if exiterr, ok := err.(*exec.ExitError); ok { + if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok { + return procExit.ExitStatus(), nil + } + } + return exitCode, fmt.Errorf("failed to get exit code") +} + +func processExitCode(err error) (exitCode int) { + if err != nil { + var exiterr error + if exitCode, exiterr = getExitCode(err); exiterr != nil { + // TODO: Fix this so we check the error's text. + // we've failed to retrieve exit code, so we set it to 127 + exitCode = 127 + } + } + return +} + +func runCommandWithOutput(cmd *exec.Cmd) (output string, exitCode int, err error) { + exitCode = 0 + out, err := cmd.CombinedOutput() + exitCode = processExitCode(err) + output = string(out) + return +} diff --git a/pkg/cli/x_completion.go b/pkg/cli/x_completion.go index 081300e6d5..d08aa8d4ad 100644 --- a/pkg/cli/x_completion.go +++ b/pkg/cli/x_completion.go @@ -53,10 +53,10 @@ func wordifyName(name string, kind string) string { func runCompletion(cmd *Command, args []string) error { if completionHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(args) != 1 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } category := args[0] diff --git a/pkg/cli/x_flushcache.go b/pkg/cli/x_flushcache.go index cbb318c840..a4049f1467 100644 --- a/pkg/cli/x_flushcache.go +++ b/pkg/cli/x_flushcache.go @@ -27,10 +27,10 @@ var flushCacheHelp bool // -h, --help flag func runFlushCache(cmd *Command, args []string) error { if flushCacheHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(args) > 0 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } err := cmd.API.Cache.Flush() diff --git a/pkg/cli/x_patch.go b/pkg/cli/x_patch.go index aa3e391cdf..bfa42ca7cb 100644 --- a/pkg/cli/x_patch.go +++ b/pkg/cli/x_patch.go @@ -34,10 +34,10 @@ var patchHelp bool // -h, --help flag func runPatch(cmd *Command, args []string) error { if patchHelp { - cmd.PrintUsage() + return cmd.PrintUsage() } if len(args) != 2 { - cmd.PrintShortUsage() + return cmd.PrintShortUsage() } // Parsing FIELD=VALUE diff --git a/pkg/commands/command.go b/pkg/commands/command.go index e147b9bfc1..392ec584e5 100644 --- a/pkg/commands/command.go +++ b/pkg/commands/command.go @@ -12,11 +12,17 @@ import ( "github.com/scaleway/scaleway-cli/pkg/api" ) +// Streams is used to redirects the streams +type Streams struct { + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer +} + // CommandContext is passed to all commands and contains streams, environment, api and arguments type CommandContext struct { - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer + Streams + Env []string RawArgs []string API *api.ScalewayAPI diff --git a/pkg/commands/command_test.go b/pkg/commands/command_test.go index b55f6a58b3..05d97ddbdb 100644 --- a/pkg/commands/command_test.go +++ b/pkg/commands/command_test.go @@ -20,9 +20,11 @@ func ExampleCommandContext() CommandContext { } ctx := CommandContext{ - Stdin: os.Stdin, - Stdout: os.Stdout, - Stderr: os.Stderr, + Streams: Streams{ + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + }, Env: []string{ "HOME" + os.Getenv("HOME"), }, From 77979a1c61c1b2474b77ee8a543861367e94e0b8 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Wed, 19 Aug 2015 19:35:09 +0200 Subject: [PATCH 03/17] Do not test everything 2 times --- .travis.yml | 5 ----- Makefile | 11 +---------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 17fe1dc8be..93c862bbaf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,13 +27,8 @@ before_install: - go get golang.org/x/tools/cmd/cover -install: - - make travis_install - - script: - make build - ./scw version - - make travis_run - make cover - goveralls -service=travis-ci -v -coverprofile=profile.out diff --git a/Makefile b/Makefile index 30b24dc0a8..ea556e421e 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ $(IREF_LIST): %_iref: pkg/scwversion/version.go $(TEST_LIST): %_test: $(GOTEST) ./$* $(COVER_LIST): %_cover: - $(GOTEST) -coverprofile=file-profile.out ./$* + $(GOTEST) -covermode=count -v -coverprofile=file-profile.out ./$* if [ -f file-profile.out ]; then cat file-profile.out | grep -v "mode: set" >> profile.out || true; rm -f file-profile.out; fi $(FMT_LIST): %_fmt: $(GOFMT) ./$* @@ -130,15 +130,6 @@ packages: #publish_packages: # docker run -v $(PWD)/dist moul/dput ppa:moul/scw dist/scw_$(FPM_VERSION)_arm.changes - -travis_install: - go get golang.org/x/tools/cmd/cover - - -travis_run: build - go test -v -covermode=count $(foreach int, $(SRC) $(PACKAGES), ./$(int)) - - golint: @go get github.com/golang/lint/golint @for dir in */; do golint $$dir; done From 4f102f27d6ad7ff801b2cfcd4bca112175659298 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 14:32:05 +0200 Subject: [PATCH 04/17] Moved config to dedicated package --- Makefile | 2 +- pkg/api/config.go | 47 ----------------- pkg/cli/main.go | 42 ++------------- pkg/commands/info.go | 4 +- pkg/commands/login.go | 6 +-- pkg/commands/logout.go | 4 +- pkg/commands/run.go | 3 +- pkg/config/config.go | 106 ++++++++++++++++++++++++++++++++++++++ pkg/config/config_test.go | 32 ++++++++++++ pkg/utils/utils.go | 21 -------- pkg/utils/utils_test.go | 21 -------- 11 files changed, 151 insertions(+), 137 deletions(-) delete mode 100644 pkg/api/config.go create mode 100644 pkg/config/config.go create mode 100644 pkg/config/config_test.go diff --git a/Makefile b/Makefile index ea556e421e..d71b76a382 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ FPM_ARGS ?= \ NAME = scw SRC = cmd/scw -PACKAGES = pkg/api pkg/commands pkg/utils pkg/cli pkg/sshcommand +PACKAGES = pkg/api pkg/commands pkg/utils pkg/cli pkg/sshcommand pkg/config REV = $(shell git rev-parse HEAD || echo "nogit") TAG = $(shell git describe --tags --always || echo "nogit") BUILDER = scaleway-cli-builder diff --git a/pkg/api/config.go b/pkg/api/config.go deleted file mode 100644 index 2e60a577e5..0000000000 --- a/pkg/api/config.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2015 Scaleway. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE.md file. - -package api - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/scaleway/scaleway-cli/pkg/utils" -) - -// Config is a Scaleway CLI configuration file -type Config struct { - // ComputeAPI is the endpoint to the Scaleway API - ComputeAPI string `json:"api_endpoint"` - - // AccountAPI is the endpoint to the Scaleway Account API - AccountAPI string `json:"account_endpoint"` - - // Organization is the identifier of the Scaleway orgnization - Organization string `json:"organization"` - - // Token is the authentication token for the Scaleway organization - Token string `json:"token"` -} - -// Save write the config file -func (c *Config) Save() error { - scwrcPath, err := utils.GetConfigFilePath() - if err != nil { - return fmt.Errorf("Unable to get scwrc config file path: %s", err) - } - scwrc, err := os.OpenFile(scwrcPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0600) - if err != nil { - return fmt.Errorf("Unable to create scwrc config file: %s", err) - } - defer scwrc.Close() - encoder := json.NewEncoder(scwrc) - err = encoder.Encode(c) - if err != nil { - return fmt.Errorf("Unable to encode scw config file: %s", err) - } - return nil -} diff --git a/pkg/cli/main.go b/pkg/cli/main.go index a0898ae591..da465c9c95 100644 --- a/pkg/cli/main.go +++ b/pkg/cli/main.go @@ -5,7 +5,6 @@ package cli import ( - "encoding/json" "fmt" "io/ioutil" "os" @@ -15,6 +14,7 @@ import ( "github.com/scaleway/scaleway-cli/pkg/api" "github.com/scaleway/scaleway-cli/pkg/commands" + "github.com/scaleway/scaleway-cli/pkg/config" "github.com/scaleway/scaleway-cli/pkg/scwversion" "github.com/scaleway/scaleway-cli/pkg/utils" ) @@ -40,7 +40,7 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) { flag.CommandLine.Parse(rawArgs) - config, cfgErr := getConfig() + config, cfgErr := config.GetConfig() if cfgErr != nil && !os.IsNotExist(cfgErr) { return 1, fmt.Errorf("unable to open .scwrc config file: %v", cfgErr) } @@ -130,46 +130,10 @@ func Start(rawArgs []string, streams *commands.Streams) (int, error) { return 1, fmt.Errorf("scw: unknown subcommand %s\nRun 'scw help' for usage.", name) } -// getConfig returns the Scaleway CLI config file for the current user -func getConfig() (*api.Config, error) { - scwrcPath, err := utils.GetConfigFilePath() - if err != nil { - return nil, err - } - - stat, err := os.Stat(scwrcPath) - // we don't care if it fails, the user just won't see the warning - if err == nil { - mode := stat.Mode() - if mode&0066 != 0 { - return nil, fmt.Errorf("permissions %#o for .scwrc are too open.", mode) - } - } - - file, err := ioutil.ReadFile(scwrcPath) - if err != nil { - return nil, err - } - var config api.Config - err = json.Unmarshal(file, &config) - if err != nil { - return nil, err - } - // check if he has an old scwrc version - if config.AccountAPI == "" { - config.AccountAPI = "https://account.scaleway.com" - config.Save() - } - if os.Getenv("scaleway_api_endpoint") == "" { - os.Setenv("scaleway_api_endpoint", config.ComputeAPI) - } - return &config, nil -} - // getScalewayAPI returns a ScalewayAPI using the user config file func getScalewayAPI() (*api.ScalewayAPI, error) { // We already get config globally, but whis way we can get explicit error when trying to create a ScalewayAPI object - config, err := getConfig() + config, err := config.GetConfig() if err != nil { return nil, err } diff --git a/pkg/commands/info.go b/pkg/commands/info.go index 8dc3139cf8..f349c56467 100644 --- a/pkg/commands/info.go +++ b/pkg/commands/info.go @@ -11,7 +11,7 @@ import ( "github.com/scaleway/scaleway-cli/vendor/github.com/kardianos/osext" - "github.com/scaleway/scaleway-cli/pkg/utils" + "github.com/scaleway/scaleway-cli/pkg/config" ) // InfoArgs are flags for the `RunInfo` function @@ -26,7 +26,7 @@ func RunInfo(ctx CommandContext, args InfoArgs) error { fmt.Fprintf(ctx.Stdout, "Organization: %s\n", ctx.API.Organization) // FIXME: add partially-masked token fmt.Fprintf(ctx.Stdout, "API Endpoint: %s\n", ctx.Getenv("scaleway_api_endpoint")) - configPath, _ := utils.GetConfigFilePath() + configPath, _ := config.GetConfigFilePath() fmt.Fprintf(ctx.Stdout, "RC file: %s\n", configPath) fmt.Fprintf(ctx.Stdout, "User: %s\n", ctx.Getenv("USER")) fmt.Fprintf(ctx.Stdout, "CPUs: %d\n", runtime.NumCPU()) diff --git a/pkg/commands/login.go b/pkg/commands/login.go index e5d857978f..bd3f87fadd 100644 --- a/pkg/commands/login.go +++ b/pkg/commands/login.go @@ -17,7 +17,7 @@ import ( "github.com/scaleway/scaleway-cli/vendor/golang.org/x/crypto/ssh/terminal" "github.com/scaleway/scaleway-cli/pkg/api" - "github.com/scaleway/scaleway-cli/pkg/utils" + "github.com/scaleway/scaleway-cli/pkg/config" ) // LoginArgs are arguments passed to `RunLogin` @@ -30,7 +30,7 @@ type LoginArgs struct { // selectKey allows to choice a key in ~/.ssh func selectKey(args *LoginArgs) error { fmt.Println("Do you want to upload a SSH key ?") - home, err := utils.GetHomeDir() + home, err := config.GetHomeDir() if err != nil { return err } @@ -88,7 +88,7 @@ func RunLogin(ctx CommandContext, args LoginArgs) error { promptUser("Token: ", &args.Token, false) } - cfg := &api.Config{ + cfg := &config.Config{ ComputeAPI: "https://api.scaleway.com/", AccountAPI: "https://account.scaleway.com/", Organization: strings.Trim(args.Organization, "\n"), diff --git a/pkg/commands/logout.go b/pkg/commands/logout.go index da4e3ab159..99397d9e69 100644 --- a/pkg/commands/logout.go +++ b/pkg/commands/logout.go @@ -8,7 +8,7 @@ import ( "fmt" "os" - "github.com/scaleway/scaleway-cli/pkg/utils" + "github.com/scaleway/scaleway-cli/pkg/config" ) // LogoutArgs are flags for the `RunLogout` function @@ -17,7 +17,7 @@ type LogoutArgs struct{} // RunLogout is the handler for 'scw logout' func RunLogout(ctx CommandContext, args LogoutArgs) error { // FIXME: ask if we need to remove the local ssh key on the account - scwrcPath, err := utils.GetConfigFilePath() + scwrcPath, err := config.GetConfigFilePath() if err != nil { return fmt.Errorf("unable to get scwrc config file path: %v", err) } diff --git a/pkg/commands/run.go b/pkg/commands/run.go index ab1171cfa1..06018b034d 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-cli/pkg/config" "github.com/scaleway/scaleway-cli/pkg/utils" "github.com/scaleway/scaleway-cli/vendor/github.com/Sirupsen/logrus" ) @@ -35,7 +36,7 @@ type RunArgs struct { // AddSSHKeyToTags adds the ssh key in the tags func AddSSHKeyToTags(ctx CommandContext, tags *[]string, image string) error { - home, err := utils.GetHomeDir() + home, err := config.GetHomeDir() if err != nil { return fmt.Errorf("unable to find your home %v", err) } diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000000..a56a8e4073 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,106 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +// ~/.scwrc management +package config + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +// Config is a Scaleway CLI configuration file +type Config struct { + // ComputeAPI is the endpoint to the Scaleway API + ComputeAPI string `json:"api_endpoint"` + + // AccountAPI is the endpoint to the Scaleway Account API + AccountAPI string `json:"account_endpoint"` + + // Organization is the identifier of the Scaleway orgnization + Organization string `json:"organization"` + + // Token is the authentication token for the Scaleway organization + Token string `json:"token"` +} + +// Save write the config file +func (c *Config) Save() error { + scwrcPath, err := GetConfigFilePath() + if err != nil { + return fmt.Errorf("Unable to get scwrc config file path: %s", err) + } + scwrc, err := os.OpenFile(scwrcPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0600) + if err != nil { + return fmt.Errorf("Unable to create scwrc config file: %s", err) + } + defer scwrc.Close() + encoder := json.NewEncoder(scwrc) + err = encoder.Encode(c) + if err != nil { + return fmt.Errorf("Unable to encode scw config file: %s", err) + } + return nil +} + +// GetConfig returns the Scaleway CLI config file for the current user +func GetConfig() (*Config, error) { + scwrcPath, err := GetConfigFilePath() + if err != nil { + return nil, err + } + + stat, err := os.Stat(scwrcPath) + // we don't care if it fails, the user just won't see the warning + if err == nil { + mode := stat.Mode() + if mode&0066 != 0 { + return nil, fmt.Errorf("permissions %#o for .scwrc are too open.", mode) + } + } + + file, err := ioutil.ReadFile(scwrcPath) + if err != nil { + return nil, err + } + var config Config + err = json.Unmarshal(file, &config) + if err != nil { + return nil, err + } + // check if he has an old scwrc version + if config.AccountAPI == "" { + config.AccountAPI = "https://account.scaleway.com" + config.Save() + } + if os.Getenv("scaleway_api_endpoint") == "" { + os.Setenv("scaleway_api_endpoint", config.ComputeAPI) + } + return &config, nil +} + +// GetConfigFilePath returns the path to the Scaleway CLI config file +func GetConfigFilePath() (string, error) { + path, err := GetHomeDir() + if err != nil { + return "", err + } + return filepath.Join(path, ".scwrc"), nil +} + +// GetHomeDir returns the path to your home +func GetHomeDir() (string, error) { + homeDir := os.Getenv("HOME") // *nix + if homeDir == "" { // Windows + homeDir = os.Getenv("USERPROFILE") + } + if homeDir == "" { + return "", errors.New("user home directory not found") + } + return homeDir, nil +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go new file mode 100644 index 0000000000..cebe5d8aa2 --- /dev/null +++ b/pkg/config/config_test.go @@ -0,0 +1,32 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package config + +import ( + "strings" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestGetConfigFilePath(t *testing.T) { + Convey("Testing GetConfigFilePath()", t, func() { + configPath, err := GetConfigFilePath() + So(err, ShouldBeNil) + So(configPath, ShouldNotEqual, "") + + homedir, err := GetHomeDir() + So(err, ShouldBeNil) + So(strings.Contains(configPath, homedir), ShouldBeTrue) + }) +} + +func TestGetHomeDir(t *testing.T) { + Convey("Testing GetHomeDir()", t, func() { + homedir, err := GetHomeDir() + So(err, ShouldBeNil) + So(homedir, ShouldNotEqual, "") + }) +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index a28f950be6..127d81dd39 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -178,27 +178,6 @@ func RemoveDuplicates(elements []string) []string { return result } -// GetHomeDir returns the path to your home -func GetHomeDir() (string, error) { - homeDir := os.Getenv("HOME") // *nix - if homeDir == "" { // Windows - homeDir = os.Getenv("USERPROFILE") - } - if homeDir == "" { - return "", errors.New("user home directory not found") - } - return homeDir, nil -} - -// GetConfigFilePath returns the path to the Scaleway CLI config file -func GetConfigFilePath() (string, error) { - path, err := GetHomeDir() - if err != nil { - return "", err - } - return filepath.Join(path, ".scwrc"), nil -} - const termjsBin string = "termjs-cli" // AttachToSerial tries to connect to server serial using 'term.js-cli' and fallback with a help message diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 3fffbcd7d3..de144324f5 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -5,7 +5,6 @@ import ( "os" "path/filepath" "sort" - "strings" "testing" . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" @@ -68,26 +67,6 @@ func TestRemoveDuplicates(t *testing.T) { }) } -func TestGetHomeDir(t *testing.T) { - Convey("Testing GetHomeDir()", t, func() { - homedir, err := GetHomeDir() - So(err, ShouldBeNil) - So(homedir, ShouldNotEqual, "") - }) -} - -func TestGetConfigFilePath(t *testing.T) { - Convey("Testing GetConfigFilePath()", t, func() { - configPath, err := GetConfigFilePath() - So(err, ShouldBeNil) - So(configPath, ShouldNotEqual, "") - - homedir, err := GetHomeDir() - So(err, ShouldBeNil) - So(strings.Contains(configPath, homedir), ShouldBeTrue) - }) -} - func TestGeneratingAnSSHKey(t *testing.T) { Convey("Testing GeneratingAnSSHKey()", t, func() { streams := SpawnRedirection{ From b94a0a0934b69b59056ee1ea6eb03f57d66eca3a Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 14:56:04 +0200 Subject: [PATCH 05/17] Refactored tests for commands --- pkg/commands/command_test.go | 34 ++++++++++++++++++++++++++++++++++ pkg/commands/version_test.go | 27 +++++++++++++++------------ 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/pkg/commands/command_test.go b/pkg/commands/command_test.go index 05d97ddbdb..847b8d7b32 100644 --- a/pkg/commands/command_test.go +++ b/pkg/commands/command_test.go @@ -6,10 +6,13 @@ package commands import ( + "bytes" "os" "testing" + "github.com/Sirupsen/logrus" "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-cli/pkg/config" "github.com/scaleway/scaleway-cli/vendor/github.com/stretchr/testify/assert" ) @@ -39,3 +42,34 @@ func TestCommandContext_Getenv(t *testing.T) { assert.Equal(t, ctx.Getenv("HOME"), os.Getenv("HOME")) assert.Equal(t, ctx.Getenv("DONTEXISTS"), "") } + +func RealAPIContext() *CommandContext { + config, err := config.GetConfig() + if err != nil { + logrus.Warnf("RealAPIContext: failed to call config.GetConfig(): %v", err) + return nil + } + + apiClient, err := api.NewScalewayAPI(config.ComputeAPI, config.AccountAPI, config.Organization, config.Token) + if err != nil { + logrus.Warnf("RealAPIContext: failed to call api.NewScalewayAPI(): %v", err) + return nil + } + + stdout := bytes.Buffer{} + stderr := bytes.Buffer{} + + ctx := CommandContext{ + Streams: Streams{ + Stdin: os.Stdin, + Stdout: &stdout, + Stderr: &stderr, + }, + Env: []string{ + "HOME" + os.Getenv("HOME"), + }, + RawArgs: []string{}, + API: apiClient, + } + return &ctx +} diff --git a/pkg/commands/version_test.go b/pkg/commands/version_test.go index 864ac3ffc1..bb67d0a103 100644 --- a/pkg/commands/version_test.go +++ b/pkg/commands/version_test.go @@ -8,23 +8,26 @@ import ( "bytes" "testing" - "github.com/scaleway/scaleway-cli/vendor/github.com/stretchr/testify/assert" + . "github.com/smartystreets/goconvey/convey" ) -func TestToto(t *testing.T) { - ctx := ExampleCommandContext() - var buf bytes.Buffer - ctx.Stdout = &buf +func TestVersion(t *testing.T) { + Convey("Testing Version()", t, func() { + ctx := ExampleCommandContext() + var buf bytes.Buffer + ctx.Stdout = &buf - args := VersionArgs{} + args := VersionArgs{} + + err := Version(ctx, args) - err := Version(ctx, args) + So(err, ShouldBeNil) + So(buf.String(), ShouldContainSubstring, "Client version: ") + So(buf.String(), ShouldContainSubstring, "Go version (client): ") + So(buf.String(), ShouldContainSubstring, "Git commit (client): ") + So(buf.String(), ShouldContainSubstring, "OS/Arch (client): ") - assert.Nil(t, err) - assert.Contains(t, buf.String(), "Client version: ") - assert.Contains(t, buf.String(), "Go version (client): ") - assert.Contains(t, buf.String(), "Git commit (client): ") - assert.Contains(t, buf.String(), "OS/Arch (client): ") + }) } func ExampleVersion() { From 179a16f2e8a77a12ff2577ad5dd6d4ac580d4bcb Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 14:56:13 +0200 Subject: [PATCH 06/17] Testing commands.Images() --- pkg/commands/images_test.go | 144 ++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 pkg/commands/images_test.go diff --git a/pkg/commands/images_test.go b/pkg/commands/images_test.go new file mode 100644 index 0000000000..511d5ffd4b --- /dev/null +++ b/pkg/commands/images_test.go @@ -0,0 +1,144 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package commands + +import ( + "bytes" + "strings" + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func ExampleRunImages() { + ctx := ExampleCommandContext() + args := ImagesArgs{} + RunImages(ctx, args) +} + +func ExampleRunImages_complex() { + ctx := ExampleCommandContext() + args := ImagesArgs{ + All: false, + NoTrunc: false, + Quiet: false, + } + RunImages(ctx, args) +} + +func ExampleRunImages_quiet() { + ctx := ExampleCommandContext() + args := ImagesArgs{ + All: false, + NoTrunc: false, + Quiet: true, + } + RunImages(ctx, args) +} + +func ExampleRunImages_all() { + ctx := ExampleCommandContext() + args := ImagesArgs{ + All: true, + NoTrunc: false, + Quiet: false, + } + RunImages(ctx, args) +} + +func ExampleRunImages_notrunc() { + ctx := ExampleCommandContext() + args := ImagesArgs{ + All: false, + NoTrunc: true, + Quiet: false, + } + RunImages(ctx, args) +} + +func TestRunImages_realAPI(t *testing.T) { + ctx := RealAPIContext() + if ctx == nil { + t.Skip() + } + Convey("Testing RunImages() on real API", t, func() { + Convey("no options", func() { + args := ImagesArgs{ + All: false, + NoTrunc: false, + Quiet: false, + } + err := RunImages(*ctx, args) + So(err, ShouldBeNil) + + stderr := ctx.Stderr.(*bytes.Buffer).String() + So(stderr, ShouldBeEmpty) + + stdout := ctx.Stdout.(*bytes.Buffer).String() + lines := strings.Split(stdout, "\n") + So(len(lines), ShouldBeGreaterThan, 0) + + firstLine := lines[0] + colNames := strings.Fields(firstLine) + So(colNames, ShouldResemble, []string{"REPOSITORY", "TAG", "IMAGE", "ID", "CREATED", "VIRTUAL", "SIZE"}) + + // FIXME: test public images + }) + Convey("--all", func() { + args := ImagesArgs{ + All: true, + NoTrunc: false, + Quiet: false, + } + err := RunImages(*ctx, args) + So(err, ShouldBeNil) + + stderr := ctx.Stderr.(*bytes.Buffer).String() + So(stderr, ShouldBeEmpty) + + stdout := ctx.Stdout.(*bytes.Buffer).String() + lines := strings.Split(stdout, "\n") + So(len(lines), ShouldBeGreaterThan, 0) + + firstLine := lines[0] + colNames := strings.Fields(firstLine) + So(colNames, ShouldResemble, []string{"REPOSITORY", "TAG", "IMAGE", "ID", "CREATED", "VIRTUAL", "SIZE"}) + + // FIXME: test public images + // FIXME: test bootscripts + // FIXME: test snapshots + }) + Convey("--quiet", func() { + args := ImagesArgs{ + All: false, + NoTrunc: false, + Quiet: true, + } + err := RunImages(*ctx, args) + So(err, ShouldBeNil) + + stderr := ctx.Stderr.(*bytes.Buffer).String() + So(stderr, ShouldBeEmpty) + + stdout := ctx.Stdout.(*bytes.Buffer).String() + lines := strings.Split(stdout, "\n") + // So(len(lines), ShouldBeGreaterThan, 0) + + if len(lines) > 0 { + firstLine := lines[0] + colNames := strings.Fields(firstLine) + So(colNames, ShouldNotResemble, []string{"REPOSITORY", "TAG", "IMAGE", "ID", "CREATED", "VIRTUAL", "SIZE"}) + + // FIXME: test public images + // FIXME: test bootscripts + // FIXME: test snapshots + } + }) + Reset(func() { + ctx.Stdout.(*bytes.Buffer).Reset() + ctx.Stderr.(*bytes.Buffer).Reset() + }) + }) +} From 22d03f0bd415d28e2214d2bc3e5556906b74a787 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 15:03:46 +0200 Subject: [PATCH 07/17] Fixed cover profile generation --- Makefile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d71b76a382..d50848d88f 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,11 @@ install: $(INSTALL_LIST) test: $(TEST_LIST) iref: $(IREF_LIST) fmt: $(FMT_LIST) + +noop = +comma = , +space = $(noop) $(noop) + cover: rm -f profile.out $(MAKE) $(COVER_LIST) @@ -77,7 +82,8 @@ $(IREF_LIST): %_iref: pkg/scwversion/version.go $(TEST_LIST): %_test: $(GOTEST) ./$* $(COVER_LIST): %_cover: - $(GOTEST) -covermode=count -v -coverprofile=file-profile.out ./$* + #$(GOTEST) -covermode=set -coverpkg=$(subst $(space),$(comma),$(addprefix ./, $(PACKAGES))) -v -coverprofile=file-profile.out ./$* + $(GOTEST) -covermode=set -v -coverprofile=file-profile.out ./$* if [ -f file-profile.out ]; then cat file-profile.out | grep -v "mode: set" >> profile.out || true; rm -f file-profile.out; fi $(FMT_LIST): %_fmt: $(GOFMT) ./$* From 3631753788f703618192038ace28b8d524a34429 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 16:13:30 +0200 Subject: [PATCH 08/17] Testing create --- pkg/commands/create_test.go | 102 ++++++++++++++++++++++++++++++++++++ pkg/commands/test.go | 18 +++++++ 2 files changed, 120 insertions(+) create mode 100644 pkg/commands/create_test.go create mode 100644 pkg/commands/test.go diff --git a/pkg/commands/create_test.go b/pkg/commands/create_test.go new file mode 100644 index 0000000000..7f0f58fca4 --- /dev/null +++ b/pkg/commands/create_test.go @@ -0,0 +1,102 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package commands + +import ( + "bytes" + "strings" + "testing" + + . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" +) + +func ExampleRunCreate() { + ctx := ExampleCommandContext() + args := CreateArgs{} + RunCreate(ctx, args) +} + +func ExampleRunCreate_complex() { + ctx := ExampleCommandContext() + args := CreateArgs{ + Name: "test", + Bootscript: "rescue", + Tags: []string{"tag1", "tag2"}, + Volumes: []string{}, + Image: "ubuntu-vivid", + TmpSSHKey: false, + } + RunCreate(ctx, args) +} + +func TestRunCreate_realAPI(t *testing.T) { + createdUUIDs := []string{} + ctx := RealAPIContext() + if ctx == nil { + t.Skip() + } + + // FIXME: test empty settings + // FIXME: test cache duplicates + // FIXME: test TmpSSHKey + // FIXME: test Volumes + // FIXME: test Tags + // FIXME: test Bootscript + + Convey("Testing RunCreate() on real API", t, func() { + // Error when image is empty ! + /* + Convey("no options", func() { + args := CreateArgs{} + err := RunCreate(*ctx, args) + So(err, ShouldBeNil) + + stderr := ctx.Stderr.(*bytes.Buffer).String() + stdout := ctx.Stdout.(*bytes.Buffer).String() + fmt.Println(stderr) + fmt.Println(stdout) + }) + */ + + Convey("--name=unittest-create-standard ubuntu-vivid", func() { + args := CreateArgs{ + Name: "unittest-create-standard", + Image: "ubuntu-vivid", + } + err := RunCreate(*ctx, args) + So(err, ShouldBeNil) + + stderr := ctx.Stderr.(*bytes.Buffer).String() + stdout := ctx.Stdout.(*bytes.Buffer).String() + So(stderr, ShouldBeEmpty) + So(stdout, shouldBeAnUUID) + + createdUUIDs = append(createdUUIDs, strings.TrimSpace(stdout)) + }) + + Reset(func() { + ctx.Stdout.(*bytes.Buffer).Reset() + ctx.Stderr.(*bytes.Buffer).Reset() + + if len(createdUUIDs) > 0 { + err := RunRm(*ctx, RmArgs{ + Servers: createdUUIDs, + }) + So(err, ShouldBeNil) + + stderr := ctx.Stderr.(*bytes.Buffer).String() + stdout := ctx.Stdout.(*bytes.Buffer).String() + So(stderr, ShouldBeEmpty) + removedUUIDs := strings.Split(strings.TrimSpace(stdout), "\n") + So(removedUUIDs, ShouldResemble, createdUUIDs) + + createdUUIDs = createdUUIDs[:0] + + ctx.Stdout.(*bytes.Buffer).Reset() + ctx.Stderr.(*bytes.Buffer).Reset() + } + }) + }) +} diff --git a/pkg/commands/test.go b/pkg/commands/test.go new file mode 100644 index 0000000000..3df1d0fce5 --- /dev/null +++ b/pkg/commands/test.go @@ -0,0 +1,18 @@ +package commands + +import ( + "fmt" + "strings" + + "github.com/pborman/uuid" +) + +func shouldBeAnUUID(actual interface{}, expected ...interface{}) string { + input := actual.(string) + input = strings.TrimSpace(input) + uuid := uuid.Parse(input) + if uuid == nil { + return fmt.Sprintf("%q should be an UUID", actual) + } + return "" +} From 9a6a1778c7f68958373ce3dfbaa13d20349f207e Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 16:58:20 +0200 Subject: [PATCH 09/17] Added helper to call scoped command --- pkg/commands/command_test.go | 34 ------------------------- pkg/commands/create_test.go | 35 +++++++++----------------- pkg/commands/images_test.go | 33 +++++++++++-------------- pkg/commands/test.go | 48 ++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 75 deletions(-) diff --git a/pkg/commands/command_test.go b/pkg/commands/command_test.go index 847b8d7b32..05d97ddbdb 100644 --- a/pkg/commands/command_test.go +++ b/pkg/commands/command_test.go @@ -6,13 +6,10 @@ package commands import ( - "bytes" "os" "testing" - "github.com/Sirupsen/logrus" "github.com/scaleway/scaleway-cli/pkg/api" - "github.com/scaleway/scaleway-cli/pkg/config" "github.com/scaleway/scaleway-cli/vendor/github.com/stretchr/testify/assert" ) @@ -42,34 +39,3 @@ func TestCommandContext_Getenv(t *testing.T) { assert.Equal(t, ctx.Getenv("HOME"), os.Getenv("HOME")) assert.Equal(t, ctx.Getenv("DONTEXISTS"), "") } - -func RealAPIContext() *CommandContext { - config, err := config.GetConfig() - if err != nil { - logrus.Warnf("RealAPIContext: failed to call config.GetConfig(): %v", err) - return nil - } - - apiClient, err := api.NewScalewayAPI(config.ComputeAPI, config.AccountAPI, config.Organization, config.Token) - if err != nil { - logrus.Warnf("RealAPIContext: failed to call api.NewScalewayAPI(): %v", err) - return nil - } - - stdout := bytes.Buffer{} - stderr := bytes.Buffer{} - - ctx := CommandContext{ - Streams: Streams{ - Stdin: os.Stdin, - Stdout: &stdout, - Stderr: &stderr, - }, - Env: []string{ - "HOME" + os.Getenv("HOME"), - }, - RawArgs: []string{}, - API: apiClient, - } - return &ctx -} diff --git a/pkg/commands/create_test.go b/pkg/commands/create_test.go index 7f0f58fca4..8174cd7b04 100644 --- a/pkg/commands/create_test.go +++ b/pkg/commands/create_test.go @@ -5,7 +5,6 @@ package commands import ( - "bytes" "strings" "testing" @@ -65,37 +64,27 @@ func TestRunCreate_realAPI(t *testing.T) { Name: "unittest-create-standard", Image: "ubuntu-vivid", } - err := RunCreate(*ctx, args) - So(err, ShouldBeNil) - stderr := ctx.Stderr.(*bytes.Buffer).String() - stdout := ctx.Stdout.(*bytes.Buffer).String() - So(stderr, ShouldBeEmpty) - So(stdout, shouldBeAnUUID) + scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx) + err := RunCreate(*scopedCtx, args) + So(err, ShouldBeNil) + So(scopedStderr.String(), ShouldBeEmpty) + So(scopedStdout.String(), shouldBeAnUUID) - createdUUIDs = append(createdUUIDs, strings.TrimSpace(stdout)) + uuid := strings.TrimSpace(scopedStdout.String()) + createdUUIDs = append(createdUUIDs, uuid) }) Reset(func() { - ctx.Stdout.(*bytes.Buffer).Reset() - ctx.Stderr.(*bytes.Buffer).Reset() - if len(createdUUIDs) > 0 { - err := RunRm(*ctx, RmArgs{ - Servers: createdUUIDs, - }) - So(err, ShouldBeNil) + rmCtx, rmStdout, rmStderr := getScopedCtx(ctx) + rmErr := RunRm(*rmCtx, RmArgs{Servers: createdUUIDs}) + So(rmErr, ShouldBeNil) + So(rmStderr.String(), ShouldBeEmpty) - stderr := ctx.Stderr.(*bytes.Buffer).String() - stdout := ctx.Stdout.(*bytes.Buffer).String() - So(stderr, ShouldBeEmpty) - removedUUIDs := strings.Split(strings.TrimSpace(stdout), "\n") + removedUUIDs := strings.Split(strings.TrimSpace(rmStdout.String()), "\n") So(removedUUIDs, ShouldResemble, createdUUIDs) - createdUUIDs = createdUUIDs[:0] - - ctx.Stdout.(*bytes.Buffer).Reset() - ctx.Stderr.(*bytes.Buffer).Reset() } }) }) diff --git a/pkg/commands/images_test.go b/pkg/commands/images_test.go index 511d5ffd4b..4277095e44 100644 --- a/pkg/commands/images_test.go +++ b/pkg/commands/images_test.go @@ -70,14 +70,13 @@ func TestRunImages_realAPI(t *testing.T) { NoTrunc: false, Quiet: false, } - err := RunImages(*ctx, args) - So(err, ShouldBeNil) - stderr := ctx.Stderr.(*bytes.Buffer).String() - So(stderr, ShouldBeEmpty) + scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx) + err := RunImages(*scopedCtx, args) + So(err, ShouldBeNil) + So(scopedStderr.String(), ShouldBeEmpty) - stdout := ctx.Stdout.(*bytes.Buffer).String() - lines := strings.Split(stdout, "\n") + lines := strings.Split(scopedStdout.String(), "\n") So(len(lines), ShouldBeGreaterThan, 0) firstLine := lines[0] @@ -92,14 +91,13 @@ func TestRunImages_realAPI(t *testing.T) { NoTrunc: false, Quiet: false, } - err := RunImages(*ctx, args) - So(err, ShouldBeNil) - stderr := ctx.Stderr.(*bytes.Buffer).String() - So(stderr, ShouldBeEmpty) + scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx) + err := RunImages(*scopedCtx, args) + So(err, ShouldBeNil) + So(scopedStderr.String(), ShouldBeEmpty) - stdout := ctx.Stdout.(*bytes.Buffer).String() - lines := strings.Split(stdout, "\n") + lines := strings.Split(scopedStdout.String(), "\n") So(len(lines), ShouldBeGreaterThan, 0) firstLine := lines[0] @@ -116,14 +114,13 @@ func TestRunImages_realAPI(t *testing.T) { NoTrunc: false, Quiet: true, } - err := RunImages(*ctx, args) - So(err, ShouldBeNil) - stderr := ctx.Stderr.(*bytes.Buffer).String() - So(stderr, ShouldBeEmpty) + scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx) + err := RunImages(*scopedCtx, args) + So(err, ShouldBeNil) + So(scopedStderr.String(), ShouldBeEmpty) - stdout := ctx.Stdout.(*bytes.Buffer).String() - lines := strings.Split(stdout, "\n") + lines := strings.Split(scopedStdout.String(), "\n") // So(len(lines), ShouldBeGreaterThan, 0) if len(lines) > 0 { diff --git a/pkg/commands/test.go b/pkg/commands/test.go index 3df1d0fce5..865509eebc 100644 --- a/pkg/commands/test.go +++ b/pkg/commands/test.go @@ -1,10 +1,15 @@ package commands import ( + "bytes" "fmt" + "os" "strings" + "github.com/Sirupsen/logrus" "github.com/pborman/uuid" + "github.com/scaleway/scaleway-cli/pkg/api" + "github.com/scaleway/scaleway-cli/pkg/config" ) func shouldBeAnUUID(actual interface{}, expected ...interface{}) string { @@ -16,3 +21,46 @@ func shouldBeAnUUID(actual interface{}, expected ...interface{}) string { } return "" } + +func getScopedCtx(sessionCtx *CommandContext) (*CommandContext, *bytes.Buffer, *bytes.Buffer) { + stdout := bytes.Buffer{} + stderr := bytes.Buffer{} + + var newCtx CommandContext + newCtx = *sessionCtx + newCtx.Stdout = &stdout + newCtx.Stderr = &stderr + + return &newCtx, &stdout, &stderr +} + +func RealAPIContext() *CommandContext { + config, err := config.GetConfig() + if err != nil { + logrus.Warnf("RealAPIContext: failed to call config.GetConfig(): %v", err) + return nil + } + + apiClient, err := api.NewScalewayAPI(config.ComputeAPI, config.AccountAPI, config.Organization, config.Token) + if err != nil { + logrus.Warnf("RealAPIContext: failed to call api.NewScalewayAPI(): %v", err) + return nil + } + + stdout := bytes.Buffer{} + stderr := bytes.Buffer{} + + ctx := CommandContext{ + Streams: Streams{ + Stdin: os.Stdin, + Stdout: &stdout, + Stderr: &stderr, + }, + Env: []string{ + "HOME" + os.Getenv("HOME"), + }, + RawArgs: []string{}, + API: apiClient, + } + return &ctx +} From d87c9b128673bb8a21d54cf52e2a3666e5dd8e11 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 17:06:24 +0200 Subject: [PATCH 10/17] Testing RunHistory --- pkg/commands/history_test.go | 64 ++++++++++++++++++++++++++++++++++++ pkg/commands/images_test.go | 5 --- pkg/commands/test.go | 4 +-- 3 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 pkg/commands/history_test.go diff --git a/pkg/commands/history_test.go b/pkg/commands/history_test.go new file mode 100644 index 0000000000..e93a20cb84 --- /dev/null +++ b/pkg/commands/history_test.go @@ -0,0 +1,64 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package commands + +import ( + "fmt" + "strings" + "testing" + + . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" +) + +func ExampleRunHistory() { + ctx := ExampleCommandContext() + args := HistoryArgs{} + RunHistory(ctx, args) +} + +func ExampleRunHistory_complex() { + ctx := ExampleCommandContext() + args := HistoryArgs{ + NoTrunc: false, + Quiet: false, + Image: "", + } + RunHistory(ctx, args) +} + +func TestRunHistory_realAPI(t *testing.T) { + ctx := RealAPIContext() + if ctx == nil { + t.Skip() + } + Convey("Testing RunHistory() on real API", t, func() { + Convey("ubuntu-vivid", func() { + args := HistoryArgs{ + NoTrunc: false, + Quiet: false, + Image: "ubuntu-vivid", + } + + scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx) + err := RunHistory(*scopedCtx, args) + So(err, ShouldBeNil) + So(scopedStderr.String(), ShouldBeEmpty) + + lines := strings.Split(scopedStdout.String(), "\n") + So(len(lines), ShouldBeGreaterThan, 0) + + firstLine := lines[0] + colNames := strings.Fields(firstLine) + So(colNames, ShouldResemble, []string{"IMAGE", "CREATED", "CREATED", "BY", "SIZE"}) + + fmt.Println(scopedStdout.String()) + }) + + // FIXME: test invalid image + // FIXME: test image with duplicates cache + // FIXME: test quiet + // FIXME: test no-trunc + }) +} diff --git a/pkg/commands/images_test.go b/pkg/commands/images_test.go index 4277095e44..d602a6c5a5 100644 --- a/pkg/commands/images_test.go +++ b/pkg/commands/images_test.go @@ -5,7 +5,6 @@ package commands import ( - "bytes" "strings" "testing" @@ -133,9 +132,5 @@ func TestRunImages_realAPI(t *testing.T) { // FIXME: test snapshots } }) - Reset(func() { - ctx.Stdout.(*bytes.Buffer).Reset() - ctx.Stderr.(*bytes.Buffer).Reset() - }) }) } diff --git a/pkg/commands/test.go b/pkg/commands/test.go index 865509eebc..a7c0ff3fa1 100644 --- a/pkg/commands/test.go +++ b/pkg/commands/test.go @@ -6,10 +6,10 @@ import ( "os" "strings" - "github.com/Sirupsen/logrus" - "github.com/pborman/uuid" "github.com/scaleway/scaleway-cli/pkg/api" "github.com/scaleway/scaleway-cli/pkg/config" + "github.com/scaleway/scaleway-cli/vendor/github.com/Sirupsen/logrus" + "github.com/scaleway/scaleway-cli/vendor/github.com/pborman/uuid" ) func shouldBeAnUUID(actual interface{}, expected ...interface{}) string { From 4f3109070d875d4bffcbe77657878115ef9f8813 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 14:59:10 +0200 Subject: [PATCH 11/17] party -c -t -d=vendor --- pkg/commands/images_test.go | 2 +- pkg/commands/version_test.go | 2 +- pkg/config/config_test.go | 2 +- vendor/github.com/pborman/uuid/CONTRIBUTORS | 1 + vendor/github.com/pborman/uuid/LICENSE | 27 ++ vendor/github.com/pborman/uuid/dce.go | 84 +++++ vendor/github.com/pborman/uuid/doc.go | 8 + vendor/github.com/pborman/uuid/hash.go | 53 +++ vendor/github.com/pborman/uuid/json.go | 30 ++ vendor/github.com/pborman/uuid/json_test.go | 32 ++ vendor/github.com/pborman/uuid/node.go | 101 +++++ vendor/github.com/pborman/uuid/seq_test.go | 66 ++++ vendor/github.com/pborman/uuid/time.go | 132 +++++++ vendor/github.com/pborman/uuid/util.go | 43 +++ vendor/github.com/pborman/uuid/uuid.go | 163 ++++++++ vendor/github.com/pborman/uuid/uuid_test.go | 390 ++++++++++++++++++++ vendor/github.com/pborman/uuid/version1.go | 41 ++ vendor/github.com/pborman/uuid/version4.go | 25 ++ 18 files changed, 1199 insertions(+), 3 deletions(-) create mode 100644 vendor/github.com/pborman/uuid/CONTRIBUTORS create mode 100644 vendor/github.com/pborman/uuid/LICENSE create mode 100755 vendor/github.com/pborman/uuid/dce.go create mode 100755 vendor/github.com/pborman/uuid/doc.go create mode 100644 vendor/github.com/pborman/uuid/hash.go create mode 100644 vendor/github.com/pborman/uuid/json.go create mode 100644 vendor/github.com/pborman/uuid/json_test.go create mode 100755 vendor/github.com/pborman/uuid/node.go create mode 100644 vendor/github.com/pborman/uuid/seq_test.go create mode 100755 vendor/github.com/pborman/uuid/time.go create mode 100644 vendor/github.com/pborman/uuid/util.go create mode 100755 vendor/github.com/pborman/uuid/uuid.go create mode 100755 vendor/github.com/pborman/uuid/uuid_test.go create mode 100644 vendor/github.com/pborman/uuid/version1.go create mode 100644 vendor/github.com/pborman/uuid/version4.go diff --git a/pkg/commands/images_test.go b/pkg/commands/images_test.go index d602a6c5a5..dbed38d5c5 100644 --- a/pkg/commands/images_test.go +++ b/pkg/commands/images_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - . "github.com/smartystreets/goconvey/convey" + . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" ) func ExampleRunImages() { diff --git a/pkg/commands/version_test.go b/pkg/commands/version_test.go index bb67d0a103..c1611a4711 100644 --- a/pkg/commands/version_test.go +++ b/pkg/commands/version_test.go @@ -8,7 +8,7 @@ import ( "bytes" "testing" - . "github.com/smartystreets/goconvey/convey" + . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" ) func TestVersion(t *testing.T) { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index cebe5d8aa2..bcdf0ed6e3 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - . "github.com/smartystreets/goconvey/convey" + . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" ) func TestGetConfigFilePath(t *testing.T) { diff --git a/vendor/github.com/pborman/uuid/CONTRIBUTORS b/vendor/github.com/pborman/uuid/CONTRIBUTORS new file mode 100644 index 0000000000..b382a04eda --- /dev/null +++ b/vendor/github.com/pborman/uuid/CONTRIBUTORS @@ -0,0 +1 @@ +Paul Borman diff --git a/vendor/github.com/pborman/uuid/LICENSE b/vendor/github.com/pborman/uuid/LICENSE new file mode 100644 index 0000000000..5dc68268d9 --- /dev/null +++ b/vendor/github.com/pborman/uuid/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/pborman/uuid/dce.go b/vendor/github.com/pborman/uuid/dce.go new file mode 100755 index 0000000000..50a0f2d099 --- /dev/null +++ b/vendor/github.com/pborman/uuid/dce.go @@ -0,0 +1,84 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "fmt" + "os" +) + +// A Domain represents a Version 2 domain +type Domain byte + +// Domain constants for DCE Security (Version 2) UUIDs. +const ( + Person = Domain(0) + Group = Domain(1) + Org = Domain(2) +) + +// NewDCESecurity returns a DCE Security (Version 2) UUID. +// +// The domain should be one of Person, Group or Org. +// On a POSIX system the id should be the users UID for the Person +// domain and the users GID for the Group. The meaning of id for +// the domain Org or on non-POSIX systems is site defined. +// +// For a given domain/id pair the same token may be returned for up to +// 7 minutes and 10 seconds. +func NewDCESecurity(domain Domain, id uint32) UUID { + uuid := NewUUID() + if uuid != nil { + uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 + uuid[9] = byte(domain) + binary.BigEndian.PutUint32(uuid[0:], id) + } + return uuid +} + +// NewDCEPerson returns a DCE Security (Version 2) UUID in the person +// domain with the id returned by os.Getuid. +// +// NewDCEPerson(Person, uint32(os.Getuid())) +func NewDCEPerson() UUID { + return NewDCESecurity(Person, uint32(os.Getuid())) +} + +// NewDCEGroup returns a DCE Security (Version 2) UUID in the group +// domain with the id returned by os.Getgid. +// +// NewDCEGroup(Group, uint32(os.Getgid())) +func NewDCEGroup() UUID { + return NewDCESecurity(Group, uint32(os.Getgid())) +} + +// Domain returns the domain for a Version 2 UUID or false. +func (uuid UUID) Domain() (Domain, bool) { + if v, _ := uuid.Version(); v != 2 { + return 0, false + } + return Domain(uuid[9]), true +} + +// Id returns the id for a Version 2 UUID or false. +func (uuid UUID) Id() (uint32, bool) { + if v, _ := uuid.Version(); v != 2 { + return 0, false + } + return binary.BigEndian.Uint32(uuid[0:4]), true +} + +func (d Domain) String() string { + switch d { + case Person: + return "Person" + case Group: + return "Group" + case Org: + return "Org" + } + return fmt.Sprintf("Domain%d", int(d)) +} diff --git a/vendor/github.com/pborman/uuid/doc.go b/vendor/github.com/pborman/uuid/doc.go new file mode 100755 index 0000000000..d8bd013e68 --- /dev/null +++ b/vendor/github.com/pborman/uuid/doc.go @@ -0,0 +1,8 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The uuid package generates and inspects UUIDs. +// +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services. +package uuid diff --git a/vendor/github.com/pborman/uuid/hash.go b/vendor/github.com/pborman/uuid/hash.go new file mode 100644 index 0000000000..cdd4192fd9 --- /dev/null +++ b/vendor/github.com/pborman/uuid/hash.go @@ -0,0 +1,53 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "crypto/md5" + "crypto/sha1" + "hash" +) + +// Well known Name Space IDs and UUIDs +var ( + NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8") + NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8") + NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8") + NIL = Parse("00000000-0000-0000-0000-000000000000") +) + +// NewHash returns a new UUID dervied from the hash of space concatenated with +// data generated by h. The hash should be at least 16 byte in length. The +// first 16 bytes of the hash are used to form the UUID. The version of the +// UUID will be the lower 4 bits of version. NewHash is used to implement +// NewMD5 and NewSHA1. +func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { + h.Reset() + h.Write(space) + h.Write([]byte(data)) + s := h.Sum(nil) + uuid := make([]byte, 16) + copy(uuid, s) + uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant + return uuid +} + +// NewMD5 returns a new MD5 (Version 3) UUID based on the +// supplied name space and data. +// +// NewHash(md5.New(), space, data, 3) +func NewMD5(space UUID, data []byte) UUID { + return NewHash(md5.New(), space, data, 3) +} + +// NewSHA1 returns a new SHA1 (Version 5) UUID based on the +// supplied name space and data. +// +// NewHash(sha1.New(), space, data, 5) +func NewSHA1(space UUID, data []byte) UUID { + return NewHash(sha1.New(), space, data, 5) +} diff --git a/vendor/github.com/pborman/uuid/json.go b/vendor/github.com/pborman/uuid/json.go new file mode 100644 index 0000000000..760580a504 --- /dev/null +++ b/vendor/github.com/pborman/uuid/json.go @@ -0,0 +1,30 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "errors" + +func (u UUID) MarshalJSON() ([]byte, error) { + if len(u) == 0 { + return []byte(`""`), nil + } + return []byte(`"` + u.String() + `"`), nil +} + +func (u *UUID) UnmarshalJSON(data []byte) error { + if len(data) == 0 || string(data) == `""` { + return nil + } + if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' { + return errors.New("invalid UUID format") + } + data = data[1 : len(data)-1] + uu := Parse(string(data)) + if uu == nil { + return errors.New("invalid UUID format") + } + *u = uu + return nil +} diff --git a/vendor/github.com/pborman/uuid/json_test.go b/vendor/github.com/pborman/uuid/json_test.go new file mode 100644 index 0000000000..b5eae09247 --- /dev/null +++ b/vendor/github.com/pborman/uuid/json_test.go @@ -0,0 +1,32 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/json" + "reflect" + "testing" +) + +var testUUID = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479") + +func TestJSON(t *testing.T) { + type S struct { + ID1 UUID + ID2 UUID + } + s1 := S{ID1: testUUID} + data, err := json.Marshal(&s1) + if err != nil { + t.Fatal(err) + } + var s2 S + if err := json.Unmarshal(data, &s2); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(&s1, &s2) { + t.Errorf("got %#v, want %#v", s2, s1) + } +} diff --git a/vendor/github.com/pborman/uuid/node.go b/vendor/github.com/pborman/uuid/node.go new file mode 100755 index 0000000000..dd0a8ac189 --- /dev/null +++ b/vendor/github.com/pborman/uuid/node.go @@ -0,0 +1,101 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "net" + +var ( + interfaces []net.Interface // cached list of interfaces + ifname string // name of interface being used + nodeID []byte // hardware for version 1 UUIDs +) + +// NodeInterface returns the name of the interface from which the NodeID was +// derived. The interface "user" is returned if the NodeID was set by +// SetNodeID. +func NodeInterface() string { + return ifname +} + +// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. +// If name is "" then the first usable interface found will be used or a random +// Node ID will be generated. If a named interface cannot be found then false +// is returned. +// +// SetNodeInterface never fails when name is "". +func SetNodeInterface(name string) bool { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil && name != "" { + return false + } + } + + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + if setNodeID(ifs.HardwareAddr) { + ifname = ifs.Name + return true + } + } + } + + // We found no interfaces with a valid hardware address. If name + // does not specify a specific interface generate a random Node ID + // (section 4.1.6) + if name == "" { + if nodeID == nil { + nodeID = make([]byte, 6) + } + randomBits(nodeID) + return true + } + return false +} + +// NodeID returns a slice of a copy of the current Node ID, setting the Node ID +// if not already set. +func NodeID() []byte { + if nodeID == nil { + SetNodeInterface("") + } + nid := make([]byte, 6) + copy(nid, nodeID) + return nid +} + +// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes +// of id are used. If id is less than 6 bytes then false is returned and the +// Node ID is not set. +func SetNodeID(id []byte) bool { + if setNodeID(id) { + ifname = "user" + return true + } + return false +} + +func setNodeID(id []byte) bool { + if len(id) < 6 { + return false + } + if nodeID == nil { + nodeID = make([]byte, 6) + } + copy(nodeID, id) + return true +} + +// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is +// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) NodeID() []byte { + if len(uuid) != 16 { + return nil + } + node := make([]byte, 6) + copy(node, uuid[10:]) + return node +} diff --git a/vendor/github.com/pborman/uuid/seq_test.go b/vendor/github.com/pborman/uuid/seq_test.go new file mode 100644 index 0000000000..3b3d1430d5 --- /dev/null +++ b/vendor/github.com/pborman/uuid/seq_test.go @@ -0,0 +1,66 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "flag" + "runtime" + "testing" + "time" +) + +// This test is only run when --regressions is passed on the go test line. +var regressions = flag.Bool("regressions", false, "run uuid regression tests") + +// TestClockSeqRace tests for a particular race condition of returning two +// identical Version1 UUIDs. The duration of 1 minute was chosen as the race +// condition, before being fixed, nearly always occured in under 30 seconds. +func TestClockSeqRace(t *testing.T) { + if !*regressions { + t.Skip("skipping regression tests") + } + duration := time.Minute + + done := make(chan struct{}) + defer close(done) + + ch := make(chan UUID, 10000) + ncpu := runtime.NumCPU() + switch ncpu { + case 0, 1: + // We can't run the test effectively. + t.Skip("skipping race test, only one CPU detected") + return + default: + runtime.GOMAXPROCS(ncpu) + } + for i := 0; i < ncpu; i++ { + go func() { + for { + select { + case <-done: + return + case ch <- NewUUID(): + } + } + }() + } + + uuids := make(map[string]bool) + cnt := 0 + start := time.Now() + for u := range ch { + s := u.String() + if uuids[s] { + t.Errorf("duplicate uuid after %d in %v: %s", cnt, time.Since(start), s) + return + } + uuids[s] = true + if time.Since(start) > duration { + return + } + cnt++ + } +} diff --git a/vendor/github.com/pborman/uuid/time.go b/vendor/github.com/pborman/uuid/time.go new file mode 100755 index 0000000000..7ebc9bef10 --- /dev/null +++ b/vendor/github.com/pborman/uuid/time.go @@ -0,0 +1,132 @@ +// Copyright 2014 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "sync" + "time" +) + +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct +// 1582. +type Time int64 + +const ( + lillian = 2299160 // Julian day of 15 Oct 1582 + unix = 2440587 // Julian day of 1 Jan 1970 + epoch = unix - lillian // Days between epochs + g1582 = epoch * 86400 // seconds between epochs + g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs +) + +var ( + mu sync.Mutex + lasttime uint64 // last time we returned + clock_seq uint16 // clock sequence for this run + + timeNow = time.Now // for testing +) + +// UnixTime converts t the number of seconds and nanoseconds using the Unix +// epoch of 1 Jan 1970. +func (t Time) UnixTime() (sec, nsec int64) { + sec = int64(t - g1582ns100) + nsec = (sec % 10000000) * 100 + sec /= 10000000 + return sec, nsec +} + +// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and +// clock sequence as well as adjusting the clock sequence as needed. An error +// is returned if the current time cannot be determined. +func GetTime() (Time, uint16, error) { + defer mu.Unlock() + mu.Lock() + return getTime() +} + +func getTime() (Time, uint16, error) { + t := timeNow() + + // If we don't have a clock sequence already, set one. + if clock_seq == 0 { + setClockSequence(-1) + } + now := uint64(t.UnixNano()/100) + g1582ns100 + + // If time has gone backwards with this clock sequence then we + // increment the clock sequence + if now <= lasttime { + clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000 + } + lasttime = now + return Time(now), clock_seq, nil +} + +// ClockSequence returns the current clock sequence, generating one if not +// already set. The clock sequence is only used for Version 1 UUIDs. +// +// The uuid package does not use global static storage for the clock sequence or +// the last time a UUID was generated. Unless SetClockSequence a new random +// clock sequence is generated the first time a clock sequence is requested by +// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated +// for +func ClockSequence() int { + defer mu.Unlock() + mu.Lock() + return clockSequence() +} + +func clockSequence() int { + if clock_seq == 0 { + setClockSequence(-1) + } + return int(clock_seq & 0x3fff) +} + +// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to +// -1 causes a new sequence to be generated. +func SetClockSequence(seq int) { + defer mu.Unlock() + mu.Lock() + setClockSequence(seq) +} + +func setClockSequence(seq int) { + if seq == -1 { + var b [2]byte + randomBits(b[:]) // clock sequence + seq = int(b[0])<<8 | int(b[1]) + } + old_seq := clock_seq + clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant + if old_seq != clock_seq { + lasttime = 0 + } +} + +// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in +// uuid. It returns false if uuid is not valid. The time is only well defined +// for version 1 and 2 UUIDs. +func (uuid UUID) Time() (Time, bool) { + if len(uuid) != 16 { + return 0, false + } + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + return Time(time), true +} + +// ClockSequence returns the clock sequence encoded in uuid. It returns false +// if uuid is not valid. The clock sequence is only well defined for version 1 +// and 2 UUIDs. +func (uuid UUID) ClockSequence() (int, bool) { + if len(uuid) != 16 { + return 0, false + } + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true +} diff --git a/vendor/github.com/pborman/uuid/util.go b/vendor/github.com/pborman/uuid/util.go new file mode 100644 index 0000000000..de40b102c4 --- /dev/null +++ b/vendor/github.com/pborman/uuid/util.go @@ -0,0 +1,43 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// randomBits completely fills slice b with random data. +func randomBits(b []byte) { + if _, err := io.ReadFull(rander, b); err != nil { + panic(err.Error()) // rand should never fail + } +} + +// xvalues returns the value of a byte as a hexadecimal digit or 255. +var xvalues = []byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +} + +// xtob converts the the first two hex bytes of x into a byte. +func xtob(x string) (byte, bool) { + b1 := xvalues[x[0]] + b2 := xvalues[x[1]] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} diff --git a/vendor/github.com/pborman/uuid/uuid.go b/vendor/github.com/pborman/uuid/uuid.go new file mode 100755 index 0000000000..2920fae632 --- /dev/null +++ b/vendor/github.com/pborman/uuid/uuid.go @@ -0,0 +1,163 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "crypto/rand" + "fmt" + "io" + "strings" +) + +// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC +// 4122. +type UUID []byte + +// A Version represents a UUIDs version. +type Version byte + +// A Variant represents a UUIDs variant. +type Variant byte + +// Constants returned by Variant. +const ( + Invalid = Variant(iota) // Invalid UUID + RFC4122 // The variant specified in RFC4122 + Reserved // Reserved, NCS backward compatibility. + Microsoft // Reserved, Microsoft Corporation backward compatibility. + Future // Reserved for future definition. +) + +var rander = rand.Reader // random function + +// New returns a new random (version 4) UUID as a string. It is a convenience +// function for NewRandom().String(). +func New() string { + return NewRandom().String() +} + +// Parse decodes s into a UUID or returns nil. Both the UUID form of +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded. +func Parse(s string) UUID { + if len(s) == 36+9 { + if strings.ToLower(s[:9]) != "urn:uuid:" { + return nil + } + s = s[9:] + } else if len(s) != 36 { + return nil + } + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return nil + } + uuid := make([]byte, 16) + for i, x := range []int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + if v, ok := xtob(s[x:]); !ok { + return nil + } else { + uuid[i] = v + } + } + return uuid +} + +// Equal returns true if uuid1 and uuid2 are equal. +func Equal(uuid1, uuid2 UUID) bool { + return bytes.Equal(uuid1, uuid2) +} + +// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// , or "" if uuid is invalid. +func (uuid UUID) String() string { + if uuid == nil || len(uuid) != 16 { + return "" + } + b := []byte(uuid) + return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x", + b[:4], b[4:6], b[6:8], b[8:10], b[10:]) +} + +// URN returns the RFC 2141 URN form of uuid, +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. +func (uuid UUID) URN() string { + if uuid == nil || len(uuid) != 16 { + return "" + } + b := []byte(uuid) + return fmt.Sprintf("urn:uuid:%08x-%04x-%04x-%04x-%012x", + b[:4], b[4:6], b[6:8], b[8:10], b[10:]) +} + +// Variant returns the variant encoded in uuid. It returns Invalid if +// uuid is invalid. +func (uuid UUID) Variant() Variant { + if len(uuid) != 16 { + return Invalid + } + switch { + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + default: + return Reserved + } + panic("unreachable") +} + +// Version returns the verison of uuid. It returns false if uuid is not +// valid. +func (uuid UUID) Version() (Version, bool) { + if len(uuid) != 16 { + return 0, false + } + return Version(uuid[6] >> 4), true +} + +func (v Version) String() string { + if v > 15 { + return fmt.Sprintf("BAD_VERSION_%d", v) + } + return fmt.Sprintf("VERSION_%d", v) +} + +func (v Variant) String() string { + switch v { + case RFC4122: + return "RFC4122" + case Reserved: + return "Reserved" + case Microsoft: + return "Microsoft" + case Future: + return "Future" + case Invalid: + return "Invalid" + } + return fmt.Sprintf("BadVariant%d", int(v)) +} + +// SetRand sets the random number generator to r, which implents io.Reader. +// If r.Read returns an error when the package requests random data then +// a panic will be issued. +// +// Calling SetRand with nil sets the random number generator to the default +// generator. +func SetRand(r io.Reader) { + if r == nil { + rander = rand.Reader + return + } + rander = r +} diff --git a/vendor/github.com/pborman/uuid/uuid_test.go b/vendor/github.com/pborman/uuid/uuid_test.go new file mode 100755 index 0000000000..417ebeb26a --- /dev/null +++ b/vendor/github.com/pborman/uuid/uuid_test.go @@ -0,0 +1,390 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "fmt" + "os" + "strings" + "testing" + "time" +) + +type test struct { + in string + version Version + variant Variant + isuuid bool +} + +var tests = []test{ + {"f47ac10b-58cc-0372-8567-0e02b2c3d479", 0, RFC4122, true}, + {"f47ac10b-58cc-1372-8567-0e02b2c3d479", 1, RFC4122, true}, + {"f47ac10b-58cc-2372-8567-0e02b2c3d479", 2, RFC4122, true}, + {"f47ac10b-58cc-3372-8567-0e02b2c3d479", 3, RFC4122, true}, + {"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-5372-8567-0e02b2c3d479", 5, RFC4122, true}, + {"f47ac10b-58cc-6372-8567-0e02b2c3d479", 6, RFC4122, true}, + {"f47ac10b-58cc-7372-8567-0e02b2c3d479", 7, RFC4122, true}, + {"f47ac10b-58cc-8372-8567-0e02b2c3d479", 8, RFC4122, true}, + {"f47ac10b-58cc-9372-8567-0e02b2c3d479", 9, RFC4122, true}, + {"f47ac10b-58cc-a372-8567-0e02b2c3d479", 10, RFC4122, true}, + {"f47ac10b-58cc-b372-8567-0e02b2c3d479", 11, RFC4122, true}, + {"f47ac10b-58cc-c372-8567-0e02b2c3d479", 12, RFC4122, true}, + {"f47ac10b-58cc-d372-8567-0e02b2c3d479", 13, RFC4122, true}, + {"f47ac10b-58cc-e372-8567-0e02b2c3d479", 14, RFC4122, true}, + {"f47ac10b-58cc-f372-8567-0e02b2c3d479", 15, RFC4122, true}, + + {"urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, + {"URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-1567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-2567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-3567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-4567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-5567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-6567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-7567-0e02b2c3d479", 4, Reserved, true}, + {"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-9567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-a567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-b567-0e02b2c3d479", 4, RFC4122, true}, + {"f47ac10b-58cc-4372-c567-0e02b2c3d479", 4, Microsoft, true}, + {"f47ac10b-58cc-4372-d567-0e02b2c3d479", 4, Microsoft, true}, + {"f47ac10b-58cc-4372-e567-0e02b2c3d479", 4, Future, true}, + {"f47ac10b-58cc-4372-f567-0e02b2c3d479", 4, Future, true}, + + {"f47ac10b158cc-5372-a567-0e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc25372-a567-0e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc-53723a567-0e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc-5372-a56740e02b2c3d479", 0, Invalid, false}, + {"f47ac10b-58cc-5372-a567-0e02-2c3d479", 0, Invalid, false}, + {"g47ac10b-58cc-4372-a567-0e02b2c3d479", 0, Invalid, false}, +} + +var constants = []struct { + c interface{} + name string +}{ + {Person, "Person"}, + {Group, "Group"}, + {Org, "Org"}, + {Invalid, "Invalid"}, + {RFC4122, "RFC4122"}, + {Reserved, "Reserved"}, + {Microsoft, "Microsoft"}, + {Future, "Future"}, + {Domain(17), "Domain17"}, + {Variant(42), "BadVariant42"}, +} + +func testTest(t *testing.T, in string, tt test) { + uuid := Parse(in) + if ok := (uuid != nil); ok != tt.isuuid { + t.Errorf("Parse(%s) got %v expected %v\b", in, ok, tt.isuuid) + } + if uuid == nil { + return + } + + if v := uuid.Variant(); v != tt.variant { + t.Errorf("Variant(%s) got %d expected %d\b", in, v, tt.variant) + } + if v, _ := uuid.Version(); v != tt.version { + t.Errorf("Version(%s) got %d expected %d\b", in, v, tt.version) + } +} + +func TestUUID(t *testing.T) { + for _, tt := range tests { + testTest(t, tt.in, tt) + testTest(t, strings.ToUpper(tt.in), tt) + } +} + +func TestConstants(t *testing.T) { + for x, tt := range constants { + v, ok := tt.c.(fmt.Stringer) + if !ok { + t.Errorf("%x: %v: not a stringer", x, v) + } else if s := v.String(); s != tt.name { + v, _ := tt.c.(int) + t.Errorf("%x: Constant %T:%d gives %q, expected %q\n", x, tt.c, v, s, tt.name) + } + } +} + +func TestRandomUUID(t *testing.T) { + m := make(map[string]bool) + for x := 1; x < 32; x++ { + uuid := NewRandom() + s := uuid.String() + if m[s] { + t.Errorf("NewRandom returned duplicated UUID %s\n", s) + } + m[s] = true + if v, _ := uuid.Version(); v != 4 { + t.Errorf("Random UUID of version %s\n", v) + } + if uuid.Variant() != RFC4122 { + t.Errorf("Random UUID is variant %d\n", uuid.Variant()) + } + } +} + +func TestNew(t *testing.T) { + m := make(map[string]bool) + for x := 1; x < 32; x++ { + s := New() + if m[s] { + t.Errorf("New returned duplicated UUID %s\n", s) + } + m[s] = true + uuid := Parse(s) + if uuid == nil { + t.Errorf("New returned %q which does not decode\n", s) + continue + } + if v, _ := uuid.Version(); v != 4 { + t.Errorf("Random UUID of version %s\n", v) + } + if uuid.Variant() != RFC4122 { + t.Errorf("Random UUID is variant %d\n", uuid.Variant()) + } + } +} + +func clockSeq(t *testing.T, uuid UUID) int { + seq, ok := uuid.ClockSequence() + if !ok { + t.Fatalf("%s: invalid clock sequence\n", uuid) + } + return seq +} + +func TestClockSeq(t *testing.T) { + // Fake time.Now for this test to return a monotonically advancing time; restore it at end. + defer func(orig func() time.Time) { timeNow = orig }(timeNow) + monTime := time.Now() + timeNow = func() time.Time { + monTime = monTime.Add(1 * time.Second) + return monTime + } + + SetClockSequence(-1) + uuid1 := NewUUID() + uuid2 := NewUUID() + + if clockSeq(t, uuid1) != clockSeq(t, uuid2) { + t.Errorf("clock sequence %d != %d\n", clockSeq(t, uuid1), clockSeq(t, uuid2)) + } + + SetClockSequence(-1) + uuid2 = NewUUID() + + // Just on the very off chance we generated the same sequence + // two times we try again. + if clockSeq(t, uuid1) == clockSeq(t, uuid2) { + SetClockSequence(-1) + uuid2 = NewUUID() + } + if clockSeq(t, uuid1) == clockSeq(t, uuid2) { + t.Errorf("Duplicate clock sequence %d\n", clockSeq(t, uuid1)) + } + + SetClockSequence(0x1234) + uuid1 = NewUUID() + if seq := clockSeq(t, uuid1); seq != 0x1234 { + t.Errorf("%s: expected seq 0x1234 got 0x%04x\n", uuid1, seq) + } +} + +func TestCoding(t *testing.T) { + text := "7d444840-9dc0-11d1-b245-5ffdce74fad2" + urn := "urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2" + data := UUID{ + 0x7d, 0x44, 0x48, 0x40, + 0x9d, 0xc0, + 0x11, 0xd1, + 0xb2, 0x45, + 0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2, + } + if v := data.String(); v != text { + t.Errorf("%x: encoded to %s, expected %s\n", data, v, text) + } + if v := data.URN(); v != urn { + t.Errorf("%x: urn is %s, expected %s\n", data, v, urn) + } + + uuid := Parse(text) + if !Equal(uuid, data) { + t.Errorf("%s: decoded to %s, expected %s\n", text, uuid, data) + } +} + +func TestVersion1(t *testing.T) { + uuid1 := NewUUID() + uuid2 := NewUUID() + + if Equal(uuid1, uuid2) { + t.Errorf("%s:duplicate uuid\n", uuid1) + } + if v, _ := uuid1.Version(); v != 1 { + t.Errorf("%s: version %s expected 1\n", uuid1, v) + } + if v, _ := uuid2.Version(); v != 1 { + t.Errorf("%s: version %s expected 1\n", uuid2, v) + } + n1 := uuid1.NodeID() + n2 := uuid2.NodeID() + if !bytes.Equal(n1, n2) { + t.Errorf("Different nodes %x != %x\n", n1, n2) + } + t1, ok := uuid1.Time() + if !ok { + t.Errorf("%s: invalid time\n", uuid1) + } + t2, ok := uuid2.Time() + if !ok { + t.Errorf("%s: invalid time\n", uuid2) + } + q1, ok := uuid1.ClockSequence() + if !ok { + t.Errorf("%s: invalid clock sequence\n", uuid1) + } + q2, ok := uuid2.ClockSequence() + if !ok { + t.Errorf("%s: invalid clock sequence", uuid2) + } + + switch { + case t1 == t2 && q1 == q2: + t.Errorf("time stopped\n") + case t1 > t2 && q1 == q2: + t.Errorf("time reversed\n") + case t1 < t2 && q1 != q2: + t.Errorf("clock sequence chaned unexpectedly\n") + } +} + +func TestNodeAndTime(t *testing.T) { + // Time is February 5, 1998 12:30:23.136364800 AM GMT + + uuid := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2") + node := []byte{0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2} + + ts, ok := uuid.Time() + if ok { + c := time.Unix(ts.UnixTime()) + want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC) + if !c.Equal(want) { + t.Errorf("Got time %v, want %v", c, want) + } + } else { + t.Errorf("%s: bad time\n", uuid) + } + if !bytes.Equal(node, uuid.NodeID()) { + t.Errorf("Expected node %v got %v\n", node, uuid.NodeID()) + } +} + +func TestMD5(t *testing.T) { + uuid := NewMD5(NameSpace_DNS, []byte("python.org")).String() + want := "6fa459ea-ee8a-3ca4-894e-db77e160355e" + if uuid != want { + t.Errorf("MD5: got %q expected %q\n", uuid, want) + } +} + +func TestSHA1(t *testing.T) { + uuid := NewSHA1(NameSpace_DNS, []byte("python.org")).String() + want := "886313e1-3b8a-5372-9b90-0c9aee199e5d" + if uuid != want { + t.Errorf("SHA1: got %q expected %q\n", uuid, want) + } +} + +func TestNodeID(t *testing.T) { + nid := []byte{1, 2, 3, 4, 5, 6} + SetNodeInterface("") + s := NodeInterface() + if s == "" || s == "user" { + t.Errorf("NodeInterface %q after SetInteface\n", s) + } + node1 := NodeID() + if node1 == nil { + t.Errorf("NodeID nil after SetNodeInterface\n", s) + } + SetNodeID(nid) + s = NodeInterface() + if s != "user" { + t.Errorf("Expected NodeInterface %q got %q\n", "user", s) + } + node2 := NodeID() + if node2 == nil { + t.Errorf("NodeID nil after SetNodeID\n", s) + } + if bytes.Equal(node1, node2) { + t.Errorf("NodeID not changed after SetNodeID\n", s) + } else if !bytes.Equal(nid, node2) { + t.Errorf("NodeID is %x, expected %x\n", node2, nid) + } +} + +func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) { + if uuid == nil { + t.Errorf("%s failed\n", name) + return + } + if v, _ := uuid.Version(); v != 2 { + t.Errorf("%s: %s: expected version 2, got %s\n", name, uuid, v) + return + } + if v, ok := uuid.Domain(); !ok || v != domain { + if !ok { + t.Errorf("%s: %d: Domain failed\n", name, uuid) + } else { + t.Errorf("%s: %s: expected domain %d, got %d\n", name, uuid, domain, v) + } + } + if v, ok := uuid.Id(); !ok || v != id { + if !ok { + t.Errorf("%s: %d: Id failed\n", name, uuid) + } else { + t.Errorf("%s: %s: expected id %d, got %d\n", name, uuid, id, v) + } + } +} + +func TestDCE(t *testing.T) { + testDCE(t, "NewDCESecurity", NewDCESecurity(42, 12345678), 42, 12345678) + testDCE(t, "NewDCEPerson", NewDCEPerson(), Person, uint32(os.Getuid())) + testDCE(t, "NewDCEGroup", NewDCEGroup(), Group, uint32(os.Getgid())) +} + +type badRand struct{} + +func (r badRand) Read(buf []byte) (int, error) { + for i, _ := range buf { + buf[i] = byte(i) + } + return len(buf), nil +} + +func TestBadRand(t *testing.T) { + SetRand(badRand{}) + uuid1 := New() + uuid2 := New() + if uuid1 != uuid2 { + t.Errorf("execpted duplicates, got %q and %q\n", uuid1, uuid2) + } + SetRand(nil) + uuid1 = New() + uuid2 = New() + if uuid1 == uuid2 { + t.Errorf("unexecpted duplicates, got %q\n", uuid1) + } +} diff --git a/vendor/github.com/pborman/uuid/version1.go b/vendor/github.com/pborman/uuid/version1.go new file mode 100644 index 0000000000..0127eacfab --- /dev/null +++ b/vendor/github.com/pborman/uuid/version1.go @@ -0,0 +1,41 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" +) + +// NewUUID returns a Version 1 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewUUID returns nil. If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewUUID returns nil. +func NewUUID() UUID { + if nodeID == nil { + SetNodeInterface("") + } + + now, seq, err := GetTime() + if err != nil { + return nil + } + + uuid := make([]byte, 16) + + time_low := uint32(now & 0xffffffff) + time_mid := uint16((now >> 32) & 0xffff) + time_hi := uint16((now >> 48) & 0x0fff) + time_hi |= 0x1000 // Version 1 + + binary.BigEndian.PutUint32(uuid[0:], time_low) + binary.BigEndian.PutUint16(uuid[4:], time_mid) + binary.BigEndian.PutUint16(uuid[6:], time_hi) + binary.BigEndian.PutUint16(uuid[8:], seq) + copy(uuid[10:], nodeID) + + return uuid +} diff --git a/vendor/github.com/pborman/uuid/version4.go b/vendor/github.com/pborman/uuid/version4.go new file mode 100644 index 0000000000..b3d4a368dd --- /dev/null +++ b/vendor/github.com/pborman/uuid/version4.go @@ -0,0 +1,25 @@ +// Copyright 2011 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +// Random returns a Random (Version 4) UUID or panics. +// +// The strength of the UUIDs is based on the strength of the crypto/rand +// package. +// +// A note about uniqueness derived from from the UUID Wikipedia entry: +// +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. +func NewRandom() UUID { + uuid := make([]byte, 16) + randomBits([]byte(uuid)) + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid +} From 06b542b41e18134f420264454d602cbc79533fe6 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 17:15:29 +0200 Subject: [PATCH 12/17] Testing RunSearch --- pkg/commands/search_test.go | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 pkg/commands/search_test.go diff --git a/pkg/commands/search_test.go b/pkg/commands/search_test.go new file mode 100644 index 0000000000..872977bb02 --- /dev/null +++ b/pkg/commands/search_test.go @@ -0,0 +1,57 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package commands + +import ( + "strings" + "testing" + + . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" +) + +func ExampleRunSearch() { + ctx := ExampleCommandContext() + args := SearchArgs{} + RunSearch(ctx, args) +} + +func ExampleRunSearch_complex() { + ctx := ExampleCommandContext() + args := SearchArgs{ + Term: "", + NoTrunc: false, + } + RunSearch(ctx, args) +} + +func TestRunSearch_realAPI(t *testing.T) { + ctx := RealAPIContext() + if ctx == nil { + t.Skip() + } + Convey("Testing RunSearch() on real API", t, func() { + Convey("ubuntu", func() { + args := SearchArgs{ + Term: "ubuntu", + NoTrunc: false, + } + + scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx) + err := RunSearch(*scopedCtx, args) + So(err, ShouldBeNil) + So(scopedStderr.String(), ShouldBeEmpty) + + lines := strings.Split(scopedStdout.String(), "\n") + So(len(lines), ShouldBeGreaterThan, 0) + + firstLine := lines[0] + colNames := strings.Fields(firstLine) + So(colNames, ShouldResemble, []string{"NAME", "DESCRIPTION", "STARS", "OFFICIAL", "AUTOMATED"}) + }) + + // FIXME: test invalid word + // FIXME: test no-trunc + }) +} From 574f42fc30175f8bdef801340d6043846ae95eb7 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 17:32:33 +0200 Subject: [PATCH 13/17] Testing inspect --- pkg/commands/inspect_test.go | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 pkg/commands/inspect_test.go diff --git a/pkg/commands/inspect_test.go b/pkg/commands/inspect_test.go new file mode 100644 index 0000000000..e436f490ff --- /dev/null +++ b/pkg/commands/inspect_test.go @@ -0,0 +1,74 @@ +// Copyright (C) 2015 Scaleway. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE.md file. + +package commands + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/scaleway/scaleway-cli/pkg/api" + . "github.com/scaleway/scaleway-cli/vendor/github.com/smartystreets/goconvey/convey" +) + +func ExampleRunInspect() { + ctx := ExampleCommandContext() + args := InspectArgs{} + RunInspect(ctx, args) +} + +func ExampleRunInspect_complex() { + ctx := ExampleCommandContext() + args := InspectArgs{ + Format: "", + Browser: false, + Identifiers: []string{}, + } + RunInspect(ctx, args) +} + +func TestRunInspect_realAPI(t *testing.T) { + ctx := RealAPIContext() + if ctx == nil { + t.Skip() + } + Convey("Testing RunInspect() on real API", t, func() { + Convey("image:ubuntu-vivid", func() { + args := InspectArgs{ + Format: "", + Browser: false, + Identifiers: []string{"image:ubuntu-vivid"}, + } + + scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx) + err := RunInspect(*scopedCtx, args) + So(err, ShouldBeNil) + So(scopedStderr.String(), ShouldBeEmpty) + fmt.Println(scopedStdout) + var results []api.ScalewayImage + err = json.Unmarshal(scopedStdout.Bytes(), &results) + So(err, ShouldBeNil) + So(len(results), ShouldEqual, 1) + So(strings.ToLower(results[0].Name), ShouldContainSubstring, "ubuntu") + So(strings.ToLower(results[0].Name), ShouldContainSubstring, "vivid") + + Convey("-f \"{{.Identifier}}\" image:ubuntu-vivid", func() { + args := InspectArgs{ + Format: "{{.Identifier}}", + Browser: false, + Identifiers: []string{"image:ubuntu-vivid"}, + } + + scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx) + err := RunInspect(*scopedCtx, args) + So(err, ShouldBeNil) + So(scopedStderr.String(), ShouldBeEmpty) + uuid := strings.TrimSpace(scopedStdout.String()) + So(results[0].Identifier, ShouldEqual, uuid) + }) + }) + }) +} From 4b594dad59c404fd196a9b8cff234414656086fb Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Thu, 20 Aug 2015 18:00:43 +0200 Subject: [PATCH 14/17] Testing the real API with Travis --- .travis.yml | 28 ++++++++++++++++++---------- Makefile | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 93c862bbaf..0eac970a56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,13 +9,12 @@ env: - PATH=$HOME/gopath/bin:$PATH -go: - - 1.3 - - 1.4 - - 1.5 - - tip - matrix: + include: + - go: 1.3 + - go: 1.4 + - go: 1.5 TEST_WITH_REAL_API=1 + - go: tip allow_failures: - go: tip @@ -28,7 +27,16 @@ before_install: script: - - make build - - ./scw version - - make cover - - goveralls -service=travis-ci -v -coverprofile=profile.out + - make build show_version + - if [ -z "${TEST_WITH_REAL_API}" -o -z "${TRAVIS_SCALEWAY_TOKEN}" ]; then make test; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_login; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_cleanup || true; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make cover; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_coveralls; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_cleanup; fi + +after_success: + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_cleanup; fi + +after_failure: + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_cleanup; fi diff --git a/Makefile b/Makefile index d50848d88f..d2bcacb80c 100644 --- a/Makefile +++ b/Makefile @@ -149,3 +149,33 @@ party: convey: go get github.com/smartystreets/goconvey goconvey -cover -port=9042 -workDir="$(realpath .)/pkg" -depth=-1 + + +.PHONY: travis_login +travis_login: + @if [ "$(TRAVIS_SCALEWAY_TOKEN)" -a "$(TRAVIS_SCALEWAY_ORGANIZATION)" ]; then \ + echo '{"api_endpoint":"https://api.scaleway.com/","account_endpoint":"https://account.scaleway.com/","organization":"$(TRAVIS_SCALEWAY_ORGANIZATION)","token":"$(TRAVIS_SCALEWAY_TOKEN)"}' > ~/.scwrc && \ + chmod 600 ~/.scwrc; \ + else \ + echo "Cannot login, credentials are missing"; \ + fi + + +.PHONY: travis_coveralls +travis_coveralls: + if [ -f ~/.scwrc ]; then goveralls -service=travis-ci -v -coverprofile=profile.out; fi + + +.PHONY: travis_cleanup +travis_cleanup: + # FIXME: delete only resources created for this project + if [ "$(TRAVIS_SCALEWAY_TOKEN)" -a "$(TRAVIS_SCALEWAY_ORGANIZATION)" ]; then \ + ./scw stop -t $(shell ./scw ps -q) || true; \ + ./scw rm $(shell ./scw ps -aq) || true; \ + ./scw rmi $(shell ./scw images -q) || true; \ + fi + + +.PHONY: show_version +show_version: + ./scw version From b0a9e702d0e3d6e9f0a30d1c9263c8160e9f6585 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Fri, 21 Aug 2015 14:46:47 +0200 Subject: [PATCH 15/17] Improved makefile rules to build profile.out, fixed overlapping results, switched to covermode=count --- Makefile | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index d2bcacb80c..9cfe1cdae5 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ GOCLEAN ?= $(GOCMD) clean GOINSTALL ?= $(GOCMD) install GOTEST ?= $(GOCMD) test GOFMT ?= gofmt -w +GOCOVER ?= $(GOTEST) -covermode=count -v FPM_VERSION ?= $(shell ./dist/scw-Darwin-i386 --version | sed 's/.*v\([0-9.]*\),.*/\1/g') FPM_DOCKER ?= \ @@ -24,11 +25,11 @@ FPM_ARGS ?= \ NAME = scw SRC = cmd/scw -PACKAGES = pkg/api pkg/commands pkg/utils pkg/cli pkg/sshcommand pkg/config +PACKAGES = pkg/api pkg/commands pkg/utils pkg/cli pkg/sshcommand pkg/config pkg/scwversion REV = $(shell git rev-parse HEAD || echo "nogit") TAG = $(shell git describe --tags --always || echo "nogit") BUILDER = scaleway-cli-builder - +ALL_GO_FILES = $(shell find . -type f -name "*.go") BUILD_LIST = $(foreach int, $(SRC), $(int)_build) CLEAN_LIST = $(foreach int, $(SRC) $(PACKAGES), $(int)_clean) @@ -36,10 +37,10 @@ INSTALL_LIST = $(foreach int, $(SRC), $(int)_install) IREF_LIST = $(foreach int, $(SRC) $(PACKAGES), $(int)_iref) TEST_LIST = $(foreach int, $(SRC) $(PACKAGES), $(int)_test) FMT_LIST = $(foreach int, $(SRC) $(PACKAGES), $(int)_fmt) -COVER_LIST = $(foreach int, $(PACKAGES), $(int)_cover) +COVERPROFILE_LIST = $(foreach int, $(PACKAGES), $(int)/profile.out) -.PHONY: $(CLEAN_LIST) $(TEST_LIST) $(FMT_LIST) $(INSTALL_LIST) $(BUILD_LIST) $(IREF_LIST) $(COVER_LIST) +.PHONY: $(CLEAN_LIST) $(TEST_LIST) $(FMT_LIST) $(INSTALL_LIST) $(BUILD_LIST) $(IREF_LIST) all: build @@ -50,15 +51,6 @@ test: $(TEST_LIST) iref: $(IREF_LIST) fmt: $(FMT_LIST) -noop = -comma = , -space = $(noop) $(noop) - -cover: - rm -f profile.out - $(MAKE) $(COVER_LIST) - echo "mode: set" | cat - profile.out > profile.out.tmp && mv profile.out.tmp profile.out - .git: touch $@ @@ -81,10 +73,6 @@ $(IREF_LIST): %_iref: pkg/scwversion/version.go $(GOTEST) -i ./$* $(TEST_LIST): %_test: $(GOTEST) ./$* -$(COVER_LIST): %_cover: - #$(GOTEST) -covermode=set -coverpkg=$(subst $(space),$(comma),$(addprefix ./, $(PACKAGES))) -v -coverprofile=file-profile.out ./$* - $(GOTEST) -covermode=set -v -coverprofile=file-profile.out ./$* - if [ -f file-profile.out ]; then cat file-profile.out | grep -v "mode: set" >> profile.out || true; rm -f file-profile.out; fi $(FMT_LIST): %_fmt: $(GOFMT) ./$* @@ -161,9 +149,22 @@ travis_login: fi +.PHONY: cover +cover: profile.out + +$(COVERPROFILE_LIST): $(ALL_GO_FILES) + rm -f $@ + $(GOCOVER) -coverpkg=./pkg/... -coverprofile=$@ ./$(dir $@) + +profile.out: $(COVERPROFILE_LIST) + rm -f $@ + echo "mode: set" > $@ + cat ./pkg/*/profile.out | grep -v mode: | sort -r | awk '{if($$1 != last) {print $$0;last=$$1}}' >> $@ + + .PHONY: travis_coveralls travis_coveralls: - if [ -f ~/.scwrc ]; then goveralls -service=travis-ci -v -coverprofile=profile.out; fi + if [ -f ~/.scwrc ]; then goveralls -covermode=count -service=travis-ci -v -coverprofile=profile.out; fi .PHONY: travis_cleanup From 3febafac9d7d19ec08ddfc4ef18b5a79b57f8836 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Fri, 21 Aug 2015 16:42:19 +0200 Subject: [PATCH 16/17] Hiding token from travis logs --- .travis.yml | 16 ++++++++-------- Makefile | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0eac970a56..af2fabca34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,15 +28,15 @@ before_install: script: - make build show_version - - if [ -z "${TEST_WITH_REAL_API}" -o -z "${TRAVIS_SCALEWAY_TOKEN}" ]; then make test; fi - - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_login; fi - - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_cleanup || true; fi - - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make cover; fi - - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_coveralls; fi - - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_cleanup; fi + - if [ -z "${TEST_WITH_REAL_API}" -o -z "${TRAVIS_SCALEWAY_ORGANIZATION}" ]; then make test; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_ORGANIZATION}" ]; then make travis_login; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_ORGANIZATION}" ]; then make travis_cleanup || true; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_ORGANIZATION}" ]; then make cover; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_ORGANIZATION}" ]; then make travis_coveralls; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_ORGANIZATION}" ]; then make travis_cleanup; fi after_success: - - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_cleanup; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_ORGANIZATION}" ]; then make travis_cleanup; fi after_failure: - - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_TOKEN}" ]; then make travis_cleanup; fi + - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_ORGANIZATION}" ]; then make travis_cleanup; fi diff --git a/Makefile b/Makefile index 9cfe1cdae5..b23f920814 100644 --- a/Makefile +++ b/Makefile @@ -170,7 +170,7 @@ travis_coveralls: .PHONY: travis_cleanup travis_cleanup: # FIXME: delete only resources created for this project - if [ "$(TRAVIS_SCALEWAY_TOKEN)" -a "$(TRAVIS_SCALEWAY_ORGANIZATION)" ]; then \ + @if [ "$(TRAVIS_SCALEWAY_TOKEN)" -a "$(TRAVIS_SCALEWAY_ORGANIZATION)" ]; then \ ./scw stop -t $(shell ./scw ps -q) || true; \ ./scw rm $(shell ./scw ps -aq) || true; \ ./scw rmi $(shell ./scw images -q) || true; \ From 3a8f19ba2c34e938fbef694460284942b203b342 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Fri, 21 Aug 2015 16:44:41 +0200 Subject: [PATCH 17/17] Showing up Travis environment --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index af2fabca34..a7eb399f6a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,9 +24,11 @@ before_install: - go get -u github.com/axw/gocov/gocov - go get -u github.com/mattn/goveralls - go get golang.org/x/tools/cmd/cover + - go get github.com/moul/anonuuid/cmd/anonuuid script: + - env | anonuuid - make build show_version - if [ -z "${TEST_WITH_REAL_API}" -o -z "${TRAVIS_SCALEWAY_ORGANIZATION}" ]; then make test; fi - if [ "${TEST_WITH_REAL_API}" -a "${TRAVIS_SCALEWAY_ORGANIZATION}" ]; then make travis_login; fi