mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-01 00:23:56 +08:00 
			
		
		
		
	build: move funcs related to solve opts handling
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
		
							
								
								
									
										605
									
								
								build/build.go
									
									
									
									
									
								
							
							
						
						
									
										605
									
								
								build/build.go
									
									
									
									
									
								
							| @@ -1,7 +1,6 @@ | |||||||
| package build | package build | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"bufio" |  | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"context" | 	"context" | ||||||
| 	_ "crypto/sha256" // ensure digests can be computed | 	_ "crypto/sha256" // ensure digests can be computed | ||||||
| @@ -10,44 +9,32 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path/filepath" |  | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"syscall" |  | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/containerd/containerd/content" |  | ||||||
| 	"github.com/containerd/containerd/content/local" |  | ||||||
| 	"github.com/containerd/containerd/images" | 	"github.com/containerd/containerd/images" | ||||||
| 	"github.com/containerd/containerd/platforms" |  | ||||||
| 	"github.com/distribution/reference" | 	"github.com/distribution/reference" | ||||||
| 	"github.com/docker/buildx/builder" | 	"github.com/docker/buildx/builder" | ||||||
| 	"github.com/docker/buildx/driver" | 	"github.com/docker/buildx/driver" | ||||||
| 	"github.com/docker/buildx/util/confutil" |  | ||||||
| 	"github.com/docker/buildx/util/desktop" | 	"github.com/docker/buildx/util/desktop" | ||||||
| 	"github.com/docker/buildx/util/dockerutil" | 	"github.com/docker/buildx/util/dockerutil" | ||||||
| 	"github.com/docker/buildx/util/imagetools" | 	"github.com/docker/buildx/util/imagetools" | ||||||
| 	"github.com/docker/buildx/util/osutil" |  | ||||||
| 	"github.com/docker/buildx/util/progress" | 	"github.com/docker/buildx/util/progress" | ||||||
| 	"github.com/docker/buildx/util/resolver" | 	"github.com/docker/buildx/util/resolver" | ||||||
| 	"github.com/docker/buildx/util/waitmap" | 	"github.com/docker/buildx/util/waitmap" | ||||||
| 	"github.com/docker/cli/opts" | 	"github.com/docker/cli/opts" | ||||||
| 	imagetypes "github.com/docker/docker/api/types/image" | 	imagetypes "github.com/docker/docker/api/types/image" | ||||||
| 	"github.com/docker/docker/builder/remotecontext/urlutil" |  | ||||||
| 	"github.com/docker/docker/pkg/jsonmessage" | 	"github.com/docker/docker/pkg/jsonmessage" | ||||||
| 	"github.com/moby/buildkit/client" | 	"github.com/moby/buildkit/client" | ||||||
| 	"github.com/moby/buildkit/client/llb" | 	"github.com/moby/buildkit/client/llb" | ||||||
| 	"github.com/moby/buildkit/client/ociindex" |  | ||||||
| 	"github.com/moby/buildkit/exporter/containerimage/exptypes" | 	"github.com/moby/buildkit/exporter/containerimage/exptypes" | ||||||
| 	gateway "github.com/moby/buildkit/frontend/gateway/client" | 	gateway "github.com/moby/buildkit/frontend/gateway/client" | ||||||
| 	"github.com/moby/buildkit/identity" |  | ||||||
| 	"github.com/moby/buildkit/session" | 	"github.com/moby/buildkit/session" | ||||||
| 	"github.com/moby/buildkit/session/upload/uploadprovider" |  | ||||||
| 	"github.com/moby/buildkit/solver/errdefs" | 	"github.com/moby/buildkit/solver/errdefs" | ||||||
| 	"github.com/moby/buildkit/solver/pb" | 	"github.com/moby/buildkit/solver/pb" | ||||||
| 	spb "github.com/moby/buildkit/sourcepolicy/pb" | 	spb "github.com/moby/buildkit/sourcepolicy/pb" | ||||||
| 	"github.com/moby/buildkit/util/apicaps" |  | ||||||
| 	"github.com/moby/buildkit/util/entitlements" | 	"github.com/moby/buildkit/util/entitlements" | ||||||
| 	"github.com/moby/buildkit/util/progress/progresswriter" | 	"github.com/moby/buildkit/util/progress/progresswriter" | ||||||
| 	"github.com/moby/buildkit/util/tracing" | 	"github.com/moby/buildkit/util/tracing" | ||||||
| @@ -157,324 +144,6 @@ func toRepoOnly(in string) (string, error) { | |||||||
| 	return strings.Join(out, ","), nil | 	return strings.Join(out, ","), nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Options, bopts gateway.BuildOpts, configDir string, pw progress.Writer, docker *dockerutil.Client) (solveOpt *client.SolveOpt, release func(), err error) { |  | ||||||
| 	nodeDriver := node.Driver |  | ||||||
| 	defers := make([]func(), 0, 2) |  | ||||||
| 	releaseF := func() { |  | ||||||
| 		for _, f := range defers { |  | ||||||
| 			f() |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	defer func() { |  | ||||||
| 		if err != nil { |  | ||||||
| 			releaseF() |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	// inline cache from build arg |  | ||||||
| 	if v, ok := opt.BuildArgs["BUILDKIT_INLINE_CACHE"]; ok { |  | ||||||
| 		if v, _ := strconv.ParseBool(v); v { |  | ||||||
| 			opt.CacheTo = append(opt.CacheTo, client.CacheOptionsEntry{ |  | ||||||
| 				Type:  "inline", |  | ||||||
| 				Attrs: map[string]string{}, |  | ||||||
| 			}) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, e := range opt.CacheTo { |  | ||||||
| 		if e.Type != "inline" && !nodeDriver.Features(ctx)[driver.CacheExport] { |  | ||||||
| 			return nil, nil, notSupported(driver.CacheExport, nodeDriver, "https://docs.docker.com/go/build-cache-backends/") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	cacheTo := make([]client.CacheOptionsEntry, 0, len(opt.CacheTo)) |  | ||||||
| 	for _, e := range opt.CacheTo { |  | ||||||
| 		if e.Type == "gha" { |  | ||||||
| 			if !bopts.LLBCaps.Contains(apicaps.CapID("cache.gha")) { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 		} else if e.Type == "s3" { |  | ||||||
| 			if !bopts.LLBCaps.Contains(apicaps.CapID("cache.s3")) { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		cacheTo = append(cacheTo, e) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	cacheFrom := make([]client.CacheOptionsEntry, 0, len(opt.CacheFrom)) |  | ||||||
| 	for _, e := range opt.CacheFrom { |  | ||||||
| 		if e.Type == "gha" { |  | ||||||
| 			if !bopts.LLBCaps.Contains(apicaps.CapID("cache.gha")) { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 		} else if e.Type == "s3" { |  | ||||||
| 			if !bopts.LLBCaps.Contains(apicaps.CapID("cache.s3")) { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		cacheFrom = append(cacheFrom, e) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	so := client.SolveOpt{ |  | ||||||
| 		Ref:                 opt.Ref, |  | ||||||
| 		Frontend:            "dockerfile.v0", |  | ||||||
| 		FrontendAttrs:       map[string]string{}, |  | ||||||
| 		LocalDirs:           map[string]string{}, |  | ||||||
| 		CacheExports:        cacheTo, |  | ||||||
| 		CacheImports:        cacheFrom, |  | ||||||
| 		AllowedEntitlements: opt.Allow, |  | ||||||
| 		SourcePolicy:        opt.SourcePolicy, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if so.Ref == "" { |  | ||||||
| 		so.Ref = identity.NewID() |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if opt.CgroupParent != "" { |  | ||||||
| 		so.FrontendAttrs["cgroup-parent"] = opt.CgroupParent |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if v, ok := opt.BuildArgs["BUILDKIT_MULTI_PLATFORM"]; ok { |  | ||||||
| 		if v, _ := strconv.ParseBool(v); v { |  | ||||||
| 			so.FrontendAttrs["multi-platform"] = "true" |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if multiDriver { |  | ||||||
| 		// force creation of manifest list |  | ||||||
| 		so.FrontendAttrs["multi-platform"] = "true" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	attests := make(map[string]string) |  | ||||||
| 	for k, v := range opt.Attests { |  | ||||||
| 		if v != nil { |  | ||||||
| 			attests[k] = *v |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	supportAttestations := bopts.LLBCaps.Contains(apicaps.CapID("exporter.image.attestations")) && nodeDriver.Features(ctx)[driver.MultiPlatform] |  | ||||||
| 	if len(attests) > 0 { |  | ||||||
| 		if !supportAttestations { |  | ||||||
| 			if !nodeDriver.Features(ctx)[driver.MultiPlatform] { |  | ||||||
| 				return nil, nil, notSupported("Attestation", nodeDriver, "https://docs.docker.com/go/attestations/") |  | ||||||
| 			} |  | ||||||
| 			return nil, nil, errors.Errorf("Attestations are not supported by the current BuildKit daemon") |  | ||||||
| 		} |  | ||||||
| 		for k, v := range attests { |  | ||||||
| 			so.FrontendAttrs["attest:"+k] = v |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if _, ok := opt.Attests["provenance"]; !ok && supportAttestations { |  | ||||||
| 		const noAttestEnv = "BUILDX_NO_DEFAULT_ATTESTATIONS" |  | ||||||
| 		var noProv bool |  | ||||||
| 		if v, ok := os.LookupEnv(noAttestEnv); ok { |  | ||||||
| 			noProv, err = strconv.ParseBool(v) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return nil, nil, errors.Wrap(err, "invalid "+noAttestEnv) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !noProv { |  | ||||||
| 			so.FrontendAttrs["attest:provenance"] = "mode=min,inline-only=true" |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch len(opt.Exports) { |  | ||||||
| 	case 1: |  | ||||||
| 		// valid |  | ||||||
| 	case 0: |  | ||||||
| 		if nodeDriver.IsMobyDriver() && !noDefaultLoad() { |  | ||||||
| 			// backwards compat for docker driver only: |  | ||||||
| 			// this ensures the build results in a docker image. |  | ||||||
| 			opt.Exports = []client.ExportEntry{{Type: "image", Attrs: map[string]string{}}} |  | ||||||
| 		} |  | ||||||
| 	default: |  | ||||||
| 		if err := bopts.LLBCaps.Supports(pb.CapMultipleExporters); err != nil { |  | ||||||
| 			return nil, nil, errors.Errorf("multiple outputs currently unsupported by the current BuildKit daemon, please upgrade to version v0.13+ or use a single output") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// fill in image exporter names from tags |  | ||||||
| 	if len(opt.Tags) > 0 { |  | ||||||
| 		tags := make([]string, len(opt.Tags)) |  | ||||||
| 		for i, tag := range opt.Tags { |  | ||||||
| 			ref, err := reference.Parse(tag) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return nil, nil, errors.Wrapf(err, "invalid tag %q", tag) |  | ||||||
| 			} |  | ||||||
| 			tags[i] = ref.String() |  | ||||||
| 		} |  | ||||||
| 		for i, e := range opt.Exports { |  | ||||||
| 			switch e.Type { |  | ||||||
| 			case "image", "oci", "docker": |  | ||||||
| 				opt.Exports[i].Attrs["name"] = strings.Join(tags, ",") |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		for _, e := range opt.Exports { |  | ||||||
| 			if e.Type == "image" && e.Attrs["name"] == "" && e.Attrs["push"] != "" { |  | ||||||
| 				if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok { |  | ||||||
| 					return nil, nil, errors.Errorf("tag is needed when pushing to registry") |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// cacheonly is a fake exporter to opt out of default behaviors |  | ||||||
| 	exports := make([]client.ExportEntry, 0, len(opt.Exports)) |  | ||||||
| 	for _, e := range opt.Exports { |  | ||||||
| 		if e.Type != "cacheonly" { |  | ||||||
| 			exports = append(exports, e) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	opt.Exports = exports |  | ||||||
|  |  | ||||||
| 	// set up exporters |  | ||||||
| 	for i, e := range opt.Exports { |  | ||||||
| 		if e.Type == "oci" && !nodeDriver.Features(ctx)[driver.OCIExporter] { |  | ||||||
| 			return nil, nil, notSupported(driver.OCIExporter, nodeDriver, "https://docs.docker.com/go/build-exporters/") |  | ||||||
| 		} |  | ||||||
| 		if e.Type == "docker" { |  | ||||||
| 			features := docker.Features(ctx, e.Attrs["context"]) |  | ||||||
| 			if features[dockerutil.OCIImporter] && e.Output == nil { |  | ||||||
| 				// rely on oci importer if available (which supports |  | ||||||
| 				// multi-platform images), otherwise fall back to docker |  | ||||||
| 				opt.Exports[i].Type = "oci" |  | ||||||
| 			} else if len(opt.Platforms) > 1 || len(attests) > 0 { |  | ||||||
| 				if e.Output != nil { |  | ||||||
| 					return nil, nil, errors.Errorf("docker exporter does not support exporting manifest lists, use the oci exporter instead") |  | ||||||
| 				} |  | ||||||
| 				return nil, nil, errors.Errorf("docker exporter does not currently support exporting manifest lists") |  | ||||||
| 			} |  | ||||||
| 			if e.Output == nil { |  | ||||||
| 				if nodeDriver.IsMobyDriver() { |  | ||||||
| 					e.Type = "image" |  | ||||||
| 				} else { |  | ||||||
| 					w, cancel, err := docker.LoadImage(ctx, e.Attrs["context"], pw) |  | ||||||
| 					if err != nil { |  | ||||||
| 						return nil, nil, err |  | ||||||
| 					} |  | ||||||
| 					defers = append(defers, cancel) |  | ||||||
| 					opt.Exports[i].Output = func(_ map[string]string) (io.WriteCloser, error) { |  | ||||||
| 						return w, nil |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} else if !nodeDriver.Features(ctx)[driver.DockerExporter] { |  | ||||||
| 				return nil, nil, notSupported(driver.DockerExporter, nodeDriver, "https://docs.docker.com/go/build-exporters/") |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if e.Type == "image" && nodeDriver.IsMobyDriver() { |  | ||||||
| 			opt.Exports[i].Type = "moby" |  | ||||||
| 			if e.Attrs["push"] != "" { |  | ||||||
| 				if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok { |  | ||||||
| 					if ok, _ := strconv.ParseBool(e.Attrs["push-by-digest"]); ok { |  | ||||||
| 						return nil, nil, errors.Errorf("push-by-digest is currently not implemented for docker driver, please create a new builder instance") |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if e.Type == "docker" || e.Type == "image" || e.Type == "oci" { |  | ||||||
| 			// inline buildinfo attrs from build arg |  | ||||||
| 			if v, ok := opt.BuildArgs["BUILDKIT_INLINE_BUILDINFO_ATTRS"]; ok { |  | ||||||
| 				e.Attrs["buildinfo-attrs"] = v |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	so.Exports = opt.Exports |  | ||||||
| 	so.Session = opt.Session |  | ||||||
|  |  | ||||||
| 	releaseLoad, err := LoadInputs(ctx, nodeDriver, opt.Inputs, pw, &so) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
| 	defers = append(defers, releaseLoad) |  | ||||||
|  |  | ||||||
| 	if sharedKey := so.LocalDirs["context"]; sharedKey != "" { |  | ||||||
| 		if p, err := filepath.Abs(sharedKey); err == nil { |  | ||||||
| 			sharedKey = filepath.Base(p) |  | ||||||
| 		} |  | ||||||
| 		so.SharedKey = sharedKey + ":" + confutil.TryNodeIdentifier(configDir) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if opt.Pull { |  | ||||||
| 		so.FrontendAttrs["image-resolve-mode"] = pb.AttrImageResolveModeForcePull |  | ||||||
| 	} else if nodeDriver.IsMobyDriver() { |  | ||||||
| 		// moby driver always resolves local images by default |  | ||||||
| 		so.FrontendAttrs["image-resolve-mode"] = pb.AttrImageResolveModePreferLocal |  | ||||||
| 	} |  | ||||||
| 	if opt.Target != "" { |  | ||||||
| 		so.FrontendAttrs["target"] = opt.Target |  | ||||||
| 	} |  | ||||||
| 	if len(opt.NoCacheFilter) > 0 { |  | ||||||
| 		so.FrontendAttrs["no-cache"] = strings.Join(opt.NoCacheFilter, ",") |  | ||||||
| 	} |  | ||||||
| 	if opt.NoCache { |  | ||||||
| 		so.FrontendAttrs["no-cache"] = "" |  | ||||||
| 	} |  | ||||||
| 	for k, v := range opt.BuildArgs { |  | ||||||
| 		so.FrontendAttrs["build-arg:"+k] = v |  | ||||||
| 	} |  | ||||||
| 	for k, v := range opt.Labels { |  | ||||||
| 		so.FrontendAttrs["label:"+k] = v |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for k, v := range node.ProxyConfig { |  | ||||||
| 		if _, ok := opt.BuildArgs[k]; !ok { |  | ||||||
| 			so.FrontendAttrs["build-arg:"+k] = v |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// set platforms |  | ||||||
| 	if len(opt.Platforms) != 0 { |  | ||||||
| 		pp := make([]string, len(opt.Platforms)) |  | ||||||
| 		for i, p := range opt.Platforms { |  | ||||||
| 			pp[i] = platforms.Format(p) |  | ||||||
| 		} |  | ||||||
| 		if len(pp) > 1 && !nodeDriver.Features(ctx)[driver.MultiPlatform] { |  | ||||||
| 			return nil, nil, notSupported(driver.MultiPlatform, nodeDriver, "https://docs.docker.com/go/build-multi-platform/") |  | ||||||
| 		} |  | ||||||
| 		so.FrontendAttrs["platform"] = strings.Join(pp, ",") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// setup networkmode |  | ||||||
| 	switch opt.NetworkMode { |  | ||||||
| 	case "host": |  | ||||||
| 		so.FrontendAttrs["force-network-mode"] = opt.NetworkMode |  | ||||||
| 		so.AllowedEntitlements = append(so.AllowedEntitlements, entitlements.EntitlementNetworkHost) |  | ||||||
| 	case "none": |  | ||||||
| 		so.FrontendAttrs["force-network-mode"] = opt.NetworkMode |  | ||||||
| 	case "", "default": |  | ||||||
| 	default: |  | ||||||
| 		return nil, nil, errors.Errorf("network mode %q not supported by buildkit - you can define a custom network for your builder using the network driver-opt in buildx create", opt.NetworkMode) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// setup extrahosts |  | ||||||
| 	extraHosts, err := toBuildkitExtraHosts(ctx, opt.ExtraHosts, nodeDriver) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
| 	if len(extraHosts) > 0 { |  | ||||||
| 		so.FrontendAttrs["add-hosts"] = extraHosts |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// setup shm size |  | ||||||
| 	if opt.ShmSize.Value() > 0 { |  | ||||||
| 		so.FrontendAttrs["shm-size"] = strconv.FormatInt(opt.ShmSize.Value(), 10) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// setup ulimits |  | ||||||
| 	ulimits, err := toBuildkitUlimits(opt.Ulimits) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} else if len(ulimits) > 0 { |  | ||||||
| 		so.FrontendAttrs["ulimit"] = ulimits |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &so, releaseF, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Build(ctx context.Context, nodes []builder.Node, opt map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer) (resp map[string]*client.SolveResponse, err error) { | func Build(ctx context.Context, nodes []builder.Node, opt map[string]Options, docker *dockerutil.Client, configDir string, w progress.Writer) (resp map[string]*client.SolveResponse, err error) { | ||||||
| 	return BuildWithResultHandler(ctx, nodes, opt, docker, configDir, w, nil) | 	return BuildWithResultHandler(ctx, nodes, opt, docker, configDir, w, nil) | ||||||
| } | } | ||||||
| @@ -1095,231 +764,6 @@ func remoteDigestWithMoby(ctx context.Context, d driver.Driver, name string) (st | |||||||
| 	return remoteImage.Descriptor.Digest.String(), nil | 	return remoteImage.Descriptor.Digest.String(), nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func createTempDockerfile(r io.Reader) (string, error) { |  | ||||||
| 	dir, err := os.MkdirTemp("", "dockerfile") |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	f, err := os.Create(filepath.Join(dir, "Dockerfile")) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	defer f.Close() |  | ||||||
| 	if _, err := io.Copy(f, r); err != nil { |  | ||||||
| 		return "", err |  | ||||||
| 	} |  | ||||||
| 	return dir, err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func LoadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, pw progress.Writer, target *client.SolveOpt) (func(), error) { |  | ||||||
| 	if inp.ContextPath == "" { |  | ||||||
| 		return nil, errors.New("please specify build context (e.g. \".\" for the current directory)") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// TODO: handle stdin, symlinks, remote contexts, check files exist |  | ||||||
|  |  | ||||||
| 	var ( |  | ||||||
| 		err              error |  | ||||||
| 		dockerfileReader io.Reader |  | ||||||
| 		dockerfileDir    string |  | ||||||
| 		dockerfileName   = inp.DockerfilePath |  | ||||||
| 		toRemove         []string |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	switch { |  | ||||||
| 	case inp.ContextState != nil: |  | ||||||
| 		if target.FrontendInputs == nil { |  | ||||||
| 			target.FrontendInputs = make(map[string]llb.State) |  | ||||||
| 		} |  | ||||||
| 		target.FrontendInputs["context"] = *inp.ContextState |  | ||||||
| 		target.FrontendInputs["dockerfile"] = *inp.ContextState |  | ||||||
| 	case inp.ContextPath == "-": |  | ||||||
| 		if inp.DockerfilePath == "-" { |  | ||||||
| 			return nil, errStdinConflict |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		buf := bufio.NewReader(inp.InStream) |  | ||||||
| 		magic, err := buf.Peek(archiveHeaderSize * 2) |  | ||||||
| 		if err != nil && err != io.EOF { |  | ||||||
| 			return nil, errors.Wrap(err, "failed to peek context header from STDIN") |  | ||||||
| 		} |  | ||||||
| 		if !(err == io.EOF && len(magic) == 0) { |  | ||||||
| 			if isArchive(magic) { |  | ||||||
| 				// stdin is context |  | ||||||
| 				up := uploadprovider.New() |  | ||||||
| 				target.FrontendAttrs["context"] = up.Add(buf) |  | ||||||
| 				target.Session = append(target.Session, up) |  | ||||||
| 			} else { |  | ||||||
| 				if inp.DockerfilePath != "" { |  | ||||||
| 					return nil, errDockerfileConflict |  | ||||||
| 				} |  | ||||||
| 				// stdin is dockerfile |  | ||||||
| 				dockerfileReader = buf |  | ||||||
| 				inp.ContextPath, _ = os.MkdirTemp("", "empty-dir") |  | ||||||
| 				toRemove = append(toRemove, inp.ContextPath) |  | ||||||
| 				target.LocalDirs["context"] = inp.ContextPath |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case osutil.IsLocalDir(inp.ContextPath): |  | ||||||
| 		target.LocalDirs["context"] = inp.ContextPath |  | ||||||
| 		switch inp.DockerfilePath { |  | ||||||
| 		case "-": |  | ||||||
| 			dockerfileReader = inp.InStream |  | ||||||
| 		case "": |  | ||||||
| 			dockerfileDir = inp.ContextPath |  | ||||||
| 		default: |  | ||||||
| 			dockerfileDir = filepath.Dir(inp.DockerfilePath) |  | ||||||
| 			dockerfileName = filepath.Base(inp.DockerfilePath) |  | ||||||
| 		} |  | ||||||
| 	case IsRemoteURL(inp.ContextPath): |  | ||||||
| 		if inp.DockerfilePath == "-" { |  | ||||||
| 			dockerfileReader = inp.InStream |  | ||||||
| 		} else if filepath.IsAbs(inp.DockerfilePath) { |  | ||||||
| 			dockerfileDir = filepath.Dir(inp.DockerfilePath) |  | ||||||
| 			dockerfileName = filepath.Base(inp.DockerfilePath) |  | ||||||
| 			target.FrontendAttrs["dockerfilekey"] = "dockerfile" |  | ||||||
| 		} |  | ||||||
| 		target.FrontendAttrs["context"] = inp.ContextPath |  | ||||||
| 	default: |  | ||||||
| 		return nil, errors.Errorf("unable to prepare context: path %q not found", inp.ContextPath) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if inp.DockerfileInline != "" { |  | ||||||
| 		dockerfileReader = strings.NewReader(inp.DockerfileInline) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if dockerfileReader != nil { |  | ||||||
| 		dockerfileDir, err = createTempDockerfile(dockerfileReader) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		toRemove = append(toRemove, dockerfileDir) |  | ||||||
| 		dockerfileName = "Dockerfile" |  | ||||||
| 		target.FrontendAttrs["dockerfilekey"] = "dockerfile" |  | ||||||
| 	} |  | ||||||
| 	if urlutil.IsURL(inp.DockerfilePath) { |  | ||||||
| 		dockerfileDir, err = createTempDockerfileFromURL(ctx, d, inp.DockerfilePath, pw) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 		toRemove = append(toRemove, dockerfileDir) |  | ||||||
| 		dockerfileName = "Dockerfile" |  | ||||||
| 		target.FrontendAttrs["dockerfilekey"] = "dockerfile" |  | ||||||
| 		delete(target.FrontendInputs, "dockerfile") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if dockerfileName == "" { |  | ||||||
| 		dockerfileName = "Dockerfile" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if dockerfileDir != "" { |  | ||||||
| 		target.LocalDirs["dockerfile"] = dockerfileDir |  | ||||||
| 		dockerfileName = handleLowercaseDockerfile(dockerfileDir, dockerfileName) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	target.FrontendAttrs["filename"] = dockerfileName |  | ||||||
|  |  | ||||||
| 	for k, v := range inp.NamedContexts { |  | ||||||
| 		target.FrontendAttrs["frontend.caps"] = "moby.buildkit.frontend.contexts+forward" |  | ||||||
| 		if v.State != nil { |  | ||||||
| 			target.FrontendAttrs["context:"+k] = "input:" + k |  | ||||||
| 			if target.FrontendInputs == nil { |  | ||||||
| 				target.FrontendInputs = make(map[string]llb.State) |  | ||||||
| 			} |  | ||||||
| 			target.FrontendInputs[k] = *v.State |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if IsRemoteURL(v.Path) || strings.HasPrefix(v.Path, "docker-image://") || strings.HasPrefix(v.Path, "target:") { |  | ||||||
| 			target.FrontendAttrs["context:"+k] = v.Path |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		// handle OCI layout |  | ||||||
| 		if strings.HasPrefix(v.Path, "oci-layout://") { |  | ||||||
| 			pathAlone := strings.TrimPrefix(v.Path, "oci-layout://") |  | ||||||
| 			localPath := pathAlone |  | ||||||
| 			localPath, dig, hasDigest := strings.Cut(localPath, "@") |  | ||||||
| 			localPath, tag, hasTag := strings.Cut(localPath, ":") |  | ||||||
| 			if !hasTag { |  | ||||||
| 				tag = "latest" |  | ||||||
| 				hasTag = true |  | ||||||
| 			} |  | ||||||
| 			idx := ociindex.NewStoreIndex(localPath) |  | ||||||
| 			if !hasDigest { |  | ||||||
| 				// lookup by name |  | ||||||
| 				desc, err := idx.Get(tag) |  | ||||||
| 				if err != nil { |  | ||||||
| 					return nil, err |  | ||||||
| 				} |  | ||||||
| 				if desc != nil { |  | ||||||
| 					dig = string(desc.Digest) |  | ||||||
| 					hasDigest = true |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			if !hasDigest { |  | ||||||
| 				// lookup single |  | ||||||
| 				desc, err := idx.GetSingle() |  | ||||||
| 				if err != nil { |  | ||||||
| 					return nil, err |  | ||||||
| 				} |  | ||||||
| 				if desc != nil { |  | ||||||
| 					dig = string(desc.Digest) |  | ||||||
| 					hasDigest = true |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			if !hasDigest { |  | ||||||
| 				return nil, errors.Errorf("oci-layout reference %q could not be resolved", v.Path) |  | ||||||
| 			} |  | ||||||
| 			_, err := digest.Parse(dig) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return nil, errors.Wrapf(err, "invalid oci-layout digest %s", dig) |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			store, err := local.NewStore(localPath) |  | ||||||
| 			if err != nil { |  | ||||||
| 				return nil, errors.Wrapf(err, "invalid store at %s", localPath) |  | ||||||
| 			} |  | ||||||
| 			storeName := identity.NewID() |  | ||||||
| 			if target.OCIStores == nil { |  | ||||||
| 				target.OCIStores = map[string]content.Store{} |  | ||||||
| 			} |  | ||||||
| 			target.OCIStores[storeName] = store |  | ||||||
|  |  | ||||||
| 			layout := "oci-layout://" + storeName |  | ||||||
| 			if hasTag { |  | ||||||
| 				layout += ":" + tag |  | ||||||
| 			} |  | ||||||
| 			if hasDigest { |  | ||||||
| 				layout += "@" + dig |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			target.FrontendAttrs["context:"+k] = layout |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		st, err := os.Stat(v.Path) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, errors.Wrapf(err, "failed to get build context %v", k) |  | ||||||
| 		} |  | ||||||
| 		if !st.IsDir() { |  | ||||||
| 			return nil, errors.Wrapf(syscall.ENOTDIR, "failed to get build context path %v", v) |  | ||||||
| 		} |  | ||||||
| 		localName := k |  | ||||||
| 		if k == "context" || k == "dockerfile" { |  | ||||||
| 			localName = "_" + k // underscore to avoid collisions |  | ||||||
| 		} |  | ||||||
| 		target.LocalDirs[localName] = v.Path |  | ||||||
| 		target.FrontendAttrs["context:"+k] = "local:" + localName |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	release := func() { |  | ||||||
| 		for _, dir := range toRemove { |  | ||||||
| 			os.RemoveAll(dir) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return release, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func resultKey(index int, name string) string { | func resultKey(index int, name string) string { | ||||||
| 	return fmt.Sprintf("%d-%s", index, name) | 	return fmt.Sprintf("%d-%s", index, name) | ||||||
| } | } | ||||||
| @@ -1428,55 +872,6 @@ func waitContextDeps(ctx context.Context, index int, results *waitmap.Map, so *c | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func notSupported(f driver.Feature, d driver.Driver, docs string) error { |  | ||||||
| 	return errors.Errorf(`%s is not supported for the %s driver. |  | ||||||
| Switch to a different driver, or turn on the containerd image store, and try again. |  | ||||||
| Learn more at %s`, f, d.Factory().Name(), docs) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func noDefaultLoad() bool { |  | ||||||
| 	v, ok := os.LookupEnv("BUILDX_NO_DEFAULT_LOAD") |  | ||||||
| 	if !ok { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	b, err := strconv.ParseBool(v) |  | ||||||
| 	if err != nil { |  | ||||||
| 		logrus.Warnf("invalid non-bool value for BUILDX_NO_DEFAULT_LOAD: %s", v) |  | ||||||
| 	} |  | ||||||
| 	return b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // handle https://github.com/moby/moby/pull/10858 |  | ||||||
| func handleLowercaseDockerfile(dir, p string) string { |  | ||||||
| 	if filepath.Base(p) != "Dockerfile" { |  | ||||||
| 		return p |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	f, err := os.Open(filepath.Dir(filepath.Join(dir, p))) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return p |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	names, err := f.Readdirnames(-1) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return p |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	foundLowerCase := false |  | ||||||
| 	for _, n := range names { |  | ||||||
| 		if n == "Dockerfile" { |  | ||||||
| 			return p |  | ||||||
| 		} |  | ||||||
| 		if n == "dockerfile" { |  | ||||||
| 			foundLowerCase = true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if foundLowerCase { |  | ||||||
| 		return filepath.Join(filepath.Dir(p), "dockerfile") |  | ||||||
| 	} |  | ||||||
| 	return p |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func noPrintFunc(opt map[string]Options) bool { | func noPrintFunc(opt map[string]Options) bool { | ||||||
| 	for _, v := range opt { | 	for _, v := range opt { | ||||||
| 		if v.PrintFunc != nil { | 		if v.PrintFunc != nil { | ||||||
|   | |||||||
							
								
								
									
										609
									
								
								build/opt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										609
									
								
								build/opt.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,609 @@ | |||||||
|  | package build | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"context" | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 	"syscall" | ||||||
|  |  | ||||||
|  | 	"github.com/containerd/containerd/content" | ||||||
|  | 	"github.com/containerd/containerd/content/local" | ||||||
|  | 	"github.com/containerd/containerd/platforms" | ||||||
|  | 	"github.com/distribution/reference" | ||||||
|  | 	"github.com/docker/buildx/builder" | ||||||
|  | 	"github.com/docker/buildx/driver" | ||||||
|  | 	"github.com/docker/buildx/util/confutil" | ||||||
|  | 	"github.com/docker/buildx/util/dockerutil" | ||||||
|  | 	"github.com/docker/buildx/util/osutil" | ||||||
|  | 	"github.com/docker/buildx/util/progress" | ||||||
|  | 	"github.com/docker/docker/builder/remotecontext/urlutil" | ||||||
|  | 	"github.com/moby/buildkit/client" | ||||||
|  | 	"github.com/moby/buildkit/client/llb" | ||||||
|  | 	"github.com/moby/buildkit/client/ociindex" | ||||||
|  | 	gateway "github.com/moby/buildkit/frontend/gateway/client" | ||||||
|  | 	"github.com/moby/buildkit/identity" | ||||||
|  | 	"github.com/moby/buildkit/session/upload/uploadprovider" | ||||||
|  | 	"github.com/moby/buildkit/solver/pb" | ||||||
|  | 	"github.com/moby/buildkit/util/apicaps" | ||||||
|  | 	"github.com/moby/buildkit/util/entitlements" | ||||||
|  | 	"github.com/opencontainers/go-digest" | ||||||
|  | 	"github.com/pkg/errors" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Options, bopts gateway.BuildOpts, configDir string, pw progress.Writer, docker *dockerutil.Client) (solveOpt *client.SolveOpt, release func(), err error) { | ||||||
|  | 	nodeDriver := node.Driver | ||||||
|  | 	defers := make([]func(), 0, 2) | ||||||
|  | 	releaseF := func() { | ||||||
|  | 		for _, f := range defers { | ||||||
|  | 			f() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	defer func() { | ||||||
|  | 		if err != nil { | ||||||
|  | 			releaseF() | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	// inline cache from build arg | ||||||
|  | 	if v, ok := opt.BuildArgs["BUILDKIT_INLINE_CACHE"]; ok { | ||||||
|  | 		if v, _ := strconv.ParseBool(v); v { | ||||||
|  | 			opt.CacheTo = append(opt.CacheTo, client.CacheOptionsEntry{ | ||||||
|  | 				Type:  "inline", | ||||||
|  | 				Attrs: map[string]string{}, | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, e := range opt.CacheTo { | ||||||
|  | 		if e.Type != "inline" && !nodeDriver.Features(ctx)[driver.CacheExport] { | ||||||
|  | 			return nil, nil, notSupported(driver.CacheExport, nodeDriver, "https://docs.docker.com/go/build-cache-backends/") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cacheTo := make([]client.CacheOptionsEntry, 0, len(opt.CacheTo)) | ||||||
|  | 	for _, e := range opt.CacheTo { | ||||||
|  | 		if e.Type == "gha" { | ||||||
|  | 			if !bopts.LLBCaps.Contains(apicaps.CapID("cache.gha")) { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		} else if e.Type == "s3" { | ||||||
|  | 			if !bopts.LLBCaps.Contains(apicaps.CapID("cache.s3")) { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		cacheTo = append(cacheTo, e) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cacheFrom := make([]client.CacheOptionsEntry, 0, len(opt.CacheFrom)) | ||||||
|  | 	for _, e := range opt.CacheFrom { | ||||||
|  | 		if e.Type == "gha" { | ||||||
|  | 			if !bopts.LLBCaps.Contains(apicaps.CapID("cache.gha")) { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		} else if e.Type == "s3" { | ||||||
|  | 			if !bopts.LLBCaps.Contains(apicaps.CapID("cache.s3")) { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		cacheFrom = append(cacheFrom, e) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	so := client.SolveOpt{ | ||||||
|  | 		Ref:                 opt.Ref, | ||||||
|  | 		Frontend:            "dockerfile.v0", | ||||||
|  | 		FrontendAttrs:       map[string]string{}, | ||||||
|  | 		LocalDirs:           map[string]string{}, | ||||||
|  | 		CacheExports:        cacheTo, | ||||||
|  | 		CacheImports:        cacheFrom, | ||||||
|  | 		AllowedEntitlements: opt.Allow, | ||||||
|  | 		SourcePolicy:        opt.SourcePolicy, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if so.Ref == "" { | ||||||
|  | 		so.Ref = identity.NewID() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if opt.CgroupParent != "" { | ||||||
|  | 		so.FrontendAttrs["cgroup-parent"] = opt.CgroupParent | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if v, ok := opt.BuildArgs["BUILDKIT_MULTI_PLATFORM"]; ok { | ||||||
|  | 		if v, _ := strconv.ParseBool(v); v { | ||||||
|  | 			so.FrontendAttrs["multi-platform"] = "true" | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if multiDriver { | ||||||
|  | 		// force creation of manifest list | ||||||
|  | 		so.FrontendAttrs["multi-platform"] = "true" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	attests := make(map[string]string) | ||||||
|  | 	for k, v := range opt.Attests { | ||||||
|  | 		if v != nil { | ||||||
|  | 			attests[k] = *v | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	supportAttestations := bopts.LLBCaps.Contains(apicaps.CapID("exporter.image.attestations")) && nodeDriver.Features(ctx)[driver.MultiPlatform] | ||||||
|  | 	if len(attests) > 0 { | ||||||
|  | 		if !supportAttestations { | ||||||
|  | 			if !nodeDriver.Features(ctx)[driver.MultiPlatform] { | ||||||
|  | 				return nil, nil, notSupported("Attestation", nodeDriver, "https://docs.docker.com/go/attestations/") | ||||||
|  | 			} | ||||||
|  | 			return nil, nil, errors.Errorf("Attestations are not supported by the current BuildKit daemon") | ||||||
|  | 		} | ||||||
|  | 		for k, v := range attests { | ||||||
|  | 			so.FrontendAttrs["attest:"+k] = v | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, ok := opt.Attests["provenance"]; !ok && supportAttestations { | ||||||
|  | 		const noAttestEnv = "BUILDX_NO_DEFAULT_ATTESTATIONS" | ||||||
|  | 		var noProv bool | ||||||
|  | 		if v, ok := os.LookupEnv(noAttestEnv); ok { | ||||||
|  | 			noProv, err = strconv.ParseBool(v) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, nil, errors.Wrap(err, "invalid "+noAttestEnv) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if !noProv { | ||||||
|  | 			so.FrontendAttrs["attest:provenance"] = "mode=min,inline-only=true" | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch len(opt.Exports) { | ||||||
|  | 	case 1: | ||||||
|  | 		// valid | ||||||
|  | 	case 0: | ||||||
|  | 		if nodeDriver.IsMobyDriver() && !noDefaultLoad() { | ||||||
|  | 			// backwards compat for docker driver only: | ||||||
|  | 			// this ensures the build results in a docker image. | ||||||
|  | 			opt.Exports = []client.ExportEntry{{Type: "image", Attrs: map[string]string{}}} | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		if err := bopts.LLBCaps.Supports(pb.CapMultipleExporters); err != nil { | ||||||
|  | 			return nil, nil, errors.Errorf("multiple outputs currently unsupported by the current BuildKit daemon, please upgrade to version v0.13+ or use a single output") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// fill in image exporter names from tags | ||||||
|  | 	if len(opt.Tags) > 0 { | ||||||
|  | 		tags := make([]string, len(opt.Tags)) | ||||||
|  | 		for i, tag := range opt.Tags { | ||||||
|  | 			ref, err := reference.Parse(tag) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, nil, errors.Wrapf(err, "invalid tag %q", tag) | ||||||
|  | 			} | ||||||
|  | 			tags[i] = ref.String() | ||||||
|  | 		} | ||||||
|  | 		for i, e := range opt.Exports { | ||||||
|  | 			switch e.Type { | ||||||
|  | 			case "image", "oci", "docker": | ||||||
|  | 				opt.Exports[i].Attrs["name"] = strings.Join(tags, ",") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		for _, e := range opt.Exports { | ||||||
|  | 			if e.Type == "image" && e.Attrs["name"] == "" && e.Attrs["push"] != "" { | ||||||
|  | 				if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok { | ||||||
|  | 					return nil, nil, errors.Errorf("tag is needed when pushing to registry") | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// cacheonly is a fake exporter to opt out of default behaviors | ||||||
|  | 	exports := make([]client.ExportEntry, 0, len(opt.Exports)) | ||||||
|  | 	for _, e := range opt.Exports { | ||||||
|  | 		if e.Type != "cacheonly" { | ||||||
|  | 			exports = append(exports, e) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	opt.Exports = exports | ||||||
|  |  | ||||||
|  | 	// set up exporters | ||||||
|  | 	for i, e := range opt.Exports { | ||||||
|  | 		if e.Type == "oci" && !nodeDriver.Features(ctx)[driver.OCIExporter] { | ||||||
|  | 			return nil, nil, notSupported(driver.OCIExporter, nodeDriver, "https://docs.docker.com/go/build-exporters/") | ||||||
|  | 		} | ||||||
|  | 		if e.Type == "docker" { | ||||||
|  | 			features := docker.Features(ctx, e.Attrs["context"]) | ||||||
|  | 			if features[dockerutil.OCIImporter] && e.Output == nil { | ||||||
|  | 				// rely on oci importer if available (which supports | ||||||
|  | 				// multi-platform images), otherwise fall back to docker | ||||||
|  | 				opt.Exports[i].Type = "oci" | ||||||
|  | 			} else if len(opt.Platforms) > 1 || len(attests) > 0 { | ||||||
|  | 				if e.Output != nil { | ||||||
|  | 					return nil, nil, errors.Errorf("docker exporter does not support exporting manifest lists, use the oci exporter instead") | ||||||
|  | 				} | ||||||
|  | 				return nil, nil, errors.Errorf("docker exporter does not currently support exporting manifest lists") | ||||||
|  | 			} | ||||||
|  | 			if e.Output == nil { | ||||||
|  | 				if nodeDriver.IsMobyDriver() { | ||||||
|  | 					e.Type = "image" | ||||||
|  | 				} else { | ||||||
|  | 					w, cancel, err := docker.LoadImage(ctx, e.Attrs["context"], pw) | ||||||
|  | 					if err != nil { | ||||||
|  | 						return nil, nil, err | ||||||
|  | 					} | ||||||
|  | 					defers = append(defers, cancel) | ||||||
|  | 					opt.Exports[i].Output = func(_ map[string]string) (io.WriteCloser, error) { | ||||||
|  | 						return w, nil | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} else if !nodeDriver.Features(ctx)[driver.DockerExporter] { | ||||||
|  | 				return nil, nil, notSupported(driver.DockerExporter, nodeDriver, "https://docs.docker.com/go/build-exporters/") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if e.Type == "image" && nodeDriver.IsMobyDriver() { | ||||||
|  | 			opt.Exports[i].Type = "moby" | ||||||
|  | 			if e.Attrs["push"] != "" { | ||||||
|  | 				if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok { | ||||||
|  | 					if ok, _ := strconv.ParseBool(e.Attrs["push-by-digest"]); ok { | ||||||
|  | 						return nil, nil, errors.Errorf("push-by-digest is currently not implemented for docker driver, please create a new builder instance") | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if e.Type == "docker" || e.Type == "image" || e.Type == "oci" { | ||||||
|  | 			// inline buildinfo attrs from build arg | ||||||
|  | 			if v, ok := opt.BuildArgs["BUILDKIT_INLINE_BUILDINFO_ATTRS"]; ok { | ||||||
|  | 				e.Attrs["buildinfo-attrs"] = v | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	so.Exports = opt.Exports | ||||||
|  | 	so.Session = opt.Session | ||||||
|  |  | ||||||
|  | 	releaseLoad, err := LoadInputs(ctx, nodeDriver, opt.Inputs, pw, &so) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	defers = append(defers, releaseLoad) | ||||||
|  |  | ||||||
|  | 	if sharedKey := so.LocalDirs["context"]; sharedKey != "" { | ||||||
|  | 		if p, err := filepath.Abs(sharedKey); err == nil { | ||||||
|  | 			sharedKey = filepath.Base(p) | ||||||
|  | 		} | ||||||
|  | 		so.SharedKey = sharedKey + ":" + confutil.TryNodeIdentifier(configDir) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if opt.Pull { | ||||||
|  | 		so.FrontendAttrs["image-resolve-mode"] = pb.AttrImageResolveModeForcePull | ||||||
|  | 	} else if nodeDriver.IsMobyDriver() { | ||||||
|  | 		// moby driver always resolves local images by default | ||||||
|  | 		so.FrontendAttrs["image-resolve-mode"] = pb.AttrImageResolveModePreferLocal | ||||||
|  | 	} | ||||||
|  | 	if opt.Target != "" { | ||||||
|  | 		so.FrontendAttrs["target"] = opt.Target | ||||||
|  | 	} | ||||||
|  | 	if len(opt.NoCacheFilter) > 0 { | ||||||
|  | 		so.FrontendAttrs["no-cache"] = strings.Join(opt.NoCacheFilter, ",") | ||||||
|  | 	} | ||||||
|  | 	if opt.NoCache { | ||||||
|  | 		so.FrontendAttrs["no-cache"] = "" | ||||||
|  | 	} | ||||||
|  | 	for k, v := range opt.BuildArgs { | ||||||
|  | 		so.FrontendAttrs["build-arg:"+k] = v | ||||||
|  | 	} | ||||||
|  | 	for k, v := range opt.Labels { | ||||||
|  | 		so.FrontendAttrs["label:"+k] = v | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for k, v := range node.ProxyConfig { | ||||||
|  | 		if _, ok := opt.BuildArgs[k]; !ok { | ||||||
|  | 			so.FrontendAttrs["build-arg:"+k] = v | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// set platforms | ||||||
|  | 	if len(opt.Platforms) != 0 { | ||||||
|  | 		pp := make([]string, len(opt.Platforms)) | ||||||
|  | 		for i, p := range opt.Platforms { | ||||||
|  | 			pp[i] = platforms.Format(p) | ||||||
|  | 		} | ||||||
|  | 		if len(pp) > 1 && !nodeDriver.Features(ctx)[driver.MultiPlatform] { | ||||||
|  | 			return nil, nil, notSupported(driver.MultiPlatform, nodeDriver, "https://docs.docker.com/go/build-multi-platform/") | ||||||
|  | 		} | ||||||
|  | 		so.FrontendAttrs["platform"] = strings.Join(pp, ",") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// setup networkmode | ||||||
|  | 	switch opt.NetworkMode { | ||||||
|  | 	case "host": | ||||||
|  | 		so.FrontendAttrs["force-network-mode"] = opt.NetworkMode | ||||||
|  | 		so.AllowedEntitlements = append(so.AllowedEntitlements, entitlements.EntitlementNetworkHost) | ||||||
|  | 	case "none": | ||||||
|  | 		so.FrontendAttrs["force-network-mode"] = opt.NetworkMode | ||||||
|  | 	case "", "default": | ||||||
|  | 	default: | ||||||
|  | 		return nil, nil, errors.Errorf("network mode %q not supported by buildkit - you can define a custom network for your builder using the network driver-opt in buildx create", opt.NetworkMode) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// setup extrahosts | ||||||
|  | 	extraHosts, err := toBuildkitExtraHosts(ctx, opt.ExtraHosts, nodeDriver) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} | ||||||
|  | 	if len(extraHosts) > 0 { | ||||||
|  | 		so.FrontendAttrs["add-hosts"] = extraHosts | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// setup shm size | ||||||
|  | 	if opt.ShmSize.Value() > 0 { | ||||||
|  | 		so.FrontendAttrs["shm-size"] = strconv.FormatInt(opt.ShmSize.Value(), 10) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// setup ulimits | ||||||
|  | 	ulimits, err := toBuildkitUlimits(opt.Ulimits) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, nil, err | ||||||
|  | 	} else if len(ulimits) > 0 { | ||||||
|  | 		so.FrontendAttrs["ulimit"] = ulimits | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &so, releaseF, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func LoadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, pw progress.Writer, target *client.SolveOpt) (func(), error) { | ||||||
|  | 	if inp.ContextPath == "" { | ||||||
|  | 		return nil, errors.New("please specify build context (e.g. \".\" for the current directory)") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// TODO: handle stdin, symlinks, remote contexts, check files exist | ||||||
|  |  | ||||||
|  | 	var ( | ||||||
|  | 		err              error | ||||||
|  | 		dockerfileReader io.Reader | ||||||
|  | 		dockerfileDir    string | ||||||
|  | 		dockerfileName   = inp.DockerfilePath | ||||||
|  | 		toRemove         []string | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	switch { | ||||||
|  | 	case inp.ContextState != nil: | ||||||
|  | 		if target.FrontendInputs == nil { | ||||||
|  | 			target.FrontendInputs = make(map[string]llb.State) | ||||||
|  | 		} | ||||||
|  | 		target.FrontendInputs["context"] = *inp.ContextState | ||||||
|  | 		target.FrontendInputs["dockerfile"] = *inp.ContextState | ||||||
|  | 	case inp.ContextPath == "-": | ||||||
|  | 		if inp.DockerfilePath == "-" { | ||||||
|  | 			return nil, errStdinConflict | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		buf := bufio.NewReader(inp.InStream) | ||||||
|  | 		magic, err := buf.Peek(archiveHeaderSize * 2) | ||||||
|  | 		if err != nil && err != io.EOF { | ||||||
|  | 			return nil, errors.Wrap(err, "failed to peek context header from STDIN") | ||||||
|  | 		} | ||||||
|  | 		if !(err == io.EOF && len(magic) == 0) { | ||||||
|  | 			if isArchive(magic) { | ||||||
|  | 				// stdin is context | ||||||
|  | 				up := uploadprovider.New() | ||||||
|  | 				target.FrontendAttrs["context"] = up.Add(buf) | ||||||
|  | 				target.Session = append(target.Session, up) | ||||||
|  | 			} else { | ||||||
|  | 				if inp.DockerfilePath != "" { | ||||||
|  | 					return nil, errDockerfileConflict | ||||||
|  | 				} | ||||||
|  | 				// stdin is dockerfile | ||||||
|  | 				dockerfileReader = buf | ||||||
|  | 				inp.ContextPath, _ = os.MkdirTemp("", "empty-dir") | ||||||
|  | 				toRemove = append(toRemove, inp.ContextPath) | ||||||
|  | 				target.LocalDirs["context"] = inp.ContextPath | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	case osutil.IsLocalDir(inp.ContextPath): | ||||||
|  | 		target.LocalDirs["context"] = inp.ContextPath | ||||||
|  | 		switch inp.DockerfilePath { | ||||||
|  | 		case "-": | ||||||
|  | 			dockerfileReader = inp.InStream | ||||||
|  | 		case "": | ||||||
|  | 			dockerfileDir = inp.ContextPath | ||||||
|  | 		default: | ||||||
|  | 			dockerfileDir = filepath.Dir(inp.DockerfilePath) | ||||||
|  | 			dockerfileName = filepath.Base(inp.DockerfilePath) | ||||||
|  | 		} | ||||||
|  | 	case IsRemoteURL(inp.ContextPath): | ||||||
|  | 		if inp.DockerfilePath == "-" { | ||||||
|  | 			dockerfileReader = inp.InStream | ||||||
|  | 		} else if filepath.IsAbs(inp.DockerfilePath) { | ||||||
|  | 			dockerfileDir = filepath.Dir(inp.DockerfilePath) | ||||||
|  | 			dockerfileName = filepath.Base(inp.DockerfilePath) | ||||||
|  | 			target.FrontendAttrs["dockerfilekey"] = "dockerfile" | ||||||
|  | 		} | ||||||
|  | 		target.FrontendAttrs["context"] = inp.ContextPath | ||||||
|  | 	default: | ||||||
|  | 		return nil, errors.Errorf("unable to prepare context: path %q not found", inp.ContextPath) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if inp.DockerfileInline != "" { | ||||||
|  | 		dockerfileReader = strings.NewReader(inp.DockerfileInline) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if dockerfileReader != nil { | ||||||
|  | 		dockerfileDir, err = createTempDockerfile(dockerfileReader) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		toRemove = append(toRemove, dockerfileDir) | ||||||
|  | 		dockerfileName = "Dockerfile" | ||||||
|  | 		target.FrontendAttrs["dockerfilekey"] = "dockerfile" | ||||||
|  | 	} | ||||||
|  | 	if urlutil.IsURL(inp.DockerfilePath) { | ||||||
|  | 		dockerfileDir, err = createTempDockerfileFromURL(ctx, d, inp.DockerfilePath, pw) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		toRemove = append(toRemove, dockerfileDir) | ||||||
|  | 		dockerfileName = "Dockerfile" | ||||||
|  | 		target.FrontendAttrs["dockerfilekey"] = "dockerfile" | ||||||
|  | 		delete(target.FrontendInputs, "dockerfile") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if dockerfileName == "" { | ||||||
|  | 		dockerfileName = "Dockerfile" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if dockerfileDir != "" { | ||||||
|  | 		target.LocalDirs["dockerfile"] = dockerfileDir | ||||||
|  | 		dockerfileName = handleLowercaseDockerfile(dockerfileDir, dockerfileName) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	target.FrontendAttrs["filename"] = dockerfileName | ||||||
|  |  | ||||||
|  | 	for k, v := range inp.NamedContexts { | ||||||
|  | 		target.FrontendAttrs["frontend.caps"] = "moby.buildkit.frontend.contexts+forward" | ||||||
|  | 		if v.State != nil { | ||||||
|  | 			target.FrontendAttrs["context:"+k] = "input:" + k | ||||||
|  | 			if target.FrontendInputs == nil { | ||||||
|  | 				target.FrontendInputs = make(map[string]llb.State) | ||||||
|  | 			} | ||||||
|  | 			target.FrontendInputs[k] = *v.State | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if IsRemoteURL(v.Path) || strings.HasPrefix(v.Path, "docker-image://") || strings.HasPrefix(v.Path, "target:") { | ||||||
|  | 			target.FrontendAttrs["context:"+k] = v.Path | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// handle OCI layout | ||||||
|  | 		if strings.HasPrefix(v.Path, "oci-layout://") { | ||||||
|  | 			pathAlone := strings.TrimPrefix(v.Path, "oci-layout://") | ||||||
|  | 			localPath := pathAlone | ||||||
|  | 			localPath, dig, hasDigest := strings.Cut(localPath, "@") | ||||||
|  | 			localPath, tag, hasTag := strings.Cut(localPath, ":") | ||||||
|  | 			if !hasTag { | ||||||
|  | 				tag = "latest" | ||||||
|  | 				hasTag = true | ||||||
|  | 			} | ||||||
|  | 			idx := ociindex.NewStoreIndex(localPath) | ||||||
|  | 			if !hasDigest { | ||||||
|  | 				// lookup by name | ||||||
|  | 				desc, err := idx.Get(tag) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				if desc != nil { | ||||||
|  | 					dig = string(desc.Digest) | ||||||
|  | 					hasDigest = true | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if !hasDigest { | ||||||
|  | 				// lookup single | ||||||
|  | 				desc, err := idx.GetSingle() | ||||||
|  | 				if err != nil { | ||||||
|  | 					return nil, err | ||||||
|  | 				} | ||||||
|  | 				if desc != nil { | ||||||
|  | 					dig = string(desc.Digest) | ||||||
|  | 					hasDigest = true | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if !hasDigest { | ||||||
|  | 				return nil, errors.Errorf("oci-layout reference %q could not be resolved", v.Path) | ||||||
|  | 			} | ||||||
|  | 			_, err := digest.Parse(dig) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, errors.Wrapf(err, "invalid oci-layout digest %s", dig) | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			store, err := local.NewStore(localPath) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, errors.Wrapf(err, "invalid store at %s", localPath) | ||||||
|  | 			} | ||||||
|  | 			storeName := identity.NewID() | ||||||
|  | 			if target.OCIStores == nil { | ||||||
|  | 				target.OCIStores = map[string]content.Store{} | ||||||
|  | 			} | ||||||
|  | 			target.OCIStores[storeName] = store | ||||||
|  |  | ||||||
|  | 			layout := "oci-layout://" + storeName | ||||||
|  | 			if hasTag { | ||||||
|  | 				layout += ":" + tag | ||||||
|  | 			} | ||||||
|  | 			if hasDigest { | ||||||
|  | 				layout += "@" + dig | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			target.FrontendAttrs["context:"+k] = layout | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		st, err := os.Stat(v.Path) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, errors.Wrapf(err, "failed to get build context %v", k) | ||||||
|  | 		} | ||||||
|  | 		if !st.IsDir() { | ||||||
|  | 			return nil, errors.Wrapf(syscall.ENOTDIR, "failed to get build context path %v", v) | ||||||
|  | 		} | ||||||
|  | 		localName := k | ||||||
|  | 		if k == "context" || k == "dockerfile" { | ||||||
|  | 			localName = "_" + k // underscore to avoid collisions | ||||||
|  | 		} | ||||||
|  | 		target.LocalDirs[localName] = v.Path | ||||||
|  | 		target.FrontendAttrs["context:"+k] = "local:" + localName | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	release := func() { | ||||||
|  | 		for _, dir := range toRemove { | ||||||
|  | 			os.RemoveAll(dir) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return release, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func createTempDockerfile(r io.Reader) (string, error) { | ||||||
|  | 	dir, err := os.MkdirTemp("", "dockerfile") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	f, err := os.Create(filepath.Join(dir, "Dockerfile")) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	defer f.Close() | ||||||
|  | 	if _, err := io.Copy(f, r); err != nil { | ||||||
|  | 		return "", err | ||||||
|  | 	} | ||||||
|  | 	return dir, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // handle https://github.com/moby/moby/pull/10858 | ||||||
|  | func handleLowercaseDockerfile(dir, p string) string { | ||||||
|  | 	if filepath.Base(p) != "Dockerfile" { | ||||||
|  | 		return p | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	f, err := os.Open(filepath.Dir(filepath.Join(dir, p))) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return p | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	names, err := f.Readdirnames(-1) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return p | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	foundLowerCase := false | ||||||
|  | 	for _, n := range names { | ||||||
|  | 		if n == "Dockerfile" { | ||||||
|  | 			return p | ||||||
|  | 		} | ||||||
|  | 		if n == "dockerfile" { | ||||||
|  | 			foundLowerCase = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if foundLowerCase { | ||||||
|  | 		return filepath.Join(filepath.Dir(p), "dockerfile") | ||||||
|  | 	} | ||||||
|  | 	return p | ||||||
|  | } | ||||||
| @@ -5,6 +5,8 @@ import ( | |||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"context" | 	"context" | ||||||
| 	"net" | 	"net" | ||||||
|  | 	"os" | ||||||
|  | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/docker/buildx/driver" | 	"github.com/docker/buildx/driver" | ||||||
| @@ -12,6 +14,7 @@ import ( | |||||||
| 	"github.com/docker/docker/builder/remotecontext/urlutil" | 	"github.com/docker/docker/builder/remotecontext/urlutil" | ||||||
| 	"github.com/moby/buildkit/util/gitutil" | 	"github.com/moby/buildkit/util/gitutil" | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
|  | 	"github.com/sirupsen/logrus" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -101,3 +104,21 @@ func toBuildkitUlimits(inp *opts.UlimitOpt) (string, error) { | |||||||
| 	} | 	} | ||||||
| 	return strings.Join(ulimits, ","), nil | 	return strings.Join(ulimits, ","), nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func notSupported(f driver.Feature, d driver.Driver, docs string) error { | ||||||
|  | 	return errors.Errorf(`%s is not supported for the %s driver. | ||||||
|  | Switch to a different driver, or turn on the containerd image store, and try again. | ||||||
|  | Learn more at %s`, f, d.Factory().Name(), docs) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func noDefaultLoad() bool { | ||||||
|  | 	v, ok := os.LookupEnv("BUILDX_NO_DEFAULT_LOAD") | ||||||
|  | 	if !ok { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	b, err := strconv.ParseBool(v) | ||||||
|  | 	if err != nil { | ||||||
|  | 		logrus.Warnf("invalid non-bool value for BUILDX_NO_DEFAULT_LOAD: %s", v) | ||||||
|  | 	} | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 CrazyMax
					CrazyMax