vendor: update buildkit with typed errors support

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2020-06-24 22:20:05 -07:00
parent 0269388aa7
commit 2d720a1e0b
619 changed files with 38296 additions and 104947 deletions

View File

@ -6,12 +6,16 @@ import (
"crypto/x509"
"io/ioutil"
"net"
"time"
"net/url"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
controlapi "github.com/moby/buildkit/api/services/control"
"github.com/moby/buildkit/client/connhelper"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/grpchijack"
"github.com/moby/buildkit/util/appdefaults"
"github.com/moby/buildkit/util/grpcerrors"
opentracing "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"google.golang.org/grpc"
@ -29,6 +33,10 @@ func New(ctx context.Context, address string, opts ...ClientOpt) (*Client, error
gopts := []grpc.DialOption{}
needDialer := true
needWithInsecure := true
var unary []grpc.UnaryClientInterceptor
var stream []grpc.StreamClientInterceptor
for _, o := range opts {
if _, ok := o.(*withFailFast); ok {
gopts = append(gopts, grpc.FailOnNonTempDialError(true))
@ -42,12 +50,11 @@ func New(ctx context.Context, address string, opts ...ClientOpt) (*Client, error
needWithInsecure = false
}
if wt, ok := o.(*withTracer); ok {
gopts = append(gopts,
grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(wt.tracer, otgrpc.LogPayloads())),
grpc.WithStreamInterceptor(otgrpc.OpenTracingStreamClientInterceptor(wt.tracer)))
unary = append(unary, otgrpc.OpenTracingClientInterceptor(wt.tracer, otgrpc.LogPayloads()))
stream = append(stream, otgrpc.OpenTracingStreamClientInterceptor(wt.tracer))
}
if wd, ok := o.(*withDialer); ok {
gopts = append(gopts, grpc.WithDialer(wd.dialer))
gopts = append(gopts, grpc.WithContextDialer(wd.dialer))
needDialer = false
}
}
@ -56,9 +63,7 @@ func New(ctx context.Context, address string, opts ...ClientOpt) (*Client, error
if err != nil {
return nil, err
}
// TODO(AkihiroSuda): use WithContextDialer (requires grpc 1.19)
// https://github.com/grpc/grpc-go/commit/40cb5618f475e7b9d61aa7920ae4b04ef9bbaf89
gopts = append(gopts, grpc.WithDialer(dialFn))
gopts = append(gopts, grpc.WithContextDialer(dialFn))
}
if needWithInsecure {
gopts = append(gopts, grpc.WithInsecure())
@ -66,6 +71,31 @@ func New(ctx context.Context, address string, opts ...ClientOpt) (*Client, error
if address == "" {
address = appdefaults.Address
}
// grpc-go uses a slightly different naming scheme: https://github.com/grpc/grpc/blob/master/doc/naming.md
// This will end up setting rfc non-complient :authority header to address string (e.g. tcp://127.0.0.1:1234).
// So, here sets right authority header via WithAuthority DialOption.
addressURL, err := url.Parse(address)
if err != nil {
return nil, err
}
gopts = append(gopts, grpc.WithAuthority(addressURL.Host))
unary = append(unary, grpcerrors.UnaryClientInterceptor)
stream = append(stream, grpcerrors.StreamClientInterceptor)
if len(unary) == 1 {
gopts = append(gopts, grpc.WithUnaryInterceptor(unary[0]))
} else if len(unary) > 1 {
gopts = append(gopts, grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unary...)))
}
if len(stream) == 1 {
gopts = append(gopts, grpc.WithStreamInterceptor(stream[0]))
} else if len(stream) > 1 {
gopts = append(gopts, grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(stream...)))
}
conn, err := grpc.DialContext(ctx, address, gopts...)
if err != nil {
return nil, errors.Wrapf(err, "failed to dial %q . make sure buildkitd is running", address)
@ -80,6 +110,10 @@ func (c *Client) controlClient() controlapi.ControlClient {
return controlapi.NewControlClient(c.conn)
}
func (c *Client) Dialer() session.Dialer {
return grpchijack.Dialer(c.controlClient())
}
func (c *Client) Close() error {
return c.conn.Close()
}
@ -91,10 +125,10 @@ func WithFailFast() ClientOpt {
}
type withDialer struct {
dialer func(string, time.Duration) (net.Conn, error)
dialer func(context.Context, string) (net.Conn, error)
}
func WithDialer(df func(string, time.Duration) (net.Conn, error)) ClientOpt {
func WithContextDialer(df func(context.Context, string) (net.Conn, error)) ClientOpt {
return &withDialer{dialer: df}
}
@ -152,17 +186,13 @@ type withTracer struct {
tracer opentracing.Tracer
}
func resolveDialer(address string) (func(string, time.Duration) (net.Conn, error), error) {
func resolveDialer(address string) (func(context.Context, string) (net.Conn, error), error) {
ch, err := connhelper.GetConnectionHelper(address)
if err != nil {
return nil, err
}
if ch != nil {
f := func(a string, _ time.Duration) (net.Conn, error) {
ctx := context.Background()
return ch.ContextDialer(ctx, a)
}
return f, nil
return ch.ContextDialer, nil
}
// basic dialer
return dialer, nil

View File

@ -3,17 +3,18 @@
package client
import (
"context"
"net"
"strings"
"time"
"github.com/pkg/errors"
)
func dialer(address string, timeout time.Duration) (net.Conn, error) {
func dialer(ctx context.Context, address string) (net.Conn, error) {
addrParts := strings.SplitN(address, "://", 2)
if len(addrParts) != 2 {
return nil, errors.Errorf("invalid address %s", address)
}
return net.DialTimeout(addrParts[0], addrParts[1], timeout)
var d net.Dialer
return d.DialContext(ctx, addrParts[0], addrParts[1])
}

View File

@ -1,15 +1,15 @@
package client
import (
"context"
"net"
"strings"
"time"
winio "github.com/Microsoft/go-winio"
"github.com/pkg/errors"
)
func dialer(address string, timeout time.Duration) (net.Conn, error) {
func dialer(ctx context.Context, address string) (net.Conn, error) {
addrParts := strings.SplitN(address, "://", 2)
if len(addrParts) != 2 {
return nil, errors.Errorf("invalid address %s", address)
@ -17,8 +17,9 @@ func dialer(address string, timeout time.Duration) (net.Conn, error) {
switch addrParts[0] {
case "npipe":
address = strings.Replace(addrParts[1], "/", "\\", -1)
return winio.DialPipe(address, &timeout)
return winio.DialPipeContext(ctx, address)
default:
return net.DialTimeout(addrParts[0], addrParts[1], timeout)
var d net.Dialer
return d.DialContext(ctx, addrParts[0], addrParts[1])
}
}

View File

@ -1,4 +1,5 @@
// Package connhelper provides helpers for connecting to a remote daemon host with custom logic.
// Package connhelper provides helpers for connecting to a remote daemon host
// with custom logic.
package connhelper
import (

98
vendor/github.com/moby/buildkit/client/llb/async.go generated vendored Normal file
View File

@ -0,0 +1,98 @@
package llb
import (
"context"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/flightcontrol"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
type asyncState struct {
f func(context.Context, State) (State, error)
prev State
target State
set bool
err error
g flightcontrol.Group
}
func (as *asyncState) Output() Output {
return as
}
func (as *asyncState) Vertex(ctx context.Context) Vertex {
err := as.Do(ctx)
if err != nil {
return &errVertex{err}
}
if as.set {
out := as.target.Output()
if out == nil {
return nil
}
return out.Vertex(ctx)
}
return nil
}
func (as *asyncState) ToInput(ctx context.Context, c *Constraints) (*pb.Input, error) {
err := as.Do(ctx)
if err != nil {
return nil, err
}
if as.set {
out := as.target.Output()
if out == nil {
return nil, nil
}
return out.ToInput(ctx, c)
}
return nil, nil
}
func (as *asyncState) Do(ctx context.Context) error {
_, err := as.g.Do(ctx, "", func(ctx context.Context) (interface{}, error) {
if as.set {
return as.target, as.err
}
res, err := as.f(ctx, as.prev)
if err != nil {
select {
case <-ctx.Done():
if errors.Is(err, ctx.Err()) {
return res, err
}
default:
}
}
as.target = res
as.err = err
as.set = true
return res, err
})
if err != nil {
return err
}
return as.err
}
type errVertex struct {
err error
}
func (v *errVertex) Validate(context.Context) error {
return v.err
}
func (v *errVertex) Marshal(context.Context, *Constraints) (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error) {
return "", nil, nil, nil, v.err
}
func (v *errVertex) Output() Output {
return nil
}
func (v *errVertex) Inputs() []Output {
return nil
}
var _ Vertex = &errVertex{}

View File

@ -1,6 +1,9 @@
package llb
import (
"context"
"sync"
"github.com/moby/buildkit/solver/pb"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go/v1"
@ -13,18 +16,22 @@ import (
// LLB state can be reconstructed from the definition.
type DefinitionOp struct {
MarshalCache
ops map[digest.Digest]*pb.Op
defs map[digest.Digest][]byte
metas map[digest.Digest]pb.OpMetadata
platforms map[digest.Digest]*specs.Platform
dgst digest.Digest
index pb.OutputIndex
mu sync.Mutex
ops map[digest.Digest]*pb.Op
defs map[digest.Digest][]byte
metas map[digest.Digest]pb.OpMetadata
sources map[digest.Digest][]*SourceLocation
platforms map[digest.Digest]*specs.Platform
dgst digest.Digest
index pb.OutputIndex
inputCache map[digest.Digest][]*DefinitionOp
}
// NewDefinitionOp returns a new operation from a marshalled definition.
func NewDefinitionOp(def *pb.Definition) (*DefinitionOp, error) {
ops := make(map[digest.Digest]*pb.Op)
defs := make(map[digest.Digest][]byte)
platforms := make(map[digest.Digest]*specs.Platform)
var dgst digest.Digest
for _, dt := range def.Def {
@ -35,6 +42,45 @@ func NewDefinitionOp(def *pb.Definition) (*DefinitionOp, error) {
dgst = digest.FromBytes(dt)
ops[dgst] = &op
defs[dgst] = dt
var platform *specs.Platform
if op.Platform != nil {
spec := op.Platform.Spec()
platform = &spec
}
platforms[dgst] = platform
}
srcs := map[digest.Digest][]*SourceLocation{}
if def.Source != nil {
sourceMaps := make([]*SourceMap, len(def.Source.Infos))
for i, info := range def.Source.Infos {
var st *State
sdef := info.Definition
if sdef != nil {
op, err := NewDefinitionOp(sdef)
if err != nil {
return nil, err
}
state := NewState(op)
st = &state
}
sourceMaps[i] = NewSourceMap(st, info.Filename, info.Data)
}
for dgst, locs := range def.Source.Locations {
for _, loc := range locs.Locations {
if loc.SourceIndex < 0 || int(loc.SourceIndex) >= len(sourceMaps) {
return nil, errors.Errorf("failed to find source map with index %d", loc.SourceIndex)
}
srcs[digest.Digest(dgst)] = append(srcs[digest.Digest(dgst)], &SourceLocation{
SourceMap: sourceMaps[int(loc.SourceIndex)],
Ranges: loc.Ranges,
})
}
}
}
var index pb.OutputIndex
@ -44,29 +90,34 @@ func NewDefinitionOp(def *pb.Definition) (*DefinitionOp, error) {
}
return &DefinitionOp{
ops: ops,
defs: defs,
metas: def.Metadata,
platforms: make(map[digest.Digest]*specs.Platform),
dgst: dgst,
index: index,
ops: ops,
defs: defs,
metas: def.Metadata,
sources: srcs,
platforms: platforms,
dgst: dgst,
index: index,
inputCache: make(map[digest.Digest][]*DefinitionOp),
}, nil
}
func (d *DefinitionOp) ToInput(c *Constraints) (*pb.Input, error) {
return d.Output().ToInput(c)
func (d *DefinitionOp) ToInput(ctx context.Context, c *Constraints) (*pb.Input, error) {
return d.Output().ToInput(ctx, c)
}
func (d *DefinitionOp) Vertex() Vertex {
func (d *DefinitionOp) Vertex(context.Context) Vertex {
return d
}
func (d *DefinitionOp) Validate() error {
func (d *DefinitionOp) Validate(context.Context) error {
// Scratch state has no digest, ops or metas.
if d.dgst == "" {
return nil
}
d.mu.Lock()
defer d.mu.Unlock()
if len(d.ops) == 0 || len(d.defs) == 0 || len(d.metas) == 0 {
return errors.Errorf("invalid definition op with no ops %d %d", len(d.ops), len(d.metas))
}
@ -95,17 +146,20 @@ func (d *DefinitionOp) Validate() error {
return nil
}
func (d *DefinitionOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) {
func (d *DefinitionOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error) {
if d.dgst == "" {
return "", nil, nil, errors.Errorf("cannot marshal empty definition op")
return "", nil, nil, nil, errors.Errorf("cannot marshal empty definition op")
}
if err := d.Validate(); err != nil {
return "", nil, nil, err
if err := d.Validate(ctx); err != nil {
return "", nil, nil, nil, err
}
d.mu.Lock()
defer d.mu.Unlock()
meta := d.metas[d.dgst]
return d.dgst, d.defs[d.dgst], &meta, nil
return d.dgst, d.defs[d.dgst], &meta, d.sources[d.dgst], nil
}
@ -114,7 +168,11 @@ func (d *DefinitionOp) Output() Output {
return nil
}
return &output{vertex: d, platform: d.platform(), getIndex: func() (pb.OutputIndex, error) {
d.mu.Lock()
platform := d.platforms[d.dgst]
d.mu.Unlock()
return &output{vertex: d, platform: platform, getIndex: func() (pb.OutputIndex, error) {
return d.index, nil
}}
}
@ -126,36 +184,44 @@ func (d *DefinitionOp) Inputs() []Output {
var inputs []Output
d.mu.Lock()
op := d.ops[d.dgst]
platform := d.platforms[d.dgst]
d.mu.Unlock()
for _, input := range op.Inputs {
vtx := &DefinitionOp{
ops: d.ops,
defs: d.defs,
metas: d.metas,
platforms: d.platforms,
dgst: input.Digest,
index: input.Index,
var vtx *DefinitionOp
d.mu.Lock()
if existingIndexes, ok := d.inputCache[input.Digest]; ok {
if int(input.Index) < len(existingIndexes) && existingIndexes[input.Index] != nil {
vtx = existingIndexes[input.Index]
}
}
inputs = append(inputs, &output{vertex: vtx, platform: d.platform(), getIndex: func() (pb.OutputIndex, error) {
if vtx == nil {
vtx = &DefinitionOp{
ops: d.ops,
defs: d.defs,
metas: d.metas,
platforms: d.platforms,
dgst: input.Digest,
index: input.Index,
inputCache: d.inputCache,
}
existingIndexes := d.inputCache[input.Digest]
indexDiff := int(input.Index) - len(existingIndexes)
if indexDiff >= 0 {
// make room in the slice for the new index being set
existingIndexes = append(existingIndexes, make([]*DefinitionOp, indexDiff+1)...)
}
existingIndexes[input.Index] = vtx
d.inputCache[input.Digest] = existingIndexes
}
d.mu.Unlock()
inputs = append(inputs, &output{vertex: vtx, platform: platform, getIndex: func() (pb.OutputIndex, error) {
return pb.OutputIndex(vtx.index), nil
}})
}
return inputs
}
func (d *DefinitionOp) platform() *specs.Platform {
platform, ok := d.platforms[d.dgst]
if ok {
return platform
}
op := d.ops[d.dgst]
if op.Platform != nil {
spec := op.Platform.Spec()
platform = &spec
}
d.platforms[d.dgst] = platform
return platform
}

View File

@ -1,7 +1,8 @@
package llb
import (
_ "crypto/sha256"
"context"
_ "crypto/sha256" // for opencontainers/go-digest
"fmt"
"net"
"sort"
@ -12,19 +13,9 @@ import (
"github.com/pkg/errors"
)
type Meta struct {
Args []string
Env EnvList
Cwd string
User string
ProxyEnv *ProxyEnv
ExtraHosts []HostIP
Network pb.NetMode
Security pb.SecurityMode
}
func NewExecOp(root Output, meta Meta, readOnly bool, c Constraints) *ExecOp {
e := &ExecOp{meta: meta, constraints: c}
func NewExecOp(base State, proxyEnv *ProxyEnv, readOnly bool, c Constraints) *ExecOp {
e := &ExecOp{base: base, constraints: c, proxyEnv: proxyEnv}
root := base.Output()
rootMount := &mount{
target: pb.RootMount,
source: root,
@ -58,9 +49,10 @@ type mount struct {
type ExecOp struct {
MarshalCache
proxyEnv *ProxyEnv
root Output
mounts []*mount
meta Meta
base State
constraints Constraints
isValidated bool
secrets []SecretInfo
@ -89,7 +81,7 @@ func (e *ExecOp) AddMount(target string, source Output, opt ...MountOption) Outp
}
m.output = o
}
e.Store(nil, nil, nil)
e.Store(nil, nil, nil, nil)
e.isValidated = false
return m.output
}
@ -103,19 +95,27 @@ func (e *ExecOp) GetMount(target string) Output {
return nil
}
func (e *ExecOp) Validate() error {
func (e *ExecOp) Validate(ctx context.Context) error {
if e.isValidated {
return nil
}
if len(e.meta.Args) == 0 {
args, err := getArgs(e.base)(ctx)
if err != nil {
return err
}
if len(args) == 0 {
return errors.Errorf("arguments are required")
}
if e.meta.Cwd == "" {
cwd, err := getDir(e.base)(ctx)
if err != nil {
return err
}
if cwd == "" {
return errors.Errorf("working directory is required")
}
for _, m := range e.mounts {
if m.source != nil {
if err := m.source.Vertex().Validate(); err != nil {
if err := m.source.Vertex(ctx).Validate(ctx); err != nil {
return err
}
}
@ -124,68 +124,102 @@ func (e *ExecOp) Validate() error {
return nil
}
func (e *ExecOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) {
func (e *ExecOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error) {
if e.Cached(c) {
return e.Load()
}
if err := e.Validate(); err != nil {
return "", nil, nil, err
if err := e.Validate(ctx); err != nil {
return "", nil, nil, nil, err
}
// make sure mounts are sorted
sort.Slice(e.mounts, func(i, j int) bool {
return e.mounts[i].target < e.mounts[j].target
})
env, err := getEnv(e.base)(ctx)
if err != nil {
return "", nil, nil, nil, err
}
if len(e.ssh) > 0 {
for i, s := range e.ssh {
if s.Target == "" {
e.ssh[i].Target = fmt.Sprintf("/run/buildkit/ssh_agent.%d", i)
}
}
if _, ok := e.meta.Env.Get("SSH_AUTH_SOCK"); !ok {
e.meta.Env = e.meta.Env.AddOrReplace("SSH_AUTH_SOCK", e.ssh[0].Target)
if _, ok := env.Get("SSH_AUTH_SOCK"); !ok {
env = env.AddOrReplace("SSH_AUTH_SOCK", e.ssh[0].Target)
}
}
if c.Caps != nil {
if err := c.Caps.Supports(pb.CapExecMetaSetsDefaultPath); err != nil {
e.meta.Env = e.meta.Env.SetDefault("PATH", system.DefaultPathEnv)
env = env.SetDefault("PATH", system.DefaultPathEnv)
} else {
addCap(&e.constraints, pb.CapExecMetaSetsDefaultPath)
}
}
meta := &pb.Meta{
Args: e.meta.Args,
Env: e.meta.Env.ToArray(),
Cwd: e.meta.Cwd,
User: e.meta.User,
args, err := getArgs(e.base)(ctx)
if err != nil {
return "", nil, nil, nil, err
}
if len(e.meta.ExtraHosts) > 0 {
hosts := make([]*pb.HostIP, len(e.meta.ExtraHosts))
for i, h := range e.meta.ExtraHosts {
cwd, err := getDir(e.base)(ctx)
if err != nil {
return "", nil, nil, nil, err
}
user, err := getUser(e.base)(ctx)
if err != nil {
return "", nil, nil, nil, err
}
meta := &pb.Meta{
Args: args,
Env: env.ToArray(),
Cwd: cwd,
User: user,
}
extraHosts, err := getExtraHosts(e.base)(ctx)
if err != nil {
return "", nil, nil, nil, err
}
if len(extraHosts) > 0 {
hosts := make([]*pb.HostIP, len(extraHosts))
for i, h := range extraHosts {
hosts[i] = &pb.HostIP{Host: h.Host, IP: h.IP.String()}
}
meta.ExtraHosts = hosts
}
network, err := getNetwork(e.base)(ctx)
if err != nil {
return "", nil, nil, nil, err
}
security, err := getSecurity(e.base)(ctx)
if err != nil {
return "", nil, nil, nil, err
}
peo := &pb.ExecOp{
Meta: meta,
Network: e.meta.Network,
Security: e.meta.Security,
Network: network,
Security: security,
}
if e.meta.Network != NetModeSandbox {
if network != NetModeSandbox {
addCap(&e.constraints, pb.CapExecMetaNetwork)
}
if e.meta.Security != SecurityModeSandbox {
if security != SecurityModeSandbox {
addCap(&e.constraints, pb.CapExecMetaSecurity)
}
if p := e.meta.ProxyEnv; p != nil {
if p := e.proxyEnv; p != nil {
peo.Meta.ProxyEnv = &pb.ProxyEnv{
HttpProxy: p.HttpProxy,
HttpsProxy: p.HttpsProxy,
FtpProxy: p.FtpProxy,
HttpProxy: p.HTTPProxy,
HttpsProxy: p.HTTPSProxy,
FtpProxy: p.FTPProxy,
NoProxy: p.NoProxy,
}
addCap(&e.constraints, pb.CapExecMetaProxy)
@ -215,6 +249,14 @@ func (e *ExecOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata,
addCap(&e.constraints, pb.CapExecMountSSH)
}
if e.constraints.Platform == nil {
p, err := getPlatform(e.base)(ctx)
if err != nil {
return "", nil, nil, nil, err
}
e.constraints.Platform = p
}
pop, md := MarshalConstraints(c, &e.constraints)
pop.Op = &pb.Op_Exec{
Exec: peo,
@ -225,11 +267,11 @@ func (e *ExecOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata,
inputIndex := pb.InputIndex(len(pop.Inputs))
if m.source != nil {
if m.tmpfs {
return "", nil, nil, errors.Errorf("tmpfs mounts must use scratch")
return "", nil, nil, nil, errors.Errorf("tmpfs mounts must use scratch")
}
inp, err := m.source.ToInput(c)
inp, err := m.source.ToInput(ctx, c)
if err != nil {
return "", nil, nil, err
return "", nil, nil, nil, err
}
newInput := true
@ -314,9 +356,9 @@ func (e *ExecOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata,
dt, err := pop.Marshal()
if err != nil {
return "", nil, nil, err
return "", nil, nil, nil, err
}
e.Store(dt, md, c)
e.Store(dt, md, e.constraints.SourceLocations, c)
return e.Load()
}
@ -346,7 +388,7 @@ func (e *ExecOp) getMountIndexFn(m *mount) func() (pb.OutputIndex, error) {
i := 0
for _, m2 := range e.mounts {
if m2.noOutput || m2.readonly || m2.cacheID != "" {
if m2.noOutput || m2.readonly || m2.tmpfs || m2.cacheID != "" {
continue
}
if m == m2 {
@ -414,17 +456,11 @@ func (fn runOptionFunc) SetRunOption(ei *ExecInfo) {
fn(ei)
}
func Network(n pb.NetMode) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = network(n)(ei.State)
})
func (fn StateOption) SetRunOption(ei *ExecInfo) {
ei.State = ei.State.With(fn)
}
func Security(s pb.SecurityMode) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = security(s)(ei.State)
})
}
var _ RunOption = StateOption(func(_ State) State { return State{} })
func Shlex(str string) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
@ -443,47 +479,12 @@ func Args(a []string) RunOption {
})
}
func AddEnv(key, value string) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = ei.State.AddEnv(key, value)
})
}
func AddEnvf(key, value string, v ...interface{}) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = ei.State.AddEnvf(key, value, v...)
})
}
func User(str string) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = ei.State.User(str)
})
}
func Dir(str string) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = ei.State.Dir(str)
})
}
func Dirf(str string, v ...interface{}) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = ei.State.Dirf(str, v...)
})
}
func AddExtraHost(host string, ip net.IP) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = ei.State.AddExtraHost(host, ip)
})
}
func Reset(s State) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = ei.State.Reset(s)
})
}
func With(so ...StateOption) RunOption {
return runOptionFunc(func(ei *ExecInfo) {
ei.State = ei.State.With(so...)
@ -628,9 +629,9 @@ type MountInfo struct {
}
type ProxyEnv struct {
HttpProxy string
HttpsProxy string
FtpProxy string
HTTPProxy string
HTTPSProxy string
FTPProxy string
NoProxy string
}

View File

@ -1,7 +1,8 @@
package llb
import (
_ "crypto/sha256"
"context"
_ "crypto/sha256" // for opencontainers/go-digest
"os"
"path"
"strconv"
@ -52,7 +53,7 @@ type CopyInput interface {
}
type subAction interface {
toProtoAction(string, pb.InputIndex) pb.IsFileAction
toProtoAction(context.Context, string, pb.InputIndex) (pb.IsFileAction, error)
}
type FileAction struct {
@ -146,7 +147,7 @@ type fileActionMkdir struct {
info MkdirInfo
}
func (a *fileActionMkdir) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
func (a *fileActionMkdir) toProtoAction(ctx context.Context, parent string, base pb.InputIndex) (pb.IsFileAction, error) {
return &pb.FileAction_Mkdir{
Mkdir: &pb.FileActionMkDir{
Path: normalizePath(parent, a.file, false),
@ -155,7 +156,7 @@ func (a *fileActionMkdir) toProtoAction(parent string, base pb.InputIndex) pb.Is
Owner: a.info.ChownOpt.marshal(base),
Timestamp: marshalTime(a.info.CreatedTime),
},
}
}, nil
}
type MkdirOption interface {
@ -251,13 +252,13 @@ func (co ChownOpt) SetCopyOption(mi *CopyInfo) {
mi.ChownOpt = &co
}
func (cp *ChownOpt) marshal(base pb.InputIndex) *pb.ChownOpt {
if cp == nil {
func (co *ChownOpt) marshal(base pb.InputIndex) *pb.ChownOpt {
if co == nil {
return nil
}
return &pb.ChownOpt{
User: cp.User.marshal(base),
Group: cp.Group.marshal(base),
User: co.User.marshal(base),
Group: co.Group.marshal(base),
}
}
@ -315,7 +316,7 @@ type fileActionMkfile struct {
info MkfileInfo
}
func (a *fileActionMkfile) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
func (a *fileActionMkfile) toProtoAction(ctx context.Context, parent string, base pb.InputIndex) (pb.IsFileAction, error) {
return &pb.FileAction_Mkfile{
Mkfile: &pb.FileActionMkFile{
Path: normalizePath(parent, a.file, false),
@ -324,7 +325,7 @@ func (a *fileActionMkfile) toProtoAction(parent string, base pb.InputIndex) pb.I
Owner: a.info.ChownOpt.marshal(base),
Timestamp: marshalTime(a.info.CreatedTime),
},
}
}, nil
}
func Rm(p string, opts ...RmOption) *FileAction {
@ -379,14 +380,14 @@ type fileActionRm struct {
info RmInfo
}
func (a *fileActionRm) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
func (a *fileActionRm) toProtoAction(ctx context.Context, parent string, base pb.InputIndex) (pb.IsFileAction, error) {
return &pb.FileAction_Rm{
Rm: &pb.FileActionRm{
Path: normalizePath(parent, a.file, false),
AllowNotFound: a.info.AllowNotFound,
AllowWildcard: a.info.AllowWildcard,
},
}
}, nil
}
func Copy(input CopyInput, src, dest string, opts ...CopyOption) *FileAction {
@ -448,9 +449,13 @@ type fileActionCopy struct {
info CopyInfo
}
func (a *fileActionCopy) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
func (a *fileActionCopy) toProtoAction(ctx context.Context, parent string, base pb.InputIndex) (pb.IsFileAction, error) {
src, err := a.sourcePath(ctx)
if err != nil {
return nil, err
}
c := &pb.FileActionCopy{
Src: a.sourcePath(),
Src: src,
Dest: normalizePath(parent, a.dest, true),
Owner: a.info.ChownOpt.marshal(base),
AllowWildcard: a.info.AllowWildcard,
@ -468,19 +473,27 @@ func (a *fileActionCopy) toProtoAction(parent string, base pb.InputIndex) pb.IsF
}
return &pb.FileAction_Copy{
Copy: c,
}
}, nil
}
func (c *fileActionCopy) sourcePath() string {
p := path.Clean(c.src)
func (a *fileActionCopy) sourcePath(ctx context.Context) (string, error) {
p := path.Clean(a.src)
if !path.IsAbs(p) {
if c.state != nil {
p = path.Join("/", c.state.GetDir(), p)
} else if c.fas != nil {
p = path.Join("/", c.fas.state.GetDir(), p)
if a.state != nil {
dir, err := a.state.GetDir(ctx)
if err != nil {
return "", err
}
p = path.Join("/", dir, p)
} else if a.fas != nil {
dir, err := a.fas.state.GetDir(ctx)
if err != nil {
return "", err
}
p = path.Join("/", dir, p)
}
}
return p
return p, nil
}
type CreatedTime time.Time
@ -517,7 +530,7 @@ type FileOp struct {
isValidated bool
}
func (f *FileOp) Validate() error {
func (f *FileOp) Validate(context.Context) error {
if f.isValidated {
return nil
}
@ -529,14 +542,16 @@ func (f *FileOp) Validate() error {
}
type marshalState struct {
ctx context.Context
visited map[*FileAction]*fileActionState
inputs []*pb.Input
actions []*fileActionState
}
func newMarshalState() *marshalState {
func newMarshalState(ctx context.Context) *marshalState {
return &marshalState{
visited: map[*FileAction]*fileActionState{},
ctx: ctx,
}
}
@ -552,7 +567,7 @@ type fileActionState struct {
}
func (ms *marshalState) addInput(st *fileActionState, c *Constraints, o Output) (pb.InputIndex, error) {
inp, err := o.ToInput(c)
inp, err := o.ToInput(ms.ctx, c)
if err != nil {
return 0, err
}
@ -634,12 +649,12 @@ func (ms *marshalState) add(fa *FileAction, c *Constraints) (*fileActionState, e
return st, nil
}
func (f *FileOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) {
func (f *FileOp) Marshal(ctx context.Context, c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error) {
if f.Cached(c) {
return f.Load()
}
if err := f.Validate(); err != nil {
return "", nil, nil, err
if err := f.Validate(ctx); err != nil {
return "", nil, nil, nil, err
}
addCap(&f.constraints, pb.CapFileBase)
@ -651,10 +666,10 @@ func (f *FileOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata,
File: pfo,
}
state := newMarshalState()
state := newMarshalState(ctx)
_, err := state.add(f.action, c)
if err != nil {
return "", nil, nil, err
return "", nil, nil, nil, err
}
pop.Inputs = state.inputs
@ -666,22 +681,30 @@ func (f *FileOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata,
var parent string
if st.fa.state != nil {
parent = st.fa.state.GetDir()
parent, err = st.fa.state.GetDir(ctx)
if err != nil {
return "", nil, nil, nil, err
}
}
action, err := st.action.toProtoAction(ctx, parent, st.base)
if err != nil {
return "", nil, nil, nil, err
}
pfo.Actions = append(pfo.Actions, &pb.FileAction{
Input: getIndex(st.input, len(state.inputs), st.inputRelative),
SecondaryInput: getIndex(st.input2, len(state.inputs), st.input2Relative),
Output: output,
Action: st.action.toProtoAction(parent, st.base),
Action: action,
})
}
dt, err := pop.Marshal()
if err != nil {
return "", nil, nil, err
return "", nil, nil, nil, err
}
f.Store(dt, md, c)
f.Store(dt, md, f.constraints.SourceLocations, c)
return f.Load()
}

View File

@ -14,21 +14,24 @@ import (
type Definition struct {
Def [][]byte
Metadata map[digest.Digest]pb.OpMetadata
Source *pb.Source
}
func (def *Definition) ToPB() *pb.Definition {
md := make(map[digest.Digest]pb.OpMetadata)
md := make(map[digest.Digest]pb.OpMetadata, len(def.Metadata))
for k, v := range def.Metadata {
md[k] = v
}
return &pb.Definition{
Def: def.Def,
Source: def.Source,
Metadata: md,
}
}
func (def *Definition) FromPB(x *pb.Definition) {
def.Def = x.Def
def.Source = x.Source
def.Metadata = make(map[digest.Digest]pb.OpMetadata)
for k, v := range x.Metadata {
def.Metadata[k] = v
@ -95,18 +98,20 @@ type MarshalCache struct {
digest digest.Digest
dt []byte
md *pb.OpMetadata
srcs []*SourceLocation
constraints *Constraints
}
func (mc *MarshalCache) Cached(c *Constraints) bool {
return mc.dt != nil && mc.constraints == c
}
func (mc *MarshalCache) Load() (digest.Digest, []byte, *pb.OpMetadata, error) {
return mc.digest, mc.dt, mc.md, nil
func (mc *MarshalCache) Load() (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error) {
return mc.digest, mc.dt, mc.md, mc.srcs, nil
}
func (mc *MarshalCache) Store(dt []byte, md *pb.OpMetadata, c *Constraints) {
func (mc *MarshalCache) Store(dt []byte, md *pb.OpMetadata, srcs []*SourceLocation, c *Constraints) {
mc.digest = digest.FromBytes(dt)
mc.dt = dt
mc.md = md
mc.constraints = c
mc.srcs = srcs
}

View File

@ -1,6 +1,7 @@
package llb
import (
"context"
"fmt"
"net"
"path"
@ -24,79 +25,122 @@ var (
keySecurity = contextKeyT("llb.security")
)
func AddEnvf(key, value string, v ...interface{}) StateOption {
return addEnvf(key, value, true, v...)
}
func AddEnv(key, value string) StateOption {
return addEnvf(key, value, false)
}
func addEnvf(key, value string, replace bool, v ...interface{}) StateOption {
if replace {
value = fmt.Sprintf(value, v...)
}
return func(s State) State {
return s.WithValue(keyEnv, getEnv(s).AddOrReplace(key, value))
return s.withValue(keyEnv, func(ctx context.Context) (interface{}, error) {
env, err := getEnv(s)(ctx)
if err != nil {
return nil, err
}
return env.AddOrReplace(key, value), nil
})
}
}
func dir(str string) StateOption {
func Dir(str string) StateOption {
return dirf(str, false)
}
func Dirf(str string, v ...interface{}) StateOption {
return dirf(str, true, v...)
}
func dirf(value string, replace bool, v ...interface{}) StateOption {
if replace {
value = fmt.Sprintf(value, v...)
}
return func(s State) State {
if !path.IsAbs(value) {
prev := getDir(s)
if prev == "" {
prev = "/"
return s.withValue(keyDir, func(ctx context.Context) (interface{}, error) {
if !path.IsAbs(value) {
prev, err := getDir(s)(ctx)
if err != nil {
return nil, err
}
if prev == "" {
prev = "/"
}
value = path.Join(prev, value)
}
value = path.Join(prev, value)
}
return s.WithValue(keyDir, value)
return value, nil
})
}
}
func user(str string) StateOption {
func User(str string) StateOption {
return func(s State) State {
return s.WithValue(keyUser, str)
}
}
func reset(s_ State) StateOption {
func Reset(other State) StateOption {
return func(s State) State {
s = NewState(s.Output())
s.ctx = s_.ctx
s.prev = &other
return s
}
}
func getEnv(s State) EnvList {
v := s.Value(keyEnv)
if v != nil {
return v.(EnvList)
func getEnv(s State) func(context.Context) (EnvList, error) {
return func(ctx context.Context) (EnvList, error) {
v, err := s.getValue(keyEnv)(ctx)
if err != nil {
return nil, err
}
if v != nil {
return v.(EnvList), nil
}
return EnvList{}, nil
}
return EnvList{}
}
func getDir(s State) string {
v := s.Value(keyDir)
if v != nil {
return v.(string)
func getDir(s State) func(context.Context) (string, error) {
return func(ctx context.Context) (string, error) {
v, err := s.getValue(keyDir)(ctx)
if err != nil {
return "", err
}
if v != nil {
return v.(string), nil
}
return "", nil
}
return ""
}
func getArgs(s State) []string {
v := s.Value(keyArgs)
if v != nil {
return v.([]string)
func getArgs(s State) func(context.Context) ([]string, error) {
return func(ctx context.Context) ([]string, error) {
v, err := s.getValue(keyArgs)(ctx)
if err != nil {
return nil, err
}
if v != nil {
return v.([]string), nil
}
return nil, nil
}
return nil
}
func getUser(s State) string {
v := s.Value(keyUser)
if v != nil {
return v.(string)
func getUser(s State) func(context.Context) (string, error) {
return func(ctx context.Context) (string, error) {
v, err := s.getValue(keyUser)(ctx)
if err != nil {
return "", err
}
if v != nil {
return v.(string), nil
}
return "", nil
}
return ""
}
func args(args ...string) StateOption {
@ -111,7 +155,7 @@ func shlexf(str string, replace bool, v ...interface{}) StateOption {
}
return func(s State) State {
arg, err := shlex.Split(str)
if err != nil {
if err != nil { //nolint
// TODO: handle error
}
return args(arg...)(s)
@ -124,27 +168,43 @@ func platform(p specs.Platform) StateOption {
}
}
func getPlatform(s State) *specs.Platform {
v := s.Value(keyPlatform)
if v != nil {
p := v.(specs.Platform)
return &p
func getPlatform(s State) func(context.Context) (*specs.Platform, error) {
return func(ctx context.Context) (*specs.Platform, error) {
v, err := s.getValue(keyPlatform)(ctx)
if err != nil {
return nil, err
}
if v != nil {
p := v.(specs.Platform)
return &p, nil
}
return nil, nil
}
return nil
}
func extraHost(host string, ip net.IP) StateOption {
return func(s State) State {
return s.WithValue(keyExtraHost, append(getExtraHosts(s), HostIP{Host: host, IP: ip}))
return s.withValue(keyExtraHost, func(ctx context.Context) (interface{}, error) {
v, err := getExtraHosts(s)(ctx)
if err != nil {
return nil, err
}
return append(v, HostIP{Host: host, IP: ip}), nil
})
}
}
func getExtraHosts(s State) []HostIP {
v := s.Value(keyExtraHost)
if v != nil {
return v.([]HostIP)
func getExtraHosts(s State) func(context.Context) ([]HostIP, error) {
return func(ctx context.Context) ([]HostIP, error) {
v, err := s.getValue(keyExtraHost)(ctx)
if err != nil {
return nil, err
}
if v != nil {
return v.([]HostIP), nil
}
return nil, nil
}
return nil
}
type HostIP struct {
@ -152,32 +212,42 @@ type HostIP struct {
IP net.IP
}
func network(v pb.NetMode) StateOption {
func Network(v pb.NetMode) StateOption {
return func(s State) State {
return s.WithValue(keyNetwork, v)
}
}
func getNetwork(s State) pb.NetMode {
v := s.Value(keyNetwork)
if v != nil {
n := v.(pb.NetMode)
return n
func getNetwork(s State) func(context.Context) (pb.NetMode, error) {
return func(ctx context.Context) (pb.NetMode, error) {
v, err := s.getValue(keyNetwork)(ctx)
if err != nil {
return 0, err
}
if v != nil {
n := v.(pb.NetMode)
return n, nil
}
return NetModeSandbox, nil
}
return NetModeSandbox
}
func security(v pb.SecurityMode) StateOption {
func Security(v pb.SecurityMode) StateOption {
return func(s State) State {
return s.WithValue(keySecurity, v)
}
}
func getSecurity(s State) pb.SecurityMode {
v := s.Value(keySecurity)
if v != nil {
n := v.(pb.SecurityMode)
return n
func getSecurity(s State) func(context.Context) (pb.SecurityMode, error) {
return func(ctx context.Context) (pb.SecurityMode, error) {
v, err := s.getValue(keySecurity)(ctx)
if err != nil {
return 0, err
}
if v != nil {
n := v.(pb.SecurityMode)
return n, nil
}
return SecurityModeSandbox, nil
}
return SecurityModeSandbox
}
type EnvList []KeyValue

View File

@ -14,6 +14,15 @@ func WithMetaResolver(mr ImageMetaResolver) ImageOption {
})
}
// ResolveDigest uses the meta resolver to update the ref of image with full digest before marshaling.
// This makes image ref immutable and is recommended if you want to make sure meta resolver data
// matches the image used during the build.
func ResolveDigest(v bool) ImageOption {
return imageOptionFunc(func(ii *ImageInfo) {
ii.resolveDigest = v
})
}
// ImageMetaResolver can resolve image config metadata from a reference
type ImageMetaResolver interface {
ResolveImageConfig(ctx context.Context, ref string, opt ResolveImageConfigOpt) (digest.Digest, []byte, error)

View File

@ -2,7 +2,7 @@ package llb
import (
"context"
_ "crypto/sha256"
_ "crypto/sha256" // for opencontainers/go-digest
"encoding/json"
"os"
"strconv"
@ -34,7 +34,7 @@ func NewSource(id string, attrs map[string]string, c Constraints) *SourceOp {
return s
}
func (s *SourceOp) Validate() error {
func (s *SourceOp) Validate(ctx context.Context) error {
if s.err != nil {
return s.err
}
@ -44,12 +44,12 @@ func (s *SourceOp) Validate() error {
return nil
}
func (s *SourceOp) Marshal(constraints *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) {
func (s *SourceOp) Marshal(ctx context.Context, constraints *Constraints) (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error) {
if s.Cached(constraints) {
return s.Load()
}
if err := s.Validate(); err != nil {
return "", nil, nil, err
if err := s.Validate(ctx); err != nil {
return "", nil, nil, nil, err
}
if strings.HasPrefix(s.id, "local://") {
@ -74,10 +74,10 @@ func (s *SourceOp) Marshal(constraints *Constraints) (digest.Digest, []byte, *pb
dt, err := proto.Marshal()
if err != nil {
return "", nil, nil, err
return "", nil, nil, nil, err
}
s.Store(dt, md, constraints)
s.Store(dt, md, s.constraints.SourceLocations, constraints)
return s.Load()
}
@ -92,7 +92,8 @@ func (s *SourceOp) Inputs() []Output {
func Image(ref string, opts ...ImageOption) State {
r, err := reference.ParseNormalizedNamed(ref)
if err == nil {
ref = reference.TagNameOnly(r).String()
r = reference.TagNameOnly(r)
ref = r.String()
}
var info ImageInfo
for _, opt := range opts {
@ -116,21 +117,35 @@ func Image(ref string, opts ...ImageOption) State {
src := NewSource("docker-image://"+ref, attrs, info.Constraints) // controversial
if err != nil {
src.err = err
}
if info.metaResolver != nil {
_, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref, ResolveImageConfigOpt{
Platform: info.Constraints.Platform,
ResolveMode: info.resolveMode.String(),
})
if err != nil {
src.err = err
} else {
st, err := NewState(src.Output()).WithImageConfig(dt)
if err == nil {
return st
}
src.err = err
} else if info.metaResolver != nil {
if _, ok := r.(reference.Digested); ok || !info.resolveDigest {
return NewState(src.Output()).Async(func(ctx context.Context, st State) (State, error) {
_, dt, err := info.metaResolver.ResolveImageConfig(ctx, ref, ResolveImageConfigOpt{
Platform: info.Constraints.Platform,
ResolveMode: info.resolveMode.String(),
})
if err != nil {
return State{}, err
}
return st.WithImageConfig(dt)
})
}
return Scratch().Async(func(ctx context.Context, _ State) (State, error) {
dgst, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref, ResolveImageConfigOpt{
Platform: info.Constraints.Platform,
ResolveMode: info.resolveMode.String(),
})
if err != nil {
return State{}, err
}
if dgst != "" {
r, err = reference.WithDigest(r, dgst)
if err != nil {
return State{}, err
}
}
return NewState(NewSource("docker-image://"+r.String(), attrs, info.Constraints).Output()).WithImageConfig(dt)
})
}
return NewState(src.Output())
}
@ -176,9 +191,10 @@ func (r ResolveMode) String() string {
type ImageInfo struct {
constraintsWrapper
metaResolver ImageMetaResolver
resolveMode ResolveMode
RecordType string
metaResolver ImageMetaResolver
resolveDigest bool
resolveMode ResolveMode
RecordType string
}
func Git(remote, ref string, opts ...GitOption) State {
@ -199,7 +215,10 @@ func Git(remote, ref string, opts ...GitOption) State {
id += "#" + ref
}
gi := &GitInfo{}
gi := &GitInfo{
AuthHeaderSecret: "GIT_AUTH_HEADER",
AuthTokenSecret: "GIT_AUTH_TOKEN",
}
for _, o := range opts {
o.SetGitOption(gi)
}
@ -212,6 +231,14 @@ func Git(remote, ref string, opts ...GitOption) State {
attrs[pb.AttrFullRemoteURL] = url
addCap(&gi.Constraints, pb.CapSourceGitFullURL)
}
if gi.AuthTokenSecret != "" {
attrs[pb.AttrAuthTokenSecret] = gi.AuthTokenSecret
addCap(&gi.Constraints, pb.CapSourceGitHTTPAuth)
}
if gi.AuthHeaderSecret != "" {
attrs[pb.AttrAuthHeaderSecret] = gi.AuthHeaderSecret
addCap(&gi.Constraints, pb.CapSourceGitHTTPAuth)
}
addCap(&gi.Constraints, pb.CapSourceGit)
@ -230,7 +257,9 @@ func (fn gitOptionFunc) SetGitOption(gi *GitInfo) {
type GitInfo struct {
constraintsWrapper
KeepGitDir bool
KeepGitDir bool
AuthTokenSecret string
AuthHeaderSecret string
}
func KeepGitDir() GitOption {
@ -239,6 +268,18 @@ func KeepGitDir() GitOption {
})
}
func AuthTokenSecret(v string) GitOption {
return gitOptionFunc(func(gi *GitInfo) {
gi.AuthTokenSecret = v
})
}
func AuthHeaderSecret(v string) GitOption {
return gitOptionFunc(func(gi *GitInfo) {
gi.AuthHeaderSecret = v
})
}
func Scratch() State {
return NewState(nil)
}

111
vendor/github.com/moby/buildkit/client/llb/sourcemap.go generated vendored Normal file
View File

@ -0,0 +1,111 @@
package llb
import (
"context"
"github.com/moby/buildkit/solver/pb"
"github.com/opencontainers/go-digest"
)
type SourceMap struct {
State *State
Definition *Definition
Filename string
Data []byte
}
func NewSourceMap(st *State, filename string, dt []byte) *SourceMap {
return &SourceMap{
State: st,
Filename: filename,
Data: dt,
}
}
func (s *SourceMap) Location(r []*pb.Range) ConstraintsOpt {
return constraintsOptFunc(func(c *Constraints) {
if s == nil {
return
}
c.SourceLocations = append(c.SourceLocations, &SourceLocation{
SourceMap: s,
Ranges: r,
})
})
}
type SourceLocation struct {
SourceMap *SourceMap
Ranges []*pb.Range
}
type sourceMapCollector struct {
maps []*SourceMap
index map[*SourceMap]int
locations map[digest.Digest][]*SourceLocation
}
func newSourceMapCollector() *sourceMapCollector {
return &sourceMapCollector{
index: map[*SourceMap]int{},
locations: map[digest.Digest][]*SourceLocation{},
}
}
func (smc *sourceMapCollector) Add(dgst digest.Digest, ls []*SourceLocation) {
for _, l := range ls {
idx, ok := smc.index[l.SourceMap]
if !ok {
idx = len(smc.maps)
smc.maps = append(smc.maps, l.SourceMap)
}
smc.index[l.SourceMap] = idx
}
smc.locations[dgst] = ls
}
func (smc *sourceMapCollector) Marshal(ctx context.Context, co ...ConstraintsOpt) (*pb.Source, error) {
s := &pb.Source{
Locations: make(map[string]*pb.Locations),
}
for _, m := range smc.maps {
def := m.Definition
if def == nil && m.State != nil {
var err error
def, err = m.State.Marshal(ctx, co...)
if err != nil {
return nil, err
}
m.Definition = def
}
info := &pb.SourceInfo{
Data: m.Data,
Filename: m.Filename,
}
if def != nil {
info.Definition = def.ToPB()
}
s.Infos = append(s.Infos, info)
}
for dgst, locs := range smc.locations {
pbLocs, ok := s.Locations[dgst.String()]
if !ok {
pbLocs = &pb.Locations{}
}
for _, loc := range locs {
pbLocs.Locations = append(pbLocs.Locations, &pb.Location{
SourceIndex: int32(smc.index[loc.SourceMap]),
Ranges: loc.Ranges,
})
}
s.Locations[dgst.String()] = pbLocs
}
return s, nil
}

View File

@ -18,13 +18,13 @@ import (
type StateOption func(State) State
type Output interface {
ToInput(*Constraints) (*pb.Input, error)
Vertex() Vertex
ToInput(context.Context, *Constraints) (*pb.Input, error)
Vertex(context.Context) Vertex
}
type Vertex interface {
Validate() error
Marshal(*Constraints) (digest.Digest, []byte, *pb.OpMetadata, error)
Validate(context.Context) error
Marshal(context.Context, *Constraints) (digest.Digest, []byte, *pb.OpMetadata, []*SourceLocation, error)
Output() Output
Inputs() []Output
}
@ -32,17 +32,18 @@ type Vertex interface {
func NewState(o Output) State {
s := State{
out: o,
ctx: context.Background(),
}
s = dir("/")(s)
}.Dir("/")
s = s.ensurePlatform()
return s
}
type State struct {
out Output
ctx context.Context
opts []ConstraintsOpt
out Output
prev *State
key interface{}
value func(context.Context) (interface{}, error)
opts []ConstraintsOpt
async *asyncState
}
func (s State) ensurePlatform() State {
@ -57,14 +58,48 @@ func (s State) ensurePlatform() State {
}
func (s State) WithValue(k, v interface{}) State {
return s.withValue(k, func(context.Context) (interface{}, error) {
return v, nil
})
}
func (s State) withValue(k interface{}, v func(context.Context) (interface{}, error)) State {
return State{
out: s.out,
ctx: context.WithValue(s.ctx, k, v),
out: s.Output(),
prev: &s, // doesn't need to be original pointer
key: k,
value: v,
}
}
func (s State) Value(k interface{}) interface{} {
return s.ctx.Value(k)
func (s State) Value(ctx context.Context, k interface{}) (interface{}, error) {
return s.getValue(k)(ctx)
}
func (s State) getValue(k interface{}) func(context.Context) (interface{}, error) {
if s.key == k {
return s.value
}
if s.async != nil {
return func(ctx context.Context) (interface{}, error) {
err := s.async.Do(ctx)
if err != nil {
return nil, err
}
return s.async.target.getValue(k)(ctx)
}
}
if s.prev == nil {
return nilValue
}
return s.prev.getValue(k)
}
func (s State) Async(f func(context.Context, State) (State, error)) State {
s2 := State{
async: &asyncState{f: f, prev: s},
}
return s2
}
func (s State) SetMarshalDefaults(co ...ConstraintsOpt) State {
@ -72,11 +107,11 @@ func (s State) SetMarshalDefaults(co ...ConstraintsOpt) State {
return s
}
func (s State) Marshal(co ...ConstraintsOpt) (*Definition, error) {
func (s State) Marshal(ctx context.Context, co ...ConstraintsOpt) (*Definition, error) {
def := &Definition{
Metadata: make(map[digest.Digest]pb.OpMetadata, 0),
}
if s.Output() == nil {
if s.Output() == nil || s.Output().Vertex(ctx) == nil {
return def, nil
}
@ -89,11 +124,13 @@ func (s State) Marshal(co ...ConstraintsOpt) (*Definition, error) {
o.SetConstraintsOption(c)
}
def, err := marshal(s.Output().Vertex(), def, map[digest.Digest]struct{}{}, map[Vertex]struct{}{}, c)
smc := newSourceMapCollector()
def, err := marshal(ctx, s.Output().Vertex(ctx), def, smc, map[digest.Digest]struct{}{}, map[Vertex]struct{}{}, c)
if err != nil {
return def, err
}
inp, err := s.Output().ToInput(c)
inp, err := s.Output().ToInput(ctx, c)
if err != nil {
return def, err
}
@ -124,23 +161,28 @@ func (s State) Marshal(co ...ConstraintsOpt) (*Definition, error) {
}
def.Metadata[dgst] = md
sm, err := smc.Marshal(ctx, co...)
if err != nil {
return nil, err
}
def.Source = sm
return def, nil
}
func marshal(v Vertex, def *Definition, cache map[digest.Digest]struct{}, vertexCache map[Vertex]struct{}, c *Constraints) (*Definition, error) {
func marshal(ctx context.Context, v Vertex, def *Definition, s *sourceMapCollector, cache map[digest.Digest]struct{}, vertexCache map[Vertex]struct{}, c *Constraints) (*Definition, error) {
if _, ok := vertexCache[v]; ok {
return def, nil
}
for _, inp := range v.Inputs() {
var err error
def, err = marshal(inp.Vertex(), def, cache, vertexCache, c)
def, err = marshal(ctx, inp.Vertex(ctx), def, s, cache, vertexCache, c)
if err != nil {
return def, err
}
}
dgst, dt, opMeta, err := v.Marshal(c)
dgst, dt, opMeta, sls, err := v.Marshal(ctx, c)
if err != nil {
return def, err
}
@ -151,23 +193,28 @@ func marshal(v Vertex, def *Definition, cache map[digest.Digest]struct{}, vertex
if _, ok := cache[dgst]; ok {
return def, nil
}
s.Add(dgst, sls)
def.Def = append(def.Def, dt)
cache[dgst] = struct{}{}
return def, nil
}
func (s State) Validate() error {
return s.Output().Vertex().Validate()
func (s State) Validate(ctx context.Context) error {
return s.Output().Vertex(ctx).Validate(ctx)
}
func (s State) Output() Output {
if s.async != nil {
return s.async.Output()
}
return s.out
}
func (s State) WithOutput(o Output) State {
prev := s
s = State{
out: o,
ctx: s.ctx,
out: o,
prev: &prev,
}
s = s.ensurePlatform()
return s
@ -200,24 +247,10 @@ func (s State) WithImageConfig(c []byte) (State, error) {
func (s State) Run(ro ...RunOption) ExecState {
ei := &ExecInfo{State: s}
if p := s.GetPlatform(); p != nil {
ei.Constraints.Platform = p
}
for _, o := range ro {
o.SetRunOption(ei)
}
meta := Meta{
Args: getArgs(ei.State),
Cwd: getDir(ei.State),
Env: getEnv(ei.State),
User: getUser(ei.State),
ProxyEnv: ei.ProxyEnv,
ExtraHosts: getExtraHosts(ei.State),
Network: getNetwork(ei.State),
Security: getSecurity(ei.State),
}
exec := NewExecOp(s.Output(), meta, ei.ReadonlyRootFS, ei.Constraints)
exec := NewExecOp(ei.State, ei.ProxyEnv, ei.ReadonlyRootFS, ei.Constraints)
for _, m := range ei.Mounts {
exec.AddMount(m.Target, m.Source, m.Opts...)
}
@ -240,65 +273,74 @@ func (s State) File(a *FileAction, opts ...ConstraintsOpt) State {
}
func (s State) AddEnv(key, value string) State {
return addEnvf(key, value, false)(s)
return AddEnv(key, value)(s)
}
func (s State) AddEnvf(key, value string, v ...interface{}) State {
return addEnvf(key, value, true, v...)(s)
return AddEnvf(key, value, v...)(s)
}
func (s State) Dir(str string) State {
return dirf(str, false)(s)
return Dir(str)(s)
}
func (s State) Dirf(str string, v ...interface{}) State {
return dirf(str, true, v...)(s)
return Dirf(str, v...)(s)
}
func (s State) GetEnv(key string) (string, bool) {
return getEnv(s).Get(key)
func (s State) GetEnv(ctx context.Context, key string) (string, bool, error) {
env, err := getEnv(s)(ctx)
if err != nil {
return "", false, err
}
v, ok := env.Get(key)
return v, ok, nil
}
func (s State) Env() []string {
return getEnv(s).ToArray()
func (s State) Env(ctx context.Context) ([]string, error) {
env, err := getEnv(s)(ctx)
if err != nil {
return nil, err
}
return env.ToArray(), nil
}
func (s State) GetDir() string {
return getDir(s)
func (s State) GetDir(ctx context.Context) (string, error) {
return getDir(s)(ctx)
}
func (s State) GetArgs() []string {
return getArgs(s)
func (s State) GetArgs(ctx context.Context) ([]string, error) {
return getArgs(s)(ctx)
}
func (s State) Reset(s2 State) State {
return reset(s2)(s)
return Reset(s2)(s)
}
func (s State) User(v string) State {
return user(v)(s)
return User(v)(s)
}
func (s State) Platform(p specs.Platform) State {
return platform(p)(s)
}
func (s State) GetPlatform() *specs.Platform {
return getPlatform(s)
func (s State) GetPlatform(ctx context.Context) (*specs.Platform, error) {
return getPlatform(s)(ctx)
}
func (s State) Network(n pb.NetMode) State {
return network(n)(s)
return Network(n)(s)
}
func (s State) GetNetwork() pb.NetMode {
return getNetwork(s)
func (s State) GetNetwork(ctx context.Context) (pb.NetMode, error) {
return getNetwork(s)(ctx)
}
func (s State) Security(n pb.SecurityMode) State {
return security(n)(s)
return Security(n)(s)
}
func (s State) GetSecurity() pb.SecurityMode {
return getSecurity(s)
func (s State) GetSecurity(ctx context.Context) (pb.SecurityMode, error) {
return getSecurity(s)(ctx)
}
func (s State) With(so ...StateOption) State {
@ -321,7 +363,7 @@ type output struct {
platform *specs.Platform
}
func (o *output) ToInput(c *Constraints) (*pb.Input, error) {
func (o *output) ToInput(ctx context.Context, c *Constraints) (*pb.Input, error) {
if o.err != nil {
return nil, o.err
}
@ -333,14 +375,14 @@ func (o *output) ToInput(c *Constraints) (*pb.Input, error) {
return nil, err
}
}
dgst, _, _, err := o.vertex.Marshal(c)
dgst, _, _, _, err := o.vertex.Marshal(ctx, c)
if err != nil {
return nil, err
}
return &pb.Input{Digest: dgst, Index: index}, nil
}
func (o *output) Vertex() Vertex {
func (o *output) Vertex(context.Context) Vertex {
return o.vertex
}
@ -480,6 +522,7 @@ type Constraints struct {
Metadata pb.OpMetadata
LocalUniqueID string
Caps *apicaps.CapSet
SourceLocations []*SourceLocation
}
func Platform(p specs.Platform) ConstraintsOpt {
@ -513,3 +556,7 @@ func Require(filters ...string) ConstraintsOpt {
}
})
}
func nilValue(context.Context) (interface{}, error) {
return nil, nil
}

View File

@ -6,7 +6,7 @@ import (
"os"
"github.com/gofrs/flock"
"github.com/opencontainers/image-spec/specs-go/v1"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)

View File

@ -115,6 +115,12 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
}
var ex ExportEntry
if len(opt.Exports) > 1 {
return nil, errors.New("currently only single Exports can be specified")
}
if len(opt.Exports) == 1 {
ex = opt.Exports[0]
}
if !opt.SessionPreInitialized {
if len(syncedDirs) > 0 {
@ -125,13 +131,6 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
s.Allow(a)
}
if len(opt.Exports) > 1 {
return nil, errors.New("currently only single Exports can be specified")
}
if len(opt.Exports) == 1 {
ex = opt.Exports[0]
}
switch ex.Type {
case ExporterLocal:
if ex.Output != nil {
@ -192,7 +191,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG
frontendInputs := make(map[string]*pb.Definition)
for key, st := range opt.FrontendInputs {
def, err := st.Marshal()
def, err := st.Marshal(ctx)
if err != nil {
return err
}