Skip to content

Commit f3ad8ab

Browse files
committed
wcow: enable custom frontend bridge communication via named pipe
Signed-off-by: Billy Owire <billyowire95@gmail.com>
1 parent 40a5c2d commit f3ad8ab

File tree

10 files changed

+163
-7
lines changed

10 files changed

+163
-7
lines changed

executor/containerdexecutor/executor_windows.go

+13
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,18 @@ 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+
frontendGrpcBridge := os.Getenv("FrontendGRPCPipe")
95+
spec.Mounts = append(spec.Mounts, specs.Mount{
96+
Source: frontendGrpcBridge,
97+
Destination: frontendGrpcBridge,
98+
Type: "",
99+
})
100+
spec.Process.Env = append(spec.Process.Env,
101+
"FrontendGRPCPipe="+frontendGrpcBridge,
102+
)
103+
}
91104
return spec, releaseAll, nil
92105
}
93106

frontend/dockerfile/builder/build.go

+8
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ package builder
22

33
import (
44
"context"
5+
"os"
6+
"runtime"
57
"strings"
68
"sync"
79

810
"github.com/containerd/platforms"
11+
"github.com/google/uuid"
912
"github.com/moby/buildkit/client/llb"
1013
"github.com/moby/buildkit/client/llb/sourceresolver"
1114
"github.com/moby/buildkit/frontend"
@@ -22,6 +25,7 @@ import (
2225
"github.com/moby/buildkit/solver/errdefs"
2326
"github.com/moby/buildkit/solver/pb"
2427
"github.com/moby/buildkit/solver/result"
28+
"github.com/moby/buildkit/util/appdefaults"
2529
dockerspec "github.com/moby/docker-image-spec/specs-go/v1"
2630
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
2731
"github.com/pkg/errors"
@@ -59,6 +63,10 @@ func Build(ctx context.Context, c client.Client) (_ *client.Result, err error) {
5963
}
6064
return res, err
6165
} else if ref, cmdline, loc, ok := parser.DetectSyntax(src.Data); ok {
66+
if runtime.GOOS == "windows" {
67+
_ = os.Setenv("FrontendGRPCPipe", `\\.\pipe\buildkit-frontend-bridge`+uuid.NewString())
68+
ctx = context.WithValue(ctx, appdefaults.ContextKeyCustomFrontend, true)
69+
}
6270
res, err := forwardGateway(ctx, c, ref, cmdline)
6371
if err != nil && len(errdefs.Sources(err)) == 0 {
6472
return nil, wrapSource(err, src.SourceMap, loc)

frontend/gateway/gateway.go

+12
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"
@@ -501,6 +502,8 @@ func newBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridg
501502
}
502503

503504
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) {
505+
var listener net.Listener
506+
isWindowsPlatform := runtime.GOOS == "windows"
504507
ctx, cancel := context.WithCancelCause(ctx)
505508
lbf := newBridgeForwarder(ctx, llbBridge, exec, workers, inputs, sid, sm)
506509
serverOpt := []grpc.ServerOption{
@@ -514,11 +517,20 @@ func serveLLBBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLB
514517
pb.RegisterLLBBridgeServer(server, lbf)
515518

516519
go func() {
520+
if isWindowsPlatform {
521+
listener = createNPipeListener()
522+
if err := handleWindowsPipeConn(ctx, listener, lbf, cancel); err != nil {
523+
return
524+
}
525+
}
517526
serve(ctx, server, lbf.conn)
518527
select {
519528
case <-ctx.Done():
520529
default:
521530
lbf.isErrServerClosed = true
531+
if isWindowsPlatform && listener != nil {
532+
_ = listener.Close()
533+
}
522534
}
523535
cancel(errors.WithStack(context.Canceled))
524536
}()

frontend/gateway/gateway_unix.go

+16
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() net.Listener {
11+
return nil
12+
}
13+
14+
func handleWindowsPipeConn(_ context.Context, _ net.Listener, _ *llbBridgeForwarder, _ context.CancelCauseFunc) error {
15+
return nil
16+
}

frontend/gateway/gateway_windows.go

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

frontend/gateway/grpcclient/client.go

+12-4
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
}
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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/pkg/errors"
12+
"google.golang.org/grpc"
13+
)
14+
15+
func nPipeDialer() (string, grpc.DialOption) {
16+
addr := os.Getenv("FrontendGRPCPipe")
17+
dialFn := func(ctx context.Context, _ string) (net.Conn, error) {
18+
conn, err := winio.DialPipe(addr, nil)
19+
if err != nil {
20+
return nil, errors.Wrap(err, "Failed to connect to gRPC server")
21+
}
22+
return conn, nil
23+
}
24+
return addr, grpc.WithContextDialer(dialFn)
25+
}

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ require (
4040
github.com/golang/protobuf v1.5.4
4141
github.com/google/go-cmp v0.7.0
4242
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
43+
github.com/google/uuid v1.6.0
4344
github.com/hashicorp/go-cleanhttp v0.5.2
4445
github.com/hashicorp/go-immutable-radix/v2 v2.1.0
4546
github.com/hashicorp/go-multierror v1.1.1
@@ -155,7 +156,6 @@ require (
155156
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
156157
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
157158
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect
158-
github.com/google/uuid v1.6.0 // indirect
159159
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
160160
github.com/hanwen/go-fuse/v2 v2.6.3 // indirect
161161
github.com/hashicorp/errwrap v1.1.0 // indirect

util/appdefaults/appdefaults.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package appdefaults
22

3+
type contextKey string
4+
35
const (
4-
BridgeName = "buildkit0"
5-
BridgeSubnet = "10.10.0.0/16"
6+
BridgeName = "buildkit0"
7+
BridgeSubnet = "10.10.0.0/16"
8+
ContextKeyCustomFrontend = contextKey("custom.frontend.context.flag")
69
)

0 commit comments

Comments
 (0)