2017-02-11 00:48:15 +08:00
|
|
|
package buildah
|
|
|
|
|
|
|
|
import (
|
2018-04-12 22:20:36 +08:00
|
|
|
"context"
|
2017-02-11 00:48:15 +08:00
|
|
|
"fmt"
|
2018-10-20 02:49:51 +08:00
|
|
|
"math/rand"
|
2020-10-19 16:44:22 +08:00
|
|
|
"os"
|
2017-03-24 04:18:40 +08:00
|
|
|
"strings"
|
2017-02-11 00:48:15 +08:00
|
|
|
|
2018-09-18 03:20:16 +08:00
|
|
|
"github.com/containers/buildah/util"
|
2020-10-19 16:44:22 +08:00
|
|
|
"github.com/containers/image/v5/docker"
|
2019-11-01 03:18:10 +08:00
|
|
|
"github.com/containers/image/v5/image"
|
2019-10-26 05:19:30 +08:00
|
|
|
"github.com/containers/image/v5/manifest"
|
2020-10-19 16:44:22 +08:00
|
|
|
"github.com/containers/image/v5/pkg/shortnames"
|
2019-10-26 05:19:30 +08:00
|
|
|
is "github.com/containers/image/v5/storage"
|
|
|
|
"github.com/containers/image/v5/transports"
|
|
|
|
"github.com/containers/image/v5/transports/alltransports"
|
|
|
|
"github.com/containers/image/v5/types"
|
2017-05-17 23:53:28 +08:00
|
|
|
"github.com/containers/storage"
|
2019-11-01 03:18:10 +08:00
|
|
|
digest "github.com/opencontainers/go-digest"
|
2017-03-22 04:57:07 +08:00
|
|
|
"github.com/openshift/imagebuilder"
|
2017-06-02 03:23:02 +08:00
|
|
|
"github.com/pkg/errors"
|
2017-10-10 03:05:56 +08:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-02-11 00:48:15 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2017-02-11 03:45:06 +08:00
|
|
|
// BaseImageFakeName is the "name" of a source image which we interpret
|
|
|
|
// as "no image".
|
2017-03-22 04:57:07 +08:00
|
|
|
BaseImageFakeName = imagebuilder.NoBaseImageSpecifier
|
2017-02-11 00:48:15 +08:00
|
|
|
)
|
|
|
|
|
2019-02-22 07:20:36 +08:00
|
|
|
func pullAndFindImage(ctx context.Context, store storage.Store, srcRef types.ImageReference, options BuilderOptions, sc *types.SystemContext) (*storage.Image, types.ImageReference, error) {
|
2018-08-09 04:38:53 +08:00
|
|
|
pullOptions := PullOptions{
|
2020-04-02 02:15:56 +08:00
|
|
|
ReportWriter: options.ReportWriter,
|
|
|
|
Store: store,
|
|
|
|
SystemContext: options.SystemContext,
|
|
|
|
BlobDirectory: options.BlobDirectory,
|
|
|
|
MaxRetries: options.MaxPullRetries,
|
|
|
|
RetryDelay: options.PullRetryDelay,
|
|
|
|
OciDecryptConfig: options.OciDecryptConfig,
|
2018-08-09 04:38:53 +08:00
|
|
|
}
|
2019-02-22 07:20:36 +08:00
|
|
|
ref, err := pullImage(ctx, store, srcRef, pullOptions, sc)
|
2017-06-29 05:07:58 +08:00
|
|
|
if err != nil {
|
2019-02-22 07:20:36 +08:00
|
|
|
logrus.Debugf("error pulling image %q: %v", transports.ImageName(srcRef), err)
|
2017-06-29 05:07:58 +08:00
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
img, err := is.Transport.GetStoreImage(store, ref)
|
|
|
|
if err != nil {
|
2019-02-22 07:20:36 +08:00
|
|
|
logrus.Debugf("error reading pulled image %q: %v", transports.ImageName(srcRef), err)
|
2018-10-03 22:05:46 +08:00
|
|
|
return nil, nil, errors.Wrapf(err, "error locating image %q in local storage", transports.ImageName(ref))
|
2017-06-29 05:07:58 +08:00
|
|
|
}
|
|
|
|
return img, ref, nil
|
|
|
|
}
|
|
|
|
|
2018-01-18 20:04:09 +08:00
|
|
|
func getImageName(name string, img *storage.Image) string {
|
|
|
|
imageName := name
|
|
|
|
if len(img.Names) > 0 {
|
|
|
|
imageName = img.Names[0]
|
|
|
|
// When the image used by the container is a tagged image
|
|
|
|
// the container name might be set to the original image instead of
|
2018-10-03 22:05:46 +08:00
|
|
|
// the image given in the "from" command line.
|
2018-01-18 20:04:09 +08:00
|
|
|
// This loop is supposed to fix this.
|
|
|
|
for _, n := range img.Names {
|
|
|
|
if strings.Contains(n, name) {
|
|
|
|
imageName = n
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return imageName
|
|
|
|
}
|
|
|
|
|
2017-06-29 05:07:58 +08:00
|
|
|
func imageNamePrefix(imageName string) string {
|
|
|
|
prefix := imageName
|
2020-01-03 19:01:41 +08:00
|
|
|
s := strings.Split(prefix, ":")
|
2017-06-29 05:07:58 +08:00
|
|
|
if len(s) > 0 {
|
2020-01-03 19:01:41 +08:00
|
|
|
prefix = s[0]
|
2017-06-29 05:07:58 +08:00
|
|
|
}
|
2020-01-03 19:01:41 +08:00
|
|
|
s = strings.Split(prefix, "/")
|
2017-06-29 05:07:58 +08:00
|
|
|
if len(s) > 0 {
|
2020-01-03 19:01:41 +08:00
|
|
|
prefix = s[len(s)-1]
|
2017-06-29 05:07:58 +08:00
|
|
|
}
|
|
|
|
s = strings.Split(prefix, "@")
|
|
|
|
if len(s) > 0 {
|
|
|
|
prefix = s[0]
|
|
|
|
}
|
|
|
|
return prefix
|
|
|
|
}
|
|
|
|
|
2018-03-13 01:53:12 +08:00
|
|
|
func newContainerIDMappingOptions(idmapOptions *IDMappingOptions) storage.IDMappingOptions {
|
|
|
|
var options storage.IDMappingOptions
|
|
|
|
if idmapOptions != nil {
|
|
|
|
options.HostUIDMapping = idmapOptions.HostUIDMapping
|
|
|
|
options.HostGIDMapping = idmapOptions.HostGIDMapping
|
|
|
|
uidmap, gidmap := convertRuntimeIDMaps(idmapOptions.UIDMap, idmapOptions.GIDMap)
|
|
|
|
if len(uidmap) > 0 && len(gidmap) > 0 {
|
|
|
|
options.UIDMap = uidmap
|
|
|
|
options.GIDMap = gidmap
|
|
|
|
} else {
|
|
|
|
options.HostUIDMapping = true
|
|
|
|
options.HostGIDMapping = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return options
|
|
|
|
}
|
2018-06-02 02:52:16 +08:00
|
|
|
|
2020-10-19 16:44:22 +08:00
|
|
|
func resolveLocalImage(systemContext *types.SystemContext, store storage.Store, options BuilderOptions) (types.ImageReference, string, *storage.Image, error) {
|
|
|
|
candidates, _, _, err := util.ResolveName(options.FromImage, options.Registry, systemContext, store)
|
2018-08-22 04:33:36 +08:00
|
|
|
if err != nil {
|
2020-10-19 16:44:22 +08:00
|
|
|
return nil, "", nil, errors.Wrapf(err, "error resolving local image %q", options.FromImage)
|
2018-08-22 04:33:36 +08:00
|
|
|
}
|
Improve reporting about individual pull failures
Instead of just using multierror to collect the error strings (and hope that they
contain enough context about the attempted image names), explicitly collect pairs of
(attempted image name, error).
Then, report an appropriate error text depending on the failures and environment.
Notably, if there was only one attempt (e.g. a fully-qualified name), just report
the error with minimal context, instead of adding extra "1 error occurred:\n\n".
If search registries were used and empty, note that in the error message (now
for real).
Also, make sure we return _some_ error if util.ResolveName ever returned
an empty list for osome reason.
It seems fairly attractive now to split resolveImage into the outer loop
and a separate function for making one attempt, to consolidate the
repetitive failures = append(...) code. OTOH that would force us to add
another return value as a "fatal" indication. Maybe later.
NOTABLY CHANGES BEHAVIOR:
- Changes the error format.
- Restores more detailed error reporting if we have no search registries,
but had multiple candidates anyway, which was given up in the previous commit.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Closes: #909
Approved by: rhatdan
2018-08-04 10:14:03 +08:00
|
|
|
for _, image := range candidates {
|
2020-10-19 16:44:22 +08:00
|
|
|
img, err := store.Image(image)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Cause(err) == storage.ErrImageUnknown {
|
2019-02-22 06:22:38 +08:00
|
|
|
continue
|
|
|
|
}
|
2020-10-19 16:44:22 +08:00
|
|
|
return nil, "", nil, err
|
2017-06-29 05:07:58 +08:00
|
|
|
}
|
2020-10-19 16:44:22 +08:00
|
|
|
ref, err := is.Transport.ParseStoreReference(store, img.ID)
|
2018-06-02 02:52:16 +08:00
|
|
|
if err != nil {
|
2020-10-19 16:44:22 +08:00
|
|
|
return nil, "", nil, errors.Wrapf(err, "error parsing reference to image %q", img.ID)
|
2017-06-29 05:07:58 +08:00
|
|
|
}
|
2020-10-19 16:44:22 +08:00
|
|
|
return ref, ref.Transport().Name(), img, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, "", nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// getShortNameMode looks up the `CONTAINERS_SHORT_NAME_ALIASING` environment
|
|
|
|
// variable. If it's "on", return `nil` to use the defaults from
|
|
|
|
// containers/image and the registries.conf files on the system. If it's
|
|
|
|
// "off", empty or unset, return types.ShortNameModeDisabled to turn off
|
|
|
|
// short-name aliasing by default.
|
|
|
|
//
|
|
|
|
// TODO: remove this function once we want to default to short-name aliasing.
|
|
|
|
func getShortNameMode() *types.ShortNameMode {
|
|
|
|
env := os.Getenv("CONTAINERS_SHORT_NAME_ALIASING")
|
|
|
|
if strings.ToLower(env) == "on" {
|
|
|
|
return nil // default to whatever registries.conf and c/image decide
|
|
|
|
}
|
|
|
|
mode := types.ShortNameModeDisabled
|
|
|
|
return &mode
|
|
|
|
}
|
|
|
|
|
|
|
|
func resolveImage(ctx context.Context, systemContext *types.SystemContext, store storage.Store, options BuilderOptions) (types.ImageReference, string, *storage.Image, error) {
|
|
|
|
if systemContext == nil {
|
|
|
|
systemContext = &types.SystemContext{}
|
|
|
|
}
|
|
|
|
systemContext.ShortNameMode = getShortNameMode()
|
2017-07-29 05:29:37 +08:00
|
|
|
|
2020-10-19 16:44:22 +08:00
|
|
|
fromImage := options.FromImage
|
|
|
|
// If the image name includes a transport we can use it as it. Special
|
|
|
|
// treatment for docker references which are subject to pull policies
|
|
|
|
// that we're handling below.
|
|
|
|
srcRef, err := alltransports.ParseImageName(options.FromImage)
|
|
|
|
if err == nil {
|
|
|
|
if srcRef.Transport().Name() == docker.Transport.Name() {
|
|
|
|
fromImage = srcRef.DockerReference().String()
|
|
|
|
} else {
|
2019-02-22 07:20:36 +08:00
|
|
|
pulledImg, pulledReference, err := pullAndFindImage(ctx, store, srcRef, options, systemContext)
|
2020-10-19 16:44:22 +08:00
|
|
|
return pulledReference, srcRef.Transport().Name(), pulledImg, err
|
2019-02-22 07:11:51 +08:00
|
|
|
}
|
2020-10-19 16:44:22 +08:00
|
|
|
}
|
2019-02-22 07:11:51 +08:00
|
|
|
|
2020-10-19 16:44:22 +08:00
|
|
|
localImageRef, _, localImage, err := resolveLocalImage(systemContext, store, options)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we could resolve the image locally, check if it was referenced by
|
|
|
|
// ID. In that case, we don't need to bother any further and can
|
|
|
|
// prevent prompting the user.
|
|
|
|
if localImage != nil && strings.HasPrefix(localImage.ID, options.FromImage) {
|
|
|
|
return localImageRef, localImageRef.Transport().Name(), localImage, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if options.PullPolicy == PullNever || options.PullPolicy == PullIfMissing {
|
|
|
|
if localImage != nil {
|
|
|
|
return localImageRef, localImageRef.Transport().Name(), localImage, nil
|
2017-06-29 05:07:58 +08:00
|
|
|
}
|
2020-10-19 16:44:22 +08:00
|
|
|
if options.PullPolicy == PullNever {
|
|
|
|
return nil, "", nil, errors.Errorf("pull policy is %q but %q could not be found locally", "never", options.FromImage)
|
2017-06-29 05:07:58 +08:00
|
|
|
}
|
2020-10-19 16:44:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
resolved, err := shortnames.Resolve(systemContext, fromImage)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print the image-resolution description unless we're looking for a
|
|
|
|
// new image and already found a local image. In many cases, the
|
|
|
|
// description will be more confusing than helpful (e.g., `buildah from
|
|
|
|
// localImage`).
|
|
|
|
if desc := resolved.Description(); len(desc) > 0 {
|
|
|
|
logrus.Debug(desc)
|
|
|
|
if !(options.PullPolicy == PullIfNewer && localImage != nil) {
|
|
|
|
if options.ReportWriter != nil {
|
|
|
|
if _, err := options.ReportWriter.Write([]byte(desc + "\n")); err != nil {
|
|
|
|
return nil, "", nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var pullErrors []error
|
|
|
|
for _, pullCandidate := range resolved.PullCandidates {
|
|
|
|
ref, err := docker.NewReference(pullCandidate.Value)
|
2017-06-29 05:07:58 +08:00
|
|
|
if err != nil {
|
2020-10-19 16:44:22 +08:00
|
|
|
return nil, "", nil, err
|
2017-02-11 00:48:15 +08:00
|
|
|
}
|
2018-08-04 08:54:39 +08:00
|
|
|
|
2020-10-19 16:44:22 +08:00
|
|
|
// We're tasked to pull a "newer" image. If there's no local
|
|
|
|
// image, we have no base for comparison, so we'll pull the
|
|
|
|
// first available image.
|
|
|
|
//
|
|
|
|
// If there's a local image, the `pullCandidate` is considered
|
|
|
|
// to be newer if its time stamp differs from the local one.
|
|
|
|
// Otherwise, we don't pull and skip it.
|
|
|
|
if options.PullPolicy == PullIfNewer && localImage != nil {
|
|
|
|
remoteImage, err := ref.NewImage(ctx, systemContext)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Debugf("unable to remote-inspect image %q: %v", pullCandidate.Value.String(), err)
|
|
|
|
pullErrors = append(pullErrors, err)
|
|
|
|
continue
|
Fix --pull=true||false and add --pull-never to bud and from (retry)
(Replaces #1873 as it had lint issues that were timing out tests that I couldn't
track down easily)
Prior to this fix, if someone did `buildah bud --pull=false .` and the image in
the Containerfile's FROM statement was not local, the build would fail. The same
build on Docker will succeed. In Docker, when `--pull` is set to false, it only
pulls the image from the registry if there was not one locally. Buildah would never
pull the image and if the image was not locally available, it would throw an error.
In certain Kubernetes environments, this was especially troublesome.
To retain the old `--pull=false` functionality, I've created a new `--pull-never`
option that fails if an image is not locally available just like the old
`--pull=false` option used to do.
In addition, if there was a newer version of the image on the repository than
the one locally, the `--pull=true` option would not pull the image as it should
have, this corrects that.
Changes both the from and bud commands.
Addresses: #1675
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
Closes: #1959
Approved by: rhatdan
2019-10-31 22:15:56 +08:00
|
|
|
}
|
2020-10-19 16:44:22 +08:00
|
|
|
defer remoteImage.Close()
|
|
|
|
|
|
|
|
remoteData, err := remoteImage.Inspect(ctx)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Debugf("unable to remote-inspect image %q: %v", pullCandidate.Value.String(), err)
|
|
|
|
pullErrors = append(pullErrors, err)
|
|
|
|
continue
|
Fix --pull=true||false and add --pull-never to bud and from (retry)
(Replaces #1873 as it had lint issues that were timing out tests that I couldn't
track down easily)
Prior to this fix, if someone did `buildah bud --pull=false .` and the image in
the Containerfile's FROM statement was not local, the build would fail. The same
build on Docker will succeed. In Docker, when `--pull` is set to false, it only
pulls the image from the registry if there was not one locally. Buildah would never
pull the image and if the image was not locally available, it would throw an error.
In certain Kubernetes environments, this was especially troublesome.
To retain the old `--pull=false` functionality, I've created a new `--pull-never`
option that fails if an image is not locally available just like the old
`--pull=false` option used to do.
In addition, if there was a newer version of the image on the repository than
the one locally, the `--pull=true` option would not pull the image as it should
have, this corrects that.
Changes both the from and bud commands.
Addresses: #1675
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
Closes: #1959
Approved by: rhatdan
2019-10-31 22:15:56 +08:00
|
|
|
}
|
2020-10-19 16:44:22 +08:00
|
|
|
|
|
|
|
// FIXME: we should compare image digests not time stamps.
|
|
|
|
// Comparing time stamps is flawed. Be aware that fixing
|
|
|
|
// it may entail non-trivial changes to the tests. Please
|
|
|
|
// refer to https://github.com/containers/buildah/issues/2779
|
|
|
|
// for more.
|
|
|
|
if localImage.Created.Equal(*remoteData.Created) {
|
Fix --pull=true||false and add --pull-never to bud and from (retry)
(Replaces #1873 as it had lint issues that were timing out tests that I couldn't
track down easily)
Prior to this fix, if someone did `buildah bud --pull=false .` and the image in
the Containerfile's FROM statement was not local, the build would fail. The same
build on Docker will succeed. In Docker, when `--pull` is set to false, it only
pulls the image from the registry if there was not one locally. Buildah would never
pull the image and if the image was not locally available, it would throw an error.
In certain Kubernetes environments, this was especially troublesome.
To retain the old `--pull=false` functionality, I've created a new `--pull-never`
option that fails if an image is not locally available just like the old
`--pull=false` option used to do.
In addition, if there was a newer version of the image on the repository than
the one locally, the `--pull=true` option would not pull the image as it should
have, this corrects that.
Changes both the from and bud commands.
Addresses: #1675
Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
Closes: #1959
Approved by: rhatdan
2019-10-31 22:15:56 +08:00
|
|
|
continue
|
|
|
|
}
|
2018-08-04 08:54:39 +08:00
|
|
|
}
|
|
|
|
|
2020-10-19 16:44:22 +08:00
|
|
|
pulledImg, pulledReference, err := pullAndFindImage(ctx, store, ref, options, systemContext)
|
2017-02-11 00:48:15 +08:00
|
|
|
if err != nil {
|
2020-10-19 16:44:22 +08:00
|
|
|
logrus.Debugf("unable to pull and read image %q: %v", pullCandidate.Value.String(), err)
|
|
|
|
pullErrors = append(pullErrors, err)
|
2018-08-04 08:54:39 +08:00
|
|
|
continue
|
2017-02-11 00:48:15 +08:00
|
|
|
}
|
Improve reporting about individual pull failures
Instead of just using multierror to collect the error strings (and hope that they
contain enough context about the attempted image names), explicitly collect pairs of
(attempted image name, error).
Then, report an appropriate error text depending on the failures and environment.
Notably, if there was only one attempt (e.g. a fully-qualified name), just report
the error with minimal context, instead of adding extra "1 error occurred:\n\n".
If search registries were used and empty, note that in the error message (now
for real).
Also, make sure we return _some_ error if util.ResolveName ever returned
an empty list for osome reason.
It seems fairly attractive now to split resolveImage into the outer loop
and a separate function for making one attempt, to consolidate the
repetitive failures = append(...) code. OTOH that would force us to add
another return value as a "fatal" indication. Maybe later.
NOTABLY CHANGES BEHAVIOR:
- Changes the error format.
- Restores more detailed error reporting if we have no search registries,
but had multiple candidates anyway, which was given up in the previous commit.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Closes: #909
Approved by: rhatdan
2018-08-04 10:14:03 +08:00
|
|
|
|
2020-10-19 16:44:22 +08:00
|
|
|
// Make sure to record the short-name alias if necessary.
|
|
|
|
if err = pullCandidate.Record(); err != nil {
|
|
|
|
return nil, "", nil, err
|
Improve reporting about individual pull failures
Instead of just using multierror to collect the error strings (and hope that they
contain enough context about the attempted image names), explicitly collect pairs of
(attempted image name, error).
Then, report an appropriate error text depending on the failures and environment.
Notably, if there was only one attempt (e.g. a fully-qualified name), just report
the error with minimal context, instead of adding extra "1 error occurred:\n\n".
If search registries were used and empty, note that in the error message (now
for real).
Also, make sure we return _some_ error if util.ResolveName ever returned
an empty list for osome reason.
It seems fairly attractive now to split resolveImage into the outer loop
and a separate function for making one attempt, to consolidate the
repetitive failures = append(...) code. OTOH that would force us to add
another return value as a "fatal" indication. Maybe later.
NOTABLY CHANGES BEHAVIOR:
- Changes the error format.
- Restores more detailed error reporting if we have no search registries,
but had multiple candidates anyway, which was given up in the previous commit.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Closes: #909
Approved by: rhatdan
2018-08-04 10:14:03 +08:00
|
|
|
}
|
|
|
|
|
2020-10-19 16:44:22 +08:00
|
|
|
return pulledReference, "", pulledImg, nil
|
|
|
|
}
|
Improve reporting about individual pull failures
Instead of just using multierror to collect the error strings (and hope that they
contain enough context about the attempted image names), explicitly collect pairs of
(attempted image name, error).
Then, report an appropriate error text depending on the failures and environment.
Notably, if there was only one attempt (e.g. a fully-qualified name), just report
the error with minimal context, instead of adding extra "1 error occurred:\n\n".
If search registries were used and empty, note that in the error message (now
for real).
Also, make sure we return _some_ error if util.ResolveName ever returned
an empty list for osome reason.
It seems fairly attractive now to split resolveImage into the outer loop
and a separate function for making one attempt, to consolidate the
repetitive failures = append(...) code. OTOH that would force us to add
another return value as a "fatal" indication. Maybe later.
NOTABLY CHANGES BEHAVIOR:
- Changes the error format.
- Restores more detailed error reporting if we have no search registries,
but had multiple candidates anyway, which was given up in the previous commit.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
Closes: #909
Approved by: rhatdan
2018-08-04 10:14:03 +08:00
|
|
|
|
2020-10-19 16:44:22 +08:00
|
|
|
// If we were looking for a newer image but could not find one, return
|
|
|
|
// the local image if present.
|
|
|
|
if options.PullPolicy == PullIfNewer && localImage != nil {
|
|
|
|
return localImageRef, localImageRef.Transport().Name(), localImage, nil
|
2018-08-04 09:29:16 +08:00
|
|
|
}
|
2020-10-19 16:44:22 +08:00
|
|
|
|
|
|
|
return nil, "", nil, resolved.FormatPullErrors(pullErrors)
|
2018-06-02 02:52:16 +08:00
|
|
|
}
|
2017-06-29 05:07:58 +08:00
|
|
|
|
2018-10-20 02:49:51 +08:00
|
|
|
func containerNameExist(name string, containers []storage.Container) bool {
|
|
|
|
for _, container := range containers {
|
|
|
|
for _, cname := range container.Names {
|
|
|
|
if cname == name {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func findUnusedContainer(name string, containers []storage.Container) string {
|
|
|
|
suffix := 1
|
|
|
|
tmpName := name
|
|
|
|
for containerNameExist(tmpName, containers) {
|
|
|
|
tmpName = fmt.Sprintf("%s-%d", name, suffix)
|
|
|
|
suffix++
|
|
|
|
}
|
|
|
|
return tmpName
|
|
|
|
}
|
|
|
|
|
2018-06-02 02:52:16 +08:00
|
|
|
func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions) (*Builder, error) {
|
2019-02-02 20:29:05 +08:00
|
|
|
var (
|
|
|
|
ref types.ImageReference
|
|
|
|
img *storage.Image
|
|
|
|
err error
|
|
|
|
)
|
2018-06-02 02:52:16 +08:00
|
|
|
if options.FromImage == BaseImageFakeName {
|
|
|
|
options.FromImage = ""
|
|
|
|
}
|
|
|
|
|
2019-03-14 04:03:13 +08:00
|
|
|
systemContext := getSystemContext(store, options.SystemContext, options.SignaturePolicyPath)
|
2018-06-02 02:52:16 +08:00
|
|
|
|
2018-08-04 09:02:06 +08:00
|
|
|
if options.FromImage != "" && options.FromImage != "scratch" {
|
2019-02-02 20:29:05 +08:00
|
|
|
ref, _, img, err = resolveImage(ctx, systemContext, store, options)
|
2018-06-02 02:52:16 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-06-29 05:07:58 +08:00
|
|
|
}
|
2019-11-01 03:18:10 +08:00
|
|
|
imageSpec := options.FromImage
|
2017-06-29 05:07:58 +08:00
|
|
|
imageID := ""
|
2019-07-02 05:14:07 +08:00
|
|
|
imageDigest := ""
|
2018-06-09 00:55:46 +08:00
|
|
|
topLayer := ""
|
2017-06-29 05:07:58 +08:00
|
|
|
if img != nil {
|
2019-11-01 03:18:10 +08:00
|
|
|
imageSpec = getImageName(imageNamePrefix(imageSpec), img)
|
2017-03-16 05:19:29 +08:00
|
|
|
imageID = img.ID
|
2018-06-09 00:55:46 +08:00
|
|
|
topLayer = img.TopLayer
|
2017-06-29 05:07:58 +08:00
|
|
|
}
|
2019-11-01 03:18:10 +08:00
|
|
|
var src types.Image
|
2018-06-12 06:31:04 +08:00
|
|
|
if ref != nil {
|
2019-11-01 03:18:10 +08:00
|
|
|
srcSrc, err := ref.NewImageSource(ctx, systemContext)
|
2018-06-12 06:31:04 +08:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "error instantiating image for %q", transports.ImageName(ref))
|
|
|
|
}
|
2019-11-01 03:18:10 +08:00
|
|
|
defer srcSrc.Close()
|
|
|
|
manifestBytes, manifestType, err := srcSrc.GetManifest(ctx, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "error loading image manifest for %q", transports.ImageName(ref))
|
|
|
|
}
|
|
|
|
if manifestDigest, err := manifest.Digest(manifestBytes); err == nil {
|
|
|
|
imageDigest = manifestDigest.String()
|
|
|
|
}
|
|
|
|
var instanceDigest *digest.Digest
|
|
|
|
if manifest.MIMETypeIsMultiImage(manifestType) {
|
|
|
|
list, err := manifest.ListFromBlob(manifestBytes, manifestType)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "error parsing image manifest for %q as list", transports.ImageName(ref))
|
2019-07-02 05:14:07 +08:00
|
|
|
}
|
2019-11-01 03:18:10 +08:00
|
|
|
instance, err := list.ChooseInstance(systemContext)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "error finding an appropriate image in manifest list %q", transports.ImageName(ref))
|
|
|
|
}
|
|
|
|
instanceDigest = &instance
|
|
|
|
}
|
|
|
|
src, err = image.FromUnparsedImage(ctx, systemContext, image.UnparsedInstance(srcSrc, instanceDigest))
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "error instantiating image for %q instance %q", transports.ImageName(ref), instanceDigest)
|
2019-07-02 05:14:07 +08:00
|
|
|
}
|
2018-06-12 06:31:04 +08:00
|
|
|
}
|
|
|
|
|
2017-07-29 05:29:37 +08:00
|
|
|
name := "working-container"
|
|
|
|
if options.Container != "" {
|
|
|
|
name = options.Container
|
|
|
|
} else {
|
2019-11-01 03:18:10 +08:00
|
|
|
if imageSpec != "" {
|
|
|
|
name = imageNamePrefix(imageSpec) + "-" + name
|
2017-07-29 05:29:37 +08:00
|
|
|
}
|
|
|
|
}
|
2018-10-20 02:49:51 +08:00
|
|
|
var container *storage.Container
|
|
|
|
tmpName := name
|
|
|
|
if options.Container == "" {
|
|
|
|
containers, err := store.Containers()
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "unable to check for container names")
|
|
|
|
}
|
|
|
|
tmpName = findUnusedContainer(tmpName, containers)
|
|
|
|
}
|
2017-06-29 05:07:58 +08:00
|
|
|
|
2018-10-20 02:49:51 +08:00
|
|
|
conflict := 100
|
2019-02-23 10:08:09 +08:00
|
|
|
for {
|
2018-10-20 02:49:51 +08:00
|
|
|
coptions := storage.ContainerOptions{
|
|
|
|
LabelOpts: options.CommonBuildOpts.LabelOpts,
|
|
|
|
IDMappingOptions: newContainerIDMappingOptions(options.IDMappingOptions),
|
|
|
|
}
|
|
|
|
container, err = store.CreateContainer("", []string{tmpName}, imageID, "", "", &coptions)
|
|
|
|
if err == nil {
|
2018-09-13 02:34:05 +08:00
|
|
|
name = tmpName
|
2018-10-20 02:49:51 +08:00
|
|
|
break
|
2018-09-13 02:34:05 +08:00
|
|
|
}
|
2018-10-20 02:49:51 +08:00
|
|
|
if errors.Cause(err) != storage.ErrDuplicateName || options.Container != "" {
|
|
|
|
return nil, errors.Wrapf(err, "error creating container")
|
|
|
|
}
|
|
|
|
tmpName = fmt.Sprintf("%s-%d", name, rand.Int()%conflict)
|
|
|
|
conflict = conflict * 10
|
2018-09-13 02:34:05 +08:00
|
|
|
}
|
2017-02-11 00:48:15 +08:00
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
2018-10-18 04:58:32 +08:00
|
|
|
if err2 := store.DeleteContainer(container.ID); err2 != nil {
|
2017-02-11 00:48:15 +08:00
|
|
|
logrus.Errorf("error deleting container %q: %v", container.ID, err2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2018-03-08 07:11:43 +08:00
|
|
|
uidmap, gidmap := convertStorageIDMaps(container.UIDMap, container.GIDMap)
|
2018-06-27 04:35:43 +08:00
|
|
|
|
|
|
|
defaultNamespaceOptions, err := DefaultNamespaceOptions()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
namespaceOptions := defaultNamespaceOptions
|
2018-03-08 07:11:43 +08:00
|
|
|
namespaceOptions.AddOrReplace(options.NamespaceOptions...)
|
2017-10-20 05:47:15 +08:00
|
|
|
|
2017-02-11 00:48:15 +08:00
|
|
|
builder := &Builder{
|
2017-11-08 06:44:24 +08:00
|
|
|
store: store,
|
|
|
|
Type: containerType,
|
2019-11-01 03:18:10 +08:00
|
|
|
FromImage: imageSpec,
|
2017-11-08 06:44:24 +08:00
|
|
|
FromImageID: imageID,
|
2019-07-02 05:14:07 +08:00
|
|
|
FromImageDigest: imageDigest,
|
2017-11-08 06:44:24 +08:00
|
|
|
Container: name,
|
|
|
|
ContainerID: container.ID,
|
|
|
|
ImageAnnotations: map[string]string{},
|
|
|
|
ImageCreatedBy: "",
|
2018-10-20 02:49:51 +08:00
|
|
|
ProcessLabel: container.ProcessLabel(),
|
|
|
|
MountLabel: container.MountLabel(),
|
2017-11-08 06:44:24 +08:00
|
|
|
DefaultMountsFilePath: options.DefaultMountsFilePath,
|
2018-05-12 01:00:14 +08:00
|
|
|
Isolation: options.Isolation,
|
2018-03-08 07:11:43 +08:00
|
|
|
NamespaceOptions: namespaceOptions,
|
2018-04-14 06:20:25 +08:00
|
|
|
ConfigureNetwork: options.ConfigureNetwork,
|
|
|
|
CNIPluginPath: options.CNIPluginPath,
|
|
|
|
CNIConfigDir: options.CNIConfigDir,
|
2018-03-08 07:11:43 +08:00
|
|
|
IDMappingOptions: IDMappingOptions{
|
|
|
|
HostUIDMapping: len(uidmap) == 0,
|
|
|
|
HostGIDMapping: len(uidmap) == 0,
|
|
|
|
UIDMap: uidmap,
|
|
|
|
GIDMap: gidmap,
|
|
|
|
},
|
2020-01-14 20:12:56 +08:00
|
|
|
Capabilities: copyStringSlice(options.Capabilities),
|
|
|
|
CommonBuildOpts: options.CommonBuildOpts,
|
|
|
|
TopLayer: topLayer,
|
|
|
|
Args: options.Args,
|
|
|
|
Format: options.Format,
|
|
|
|
TempVolumes: map[string]bool{},
|
|
|
|
Devices: options.Devices,
|
2017-02-11 00:48:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if options.Mount {
|
2018-10-20 02:49:51 +08:00
|
|
|
_, err = builder.Mount(container.MountLabel())
|
2017-02-11 00:48:15 +08:00
|
|
|
if err != nil {
|
2018-10-03 22:05:46 +08:00
|
|
|
return nil, errors.Wrapf(err, "error mounting build container %q", builder.ContainerID)
|
2017-02-11 00:48:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-12 06:41:11 +08:00
|
|
|
if err := builder.initConfig(ctx, src); err != nil {
|
2018-06-12 05:43:21 +08:00
|
|
|
return nil, errors.Wrapf(err, "error preparing image configuration")
|
|
|
|
}
|
2017-02-11 00:48:15 +08:00
|
|
|
err = builder.Save()
|
|
|
|
if err != nil {
|
2018-10-03 22:05:46 +08:00
|
|
|
return nil, errors.Wrapf(err, "error saving builder state for container %q", builder.ContainerID)
|
2017-02-11 00:48:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return builder, nil
|
|
|
|
}
|