Skip to content

Commit baa8cdf

Browse files
committed
dap: add debug adapter implementation
Adds a simple implementation of the debug adapter that supports the very basics of a debug adapter. It supports the launch request, the configuration done request, the creation of threads, stopping, resuming, and disconnecting from server. It does not support custom breakpoints, stack traces, or variable inspection yet. These are planned to be added in the future. It also does not support sending output through the debug adapter yet. Signed-off-by: Jonathan A. Sternberg <[email protected]>
1 parent 43e2f27 commit baa8cdf

File tree

25 files changed

+3777
-196
lines changed

25 files changed

+3777
-196
lines changed

build/build.go

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -313,9 +313,12 @@ func toRepoOnly(in string) (string, error) {
313313
return strings.Join(out, ","), nil
314314
}
315315

316-
type Handler struct {
317-
Evaluate func(ctx context.Context, c gateway.Client, res *gateway.Result) error
318-
}
316+
type (
317+
EvaluateFunc func(ctx context.Context, name string, c gateway.Client, res *gateway.Result) error
318+
Handler struct {
319+
Evaluate EvaluateFunc
320+
}
321+
)
319322

320323
func Build(ctx context.Context, nodes []builder.Node, opts map[string]Options, docker *dockerutil.Client, cfg *confutil.Config, w progress.Writer) (resp map[string]*client.SolveResponse, err error) {
321324
return BuildWithResultHandler(ctx, nodes, opts, docker, cfg, w, nil)
@@ -510,12 +513,25 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
510513
rKey := resultKey(dp.driverIndex, k)
511514
results.Set(rKey, res)
512515

516+
forceEval := false
513517
if children := childTargets[rKey]; len(children) > 0 {
514-
if err := waitForChildren(ctx, bh, c, res, results, children); err != nil {
518+
// wait for the child targets to register their LLB before evaluating
519+
_, err := results.Get(ctx, children...)
520+
if err != nil {
515521
return nil, err
516522
}
517-
} else if bh != nil && bh.Evaluate != nil {
518-
if err := bh.Evaluate(ctx, c, res); err != nil {
523+
forceEval = true
524+
}
525+
526+
// invoke custom evaluate handler if it is present
527+
if bh != nil && bh.Evaluate != nil {
528+
if err := bh.Evaluate(ctx, k, c, res); err != nil {
529+
return nil, err
530+
}
531+
} else if forceEval {
532+
if err := res.EachRef(func(ref gateway.Reference) error {
533+
return ref.Evaluate(ctx)
534+
}); err != nil {
519535
return nil, err
520536
}
521537
}
@@ -1199,29 +1215,6 @@ func solve(ctx context.Context, c gateway.Client, req gateway.SolveRequest) (*ga
11991215
return res, nil
12001216
}
12011217

1202-
func waitForChildren(ctx context.Context, bh *Handler, c gateway.Client, res *gateway.Result, results *waitmap.Map, children []string) error {
1203-
// wait for the child targets to register their LLB before evaluating
1204-
_, err := results.Get(ctx, children...)
1205-
if err != nil {
1206-
return err
1207-
}
1208-
// we need to wait until the child targets have completed before we can release
1209-
eg, ctx := errgroup.WithContext(ctx)
1210-
eg.Go(func() error {
1211-
if bh != nil && bh.Evaluate != nil {
1212-
return bh.Evaluate(ctx, c, res)
1213-
}
1214-
return res.EachRef(func(ref gateway.Reference) error {
1215-
return ref.Evaluate(ctx)
1216-
})
1217-
})
1218-
eg.Go(func() error {
1219-
_, err := results.Get(ctx, children...)
1220-
return err
1221-
})
1222-
return eg.Wait()
1223-
}
1224-
12251218
func catchFrontendError(retErr, frontendErr *error) {
12261219
*frontendErr = *retErr
12271220
if errors.Is(*retErr, ErrRestart) {

build/invoke.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ import (
1414
)
1515

1616
type InvokeConfig struct {
17-
Entrypoint []string
18-
Cmd []string
19-
NoCmd bool
20-
Env []string
21-
User string
22-
NoUser bool
23-
Cwd string
24-
NoCwd bool
25-
Tty bool
26-
Rollback bool
27-
Initial bool
28-
SuspendOn SuspendOn
17+
Entrypoint []string `json:"entrypoint,omitempty"`
18+
Cmd []string `json:"cmd,omitempty"`
19+
NoCmd bool `json:"noCmd,omitempty"`
20+
Env []string `json:"env,omitempty"`
21+
User string `json:"user,omitempty"`
22+
NoUser bool `json:"noUser,omitempty"`
23+
Cwd string `json:"cwd,omitempty"`
24+
NoCwd bool `json:"noCwd,omitempty"`
25+
Tty bool `json:"tty,omitempty"`
26+
Rollback bool `json:"rollback,omitempty"`
27+
Initial bool `json:"initial,omitempty"`
28+
SuspendOn SuspendOn `json:"suspendOn,omitempty"`
2929
}
3030

3131
func (cfg *InvokeConfig) NeedsDebug(err error) bool {
@@ -43,6 +43,18 @@ func (s SuspendOn) DebugEnabled(err error) bool {
4343
return err != nil || s == SuspendAlways
4444
}
4545

46+
func (s *SuspendOn) UnmarshalText(text []byte) error {
47+
switch string(text) {
48+
case "error":
49+
*s = SuspendError
50+
case "always":
51+
*s = SuspendAlways
52+
default:
53+
return errors.Errorf("unknown suspend name: %s", string(text))
54+
}
55+
return nil
56+
}
57+
4658
type Container struct {
4759
cancelOnce sync.Once
4860
containerCancel func(error)

build/result.go

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,18 @@ import (
1616
"github.com/sirupsen/logrus"
1717
)
1818

19-
// NewResultHandle stores a gateway client, gateway result, and the error from
19+
// NewResultHandle stores a gateway client, gateway reference, and the error from
2020
// an evaluate call if it is present.
2121
//
2222
// This ResultHandle can be used to execute additional build steps in the same
2323
// context as the build occurred, which can allow easy debugging of build
2424
// failures and successes.
2525
//
2626
// If the returned ResultHandle is not nil, the caller must call Done() on it.
27-
func NewResultHandle(ctx context.Context, c gateway.Client, res *gateway.Result, err error) *ResultHandle {
27+
func NewResultHandle(ctx context.Context, c gateway.Client, ref gateway.Reference, meta map[string][]byte, err error) *ResultHandle {
2828
rCtx := &ResultHandle{
29-
res: res,
29+
ref: ref,
30+
meta: meta,
3031
gwClient: c,
3132
}
3233
if err != nil && !errors.As(err, &rCtx.solveErr) {
@@ -37,8 +38,9 @@ func NewResultHandle(ctx context.Context, c gateway.Client, res *gateway.Result,
3738

3839
// ResultHandle is a build result with the client that built it.
3940
type ResultHandle struct {
40-
res *gateway.Result
41+
ref gateway.Reference
4142
solveErr *errdefs.SolveError
43+
meta map[string][]byte
4244
gwClient gateway.Client
4345

4446
doneOnce sync.Once
@@ -74,9 +76,9 @@ func (r *ResultHandle) NewContainer(ctx context.Context, cfg *InvokeConfig) (gat
7476
}
7577

7678
func (r *ResultHandle) getContainerConfig(cfg *InvokeConfig) (containerCfg gateway.NewContainerRequest, _ error) {
77-
if r.res != nil && r.solveErr == nil {
79+
if r.ref != nil && r.solveErr == nil {
7880
logrus.Debugf("creating container from successful build")
79-
ccfg, err := containerConfigFromResult(r.res, cfg)
81+
ccfg, err := containerConfigFromResult(r.ref, cfg)
8082
if err != nil {
8183
return containerCfg, err
8284
}
@@ -94,9 +96,9 @@ func (r *ResultHandle) getContainerConfig(cfg *InvokeConfig) (containerCfg gatew
9496

9597
func (r *ResultHandle) getProcessConfig(cfg *InvokeConfig, stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) (_ gateway.StartRequest, err error) {
9698
processCfg := newStartRequest(stdin, stdout, stderr)
97-
if r.res != nil && r.solveErr == nil {
99+
if r.ref != nil && r.solveErr == nil {
98100
logrus.Debugf("creating container from successful build")
99-
if err := populateProcessConfigFromResult(&processCfg, r.res, cfg); err != nil {
101+
if err := populateProcessConfigFromResult(&processCfg, r.meta, cfg); err != nil {
100102
return processCfg, err
101103
}
102104
} else {
@@ -108,20 +110,11 @@ func (r *ResultHandle) getProcessConfig(cfg *InvokeConfig, stdin io.ReadCloser,
108110
return processCfg, nil
109111
}
110112

111-
func containerConfigFromResult(res *gateway.Result, cfg *InvokeConfig) (*gateway.NewContainerRequest, error) {
113+
func containerConfigFromResult(ref gateway.Reference, cfg *InvokeConfig) (*gateway.NewContainerRequest, error) {
112114
if cfg.Initial {
113115
return nil, errors.Errorf("starting from the container from the initial state of the step is supported only on the failed steps")
114116
}
115117

116-
ps, err := exptypes.ParsePlatforms(res.Metadata)
117-
if err != nil {
118-
return nil, err
119-
}
120-
ref, ok := res.FindRef(ps.Platforms[0].ID)
121-
if !ok {
122-
return nil, errors.Errorf("no reference found")
123-
}
124-
125118
return &gateway.NewContainerRequest{
126119
Mounts: []gateway.Mount{
127120
{
@@ -133,8 +126,8 @@ func containerConfigFromResult(res *gateway.Result, cfg *InvokeConfig) (*gateway
133126
}, nil
134127
}
135128

136-
func populateProcessConfigFromResult(req *gateway.StartRequest, res *gateway.Result, cfg *InvokeConfig) error {
137-
imgData := res.Metadata[exptypes.ExporterImageConfigKey]
129+
func populateProcessConfigFromResult(req *gateway.StartRequest, meta map[string][]byte, cfg *InvokeConfig) error {
130+
imgData := meta[exptypes.ExporterImageConfigKey]
138131
var img *ocispecs.Image
139132
if len(imgData) > 0 {
140133
img = &ocispecs.Image{}

0 commit comments

Comments
 (0)