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

@ -1,3 +1,3 @@
package moby_buildkit_v1
package moby_buildkit_v1 //nolint:golint
//go:generate protoc -I=. -I=../../../vendor/ -I=../../../../../../ --gogo_out=plugins=grpc:. control.proto

View File

@ -1,3 +1,3 @@
package moby_buildkit_v1_types
package moby_buildkit_v1_types //nolint:golint
//go:generate protoc -I=. -I=../../vendor/ -I=../../../../../ --gogo_out=plugins=grpc:. worker.proto

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
}

View File

@ -10,11 +10,14 @@ import (
"time"
"github.com/gogo/googleapis/google/rpc"
gogotypes "github.com/gogo/protobuf/types"
"github.com/golang/protobuf/ptypes/any"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/frontend/gateway/client"
pb "github.com/moby/buildkit/frontend/gateway/pb"
opspb "github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/apicaps"
"github.com/moby/buildkit/util/grpcerrors"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
fstypes "github.com/tonistiigi/fsutil/types"
@ -29,7 +32,7 @@ type GrpcClient interface {
}
func New(ctx context.Context, opts map[string]string, session, product string, c pb.LLBBridgeClient, w []client.WorkerInfo) (GrpcClient, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
defer cancel()
resp, err := c.Ping(ctx, &pb.PingRequest{})
if err != nil {
@ -150,12 +153,12 @@ func (c *grpcClient) Run(ctx context.Context, f client.BuildFunc) (retError erro
}
}
if retError != nil {
st, _ := status.FromError(errors.Cause(retError))
st, _ := status.FromError(grpcerrors.ToGRPC(retError))
stp := st.Proto()
req.Error = &rpc.Status{
Code: stp.Code,
Message: stp.Message,
// Details: stp.Details,
Details: convertToGogoAny(stp.Details),
}
}
if _, err := c.client.Return(ctx, req); err != nil && retError == nil {
@ -424,10 +427,9 @@ func (c *grpcClient) Inputs(ctx context.Context) (map[string]llb.State, error) {
}
type reference struct {
c *grpcClient
id string
def *opspb.Definition
output llb.Output
c *grpcClient
id string
def *opspb.Definition
}
func newReference(c *grpcClient, ref *pb.Ref) (*reference, error) {
@ -499,11 +501,11 @@ func (r *reference) StatFile(ctx context.Context, req client.StatRequest) (*fsty
}
func grpcClientConn(ctx context.Context) (context.Context, *grpc.ClientConn, error) {
dialOpt := grpc.WithDialer(func(addr string, d time.Duration) (net.Conn, error) {
dialOpt := grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
return stdioConn(), nil
})
cc, err := grpc.DialContext(ctx, "", dialOpt, grpc.WithInsecure())
cc, err := grpc.DialContext(ctx, "", dialOpt, grpc.WithInsecure(), grpc.WithUnaryInterceptor(grpcerrors.UnaryClientInterceptor), grpc.WithStreamInterceptor(grpcerrors.StreamClientInterceptor))
if err != nil {
return nil, nil, errors.Wrap(err, "failed to create grpc client")
}
@ -589,3 +591,11 @@ func workers() []client.WorkerInfo {
func product() string {
return os.Getenv("BUILDKIT_EXPORTEDPRODUCT")
}
func convertToGogoAny(in []*any.Any) []*gogotypes.Any {
out := make([]*gogotypes.Any, len(in))
for i := range in {
out[i] = &gogotypes.Any{TypeUrl: in[i].TypeUrl, Value: in[i].Value}
}
return out
}

View File

@ -1,4 +1,4 @@
package moby_buildkit_v1_frontend
package moby_buildkit_v1_frontend //nolint:golint
import "github.com/moby/buildkit/util/apicaps"

View File

@ -32,7 +32,7 @@ service LLBBridge {
message Result {
oneof result {
// Deprecated non-array refs.
// Deprecated non-array refs.
string refDeprecated = 1;
RefMapDeprecated refsDeprecated = 2;
@ -67,7 +67,7 @@ message InputsRequest {
}
message InputsResponse {
map<string, pb.Definition> Definitions = 1;
map<string, pb.Definition> Definitions = 1;
}
message ResolveImageConfigRequest {
@ -87,9 +87,9 @@ message SolveRequest {
string Frontend = 2;
map<string, string> FrontendOpt = 3;
// ImportCacheRefsDeprecated is deprecated in favor or the new Imports since BuildKit v0.4.0.
// When ImportCacheRefsDeprecated is set, the solver appends
// {.Type = "registry", .Attrs = {"ref": importCacheRef}}
// for each of the ImportCacheRefs entry to CacheImports for compatibility. (planned to be removed)
// When ImportCacheRefsDeprecated is set, the solver appends
// {.Type = "registry", .Attrs = {"ref": importCacheRef}}
// for each of the ImportCacheRefs entry to CacheImports for compatibility. (planned to be removed)
repeated string ImportCacheRefsDeprecated = 4;
bool allowResultReturn = 5;
bool allowResultArrayRef = 6;

View File

@ -1,3 +1,3 @@
package moby_buildkit_v1_frontend
package moby_buildkit_v1_frontend //nolint:golint
//go:generate protoc -I=. -I=../../../vendor/ -I=../../../../../../ --gogo_out=plugins=grpc:. gateway.proto

View File

@ -4,24 +4,33 @@ import (
"context"
"github.com/moby/buildkit/session"
"github.com/pkg/errors"
"github.com/moby/buildkit/util/grpcerrors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
func CredentialsFunc(ctx context.Context, c session.Caller) func(string) (string, string, error) {
return func(host string) (string, string, error) {
client := NewAuthClient(c.Conn())
func CredentialsFunc(sm *session.Manager, g session.Group) func(string) (session, username, secret string, err error) {
return func(host string) (string, string, string, error) {
var sessionID, user, secret string
err := sm.Any(context.TODO(), g, func(ctx context.Context, id string, c session.Caller) error {
client := NewAuthClient(c.Conn())
resp, err := client.Credentials(ctx, &CredentialsRequest{
Host: host,
resp, err := client.Credentials(ctx, &CredentialsRequest{
Host: host,
})
if err != nil {
if grpcerrors.Code(err) == codes.Unimplemented {
return nil
}
return err
}
sessionID = id
user = resp.Username
secret = resp.Secret
return nil
})
if err != nil {
if st, ok := status.FromError(errors.Cause(err)); ok && st.Code() == codes.Unimplemented {
return "", "", nil
}
return "", "", errors.WithStack(err)
return "", "", "", err
}
return resp.Username, resp.Secret, nil
return sessionID, user, secret, nil
}
}

View File

@ -1,22 +0,0 @@
package session
import "context"
type contextKeyT string
var contextKey = contextKeyT("buildkit/session-id")
func NewContext(ctx context.Context, id string) context.Context {
if id != "" {
return context.WithValue(ctx, contextKey, id)
}
return ctx
}
func FromContext(ctx context.Context) string {
v := ctx.Value(contextKey)
if v == nil {
return ""
}
return v.(string)
}

View File

@ -2,6 +2,7 @@ package filesync
import (
"bufio"
"context"
io "io"
"os"
"time"
@ -13,7 +14,13 @@ import (
"google.golang.org/grpc"
)
func sendDiffCopy(stream grpc.Stream, fs fsutil.FS, progress progressCb) error {
type Stream interface {
Context() context.Context
SendMsg(m interface{}) error
RecvMsg(m interface{}) error
}
func sendDiffCopy(stream Stream, fs fsutil.FS, progress progressCb) error {
return errors.WithStack(fsutil.Send(stream.Context(), stream, fs, progress))
}
@ -41,7 +48,7 @@ type streamWriterCloser struct {
func (wc *streamWriterCloser) Write(dt []byte) (int, error) {
if err := wc.ClientStream.SendMsg(&BytesMessage{Data: dt}); err != nil {
// SendMsg return EOF on remote errors
if errors.Cause(err) == io.EOF {
if errors.Is(err, io.EOF) {
if err := errors.WithStack(wc.ClientStream.RecvMsg(struct{}{})); err != nil {
return 0, err
}
@ -63,7 +70,7 @@ func (wc *streamWriterCloser) Close() error {
return nil
}
func recvDiffCopy(ds grpc.Stream, dest string, cu CacheUpdater, progress progressCb, filter func(string, *fstypes.Stat) bool) error {
func recvDiffCopy(ds grpc.ClientStream, dest string, cu CacheUpdater, progress progressCb, filter func(string, *fstypes.Stat) bool) error {
st := time.Now()
defer func() {
logrus.Debugf("diffcopy took: %v", time.Since(st))
@ -83,7 +90,7 @@ func recvDiffCopy(ds grpc.Stream, dest string, cu CacheUpdater, progress progres
}))
}
func syncTargetDiffCopy(ds grpc.Stream, dest string) error {
func syncTargetDiffCopy(ds grpc.ServerStream, dest string) error {
if err := os.MkdirAll(dest, 0700); err != nil {
return errors.Wrapf(err, "failed to create synctarget dest dir %s", dest)
}
@ -101,11 +108,11 @@ func syncTargetDiffCopy(ds grpc.Stream, dest string) error {
}))
}
func writeTargetFile(ds grpc.Stream, wc io.WriteCloser) error {
func writeTargetFile(ds grpc.ServerStream, wc io.WriteCloser) error {
for {
bm := BytesMessage{}
if err := ds.RecvMsg(&bm); err != nil {
if errors.Cause(err) == io.EOF {
if errors.Is(err, io.EOF) {
return nil
}
return errors.WithStack(err)

View File

@ -129,8 +129,8 @@ type progressCb func(int, bool)
type protocol struct {
name string
sendFn func(stream grpc.Stream, fs fsutil.FS, progress progressCb) error
recvFn func(stream grpc.Stream, destDir string, cu CacheUpdater, progress progressCb, mapFunc func(string, *fstypes.Stat) bool) error
sendFn func(stream Stream, fs fsutil.FS, progress progressCb) error
recvFn func(stream grpc.ClientStream, destDir string, cu CacheUpdater, progress progressCb, mapFunc func(string, *fstypes.Stat) bool) error
}
func isProtoSupported(p string) bool {

88
vendor/github.com/moby/buildkit/session/group.go generated vendored Normal file
View File

@ -0,0 +1,88 @@
package session
import (
"context"
"time"
"github.com/pkg/errors"
)
type Group interface {
SessionIterator() Iterator
}
type Iterator interface {
NextSession() string
}
func NewGroup(ids ...string) Group {
return &group{ids: ids}
}
type group struct {
ids []string
}
func (g *group) SessionIterator() Iterator {
return &group{ids: g.ids}
}
func (g *group) NextSession() string {
if len(g.ids) == 0 {
return ""
}
v := g.ids[0]
g.ids = g.ids[1:]
return v
}
func AllSessionIDs(g Group) (out []string) {
if g == nil {
return nil
}
it := g.SessionIterator()
if it == nil {
return nil
}
for {
v := it.NextSession()
if v == "" {
return
}
out = append(out, v)
}
}
func (sm *Manager) Any(ctx context.Context, g Group, f func(context.Context, string, Caller) error) error {
if g == nil {
return nil
}
iter := g.SessionIterator()
if iter == nil {
return nil
}
var lastErr error
for {
id := iter.NextSession()
if id == "" {
if lastErr != nil {
return lastErr
}
return errors.Errorf("no active sessions")
}
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
c, err := sm.Get(timeoutCtx, id, false)
if err != nil {
lastErr = err
continue
}
if err := f(ctx, id, c); err != nil {
lastErr = err
continue
}
return nil
}
}

View File

@ -6,7 +6,9 @@ import (
"sync/atomic"
"time"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
"github.com/moby/buildkit/util/grpcerrors"
opentracing "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@ -25,8 +27,11 @@ func serve(ctx context.Context, grpcServer *grpc.Server, conn net.Conn) {
}
func grpcClientConn(ctx context.Context, conn net.Conn) (context.Context, *grpc.ClientConn, error) {
var unary []grpc.UnaryClientInterceptor
var stream []grpc.StreamClientInterceptor
var dialCount int64
dialer := grpc.WithDialer(func(addr string, d time.Duration) (net.Conn, error) {
dialer := grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
if c := atomic.AddInt64(&dialCount, 1); c > 1 {
return nil, errors.Errorf("only one connection allowed")
}
@ -40,10 +45,23 @@ func grpcClientConn(ctx context.Context, conn net.Conn) (context.Context, *grpc.
if span := opentracing.SpanFromContext(ctx); span != nil {
tracer := span.Tracer()
dialOpts = append(dialOpts,
grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(tracer, traceFilter())),
grpc.WithStreamInterceptor(otgrpc.OpenTracingStreamClientInterceptor(tracer, traceFilter())),
)
unary = append(unary, otgrpc.OpenTracingClientInterceptor(tracer, traceFilter()))
stream = append(stream, otgrpc.OpenTracingStreamClientInterceptor(tracer, traceFilter()))
}
unary = append(unary, grpcerrors.UnaryClientInterceptor)
stream = append(stream, grpcerrors.StreamClientInterceptor)
if len(unary) == 1 {
dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(unary[0]))
} else if len(unary) > 1 {
dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unary...)))
}
if len(stream) == 1 {
dialOpts = append(dialOpts, grpc.WithStreamInterceptor(stream[0]))
} else if len(stream) > 1 {
dialOpts = append(dialOpts, grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(stream...)))
}
cc, err := grpc.DialContext(ctx, "", dialOpts...)

View File

@ -33,21 +33,26 @@ func Dialer(api controlapi.ControlClient) session.Dialer {
}
}
func streamToConn(stream grpc.Stream) (net.Conn, <-chan struct{}) {
type stream interface {
Context() context.Context
SendMsg(m interface{}) error
RecvMsg(m interface{}) error
}
func streamToConn(stream stream) (net.Conn, <-chan struct{}) {
closeCh := make(chan struct{})
c := &conn{stream: stream, buf: make([]byte, 32*1<<10), closeCh: closeCh}
return c, closeCh
}
type conn struct {
stream grpc.Stream
stream stream
buf []byte
lastBuf []byte
closedOnce sync.Once
readMu sync.Mutex
writeMu sync.Mutex
err error
closeCh chan struct{}
}

View File

@ -149,7 +149,7 @@ func (sm *Manager) handleConn(ctx context.Context, conn net.Conn, opts map[strin
}
// Get returns a session by ID
func (sm *Manager) Get(ctx context.Context, id string) (Caller, error) {
func (sm *Manager) Get(ctx context.Context, id string, noWait bool) (Caller, error) {
// session prefix is used to identify vertexes with different contexts so
// they would not collide, but for lookup we don't need the prefix
if p := strings.SplitN(id, ":", 2); len(p) == 2 && len(p[1]) > 0 {
@ -180,7 +180,7 @@ func (sm *Manager) Get(ctx context.Context, id string) (Caller, error) {
}
var ok bool
c, ok = sm.sessions[id]
if !ok || c.closed() {
if (!ok || c.closed()) && !noWait {
sm.updateCondition.Wait()
continue
}
@ -188,6 +188,10 @@ func (sm *Manager) Get(ctx context.Context, id string) (Caller, error) {
break
}
if c == nil {
return nil, nil
}
return c, nil
}

View File

@ -4,9 +4,9 @@ import (
"context"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/pkg/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type SecretStore interface {
@ -21,10 +21,10 @@ func GetSecret(ctx context.Context, c session.Caller, id string) ([]byte, error)
ID: id,
})
if err != nil {
if st, ok := status.FromError(errors.Cause(err)); ok && (st.Code() == codes.Unimplemented || st.Code() == codes.NotFound) {
if code := grpcerrors.Code(err); code == codes.Unimplemented || code == codes.NotFound {
return nil, errors.Wrapf(ErrNotFound, "secret %s not found", id)
}
return nil, errors.WithStack(err)
return nil, err
}
return resp.Data, nil
}

View File

@ -1,54 +0,0 @@
package secretsprovider
import (
"context"
"io/ioutil"
"os"
"github.com/moby/buildkit/session/secrets"
"github.com/pkg/errors"
)
type FileSource struct {
ID string
FilePath string
}
func NewFileStore(files []FileSource) (secrets.SecretStore, error) {
m := map[string]FileSource{}
for _, f := range files {
if f.ID == "" {
return nil, errors.Errorf("secret missing ID")
}
if f.FilePath == "" {
f.FilePath = f.ID
}
fi, err := os.Stat(f.FilePath)
if err != nil {
return nil, errors.Wrapf(err, "failed to stat %s", f.FilePath)
}
if fi.Size() > MaxSecretSize {
return nil, errors.Errorf("secret %s too big. max size 500KB", f.ID)
}
m[f.ID] = f
}
return &fileStore{
m: m,
}, nil
}
type fileStore struct {
m map[string]FileSource
}
func (fs *fileStore) GetSecret(ctx context.Context, id string) ([]byte, error) {
v, ok := fs.m[id]
if !ok {
return nil, errors.WithStack(secrets.ErrNotFound)
}
dt, err := ioutil.ReadFile(v.FilePath)
if err != nil {
return nil, err
}
return dt, nil
}

View File

@ -31,7 +31,7 @@ func (sp *secretProvider) Register(server *grpc.Server) {
func (sp *secretProvider) GetSecret(ctx context.Context, req *secrets.GetSecretRequest) (*secrets.GetSecretResponse, error) {
dt, err := sp.store.GetSecret(ctx, req.ID)
if err != nil {
if errors.Cause(err) == secrets.ErrNotFound {
if errors.Is(err, secrets.ErrNotFound) {
return nil, status.Errorf(codes.NotFound, err.Error())
}
return nil, err

View File

@ -0,0 +1,65 @@
package secretsprovider
import (
"context"
"io/ioutil"
"os"
"github.com/moby/buildkit/session/secrets"
"github.com/pkg/errors"
"github.com/tonistiigi/units"
)
type Source struct {
ID string
FilePath string
Env string
}
func NewStore(files []Source) (secrets.SecretStore, error) {
m := map[string]Source{}
for _, f := range files {
if f.ID == "" {
return nil, errors.Errorf("secret missing ID")
}
if f.Env == "" && f.FilePath == "" {
if _, ok := os.LookupEnv(f.ID); ok {
f.Env = f.ID
} else {
f.FilePath = f.ID
}
}
if f.FilePath != "" {
fi, err := os.Stat(f.FilePath)
if err != nil {
return nil, errors.Wrapf(err, "failed to stat %s", f.FilePath)
}
if fi.Size() > MaxSecretSize {
return nil, errors.Errorf("secret %s too big. max size %#.f", f.ID, MaxSecretSize*units.B)
}
}
m[f.ID] = f
}
return &fileStore{
m: m,
}, nil
}
type fileStore struct {
m map[string]Source
}
func (fs *fileStore) GetSecret(ctx context.Context, id string) ([]byte, error) {
v, ok := fs.m[id]
if !ok {
return nil, errors.WithStack(secrets.ErrNotFound)
}
if v.Env != "" {
return []byte(os.Getenv(v.Env)), nil
}
dt, err := ioutil.ReadFile(v.FilePath)
if err != nil {
return nil, err
}
return dt, nil
}

View File

@ -5,8 +5,10 @@ import (
"net"
"strings"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/util/grpcerrors"
opentracing "github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"google.golang.org/grpc"
@ -45,13 +47,29 @@ type Session struct {
func NewSession(ctx context.Context, name, sharedKey string) (*Session, error) {
id := identity.NewID()
var unary []grpc.UnaryServerInterceptor
var stream []grpc.StreamServerInterceptor
serverOpts := []grpc.ServerOption{}
if span := opentracing.SpanFromContext(ctx); span != nil {
tracer := span.Tracer()
serverOpts = []grpc.ServerOption{
grpc.StreamInterceptor(otgrpc.OpenTracingStreamServerInterceptor(span.Tracer(), traceFilter())),
grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer, traceFilter())),
}
unary = append(unary, otgrpc.OpenTracingServerInterceptor(tracer, traceFilter()))
stream = append(stream, otgrpc.OpenTracingStreamServerInterceptor(span.Tracer(), traceFilter()))
}
unary = append(unary, grpcerrors.UnaryServerInterceptor)
stream = append(stream, grpcerrors.StreamServerInterceptor)
if len(unary) == 1 {
serverOpts = append(serverOpts, grpc.UnaryInterceptor(unary[0]))
} else if len(unary) > 1 {
serverOpts = append(serverOpts, grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(unary...)))
}
if len(stream) == 1 {
serverOpts = append(serverOpts, grpc.StreamInterceptor(stream[0]))
} else if len(stream) > 1 {
serverOpts = append(serverOpts, grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(stream...)))
}
s := &Session{

View File

@ -6,10 +6,14 @@ import (
"github.com/pkg/errors"
context "golang.org/x/net/context"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
)
func Copy(ctx context.Context, conn io.ReadWriteCloser, stream grpc.Stream, closeStream func() error) error {
type Stream interface {
SendMsg(m interface{}) error
RecvMsg(m interface{}) error
}
func Copy(ctx context.Context, conn io.ReadWriteCloser, stream Stream, closeStream func() error) error {
g, ctx := errgroup.WithContext(ctx)
g.Go(func() (retErr error) {

View File

@ -0,0 +1,129 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: errdefs.proto
package errdefs
import (
fmt "fmt"
proto "github.com/gogo/protobuf/proto"
pb "github.com/moby/buildkit/solver/pb"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type Vertex struct {
Digest string `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Vertex) Reset() { *m = Vertex{} }
func (m *Vertex) String() string { return proto.CompactTextString(m) }
func (*Vertex) ProtoMessage() {}
func (*Vertex) Descriptor() ([]byte, []int) {
return fileDescriptor_689dc58a5060aff5, []int{0}
}
func (m *Vertex) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Vertex.Unmarshal(m, b)
}
func (m *Vertex) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Vertex.Marshal(b, m, deterministic)
}
func (m *Vertex) XXX_Merge(src proto.Message) {
xxx_messageInfo_Vertex.Merge(m, src)
}
func (m *Vertex) XXX_Size() int {
return xxx_messageInfo_Vertex.Size(m)
}
func (m *Vertex) XXX_DiscardUnknown() {
xxx_messageInfo_Vertex.DiscardUnknown(m)
}
var xxx_messageInfo_Vertex proto.InternalMessageInfo
func (m *Vertex) GetDigest() string {
if m != nil {
return m.Digest
}
return ""
}
type Source struct {
Info *pb.SourceInfo `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"`
Ranges []*pb.Range `protobuf:"bytes,2,rep,name=ranges,proto3" json:"ranges,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Source) Reset() { *m = Source{} }
func (m *Source) String() string { return proto.CompactTextString(m) }
func (*Source) ProtoMessage() {}
func (*Source) Descriptor() ([]byte, []int) {
return fileDescriptor_689dc58a5060aff5, []int{1}
}
func (m *Source) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Source.Unmarshal(m, b)
}
func (m *Source) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Source.Marshal(b, m, deterministic)
}
func (m *Source) XXX_Merge(src proto.Message) {
xxx_messageInfo_Source.Merge(m, src)
}
func (m *Source) XXX_Size() int {
return xxx_messageInfo_Source.Size(m)
}
func (m *Source) XXX_DiscardUnknown() {
xxx_messageInfo_Source.DiscardUnknown(m)
}
var xxx_messageInfo_Source proto.InternalMessageInfo
func (m *Source) GetInfo() *pb.SourceInfo {
if m != nil {
return m.Info
}
return nil
}
func (m *Source) GetRanges() []*pb.Range {
if m != nil {
return m.Ranges
}
return nil
}
func init() {
proto.RegisterType((*Vertex)(nil), "errdefs.Vertex")
proto.RegisterType((*Source)(nil), "errdefs.Source")
}
func init() { proto.RegisterFile("errdefs.proto", fileDescriptor_689dc58a5060aff5) }
var fileDescriptor_689dc58a5060aff5 = []byte{
// 177 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x2c, 0xcd, 0xc1, 0x8a, 0x83, 0x30,
0x10, 0x80, 0x61, 0xdc, 0x5d, 0xb2, 0x18, 0xd9, 0x3d, 0xe4, 0x50, 0xa4, 0x27, 0xeb, 0xc9, 0x43,
0x49, 0xc0, 0x3e, 0x45, 0x4f, 0x85, 0x14, 0x7a, 0x6f, 0x74, 0xb4, 0xa1, 0xea, 0x84, 0x49, 0x2c,
0xed, 0xdb, 0x17, 0x6d, 0x8e, 0xff, 0x7c, 0x33, 0x0c, 0xff, 0x03, 0xa2, 0x16, 0x3a, 0x2f, 0x1d,
0x61, 0x40, 0xf1, 0x1b, 0x73, 0xbb, 0xef, 0x6d, 0xb8, 0xcd, 0x46, 0x36, 0x38, 0xaa, 0x11, 0xcd,
0x4b, 0x99, 0xd9, 0x0e, 0xed, 0xdd, 0x06, 0xe5, 0x71, 0x78, 0x00, 0x29, 0x67, 0x14, 0xba, 0x78,
0x56, 0x16, 0x9c, 0x5d, 0x80, 0x02, 0x3c, 0xc5, 0x86, 0xb3, 0xd6, 0xf6, 0xe0, 0x43, 0x9e, 0x14,
0x49, 0x95, 0xea, 0x58, 0xe5, 0x89, 0xb3, 0x33, 0xce, 0xd4, 0x80, 0x28, 0xf9, 0x8f, 0x9d, 0x3a,
0x5c, 0x3d, 0xab, 0xff, 0xa5, 0x33, 0xf2, 0x23, 0xc7, 0xa9, 0x43, 0xbd, 0x9a, 0xd8, 0x71, 0x46,
0xd7, 0xa9, 0x07, 0x9f, 0x7f, 0x15, 0xdf, 0x55, 0x56, 0xa7, 0xcb, 0x96, 0x5e, 0x26, 0x3a, 0x82,
0x61, 0xeb, 0xe7, 0xc3, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x93, 0xb5, 0x8b, 0x2a, 0xc1, 0x00, 0x00,
0x00,
}

View File

@ -0,0 +1,14 @@
syntax = "proto3";
package errdefs;
import "github.com/moby/buildkit/solver/pb/ops.proto";
message Vertex {
string digest = 1;
}
message Source {
pb.SourceInfo info = 1;
repeated pb.Range ranges = 2;
}

View File

@ -0,0 +1,3 @@
package errdefs
//go:generate protoc -I=. -I=../../vendor/ -I=../../../../../ --gogo_out=. errdefs.proto

View File

@ -0,0 +1,128 @@
package errdefs
import (
"fmt"
"io"
"strings"
pb "github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/pkg/errors"
)
func WithSource(err error, src Source) error {
if err == nil {
return nil
}
return &ErrorSource{Source: src, error: err}
}
type ErrorSource struct {
Source
error
}
func (e *ErrorSource) Unwrap() error {
return e.error
}
func (e *ErrorSource) ToProto() grpcerrors.TypedErrorProto {
return &e.Source
}
func Sources(err error) []*Source {
var out []*Source
var es *ErrorSource
if errors.As(err, &es) {
out = Sources(es.Unwrap())
out = append(out, &es.Source)
}
return out
}
func (s *Source) WrapError(err error) error {
return &ErrorSource{error: err, Source: *s}
}
func (s *Source) Print(w io.Writer) error {
si := s.Info
if si == nil {
return nil
}
lines := strings.Split(string(si.Data), "\n")
start, end, ok := getStartEndLine(s.Ranges)
if !ok {
return nil
}
if start > len(lines) || start < 1 {
return nil
}
if end > len(lines) {
end = len(lines)
}
pad := 2
if end == start {
pad = 4
}
var p int
prepadStart := start
for {
if p >= pad {
break
}
if start > 1 {
start--
p++
}
if end != len(lines) {
end++
p++
}
p++
}
fmt.Fprintf(w, "%s:%d\n--------------------\n", si.Filename, prepadStart)
for i := start; i <= end; i++ {
pfx := " "
if containsLine(s.Ranges, i) {
pfx = ">>>"
}
fmt.Fprintf(w, " %3d | %s %s\n", i, pfx, lines[i-1])
}
fmt.Fprintf(w, "--------------------\n")
return nil
}
func containsLine(rr []*pb.Range, l int) bool {
for _, r := range rr {
e := r.End.Line
if e < r.Start.Line {
e = r.Start.Line
}
if r.Start.Line <= int32(l) && e >= int32(l) {
return true
}
}
return false
}
func getStartEndLine(rr []*pb.Range) (start int, end int, ok bool) {
first := true
for _, r := range rr {
e := r.End.Line
if e < r.Start.Line {
e = r.Start.Line
}
if first || int(r.Start.Line) < start {
start = int(r.Start.Line)
}
if int(e) > end {
end = int(e)
}
first = false
}
return start, end, !first
}

View File

@ -0,0 +1,36 @@
package errdefs
import (
"github.com/containerd/typeurl"
"github.com/moby/buildkit/util/grpcerrors"
digest "github.com/opencontainers/go-digest"
)
func init() {
typeurl.Register((*Vertex)(nil), "github.com/moby/buildkit", "errdefs.Vertex+json")
typeurl.Register((*Source)(nil), "github.com/moby/buildkit", "errdefs.Source+json")
}
type VertexError struct {
Vertex
error
}
func (e *VertexError) Unwrap() error {
return e.error
}
func (e *VertexError) ToProto() grpcerrors.TypedErrorProto {
return &e.Vertex
}
func WrapVertex(err error, dgst digest.Digest) error {
if err == nil {
return nil
}
return &VertexError{Vertex: Vertex{Digest: dgst.String()}, error: err}
}
func (v *Vertex) WrapError(err error) error {
return &VertexError{error: err, Vertex: *v}
}

View File

@ -2,6 +2,8 @@ package pb
const AttrKeepGitDir = "git.keepgitdir"
const AttrFullRemoteURL = "git.fullurl"
const AttrAuthHeaderSecret = "git.authheadersecret"
const AttrAuthTokenSecret = "git.authtokensecret"
const AttrLocalSessionID = "local.session"
const AttrLocalUniqueID = "local.unique"
const AttrIncludePatterns = "local.includepattern"

View File

@ -19,9 +19,10 @@ const (
CapSourceLocalExcludePatterns apicaps.CapID = "source.local.excludepatterns"
CapSourceLocalSharedKeyHint apicaps.CapID = "source.local.sharedkeyhint"
CapSourceGit apicaps.CapID = "source.git"
CapSourceGitKeepDir apicaps.CapID = "source.git.keepgitdir"
CapSourceGitFullURL apicaps.CapID = "source.git.fullurl"
CapSourceGit apicaps.CapID = "source.git"
CapSourceGitKeepDir apicaps.CapID = "source.git.keepgitdir"
CapSourceGitFullURL apicaps.CapID = "source.git.fullurl"
CapSourceGitHTTPAuth apicaps.CapID = "source.git.httpauth"
CapSourceHTTP apicaps.CapID = "source.http"
CapSourceHTTPChecksum apicaps.CapID = "source.http.checksum"
@ -131,6 +132,12 @@ func init() {
Status: apicaps.CapStatusExperimental,
})
Caps.Init(apicaps.Cap{
ID: CapSourceGitHTTPAuth,
Enabled: true,
Status: apicaps.CapStatusExperimental,
})
Caps.Init(apicaps.Cap{
ID: CapSourceHTTP,
Enabled: true,

File diff suppressed because it is too large Load Diff

View File

@ -177,6 +177,42 @@ message OpMetadata {
map<string, bool> caps = 5 [(gogoproto.castkey) = "github.com/moby/buildkit/util/apicaps.CapID", (gogoproto.nullable) = false];
}
// Source is a source mapping description for a file
message Source {
map<string, Locations> locations = 1;
repeated SourceInfo infos = 2;
}
// Locations is a list of ranges with a index to its source map.
message Locations {
repeated Location locations = 1;
}
// Source info contains the shared metadata of a source mapping
message SourceInfo {
string filename = 1;
bytes data = 2;
Definition definition = 3;
}
// Location defines list of areas in to source file
message Location {
int32 sourceIndex = 1;
repeated Range ranges = 2;
}
// Range is an area in the source file
message Range {
Position start = 1 [(gogoproto.nullable) = false];
Position end = 2 [(gogoproto.nullable) = false];
}
// Position is single location in a source file
message Position {
int32 Line = 1;
int32 Character = 2;
}
message ExportCache {
bool Value = 1;
}
@ -200,6 +236,8 @@ message Definition {
// metadata contains metadata for the each of the Op messages.
// A key must be an LLB op digest string. Currently, empty string is not expected as a key, but it may change in the future.
map<string, OpMetadata> metadata = 2 [(gogoproto.castkey) = "github.com/opencontainers/go-digest.Digest", (gogoproto.nullable) = false];
// Source contains the source mapping information for the vertexes in the definition
Source Source = 3;
}
message HostIP {
@ -302,4 +340,4 @@ message UserOpt {
message NamedUserOpt {
string name = 1;
int64 input = 2 [(gogoproto.customtype) = "InputIndex", (gogoproto.nullable) = false];
}
}

View File

@ -1,3 +1,3 @@
package moby_buildkit_v1_apicaps
package moby_buildkit_v1_apicaps //nolint:golint
//go:generate protoc -I=. -I=../../../vendor/ -I=../../../../../../ --gogo_out=plugins=grpc:. caps.proto

View File

@ -0,0 +1,341 @@
package flightcontrol
import (
"context"
"io"
"runtime"
"sort"
"sync"
"time"
"github.com/moby/buildkit/util/progress"
"github.com/pkg/errors"
)
// flightcontrol is like singleflight but with support for cancellation and
// nested progress reporting
var (
errRetry = errors.Errorf("retry")
errRetryTimeout = errors.Errorf("exceeded retry timeout")
)
type contextKeyT string
var contextKey = contextKeyT("buildkit/util/flightcontrol.progress")
// Group is a flightcontrol synchronization group
type Group struct {
mu sync.Mutex // protects m
m map[string]*call // lazily initialized
}
// Do executes a context function syncronized by the key
func (g *Group) Do(ctx context.Context, key string, fn func(ctx context.Context) (interface{}, error)) (v interface{}, err error) {
var backoff time.Duration
for {
v, err = g.do(ctx, key, fn)
if err == nil || !errors.Is(err, errRetry) {
return v, err
}
// backoff logic
if backoff >= 3*time.Second {
err = errors.Wrapf(errRetryTimeout, "flightcontrol")
return v, err
}
runtime.Gosched()
if backoff > 0 {
time.Sleep(backoff)
backoff *= 2
} else {
backoff = time.Millisecond
}
}
}
func (g *Group) do(ctx context.Context, key string, fn func(ctx context.Context) (interface{}, error)) (interface{}, error) {
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok { // register 2nd waiter
g.mu.Unlock()
return c.wait(ctx)
}
c := newCall(fn)
g.m[key] = c
go func() {
// cleanup after a caller has returned
<-c.ready
g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()
close(c.cleaned)
}()
g.mu.Unlock()
return c.wait(ctx)
}
type call struct {
mu sync.Mutex
result interface{}
err error
ready chan struct{}
cleaned chan struct{}
ctx *sharedContext
ctxs []context.Context
fn func(ctx context.Context) (interface{}, error)
once sync.Once
closeProgressWriter func()
progressState *progressState
progressCtx context.Context
}
func newCall(fn func(ctx context.Context) (interface{}, error)) *call {
c := &call{
fn: fn,
ready: make(chan struct{}),
cleaned: make(chan struct{}),
progressState: newProgressState(),
}
ctx := newContext(c) // newSharedContext
pr, pctx, closeProgressWriter := progress.NewContext(context.Background())
c.progressCtx = pctx
c.ctx = ctx
c.closeProgressWriter = closeProgressWriter
go c.progressState.run(pr) // TODO: remove this, wrap writer instead
return c
}
func (c *call) run() {
defer c.closeProgressWriter()
ctx, cancel := context.WithCancel(c.ctx)
defer cancel()
v, err := c.fn(ctx)
c.mu.Lock()
c.result = v
c.err = err
c.mu.Unlock()
close(c.ready)
}
func (c *call) wait(ctx context.Context) (v interface{}, err error) {
c.mu.Lock()
// detect case where caller has just returned, let it clean up before
select {
case <-c.ready: // could return if no error
c.mu.Unlock()
<-c.cleaned
return nil, errRetry
default:
}
pw, ok, ctx := progress.FromContext(ctx)
if ok {
c.progressState.add(pw)
}
c.ctxs = append(c.ctxs, ctx)
c.mu.Unlock()
go c.once.Do(c.run)
select {
case <-ctx.Done():
select {
case <-c.ctx.Done():
// if this cancelled the last context, then wait for function to shut down
// and don't accept any more callers
<-c.ready
return c.result, c.err
default:
if ok {
c.progressState.close(pw)
}
return nil, ctx.Err()
}
case <-c.ready:
return c.result, c.err // shared not implemented yet
}
}
func (c *call) Deadline() (deadline time.Time, ok bool) {
c.mu.Lock()
defer c.mu.Unlock()
for _, ctx := range c.ctxs {
select {
case <-ctx.Done():
default:
dl, ok := ctx.Deadline()
if ok {
return dl, ok
}
}
}
return time.Time{}, false
}
func (c *call) Done() <-chan struct{} {
c.mu.Lock()
c.ctx.signal()
c.mu.Unlock()
return c.ctx.done
}
func (c *call) Err() error {
select {
case <-c.ctx.Done():
return c.ctx.err
default:
return nil
}
}
func (c *call) Value(key interface{}) interface{} {
if key == contextKey {
return c.progressState
}
c.mu.Lock()
defer c.mu.Unlock()
ctx := c.progressCtx
select {
case <-ctx.Done():
default:
if v := ctx.Value(key); v != nil {
return v
}
}
if len(c.ctxs) > 0 {
ctx = c.ctxs[0]
select {
case <-ctx.Done():
default:
if v := ctx.Value(key); v != nil {
return v
}
}
}
return nil
}
type sharedContext struct {
*call
done chan struct{}
err error
}
func newContext(c *call) *sharedContext {
return &sharedContext{call: c, done: make(chan struct{})}
}
// call with lock
func (c *sharedContext) signal() {
select {
case <-c.done:
default:
var err error
for _, ctx := range c.ctxs {
select {
case <-ctx.Done():
err = ctx.Err()
default:
return
}
}
c.err = err
close(c.done)
}
}
type rawProgressWriter interface {
WriteRawProgress(*progress.Progress) error
Close() error
}
type progressState struct {
mu sync.Mutex
items map[string]*progress.Progress
writers []rawProgressWriter
done bool
}
func newProgressState() *progressState {
return &progressState{
items: make(map[string]*progress.Progress),
}
}
func (ps *progressState) run(pr progress.Reader) {
for {
p, err := pr.Read(context.TODO())
if err != nil {
if err == io.EOF {
ps.mu.Lock()
ps.done = true
ps.mu.Unlock()
for _, w := range ps.writers {
w.Close()
}
}
return
}
ps.mu.Lock()
for _, p := range p {
for _, w := range ps.writers {
w.WriteRawProgress(p)
}
ps.items[p.ID] = p
}
ps.mu.Unlock()
}
}
func (ps *progressState) add(pw progress.Writer) {
rw, ok := pw.(rawProgressWriter)
if !ok {
return
}
ps.mu.Lock()
plist := make([]*progress.Progress, 0, len(ps.items))
for _, p := range ps.items {
plist = append(plist, p)
}
sort.Slice(plist, func(i, j int) bool {
return plist[i].Timestamp.Before(plist[j].Timestamp)
})
for _, p := range plist {
rw.WriteRawProgress(p)
}
if ps.done {
rw.Close()
} else {
ps.writers = append(ps.writers, rw)
}
ps.mu.Unlock()
}
func (ps *progressState) close(pw progress.Writer) {
rw, ok := pw.(rawProgressWriter)
if !ok {
return
}
ps.mu.Lock()
for i, w := range ps.writers {
if w == rw {
w.Close()
ps.writers = append(ps.writers[:i], ps.writers[i+1:]...)
break
}
}
ps.mu.Unlock()
}

View File

@ -0,0 +1,203 @@
package grpcerrors
import (
"encoding/json"
"errors"
"github.com/containerd/typeurl"
gogotypes "github.com/gogo/protobuf/types"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/any"
"github.com/moby/buildkit/util/stack"
"github.com/sirupsen/logrus"
spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type TypedError interface {
ToProto() TypedErrorProto
}
type TypedErrorProto interface {
proto.Message
WrapError(error) error
}
func ToGRPC(err error) error {
if err == nil {
return nil
}
st, ok := AsGRPCStatus(err)
if !ok || st == nil {
st = status.New(Code(err), err.Error())
}
if st.Code() != Code(err) {
pb := st.Proto()
pb.Code = int32(Code(err))
st = status.FromProto(pb)
}
var details []proto.Message
for _, st := range stack.Traces(err) {
details = append(details, st)
}
each(err, func(err error) {
if te, ok := err.(TypedError); ok {
details = append(details, te.ToProto())
}
})
if len(details) > 0 {
if st2, err := withDetails(st, details...); err == nil {
st = st2
}
}
return st.Err()
}
func withDetails(s *status.Status, details ...proto.Message) (*status.Status, error) {
if s.Code() == codes.OK {
return nil, errors.New("no error details for status with code OK")
}
p := s.Proto()
for _, detail := range details {
url, err := typeurl.TypeURL(detail)
if err != nil {
logrus.Warnf("ignoring typed error %T: not registered", detail)
continue
}
dt, err := json.Marshal(detail)
if err != nil {
return nil, err
}
p.Details = append(p.Details, &any.Any{TypeUrl: url, Value: dt})
}
return status.FromProto(p), nil
}
func Code(err error) codes.Code {
if se, ok := err.(interface {
Code() codes.Code
}); ok {
return se.Code()
}
if se, ok := err.(interface {
GRPCStatus() *status.Status
}); ok {
return se.GRPCStatus().Code()
}
wrapped, ok := err.(interface {
Unwrap() error
})
if ok {
return Code(wrapped.Unwrap())
}
return status.FromContextError(err).Code()
}
func WrapCode(err error, code codes.Code) error {
return &withCode{error: err, code: code}
}
func AsGRPCStatus(err error) (*status.Status, bool) {
if err == nil {
return nil, true
}
if se, ok := err.(interface {
GRPCStatus() *status.Status
}); ok {
return se.GRPCStatus(), true
}
wrapped, ok := err.(interface {
Unwrap() error
})
if ok {
return AsGRPCStatus(wrapped.Unwrap())
}
return nil, false
}
func FromGRPC(err error) error {
if err == nil {
return nil
}
st, ok := status.FromError(err)
if !ok {
return err
}
pb := st.Proto()
n := &spb.Status{
Code: pb.Code,
Message: pb.Message,
}
details := make([]TypedErrorProto, 0, len(pb.Details))
stacks := make([]*stack.Stack, 0, len(pb.Details))
// details that we don't understand are copied as proto
for _, d := range pb.Details {
m, err := typeurl.UnmarshalAny(gogoAny(d))
if err != nil {
continue
}
switch v := m.(type) {
case *stack.Stack:
stacks = append(stacks, v)
case TypedErrorProto:
details = append(details, v)
default:
n.Details = append(n.Details, d)
}
}
err = status.FromProto(n).Err()
for _, s := range stacks {
if s != nil {
err = stack.Wrap(err, *s)
}
}
for _, d := range details {
err = d.WrapError(err)
}
return stack.Enable(err)
}
type withCode struct {
code codes.Code
error
}
func (e *withCode) Unwrap() error {
return e.error
}
func each(err error, fn func(error)) {
fn(err)
if wrapped, ok := err.(interface {
Unwrap() error
}); ok {
each(wrapped.Unwrap(), fn)
}
}
func gogoAny(in *any.Any) *gogotypes.Any {
return &gogotypes.Any{
TypeUrl: in.TypeUrl,
Value: in.Value,
}
}

View File

@ -0,0 +1,28 @@
package grpcerrors
import (
"context"
"google.golang.org/grpc"
)
func UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
resp, err = handler(ctx, req)
if err != nil {
err = ToGRPC(err)
}
return resp, err
}
func StreamServerInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
return ToGRPC(handler(srv, ss))
}
func UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
return FromGRPC(invoker(ctx, method, req, reply, cc, opts...))
}
func StreamClientInterceptor(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
s, err := streamer(ctx, desc, cc, method, opts...)
return s, ToGRPC(err)
}

View File

@ -0,0 +1,77 @@
package progress
import (
"context"
"io"
"sync"
)
type MultiReader struct {
mu sync.Mutex
main Reader
initialized bool
done chan struct{}
writers map[*progressWriter]func()
}
func NewMultiReader(pr Reader) *MultiReader {
mr := &MultiReader{
main: pr,
writers: make(map[*progressWriter]func()),
done: make(chan struct{}),
}
return mr
}
func (mr *MultiReader) Reader(ctx context.Context) Reader {
mr.mu.Lock()
defer mr.mu.Unlock()
pr, ctx, closeWriter := NewContext(ctx)
pw, _, ctx := FromContext(ctx)
w := pw.(*progressWriter)
mr.writers[w] = closeWriter
go func() {
select {
case <-ctx.Done():
case <-mr.done:
}
mr.mu.Lock()
defer mr.mu.Unlock()
delete(mr.writers, w)
}()
if !mr.initialized {
go mr.handle()
mr.initialized = true
}
return pr
}
func (mr *MultiReader) handle() error {
for {
p, err := mr.main.Read(context.TODO())
if err != nil {
if err == io.EOF {
mr.mu.Lock()
for w, c := range mr.writers {
w.Close()
c()
}
mr.mu.Unlock()
return nil
}
return err
}
mr.mu.Lock()
for _, p := range p {
for w := range mr.writers {
w.writeRawProgress(p)
}
}
mr.mu.Unlock()
}
}

View File

@ -0,0 +1,104 @@
package progress
import (
"sort"
"sync"
"time"
)
type rawProgressWriter interface {
WriteRawProgress(*Progress) error
Close() error
}
type MultiWriter struct {
mu sync.Mutex
items []*Progress
writers map[rawProgressWriter]struct{}
meta map[string]interface{}
}
func NewMultiWriter(opts ...WriterOption) *MultiWriter {
mw := &MultiWriter{
writers: map[rawProgressWriter]struct{}{},
meta: map[string]interface{}{},
}
for _, o := range opts {
o(mw)
}
return mw
}
func (ps *MultiWriter) Add(pw Writer) {
rw, ok := pw.(rawProgressWriter)
if !ok {
return
}
ps.mu.Lock()
plist := make([]*Progress, 0, len(ps.items))
for _, p := range ps.items {
plist = append(plist, p)
}
sort.Slice(plist, func(i, j int) bool {
return plist[i].Timestamp.Before(plist[j].Timestamp)
})
for _, p := range plist {
rw.WriteRawProgress(p)
}
ps.writers[rw] = struct{}{}
ps.mu.Unlock()
}
func (ps *MultiWriter) Delete(pw Writer) {
rw, ok := pw.(rawProgressWriter)
if !ok {
return
}
ps.mu.Lock()
delete(ps.writers, rw)
ps.mu.Unlock()
}
func (ps *MultiWriter) Write(id string, v interface{}) error {
p := &Progress{
ID: id,
Timestamp: time.Now(),
Sys: v,
meta: ps.meta,
}
return ps.WriteRawProgress(p)
}
func (ps *MultiWriter) WriteRawProgress(p *Progress) error {
meta := p.meta
if len(ps.meta) > 0 {
meta = map[string]interface{}{}
for k, v := range p.meta {
meta[k] = v
}
for k, v := range ps.meta {
if _, ok := meta[k]; !ok {
meta[k] = v
}
}
}
p.meta = meta
return ps.writeRawProgress(p)
}
func (ps *MultiWriter) writeRawProgress(p *Progress) error {
ps.mu.Lock()
defer ps.mu.Unlock()
ps.items = append(ps.items, p)
for w := range ps.writers {
if err := w.WriteRawProgress(p); err != nil {
return err
}
}
return nil
}
func (ps *MultiWriter) Close() error {
return nil
}

View File

@ -0,0 +1,261 @@
package progress
import (
"context"
"io"
"sort"
"sync"
"time"
"github.com/pkg/errors"
)
// Progress package provides utility functions for using the context to capture
// progress of a running function. All progress items written contain an ID
// that is used to collapse unread messages.
type contextKeyT string
var contextKey = contextKeyT("buildkit/util/progress")
// FromContext returns a progress writer from a context.
func FromContext(ctx context.Context, opts ...WriterOption) (Writer, bool, context.Context) {
v := ctx.Value(contextKey)
pw, ok := v.(*progressWriter)
if !ok {
if pw, ok := v.(*MultiWriter); ok {
return pw, true, ctx
}
return &noOpWriter{}, false, ctx
}
pw = newWriter(pw)
for _, o := range opts {
o(pw)
}
ctx = context.WithValue(ctx, contextKey, pw)
return pw, true, ctx
}
type WriterOption func(Writer)
// NewContext returns a new context and a progress reader that captures all
// progress items writtern to this context. Last returned parameter is a closer
// function to signal that no new writes will happen to this context.
func NewContext(ctx context.Context) (Reader, context.Context, func()) {
pr, pw, cancel := pipe()
ctx = WithProgress(ctx, pw)
return pr, ctx, cancel
}
func WithProgress(ctx context.Context, pw Writer) context.Context {
return context.WithValue(ctx, contextKey, pw)
}
func WithMetadata(key string, val interface{}) WriterOption {
return func(w Writer) {
if pw, ok := w.(*progressWriter); ok {
pw.meta[key] = val
}
if pw, ok := w.(*MultiWriter); ok {
pw.meta[key] = val
}
}
}
type Controller interface {
Start(context.Context) (context.Context, func(error))
Status(id string, action string) func()
}
type Writer interface {
Write(id string, value interface{}) error
Close() error
}
type Reader interface {
Read(context.Context) ([]*Progress, error)
}
type Progress struct {
ID string
Timestamp time.Time
Sys interface{}
meta map[string]interface{}
}
type Status struct {
Action string
Current int
Total int
Started *time.Time
Completed *time.Time
}
type progressReader struct {
ctx context.Context
cond *sync.Cond
mu sync.Mutex
writers map[*progressWriter]struct{}
dirty map[string]*Progress
}
func (pr *progressReader) Read(ctx context.Context) ([]*Progress, error) {
done := make(chan struct{})
defer close(done)
go func() {
select {
case <-done:
case <-ctx.Done():
pr.mu.Lock()
pr.cond.Broadcast()
pr.mu.Unlock()
}
}()
pr.mu.Lock()
for {
select {
case <-ctx.Done():
pr.mu.Unlock()
return nil, ctx.Err()
default:
}
dmap := pr.dirty
if len(dmap) == 0 {
select {
case <-pr.ctx.Done():
if len(pr.writers) == 0 {
pr.mu.Unlock()
return nil, io.EOF
}
default:
}
pr.cond.Wait()
continue
}
pr.dirty = make(map[string]*Progress)
pr.mu.Unlock()
out := make([]*Progress, 0, len(dmap))
for _, p := range dmap {
out = append(out, p)
}
sort.Slice(out, func(i, j int) bool {
return out[i].Timestamp.Before(out[j].Timestamp)
})
return out, nil
}
}
func (pr *progressReader) append(pw *progressWriter) {
pr.mu.Lock()
defer pr.mu.Unlock()
select {
case <-pr.ctx.Done():
return
default:
pr.writers[pw] = struct{}{}
}
}
func pipe() (*progressReader, *progressWriter, func()) {
ctx, cancel := context.WithCancel(context.Background())
pr := &progressReader{
ctx: ctx,
writers: make(map[*progressWriter]struct{}),
dirty: make(map[string]*Progress),
}
pr.cond = sync.NewCond(&pr.mu)
go func() {
<-ctx.Done()
pr.mu.Lock()
pr.cond.Broadcast()
pr.mu.Unlock()
}()
pw := &progressWriter{
reader: pr,
}
return pr, pw, cancel
}
func newWriter(pw *progressWriter) *progressWriter {
meta := make(map[string]interface{})
for k, v := range pw.meta {
meta[k] = v
}
pw = &progressWriter{
reader: pw.reader,
meta: meta,
}
pw.reader.append(pw)
return pw
}
type progressWriter struct {
done bool
reader *progressReader
meta map[string]interface{}
}
func (pw *progressWriter) Write(id string, v interface{}) error {
if pw.done {
return errors.Errorf("writing %s to closed progress writer", id)
}
return pw.writeRawProgress(&Progress{
ID: id,
Timestamp: time.Now(),
Sys: v,
meta: pw.meta,
})
}
func (pw *progressWriter) WriteRawProgress(p *Progress) error {
meta := p.meta
if len(pw.meta) > 0 {
meta = map[string]interface{}{}
for k, v := range p.meta {
meta[k] = v
}
for k, v := range pw.meta {
if _, ok := meta[k]; !ok {
meta[k] = v
}
}
}
p.meta = meta
return pw.writeRawProgress(p)
}
func (pw *progressWriter) writeRawProgress(p *Progress) error {
pw.reader.mu.Lock()
pw.reader.dirty[p.ID] = p
pw.reader.cond.Broadcast()
pw.reader.mu.Unlock()
return nil
}
func (pw *progressWriter) Close() error {
pw.reader.mu.Lock()
delete(pw.reader.writers, pw)
pw.reader.mu.Unlock()
pw.reader.cond.Broadcast()
pw.done = true
return nil
}
func (p *Progress) Meta(key string) (interface{}, bool) {
v, ok := p.meta[key]
return v, ok
}
type noOpWriter struct{}
func (pw *noOpWriter) Write(_ string, _ interface{}) error {
return nil
}
func (pw *noOpWriter) Close() error {
return nil
}

View File

@ -273,7 +273,14 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) {
if v.Started != nil {
ts = l.Timestamp.Sub(*v.Started)
}
v.logs = append(v.logs, []byte(fmt.Sprintf("#%d %s %s", v.index, fmt.Sprintf("%#.4g", ts.Seconds())[:5], dt)))
prec := 1
sec := ts.Seconds()
if sec < 10 {
prec = 3
} else if sec < 100 {
prec = 2
}
v.logs = append(v.logs, []byte(fmt.Sprintf("#%d %s %s", v.index, fmt.Sprintf("%.[2]*[1]f", sec, prec), dt)))
}
i++
})
@ -548,6 +555,9 @@ func align(l, r string, w int) string {
}
func wrapHeight(j []*job, limit int) []*job {
if limit < 0 {
return nil
}
var wrapped []*job
wrapped = append(wrapped, j...)
if len(j) > limit {

View File

@ -57,11 +57,11 @@ func (p *textMux) printVtx(t *trace, dgst digest.Digest) {
p.notFirst = true
}
if os.Getenv("PROGRESS_NO_TRUNC") == "1" {
if os.Getenv("PROGRESS_NO_TRUNC") == "0" {
fmt.Fprintf(p.w, "#%d %s\n", v.index, limitString(v.Name, 72))
} else {
fmt.Fprintf(p.w, "#%d %s\n", v.index, v.Name)
fmt.Fprintf(p.w, "#%d %s\n", v.index, v.Digest)
} else {
fmt.Fprintf(p.w, "#%d %s\n", v.index, limitString(v.Name, 72))
}
}

View File

@ -0,0 +1,3 @@
package stack
//go:generate protoc -I=. -I=../../vendor/ --go_out=. stack.proto

156
vendor/github.com/moby/buildkit/util/stack/stack.go generated vendored Normal file
View File

@ -0,0 +1,156 @@
package stack
import (
"fmt"
io "io"
"os"
"strconv"
"strings"
"github.com/containerd/typeurl"
"github.com/pkg/errors"
)
func init() {
typeurl.Register((*Stack)(nil), "github.com/moby/buildkit", "stack.Stack+json")
}
var version string
var revision string
func SetVersionInfo(v, r string) {
version = v
revision = r
}
func Traces(err error) []*Stack {
var st []*Stack
wrapped, ok := err.(interface {
Unwrap() error
})
if ok {
st = Traces(wrapped.Unwrap())
}
if ste, ok := err.(interface {
StackTrace() errors.StackTrace
}); ok {
st = append(st, convertStack(ste.StackTrace()))
}
if ste, ok := err.(interface {
StackTrace() *Stack
}); ok {
st = append(st, ste.StackTrace())
}
return st
}
func Enable(err error) error {
if err == nil {
return nil
}
if !hasLocalStackTrace(err) {
return errors.WithStack(err)
}
return err
}
func Wrap(err error, s Stack) error {
return &withStack{stack: s, error: err}
}
func hasLocalStackTrace(err error) bool {
wrapped, ok := err.(interface {
Unwrap() error
})
if ok && hasLocalStackTrace(wrapped.Unwrap()) {
return true
}
_, ok = err.(interface {
StackTrace() errors.StackTrace
})
return ok
}
func Formatter(err error) fmt.Formatter {
return &formatter{err}
}
type formatter struct {
error
}
func (w *formatter) Format(s fmt.State, verb rune) {
if w.error == nil {
fmt.Fprintf(s, "%v", w.error)
return
}
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%s\n", w.Error())
for _, stack := range Traces(w.error) {
fmt.Fprintf(s, "%d %s %s\n", stack.Pid, stack.Version, strings.Join(stack.Cmdline, " "))
for _, f := range stack.Frames {
fmt.Fprintf(s, "%s\n\t%s:%d\n", f.Name, f.File, f.Line)
}
fmt.Fprintln(s)
}
return
}
fallthrough
case 's':
io.WriteString(s, w.Error())
case 'q':
fmt.Fprintf(s, "%q", w.Error())
}
}
func convertStack(s errors.StackTrace) *Stack {
var out Stack
for _, f := range s {
dt, err := f.MarshalText()
if err != nil {
continue
}
p := strings.SplitN(string(dt), " ", 2)
if len(p) != 2 {
continue
}
idx := strings.LastIndexByte(p[1], ':')
if idx == -1 {
continue
}
line, err := strconv.Atoi(p[1][idx+1:])
if err != nil {
continue
}
out.Frames = append(out.Frames, &Frame{
Name: p[0],
File: p[1][:idx],
Line: int32(line),
})
}
out.Cmdline = os.Args
out.Pid = int32(os.Getpid())
out.Version = version
out.Revision = revision
return &out
}
type withStack struct {
stack Stack
error
}
func (e *withStack) Unwrap() error {
return e.error
}
func (e *withStack) StackTrace() *Stack {
return &e.stack
}

170
vendor/github.com/moby/buildkit/util/stack/stack.pb.go generated vendored Normal file
View File

@ -0,0 +1,170 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: stack.proto
package stack
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Stack struct {
Frames []*Frame `protobuf:"bytes,1,rep,name=frames,proto3" json:"frames,omitempty"`
Cmdline []string `protobuf:"bytes,2,rep,name=cmdline,proto3" json:"cmdline,omitempty"`
Pid int32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"`
Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"`
Revision string `protobuf:"bytes,5,opt,name=revision,proto3" json:"revision,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Stack) Reset() { *m = Stack{} }
func (m *Stack) String() string { return proto.CompactTextString(m) }
func (*Stack) ProtoMessage() {}
func (*Stack) Descriptor() ([]byte, []int) {
return fileDescriptor_b44c07feb2ca0a5a, []int{0}
}
func (m *Stack) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Stack.Unmarshal(m, b)
}
func (m *Stack) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Stack.Marshal(b, m, deterministic)
}
func (m *Stack) XXX_Merge(src proto.Message) {
xxx_messageInfo_Stack.Merge(m, src)
}
func (m *Stack) XXX_Size() int {
return xxx_messageInfo_Stack.Size(m)
}
func (m *Stack) XXX_DiscardUnknown() {
xxx_messageInfo_Stack.DiscardUnknown(m)
}
var xxx_messageInfo_Stack proto.InternalMessageInfo
func (m *Stack) GetFrames() []*Frame {
if m != nil {
return m.Frames
}
return nil
}
func (m *Stack) GetCmdline() []string {
if m != nil {
return m.Cmdline
}
return nil
}
func (m *Stack) GetPid() int32 {
if m != nil {
return m.Pid
}
return 0
}
func (m *Stack) GetVersion() string {
if m != nil {
return m.Version
}
return ""
}
func (m *Stack) GetRevision() string {
if m != nil {
return m.Revision
}
return ""
}
type Frame struct {
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
File string `protobuf:"bytes,2,opt,name=File,proto3" json:"File,omitempty"`
Line int32 `protobuf:"varint,3,opt,name=Line,proto3" json:"Line,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Frame) Reset() { *m = Frame{} }
func (m *Frame) String() string { return proto.CompactTextString(m) }
func (*Frame) ProtoMessage() {}
func (*Frame) Descriptor() ([]byte, []int) {
return fileDescriptor_b44c07feb2ca0a5a, []int{1}
}
func (m *Frame) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Frame.Unmarshal(m, b)
}
func (m *Frame) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Frame.Marshal(b, m, deterministic)
}
func (m *Frame) XXX_Merge(src proto.Message) {
xxx_messageInfo_Frame.Merge(m, src)
}
func (m *Frame) XXX_Size() int {
return xxx_messageInfo_Frame.Size(m)
}
func (m *Frame) XXX_DiscardUnknown() {
xxx_messageInfo_Frame.DiscardUnknown(m)
}
var xxx_messageInfo_Frame proto.InternalMessageInfo
func (m *Frame) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Frame) GetFile() string {
if m != nil {
return m.File
}
return ""
}
func (m *Frame) GetLine() int32 {
if m != nil {
return m.Line
}
return 0
}
func init() {
proto.RegisterType((*Stack)(nil), "stack.Stack")
proto.RegisterType((*Frame)(nil), "stack.Frame")
}
func init() { proto.RegisterFile("stack.proto", fileDescriptor_b44c07feb2ca0a5a) }
var fileDescriptor_b44c07feb2ca0a5a = []byte{
// 185 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x3c, 0x8f, 0x3d, 0xce, 0x82, 0x40,
0x10, 0x86, 0xb3, 0xdf, 0xb2, 0x7c, 0x3a, 0x58, 0x98, 0xa9, 0x36, 0x56, 0x1b, 0x62, 0x41, 0x45,
0xa1, 0x47, 0x30, 0xa1, 0x32, 0x16, 0x78, 0x02, 0x84, 0x35, 0xd9, 0xc8, 0x5f, 0x76, 0x09, 0xd7,
0xf0, 0xca, 0x66, 0x06, 0xb4, 0x7b, 0xde, 0x9f, 0xe4, 0x9d, 0x81, 0x24, 0x4c, 0x55, 0xfd, 0xca,
0x47, 0x3f, 0x4c, 0x03, 0x2a, 0x16, 0xe9, 0x5b, 0x80, 0xba, 0x13, 0xe1, 0x11, 0xe2, 0xa7, 0xaf,
0x3a, 0x1b, 0xb4, 0x30, 0x32, 0x4b, 0x4e, 0xbb, 0x7c, 0xa9, 0x17, 0x64, 0x96, 0x6b, 0x86, 0x1a,
0xfe, 0xeb, 0xae, 0x69, 0x5d, 0x6f, 0xf5, 0x9f, 0x91, 0xd9, 0xb6, 0xfc, 0x4a, 0xdc, 0x83, 0x1c,
0x5d, 0xa3, 0xa5, 0x11, 0x99, 0x2a, 0x09, 0xa9, 0x3b, 0x5b, 0x1f, 0xdc, 0xd0, 0xeb, 0xc8, 0x08,
0xea, 0xae, 0x12, 0x0f, 0xb0, 0xf1, 0x76, 0x76, 0x1c, 0x29, 0x8e, 0x7e, 0x3a, 0xbd, 0x80, 0xe2,
0x49, 0x44, 0x88, 0x6e, 0x55, 0x67, 0xb5, 0xe0, 0x02, 0x33, 0x79, 0x85, 0x6b, 0x69, 0x9b, 0x3d,
0x62, 0xf2, 0xae, 0x74, 0xcf, 0xb2, 0xcc, 0xfc, 0x88, 0xf9, 0xc9, 0xf3, 0x27, 0x00, 0x00, 0xff,
0xff, 0xfd, 0x2c, 0xbb, 0xfb, 0xf3, 0x00, 0x00, 0x00,
}

17
vendor/github.com/moby/buildkit/util/stack/stack.proto generated vendored Normal file
View File

@ -0,0 +1,17 @@
syntax = "proto3";
package stack;
message Stack {
repeated Frame frames = 1;
repeated string cmdline = 2;
int32 pid = 3;
string version = 4;
string revision = 5;
}
message Frame {
string Name = 1;
string File = 2;
int32 Line = 3;
}

8
vendor/github.com/moby/term/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# if you want to ignore files created by your editor/tools, consider using a
# global .gitignore or .git/info/exclude see https://help.github.com/articles/ignoring-files
.*
!.github
!.gitignore
profile.out
# support running go modules in vendor mode for local development
vendor/

191
vendor/github.com/moby/term/LICENSE generated vendored Normal file
View File

@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2013-2018 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

36
vendor/github.com/moby/term/README.md generated vendored Normal file
View File

@ -0,0 +1,36 @@
# term - utilities for dealing with terminals
![Test](https://github.com/moby/term/workflows/Test/badge.svg) [![GoDoc](https://godoc.org/github.com/moby/term?status.svg)](https://godoc.org/github.com/moby/term) [![Go Report Card](https://goreportcard.com/badge/github.com/moby/term)](https://goreportcard.com/report/github.com/moby/term)
term provides structures and helper functions to work with terminal (state, sizes).
#### Using term
```go
package main
import (
"log"
"os"
"github.com/moby/term"
)
func main() {
fd := os.Stdin.Fd()
if term.IsTerminal(fd) {
ws, err := term.GetWinsize(fd)
if err != nil {
log.Fatalf("term.GetWinsize: %s", err)
}
log.Printf("%d:%d\n", ws.Height, ws.Width)
}
}
```
## Contributing
Want to hack on term? [Docker's contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) apply.
## Copyright and license
Code and documentation copyright 2015 Docker, inc. Code released under the Apache 2.0 license. Docs released under Creative commons.

66
vendor/github.com/moby/term/ascii.go generated vendored Normal file
View File

@ -0,0 +1,66 @@
package term
import (
"fmt"
"strings"
)
// ASCII list the possible supported ASCII key sequence
var ASCII = []string{
"ctrl-@",
"ctrl-a",
"ctrl-b",
"ctrl-c",
"ctrl-d",
"ctrl-e",
"ctrl-f",
"ctrl-g",
"ctrl-h",
"ctrl-i",
"ctrl-j",
"ctrl-k",
"ctrl-l",
"ctrl-m",
"ctrl-n",
"ctrl-o",
"ctrl-p",
"ctrl-q",
"ctrl-r",
"ctrl-s",
"ctrl-t",
"ctrl-u",
"ctrl-v",
"ctrl-w",
"ctrl-x",
"ctrl-y",
"ctrl-z",
"ctrl-[",
"ctrl-\\",
"ctrl-]",
"ctrl-^",
"ctrl-_",
}
// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code.
func ToBytes(keys string) ([]byte, error) {
codes := []byte{}
next:
for _, key := range strings.Split(keys, ",") {
if len(key) != 1 {
for code, ctrl := range ASCII {
if ctrl == key {
codes = append(codes, byte(code))
continue next
}
}
if key == "DEL" {
codes = append(codes, 127)
} else {
return nil, fmt.Errorf("Unknown character: '%s'", key)
}
} else {
codes = append(codes, key[0])
}
}
return codes, nil
}

12
vendor/github.com/moby/term/go.mod generated vendored Normal file
View File

@ -0,0 +1,12 @@
module github.com/moby/term
go 1.13
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78
github.com/creack/pty v1.1.9
github.com/google/go-cmp v0.4.0
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a
gotest.tools/v3 v3.0.2
)

23
vendor/github.com/moby/term/go.sum generated vendored Normal file
View File

@ -0,0 +1,23 @@
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a h1:i47hUS795cOydZI4AwJQCKXOr4BvxzvikwDoDtHhP2Y=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gotest.tools/v3 v3.0.2 h1:kG1BFyqVHuQoVQiR1bWGnfz/fmHvvuiSPIV7rvl360E=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=

88
vendor/github.com/moby/term/proxy.go generated vendored Normal file
View File

@ -0,0 +1,88 @@
package term
import (
"io"
)
// EscapeError is special error which returned by a TTY proxy reader's Read()
// method in case its detach escape sequence is read.
type EscapeError struct{}
func (EscapeError) Error() string {
return "read escape sequence"
}
// escapeProxy is used only for attaches with a TTY. It is used to proxy
// stdin keypresses from the underlying reader and look for the passed in
// escape key sequence to signal a detach.
type escapeProxy struct {
escapeKeys []byte
escapeKeyPos int
r io.Reader
buf []byte
}
// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader
// and detects when the specified escape keys are read, in which case the Read
// method will return an error of type EscapeError.
func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader {
return &escapeProxy{
escapeKeys: escapeKeys,
r: r,
}
}
func (r *escapeProxy) Read(buf []byte) (n int, err error) {
if len(r.escapeKeys) > 0 && r.escapeKeyPos == len(r.escapeKeys) {
return 0, EscapeError{}
}
if len(r.buf) > 0 {
n = copy(buf, r.buf)
r.buf = r.buf[n:]
}
nr, err := r.r.Read(buf[n:])
n += nr
if len(r.escapeKeys) == 0 {
return n, err
}
for i := 0; i < n; i++ {
if buf[i] == r.escapeKeys[r.escapeKeyPos] {
r.escapeKeyPos++
// Check if the full escape sequence is matched.
if r.escapeKeyPos == len(r.escapeKeys) {
n = i + 1 - r.escapeKeyPos
if n < 0 {
n = 0
}
return n, EscapeError{}
}
continue
}
// If we need to prepend a partial escape sequence from the previous
// read, make sure the new buffer size doesn't exceed len(buf).
// Otherwise, preserve any extra data in a buffer for the next read.
if i < r.escapeKeyPos {
preserve := make([]byte, 0, r.escapeKeyPos+n)
preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...)
preserve = append(preserve, buf[:n]...)
n = copy(buf, preserve)
i += r.escapeKeyPos
r.buf = append(r.buf, preserve[n:]...)
}
r.escapeKeyPos = 0
}
// If we're in the middle of reading an escape sequence, make sure we don't
// let the caller read it. If later on we find that this is not the escape
// sequence, we'll prepend it back to buf.
n -= r.escapeKeyPos
if n < 0 {
n = 0
}
return n, err
}

19
vendor/github.com/moby/term/tc.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// +build !windows
package term
import (
"golang.org/x/sys/unix"
)
func tcget(fd uintptr) (*Termios, error) {
p, err := unix.IoctlGetTermios(int(fd), getTermios)
if err != nil {
return nil, err
}
return (*Termios)(p), nil
}
func tcset(fd uintptr, p *Termios) error {
return unix.IoctlSetTermios(int(fd), setTermios, (*unix.Termios)(p))
}

120
vendor/github.com/moby/term/term.go generated vendored Normal file
View File

@ -0,0 +1,120 @@
// +build !windows
// Package term provides structures and helper functions to work with
// terminal (state, sizes).
package term
import (
"errors"
"fmt"
"io"
"os"
"os/signal"
"golang.org/x/sys/unix"
)
var (
// ErrInvalidState is returned if the state of the terminal is invalid.
ErrInvalidState = errors.New("Invalid terminal state")
)
// State represents the state of the terminal.
type State struct {
termios Termios
}
// Winsize represents the size of the terminal window.
type Winsize struct {
Height uint16
Width uint16
x uint16
y uint16
}
// StdStreams returns the standard streams (stdin, stdout, stderr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
return os.Stdin, os.Stdout, os.Stderr
}
// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
var inFd uintptr
var isTerminalIn bool
if file, ok := in.(*os.File); ok {
inFd = file.Fd()
isTerminalIn = IsTerminal(inFd)
}
return inFd, isTerminalIn
}
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
_, err := tcget(fd)
return err == nil
}
// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
if state == nil {
return ErrInvalidState
}
return tcset(fd, &state.termios)
}
// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) {
termios, err := tcget(fd)
if err != nil {
return nil, err
}
return &State{termios: *termios}, nil
}
// DisableEcho applies the specified state to the terminal connected to the file
// descriptor, with echo disabled.
func DisableEcho(fd uintptr, state *State) error {
newState := state.termios
newState.Lflag &^= unix.ECHO
if err := tcset(fd, &newState); err != nil {
return err
}
handleInterrupt(fd, state)
return nil
}
// SetRawTerminal puts the terminal connected to the given file descriptor into
// raw mode and returns the previous state. On UNIX, this puts both the input
// and output into raw mode. On Windows, it only puts the input into raw mode.
func SetRawTerminal(fd uintptr) (*State, error) {
oldState, err := MakeRaw(fd)
if err != nil {
return nil, err
}
handleInterrupt(fd, oldState)
return oldState, err
}
// SetRawTerminalOutput puts the output of terminal connected to the given file
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
// state. On Windows, it disables LF -> CRLF translation.
func SetRawTerminalOutput(fd uintptr) (*State, error) {
return nil, nil
}
func handleInterrupt(fd uintptr, state *State) {
sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, os.Interrupt)
go func() {
for range sigchan {
// quit cleanly and the new terminal item is on a new line
fmt.Println()
signal.Stop(sigchan)
close(sigchan)
RestoreTerminal(fd, state)
os.Exit(1)
}
}()
}

228
vendor/github.com/moby/term/term_windows.go generated vendored Normal file
View File

@ -0,0 +1,228 @@
package term
import (
"io"
"os"
"os/signal"
windowsconsole "github.com/moby/term/windows"
"golang.org/x/sys/windows"
)
// State holds the console mode for the terminal.
type State struct {
mode uint32
}
// Winsize is used for window size.
type Winsize struct {
Height uint16
Width uint16
}
// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
var vtInputSupported bool
// StdStreams returns the standard streams (stdin, stdout, stderr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
// Turn on VT handling on all std handles, if possible. This might
// fail, in which case we will fall back to terminal emulation.
var (
emulateStdin, emulateStdout, emulateStderr bool
mode uint32
)
fd := windows.Handle(os.Stdin.Fd())
if err := windows.GetConsoleMode(fd, &mode); err == nil {
// Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
emulateStdin = true
} else {
vtInputSupported = true
}
// Unconditionally set the console mode back even on failure because SetConsoleMode
// remembers invalid bits on input handles.
_ = windows.SetConsoleMode(fd, mode)
}
fd = windows.Handle(os.Stdout.Fd())
if err := windows.GetConsoleMode(fd, &mode); err == nil {
// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
emulateStdout = true
} else {
_ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
}
}
fd = windows.Handle(os.Stderr.Fd())
if err := windows.GetConsoleMode(fd, &mode); err == nil {
// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
if err = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING|windows.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
emulateStderr = true
} else {
_ = windows.SetConsoleMode(fd, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
}
}
// Temporarily use STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and
// STD_ERROR_HANDLE from syscall rather than x/sys/windows as long as
// go-ansiterm hasn't switch to x/sys/windows.
// TODO: switch back to x/sys/windows once go-ansiterm has switched
if emulateStdin {
stdIn = windowsconsole.NewAnsiReader(windows.STD_INPUT_HANDLE)
} else {
stdIn = os.Stdin
}
if emulateStdout {
stdOut = windowsconsole.NewAnsiWriter(windows.STD_OUTPUT_HANDLE)
} else {
stdOut = os.Stdout
}
if emulateStderr {
stdErr = windowsconsole.NewAnsiWriter(windows.STD_ERROR_HANDLE)
} else {
stdErr = os.Stderr
}
return
}
// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
return windowsconsole.GetHandleInfo(in)
}
// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
var info windows.ConsoleScreenBufferInfo
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
return nil, err
}
winsize := &Winsize{
Width: uint16(info.Window.Right - info.Window.Left + 1),
Height: uint16(info.Window.Bottom - info.Window.Top + 1),
}
return winsize, nil
}
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
var mode uint32
err := windows.GetConsoleMode(windows.Handle(fd), &mode)
return err == nil
}
// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
return windows.SetConsoleMode(windows.Handle(fd), state.mode)
}
// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) {
var mode uint32
if err := windows.GetConsoleMode(windows.Handle(fd), &mode); err != nil {
return nil, err
}
return &State{mode: mode}, nil
}
// DisableEcho disables echo for the terminal connected to the given file descriptor.
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
func DisableEcho(fd uintptr, state *State) error {
mode := state.mode
mode &^= windows.ENABLE_ECHO_INPUT
mode |= windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT
err := windows.SetConsoleMode(windows.Handle(fd), mode)
if err != nil {
return err
}
// Register an interrupt handler to catch and restore prior state
restoreAtInterrupt(fd, state)
return nil
}
// SetRawTerminal puts the terminal connected to the given file descriptor into
// raw mode and returns the previous state. On UNIX, this puts both the input
// and output into raw mode. On Windows, it only puts the input into raw mode.
func SetRawTerminal(fd uintptr) (*State, error) {
state, err := MakeRaw(fd)
if err != nil {
return nil, err
}
// Register an interrupt handler to catch and restore prior state
restoreAtInterrupt(fd, state)
return state, err
}
// SetRawTerminalOutput puts the output of terminal connected to the given file
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
// state. On Windows, it disables LF -> CRLF translation.
func SetRawTerminalOutput(fd uintptr) (*State, error) {
state, err := SaveState(fd)
if err != nil {
return nil, err
}
// Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
// version of Windows.
_ = windows.SetConsoleMode(windows.Handle(fd), state.mode|windows.DISABLE_NEWLINE_AUTO_RETURN)
return state, err
}
// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be restored.
func MakeRaw(fd uintptr) (*State, error) {
state, err := SaveState(fd)
if err != nil {
return nil, err
}
mode := state.mode
// See
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
// Disable these modes
mode &^= windows.ENABLE_ECHO_INPUT
mode &^= windows.ENABLE_LINE_INPUT
mode &^= windows.ENABLE_MOUSE_INPUT
mode &^= windows.ENABLE_WINDOW_INPUT
mode &^= windows.ENABLE_PROCESSED_INPUT
// Enable these modes
mode |= windows.ENABLE_EXTENDED_FLAGS
mode |= windows.ENABLE_INSERT_MODE
mode |= windows.ENABLE_QUICK_EDIT_MODE
if vtInputSupported {
mode |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
}
err = windows.SetConsoleMode(windows.Handle(fd), mode)
if err != nil {
return nil, err
}
return state, nil
}
func restoreAtInterrupt(fd uintptr, state *State) {
sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, os.Interrupt)
go func() {
_ = <-sigchan
_ = RestoreTerminal(fd, state)
os.Exit(0)
}()
}

36
vendor/github.com/moby/term/termios.go generated vendored Normal file
View File

@ -0,0 +1,36 @@
// +build !windows
package term
import (
"golang.org/x/sys/unix"
)
// Termios is the Unix API for terminal I/O.
type Termios unix.Termios
// MakeRaw puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
termios, err := tcget(fd)
if err != nil {
return nil, err
}
var oldState State
oldState.termios = Termios(*termios)
termios.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
termios.Oflag &^= unix.OPOST
termios.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
termios.Cflag &^= (unix.CSIZE | unix.PARENB)
termios.Cflag |= unix.CS8
termios.Cc[unix.VMIN] = 1
termios.Cc[unix.VTIME] = 0
if err := tcset(fd, termios); err != nil {
return nil, err
}
return &oldState, nil
}

12
vendor/github.com/moby/term/termios_bsd.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// +build darwin freebsd openbsd netbsd
package term
import (
"golang.org/x/sys/unix"
)
const (
getTermios = unix.TIOCGETA
setTermios = unix.TIOCSETA
)

12
vendor/github.com/moby/term/termios_nonbsd.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
//+build !darwin,!freebsd,!netbsd,!openbsd,!windows
package term
import (
"golang.org/x/sys/unix"
)
const (
getTermios = unix.TCGETS
setTermios = unix.TCSETS
)

252
vendor/github.com/moby/term/windows/ansi_reader.go generated vendored Normal file
View File

@ -0,0 +1,252 @@
// +build windows
package windowsconsole
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"strings"
"unsafe"
ansiterm "github.com/Azure/go-ansiterm"
"github.com/Azure/go-ansiterm/winterm"
)
const (
escapeSequence = ansiterm.KEY_ESC_CSI
)
// ansiReader wraps a standard input file (e.g., os.Stdin) providing ANSI sequence translation.
type ansiReader struct {
file *os.File
fd uintptr
buffer []byte
cbBuffer int
command []byte
}
// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a
// Windows console input handle.
func NewAnsiReader(nFile int) io.ReadCloser {
file, fd := winterm.GetStdFile(nFile)
return &ansiReader{
file: file,
fd: fd,
command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
buffer: make([]byte, 0),
}
}
// Close closes the wrapped file.
func (ar *ansiReader) Close() (err error) {
return ar.file.Close()
}
// Fd returns the file descriptor of the wrapped file.
func (ar *ansiReader) Fd() uintptr {
return ar.fd
}
// Read reads up to len(p) bytes of translated input events into p.
func (ar *ansiReader) Read(p []byte) (int, error) {
if len(p) == 0 {
return 0, nil
}
// Previously read bytes exist, read as much as we can and return
if len(ar.buffer) > 0 {
originalLength := len(ar.buffer)
copiedLength := copy(p, ar.buffer)
if copiedLength == originalLength {
ar.buffer = make([]byte, 0, len(p))
} else {
ar.buffer = ar.buffer[copiedLength:]
}
return copiedLength, nil
}
// Read and translate key events
events, err := readInputEvents(ar, len(p))
if err != nil {
return 0, err
} else if len(events) == 0 {
return 0, nil
}
keyBytes := translateKeyEvents(events, []byte(escapeSequence))
// Save excess bytes and right-size keyBytes
if len(keyBytes) > len(p) {
ar.buffer = keyBytes[len(p):]
keyBytes = keyBytes[:len(p)]
} else if len(keyBytes) == 0 {
return 0, nil
}
copiedLength := copy(p, keyBytes)
if copiedLength != len(keyBytes) {
return 0, errors.New("unexpected copy length encountered")
}
return copiedLength, nil
}
// readInputEvents polls until at least one event is available.
func readInputEvents(ar *ansiReader, maxBytes int) ([]winterm.INPUT_RECORD, error) {
// Determine the maximum number of records to retrieve
// -- Cast around the type system to obtain the size of a single INPUT_RECORD.
// unsafe.Sizeof requires an expression vs. a type-reference; the casting
// tricks the type system into believing it has such an expression.
recordSize := int(unsafe.Sizeof(*((*winterm.INPUT_RECORD)(unsafe.Pointer(&maxBytes)))))
countRecords := maxBytes / recordSize
if countRecords > ansiterm.MAX_INPUT_EVENTS {
countRecords = ansiterm.MAX_INPUT_EVENTS
} else if countRecords == 0 {
countRecords = 1
}
// Wait for and read input events
events := make([]winterm.INPUT_RECORD, countRecords)
nEvents := uint32(0)
eventsExist, err := winterm.WaitForSingleObject(ar.fd, winterm.WAIT_INFINITE)
if err != nil {
return nil, err
}
if eventsExist {
err = winterm.ReadConsoleInput(ar.fd, events, &nEvents)
if err != nil {
return nil, err
}
}
// Return a slice restricted to the number of returned records
return events[:nEvents], nil
}
// KeyEvent Translation Helpers
var arrowKeyMapPrefix = map[uint16]string{
winterm.VK_UP: "%s%sA",
winterm.VK_DOWN: "%s%sB",
winterm.VK_RIGHT: "%s%sC",
winterm.VK_LEFT: "%s%sD",
}
var keyMapPrefix = map[uint16]string{
winterm.VK_UP: "\x1B[%sA",
winterm.VK_DOWN: "\x1B[%sB",
winterm.VK_RIGHT: "\x1B[%sC",
winterm.VK_LEFT: "\x1B[%sD",
winterm.VK_HOME: "\x1B[1%s~", // showkey shows ^[[1
winterm.VK_END: "\x1B[4%s~", // showkey shows ^[[4
winterm.VK_INSERT: "\x1B[2%s~",
winterm.VK_DELETE: "\x1B[3%s~",
winterm.VK_PRIOR: "\x1B[5%s~",
winterm.VK_NEXT: "\x1B[6%s~",
winterm.VK_F1: "",
winterm.VK_F2: "",
winterm.VK_F3: "\x1B[13%s~",
winterm.VK_F4: "\x1B[14%s~",
winterm.VK_F5: "\x1B[15%s~",
winterm.VK_F6: "\x1B[17%s~",
winterm.VK_F7: "\x1B[18%s~",
winterm.VK_F8: "\x1B[19%s~",
winterm.VK_F9: "\x1B[20%s~",
winterm.VK_F10: "\x1B[21%s~",
winterm.VK_F11: "\x1B[23%s~",
winterm.VK_F12: "\x1B[24%s~",
}
// translateKeyEvents converts the input events into the appropriate ANSI string.
func translateKeyEvents(events []winterm.INPUT_RECORD, escapeSequence []byte) []byte {
var buffer bytes.Buffer
for _, event := range events {
if event.EventType == winterm.KEY_EVENT && event.KeyEvent.KeyDown != 0 {
buffer.WriteString(keyToString(&event.KeyEvent, escapeSequence))
}
}
return buffer.Bytes()
}
// keyToString maps the given input event record to the corresponding string.
func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) string {
if keyEvent.UnicodeChar == 0 {
return formatVirtualKey(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence)
}
_, alt, control := getControlKeys(keyEvent.ControlKeyState)
if control {
// TODO(azlinux): Implement following control sequences
// <Ctrl>-D Signals the end of input from the keyboard; also exits current shell.
// <Ctrl>-H Deletes the first character to the left of the cursor. Also called the ERASE key.
// <Ctrl>-Q Restarts printing after it has been stopped with <Ctrl>-s.
// <Ctrl>-S Suspends printing on the screen (does not stop the program).
// <Ctrl>-U Deletes all characters on the current line. Also called the KILL key.
// <Ctrl>-E Quits current command and creates a core
}
// <Alt>+Key generates ESC N Key
if !control && alt {
return ansiterm.KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar))
}
return string(keyEvent.UnicodeChar)
}
// formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string.
func formatVirtualKey(key uint16, controlState uint32, escapeSequence []byte) string {
shift, alt, control := getControlKeys(controlState)
modifier := getControlKeysModifier(shift, alt, control)
if format, ok := arrowKeyMapPrefix[key]; ok {
return fmt.Sprintf(format, escapeSequence, modifier)
}
if format, ok := keyMapPrefix[key]; ok {
return fmt.Sprintf(format, modifier)
}
return ""
}
// getControlKeys extracts the shift, alt, and ctrl key states.
func getControlKeys(controlState uint32) (shift, alt, control bool) {
shift = 0 != (controlState & winterm.SHIFT_PRESSED)
alt = 0 != (controlState & (winterm.LEFT_ALT_PRESSED | winterm.RIGHT_ALT_PRESSED))
control = 0 != (controlState & (winterm.LEFT_CTRL_PRESSED | winterm.RIGHT_CTRL_PRESSED))
return shift, alt, control
}
// getControlKeysModifier returns the ANSI modifier for the given combination of control keys.
func getControlKeysModifier(shift, alt, control bool) string {
if shift && alt && control {
return ansiterm.KEY_CONTROL_PARAM_8
}
if alt && control {
return ansiterm.KEY_CONTROL_PARAM_7
}
if shift && control {
return ansiterm.KEY_CONTROL_PARAM_6
}
if control {
return ansiterm.KEY_CONTROL_PARAM_5
}
if shift && alt {
return ansiterm.KEY_CONTROL_PARAM_4
}
if alt {
return ansiterm.KEY_CONTROL_PARAM_3
}
if shift {
return ansiterm.KEY_CONTROL_PARAM_2
}
return ""
}

56
vendor/github.com/moby/term/windows/ansi_writer.go generated vendored Normal file
View File

@ -0,0 +1,56 @@
// +build windows
package windowsconsole
import (
"io"
"os"
ansiterm "github.com/Azure/go-ansiterm"
"github.com/Azure/go-ansiterm/winterm"
)
// ansiWriter wraps a standard output file (e.g., os.Stdout) providing ANSI sequence translation.
type ansiWriter struct {
file *os.File
fd uintptr
infoReset *winterm.CONSOLE_SCREEN_BUFFER_INFO
command []byte
escapeSequence []byte
inAnsiSequence bool
parser *ansiterm.AnsiParser
}
// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a
// Windows console output handle.
func NewAnsiWriter(nFile int) io.Writer {
file, fd := winterm.GetStdFile(nFile)
info, err := winterm.GetConsoleScreenBufferInfo(fd)
if err != nil {
return nil
}
parser := ansiterm.CreateParser("Ground", winterm.CreateWinEventHandler(fd, file))
return &ansiWriter{
file: file,
fd: fd,
infoReset: info,
command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
escapeSequence: []byte(ansiterm.KEY_ESC_CSI),
parser: parser,
}
}
func (aw *ansiWriter) Fd() uintptr {
return aw.fd
}
// Write writes len(p) bytes from p to the underlying data stream.
func (aw *ansiWriter) Write(p []byte) (total int, err error) {
if len(p) == 0 {
return 0, nil
}
return aw.parser.Parse(p)
}

39
vendor/github.com/moby/term/windows/console.go generated vendored Normal file
View File

@ -0,0 +1,39 @@
// +build windows
package windowsconsole
import (
"os"
"golang.org/x/sys/windows"
)
// GetHandleInfo returns file descriptor and bool indicating whether the file is a console.
func GetHandleInfo(in interface{}) (uintptr, bool) {
switch t := in.(type) {
case *ansiReader:
return t.Fd(), true
case *ansiWriter:
return t.Fd(), true
}
var inFd uintptr
var isTerminal bool
if file, ok := in.(*os.File); ok {
inFd = file.Fd()
isTerminal = isConsole(inFd)
}
return inFd, isTerminal
}
// IsConsole returns true if the given file descriptor is a Windows Console.
// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console.
// Deprecated: use golang.org/x/sys/windows.GetConsoleMode() or golang.org/x/crypto/ssh/terminal.IsTerminal()
var IsConsole = isConsole
func isConsole(fd uintptr) bool {
var mode uint32
err := windows.GetConsoleMode(windows.Handle(fd), &mode)
return err == nil
}

5
vendor/github.com/moby/term/windows/doc.go generated vendored Normal file
View File

@ -0,0 +1,5 @@
// These files implement ANSI-aware input and output streams for use by the Docker Windows client.
// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create
// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls.
package windowsconsole

20
vendor/github.com/moby/term/winsize.go generated vendored Normal file
View File

@ -0,0 +1,20 @@
// +build !windows
package term
import (
"golang.org/x/sys/unix"
)
// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
ws := &Winsize{Height: uws.Row, Width: uws.Col, x: uws.Xpixel, y: uws.Ypixel}
return ws, err
}
// SetWinsize tries to set the specified window size for the specified file descriptor.
func SetWinsize(fd uintptr, ws *Winsize) error {
uws := &unix.Winsize{Row: ws.Height, Col: ws.Width, Xpixel: ws.x, Ypixel: ws.y}
return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, uws)
}