From c9e9a932e275e736e35fc2cba1c47748a33d3300 Mon Sep 17 00:00:00 2001 From: Patrik Cyvoct Date: Fri, 24 Apr 2020 16:17:02 +0200 Subject: [PATCH] add tests Signed-off-by: Patrik Cyvoct --- ...-usage-registry-login-usage.stderr.golden} | 5 +- ...usage-registry-logout-usage.stderr.golden} | 5 +- ...age-registry-namespace-usage.stderr.golden | 2 - ...est-all-usage-registry-usage.stderr.golden | 2 + internal/core/bootstrap.go | 30 +++-- internal/core/context.go | 29 ++++- internal/core/exec.go | 21 ++++ internal/core/testing.go | 119 ++++++++++-------- ...tom_namespace_login.go => custom_login.go} | 62 +++------ .../registry/v1/custom_login_test.go | 28 +++++ ...m_namespace_logout.go => custom_logout.go} | 58 ++------- .../registry/v1/custom_logout_test.go | 28 +++++ .../registry/v1/helpers_login_logout.go | 20 +-- .../registry/v1/helpers_login_logout_test.go | 56 +++++++++ .../testdata/test-login-docker.stdout.golden | 1 + .../testdata/test-login-podman.stdout.golden | 1 + .../testdata/test-logout-docker.stdout.golden | 1 + .../testdata/test-logout-podman.stdout.golden | 1 + .../test-namespace-login-docker.cassette.yaml | 35 ++++++ .../test-namespace-login-podman.cassette.yaml | 35 ++++++ ...test-namespace-logout-docker.cassette.yaml | 35 ++++++ ...test-namespace-logout-podman.cassette.yaml | 35 ++++++ 22 files changed, 436 insertions(+), 173 deletions(-) rename cmd/scw/testdata/{test-all-usage-registry-namespace-login-usage.stderr.golden => test-all-usage-registry-login-usage.stderr.golden} (77%) rename cmd/scw/testdata/{test-all-usage-registry-namespace-logout-usage.stderr.golden => test-all-usage-registry-logout-usage.stderr.golden} (77%) create mode 100644 internal/core/exec.go rename internal/namespaces/registry/v1/{custom_namespace_login.go => custom_login.go} (53%) create mode 100644 internal/namespaces/registry/v1/custom_login_test.go rename internal/namespaces/registry/v1/{custom_namespace_logout.go => custom_logout.go} (52%) create mode 100644 internal/namespaces/registry/v1/custom_logout_test.go create mode 100644 internal/namespaces/registry/v1/helpers_login_logout_test.go create mode 100644 internal/namespaces/registry/v1/testdata/test-login-docker.stdout.golden create mode 100644 internal/namespaces/registry/v1/testdata/test-login-podman.stdout.golden create mode 100644 internal/namespaces/registry/v1/testdata/test-logout-docker.stdout.golden create mode 100644 internal/namespaces/registry/v1/testdata/test-logout-podman.stdout.golden create mode 100644 internal/namespaces/registry/v1/testdata/test-namespace-login-docker.cassette.yaml create mode 100644 internal/namespaces/registry/v1/testdata/test-namespace-login-podman.cassette.yaml create mode 100644 internal/namespaces/registry/v1/testdata/test-namespace-logout-docker.cassette.yaml create mode 100644 internal/namespaces/registry/v1/testdata/test-namespace-logout-podman.cassette.yaml diff --git a/cmd/scw/testdata/test-all-usage-registry-namespace-login-usage.stderr.golden b/cmd/scw/testdata/test-all-usage-registry-login-usage.stderr.golden similarity index 77% rename from cmd/scw/testdata/test-all-usage-registry-namespace-login-usage.stderr.golden rename to cmd/scw/testdata/test-all-usage-registry-login-usage.stderr.golden index ea04a1d1c3..04c7137735 100644 --- a/cmd/scw/testdata/test-all-usage-registry-namespace-login-usage.stderr.golden +++ b/cmd/scw/testdata/test-all-usage-registry-login-usage.stderr.golden @@ -1,11 +1,10 @@ -This command will run the correct command in order to log you in on the namespace with the chosen program. +This command will run the correct command in order to log you in on the registry with the chosen program. You will need to have the chosen binary installed on your system and in your PATH. USAGE: - scw registry namespace login [arg=value ...] + scw registry login [arg=value ...] ARGS: - namespace Namespace name or ID to log in to [program=docker] Program used to log in to the namespace (docker | podman) [region] Region to target. If none is passed will use default region from the config diff --git a/cmd/scw/testdata/test-all-usage-registry-namespace-logout-usage.stderr.golden b/cmd/scw/testdata/test-all-usage-registry-logout-usage.stderr.golden similarity index 77% rename from cmd/scw/testdata/test-all-usage-registry-namespace-logout-usage.stderr.golden rename to cmd/scw/testdata/test-all-usage-registry-logout-usage.stderr.golden index ff8308919b..60a77250d0 100644 --- a/cmd/scw/testdata/test-all-usage-registry-namespace-logout-usage.stderr.golden +++ b/cmd/scw/testdata/test-all-usage-registry-logout-usage.stderr.golden @@ -1,11 +1,10 @@ -This command will run the correct command in order to log you out of the namespace with the chosen program. +This command will run the correct command in order to log you out of the registry with the chosen program. You will need to have the chosen binary installed on your system and in your PATH. USAGE: - scw registry namespace logout [arg=value ...] + scw registry logout [arg=value ...] ARGS: - namespace Namespace name or ID to log out of [program=docker] Program used to log in to the namespace (docker | podman) [region] Region to target. If none is passed will use default region from the config diff --git a/cmd/scw/testdata/test-all-usage-registry-namespace-usage.stderr.golden b/cmd/scw/testdata/test-all-usage-registry-namespace-usage.stderr.golden index 4e02710bb9..913fe74b1d 100644 --- a/cmd/scw/testdata/test-all-usage-registry-namespace-usage.stderr.golden +++ b/cmd/scw/testdata/test-all-usage-registry-namespace-usage.stderr.golden @@ -17,8 +17,6 @@ AVAILABLE COMMANDS: create Create a new namespace update Update an existing namespace delete Delete an existing namespace - login Login to a namespace - logout Logout of a namespace FLAGS: -h, --help help for namespace diff --git a/cmd/scw/testdata/test-all-usage-registry-usage.stderr.golden b/cmd/scw/testdata/test-all-usage-registry-usage.stderr.golden index 36a2739d4d..e5424b35cd 100644 --- a/cmd/scw/testdata/test-all-usage-registry-usage.stderr.golden +++ b/cmd/scw/testdata/test-all-usage-registry-usage.stderr.golden @@ -7,6 +7,8 @@ AVAILABLE COMMANDS: namespace A namespace is for images what a folder is for files image An image represents a container image tag A tag represents a container tag of an image + login Login to a registry + logout Logout of a registry FLAGS: -h, --help help for registry diff --git a/internal/core/bootstrap.go b/internal/core/bootstrap.go index de8f3844d3..cb554f2922 100644 --- a/internal/core/bootstrap.go +++ b/internal/core/bootstrap.go @@ -40,6 +40,10 @@ type BootstrapConfig struct { // OverrideEnv overrides environment variables returned by core.ExtractEnv function. // This is useful for tests as it allows overriding env without relying on global state. OverrideEnv map[string]string + + // OverrideExecCommmand contains programs that will be overriden during the test. + // This is useful when commands include external commands + OverrideExecCommand map[string]ExecCmd } // Bootstrap is the main entry point. It is directly called from main. @@ -57,16 +61,17 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e // Meta store globally available variables like SDK client. // Meta is injected in a context object that will be passed to all commands. meta := &meta{ - BinaryName: config.Args[0], - BuildInfo: config.BuildInfo, - stdout: config.Stdout, - stderr: config.Stderr, - Client: config.Client, - Commands: config.Commands, - Printer: globalPrinter, - OverrideEnv: config.OverrideEnv, - result: nil, // result is later injected by cobra_utils.go/cobraRun() - command: nil, // command is later injected by cobra_utils.go/cobraRun() + BinaryName: config.Args[0], + BuildInfo: config.BuildInfo, + stdout: config.Stdout, + stderr: config.Stderr, + Client: config.Client, + Commands: config.Commands, + Printer: globalPrinter, + OverrideEnv: config.OverrideEnv, + OverrideExecCommand: config.OverrideExecCommand, + result: nil, // result is later injected by cobra_utils.go/cobraRun() + command: nil, // command is later injected by cobra_utils.go/cobraRun() } // We make sure OverrideEnv is never nil in meta. @@ -74,6 +79,11 @@ func Bootstrap(config *BootstrapConfig) (exitCode int, result interface{}, err e meta.OverrideEnv = map[string]string{} } + // We make sure OverrideExecCommand is never nil in meta. + if meta.OverrideExecCommand == nil { + meta.OverrideExecCommand = map[string]ExecCmd{} + } + // Send Matomo telemetry when exiting the bootstrap start := time.Now() defer func() { diff --git a/internal/core/context.go b/internal/core/context.go index 9f9ebf9014..975a8748ee 100644 --- a/internal/core/context.go +++ b/internal/core/context.go @@ -4,6 +4,7 @@ import ( "context" "io" "os" + "os/exec" "github.com/scaleway/scaleway-cli/internal/printer" "github.com/scaleway/scaleway-sdk-go/scw" @@ -17,11 +18,12 @@ type meta struct { DebugModeFlag bool PrinterTypeFlag printer.Type - BuildInfo *BuildInfo - Client *scw.Client - Printer printer.Printer - Commands *Commands - OverrideEnv map[string]string + BuildInfo *BuildInfo + Client *scw.Client + Printer printer.Printer + Commands *Commands + OverrideEnv map[string]string + OverrideExecCommand map[string]ExecCmd command *Command stdout io.Writer @@ -99,3 +101,20 @@ func ExtractUserHomeDir(ctx context.Context) string { func ExtractBinaryName(ctx context.Context) string { return extractMeta(ctx).BinaryName } + +func ExtractAndExecCommand(ctx context.Context, function string, args ...string) Cmd { + meta := extractMeta(ctx) + var cmd Cmd + if f, exist := meta.OverrideExecCommand[function]; exist { + cmd = f(function, os.Stdin, meta.stdout, meta.stderr, args...) + } else { + realCmd := exec.Command(function, args...) + realCmd.Stdout = meta.stdout + realCmd.Stderr = meta.stderr + cmd = &RealCmd{ + realCmd, + } + } + + return cmd +} diff --git a/internal/core/exec.go b/internal/core/exec.go new file mode 100644 index 0000000000..c6bc30a14a --- /dev/null +++ b/internal/core/exec.go @@ -0,0 +1,21 @@ +package core + +import ( + "io" + "os/exec" +) + +type ExecCmd func(name string, stdin io.Reader, stdout io.Writer, stderr io.Writer, args ...string) Cmd + +type Cmd interface { + Run() error + SetStdin(io.Reader) +} + +type RealCmd struct { + *exec.Cmd +} + +func (rc *RealCmd) SetStdin(r io.Reader) { + rc.Stdin = r +} diff --git a/internal/core/testing.go b/internal/core/testing.go index 86a63b1a62..b792624997 100644 --- a/internal/core/testing.go +++ b/internal/core/testing.go @@ -57,6 +57,9 @@ type CheckFuncCtx struct { // OverrideEnv passed in the TestConfig OverrideEnv map[string]string + + // OverrideExecCommand passed in the TestConfig + OverrideExecCommand map[string]ExecCmd } // TestCheck is a function that perform assertion on a CheckFuncCtx @@ -67,20 +70,22 @@ type BeforeFunc func(ctx *BeforeFuncCtx) error type AfterFunc func(ctx *AfterFuncCtx) error type BeforeFuncCtx struct { - T *testing.T - Client *scw.Client - ExecuteCmd func(args []string) interface{} - Meta map[string]interface{} - OverrideEnv map[string]string + T *testing.T + Client *scw.Client + ExecuteCmd func(args []string) interface{} + Meta map[string]interface{} + OverrideEnv map[string]string + OverrideExecCommand map[string]ExecCmd } type AfterFuncCtx struct { - T *testing.T - Client *scw.Client - ExecuteCmd func(args []string) interface{} - Meta map[string]interface{} - CmdResult interface{} - OverrideEnv map[string]string + T *testing.T + Client *scw.Client + ExecuteCmd func(args []string) interface{} + Meta map[string]interface{} + CmdResult interface{} + OverrideEnv map[string]string + OverrideExecCommand map[string]ExecCmd } // TestConfig contain configuration that can be used with the Test function @@ -130,6 +135,9 @@ type TestConfig struct { // Custom client to use for test, if none are provided will create one automatically Client *scw.Client + + // OverrideExecCommand contains programs that will be overriden during the test. + OverrideExecCommand map[string]ExecCmd } // getTestFilePath returns a valid filename path based on the go test name and suffix. (Take care of non fs friendly char) @@ -158,6 +166,7 @@ func createTestClient(t *testing.T, testConfig *TestConfig) (client *scw.Client, scw.WithEnv(), scw.WithUserAgent("cli-e2e-test"), scw.WithDefaultOrganizationID("11111111-1111-1111-1111-111111111111"), + scw.WithAuth("SCWXXXXXXXXXXXXXXXXX", "11111111-1111-1111-1111-111111111111"), } // If client is NOT an E2E client we init http recorder and load configuration. @@ -222,9 +231,14 @@ func Test(config *TestConfig) func(t *testing.T) { meta := map[string]interface{}{} - overideEnv := config.OverrideEnv - if overideEnv == nil { - overideEnv = map[string]string{} + overrideEnv := config.OverrideEnv + if overrideEnv == nil { + overrideEnv = map[string]string{} + } + + overrideExecCommand := config.OverrideExecCommand + if overrideExecCommand == nil { + overrideExecCommand = map[string]ExecCmd{} } if config.TmpHomeDir { @@ -234,7 +248,7 @@ func Test(config *TestConfig) func(t *testing.T) { err = os.RemoveAll(dir) assert.NoError(t, err) }() - overideEnv["HOME"] = dir + overrideEnv["HOME"] = dir } executeCmd := func(args []string) interface{} { @@ -242,14 +256,15 @@ func Test(config *TestConfig) func(t *testing.T) { stderrBuffer := &bytes.Buffer{} logger.Debugf("command: %s", args) _, result, err := Bootstrap(&BootstrapConfig{ - Args: args, - Commands: config.Commands, - BuildInfo: &config.BuildInfo, - Stdout: stdoutBuffer, - Stderr: stderrBuffer, - Client: client, - DisableTelemetry: true, - OverrideEnv: overideEnv, + Args: args, + Commands: config.Commands, + BuildInfo: &config.BuildInfo, + Stdout: stdoutBuffer, + Stderr: stderrBuffer, + Client: client, + DisableTelemetry: true, + OverrideEnv: overrideEnv, + OverrideExecCommand: overrideExecCommand, }) require.NoError(t, err, "stdout: %s\nstderr: %s", stdoutBuffer.String(), stderrBuffer.String()) @@ -259,11 +274,12 @@ func Test(config *TestConfig) func(t *testing.T) { // Run config.BeforeFunc if config.BeforeFunc != nil { require.NoError(t, config.BeforeFunc(&BeforeFuncCtx{ - T: t, - Client: client, - ExecuteCmd: executeCmd, - Meta: meta, - OverrideEnv: overideEnv, + T: t, + Client: client, + ExecuteCmd: executeCmd, + Meta: meta, + OverrideEnv: overrideEnv, + OverrideExecCommand: overrideExecCommand, })) } @@ -280,38 +296,41 @@ func Test(config *TestConfig) func(t *testing.T) { stderr := &bytes.Buffer{} logger.Debugf("command: %s", args) exitCode, result, err = Bootstrap(&BootstrapConfig{ - Args: args, - Commands: config.Commands, - BuildInfo: &config.BuildInfo, - Stdout: stdout, - Stderr: stderr, - Client: client, - DisableTelemetry: true, - OverrideEnv: overideEnv, + Args: args, + Commands: config.Commands, + BuildInfo: &config.BuildInfo, + Stdout: stdout, + Stderr: stderr, + Client: client, + DisableTelemetry: true, + OverrideEnv: overrideEnv, + OverrideExecCommand: overrideExecCommand, }) meta["CmdResult"] = result config.Check(t, &CheckFuncCtx{ - ExitCode: exitCode, - Stdout: stdout.Bytes(), - Stderr: stderr.Bytes(), - Meta: meta, - Result: result, - Err: err, - Client: client, - OverrideEnv: overideEnv, + ExitCode: exitCode, + Stdout: stdout.Bytes(), + Stderr: stderr.Bytes(), + Meta: meta, + Result: result, + Err: err, + Client: client, + OverrideEnv: overrideEnv, + OverrideExecCommand: overrideExecCommand, }) } // Run config.AfterFunc if config.AfterFunc != nil { require.NoError(t, config.AfterFunc(&AfterFuncCtx{ - T: t, - Client: client, - ExecuteCmd: executeCmd, - Meta: meta, - CmdResult: result, - OverrideEnv: overideEnv, + T: t, + Client: client, + ExecuteCmd: executeCmd, + Meta: meta, + CmdResult: result, + OverrideEnv: overrideEnv, + OverrideExecCommand: overrideExecCommand, })) } } diff --git a/internal/namespaces/registry/v1/custom_namespace_login.go b/internal/namespaces/registry/v1/custom_login.go similarity index 53% rename from internal/namespaces/registry/v1/custom_namespace_login.go rename to internal/namespaces/registry/v1/custom_login.go index 770fc5e8c3..e9d4150344 100644 --- a/internal/namespaces/registry/v1/custom_namespace_login.go +++ b/internal/namespaces/registry/v1/custom_login.go @@ -3,46 +3,35 @@ package registry import ( "context" "fmt" - "os" "os/exec" "reflect" "strings" "github.com/scaleway/scaleway-cli/internal/core" - "github.com/scaleway/scaleway-sdk-go/api/registry/v1" "github.com/scaleway/scaleway-sdk-go/scw" - "github.com/scaleway/scaleway-sdk-go/validation" ) type registryLoginArgs struct { - Namespace string - Region scw.Region - Program string + Region scw.Region + Program string } func registryLoginCommand() *core.Command { return &core.Command{ - Short: `Login to a namespace`, - Long: `This command will run the correct command in order to log you in on the namespace with the chosen program. + Short: `Login to a registry`, + Long: `This command will run the correct command in order to log you in on the registry with the chosen program. You will need to have the chosen binary installed on your system and in your PATH.`, Namespace: "registry", - Resource: "namespace", - Verb: "login", + Resource: "login", ArgsType: reflect.TypeOf(registryLoginArgs{}), ArgSpecs: []*core.ArgSpec{ - { - Name: "namespace", - Short: `Namespace name or ID to log in to`, - Required: true, - Positional: true, - }, { Name: "program", Short: "Program used to log in to the namespace", Default: core.DefaultValueSetter(string(docker)), EnumValues: availablePrograms.StringArray(), }, - core.RegionArgSpec(), + core.RegionArgSpec(scw.AllRegions...), }, Run: registryLoginRun, } @@ -50,35 +39,18 @@ You will need to have the chosen binary installed on your system and in your PAT func registryLoginRun(ctx context.Context, argsI interface{}) (i interface{}, e error) { args := argsI.(*registryLoginArgs) - client := core.ExtractClient(ctx) - api := registry.NewAPI(client) - var endpoint string - if validation.IsUUID(args.Namespace) { - namespaceResp, err := api.GetNamespace(®istry.GetNamespaceRequest{ - Region: args.Region, - NamespaceID: args.Namespace, - }) - if err != nil { - return nil, err + region := args.Region.String() + if region == "" { + scwRegion, ok := client.GetDefaultRegion() + if !ok { + return nil, fmt.Errorf("no default region configured") } - endpoint = namespaceResp.Endpoint - } else { - namespacesResp, err := api.ListNamespaces(®istry.ListNamespacesRequest{ - Region: args.Region, - Name: &args.Namespace, - }, scw.WithAllPages()) - if err != nil { - return nil, err - } - if len(namespacesResp.Namespaces) == 0 { - return nil, fmt.Errorf("namespace not found") - } else if len(namespacesResp.Namespaces) > 1 { - return nil, fmt.Errorf("multiple namespaces match") - } - endpoint = namespacesResp.Namespaces[0].Endpoint + region = scwRegion.String() } + endpoint := endpointPrefix + region + endpointSuffix + secretKey, ok := client.GetSecretKey() if !ok { return nil, fmt.Errorf("could not get secret key") @@ -93,11 +65,9 @@ func registryLoginRun(ctx context.Context, argsI interface{}) (i interface{}, e return nil, fmt.Errorf("unknown program") } - loginCommand := exec.Command(args.Program, commandArgs...) + loginCommand := core.ExtractAndExecCommand(ctx, args.Program, commandArgs...) - loginCommand.Stdin = strings.NewReader(secretKey) - loginCommand.Stderr = os.Stderr - loginCommand.Stdout = os.Stdout + loginCommand.SetStdin(strings.NewReader(secretKey)) if err := loginCommand.Run(); err != nil { if execErr, ok := err.(*exec.ExitError); ok { diff --git a/internal/namespaces/registry/v1/custom_login_test.go b/internal/namespaces/registry/v1/custom_login_test.go new file mode 100644 index 0000000000..2d989142f3 --- /dev/null +++ b/internal/namespaces/registry/v1/custom_login_test.go @@ -0,0 +1,28 @@ +package registry + +import ( + "testing" + + "github.com/scaleway/scaleway-cli/internal/core" +) + +func Test_Login(t *testing.T) { + t.Run("docker", core.Test(&core.TestConfig{ + Commands: GetCommands(), + Cmd: "scw registry login program=docker", + Check: core.TestCheckCombine( + core.TestCheckGolden(), + core.TestCheckExitCode(0), + ), + OverrideExecCommand: map[string]core.ExecCmd{"docker": dockerFakeCommand}, + })) + t.Run("podman", core.Test(&core.TestConfig{ + Commands: GetCommands(), + Cmd: "scw registry login program=podman", + Check: core.TestCheckCombine( + core.TestCheckGolden(), + core.TestCheckExitCode(0), + ), + OverrideExecCommand: map[string]core.ExecCmd{"podman": podmanFakeCommand}, + })) +} diff --git a/internal/namespaces/registry/v1/custom_namespace_logout.go b/internal/namespaces/registry/v1/custom_logout.go similarity index 52% rename from internal/namespaces/registry/v1/custom_namespace_logout.go rename to internal/namespaces/registry/v1/custom_logout.go index f1fcf03652..af156ab8fb 100644 --- a/internal/namespaces/registry/v1/custom_namespace_logout.go +++ b/internal/namespaces/registry/v1/custom_logout.go @@ -3,38 +3,27 @@ package registry import ( "context" "fmt" - "os" "os/exec" "reflect" "github.com/scaleway/scaleway-cli/internal/core" - "github.com/scaleway/scaleway-sdk-go/api/registry/v1" "github.com/scaleway/scaleway-sdk-go/scw" - "github.com/scaleway/scaleway-sdk-go/validation" ) type registryLogoutArgs struct { - Namespace string - Region scw.Region - Program string + Region scw.Region + Program string } func registryLogoutCommand() *core.Command { return &core.Command{ - Short: `Logout of a namespace`, - Long: `This command will run the correct command in order to log you out of the namespace with the chosen program. + Short: `Logout of a registry`, + Long: `This command will run the correct command in order to log you out of the registry with the chosen program. You will need to have the chosen binary installed on your system and in your PATH.`, Namespace: "registry", - Resource: "namespace", - Verb: "logout", + Resource: "logout", ArgsType: reflect.TypeOf(registryLogoutArgs{}), ArgSpecs: []*core.ArgSpec{ - { - Name: "namespace", - Short: `Namespace name or ID to log out of`, - Required: true, - Positional: true, - }, { Name: "program", Short: "Program used to log in to the namespace", @@ -49,35 +38,17 @@ You will need to have the chosen binary installed on your system and in your PAT func registryLogoutRun(ctx context.Context, argsI interface{}) (i interface{}, e error) { args := argsI.(*registryLogoutArgs) - client := core.ExtractClient(ctx) - api := registry.NewAPI(client) - var endpoint string - if validation.IsUUID(args.Namespace) { - namespaceResp, err := api.GetNamespace(®istry.GetNamespaceRequest{ - Region: args.Region, - NamespaceID: args.Namespace, - }) - if err != nil { - return nil, err + region := args.Region.String() + if region == "" { + scwRegion, ok := client.GetDefaultRegion() + if !ok { + return nil, fmt.Errorf("no default region configured") } - endpoint = namespaceResp.Endpoint - } else { - namespacesResp, err := api.ListNamespaces(®istry.ListNamespacesRequest{ - Region: args.Region, - Name: &args.Namespace, - }, scw.WithAllPages()) - if err != nil { - return nil, err - } - if len(namespacesResp.Namespaces) == 0 { - return nil, fmt.Errorf("namespace not found") - } else if len(namespacesResp.Namespaces) > 1 { - return nil, fmt.Errorf("multiple namespaces match") - } - endpoint = namespacesResp.Namespaces[0].Endpoint + region = scwRegion.String() } + endpoint := endpointPrefix + region + endpointSuffix var commandArgs []string @@ -88,10 +59,7 @@ func registryLogoutRun(ctx context.Context, argsI interface{}) (i interface{}, e return nil, fmt.Errorf("unknown program") } - logoutCommand := exec.Command(args.Program, commandArgs...) - - logoutCommand.Stderr = os.Stderr - logoutCommand.Stdout = os.Stdout + logoutCommand := core.ExtractAndExecCommand(ctx, args.Program, commandArgs...) if err := logoutCommand.Run(); err != nil { if execErr, ok := err.(*exec.ExitError); ok { diff --git a/internal/namespaces/registry/v1/custom_logout_test.go b/internal/namespaces/registry/v1/custom_logout_test.go new file mode 100644 index 0000000000..10bb19d075 --- /dev/null +++ b/internal/namespaces/registry/v1/custom_logout_test.go @@ -0,0 +1,28 @@ +package registry + +import ( + "testing" + + "github.com/scaleway/scaleway-cli/internal/core" +) + +func Test_Logout(t *testing.T) { + t.Run("docker", core.Test(&core.TestConfig{ + Commands: GetCommands(), + Cmd: "scw registry logout program=docker", + Check: core.TestCheckCombine( + core.TestCheckGolden(), + core.TestCheckExitCode(0), + ), + OverrideExecCommand: map[string]core.ExecCmd{"docker": dockerFakeCommand}, + })) + t.Run("podman", core.Test(&core.TestConfig{ + Commands: GetCommands(), + Cmd: "scw registry logout program=podman", + Check: core.TestCheckCombine( + core.TestCheckGolden(), + core.TestCheckExitCode(0), + ), + OverrideExecCommand: map[string]core.ExecCmd{"podman": podmanFakeCommand}, + })) +} diff --git a/internal/namespaces/registry/v1/helpers_login_logout.go b/internal/namespaces/registry/v1/helpers_login_logout.go index 97581c9926..736ed3dc95 100644 --- a/internal/namespaces/registry/v1/helpers_login_logout.go +++ b/internal/namespaces/registry/v1/helpers_login_logout.go @@ -3,6 +3,17 @@ package registry type program string type programs []program +const ( + docker = program("docker") + podman = program("podman") + endpointPrefix = "rg." + endpointSuffix = ".scw.cloud" +) + +var ( + availablePrograms = programs{docker, podman} +) + func (p programs) StringArray() []string { var res []string for _, prog := range p { @@ -10,12 +21,3 @@ func (p programs) StringArray() []string { } return res } - -const ( - docker = program("docker") - podman = program("podman") -) - -var ( - availablePrograms = programs{docker, podman} -) diff --git a/internal/namespaces/registry/v1/helpers_login_logout_test.go b/internal/namespaces/registry/v1/helpers_login_logout_test.go new file mode 100644 index 0000000000..8295caa85b --- /dev/null +++ b/internal/namespaces/registry/v1/helpers_login_logout_test.go @@ -0,0 +1,56 @@ +package registry + +import ( + "fmt" + "io" + + "github.com/scaleway/scaleway-cli/internal/core" +) + +type fakeDockerCommand struct { + stdout io.Writer + stderr io.Writer + args []string +} + +func (f *fakeDockerCommand) Run() error { + if f.args[0] == "login" { + fmt.Fprintln(f.stdout, "Login Succeeded") + } else if f.args[0] == "logout" { + fmt.Fprintln(f.stdout, "Removing login credentials for rg.fr-par.scw.cloud") + } + return nil +} +func (f *fakeDockerCommand) SetStdin(stdin io.Reader) {} + +func dockerFakeCommand(name string, stdin io.Reader, stdout io.Writer, stderr io.Writer, args ...string) core.Cmd { + return &fakeDockerCommand{ + stdout, + stderr, + args, + } +} + +type fakePodmanCommand struct { + stdout io.Writer + stderr io.Writer + args []string +} + +func (f *fakePodmanCommand) Run() error { + if f.args[0] == "login" { + fmt.Fprintln(f.stdout, "Login Succeeded!") + } else if f.args[0] == "logout" { + fmt.Fprintln(f.stdout, "Removed login credentials for rg.fr-par.scw.cloud/testcli") + } + return nil +} +func (f *fakePodmanCommand) SetStdin(stdin io.Reader) {} + +func podmanFakeCommand(name string, stdin io.Reader, stdout io.Writer, stderr io.Writer, args ...string) core.Cmd { + return &fakePodmanCommand{ + stdout, + stderr, + args, + } +} diff --git a/internal/namespaces/registry/v1/testdata/test-login-docker.stdout.golden b/internal/namespaces/registry/v1/testdata/test-login-docker.stdout.golden new file mode 100644 index 0000000000..73f115d1ce --- /dev/null +++ b/internal/namespaces/registry/v1/testdata/test-login-docker.stdout.golden @@ -0,0 +1 @@ +Login Succeeded diff --git a/internal/namespaces/registry/v1/testdata/test-login-podman.stdout.golden b/internal/namespaces/registry/v1/testdata/test-login-podman.stdout.golden new file mode 100644 index 0000000000..734025b64d --- /dev/null +++ b/internal/namespaces/registry/v1/testdata/test-login-podman.stdout.golden @@ -0,0 +1 @@ +Login Succeeded! diff --git a/internal/namespaces/registry/v1/testdata/test-logout-docker.stdout.golden b/internal/namespaces/registry/v1/testdata/test-logout-docker.stdout.golden new file mode 100644 index 0000000000..3ea34d2ede --- /dev/null +++ b/internal/namespaces/registry/v1/testdata/test-logout-docker.stdout.golden @@ -0,0 +1 @@ +Removing login credentials for rg.fr-par.scw.cloud diff --git a/internal/namespaces/registry/v1/testdata/test-logout-podman.stdout.golden b/internal/namespaces/registry/v1/testdata/test-logout-podman.stdout.golden new file mode 100644 index 0000000000..c2ca11fcfc --- /dev/null +++ b/internal/namespaces/registry/v1/testdata/test-logout-podman.stdout.golden @@ -0,0 +1 @@ +Removed login credentials for rg.fr-par.scw.cloud/testcli diff --git a/internal/namespaces/registry/v1/testdata/test-namespace-login-docker.cassette.yaml b/internal/namespaces/registry/v1/testdata/test-namespace-login-docker.cassette.yaml new file mode 100644 index 0000000000..15ecd9425a --- /dev/null +++ b/internal/namespaces/registry/v1/testdata/test-namespace-login-docker.cassette.yaml @@ -0,0 +1,35 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.6+dev (go1.14.2; linux; amd64) cli-e2e-test + url: https://api.scaleway.com/registry/v1/regions/fr-par/namespaces?name=testcli&order_by=created_at_asc&page=1 + method: GET + response: + body: '{"namespaces":[{"id":"a36af907-1539-44ca-b08a-383b58c3308f","name":"testcli","description":"","organization_id":"a6c249e9-c32d-46e4-badb-b57528eafc4b","status":"ready","status_message":"","endpoint":"rg.fr-par.scw.cloud/testcli","is_public":false,"size":0,"created_at":"2020-04-22T17:05:34.279271Z","updated_at":"2020-04-22T17:05:34.279271Z","image_count":0,"region":"fr-par"}],"total_count":1}' + headers: + Content-Length: + - "394" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Fri, 24 Apr 2020 15:47:08 GMT + Server: + - scaleway_api + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 9a3649bf-20d8-41da-b41d-8db31412e01e + status: 200 OK + code: 200 + duration: "" diff --git a/internal/namespaces/registry/v1/testdata/test-namespace-login-podman.cassette.yaml b/internal/namespaces/registry/v1/testdata/test-namespace-login-podman.cassette.yaml new file mode 100644 index 0000000000..041e01c234 --- /dev/null +++ b/internal/namespaces/registry/v1/testdata/test-namespace-login-podman.cassette.yaml @@ -0,0 +1,35 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.6+dev (go1.14.2; linux; amd64) cli-e2e-test + url: https://api.scaleway.com/registry/v1/regions/fr-par/namespaces?name=testcli&order_by=created_at_asc&page=1 + method: GET + response: + body: '{"namespaces":[{"id":"a36af907-1539-44ca-b08a-383b58c3308f","name":"testcli","description":"","organization_id":"a6c249e9-c32d-46e4-badb-b57528eafc4b","status":"ready","status_message":"","endpoint":"rg.fr-par.scw.cloud/testcli","is_public":false,"size":0,"created_at":"2020-04-22T17:05:34.279271Z","updated_at":"2020-04-22T17:05:34.279271Z","image_count":0,"region":"fr-par"}],"total_count":1}' + headers: + Content-Length: + - "394" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Fri, 24 Apr 2020 15:47:08 GMT + Server: + - scaleway_api + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 23644c4b-0892-42b9-970d-684d8efa26ab + status: 200 OK + code: 200 + duration: "" diff --git a/internal/namespaces/registry/v1/testdata/test-namespace-logout-docker.cassette.yaml b/internal/namespaces/registry/v1/testdata/test-namespace-logout-docker.cassette.yaml new file mode 100644 index 0000000000..885f2f2c00 --- /dev/null +++ b/internal/namespaces/registry/v1/testdata/test-namespace-logout-docker.cassette.yaml @@ -0,0 +1,35 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.6+dev (go1.14.2; linux; amd64) cli-e2e-test + url: https://api.scaleway.com/registry/v1/regions/fr-par/namespaces?name=testcli&order_by=created_at_asc&page=1 + method: GET + response: + body: '{"namespaces":[{"id":"a36af907-1539-44ca-b08a-383b58c3308f","name":"testcli","description":"","organization_id":"a6c249e9-c32d-46e4-badb-b57528eafc4b","status":"ready","status_message":"","endpoint":"rg.fr-par.scw.cloud/testcli","is_public":false,"size":0,"created_at":"2020-04-22T17:05:34.279271Z","updated_at":"2020-04-22T17:05:34.279271Z","image_count":0,"region":"fr-par"}],"total_count":1}' + headers: + Content-Length: + - "394" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Fri, 24 Apr 2020 15:47:08 GMT + Server: + - scaleway_api + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - 2a07de71-d6ad-446a-9c06-ad901e301c01 + status: 200 OK + code: 200 + duration: "" diff --git a/internal/namespaces/registry/v1/testdata/test-namespace-logout-podman.cassette.yaml b/internal/namespaces/registry/v1/testdata/test-namespace-logout-podman.cassette.yaml new file mode 100644 index 0000000000..077048d85c --- /dev/null +++ b/internal/namespaces/registry/v1/testdata/test-namespace-logout-podman.cassette.yaml @@ -0,0 +1,35 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - scaleway-sdk-go/v1.0.0-beta.6+dev (go1.14.2; linux; amd64) cli-e2e-test + url: https://api.scaleway.com/registry/v1/regions/fr-par/namespaces?name=testcli&order_by=created_at_asc&page=1 + method: GET + response: + body: '{"namespaces":[{"id":"a36af907-1539-44ca-b08a-383b58c3308f","name":"testcli","description":"","organization_id":"a6c249e9-c32d-46e4-badb-b57528eafc4b","status":"ready","status_message":"","endpoint":"rg.fr-par.scw.cloud/testcli","is_public":false,"size":0,"created_at":"2020-04-22T17:05:34.279271Z","updated_at":"2020-04-22T17:05:34.279271Z","image_count":0,"region":"fr-par"}],"total_count":1}' + headers: + Content-Length: + - "394" + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Fri, 24 Apr 2020 15:47:08 GMT + Server: + - scaleway_api + Strict-Transport-Security: + - max-age=63072000 + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + X-Request-Id: + - da57461c-2f26-47e5-92f1-ab8f0ce5dcd6 + status: 200 OK + code: 200 + duration: ""