mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-09 21:17:09 +08:00
625
vendor/github.com/moby/buildkit/client/llb/exec.go
generated
vendored
Normal file
625
vendor/github.com/moby/buildkit/client/llb/exec.go
generated
vendored
Normal file
@ -0,0 +1,625 @@
|
||||
package llb
|
||||
|
||||
import (
|
||||
_ "crypto/sha256"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/system"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Meta struct {
|
||||
Args []string
|
||||
Env EnvList
|
||||
Cwd string
|
||||
User string
|
||||
ProxyEnv *ProxyEnv
|
||||
ExtraHosts []HostIP
|
||||
Network pb.NetMode
|
||||
}
|
||||
|
||||
func NewExecOp(root Output, meta Meta, readOnly bool, c Constraints) *ExecOp {
|
||||
e := &ExecOp{meta: meta, constraints: c}
|
||||
rootMount := &mount{
|
||||
target: pb.RootMount,
|
||||
source: root,
|
||||
readonly: readOnly,
|
||||
}
|
||||
e.mounts = append(e.mounts, rootMount)
|
||||
if readOnly {
|
||||
e.root = root
|
||||
} else {
|
||||
o := &output{vertex: e, getIndex: e.getMountIndexFn(rootMount)}
|
||||
if p := c.Platform; p != nil {
|
||||
o.platform = p
|
||||
}
|
||||
e.root = o
|
||||
}
|
||||
rootMount.output = e.root
|
||||
return e
|
||||
}
|
||||
|
||||
type mount struct {
|
||||
target string
|
||||
readonly bool
|
||||
source Output
|
||||
output Output
|
||||
selector string
|
||||
cacheID string
|
||||
tmpfs bool
|
||||
cacheSharing CacheMountSharingMode
|
||||
// hasOutput bool
|
||||
}
|
||||
|
||||
type ExecOp struct {
|
||||
MarshalCache
|
||||
root Output
|
||||
mounts []*mount
|
||||
meta Meta
|
||||
constraints Constraints
|
||||
isValidated bool
|
||||
secrets []SecretInfo
|
||||
ssh []SSHInfo
|
||||
}
|
||||
|
||||
func (e *ExecOp) AddMount(target string, source Output, opt ...MountOption) Output {
|
||||
m := &mount{
|
||||
target: target,
|
||||
source: source,
|
||||
}
|
||||
for _, o := range opt {
|
||||
o(m)
|
||||
}
|
||||
e.mounts = append(e.mounts, m)
|
||||
if m.readonly {
|
||||
m.output = source
|
||||
} else if m.tmpfs {
|
||||
m.output = &output{vertex: e, err: errors.Errorf("tmpfs mount for %s can't be used as a parent", target)}
|
||||
} else {
|
||||
o := &output{vertex: e, getIndex: e.getMountIndexFn(m)}
|
||||
if p := e.constraints.Platform; p != nil {
|
||||
o.platform = p
|
||||
}
|
||||
m.output = o
|
||||
}
|
||||
e.Store(nil, nil, nil)
|
||||
e.isValidated = false
|
||||
return m.output
|
||||
}
|
||||
|
||||
func (e *ExecOp) GetMount(target string) Output {
|
||||
for _, m := range e.mounts {
|
||||
if m.target == target {
|
||||
return m.output
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ExecOp) Validate() error {
|
||||
if e.isValidated {
|
||||
return nil
|
||||
}
|
||||
if len(e.meta.Args) == 0 {
|
||||
return errors.Errorf("arguments are required")
|
||||
}
|
||||
if e.meta.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 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
e.isValidated = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ExecOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) {
|
||||
if e.Cached(c) {
|
||||
return e.Load()
|
||||
}
|
||||
if err := e.Validate(); err != nil {
|
||||
return "", 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
|
||||
})
|
||||
|
||||
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 c.Caps != nil {
|
||||
if err := c.Caps.Supports(pb.CapExecMetaSetsDefaultPath); err != nil {
|
||||
e.meta.Env = e.meta.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,
|
||||
}
|
||||
if len(e.meta.ExtraHosts) > 0 {
|
||||
hosts := make([]*pb.HostIP, len(e.meta.ExtraHosts))
|
||||
for i, h := range e.meta.ExtraHosts {
|
||||
hosts[i] = &pb.HostIP{Host: h.Host, IP: h.IP.String()}
|
||||
}
|
||||
meta.ExtraHosts = hosts
|
||||
}
|
||||
|
||||
peo := &pb.ExecOp{
|
||||
Meta: meta,
|
||||
Network: e.meta.Network,
|
||||
}
|
||||
if e.meta.Network != NetModeSandbox {
|
||||
addCap(&e.constraints, pb.CapExecMetaNetwork)
|
||||
}
|
||||
|
||||
if p := e.meta.ProxyEnv; p != nil {
|
||||
peo.Meta.ProxyEnv = &pb.ProxyEnv{
|
||||
HttpProxy: p.HttpProxy,
|
||||
HttpsProxy: p.HttpsProxy,
|
||||
FtpProxy: p.FtpProxy,
|
||||
NoProxy: p.NoProxy,
|
||||
}
|
||||
addCap(&e.constraints, pb.CapExecMetaProxy)
|
||||
}
|
||||
|
||||
addCap(&e.constraints, pb.CapExecMetaBase)
|
||||
|
||||
for _, m := range e.mounts {
|
||||
if m.selector != "" {
|
||||
addCap(&e.constraints, pb.CapExecMountSelector)
|
||||
}
|
||||
if m.cacheID != "" {
|
||||
addCap(&e.constraints, pb.CapExecMountCache)
|
||||
addCap(&e.constraints, pb.CapExecMountCacheSharing)
|
||||
} else if m.tmpfs {
|
||||
addCap(&e.constraints, pb.CapExecMountTmpfs)
|
||||
} else if m.source != nil {
|
||||
addCap(&e.constraints, pb.CapExecMountBind)
|
||||
}
|
||||
}
|
||||
|
||||
if len(e.secrets) > 0 {
|
||||
addCap(&e.constraints, pb.CapExecMountSecret)
|
||||
}
|
||||
|
||||
if len(e.ssh) > 0 {
|
||||
addCap(&e.constraints, pb.CapExecMountSSH)
|
||||
}
|
||||
|
||||
pop, md := MarshalConstraints(c, &e.constraints)
|
||||
pop.Op = &pb.Op_Exec{
|
||||
Exec: peo,
|
||||
}
|
||||
|
||||
outIndex := 0
|
||||
for _, m := range e.mounts {
|
||||
inputIndex := pb.InputIndex(len(pop.Inputs))
|
||||
if m.source != nil {
|
||||
if m.tmpfs {
|
||||
return "", nil, nil, errors.Errorf("tmpfs mounts must use scratch")
|
||||
}
|
||||
inp, err := m.source.ToInput(c)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
newInput := true
|
||||
|
||||
for i, inp2 := range pop.Inputs {
|
||||
if *inp == *inp2 {
|
||||
inputIndex = pb.InputIndex(i)
|
||||
newInput = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if newInput {
|
||||
pop.Inputs = append(pop.Inputs, inp)
|
||||
}
|
||||
} else {
|
||||
inputIndex = pb.Empty
|
||||
}
|
||||
|
||||
outputIndex := pb.OutputIndex(-1)
|
||||
if !m.readonly && m.cacheID == "" && !m.tmpfs {
|
||||
outputIndex = pb.OutputIndex(outIndex)
|
||||
outIndex++
|
||||
}
|
||||
|
||||
pm := &pb.Mount{
|
||||
Input: inputIndex,
|
||||
Dest: m.target,
|
||||
Readonly: m.readonly,
|
||||
Output: outputIndex,
|
||||
Selector: m.selector,
|
||||
}
|
||||
if m.cacheID != "" {
|
||||
pm.MountType = pb.MountType_CACHE
|
||||
pm.CacheOpt = &pb.CacheOpt{
|
||||
ID: m.cacheID,
|
||||
}
|
||||
switch m.cacheSharing {
|
||||
case CacheMountShared:
|
||||
pm.CacheOpt.Sharing = pb.CacheSharingOpt_SHARED
|
||||
case CacheMountPrivate:
|
||||
pm.CacheOpt.Sharing = pb.CacheSharingOpt_PRIVATE
|
||||
case CacheMountLocked:
|
||||
pm.CacheOpt.Sharing = pb.CacheSharingOpt_LOCKED
|
||||
}
|
||||
}
|
||||
if m.tmpfs {
|
||||
pm.MountType = pb.MountType_TMPFS
|
||||
}
|
||||
peo.Mounts = append(peo.Mounts, pm)
|
||||
}
|
||||
|
||||
for _, s := range e.secrets {
|
||||
pm := &pb.Mount{
|
||||
Dest: s.Target,
|
||||
MountType: pb.MountType_SECRET,
|
||||
SecretOpt: &pb.SecretOpt{
|
||||
ID: s.ID,
|
||||
Uid: uint32(s.UID),
|
||||
Gid: uint32(s.GID),
|
||||
Optional: s.Optional,
|
||||
Mode: uint32(s.Mode),
|
||||
},
|
||||
}
|
||||
peo.Mounts = append(peo.Mounts, pm)
|
||||
}
|
||||
|
||||
for _, s := range e.ssh {
|
||||
pm := &pb.Mount{
|
||||
Dest: s.Target,
|
||||
MountType: pb.MountType_SSH,
|
||||
SSHOpt: &pb.SSHOpt{
|
||||
ID: s.ID,
|
||||
Uid: uint32(s.UID),
|
||||
Gid: uint32(s.GID),
|
||||
Mode: uint32(s.Mode),
|
||||
Optional: s.Optional,
|
||||
},
|
||||
}
|
||||
peo.Mounts = append(peo.Mounts, pm)
|
||||
}
|
||||
|
||||
dt, err := pop.Marshal()
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
e.Store(dt, md, c)
|
||||
return e.Load()
|
||||
}
|
||||
|
||||
func (e *ExecOp) Output() Output {
|
||||
return e.root
|
||||
}
|
||||
|
||||
func (e *ExecOp) Inputs() (inputs []Output) {
|
||||
mm := map[Output]struct{}{}
|
||||
for _, m := range e.mounts {
|
||||
if m.source != nil {
|
||||
mm[m.source] = struct{}{}
|
||||
}
|
||||
}
|
||||
for o := range mm {
|
||||
inputs = append(inputs, o)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *ExecOp) getMountIndexFn(m *mount) func() (pb.OutputIndex, error) {
|
||||
return func() (pb.OutputIndex, error) {
|
||||
// make sure mounts are sorted
|
||||
sort.Slice(e.mounts, func(i, j int) bool {
|
||||
return e.mounts[i].target < e.mounts[j].target
|
||||
})
|
||||
|
||||
i := 0
|
||||
for _, m2 := range e.mounts {
|
||||
if m2.readonly || m2.cacheID != "" {
|
||||
continue
|
||||
}
|
||||
if m == m2 {
|
||||
return pb.OutputIndex(i), nil
|
||||
}
|
||||
i++
|
||||
}
|
||||
return pb.OutputIndex(0), errors.Errorf("invalid mount: %s", m.target)
|
||||
}
|
||||
}
|
||||
|
||||
type ExecState struct {
|
||||
State
|
||||
exec *ExecOp
|
||||
}
|
||||
|
||||
func (e ExecState) AddMount(target string, source State, opt ...MountOption) State {
|
||||
return source.WithOutput(e.exec.AddMount(target, source.Output(), opt...))
|
||||
}
|
||||
|
||||
func (e ExecState) GetMount(target string) State {
|
||||
return NewState(e.exec.GetMount(target))
|
||||
}
|
||||
|
||||
func (e ExecState) Root() State {
|
||||
return e.State
|
||||
}
|
||||
|
||||
type MountOption func(*mount)
|
||||
|
||||
func Readonly(m *mount) {
|
||||
m.readonly = true
|
||||
}
|
||||
|
||||
func SourcePath(src string) MountOption {
|
||||
return func(m *mount) {
|
||||
m.selector = src
|
||||
}
|
||||
}
|
||||
|
||||
func AsPersistentCacheDir(id string, sharing CacheMountSharingMode) MountOption {
|
||||
return func(m *mount) {
|
||||
m.cacheID = id
|
||||
m.cacheSharing = sharing
|
||||
}
|
||||
}
|
||||
|
||||
func Tmpfs() MountOption {
|
||||
return func(m *mount) {
|
||||
m.tmpfs = true
|
||||
}
|
||||
}
|
||||
|
||||
type RunOption interface {
|
||||
SetRunOption(es *ExecInfo)
|
||||
}
|
||||
|
||||
type runOptionFunc func(*ExecInfo)
|
||||
|
||||
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 Shlex(str string) RunOption {
|
||||
return Shlexf(str)
|
||||
}
|
||||
func Shlexf(str string, v ...interface{}) RunOption {
|
||||
return runOptionFunc(func(ei *ExecInfo) {
|
||||
ei.State = shlexf(str, v...)(ei.State)
|
||||
})
|
||||
}
|
||||
|
||||
func Args(a []string) RunOption {
|
||||
return runOptionFunc(func(ei *ExecInfo) {
|
||||
ei.State = args(a...)(ei.State)
|
||||
})
|
||||
}
|
||||
|
||||
func AddEnv(key, value string) RunOption {
|
||||
return AddEnvf(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 Dirf(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...)
|
||||
})
|
||||
}
|
||||
|
||||
func AddMount(dest string, mountState State, opts ...MountOption) RunOption {
|
||||
return runOptionFunc(func(ei *ExecInfo) {
|
||||
ei.Mounts = append(ei.Mounts, MountInfo{dest, mountState.Output(), opts})
|
||||
})
|
||||
}
|
||||
|
||||
func AddSSHSocket(opts ...SSHOption) RunOption {
|
||||
return runOptionFunc(func(ei *ExecInfo) {
|
||||
s := &SSHInfo{
|
||||
Mode: 0600,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt.SetSSHOption(s)
|
||||
}
|
||||
ei.SSH = append(ei.SSH, *s)
|
||||
})
|
||||
}
|
||||
|
||||
type SSHOption interface {
|
||||
SetSSHOption(*SSHInfo)
|
||||
}
|
||||
|
||||
type sshOptionFunc func(*SSHInfo)
|
||||
|
||||
func (fn sshOptionFunc) SetSSHOption(si *SSHInfo) {
|
||||
fn(si)
|
||||
}
|
||||
|
||||
func SSHID(id string) SSHOption {
|
||||
return sshOptionFunc(func(si *SSHInfo) {
|
||||
si.ID = id
|
||||
})
|
||||
}
|
||||
|
||||
func SSHSocketTarget(target string) SSHOption {
|
||||
return sshOptionFunc(func(si *SSHInfo) {
|
||||
si.Target = target
|
||||
})
|
||||
}
|
||||
|
||||
func SSHSocketOpt(target string, uid, gid, mode int) SSHOption {
|
||||
return sshOptionFunc(func(si *SSHInfo) {
|
||||
si.Target = target
|
||||
si.UID = uid
|
||||
si.GID = gid
|
||||
si.Mode = mode
|
||||
})
|
||||
}
|
||||
|
||||
var SSHOptional = sshOptionFunc(func(si *SSHInfo) {
|
||||
si.Optional = true
|
||||
})
|
||||
|
||||
type SSHInfo struct {
|
||||
ID string
|
||||
Target string
|
||||
Mode int
|
||||
UID int
|
||||
GID int
|
||||
Optional bool
|
||||
}
|
||||
|
||||
func AddSecret(dest string, opts ...SecretOption) RunOption {
|
||||
return runOptionFunc(func(ei *ExecInfo) {
|
||||
s := &SecretInfo{ID: dest, Target: dest, Mode: 0400}
|
||||
for _, opt := range opts {
|
||||
opt.SetSecretOption(s)
|
||||
}
|
||||
ei.Secrets = append(ei.Secrets, *s)
|
||||
})
|
||||
}
|
||||
|
||||
type SecretOption interface {
|
||||
SetSecretOption(*SecretInfo)
|
||||
}
|
||||
|
||||
type secretOptionFunc func(*SecretInfo)
|
||||
|
||||
func (fn secretOptionFunc) SetSecretOption(si *SecretInfo) {
|
||||
fn(si)
|
||||
}
|
||||
|
||||
type SecretInfo struct {
|
||||
ID string
|
||||
Target string
|
||||
Mode int
|
||||
UID int
|
||||
GID int
|
||||
Optional bool
|
||||
}
|
||||
|
||||
var SecretOptional = secretOptionFunc(func(si *SecretInfo) {
|
||||
si.Optional = true
|
||||
})
|
||||
|
||||
func SecretID(id string) SecretOption {
|
||||
return secretOptionFunc(func(si *SecretInfo) {
|
||||
si.ID = id
|
||||
})
|
||||
}
|
||||
|
||||
func SecretFileOpt(uid, gid, mode int) SecretOption {
|
||||
return secretOptionFunc(func(si *SecretInfo) {
|
||||
si.UID = uid
|
||||
si.GID = gid
|
||||
si.Mode = mode
|
||||
})
|
||||
}
|
||||
|
||||
func ReadonlyRootFS() RunOption {
|
||||
return runOptionFunc(func(ei *ExecInfo) {
|
||||
ei.ReadonlyRootFS = true
|
||||
})
|
||||
}
|
||||
|
||||
func WithProxy(ps ProxyEnv) RunOption {
|
||||
return runOptionFunc(func(ei *ExecInfo) {
|
||||
ei.ProxyEnv = &ps
|
||||
})
|
||||
}
|
||||
|
||||
type ExecInfo struct {
|
||||
constraintsWrapper
|
||||
State State
|
||||
Mounts []MountInfo
|
||||
ReadonlyRootFS bool
|
||||
ProxyEnv *ProxyEnv
|
||||
Secrets []SecretInfo
|
||||
SSH []SSHInfo
|
||||
}
|
||||
|
||||
type MountInfo struct {
|
||||
Target string
|
||||
Source Output
|
||||
Opts []MountOption
|
||||
}
|
||||
|
||||
type ProxyEnv struct {
|
||||
HttpProxy string
|
||||
HttpsProxy string
|
||||
FtpProxy string
|
||||
NoProxy string
|
||||
}
|
||||
|
||||
type CacheMountSharingMode int
|
||||
|
||||
const (
|
||||
CacheMountShared CacheMountSharingMode = iota
|
||||
CacheMountPrivate
|
||||
CacheMountLocked
|
||||
)
|
||||
|
||||
const (
|
||||
NetModeSandbox = pb.NetMode_UNSET
|
||||
NetModeHost = pb.NetMode_HOST
|
||||
NetModeNone = pb.NetMode_NONE
|
||||
)
|
727
vendor/github.com/moby/buildkit/client/llb/fileop.go
generated
vendored
Normal file
727
vendor/github.com/moby/buildkit/client/llb/fileop.go
generated
vendored
Normal file
@ -0,0 +1,727 @@
|
||||
package llb
|
||||
|
||||
import (
|
||||
_ "crypto/sha256"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Examples:
|
||||
// local := llb.Local(...)
|
||||
// llb.Image().Dir("/abc").File(Mkdir("./foo").Mkfile("/abc/foo/bar", []byte("data")))
|
||||
// llb.Image().File(Mkdir("/foo").Mkfile("/foo/bar", []byte("data")))
|
||||
// llb.Image().File(Copy(local, "/foo", "/bar")).File(Copy(local, "/foo2", "/bar2"))
|
||||
//
|
||||
// a := Mkdir("./foo") // *FileAction /ced/foo
|
||||
// b := Mkdir("./bar") // /abc/bar
|
||||
// c := b.Copy(a.WithState(llb.Scratch().Dir("/ced")), "./foo", "./baz") // /abc/baz
|
||||
// llb.Image().Dir("/abc").File(c)
|
||||
//
|
||||
// In future this can be extended to multiple outputs with:
|
||||
// a := Mkdir("./foo")
|
||||
// b, id := a.GetSelector()
|
||||
// c := b.Mkdir("./bar")
|
||||
// filestate = state.File(c)
|
||||
// filestate.GetOutput(id).Exec()
|
||||
|
||||
func NewFileOp(s State, action *FileAction, c Constraints) *FileOp {
|
||||
action = action.bind(s)
|
||||
|
||||
f := &FileOp{
|
||||
action: action,
|
||||
constraints: c,
|
||||
}
|
||||
|
||||
f.output = &output{vertex: f, getIndex: func() (pb.OutputIndex, error) {
|
||||
return pb.OutputIndex(0), nil
|
||||
}}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// CopyInput is either llb.State or *FileActionWithState
|
||||
type CopyInput interface {
|
||||
isFileOpCopyInput()
|
||||
}
|
||||
|
||||
type subAction interface {
|
||||
toProtoAction(string, pb.InputIndex) pb.IsFileAction
|
||||
}
|
||||
|
||||
type FileAction struct {
|
||||
state *State
|
||||
prev *FileAction
|
||||
action subAction
|
||||
err error
|
||||
}
|
||||
|
||||
func (fa *FileAction) Mkdir(p string, m os.FileMode, opt ...MkdirOption) *FileAction {
|
||||
a := Mkdir(p, m, opt...)
|
||||
a.prev = fa
|
||||
return a
|
||||
}
|
||||
|
||||
func (fa *FileAction) Mkfile(p string, m os.FileMode, dt []byte, opt ...MkfileOption) *FileAction {
|
||||
a := Mkfile(p, m, dt, opt...)
|
||||
a.prev = fa
|
||||
return a
|
||||
}
|
||||
|
||||
func (fa *FileAction) Rm(p string, opt ...RmOption) *FileAction {
|
||||
a := Rm(p, opt...)
|
||||
a.prev = fa
|
||||
return a
|
||||
}
|
||||
|
||||
func (fa *FileAction) Copy(input CopyInput, src, dest string, opt ...CopyOption) *FileAction {
|
||||
a := Copy(input, src, dest, opt...)
|
||||
a.prev = fa
|
||||
return a
|
||||
}
|
||||
|
||||
func (fa *FileAction) allOutputs(m map[Output]struct{}) {
|
||||
if fa == nil {
|
||||
return
|
||||
}
|
||||
if fa.state != nil && fa.state.Output() != nil {
|
||||
m[fa.state.Output()] = struct{}{}
|
||||
}
|
||||
|
||||
if a, ok := fa.action.(*fileActionCopy); ok {
|
||||
if a.state != nil {
|
||||
if out := a.state.Output(); out != nil {
|
||||
m[out] = struct{}{}
|
||||
}
|
||||
} else if a.fas != nil {
|
||||
a.fas.allOutputs(m)
|
||||
}
|
||||
}
|
||||
fa.prev.allOutputs(m)
|
||||
}
|
||||
|
||||
func (fa *FileAction) bind(s State) *FileAction {
|
||||
if fa == nil {
|
||||
return nil
|
||||
}
|
||||
fa2 := *fa
|
||||
fa2.prev = fa.prev.bind(s)
|
||||
fa2.state = &s
|
||||
return &fa2
|
||||
}
|
||||
|
||||
func (fa *FileAction) WithState(s State) CopyInput {
|
||||
return &fileActionWithState{FileAction: fa.bind(s)}
|
||||
}
|
||||
|
||||
type fileActionWithState struct {
|
||||
*FileAction
|
||||
}
|
||||
|
||||
func (fas *fileActionWithState) isFileOpCopyInput() {}
|
||||
|
||||
func Mkdir(p string, m os.FileMode, opt ...MkdirOption) *FileAction {
|
||||
var mi MkdirInfo
|
||||
for _, o := range opt {
|
||||
o.SetMkdirOption(&mi)
|
||||
}
|
||||
return &FileAction{
|
||||
action: &fileActionMkdir{
|
||||
file: p,
|
||||
mode: m,
|
||||
info: mi,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type fileActionMkdir struct {
|
||||
file string
|
||||
mode os.FileMode
|
||||
info MkdirInfo
|
||||
}
|
||||
|
||||
func (a *fileActionMkdir) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
|
||||
return &pb.FileAction_Mkdir{
|
||||
Mkdir: &pb.FileActionMkDir{
|
||||
Path: normalizePath(parent, a.file, false),
|
||||
Mode: int32(a.mode & 0777),
|
||||
MakeParents: a.info.MakeParents,
|
||||
Owner: a.info.ChownOpt.marshal(base),
|
||||
Timestamp: marshalTime(a.info.CreatedTime),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type MkdirOption interface {
|
||||
SetMkdirOption(*MkdirInfo)
|
||||
}
|
||||
|
||||
type ChownOption interface {
|
||||
MkdirOption
|
||||
MkfileOption
|
||||
CopyOption
|
||||
}
|
||||
|
||||
type mkdirOptionFunc func(*MkdirInfo)
|
||||
|
||||
func (fn mkdirOptionFunc) SetMkdirOption(mi *MkdirInfo) {
|
||||
fn(mi)
|
||||
}
|
||||
|
||||
var _ MkdirOption = &MkdirInfo{}
|
||||
|
||||
func WithParents(b bool) MkdirOption {
|
||||
return mkdirOptionFunc(func(mi *MkdirInfo) {
|
||||
mi.MakeParents = b
|
||||
})
|
||||
}
|
||||
|
||||
type MkdirInfo struct {
|
||||
MakeParents bool
|
||||
ChownOpt *ChownOpt
|
||||
CreatedTime *time.Time
|
||||
}
|
||||
|
||||
func (mi *MkdirInfo) SetMkdirOption(mi2 *MkdirInfo) {
|
||||
*mi2 = *mi
|
||||
}
|
||||
|
||||
func WithUser(name string) ChownOption {
|
||||
opt := ChownOpt{}
|
||||
|
||||
parts := strings.SplitN(name, ":", 2)
|
||||
for i, v := range parts {
|
||||
switch i {
|
||||
case 0:
|
||||
uid, err := parseUID(v)
|
||||
if err != nil {
|
||||
opt.User = &UserOpt{Name: v}
|
||||
} else {
|
||||
opt.User = &UserOpt{UID: uid}
|
||||
}
|
||||
case 1:
|
||||
gid, err := parseUID(v)
|
||||
if err != nil {
|
||||
opt.Group = &UserOpt{Name: v}
|
||||
} else {
|
||||
opt.Group = &UserOpt{UID: gid}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return opt
|
||||
}
|
||||
|
||||
func parseUID(str string) (int, error) {
|
||||
if str == "root" {
|
||||
return 0, nil
|
||||
}
|
||||
uid, err := strconv.ParseInt(str, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(uid), nil
|
||||
}
|
||||
|
||||
func WithUIDGID(uid, gid int) ChownOption {
|
||||
return ChownOpt{
|
||||
User: &UserOpt{UID: uid},
|
||||
Group: &UserOpt{UID: gid},
|
||||
}
|
||||
}
|
||||
|
||||
type ChownOpt struct {
|
||||
User *UserOpt
|
||||
Group *UserOpt
|
||||
}
|
||||
|
||||
func (co ChownOpt) SetMkdirOption(mi *MkdirInfo) {
|
||||
mi.ChownOpt = &co
|
||||
}
|
||||
func (co ChownOpt) SetMkfileOption(mi *MkfileInfo) {
|
||||
mi.ChownOpt = &co
|
||||
}
|
||||
func (co ChownOpt) SetCopyOption(mi *CopyInfo) {
|
||||
mi.ChownOpt = &co
|
||||
}
|
||||
|
||||
func (cp *ChownOpt) marshal(base pb.InputIndex) *pb.ChownOpt {
|
||||
if cp == nil {
|
||||
return nil
|
||||
}
|
||||
return &pb.ChownOpt{
|
||||
User: cp.User.marshal(base),
|
||||
Group: cp.Group.marshal(base),
|
||||
}
|
||||
}
|
||||
|
||||
type UserOpt struct {
|
||||
UID int
|
||||
Name string
|
||||
}
|
||||
|
||||
func (up *UserOpt) marshal(base pb.InputIndex) *pb.UserOpt {
|
||||
if up == nil {
|
||||
return nil
|
||||
}
|
||||
if up.Name != "" {
|
||||
return &pb.UserOpt{User: &pb.UserOpt_ByName{ByName: &pb.NamedUserOpt{
|
||||
Name: up.Name, Input: base}}}
|
||||
}
|
||||
return &pb.UserOpt{User: &pb.UserOpt_ByID{ByID: uint32(up.UID)}}
|
||||
}
|
||||
|
||||
func Mkfile(p string, m os.FileMode, dt []byte, opts ...MkfileOption) *FileAction {
|
||||
var mi MkfileInfo
|
||||
for _, o := range opts {
|
||||
o.SetMkfileOption(&mi)
|
||||
}
|
||||
|
||||
return &FileAction{
|
||||
action: &fileActionMkfile{
|
||||
file: p,
|
||||
mode: m,
|
||||
dt: dt,
|
||||
info: mi,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type MkfileOption interface {
|
||||
SetMkfileOption(*MkfileInfo)
|
||||
}
|
||||
|
||||
type MkfileInfo struct {
|
||||
ChownOpt *ChownOpt
|
||||
CreatedTime *time.Time
|
||||
}
|
||||
|
||||
func (mi *MkfileInfo) SetMkfileOption(mi2 *MkfileInfo) {
|
||||
*mi2 = *mi
|
||||
}
|
||||
|
||||
var _ MkfileOption = &MkfileInfo{}
|
||||
|
||||
type fileActionMkfile struct {
|
||||
file string
|
||||
mode os.FileMode
|
||||
dt []byte
|
||||
info MkfileInfo
|
||||
}
|
||||
|
||||
func (a *fileActionMkfile) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
|
||||
return &pb.FileAction_Mkfile{
|
||||
Mkfile: &pb.FileActionMkFile{
|
||||
Path: normalizePath(parent, a.file, false),
|
||||
Mode: int32(a.mode & 0777),
|
||||
Data: a.dt,
|
||||
Owner: a.info.ChownOpt.marshal(base),
|
||||
Timestamp: marshalTime(a.info.CreatedTime),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Rm(p string, opts ...RmOption) *FileAction {
|
||||
var mi RmInfo
|
||||
for _, o := range opts {
|
||||
o.SetRmOption(&mi)
|
||||
}
|
||||
|
||||
return &FileAction{
|
||||
action: &fileActionRm{
|
||||
file: p,
|
||||
info: mi,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type RmOption interface {
|
||||
SetRmOption(*RmInfo)
|
||||
}
|
||||
|
||||
type rmOptionFunc func(*RmInfo)
|
||||
|
||||
func (fn rmOptionFunc) SetRmOption(mi *RmInfo) {
|
||||
fn(mi)
|
||||
}
|
||||
|
||||
type RmInfo struct {
|
||||
AllowNotFound bool
|
||||
AllowWildcard bool
|
||||
}
|
||||
|
||||
func (mi *RmInfo) SetRmOption(mi2 *RmInfo) {
|
||||
*mi2 = *mi
|
||||
}
|
||||
|
||||
var _ RmOption = &RmInfo{}
|
||||
|
||||
func WithAllowNotFound(b bool) RmOption {
|
||||
return rmOptionFunc(func(mi *RmInfo) {
|
||||
mi.AllowNotFound = b
|
||||
})
|
||||
}
|
||||
|
||||
func WithAllowWildcard(b bool) RmOption {
|
||||
return rmOptionFunc(func(mi *RmInfo) {
|
||||
mi.AllowWildcard = b
|
||||
})
|
||||
}
|
||||
|
||||
type fileActionRm struct {
|
||||
file string
|
||||
info RmInfo
|
||||
}
|
||||
|
||||
func (a *fileActionRm) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
|
||||
return &pb.FileAction_Rm{
|
||||
Rm: &pb.FileActionRm{
|
||||
Path: normalizePath(parent, a.file, false),
|
||||
AllowNotFound: a.info.AllowNotFound,
|
||||
AllowWildcard: a.info.AllowWildcard,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func Copy(input CopyInput, src, dest string, opts ...CopyOption) *FileAction {
|
||||
var state *State
|
||||
var fas *fileActionWithState
|
||||
var err error
|
||||
if st, ok := input.(State); ok {
|
||||
state = &st
|
||||
} else if v, ok := input.(*fileActionWithState); ok {
|
||||
fas = v
|
||||
} else {
|
||||
err = errors.Errorf("invalid input type %T for copy", input)
|
||||
}
|
||||
|
||||
var mi CopyInfo
|
||||
for _, o := range opts {
|
||||
o.SetCopyOption(&mi)
|
||||
}
|
||||
|
||||
return &FileAction{
|
||||
action: &fileActionCopy{
|
||||
state: state,
|
||||
fas: fas,
|
||||
src: src,
|
||||
dest: dest,
|
||||
info: mi,
|
||||
},
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
type CopyOption interface {
|
||||
SetCopyOption(*CopyInfo)
|
||||
}
|
||||
|
||||
type CopyInfo struct {
|
||||
Mode *os.FileMode
|
||||
FollowSymlinks bool
|
||||
CopyDirContentsOnly bool
|
||||
AttemptUnpack bool
|
||||
CreateDestPath bool
|
||||
AllowWildcard bool
|
||||
AllowEmptyWildcard bool
|
||||
ChownOpt *ChownOpt
|
||||
CreatedTime *time.Time
|
||||
}
|
||||
|
||||
func (mi *CopyInfo) SetCopyOption(mi2 *CopyInfo) {
|
||||
*mi2 = *mi
|
||||
}
|
||||
|
||||
var _ CopyOption = &CopyInfo{}
|
||||
|
||||
type fileActionCopy struct {
|
||||
state *State
|
||||
fas *fileActionWithState
|
||||
src string
|
||||
dest string
|
||||
info CopyInfo
|
||||
}
|
||||
|
||||
func (a *fileActionCopy) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
|
||||
c := &pb.FileActionCopy{
|
||||
Src: a.sourcePath(),
|
||||
Dest: normalizePath(parent, a.dest, true),
|
||||
Owner: a.info.ChownOpt.marshal(base),
|
||||
AllowWildcard: a.info.AllowWildcard,
|
||||
AllowEmptyWildcard: a.info.AllowEmptyWildcard,
|
||||
FollowSymlink: a.info.FollowSymlinks,
|
||||
DirCopyContents: a.info.CopyDirContentsOnly,
|
||||
AttemptUnpackDockerCompatibility: a.info.AttemptUnpack,
|
||||
CreateDestPath: a.info.CreateDestPath,
|
||||
Timestamp: marshalTime(a.info.CreatedTime),
|
||||
}
|
||||
if a.info.Mode != nil {
|
||||
c.Mode = int32(*a.info.Mode)
|
||||
} else {
|
||||
c.Mode = -1
|
||||
}
|
||||
return &pb.FileAction_Copy{
|
||||
Copy: c,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *fileActionCopy) sourcePath() string {
|
||||
p := path.Clean(c.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)
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
type CreatedTime time.Time
|
||||
|
||||
func WithCreatedTime(t time.Time) CreatedTime {
|
||||
return CreatedTime(t)
|
||||
}
|
||||
|
||||
func (c CreatedTime) SetMkdirOption(mi *MkdirInfo) {
|
||||
mi.CreatedTime = (*time.Time)(&c)
|
||||
}
|
||||
|
||||
func (c CreatedTime) SetMkfileOption(mi *MkfileInfo) {
|
||||
mi.CreatedTime = (*time.Time)(&c)
|
||||
}
|
||||
|
||||
func (c CreatedTime) SetCopyOption(mi *CopyInfo) {
|
||||
mi.CreatedTime = (*time.Time)(&c)
|
||||
}
|
||||
|
||||
func marshalTime(t *time.Time) int64 {
|
||||
if t == nil {
|
||||
return -1
|
||||
}
|
||||
return t.UnixNano()
|
||||
}
|
||||
|
||||
type FileOp struct {
|
||||
MarshalCache
|
||||
action *FileAction
|
||||
output Output
|
||||
|
||||
constraints Constraints
|
||||
isValidated bool
|
||||
}
|
||||
|
||||
func (f *FileOp) Validate() error {
|
||||
if f.isValidated {
|
||||
return nil
|
||||
}
|
||||
if f.action == nil {
|
||||
return errors.Errorf("action is required")
|
||||
}
|
||||
f.isValidated = true
|
||||
return nil
|
||||
}
|
||||
|
||||
type marshalState struct {
|
||||
visited map[*FileAction]*fileActionState
|
||||
inputs []*pb.Input
|
||||
actions []*fileActionState
|
||||
}
|
||||
|
||||
func newMarshalState() *marshalState {
|
||||
return &marshalState{
|
||||
visited: map[*FileAction]*fileActionState{},
|
||||
}
|
||||
}
|
||||
|
||||
type fileActionState struct {
|
||||
base pb.InputIndex
|
||||
input pb.InputIndex
|
||||
inputRelative *int
|
||||
input2 pb.InputIndex
|
||||
input2Relative *int
|
||||
target int
|
||||
action subAction
|
||||
fa *FileAction
|
||||
}
|
||||
|
||||
func (ms *marshalState) addInput(st *fileActionState, c *Constraints, o Output) (pb.InputIndex, error) {
|
||||
inp, err := o.ToInput(c)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for i, inp2 := range ms.inputs {
|
||||
if *inp == *inp2 {
|
||||
return pb.InputIndex(i), nil
|
||||
}
|
||||
}
|
||||
i := pb.InputIndex(len(ms.inputs))
|
||||
ms.inputs = append(ms.inputs, inp)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (ms *marshalState) add(fa *FileAction, c *Constraints) (*fileActionState, error) {
|
||||
if st, ok := ms.visited[fa]; ok {
|
||||
return st, nil
|
||||
}
|
||||
|
||||
if fa.err != nil {
|
||||
return nil, fa.err
|
||||
}
|
||||
|
||||
var prevState *fileActionState
|
||||
if parent := fa.prev; parent != nil {
|
||||
var err error
|
||||
prevState, err = ms.add(parent, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
st := &fileActionState{
|
||||
action: fa.action,
|
||||
input: -1,
|
||||
input2: -1,
|
||||
base: -1,
|
||||
fa: fa,
|
||||
}
|
||||
|
||||
if source := fa.state.Output(); source != nil {
|
||||
inp, err := ms.addInput(st, c, source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st.base = inp
|
||||
}
|
||||
|
||||
if fa.prev == nil {
|
||||
st.input = st.base
|
||||
} else {
|
||||
st.inputRelative = &prevState.target
|
||||
}
|
||||
|
||||
if a, ok := fa.action.(*fileActionCopy); ok {
|
||||
if a.state != nil {
|
||||
if out := a.state.Output(); out != nil {
|
||||
inp, err := ms.addInput(st, c, out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st.input2 = inp
|
||||
}
|
||||
} else if a.fas != nil {
|
||||
src, err := ms.add(a.fas.FileAction, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st.input2Relative = &src.target
|
||||
} else {
|
||||
return nil, errors.Errorf("invalid empty source for copy")
|
||||
}
|
||||
}
|
||||
|
||||
st.target = len(ms.actions)
|
||||
|
||||
ms.visited[fa] = st
|
||||
ms.actions = append(ms.actions, st)
|
||||
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (f *FileOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) {
|
||||
if f.Cached(c) {
|
||||
return f.Load()
|
||||
}
|
||||
if err := f.Validate(); err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
addCap(&f.constraints, pb.CapFileBase)
|
||||
|
||||
pfo := &pb.FileOp{}
|
||||
|
||||
pop, md := MarshalConstraints(c, &f.constraints)
|
||||
pop.Op = &pb.Op_File{
|
||||
File: pfo,
|
||||
}
|
||||
|
||||
state := newMarshalState()
|
||||
_, err := state.add(f.action, c)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
pop.Inputs = state.inputs
|
||||
|
||||
for i, st := range state.actions {
|
||||
output := pb.OutputIndex(-1)
|
||||
if i+1 == len(state.actions) {
|
||||
output = 0
|
||||
}
|
||||
|
||||
var parent string
|
||||
if st.fa.state != nil {
|
||||
parent = st.fa.state.GetDir()
|
||||
}
|
||||
|
||||
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),
|
||||
})
|
||||
}
|
||||
|
||||
dt, err := pop.Marshal()
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
f.Store(dt, md, c)
|
||||
return f.Load()
|
||||
}
|
||||
|
||||
func normalizePath(parent, p string, keepSlash bool) string {
|
||||
origPath := p
|
||||
p = path.Clean(p)
|
||||
if !path.IsAbs(p) {
|
||||
p = path.Join("/", parent, p)
|
||||
}
|
||||
if keepSlash {
|
||||
if strings.HasSuffix(origPath, "/") && !strings.HasSuffix(p, "/") {
|
||||
p += "/"
|
||||
} else if strings.HasSuffix(origPath, "/.") {
|
||||
if p != "/" {
|
||||
p += "/"
|
||||
}
|
||||
p += "."
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (f *FileOp) Output() Output {
|
||||
return f.output
|
||||
}
|
||||
|
||||
func (f *FileOp) Inputs() (inputs []Output) {
|
||||
mm := map[Output]struct{}{}
|
||||
|
||||
f.action.allOutputs(mm)
|
||||
|
||||
for o := range mm {
|
||||
inputs = append(inputs, o)
|
||||
}
|
||||
return inputs
|
||||
}
|
||||
|
||||
func getIndex(input pb.InputIndex, len int, relative *int) pb.InputIndex {
|
||||
if relative != nil {
|
||||
return pb.InputIndex(len + *relative)
|
||||
}
|
||||
return input
|
||||
}
|
112
vendor/github.com/moby/buildkit/client/llb/marshal.go
generated
vendored
Normal file
112
vendor/github.com/moby/buildkit/client/llb/marshal.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
package llb
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// Definition is the LLB definition structure with per-vertex metadata entries
|
||||
// Corresponds to the Definition structure defined in solver/pb.Definition.
|
||||
type Definition struct {
|
||||
Def [][]byte
|
||||
Metadata map[digest.Digest]pb.OpMetadata
|
||||
}
|
||||
|
||||
func (def *Definition) ToPB() *pb.Definition {
|
||||
md := make(map[digest.Digest]pb.OpMetadata)
|
||||
for k, v := range def.Metadata {
|
||||
md[k] = v
|
||||
}
|
||||
return &pb.Definition{
|
||||
Def: def.Def,
|
||||
Metadata: md,
|
||||
}
|
||||
}
|
||||
|
||||
func (def *Definition) FromPB(x *pb.Definition) {
|
||||
def.Def = x.Def
|
||||
def.Metadata = make(map[digest.Digest]pb.OpMetadata)
|
||||
for k, v := range x.Metadata {
|
||||
def.Metadata[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func WriteTo(def *Definition, w io.Writer) error {
|
||||
b, err := def.ToPB().Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(b)
|
||||
return err
|
||||
}
|
||||
|
||||
func ReadFrom(r io.Reader) (*Definition, error) {
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbDef pb.Definition
|
||||
if err := pbDef.Unmarshal(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var def Definition
|
||||
def.FromPB(&pbDef)
|
||||
return &def, nil
|
||||
}
|
||||
|
||||
func MarshalConstraints(base, override *Constraints) (*pb.Op, *pb.OpMetadata) {
|
||||
c := *base
|
||||
c.WorkerConstraints = append([]string{}, c.WorkerConstraints...)
|
||||
|
||||
if p := override.Platform; p != nil {
|
||||
c.Platform = p
|
||||
}
|
||||
|
||||
for _, wc := range override.WorkerConstraints {
|
||||
c.WorkerConstraints = append(c.WorkerConstraints, wc)
|
||||
}
|
||||
|
||||
c.Metadata = mergeMetadata(c.Metadata, override.Metadata)
|
||||
|
||||
if c.Platform == nil {
|
||||
defaultPlatform := platforms.Normalize(platforms.DefaultSpec())
|
||||
c.Platform = &defaultPlatform
|
||||
}
|
||||
|
||||
return &pb.Op{
|
||||
Platform: &pb.Platform{
|
||||
OS: c.Platform.OS,
|
||||
Architecture: c.Platform.Architecture,
|
||||
Variant: c.Platform.Variant,
|
||||
OSVersion: c.Platform.OSVersion,
|
||||
OSFeatures: c.Platform.OSFeatures,
|
||||
},
|
||||
Constraints: &pb.WorkerConstraints{
|
||||
Filter: c.WorkerConstraints,
|
||||
},
|
||||
}, &c.Metadata
|
||||
}
|
||||
|
||||
type MarshalCache struct {
|
||||
digest digest.Digest
|
||||
dt []byte
|
||||
md *pb.OpMetadata
|
||||
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) Store(dt []byte, md *pb.OpMetadata, c *Constraints) {
|
||||
mc.digest = digest.FromBytes(dt)
|
||||
mc.dt = dt
|
||||
mc.md = md
|
||||
mc.constraints = c
|
||||
}
|
211
vendor/github.com/moby/buildkit/client/llb/meta.go
generated
vendored
Normal file
211
vendor/github.com/moby/buildkit/client/llb/meta.go
generated
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
package llb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/google/shlex"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type contextKeyT string
|
||||
|
||||
var (
|
||||
keyArgs = contextKeyT("llb.exec.args")
|
||||
keyDir = contextKeyT("llb.exec.dir")
|
||||
keyEnv = contextKeyT("llb.exec.env")
|
||||
keyUser = contextKeyT("llb.exec.user")
|
||||
keyExtraHost = contextKeyT("llb.exec.extrahost")
|
||||
keyPlatform = contextKeyT("llb.platform")
|
||||
keyNetwork = contextKeyT("llb.network")
|
||||
)
|
||||
|
||||
func addEnvf(key, value string, v ...interface{}) StateOption {
|
||||
return func(s State) State {
|
||||
return s.WithValue(keyEnv, getEnv(s).AddOrReplace(key, fmt.Sprintf(value, v...)))
|
||||
}
|
||||
}
|
||||
|
||||
func dir(str string) StateOption {
|
||||
return dirf(str)
|
||||
}
|
||||
|
||||
func dirf(str string, v ...interface{}) StateOption {
|
||||
return func(s State) State {
|
||||
value := fmt.Sprintf(str, v...)
|
||||
if !path.IsAbs(value) {
|
||||
prev := getDir(s)
|
||||
if prev == "" {
|
||||
prev = "/"
|
||||
}
|
||||
value = path.Join(prev, value)
|
||||
}
|
||||
return s.WithValue(keyDir, value)
|
||||
}
|
||||
}
|
||||
|
||||
func user(str string) StateOption {
|
||||
return func(s State) State {
|
||||
return s.WithValue(keyUser, str)
|
||||
}
|
||||
}
|
||||
|
||||
func reset(s_ State) StateOption {
|
||||
return func(s State) State {
|
||||
s = NewState(s.Output())
|
||||
s.ctx = s_.ctx
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func getEnv(s State) EnvList {
|
||||
v := s.Value(keyEnv)
|
||||
if v != nil {
|
||||
return v.(EnvList)
|
||||
}
|
||||
return EnvList{}
|
||||
}
|
||||
|
||||
func getDir(s State) string {
|
||||
v := s.Value(keyDir)
|
||||
if v != nil {
|
||||
return v.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getArgs(s State) []string {
|
||||
v := s.Value(keyArgs)
|
||||
if v != nil {
|
||||
return v.([]string)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUser(s State) string {
|
||||
v := s.Value(keyUser)
|
||||
if v != nil {
|
||||
return v.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func args(args ...string) StateOption {
|
||||
return func(s State) State {
|
||||
return s.WithValue(keyArgs, args)
|
||||
}
|
||||
}
|
||||
|
||||
func shlexf(str string, v ...interface{}) StateOption {
|
||||
return func(s State) State {
|
||||
arg, err := shlex.Split(fmt.Sprintf(str, v...))
|
||||
if err != nil {
|
||||
// TODO: handle error
|
||||
}
|
||||
return args(arg...)(s)
|
||||
}
|
||||
}
|
||||
|
||||
func platform(p specs.Platform) StateOption {
|
||||
return func(s State) State {
|
||||
return s.WithValue(keyPlatform, platforms.Normalize(p))
|
||||
}
|
||||
}
|
||||
|
||||
func getPlatform(s State) *specs.Platform {
|
||||
v := s.Value(keyPlatform)
|
||||
if v != nil {
|
||||
p := v.(specs.Platform)
|
||||
return &p
|
||||
}
|
||||
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}))
|
||||
}
|
||||
}
|
||||
|
||||
func getExtraHosts(s State) []HostIP {
|
||||
v := s.Value(keyExtraHost)
|
||||
if v != nil {
|
||||
return v.([]HostIP)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type HostIP struct {
|
||||
Host string
|
||||
IP net.IP
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return NetModeSandbox
|
||||
}
|
||||
|
||||
type EnvList []KeyValue
|
||||
|
||||
type KeyValue struct {
|
||||
key string
|
||||
value string
|
||||
}
|
||||
|
||||
func (e EnvList) AddOrReplace(k, v string) EnvList {
|
||||
e = e.Delete(k)
|
||||
e = append(e, KeyValue{key: k, value: v})
|
||||
return e
|
||||
}
|
||||
|
||||
func (e EnvList) SetDefault(k, v string) EnvList {
|
||||
if _, ok := e.Get(k); !ok {
|
||||
e = append(e, KeyValue{key: k, value: v})
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (e EnvList) Delete(k string) EnvList {
|
||||
e = append([]KeyValue(nil), e...)
|
||||
if i, ok := e.Index(k); ok {
|
||||
return append(e[:i], e[i+1:]...)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (e EnvList) Get(k string) (string, bool) {
|
||||
if index, ok := e.Index(k); ok {
|
||||
return e[index].value, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (e EnvList) Index(k string) (int, bool) {
|
||||
for i, kv := range e {
|
||||
if kv.key == k {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
func (e EnvList) ToArray() []string {
|
||||
out := make([]string, 0, len(e))
|
||||
for _, kv := range e {
|
||||
out = append(out, kv.key+"="+kv.value)
|
||||
}
|
||||
return out
|
||||
}
|
20
vendor/github.com/moby/buildkit/client/llb/resolver.go
generated
vendored
Normal file
20
vendor/github.com/moby/buildkit/client/llb/resolver.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
package llb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
gw "github.com/moby/buildkit/frontend/gateway/client"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
// WithMetaResolver adds a metadata resolver to an image
|
||||
func WithMetaResolver(mr ImageMetaResolver) ImageOption {
|
||||
return imageOptionFunc(func(ii *ImageInfo) {
|
||||
ii.metaResolver = mr
|
||||
})
|
||||
}
|
||||
|
||||
// ImageMetaResolver can resolve image config metadata from a reference
|
||||
type ImageMetaResolver interface {
|
||||
ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error)
|
||||
}
|
429
vendor/github.com/moby/buildkit/client/llb/source.go
generated
vendored
Normal file
429
vendor/github.com/moby/buildkit/client/llb/source.go
generated
vendored
Normal file
@ -0,0 +1,429 @@
|
||||
package llb
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "crypto/sha256"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
gw "github.com/moby/buildkit/frontend/gateway/client"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/apicaps"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type SourceOp struct {
|
||||
MarshalCache
|
||||
id string
|
||||
attrs map[string]string
|
||||
output Output
|
||||
constraints Constraints
|
||||
err error
|
||||
}
|
||||
|
||||
func NewSource(id string, attrs map[string]string, c Constraints) *SourceOp {
|
||||
s := &SourceOp{
|
||||
id: id,
|
||||
attrs: attrs,
|
||||
constraints: c,
|
||||
}
|
||||
s.output = &output{vertex: s, platform: c.Platform}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *SourceOp) Validate() error {
|
||||
if s.err != nil {
|
||||
return s.err
|
||||
}
|
||||
if s.id == "" {
|
||||
return errors.Errorf("source identifier can't be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SourceOp) Marshal(constraints *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) {
|
||||
if s.Cached(constraints) {
|
||||
return s.Load()
|
||||
}
|
||||
if err := s.Validate(); err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
if strings.HasPrefix(s.id, "local://") {
|
||||
if _, hasSession := s.attrs[pb.AttrLocalSessionID]; !hasSession {
|
||||
uid := s.constraints.LocalUniqueID
|
||||
if uid == "" {
|
||||
uid = constraints.LocalUniqueID
|
||||
}
|
||||
s.attrs[pb.AttrLocalUniqueID] = uid
|
||||
addCap(&s.constraints, pb.CapSourceLocalUnique)
|
||||
}
|
||||
}
|
||||
proto, md := MarshalConstraints(constraints, &s.constraints)
|
||||
|
||||
proto.Op = &pb.Op_Source{
|
||||
Source: &pb.SourceOp{Identifier: s.id, Attrs: s.attrs},
|
||||
}
|
||||
|
||||
if !platformSpecificSource(s.id) {
|
||||
proto.Platform = nil
|
||||
}
|
||||
|
||||
dt, err := proto.Marshal()
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
s.Store(dt, md, constraints)
|
||||
return s.Load()
|
||||
}
|
||||
|
||||
func (s *SourceOp) Output() Output {
|
||||
return s.output
|
||||
}
|
||||
|
||||
func (s *SourceOp) Inputs() []Output {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Image(ref string, opts ...ImageOption) State {
|
||||
r, err := reference.ParseNormalizedNamed(ref)
|
||||
if err == nil {
|
||||
ref = reference.TagNameOnly(r).String()
|
||||
}
|
||||
var info ImageInfo
|
||||
for _, opt := range opts {
|
||||
opt.SetImageOption(&info)
|
||||
}
|
||||
|
||||
addCap(&info.Constraints, pb.CapSourceImage)
|
||||
|
||||
attrs := map[string]string{}
|
||||
if info.resolveMode != 0 {
|
||||
attrs[pb.AttrImageResolveMode] = info.resolveMode.String()
|
||||
if info.resolveMode == ResolveModeForcePull {
|
||||
addCap(&info.Constraints, pb.CapSourceImageResolveMode) // only require cap for security enforced mode
|
||||
}
|
||||
}
|
||||
|
||||
if info.RecordType != "" {
|
||||
attrs[pb.AttrImageRecordType] = info.RecordType
|
||||
}
|
||||
|
||||
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, gw.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
|
||||
}
|
||||
}
|
||||
return NewState(src.Output())
|
||||
}
|
||||
|
||||
type ImageOption interface {
|
||||
SetImageOption(*ImageInfo)
|
||||
}
|
||||
|
||||
type imageOptionFunc func(*ImageInfo)
|
||||
|
||||
func (fn imageOptionFunc) SetImageOption(ii *ImageInfo) {
|
||||
fn(ii)
|
||||
}
|
||||
|
||||
var MarkImageInternal = imageOptionFunc(func(ii *ImageInfo) {
|
||||
ii.RecordType = "internal"
|
||||
})
|
||||
|
||||
type ResolveMode int
|
||||
|
||||
const (
|
||||
ResolveModeDefault ResolveMode = iota
|
||||
ResolveModeForcePull
|
||||
ResolveModePreferLocal
|
||||
)
|
||||
|
||||
func (r ResolveMode) SetImageOption(ii *ImageInfo) {
|
||||
ii.resolveMode = r
|
||||
}
|
||||
|
||||
func (r ResolveMode) String() string {
|
||||
switch r {
|
||||
case ResolveModeDefault:
|
||||
return pb.AttrImageResolveModeDefault
|
||||
case ResolveModeForcePull:
|
||||
return pb.AttrImageResolveModeForcePull
|
||||
case ResolveModePreferLocal:
|
||||
return pb.AttrImageResolveModePreferLocal
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type ImageInfo struct {
|
||||
constraintsWrapper
|
||||
metaResolver ImageMetaResolver
|
||||
resolveMode ResolveMode
|
||||
RecordType string
|
||||
}
|
||||
|
||||
func Git(remote, ref string, opts ...GitOption) State {
|
||||
url := ""
|
||||
|
||||
for _, prefix := range []string{
|
||||
"http://", "https://", "git://", "git@",
|
||||
} {
|
||||
if strings.HasPrefix(remote, prefix) {
|
||||
url = strings.Split(remote, "#")[0]
|
||||
remote = strings.TrimPrefix(remote, prefix)
|
||||
}
|
||||
}
|
||||
|
||||
id := remote
|
||||
|
||||
if ref != "" {
|
||||
id += "#" + ref
|
||||
}
|
||||
|
||||
gi := &GitInfo{}
|
||||
for _, o := range opts {
|
||||
o.SetGitOption(gi)
|
||||
}
|
||||
attrs := map[string]string{}
|
||||
if gi.KeepGitDir {
|
||||
attrs[pb.AttrKeepGitDir] = "true"
|
||||
addCap(&gi.Constraints, pb.CapSourceGitKeepDir)
|
||||
}
|
||||
if url != "" {
|
||||
attrs[pb.AttrFullRemoteURL] = url
|
||||
addCap(&gi.Constraints, pb.CapSourceGitFullURL)
|
||||
}
|
||||
|
||||
addCap(&gi.Constraints, pb.CapSourceGit)
|
||||
|
||||
source := NewSource("git://"+id, attrs, gi.Constraints)
|
||||
return NewState(source.Output())
|
||||
}
|
||||
|
||||
type GitOption interface {
|
||||
SetGitOption(*GitInfo)
|
||||
}
|
||||
type gitOptionFunc func(*GitInfo)
|
||||
|
||||
func (fn gitOptionFunc) SetGitOption(gi *GitInfo) {
|
||||
fn(gi)
|
||||
}
|
||||
|
||||
type GitInfo struct {
|
||||
constraintsWrapper
|
||||
KeepGitDir bool
|
||||
}
|
||||
|
||||
func KeepGitDir() GitOption {
|
||||
return gitOptionFunc(func(gi *GitInfo) {
|
||||
gi.KeepGitDir = true
|
||||
})
|
||||
}
|
||||
|
||||
func Scratch() State {
|
||||
return NewState(nil)
|
||||
}
|
||||
|
||||
func Local(name string, opts ...LocalOption) State {
|
||||
gi := &LocalInfo{}
|
||||
|
||||
for _, o := range opts {
|
||||
o.SetLocalOption(gi)
|
||||
}
|
||||
attrs := map[string]string{}
|
||||
if gi.SessionID != "" {
|
||||
attrs[pb.AttrLocalSessionID] = gi.SessionID
|
||||
addCap(&gi.Constraints, pb.CapSourceLocalSessionID)
|
||||
}
|
||||
if gi.IncludePatterns != "" {
|
||||
attrs[pb.AttrIncludePatterns] = gi.IncludePatterns
|
||||
addCap(&gi.Constraints, pb.CapSourceLocalIncludePatterns)
|
||||
}
|
||||
if gi.FollowPaths != "" {
|
||||
attrs[pb.AttrFollowPaths] = gi.FollowPaths
|
||||
addCap(&gi.Constraints, pb.CapSourceLocalFollowPaths)
|
||||
}
|
||||
if gi.ExcludePatterns != "" {
|
||||
attrs[pb.AttrExcludePatterns] = gi.ExcludePatterns
|
||||
addCap(&gi.Constraints, pb.CapSourceLocalExcludePatterns)
|
||||
}
|
||||
if gi.SharedKeyHint != "" {
|
||||
attrs[pb.AttrSharedKeyHint] = gi.SharedKeyHint
|
||||
addCap(&gi.Constraints, pb.CapSourceLocalSharedKeyHint)
|
||||
}
|
||||
|
||||
addCap(&gi.Constraints, pb.CapSourceLocal)
|
||||
|
||||
source := NewSource("local://"+name, attrs, gi.Constraints)
|
||||
return NewState(source.Output())
|
||||
}
|
||||
|
||||
type LocalOption interface {
|
||||
SetLocalOption(*LocalInfo)
|
||||
}
|
||||
|
||||
type localOptionFunc func(*LocalInfo)
|
||||
|
||||
func (fn localOptionFunc) SetLocalOption(li *LocalInfo) {
|
||||
fn(li)
|
||||
}
|
||||
|
||||
func SessionID(id string) LocalOption {
|
||||
return localOptionFunc(func(li *LocalInfo) {
|
||||
li.SessionID = id
|
||||
})
|
||||
}
|
||||
|
||||
func IncludePatterns(p []string) LocalOption {
|
||||
return localOptionFunc(func(li *LocalInfo) {
|
||||
if len(p) == 0 {
|
||||
li.IncludePatterns = ""
|
||||
return
|
||||
}
|
||||
dt, _ := json.Marshal(p) // empty on error
|
||||
li.IncludePatterns = string(dt)
|
||||
})
|
||||
}
|
||||
|
||||
func FollowPaths(p []string) LocalOption {
|
||||
return localOptionFunc(func(li *LocalInfo) {
|
||||
if len(p) == 0 {
|
||||
li.FollowPaths = ""
|
||||
return
|
||||
}
|
||||
dt, _ := json.Marshal(p) // empty on error
|
||||
li.FollowPaths = string(dt)
|
||||
})
|
||||
}
|
||||
|
||||
func ExcludePatterns(p []string) LocalOption {
|
||||
return localOptionFunc(func(li *LocalInfo) {
|
||||
if len(p) == 0 {
|
||||
li.ExcludePatterns = ""
|
||||
return
|
||||
}
|
||||
dt, _ := json.Marshal(p) // empty on error
|
||||
li.ExcludePatterns = string(dt)
|
||||
})
|
||||
}
|
||||
|
||||
func SharedKeyHint(h string) LocalOption {
|
||||
return localOptionFunc(func(li *LocalInfo) {
|
||||
li.SharedKeyHint = h
|
||||
})
|
||||
}
|
||||
|
||||
type LocalInfo struct {
|
||||
constraintsWrapper
|
||||
SessionID string
|
||||
IncludePatterns string
|
||||
ExcludePatterns string
|
||||
FollowPaths string
|
||||
SharedKeyHint string
|
||||
}
|
||||
|
||||
func HTTP(url string, opts ...HTTPOption) State {
|
||||
hi := &HTTPInfo{}
|
||||
for _, o := range opts {
|
||||
o.SetHTTPOption(hi)
|
||||
}
|
||||
attrs := map[string]string{}
|
||||
if hi.Checksum != "" {
|
||||
attrs[pb.AttrHTTPChecksum] = hi.Checksum.String()
|
||||
addCap(&hi.Constraints, pb.CapSourceHTTPChecksum)
|
||||
}
|
||||
if hi.Filename != "" {
|
||||
attrs[pb.AttrHTTPFilename] = hi.Filename
|
||||
}
|
||||
if hi.Perm != 0 {
|
||||
attrs[pb.AttrHTTPPerm] = "0" + strconv.FormatInt(int64(hi.Perm), 8)
|
||||
addCap(&hi.Constraints, pb.CapSourceHTTPPerm)
|
||||
}
|
||||
if hi.UID != 0 {
|
||||
attrs[pb.AttrHTTPUID] = strconv.Itoa(hi.UID)
|
||||
addCap(&hi.Constraints, pb.CapSourceHTTPUIDGID)
|
||||
}
|
||||
if hi.GID != 0 {
|
||||
attrs[pb.AttrHTTPGID] = strconv.Itoa(hi.GID)
|
||||
addCap(&hi.Constraints, pb.CapSourceHTTPUIDGID)
|
||||
}
|
||||
|
||||
addCap(&hi.Constraints, pb.CapSourceHTTP)
|
||||
source := NewSource(url, attrs, hi.Constraints)
|
||||
return NewState(source.Output())
|
||||
}
|
||||
|
||||
type HTTPInfo struct {
|
||||
constraintsWrapper
|
||||
Checksum digest.Digest
|
||||
Filename string
|
||||
Perm int
|
||||
UID int
|
||||
GID int
|
||||
}
|
||||
|
||||
type HTTPOption interface {
|
||||
SetHTTPOption(*HTTPInfo)
|
||||
}
|
||||
|
||||
type httpOptionFunc func(*HTTPInfo)
|
||||
|
||||
func (fn httpOptionFunc) SetHTTPOption(hi *HTTPInfo) {
|
||||
fn(hi)
|
||||
}
|
||||
|
||||
func Checksum(dgst digest.Digest) HTTPOption {
|
||||
return httpOptionFunc(func(hi *HTTPInfo) {
|
||||
hi.Checksum = dgst
|
||||
})
|
||||
}
|
||||
|
||||
func Chmod(perm os.FileMode) HTTPOption {
|
||||
return httpOptionFunc(func(hi *HTTPInfo) {
|
||||
hi.Perm = int(perm) & 0777
|
||||
})
|
||||
}
|
||||
|
||||
func Filename(name string) HTTPOption {
|
||||
return httpOptionFunc(func(hi *HTTPInfo) {
|
||||
hi.Filename = name
|
||||
})
|
||||
}
|
||||
|
||||
func Chown(uid, gid int) HTTPOption {
|
||||
return httpOptionFunc(func(hi *HTTPInfo) {
|
||||
hi.UID = uid
|
||||
hi.GID = gid
|
||||
})
|
||||
}
|
||||
|
||||
func platformSpecificSource(id string) bool {
|
||||
return strings.HasPrefix(id, "docker-image://")
|
||||
}
|
||||
|
||||
func addCap(c *Constraints, id apicaps.CapID) {
|
||||
if c.Metadata.Caps == nil {
|
||||
c.Metadata.Caps = make(map[apicaps.CapID]bool)
|
||||
}
|
||||
c.Metadata.Caps[id] = true
|
||||
}
|
507
vendor/github.com/moby/buildkit/client/llb/state.go
generated
vendored
Normal file
507
vendor/github.com/moby/buildkit/client/llb/state.go
generated
vendored
Normal file
@ -0,0 +1,507 @@
|
||||
package llb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/moby/buildkit/identity"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/apicaps"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type StateOption func(State) State
|
||||
|
||||
type Output interface {
|
||||
ToInput(*Constraints) (*pb.Input, error)
|
||||
Vertex() Vertex
|
||||
}
|
||||
|
||||
type Vertex interface {
|
||||
Validate() error
|
||||
Marshal(*Constraints) (digest.Digest, []byte, *pb.OpMetadata, error)
|
||||
Output() Output
|
||||
Inputs() []Output
|
||||
}
|
||||
|
||||
func NewState(o Output) State {
|
||||
s := State{
|
||||
out: o,
|
||||
ctx: context.Background(),
|
||||
}
|
||||
s = dir("/")(s)
|
||||
s = s.ensurePlatform()
|
||||
return s
|
||||
}
|
||||
|
||||
type State struct {
|
||||
out Output
|
||||
ctx context.Context
|
||||
opts []ConstraintsOpt
|
||||
}
|
||||
|
||||
func (s State) ensurePlatform() State {
|
||||
if o, ok := s.out.(interface {
|
||||
Platform() *specs.Platform
|
||||
}); ok {
|
||||
if p := o.Platform(); p != nil {
|
||||
s = platform(*p)(s)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s State) WithValue(k, v interface{}) State {
|
||||
return State{
|
||||
out: s.out,
|
||||
ctx: context.WithValue(s.ctx, k, v),
|
||||
}
|
||||
}
|
||||
|
||||
func (s State) Value(k interface{}) interface{} {
|
||||
return s.ctx.Value(k)
|
||||
}
|
||||
|
||||
func (s State) SetMarshalDefaults(co ...ConstraintsOpt) State {
|
||||
s.opts = co
|
||||
return s
|
||||
}
|
||||
|
||||
func (s State) Marshal(co ...ConstraintsOpt) (*Definition, error) {
|
||||
def := &Definition{
|
||||
Metadata: make(map[digest.Digest]pb.OpMetadata, 0),
|
||||
}
|
||||
if s.Output() == nil {
|
||||
return def, nil
|
||||
}
|
||||
|
||||
defaultPlatform := platforms.Normalize(platforms.DefaultSpec())
|
||||
c := &Constraints{
|
||||
Platform: &defaultPlatform,
|
||||
LocalUniqueID: identity.NewID(),
|
||||
}
|
||||
for _, o := range append(s.opts, co...) {
|
||||
o.SetConstraintsOption(c)
|
||||
}
|
||||
|
||||
def, err := marshal(s.Output().Vertex(), def, map[digest.Digest]struct{}{}, map[Vertex]struct{}{}, c)
|
||||
if err != nil {
|
||||
return def, err
|
||||
}
|
||||
inp, err := s.Output().ToInput(c)
|
||||
if err != nil {
|
||||
return def, err
|
||||
}
|
||||
proto := &pb.Op{Inputs: []*pb.Input{inp}}
|
||||
dt, err := proto.Marshal()
|
||||
if err != nil {
|
||||
return def, err
|
||||
}
|
||||
def.Def = append(def.Def, dt)
|
||||
|
||||
dgst := digest.FromBytes(dt)
|
||||
md := def.Metadata[dgst]
|
||||
md.Caps = map[apicaps.CapID]bool{
|
||||
pb.CapConstraints: true,
|
||||
pb.CapPlatform: true,
|
||||
}
|
||||
|
||||
for _, m := range def.Metadata {
|
||||
if m.IgnoreCache {
|
||||
md.Caps[pb.CapMetaIgnoreCache] = true
|
||||
}
|
||||
if m.Description != nil {
|
||||
md.Caps[pb.CapMetaDescription] = true
|
||||
}
|
||||
if m.ExportCache != nil {
|
||||
md.Caps[pb.CapMetaExportCache] = true
|
||||
}
|
||||
}
|
||||
|
||||
def.Metadata[dgst] = md
|
||||
|
||||
return def, nil
|
||||
}
|
||||
|
||||
func marshal(v Vertex, def *Definition, 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)
|
||||
if err != nil {
|
||||
return def, err
|
||||
}
|
||||
}
|
||||
|
||||
dgst, dt, opMeta, err := v.Marshal(c)
|
||||
if err != nil {
|
||||
return def, err
|
||||
}
|
||||
vertexCache[v] = struct{}{}
|
||||
if opMeta != nil {
|
||||
def.Metadata[dgst] = mergeMetadata(def.Metadata[dgst], *opMeta)
|
||||
}
|
||||
if _, ok := cache[dgst]; ok {
|
||||
return def, nil
|
||||
}
|
||||
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) Output() Output {
|
||||
return s.out
|
||||
}
|
||||
|
||||
func (s State) WithOutput(o Output) State {
|
||||
s = State{
|
||||
out: o,
|
||||
ctx: s.ctx,
|
||||
}
|
||||
s = s.ensurePlatform()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s State) WithImageConfig(c []byte) (State, error) {
|
||||
var img struct {
|
||||
Config struct {
|
||||
Env []string `json:"Env,omitempty"`
|
||||
WorkingDir string `json:"WorkingDir,omitempty"`
|
||||
User string `json:"User,omitempty"`
|
||||
} `json:"config,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal(c, &img); err != nil {
|
||||
return State{}, err
|
||||
}
|
||||
for _, env := range img.Config.Env {
|
||||
parts := strings.SplitN(env, "=", 2)
|
||||
if len(parts[0]) > 0 {
|
||||
var v string
|
||||
if len(parts) > 1 {
|
||||
v = parts[1]
|
||||
}
|
||||
s = s.AddEnv(parts[0], v)
|
||||
}
|
||||
}
|
||||
s = s.Dir(img.Config.WorkingDir)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
exec := NewExecOp(s.Output(), meta, ei.ReadonlyRootFS, ei.Constraints)
|
||||
for _, m := range ei.Mounts {
|
||||
exec.AddMount(m.Target, m.Source, m.Opts...)
|
||||
}
|
||||
exec.secrets = ei.Secrets
|
||||
exec.ssh = ei.SSH
|
||||
|
||||
return ExecState{
|
||||
State: s.WithOutput(exec.Output()),
|
||||
exec: exec,
|
||||
}
|
||||
}
|
||||
|
||||
func (s State) File(a *FileAction, opts ...ConstraintsOpt) State {
|
||||
var c Constraints
|
||||
for _, o := range opts {
|
||||
o.SetConstraintsOption(&c)
|
||||
}
|
||||
|
||||
return s.WithOutput(NewFileOp(s, a, c).Output())
|
||||
}
|
||||
|
||||
func (s State) AddEnv(key, value string) State {
|
||||
return s.AddEnvf(key, value)
|
||||
}
|
||||
|
||||
func (s State) AddEnvf(key, value string, v ...interface{}) State {
|
||||
return addEnvf(key, value, v...)(s)
|
||||
}
|
||||
|
||||
func (s State) Dir(str string) State {
|
||||
return s.Dirf(str)
|
||||
}
|
||||
func (s State) Dirf(str string, v ...interface{}) State {
|
||||
return dirf(str, v...)(s)
|
||||
}
|
||||
|
||||
func (s State) GetEnv(key string) (string, bool) {
|
||||
return getEnv(s).Get(key)
|
||||
}
|
||||
|
||||
func (s State) Env() []string {
|
||||
return getEnv(s).ToArray()
|
||||
}
|
||||
|
||||
func (s State) GetDir() string {
|
||||
return getDir(s)
|
||||
}
|
||||
|
||||
func (s State) GetArgs() []string {
|
||||
return getArgs(s)
|
||||
}
|
||||
|
||||
func (s State) Reset(s2 State) State {
|
||||
return reset(s2)(s)
|
||||
}
|
||||
|
||||
func (s State) User(v string) State {
|
||||
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) Network(n pb.NetMode) State {
|
||||
return network(n)(s)
|
||||
}
|
||||
|
||||
func (s State) GetNetwork() pb.NetMode {
|
||||
return getNetwork(s)
|
||||
}
|
||||
|
||||
func (s State) With(so ...StateOption) State {
|
||||
for _, o := range so {
|
||||
s = o(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s State) AddExtraHost(host string, ip net.IP) State {
|
||||
return extraHost(host, ip)(s)
|
||||
}
|
||||
|
||||
func (s State) isFileOpCopyInput() {}
|
||||
|
||||
type output struct {
|
||||
vertex Vertex
|
||||
getIndex func() (pb.OutputIndex, error)
|
||||
err error
|
||||
platform *specs.Platform
|
||||
}
|
||||
|
||||
func (o *output) ToInput(c *Constraints) (*pb.Input, error) {
|
||||
if o.err != nil {
|
||||
return nil, o.err
|
||||
}
|
||||
var index pb.OutputIndex
|
||||
if o.getIndex != nil {
|
||||
var err error
|
||||
index, err = o.getIndex()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
dgst, _, _, err := o.vertex.Marshal(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pb.Input{Digest: dgst, Index: index}, nil
|
||||
}
|
||||
|
||||
func (o *output) Vertex() Vertex {
|
||||
return o.vertex
|
||||
}
|
||||
|
||||
func (o *output) Platform() *specs.Platform {
|
||||
return o.platform
|
||||
}
|
||||
|
||||
type ConstraintsOpt interface {
|
||||
SetConstraintsOption(*Constraints)
|
||||
RunOption
|
||||
LocalOption
|
||||
HTTPOption
|
||||
ImageOption
|
||||
GitOption
|
||||
}
|
||||
|
||||
type constraintsOptFunc func(m *Constraints)
|
||||
|
||||
func (fn constraintsOptFunc) SetConstraintsOption(m *Constraints) {
|
||||
fn(m)
|
||||
}
|
||||
|
||||
func (fn constraintsOptFunc) SetRunOption(ei *ExecInfo) {
|
||||
ei.applyConstraints(fn)
|
||||
}
|
||||
|
||||
func (fn constraintsOptFunc) SetLocalOption(li *LocalInfo) {
|
||||
li.applyConstraints(fn)
|
||||
}
|
||||
|
||||
func (fn constraintsOptFunc) SetHTTPOption(hi *HTTPInfo) {
|
||||
hi.applyConstraints(fn)
|
||||
}
|
||||
|
||||
func (fn constraintsOptFunc) SetImageOption(ii *ImageInfo) {
|
||||
ii.applyConstraints(fn)
|
||||
}
|
||||
|
||||
func (fn constraintsOptFunc) SetGitOption(gi *GitInfo) {
|
||||
gi.applyConstraints(fn)
|
||||
}
|
||||
|
||||
func mergeMetadata(m1, m2 pb.OpMetadata) pb.OpMetadata {
|
||||
if m2.IgnoreCache {
|
||||
m1.IgnoreCache = true
|
||||
}
|
||||
if len(m2.Description) > 0 {
|
||||
if m1.Description == nil {
|
||||
m1.Description = make(map[string]string)
|
||||
}
|
||||
for k, v := range m2.Description {
|
||||
m1.Description[k] = v
|
||||
}
|
||||
}
|
||||
if m2.ExportCache != nil {
|
||||
m1.ExportCache = m2.ExportCache
|
||||
}
|
||||
|
||||
for k := range m2.Caps {
|
||||
if m1.Caps == nil {
|
||||
m1.Caps = make(map[apicaps.CapID]bool, len(m2.Caps))
|
||||
}
|
||||
m1.Caps[k] = true
|
||||
}
|
||||
|
||||
return m1
|
||||
}
|
||||
|
||||
var IgnoreCache = constraintsOptFunc(func(c *Constraints) {
|
||||
c.Metadata.IgnoreCache = true
|
||||
})
|
||||
|
||||
func WithDescription(m map[string]string) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
if c.Metadata.Description == nil {
|
||||
c.Metadata.Description = map[string]string{}
|
||||
}
|
||||
for k, v := range m {
|
||||
c.Metadata.Description[k] = v
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func WithCustomName(name string) ConstraintsOpt {
|
||||
return WithDescription(map[string]string{
|
||||
"llb.customname": name,
|
||||
})
|
||||
}
|
||||
|
||||
func WithCustomNamef(name string, a ...interface{}) ConstraintsOpt {
|
||||
return WithCustomName(fmt.Sprintf(name, a...))
|
||||
}
|
||||
|
||||
// WithExportCache forces results for this vertex to be exported with the cache
|
||||
func WithExportCache() ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
c.Metadata.ExportCache = &pb.ExportCache{Value: true}
|
||||
})
|
||||
}
|
||||
|
||||
// WithoutExportCache sets results for this vertex to be not exported with
|
||||
// the cache
|
||||
func WithoutExportCache() ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
// ExportCache with value false means to disable exporting
|
||||
c.Metadata.ExportCache = &pb.ExportCache{Value: false}
|
||||
})
|
||||
}
|
||||
|
||||
// WithoutDefaultExportCache resets the cache export for the vertex to use
|
||||
// the default defined by the build configuration.
|
||||
func WithoutDefaultExportCache() ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
// nil means no vertex based config has been set
|
||||
c.Metadata.ExportCache = nil
|
||||
})
|
||||
}
|
||||
|
||||
// WithCaps exposes supported LLB caps to the marshaler
|
||||
func WithCaps(caps apicaps.CapSet) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
c.Caps = &caps
|
||||
})
|
||||
}
|
||||
|
||||
type constraintsWrapper struct {
|
||||
Constraints
|
||||
}
|
||||
|
||||
func (cw *constraintsWrapper) applyConstraints(f func(c *Constraints)) {
|
||||
f(&cw.Constraints)
|
||||
}
|
||||
|
||||
type Constraints struct {
|
||||
Platform *specs.Platform
|
||||
WorkerConstraints []string
|
||||
Metadata pb.OpMetadata
|
||||
LocalUniqueID string
|
||||
Caps *apicaps.CapSet
|
||||
}
|
||||
|
||||
func Platform(p specs.Platform) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
c.Platform = &p
|
||||
})
|
||||
}
|
||||
|
||||
func LocalUniqueID(v string) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
c.LocalUniqueID = v
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
LinuxAmd64 = Platform(specs.Platform{OS: "linux", Architecture: "amd64"})
|
||||
LinuxArmhf = Platform(specs.Platform{OS: "linux", Architecture: "arm", Variant: "v7"})
|
||||
LinuxArm = LinuxArmhf
|
||||
LinuxArmel = Platform(specs.Platform{OS: "linux", Architecture: "arm", Variant: "v6"})
|
||||
LinuxArm64 = Platform(specs.Platform{OS: "linux", Architecture: "arm64"})
|
||||
LinuxS390x = Platform(specs.Platform{OS: "linux", Architecture: "s390x"})
|
||||
LinuxPpc64le = Platform(specs.Platform{OS: "linux", Architecture: "ppc64le"})
|
||||
Darwin = Platform(specs.Platform{OS: "darwin", Architecture: "amd64"})
|
||||
Windows = Platform(specs.Platform{OS: "windows", Architecture: "amd64"})
|
||||
)
|
||||
|
||||
func Require(filters ...string) ConstraintsOpt {
|
||||
return constraintsOptFunc(func(c *Constraints) {
|
||||
for _, f := range filters {
|
||||
c.WorkerConstraints = append(c.WorkerConstraints, f)
|
||||
}
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user