Skip to content

Commit c40a32d

Browse files
make events more generic
1 parent e88bce7 commit c40a32d

File tree

12 files changed

+201
-244
lines changed

12 files changed

+201
-244
lines changed

internal/container/status.go

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package container
33
import (
44
"context"
55
"fmt"
6+
"net/http"
67
"time"
78

89
"github.com/localstack/lstk/internal/config"
@@ -18,13 +19,17 @@ func Status(ctx context.Context, rt runtime.Runtime, containers []config.Contain
1819
ctx, cancel := context.WithTimeout(ctx, statusTimeout)
1920
defer cancel()
2021

22+
output.EmitSpinnerStart(sink, "Fetching LocalStack status")
23+
2124
for _, c := range containers {
2225
name := c.Name()
2326
running, err := rt.IsRunning(ctx, name)
2427
if err != nil {
28+
output.EmitSpinnerStop(sink)
2529
return fmt.Errorf("checking %s running: %w", name, err)
2630
}
2731
if !running {
32+
output.EmitSpinnerStop(sink)
2833
output.EmitError(sink, output.ErrorEvent{
2934
Title: fmt.Sprintf("%s is not running", c.DisplayName()),
3035
Actions: []output.ErrorAction{
@@ -43,49 +48,52 @@ func Status(ctx context.Context, rt runtime.Runtime, containers []config.Contain
4348
}
4449

4550
var version string
51+
var rows []aws.Resource
4652
switch c.Type {
4753
case config.EmulatorAWS:
48-
emulatorClient := aws.NewClient(nil)
54+
emulatorClient := aws.NewClient(&http.Client{})
4955
if v, err := emulatorClient.FetchVersion(ctx, host); err != nil {
56+
output.EmitSpinnerStop(sink)
5057
output.EmitWarning(sink, fmt.Sprintf("Could not fetch version: %v", err))
5158
} else {
5259
version = v
5360
}
5461

55-
output.Emit(sink, output.InstanceInfoEvent{
56-
EmulatorName: c.DisplayName(),
57-
Version: version,
58-
Host: host,
59-
ContainerName: name,
60-
Uptime: uptime,
61-
})
62-
63-
rows, err := emulatorClient.FetchResources(ctx, host)
64-
if err != nil {
65-
return err
62+
var fetchErr error
63+
rows, fetchErr = emulatorClient.FetchResources(ctx, host)
64+
if fetchErr != nil {
65+
output.EmitSpinnerStop(sink)
66+
return fetchErr
6667
}
68+
}
6769

70+
output.EmitSpinnerStop(sink)
71+
72+
output.Emit(sink, output.InstanceInfoEvent{
73+
EmulatorName: c.DisplayName(),
74+
Version: version,
75+
Host: host,
76+
ContainerName: name,
77+
Uptime: uptime,
78+
})
79+
80+
if c.Type == config.EmulatorAWS {
6881
if len(rows) == 0 {
6982
output.EmitNote(sink, "No resources deployed")
7083
continue
7184
}
7285

86+
tableRows := make([][]string, len(rows))
7387
services := map[string]struct{}{}
74-
for _, r := range rows {
88+
for i, r := range rows {
89+
tableRows[i] = []string{r.Service, r.Name, r.Region, r.Account}
7590
services[r.Service] = struct{}{}
7691
}
77-
output.Emit(sink, output.ResourceSummaryEvent{
78-
ResourceCount: len(rows),
79-
ServiceCount: len(services),
80-
})
81-
output.Emit(sink, output.ResourceTableEvent{Rows: rows})
82-
default:
83-
output.Emit(sink, output.InstanceInfoEvent{
84-
EmulatorName: c.DisplayName(),
85-
Version: version,
86-
Host: host,
87-
ContainerName: name,
88-
Uptime: uptime,
92+
93+
output.EmitInfo(sink, fmt.Sprintf("~ %d resources · %d services", len(rows), len(services)))
94+
output.Emit(sink, output.TableEvent{
95+
Headers: []string{"SERVICE", "RESOURCE", "REGION", "ACCOUNT"},
96+
Rows: tableRows,
8997
})
9098
}
9199
}

internal/container/status_test.go

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import (
44
"context"
55
"fmt"
66
"io"
7-
"net/http"
8-
"net/http/httptest"
9-
"strings"
107
"testing"
11-
"time"
128

139
"github.com/localstack/lstk/internal/config"
1410
"github.com/localstack/lstk/internal/output"
@@ -18,21 +14,6 @@ import (
1814
"go.uber.org/mock/gomock"
1915
)
2016

21-
func TestStatus_NotRunning(t *testing.T) {
22-
ctrl := gomock.NewController(t)
23-
mockRT := runtime.NewMockRuntime(ctrl)
24-
mockRT.EXPECT().IsRunning(gomock.Any(), "localstack-aws").Return(false, nil)
25-
26-
containers := []config.ContainerConfig{{Type: config.EmulatorAWS}}
27-
sink := output.NewPlainSink(io.Discard)
28-
29-
err := Status(context.Background(), mockRT, containers, "", sink)
30-
31-
require.Error(t, err)
32-
assert.True(t, output.IsSilent(err))
33-
assert.Contains(t, err.Error(), "is not running")
34-
}
35-
3617
func TestStatus_IsRunningError(t *testing.T) {
3718
ctrl := gomock.NewController(t)
3819
mockRT := runtime.NewMockRuntime(ctrl)
@@ -47,76 +28,6 @@ func TestStatus_IsRunningError(t *testing.T) {
4728
assert.Contains(t, err.Error(), "docker unavailable")
4829
}
4930

50-
func TestStatus_RunningWithResources(t *testing.T) {
51-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
52-
switch r.URL.Path {
53-
case "/_localstack/health":
54-
w.Header().Set("Content-Type", "application/json")
55-
_, _ = fmt.Fprintln(w, `{"version": "4.14.1"}`)
56-
case "/_localstack/resources":
57-
w.Header().Set("Content-Type", "application/x-ndjson")
58-
_, _ = fmt.Fprintln(w, `{"AWS::S3::Bucket": [{"region_name": "us-east-1", "account_id": "000000000000", "id": "my-bucket"}]}`)
59-
_, _ = fmt.Fprintln(w, `{"AWS::Lambda::Function": [{"region_name": "us-east-1", "account_id": "000000000000", "id": "my-func"}]}`)
60-
}
61-
}))
62-
defer server.Close()
63-
64-
host := strings.TrimPrefix(server.URL, "http://")
65-
66-
ctrl := gomock.NewController(t)
67-
mockRT := runtime.NewMockRuntime(ctrl)
68-
mockRT.EXPECT().IsRunning(gomock.Any(), "localstack-aws").Return(true, nil)
69-
mockRT.EXPECT().ContainerStartedAt(gomock.Any(), "localstack-aws").Return(time.Now().Add(-5*time.Minute), nil)
70-
71-
containers := []config.ContainerConfig{{Type: config.EmulatorAWS}}
72-
var buf strings.Builder
73-
sink := output.NewPlainSink(&buf)
74-
75-
err := Status(context.Background(), mockRT, containers, host, sink)
76-
77-
require.NoError(t, err)
78-
out := buf.String()
79-
assert.Contains(t, out, "is running")
80-
assert.Contains(t, out, "4.14.1")
81-
assert.Contains(t, out, "S3")
82-
assert.Contains(t, out, "my-bucket")
83-
assert.Contains(t, out, "Lambda")
84-
assert.Contains(t, out, "my-func")
85-
assert.Contains(t, out, "2 resources")
86-
assert.Contains(t, out, "2 services")
87-
}
88-
89-
func TestStatus_RunningNoResources(t *testing.T) {
90-
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
91-
switch r.URL.Path {
92-
case "/_localstack/health":
93-
w.Header().Set("Content-Type", "application/json")
94-
_, _ = fmt.Fprintln(w, `{"version": "4.14.1"}`)
95-
case "/_localstack/resources":
96-
w.Header().Set("Content-Type", "application/x-ndjson")
97-
}
98-
}))
99-
defer server.Close()
100-
101-
host := strings.TrimPrefix(server.URL, "http://")
102-
103-
ctrl := gomock.NewController(t)
104-
mockRT := runtime.NewMockRuntime(ctrl)
105-
mockRT.EXPECT().IsRunning(gomock.Any(), "localstack-aws").Return(true, nil)
106-
mockRT.EXPECT().ContainerStartedAt(gomock.Any(), "localstack-aws").Return(time.Time{}, fmt.Errorf("not found"))
107-
108-
containers := []config.ContainerConfig{{Type: config.EmulatorAWS}}
109-
var buf strings.Builder
110-
sink := output.NewPlainSink(&buf)
111-
112-
err := Status(context.Background(), mockRT, containers, host, sink)
113-
114-
require.NoError(t, err)
115-
out := buf.String()
116-
assert.Contains(t, out, "is running")
117-
assert.Contains(t, out, "No resources deployed")
118-
}
119-
12031
func TestStatus_MultipleContainers_StopsAtFirstNotRunning(t *testing.T) {
12132
ctrl := gomock.NewController(t)
12233
mockRT := runtime.NewMockRuntime(ctrl)

internal/emulator/aws/aws.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,21 @@ package aws
33
import (
44
"context"
55
"net/http"
6-
7-
"github.com/localstack/lstk/internal/output"
86
)
97

8+
type Resource struct {
9+
Service string
10+
Name string
11+
Region string
12+
Account string
13+
}
14+
1015
// Client defines the interface for communicating with a running AWS emulator instance.
1116
type Client interface {
1217
FetchVersion(ctx context.Context, host string) (string, error)
13-
FetchResources(ctx context.Context, host string) ([]output.ResourceRow, error)
18+
FetchResources(ctx context.Context, host string) ([]Resource, error)
1419
}
1520

1621
func NewClient(httpClient *http.Client) Client {
17-
if httpClient == nil {
18-
httpClient = &http.Client{}
19-
}
2022
return &client{http: httpClient}
2123
}

internal/emulator/aws/client.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,8 @@ import (
88
"net/http"
99
"sort"
1010
"strings"
11-
12-
"github.com/localstack/lstk/internal/output"
1311
)
1412

15-
// Ensure client implements Client at compile time.
16-
var _ Client = (*client)(nil)
17-
1813
type client struct {
1914
http *http.Client
2015
}
@@ -53,7 +48,7 @@ func (c *client) FetchVersion(ctx context.Context, host string) (string, error)
5348
return h.Version, nil
5449
}
5550

56-
func (c *client) FetchResources(ctx context.Context, host string) ([]output.ResourceRow, error) {
51+
func (c *client) FetchResources(ctx context.Context, host string) ([]Resource, error) {
5752
url := fmt.Sprintf("http://%s/_localstack/resources", host)
5853
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
5954
if err != nil {
@@ -72,7 +67,7 @@ func (c *client) FetchResources(ctx context.Context, host string) ([]output.Reso
7267

7368
// Each line of the NDJSON stream is a JSON object mapping an AWS resource type
7469
// (e.g. "AWS::S3::Bucket") to a list of resource entries.
75-
var rows []output.ResourceRow
70+
var rows []Resource
7671
scanner := bufio.NewScanner(resp.Body)
7772
buf := make([]byte, 1024*1024)
7873
scanner.Buffer(buf, 1024*1024)
@@ -96,11 +91,11 @@ func (c *client) FetchResources(ctx context.Context, host string) ([]output.Reso
9691
}
9792

9893
for _, e := range entries {
99-
rows = append(rows, output.ResourceRow{
100-
Service: service,
101-
Resource: extractResourceName(e.ID),
102-
Region: e.RegionName,
103-
Account: e.AccountID,
94+
rows = append(rows, Resource{
95+
Service: service,
96+
Name: extractResourceName(e.ID),
97+
Region: e.RegionName,
98+
Account: e.AccountID,
10499
})
105100
}
106101
}
@@ -114,7 +109,7 @@ func (c *client) FetchResources(ctx context.Context, host string) ([]output.Reso
114109
if rows[i].Service != rows[j].Service {
115110
return rows[i].Service < rows[j].Service
116111
}
117-
return rows[i].Resource < rows[j].Resource
112+
return rows[i].Name < rows[j].Name
118113
})
119114

120115
return rows, nil

internal/emulator/aws/client_test.go

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestFetchVersion(t *testing.T) {
2323
}))
2424
defer server.Close()
2525

26-
c := NewClient(nil)
26+
c := NewClient(&http.Client{})
2727
version, err := c.FetchVersion(context.Background(), server.Listener.Addr().String())
2828
require.NoError(t, err)
2929
assert.Equal(t, "4.14.1", version)
@@ -36,7 +36,7 @@ func TestFetchVersion(t *testing.T) {
3636
}))
3737
defer server.Close()
3838

39-
c := NewClient(nil)
39+
c := NewClient(&http.Client{})
4040
_, err := c.FetchVersion(context.Background(), server.Listener.Addr().String())
4141
require.Error(t, err)
4242
})
@@ -54,16 +54,16 @@ func TestFetchResources(t *testing.T) {
5454
}))
5555
defer server.Close()
5656

57-
c := NewClient(nil)
57+
c := NewClient(&http.Client{})
5858
rows, err := c.FetchResources(context.Background(), server.Listener.Addr().String())
5959
require.NoError(t, err)
6060
require.Len(t, rows, 2)
6161
assert.Equal(t, "Lambda", rows[0].Service)
62-
assert.Equal(t, "my-function", rows[0].Resource)
62+
assert.Equal(t, "my-function", rows[0].Name)
6363
assert.Equal(t, "us-east-1", rows[0].Region)
6464
assert.Equal(t, "000000000000", rows[0].Account)
6565
assert.Equal(t, "S3", rows[1].Service)
66-
assert.Equal(t, "my-bucket", rows[1].Resource)
66+
assert.Equal(t, "my-bucket", rows[1].Name)
6767
})
6868

6969
t.Run("extracts name from ARN", func(t *testing.T) {
@@ -74,11 +74,11 @@ func TestFetchResources(t *testing.T) {
7474
}))
7575
defer server.Close()
7676

77-
c := NewClient(nil)
77+
c := NewClient(&http.Client{})
7878
rows, err := c.FetchResources(context.Background(), server.Listener.Addr().String())
7979
require.NoError(t, err)
8080
require.Len(t, rows, 1)
81-
assert.Equal(t, "my-topic", rows[0].Resource)
81+
assert.Equal(t, "my-topic", rows[0].Name)
8282
})
8383

8484
t.Run("returns empty slice when no resources", func(t *testing.T) {
@@ -88,7 +88,7 @@ func TestFetchResources(t *testing.T) {
8888
}))
8989
defer server.Close()
9090

91-
c := NewClient(nil)
91+
c := NewClient(&http.Client{})
9292
rows, err := c.FetchResources(context.Background(), server.Listener.Addr().String())
9393
require.NoError(t, err)
9494
assert.Empty(t, rows)
@@ -101,24 +101,9 @@ func TestFetchResources(t *testing.T) {
101101
}))
102102
defer server.Close()
103103

104-
c := NewClient(nil)
104+
c := NewClient(&http.Client{})
105105
_, err := c.FetchResources(context.Background(), server.Listener.Addr().String())
106106
require.Error(t, err)
107107
})
108108
}
109109

110-
func TestExtractResourceName(t *testing.T) {
111-
t.Parallel()
112-
tests := []struct {
113-
input string
114-
want string
115-
}{
116-
{"my-bucket", "my-bucket"},
117-
{"arn:aws:sns:us-east-1:000000000000:my-topic", "my-topic"},
118-
{"arn:aws:iam::000000000000:role/my-role", "my-role"},
119-
{"arn:aws:lambda:us-east-1:000000000000:function:my-func", "my-func"},
120-
}
121-
for _, tt := range tests {
122-
assert.Equal(t, tt.want, extractResourceName(tt.input), "extractResourceName(%q)", tt.input)
123-
}
124-
}

0 commit comments

Comments
 (0)