| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | package imagebuildah | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-04-12 22:20:36 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2020-05-19 20:20:14 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2024-02-13 03:11:59 +08:00
										 |  |  | 	gotypes "go/types" | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 	"os/exec" | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	"github.com/containerd/containerd/platforms" | 
					
						
							| 
									
										
										
										
											2023-11-08 06:27:39 +08:00
										 |  |  | 	"github.com/containers/buildah" | 
					
						
							| 
									
										
										
										
											2021-02-07 06:49:40 +08:00
										 |  |  | 	"github.com/containers/buildah/define" | 
					
						
							| 
									
										
										
										
											2022-09-26 14:39:06 +08:00
										 |  |  | 	internalUtil "github.com/containers/buildah/internal/util" | 
					
						
							| 
									
										
										
										
											2023-05-15 19:14:50 +08:00
										 |  |  | 	"github.com/containers/buildah/pkg/parse" | 
					
						
							| 
									
										
										
										
											2021-05-12 03:05:48 +08:00
										 |  |  | 	"github.com/containers/buildah/util" | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	"github.com/containers/common/libimage" | 
					
						
							| 
									
										
										
										
											2020-02-08 01:54:18 +08:00
										 |  |  | 	"github.com/containers/common/pkg/config" | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	"github.com/containers/image/v5/docker" | 
					
						
							| 
									
										
										
										
											2019-10-26 05:19:30 +08:00
										 |  |  | 	"github.com/containers/image/v5/docker/reference" | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	"github.com/containers/image/v5/manifest" | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	"github.com/containers/image/v5/pkg/shortnames" | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	istorage "github.com/containers/image/v5/storage" | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	"github.com/containers/image/v5/types" | 
					
						
							| 
									
										
										
										
											2017-05-17 23:53:28 +08:00
										 |  |  | 	"github.com/containers/storage" | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	"github.com/containers/storage/pkg/archive" | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	"github.com/hashicorp/go-multierror" | 
					
						
							| 
									
										
										
										
											2022-05-14 18:58:34 +08:00
										 |  |  | 	"github.com/mattn/go-shellwords" | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	v1 "github.com/opencontainers/image-spec/specs-go/v1" | 
					
						
							| 
									
										
										
										
											2019-10-02 04:03:57 +08:00
										 |  |  | 	specs "github.com/opencontainers/runtime-spec/specs-go" | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	"github.com/openshift/imagebuilder" | 
					
						
							| 
									
										
										
										
											2017-10-10 03:05:56 +08:00
										 |  |  | 	"github.com/sirupsen/logrus" | 
					
						
							| 
									
										
										
										
											2024-02-13 03:11:59 +08:00
										 |  |  | 	"golang.org/x/exp/slices" | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	"golang.org/x/sync/semaphore" | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-11 02:15:30 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2021-02-07 06:49:40 +08:00
										 |  |  | 	PullIfMissing = define.PullIfMissing | 
					
						
							|  |  |  | 	PullAlways    = define.PullAlways | 
					
						
							|  |  |  | 	PullIfNewer   = define.PullIfNewer | 
					
						
							|  |  |  | 	PullNever     = define.PullNever | 
					
						
							| 
									
										
										
										
											2017-04-11 22:27:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Gzip         = archive.Gzip | 
					
						
							|  |  |  | 	Bzip2        = archive.Bzip2 | 
					
						
							|  |  |  | 	Xz           = archive.Xz | 
					
						
							| 
									
										
										
										
											2019-07-09 05:50:33 +08:00
										 |  |  | 	Zstd         = archive.Zstd | 
					
						
							| 
									
										
										
										
											2017-04-11 22:27:05 +08:00
										 |  |  | 	Uncompressed = archive.Uncompressed | 
					
						
							| 
									
										
										
										
											2017-04-11 02:15:30 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-11 22:27:05 +08:00
										 |  |  | // Mount is a mountpoint for the build container.
 | 
					
						
							| 
									
										
										
										
											2021-07-21 04:23:25 +08:00
										 |  |  | type Mount = specs.Mount | 
					
						
							| 
									
										
										
										
											2017-04-11 22:27:05 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-02 02:07:58 +08:00
										 |  |  | type BuildOptions = define.BuildOptions | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // BuildDockerfiles parses a set of one or more Dockerfiles (which may be
 | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | // URLs), creates one or more new Executors, and then runs
 | 
					
						
							|  |  |  | // Prepare/Execute/Commit/Delete over the entire set of instructions.
 | 
					
						
							|  |  |  | // If the Manifest option is set, returns the ID of the manifest list, else it
 | 
					
						
							|  |  |  | // returns the ID of the built image, and if a name was assigned to it, a
 | 
					
						
							|  |  |  | // canonical reference for that image.
 | 
					
						
							|  |  |  | func BuildDockerfiles(ctx context.Context, store storage.Store, options define.BuildOptions, paths ...string) (id string, ref reference.Canonical, err error) { | 
					
						
							| 
									
										
										
										
											2021-10-14 22:58:24 +08:00
										 |  |  | 	if options.CommonBuildOpts == nil { | 
					
						
							|  |  |  | 		options.CommonBuildOpts = &define.CommonBuildOptions{} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-05-15 19:14:50 +08:00
										 |  |  | 	if err := parse.Volumes(options.CommonBuildOpts.Volumes); err != nil { | 
					
						
							|  |  |  | 		return "", nil, fmt.Errorf("validating volumes: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-11 01:35:03 +08:00
										 |  |  | 	if len(paths) == 0 { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return "", nil, errors.New("building: no dockerfiles specified") | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	if len(options.Platforms) > 1 && options.IIDFile != "" { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 		return "", nil, fmt.Errorf("building multiple images, but iidfile %q can only be used to store one image ID", options.IIDFile) | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-08 01:38:44 +08:00
										 |  |  | 	logger := logrus.New() | 
					
						
							|  |  |  | 	if options.Err != nil { | 
					
						
							|  |  |  | 		logger.SetOutput(options.Err) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		logger.SetOutput(os.Stderr) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	logger.SetLevel(logrus.GetLevel()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 06:40:44 +08:00
										 |  |  | 	var dockerfiles []io.ReadCloser | 
					
						
							|  |  |  | 	defer func(dockerfiles ...io.ReadCloser) { | 
					
						
							|  |  |  | 		for _, d := range dockerfiles { | 
					
						
							|  |  |  | 			d.Close() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}(dockerfiles...) | 
					
						
							| 
									
										
										
										
											2018-09-22 07:37:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 03:05:48 +08:00
										 |  |  | 	for _, tag := range append([]string{options.Output}, options.AdditionalTags...) { | 
					
						
							|  |  |  | 		if tag == "" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if _, err := util.VerifyTagName(tag); err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 			return "", nil, fmt.Errorf("tag %s: %w", tag, err) | 
					
						
							| 
									
										
										
										
											2021-05-12 03:05:48 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-11 01:35:03 +08:00
										 |  |  | 	for _, dfile := range paths { | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 		var data io.ReadCloser | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 		if strings.HasPrefix(dfile, "http://") || strings.HasPrefix(dfile, "https://") { | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 			logger.Debugf("reading remote Dockerfile %q", dfile) | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 			resp, err := http.Get(dfile) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2020-10-15 17:16:50 +08:00
										 |  |  | 				return "", nil, err | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			if resp.ContentLength == 0 { | 
					
						
							|  |  |  | 				resp.Body.Close() | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 				return "", nil, fmt.Errorf("no contents in %q", dfile) | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 			data = resp.Body | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2018-10-28 03:47:03 +08:00
										 |  |  | 			dinfo, err := os.Stat(dfile) | 
					
						
							| 
									
										
										
										
											2020-11-09 11:14:56 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				// If the Dockerfile isn't available, try again with
 | 
					
						
							|  |  |  | 				// context directory prepended (if not prepended yet).
 | 
					
						
							| 
									
										
										
										
											2020-10-13 09:30:01 +08:00
										 |  |  | 				if !strings.HasPrefix(dfile, options.ContextDirectory) { | 
					
						
							|  |  |  | 					dfile = filepath.Join(options.ContextDirectory, dfile) | 
					
						
							| 
									
										
										
										
											2020-11-09 11:14:56 +08:00
										 |  |  | 					dinfo, err = os.Stat(dfile) | 
					
						
							| 
									
										
										
										
											2020-10-13 09:30:01 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-10-28 03:47:03 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-09 11:14:56 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return "", nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-15 19:52:22 +08:00
										 |  |  | 			var contents *os.File | 
					
						
							| 
									
										
										
										
											2022-07-07 13:33:04 +08:00
										 |  |  | 			// If given a directory error out since `-f` does not supports path to directory
 | 
					
						
							| 
									
										
										
										
											2018-10-28 03:47:03 +08:00
										 |  |  | 			if dinfo.Mode().IsDir() { | 
					
						
							| 
									
										
										
										
											2022-07-07 13:33:04 +08:00
										 |  |  | 				return "", nil, fmt.Errorf("containerfile: %q cannot be path to a directory", dfile) | 
					
						
							| 
									
										
										
										
											2018-10-28 03:47:03 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-07-07 13:33:04 +08:00
										 |  |  | 			contents, err = os.Open(dfile) | 
					
						
							| 
									
										
										
										
											2018-02-24 18:09:25 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2020-10-15 17:16:50 +08:00
										 |  |  | 				return "", nil, err | 
					
						
							| 
									
										
										
										
											2018-02-24 18:09:25 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-10-28 03:47:03 +08:00
										 |  |  | 			dinfo, err = contents.Stat() | 
					
						
							| 
									
										
										
										
											2018-02-24 18:09:25 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				contents.Close() | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 				return "", nil, fmt.Errorf("reading info about %q: %w", dfile, err) | 
					
						
							| 
									
										
										
										
											2018-02-24 18:09:25 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-07-27 22:48:16 +08:00
										 |  |  | 			if dinfo.Mode().IsRegular() && dinfo.Size() == 0 { | 
					
						
							| 
									
										
										
										
											2018-02-24 18:09:25 +08:00
										 |  |  | 				contents.Close() | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 				return "", nil, fmt.Errorf("no contents in %q", dfile) | 
					
						
							| 
									
										
										
										
											2018-02-24 18:09:25 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 			data = contents | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// pre-process Dockerfiles with ".in" suffix
 | 
					
						
							|  |  |  | 		if strings.HasSuffix(dfile, ".in") { | 
					
						
							| 
									
										
										
										
											2022-05-14 18:58:34 +08:00
										 |  |  | 			pData, err := preprocessContainerfileContents(logger, dfile, data, options.ContextDirectory, options.CPPFlags) | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2018-10-12 04:58:04 +08:00
										 |  |  | 				return "", nil, err | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-11-15 00:22:45 +08:00
										 |  |  | 			data = io.NopCloser(pData) | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		dockerfiles = append(dockerfiles, data) | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-17 06:43:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	var files [][]byte | 
					
						
							|  |  |  | 	for _, dockerfile := range dockerfiles { | 
					
						
							|  |  |  | 		var b bytes.Buffer | 
					
						
							|  |  |  | 		if _, err := b.ReadFrom(dockerfile); err != nil { | 
					
						
							|  |  |  | 			return "", nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		files = append(files, b.Bytes()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-28 07:08:53 +08:00
										 |  |  | 	if options.JobSemaphore == nil { | 
					
						
							|  |  |  | 		if options.Jobs != nil { | 
					
						
							|  |  |  | 			if *options.Jobs < 0 { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 				return "", nil, errors.New("building: invalid value for jobs.  It must be a positive integer") | 
					
						
							| 
									
										
										
										
											2022-01-28 07:08:53 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			if *options.Jobs > 0 { | 
					
						
							|  |  |  | 				options.JobSemaphore = semaphore.NewWeighted(int64(*options.Jobs)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			options.JobSemaphore = semaphore.NewWeighted(1) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	manifestList := options.Manifest | 
					
						
							|  |  |  | 	options.Manifest = "" | 
					
						
							|  |  |  | 	type instance struct { | 
					
						
							|  |  |  | 		v1.Platform | 
					
						
							| 
									
										
										
										
											2022-07-18 19:12:57 +08:00
										 |  |  | 		ID  string | 
					
						
							|  |  |  | 		Ref reference.Canonical | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	var instances []instance | 
					
						
							|  |  |  | 	var instancesLock sync.Mutex | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var builds multierror.Group | 
					
						
							|  |  |  | 	if options.SystemContext == nil { | 
					
						
							|  |  |  | 		options.SystemContext = &types.SystemContext{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(options.Platforms) == 0 { | 
					
						
							|  |  |  | 		options.Platforms = append(options.Platforms, struct{ OS, Arch, Variant string }{ | 
					
						
							|  |  |  | 			OS:   options.SystemContext.OSChoice, | 
					
						
							|  |  |  | 			Arch: options.SystemContext.ArchitectureChoice, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	if options.AllPlatforms { | 
					
						
							| 
									
										
										
										
											2022-05-10 18:11:37 +08:00
										 |  |  | 		if options.AdditionalBuildContexts == nil { | 
					
						
							|  |  |  | 			options.AdditionalBuildContexts = make(map[string]*define.AdditionalBuildContext) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		options.Platforms, err = platformsForBaseImages(ctx, logger, paths, files, options.From, options.Args, options.AdditionalBuildContexts, options.SystemContext) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	systemContext := options.SystemContext | 
					
						
							|  |  |  | 	for _, platform := range options.Platforms { | 
					
						
							|  |  |  | 		platformContext := *systemContext | 
					
						
							| 
									
										
										
										
											2022-09-26 14:39:06 +08:00
										 |  |  | 		platformSpec := internalUtil.NormalizePlatform(v1.Platform{ | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 			OS:           platform.OS, | 
					
						
							|  |  |  | 			Architecture: platform.Arch, | 
					
						
							|  |  |  | 			Variant:      platform.Variant, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2022-09-26 14:39:06 +08:00
										 |  |  | 		// internalUtil.NormalizePlatform converts an empty os value to GOOS
 | 
					
						
							| 
									
										
										
										
											2022-01-10 22:27:26 +08:00
										 |  |  | 		// so we have to check the original value here to not overwrite the default for no reason
 | 
					
						
							|  |  |  | 		if platform.OS != "" { | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 			platformContext.OSChoice = platformSpec.OS | 
					
						
							| 
									
										
										
										
											2022-01-10 22:27:26 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if platform.Arch != "" { | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 			platformContext.ArchitectureChoice = platformSpec.Architecture | 
					
						
							|  |  |  | 			platformContext.VariantChoice = platformSpec.Variant | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		platformOptions := options | 
					
						
							|  |  |  | 		platformOptions.SystemContext = &platformContext | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 		platformOptions.OS = platformContext.OSChoice | 
					
						
							|  |  |  | 		platformOptions.Architecture = platformContext.ArchitectureChoice | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		logPrefix := "" | 
					
						
							|  |  |  | 		if len(options.Platforms) > 1 { | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 			logPrefix = "[" + platforms.Format(platformSpec) + "] " | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-05-04 12:46:07 +08:00
										 |  |  | 		// Deep copy args to prevent concurrent read/writes over Args.
 | 
					
						
							|  |  |  | 		argsCopy := make(map[string]string) | 
					
						
							|  |  |  | 		for key, value := range options.Args { | 
					
						
							|  |  |  | 			argsCopy[key] = value | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		platformOptions.Args = argsCopy | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		builds.Go(func() error { | 
					
						
							| 
									
										
										
										
											2022-06-06 14:33:15 +08:00
										 |  |  | 			loggerPerPlatform := logger | 
					
						
							|  |  |  | 			if platformOptions.LogFile != "" && platformOptions.LogSplitByPlatform { | 
					
						
							|  |  |  | 				logFile := platformOptions.LogFile + "_" + platformOptions.OS + "_" + platformOptions.Architecture | 
					
						
							| 
									
										
										
										
											2022-09-27 10:31:20 +08:00
										 |  |  | 				f, err := os.OpenFile(logFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600) | 
					
						
							| 
									
										
										
										
											2022-06-06 14:33:15 +08:00
										 |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 					return fmt.Errorf("opening logfile: %q: %w", logFile, err) | 
					
						
							| 
									
										
										
										
											2022-06-06 14:33:15 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				defer f.Close() | 
					
						
							|  |  |  | 				loggerPerPlatform = logrus.New() | 
					
						
							|  |  |  | 				loggerPerPlatform.SetOutput(f) | 
					
						
							|  |  |  | 				loggerPerPlatform.SetLevel(logrus.GetLevel()) | 
					
						
							|  |  |  | 				stdout := f | 
					
						
							|  |  |  | 				stderr := f | 
					
						
							|  |  |  | 				reporter := f | 
					
						
							|  |  |  | 				platformOptions.Out = stdout | 
					
						
							|  |  |  | 				platformOptions.ReportWriter = reporter | 
					
						
							|  |  |  | 				platformOptions.Err = stderr | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			thisID, thisRef, err := buildDockerfilesOnce(ctx, store, loggerPerPlatform, logPrefix, platformOptions, paths, files) | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2023-10-28 02:32:27 +08:00
										 |  |  | 				if errorContext := strings.TrimSpace(logPrefix); errorContext != "" { | 
					
						
							|  |  |  | 					return fmt.Errorf("%s: %w", errorContext, err) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 			instancesLock.Lock() | 
					
						
							|  |  |  | 			instances = append(instances, instance{ | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 				ID:       thisID, | 
					
						
							| 
									
										
										
										
											2022-07-18 19:12:57 +08:00
										 |  |  | 				Ref:      thisRef, | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 				Platform: platformSpec, | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 			}) | 
					
						
							|  |  |  | 			instancesLock.Unlock() | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if merr := builds.Wait(); merr != nil { | 
					
						
							|  |  |  | 		if merr.Len() == 1 { | 
					
						
							|  |  |  | 			return "", nil, merr.Errors[0] | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return "", nil, merr.ErrorOrNil() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 18:15:48 +08:00
										 |  |  | 	// Reasons for this id, ref assignment w.r.t to use-case:
 | 
					
						
							| 
									
										
										
										
											2022-07-18 19:12:57 +08:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// * Single-platform build: On single platform build we only
 | 
					
						
							|  |  |  | 	//   have one built instance i.e on indice 0 of built instances,
 | 
					
						
							|  |  |  | 	//   so assign that.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// * Multi-platform build with manifestList: If this is a build for
 | 
					
						
							|  |  |  | 	//   multiple platforms ( more than one platform ) and --manifest
 | 
					
						
							|  |  |  | 	//   option then this assignment is insignificant since it will be
 | 
					
						
							| 
									
										
										
										
											2022-09-27 18:15:48 +08:00
										 |  |  | 	//   overridden anyways with the id and ref of manifest list later in
 | 
					
						
							| 
									
										
										
										
											2022-07-18 19:12:57 +08:00
										 |  |  | 	//   in this code.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// * Multi-platform build without manifest list: If this is a build for
 | 
					
						
							|  |  |  | 	//   multiple platforms without --manifest then we are free to return
 | 
					
						
							|  |  |  | 	//   id and ref of any one of the image in the instance list so always
 | 
					
						
							|  |  |  | 	//   return indice 0 for predictable output instead returning the id and
 | 
					
						
							|  |  |  | 	//   ref of the go routine which completed at last.
 | 
					
						
							|  |  |  | 	id, ref = instances[0].ID, instances[0].Ref | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	if manifestList != "" { | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		rt, err := libimage.RuntimeFromStore(store, nil) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		// Create the manifest list ourselves, so that it's not in a
 | 
					
						
							|  |  |  | 		// partially-populated state at any point if we're creating it
 | 
					
						
							|  |  |  | 		// fresh.
 | 
					
						
							|  |  |  | 		list, err := rt.LookupManifestList(manifestList) | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 		if err != nil && errors.Is(err, storage.ErrImageUnknown) { | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 			list, err = rt.CreateManifestList(manifestList) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		// Add each instance to the list in turn.
 | 
					
						
							|  |  |  | 		storeTransportName := istorage.Transport.Name() | 
					
						
							|  |  |  | 		for _, instance := range instances { | 
					
						
							|  |  |  | 			instanceDigest, err := list.Add(ctx, storeTransportName+":"+instance.ID, nil) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return "", nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			err = list.AnnotateInstance(instanceDigest, &libimage.ManifestListAnnotateOptions{ | 
					
						
							|  |  |  | 				Architecture: instance.Architecture, | 
					
						
							|  |  |  | 				OS:           instance.OS, | 
					
						
							|  |  |  | 				Variant:      instance.Variant, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return "", nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		id, ref = list.ID(), nil | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		// Put together a canonical reference
 | 
					
						
							|  |  |  | 		storeRef, err := istorage.Transport.NewStoreReference(store, nil, list.ID()) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		imgSource, err := storeRef.NewImageSource(ctx, nil) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		defer imgSource.Close() | 
					
						
							|  |  |  | 		manifestBytes, _, err := imgSource.GetManifest(ctx, nil) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		manifestDigest, err := manifest.Digest(manifestBytes) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		img, err := store.Image(id) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for _, name := range img.Names { | 
					
						
							|  |  |  | 			if named, err := reference.ParseNamed(name); err == nil { | 
					
						
							|  |  |  | 				if r, err := reference.WithDigest(reference.TrimNamed(named), manifestDigest); err == nil { | 
					
						
							|  |  |  | 					ref = r | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	return id, ref, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-13 14:35:34 +08:00
										 |  |  | func buildDockerfilesOnce(ctx context.Context, store storage.Store, logger *logrus.Logger, logPrefix string, options define.BuildOptions, containerFiles []string, dockerfilecontents [][]byte) (string, reference.Canonical, error) { | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	mainNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(dockerfilecontents[0])) | 
					
						
							| 
									
										
										
										
											2018-02-25 06:40:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return "", nil, fmt.Errorf("parsing main Dockerfile: %s: %w", containerFiles[0], err) | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-05-19 20:20:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 02:32:36 +08:00
										 |  |  | 	// --platform was explicitly selected for this build
 | 
					
						
							|  |  |  | 	// so set correct TARGETPLATFORM in args if it is not
 | 
					
						
							|  |  |  | 	// already selected by the user.
 | 
					
						
							|  |  |  | 	if options.SystemContext.OSChoice != "" && options.SystemContext.ArchitectureChoice != "" { | 
					
						
							|  |  |  | 		// os component from --platform string populates TARGETOS
 | 
					
						
							|  |  |  | 		// buildkit parity: give priority to user's `--build-arg`
 | 
					
						
							|  |  |  | 		if _, ok := options.Args["TARGETOS"]; !ok { | 
					
						
							|  |  |  | 			options.Args["TARGETOS"] = options.SystemContext.OSChoice | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// arch component from --platform string populates TARGETARCH
 | 
					
						
							|  |  |  | 		// buildkit parity: give priority to user's `--build-arg`
 | 
					
						
							|  |  |  | 		if _, ok := options.Args["TARGETARCH"]; !ok { | 
					
						
							|  |  |  | 			options.Args["TARGETARCH"] = options.SystemContext.ArchitectureChoice | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// variant component from --platform string populates TARGETVARIANT
 | 
					
						
							|  |  |  | 		// buildkit parity: give priority to user's `--build-arg`
 | 
					
						
							|  |  |  | 		if _, ok := options.Args["TARGETVARIANT"]; !ok { | 
					
						
							|  |  |  | 			if options.SystemContext.VariantChoice != "" { | 
					
						
							|  |  |  | 				options.Args["TARGETVARIANT"] = options.SystemContext.VariantChoice | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// buildkit parity: give priority to user's `--build-arg`
 | 
					
						
							|  |  |  | 		if _, ok := options.Args["TARGETPLATFORM"]; !ok { | 
					
						
							|  |  |  | 			// buildkit parity: TARGETPLATFORM should be always created
 | 
					
						
							|  |  |  | 			// from SystemContext and not `TARGETOS` and `TARGETARCH` because
 | 
					
						
							|  |  |  | 			// users can always override values of `TARGETOS` and `TARGETARCH`
 | 
					
						
							|  |  |  | 			// but `TARGETPLATFORM` should be set independent of those values.
 | 
					
						
							|  |  |  | 			options.Args["TARGETPLATFORM"] = options.SystemContext.OSChoice + "/" + options.SystemContext.ArchitectureChoice | 
					
						
							|  |  |  | 			if options.SystemContext.VariantChoice != "" { | 
					
						
							|  |  |  | 				options.Args["TARGETPLATFORM"] = options.Args["TARGETPLATFORM"] + "/" + options.SystemContext.VariantChoice | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	for i, d := range dockerfilecontents[1:] { | 
					
						
							|  |  |  | 		additionalNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(d)) | 
					
						
							| 
									
										
										
										
											2018-02-25 06:40:44 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-13 14:35:34 +08:00
										 |  |  | 			containerFiles := containerFiles[1:] | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 			return "", nil, fmt.Errorf("parsing additional Dockerfile %s: %w", containerFiles[i], err) | 
					
						
							| 
									
										
										
										
											2018-02-25 06:40:44 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		mainNode.Children = append(mainNode.Children, additionalNode.Children...) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-14 18:44:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-13 14:35:34 +08:00
										 |  |  | 	exec, err := newExecutor(logger, logPrefix, store, options, mainNode, containerFiles) | 
					
						
							| 
									
										
										
										
											2018-02-25 06:40:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return "", nil, fmt.Errorf("creating build executor: %w", err) | 
					
						
							| 
									
										
										
										
											2018-02-25 06:40:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	b := imagebuilder.NewBuilder(options.Args) | 
					
						
							| 
									
										
										
										
											2020-02-08 01:54:18 +08:00
										 |  |  | 	defaultContainerConfig, err := config.Default() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 		return "", nil, fmt.Errorf("failed to get container config: %w", err) | 
					
						
							| 
									
										
										
										
											2020-02-08 01:54:18 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	b.Env = append(defaultContainerConfig.GetDefaultEnv(), b.Env...) | 
					
						
							| 
									
										
										
										
											2018-11-08 18:31:14 +08:00
										 |  |  | 	stages, err := imagebuilder.NewStages(mainNode, b) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return "", nil, fmt.Errorf("reading multiple stages: %w", err) | 
					
						
							| 
									
										
										
										
											2018-11-08 18:31:14 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-03 07:31:44 +08:00
										 |  |  | 	if options.Target != "" { | 
					
						
							|  |  |  | 		stagesTargeted, ok := stages.ThroughTarget(options.Target) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 			return "", nil, fmt.Errorf("The target %q was not found in the provided Dockerfile", options.Target) | 
					
						
							| 
									
										
										
										
											2019-02-03 07:31:44 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		stages = stagesTargeted | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-04-12 22:20:36 +08:00
										 |  |  | 	return exec.Build(ctx, stages) | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-08-01 00:02:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 21:05:47 +08:00
										 |  |  | // preprocessContainerfileContents runs CPP(1) in preprocess-only mode on the input
 | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | // dockerfile content and will use ctxDir as the base include path.
 | 
					
						
							| 
									
										
										
										
											2022-05-14 18:58:34 +08:00
										 |  |  | func preprocessContainerfileContents(logger *logrus.Logger, containerfile string, r io.Reader, ctxDir string, cppFlags []string) (stdout io.Reader, err error) { | 
					
						
							| 
									
										
										
										
											2021-06-10 01:23:12 +08:00
										 |  |  | 	cppCommand := "cpp" | 
					
						
							|  |  |  | 	cppPath, err := exec.LookPath(cppCommand) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-03-17 16:44:31 +08:00
										 |  |  | 		if errors.Is(err, exec.ErrNotFound) { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 			err = fmt.Errorf("%v: .in support requires %s to be installed", err, cppCommand) | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-10 01:23:12 +08:00
										 |  |  | 	stdoutBuffer := bytes.Buffer{} | 
					
						
							|  |  |  | 	stderrBuffer := bytes.Buffer{} | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-14 18:58:34 +08:00
										 |  |  | 	cppArgs := []string{"-E", "-iquote", ctxDir, "-traditional", "-undef", "-"} | 
					
						
							|  |  |  | 	if flags, ok := os.LookupEnv("BUILDAH_CPPFLAGS"); ok { | 
					
						
							|  |  |  | 		args, err := shellwords.Parse(flags) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 			return nil, fmt.Errorf("parsing BUILDAH_CPPFLAGS %q: %v", flags, err) | 
					
						
							| 
									
										
										
										
											2022-05-14 18:58:34 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		cppArgs = append(cppArgs, args...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cppArgs = append(cppArgs, cppFlags...) | 
					
						
							|  |  |  | 	cmd := exec.Command(cppPath, cppArgs...) | 
					
						
							| 
									
										
										
										
											2021-06-10 01:23:12 +08:00
										 |  |  | 	cmd.Stdin = r | 
					
						
							|  |  |  | 	cmd.Stdout = &stdoutBuffer | 
					
						
							|  |  |  | 	cmd.Stderr = &stderrBuffer | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if err = cmd.Start(); err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 		return nil, fmt.Errorf("preprocessing %s: %w", containerfile, err) | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if err = cmd.Wait(); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-10 01:23:12 +08:00
										 |  |  | 		if stderrBuffer.Len() != 0 { | 
					
						
							|  |  |  | 			logger.Warnf("Ignoring %s\n", stderrBuffer.String()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if stdoutBuffer.Len() == 0 { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 			return nil, fmt.Errorf("preprocessing %s: preprocessor produced no output: %w", containerfile, err) | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-10 01:23:12 +08:00
										 |  |  | 	return &stdoutBuffer, nil | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-13 03:11:59 +08:00
										 |  |  | // platformIsUnknown checks if the platform value indicates that the
 | 
					
						
							|  |  |  | // corresponding index entry is suitable for use as a base image
 | 
					
						
							|  |  |  | func platformIsAcceptable(platform *v1.Platform, logger *logrus.Logger) bool { | 
					
						
							|  |  |  | 	if platform == nil { | 
					
						
							|  |  |  | 		logger.Trace("rejecting potential base image with no platform information") | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if gotypes.SizesFor("gc", platform.Architecture) == nil { | 
					
						
							|  |  |  | 		// the compiler's never heard of this
 | 
					
						
							|  |  |  | 		logger.Tracef("rejecting potential base image architecture %q for which Go has no knowledge of how to do unsafe code", platform.Architecture) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if slices.Contains([]string{"", "unknown"}, platform.OS) { | 
					
						
							|  |  |  | 		// we're hard-wired to reject images with these values
 | 
					
						
							|  |  |  | 		logger.Tracef("rejecting potential base image for which the OS value is always-rejected value %q", platform.OS) | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | // platformsForBaseImages resolves the names of base images from the
 | 
					
						
							|  |  |  | // dockerfiles, and if they are all valid references to manifest lists, returns
 | 
					
						
							|  |  |  | // the list of platforms that are supported by all of the base images.
 | 
					
						
							| 
									
										
										
										
											2022-05-10 18:11:37 +08:00
										 |  |  | func platformsForBaseImages(ctx context.Context, logger *logrus.Logger, dockerfilepaths []string, dockerfiles [][]byte, from string, args map[string]string, additionalBuildContext map[string]*define.AdditionalBuildContext, systemContext *types.SystemContext) ([]struct{ OS, Arch, Variant string }, error) { | 
					
						
							|  |  |  | 	baseImages, err := baseImages(dockerfilepaths, dockerfiles, from, args, additionalBuildContext) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 		return nil, fmt.Errorf("determining list of base images: %w", err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	logrus.Debugf("unresolved base images: %v", baseImages) | 
					
						
							|  |  |  | 	if len(baseImages) == 0 { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 		return nil, fmt.Errorf("build uses no non-scratch base images: %w", err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	targetPlatforms := make(map[string]struct{}) | 
					
						
							|  |  |  | 	var platformList []struct{ OS, Arch, Variant string } | 
					
						
							|  |  |  | 	for baseImageIndex, baseImage := range baseImages { | 
					
						
							|  |  |  | 		resolved, err := shortnames.Resolve(systemContext, baseImage) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 			return nil, fmt.Errorf("resolving image name %q: %w", baseImage, err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		var manifestBytes []byte | 
					
						
							|  |  |  | 		var manifestType string | 
					
						
							|  |  |  | 		for _, candidate := range resolved.PullCandidates { | 
					
						
							|  |  |  | 			ref, err := docker.NewReference(candidate.Value) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				logrus.Debugf("parsing image reference %q: %v", candidate.Value.String(), err) | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			src, err := ref.NewImageSource(ctx, systemContext) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				logrus.Debugf("preparing to read image manifest for %q: %v", baseImage, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			candidateBytes, candidateType, err := src.GetManifest(ctx, nil) | 
					
						
							|  |  |  | 			_ = src.Close() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				logrus.Debugf("reading image manifest for %q: %v", baseImage, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if !manifest.MIMETypeIsMultiImage(candidateType) { | 
					
						
							|  |  |  | 				logrus.Debugf("base image %q is not a reference to a manifest list: %v", baseImage, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if err := candidate.Record(); err != nil { | 
					
						
							|  |  |  | 				logrus.Debugf("error recording name %q for base image %q: %v", candidate.Value.String(), baseImage, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			baseImage = candidate.Value.String() | 
					
						
							|  |  |  | 			manifestBytes, manifestType = candidateBytes, candidateType | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(manifestBytes) == 0 { | 
					
						
							|  |  |  | 			if len(resolved.PullCandidates) > 0 { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 				return nil, fmt.Errorf("base image name %q didn't resolve to a manifest list", baseImage) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 			return nil, fmt.Errorf("base image name %q didn't resolve to anything", baseImage) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if manifestType != v1.MediaTypeImageIndex { | 
					
						
							|  |  |  | 			list, err := manifest.ListFromBlob(manifestBytes, manifestType) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 				return nil, fmt.Errorf("parsing manifest list from base image %q: %w", baseImage, err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			list, err = list.ConvertToMIMEType(v1.MediaTypeImageIndex) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 				return nil, fmt.Errorf("converting manifest list from base image %q to v2s2 list: %w", baseImage, err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			manifestBytes, err = list.Serialize() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 				return nil, fmt.Errorf("encoding converted v2s2 manifest list for base image %q: %w", baseImage, err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		index, err := manifest.OCI1IndexFromManifest(manifestBytes) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 			return nil, fmt.Errorf("decoding manifest list for base image %q: %w", baseImage, err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if baseImageIndex == 0 { | 
					
						
							|  |  |  | 			// populate the list with the first image's normalized platforms
 | 
					
						
							|  |  |  | 			for _, instance := range index.Manifests { | 
					
						
							| 
									
										
										
										
											2024-02-13 03:11:59 +08:00
										 |  |  | 				if !platformIsAcceptable(instance.Platform, logger) { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if instance.ArtifactType != "" { | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-09-26 14:39:06 +08:00
										 |  |  | 				platform := internalUtil.NormalizePlatform(*instance.Platform) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 				targetPlatforms[platforms.Format(platform)] = struct{}{} | 
					
						
							|  |  |  | 				logger.Debugf("image %q supports %q", baseImage, platforms.Format(platform)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// prune the list of any normalized platforms this base image doesn't support
 | 
					
						
							|  |  |  | 			imagePlatforms := make(map[string]struct{}) | 
					
						
							|  |  |  | 			for _, instance := range index.Manifests { | 
					
						
							| 
									
										
										
										
											2024-02-13 03:11:59 +08:00
										 |  |  | 				if !platformIsAcceptable(instance.Platform, logger) { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if instance.ArtifactType != "" { | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-09-26 14:39:06 +08:00
										 |  |  | 				platform := internalUtil.NormalizePlatform(*instance.Platform) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 				imagePlatforms[platforms.Format(platform)] = struct{}{} | 
					
						
							|  |  |  | 				logger.Debugf("image %q supports %q", baseImage, platforms.Format(platform)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			var removed []string | 
					
						
							|  |  |  | 			for platform := range targetPlatforms { | 
					
						
							|  |  |  | 				if _, present := imagePlatforms[platform]; !present { | 
					
						
							|  |  |  | 					removed = append(removed, platform) | 
					
						
							|  |  |  | 					logger.Debugf("image %q does not support %q", baseImage, platform) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for _, remove := range removed { | 
					
						
							|  |  |  | 				delete(targetPlatforms, remove) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if baseImageIndex == len(baseImages)-1 && len(targetPlatforms) > 0 { | 
					
						
							|  |  |  | 			// extract the list
 | 
					
						
							|  |  |  | 			for platform := range targetPlatforms { | 
					
						
							|  |  |  | 				platform, err := platforms.Parse(platform) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 					return nil, fmt.Errorf("parsing platform double/triple %q: %w", platform, err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				platformList = append(platformList, struct{ OS, Arch, Variant string }{ | 
					
						
							|  |  |  | 					OS:      platform.OS, | 
					
						
							|  |  |  | 					Arch:    platform.Architecture, | 
					
						
							|  |  |  | 					Variant: platform.Variant, | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 				logger.Debugf("base images all support %q", platform) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(platformList) == 0 { | 
					
						
							|  |  |  | 		return nil, errors.New("base images have no platforms in common") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return platformList, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // baseImages parses the dockerfilecontents, possibly replacing the first
 | 
					
						
							|  |  |  | // stage's base image with FROM, and returns the list of base images as
 | 
					
						
							|  |  |  | // provided.  Each entry in the dockerfilenames slice corresponds to a slice in
 | 
					
						
							|  |  |  | // dockerfilecontents.
 | 
					
						
							| 
									
										
										
										
											2022-05-10 18:11:37 +08:00
										 |  |  | func baseImages(dockerfilenames []string, dockerfilecontents [][]byte, from string, args map[string]string, additionalBuildContext map[string]*define.AdditionalBuildContext) ([]string, error) { | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	mainNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(dockerfilecontents[0])) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return nil, fmt.Errorf("parsing main Dockerfile: %s: %w", dockerfilenames[0], err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, d := range dockerfilecontents[1:] { | 
					
						
							|  |  |  | 		additionalNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(d)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-26 22:19:11 +08:00
										 |  |  | 			dockerfilenames := dockerfilenames[1:] | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 			return nil, fmt.Errorf("parsing additional Dockerfile %s: %w", dockerfilenames[i], err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		mainNode.Children = append(mainNode.Children, additionalNode.Children...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b := imagebuilder.NewBuilder(args) | 
					
						
							|  |  |  | 	defaultContainerConfig, err := config.Default() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-06 17:14:06 +08:00
										 |  |  | 		return nil, fmt.Errorf("failed to get container config: %w", err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	b.Env = defaultContainerConfig.GetDefaultEnv() | 
					
						
							|  |  |  | 	stages, err := imagebuilder.NewStages(mainNode, b) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-09-18 18:36:08 +08:00
										 |  |  | 		return nil, fmt.Errorf("reading multiple stages: %w", err) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	var baseImages []string | 
					
						
							| 
									
										
										
										
											2023-12-31 01:42:31 +08:00
										 |  |  | 	nicknames := make(map[string]struct{}) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 	for stageIndex, stage := range stages { | 
					
						
							|  |  |  | 		node := stage.Node // first line
 | 
					
						
							|  |  |  | 		for node != nil {  // each line
 | 
					
						
							|  |  |  | 			for _, child := range node.Children { // tokens on this line, though we only care about the first
 | 
					
						
							|  |  |  | 				switch strings.ToUpper(child.Value) { // first token - instruction
 | 
					
						
							|  |  |  | 				case "FROM": | 
					
						
							|  |  |  | 					if child.Next != nil { // second token on this line
 | 
					
						
							|  |  |  | 						// If we have a fromOverride, replace the value of
 | 
					
						
							|  |  |  | 						// image name for the first FROM in the Containerfile.
 | 
					
						
							|  |  |  | 						if from != "" { | 
					
						
							|  |  |  | 							child.Next.Value = from | 
					
						
							|  |  |  | 							from = "" | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2022-05-10 18:11:37 +08:00
										 |  |  | 						if replaceBuildContext, ok := additionalBuildContext[child.Next.Value]; ok { | 
					
						
							|  |  |  | 							if replaceBuildContext.IsImage { | 
					
						
							|  |  |  | 								child.Next.Value = replaceBuildContext.Value | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								return nil, fmt.Errorf("build context %q is not an image, can not be used for FROM %q", child.Next.Value, child.Next.Value) | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 						base := child.Next.Value | 
					
						
							| 
									
										
										
										
											2023-12-31 01:42:31 +08:00
										 |  |  | 						if base != "" && base != buildah.BaseImageFakeName && !internalUtil.SetHas(nicknames, base) { | 
					
						
							| 
									
										
										
										
											2022-11-04 13:21:12 +08:00
										 |  |  | 							headingArgs := argsMapToSlice(stage.Builder.HeadingArgs) | 
					
						
							|  |  |  | 							userArgs := argsMapToSlice(stage.Builder.Args) | 
					
						
							|  |  |  | 							// append heading args so if --build-arg key=value is not
 | 
					
						
							|  |  |  | 							// specified but default value is set in Containerfile
 | 
					
						
							|  |  |  | 							// via `ARG key=value` so default value can be used.
 | 
					
						
							|  |  |  | 							userArgs = append(headingArgs, userArgs...) | 
					
						
							|  |  |  | 							baseWithArg, err := imagebuilder.ProcessWord(base, userArgs) | 
					
						
							| 
									
										
										
										
											2022-11-04 04:50:25 +08:00
										 |  |  | 							if err != nil { | 
					
						
							| 
									
										
										
										
											2022-11-04 13:21:12 +08:00
										 |  |  | 								return nil, fmt.Errorf("while replacing arg variables with values for format %q: %w", base, err) | 
					
						
							| 
									
										
										
										
											2022-11-04 04:50:25 +08:00
										 |  |  | 							} | 
					
						
							| 
									
										
										
										
											2022-11-04 13:21:12 +08:00
										 |  |  | 							baseImages = append(baseImages, baseWithArg) | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			node = node.Next // next line
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if stage.Name != strconv.Itoa(stageIndex) { | 
					
						
							| 
									
										
										
										
											2023-12-31 01:42:31 +08:00
										 |  |  | 			nicknames[stage.Name] = struct{}{} | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return baseImages, nil | 
					
						
							|  |  |  | } |