Skip to content

Commit adfce43

Browse files
inline telemetry lifecycle event calls
1 parent 3cc2388 commit adfce43

5 files changed

Lines changed: 74 additions & 54 deletions

File tree

internal/container/start.go

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -42,34 +42,6 @@ type StartOptions struct {
4242
Telemetry *telemetry.Client
4343
}
4444

45-
func emitEmulatorStartError(ctx context.Context, tel *telemetry.Client, c runtime.ContainerConfig, errorCode, errorMsg string) {
46-
if tel == nil {
47-
return
48-
}
49-
tel.EmitEmulatorLifecycleEvent(ctx, telemetry.LifecycleEvent{
50-
EventType: telemetry.LifecycleStartError,
51-
Emulator: c.EmulatorType,
52-
Image: c.Image,
53-
ErrorCode: errorCode,
54-
ErrorMsg: errorMsg,
55-
})
56-
}
57-
58-
func emitEmulatorStartSuccess(ctx context.Context, tel *telemetry.Client, c runtime.ContainerConfig, containerID string, durationMS int64, pulled bool, info *telemetry.LocalStackInfo) {
59-
if tel == nil {
60-
return
61-
}
62-
tel.EmitEmulatorLifecycleEvent(ctx, telemetry.LifecycleEvent{
63-
EventType: telemetry.LifecycleStartSuccess,
64-
Emulator: c.EmulatorType,
65-
Image: c.Image,
66-
ContainerID: containerID,
67-
DurationMS: durationMS,
68-
Pulled: pulled,
69-
LocalStackInfo: info,
70-
})
71-
}
72-
7345
func Start(ctx context.Context, rt runtime.Runtime, sink output.Sink, opts StartOptions, interactive bool) error {
7446
if err := rt.IsHealthy(ctx); err != nil {
7547
rt.EmitUnhealthyError(sink, err)
@@ -87,9 +59,7 @@ func Start(ctx context.Context, rt runtime.Runtime, sink output.Sink, opts Start
8759
return err
8860
}
8961

90-
if opts.Telemetry != nil {
91-
opts.Telemetry.SetAuthToken(token)
92-
}
62+
opts.Telemetry.SetAuthToken(token)
9363

9464
if hasDuplicateContainerTypes(opts.Containers) {
9565
output.EmitWarning(sink, "Multiple emulators of the same type are defined in your config; this setup is not supported yet")
@@ -275,7 +245,13 @@ func pullImages(ctx context.Context, rt runtime.Runtime, sink output.Sink, tel *
275245
Title: fmt.Sprintf("Failed to pull %s", c.Image),
276246
Summary: err.Error(),
277247
})
278-
emitEmulatorStartError(ctx, tel, c, telemetry.ErrCodeImagePullFailed, err.Error())
248+
tel.EmitEmulatorLifecycleEvent(ctx, telemetry.LifecycleEvent{
249+
EventType: telemetry.LifecycleStartError,
250+
Emulator: c.EmulatorType,
251+
Image: c.Image,
252+
ErrorCode: telemetry.ErrCodeImagePullFailed,
253+
ErrorMsg: err.Error(),
254+
})
279255
return nil, output.NewSilentError(fmt.Errorf("failed to pull image %s: %w", c.Image, err))
280256
}
281257
output.EmitSpinnerStop(sink)
@@ -337,21 +313,41 @@ func startContainers(ctx context.Context, rt runtime.Runtime, sink output.Sink,
337313
output.EmitStatus(sink, "starting", c.Name, "")
338314
containerID, err := rt.Start(ctx, c)
339315
if err != nil {
340-
emitEmulatorStartError(ctx, tel, c, telemetry.ErrCodeStartFailed, err.Error())
316+
tel.EmitEmulatorLifecycleEvent(ctx, telemetry.LifecycleEvent{
317+
EventType: telemetry.LifecycleStartError,
318+
Emulator: c.EmulatorType,
319+
Image: c.Image,
320+
ErrorCode: telemetry.ErrCodeStartFailed,
321+
ErrorMsg: err.Error(),
322+
})
341323
return fmt.Errorf("failed to start LocalStack: %w", err)
342324
}
343325

344326
output.EmitStatus(sink, "waiting", c.Name, "")
345327
healthURL := fmt.Sprintf("http://localhost:%s%s", c.Port, c.HealthPath)
346328
if err := awaitStartup(ctx, rt, sink, containerID, "LocalStack", healthURL); err != nil {
347-
emitEmulatorStartError(ctx, tel, c, telemetry.ErrCodeStartFailed, err.Error())
329+
tel.EmitEmulatorLifecycleEvent(ctx, telemetry.LifecycleEvent{
330+
EventType: telemetry.LifecycleStartError,
331+
Emulator: c.EmulatorType,
332+
Image: c.Image,
333+
ErrorCode: telemetry.ErrCodeStartFailed,
334+
ErrorMsg: err.Error(),
335+
})
348336
return err
349337
}
350338

351339
output.EmitStatus(sink, "ready", c.Name, fmt.Sprintf("containerId: %s", containerID[:12]))
352340

353341
lsInfo, _ := fetchLocalStackInfo(ctx, c.Port)
354-
emitEmulatorStartSuccess(ctx, tel, c, containerID[:12], time.Since(startTime).Milliseconds(), pulled[c.Name], lsInfo)
342+
tel.EmitEmulatorLifecycleEvent(ctx, telemetry.LifecycleEvent{
343+
EventType: telemetry.LifecycleStartSuccess,
344+
Emulator: c.EmulatorType,
345+
Image: c.Image,
346+
ContainerID: containerID[:12],
347+
DurationMS: time.Since(startTime).Milliseconds(),
348+
Pulled: pulled[c.Name],
349+
LocalStackInfo: lsInfo,
350+
})
355351
}
356352
return nil
357353
}
@@ -374,7 +370,13 @@ func selectContainersToStart(ctx context.Context, rt runtime.Runtime, sink outpu
374370
}
375371
if err := ports.CheckAvailable(c.Port); err != nil {
376372
emitPortInUseError(sink, c.Port)
377-
emitEmulatorStartError(ctx, tel, c, telemetry.ErrCodePortConflict, err.Error())
373+
tel.EmitEmulatorLifecycleEvent(ctx, telemetry.LifecycleEvent{
374+
EventType: telemetry.LifecycleStartError,
375+
Emulator: c.EmulatorType,
376+
Image: c.Image,
377+
ErrorCode: telemetry.ErrCodePortConflict,
378+
ErrorMsg: err.Error(),
379+
})
378380
return nil, output.NewSilentError(err)
379381
}
380382
filtered = append(filtered, c)
@@ -423,7 +425,13 @@ func validateLicense(ctx context.Context, sink output.Sink, opts StartOptions, t
423425
if errors.As(err, &licErr) && licErr.Detail != "" {
424426
opts.Logger.Error("license server response (HTTP %d): %s", licErr.Status, licErr.Detail)
425427
}
426-
emitEmulatorStartError(ctx, tel, containerConfig, telemetry.ErrCodeLicenseInvalid, err.Error())
428+
tel.EmitEmulatorLifecycleEvent(ctx, telemetry.LifecycleEvent{
429+
EventType: telemetry.LifecycleStartError,
430+
Emulator: containerConfig.EmulatorType,
431+
Image: containerConfig.Image,
432+
ErrorCode: telemetry.ErrCodeLicenseInvalid,
433+
ErrorMsg: err.Error(),
434+
})
427435
return fmt.Errorf("license validation failed for %s:%s: %w", containerConfig.ProductName, version, err)
428436
}
429437

internal/container/stop.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,12 @@ func Stop(ctx context.Context, rt runtime.Runtime, sink output.Sink, containers
5252
output.EmitSpinnerStop(sink)
5353
output.EmitSuccess(sink, "LocalStack stopped")
5454

55-
if opts.Telemetry != nil {
56-
opts.Telemetry.EmitEmulatorLifecycleEvent(ctx, telemetry.LifecycleEvent{
57-
EventType: telemetry.LifecycleStop,
58-
Emulator: string(c.Type),
59-
DurationMS: time.Since(stopStart).Milliseconds(),
60-
LocalStackInfo: lsInfo,
61-
})
62-
}
55+
opts.Telemetry.EmitEmulatorLifecycleEvent(ctx, telemetry.LifecycleEvent{
56+
EventType: telemetry.LifecycleStop,
57+
Emulator: string(c.Type),
58+
DurationMS: time.Since(stopStart).Milliseconds(),
59+
LocalStackInfo: lsInfo,
60+
})
6361
}
6462

6563
return nil

internal/container/telemetry_test.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,21 +85,29 @@ func TestStop_SkipsTelemetryWhenNil(t *testing.T) {
8585
require.NoError(t, err)
8686
}
8787

88-
func TestEmitEmulatorStartError_IsNoOpWhenTelNil(t *testing.T) {
89-
c := runtime.ContainerConfig{EmulatorType: "aws", Image: "localstack/localstack-pro:latest"}
88+
func TestEmitEmulatorLifecycleEvent_IsNoOpWhenNil(t *testing.T) {
89+
var tel *telemetry.Client
9090
// Must not panic.
91-
emitEmulatorStartError(context.Background(), nil, c, telemetry.ErrCodePortConflict, "port 4566 in use")
91+
tel.EmitEmulatorLifecycleEvent(context.Background(), telemetry.LifecycleEvent{
92+
EventType: telemetry.LifecycleStartError,
93+
Emulator: "aws",
94+
Image: "localstack/localstack-pro:latest",
95+
ErrorCode: telemetry.ErrCodePortConflict,
96+
ErrorMsg: "port 4566 in use",
97+
})
9298
}
9399

94-
func TestEmitEmulatorStartError_SendsLifecycleEvent(t *testing.T) {
100+
func TestEmitEmulatorLifecycleEvent_SendsStartErrorEvent(t *testing.T) {
95101
tel, ch := newCapturingTelClient(t)
96102
tel.SetAuthToken("ls-xyz")
97103

98-
c := runtime.ContainerConfig{
99-
EmulatorType: "aws",
100-
Image: "localstack/localstack-pro:latest",
101-
}
102-
emitEmulatorStartError(context.Background(), tel, c, telemetry.ErrCodePortConflict, "port 4566 already in use")
104+
tel.EmitEmulatorLifecycleEvent(context.Background(), telemetry.LifecycleEvent{
105+
EventType: telemetry.LifecycleStartError,
106+
Emulator: "aws",
107+
Image: "localstack/localstack-pro:latest",
108+
ErrorCode: telemetry.ErrCodePortConflict,
109+
ErrorMsg: "port 4566 already in use",
110+
})
103111
tel.Close()
104112

105113
var got map[string]any

internal/telemetry/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ type Client struct {
3636
// SetAuthToken stores the resolved auth token for inclusion in telemetry events.
3737
// Call this once the token is known (e.g. after keyring resolution or interactive login).
3838
func (c *Client) SetAuthToken(token string) {
39+
if c == nil {
40+
return
41+
}
3942
c.authToken = token
4043
}
4144

internal/telemetry/events.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ func (c *Client) EmitCommand(ctx context.Context, command string, flags []string
120120
// Environment field is populated automatically from the client state; any
121121
// value set by the caller is overwritten.
122122
func (c *Client) EmitEmulatorLifecycleEvent(ctx context.Context, event LifecycleEvent) {
123+
if c == nil {
124+
return
125+
}
123126
event.Environment = c.GetEnvironment()
124127
c.Emit(ctx, "lstk_lifecycle", ToMap(event))
125128
}

0 commit comments

Comments
 (0)