Skip to content

Commit fae9dfb

Browse files
committed
wcow: enable custom frontend bridge communication via named pipe
Signed-off-by: Billy Owire <[email protected]>
1 parent 7841a73 commit fae9dfb

File tree

13 files changed

+184
-7
lines changed

13 files changed

+184
-7
lines changed

executor/containerdexecutor/executor_windows.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/moby/buildkit/executor/oci"
1313
"github.com/moby/buildkit/snapshot"
1414
"github.com/moby/buildkit/solver/pb"
15+
"github.com/moby/buildkit/util/appdefaults"
1516
"github.com/moby/buildkit/util/network"
1617
"github.com/moby/sys/user"
1718
"github.com/opencontainers/runtime-spec/specs-go"
@@ -88,6 +89,16 @@ func (w *containerdExecutor) createOCISpec(ctx context.Context, id, _, _ string,
8889
return nil, nil, err
8990
}
9091
releasers = append(releasers, cleanup)
92+
93+
if v, ok := ctx.Value(appdefaults.ContextKeyCustomFrontend).(bool); ok && v {
94+
frontendID := os.Getenv("BUILDKIT_FRONTEND_ID")
95+
frontendGrpcBridge := appdefaults.FrontendGRPCPipe + frontendID
96+
spec.Mounts = append(spec.Mounts, specs.Mount{
97+
Source: frontendGrpcBridge,
98+
Destination: frontendGrpcBridge,
99+
})
100+
spec.Process.Env = append(spec.Process.Env, "BUILDKIT_FRONTEND_ID="+frontendID)
101+
}
91102
return spec, releaseAll, nil
92103
}
93104

frontend/frontend.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type FrontendLLBBridge interface {
3131
sourceresolver.MetaResolver
3232
Solve(ctx context.Context, req SolveRequest, sid string) (*Result, error)
3333
Warn(ctx context.Context, dgst digest.Digest, msg string, opts WarnOpts) error
34+
GetFrontendID() string
3435
}
3536

3637
type SolveRequest = gw.SolveRequest

frontend/gateway/gateway.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net"
1010
"os"
1111
"path/filepath"
12+
"runtime"
1213
"slices"
1314
"strconv"
1415
"strings"
@@ -41,6 +42,7 @@ import (
4142
llberrdefs "github.com/moby/buildkit/solver/llbsolver/errdefs"
4243
opspb "github.com/moby/buildkit/solver/pb"
4344
"github.com/moby/buildkit/util/apicaps"
45+
"github.com/moby/buildkit/util/appdefaults"
4446
"github.com/moby/buildkit/util/bklog"
4547
"github.com/moby/buildkit/util/grpcerrors"
4648
"github.com/moby/buildkit/util/stack"
@@ -495,13 +497,17 @@ func newBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridg
495497
sm: sm,
496498
ctrs: map[string]gwclient.Container{},
497499
executor: exec,
500+
id: identity.NewID(),
498501
}
499502
return lbf
500503
}
501504

502505
func serveLLBBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridge, exec executor.Executor, workers worker.Infos, inputs map[string]*opspb.Definition, sid string, sm *session.Manager) (*llbBridgeForwarder, context.Context) {
506+
var listener net.Listener
507+
isWindowsPlatform := runtime.GOOS == "windows"
503508
ctx, cancel := context.WithCancelCause(ctx)
504509
lbf := newBridgeForwarder(ctx, llbBridge, exec, workers, inputs, sid, sm)
510+
505511
serverOpt := []grpc.ServerOption{
506512
grpc.UnaryInterceptor(grpcerrors.UnaryServerInterceptor),
507513
grpc.StreamInterceptor(grpcerrors.StreamServerInterceptor),
@@ -513,11 +519,20 @@ func serveLLBBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLB
513519
pb.RegisterLLBBridgeServer(server, lbf)
514520

515521
go func() {
522+
if isWindowsPlatform {
523+
listener = createNPipeListener(llbBridge.GetFrontendID())
524+
if err := handleWindowsPipeConn(ctx, listener, lbf, cancel); err != nil {
525+
return
526+
}
527+
}
516528
serve(ctx, server, lbf.conn)
517529
select {
518530
case <-ctx.Done():
519531
default:
520532
lbf.isErrServerClosed = true
533+
if isWindowsPlatform && listener != nil {
534+
_ = listener.Close()
535+
}
521536
}
522537
cancel(errors.WithStack(context.Canceled))
523538
}()
@@ -609,6 +624,7 @@ type llbBridgeForwarder struct {
609624
*pipe
610625
ctrs map[string]gwclient.Container
611626
ctrsMu sync.Mutex
627+
id string
612628
}
613629

614630
func (lbf *llbBridgeForwarder) ResolveSourceMeta(ctx context.Context, req *pb.ResolveSourceMetaRequest) (*pb.ResolveSourceMetaResponse, error) {
@@ -766,6 +782,10 @@ func (lbf *llbBridgeForwarder) Solve(ctx context.Context, req *pb.SolveRequest)
766782
}
767783
}
768784

785+
if runtime.GOOS == "windows" {
786+
ctx = context.WithValue(ctx, appdefaults.ContextKeyCustomFrontend, true)
787+
}
788+
769789
ctx = tracing.ContextWithSpanFromContext(ctx, lbf.callCtx)
770790
res, err := lbf.llbBridge.Solve(ctx, frontend.SolveRequest{
771791
Evaluate: req.Evaluate,

frontend/gateway/gateway_unix.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//go:build !windows
2+
3+
package gateway
4+
5+
import (
6+
"context"
7+
"net"
8+
)
9+
10+
func createNPipeListener(_ string) net.Listener {
11+
return nil
12+
}
13+
14+
func handleWindowsPipeConn(_ context.Context, _ net.Listener, _ *llbBridgeForwarder, _ context.CancelCauseFunc) error {
15+
return nil
16+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//go:build windows
2+
3+
package gateway
4+
5+
import (
6+
"context"
7+
"net"
8+
"os"
9+
10+
"github.com/Microsoft/go-winio"
11+
"github.com/moby/buildkit/util/appdefaults"
12+
"github.com/moby/buildkit/util/bklog"
13+
"github.com/pkg/errors"
14+
)
15+
16+
func createNPipeListener(frontendID string) net.Listener {
17+
os.Setenv("BUILDKIT_FRONTEND_ID", frontendID)
18+
pipeCfg := &winio.PipeConfig{
19+
SecurityDescriptor: "D:P(A;;GA;;;AU)",
20+
MessageMode: false,
21+
InputBufferSize: 4096,
22+
OutputBufferSize: 4096,
23+
}
24+
listener, err := winio.ListenPipe(appdefaults.FrontendGRPCPipe+frontendID, pipeCfg)
25+
if err != nil {
26+
bklog.L.Errorf("Failed to initialize named pipe listener: %s", err)
27+
return nil
28+
}
29+
return listener
30+
}
31+
32+
// handleWindowsPipeConn waits for a client to connect to the named pipe.
33+
// It assigns the connection to lbf.conn or cancels the context on error or timeout.
34+
func handleWindowsPipeConn(ctx context.Context, listener net.Listener, lbf *llbBridgeForwarder, cancel context.CancelCauseFunc) error {
35+
connCh := make(chan net.Conn, 1)
36+
errCh := make(chan error, 1)
37+
38+
go func() {
39+
defer func() {
40+
if r := recover(); r != nil {
41+
errCh <- errors.Errorf("panic in Accept: %v", r)
42+
}
43+
}()
44+
conn, err := listener.Accept()
45+
if err != nil {
46+
errCh <- err
47+
return
48+
}
49+
connCh <- conn
50+
}()
51+
52+
select {
53+
case <-ctx.Done():
54+
_ = listener.Close()
55+
return context.Cause(ctx)
56+
case err := <-errCh:
57+
lbf.isErrServerClosed = true
58+
cancel(errors.WithStack(err))
59+
return err
60+
case conn := <-connCh:
61+
lbf.conn = conn
62+
return nil
63+
}
64+
}

frontend/gateway/grpcclient/client.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"io"
88
"net"
99
"os"
10+
"runtime"
1011
"strings"
1112
"sync"
1213
"syscall"
@@ -1235,10 +1236,17 @@ func (r *reference) StatFile(ctx context.Context, req client.StatRequest) (*fsty
12351236
}
12361237

12371238
func grpcClientConn(ctx context.Context) (context.Context, *grpc.ClientConn, error) {
1239+
addr := "localhost"
1240+
dialer := grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
1241+
return stdioConn(), nil
1242+
})
1243+
1244+
if runtime.GOOS == "windows" {
1245+
addr, dialer = nPipeDialer()
1246+
}
1247+
12381248
dialOpts := []grpc.DialOption{
1239-
grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
1240-
return stdioConn(), nil
1241-
}),
1249+
dialer,
12421250
grpc.WithTransportCredentials(insecure.NewCredentials()),
12431251
grpc.WithUnaryInterceptor(grpcerrors.UnaryClientInterceptor),
12441252
grpc.WithStreamInterceptor(grpcerrors.StreamClientInterceptor),
@@ -1247,7 +1255,7 @@ func grpcClientConn(ctx context.Context) (context.Context, *grpc.ClientConn, err
12471255
}
12481256

12491257
//nolint:staticcheck // ignore SA1019 NewClient has different behavior and needs to be tested
1250-
cc, err := grpc.DialContext(ctx, "localhost", dialOpts...)
1258+
cc, err := grpc.DialContext(ctx, addr, dialOpts...)
12511259
if err != nil {
12521260
return ctx, nil, errors.Wrap(err, "failed to create grpc client")
12531261
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build !windows
2+
3+
package grpcclient
4+
5+
import "google.golang.org/grpc"
6+
7+
func nPipeDialer() (string, grpc.DialOption) {
8+
return "", nil
9+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//go:build windows
2+
3+
package grpcclient
4+
5+
import (
6+
"context"
7+
"net"
8+
"os"
9+
10+
"github.com/Microsoft/go-winio"
11+
"github.com/moby/buildkit/util/appdefaults"
12+
"github.com/pkg/errors"
13+
"google.golang.org/grpc"
14+
)
15+
16+
func nPipeDialer() (string, grpc.DialOption) {
17+
addr := appdefaults.FrontendGRPCPipe + os.Getenv("BUILDKIT_FRONTEND_ID")
18+
dialFn := func(ctx context.Context, _ string) (net.Conn, error) {
19+
conn, err := winio.DialPipe(addr, nil)
20+
if err != nil {
21+
return nil, errors.Wrap(err, "Failed to connect to gRPC server")
22+
}
23+
return conn, nil
24+
}
25+
return addr, grpc.WithContextDialer(dialFn)
26+
}

solver/llbsolver/bridge.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ func (b *llbBridge) Exec(ctx context.Context, id string, process executor.Proces
192192
return b.executor.Exec(ctx, id, process)
193193
}
194194

195+
func (b *llbBridge) GetFrontendID() string {
196+
return identity.NewID()
197+
}
198+
195199
func (b *llbBridge) loadExecutor() error {
196200
b.executorOnce.Do(func() {
197201
w, err := b.resolveWorker()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build !windows
2+
3+
package llbsolver
4+
5+
func (b *provenanceBridge) GetFrontendID() string {
6+
return ""
7+
}

0 commit comments

Comments
 (0)