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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion cmd/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import (

"github.com/localstack/lstk/internal/api"
"github.com/localstack/lstk/internal/auth"
"github.com/localstack/lstk/internal/config"
"github.com/localstack/lstk/internal/container"
"github.com/localstack/lstk/internal/env"
"github.com/localstack/lstk/internal/output"
"github.com/localstack/lstk/internal/runtime"
"github.com/localstack/lstk/internal/ui"
"github.com/spf13/cobra"
)
Expand All @@ -20,8 +23,16 @@ func newLogoutCmd(cfg *env.Env) *cobra.Command {
PreRunE: initConfig,
RunE: func(cmd *cobra.Command, args []string) error {
platformClient := api.NewPlatformClient(cfg.APIEndpoint)
appConfig, err := config.Get()
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}
var rt runtime.Runtime
if dockerRuntime, err := runtime.NewDockerRuntime(); err == nil {
rt = dockerRuntime
}
if isInteractiveMode(cfg) {
return ui.RunLogout(cmd.Context(), platformClient, cfg.AuthToken, cfg.ForceFileKeyring)
return ui.RunLogout(cmd.Context(), rt, platformClient, cfg.AuthToken, cfg.ForceFileKeyring, appConfig.Containers)
}

sink := output.NewPlainSink(os.Stdout)
Expand All @@ -36,6 +47,12 @@ func newLogoutCmd(cfg *env.Env) *cobra.Command {
}
return fmt.Errorf("failed to logout: %w", err)
}

if rt != nil {
if running, err := container.AnyRunning(cmd.Context(), rt, appConfig.Containers); err == nil && running {
output.EmitNote(sink, "LocalStack is still running in the background")
}
}
return nil
},
}
Expand Down
8 changes: 7 additions & 1 deletion cmd/logs.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package cmd

import (
"fmt"
"os"

"github.com/localstack/lstk/internal/config"
"github.com/localstack/lstk/internal/container"
"github.com/localstack/lstk/internal/output"
"github.com/localstack/lstk/internal/runtime"
Expand All @@ -24,7 +26,11 @@ func newLogsCmd() *cobra.Command {
if err != nil {
return err
}
return container.Logs(cmd.Context(), rt, output.NewPlainSink(os.Stdout), follow)
appConfig, err := config.Get()
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}
return container.Logs(cmd.Context(), rt, output.NewPlainSink(os.Stdout), appConfig.Containers, follow)
},
}
cmd.Flags().BoolP("follow", "f", false, "Follow log output")
Expand Down
7 changes: 7 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,19 @@ func runStart(ctx context.Context, rt runtime.Runtime, cfg *env.Env, tel *teleme
// TODO: replace map with a typed payload struct once event schema is finalised
tel.Emit(ctx, "cli_cmd", map[string]any{"cmd": "lstk start", "params": []string{}})

appConfig, err := config.Get()
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}

opts := container.StartOptions{
PlatformClient: api.NewPlatformClient(cfg.APIEndpoint),
AuthToken: cfg.AuthToken,
ForceFileKeyring: cfg.ForceFileKeyring,
WebAppURL: cfg.WebAppURL,
LocalStackHost: cfg.LocalStackHost,
Containers: appConfig.Containers,
Env: appConfig.Env,
}
if isInteractiveMode(cfg) {
return ui.Run(ctx, rt, version.Version(), opts)
Expand Down
11 changes: 8 additions & 3 deletions cmd/stop.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package cmd

import (
"fmt"
"os"

"github.com/localstack/lstk/internal/config"
"github.com/localstack/lstk/internal/container"
"github.com/localstack/lstk/internal/env"
"github.com/localstack/lstk/internal/output"
Expand All @@ -22,13 +24,16 @@ func newStopCmd(cfg *env.Env) *cobra.Command {
if err != nil {
return err
}
appConfig, err := config.Get()
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}

if isInteractiveMode(cfg) {
return ui.RunStop(cmd.Context(), rt)
return ui.RunStop(cmd.Context(), rt, appConfig.Containers)
}

return container.Stop(cmd.Context(), rt, output.NewPlainSink(os.Stdout))
return container.Stop(cmd.Context(), rt, output.NewPlainSink(os.Stdout), appConfig.Containers)
},
}
}

10 changes: 3 additions & 7 deletions internal/container/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,13 @@ import (
"github.com/localstack/lstk/internal/runtime"
)

func Logs(ctx context.Context, rt runtime.Runtime, sink output.Sink, follow bool) error {
cfg, err := config.Get()
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}
if len(cfg.Containers) == 0 {
func Logs(ctx context.Context, rt runtime.Runtime, sink output.Sink, containers []config.ContainerConfig, follow bool) error {
if len(containers) == 0 {
return fmt.Errorf("no containers configured")
}

// TODO: handle logs per container
c := cfg.Containers[0]
c := containers[0]

pr, pw := io.Pipe()
errCh := make(chan error, 1)
Expand Down
23 changes: 23 additions & 0 deletions internal/container/running.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package container

import (
"context"
"fmt"

"github.com/localstack/lstk/internal/config"
"github.com/localstack/lstk/internal/runtime"
)

func AnyRunning(ctx context.Context, rt runtime.Runtime, containers []config.ContainerConfig) (bool, error) {
for _, c := range containers {
running, err := rt.IsRunning(ctx, c.Name())
if err != nil {
return false, fmt.Errorf("checking %s running: %w", c.Name(), err)
}
if running {
return true, nil
}
}

return false, nil
}
17 changes: 7 additions & 10 deletions internal/container/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type StartOptions struct {
ForceFileKeyring bool
WebAppURL string
LocalStackHost string
Containers []config.ContainerConfig
Env map[string]map[string]string
}

func Start(ctx context.Context, rt runtime.Runtime, sink output.Sink, opts StartOptions, interactive bool) error {
Expand All @@ -48,17 +50,12 @@ func Start(ctx context.Context, rt runtime.Runtime, sink output.Sink, opts Start
return err
}

cfg, err := config.Get()
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}

if hasDuplicateContainerTypes(cfg.Containers) {
if hasDuplicateContainerTypes(opts.Containers) {
output.EmitWarning(sink, "Multiple emulators of the same type are defined in your config; this setup is not supported yet")
}

containers := make([]runtime.ContainerConfig, len(cfg.Containers))
for i, c := range cfg.Containers {
containers := make([]runtime.ContainerConfig, len(opts.Containers))
for i, c := range opts.Containers {
image, err := c.Image()
if err != nil {
return err
Expand All @@ -72,7 +69,7 @@ func Start(ctx context.Context, rt runtime.Runtime, sink output.Sink, opts Start
return err
}

resolvedEnv, err := c.ResolvedEnv(cfg.Env)
resolvedEnv, err := c.ResolvedEnv(opts.Env)
if err != nil {
return err
}
Expand Down Expand Up @@ -115,7 +112,7 @@ func Start(ctx context.Context, rt runtime.Runtime, sink output.Sink, opts Start
setups := map[config.EmulatorType]postStartSetupFunc{
config.EmulatorAWS: awsconfig.Setup,
}
return runPostStartSetups(ctx, sink, cfg.Containers, interactive, opts.LocalStackHost, setups)
return runPostStartSetups(ctx, sink, opts.Containers, interactive, opts.LocalStackHost, setups)
}

func runPostStartSetups(ctx context.Context, sink output.Sink, containers []config.ContainerConfig, interactive bool, localStackHost string, setups map[config.EmulatorType]postStartSetupFunc) error {
Expand Down
9 changes: 2 additions & 7 deletions internal/container/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@ import (
"github.com/localstack/lstk/internal/runtime"
)

func Stop(ctx context.Context, rt runtime.Runtime, sink output.Sink) error {
cfg, err := config.Get()
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}

for _, c := range cfg.Containers {
func Stop(ctx context.Context, rt runtime.Runtime, sink output.Sink, containers []config.ContainerConfig) error {
for _, c := range containers {
name := c.Name()
running, err := rt.IsRunning(ctx, name)
if err != nil {
Expand Down
9 changes: 4 additions & 5 deletions internal/ui/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"os"

tea "github.com/charmbracelet/bubbletea"
"github.com/localstack/lstk/internal/config"
"github.com/localstack/lstk/internal/container"
"github.com/localstack/lstk/internal/endpoint"
"github.com/localstack/lstk/internal/output"
Expand All @@ -32,10 +31,10 @@ func Run(parentCtx context.Context, rt runtime.Runtime, version string, opts con
// FIXME: This assumes a single emulator; revisit for proper multi-emulator support
emulatorName := "LocalStack Emulator"
host := endpoint.Hostname
if cfg, err := config.Get(); err == nil && len(cfg.Containers) > 0 {
emulatorName = cfg.Containers[0].DisplayName()
if cfg.Containers[0].Port != "" {
host, _ = endpoint.ResolveHost(cfg.Containers[0].Port, opts.LocalStackHost)
if len(opts.Containers) > 0 {
emulatorName = opts.Containers[0].DisplayName()
if opts.Containers[0].Port != "" {
host, _ = endpoint.ResolveHost(opts.Containers[0].Port, opts.LocalStackHost)
}
}

Expand Down
15 changes: 12 additions & 3 deletions internal/ui/run_logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import (
tea "github.com/charmbracelet/bubbletea"
"github.com/localstack/lstk/internal/api"
"github.com/localstack/lstk/internal/auth"
"github.com/localstack/lstk/internal/config"
"github.com/localstack/lstk/internal/container"
"github.com/localstack/lstk/internal/output"
"github.com/localstack/lstk/internal/runtime"
)

func RunLogout(parentCtx context.Context, platformClient api.PlatformAPI, authToken string, forceFileKeyring bool) error {
_, cancel := context.WithCancel(parentCtx)
func RunLogout(parentCtx context.Context, rt runtime.Runtime, platformClient api.PlatformAPI, authToken string, forceFileKeyring bool, containers []config.ContainerConfig) error {
ctx, cancel := context.WithCancel(parentCtx)
defer cancel()

app := NewApp("", "", "", cancel, withoutHeader())
Expand All @@ -28,8 +31,14 @@ func RunLogout(parentCtx context.Context, platformClient api.PlatformAPI, authTo
return
}

a := auth.New(output.NewTUISink(programSender{p: p}), platformClient, tokenStorage, authToken, "", false)
sink := output.NewTUISink(programSender{p: p})
a := auth.New(sink, platformClient, tokenStorage, authToken, "", false)
err = a.Logout()
if err == nil && rt != nil {
if running, runningErr := container.AnyRunning(ctx, rt, containers); runningErr == nil && running {
output.EmitNote(sink, "LocalStack is still running in the background")
}
}

runErrCh <- err
if err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, auth.ErrNotLoggedIn) {
Expand Down
5 changes: 3 additions & 2 deletions internal/ui/run_stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import (
"os"

tea "github.com/charmbracelet/bubbletea"
"github.com/localstack/lstk/internal/config"
"github.com/localstack/lstk/internal/container"
"github.com/localstack/lstk/internal/output"
"github.com/localstack/lstk/internal/runtime"
)

func RunStop(parentCtx context.Context, rt runtime.Runtime) error {
func RunStop(parentCtx context.Context, rt runtime.Runtime, containers []config.ContainerConfig) error {
ctx, cancel := context.WithCancel(parentCtx)
defer cancel()

Expand All @@ -20,7 +21,7 @@ func RunStop(parentCtx context.Context, rt runtime.Runtime) error {
runErrCh := make(chan error, 1)

go func() {
err := container.Stop(ctx, rt, output.NewTUISink(programSender{p: p}))
err := container.Stop(ctx, rt, output.NewTUISink(programSender{p: p}), containers)
runErrCh <- err
if err != nil && !errors.Is(err, context.Canceled) {
p.Send(runErrMsg{err: err})
Expand Down
19 changes: 19 additions & 0 deletions test/integration/logout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,22 @@ func TestLogoutCommandWithEnvVarToken(t *testing.T) {
require.NoError(t, err, "lstk logout should succeed: %s", stderr)
assert.Contains(t, stdout, "LOCALSTACK_AUTH_TOKEN")
}

func TestLogoutCommandNotesWhenEmulatorStillRunning(t *testing.T) {
requireDocker(t)
cleanup()
t.Cleanup(cleanup)
t.Cleanup(func() {
_ = DeleteAuthTokenFromKeyring()
})

ctx := testContext(t)
startTestContainer(t, ctx)

err := SetAuthTokenInKeyring("test-token")
require.NoError(t, err, "failed to store token in keyring")

stdout, stderr, err := runLstk(t, ctx, "", nil, "logout")
require.NoError(t, err, "lstk logout failed: %s", stderr)
assert.Contains(t, stdout, "LocalStack is still running in the background")
}
Loading