mirror of
https://github.com/mudler/luet.git
synced 2025-09-03 16:25:19 +00:00
Update gomod and vendor
This commit is contained in:
106
vendor/github.com/moby/buildkit/cache/remotecache/inline/inline.go
generated
vendored
106
vendor/github.com/moby/buildkit/cache/remotecache/inline/inline.go
generated
vendored
@@ -1,106 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/moby/buildkit/cache/remotecache"
|
||||
v1 "github.com/moby/buildkit/cache/remotecache/v1"
|
||||
"github.com/moby/buildkit/solver"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func ResolveCacheExporterFunc() remotecache.ResolveCacheExporterFunc {
|
||||
return func(ctx context.Context, _ map[string]string) (remotecache.Exporter, error) {
|
||||
return NewExporter(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewExporter() remotecache.Exporter {
|
||||
cc := v1.NewCacheChains()
|
||||
return &exporter{CacheExporterTarget: cc, chains: cc}
|
||||
}
|
||||
|
||||
type exporter struct {
|
||||
solver.CacheExporterTarget
|
||||
chains *v1.CacheChains
|
||||
}
|
||||
|
||||
func (ce *exporter) Finalize(ctx context.Context) (map[string]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ce *exporter) reset() {
|
||||
cc := v1.NewCacheChains()
|
||||
ce.CacheExporterTarget = cc
|
||||
ce.chains = cc
|
||||
}
|
||||
|
||||
func (ce *exporter) ExportForLayers(layers []digest.Digest) ([]byte, error) {
|
||||
config, descs, err := ce.chains.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
descs2 := map[digest.Digest]v1.DescriptorProviderPair{}
|
||||
for _, k := range layers {
|
||||
if v, ok := descs[k]; ok {
|
||||
descs2[k] = v
|
||||
continue
|
||||
}
|
||||
// fallback for uncompressed digests
|
||||
for _, v := range descs {
|
||||
if uc := v.Descriptor.Annotations["containerd.io/uncompressed"]; uc == string(k) {
|
||||
descs2[v.Descriptor.Digest] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cc := v1.NewCacheChains()
|
||||
if err := v1.ParseConfig(*config, descs2, cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg, _, err := cc.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(cfg.Layers) == 0 {
|
||||
logrus.Warn("failed to match any cache with layers")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cache := map[int]int{}
|
||||
|
||||
// reorder layers based on the order in the image
|
||||
for i, r := range cfg.Records {
|
||||
for j, rr := range r.Results {
|
||||
n := getSortedLayerIndex(rr.LayerIndex, cfg.Layers, cache)
|
||||
rr.LayerIndex = n
|
||||
r.Results[j] = rr
|
||||
cfg.Records[i] = r
|
||||
}
|
||||
}
|
||||
|
||||
dt, err := json.Marshal(cfg.Records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ce.reset()
|
||||
|
||||
return dt, nil
|
||||
}
|
||||
|
||||
func getSortedLayerIndex(idx int, layers []v1.CacheLayer, cache map[int]int) int {
|
||||
if idx == -1 {
|
||||
return -1
|
||||
}
|
||||
l := layers[idx]
|
||||
if i, ok := cache[idx]; ok {
|
||||
return i
|
||||
}
|
||||
cache[idx] = getSortedLayerIndex(l.ParentIndex, layers, cache) + 1
|
||||
return cache[idx]
|
||||
}
|
83
vendor/github.com/moby/buildkit/cache/remotecache/local/local.go
generated
vendored
83
vendor/github.com/moby/buildkit/cache/remotecache/local/local.go
generated
vendored
@@ -1,83 +0,0 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/moby/buildkit/cache/remotecache"
|
||||
"github.com/moby/buildkit/session"
|
||||
sessioncontent "github.com/moby/buildkit/session/content"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
attrDigest = "digest"
|
||||
attrSrc = "src"
|
||||
attrDest = "dest"
|
||||
contentStoreIDPrefix = "local:"
|
||||
)
|
||||
|
||||
// ResolveCacheExporterFunc for "local" cache exporter.
|
||||
func ResolveCacheExporterFunc(sm *session.Manager) remotecache.ResolveCacheExporterFunc {
|
||||
return func(ctx context.Context, attrs map[string]string) (remotecache.Exporter, error) {
|
||||
store := attrs[attrDest]
|
||||
if store == "" {
|
||||
return nil, errors.New("local cache exporter requires dest")
|
||||
}
|
||||
csID := contentStoreIDPrefix + store
|
||||
cs, err := getContentStore(ctx, sm, csID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return remotecache.NewExporter(cs), nil
|
||||
}
|
||||
}
|
||||
|
||||
// ResolveCacheImporterFunc for "local" cache importer.
|
||||
func ResolveCacheImporterFunc(sm *session.Manager) remotecache.ResolveCacheImporterFunc {
|
||||
return func(ctx context.Context, attrs map[string]string) (remotecache.Importer, specs.Descriptor, error) {
|
||||
dgstStr := attrs[attrDigest]
|
||||
if dgstStr == "" {
|
||||
return nil, specs.Descriptor{}, errors.New("local cache importer requires explicit digest")
|
||||
}
|
||||
dgst := digest.Digest(dgstStr)
|
||||
store := attrs[attrSrc]
|
||||
if store == "" {
|
||||
return nil, specs.Descriptor{}, errors.New("local cache importer requires src")
|
||||
}
|
||||
csID := contentStoreIDPrefix + store
|
||||
cs, err := getContentStore(ctx, sm, csID)
|
||||
if err != nil {
|
||||
return nil, specs.Descriptor{}, err
|
||||
}
|
||||
info, err := cs.Info(ctx, dgst)
|
||||
if err != nil {
|
||||
return nil, specs.Descriptor{}, err
|
||||
}
|
||||
desc := specs.Descriptor{
|
||||
// MediaType is typically MediaTypeDockerSchema2ManifestList,
|
||||
// but we leave it empty until we get correct support for local index.json
|
||||
Digest: dgst,
|
||||
Size: info.Size,
|
||||
}
|
||||
return remotecache.NewImporter(cs), desc, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getContentStore(ctx context.Context, sm *session.Manager, storeID string) (content.Store, error) {
|
||||
sessionID := session.FromContext(ctx)
|
||||
if sessionID == "" {
|
||||
return nil, errors.New("local cache exporter/importer requires session")
|
||||
}
|
||||
timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
caller, err := sm.Get(timeoutCtx, sessionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sessioncontent.NewCallerStore(caller, storeID), nil
|
||||
}
|
96
vendor/github.com/moby/buildkit/cache/remotecache/registry/registry.go
generated
vendored
96
vendor/github.com/moby/buildkit/cache/remotecache/registry/registry.go
generated
vendored
@@ -1,96 +0,0 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/remotes/docker"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/moby/buildkit/cache/remotecache"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/util/contentutil"
|
||||
"github.com/moby/buildkit/util/resolver"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func canonicalizeRef(rawRef string) (string, error) {
|
||||
if rawRef == "" {
|
||||
return "", errors.New("missing ref")
|
||||
}
|
||||
parsed, err := reference.ParseNormalizedNamed(rawRef)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return reference.TagNameOnly(parsed).String(), nil
|
||||
}
|
||||
|
||||
const (
|
||||
attrRef = "ref"
|
||||
)
|
||||
|
||||
func ResolveCacheExporterFunc(sm *session.Manager, hosts docker.RegistryHosts) remotecache.ResolveCacheExporterFunc {
|
||||
return func(ctx context.Context, attrs map[string]string) (remotecache.Exporter, error) {
|
||||
ref, err := canonicalizeRef(attrs[attrRef])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
remote := resolver.New(ctx, hosts, sm)
|
||||
pusher, err := remote.Pusher(ctx, ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return remotecache.NewExporter(contentutil.FromPusher(pusher)), nil
|
||||
}
|
||||
}
|
||||
|
||||
func ResolveCacheImporterFunc(sm *session.Manager, cs content.Store, hosts docker.RegistryHosts) remotecache.ResolveCacheImporterFunc {
|
||||
return func(ctx context.Context, attrs map[string]string) (remotecache.Importer, specs.Descriptor, error) {
|
||||
ref, err := canonicalizeRef(attrs[attrRef])
|
||||
if err != nil {
|
||||
return nil, specs.Descriptor{}, err
|
||||
}
|
||||
remote := resolver.New(ctx, hosts, sm)
|
||||
xref, desc, err := remote.Resolve(ctx, ref)
|
||||
if err != nil {
|
||||
return nil, specs.Descriptor{}, err
|
||||
}
|
||||
fetcher, err := remote.Fetcher(ctx, xref)
|
||||
if err != nil {
|
||||
return nil, specs.Descriptor{}, err
|
||||
}
|
||||
src := &withDistributionSourceLabel{
|
||||
Provider: contentutil.FromFetcher(fetcher),
|
||||
ref: ref,
|
||||
source: cs,
|
||||
}
|
||||
return remotecache.NewImporter(src), desc, nil
|
||||
}
|
||||
}
|
||||
|
||||
type withDistributionSourceLabel struct {
|
||||
content.Provider
|
||||
ref string
|
||||
source content.Manager
|
||||
}
|
||||
|
||||
var _ remotecache.DistributionSourceLabelSetter = &withDistributionSourceLabel{}
|
||||
|
||||
func (dsl *withDistributionSourceLabel) SetDistributionSourceLabel(ctx context.Context, dgst digest.Digest) error {
|
||||
hf, err := docker.AppendDistributionSourceLabel(dsl.source, dsl.ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = hf(ctx, ocispec.Descriptor{Digest: dgst})
|
||||
return err
|
||||
}
|
||||
|
||||
func (dsl *withDistributionSourceLabel) SetDistributionSourceAnnotation(desc ocispec.Descriptor) ocispec.Descriptor {
|
||||
if desc.Annotations == nil {
|
||||
desc.Annotations = map[string]string{}
|
||||
}
|
||||
desc.Annotations["containerd.io/distribution.source.ref"] = dsl.ref
|
||||
return desc
|
||||
}
|
109
vendor/github.com/moby/buildkit/client/llb/imagemetaresolver/resolver.go
generated
vendored
109
vendor/github.com/moby/buildkit/client/llb/imagemetaresolver/resolver.go
generated
vendored
@@ -1,109 +0,0 @@
|
||||
package imagemetaresolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/remotes"
|
||||
"github.com/containerd/containerd/remotes/docker"
|
||||
"github.com/docker/docker/pkg/locker"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/util/contentutil"
|
||||
"github.com/moby/buildkit/util/imageutil"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
var defaultImageMetaResolver llb.ImageMetaResolver
|
||||
var defaultImageMetaResolverOnce sync.Once
|
||||
|
||||
var WithDefault = imageOptionFunc(func(ii *llb.ImageInfo) {
|
||||
llb.WithMetaResolver(Default()).SetImageOption(ii)
|
||||
})
|
||||
|
||||
type imageMetaResolverOpts struct {
|
||||
platform *specs.Platform
|
||||
}
|
||||
|
||||
type ImageMetaResolverOpt func(o *imageMetaResolverOpts)
|
||||
|
||||
func WithDefaultPlatform(p *specs.Platform) ImageMetaResolverOpt {
|
||||
return func(o *imageMetaResolverOpts) {
|
||||
o.platform = p
|
||||
}
|
||||
}
|
||||
|
||||
func New(with ...ImageMetaResolverOpt) llb.ImageMetaResolver {
|
||||
var opts imageMetaResolverOpts
|
||||
for _, f := range with {
|
||||
f(&opts)
|
||||
}
|
||||
return &imageMetaResolver{
|
||||
resolver: docker.NewResolver(docker.ResolverOptions{
|
||||
Client: http.DefaultClient,
|
||||
}),
|
||||
platform: opts.platform,
|
||||
buffer: contentutil.NewBuffer(),
|
||||
cache: map[string]resolveResult{},
|
||||
locker: locker.New(),
|
||||
}
|
||||
}
|
||||
|
||||
func Default() llb.ImageMetaResolver {
|
||||
defaultImageMetaResolverOnce.Do(func() {
|
||||
defaultImageMetaResolver = New()
|
||||
})
|
||||
return defaultImageMetaResolver
|
||||
}
|
||||
|
||||
type imageMetaResolver struct {
|
||||
resolver remotes.Resolver
|
||||
buffer contentutil.Buffer
|
||||
platform *specs.Platform
|
||||
locker *locker.Locker
|
||||
cache map[string]resolveResult
|
||||
}
|
||||
|
||||
type resolveResult struct {
|
||||
config []byte
|
||||
dgst digest.Digest
|
||||
}
|
||||
|
||||
func (imr *imageMetaResolver) ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (digest.Digest, []byte, error) {
|
||||
imr.locker.Lock(ref)
|
||||
defer imr.locker.Unlock(ref)
|
||||
|
||||
platform := opt.Platform
|
||||
if platform == nil {
|
||||
platform = imr.platform
|
||||
}
|
||||
|
||||
k := imr.key(ref, platform)
|
||||
|
||||
if res, ok := imr.cache[k]; ok {
|
||||
return res.dgst, res.config, nil
|
||||
}
|
||||
|
||||
dgst, config, err := imageutil.Config(ctx, ref, imr.resolver, imr.buffer, nil, platform)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
imr.cache[k] = resolveResult{dgst: dgst, config: config}
|
||||
return dgst, config, nil
|
||||
}
|
||||
|
||||
func (imr *imageMetaResolver) key(ref string, platform *specs.Platform) string {
|
||||
if platform != nil {
|
||||
ref += platforms.Format(*platform)
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
type imageOptionFunc func(*llb.ImageInfo)
|
||||
|
||||
func (fn imageOptionFunc) SetImageOption(ii *llb.ImageInfo) {
|
||||
fn(ii)
|
||||
}
|
337
vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go
generated
vendored
337
vendor/github.com/moby/buildkit/executor/runcexecutor/executor.go
generated
vendored
@@ -1,337 +0,0 @@
|
||||
package runcexecutor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/mount"
|
||||
containerdoci "github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/continuity/fs"
|
||||
runc "github.com/containerd/go-runc"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/moby/buildkit/cache"
|
||||
"github.com/moby/buildkit/executor"
|
||||
"github.com/moby/buildkit/executor/oci"
|
||||
"github.com/moby/buildkit/identity"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/network"
|
||||
rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
// root directory
|
||||
Root string
|
||||
CommandCandidates []string
|
||||
// without root privileges (has nothing to do with Opt.Root directory)
|
||||
Rootless bool
|
||||
// DefaultCgroupParent is the cgroup-parent name for executor
|
||||
DefaultCgroupParent string
|
||||
// ProcessMode
|
||||
ProcessMode oci.ProcessMode
|
||||
IdentityMapping *idtools.IdentityMapping
|
||||
// runc run --no-pivot (unrecommended)
|
||||
NoPivot bool
|
||||
DNS *oci.DNSConfig
|
||||
OOMScoreAdj *int
|
||||
}
|
||||
|
||||
var defaultCommandCandidates = []string{"buildkit-runc", "runc"}
|
||||
|
||||
type runcExecutor struct {
|
||||
runc *runc.Runc
|
||||
root string
|
||||
cmd string
|
||||
cgroupParent string
|
||||
rootless bool
|
||||
networkProviders map[pb.NetMode]network.Provider
|
||||
processMode oci.ProcessMode
|
||||
idmap *idtools.IdentityMapping
|
||||
noPivot bool
|
||||
dns *oci.DNSConfig
|
||||
oomScoreAdj *int
|
||||
}
|
||||
|
||||
func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) {
|
||||
cmds := opt.CommandCandidates
|
||||
if cmds == nil {
|
||||
cmds = defaultCommandCandidates
|
||||
}
|
||||
|
||||
var cmd string
|
||||
var found bool
|
||||
for _, cmd = range cmds {
|
||||
if _, err := exec.LookPath(cmd); err == nil {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, errors.Errorf("failed to find %s binary", cmd)
|
||||
}
|
||||
|
||||
root := opt.Root
|
||||
|
||||
if err := os.MkdirAll(root, 0711); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create %s", root)
|
||||
}
|
||||
|
||||
root, err := filepath.Abs(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root, err = filepath.EvalSymlinks(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// clean up old hosts/resolv.conf file. ignore errors
|
||||
os.RemoveAll(filepath.Join(root, "hosts"))
|
||||
os.RemoveAll(filepath.Join(root, "resolv.conf"))
|
||||
|
||||
runtime := &runc.Runc{
|
||||
Command: cmd,
|
||||
Log: filepath.Join(root, "runc-log.json"),
|
||||
LogFormat: runc.JSON,
|
||||
PdeathSignal: syscall.SIGKILL, // this can still leak the process
|
||||
Setpgid: true,
|
||||
// we don't execute runc with --rootless=(true|false) explicitly,
|
||||
// so as to support non-runc runtimes
|
||||
}
|
||||
|
||||
w := &runcExecutor{
|
||||
runc: runtime,
|
||||
root: root,
|
||||
cgroupParent: opt.DefaultCgroupParent,
|
||||
rootless: opt.Rootless,
|
||||
networkProviders: networkProviders,
|
||||
processMode: opt.ProcessMode,
|
||||
idmap: opt.IdentityMapping,
|
||||
noPivot: opt.NoPivot,
|
||||
dns: opt.DNS,
|
||||
oomScoreAdj: opt.OOMScoreAdj,
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.Mountable, mounts []executor.Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error {
|
||||
provider, ok := w.networkProviders[meta.NetMode]
|
||||
if !ok {
|
||||
return errors.Errorf("unknown network mode %s", meta.NetMode)
|
||||
}
|
||||
namespace, err := provider.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer namespace.Close()
|
||||
|
||||
if meta.NetMode == pb.NetMode_HOST {
|
||||
logrus.Info("enabling HostNetworking")
|
||||
}
|
||||
|
||||
resolvConf, err := oci.GetResolvConf(ctx, w.root, w.idmap, w.dns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostsFile, clean, err := oci.GetHostsFile(ctx, w.root, meta.ExtraHosts, w.idmap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if clean != nil {
|
||||
defer clean()
|
||||
}
|
||||
|
||||
mountable, err := root.Mount(ctx, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rootMount, release, err := mountable.Mount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if release != nil {
|
||||
defer release()
|
||||
}
|
||||
|
||||
id := identity.NewID()
|
||||
bundle := filepath.Join(w.root, id)
|
||||
|
||||
if err := os.Mkdir(bundle, 0711); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(bundle)
|
||||
|
||||
identity := idtools.Identity{}
|
||||
if w.idmap != nil {
|
||||
identity = w.idmap.RootPair()
|
||||
}
|
||||
|
||||
rootFSPath := filepath.Join(bundle, "rootfs")
|
||||
if err := idtools.MkdirAllAndChown(rootFSPath, 0700, identity); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mount.All(rootMount, rootFSPath); err != nil {
|
||||
return err
|
||||
}
|
||||
defer mount.Unmount(rootFSPath, 0)
|
||||
|
||||
uid, gid, sgids, err := oci.GetUser(ctx, rootFSPath, meta.User)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(filepath.Join(bundle, "config.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
opts := []containerdoci.SpecOpts{oci.WithUIDGID(uid, gid, sgids)}
|
||||
|
||||
if meta.ReadonlyRootFS {
|
||||
opts = append(opts, containerdoci.WithRootFSReadonly())
|
||||
}
|
||||
|
||||
identity = idtools.Identity{
|
||||
UID: int(uid),
|
||||
GID: int(gid),
|
||||
}
|
||||
if w.idmap != nil {
|
||||
identity, err = w.idmap.ToHost(identity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if w.cgroupParent != "" {
|
||||
var cgroupsPath string
|
||||
lastSeparator := w.cgroupParent[len(w.cgroupParent)-1:]
|
||||
if strings.Contains(w.cgroupParent, ".slice") && lastSeparator == ":" {
|
||||
cgroupsPath = w.cgroupParent + id
|
||||
} else {
|
||||
cgroupsPath = filepath.Join("/", w.cgroupParent, "buildkit", id)
|
||||
}
|
||||
opts = append(opts, containerdoci.WithCgroup(cgroupsPath))
|
||||
}
|
||||
spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.processMode, w.idmap, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
spec.Root.Path = rootFSPath
|
||||
if _, ok := root.(cache.ImmutableRef); ok { // TODO: pass in with mount, not ref type
|
||||
spec.Root.Readonly = true
|
||||
}
|
||||
|
||||
newp, err := fs.RootPath(rootFSPath, meta.Cwd)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "working dir %s points to invalid target", newp)
|
||||
}
|
||||
if _, err := os.Stat(newp); err != nil {
|
||||
if err := idtools.MkdirAllAndChown(newp, 0755, identity); err != nil {
|
||||
return errors.Wrapf(err, "failed to create working directory %s", newp)
|
||||
}
|
||||
}
|
||||
|
||||
spec.Process.OOMScoreAdj = w.oomScoreAdj
|
||||
if w.rootless {
|
||||
if err := rootlessspecconv.ToRootless(spec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(f).Encode(spec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// runCtx/killCtx is used for extra check in case the kill command blocks
|
||||
runCtx, cancelRun := context.WithCancel(context.Background())
|
||||
defer cancelRun()
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
killCtx, timeout := context.WithTimeout(context.Background(), 7*time.Second)
|
||||
if err := w.runc.Kill(killCtx, id, int(syscall.SIGKILL), nil); err != nil {
|
||||
logrus.Errorf("failed to kill runc %s: %+v", id, err)
|
||||
select {
|
||||
case <-killCtx.Done():
|
||||
timeout()
|
||||
cancelRun()
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
timeout()
|
||||
select {
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
logrus.Debugf("> creating %s %v", id, meta.Args)
|
||||
status, err := w.runc.Run(runCtx, id, bundle, &runc.CreateOpts{
|
||||
IO: &forwardIO{stdin: stdin, stdout: stdout, stderr: stderr},
|
||||
NoPivot: w.noPivot,
|
||||
})
|
||||
close(done)
|
||||
|
||||
if status != 0 || err != nil {
|
||||
if err == nil {
|
||||
err = errors.Errorf("exit code: %d", status)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.Wrapf(ctx.Err(), err.Error())
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type forwardIO struct {
|
||||
stdin io.ReadCloser
|
||||
stdout, stderr io.WriteCloser
|
||||
}
|
||||
|
||||
func (s *forwardIO) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *forwardIO) Set(cmd *exec.Cmd) {
|
||||
cmd.Stdin = s.stdin
|
||||
cmd.Stdout = s.stdout
|
||||
cmd.Stderr = s.stderr
|
||||
}
|
||||
|
||||
func (s *forwardIO) Stdin() io.WriteCloser {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *forwardIO) Stdout() io.ReadCloser {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *forwardIO) Stderr() io.ReadCloser {
|
||||
return nil
|
||||
}
|
641
vendor/github.com/moby/buildkit/frontend/dockerfile/builder/build.go
generated
vendored
641
vendor/github.com/moby/buildkit/frontend/dockerfile/builder/build.go
generated
vendored
@@ -1,641 +0,0 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/docker/docker/builder/dockerignore"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
|
||||
"github.com/moby/buildkit/frontend/gateway/client"
|
||||
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/apicaps"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultLocalNameContext = "context"
|
||||
DefaultLocalNameDockerfile = "dockerfile"
|
||||
keyTarget = "target"
|
||||
keyFilename = "filename"
|
||||
keyCacheFrom = "cache-from" // for registry only. deprecated in favor of keyCacheImports
|
||||
keyCacheImports = "cache-imports" // JSON representation of []CacheOptionsEntry
|
||||
keyCacheNS = "build-arg:BUILDKIT_CACHE_MOUNT_NS"
|
||||
defaultDockerfileName = "Dockerfile"
|
||||
dockerignoreFilename = ".dockerignore"
|
||||
buildArgPrefix = "build-arg:"
|
||||
labelPrefix = "label:"
|
||||
keyNoCache = "no-cache"
|
||||
keyTargetPlatform = "platform"
|
||||
keyMultiPlatform = "multi-platform"
|
||||
keyImageResolveMode = "image-resolve-mode"
|
||||
keyGlobalAddHosts = "add-hosts"
|
||||
keyForceNetwork = "force-network-mode"
|
||||
keyOverrideCopyImage = "override-copy-image" // remove after CopyOp implemented
|
||||
keyNameContext = "contextkey"
|
||||
keyNameDockerfile = "dockerfilekey"
|
||||
keyContextSubDir = "contextsubdir"
|
||||
keyContextKeepGitDir = "build-arg:BUILDKIT_CONTEXT_KEEP_GIT_DIR"
|
||||
)
|
||||
|
||||
var httpPrefix = regexp.MustCompile(`^https?://`)
|
||||
var gitUrlPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
|
||||
|
||||
func Build(ctx context.Context, c client.Client) (*client.Result, error) {
|
||||
opts := c.BuildOpts().Opts
|
||||
caps := c.BuildOpts().LLBCaps
|
||||
gwcaps := c.BuildOpts().Caps
|
||||
|
||||
marshalOpts := []llb.ConstraintsOpt{llb.WithCaps(caps)}
|
||||
|
||||
localNameContext := DefaultLocalNameContext
|
||||
if v, ok := opts[keyNameContext]; ok {
|
||||
localNameContext = v
|
||||
}
|
||||
|
||||
forceLocalDockerfile := false
|
||||
localNameDockerfile := DefaultLocalNameDockerfile
|
||||
if v, ok := opts[keyNameDockerfile]; ok {
|
||||
forceLocalDockerfile = true
|
||||
localNameDockerfile = v
|
||||
}
|
||||
|
||||
defaultBuildPlatform := platforms.DefaultSpec()
|
||||
if workers := c.BuildOpts().Workers; len(workers) > 0 && len(workers[0].Platforms) > 0 {
|
||||
defaultBuildPlatform = workers[0].Platforms[0]
|
||||
}
|
||||
|
||||
buildPlatforms := []specs.Platform{defaultBuildPlatform}
|
||||
targetPlatforms := []*specs.Platform{nil}
|
||||
if v := opts[keyTargetPlatform]; v != "" {
|
||||
var err error
|
||||
targetPlatforms, err = parsePlatforms(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resolveMode, err := parseResolveMode(opts[keyImageResolveMode])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extraHosts, err := parseExtraHosts(opts[keyGlobalAddHosts])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse additional hosts")
|
||||
}
|
||||
|
||||
defaultNetMode, err := parseNetMode(opts[keyForceNetwork])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filename := opts[keyFilename]
|
||||
if filename == "" {
|
||||
filename = defaultDockerfileName
|
||||
}
|
||||
|
||||
var ignoreCache []string
|
||||
if v, ok := opts[keyNoCache]; ok {
|
||||
if v == "" {
|
||||
ignoreCache = []string{} // means all stages
|
||||
} else {
|
||||
ignoreCache = strings.Split(v, ",")
|
||||
}
|
||||
}
|
||||
|
||||
name := "load build definition from " + filename
|
||||
|
||||
src := llb.Local(localNameDockerfile,
|
||||
llb.FollowPaths([]string{filename, filename + ".dockerignore"}),
|
||||
llb.SessionID(c.BuildOpts().SessionID),
|
||||
llb.SharedKeyHint(localNameDockerfile),
|
||||
dockerfile2llb.WithInternalName(name),
|
||||
)
|
||||
|
||||
fileop := useFileOp(opts, &caps)
|
||||
|
||||
var buildContext *llb.State
|
||||
isNotLocalContext := false
|
||||
if st, ok := detectGitContext(opts[localNameContext], opts[keyContextKeepGitDir]); ok {
|
||||
if !forceLocalDockerfile {
|
||||
src = *st
|
||||
}
|
||||
buildContext = st
|
||||
} else if httpPrefix.MatchString(opts[localNameContext]) {
|
||||
httpContext := llb.HTTP(opts[localNameContext], llb.Filename("context"), dockerfile2llb.WithInternalName("load remote build context"))
|
||||
def, err := httpContext.Marshal(marshalOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal httpcontext")
|
||||
}
|
||||
res, err := c.Solve(ctx, client.SolveRequest{
|
||||
Definition: def.ToPB(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to resolve httpcontext")
|
||||
}
|
||||
|
||||
ref, err := res.SingleRef()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dt, err := ref.ReadFile(ctx, client.ReadRequest{
|
||||
Filename: "context",
|
||||
Range: &client.FileRange{
|
||||
Length: 1024,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("failed to read downloaded context")
|
||||
}
|
||||
if isArchive(dt) {
|
||||
if fileop {
|
||||
bc := llb.Scratch().File(llb.Copy(httpContext, "/context", "/", &llb.CopyInfo{
|
||||
AttemptUnpack: true,
|
||||
}))
|
||||
if !forceLocalDockerfile {
|
||||
src = bc
|
||||
}
|
||||
buildContext = &bc
|
||||
} else {
|
||||
copyImage := opts[keyOverrideCopyImage]
|
||||
if copyImage == "" {
|
||||
copyImage = dockerfile2llb.DefaultCopyImage
|
||||
}
|
||||
unpack := llb.Image(copyImage, dockerfile2llb.WithInternalName("helper image for file operations")).
|
||||
Run(llb.Shlex("copy --unpack /src/context /out/"), llb.ReadonlyRootFS(), dockerfile2llb.WithInternalName("extracting build context"))
|
||||
unpack.AddMount("/src", httpContext, llb.Readonly)
|
||||
bc := unpack.AddMount("/out", llb.Scratch())
|
||||
if !forceLocalDockerfile {
|
||||
src = bc
|
||||
}
|
||||
buildContext = &bc
|
||||
}
|
||||
} else {
|
||||
filename = "context"
|
||||
if !forceLocalDockerfile {
|
||||
src = httpContext
|
||||
}
|
||||
buildContext = &httpContext
|
||||
isNotLocalContext = true
|
||||
}
|
||||
} else if (&gwcaps).Supports(gwpb.CapFrontendInputs) == nil {
|
||||
inputs, err := c.Inputs(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get frontend inputs")
|
||||
}
|
||||
|
||||
if !forceLocalDockerfile {
|
||||
inputDockerfile, ok := inputs[DefaultLocalNameDockerfile]
|
||||
if ok {
|
||||
src = inputDockerfile
|
||||
}
|
||||
}
|
||||
|
||||
inputCtx, ok := inputs[DefaultLocalNameContext]
|
||||
if ok {
|
||||
buildContext = &inputCtx
|
||||
isNotLocalContext = true
|
||||
}
|
||||
}
|
||||
|
||||
if buildContext != nil {
|
||||
if sub, ok := opts[keyContextSubDir]; ok {
|
||||
buildContext = scopeToSubDir(buildContext, fileop, sub)
|
||||
}
|
||||
}
|
||||
|
||||
def, err := src.Marshal(marshalOpts...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to marshal local source")
|
||||
}
|
||||
|
||||
eg, ctx2 := errgroup.WithContext(ctx)
|
||||
var dtDockerfile []byte
|
||||
var dtDockerignore []byte
|
||||
var dtDockerignoreDefault []byte
|
||||
eg.Go(func() error {
|
||||
res, err := c.Solve(ctx2, client.SolveRequest{
|
||||
Definition: def.ToPB(),
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to resolve dockerfile")
|
||||
}
|
||||
|
||||
ref, err := res.SingleRef()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dtDockerfile, err = ref.ReadFile(ctx2, client.ReadRequest{
|
||||
Filename: filename,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read dockerfile")
|
||||
}
|
||||
|
||||
dt, err := ref.ReadFile(ctx2, client.ReadRequest{
|
||||
Filename: filename + ".dockerignore",
|
||||
})
|
||||
if err == nil {
|
||||
dtDockerignore = dt
|
||||
}
|
||||
return nil
|
||||
})
|
||||
var excludes []string
|
||||
if !isNotLocalContext {
|
||||
eg.Go(func() error {
|
||||
dockerignoreState := buildContext
|
||||
if dockerignoreState == nil {
|
||||
st := llb.Local(localNameContext,
|
||||
llb.SessionID(c.BuildOpts().SessionID),
|
||||
llb.FollowPaths([]string{dockerignoreFilename}),
|
||||
llb.SharedKeyHint(localNameContext+"-"+dockerignoreFilename),
|
||||
dockerfile2llb.WithInternalName("load "+dockerignoreFilename),
|
||||
)
|
||||
dockerignoreState = &st
|
||||
}
|
||||
def, err := dockerignoreState.Marshal(marshalOpts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := c.Solve(ctx2, client.SolveRequest{
|
||||
Definition: def.ToPB(),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ref, err := res.SingleRef()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dtDockerignoreDefault, err = ref.ReadFile(ctx2, client.ReadRequest{
|
||||
Filename: dockerignoreFilename,
|
||||
})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dtDockerignore == nil {
|
||||
dtDockerignore = dtDockerignoreDefault
|
||||
}
|
||||
if dtDockerignore != nil {
|
||||
excludes, err = dockerignore.ReadAll(bytes.NewBuffer(dtDockerignore))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse dockerignore")
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := opts["cmdline"]; !ok {
|
||||
ref, cmdline, ok := dockerfile2llb.DetectSyntax(bytes.NewBuffer(dtDockerfile))
|
||||
if ok {
|
||||
return forwardGateway(ctx, c, ref, cmdline)
|
||||
}
|
||||
}
|
||||
|
||||
exportMap := len(targetPlatforms) > 1
|
||||
|
||||
if v := opts[keyMultiPlatform]; v != "" {
|
||||
b, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid boolean value %s", v)
|
||||
}
|
||||
if !b && exportMap {
|
||||
return nil, errors.Errorf("returning multiple target plaforms is not allowed")
|
||||
}
|
||||
exportMap = b
|
||||
}
|
||||
|
||||
expPlatforms := &exptypes.Platforms{
|
||||
Platforms: make([]exptypes.Platform, len(targetPlatforms)),
|
||||
}
|
||||
res := client.NewResult()
|
||||
|
||||
eg, ctx = errgroup.WithContext(ctx)
|
||||
|
||||
for i, tp := range targetPlatforms {
|
||||
func(i int, tp *specs.Platform) {
|
||||
eg.Go(func() error {
|
||||
st, img, err := dockerfile2llb.Dockerfile2LLB(ctx, dtDockerfile, dockerfile2llb.ConvertOpt{
|
||||
Target: opts[keyTarget],
|
||||
MetaResolver: c,
|
||||
BuildArgs: filter(opts, buildArgPrefix),
|
||||
Labels: filter(opts, labelPrefix),
|
||||
CacheIDNamespace: opts[keyCacheNS],
|
||||
SessionID: c.BuildOpts().SessionID,
|
||||
BuildContext: buildContext,
|
||||
Excludes: excludes,
|
||||
IgnoreCache: ignoreCache,
|
||||
TargetPlatform: tp,
|
||||
BuildPlatforms: buildPlatforms,
|
||||
ImageResolveMode: resolveMode,
|
||||
PrefixPlatform: exportMap,
|
||||
ExtraHosts: extraHosts,
|
||||
ForceNetMode: defaultNetMode,
|
||||
OverrideCopyImage: opts[keyOverrideCopyImage],
|
||||
LLBCaps: &caps,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create LLB definition")
|
||||
}
|
||||
|
||||
def, err := st.Marshal()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to marshal LLB definition")
|
||||
}
|
||||
|
||||
config, err := json.Marshal(img)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to marshal image config")
|
||||
}
|
||||
|
||||
var cacheImports []client.CacheOptionsEntry
|
||||
// new API
|
||||
if cacheImportsStr := opts[keyCacheImports]; cacheImportsStr != "" {
|
||||
var cacheImportsUM []controlapi.CacheOptionsEntry
|
||||
if err := json.Unmarshal([]byte(cacheImportsStr), &cacheImportsUM); err != nil {
|
||||
return errors.Wrapf(err, "failed to unmarshal %s (%q)", keyCacheImports, cacheImportsStr)
|
||||
}
|
||||
for _, um := range cacheImportsUM {
|
||||
cacheImports = append(cacheImports, client.CacheOptionsEntry{Type: um.Type, Attrs: um.Attrs})
|
||||
}
|
||||
}
|
||||
// old API
|
||||
if cacheFromStr := opts[keyCacheFrom]; cacheFromStr != "" {
|
||||
cacheFrom := strings.Split(cacheFromStr, ",")
|
||||
for _, s := range cacheFrom {
|
||||
im := client.CacheOptionsEntry{
|
||||
Type: "registry",
|
||||
Attrs: map[string]string{
|
||||
"ref": s,
|
||||
},
|
||||
}
|
||||
// FIXME(AkihiroSuda): skip append if already exists
|
||||
cacheImports = append(cacheImports, im)
|
||||
}
|
||||
}
|
||||
|
||||
r, err := c.Solve(ctx, client.SolveRequest{
|
||||
Definition: def.ToPB(),
|
||||
CacheImports: cacheImports,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ref, err := r.SingleRef()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exportMap {
|
||||
res.AddMeta(exptypes.ExporterImageConfigKey, config)
|
||||
res.SetRef(ref)
|
||||
} else {
|
||||
p := platforms.DefaultSpec()
|
||||
if tp != nil {
|
||||
p = *tp
|
||||
}
|
||||
|
||||
k := platforms.Format(p)
|
||||
res.AddMeta(fmt.Sprintf("%s/%s", exptypes.ExporterImageConfigKey, k), config)
|
||||
res.AddRef(k, ref)
|
||||
expPlatforms.Platforms[i] = exptypes.Platform{
|
||||
ID: k,
|
||||
Platform: p,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}(i, tp)
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exportMap {
|
||||
dt, err := json.Marshal(expPlatforms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.AddMeta(exptypes.ExporterPlatformsKey, dt)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func forwardGateway(ctx context.Context, c client.Client, ref string, cmdline string) (*client.Result, error) {
|
||||
opts := c.BuildOpts().Opts
|
||||
if opts == nil {
|
||||
opts = map[string]string{}
|
||||
}
|
||||
opts["cmdline"] = cmdline
|
||||
opts["source"] = ref
|
||||
|
||||
gwcaps := c.BuildOpts().Caps
|
||||
var frontendInputs map[string]*pb.Definition
|
||||
if (&gwcaps).Supports(gwpb.CapFrontendInputs) == nil {
|
||||
inputs, err := c.Inputs(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get frontend inputs")
|
||||
}
|
||||
|
||||
frontendInputs = make(map[string]*pb.Definition)
|
||||
for name, state := range inputs {
|
||||
def, err := state.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frontendInputs[name] = def.ToPB()
|
||||
}
|
||||
}
|
||||
|
||||
return c.Solve(ctx, client.SolveRequest{
|
||||
Frontend: "gateway.v0",
|
||||
FrontendOpt: opts,
|
||||
FrontendInputs: frontendInputs,
|
||||
})
|
||||
}
|
||||
|
||||
func filter(opt map[string]string, key string) map[string]string {
|
||||
m := map[string]string{}
|
||||
for k, v := range opt {
|
||||
if strings.HasPrefix(k, key) {
|
||||
m[strings.TrimPrefix(k, key)] = v
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func detectGitContext(ref, gitContext string) (*llb.State, bool) {
|
||||
found := false
|
||||
if httpPrefix.MatchString(ref) && gitUrlPathWithFragmentSuffix.MatchString(ref) {
|
||||
found = true
|
||||
}
|
||||
|
||||
keepGit := false
|
||||
if gitContext != "" {
|
||||
if v, err := strconv.ParseBool(gitContext); err == nil {
|
||||
keepGit = v
|
||||
}
|
||||
}
|
||||
|
||||
for _, prefix := range []string{"git://", "github.com/", "git@"} {
|
||||
if strings.HasPrefix(ref, prefix) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
parts := strings.SplitN(ref, "#", 2)
|
||||
branch := ""
|
||||
if len(parts) > 1 {
|
||||
branch = parts[1]
|
||||
}
|
||||
gitOpts := []llb.GitOption{dockerfile2llb.WithInternalName("load git source " + ref)}
|
||||
if keepGit {
|
||||
gitOpts = append(gitOpts, llb.KeepGitDir())
|
||||
}
|
||||
|
||||
st := llb.Git(parts[0], branch, gitOpts...)
|
||||
return &st, true
|
||||
}
|
||||
|
||||
func isArchive(header []byte) bool {
|
||||
for _, m := range [][]byte{
|
||||
{0x42, 0x5A, 0x68}, // bzip2
|
||||
{0x1F, 0x8B, 0x08}, // gzip
|
||||
{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, // xz
|
||||
} {
|
||||
if len(header) < len(m) {
|
||||
continue
|
||||
}
|
||||
if bytes.Equal(m, header[:len(m)]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
r := tar.NewReader(bytes.NewBuffer(header))
|
||||
_, err := r.Next()
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func parsePlatforms(v string) ([]*specs.Platform, error) {
|
||||
var pp []*specs.Platform
|
||||
for _, v := range strings.Split(v, ",") {
|
||||
p, err := platforms.Parse(v)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse target platform %s", v)
|
||||
}
|
||||
p = platforms.Normalize(p)
|
||||
pp = append(pp, &p)
|
||||
}
|
||||
return pp, nil
|
||||
}
|
||||
|
||||
func parseResolveMode(v string) (llb.ResolveMode, error) {
|
||||
switch v {
|
||||
case pb.AttrImageResolveModeDefault, "":
|
||||
return llb.ResolveModeDefault, nil
|
||||
case pb.AttrImageResolveModeForcePull:
|
||||
return llb.ResolveModeForcePull, nil
|
||||
case pb.AttrImageResolveModePreferLocal:
|
||||
return llb.ResolveModePreferLocal, nil
|
||||
default:
|
||||
return 0, errors.Errorf("invalid image-resolve-mode: %s", v)
|
||||
}
|
||||
}
|
||||
|
||||
func parseExtraHosts(v string) ([]llb.HostIP, error) {
|
||||
if v == "" {
|
||||
return nil, nil
|
||||
}
|
||||
out := make([]llb.HostIP, 0)
|
||||
csvReader := csv.NewReader(strings.NewReader(v))
|
||||
fields, err := csvReader.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, field := range fields {
|
||||
parts := strings.SplitN(field, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.Errorf("invalid key-value pair %s", field)
|
||||
}
|
||||
key := strings.ToLower(parts[0])
|
||||
val := strings.ToLower(parts[1])
|
||||
ip := net.ParseIP(val)
|
||||
if ip == nil {
|
||||
return nil, errors.Errorf("failed to parse IP %s", val)
|
||||
}
|
||||
out = append(out, llb.HostIP{Host: key, IP: ip})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func parseNetMode(v string) (pb.NetMode, error) {
|
||||
if v == "" {
|
||||
return llb.NetModeSandbox, nil
|
||||
}
|
||||
switch v {
|
||||
case "none":
|
||||
return llb.NetModeNone, nil
|
||||
case "host":
|
||||
return llb.NetModeHost, nil
|
||||
case "sandbox":
|
||||
return llb.NetModeSandbox, nil
|
||||
default:
|
||||
return 0, errors.Errorf("invalid netmode %s", v)
|
||||
}
|
||||
}
|
||||
|
||||
func useFileOp(args map[string]string, caps *apicaps.CapSet) bool {
|
||||
enabled := true
|
||||
if v, ok := args["build-arg:BUILDKIT_DISABLE_FILEOP"]; ok {
|
||||
if b, err := strconv.ParseBool(v); err == nil {
|
||||
enabled = !b
|
||||
}
|
||||
}
|
||||
return enabled && caps != nil && caps.Supports(pb.CapFileBase) == nil
|
||||
}
|
||||
|
||||
func scopeToSubDir(c *llb.State, fileop bool, dir string) *llb.State {
|
||||
if fileop {
|
||||
bc := llb.Scratch().File(llb.Copy(*c, dir, "/", &llb.CopyInfo{
|
||||
CopyDirContentsOnly: true,
|
||||
}))
|
||||
return &bc
|
||||
}
|
||||
unpack := llb.Image(dockerfile2llb.DefaultCopyImage, dockerfile2llb.WithInternalName("helper image for file operations")).
|
||||
Run(llb.Shlexf("copy %s/. /out/", path.Join("/src", dir)), llb.ReadonlyRootFS(), dockerfile2llb.WithInternalName("filtering build context"))
|
||||
unpack.AddMount("/src", *c, llb.Readonly)
|
||||
bc := unpack.AddMount("/out", llb.Scratch())
|
||||
return &bc
|
||||
}
|
46
vendor/github.com/moby/buildkit/frontend/dockerfile/command/command.go
generated
vendored
46
vendor/github.com/moby/buildkit/frontend/dockerfile/command/command.go
generated
vendored
@@ -1,46 +0,0 @@
|
||||
// Package command contains the set of Dockerfile commands.
|
||||
package command
|
||||
|
||||
// Define constants for the command strings
|
||||
const (
|
||||
Add = "add"
|
||||
Arg = "arg"
|
||||
Cmd = "cmd"
|
||||
Copy = "copy"
|
||||
Entrypoint = "entrypoint"
|
||||
Env = "env"
|
||||
Expose = "expose"
|
||||
From = "from"
|
||||
Healthcheck = "healthcheck"
|
||||
Label = "label"
|
||||
Maintainer = "maintainer"
|
||||
Onbuild = "onbuild"
|
||||
Run = "run"
|
||||
Shell = "shell"
|
||||
StopSignal = "stopsignal"
|
||||
User = "user"
|
||||
Volume = "volume"
|
||||
Workdir = "workdir"
|
||||
)
|
||||
|
||||
// Commands is list of all Dockerfile commands
|
||||
var Commands = map[string]struct{}{
|
||||
Add: {},
|
||||
Arg: {},
|
||||
Cmd: {},
|
||||
Copy: {},
|
||||
Entrypoint: {},
|
||||
Env: {},
|
||||
Expose: {},
|
||||
From: {},
|
||||
Healthcheck: {},
|
||||
Label: {},
|
||||
Maintainer: {},
|
||||
Onbuild: {},
|
||||
Run: {},
|
||||
Shell: {},
|
||||
StopSignal: {},
|
||||
User: {},
|
||||
Volume: {},
|
||||
Workdir: {},
|
||||
}
|
1323
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert.go
generated
vendored
1323
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert.go
generated
vendored
File diff suppressed because it is too large
Load Diff
16
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_norunmount.go
generated
vendored
16
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_norunmount.go
generated
vendored
@@ -1,16 +0,0 @@
|
||||
// +build !dfrunmount
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
)
|
||||
|
||||
func detectRunMount(cmd *command, allDispatchStates *dispatchStates) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) ([]llb.RunOption, error) {
|
||||
return nil, nil
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
// +build !dfrunnetwork
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
)
|
||||
|
||||
func dispatchRunNetwork(c *instructions.RunCommand) (llb.RunOption, error) {
|
||||
return nil, nil
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
// +build !dfrunsecurity
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
)
|
||||
|
||||
func dispatchRunSecurity(c *instructions.RunCommand) (llb.RunOption, error) {
|
||||
return nil, nil
|
||||
}
|
13
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_nosecrets.go
generated
vendored
13
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_nosecrets.go
generated
vendored
@@ -1,13 +0,0 @@
|
||||
// +build dfrunmount,!dfsecrets
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func dispatchSecret(m *instructions.Mount) (llb.RunOption, error) {
|
||||
return nil, errors.Errorf("secret mounts not allowed")
|
||||
}
|
13
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_nossh.go
generated
vendored
13
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_nossh.go
generated
vendored
@@ -1,13 +0,0 @@
|
||||
// +build dfrunmount,!dfssh
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func dispatchSSH(m *instructions.Mount) (llb.RunOption, error) {
|
||||
return nil, errors.Errorf("ssh mounts not allowed")
|
||||
}
|
156
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_runmount.go
generated
vendored
156
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_runmount.go
generated
vendored
@@ -1,156 +0,0 @@
|
||||
// +build dfrunmount
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func detectRunMount(cmd *command, allDispatchStates *dispatchStates) bool {
|
||||
if c, ok := cmd.Command.(*instructions.RunCommand); ok {
|
||||
mounts := instructions.GetMounts(c)
|
||||
sources := make([]*dispatchState, len(mounts))
|
||||
for i, mount := range mounts {
|
||||
if mount.From == "" && mount.Type == instructions.MountTypeCache {
|
||||
mount.From = emptyImageName
|
||||
}
|
||||
from := mount.From
|
||||
if from == "" || mount.Type == instructions.MountTypeTmpfs {
|
||||
continue
|
||||
}
|
||||
stn, ok := allDispatchStates.findStateByName(from)
|
||||
if !ok {
|
||||
stn = &dispatchState{
|
||||
stage: instructions.Stage{BaseName: from},
|
||||
deps: make(map[*dispatchState]struct{}),
|
||||
unregistered: true,
|
||||
}
|
||||
}
|
||||
sources[i] = stn
|
||||
}
|
||||
cmd.sources = sources
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func setCacheUIDGIDFileOp(m *instructions.Mount, st llb.State) llb.State {
|
||||
uid := 0
|
||||
gid := 0
|
||||
mode := os.FileMode(0755)
|
||||
if m.UID != nil {
|
||||
uid = int(*m.UID)
|
||||
}
|
||||
if m.GID != nil {
|
||||
gid = int(*m.GID)
|
||||
}
|
||||
if m.Mode != nil {
|
||||
mode = os.FileMode(*m.Mode)
|
||||
}
|
||||
return st.File(llb.Mkdir("/cache", mode, llb.WithUIDGID(uid, gid)), llb.WithCustomName("[internal] settings cache mount permissions"))
|
||||
}
|
||||
|
||||
func setCacheUIDGID(m *instructions.Mount, st llb.State, fileop bool) llb.State {
|
||||
if fileop {
|
||||
return setCacheUIDGIDFileOp(m, st)
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
if m.UID != nil {
|
||||
b.WriteString(fmt.Sprintf("chown %d /mnt/cache;", *m.UID))
|
||||
}
|
||||
if m.GID != nil {
|
||||
b.WriteString(fmt.Sprintf("chown :%d /mnt/cache;", *m.GID))
|
||||
}
|
||||
if m.Mode != nil {
|
||||
b.WriteString(fmt.Sprintf("chmod %s /mnt/cache;", strconv.FormatUint(*m.Mode, 8)))
|
||||
}
|
||||
return llb.Image("busybox").Run(llb.Shlex(fmt.Sprintf("sh -c 'mkdir -p /mnt/cache;%s'", b.String())), llb.WithCustomName("[internal] settings cache mount permissions")).AddMount("/mnt", st)
|
||||
}
|
||||
|
||||
func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) ([]llb.RunOption, error) {
|
||||
var out []llb.RunOption
|
||||
mounts := instructions.GetMounts(c)
|
||||
|
||||
for i, mount := range mounts {
|
||||
if mount.From == "" && mount.Type == instructions.MountTypeCache {
|
||||
mount.From = emptyImageName
|
||||
}
|
||||
st := opt.buildContext
|
||||
if mount.From != "" {
|
||||
st = sources[i].state
|
||||
}
|
||||
var mountOpts []llb.MountOption
|
||||
if mount.Type == instructions.MountTypeTmpfs {
|
||||
st = llb.Scratch()
|
||||
mountOpts = append(mountOpts, llb.Tmpfs())
|
||||
}
|
||||
if mount.Type == instructions.MountTypeSecret {
|
||||
secret, err := dispatchSecret(mount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, secret)
|
||||
continue
|
||||
}
|
||||
if mount.Type == instructions.MountTypeSSH {
|
||||
ssh, err := dispatchSSH(mount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, ssh)
|
||||
continue
|
||||
}
|
||||
if mount.ReadOnly {
|
||||
mountOpts = append(mountOpts, llb.Readonly)
|
||||
} else if mount.Type == instructions.MountTypeBind && opt.llbCaps.Supports(pb.CapExecMountBindReadWriteNoOuput) == nil {
|
||||
mountOpts = append(mountOpts, llb.ForceNoOutput)
|
||||
}
|
||||
if mount.Type == instructions.MountTypeCache {
|
||||
sharing := llb.CacheMountShared
|
||||
if mount.CacheSharing == instructions.MountSharingPrivate {
|
||||
sharing = llb.CacheMountPrivate
|
||||
}
|
||||
if mount.CacheSharing == instructions.MountSharingLocked {
|
||||
sharing = llb.CacheMountLocked
|
||||
}
|
||||
if mount.CacheID == "" {
|
||||
mount.CacheID = path.Clean(mount.Target)
|
||||
}
|
||||
mountOpts = append(mountOpts, llb.AsPersistentCacheDir(opt.cacheIDNamespace+"/"+mount.CacheID, sharing))
|
||||
}
|
||||
target := mount.Target
|
||||
if !filepath.IsAbs(filepath.Clean(mount.Target)) {
|
||||
target = filepath.Join("/", d.state.GetDir(), mount.Target)
|
||||
}
|
||||
if target == "/" {
|
||||
return nil, errors.Errorf("invalid mount target %q", target)
|
||||
}
|
||||
if src := path.Join("/", mount.Source); src != "/" {
|
||||
mountOpts = append(mountOpts, llb.SourcePath(src))
|
||||
} else {
|
||||
if mount.UID != nil || mount.GID != nil || mount.Mode != nil {
|
||||
st = setCacheUIDGID(mount, st, useFileOp(opt.buildArgValues, opt.llbCaps))
|
||||
mountOpts = append(mountOpts, llb.SourcePath("/cache"))
|
||||
}
|
||||
}
|
||||
|
||||
out = append(out, llb.AddMount(target, st, mountOpts...))
|
||||
|
||||
if mount.From == "" {
|
||||
d.ctxPaths[path.Join("/", filepath.ToSlash(mount.Source))] = struct{}{}
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
26
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_runnetwork.go
generated
vendored
26
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_runnetwork.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
// +build dfrunnetwork
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
)
|
||||
|
||||
func dispatchRunNetwork(c *instructions.RunCommand) (llb.RunOption, error) {
|
||||
network := instructions.GetNetwork(c)
|
||||
|
||||
switch network {
|
||||
case instructions.NetworkDefault:
|
||||
return nil, nil
|
||||
case instructions.NetworkNone:
|
||||
return llb.Network(pb.NetMode_NONE), nil
|
||||
case instructions.NetworkHost:
|
||||
return llb.Network(pb.NetMode_HOST), nil
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported network mode %q", network)
|
||||
}
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
// +build dfrunsecurity
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
)
|
||||
|
||||
func dispatchRunSecurity(c *instructions.RunCommand) (llb.RunOption, error) {
|
||||
security := instructions.GetSecurity(c)
|
||||
|
||||
switch security {
|
||||
case instructions.SecurityInsecure:
|
||||
return llb.Security(pb.SecurityMode_INSECURE), nil
|
||||
case instructions.SecuritySandbox:
|
||||
return llb.Security(pb.SecurityMode_SANDBOX), nil
|
||||
default:
|
||||
return nil, errors.Errorf("unsupported security mode %q", security)
|
||||
}
|
||||
}
|
54
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_secrets.go
generated
vendored
54
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_secrets.go
generated
vendored
@@ -1,54 +0,0 @@
|
||||
// +build dfsecrets
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func dispatchSecret(m *instructions.Mount) (llb.RunOption, error) {
|
||||
id := m.CacheID
|
||||
if m.Source != "" {
|
||||
id = m.Source
|
||||
}
|
||||
|
||||
if id == "" {
|
||||
if m.Target == "" {
|
||||
return nil, errors.Errorf("one of source, target required")
|
||||
}
|
||||
id = path.Base(m.Target)
|
||||
}
|
||||
|
||||
target := m.Target
|
||||
if target == "" {
|
||||
target = "/run/secrets/" + path.Base(id)
|
||||
}
|
||||
|
||||
opts := []llb.SecretOption{llb.SecretID(id)}
|
||||
|
||||
if !m.Required {
|
||||
opts = append(opts, llb.SecretOptional)
|
||||
}
|
||||
|
||||
if m.UID != nil || m.GID != nil || m.Mode != nil {
|
||||
var uid, gid, mode int
|
||||
if m.UID != nil {
|
||||
uid = int(*m.UID)
|
||||
}
|
||||
if m.GID != nil {
|
||||
gid = int(*m.GID)
|
||||
}
|
||||
if m.Mode != nil {
|
||||
mode = int(*m.Mode)
|
||||
} else {
|
||||
mode = 0400
|
||||
}
|
||||
opts = append(opts, llb.SecretFileOpt(uid, gid, mode))
|
||||
}
|
||||
|
||||
return llb.AddSecret(target, opts...), nil
|
||||
}
|
42
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_ssh.go
generated
vendored
42
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/convert_ssh.go
generated
vendored
@@ -1,42 +0,0 @@
|
||||
// +build dfssh
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func dispatchSSH(m *instructions.Mount) (llb.RunOption, error) {
|
||||
if m.Source != "" {
|
||||
return nil, errors.Errorf("ssh does not support source")
|
||||
}
|
||||
opts := []llb.SSHOption{llb.SSHID(m.CacheID)}
|
||||
|
||||
if m.Target != "" {
|
||||
opts = append(opts, llb.SSHSocketTarget(m.Target))
|
||||
}
|
||||
|
||||
if !m.Required {
|
||||
opts = append(opts, llb.SSHOptional)
|
||||
}
|
||||
|
||||
if m.UID != nil || m.GID != nil || m.Mode != nil {
|
||||
var uid, gid, mode int
|
||||
if m.UID != nil {
|
||||
uid = int(*m.UID)
|
||||
}
|
||||
if m.GID != nil {
|
||||
gid = int(*m.GID)
|
||||
}
|
||||
if m.Mode != nil {
|
||||
mode = int(*m.Mode)
|
||||
} else {
|
||||
mode = 0600
|
||||
}
|
||||
opts = append(opts, llb.SSHSocketOpt(m.Target, uid, gid, mode))
|
||||
}
|
||||
|
||||
return llb.AddSSHSocket(opts...), nil
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
func defaultShell() []string {
|
||||
return []string{"/bin/sh", "-c"}
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package dockerfile2llb
|
||||
|
||||
func defaultShell() []string {
|
||||
return []string{"cmd", "/S", "/C"}
|
||||
}
|
38
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/directives.go
generated
vendored
38
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/directives.go
generated
vendored
@@ -1,38 +0,0 @@
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const keySyntax = "syntax"
|
||||
|
||||
var reDirective = regexp.MustCompile(`^#\s*([a-zA-Z][a-zA-Z0-9]*)\s*=\s*(.+?)\s*$`)
|
||||
|
||||
func DetectSyntax(r io.Reader) (string, string, bool) {
|
||||
directives := ParseDirectives(r)
|
||||
if len(directives) == 0 {
|
||||
return "", "", false
|
||||
}
|
||||
v, ok := directives[keySyntax]
|
||||
if !ok {
|
||||
return "", "", false
|
||||
}
|
||||
p := strings.SplitN(v, " ", 2)
|
||||
return p[0], v, true
|
||||
}
|
||||
|
||||
func ParseDirectives(r io.Reader) map[string]string {
|
||||
m := map[string]string{}
|
||||
s := bufio.NewScanner(r)
|
||||
for s.Scan() {
|
||||
match := reDirective.FindStringSubmatch(s.Text())
|
||||
if len(match) == 0 {
|
||||
return m
|
||||
}
|
||||
m[strings.ToLower(match[1])] = match[2]
|
||||
}
|
||||
return m
|
||||
}
|
79
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/image.go
generated
vendored
79
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/image.go
generated
vendored
@@ -1,79 +0,0 @@
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/moby/buildkit/util/system"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
|
||||
type HealthConfig struct {
|
||||
// Test is the test to perform to check that the container is healthy.
|
||||
// An empty slice means to inherit the default.
|
||||
// The options are:
|
||||
// {} : inherit healthcheck
|
||||
// {"NONE"} : disable healthcheck
|
||||
// {"CMD", args...} : exec arguments directly
|
||||
// {"CMD-SHELL", command} : run command with system's default shell
|
||||
Test []string `json:",omitempty"`
|
||||
|
||||
// Zero means to inherit. Durations are expressed as integer nanoseconds.
|
||||
Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
|
||||
Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.
|
||||
StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down.
|
||||
|
||||
// Retries is the number of consecutive failures needed to consider a container as unhealthy.
|
||||
// Zero means inherit.
|
||||
Retries int `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ImageConfig is a docker compatible config for an image
|
||||
type ImageConfig struct {
|
||||
specs.ImageConfig
|
||||
|
||||
Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
|
||||
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific)
|
||||
|
||||
// NetworkDisabled bool `json:",omitempty"` // Is network disabled
|
||||
// MacAddress string `json:",omitempty"` // Mac Address of the container
|
||||
OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
|
||||
StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container
|
||||
Shell strslice.StrSlice `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
|
||||
}
|
||||
|
||||
// Image is the JSON structure which describes some basic information about the image.
|
||||
// This provides the `application/vnd.oci.image.config.v1+json` mediatype when marshalled to JSON.
|
||||
type Image struct {
|
||||
specs.Image
|
||||
|
||||
// Config defines the execution parameters which should be used as a base when running a container using the image.
|
||||
Config ImageConfig `json:"config,omitempty"`
|
||||
|
||||
// Variant defines platform variant. To be added to OCI.
|
||||
Variant string `json:"variant,omitempty"`
|
||||
}
|
||||
|
||||
func clone(src Image) Image {
|
||||
img := src
|
||||
img.Config = src.Config
|
||||
img.Config.Env = append([]string{}, src.Config.Env...)
|
||||
img.Config.Cmd = append([]string{}, src.Config.Cmd...)
|
||||
img.Config.Entrypoint = append([]string{}, src.Config.Entrypoint...)
|
||||
return img
|
||||
}
|
||||
|
||||
func emptyImage(platform specs.Platform) Image {
|
||||
img := Image{
|
||||
Image: specs.Image{
|
||||
Architecture: platform.Architecture,
|
||||
OS: platform.OS,
|
||||
},
|
||||
Variant: platform.Variant,
|
||||
}
|
||||
img.RootFS.Type = "layers"
|
||||
img.Config.WorkingDir = "/"
|
||||
img.Config.Env = []string{"PATH=" + system.DefaultPathEnv}
|
||||
return img
|
||||
}
|
58
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/platform.go
generated
vendored
58
vendor/github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb/platform.go
generated
vendored
@@ -1,58 +0,0 @@
|
||||
package dockerfile2llb
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type platformOpt struct {
|
||||
targetPlatform specs.Platform
|
||||
buildPlatforms []specs.Platform
|
||||
implicitTarget bool
|
||||
}
|
||||
|
||||
func buildPlatformOpt(opt *ConvertOpt) *platformOpt {
|
||||
buildPlatforms := opt.BuildPlatforms
|
||||
targetPlatform := opt.TargetPlatform
|
||||
implicitTargetPlatform := false
|
||||
|
||||
if opt.TargetPlatform != nil && opt.BuildPlatforms == nil {
|
||||
buildPlatforms = []specs.Platform{*opt.TargetPlatform}
|
||||
}
|
||||
if len(buildPlatforms) == 0 {
|
||||
buildPlatforms = []specs.Platform{platforms.DefaultSpec()}
|
||||
}
|
||||
|
||||
if opt.TargetPlatform == nil {
|
||||
implicitTargetPlatform = true
|
||||
targetPlatform = &buildPlatforms[0]
|
||||
}
|
||||
|
||||
return &platformOpt{
|
||||
targetPlatform: *targetPlatform,
|
||||
buildPlatforms: buildPlatforms,
|
||||
implicitTarget: implicitTargetPlatform,
|
||||
}
|
||||
}
|
||||
|
||||
func getPlatformArgs(po *platformOpt) []instructions.KeyValuePairOptional {
|
||||
bp := po.buildPlatforms[0]
|
||||
tp := po.targetPlatform
|
||||
m := map[string]string{
|
||||
"BUILDPLATFORM": platforms.Format(bp),
|
||||
"BUILDOS": bp.OS,
|
||||
"BUILDARCH": bp.Architecture,
|
||||
"BUILDVARIANT": bp.Variant,
|
||||
"TARGETPLATFORM": platforms.Format(tp),
|
||||
"TARGETOS": tp.OS,
|
||||
"TARGETARCH": tp.Architecture,
|
||||
"TARGETVARIANT": tp.Variant,
|
||||
}
|
||||
opts := make([]instructions.KeyValuePairOptional, 0, len(m))
|
||||
for k, v := range m {
|
||||
s := v
|
||||
opts = append(opts, instructions.KeyValuePairOptional{Key: k, Value: &s})
|
||||
}
|
||||
return opts
|
||||
}
|
200
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/bflag.go
generated
vendored
200
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/bflag.go
generated
vendored
@@ -1,200 +0,0 @@
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FlagType is the type of the build flag
|
||||
type FlagType int
|
||||
|
||||
const (
|
||||
boolType FlagType = iota
|
||||
stringType
|
||||
stringsType
|
||||
)
|
||||
|
||||
// BFlags contains all flags information for the builder
|
||||
type BFlags struct {
|
||||
Args []string // actual flags/args from cmd line
|
||||
flags map[string]*Flag
|
||||
used map[string]*Flag
|
||||
Err error
|
||||
}
|
||||
|
||||
// Flag contains all information for a flag
|
||||
type Flag struct {
|
||||
bf *BFlags
|
||||
name string
|
||||
flagType FlagType
|
||||
Value string
|
||||
StringValues []string
|
||||
}
|
||||
|
||||
// NewBFlags returns the new BFlags struct
|
||||
func NewBFlags() *BFlags {
|
||||
return &BFlags{
|
||||
flags: make(map[string]*Flag),
|
||||
used: make(map[string]*Flag),
|
||||
}
|
||||
}
|
||||
|
||||
// NewBFlagsWithArgs returns the new BFlags struct with Args set to args
|
||||
func NewBFlagsWithArgs(args []string) *BFlags {
|
||||
flags := NewBFlags()
|
||||
flags.Args = args
|
||||
return flags
|
||||
}
|
||||
|
||||
// AddBool adds a bool flag to BFlags
|
||||
// Note, any error will be generated when Parse() is called (see Parse).
|
||||
func (bf *BFlags) AddBool(name string, def bool) *Flag {
|
||||
flag := bf.addFlag(name, boolType)
|
||||
if flag == nil {
|
||||
return nil
|
||||
}
|
||||
if def {
|
||||
flag.Value = "true"
|
||||
} else {
|
||||
flag.Value = "false"
|
||||
}
|
||||
return flag
|
||||
}
|
||||
|
||||
// AddString adds a string flag to BFlags
|
||||
// Note, any error will be generated when Parse() is called (see Parse).
|
||||
func (bf *BFlags) AddString(name string, def string) *Flag {
|
||||
flag := bf.addFlag(name, stringType)
|
||||
if flag == nil {
|
||||
return nil
|
||||
}
|
||||
flag.Value = def
|
||||
return flag
|
||||
}
|
||||
|
||||
// AddStrings adds a string flag to BFlags that can match multiple values
|
||||
func (bf *BFlags) AddStrings(name string) *Flag {
|
||||
flag := bf.addFlag(name, stringsType)
|
||||
if flag == nil {
|
||||
return nil
|
||||
}
|
||||
return flag
|
||||
}
|
||||
|
||||
// addFlag is a generic func used by the other AddXXX() func
|
||||
// to add a new flag to the BFlags struct.
|
||||
// Note, any error will be generated when Parse() is called (see Parse).
|
||||
func (bf *BFlags) addFlag(name string, flagType FlagType) *Flag {
|
||||
if _, ok := bf.flags[name]; ok {
|
||||
bf.Err = fmt.Errorf("Duplicate flag defined: %s", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
newFlag := &Flag{
|
||||
bf: bf,
|
||||
name: name,
|
||||
flagType: flagType,
|
||||
}
|
||||
bf.flags[name] = newFlag
|
||||
|
||||
return newFlag
|
||||
}
|
||||
|
||||
// IsUsed checks if the flag is used
|
||||
func (fl *Flag) IsUsed() bool {
|
||||
if _, ok := fl.bf.used[fl.name]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTrue checks if a bool flag is true
|
||||
func (fl *Flag) IsTrue() bool {
|
||||
if fl.flagType != boolType {
|
||||
// Should never get here
|
||||
panic(fmt.Errorf("Trying to use IsTrue on a non-boolean: %s", fl.name))
|
||||
}
|
||||
return fl.Value == "true"
|
||||
}
|
||||
|
||||
// Parse parses and checks if the BFlags is valid.
|
||||
// Any error noticed during the AddXXX() funcs will be generated/returned
|
||||
// here. We do this because an error during AddXXX() is more like a
|
||||
// compile time error so it doesn't matter too much when we stop our
|
||||
// processing as long as we do stop it, so this allows the code
|
||||
// around AddXXX() to be just:
|
||||
// defFlag := AddString("description", "")
|
||||
// w/o needing to add an if-statement around each one.
|
||||
func (bf *BFlags) Parse() error {
|
||||
// If there was an error while defining the possible flags
|
||||
// go ahead and bubble it back up here since we didn't do it
|
||||
// earlier in the processing
|
||||
if bf.Err != nil {
|
||||
return fmt.Errorf("Error setting up flags: %s", bf.Err)
|
||||
}
|
||||
|
||||
for _, arg := range bf.Args {
|
||||
if !strings.HasPrefix(arg, "--") {
|
||||
return fmt.Errorf("Arg should start with -- : %s", arg)
|
||||
}
|
||||
|
||||
if arg == "--" {
|
||||
return nil
|
||||
}
|
||||
|
||||
arg = arg[2:]
|
||||
value := ""
|
||||
|
||||
index := strings.Index(arg, "=")
|
||||
if index >= 0 {
|
||||
value = arg[index+1:]
|
||||
arg = arg[:index]
|
||||
}
|
||||
|
||||
flag, ok := bf.flags[arg]
|
||||
if !ok {
|
||||
return fmt.Errorf("Unknown flag: %s", arg)
|
||||
}
|
||||
|
||||
if _, ok = bf.used[arg]; ok && flag.flagType != stringsType {
|
||||
return fmt.Errorf("Duplicate flag specified: %s", arg)
|
||||
}
|
||||
|
||||
bf.used[arg] = flag
|
||||
|
||||
switch flag.flagType {
|
||||
case boolType:
|
||||
// value == "" is only ok if no "=" was specified
|
||||
if index >= 0 && value == "" {
|
||||
return fmt.Errorf("Missing a value on flag: %s", arg)
|
||||
}
|
||||
|
||||
lower := strings.ToLower(value)
|
||||
if lower == "" {
|
||||
flag.Value = "true"
|
||||
} else if lower == "true" || lower == "false" {
|
||||
flag.Value = lower
|
||||
} else {
|
||||
return fmt.Errorf("Expecting boolean value for flag %s, not: %s", arg, value)
|
||||
}
|
||||
|
||||
case stringType:
|
||||
if index < 0 {
|
||||
return fmt.Errorf("Missing a value on flag: %s", arg)
|
||||
}
|
||||
flag.Value = value
|
||||
|
||||
case stringsType:
|
||||
if index < 0 {
|
||||
return fmt.Errorf("Missing a value on flag: %s", arg)
|
||||
}
|
||||
flag.StringValues = append(flag.StringValues, value)
|
||||
|
||||
default:
|
||||
panic("No idea what kind of flag we have! Should never get here!")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
451
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands.go
generated
vendored
451
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands.go
generated
vendored
@@ -1,451 +0,0 @@
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
)
|
||||
|
||||
// KeyValuePair represent an arbitrary named value (useful in slice instead of map[string] string to preserve ordering)
|
||||
type KeyValuePair struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (kvp *KeyValuePair) String() string {
|
||||
return kvp.Key + "=" + kvp.Value
|
||||
}
|
||||
|
||||
// KeyValuePairOptional is the same as KeyValuePair but Value is optional
|
||||
type KeyValuePairOptional struct {
|
||||
Key string
|
||||
Value *string
|
||||
}
|
||||
|
||||
func (kvpo *KeyValuePairOptional) ValueString() string {
|
||||
v := ""
|
||||
if kvpo.Value != nil {
|
||||
v = *kvpo.Value
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Command is implemented by every command present in a dockerfile
|
||||
type Command interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
// KeyValuePairs is a slice of KeyValuePair
|
||||
type KeyValuePairs []KeyValuePair
|
||||
|
||||
// withNameAndCode is the base of every command in a Dockerfile (String() returns its source code)
|
||||
type withNameAndCode struct {
|
||||
code string
|
||||
name string
|
||||
}
|
||||
|
||||
func (c *withNameAndCode) String() string {
|
||||
return c.code
|
||||
}
|
||||
|
||||
// Name of the command
|
||||
func (c *withNameAndCode) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func newWithNameAndCode(req parseRequest) withNameAndCode {
|
||||
return withNameAndCode{code: strings.TrimSpace(req.original), name: req.command}
|
||||
}
|
||||
|
||||
// SingleWordExpander is a provider for variable expansion where 1 word => 1 output
|
||||
type SingleWordExpander func(word string) (string, error)
|
||||
|
||||
// SupportsSingleWordExpansion interface marks a command as supporting variable expansion
|
||||
type SupportsSingleWordExpansion interface {
|
||||
Expand(expander SingleWordExpander) error
|
||||
}
|
||||
|
||||
// PlatformSpecific adds platform checks to a command
|
||||
type PlatformSpecific interface {
|
||||
CheckPlatform(platform string) error
|
||||
}
|
||||
|
||||
func expandKvp(kvp KeyValuePair, expander SingleWordExpander) (KeyValuePair, error) {
|
||||
key, err := expander(kvp.Key)
|
||||
if err != nil {
|
||||
return KeyValuePair{}, err
|
||||
}
|
||||
value, err := expander(kvp.Value)
|
||||
if err != nil {
|
||||
return KeyValuePair{}, err
|
||||
}
|
||||
return KeyValuePair{Key: key, Value: value}, nil
|
||||
}
|
||||
func expandKvpsInPlace(kvps KeyValuePairs, expander SingleWordExpander) error {
|
||||
for i, kvp := range kvps {
|
||||
newKvp, err := expandKvp(kvp, expander)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kvps[i] = newKvp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func expandSliceInPlace(values []string, expander SingleWordExpander) error {
|
||||
for i, v := range values {
|
||||
newValue, err := expander(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
values[i] = newValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnvCommand : ENV key1 value1 [keyN valueN...]
|
||||
type EnvCommand struct {
|
||||
withNameAndCode
|
||||
Env KeyValuePairs // kvp slice instead of map to preserve ordering
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *EnvCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandKvpsInPlace(c.Env, expander)
|
||||
}
|
||||
|
||||
// MaintainerCommand : MAINTAINER maintainer_name
|
||||
type MaintainerCommand struct {
|
||||
withNameAndCode
|
||||
Maintainer string
|
||||
}
|
||||
|
||||
// NewLabelCommand creates a new 'LABEL' command
|
||||
func NewLabelCommand(k string, v string, NoExp bool) *LabelCommand {
|
||||
kvp := KeyValuePair{Key: k, Value: v}
|
||||
c := "LABEL "
|
||||
c += kvp.String()
|
||||
nc := withNameAndCode{code: c, name: "label"}
|
||||
cmd := &LabelCommand{
|
||||
withNameAndCode: nc,
|
||||
Labels: KeyValuePairs{
|
||||
kvp,
|
||||
},
|
||||
noExpand: NoExp,
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// LabelCommand : LABEL some json data describing the image
|
||||
//
|
||||
// Sets the Label variable foo to bar,
|
||||
//
|
||||
type LabelCommand struct {
|
||||
withNameAndCode
|
||||
Labels KeyValuePairs // kvp slice instead of map to preserve ordering
|
||||
noExpand bool
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *LabelCommand) Expand(expander SingleWordExpander) error {
|
||||
if c.noExpand {
|
||||
return nil
|
||||
}
|
||||
return expandKvpsInPlace(c.Labels, expander)
|
||||
}
|
||||
|
||||
// SourcesAndDest represent a list of source files and a destination
|
||||
type SourcesAndDest []string
|
||||
|
||||
// Sources list the source paths
|
||||
func (s SourcesAndDest) Sources() []string {
|
||||
res := make([]string, len(s)-1)
|
||||
copy(res, s[:len(s)-1])
|
||||
return res
|
||||
}
|
||||
|
||||
// Dest path of the operation
|
||||
func (s SourcesAndDest) Dest() string {
|
||||
return s[len(s)-1]
|
||||
}
|
||||
|
||||
// AddCommand : ADD foo /path
|
||||
//
|
||||
// Add the file 'foo' to '/path'. Tarball and Remote URL (http, https) handling
|
||||
// exist here. If you do not wish to have this automatic handling, use COPY.
|
||||
//
|
||||
type AddCommand struct {
|
||||
withNameAndCode
|
||||
SourcesAndDest
|
||||
Chown string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *AddCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandSliceInPlace(c.SourcesAndDest, expander)
|
||||
}
|
||||
|
||||
// CopyCommand : COPY foo /path
|
||||
//
|
||||
// Same as 'ADD' but without the tar and remote url handling.
|
||||
//
|
||||
type CopyCommand struct {
|
||||
withNameAndCode
|
||||
SourcesAndDest
|
||||
From string
|
||||
Chown string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *CopyCommand) Expand(expander SingleWordExpander) error {
|
||||
expandedChown, err := expander(c.Chown)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Chown = expandedChown
|
||||
return expandSliceInPlace(c.SourcesAndDest, expander)
|
||||
}
|
||||
|
||||
// OnbuildCommand : ONBUILD <some other command>
|
||||
type OnbuildCommand struct {
|
||||
withNameAndCode
|
||||
Expression string
|
||||
}
|
||||
|
||||
// WorkdirCommand : WORKDIR /tmp
|
||||
//
|
||||
// Set the working directory for future RUN/CMD/etc statements.
|
||||
//
|
||||
type WorkdirCommand struct {
|
||||
withNameAndCode
|
||||
Path string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *WorkdirCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Path = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShellDependantCmdLine represents a cmdline optionally prepended with the shell
|
||||
type ShellDependantCmdLine struct {
|
||||
CmdLine strslice.StrSlice
|
||||
PrependShell bool
|
||||
}
|
||||
|
||||
// RunCommand : RUN some command yo
|
||||
//
|
||||
// run a command and commit the image. Args are automatically prepended with
|
||||
// the current SHELL which defaults to 'sh -c' under linux or 'cmd /S /C' under
|
||||
// Windows, in the event there is only one argument The difference in processing:
|
||||
//
|
||||
// RUN echo hi # sh -c echo hi (Linux)
|
||||
// RUN echo hi # cmd /S /C echo hi (Windows)
|
||||
// RUN [ "echo", "hi" ] # echo hi
|
||||
//
|
||||
type RunCommand struct {
|
||||
withNameAndCode
|
||||
withExternalData
|
||||
ShellDependantCmdLine
|
||||
}
|
||||
|
||||
// CmdCommand : CMD foo
|
||||
//
|
||||
// Set the default command to run in the container (which may be empty).
|
||||
// Argument handling is the same as RUN.
|
||||
//
|
||||
type CmdCommand struct {
|
||||
withNameAndCode
|
||||
ShellDependantCmdLine
|
||||
}
|
||||
|
||||
// HealthCheckCommand : HEALTHCHECK foo
|
||||
//
|
||||
// Set the default healthcheck command to run in the container (which may be empty).
|
||||
// Argument handling is the same as RUN.
|
||||
//
|
||||
type HealthCheckCommand struct {
|
||||
withNameAndCode
|
||||
Health *container.HealthConfig
|
||||
}
|
||||
|
||||
// EntrypointCommand : ENTRYPOINT /usr/sbin/nginx
|
||||
//
|
||||
// Set the entrypoint to /usr/sbin/nginx. Will accept the CMD as the arguments
|
||||
// to /usr/sbin/nginx. Uses the default shell if not in JSON format.
|
||||
//
|
||||
// Handles command processing similar to CMD and RUN, only req.runConfig.Entrypoint
|
||||
// is initialized at newBuilder time instead of through argument parsing.
|
||||
//
|
||||
type EntrypointCommand struct {
|
||||
withNameAndCode
|
||||
ShellDependantCmdLine
|
||||
}
|
||||
|
||||
// ExposeCommand : EXPOSE 6667/tcp 7000/tcp
|
||||
//
|
||||
// Expose ports for links and port mappings. This all ends up in
|
||||
// req.runConfig.ExposedPorts for runconfig.
|
||||
//
|
||||
type ExposeCommand struct {
|
||||
withNameAndCode
|
||||
Ports []string
|
||||
}
|
||||
|
||||
// UserCommand : USER foo
|
||||
//
|
||||
// Set the user to 'foo' for future commands and when running the
|
||||
// ENTRYPOINT/CMD at container run time.
|
||||
//
|
||||
type UserCommand struct {
|
||||
withNameAndCode
|
||||
User string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *UserCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.User)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.User = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// VolumeCommand : VOLUME /foo
|
||||
//
|
||||
// Expose the volume /foo for use. Will also accept the JSON array form.
|
||||
//
|
||||
type VolumeCommand struct {
|
||||
withNameAndCode
|
||||
Volumes []string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *VolumeCommand) Expand(expander SingleWordExpander) error {
|
||||
return expandSliceInPlace(c.Volumes, expander)
|
||||
}
|
||||
|
||||
// StopSignalCommand : STOPSIGNAL signal
|
||||
//
|
||||
// Set the signal that will be used to kill the container.
|
||||
type StopSignalCommand struct {
|
||||
withNameAndCode
|
||||
Signal string
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *StopSignalCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.Signal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Signal = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckPlatform checks that the command is supported in the target platform
|
||||
func (c *StopSignalCommand) CheckPlatform(platform string) error {
|
||||
if platform == "windows" {
|
||||
return errors.New("The daemon on this platform does not support the command stopsignal")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ArgCommand : ARG name[=value]
|
||||
//
|
||||
// Adds the variable foo to the trusted list of variables that can be passed
|
||||
// to builder using the --build-arg flag for expansion/substitution or passing to 'run'.
|
||||
// Dockerfile author may optionally set a default value of this variable.
|
||||
type ArgCommand struct {
|
||||
withNameAndCode
|
||||
KeyValuePairOptional
|
||||
}
|
||||
|
||||
// Expand variables
|
||||
func (c *ArgCommand) Expand(expander SingleWordExpander) error {
|
||||
p, err := expander(c.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Key = p
|
||||
if c.Value != nil {
|
||||
p, err = expander(*c.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Value = &p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ShellCommand : SHELL powershell -command
|
||||
//
|
||||
// Set the non-default shell to use.
|
||||
type ShellCommand struct {
|
||||
withNameAndCode
|
||||
Shell strslice.StrSlice
|
||||
}
|
||||
|
||||
// Stage represents a single stage in a multi-stage build
|
||||
type Stage struct {
|
||||
Name string
|
||||
Commands []Command
|
||||
BaseName string
|
||||
SourceCode string
|
||||
Platform string
|
||||
}
|
||||
|
||||
// AddCommand to the stage
|
||||
func (s *Stage) AddCommand(cmd Command) {
|
||||
// todo: validate cmd type
|
||||
s.Commands = append(s.Commands, cmd)
|
||||
}
|
||||
|
||||
// IsCurrentStage check if the stage name is the current stage
|
||||
func IsCurrentStage(s []Stage, name string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
return s[len(s)-1].Name == name
|
||||
}
|
||||
|
||||
// CurrentStage return the last stage in a slice
|
||||
func CurrentStage(s []Stage) (*Stage, error) {
|
||||
if len(s) == 0 {
|
||||
return nil, errors.New("No build stage in current context")
|
||||
}
|
||||
return &s[len(s)-1], nil
|
||||
}
|
||||
|
||||
// HasStage looks for the presence of a given stage name
|
||||
func HasStage(s []Stage, name string) (int, bool) {
|
||||
for i, stage := range s {
|
||||
// Stage name is case-insensitive by design
|
||||
if strings.EqualFold(stage.Name, name) {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
type withExternalData struct {
|
||||
m map[interface{}]interface{}
|
||||
}
|
||||
|
||||
func (c *withExternalData) getExternalValue(k interface{}) interface{} {
|
||||
return c.m[k]
|
||||
}
|
||||
|
||||
func (c *withExternalData) setExternalValue(k, v interface{}) {
|
||||
if c.m == nil {
|
||||
c.m = map[interface{}]interface{}{}
|
||||
}
|
||||
c.m[k] = v
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
// +build !dfsecrets
|
||||
|
||||
package instructions
|
||||
|
||||
func isSecretMountsSupported() bool {
|
||||
return false
|
||||
}
|
7
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_nossh.go
generated
vendored
7
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_nossh.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
// +build !dfssh
|
||||
|
||||
package instructions
|
||||
|
||||
func isSSHMountsSupported() bool {
|
||||
return false
|
||||
}
|
263
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_runmount.go
generated
vendored
263
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_runmount.go
generated
vendored
@@ -1,263 +0,0 @@
|
||||
// +build dfrunmount
|
||||
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const MountTypeBind = "bind"
|
||||
const MountTypeCache = "cache"
|
||||
const MountTypeTmpfs = "tmpfs"
|
||||
const MountTypeSecret = "secret"
|
||||
const MountTypeSSH = "ssh"
|
||||
|
||||
var allowedMountTypes = map[string]struct{}{
|
||||
MountTypeBind: {},
|
||||
MountTypeCache: {},
|
||||
MountTypeTmpfs: {},
|
||||
MountTypeSecret: {},
|
||||
MountTypeSSH: {},
|
||||
}
|
||||
|
||||
const MountSharingShared = "shared"
|
||||
const MountSharingPrivate = "private"
|
||||
const MountSharingLocked = "locked"
|
||||
|
||||
var allowedSharingTypes = map[string]struct{}{
|
||||
MountSharingShared: {},
|
||||
MountSharingPrivate: {},
|
||||
MountSharingLocked: {},
|
||||
}
|
||||
|
||||
type mountsKeyT string
|
||||
|
||||
var mountsKey = mountsKeyT("dockerfile/run/mounts")
|
||||
|
||||
func init() {
|
||||
parseRunPreHooks = append(parseRunPreHooks, runMountPreHook)
|
||||
parseRunPostHooks = append(parseRunPostHooks, runMountPostHook)
|
||||
}
|
||||
|
||||
func isValidMountType(s string) bool {
|
||||
if s == "secret" {
|
||||
if !isSecretMountsSupported() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if s == "ssh" {
|
||||
if !isSSHMountsSupported() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
_, ok := allowedMountTypes[s]
|
||||
return ok
|
||||
}
|
||||
|
||||
func runMountPreHook(cmd *RunCommand, req parseRequest) error {
|
||||
st := &mountState{}
|
||||
st.flag = req.flags.AddStrings("mount")
|
||||
cmd.setExternalValue(mountsKey, st)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runMountPostHook(cmd *RunCommand, req parseRequest) error {
|
||||
st := getMountState(cmd)
|
||||
if st == nil {
|
||||
return errors.Errorf("no mount state")
|
||||
}
|
||||
var mounts []*Mount
|
||||
for _, str := range st.flag.StringValues {
|
||||
m, err := parseMount(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mounts = append(mounts, m)
|
||||
}
|
||||
st.mounts = mounts
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMountState(cmd *RunCommand) *mountState {
|
||||
v := cmd.getExternalValue(mountsKey)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
return v.(*mountState)
|
||||
}
|
||||
|
||||
func GetMounts(cmd *RunCommand) []*Mount {
|
||||
return getMountState(cmd).mounts
|
||||
}
|
||||
|
||||
type mountState struct {
|
||||
flag *Flag
|
||||
mounts []*Mount
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
Type string
|
||||
From string
|
||||
Source string
|
||||
Target string
|
||||
ReadOnly bool
|
||||
CacheID string
|
||||
CacheSharing string
|
||||
Required bool
|
||||
Mode *uint64
|
||||
UID *uint64
|
||||
GID *uint64
|
||||
}
|
||||
|
||||
func parseMount(value string) (*Mount, error) {
|
||||
csvReader := csv.NewReader(strings.NewReader(value))
|
||||
fields, err := csvReader.Read()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse csv mounts")
|
||||
}
|
||||
|
||||
m := &Mount{Type: MountTypeBind}
|
||||
|
||||
roAuto := true
|
||||
|
||||
for _, field := range fields {
|
||||
parts := strings.SplitN(field, "=", 2)
|
||||
key := strings.ToLower(parts[0])
|
||||
|
||||
if len(parts) == 1 {
|
||||
switch key {
|
||||
case "readonly", "ro":
|
||||
m.ReadOnly = true
|
||||
roAuto = false
|
||||
continue
|
||||
case "readwrite", "rw":
|
||||
m.ReadOnly = false
|
||||
roAuto = false
|
||||
continue
|
||||
case "required":
|
||||
if m.Type == "secret" || m.Type == "ssh" {
|
||||
m.Required = true
|
||||
continue
|
||||
} else {
|
||||
return nil, errors.Errorf("unexpected key '%s' for mount type '%s'", key, m.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.Errorf("invalid field '%s' must be a key=value pair", field)
|
||||
}
|
||||
|
||||
value := parts[1]
|
||||
switch key {
|
||||
case "type":
|
||||
if !isValidMountType(strings.ToLower(value)) {
|
||||
return nil, errors.Errorf("unsupported mount type %q", value)
|
||||
}
|
||||
m.Type = strings.ToLower(value)
|
||||
case "from":
|
||||
m.From = value
|
||||
case "source", "src":
|
||||
m.Source = value
|
||||
case "target", "dst", "destination":
|
||||
m.Target = value
|
||||
case "readonly", "ro":
|
||||
m.ReadOnly, err = strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid value for %s: %s", key, value)
|
||||
}
|
||||
roAuto = false
|
||||
case "readwrite", "rw":
|
||||
rw, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid value for %s: %s", key, value)
|
||||
}
|
||||
m.ReadOnly = !rw
|
||||
roAuto = false
|
||||
case "required":
|
||||
if m.Type == "secret" || m.Type == "ssh" {
|
||||
v, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid value for %s: %s", key, value)
|
||||
}
|
||||
m.Required = v
|
||||
} else {
|
||||
return nil, errors.Errorf("unexpected key '%s' for mount type '%s'", key, m.Type)
|
||||
}
|
||||
case "id":
|
||||
m.CacheID = value
|
||||
case "sharing":
|
||||
if _, ok := allowedSharingTypes[strings.ToLower(value)]; !ok {
|
||||
return nil, errors.Errorf("unsupported sharing value %q", value)
|
||||
}
|
||||
m.CacheSharing = strings.ToLower(value)
|
||||
case "mode":
|
||||
mode, err := strconv.ParseUint(value, 8, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid value %s for mode", value)
|
||||
}
|
||||
m.Mode = &mode
|
||||
case "uid":
|
||||
uid, err := strconv.ParseUint(value, 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid value %s for uid", value)
|
||||
}
|
||||
m.UID = &uid
|
||||
case "gid":
|
||||
gid, err := strconv.ParseUint(value, 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid value %s for gid", value)
|
||||
}
|
||||
m.GID = &gid
|
||||
default:
|
||||
return nil, errors.Errorf("unexpected key '%s' in '%s'", key, field)
|
||||
}
|
||||
}
|
||||
|
||||
fileInfoAllowed := m.Type == MountTypeSecret || m.Type == MountTypeSSH || m.Type == MountTypeCache
|
||||
|
||||
if m.Mode != nil && !fileInfoAllowed {
|
||||
return nil, errors.Errorf("mode not allowed for %q type mounts", m.Type)
|
||||
}
|
||||
|
||||
if m.UID != nil && !fileInfoAllowed {
|
||||
return nil, errors.Errorf("uid not allowed for %q type mounts", m.Type)
|
||||
}
|
||||
|
||||
if m.GID != nil && !fileInfoAllowed {
|
||||
return nil, errors.Errorf("gid not allowed for %q type mounts", m.Type)
|
||||
}
|
||||
|
||||
if roAuto {
|
||||
if m.Type == MountTypeCache || m.Type == MountTypeTmpfs {
|
||||
m.ReadOnly = false
|
||||
} else {
|
||||
m.ReadOnly = true
|
||||
}
|
||||
}
|
||||
|
||||
if m.CacheSharing != "" && m.Type != MountTypeCache {
|
||||
return nil, errors.Errorf("invalid cache sharing set for %v mount", m.Type)
|
||||
}
|
||||
|
||||
if m.Type == MountTypeSecret {
|
||||
if m.From != "" {
|
||||
return nil, errors.Errorf("secret mount should not have a from")
|
||||
}
|
||||
if m.CacheSharing != "" {
|
||||
return nil, errors.Errorf("secret mount should not define sharing")
|
||||
}
|
||||
if m.Source == "" && m.Target == "" && m.CacheID == "" {
|
||||
return nil, errors.Errorf("invalid secret mount. one of source, target required")
|
||||
}
|
||||
if m.Source != "" && m.CacheID != "" {
|
||||
return nil, errors.Errorf("both source and id can't be set")
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
63
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_runnetwork.go
generated
vendored
63
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_runnetwork.go
generated
vendored
@@ -1,63 +0,0 @@
|
||||
// +build dfrunnetwork
|
||||
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
NetworkDefault = "default"
|
||||
NetworkNone = "none"
|
||||
NetworkHost = "host"
|
||||
)
|
||||
|
||||
var allowedNetwork = map[string]struct{}{
|
||||
NetworkDefault: {},
|
||||
NetworkNone: {},
|
||||
NetworkHost: {},
|
||||
}
|
||||
|
||||
func isValidNetwork(value string) bool {
|
||||
_, ok := allowedNetwork[value]
|
||||
return ok
|
||||
}
|
||||
|
||||
var networkKey = "dockerfile/run/network"
|
||||
|
||||
func init() {
|
||||
parseRunPreHooks = append(parseRunPreHooks, runNetworkPreHook)
|
||||
parseRunPostHooks = append(parseRunPostHooks, runNetworkPostHook)
|
||||
}
|
||||
|
||||
func runNetworkPreHook(cmd *RunCommand, req parseRequest) error {
|
||||
st := &networkState{}
|
||||
st.flag = req.flags.AddString("network", NetworkDefault)
|
||||
cmd.setExternalValue(networkKey, st)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runNetworkPostHook(cmd *RunCommand, req parseRequest) error {
|
||||
st := cmd.getExternalValue(networkKey).(*networkState)
|
||||
if st == nil {
|
||||
return errors.Errorf("no network state")
|
||||
}
|
||||
|
||||
value := st.flag.Value
|
||||
if !isValidNetwork(value) {
|
||||
return errors.Errorf("invalid network mode %q", value)
|
||||
}
|
||||
|
||||
st.networkMode = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetNetwork(cmd *RunCommand) string {
|
||||
return cmd.getExternalValue(networkKey).(*networkState).networkMode
|
||||
}
|
||||
|
||||
type networkState struct {
|
||||
flag *Flag
|
||||
networkMode string
|
||||
}
|
61
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_runsecurity.go
generated
vendored
61
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_runsecurity.go
generated
vendored
@@ -1,61 +0,0 @@
|
||||
// +build dfrunsecurity
|
||||
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
SecurityInsecure = "insecure"
|
||||
SecuritySandbox = "sandbox"
|
||||
)
|
||||
|
||||
var allowedSecurity = map[string]struct{}{
|
||||
SecurityInsecure: {},
|
||||
SecuritySandbox: {},
|
||||
}
|
||||
|
||||
func isValidSecurity(value string) bool {
|
||||
_, ok := allowedSecurity[value]
|
||||
return ok
|
||||
}
|
||||
|
||||
var securityKey = "dockerfile/run/security"
|
||||
|
||||
func init() {
|
||||
parseRunPreHooks = append(parseRunPreHooks, runSecurityPreHook)
|
||||
parseRunPostHooks = append(parseRunPostHooks, runSecurityPostHook)
|
||||
}
|
||||
|
||||
func runSecurityPreHook(cmd *RunCommand, req parseRequest) error {
|
||||
st := &securityState{}
|
||||
st.flag = req.flags.AddString("security", SecuritySandbox)
|
||||
cmd.setExternalValue(securityKey, st)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runSecurityPostHook(cmd *RunCommand, req parseRequest) error {
|
||||
st := cmd.getExternalValue(securityKey).(*securityState)
|
||||
if st == nil {
|
||||
return errors.Errorf("no security state")
|
||||
}
|
||||
|
||||
value := st.flag.Value
|
||||
if !isValidSecurity(value) {
|
||||
return errors.Errorf("security %q is not valid", value)
|
||||
}
|
||||
|
||||
st.security = value
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSecurity(cmd *RunCommand) string {
|
||||
return cmd.getExternalValue(securityKey).(*securityState).security
|
||||
}
|
||||
|
||||
type securityState struct {
|
||||
flag *Flag
|
||||
security string
|
||||
}
|
7
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_secrets.go
generated
vendored
7
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_secrets.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
// +build dfsecrets
|
||||
|
||||
package instructions
|
||||
|
||||
func isSecretMountsSupported() bool {
|
||||
return true
|
||||
}
|
7
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_ssh.go
generated
vendored
7
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/commands_ssh.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
// +build dfssh
|
||||
|
||||
package instructions
|
||||
|
||||
func isSSHMountsSupported() bool {
|
||||
return true
|
||||
}
|
9
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/errors_unix.go
generated
vendored
9
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/errors_unix.go
generated
vendored
@@ -1,9 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package instructions
|
||||
|
||||
import "fmt"
|
||||
|
||||
func errNotJSON(command, _ string) error {
|
||||
return fmt.Errorf("%s requires the arguments to be in JSON form", command)
|
||||
}
|
27
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/errors_windows.go
generated
vendored
27
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/errors_windows.go
generated
vendored
@@ -1,27 +0,0 @@
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func errNotJSON(command, original string) error {
|
||||
// For Windows users, give a hint if it looks like it might contain
|
||||
// a path which hasn't been escaped such as ["c:\windows\system32\prog.exe", "-param"],
|
||||
// as JSON must be escaped. Unfortunate...
|
||||
//
|
||||
// Specifically looking for quote-driveletter-colon-backslash, there's no
|
||||
// double backslash and a [] pair. No, this is not perfect, but it doesn't
|
||||
// have to be. It's simply a hint to make life a little easier.
|
||||
extra := ""
|
||||
original = filepath.FromSlash(strings.ToLower(strings.Replace(strings.ToLower(original), strings.ToLower(command)+" ", "", -1)))
|
||||
if len(regexp.MustCompile(`"[a-z]:\\.*`).FindStringSubmatch(original)) > 0 &&
|
||||
!strings.Contains(original, `\\`) &&
|
||||
strings.Contains(original, "[") &&
|
||||
strings.Contains(original, "]") {
|
||||
extra = fmt.Sprintf(`. It looks like '%s' includes a file path without an escaped back-slash. JSON requires back-slashes to be escaped such as ["c:\\path\\to\\file.exe", "/parameter"]`, original)
|
||||
}
|
||||
return fmt.Errorf("%s requires the arguments to be in JSON form%s", command, extra)
|
||||
}
|
650
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/parse.go
generated
vendored
650
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/parse.go
generated
vendored
@@ -1,650 +0,0 @@
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/strslice"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/command"
|
||||
"github.com/moby/buildkit/frontend/dockerfile/parser"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type parseRequest struct {
|
||||
command string
|
||||
args []string
|
||||
attributes map[string]bool
|
||||
flags *BFlags
|
||||
original string
|
||||
}
|
||||
|
||||
var parseRunPreHooks []func(*RunCommand, parseRequest) error
|
||||
var parseRunPostHooks []func(*RunCommand, parseRequest) error
|
||||
|
||||
func nodeArgs(node *parser.Node) []string {
|
||||
result := []string{}
|
||||
for ; node.Next != nil; node = node.Next {
|
||||
arg := node.Next
|
||||
if len(arg.Children) == 0 {
|
||||
result = append(result, arg.Value)
|
||||
} else if len(arg.Children) == 1 {
|
||||
//sub command
|
||||
result = append(result, arg.Children[0].Value)
|
||||
result = append(result, nodeArgs(arg.Children[0])...)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func newParseRequestFromNode(node *parser.Node) parseRequest {
|
||||
return parseRequest{
|
||||
command: node.Value,
|
||||
args: nodeArgs(node),
|
||||
attributes: node.Attributes,
|
||||
original: node.Original,
|
||||
flags: NewBFlagsWithArgs(node.Flags),
|
||||
}
|
||||
}
|
||||
|
||||
// ParseInstruction converts an AST to a typed instruction (either a command or a build stage beginning when encountering a `FROM` statement)
|
||||
func ParseInstruction(node *parser.Node) (interface{}, error) {
|
||||
req := newParseRequestFromNode(node)
|
||||
switch node.Value {
|
||||
case command.Env:
|
||||
return parseEnv(req)
|
||||
case command.Maintainer:
|
||||
return parseMaintainer(req)
|
||||
case command.Label:
|
||||
return parseLabel(req)
|
||||
case command.Add:
|
||||
return parseAdd(req)
|
||||
case command.Copy:
|
||||
return parseCopy(req)
|
||||
case command.From:
|
||||
return parseFrom(req)
|
||||
case command.Onbuild:
|
||||
return parseOnBuild(req)
|
||||
case command.Workdir:
|
||||
return parseWorkdir(req)
|
||||
case command.Run:
|
||||
return parseRun(req)
|
||||
case command.Cmd:
|
||||
return parseCmd(req)
|
||||
case command.Healthcheck:
|
||||
return parseHealthcheck(req)
|
||||
case command.Entrypoint:
|
||||
return parseEntrypoint(req)
|
||||
case command.Expose:
|
||||
return parseExpose(req)
|
||||
case command.User:
|
||||
return parseUser(req)
|
||||
case command.Volume:
|
||||
return parseVolume(req)
|
||||
case command.StopSignal:
|
||||
return parseStopSignal(req)
|
||||
case command.Arg:
|
||||
return parseArg(req)
|
||||
case command.Shell:
|
||||
return parseShell(req)
|
||||
}
|
||||
|
||||
return nil, &UnknownInstruction{Instruction: node.Value, Line: node.StartLine}
|
||||
}
|
||||
|
||||
// ParseCommand converts an AST to a typed Command
|
||||
func ParseCommand(node *parser.Node) (Command, error) {
|
||||
s, err := ParseInstruction(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c, ok := s.(Command); ok {
|
||||
return c, nil
|
||||
}
|
||||
return nil, errors.Errorf("%T is not a command type", s)
|
||||
}
|
||||
|
||||
// UnknownInstruction represents an error occurring when a command is unresolvable
|
||||
type UnknownInstruction struct {
|
||||
Line int
|
||||
Instruction string
|
||||
}
|
||||
|
||||
func (e *UnknownInstruction) Error() string {
|
||||
return fmt.Sprintf("unknown instruction: %s", strings.ToUpper(e.Instruction))
|
||||
}
|
||||
|
||||
// IsUnknownInstruction checks if the error is an UnknownInstruction or a parseError containing an UnknownInstruction
|
||||
func IsUnknownInstruction(err error) bool {
|
||||
_, ok := err.(*UnknownInstruction)
|
||||
if !ok {
|
||||
var pe *parseError
|
||||
if pe, ok = err.(*parseError); ok {
|
||||
_, ok = pe.inner.(*UnknownInstruction)
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
type parseError struct {
|
||||
inner error
|
||||
node *parser.Node
|
||||
}
|
||||
|
||||
func (e *parseError) Error() string {
|
||||
return fmt.Sprintf("Dockerfile parse error line %d: %v", e.node.StartLine, e.inner.Error())
|
||||
}
|
||||
|
||||
// Parse a Dockerfile into a collection of buildable stages.
|
||||
// metaArgs is a collection of ARG instructions that occur before the first FROM.
|
||||
func Parse(ast *parser.Node) (stages []Stage, metaArgs []ArgCommand, err error) {
|
||||
for _, n := range ast.Children {
|
||||
cmd, err := ParseInstruction(n)
|
||||
if err != nil {
|
||||
return nil, nil, &parseError{inner: err, node: n}
|
||||
}
|
||||
if len(stages) == 0 {
|
||||
// meta arg case
|
||||
if a, isArg := cmd.(*ArgCommand); isArg {
|
||||
metaArgs = append(metaArgs, *a)
|
||||
continue
|
||||
}
|
||||
}
|
||||
switch c := cmd.(type) {
|
||||
case *Stage:
|
||||
stages = append(stages, *c)
|
||||
case Command:
|
||||
stage, err := CurrentStage(stages)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
stage.AddCommand(c)
|
||||
default:
|
||||
return nil, nil, errors.Errorf("%T is not a command type", cmd)
|
||||
}
|
||||
|
||||
}
|
||||
return stages, metaArgs, nil
|
||||
}
|
||||
|
||||
func parseKvps(args []string, cmdName string) (KeyValuePairs, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, errAtLeastOneArgument(cmdName)
|
||||
}
|
||||
if len(args)%2 != 0 {
|
||||
// should never get here, but just in case
|
||||
return nil, errTooManyArguments(cmdName)
|
||||
}
|
||||
var res KeyValuePairs
|
||||
for j := 0; j < len(args); j += 2 {
|
||||
if len(args[j]) == 0 {
|
||||
return nil, errBlankCommandNames(cmdName)
|
||||
}
|
||||
name := args[j]
|
||||
value := args[j+1]
|
||||
res = append(res, KeyValuePair{Key: name, Value: value})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func parseEnv(req parseRequest) (*EnvCommand, error) {
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
envs, err := parseKvps(req.args, "ENV")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EnvCommand{
|
||||
Env: envs,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseMaintainer(req parseRequest) (*MaintainerCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("MAINTAINER")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &MaintainerCommand{
|
||||
Maintainer: req.args[0],
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseLabel(req parseRequest) (*LabelCommand, error) {
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels, err := parseKvps(req.args, "LABEL")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &LabelCommand{
|
||||
Labels: labels,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseAdd(req parseRequest) (*AddCommand, error) {
|
||||
if len(req.args) < 2 {
|
||||
return nil, errNoDestinationArgument("ADD")
|
||||
}
|
||||
flChown := req.flags.AddString("chown", "")
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AddCommand{
|
||||
SourcesAndDest: SourcesAndDest(req.args),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
Chown: flChown.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseCopy(req parseRequest) (*CopyCommand, error) {
|
||||
if len(req.args) < 2 {
|
||||
return nil, errNoDestinationArgument("COPY")
|
||||
}
|
||||
flChown := req.flags.AddString("chown", "")
|
||||
flFrom := req.flags.AddString("from", "")
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CopyCommand{
|
||||
SourcesAndDest: SourcesAndDest(req.args),
|
||||
From: flFrom.Value,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
Chown: flChown.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseFrom(req parseRequest) (*Stage, error) {
|
||||
stageName, err := parseBuildStageName(req.args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
flPlatform := req.flags.AddString("platform", "")
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
code := strings.TrimSpace(req.original)
|
||||
return &Stage{
|
||||
BaseName: req.args[0],
|
||||
Name: stageName,
|
||||
SourceCode: code,
|
||||
Commands: []Command{},
|
||||
Platform: flPlatform.Value,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseBuildStageName(args []string) (string, error) {
|
||||
stageName := ""
|
||||
switch {
|
||||
case len(args) == 3 && strings.EqualFold(args[1], "as"):
|
||||
stageName = strings.ToLower(args[2])
|
||||
if ok, _ := regexp.MatchString("^[a-z][a-z0-9-_\\.]*$", stageName); !ok {
|
||||
return "", errors.Errorf("invalid name for build stage: %q, name can't start with a number or contain symbols", args[2])
|
||||
}
|
||||
case len(args) != 1:
|
||||
return "", errors.New("FROM requires either one or three arguments")
|
||||
}
|
||||
|
||||
return stageName, nil
|
||||
}
|
||||
|
||||
func parseOnBuild(req parseRequest) (*OnbuildCommand, error) {
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("ONBUILD")
|
||||
}
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
triggerInstruction := strings.ToUpper(strings.TrimSpace(req.args[0]))
|
||||
switch strings.ToUpper(triggerInstruction) {
|
||||
case "ONBUILD":
|
||||
return nil, errors.New("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
|
||||
case "MAINTAINER", "FROM":
|
||||
return nil, fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
|
||||
}
|
||||
|
||||
original := regexp.MustCompile(`(?i)^\s*ONBUILD\s*`).ReplaceAllString(req.original, "")
|
||||
return &OnbuildCommand{
|
||||
Expression: original,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseWorkdir(req parseRequest) (*WorkdirCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("WORKDIR")
|
||||
}
|
||||
|
||||
err := req.flags.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &WorkdirCommand{
|
||||
Path: req.args[0],
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseShellDependentCommand(req parseRequest, emptyAsNil bool) ShellDependantCmdLine {
|
||||
args := handleJSONArgs(req.args, req.attributes)
|
||||
cmd := strslice.StrSlice(args)
|
||||
if emptyAsNil && len(cmd) == 0 {
|
||||
cmd = nil
|
||||
}
|
||||
return ShellDependantCmdLine{
|
||||
CmdLine: cmd,
|
||||
PrependShell: !req.attributes["json"],
|
||||
}
|
||||
}
|
||||
|
||||
func parseRun(req parseRequest) (*RunCommand, error) {
|
||||
cmd := &RunCommand{}
|
||||
|
||||
for _, fn := range parseRunPreHooks {
|
||||
if err := fn(cmd, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd.ShellDependantCmdLine = parseShellDependentCommand(req, false)
|
||||
cmd.withNameAndCode = newWithNameAndCode(req)
|
||||
|
||||
for _, fn := range parseRunPostHooks {
|
||||
if err := fn(cmd, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func parseCmd(req parseRequest) (*CmdCommand, error) {
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CmdCommand{
|
||||
ShellDependantCmdLine: parseShellDependentCommand(req, false),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func parseEntrypoint(req parseRequest) (*EntrypointCommand, error) {
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := &EntrypointCommand{
|
||||
ShellDependantCmdLine: parseShellDependentCommand(req, true),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
// parseOptInterval(flag) is the duration of flag.Value, or 0 if
|
||||
// empty. An error is reported if the value is given and less than minimum duration.
|
||||
func parseOptInterval(f *Flag) (time.Duration, error) {
|
||||
s := f.Value
|
||||
if s == "" {
|
||||
return 0, nil
|
||||
}
|
||||
d, err := time.ParseDuration(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if d < container.MinimumDuration {
|
||||
return 0, fmt.Errorf("Interval %#v cannot be less than %s", f.name, container.MinimumDuration)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
func parseHealthcheck(req parseRequest) (*HealthCheckCommand, error) {
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("HEALTHCHECK")
|
||||
}
|
||||
cmd := &HealthCheckCommand{
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
|
||||
typ := strings.ToUpper(req.args[0])
|
||||
args := req.args[1:]
|
||||
if typ == "NONE" {
|
||||
if len(args) != 0 {
|
||||
return nil, errors.New("HEALTHCHECK NONE takes no arguments")
|
||||
}
|
||||
test := strslice.StrSlice{typ}
|
||||
cmd.Health = &container.HealthConfig{
|
||||
Test: test,
|
||||
}
|
||||
} else {
|
||||
|
||||
healthcheck := container.HealthConfig{}
|
||||
|
||||
flInterval := req.flags.AddString("interval", "")
|
||||
flTimeout := req.flags.AddString("timeout", "")
|
||||
flStartPeriod := req.flags.AddString("start-period", "")
|
||||
flRetries := req.flags.AddString("retries", "")
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case "CMD":
|
||||
cmdSlice := handleJSONArgs(args, req.attributes)
|
||||
if len(cmdSlice) == 0 {
|
||||
return nil, errors.New("Missing command after HEALTHCHECK CMD")
|
||||
}
|
||||
|
||||
if !req.attributes["json"] {
|
||||
typ = "CMD-SHELL"
|
||||
}
|
||||
|
||||
healthcheck.Test = strslice.StrSlice(append([]string{typ}, cmdSlice...))
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown type %#v in HEALTHCHECK (try CMD)", typ)
|
||||
}
|
||||
|
||||
interval, err := parseOptInterval(flInterval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
healthcheck.Interval = interval
|
||||
|
||||
timeout, err := parseOptInterval(flTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
healthcheck.Timeout = timeout
|
||||
|
||||
startPeriod, err := parseOptInterval(flStartPeriod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
healthcheck.StartPeriod = startPeriod
|
||||
|
||||
if flRetries.Value != "" {
|
||||
retries, err := strconv.ParseInt(flRetries.Value, 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if retries < 1 {
|
||||
return nil, fmt.Errorf("--retries must be at least 1 (not %d)", retries)
|
||||
}
|
||||
healthcheck.Retries = int(retries)
|
||||
} else {
|
||||
healthcheck.Retries = 0
|
||||
}
|
||||
|
||||
cmd.Health = &healthcheck
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func parseExpose(req parseRequest) (*ExposeCommand, error) {
|
||||
portsTab := req.args
|
||||
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("EXPOSE")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Strings(portsTab)
|
||||
return &ExposeCommand{
|
||||
Ports: portsTab,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseUser(req parseRequest) (*UserCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("USER")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &UserCommand{
|
||||
User: req.args[0],
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseVolume(req parseRequest) (*VolumeCommand, error) {
|
||||
if len(req.args) == 0 {
|
||||
return nil, errAtLeastOneArgument("VOLUME")
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := &VolumeCommand{
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
|
||||
for _, v := range req.args {
|
||||
v = strings.TrimSpace(v)
|
||||
if v == "" {
|
||||
return nil, errors.New("VOLUME specified can not be an empty string")
|
||||
}
|
||||
cmd.Volumes = append(cmd.Volumes, v)
|
||||
}
|
||||
return cmd, nil
|
||||
|
||||
}
|
||||
|
||||
func parseStopSignal(req parseRequest) (*StopSignalCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("STOPSIGNAL")
|
||||
}
|
||||
sig := req.args[0]
|
||||
|
||||
cmd := &StopSignalCommand{
|
||||
Signal: sig,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}
|
||||
return cmd, nil
|
||||
|
||||
}
|
||||
|
||||
func parseArg(req parseRequest) (*ArgCommand, error) {
|
||||
if len(req.args) != 1 {
|
||||
return nil, errExactlyOneArgument("ARG")
|
||||
}
|
||||
|
||||
kvpo := KeyValuePairOptional{}
|
||||
|
||||
arg := req.args[0]
|
||||
// 'arg' can just be a name or name-value pair. Note that this is different
|
||||
// from 'env' that handles the split of name and value at the parser level.
|
||||
// The reason for doing it differently for 'arg' is that we support just
|
||||
// defining an arg and not assign it a value (while 'env' always expects a
|
||||
// name-value pair). If possible, it will be good to harmonize the two.
|
||||
if strings.Contains(arg, "=") {
|
||||
parts := strings.SplitN(arg, "=", 2)
|
||||
if len(parts[0]) == 0 {
|
||||
return nil, errBlankCommandNames("ARG")
|
||||
}
|
||||
|
||||
kvpo.Key = parts[0]
|
||||
kvpo.Value = &parts[1]
|
||||
} else {
|
||||
kvpo.Key = arg
|
||||
}
|
||||
|
||||
return &ArgCommand{
|
||||
KeyValuePairOptional: kvpo,
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseShell(req parseRequest) (*ShellCommand, error) {
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shellSlice := handleJSONArgs(req.args, req.attributes)
|
||||
switch {
|
||||
case len(shellSlice) == 0:
|
||||
// SHELL []
|
||||
return nil, errAtLeastOneArgument("SHELL")
|
||||
case req.attributes["json"]:
|
||||
// SHELL ["powershell", "-command"]
|
||||
|
||||
return &ShellCommand{
|
||||
Shell: strslice.StrSlice(shellSlice),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
default:
|
||||
// SHELL powershell -command - not JSON
|
||||
return nil, errNotJSON("SHELL", req.original)
|
||||
}
|
||||
}
|
||||
|
||||
func errAtLeastOneArgument(command string) error {
|
||||
return errors.Errorf("%s requires at least one argument", command)
|
||||
}
|
||||
|
||||
func errExactlyOneArgument(command string) error {
|
||||
return errors.Errorf("%s requires exactly one argument", command)
|
||||
}
|
||||
|
||||
func errNoDestinationArgument(command string) error {
|
||||
return errors.Errorf("%s requires at least two arguments, but only one was provided. Destination could not be determined.", command)
|
||||
}
|
||||
|
||||
func errBlankCommandNames(command string) error {
|
||||
return errors.Errorf("%s names can not be blank", command)
|
||||
}
|
||||
|
||||
func errTooManyArguments(command string) error {
|
||||
return errors.Errorf("Bad input to %s, too many arguments", command)
|
||||
}
|
19
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/support.go
generated
vendored
19
vendor/github.com/moby/buildkit/frontend/dockerfile/instructions/support.go
generated
vendored
@@ -1,19 +0,0 @@
|
||||
package instructions
|
||||
|
||||
import "strings"
|
||||
|
||||
// handleJSONArgs parses command passed to CMD, ENTRYPOINT, RUN and SHELL instruction in Dockerfile
|
||||
// for exec form it returns untouched args slice
|
||||
// for shell form it returns concatenated args as the first element of a slice
|
||||
func handleJSONArgs(args []string, attributes map[string]bool) []string {
|
||||
if len(args) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
if attributes != nil && attributes["json"] {
|
||||
return args
|
||||
}
|
||||
|
||||
// literal string command, not an exec array
|
||||
return []string{strings.Join(args, " ")}
|
||||
}
|
368
vendor/github.com/moby/buildkit/frontend/dockerfile/parser/line_parsers.go
generated
vendored
368
vendor/github.com/moby/buildkit/frontend/dockerfile/parser/line_parsers.go
generated
vendored
@@ -1,368 +0,0 @@
|
||||
package parser
|
||||
|
||||
// line parsers are dispatch calls that parse a single unit of text into a
|
||||
// Node object which contains the whole statement. Dockerfiles have varied
|
||||
// (but not usually unique, see ONBUILD for a unique example) parsing rules
|
||||
// per-command, and these unify the processing in a way that makes it
|
||||
// manageable.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var (
|
||||
errDockerfileNotStringArray = errors.New("when using JSON array syntax, arrays must be comprised of strings only")
|
||||
)
|
||||
|
||||
const (
|
||||
commandLabel = "LABEL"
|
||||
)
|
||||
|
||||
// ignore the current argument. This will still leave a command parsed, but
|
||||
// will not incorporate the arguments into the ast.
|
||||
func parseIgnore(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
return &Node{}, nil, nil
|
||||
}
|
||||
|
||||
// used for onbuild. Could potentially be used for anything that represents a
|
||||
// statement with sub-statements.
|
||||
//
|
||||
// ONBUILD RUN foo bar -> (onbuild (run foo bar))
|
||||
//
|
||||
func parseSubCommand(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
if rest == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
child, err := newNodeFromLine(rest, d)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &Node{Children: []*Node{child}}, nil, nil
|
||||
}
|
||||
|
||||
// helper to parse words (i.e space delimited or quoted strings) in a statement.
|
||||
// The quotes are preserved as part of this function and they are stripped later
|
||||
// as part of processWords().
|
||||
func parseWords(rest string, d *Directive) []string {
|
||||
const (
|
||||
inSpaces = iota // looking for start of a word
|
||||
inWord
|
||||
inQuote
|
||||
)
|
||||
|
||||
words := []string{}
|
||||
phase := inSpaces
|
||||
word := ""
|
||||
quote := '\000'
|
||||
blankOK := false
|
||||
var ch rune
|
||||
var chWidth int
|
||||
|
||||
for pos := 0; pos <= len(rest); pos += chWidth {
|
||||
if pos != len(rest) {
|
||||
ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
|
||||
}
|
||||
|
||||
if phase == inSpaces { // Looking for start of word
|
||||
if pos == len(rest) { // end of input
|
||||
break
|
||||
}
|
||||
if unicode.IsSpace(ch) { // skip spaces
|
||||
continue
|
||||
}
|
||||
phase = inWord // found it, fall through
|
||||
}
|
||||
if (phase == inWord || phase == inQuote) && (pos == len(rest)) {
|
||||
if blankOK || len(word) > 0 {
|
||||
words = append(words, word)
|
||||
}
|
||||
break
|
||||
}
|
||||
if phase == inWord {
|
||||
if unicode.IsSpace(ch) {
|
||||
phase = inSpaces
|
||||
if blankOK || len(word) > 0 {
|
||||
words = append(words, word)
|
||||
}
|
||||
word = ""
|
||||
blankOK = false
|
||||
continue
|
||||
}
|
||||
if ch == '\'' || ch == '"' {
|
||||
quote = ch
|
||||
blankOK = true
|
||||
phase = inQuote
|
||||
}
|
||||
if ch == d.escapeToken {
|
||||
if pos+chWidth == len(rest) {
|
||||
continue // just skip an escape token at end of line
|
||||
}
|
||||
// If we're not quoted and we see an escape token, then always just
|
||||
// add the escape token plus the char to the word, even if the char
|
||||
// is a quote.
|
||||
word += string(ch)
|
||||
pos += chWidth
|
||||
ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
|
||||
}
|
||||
word += string(ch)
|
||||
continue
|
||||
}
|
||||
if phase == inQuote {
|
||||
if ch == quote {
|
||||
phase = inWord
|
||||
}
|
||||
// The escape token is special except for ' quotes - can't escape anything for '
|
||||
if ch == d.escapeToken && quote != '\'' {
|
||||
if pos+chWidth == len(rest) {
|
||||
phase = inWord
|
||||
continue // just skip the escape token at end
|
||||
}
|
||||
pos += chWidth
|
||||
word += string(ch)
|
||||
ch, chWidth = utf8.DecodeRuneInString(rest[pos:])
|
||||
}
|
||||
word += string(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return words
|
||||
}
|
||||
|
||||
// parse environment like statements. Note that this does *not* handle
|
||||
// variable interpolation, which will be handled in the evaluator.
|
||||
func parseNameVal(rest string, key string, d *Directive) (*Node, error) {
|
||||
// This is kind of tricky because we need to support the old
|
||||
// variant: KEY name value
|
||||
// as well as the new one: KEY name=value ...
|
||||
// The trigger to know which one is being used will be whether we hit
|
||||
// a space or = first. space ==> old, "=" ==> new
|
||||
|
||||
words := parseWords(rest, d)
|
||||
if len(words) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Old format (KEY name value)
|
||||
if !strings.Contains(words[0], "=") {
|
||||
parts := tokenWhitespace.Split(rest, 2)
|
||||
if len(parts) < 2 {
|
||||
return nil, fmt.Errorf(key + " must have two arguments")
|
||||
}
|
||||
return newKeyValueNode(parts[0], parts[1]), nil
|
||||
}
|
||||
|
||||
var rootNode *Node
|
||||
var prevNode *Node
|
||||
for _, word := range words {
|
||||
if !strings.Contains(word, "=") {
|
||||
return nil, fmt.Errorf("Syntax error - can't find = in %q. Must be of the form: name=value", word)
|
||||
}
|
||||
|
||||
parts := strings.SplitN(word, "=", 2)
|
||||
node := newKeyValueNode(parts[0], parts[1])
|
||||
rootNode, prevNode = appendKeyValueNode(node, rootNode, prevNode)
|
||||
}
|
||||
|
||||
return rootNode, nil
|
||||
}
|
||||
|
||||
func newKeyValueNode(key, value string) *Node {
|
||||
return &Node{
|
||||
Value: key,
|
||||
Next: &Node{Value: value},
|
||||
}
|
||||
}
|
||||
|
||||
func appendKeyValueNode(node, rootNode, prevNode *Node) (*Node, *Node) {
|
||||
if rootNode == nil {
|
||||
rootNode = node
|
||||
}
|
||||
if prevNode != nil {
|
||||
prevNode.Next = node
|
||||
}
|
||||
|
||||
prevNode = node.Next
|
||||
return rootNode, prevNode
|
||||
}
|
||||
|
||||
func parseEnv(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
node, err := parseNameVal(rest, "ENV", d)
|
||||
return node, nil, err
|
||||
}
|
||||
|
||||
func parseLabel(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
node, err := parseNameVal(rest, commandLabel, d)
|
||||
return node, nil, err
|
||||
}
|
||||
|
||||
// parses a statement containing one or more keyword definition(s) and/or
|
||||
// value assignments, like `name1 name2= name3="" name4=value`.
|
||||
// Note that this is a stricter format than the old format of assignment,
|
||||
// allowed by parseNameVal(), in a way that this only allows assignment of the
|
||||
// form `keyword=[<value>]` like `name2=`, `name3=""`, and `name4=value` above.
|
||||
// In addition, a keyword definition alone is of the form `keyword` like `name1`
|
||||
// above. And the assignments `name2=` and `name3=""` are equivalent and
|
||||
// assign an empty value to the respective keywords.
|
||||
func parseNameOrNameVal(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
words := parseWords(rest, d)
|
||||
if len(words) == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
var (
|
||||
rootnode *Node
|
||||
prevNode *Node
|
||||
)
|
||||
for i, word := range words {
|
||||
node := &Node{}
|
||||
node.Value = word
|
||||
if i == 0 {
|
||||
rootnode = node
|
||||
} else {
|
||||
prevNode.Next = node
|
||||
}
|
||||
prevNode = node
|
||||
}
|
||||
|
||||
return rootnode, nil, nil
|
||||
}
|
||||
|
||||
// parses a whitespace-delimited set of arguments. The result is effectively a
|
||||
// linked list of string arguments.
|
||||
func parseStringsWhitespaceDelimited(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
if rest == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
node := &Node{}
|
||||
rootnode := node
|
||||
prevnode := node
|
||||
for _, str := range tokenWhitespace.Split(rest, -1) { // use regexp
|
||||
prevnode = node
|
||||
node.Value = str
|
||||
node.Next = &Node{}
|
||||
node = node.Next
|
||||
}
|
||||
|
||||
// XXX to get around regexp.Split *always* providing an empty string at the
|
||||
// end due to how our loop is constructed, nil out the last node in the
|
||||
// chain.
|
||||
prevnode.Next = nil
|
||||
|
||||
return rootnode, nil, nil
|
||||
}
|
||||
|
||||
// parseString just wraps the string in quotes and returns a working node.
|
||||
func parseString(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
if rest == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
n := &Node{}
|
||||
n.Value = rest
|
||||
return n, nil, nil
|
||||
}
|
||||
|
||||
// parseJSON converts JSON arrays to an AST.
|
||||
func parseJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
rest = strings.TrimLeftFunc(rest, unicode.IsSpace)
|
||||
if !strings.HasPrefix(rest, "[") {
|
||||
return nil, nil, fmt.Errorf(`Error parsing "%s" as a JSON array`, rest)
|
||||
}
|
||||
|
||||
var myJSON []interface{}
|
||||
if err := json.NewDecoder(strings.NewReader(rest)).Decode(&myJSON); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var top, prev *Node
|
||||
for _, str := range myJSON {
|
||||
s, ok := str.(string)
|
||||
if !ok {
|
||||
return nil, nil, errDockerfileNotStringArray
|
||||
}
|
||||
|
||||
node := &Node{Value: s}
|
||||
if prev == nil {
|
||||
top = node
|
||||
} else {
|
||||
prev.Next = node
|
||||
}
|
||||
prev = node
|
||||
}
|
||||
|
||||
return top, map[string]bool{"json": true}, nil
|
||||
}
|
||||
|
||||
// parseMaybeJSON determines if the argument appears to be a JSON array. If
|
||||
// so, passes to parseJSON; if not, quotes the result and returns a single
|
||||
// node.
|
||||
func parseMaybeJSON(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
if rest == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
node, attrs, err := parseJSON(rest, d)
|
||||
|
||||
if err == nil {
|
||||
return node, attrs, nil
|
||||
}
|
||||
if err == errDockerfileNotStringArray {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
node = &Node{}
|
||||
node.Value = rest
|
||||
return node, nil, nil
|
||||
}
|
||||
|
||||
// parseMaybeJSONToList determines if the argument appears to be a JSON array. If
|
||||
// so, passes to parseJSON; if not, attempts to parse it as a whitespace
|
||||
// delimited string.
|
||||
func parseMaybeJSONToList(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
node, attrs, err := parseJSON(rest, d)
|
||||
|
||||
if err == nil {
|
||||
return node, attrs, nil
|
||||
}
|
||||
if err == errDockerfileNotStringArray {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return parseStringsWhitespaceDelimited(rest, d)
|
||||
}
|
||||
|
||||
// The HEALTHCHECK command is like parseMaybeJSON, but has an extra type argument.
|
||||
func parseHealthConfig(rest string, d *Directive) (*Node, map[string]bool, error) {
|
||||
// Find end of first argument
|
||||
var sep int
|
||||
for ; sep < len(rest); sep++ {
|
||||
if unicode.IsSpace(rune(rest[sep])) {
|
||||
break
|
||||
}
|
||||
}
|
||||
next := sep
|
||||
for ; next < len(rest); next++ {
|
||||
if !unicode.IsSpace(rune(rest[next])) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sep == 0 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
typ := rest[:sep]
|
||||
cmd, attrs, err := parseMaybeJSON(rest[next:], d)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &Node{Value: typ, Next: cmd}, attrs, err
|
||||
}
|
332
vendor/github.com/moby/buildkit/frontend/dockerfile/parser/parser.go
generated
vendored
332
vendor/github.com/moby/buildkit/frontend/dockerfile/parser/parser.go
generated
vendored
@@ -1,332 +0,0 @@
|
||||
// Package parser implements a parser and parse tree dumper for Dockerfiles.
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/moby/buildkit/frontend/dockerfile/command"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Node is a structure used to represent a parse tree.
|
||||
//
|
||||
// In the node there are three fields, Value, Next, and Children. Value is the
|
||||
// current token's string value. Next is always the next non-child token, and
|
||||
// children contains all the children. Here's an example:
|
||||
//
|
||||
// (value next (child child-next child-next-next) next-next)
|
||||
//
|
||||
// This data structure is frankly pretty lousy for handling complex languages,
|
||||
// but lucky for us the Dockerfile isn't very complicated. This structure
|
||||
// works a little more effectively than a "proper" parse tree for our needs.
|
||||
//
|
||||
type Node struct {
|
||||
Value string // actual content
|
||||
Next *Node // the next item in the current sexp
|
||||
Children []*Node // the children of this sexp
|
||||
Attributes map[string]bool // special attributes for this node
|
||||
Original string // original line used before parsing
|
||||
Flags []string // only top Node should have this set
|
||||
StartLine int // the line in the original dockerfile where the node begins
|
||||
EndLine int // the line in the original dockerfile where the node ends
|
||||
}
|
||||
|
||||
// Dump dumps the AST defined by `node` as a list of sexps.
|
||||
// Returns a string suitable for printing.
|
||||
func (node *Node) Dump() string {
|
||||
str := ""
|
||||
str += node.Value
|
||||
|
||||
if len(node.Flags) > 0 {
|
||||
str += fmt.Sprintf(" %q", node.Flags)
|
||||
}
|
||||
|
||||
for _, n := range node.Children {
|
||||
str += "(" + n.Dump() + ")\n"
|
||||
}
|
||||
|
||||
for n := node.Next; n != nil; n = n.Next {
|
||||
if len(n.Children) > 0 {
|
||||
str += " " + n.Dump()
|
||||
} else {
|
||||
str += " " + strconv.Quote(n.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(str)
|
||||
}
|
||||
|
||||
func (node *Node) lines(start, end int) {
|
||||
node.StartLine = start
|
||||
node.EndLine = end
|
||||
}
|
||||
|
||||
// AddChild adds a new child node, and updates line information
|
||||
func (node *Node) AddChild(child *Node, startLine, endLine int) {
|
||||
child.lines(startLine, endLine)
|
||||
if node.StartLine < 0 {
|
||||
node.StartLine = startLine
|
||||
}
|
||||
node.EndLine = endLine
|
||||
node.Children = append(node.Children, child)
|
||||
}
|
||||
|
||||
var (
|
||||
dispatch map[string]func(string, *Directive) (*Node, map[string]bool, error)
|
||||
tokenWhitespace = regexp.MustCompile(`[\t\v\f\r ]+`)
|
||||
tokenEscapeCommand = regexp.MustCompile(`^#[ \t]*escape[ \t]*=[ \t]*(?P<escapechar>.).*$`)
|
||||
tokenComment = regexp.MustCompile(`^#.*$`)
|
||||
)
|
||||
|
||||
// DefaultEscapeToken is the default escape token
|
||||
const DefaultEscapeToken = '\\'
|
||||
|
||||
// Directive is the structure used during a build run to hold the state of
|
||||
// parsing directives.
|
||||
type Directive struct {
|
||||
escapeToken rune // Current escape token
|
||||
lineContinuationRegex *regexp.Regexp // Current line continuation regex
|
||||
processingComplete bool // Whether we are done looking for directives
|
||||
escapeSeen bool // Whether the escape directive has been seen
|
||||
}
|
||||
|
||||
// setEscapeToken sets the default token for escaping characters in a Dockerfile.
|
||||
func (d *Directive) setEscapeToken(s string) error {
|
||||
if s != "`" && s != "\\" {
|
||||
return fmt.Errorf("invalid ESCAPE '%s'. Must be ` or \\", s)
|
||||
}
|
||||
d.escapeToken = rune(s[0])
|
||||
d.lineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`)
|
||||
return nil
|
||||
}
|
||||
|
||||
// possibleParserDirective looks for parser directives, eg '# escapeToken=<char>'.
|
||||
// Parser directives must precede any builder instruction or other comments,
|
||||
// and cannot be repeated.
|
||||
func (d *Directive) possibleParserDirective(line string) error {
|
||||
if d.processingComplete {
|
||||
return nil
|
||||
}
|
||||
|
||||
tecMatch := tokenEscapeCommand.FindStringSubmatch(strings.ToLower(line))
|
||||
if len(tecMatch) != 0 {
|
||||
for i, n := range tokenEscapeCommand.SubexpNames() {
|
||||
if n == "escapechar" {
|
||||
if d.escapeSeen {
|
||||
return errors.New("only one escape parser directive can be used")
|
||||
}
|
||||
d.escapeSeen = true
|
||||
return d.setEscapeToken(tecMatch[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d.processingComplete = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDefaultDirective returns a new Directive with the default escapeToken token
|
||||
func NewDefaultDirective() *Directive {
|
||||
directive := Directive{}
|
||||
directive.setEscapeToken(string(DefaultEscapeToken))
|
||||
return &directive
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Dispatch Table. see line_parsers.go for the parse functions.
|
||||
// The command is parsed and mapped to the line parser. The line parser
|
||||
// receives the arguments but not the command, and returns an AST after
|
||||
// reformulating the arguments according to the rules in the parser
|
||||
// functions. Errors are propagated up by Parse() and the resulting AST can
|
||||
// be incorporated directly into the existing AST as a next.
|
||||
dispatch = map[string]func(string, *Directive) (*Node, map[string]bool, error){
|
||||
command.Add: parseMaybeJSONToList,
|
||||
command.Arg: parseNameOrNameVal,
|
||||
command.Cmd: parseMaybeJSON,
|
||||
command.Copy: parseMaybeJSONToList,
|
||||
command.Entrypoint: parseMaybeJSON,
|
||||
command.Env: parseEnv,
|
||||
command.Expose: parseStringsWhitespaceDelimited,
|
||||
command.From: parseStringsWhitespaceDelimited,
|
||||
command.Healthcheck: parseHealthConfig,
|
||||
command.Label: parseLabel,
|
||||
command.Maintainer: parseString,
|
||||
command.Onbuild: parseSubCommand,
|
||||
command.Run: parseMaybeJSON,
|
||||
command.Shell: parseMaybeJSON,
|
||||
command.StopSignal: parseString,
|
||||
command.User: parseString,
|
||||
command.Volume: parseMaybeJSONToList,
|
||||
command.Workdir: parseString,
|
||||
}
|
||||
}
|
||||
|
||||
// newNodeFromLine splits the line into parts, and dispatches to a function
|
||||
// based on the command and command arguments. A Node is created from the
|
||||
// result of the dispatch.
|
||||
func newNodeFromLine(line string, directive *Directive) (*Node, error) {
|
||||
cmd, flags, args, err := splitCommand(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fn := dispatch[cmd]
|
||||
// Ignore invalid Dockerfile instructions
|
||||
if fn == nil {
|
||||
fn = parseIgnore
|
||||
}
|
||||
next, attrs, err := fn(args, directive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Node{
|
||||
Value: cmd,
|
||||
Original: line,
|
||||
Flags: flags,
|
||||
Next: next,
|
||||
Attributes: attrs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Result is the result of parsing a Dockerfile
|
||||
type Result struct {
|
||||
AST *Node
|
||||
EscapeToken rune
|
||||
Warnings []string
|
||||
}
|
||||
|
||||
// PrintWarnings to the writer
|
||||
func (r *Result) PrintWarnings(out io.Writer) {
|
||||
if len(r.Warnings) == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(out, strings.Join(r.Warnings, "\n")+"\n")
|
||||
}
|
||||
|
||||
// Parse reads lines from a Reader, parses the lines into an AST and returns
|
||||
// the AST and escape token
|
||||
func Parse(rwc io.Reader) (*Result, error) {
|
||||
d := NewDefaultDirective()
|
||||
currentLine := 0
|
||||
root := &Node{StartLine: -1}
|
||||
scanner := bufio.NewScanner(rwc)
|
||||
warnings := []string{}
|
||||
|
||||
var err error
|
||||
for scanner.Scan() {
|
||||
bytesRead := scanner.Bytes()
|
||||
if currentLine == 0 {
|
||||
// First line, strip the byte-order-marker if present
|
||||
bytesRead = bytes.TrimPrefix(bytesRead, utf8bom)
|
||||
}
|
||||
bytesRead, err = processLine(d, bytesRead, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentLine++
|
||||
|
||||
startLine := currentLine
|
||||
line, isEndOfLine := trimContinuationCharacter(string(bytesRead), d)
|
||||
if isEndOfLine && line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var hasEmptyContinuationLine bool
|
||||
for !isEndOfLine && scanner.Scan() {
|
||||
bytesRead, err := processLine(d, scanner.Bytes(), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
currentLine++
|
||||
|
||||
if isComment(scanner.Bytes()) {
|
||||
// original line was a comment (processLine strips comments)
|
||||
continue
|
||||
}
|
||||
if isEmptyContinuationLine(bytesRead) {
|
||||
hasEmptyContinuationLine = true
|
||||
continue
|
||||
}
|
||||
|
||||
continuationLine := string(bytesRead)
|
||||
continuationLine, isEndOfLine = trimContinuationCharacter(continuationLine, d)
|
||||
line += continuationLine
|
||||
}
|
||||
|
||||
if hasEmptyContinuationLine {
|
||||
warnings = append(warnings, "[WARNING]: Empty continuation line found in:\n "+line)
|
||||
}
|
||||
|
||||
child, err := newNodeFromLine(line, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
root.AddChild(child, startLine, currentLine)
|
||||
}
|
||||
|
||||
if len(warnings) > 0 {
|
||||
warnings = append(warnings, "[WARNING]: Empty continuation lines will become errors in a future release.")
|
||||
}
|
||||
|
||||
if root.StartLine < 0 {
|
||||
return nil, errors.New("file with no instructions.")
|
||||
}
|
||||
|
||||
return &Result{
|
||||
AST: root,
|
||||
Warnings: warnings,
|
||||
EscapeToken: d.escapeToken,
|
||||
}, handleScannerError(scanner.Err())
|
||||
}
|
||||
|
||||
func trimComments(src []byte) []byte {
|
||||
return tokenComment.ReplaceAll(src, []byte{})
|
||||
}
|
||||
|
||||
func trimWhitespace(src []byte) []byte {
|
||||
return bytes.TrimLeftFunc(src, unicode.IsSpace)
|
||||
}
|
||||
|
||||
func isComment(line []byte) bool {
|
||||
return tokenComment.Match(trimWhitespace(line))
|
||||
}
|
||||
|
||||
func isEmptyContinuationLine(line []byte) bool {
|
||||
return len(trimWhitespace(line)) == 0
|
||||
}
|
||||
|
||||
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
|
||||
|
||||
func trimContinuationCharacter(line string, d *Directive) (string, bool) {
|
||||
if d.lineContinuationRegex.MatchString(line) {
|
||||
line = d.lineContinuationRegex.ReplaceAllString(line, "")
|
||||
return line, false
|
||||
}
|
||||
return line, true
|
||||
}
|
||||
|
||||
// TODO: remove stripLeftWhitespace after deprecation period. It seems silly
|
||||
// to preserve whitespace on continuation lines. Why is that done?
|
||||
func processLine(d *Directive, token []byte, stripLeftWhitespace bool) ([]byte, error) {
|
||||
if stripLeftWhitespace {
|
||||
token = trimWhitespace(token)
|
||||
}
|
||||
return trimComments(token), d.possibleParserDirective(string(token))
|
||||
}
|
||||
|
||||
func handleScannerError(err error) error {
|
||||
switch err {
|
||||
case bufio.ErrTooLong:
|
||||
return errors.Errorf("dockerfile line greater than max allowed size of %d", bufio.MaxScanTokenSize-1)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
118
vendor/github.com/moby/buildkit/frontend/dockerfile/parser/split_command.go
generated
vendored
118
vendor/github.com/moby/buildkit/frontend/dockerfile/parser/split_command.go
generated
vendored
@@ -1,118 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// splitCommand takes a single line of text and parses out the cmd and args,
|
||||
// which are used for dispatching to more exact parsing functions.
|
||||
func splitCommand(line string) (string, []string, string, error) {
|
||||
var args string
|
||||
var flags []string
|
||||
|
||||
// Make sure we get the same results irrespective of leading/trailing spaces
|
||||
cmdline := tokenWhitespace.Split(strings.TrimSpace(line), 2)
|
||||
cmd := strings.ToLower(cmdline[0])
|
||||
|
||||
if len(cmdline) == 2 {
|
||||
var err error
|
||||
args, flags, err = extractBuilderFlags(cmdline[1])
|
||||
if err != nil {
|
||||
return "", nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
return cmd, flags, strings.TrimSpace(args), nil
|
||||
}
|
||||
|
||||
func extractBuilderFlags(line string) (string, []string, error) {
|
||||
// Parses the BuilderFlags and returns the remaining part of the line
|
||||
|
||||
const (
|
||||
inSpaces = iota // looking for start of a word
|
||||
inWord
|
||||
inQuote
|
||||
)
|
||||
|
||||
words := []string{}
|
||||
phase := inSpaces
|
||||
word := ""
|
||||
quote := '\000'
|
||||
blankOK := false
|
||||
var ch rune
|
||||
|
||||
for pos := 0; pos <= len(line); pos++ {
|
||||
if pos != len(line) {
|
||||
ch = rune(line[pos])
|
||||
}
|
||||
|
||||
if phase == inSpaces { // Looking for start of word
|
||||
if pos == len(line) { // end of input
|
||||
break
|
||||
}
|
||||
if unicode.IsSpace(ch) { // skip spaces
|
||||
continue
|
||||
}
|
||||
|
||||
// Only keep going if the next word starts with --
|
||||
if ch != '-' || pos+1 == len(line) || rune(line[pos+1]) != '-' {
|
||||
return line[pos:], words, nil
|
||||
}
|
||||
|
||||
phase = inWord // found something with "--", fall through
|
||||
}
|
||||
if (phase == inWord || phase == inQuote) && (pos == len(line)) {
|
||||
if word != "--" && (blankOK || len(word) > 0) {
|
||||
words = append(words, word)
|
||||
}
|
||||
break
|
||||
}
|
||||
if phase == inWord {
|
||||
if unicode.IsSpace(ch) {
|
||||
phase = inSpaces
|
||||
if word == "--" {
|
||||
return line[pos:], words, nil
|
||||
}
|
||||
if blankOK || len(word) > 0 {
|
||||
words = append(words, word)
|
||||
}
|
||||
word = ""
|
||||
blankOK = false
|
||||
continue
|
||||
}
|
||||
if ch == '\'' || ch == '"' {
|
||||
quote = ch
|
||||
blankOK = true
|
||||
phase = inQuote
|
||||
continue
|
||||
}
|
||||
if ch == '\\' {
|
||||
if pos+1 == len(line) {
|
||||
continue // just skip \ at end
|
||||
}
|
||||
pos++
|
||||
ch = rune(line[pos])
|
||||
}
|
||||
word += string(ch)
|
||||
continue
|
||||
}
|
||||
if phase == inQuote {
|
||||
if ch == quote {
|
||||
phase = inWord
|
||||
continue
|
||||
}
|
||||
if ch == '\\' {
|
||||
if pos+1 == len(line) {
|
||||
phase = inWord
|
||||
continue // just skip \ at end
|
||||
}
|
||||
pos++
|
||||
ch = rune(line[pos])
|
||||
}
|
||||
word += string(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return "", words, nil
|
||||
}
|
238
vendor/github.com/moby/buildkit/frontend/dockerfile/shell/envVarTest
generated
vendored
238
vendor/github.com/moby/buildkit/frontend/dockerfile/shell/envVarTest
generated
vendored
@@ -1,238 +0,0 @@
|
||||
A|hello | hello
|
||||
A|he'll'o | hello
|
||||
A|he'llo | error
|
||||
A|he\'llo | he'llo
|
||||
A|he\\'llo | error
|
||||
A|abc\tdef | abctdef
|
||||
A|"abc\tdef" | abc\tdef
|
||||
A|"abc\\tdef" | abc\tdef
|
||||
A|'abc\tdef' | abc\tdef
|
||||
A|hello\ | hello
|
||||
A|hello\\ | hello\
|
||||
A|"hello | error
|
||||
A|"hello\" | error
|
||||
A|"hel'lo" | hel'lo
|
||||
A|'hello | error
|
||||
A|'hello\' | hello\
|
||||
A|'hello\there' | hello\there
|
||||
A|'hello\\there' | hello\\there
|
||||
A|"''" | ''
|
||||
A|$. | $.
|
||||
A|he$1x | hex
|
||||
A|he$.x | he$.x
|
||||
# Next one is different on Windows as $pwd==$PWD
|
||||
U|he$pwd. | he.
|
||||
W|he$pwd. | he/home.
|
||||
A|he$PWD | he/home
|
||||
A|he\$PWD | he$PWD
|
||||
A|he\\$PWD | he\/home
|
||||
A|"he\$PWD" | he$PWD
|
||||
A|"he\\$PWD" | he\/home
|
||||
A|\${} | ${}
|
||||
A|\${}aaa | ${}aaa
|
||||
A|he\${} | he${}
|
||||
A|he\${}xx | he${}xx
|
||||
A|${} | error
|
||||
A|${}aaa | error
|
||||
A|he${} | error
|
||||
A|he${}xx | error
|
||||
A|he${hi} | he
|
||||
A|he${hi}xx | hexx
|
||||
A|he${PWD} | he/home
|
||||
A|he${.} | error
|
||||
A|he${XXX:-000}xx | he000xx
|
||||
A|he${PWD:-000}xx | he/homexx
|
||||
A|he${XXX:-$PWD}xx | he/homexx
|
||||
A|he${XXX:-${PWD:-yyy}}xx | he/homexx
|
||||
A|he${XXX:-${YYY:-yyy}}xx | heyyyxx
|
||||
A|he${XXX:YYY} | error
|
||||
A|he${XXX?} | error
|
||||
A|he${XXX:?} | error
|
||||
A|he${PWD?} | he/home
|
||||
A|he${PWD:?} | he/home
|
||||
A|he${NULL?} | he
|
||||
A|he${NULL:?} | error
|
||||
A|he${XXX:+${PWD}}xx | hexx
|
||||
A|he${PWD:+${XXX}}xx | hexx
|
||||
A|he${PWD:+${SHELL}}xx | hebashxx
|
||||
A|he${XXX:+000}xx | hexx
|
||||
A|he${PWD:+000}xx | he000xx
|
||||
A|'he${XX}' | he${XX}
|
||||
A|"he${PWD}" | he/home
|
||||
A|"he'$PWD'" | he'/home'
|
||||
A|"$PWD" | /home
|
||||
A|'$PWD' | $PWD
|
||||
A|'\$PWD' | \$PWD
|
||||
A|'"hello"' | "hello"
|
||||
A|he\$PWD | he$PWD
|
||||
A|"he\$PWD" | he$PWD
|
||||
A|'he\$PWD' | he\$PWD
|
||||
A|he${PWD | error
|
||||
A|he${PWD:=000}xx | error
|
||||
A|he${PWD:+${PWD}:}xx | he/home:xx
|
||||
A|he${XXX:-\$PWD:}xx | he$PWD:xx
|
||||
A|he${XXX:-\${PWD}z}xx | he${PWDz}xx
|
||||
A|안녕하세요 | 안녕하세요
|
||||
A|안'녕'하세요 | 안녕하세요
|
||||
A|안'녕하세요 | error
|
||||
A|안녕\'하세요 | 안녕'하세요
|
||||
A|안\\'녕하세요 | error
|
||||
A|안녕\t하세요 | 안녕t하세요
|
||||
A|"안녕\t하세요" | 안녕\t하세요
|
||||
A|'안녕\t하세요 | error
|
||||
A|안녕하세요\ | 안녕하세요
|
||||
A|안녕하세요\\ | 안녕하세요\
|
||||
A|"안녕하세요 | error
|
||||
A|"안녕하세요\" | error
|
||||
A|"안녕'하세요" | 안녕'하세요
|
||||
A|'안녕하세요 | error
|
||||
A|'안녕하세요\' | 안녕하세요\
|
||||
A|안녕$1x | 안녕x
|
||||
A|안녕$.x | 안녕$.x
|
||||
# Next one is different on Windows as $pwd==$PWD
|
||||
U|안녕$pwd. | 안녕.
|
||||
W|안녕$pwd. | 안녕/home.
|
||||
A|안녕$PWD | 안녕/home
|
||||
A|안녕\$PWD | 안녕$PWD
|
||||
A|안녕\\$PWD | 안녕\/home
|
||||
A|안녕\${} | 안녕${}
|
||||
A|안녕\${}xx | 안녕${}xx
|
||||
A|안녕${} | error
|
||||
A|안녕${}xx | error
|
||||
A|안녕${hi} | 안녕
|
||||
A|안녕${hi}xx | 안녕xx
|
||||
A|안녕${PWD} | 안녕/home
|
||||
A|안녕${.} | error
|
||||
A|안녕${XXX:-000}xx | 안녕000xx
|
||||
A|안녕${PWD:-000}xx | 안녕/homexx
|
||||
A|안녕${XXX:-$PWD}xx | 안녕/homexx
|
||||
A|안녕${XXX:-${PWD:-yyy}}xx | 안녕/homexx
|
||||
A|안녕${XXX:-${YYY:-yyy}}xx | 안녕yyyxx
|
||||
A|안녕${XXX:YYY} | error
|
||||
A|안녕${XXX:+${PWD}}xx | 안녕xx
|
||||
A|안녕${PWD:+${XXX}}xx | 안녕xx
|
||||
A|안녕${PWD:+${SHELL}}xx | 안녕bashxx
|
||||
A|안녕${XXX:+000}xx | 안녕xx
|
||||
A|안녕${PWD:+000}xx | 안녕000xx
|
||||
A|'안녕${XX}' | 안녕${XX}
|
||||
A|"안녕${PWD}" | 안녕/home
|
||||
A|"안녕'$PWD'" | 안녕'/home'
|
||||
A|'"안녕"' | "안녕"
|
||||
A|안녕\$PWD | 안녕$PWD
|
||||
A|"안녕\$PWD" | 안녕$PWD
|
||||
A|'안녕\$PWD' | 안녕\$PWD
|
||||
A|안녕${PWD | error
|
||||
A|안녕${PWD:=000}xx | error
|
||||
A|안녕${PWD:+${PWD}:}xx | 안녕/home:xx
|
||||
A|안녕${XXX:-\$PWD:}xx | 안녕$PWD:xx
|
||||
A|안녕${XXX:-\${PWD}z}xx | 안녕${PWDz}xx
|
||||
A|$KOREAN | 한국어
|
||||
A|안녕$KOREAN | 안녕한국어
|
||||
A|${{aaa} | error
|
||||
A|${aaa}} | }
|
||||
A|${aaa | error
|
||||
A|${{aaa:-bbb} | error
|
||||
A|${aaa:-bbb}} | bbb}
|
||||
A|${aaa:-bbb | error
|
||||
A|${aaa:-bbb} | bbb
|
||||
A|${aaa:-${bbb:-ccc}} | ccc
|
||||
A|${aaa:-bbb ${foo} | error
|
||||
A|${aaa:-bbb {foo} | bbb {foo
|
||||
A|${:} | error
|
||||
A|${:-bbb} | error
|
||||
A|${:+bbb} | error
|
||||
|
||||
# Positional parameters won't be set:
|
||||
# http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_05_01
|
||||
A|$1 |
|
||||
A|${1} |
|
||||
A|${1:+bbb} |
|
||||
A|${1:-bbb} | bbb
|
||||
A|$2 |
|
||||
A|${2} |
|
||||
A|${2:+bbb} |
|
||||
A|${2:-bbb} | bbb
|
||||
A|$3 |
|
||||
A|${3} |
|
||||
A|${3:+bbb} |
|
||||
A|${3:-bbb} | bbb
|
||||
A|$4 |
|
||||
A|${4} |
|
||||
A|${4:+bbb} |
|
||||
A|${4:-bbb} | bbb
|
||||
A|$5 |
|
||||
A|${5} |
|
||||
A|${5:+bbb} |
|
||||
A|${5:-bbb} | bbb
|
||||
A|$6 |
|
||||
A|${6} |
|
||||
A|${6:+bbb} |
|
||||
A|${6:-bbb} | bbb
|
||||
A|$7 |
|
||||
A|${7} |
|
||||
A|${7:+bbb} |
|
||||
A|${7:-bbb} | bbb
|
||||
A|$8 |
|
||||
A|${8} |
|
||||
A|${8:+bbb} |
|
||||
A|${8:-bbb} | bbb
|
||||
A|$9 |
|
||||
A|${9} |
|
||||
A|${9:+bbb} |
|
||||
A|${9:-bbb} | bbb
|
||||
A|$999 |
|
||||
A|${999} |
|
||||
A|${999:+bbb} |
|
||||
A|${999:-bbb} | bbb
|
||||
A|$999aaa | aaa
|
||||
A|${999}aaa | aaa
|
||||
A|${999:+bbb}aaa | aaa
|
||||
A|${999:-bbb}aaa | bbbaaa
|
||||
A|$001 |
|
||||
A|${001} |
|
||||
A|${001:+bbb} |
|
||||
A|${001:-bbb} | bbb
|
||||
A|$001aaa | aaa
|
||||
A|${001}aaa | aaa
|
||||
A|${001:+bbb}aaa | aaa
|
||||
A|${001:-bbb}aaa | bbbaaa
|
||||
|
||||
# Special parameters won't be set in the Dockerfile:
|
||||
# http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_05_02
|
||||
A|$@ |
|
||||
A|${@} |
|
||||
A|${@:+bbb} |
|
||||
A|${@:-bbb} | bbb
|
||||
A|$@@@ | @@
|
||||
A|$@aaa | aaa
|
||||
A|${@}aaa | aaa
|
||||
A|${@:+bbb}aaa | aaa
|
||||
A|${@:-bbb}aaa | bbbaaa
|
||||
A|$* |
|
||||
A|${*} |
|
||||
A|${*:+bbb} |
|
||||
A|${*:-bbb} | bbb
|
||||
A|$# |
|
||||
A|${#} |
|
||||
A|${#:+bbb} |
|
||||
A|${#:-bbb} | bbb
|
||||
A|$? |
|
||||
A|${?} |
|
||||
A|${?:+bbb} |
|
||||
A|${?:-bbb} | bbb
|
||||
A|$- |
|
||||
A|${-} |
|
||||
A|${-:+bbb} |
|
||||
A|${-:-bbb} | bbb
|
||||
A|$$ |
|
||||
A|${$} |
|
||||
A|${$:+bbb} |
|
||||
A|${$:-bbb} | bbb
|
||||
A|$! |
|
||||
A|${!} |
|
||||
A|${!:+bbb} |
|
||||
A|${!:-bbb} | bbb
|
||||
A|$0 |
|
||||
A|${0} |
|
||||
A|${0:+bbb} |
|
||||
A|${0:-bbb} | bbb
|
10
vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_unix.go
generated
vendored
10
vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_unix.go
generated
vendored
@@ -1,10 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package shell
|
||||
|
||||
// EqualEnvKeys compare two strings and returns true if they are equal.
|
||||
// On Unix this comparison is case sensitive.
|
||||
// On Windows this comparison is case insensitive.
|
||||
func EqualEnvKeys(from, to string) bool {
|
||||
return from == to
|
||||
}
|
10
vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_windows.go
generated
vendored
10
vendor/github.com/moby/buildkit/frontend/dockerfile/shell/equal_env_windows.go
generated
vendored
@@ -1,10 +0,0 @@
|
||||
package shell
|
||||
|
||||
import "strings"
|
||||
|
||||
// EqualEnvKeys compare two strings and returns true if they are equal.
|
||||
// On Unix this comparison is case sensitive.
|
||||
// On Windows this comparison is case insensitive.
|
||||
func EqualEnvKeys(from, to string) bool {
|
||||
return strings.ToUpper(from) == strings.ToUpper(to)
|
||||
}
|
466
vendor/github.com/moby/buildkit/frontend/dockerfile/shell/lex.go
generated
vendored
466
vendor/github.com/moby/buildkit/frontend/dockerfile/shell/lex.go
generated
vendored
@@ -1,466 +0,0 @@
|
||||
package shell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
"unicode"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Lex performs shell word splitting and variable expansion.
|
||||
//
|
||||
// Lex takes a string and an array of env variables and
|
||||
// process all quotes (" and ') as well as $xxx and ${xxx} env variable
|
||||
// tokens. Tries to mimic bash shell process.
|
||||
// It doesn't support all flavors of ${xx:...} formats but new ones can
|
||||
// be added by adding code to the "special ${} format processing" section
|
||||
type Lex struct {
|
||||
escapeToken rune
|
||||
RawQuotes bool
|
||||
SkipUnsetEnv bool
|
||||
}
|
||||
|
||||
// NewLex creates a new Lex which uses escapeToken to escape quotes.
|
||||
func NewLex(escapeToken rune) *Lex {
|
||||
return &Lex{escapeToken: escapeToken}
|
||||
}
|
||||
|
||||
// ProcessWord will use the 'env' list of environment variables,
|
||||
// and replace any env var references in 'word'.
|
||||
func (s *Lex) ProcessWord(word string, env []string) (string, error) {
|
||||
word, _, err := s.process(word, BuildEnvs(env))
|
||||
return word, err
|
||||
}
|
||||
|
||||
// ProcessWords will use the 'env' list of environment variables,
|
||||
// and replace any env var references in 'word' then it will also
|
||||
// return a slice of strings which represents the 'word'
|
||||
// split up based on spaces - taking into account quotes. Note that
|
||||
// this splitting is done **after** the env var substitutions are done.
|
||||
// Note, each one is trimmed to remove leading and trailing spaces (unless
|
||||
// they are quoted", but ProcessWord retains spaces between words.
|
||||
func (s *Lex) ProcessWords(word string, env []string) ([]string, error) {
|
||||
_, words, err := s.process(word, BuildEnvs(env))
|
||||
return words, err
|
||||
}
|
||||
|
||||
// ProcessWordWithMap will use the 'env' list of environment variables,
|
||||
// and replace any env var references in 'word'.
|
||||
func (s *Lex) ProcessWordWithMap(word string, env map[string]string) (string, error) {
|
||||
word, _, err := s.process(word, env)
|
||||
return word, err
|
||||
}
|
||||
|
||||
func (s *Lex) ProcessWordsWithMap(word string, env map[string]string) ([]string, error) {
|
||||
_, words, err := s.process(word, env)
|
||||
return words, err
|
||||
}
|
||||
|
||||
func (s *Lex) process(word string, env map[string]string) (string, []string, error) {
|
||||
sw := &shellWord{
|
||||
envs: env,
|
||||
escapeToken: s.escapeToken,
|
||||
skipUnsetEnv: s.SkipUnsetEnv,
|
||||
rawQuotes: s.RawQuotes,
|
||||
}
|
||||
sw.scanner.Init(strings.NewReader(word))
|
||||
return sw.process(word)
|
||||
}
|
||||
|
||||
type shellWord struct {
|
||||
scanner scanner.Scanner
|
||||
envs map[string]string
|
||||
escapeToken rune
|
||||
rawQuotes bool
|
||||
skipUnsetEnv bool
|
||||
}
|
||||
|
||||
func (sw *shellWord) process(source string) (string, []string, error) {
|
||||
word, words, err := sw.processStopOn(scanner.EOF)
|
||||
if err != nil {
|
||||
err = errors.Wrapf(err, "failed to process %q", source)
|
||||
}
|
||||
return word, words, err
|
||||
}
|
||||
|
||||
type wordsStruct struct {
|
||||
word string
|
||||
words []string
|
||||
inWord bool
|
||||
}
|
||||
|
||||
func (w *wordsStruct) addChar(ch rune) {
|
||||
if unicode.IsSpace(ch) && w.inWord {
|
||||
if len(w.word) != 0 {
|
||||
w.words = append(w.words, w.word)
|
||||
w.word = ""
|
||||
w.inWord = false
|
||||
}
|
||||
} else if !unicode.IsSpace(ch) {
|
||||
w.addRawChar(ch)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wordsStruct) addRawChar(ch rune) {
|
||||
w.word += string(ch)
|
||||
w.inWord = true
|
||||
}
|
||||
|
||||
func (w *wordsStruct) addString(str string) {
|
||||
for _, ch := range str {
|
||||
w.addChar(ch)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wordsStruct) addRawString(str string) {
|
||||
w.word += str
|
||||
w.inWord = true
|
||||
}
|
||||
|
||||
func (w *wordsStruct) getWords() []string {
|
||||
if len(w.word) > 0 {
|
||||
w.words = append(w.words, w.word)
|
||||
|
||||
// Just in case we're called again by mistake
|
||||
w.word = ""
|
||||
w.inWord = false
|
||||
}
|
||||
return w.words
|
||||
}
|
||||
|
||||
// Process the word, starting at 'pos', and stop when we get to the
|
||||
// end of the word or the 'stopChar' character
|
||||
func (sw *shellWord) processStopOn(stopChar rune) (string, []string, error) {
|
||||
var result bytes.Buffer
|
||||
var words wordsStruct
|
||||
|
||||
var charFuncMapping = map[rune]func() (string, error){
|
||||
'\'': sw.processSingleQuote,
|
||||
'"': sw.processDoubleQuote,
|
||||
'$': sw.processDollar,
|
||||
}
|
||||
|
||||
for sw.scanner.Peek() != scanner.EOF {
|
||||
ch := sw.scanner.Peek()
|
||||
|
||||
if stopChar != scanner.EOF && ch == stopChar {
|
||||
sw.scanner.Next()
|
||||
return result.String(), words.getWords(), nil
|
||||
}
|
||||
if fn, ok := charFuncMapping[ch]; ok {
|
||||
// Call special processing func for certain chars
|
||||
tmp, err := fn()
|
||||
if err != nil {
|
||||
return "", []string{}, err
|
||||
}
|
||||
result.WriteString(tmp)
|
||||
|
||||
if ch == rune('$') {
|
||||
words.addString(tmp)
|
||||
} else {
|
||||
words.addRawString(tmp)
|
||||
}
|
||||
} else {
|
||||
// Not special, just add it to the result
|
||||
ch = sw.scanner.Next()
|
||||
|
||||
if ch == sw.escapeToken {
|
||||
// '\' (default escape token, but ` allowed) escapes, except end of line
|
||||
ch = sw.scanner.Next()
|
||||
|
||||
if ch == scanner.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
words.addRawChar(ch)
|
||||
} else {
|
||||
words.addChar(ch)
|
||||
}
|
||||
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
if stopChar != scanner.EOF {
|
||||
return "", []string{}, errors.Errorf("unexpected end of statement while looking for matching %s", string(stopChar))
|
||||
}
|
||||
return result.String(), words.getWords(), nil
|
||||
}
|
||||
|
||||
func (sw *shellWord) processSingleQuote() (string, error) {
|
||||
// All chars between single quotes are taken as-is
|
||||
// Note, you can't escape '
|
||||
//
|
||||
// From the "sh" man page:
|
||||
// Single Quotes
|
||||
// Enclosing characters in single quotes preserves the literal meaning of
|
||||
// all the characters (except single quotes, making it impossible to put
|
||||
// single-quotes in a single-quoted string).
|
||||
|
||||
var result bytes.Buffer
|
||||
|
||||
ch := sw.scanner.Next()
|
||||
if sw.rawQuotes {
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
|
||||
for {
|
||||
ch = sw.scanner.Next()
|
||||
switch ch {
|
||||
case scanner.EOF:
|
||||
return "", errors.New("unexpected end of statement while looking for matching single-quote")
|
||||
case '\'':
|
||||
if sw.rawQuotes {
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
return result.String(), nil
|
||||
}
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
|
||||
func (sw *shellWord) processDoubleQuote() (string, error) {
|
||||
// All chars up to the next " are taken as-is, even ', except any $ chars
|
||||
// But you can escape " with a \ (or ` if escape token set accordingly)
|
||||
//
|
||||
// From the "sh" man page:
|
||||
// Double Quotes
|
||||
// Enclosing characters within double quotes preserves the literal meaning
|
||||
// of all characters except dollarsign ($), backquote (`), and backslash
|
||||
// (\). The backslash inside double quotes is historically weird, and
|
||||
// serves to quote only the following characters:
|
||||
// $ ` " \ <newline>.
|
||||
// Otherwise it remains literal.
|
||||
|
||||
var result bytes.Buffer
|
||||
|
||||
ch := sw.scanner.Next()
|
||||
if sw.rawQuotes {
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
|
||||
for {
|
||||
switch sw.scanner.Peek() {
|
||||
case scanner.EOF:
|
||||
return "", errors.New("unexpected end of statement while looking for matching double-quote")
|
||||
case '"':
|
||||
ch := sw.scanner.Next()
|
||||
if sw.rawQuotes {
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
return result.String(), nil
|
||||
case '$':
|
||||
value, err := sw.processDollar()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result.WriteString(value)
|
||||
default:
|
||||
ch := sw.scanner.Next()
|
||||
if ch == sw.escapeToken {
|
||||
switch sw.scanner.Peek() {
|
||||
case scanner.EOF:
|
||||
// Ignore \ at end of word
|
||||
continue
|
||||
case '"', '$', sw.escapeToken:
|
||||
// These chars can be escaped, all other \'s are left as-is
|
||||
// Note: for now don't do anything special with ` chars.
|
||||
// Not sure what to do with them anyway since we're not going
|
||||
// to execute the text in there (not now anyway).
|
||||
ch = sw.scanner.Next()
|
||||
}
|
||||
}
|
||||
result.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sw *shellWord) processDollar() (string, error) {
|
||||
sw.scanner.Next()
|
||||
|
||||
// $xxx case
|
||||
if sw.scanner.Peek() != '{' {
|
||||
name := sw.processName()
|
||||
if name == "" {
|
||||
return "$", nil
|
||||
}
|
||||
value, found := sw.getEnv(name)
|
||||
if !found && sw.skipUnsetEnv {
|
||||
return "$" + name, nil
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
sw.scanner.Next()
|
||||
switch sw.scanner.Peek() {
|
||||
case scanner.EOF:
|
||||
return "", errors.New("syntax error: missing '}'")
|
||||
case '{', '}', ':':
|
||||
// Invalid ${{xx}, ${:xx}, ${:}. ${} case
|
||||
return "", errors.New("syntax error: bad substitution")
|
||||
}
|
||||
name := sw.processName()
|
||||
ch := sw.scanner.Next()
|
||||
switch ch {
|
||||
case '}':
|
||||
// Normal ${xx} case
|
||||
value, found := sw.getEnv(name)
|
||||
if !found && sw.skipUnsetEnv {
|
||||
return fmt.Sprintf("${%s}", name), nil
|
||||
}
|
||||
return value, nil
|
||||
case '?':
|
||||
word, _, err := sw.processStopOn('}')
|
||||
if err != nil {
|
||||
if sw.scanner.Peek() == scanner.EOF {
|
||||
return "", errors.New("syntax error: missing '}'")
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
newValue, found := sw.getEnv(name)
|
||||
if !found {
|
||||
if sw.skipUnsetEnv {
|
||||
return fmt.Sprintf("${%s?%s}", name, word), nil
|
||||
}
|
||||
message := "is not allowed to be unset"
|
||||
if word != "" {
|
||||
message = word
|
||||
}
|
||||
return "", errors.Errorf("%s: %s", name, message)
|
||||
}
|
||||
return newValue, nil
|
||||
case ':':
|
||||
// Special ${xx:...} format processing
|
||||
// Yes it allows for recursive $'s in the ... spot
|
||||
modifier := sw.scanner.Next()
|
||||
|
||||
word, _, err := sw.processStopOn('}')
|
||||
if err != nil {
|
||||
if sw.scanner.Peek() == scanner.EOF {
|
||||
return "", errors.New("syntax error: missing '}'")
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Grab the current value of the variable in question so we
|
||||
// can use to to determine what to do based on the modifier
|
||||
newValue, found := sw.getEnv(name)
|
||||
|
||||
switch modifier {
|
||||
case '+':
|
||||
if newValue != "" {
|
||||
newValue = word
|
||||
}
|
||||
if !found && sw.skipUnsetEnv {
|
||||
return fmt.Sprintf("${%s:%s%s}", name, string(modifier), word), nil
|
||||
}
|
||||
return newValue, nil
|
||||
|
||||
case '-':
|
||||
if newValue == "" {
|
||||
newValue = word
|
||||
}
|
||||
if !found && sw.skipUnsetEnv {
|
||||
return fmt.Sprintf("${%s:%s%s}", name, string(modifier), word), nil
|
||||
}
|
||||
|
||||
return newValue, nil
|
||||
|
||||
case '?':
|
||||
if !found {
|
||||
if sw.skipUnsetEnv {
|
||||
return fmt.Sprintf("${%s:%s%s}", name, string(modifier), word), nil
|
||||
}
|
||||
message := "is not allowed to be unset"
|
||||
if word != "" {
|
||||
message = word
|
||||
}
|
||||
return "", errors.Errorf("%s: %s", name, message)
|
||||
}
|
||||
if newValue == "" {
|
||||
message := "is not allowed to be empty"
|
||||
if word != "" {
|
||||
message = word
|
||||
}
|
||||
return "", errors.Errorf("%s: %s", name, message)
|
||||
}
|
||||
return newValue, nil
|
||||
|
||||
default:
|
||||
return "", errors.Errorf("unsupported modifier (%c) in substitution", modifier)
|
||||
}
|
||||
}
|
||||
return "", errors.Errorf("missing ':' in substitution")
|
||||
}
|
||||
|
||||
func (sw *shellWord) processName() string {
|
||||
// Read in a name (alphanumeric or _)
|
||||
// If it starts with a numeric then just return $#
|
||||
var name bytes.Buffer
|
||||
|
||||
for sw.scanner.Peek() != scanner.EOF {
|
||||
ch := sw.scanner.Peek()
|
||||
if name.Len() == 0 && unicode.IsDigit(ch) {
|
||||
for sw.scanner.Peek() != scanner.EOF && unicode.IsDigit(sw.scanner.Peek()) {
|
||||
// Keep reading until the first non-digit character, or EOF
|
||||
ch = sw.scanner.Next()
|
||||
name.WriteRune(ch)
|
||||
}
|
||||
return name.String()
|
||||
}
|
||||
if name.Len() == 0 && isSpecialParam(ch) {
|
||||
ch = sw.scanner.Next()
|
||||
return string(ch)
|
||||
}
|
||||
if !unicode.IsLetter(ch) && !unicode.IsDigit(ch) && ch != '_' {
|
||||
break
|
||||
}
|
||||
ch = sw.scanner.Next()
|
||||
name.WriteRune(ch)
|
||||
}
|
||||
|
||||
return name.String()
|
||||
}
|
||||
|
||||
// isSpecialParam checks if the provided character is a special parameters,
|
||||
// as defined in http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_05_02
|
||||
func isSpecialParam(char rune) bool {
|
||||
switch char {
|
||||
case '@', '*', '#', '?', '-', '$', '!', '0':
|
||||
// Special parameters
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_05_02
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (sw *shellWord) getEnv(name string) (string, bool) {
|
||||
for key, value := range sw.envs {
|
||||
if EqualEnvKeys(name, key) {
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func BuildEnvs(env []string) map[string]string {
|
||||
envs := map[string]string{}
|
||||
|
||||
for _, e := range env {
|
||||
i := strings.Index(e, "=")
|
||||
|
||||
if i < 0 {
|
||||
envs[e] = ""
|
||||
} else {
|
||||
k := e[:i]
|
||||
v := e[i+1:]
|
||||
|
||||
// overwrite value if key already exists
|
||||
envs[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return envs
|
||||
}
|
30
vendor/github.com/moby/buildkit/frontend/dockerfile/shell/wordsTest
generated
vendored
30
vendor/github.com/moby/buildkit/frontend/dockerfile/shell/wordsTest
generated
vendored
@@ -1,30 +0,0 @@
|
||||
hello | hello
|
||||
hello${hi}bye | hellobye
|
||||
ENV hi=hi
|
||||
hello${hi}bye | hellohibye
|
||||
ENV space=abc def
|
||||
hello${space}bye | helloabc,defbye
|
||||
hello"${space}"bye | helloabc defbye
|
||||
hello "${space}"bye | hello,abc defbye
|
||||
ENV leading= ab c
|
||||
hello${leading}def | hello,ab,cdef
|
||||
hello"${leading}" def | hello ab c,def
|
||||
hello"${leading}" | hello ab c
|
||||
hello${leading} | hello,ab,c
|
||||
# next line MUST have 3 trailing spaces, don't erase them!
|
||||
ENV trailing=ab c
|
||||
hello${trailing} | helloab,c
|
||||
hello${trailing}d | helloab,c,d
|
||||
hello"${trailing}"d | helloab c d
|
||||
# next line MUST have 3 trailing spaces, don't erase them!
|
||||
hel"lo${trailing}" | helloab c
|
||||
hello" there " | hello there
|
||||
hello there | hello,there
|
||||
hello\ there | hello there
|
||||
hello" there | error
|
||||
hello\" there | hello",there
|
||||
hello"\\there" | hello\there
|
||||
hello"\there" | hello\there
|
||||
hello'\\there' | hello\\there
|
||||
hello'\there' | hello\there
|
||||
hello'$there' | hello$there
|
217
vendor/github.com/moby/buildkit/frontend/gateway/forwarder/forward.go
generated
vendored
217
vendor/github.com/moby/buildkit/frontend/gateway/forwarder/forward.go
generated
vendored
@@ -1,217 +0,0 @@
|
||||
package forwarder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/moby/buildkit/cache"
|
||||
cacheutil "github.com/moby/buildkit/cache/util"
|
||||
clienttypes "github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend"
|
||||
"github.com/moby/buildkit/frontend/gateway/client"
|
||||
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/solver"
|
||||
opspb "github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/apicaps"
|
||||
"github.com/moby/buildkit/worker"
|
||||
"github.com/pkg/errors"
|
||||
fstypes "github.com/tonistiigi/fsutil/types"
|
||||
)
|
||||
|
||||
func llbBridgeToGatewayClient(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string, inputs map[string]*opspb.Definition, workerInfos []clienttypes.WorkerInfo) (*bridgeClient, error) {
|
||||
return &bridgeClient{
|
||||
opts: opts,
|
||||
inputs: inputs,
|
||||
FrontendLLBBridge: llbBridge,
|
||||
sid: session.FromContext(ctx),
|
||||
workerInfos: workerInfos,
|
||||
final: map[*ref]struct{}{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type bridgeClient struct {
|
||||
frontend.FrontendLLBBridge
|
||||
mu sync.Mutex
|
||||
opts map[string]string
|
||||
inputs map[string]*opspb.Definition
|
||||
final map[*ref]struct{}
|
||||
sid string
|
||||
exporterAttr map[string][]byte
|
||||
refs []*ref
|
||||
workerInfos []clienttypes.WorkerInfo
|
||||
}
|
||||
|
||||
func (c *bridgeClient) Solve(ctx context.Context, req client.SolveRequest) (*client.Result, error) {
|
||||
res, err := c.FrontendLLBBridge.Solve(ctx, frontend.SolveRequest{
|
||||
Definition: req.Definition,
|
||||
Frontend: req.Frontend,
|
||||
FrontendOpt: req.FrontendOpt,
|
||||
FrontendInputs: req.FrontendInputs,
|
||||
CacheImports: req.CacheImports,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cRes := &client.Result{}
|
||||
c.mu.Lock()
|
||||
for k, r := range res.Refs {
|
||||
rr, err := newRef(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.refs = append(c.refs, rr)
|
||||
cRes.AddRef(k, rr)
|
||||
}
|
||||
if r := res.Ref; r != nil {
|
||||
rr, err := newRef(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.refs = append(c.refs, rr)
|
||||
cRes.SetRef(rr)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
cRes.Metadata = res.Metadata
|
||||
|
||||
return cRes, nil
|
||||
}
|
||||
func (c *bridgeClient) BuildOpts() client.BuildOpts {
|
||||
workers := make([]client.WorkerInfo, 0, len(c.workerInfos))
|
||||
for _, w := range c.workerInfos {
|
||||
workers = append(workers, client.WorkerInfo{
|
||||
ID: w.ID,
|
||||
Labels: w.Labels,
|
||||
Platforms: w.Platforms,
|
||||
})
|
||||
}
|
||||
|
||||
return client.BuildOpts{
|
||||
Opts: c.opts,
|
||||
SessionID: c.sid,
|
||||
Workers: workers,
|
||||
Product: apicaps.ExportedProduct,
|
||||
Caps: gwpb.Caps.CapSet(gwpb.Caps.All()),
|
||||
LLBCaps: opspb.Caps.CapSet(opspb.Caps.All()),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *bridgeClient) Inputs(ctx context.Context) (map[string]llb.State, error) {
|
||||
inputs := make(map[string]llb.State)
|
||||
for key, def := range c.inputs {
|
||||
defop, err := llb.NewDefinitionOp(def)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inputs[key] = llb.NewState(defop)
|
||||
}
|
||||
return inputs, nil
|
||||
}
|
||||
|
||||
func (c *bridgeClient) toFrontendResult(r *client.Result) (*frontend.Result, error) {
|
||||
if r == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res := &frontend.Result{}
|
||||
|
||||
if r.Refs != nil {
|
||||
res.Refs = make(map[string]solver.ResultProxy, len(r.Refs))
|
||||
for k, r := range r.Refs {
|
||||
rr, ok := r.(*ref)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid reference type for forward %T", r)
|
||||
}
|
||||
c.final[rr] = struct{}{}
|
||||
res.Refs[k] = rr.ResultProxy
|
||||
}
|
||||
}
|
||||
if r := r.Ref; r != nil {
|
||||
rr, ok := r.(*ref)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid reference type for forward %T", r)
|
||||
}
|
||||
c.final[rr] = struct{}{}
|
||||
res.Ref = rr.ResultProxy
|
||||
}
|
||||
res.Metadata = r.Metadata
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *bridgeClient) discard(err error) {
|
||||
for _, r := range c.refs {
|
||||
if r != nil {
|
||||
if _, ok := c.final[r]; !ok || err != nil {
|
||||
r.Release(context.TODO())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ref struct {
|
||||
solver.ResultProxy
|
||||
}
|
||||
|
||||
func newRef(r solver.ResultProxy) (*ref, error) {
|
||||
return &ref{ResultProxy: r}, nil
|
||||
}
|
||||
|
||||
func (r *ref) ToState() (st llb.State, err error) {
|
||||
defop, err := llb.NewDefinitionOp(r.Definition())
|
||||
if err != nil {
|
||||
return st, err
|
||||
}
|
||||
return llb.NewState(defop), nil
|
||||
}
|
||||
|
||||
func (r *ref) ReadFile(ctx context.Context, req client.ReadRequest) ([]byte, error) {
|
||||
ref, err := r.getImmutableRef(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newReq := cacheutil.ReadRequest{
|
||||
Filename: req.Filename,
|
||||
}
|
||||
if r := req.Range; r != nil {
|
||||
newReq.Range = &cacheutil.FileRange{
|
||||
Offset: r.Offset,
|
||||
Length: r.Length,
|
||||
}
|
||||
}
|
||||
return cacheutil.ReadFile(ctx, ref, newReq)
|
||||
}
|
||||
|
||||
func (r *ref) ReadDir(ctx context.Context, req client.ReadDirRequest) ([]*fstypes.Stat, error) {
|
||||
ref, err := r.getImmutableRef(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newReq := cacheutil.ReadDirRequest{
|
||||
Path: req.Path,
|
||||
IncludePattern: req.IncludePattern,
|
||||
}
|
||||
return cacheutil.ReadDir(ctx, ref, newReq)
|
||||
}
|
||||
|
||||
func (r *ref) StatFile(ctx context.Context, req client.StatRequest) (*fstypes.Stat, error) {
|
||||
ref, err := r.getImmutableRef(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cacheutil.StatFile(ctx, ref, req.Path)
|
||||
}
|
||||
|
||||
func (r *ref) getImmutableRef(ctx context.Context) (cache.ImmutableRef, error) {
|
||||
rr, err := r.ResultProxy.Result(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref, ok := rr.Sys().(*worker.WorkerRef)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid ref: %T", rr.Sys())
|
||||
}
|
||||
return ref.ImmutableRef, nil
|
||||
}
|
39
vendor/github.com/moby/buildkit/frontend/gateway/forwarder/frontend.go
generated
vendored
39
vendor/github.com/moby/buildkit/frontend/gateway/forwarder/frontend.go
generated
vendored
@@ -1,39 +0,0 @@
|
||||
package forwarder
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/moby/buildkit/frontend"
|
||||
"github.com/moby/buildkit/frontend/gateway/client"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
)
|
||||
|
||||
func NewGatewayForwarder(w frontend.WorkerInfos, f client.BuildFunc) frontend.Frontend {
|
||||
return &GatewayForwarder{
|
||||
workers: w,
|
||||
f: f,
|
||||
}
|
||||
}
|
||||
|
||||
type GatewayForwarder struct {
|
||||
workers frontend.WorkerInfos
|
||||
f client.BuildFunc
|
||||
}
|
||||
|
||||
func (gf *GatewayForwarder) Solve(ctx context.Context, llbBridge frontend.FrontendLLBBridge, opts map[string]string, inputs map[string]*pb.Definition) (retRes *frontend.Result, retErr error) {
|
||||
c, err := llbBridgeToGatewayClient(ctx, llbBridge, opts, inputs, gf.workers.WorkerInfos())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
c.discard(retErr)
|
||||
}()
|
||||
|
||||
res, err := gf.f(ctx, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.toFrontendResult(res)
|
||||
}
|
459
vendor/github.com/moby/buildkit/solver/bboltcachestorage/storage.go
generated
vendored
459
vendor/github.com/moby/buildkit/solver/bboltcachestorage/storage.go
generated
vendored
@@ -1,459 +0,0 @@
|
||||
package bboltcachestorage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/moby/buildkit/solver"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
const (
|
||||
resultBucket = "_result"
|
||||
linksBucket = "_links"
|
||||
byResultBucket = "_byresult"
|
||||
backlinksBucket = "_backlinks"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
db *bolt.DB
|
||||
}
|
||||
|
||||
func NewStore(dbPath string) (*Store, error) {
|
||||
db, err := bolt.Open(dbPath, 0600, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to open database file %s", dbPath)
|
||||
}
|
||||
if err := db.Update(func(tx *bolt.Tx) error {
|
||||
for _, b := range []string{resultBucket, linksBucket, byResultBucket, backlinksBucket} {
|
||||
if _, err := tx.CreateBucketIfNotExists([]byte(b)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db.NoSync = true
|
||||
return &Store{db: db}, nil
|
||||
}
|
||||
|
||||
func (s *Store) Exists(id string) bool {
|
||||
exists := false
|
||||
err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(linksBucket)).Bucket([]byte(id))
|
||||
exists = b != nil
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return exists
|
||||
}
|
||||
|
||||
func (s *Store) Walk(fn func(id string) error) error {
|
||||
ids := make([]string, 0)
|
||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(linksBucket))
|
||||
c := b.Cursor()
|
||||
for k, v := c.First(); k != nil; k, v = c.Next() {
|
||||
if v == nil {
|
||||
ids = append(ids, string(k))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, id := range ids {
|
||||
if err := fn(id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) WalkResults(id string, fn func(solver.CacheResult) error) error {
|
||||
var list []solver.CacheResult
|
||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(resultBucket))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
b = b.Bucket([]byte(id))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return b.ForEach(func(k, v []byte) error {
|
||||
var res solver.CacheResult
|
||||
if err := json.Unmarshal(v, &res); err != nil {
|
||||
return err
|
||||
}
|
||||
list = append(list, res)
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, res := range list {
|
||||
if err := fn(res); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) Load(id string, resultID string) (solver.CacheResult, error) {
|
||||
var res solver.CacheResult
|
||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(resultBucket))
|
||||
if b == nil {
|
||||
return errors.WithStack(solver.ErrNotFound)
|
||||
}
|
||||
b = b.Bucket([]byte(id))
|
||||
if b == nil {
|
||||
return errors.WithStack(solver.ErrNotFound)
|
||||
}
|
||||
|
||||
v := b.Get([]byte(resultID))
|
||||
if v == nil {
|
||||
return errors.WithStack(solver.ErrNotFound)
|
||||
}
|
||||
|
||||
return json.Unmarshal(v, &res)
|
||||
}); err != nil {
|
||||
return solver.CacheResult{}, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (s *Store) AddResult(id string, res solver.CacheResult) error {
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
_, err := tx.Bucket([]byte(linksBucket)).CreateBucketIfNotExists([]byte(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, err := tx.Bucket([]byte(resultBucket)).CreateBucketIfNotExists([]byte(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dt, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.Put([]byte(res.ID), dt); err != nil {
|
||||
return err
|
||||
}
|
||||
b, err = tx.Bucket([]byte(byResultBucket)).CreateBucketIfNotExists([]byte(res.ID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := b.Put([]byte(id), []byte{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) WalkIDsByResult(resultID string, fn func(string) error) error {
|
||||
ids := map[string]struct{}{}
|
||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(byResultBucket))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
b = b.Bucket([]byte(resultID))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
return b.ForEach(func(k, v []byte) error {
|
||||
ids[string(k)] = struct{}{}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
for id := range ids {
|
||||
if err := fn(id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) Release(resultID string) error {
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(byResultBucket))
|
||||
if b == nil {
|
||||
return errors.WithStack(solver.ErrNotFound)
|
||||
}
|
||||
b = b.Bucket([]byte(resultID))
|
||||
if b == nil {
|
||||
return errors.WithStack(solver.ErrNotFound)
|
||||
}
|
||||
if err := b.ForEach(func(k, v []byte) error {
|
||||
return s.releaseHelper(tx, string(k), resultID)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) releaseHelper(tx *bolt.Tx, id, resultID string) error {
|
||||
results := tx.Bucket([]byte(resultBucket)).Bucket([]byte(id))
|
||||
if results == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := results.Delete([]byte(resultID)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ids := tx.Bucket([]byte(byResultBucket))
|
||||
|
||||
ids = ids.Bucket([]byte(resultID))
|
||||
if ids == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := ids.Delete([]byte(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isEmptyBucket(ids) {
|
||||
if err := tx.Bucket([]byte(byResultBucket)).DeleteBucket([]byte(resultID)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
links := tx.Bucket([]byte(resultBucket))
|
||||
if results == nil {
|
||||
return nil
|
||||
}
|
||||
links = links.Bucket([]byte(id))
|
||||
|
||||
return s.emptyBranchWithParents(tx, []byte(id))
|
||||
}
|
||||
|
||||
func (s *Store) emptyBranchWithParents(tx *bolt.Tx, id []byte) error {
|
||||
results := tx.Bucket([]byte(resultBucket)).Bucket(id)
|
||||
if results == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
isEmptyLinks := true
|
||||
links := tx.Bucket([]byte(linksBucket)).Bucket(id)
|
||||
if links != nil {
|
||||
isEmptyLinks = isEmptyBucket(links)
|
||||
}
|
||||
|
||||
if !isEmptyBucket(results) || !isEmptyLinks {
|
||||
return nil
|
||||
}
|
||||
|
||||
if backlinks := tx.Bucket([]byte(backlinksBucket)).Bucket(id); backlinks != nil {
|
||||
if err := backlinks.ForEach(func(k, v []byte) error {
|
||||
if subLinks := tx.Bucket([]byte(linksBucket)).Bucket(k); subLinks != nil {
|
||||
if err := subLinks.ForEach(func(k, v []byte) error {
|
||||
parts := bytes.Split(k, []byte("@"))
|
||||
if len(parts) != 2 {
|
||||
return errors.Errorf("invalid key %s", k)
|
||||
}
|
||||
if bytes.Equal(id, parts[1]) {
|
||||
return subLinks.Delete(k)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isEmptyBucket(subLinks) {
|
||||
if err := tx.Bucket([]byte(linksBucket)).DeleteBucket(k); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.emptyBranchWithParents(tx, k)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Bucket([]byte(backlinksBucket)).DeleteBucket(id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// intentionally ignoring errors
|
||||
tx.Bucket([]byte(linksBucket)).DeleteBucket([]byte(id))
|
||||
tx.Bucket([]byte(resultBucket)).DeleteBucket([]byte(id))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) AddLink(id string, link solver.CacheInfoLink, target string) error {
|
||||
return s.db.Update(func(tx *bolt.Tx) error {
|
||||
b, err := tx.Bucket([]byte(linksBucket)).CreateBucketIfNotExists([]byte(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dt, err := json.Marshal(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := b.Put(bytes.Join([][]byte{dt, []byte(target)}, []byte("@")), []byte{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, err = tx.Bucket([]byte(backlinksBucket)).CreateBucketIfNotExists([]byte(target))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := b.Put([]byte(id), []byte{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Store) WalkLinks(id string, link solver.CacheInfoLink, fn func(id string) error) error {
|
||||
var links []string
|
||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(linksBucket))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
b = b.Bucket([]byte(id))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
dt, err := json.Marshal(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
index := bytes.Join([][]byte{dt, {}}, []byte("@"))
|
||||
c := b.Cursor()
|
||||
k, _ := c.Seek([]byte(index))
|
||||
for {
|
||||
if k != nil && bytes.HasPrefix(k, index) {
|
||||
target := bytes.TrimPrefix(k, index)
|
||||
links = append(links, string(target))
|
||||
k, _ = c.Next()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, l := range links {
|
||||
if err := fn(l); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) HasLink(id string, link solver.CacheInfoLink, target string) bool {
|
||||
var v bool
|
||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||
b := tx.Bucket([]byte(linksBucket))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
b = b.Bucket([]byte(id))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
dt, err := json.Marshal(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v = b.Get(bytes.Join([][]byte{dt, []byte(target)}, []byte("@"))) != nil
|
||||
return nil
|
||||
}); err != nil {
|
||||
return false
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *Store) WalkBacklinks(id string, fn func(id string, link solver.CacheInfoLink) error) error {
|
||||
var outIDs []string
|
||||
var outLinks []solver.CacheInfoLink
|
||||
|
||||
if err := s.db.View(func(tx *bolt.Tx) error {
|
||||
links := tx.Bucket([]byte(linksBucket))
|
||||
if links == nil {
|
||||
return nil
|
||||
}
|
||||
backLinks := tx.Bucket([]byte(backlinksBucket))
|
||||
if backLinks == nil {
|
||||
return nil
|
||||
}
|
||||
b := backLinks.Bucket([]byte(id))
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := b.ForEach(func(bid, v []byte) error {
|
||||
b = links.Bucket(bid)
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
if err := b.ForEach(func(k, v []byte) error {
|
||||
parts := bytes.Split(k, []byte("@"))
|
||||
if len(parts) == 2 {
|
||||
if string(parts[1]) != id {
|
||||
return nil
|
||||
}
|
||||
var l solver.CacheInfoLink
|
||||
if err := json.Unmarshal(parts[0], &l); err != nil {
|
||||
return err
|
||||
}
|
||||
l.Digest = digest.FromBytes([]byte(fmt.Sprintf("%s@%d", l.Digest, l.Output)))
|
||||
l.Output = 0
|
||||
outIDs = append(outIDs, string(bid))
|
||||
outLinks = append(outLinks, l)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range outIDs {
|
||||
if err := fn(outIDs[i], outLinks[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isEmptyBucket(b *bolt.Bucket) bool {
|
||||
if b == nil {
|
||||
return true
|
||||
}
|
||||
k, _ := b.Cursor().First()
|
||||
return k == nil
|
||||
}
|
0
vendor/github.com/moby/buildkit/util/network/cniprovider/allowempty.s
generated
vendored
0
vendor/github.com/moby/buildkit/util/network/cniprovider/allowempty.s
generated
vendored
120
vendor/github.com/moby/buildkit/util/network/cniprovider/cni.go
generated
vendored
120
vendor/github.com/moby/buildkit/util/network/cniprovider/cni.go
generated
vendored
@@ -1,120 +0,0 @@
|
||||
package cniprovider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/go-cni"
|
||||
"github.com/gofrs/flock"
|
||||
"github.com/moby/buildkit/identity"
|
||||
"github.com/moby/buildkit/util/network"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
Root string
|
||||
ConfigPath string
|
||||
BinaryDir string
|
||||
}
|
||||
|
||||
func New(opt Opt) (network.Provider, error) {
|
||||
if _, err := os.Stat(opt.ConfigPath); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read cni config %q", opt.ConfigPath)
|
||||
}
|
||||
if _, err := os.Stat(opt.BinaryDir); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to read cni binary dir %q", opt.BinaryDir)
|
||||
}
|
||||
|
||||
cniHandle, err := cni.New(
|
||||
cni.WithMinNetworkCount(2),
|
||||
cni.WithConfFile(opt.ConfigPath),
|
||||
cni.WithPluginDir([]string{opt.BinaryDir}),
|
||||
cni.WithLoNetwork,
|
||||
cni.WithInterfacePrefix(("eth")))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cp := &cniProvider{CNI: cniHandle, root: opt.Root}
|
||||
if err := cp.initNetwork(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cp, nil
|
||||
}
|
||||
|
||||
type cniProvider struct {
|
||||
cni.CNI
|
||||
root string
|
||||
}
|
||||
|
||||
func (c *cniProvider) initNetwork() error {
|
||||
if v := os.Getenv("BUILDKIT_CNI_INIT_LOCK_PATH"); v != "" {
|
||||
l := flock.New(v)
|
||||
if err := l.Lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer l.Unlock()
|
||||
}
|
||||
ns, err := c.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ns.Close()
|
||||
}
|
||||
|
||||
func (c *cniProvider) New() (network.Namespace, error) {
|
||||
id := identity.NewID()
|
||||
nsPath := filepath.Join(c.root, "net/cni", id)
|
||||
if err := os.MkdirAll(filepath.Dir(nsPath), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := createNetNS(nsPath); err != nil {
|
||||
os.RemoveAll(filepath.Dir(nsPath))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := c.CNI.Setup(context.TODO(), id, nsPath); err != nil {
|
||||
os.RemoveAll(filepath.Dir(nsPath))
|
||||
return nil, errors.Wrap(err, "CNI setup error")
|
||||
}
|
||||
|
||||
return &cniNS{path: nsPath, id: id, handle: c.CNI}, nil
|
||||
}
|
||||
|
||||
type cniNS struct {
|
||||
handle cni.CNI
|
||||
id string
|
||||
path string
|
||||
}
|
||||
|
||||
func (ns *cniNS) Set(s *specs.Spec) {
|
||||
oci.WithLinuxNamespace(specs.LinuxNamespace{
|
||||
Type: specs.NetworkNamespace,
|
||||
Path: ns.path,
|
||||
})(nil, nil, nil, s)
|
||||
}
|
||||
|
||||
func (ns *cniNS) Close() error {
|
||||
err := ns.handle.Remove(context.TODO(), ns.id, ns.path)
|
||||
|
||||
if err1 := unix.Unmount(ns.path, unix.MNT_DETACH); err1 != nil {
|
||||
if err1 != syscall.EINVAL && err1 != syscall.ENOENT && err == nil {
|
||||
err = errors.Wrap(err1, "error unmounting network namespace")
|
||||
}
|
||||
}
|
||||
if err1 := os.RemoveAll(filepath.Dir(ns.path)); err1 != nil && !os.IsNotExist(err1) && err == nil {
|
||||
err = errors.Wrap(err, "error removing network namespace")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
16
vendor/github.com/moby/buildkit/util/network/cniprovider/cni_unsafe.go
generated
vendored
16
vendor/github.com/moby/buildkit/util/network/cniprovider/cni_unsafe.go
generated
vendored
@@ -1,16 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package cniprovider
|
||||
|
||||
import (
|
||||
_ "unsafe" // required for go:linkname.
|
||||
)
|
||||
|
||||
//go:linkname beforeFork syscall.runtime_BeforeFork
|
||||
func beforeFork()
|
||||
|
||||
//go:linkname afterFork syscall.runtime_AfterFork
|
||||
func afterFork()
|
||||
|
||||
//go:linkname afterForkInChild syscall.runtime_AfterForkInChild
|
||||
func afterForkInChild()
|
59
vendor/github.com/moby/buildkit/util/network/cniprovider/createns_linux.go
generated
vendored
59
vendor/github.com/moby/buildkit/util/network/cniprovider/createns_linux.go
generated
vendored
@@ -1,59 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package cniprovider
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func createNetNS(p string) error {
|
||||
f, err := os.Create(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
procNetNSBytes, err := syscall.BytePtrFromString("/proc/self/ns/net")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pBytes, err := syscall.BytePtrFromString(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
beforeFork()
|
||||
|
||||
pid, _, errno := syscall.RawSyscall6(syscall.SYS_CLONE, uintptr(syscall.SIGCHLD)|unix.CLONE_NEWNET, 0, 0, 0, 0, 0)
|
||||
if errno != 0 {
|
||||
afterFork()
|
||||
return errno
|
||||
}
|
||||
|
||||
if pid != 0 {
|
||||
afterFork()
|
||||
var ws unix.WaitStatus
|
||||
_, err = unix.Wait4(int(pid), &ws, 0, nil)
|
||||
for err == syscall.EINTR {
|
||||
_, err = unix.Wait4(int(pid), &ws, 0, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to find pid=%d process", pid)
|
||||
}
|
||||
errno = syscall.Errno(ws.ExitStatus())
|
||||
if errno != 0 {
|
||||
return errors.Wrap(errno, "failed to mount")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
afterForkInChild()
|
||||
_, _, errno = syscall.RawSyscall6(syscall.SYS_MOUNT, uintptr(unsafe.Pointer(procNetNSBytes)), uintptr(unsafe.Pointer(pBytes)), 0, uintptr(unix.MS_BIND), 0, 0)
|
||||
syscall.RawSyscall(syscall.SYS_EXIT, uintptr(errno), 0, 0)
|
||||
panic("unreachable")
|
||||
}
|
9
vendor/github.com/moby/buildkit/util/network/cniprovider/createns_nolinux.go
generated
vendored
9
vendor/github.com/moby/buildkit/util/network/cniprovider/createns_nolinux.go
generated
vendored
@@ -1,9 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
package cniprovider
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
func createNetNS(p string) error {
|
||||
return errors.Errorf("creating netns for cni not supported")
|
||||
}
|
50
vendor/github.com/moby/buildkit/util/network/netproviders/network.go
generated
vendored
50
vendor/github.com/moby/buildkit/util/network/netproviders/network.go
generated
vendored
@@ -1,50 +0,0 @@
|
||||
package netproviders
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/network"
|
||||
"github.com/moby/buildkit/util/network/cniprovider"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
CNI cniprovider.Opt
|
||||
Mode string
|
||||
}
|
||||
|
||||
// Providers returns the network provider set
|
||||
func Providers(opt Opt) (map[pb.NetMode]network.Provider, error) {
|
||||
var defaultProvider network.Provider
|
||||
switch opt.Mode {
|
||||
case "cni":
|
||||
cniProvider, err := cniprovider.New(opt.CNI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultProvider = cniProvider
|
||||
case "host":
|
||||
defaultProvider = network.NewHostProvider()
|
||||
case "auto", "":
|
||||
if _, err := os.Stat(opt.CNI.ConfigPath); err == nil {
|
||||
cniProvider, err := cniprovider.New(opt.CNI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultProvider = cniProvider
|
||||
} else {
|
||||
logrus.Warnf("using host network as the default")
|
||||
defaultProvider = network.NewHostProvider()
|
||||
}
|
||||
default:
|
||||
return nil, errors.Errorf("invalid network mode: %q", opt.Mode)
|
||||
}
|
||||
|
||||
return map[pb.NetMode]network.Provider{
|
||||
pb.NetMode_UNSET: defaultProvider,
|
||||
pb.NetMode_HOST: network.NewHostProvider(),
|
||||
pb.NetMode_NONE: network.NewNoneProvider(),
|
||||
}, nil
|
||||
}
|
40
vendor/github.com/moby/buildkit/util/rootless/specconv/specconv_linux.go
generated
vendored
40
vendor/github.com/moby/buildkit/util/rootless/specconv/specconv_linux.go
generated
vendored
@@ -1,40 +0,0 @@
|
||||
package specconv
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// ToRootless converts spec to be compatible with "rootless" runc.
|
||||
// * Remove /sys mount
|
||||
// * Remove cgroups
|
||||
//
|
||||
// See docs/rootless.md for the supported runc revision.
|
||||
func ToRootless(spec *specs.Spec) error {
|
||||
// Remove /sys mount because we can't mount /sys when the daemon netns
|
||||
// is not unshared from the host.
|
||||
//
|
||||
// Instead, we could bind-mount /sys from the host, however, `rbind, ro`
|
||||
// does not make /sys/fs/cgroup read-only (and we can't bind-mount /sys
|
||||
// without rbind)
|
||||
//
|
||||
// PR for making /sys/fs/cgroup read-only is proposed, but it is very
|
||||
// complicated: https://github.com/opencontainers/runc/pull/1869
|
||||
//
|
||||
// For buildkit usecase, we suppose we don't need to provide /sys to
|
||||
// containers and remove /sys mount as a workaround.
|
||||
var mounts []specs.Mount
|
||||
for _, mount := range spec.Mounts {
|
||||
if strings.HasPrefix(mount.Destination, "/sys") {
|
||||
continue
|
||||
}
|
||||
mounts = append(mounts, mount)
|
||||
}
|
||||
spec.Mounts = mounts
|
||||
|
||||
// Remove cgroups so as to avoid `container_linux.go:337: starting container process caused "process_linux.go:280: applying cgroup configuration for process caused \"mkdir /sys/fs/cgroup/cpuset/buildkit: permission denied\""`
|
||||
spec.Linux.Resources = nil
|
||||
spec.Linux.CgroupsPath = ""
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user