Skip to content

Commit 5dee0f1

Browse files
committed
Merge branch 'lio/compose-up-debugger'
2 parents acb6918 + b5ed1d4 commit 5dee0f1

File tree

3 files changed

+76
-59
lines changed

3 files changed

+76
-59
lines changed

src/cmd/cli/command/compose.go

Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package command
22

33
import (
4+
"context"
45
"errors"
56
"fmt"
67
"io"
@@ -89,7 +90,7 @@ func makeComposeUpCmd() *cobra.Command {
8990
}
9091

9192
track.Evt("Debug Prompted", P("loadErr", loadErr))
92-
return cli.InteractiveDebugForLoadError(ctx, client, project, loadErr)
93+
return cli.InteractiveDebugForClientError(ctx, client, project, loadErr)
9394
}
9495

9596
provider, err := newProvider(ctx, loader)
@@ -152,24 +153,8 @@ func makeComposeUpCmd() *cobra.Command {
152153
}
153154

154155
deploy, project, err := cli.ComposeUp(ctx, project, client, provider, upload, mode.Value())
155-
156156
if err != nil {
157-
if !nonInteractive && strings.Contains(err.Error(), "maximum number of projects") {
158-
if projectName, err2 := provider.RemoteProjectName(cmd.Context()); err2 == nil {
159-
term.Error("Error:", prettyError(err))
160-
if _, err := cli.InteractiveComposeDown(cmd.Context(), provider, projectName); err != nil {
161-
term.Debug("ComposeDown failed:", err)
162-
printDefangHint("To deactivate a project, do:", "compose down --project-name "+projectName)
163-
} else {
164-
printDefangHint("To try deployment again, do:", "compose up")
165-
}
166-
return nil
167-
}
168-
}
169-
if errors.Is(err, types.ErrComposeFileNotFound) {
170-
printDefangHint("To start a new project, do:", "new")
171-
}
172-
return err
157+
return handleComposeUpErr(ctx, err, project, provider)
173158
}
174159

175160
if len(deploy.Services) == 0 {
@@ -188,39 +173,18 @@ func makeComposeUpCmd() *cobra.Command {
188173
if deploy.Etag != "" {
189174
tailSource = "deployment ID " + deploy.Etag
190175
}
191-
192176
term.Info("Tailing logs for", tailSource, "; press Ctrl+C to detach:")
193177

194178
tailOptions := newTailOptionsForDeploy(deploy.Etag, since, verbose)
195179
serviceStates, err := cli.TailAndMonitor(ctx, project, provider, time.Duration(waitTimeout)*time.Second, tailOptions)
196180
if err != nil {
197-
var errDeploymentFailed cliClient.ErrDeploymentFailed
198-
if errors.As(err, &errDeploymentFailed) {
199-
// Tail got canceled because of deployment failure: prompt to show the debugger
200-
term.Warn(errDeploymentFailed)
201-
debugConfig := cli.DebugConfig{
202-
Deployment: deploy.Etag,
203-
ModelId: modelId,
204-
Project: project,
205-
Provider: provider,
206-
Since: since,
207-
}
208-
if errDeploymentFailed.Service != "" {
209-
debugConfig.FailedServices = []string{errDeploymentFailed.Service}
210-
}
211-
if nonInteractive {
212-
printDefangHint("To debug the deployment, do:", debugConfig.String())
213-
} else {
214-
track.Evt("Debug Prompted", P("failedServices", debugConfig.FailedServices), P("etag", deploy.Etag), P("reason", errDeploymentFailed))
215-
216-
// Call the AI debug endpoint using the original command context (not the tail ctx which is canceled)
217-
if nil != cli.InteractiveDebugDeployment(ctx, client, debugConfig) {
218-
// don't show this defang hint if debugging was successful
219-
tailOptions := newTailOptionsForDeploy(deploy.Etag, since, true)
220-
printDefangHint("To see the logs of the failed service, do:", tailOptions.String())
221-
}
222-
}
223-
}
181+
handleTailAndMonitorErr(ctx, err, client, cli.DebugConfig{
182+
Deployment: deploy.Etag,
183+
ModelId: modelId,
184+
Project: project,
185+
Provider: provider,
186+
Since: since,
187+
})
224188
return err
225189
}
226190

@@ -253,6 +217,59 @@ func makeComposeUpCmd() *cobra.Command {
253217
return composeUpCmd
254218
}
255219

220+
func handleComposeUpErr(ctx context.Context, err error, project *compose.Project, provider cliClient.Provider) error {
221+
if errors.Is(err, types.ErrComposeFileNotFound) {
222+
// TODO: generate a compose file based on the current project
223+
printDefangHint("To start a new project, do:", "new")
224+
}
225+
226+
if nonInteractive {
227+
return err
228+
}
229+
230+
if strings.Contains(err.Error(), "maximum number of projects") {
231+
if projectName, err2 := provider.RemoteProjectName(ctx); err2 == nil {
232+
term.Error("Error:", prettyError(err))
233+
if _, err := cli.InteractiveComposeDown(ctx, provider, projectName); err != nil {
234+
term.Debug("ComposeDown failed:", err)
235+
printDefangHint("To deactivate a project, do:", "compose down --project-name "+projectName)
236+
} else {
237+
// TODO: actually do the "compose up" (because that's what the user intended in the first place)
238+
printDefangHint("To try deployment again, do:", "compose up")
239+
}
240+
return nil
241+
}
242+
return err
243+
}
244+
245+
term.Error("Error:", prettyError(err))
246+
track.Evt("Debug Prompted", P("composeErr", err))
247+
return cli.InteractiveDebugForClientError(ctx, client, project, err)
248+
}
249+
250+
func handleTailAndMonitorErr(ctx context.Context, err error, client *cliClient.GrpcClient, debugConfig cli.DebugConfig) {
251+
var errDeploymentFailed cliClient.ErrDeploymentFailed
252+
if errors.As(err, &errDeploymentFailed) {
253+
// Tail got canceled because of deployment failure: prompt to show the debugger
254+
term.Warn(errDeploymentFailed)
255+
if errDeploymentFailed.Service != "" {
256+
debugConfig.FailedServices = []string{errDeploymentFailed.Service}
257+
}
258+
if nonInteractive {
259+
printDefangHint("To debug the deployment, do:", debugConfig.String())
260+
} else {
261+
track.Evt("Debug Prompted", P("failedServices", debugConfig.FailedServices), P("etag", debugConfig.Deployment), P("reason", errDeploymentFailed))
262+
263+
// Call the AI debug endpoint using the original command context (not the tail ctx which is canceled)
264+
if nil != cli.InteractiveDebugDeployment(ctx, client, debugConfig) {
265+
// don't show this defang hint if debugging was successful
266+
tailOptions := newTailOptionsForDeploy(debugConfig.Deployment, debugConfig.Since, true)
267+
printDefangHint("To see the logs of the failed service, do:", tailOptions.String())
268+
}
269+
}
270+
}
271+
}
272+
256273
func newTailOptionsForDeploy(deployment string, since time.Time, verbose bool) cli.TailOptions {
257274
return cli.TailOptions{
258275
Deployment: deployment,
@@ -438,7 +455,7 @@ func makeComposeConfigCmd() *cobra.Command {
438455
}
439456

440457
track.Evt("Debug Prompted", P("loadErr", loadErr))
441-
return cli.InteractiveDebugForLoadError(ctx, client, project, loadErr)
458+
return cli.InteractiveDebugForClientError(ctx, client, project, loadErr)
442459
}
443460

444461
provider, err := newProvider(ctx, loader)

src/pkg/cli/client/byoc/baseclient.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ func topologicalSort(nodes map[string]*Node) []*defangv1.ServiceInfo {
207207
// This function was based on update function from Fabric controller and slightly modified to work with BYOC
208208
func (b *ByocBaseClient) update(ctx context.Context, projectName, delegateDomain string, service composeTypes.ServiceConfig) (*defangv1.ServiceInfo, error) {
209209
if err := compose.ValidateService(&service); err != nil {
210-
return nil, fmt.Errorf("service %q: %w", service.Name, err)
210+
return nil, err // caller prepends service name
211211
}
212212

213213
pkg.Ensure(projectName != "", "ProjectName not set")
@@ -257,7 +257,7 @@ func (b *ByocBaseClient) update(ctx context.Context, projectName, delegateDomain
257257

258258
if siUpdater, ok := b.projectBackend.(ServiceInfoUpdater); ok {
259259
if err := siUpdater.UpdateServiceInfo(ctx, si, projectName, delegateDomain, service); err != nil {
260-
return nil, fmt.Errorf("service %q: %w", service.Name, err)
260+
return nil, err // caller prepends service name
261261
}
262262
}
263263

src/pkg/cli/debug.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,27 +72,27 @@ func InteractiveDebugDeployment(ctx context.Context, client client.FabricClient,
7272
return interactiveDebug(ctx, client, debugConfig, nil)
7373
}
7474

75-
func InteractiveDebugForLoadError(ctx context.Context, client client.FabricClient, project *compose.Project, loadErr error) error {
76-
return interactiveDebug(ctx, client, DebugConfig{Project: project}, loadErr)
75+
func InteractiveDebugForClientError(ctx context.Context, client client.FabricClient, project *compose.Project, clientErr error) error {
76+
return interactiveDebug(ctx, client, DebugConfig{Project: project}, clientErr)
7777
}
7878

79-
func interactiveDebug(ctx context.Context, client client.FabricClient, debugConfig DebugConfig, loadError error) error {
79+
func interactiveDebug(ctx context.Context, client client.FabricClient, debugConfig DebugConfig, clientErr error) error {
8080
var aiDebug bool
8181
if err := survey.AskOne(&survey.Confirm{
8282
Message: "Would you like to debug the deployment with AI?",
8383
Help: "This will send logs and artifacts to our backend and attempt to diagnose the issue and provide a solution.",
8484
}, &aiDebug, survey.WithStdio(term.DefaultTerm.Stdio())); err != nil {
85-
track.Evt("Debug Prompt Failed", P("etag", debugConfig.Deployment), P("reason", err), P("loadErr", loadError))
85+
track.Evt("Debug Prompt Failed", P("etag", debugConfig.Deployment), P("reason", err), P("loadErr", clientErr))
8686
return err
8787
} else if !aiDebug {
88-
track.Evt("Debug Prompt Skipped", P("etag", debugConfig.Deployment), P("loadErr", loadError))
88+
track.Evt("Debug Prompt Skipped", P("etag", debugConfig.Deployment), P("loadErr", clientErr))
8989
return ErrDebugSkipped
9090
}
9191

92-
track.Evt("Debug Prompt Accepted", P("etag", debugConfig.Deployment), P("loadErr", loadError))
92+
track.Evt("Debug Prompt Accepted", P("etag", debugConfig.Deployment), P("loadErr", clientErr))
9393

94-
if loadError != nil {
95-
if err := debugComposeFileLoadError(ctx, client, debugConfig.Project, loadError); err != nil {
94+
if clientErr != nil {
95+
if err := debugComposeFileLoadError(ctx, client, debugConfig.Project, clientErr); err != nil {
9696
term.Warnf("Failed to debug compose file load: %v", err)
9797
return err
9898
}
@@ -110,9 +110,9 @@ func interactiveDebug(ctx context.Context, client client.FabricClient, debugConf
110110
Message: "Was the debugging helpful?",
111111
Help: "Please provide feedback to help us improve the debugging experience.",
112112
}, &goodBad); err != nil {
113-
track.Evt("Debug Feedback Prompt Failed", P("etag", debugConfig.Deployment), P("reason", err), P("loadErr", loadError))
113+
track.Evt("Debug Feedback Prompt Failed", P("etag", debugConfig.Deployment), P("reason", err), P("loadErr", clientErr))
114114
} else {
115-
track.Evt("Debug Feedback Prompt Answered", P("etag", debugConfig.Deployment), P("feedback", goodBad), P("loadErr", loadError))
115+
track.Evt("Debug Feedback Prompt Answered", P("etag", debugConfig.Deployment), P("feedback", goodBad), P("loadErr", clientErr))
116116
}
117117
return nil
118118
}

0 commit comments

Comments
 (0)