Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions executor/containerdexecutor/executor_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/moby/buildkit/executor/oci"
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/appdefaults"
"github.com/moby/buildkit/util/network"
"github.com/moby/sys/user"
"github.com/opencontainers/runtime-spec/specs-go"
Expand Down Expand Up @@ -88,6 +89,16 @@ func (w *containerdExecutor) createOCISpec(ctx context.Context, id, _, _ string,
return nil, nil, err
}
releasers = append(releasers, cleanup)

if v, ok := ctx.Value(appdefaults.ContextKeyCustomFrontend).(bool); ok && v {
frontendID := os.Getenv("BUILDKIT_FRONTEND_ID")
frontendGrpcBridge := appdefaults.FrontendGRPCPipe + frontendID
spec.Mounts = append(spec.Mounts, specs.Mount{
Source: frontendGrpcBridge,
Destination: frontendGrpcBridge,
})
spec.Process.Env = append(spec.Process.Env, "BUILDKIT_FRONTEND_ID="+frontendID)
}
return spec, releaseAll, nil
}

Expand Down
1 change: 1 addition & 0 deletions frontend/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type FrontendLLBBridge interface {
sourceresolver.MetaResolver
Solve(ctx context.Context, req SolveRequest, sid string) (*Result, error)
Warn(ctx context.Context, dgst digest.Digest, msg string, opts WarnOpts) error
GetFrontendID() string
}

type SolveRequest = gw.SolveRequest
Expand Down
20 changes: 20 additions & 0 deletions frontend/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net"
"os"
"path/filepath"
"runtime"
"slices"
"strconv"
"strings"
Expand Down Expand Up @@ -41,6 +42,7 @@ import (
llberrdefs "github.com/moby/buildkit/solver/llbsolver/errdefs"
opspb "github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/apicaps"
"github.com/moby/buildkit/util/appdefaults"
"github.com/moby/buildkit/util/bklog"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/moby/buildkit/util/stack"
Expand Down Expand Up @@ -495,13 +497,17 @@ func newBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLBBridg
sm: sm,
ctrs: map[string]gwclient.Container{},
executor: exec,
id: identity.NewID(),
}
return lbf
}

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) {
var listener net.Listener
isWindowsPlatform := runtime.GOOS == "windows"
ctx, cancel := context.WithCancelCause(ctx)
lbf := newBridgeForwarder(ctx, llbBridge, exec, workers, inputs, sid, sm)

serverOpt := []grpc.ServerOption{
grpc.UnaryInterceptor(grpcerrors.UnaryServerInterceptor),
grpc.StreamInterceptor(grpcerrors.StreamServerInterceptor),
Expand All @@ -513,11 +519,20 @@ func serveLLBBridgeForwarder(ctx context.Context, llbBridge frontend.FrontendLLB
pb.RegisterLLBBridgeServer(server, lbf)

go func() {
if isWindowsPlatform {
listener = createNPipeListener(llbBridge.GetFrontendID())
if err := handleWindowsPipeConn(ctx, listener, lbf, cancel); err != nil {
return
}
}
serve(ctx, server, lbf.conn)
select {
case <-ctx.Done():
default:
lbf.isErrServerClosed = true
if isWindowsPlatform && listener != nil {
_ = listener.Close()
}
}
cancel(errors.WithStack(context.Canceled))
}()
Expand Down Expand Up @@ -609,6 +624,7 @@ type llbBridgeForwarder struct {
*pipe
ctrs map[string]gwclient.Container
ctrsMu sync.Mutex
id string
}

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

if runtime.GOOS == "windows" {
ctx = context.WithValue(ctx, appdefaults.ContextKeyCustomFrontend, true)
}

ctx = tracing.ContextWithSpanFromContext(ctx, lbf.callCtx)
res, err := lbf.llbBridge.Solve(ctx, frontend.SolveRequest{
Evaluate: req.Evaluate,
Expand Down
16 changes: 16 additions & 0 deletions frontend/gateway/gateway_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//go:build !windows

package gateway

import (
"context"
"net"
)

func createNPipeListener(_ string) net.Listener {
return nil
}

func handleWindowsPipeConn(_ context.Context, _ net.Listener, _ *llbBridgeForwarder, _ context.CancelCauseFunc) error {
return nil
}
64 changes: 64 additions & 0 deletions frontend/gateway/gateway_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//go:build windows

package gateway

import (
"context"
"net"
"os"

"github.com/Microsoft/go-winio"
"github.com/moby/buildkit/util/appdefaults"
"github.com/moby/buildkit/util/bklog"
"github.com/pkg/errors"
)

func createNPipeListener(frontendID string) net.Listener {
os.Setenv("BUILDKIT_FRONTEND_ID", frontendID)
pipeCfg := &winio.PipeConfig{
SecurityDescriptor: "D:P(A;;GA;;;AU)",
MessageMode: false,
InputBufferSize: 4096,
OutputBufferSize: 4096,
}
listener, err := winio.ListenPipe(appdefaults.FrontendGRPCPipe+frontendID, pipeCfg)
if err != nil {
bklog.L.Errorf("Failed to initialize named pipe listener: %s", err)
return nil
}
return listener
}

// handleWindowsPipeConn waits for a client to connect to the named pipe.
// It assigns the connection to lbf.conn or cancels the context on error or timeout.
func handleWindowsPipeConn(ctx context.Context, listener net.Listener, lbf *llbBridgeForwarder, cancel context.CancelCauseFunc) error {
connCh := make(chan net.Conn, 1)
errCh := make(chan error, 1)

go func() {
defer func() {
if r := recover(); r != nil {
errCh <- errors.Errorf("panic in Accept: %v", r)
}
}()
conn, err := listener.Accept()
if err != nil {
errCh <- err
return
}
connCh <- conn
}()

select {
case <-ctx.Done():
_ = listener.Close()
return context.Cause(ctx)
case err := <-errCh:
lbf.isErrServerClosed = true
cancel(errors.WithStack(err))
return err
case conn := <-connCh:
lbf.conn = conn
return nil
}
}
16 changes: 12 additions & 4 deletions frontend/gateway/grpcclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"net"
"os"
"runtime"
"strings"
"sync"
"syscall"
Expand Down Expand Up @@ -1235,10 +1236,17 @@ func (r *reference) StatFile(ctx context.Context, req client.StatRequest) (*fsty
}

func grpcClientConn(ctx context.Context) (context.Context, *grpc.ClientConn, error) {
addr := "localhost"
dialer := grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
return stdioConn(), nil
})

if runtime.GOOS == "windows" {
addr, dialer = nPipeDialer()
}

dialOpts := []grpc.DialOption{
grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
return stdioConn(), nil
}),
dialer,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(grpcerrors.UnaryClientInterceptor),
grpc.WithStreamInterceptor(grpcerrors.StreamClientInterceptor),
Expand All @@ -1247,7 +1255,7 @@ func grpcClientConn(ctx context.Context) (context.Context, *grpc.ClientConn, err
}

//nolint:staticcheck // ignore SA1019 NewClient has different behavior and needs to be tested
cc, err := grpc.DialContext(ctx, "localhost", dialOpts...)
cc, err := grpc.DialContext(ctx, addr, dialOpts...)
if err != nil {
return ctx, nil, errors.Wrap(err, "failed to create grpc client")
}
Expand Down
9 changes: 9 additions & 0 deletions frontend/gateway/grpcclient/client_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build !windows

package grpcclient

import "google.golang.org/grpc"

func nPipeDialer() (string, grpc.DialOption) {
return "", nil
}
26 changes: 26 additions & 0 deletions frontend/gateway/grpcclient/client_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//go:build windows

package grpcclient

import (
"context"
"net"
"os"

"github.com/Microsoft/go-winio"
"github.com/moby/buildkit/util/appdefaults"
"github.com/pkg/errors"
"google.golang.org/grpc"
)

func nPipeDialer() (string, grpc.DialOption) {
addr := appdefaults.FrontendGRPCPipe + os.Getenv("BUILDKIT_FRONTEND_ID")
dialFn := func(ctx context.Context, _ string) (net.Conn, error) {
conn, err := winio.DialPipe(addr, nil)
if err != nil {
return nil, errors.Wrap(err, "Failed to connect to gRPC server")
}
return conn, nil
}
return addr, grpc.WithContextDialer(dialFn)
}
4 changes: 4 additions & 0 deletions solver/llbsolver/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ func (b *llbBridge) Exec(ctx context.Context, id string, process executor.Proces
return b.executor.Exec(ctx, id, process)
}

func (b *llbBridge) GetFrontendID() string {
return identity.NewID()
}

func (b *llbBridge) loadExecutor() error {
b.executorOnce.Do(func() {
w, err := b.resolveWorker()
Expand Down
7 changes: 7 additions & 0 deletions solver/llbsolver/provenance_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//go:build !windows

package llbsolver

func (b *provenanceBridge) GetFrontendID() string {
return ""
}
7 changes: 7 additions & 0 deletions solver/llbsolver/provenance_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//go:build windows

package llbsolver

func (b *provenanceBridge) GetFrontendID() string {
return b.llbBridge.GetFrontendID()
}
7 changes: 5 additions & 2 deletions util/appdefaults/appdefaults.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package appdefaults

type contextKey string

const (
BridgeName = "buildkit0"
BridgeSubnet = "10.10.0.0/16"
BridgeName = "buildkit0"
BridgeSubnet = "10.10.0.0/16"
ContextKeyCustomFrontend = contextKey("custom.frontend.context.flag")
)
3 changes: 2 additions & 1 deletion util/appdefaults/appdefaults_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import (
)

const (
Address = "npipe:////./pipe/buildkitd"
Address = "npipe:////./pipe/buildkitd"
FrontendGRPCPipe = `\\.\pipe\buildkitd-frontend-`
)

var (
Expand Down