Skip to content

Commit 04e7305

Browse files
committed
dap: fix the check to determine whether exec will succeed
This refines the check for determining whether exec will succeed to work when an error occurs. This check previously relied on the `Ref` being populated in the result context but this would only happen if we were paused from a breakpoint or by stepping. An error would not fill in this field. The check is now refined to use the new gateway filesystem exec API so we can create the container and then check even if we don't have a returned gateway reference. The logic to determine which mount to check has also been moved. Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
1 parent d315c08 commit 04e7305

File tree

2 files changed

+47
-36
lines changed

2 files changed

+47
-36
lines changed

build/invoke.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package build
33
import (
44
"context"
55
_ "crypto/sha256" // ensure digests can be computed
6+
"fmt"
67
"io"
8+
"io/fs"
79
"sync"
810
"sync/atomic"
911
"syscall"
@@ -146,6 +148,43 @@ func (c *Container) Exec(ctx context.Context, cfg *InvokeConfig, stdin io.ReadCl
146148
return err
147149
}
148150

151+
func (c *Container) CanInvoke(ctx context.Context, cfg *InvokeConfig) (string, bool) {
152+
var cmd string
153+
if len(cfg.Entrypoint) > 0 {
154+
cmd = cfg.Entrypoint[0]
155+
} else if len(cfg.Cmd) > 0 {
156+
cmd = cfg.Cmd[0]
157+
}
158+
159+
if cmd == "" {
160+
return "no command specified", false
161+
}
162+
163+
cmd, index, err := c.resultCtx.inferMountIndex(cmd, cfg)
164+
if err != nil {
165+
return err.Error(), false
166+
}
167+
168+
st, err := c.container.StatFile(ctx, gateway.StatContainerRequest{
169+
StatRequest: gateway.StatRequest{
170+
Path: cmd,
171+
},
172+
MountIndex: index,
173+
})
174+
if err != nil {
175+
return fmt.Sprintf("stat error %s: %s", cmd, err), false
176+
}
177+
178+
mode := fs.FileMode(st.Mode)
179+
if !mode.IsRegular() {
180+
return fmt.Sprintf("%s: not a file", cmd), false
181+
}
182+
if mode&0o111 == 0 {
183+
return fmt.Sprintf("%s: not an executable", cmd), false
184+
}
185+
return "", true
186+
}
187+
149188
func (c *Container) ReadFile(ctx context.Context, req gateway.ReadContainerRequest) ([]byte, error) {
150189
return c.container.ReadFile(ctx, req)
151190
}

dap/debug_shell.go

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"fmt"
66
"io"
7-
"io/fs"
87
"net"
98
"os"
109
"path/filepath"
@@ -192,14 +191,6 @@ func (s *shell) attach(ctx context.Context, f dap.StackFrame, rCtx *build.Result
192191
}
193192
}()
194193

195-
// Check if the entrypoint is executable. If it isn't, don't bother
196-
// trying to invoke.
197-
if reason, ok := s.canInvoke(ctx, rCtx, cfg); !ok {
198-
writeLineF(in.Stdout, "Build container is not executable. (reason: %s)", reason)
199-
<-ctx.Done()
200-
return context.Cause(ctx)
201-
}
202-
203194
if err := s.sem.Acquire(ctx, 1); err != nil {
204195
return err
205196
}
@@ -211,6 +202,14 @@ func (s *shell) attach(ctx context.Context, f dap.StackFrame, rCtx *build.Result
211202
}
212203
defer ctr.Cancel()
213204

205+
// Check if the entrypoint is executable. If it isn't, don't bother
206+
// trying to invoke.
207+
if reason, ok := ctr.CanInvoke(ctx, cfg); !ok {
208+
writeLineF(in.Stdout, "Build container is not executable. (reason: %s)", reason)
209+
<-ctx.Done()
210+
return context.Cause(ctx)
211+
}
212+
214213
writeLineF(in.Stdout, "Running %s in build container from line %d.",
215214
strings.Join(append(cfg.Entrypoint, cfg.Cmd...), " "),
216215
f.Line,
@@ -231,33 +230,6 @@ func (s *shell) attach(ctx context.Context, f dap.StackFrame, rCtx *build.Result
231230
return nil
232231
}
233232

234-
func (s *shell) canInvoke(ctx context.Context, rCtx *build.ResultHandle, cfg *build.InvokeConfig) (reason string, ok bool) {
235-
var cmd string
236-
if len(cfg.Entrypoint) > 0 {
237-
cmd = cfg.Entrypoint[0]
238-
} else if len(cfg.Cmd) > 0 {
239-
cmd = cfg.Cmd[0]
240-
}
241-
242-
if cmd == "" {
243-
return "no command specified", false
244-
}
245-
246-
st, err := rCtx.StatFile(ctx, cmd, cfg)
247-
if err != nil {
248-
return fmt.Sprintf("stat error: %s", err), false
249-
}
250-
251-
mode := fs.FileMode(st.Mode)
252-
if !mode.IsRegular() {
253-
return fmt.Sprintf("%s: not a file", cmd), false
254-
}
255-
if mode&0111 == 0 {
256-
return fmt.Sprintf("%s: not an executable", cmd), false
257-
}
258-
return "", true
259-
}
260-
261233
// SendRunInTerminalRequest will send the request to the client to attach to
262234
// the socket path that was created by Init. This is intended to be run
263235
// from the adapter and interact directly with the client.

0 commit comments

Comments
 (0)