Skip to content

Commit b3e1e35

Browse files
Improve incompatible snapshot load error message (#264)
Co-authored-by: Anisa Oshafi <anisaoshafi@gmail.com>
1 parent 1f4a0f8 commit b3e1e35

3 files changed

Lines changed: 60 additions & 3 deletions

File tree

internal/emulator/aws/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ func (c *Client) ImportState(ctx context.Context, host string, src io.Reader, st
197197

198198
if resp.StatusCode == http.StatusUnprocessableEntity {
199199
body, _ := io.ReadAll(resp.Body)
200-
return fmt.Errorf("snapshot is incompatible with the running LocalStack version: %s", strings.TrimSpace(string(body)))
200+
return fmt.Errorf("%w: %s", snapshot.ErrIncompatibleSnapshot, strings.TrimSpace(string(body)))
201201
}
202202
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
203203
body, _ := io.ReadAll(resp.Body)
@@ -247,7 +247,7 @@ func (c *Client) LoadPodSnapshot(ctx context.Context, host, podName, authToken,
247247

248248
if resp.StatusCode == http.StatusUnprocessableEntity {
249249
body, _ := io.ReadAll(resp.Body)
250-
return nil, fmt.Errorf("snapshot is incompatible with the running LocalStack version: %s", strings.TrimSpace(string(body)))
250+
return nil, fmt.Errorf("%w: %s", snapshot.ErrIncompatibleSnapshot, strings.TrimSpace(string(body)))
251251
}
252252
if resp.StatusCode != http.StatusOK {
253253
body, _ := io.ReadAll(resp.Body)

internal/snapshot/load.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package snapshot
44

55
import (
66
"context"
7+
"errors"
78
"fmt"
89
"io"
910
"os"
@@ -20,6 +21,8 @@ const (
2021
MergeStrategyService = "service-merge"
2122
)
2223

24+
var ErrIncompatibleSnapshot = errors.New("snapshot is incompatible with the running LocalStack version")
25+
2326
func ValidateMergeStrategy(strategy string) error {
2427
switch strategy {
2528
case MergeStrategyAccountRegion, MergeStrategyOverwrite, MergeStrategyService:
@@ -86,7 +89,15 @@ func load(ctx context.Context, rt runtime.Runtime, containers []config.Container
8689
}
8790
}()
8891

89-
return do()
92+
err = do()
93+
if errors.Is(err, ErrIncompatibleSnapshot) {
94+
sink.Emit(output.ErrorEvent{
95+
Title: "Could not load snapshot",
96+
Summary: "Snapshot is incompatible with the running LocalStack version",
97+
})
98+
return output.NewSilentError(err)
99+
}
100+
return err
90101
}
91102

92103
func LoadLocal(ctx context.Context, rt runtime.Runtime, containers []config.ContainerConfig, client LocalLoadClient, host, src, strategy string, starter Starter, sink output.Sink) error {

internal/snapshot/load_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,52 @@ func TestLoadLocal_ImportError(t *testing.T) {
106106
assert.Contains(t, err.Error(), "incompatible version")
107107
}
108108

109+
func TestLoadLocal_IncompatibleSnapshot_StructuredError(t *testing.T) {
110+
t.Parallel()
111+
src := writeSnapshotFile(t, "ZIP_DATA")
112+
importErr := fmt.Errorf("%w: Pod state is incompatible with the current LocalStack version", snapshot.ErrIncompatibleSnapshot)
113+
client := mockLocalClientReturning(t, importErr)
114+
sink, getEvents := captureEvents(t)
115+
116+
err := snapshot.LoadLocal(context.Background(), healthyRunningMock(t), awsContainers, client, "", src, "", nopStarter, sink)
117+
require.Error(t, err)
118+
assert.True(t, output.IsSilent(err), "incompatible-snapshot error should be silent so it isn't double-rendered")
119+
120+
var errEvent *output.ErrorEvent
121+
for _, e := range getEvents() {
122+
if ev, ok := e.(output.ErrorEvent); ok {
123+
errEvent = &ev
124+
}
125+
}
126+
require.NotNil(t, errEvent, "a structured ErrorEvent should have been emitted")
127+
assert.Equal(t, "Could not load snapshot", errEvent.Title)
128+
assert.Equal(t, "Snapshot is incompatible with the running LocalStack version", errEvent.Summary)
129+
}
130+
131+
func TestLoadPod_IncompatibleSnapshot_StructuredError(t *testing.T) {
132+
t.Parallel()
133+
ctrl := gomock.NewController(t)
134+
loader := NewMockPodLoader(ctrl)
135+
loadErr := fmt.Errorf("%w: Pod state is incompatible with the current LocalStack version", snapshot.ErrIncompatibleSnapshot)
136+
loader.EXPECT().LoadPodSnapshot(gomock.Any(), gomock.Any(), "my-baseline", "test-token", gomock.Any()).
137+
Return(nil, loadErr)
138+
139+
sink, getEvents := captureEvents(t)
140+
err := snapshot.LoadPod(context.Background(), healthyRunningMock(t), awsContainers, loader, "", "my-baseline", "test-token", "", nopStarter, sink)
141+
require.Error(t, err)
142+
assert.True(t, output.IsSilent(err))
143+
144+
var errEvent *output.ErrorEvent
145+
for _, e := range getEvents() {
146+
if ev, ok := e.(output.ErrorEvent); ok {
147+
errEvent = &ev
148+
}
149+
}
150+
require.NotNil(t, errEvent, "a structured ErrorEvent should have been emitted")
151+
assert.Equal(t, "Could not load snapshot", errEvent.Title)
152+
assert.Equal(t, "Snapshot is incompatible with the running LocalStack version", errEvent.Summary)
153+
}
154+
109155
func TestLoadLocal_FileNotFound(t *testing.T) {
110156
t.Parallel()
111157
ctrl := gomock.NewController(t)

0 commit comments

Comments
 (0)