| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2020-05-19 20:20:14 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	"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" | 
					
						
							| 
									
										
										
										
											2021-02-07 06:49:40 +08:00
										 |  |  | 	"github.com/containers/buildah/define" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2020-05-19 20:20:14 +08:00
										 |  |  | 	"github.com/openshift/imagebuilder/dockerfile/parser" | 
					
						
							| 
									
										
										
										
											2017-06-02 03:23:02 +08:00
										 |  |  | 	"github.com/pkg/errors" | 
					
						
							| 
									
										
										
										
											2017-10-10 03:05:56 +08:00
										 |  |  | 	"github.com/sirupsen/logrus" | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2018-04-11 01:35:03 +08:00
										 |  |  | 	if len(paths) == 0 { | 
					
						
							| 
									
										
										
										
											2018-10-12 04:58:04 +08:00
										 |  |  | 		return "", nil, errors.Errorf("error 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 != "" { | 
					
						
							|  |  |  | 		return "", nil, errors.Errorf("building multiple images, but iidfile %q can only be used to store one image ID", options.IIDFile) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 			return "", nil, errors.Wrapf(err, "tag %s", tag) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											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() | 
					
						
							| 
									
										
										
										
											2018-10-12 04:58:04 +08:00
										 |  |  | 				return "", nil, errors.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 | 
					
						
							| 
									
										
										
										
											2018-10-28 03:47:03 +08:00
										 |  |  | 			// If given a directory, add '/Dockerfile' to it.
 | 
					
						
							|  |  |  | 			if dinfo.Mode().IsDir() { | 
					
						
							| 
									
										
										
										
											2021-03-15 19:52:22 +08:00
										 |  |  | 				for _, file := range []string{"Containerfile", "Dockerfile"} { | 
					
						
							|  |  |  | 					f := filepath.Join(dfile, file) | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 					logger.Debugf("reading local %q", f) | 
					
						
							| 
									
										
										
										
											2021-03-15 19:52:22 +08:00
										 |  |  | 					contents, err = os.Open(f) | 
					
						
							|  |  |  | 					if err == nil { | 
					
						
							|  |  |  | 						break | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				contents, err = os.Open(dfile) | 
					
						
							| 
									
										
										
										
											2018-10-28 03:47:03 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-03-15 19:52:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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() | 
					
						
							| 
									
										
										
										
											2018-10-12 04:58:04 +08:00
										 |  |  | 				return "", nil, errors.Wrapf(err, "error reading info about %q", dfile) | 
					
						
							| 
									
										
										
										
											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() | 
					
						
							| 
									
										
										
										
											2020-10-15 17:16:50 +08:00
										 |  |  | 				return "", nil, errors.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") { | 
					
						
							| 
									
										
										
										
											2021-06-10 01:23:12 +08:00
										 |  |  | 			pData, err := preprocessContainerfileContents(logger, dfile, data, options.ContextDirectory) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-06-10 01:23:12 +08:00
										 |  |  | 			data = ioutil.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()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												imagebuildah: fix an attempt to write to a nil map
If the build for a single stage fails, we break out of the loop that's
iterating through all of the stages over in its own goroutine, and start
cleaning up after the stages that were already completed.
Because the function that launched that goroutine also calls its cleanup
function in non-error cases, the cleanup function sets the map that's
used to keep track of what needs to be cleaned up to `nil` after the
function finishes iterating through the map, so that we won't try to
clean up (a given thing that needs to be cleaned up) more than once.
Because the loop that's iterating through all of the stages is running
in its own goroutine, it doesn't stop when the function that started it
returns in error cases, so it would still attempt to build subsequent
stages.  Have it check for cases where the map variable has already been
cleared, or if one of the stages that it's already run returned an
error.  If the function that it calls to build the stage, using the map
variable as a parameter, is already running at that point, it'll have a
non-`nil` map, so it won't crash, but it might not be cleaned up
correctly, either.
If such a stage finishes, either successfully or with an error, the
goroutine would try to pass the result back to its parent(?) goroutine
over a channel that was no longer being read from, and it would stall,
never releasing the jobs semaphore.  Because we started sharing that
semaphore across multiple-platform builds, builds for other platforms
would stall completely, and the whole build would stall.  Make the
results channel into a buffered channel to allow it to not stall there.
Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
											
										 
											2021-09-23 04:56:55 +08:00
										 |  |  | 	if options.JobSemaphore == nil && options.Jobs != nil && *options.Jobs != 0 { | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 		options.JobSemaphore = semaphore.NewWeighted(int64(*options.Jobs)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	manifestList := options.Manifest | 
					
						
							|  |  |  | 	options.Manifest = "" | 
					
						
							|  |  |  | 	type instance struct { | 
					
						
							|  |  |  | 		v1.Platform | 
					
						
							|  |  |  | 		ID string | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 		options.Platforms, err = platformsForBaseImages(ctx, logger, paths, files, options.From, options.Args, options.SystemContext) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return "", nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	systemContext := options.SystemContext | 
					
						
							|  |  |  | 	for _, platform := range options.Platforms { | 
					
						
							|  |  |  | 		platformContext := *systemContext | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 		platformSpec := platforms.Normalize(v1.Platform{ | 
					
						
							|  |  |  | 			OS:           platform.OS, | 
					
						
							|  |  |  | 			Architecture: platform.Arch, | 
					
						
							|  |  |  | 			Variant:      platform.Variant, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		if platformSpec.OS != "" && platformSpec.Architecture != "" { | 
					
						
							|  |  |  | 			platformContext.OSChoice = platformSpec.OS | 
					
						
							|  |  |  | 			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
										 |  |  | 		} | 
					
						
							|  |  |  | 		builds.Go(func() error { | 
					
						
							|  |  |  | 			thisID, thisRef, err := buildDockerfilesOnce(ctx, store, logger, logPrefix, platformOptions, paths, files) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			id, ref = thisID, thisRef | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 			instancesLock.Lock() | 
					
						
							|  |  |  | 			instances = append(instances, instance{ | 
					
						
							| 
									
										
										
										
											2021-09-28 05:26:01 +08:00
										 |  |  | 				ID:       thisID, | 
					
						
							|  |  |  | 				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
										 |  |  | 
 | 
					
						
							|  |  |  | 	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) | 
					
						
							|  |  |  | 		if err != nil && errors.Cause(err) == storage.ErrImageUnknown { | 
					
						
							|  |  |  | 			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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func buildDockerfilesOnce(ctx context.Context, store storage.Store, logger *logrus.Logger, logPrefix string, options define.BuildOptions, dockerfiles []string, dockerfilecontents [][]byte) (string, reference.Canonical, error) { | 
					
						
							|  |  |  | 	mainNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(dockerfilecontents[0])) | 
					
						
							| 
									
										
										
										
											2018-02-25 06:40:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-10-15 17:16:50 +08:00
										 |  |  | 		return "", nil, errors.Wrapf(err, "error parsing main Dockerfile: %s", dockerfiles[0]) | 
					
						
							| 
									
										
										
										
											2017-03-28 15:06:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-05-19 20:20:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-08 01:38:44 +08:00
										 |  |  | 	warnOnUnsetBuildArgs(logger, mainNode, options.Args) | 
					
						
							| 
									
										
										
										
											2020-05-19 20:20:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 			return "", nil, errors.Wrapf(err, "error parsing additional Dockerfile %s", dockerfiles[i]) | 
					
						
							| 
									
										
										
										
											2018-02-25 06:40:44 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		mainNode.Children = append(mainNode.Children, additionalNode.Children...) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-14 18:44:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Check if any modifications done to labels
 | 
					
						
							|  |  |  | 	// add them to node-layer so it becomes regular
 | 
					
						
							|  |  |  | 	// layer.
 | 
					
						
							|  |  |  | 	// Reason: Docker adds label modification as
 | 
					
						
							|  |  |  | 	// last step which can be processed as regular
 | 
					
						
							|  |  |  | 	// steps and if no modification is done to layers
 | 
					
						
							|  |  |  | 	// its easier to re-use cached layers.
 | 
					
						
							|  |  |  | 	if len(options.Labels) > 0 { | 
					
						
							|  |  |  | 		for _, labelSpec := range options.Labels { | 
					
						
							|  |  |  | 			label := strings.SplitN(labelSpec, "=", 2) | 
					
						
							|  |  |  | 			labelLine := "" | 
					
						
							|  |  |  | 			key := label[0] | 
					
						
							|  |  |  | 			value := "" | 
					
						
							|  |  |  | 			if len(label) > 1 { | 
					
						
							|  |  |  | 				value = label[1] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// check from only empty key since docker supports empty value
 | 
					
						
							|  |  |  | 			if key != "" { | 
					
						
							|  |  |  | 				labelLine = fmt.Sprintf("LABEL %q=%q\n", key, value) | 
					
						
							|  |  |  | 				additionalNode, err := imagebuilder.ParseDockerfile(strings.NewReader(labelLine)) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return "", nil, errors.Wrapf(err, "error while adding additional LABEL steps") | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				mainNode.Children = append(mainNode.Children, additionalNode.Children...) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-11 06:11:15 +08:00
										 |  |  | 	exec, err := newExecutor(logger, logPrefix, store, options, mainNode) | 
					
						
							| 
									
										
										
										
											2018-02-25 06:40:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-10-12 04:58:04 +08:00
										 |  |  | 		return "", nil, errors.Wrapf(err, "error creating build executor") | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 		return "", nil, errors.Wrapf(err, "failed to get container config") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	b.Env = append(defaultContainerConfig.GetDefaultEnv(), b.Env...) | 
					
						
							| 
									
										
										
										
											2018-11-08 18:31:14 +08:00
										 |  |  | 	stages, err := imagebuilder.NewStages(mainNode, b) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", nil, errors.Wrap(err, "error reading multiple stages") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-03 07:31:44 +08:00
										 |  |  | 	if options.Target != "" { | 
					
						
							|  |  |  | 		stagesTargeted, ok := stages.ThroughTarget(options.Target) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			return "", nil, errors.Errorf("The target %q was not found in the provided Dockerfile", options.Target) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		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-08 01:38:44 +08:00
										 |  |  | func warnOnUnsetBuildArgs(logger *logrus.Logger, node *parser.Node, args map[string]string) { | 
					
						
							| 
									
										
										
										
											2021-04-18 08:27:35 +08:00
										 |  |  | 	argFound := make(map[string]bool) | 
					
						
							| 
									
										
										
										
											2020-05-19 20:20:14 +08:00
										 |  |  | 	for _, child := range node.Children { | 
					
						
							|  |  |  | 		switch strings.ToUpper(child.Value) { | 
					
						
							|  |  |  | 		case "ARG": | 
					
						
							|  |  |  | 			argName := child.Next.Value | 
					
						
							| 
									
										
										
										
											2021-04-18 08:27:35 +08:00
										 |  |  | 			if strings.Contains(argName, "=") { | 
					
						
							|  |  |  | 				res := strings.Split(argName, "=") | 
					
						
							|  |  |  | 				if res[1] != "" { | 
					
						
							|  |  |  | 					argFound[res[0]] = true | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			argHasValue := true | 
					
						
							|  |  |  | 			if !strings.Contains(argName, "=") { | 
					
						
							|  |  |  | 				argHasValue = argFound[argName] | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if _, ok := args[argName]; !argHasValue && !ok { | 
					
						
							| 
									
										
										
										
											2021-05-08 01:38:44 +08:00
										 |  |  | 				logger.Warnf("missing %q build argument. Try adding %q to the command line", argName, fmt.Sprintf("--build-arg %s=<VALUE>", argName)) | 
					
						
							| 
									
										
										
										
											2020-05-19 20:20:14 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2021-06-10 01:23:12 +08:00
										 |  |  | func preprocessContainerfileContents(logger *logrus.Logger, containerfile string, r io.Reader, ctxDir string) (stdout io.Reader, err error) { | 
					
						
							|  |  |  | 	cppCommand := "cpp" | 
					
						
							|  |  |  | 	cppPath, err := exec.LookPath(cppCommand) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-01 18:31:02 +08:00
										 |  |  | 		if os.IsNotExist(err) { | 
					
						
							| 
									
										
										
										
											2021-05-19 21:05:47 +08:00
										 |  |  | 			err = errors.Errorf("error: %s support requires %s to be installed", containerfile, cppPath) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-24 19:31:09 +08:00
										 |  |  | 	cmd := exec.Command(cppPath, "-E", "-iquote", ctxDir, "-traditional", "-undef", "-") | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2021-06-10 01:23:12 +08:00
										 |  |  | 		return nil, errors.Wrapf(err, "preprocessing %s", containerfile) | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "error preprocessing %s: preprocessor produced no output", containerfile) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  | // 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.
 | 
					
						
							|  |  |  | func platformsForBaseImages(ctx context.Context, logger *logrus.Logger, dockerfilepaths []string, dockerfiles [][]byte, from string, args map[string]string, systemContext *types.SystemContext) ([]struct{ OS, Arch, Variant string }, error) { | 
					
						
							|  |  |  | 	baseImages, err := baseImages(dockerfilepaths, dockerfiles, from, args) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "determining list of base images") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	logrus.Debugf("unresolved base images: %v", baseImages) | 
					
						
							|  |  |  | 	if len(baseImages) == 0 { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "build uses no non-scratch base images") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	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 { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "resolving image name %q", baseImage) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		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 { | 
					
						
							|  |  |  | 				return nil, errors.Errorf("base image name %q didn't resolve to a manifest list", baseImage) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return nil, errors.Errorf("base image name %q didn't resolve to anything", baseImage) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if manifestType != v1.MediaTypeImageIndex { | 
					
						
							|  |  |  | 			list, err := manifest.ListFromBlob(manifestBytes, manifestType) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, errors.Wrapf(err, "parsing manifest list from base image %q", baseImage) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			list, err = list.ConvertToMIMEType(v1.MediaTypeImageIndex) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, errors.Wrapf(err, "converting manifest list from base image %q to v2s2 list", baseImage) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			manifestBytes, err = list.Serialize() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, errors.Wrapf(err, "encoding converted v2s2 manifest list for base image %q", baseImage) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		index, err := manifest.OCI1IndexFromManifest(manifestBytes) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "decoding manifest list for base image %q", baseImage) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if baseImageIndex == 0 { | 
					
						
							|  |  |  | 			// populate the list with the first image's normalized platforms
 | 
					
						
							|  |  |  | 			for _, instance := range index.Manifests { | 
					
						
							|  |  |  | 				if instance.Platform == nil { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				platform := platforms.Normalize(*instance.Platform) | 
					
						
							|  |  |  | 				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 { | 
					
						
							|  |  |  | 				if instance.Platform == nil { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				platform := platforms.Normalize(*instance.Platform) | 
					
						
							|  |  |  | 				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 { | 
					
						
							|  |  |  | 					return nil, errors.Wrapf(err, "parsing platform double/triple %q", platform) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				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.
 | 
					
						
							|  |  |  | func baseImages(dockerfilenames []string, dockerfilecontents [][]byte, from string, args map[string]string) ([]string, error) { | 
					
						
							|  |  |  | 	mainNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(dockerfilecontents[0])) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "error parsing main Dockerfile: %s", dockerfilenames[0]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, d := range dockerfilecontents[1:] { | 
					
						
							|  |  |  | 		additionalNode, err := imagebuilder.ParseDockerfile(bytes.NewReader(d)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, errors.Wrapf(err, "error parsing additional Dockerfile %s", dockerfilenames[i]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		mainNode.Children = append(mainNode.Children, additionalNode.Children...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b := imagebuilder.NewBuilder(args) | 
					
						
							|  |  |  | 	defaultContainerConfig, err := config.Default() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrapf(err, "failed to get container config") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	b.Env = defaultContainerConfig.GetDefaultEnv() | 
					
						
							|  |  |  | 	stages, err := imagebuilder.NewStages(mainNode, b) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, errors.Wrap(err, "error reading multiple stages") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var baseImages []string | 
					
						
							|  |  |  | 	nicknames := make(map[string]bool) | 
					
						
							|  |  |  | 	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 = "" | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						base := child.Next.Value | 
					
						
							|  |  |  | 						if base != "scratch" && !nicknames[base] { | 
					
						
							|  |  |  | 							// TODO: this didn't undergo variable and arg
 | 
					
						
							|  |  |  | 							// expansion, so if the AS clause in another
 | 
					
						
							|  |  |  | 							// FROM instruction uses argument values,
 | 
					
						
							|  |  |  | 							// we might not record the right value here.
 | 
					
						
							|  |  |  | 							baseImages = append(baseImages, base) | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			node = node.Next // next line
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if stage.Name != strconv.Itoa(stageIndex) { | 
					
						
							|  |  |  | 			nicknames[stage.Name] = true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return baseImages, nil | 
					
						
							|  |  |  | } |