diff options
| author | Daniel J Walsh <dwalsh@redhat.com> | 2018-06-07 01:00:07 -0400 | 
|---|---|---|
| committer | Atomic Bot <atomic-devel@projectatomic.io> | 2018-06-13 12:49:32 +0000 | 
| commit | be217caa3856c76a6b997c203422715e13b0335a (patch) | |
| tree | 49190e0813ba860ccc74d017ccf12562e009c6bc | |
| parent | 95ea3d4f3a77d014fdd1be43411ba96a85091712 (diff) | |
| download | podman-be217caa3856c76a6b997c203422715e13b0335a.tar.gz podman-be217caa3856c76a6b997c203422715e13b0335a.tar.bz2 podman-be217caa3856c76a6b997c203422715e13b0335a.zip | |
Vendor in latest buildah code
This will add --layers support.
Also add missing information in man pages on podman build features.
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
Closes: #938
Approved by: umohnani8
| -rw-r--r-- | completions/bash/podman | 18 | ||||
| -rw-r--r-- | docs/podman-build.1.md | 124 | ||||
| -rw-r--r-- | vendor.conf | 2 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/add.go | 27 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/buildah.go | 4 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/commit.go | 4 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/image.go | 7 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/imagebuildah/build.go | 286 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/new.go | 3 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/pkg/cli/common.go | 20 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/run.go | 13 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/util.go | 51 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/util/util.go | 5 | ||||
| -rw-r--r-- | vendor/github.com/projectatomic/buildah/vendor.conf | 2 | 
14 files changed, 524 insertions, 42 deletions
| diff --git a/completions/bash/podman b/completions/bash/podman index 12e4bb523..a5fd899d8 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -697,13 +697,15 @@ _podman_commit() {  _podman_build() {       local boolean_options=" -     --build       --help       -h +     --layers +     --no-cache       --pull       --pull-always       --quiet       -q +     --squash       --tls-verify    " @@ -714,6 +716,8 @@ _podman_build() {       --build-arg       --cert-dir       --cgroup-parent +     --cni-config-dir +     --cni-plugin-path       --cpu-period       --cpu-quota       --cpu-shares @@ -724,10 +728,14 @@ _podman_build() {       --file       --format       --iidfile +     --ipc       --label       -m       --memory       --memory-swap +     --net +     --network +     --pid       --runtime       --runtime-flag       --security-opt @@ -736,8 +744,14 @@ _podman_build() {       -t       --tag       --ulimit -     -v +     --userns +     --userns-uid-map +     --userns-gid-map +     --userns-uid-map-user +     --userns-gid-map-group +     --uts       --volume +     -v    "       local all_options="$options_with_args $boolean_options" diff --git a/docs/podman-build.1.md b/docs/podman-build.1.md index bdad555d2..90f4029bb 100644 --- a/docs/podman-build.1.md +++ b/docs/podman-build.1.md @@ -62,6 +62,18 @@ This option is added to be aligned with other containers CLIs.  Podman doesn't communicate with a daemon or a remote server.  Thus, compressing the data before sending it is irrelevant to Podman. +**--cni-config-dir**=*directory* + +Location of CNI configuration files which will dictate which plugins will be +used to configure network interfaces and routing for containers created for +handling `RUN` instructions, if those containers will be run in their own +network namespaces, and networking is not disabled. + +**--cni-plugin-path**=*directory[:directory[:directory[...]]]* + +List of directories in which the CNI plugins which will be used for configuring +network namespaces can be found. +  **--cpu-period**=*0*  Limit the CPU CFS (Completely Fair Scheduler) period @@ -162,6 +174,15 @@ Recognized formats include *oci* (OCI image-spec v1.0, the default) and  Write the image ID to the file. +**--ipc** *how* + +Sets the configuration for IPC namespaces when handling `RUN` instructions. +The configured value can be "" (the empty string) or "container" to indicate +that a new IPC namespace should be created, or it can be "host" to indicate +that the IPC namespace in which `buildah` itself is being run should be reused, +or it can be the path to an IPC namespace which is already in use by +another process. +  **--isolation** [Not Supported]  Podman is not currently supported on Windows, and does not have a daemon. @@ -172,6 +193,18 @@ OCI Runtime, using the --runtime flag.  Add an image *label* (e.g. label=*value*) to the image metadata. Can be used multiple times. +**--layers** + +Cache intermediate images during the build process (Default is `false`). + +Note: You can also override the default value of layers by setting the BUILDAH_LAYERS +environment variable. `export BUILDAH_LAYERS=true` + +**--logfile** *filename* + +Log output which would be sent to standard output and standard error to the +specified file instead of to standard output and standard error. +  **--memory, -m**=""  Memory limit (format: <number>[<unit>], where unit = b, k, m or g) @@ -192,9 +225,28 @@ The format of `LIMIT` is `<number>[<unit>]`. Unit can be `b` (bytes),  `k` (kilobytes), `m` (megabytes), or `g` (gigabytes). If you don't specify a  unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap. +**--net** *how* +**--network** *how* + +Sets the configuration for network namespaces when handling `RUN` instructions. +The configured value can be "" (the empty string) or "container" to indicate +that a new network namespace should be created, or it can be "host" to indicate +that the network namespace in which `buildah` itself is being run should be +reused, or it can be the path to a network namespace which is already in use by +another process. +  **--no-cache** -Do not use caching for the container build. Podman does not currently support caching so this is a NOOP. +Do not use existing cached images for the container build. Build from the start with a new set of cached layers. + +**--pid** *how* + +Sets the configuration for PID namespaces when handling `RUN` instructions. +The configured value can be "" (the empty string) or "container" to indicate +that a new PID namespace should be created, or it can be "host" to indicate +that the PID namespace in which `buildah` itself is being run should be reused, +or it can be the path to a PID namespace which is already in use by another +process.  **--pull** @@ -293,6 +345,72 @@ include:    "sigpending": maximum number of pending signals (ulimit -i)    "stack": maximum stack size (ulimit -s) +**--userns** *how* + +Sets the configuration for user namespaces when handling `RUN` instructions. +The configured value can be "" (the empty string) or "container" to indicate +that a new user namespace should be created, it can be "host" to indicate that +the user namespace in which `buildah` itself is being run should be reused, or +it can be the path to an user namespace which is already in use by another +process. + +**--userns-uid-map** *mapping* + +Directly specifies a UID mapping which should be used to set ownership, at the +filesytem level, on the working container's contents. +Commands run when handling `RUN` instructions will default to being run in +their own user namespaces, configured using the UID and GID maps. +Entries in this map take the form of one or more triples of a starting +in-container UID, a corresponding starting host-level UID, and the number of +consecutive IDs which the map entry represents. +If none of --userns-uid-map-user, --userns-gid-map-group, or --userns-uid-map +are specified, but --userns-gid-map is specified, the UID map will be set to +use the same numeric values as the GID map. + +**--userns-gid-map** *mapping* + +Directly specifies a GID mapping which should be used to set ownership, at the +filesytem level, on the working container's contents. +Commands run when handling `RUN` instructions will default to being run in +their own user namespaces, configured using the UID and GID maps. +Entries in this map take the form of one or more triples of a starting +in-container GID, a corresponding starting host-level GID, and the number of +consecutive IDs which the map entry represents. +If none of --userns-uid-map-user, --userns-gid-map-group, or --userns-gid-map +are specified, but --userns-uid-map is specified, the GID map will be set to +use the same numeric values as the UID map. + +**--userns-uid-map-user** *user* + +Specifies that a UID mapping which should be used to set ownership, at the +filesytem level, on the working container's contents, can be found in entries +in the `/etc/subuid` file which correspond to the specified user. +Commands run when handling `RUN` instructions will default to being run in +their own user namespaces, configured using the UID and GID maps. +If --userns-gid-map-group is specified, but --userns-uid-map-user is not +specified, `buildah` will assume that the specified group name is also a +suitable user name to use as the default setting for this option. + +**--userns-gid-map-group** *group* + +Specifies that a GID mapping which should be used to set ownership, at the +filesytem level, on the working container's contents, can be found in entries +in the `/etc/subgid` file which correspond to the specified group. +Commands run when handling `RUN` instructions will default to being run in +their own user namespaces, configured using the UID and GID maps. +If --userns-uid-map-user is specified, but --userns-gid-map-group is not +specified, `buildah` will assume that the specified user name is also a +suitable group name to use as the default setting for this option. + +**--uts** *how* + +Sets the configuration for UTS namespaces when the handling `RUN` instructions. +The configured value can be "" (the empty string) or "container" to indicate +that a new UTS namespace should be created, or it can be "host" to indicate +that the UTS namespace in which `buildah` itself is being run should be reused, +or it can be the path to a UTS namespace which is already in use by another +process. +  **--volume, -v**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*]     Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, podman @@ -388,6 +506,10 @@ podman build --security-opt label=level:s0:c100,c200 --cgroup-parent /path/to/cg  podman build --volume /home/test:/myvol:ro,Z -t imageName . +podman build --layers -t imageName . + +podman build --no-cache -t imageName . +  ### Building an image using a URL, Git repo, or archive    The build context directory can be specified as a URL to a Dockerfile, a Git repository, or URL to an archive. If the URL is a Dockerfile, it is downloaded to a temporary location and used as the context. When a Git repository is set as the URL, the repository is cloned locally to a temporary location and then used as the context. Lastly, if the URL is an archive, it is downloaded to a temporary location and extracted before being used as the context. diff --git a/vendor.conf b/vendor.conf index dfe51a70f..7ee7ffbb4 100644 --- a/vendor.conf +++ b/vendor.conf @@ -88,7 +88,7 @@ k8s.io/kube-openapi 275e2ce91dec4c05a4094a7b1daee5560b555ac9 https://github.com/  k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e https://github.com/kubernetes/utils  github.com/mrunalp/fileutils master  github.com/varlink/go master -github.com/projectatomic/buildah 0bd560c3690e998569f51bc1a336b0788856c13a +github.com/projectatomic/buildah 6c4bef7f2e17432d4dc4065299cec08754ca5863  github.com/Nvveen/Gotty master  github.com/fsouza/go-dockerclient master  github.com/openshift/imagebuilder master diff --git a/vendor/github.com/projectatomic/buildah/add.go b/vendor/github.com/projectatomic/buildah/add.go index ee2cf253a..5c53c8dda 100644 --- a/vendor/github.com/projectatomic/buildah/add.go +++ b/vendor/github.com/projectatomic/buildah/add.go @@ -19,15 +19,24 @@ import (  	"github.com/sirupsen/logrus"  ) -//AddAndCopyOptions holds options for add and copy commands. +// AddAndCopyOptions holds options for add and copy commands.  type AddAndCopyOptions struct { +	// Chown is a spec for the user who should be given ownership over the +	// newly-added content, potentially overriding permissions which would +	// otherwise match those of local files and directories being copied.  	Chown string +	// All of the data being copied will pass through Hasher, if set. +	// If the sources are URLs or files, their contents will be passed to +	// Hasher. +	// If the sources include directory trees, Hasher will be passed +	// tar-format archives of the directory trees. +	Hasher io.Writer  }  // addURL copies the contents of the source URL to the destination.  This is  // its own function so that deferred closes happen after we're done pulling  // down each item of potentially many. -func addURL(destination, srcurl string, owner idtools.IDPair) error { +func addURL(destination, srcurl string, owner idtools.IDPair, hasher io.Writer) error {  	logrus.Debugf("saving %q to %q", srcurl, destination)  	resp, err := http.Get(srcurl)  	if err != nil { @@ -53,7 +62,11 @@ func addURL(destination, srcurl string, owner idtools.IDPair) error {  		}  	}  	defer f.Close() -	n, err := io.Copy(f, resp.Body) +	bodyReader := io.Reader(resp.Body) +	if hasher != nil { +		bodyReader = io.TeeReader(bodyReader, hasher) +	} +	n, err := io.Copy(f, bodyReader)  	if err != nil {  		return errors.Wrapf(err, "error reading contents for %q", destination)  	} @@ -122,9 +135,9 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption  	if len(source) > 1 && (destfi == nil || !destfi.IsDir()) {  		return errors.Errorf("destination %q is not a directory", dest)  	} -	copyFileWithTar := b.copyFileWithTar(&containerOwner) -	copyWithTar := b.copyWithTar(&containerOwner) -	untarPath := b.untarPath(nil) +	copyFileWithTar := b.copyFileWithTar(&containerOwner, options.Hasher) +	copyWithTar := b.copyWithTar(&containerOwner, options.Hasher) +	untarPath := b.untarPath(nil, options.Hasher)  	for _, src := range source {  		if strings.HasPrefix(src, "http://") || strings.HasPrefix(src, "https://") {  			// We assume that source is a file, and we're copying @@ -140,7 +153,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption  			if destfi != nil && destfi.IsDir() {  				d = filepath.Join(dest, path.Base(url.Path))  			} -			if err := addURL(d, src, hostOwner); err != nil { +			if err := addURL(d, src, hostOwner, options.Hasher); err != nil {  				return err  			}  			continue diff --git a/vendor/github.com/projectatomic/buildah/buildah.go b/vendor/github.com/projectatomic/buildah/buildah.go index e90e2ee68..5d241564c 100644 --- a/vendor/github.com/projectatomic/buildah/buildah.go +++ b/vendor/github.com/projectatomic/buildah/buildah.go @@ -23,7 +23,7 @@ const (  	Package = "buildah"  	// Version for the Package.  Bump version in contrib/rpm/buildah.spec  	// too. -	Version = "1.0" +	Version = "1.2-dev"  	// The value we use to identify what type of information, currently a  	// serialized Builder structure, we are using as per-container state.  	// This should only be changed when we make incompatible changes to @@ -165,6 +165,8 @@ type Builder struct {  	IDMappingOptions IDMappingOptions  	CommonBuildOpts *CommonBuildOptions +	// TopLayer is the top layer of the image +	TopLayer string  }  // BuilderInfo are used as objects to display container information diff --git a/vendor/github.com/projectatomic/buildah/commit.go b/vendor/github.com/projectatomic/buildah/commit.go index ab46a0643..3c5958f2d 100644 --- a/vendor/github.com/projectatomic/buildah/commit.go +++ b/vendor/github.com/projectatomic/buildah/commit.go @@ -55,6 +55,8 @@ type CommitOptions struct {  	// OnBuild is a list of commands to be run by images based on this image  	OnBuild []string +	// Parent is the base image that this image was created by. +	Parent string  }  // PushOptions can be used to alter how an image is copied somewhere. @@ -106,7 +108,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options  	// Check if we're keeping everything in local storage.  If so, we can take certain shortcuts.  	_, destIsStorage := dest.Transport().(is.StoreTransport)  	exporting := !destIsStorage -	src, err := b.makeImageRef(options.PreferredManifestType, exporting, options.Squash, options.Compression, options.HistoryTimestamp) +	src, err := b.makeImageRef(options.PreferredManifestType, options.Parent, exporting, options.Squash, options.Compression, options.HistoryTimestamp)  	if err != nil {  		return imgID, errors.Wrapf(err, "error computing layer digests and building metadata")  	} diff --git a/vendor/github.com/projectatomic/buildah/image.go b/vendor/github.com/projectatomic/buildah/image.go index 1cb5d7022..070f721d4 100644 --- a/vendor/github.com/projectatomic/buildah/image.go +++ b/vendor/github.com/projectatomic/buildah/image.go @@ -55,6 +55,7 @@ type containerImageRef struct {  	exporting             bool  	squash                bool  	tarPath               func(path string) (io.ReadCloser, error) +	parent                string  }  type containerImageSource struct { @@ -178,6 +179,7 @@ func (i *containerImageRef) createConfigsAndManifests() (v1.Image, v1.Manifest,  	if err := json.Unmarshal(i.dconfig, &dimage); err != nil {  		return v1.Image{}, v1.Manifest{}, docker.V2Image{}, docker.V2S2Manifest{}, err  	} +	dimage.Parent = docker.ID(digest.FromString(i.parent))  	// Always replace this value, since we're newer than our base image.  	dimage.Created = created  	// Clear the list of diffIDs, since we always repopulate it. @@ -404,6 +406,7 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System  		EmptyLayer: false,  	}  	dimage.History = append(dimage.History, dnews) +	dimage.Parent = docker.ID(digest.FromString(i.parent))  	// Sanity check that we didn't just create a mismatch between non-empty layers in the  	// history and the number of diffIDs. @@ -578,7 +581,7 @@ func (i *containerImageSource) GetBlob(ctx context.Context, blob types.BlobInfo)  	return ioutils.NewReadCloserWrapper(layerFile, closer), size, nil  } -func (b *Builder) makeImageRef(manifestType string, exporting bool, squash bool, compress archive.Compression, historyTimestamp *time.Time) (types.ImageReference, error) { +func (b *Builder) makeImageRef(manifestType, parent string, exporting bool, squash bool, compress archive.Compression, historyTimestamp *time.Time) (types.ImageReference, error) {  	var name reference.Named  	container, err := b.store.Container(b.ContainerID)  	if err != nil { @@ -604,6 +607,7 @@ func (b *Builder) makeImageRef(manifestType string, exporting bool, squash bool,  	if historyTimestamp != nil {  		created = historyTimestamp.UTC()  	} +  	ref := &containerImageRef{  		store:                 b.store,  		compression:           compress, @@ -622,6 +626,7 @@ func (b *Builder) makeImageRef(manifestType string, exporting bool, squash bool,  		exporting:             exporting,  		squash:                squash,  		tarPath:               b.tarPath(), +		parent:                parent,  	}  	return ref, nil  } diff --git a/vendor/github.com/projectatomic/buildah/imagebuildah/build.go b/vendor/github.com/projectatomic/buildah/imagebuildah/build.go index ce0734a2b..075e588ba 100644 --- a/vendor/github.com/projectatomic/buildah/imagebuildah/build.go +++ b/vendor/github.com/projectatomic/buildah/imagebuildah/build.go @@ -9,6 +9,7 @@ import (  	"path/filepath"  	"strconv"  	"strings" +	"time"  	is "github.com/containers/image/storage"  	"github.com/containers/image/transports" @@ -20,6 +21,7 @@ import (  	"github.com/containers/storage/pkg/stringid"  	"github.com/docker/docker/builder/dockerfile/parser"  	docker "github.com/fsouza/go-dockerclient" +	"github.com/opencontainers/image-spec/specs-go/v1"  	"github.com/opencontainers/runtime-spec/specs-go"  	"github.com/openshift/imagebuilder"  	"github.com/pkg/errors" @@ -141,6 +143,11 @@ type BuildOptions struct {  	Annotations []string  	// OnBuild commands to be run by images based on this image  	OnBuild []string +	// Layers tells the builder to create a cache of images for each step in the Dockerfile +	Layers bool +	// NoCache tells the builder to build the image from scratch without checking for a cache. +	// It creates a new set of cached images for the build. +	NoCache bool  }  // Executor is a buildah-based implementation of the imagebuilder.Executor @@ -187,6 +194,9 @@ type Executor struct {  	labels                         []string  	annotations                    []string  	onbuild                        []string +	layers                         bool +	topLayers                      []string +	noCache                        bool  }  // withName creates a new child executor that will be used whenever a COPY statement uses --from=NAME. @@ -539,6 +549,8 @@ func NewExecutor(store storage.Store, options BuildOptions) (*Executor, error) {  		squash:                options.Squash,  		labels:                append([]string{}, options.Labels...),  		annotations:           append([]string{}, options.Annotations...), +		layers:                options.Layers, +		noCache:               options.NoCache,  	}  	if exec.err == nil {  		exec.err = os.Stderr @@ -646,6 +658,9 @@ func (b *Executor) Prepare(ctx context.Context, ib *imagebuilder.Builder, node *  	}  	b.mountPoint = mountPoint  	b.builder = builder +	// Add the top  layer of this image to b.topLayers so we can keep track of them +	// when building with cached images. +	b.topLayers = append(b.topLayers, builder.TopLayer)  	return nil  } @@ -661,7 +676,9 @@ func (b *Executor) Delete() (err error) {  }  // Execute runs each of the steps in the parsed tree, in turn. -func (b *Executor) Execute(ib *imagebuilder.Builder, node *parser.Node) error { +func (b *Executor) Execute(ctx context.Context, ib *imagebuilder.Builder, node *parser.Node) error { +	checkForLayers := true +	children := node.Children  	for i, node := range node.Children {  		step := ib.Step()  		if err := step.Resolve(node); err != nil { @@ -675,42 +692,270 @@ func (b *Executor) Execute(ib *imagebuilder.Builder, node *parser.Node) error {  		if i < len(node.Children)-1 {  			requiresStart = ib.RequiresStart(&parser.Node{Children: node.Children[i+1:]})  		} -		err := ib.Run(step, b, requiresStart) -		if err != nil { -			return errors.Wrapf(err, "error building at step %+v", *step) + +		if !b.layers && !b.noCache { +			err := ib.Run(step, b, requiresStart) +			if err != nil { +				return errors.Wrapf(err, "error building at step %+v", *step) +			} +			continue +		} + +		var ( +			cacheID string +			err     error +			imgID   string +		) +		// checkForLayers will be true if b.layers is true and a cached intermediate image is found. +		// checkForLayers is set to false when either there is no cached image or a break occurs where +		// the instructions in the Dockerfile change from a previous build. +		// Don't check for cache if b.noCache is set to true. +		if checkForLayers && !b.noCache { +			cacheID, err = b.layerExists(ctx, node, children[:i]) +			if err != nil { +				return errors.Wrap(err, "error checking if cached image exists from a previous build") +			} +		} +		if cacheID == "" || !checkForLayers { +			checkForLayers = false +			err := ib.Run(step, b, requiresStart) +			if err != nil { +				return errors.Wrapf(err, "error building at step %+v", *step) +			} +		} else { +			b.log("Using cache %s", cacheID) +		} +		// Commit if at the last step of the Dockerfile and a cached image is found. +		// Also commit steps if no cache is found. +		if (cacheID != "" && i == len(children)-1) || cacheID == "" { +			imgID, err = b.Commit(ctx, ib, getCreatedBy(node)) +			if err != nil { +				return errors.Wrapf(err, "error committing container for step %+v", *step) +			} +			if i == len(children)-1 { +				b.log("COMMIT %s", b.output) +			} +		} else { +			// Cache is found, assign imgID the id of the cached image so +			// it is used to create the container for the next step. +			imgID = cacheID +		} +		// Delete the intermediate container. +		if err := b.Delete(); err != nil { +			return errors.Wrap(err, "error deleting intermediate container") +		} +		// Prepare for the next step with imgID as the new base image. +		if i != len(children)-1 { +			if err := b.Prepare(ctx, ib, node, imgID); err != nil { +				return errors.Wrap(err, "error preparing container for next step") +			}  		}  	}  	return nil  } +// layerExists returns true if an intermediate image of currNode exists in the image store from a previous build. +// It verifies tihis by checking the parent of the top layer of the image and the history. +func (b *Executor) layerExists(ctx context.Context, currNode *parser.Node, children []*parser.Node) (string, error) { +	// Get the list of images available in the image store +	images, err := b.store.Images() +	if err != nil { +		return "", errors.Wrap(err, "error getting image list from store") +	} +	for _, image := range images { +		layer, err := b.store.Layer(image.TopLayer) +		if err != nil { +			return "", errors.Wrapf(err, "error getting top layer info") +		} +		// If the parent of the top layer of an image is equal to the last entry in b.topLayers +		// it means that this image is potentially a cached intermediate image from a previous +		// build. Next we double check that the history of this image is equivalent to the previous +		// lines in the Dockerfile up till the point we are at in the build. +		if layer.Parent == b.topLayers[len(b.topLayers)-1] { +			history, err := b.getImageHistory(ctx, image.ID) +			if err != nil { +				return "", errors.Wrapf(err, "error getting history of %q", image.ID) +			} +			// children + currNode is the point of the Dockerfile we are currently at. +			if historyMatches(append(children, currNode), history) { +				// This checks if the files copied during build have been changed if the node is +				// a COPY or ADD command. +				filesMatch, err := b.copiedFilesMatch(currNode, history[len(history)-1].Created) +				if err != nil { +					return "", errors.Wrapf(err, "error checking if copied files match") +				} +				if filesMatch { +					return image.ID, nil +				} +			} +		} +	} +	return "", nil +} + +// getImageHistory returns the history of imageID. +func (b *Executor) getImageHistory(ctx context.Context, imageID string) ([]v1.History, error) { +	imageRef, err := is.Transport.ParseStoreReference(b.store, "@"+imageID) +	if err != nil { +		return nil, errors.Wrapf(err, "error getting image reference %q", imageID) +	} +	ref, err := imageRef.NewImage(ctx, nil) +	if err != nil { +		return nil, errors.Wrap(err, "error creating new image from reference") +	} +	oci, err := ref.OCIConfig(ctx) +	if err != nil { +		return nil, errors.Wrapf(err, "error getting oci config of image %q", imageID) +	} +	return oci.History, nil +} + +// getCreatedBy returns the command the image at node will be created by. +func getCreatedBy(node *parser.Node) string { +	if node.Value == "run" { +		return "/bin/sh -c " + node.Original[4:] +	} +	return "/bin/sh -c #(nop) " + node.Original +} + +// historyMatches returns true if the history of the image matches the lines +// in the Dockerfile till the point of build we are at. +// Used to verify whether a cache of the intermediate image exists and whether +// to run the build again. +func historyMatches(children []*parser.Node, history []v1.History) bool { +	i := len(history) - 1 +	for j := len(children) - 1; j >= 0; j-- { +		instruction := children[j].Original +		if children[j].Value == "run" { +			instruction = instruction[4:] +		} +		if !strings.Contains(history[i].CreatedBy, instruction) { +			return false +		} +		i-- +	} +	return true +} + +// getFilesToCopy goes through node to get all the src files that are copied, added or downloaded. +// It is possible for the Dockerfile to have src as hom*, which means all files that have hom as a prefix. +// Another format is hom?.txt, which means all files that have that name format with the ? replaced by another character. +func (b *Executor) getFilesToCopy(node *parser.Node) ([]string, error) { +	currNode := node.Next +	var src []string +	for currNode.Next != nil { +		if currNode.Next == nil { +			break +		} +		if strings.HasPrefix(currNode.Value, "http://") || strings.HasPrefix(currNode.Value, "https://") { +			src = append(src, currNode.Value) +			continue +		} +		matches, err := filepath.Glob(filepath.Join(b.contextDir, currNode.Value)) +		if err != nil { +			return nil, errors.Wrapf(err, "error finding match for pattern %q", currNode.Value) +		} +		src = append(src, matches...) +		currNode = currNode.Next +	} +	return src, nil +} + +// copiedFilesMatch checks to see if the node instruction is a COPY or ADD. +// If it is either of those two it checks the timestamps on all the files copied/added +// by the dockerfile. If the host version has a time stamp greater than the time stamp +// of the build, the build will not use the cached version and will rebuild. +func (b *Executor) copiedFilesMatch(node *parser.Node, historyTime *time.Time) (bool, error) { +	if node.Value != "add" && node.Value != "copy" { +		return true, nil +	} + +	src, err := b.getFilesToCopy(node) +	if err != nil { +		return false, err +	} +	for _, item := range src { +		// for urls, check the Last-Modified field in the header. +		if strings.HasPrefix(item, "http://") || strings.HasPrefix(item, "https://") { +			urlContentNew, err := urlContentModified(item, historyTime) +			if err != nil { +				return false, err +			} +			if urlContentNew { +				return false, nil +			} +			continue +		} +		// For local files, walk the file tree and check the time stamps. +		timeIsGreater := false +		err := filepath.Walk(item, func(path string, info os.FileInfo, err error) error { +			if info.ModTime().After(*historyTime) { +				timeIsGreater = true +				return nil +			} +			return nil +		}) +		if err != nil { +			return false, errors.Wrapf(err, "error walking file tree %q", item) +		} +		if timeIsGreater { +			return false, nil +		} +	} +	return true, nil +} + +// urlContentModified sends a get request to the url and checks if the header has a value in +// Last-Modified, and if it does compares the time stamp to that of the history of the cached image. +// returns true if there is no Last-Modified value in the header. +func urlContentModified(url string, historyTime *time.Time) (bool, error) { +	resp, err := http.Get(url) +	if err != nil { +		return false, errors.Wrapf(err, "error getting %q", url) +	} +	if lastModified := resp.Header.Get("Last-Modified"); lastModified != "" { +		lastModifiedTime, err := time.Parse(time.RFC1123, lastModified) +		if err != nil { +			return false, errors.Wrapf(err, "error parsing time for %q", url) +		} +		return lastModifiedTime.After(*historyTime), nil +	} +	logrus.Debugf("Response header did not have Last-Modified %q, will rebuild.", url) +	return true, nil +} +  // Commit writes the container's contents to an image, using a passed-in tag as  // the name if there is one, generating a unique ID-based one otherwise. -func (b *Executor) Commit(ctx context.Context, ib *imagebuilder.Builder) (err error) { -	var imageRef types.ImageReference +func (b *Executor) Commit(ctx context.Context, ib *imagebuilder.Builder, createdBy string) (string, error) { +	var ( +		imageRef types.ImageReference +		err      error +	)  	if b.output != "" {  		imageRef, err = alltransports.ParseImageName(b.output)  		if err != nil {  			candidates := util.ResolveName(b.output, "", b.systemContext, b.store)  			if len(candidates) == 0 { -				return errors.Errorf("error parsing target image name %q", b.output) +				return "", errors.Errorf("error parsing target image name %q", b.output)  			}  			imageRef2, err2 := is.Transport.ParseStoreReference(b.store, candidates[0])  			if err2 != nil { -				return errors.Wrapf(err, "error parsing target image name %q", b.output) +				return "", errors.Wrapf(err, "error parsing target image name %q", b.output)  			}  			imageRef = imageRef2  		}  	} else {  		imageRef, err = is.Transport.ParseStoreReference(b.store, "@"+stringid.GenerateRandomID())  		if err != nil { -			return errors.Wrapf(err, "error parsing reference for image to be written") +			return "", errors.Wrapf(err, "error parsing reference for image to be written")  		}  	}  	if ib.Author != "" {  		b.builder.SetMaintainer(ib.Author)  	}  	config := ib.Config() +	b.builder.SetCreatedBy(createdBy)  	b.builder.SetHostname(config.Hostname)  	b.builder.SetDomainname(config.Domainname)  	b.builder.SetUser(config.User) @@ -758,32 +1003,37 @@ func (b *Executor) Commit(ctx context.Context, ib *imagebuilder.Builder) (err er  	if imageRef != nil {  		logName := transports.ImageName(imageRef)  		logrus.Debugf("COMMIT %q", logName) -		if !b.quiet { +		if !b.quiet && !b.layers && !b.noCache {  			b.log("COMMIT %s", logName)  		}  	} else {  		logrus.Debugf("COMMIT") -		if !b.quiet { +		if !b.quiet && !b.layers && !b.noCache {  			b.log("COMMIT")  		}  	} +	writer := b.reportWriter +	if b.layers || b.noCache { +		writer = nil +	}  	options := buildah.CommitOptions{  		Compression:           b.compression,  		SignaturePolicyPath:   b.signaturePolicyPath,  		AdditionalTags:        b.additionalTags, -		ReportWriter:          b.reportWriter, +		ReportWriter:          writer,  		PreferredManifestType: b.outputFormat,  		IIDFile:               b.iidfile,  		Squash:                b.squash, +		Parent:                b.builder.FromImageID,  	}  	imgID, err := b.builder.Commit(ctx, imageRef, options)  	if err != nil { -		return err +		return "", err  	}  	if options.IIDFile == "" && imgID != "" {  		fmt.Fprintf(b.out, "%s\n", imgID)  	} -	return nil +	return imgID, nil  }  // Build takes care of the details of running Prepare/Execute/Commit/Delete @@ -799,11 +1049,15 @@ func (b *Executor) Build(ctx context.Context, stages imagebuilder.Stages) error  			return err  		}  		defer stageExecutor.Delete() -		if err := stageExecutor.Execute(stage.Builder, stage.Node); err != nil { +		if err := stageExecutor.Execute(ctx, stage.Builder, stage.Node); err != nil {  			return err  		}  	} -	return stageExecutor.Commit(ctx, stages[len(stages)-1].Builder) +	if b.layers || b.noCache { +		return nil +	} +	_, err := stageExecutor.Commit(ctx, stages[len(stages)-1].Builder, "") +	return err  }  // BuildDockerfiles parses a set of one or more Dockerfiles (which may be diff --git a/vendor/github.com/projectatomic/buildah/new.go b/vendor/github.com/projectatomic/buildah/new.go index da170d591..dc349221b 100644 --- a/vendor/github.com/projectatomic/buildah/new.go +++ b/vendor/github.com/projectatomic/buildah/new.go @@ -255,9 +255,11 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions  	}  	image := options.FromImage  	imageID := "" +	topLayer := ""  	if img != nil {  		image = getImageName(imageNamePrefix(image), img)  		imageID = img.ID +		topLayer = img.TopLayer  	}  	if manifest, config, err = imageManifestAndConfig(ctx, ref, systemContext); err != nil {  		return nil, errors.Wrapf(err, "error reading data from image %q", transports.ImageName(ref)) @@ -335,6 +337,7 @@ func newBuilder(ctx context.Context, store storage.Store, options BuilderOptions  			GIDMap:         gidmap,  		},  		CommonBuildOpts: options.CommonBuildOpts, +		TopLayer:        topLayer,  	}  	if options.Mount { diff --git a/vendor/github.com/projectatomic/buildah/pkg/cli/common.go b/vendor/github.com/projectatomic/buildah/pkg/cli/common.go index e545e97d3..d00ebbdc5 100644 --- a/vendor/github.com/projectatomic/buildah/pkg/cli/common.go +++ b/vendor/github.com/projectatomic/buildah/pkg/cli/common.go @@ -5,6 +5,10 @@ package cli  // that vendor in this code can use them too.  import ( +	"fmt" +	"os" +	"strings" +  	"github.com/opencontainers/runtime-spec/specs-go"  	"github.com/projectatomic/buildah"  	"github.com/projectatomic/buildah/util" @@ -120,8 +124,12 @@ var (  			Usage: "Set metadata for an image (default [])",  		},  		cli.BoolFlag{ +			Name:  "layers", +			Usage: fmt.Sprintf("cache intermediate layers during build (default %t)", UseLayers()), +		}, +		cli.BoolFlag{  			Name:  "no-cache", -			Usage: "Do not use caching for the container build. The build process does not currently support caching so this is a NOOP.", +			Usage: "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.",  		},  		cli.StringFlag{  			Name:  "logfile", @@ -230,3 +238,13 @@ var (  		},  	}, usernsFlags...), NamespaceFlags...)  ) + +// UseLayers returns true if BUILDAH_LAYERS is set to "1" or "true" +// otherwise it returns false +func UseLayers() bool { +	layers := os.Getenv("BUILDAH_LAYERS") +	if strings.ToLower(layers) == "true" || layers == "1" { +		return true +	} +	return false +} diff --git a/vendor/github.com/projectatomic/buildah/run.go b/vendor/github.com/projectatomic/buildah/run.go index 436c2ea2e..9d7fb8d7d 100644 --- a/vendor/github.com/projectatomic/buildah/run.go +++ b/vendor/github.com/projectatomic/buildah/run.go @@ -393,7 +393,7 @@ func (b *Builder) setupMounts(mountPoint string, spec *specs.Spec, optionMounts  	// Add temporary copies of the contents of volume locations at the  	// volume locations, unless we already have something there. -	copyWithTar := b.copyWithTar(nil) +	copyWithTar := b.copyWithTar(nil, nil)  	builtins, err := runSetupBuiltinVolumes(b.MountLabel, mountPoint, cdir, copyWithTar, builtinVolumes)  	if err != nil {  		return err @@ -534,7 +534,7 @@ func runSetupVolumeMounts(mountLabel string, volumeMounts []string, optionMounts  // addNetworkConfig copies files from host and sets them up to bind mount into container  func (b *Builder) addNetworkConfig(rdir, hostPath string) (string, error) { -	copyFileWithTar := b.copyFileWithTar(nil) +	copyFileWithTar := b.copyFileWithTar(nil, nil)  	cfile := filepath.Join(rdir, filepath.Base(hostPath)) @@ -809,6 +809,15 @@ func (b *Builder) Run(command []string, options RunOptions) error {  	// Now grab the spec from the generator.  Set the generator to nil so that future contributors  	// will quickly be able to tell that they're supposed to be modifying the spec directly from here.  	spec := g.Spec() + +	//Remove capabilities if not running as root +	if user.UID != 0 { +		var caplist []string +		spec.Process.Capabilities.Permitted = caplist +		spec.Process.Capabilities.Inheritable = caplist +		spec.Process.Capabilities.Effective = caplist +		spec.Process.Capabilities.Ambient = caplist +	}  	g = nil  	if spec.Process.Cwd == "" {  		spec.Process.Cwd = DefaultWorkingDir diff --git a/vendor/github.com/projectatomic/buildah/util.go b/vendor/github.com/projectatomic/buildah/util.go index 1b8667b79..661221ebc 100644 --- a/vendor/github.com/projectatomic/buildah/util.go +++ b/vendor/github.com/projectatomic/buildah/util.go @@ -1,11 +1,13 @@  package buildah  import ( +	"archive/tar"  	"bufio"  	"io"  	"os"  	"strconv"  	"strings" +	"sync"  	"github.com/containers/image/docker/reference"  	"github.com/containers/image/pkg/sysregistries" @@ -91,30 +93,73 @@ func convertRuntimeIDMaps(UIDMap, GIDMap []rspec.LinuxIDMapping) ([]idtools.IDMa  // copyFileWithTar returns a function which copies a single file from outside  // of any container into our working container, mapping permissions using the  // container's ID maps, possibly overridden using the passed-in chownOpts -func (b *Builder) copyFileWithTar(chownOpts *idtools.IDPair) func(src, dest string) error { +func (b *Builder) copyFileWithTar(chownOpts *idtools.IDPair, hasher io.Writer) func(src, dest string) error {  	convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)  	untarMappings := idtools.NewIDMappingsFromMaps(convertedUIDMap, convertedGIDMap)  	archiver := chrootarchive.NewArchiverWithChown(nil, chownOpts, untarMappings) +	if hasher != nil { +		originalUntar := archiver.Untar +		archiver.Untar = func(tarArchive io.Reader, dest string, options *archive.TarOptions) error { +			contentReader, contentWriter, err := os.Pipe() +			if err != nil { +				return err +			} +			defer contentReader.Close() +			defer contentWriter.Close() +			var hashError error +			var hashWorker sync.WaitGroup +			hashWorker.Add(1) +			go func() { +				t := tar.NewReader(contentReader) +				_, err := t.Next() +				if err != nil { +					hashError = err +				} +				if _, err = io.Copy(hasher, t); err != nil && err != io.EOF { +					hashError = err +				} +				hashWorker.Done() +			}() +			err = originalUntar(io.TeeReader(tarArchive, contentWriter), dest, options) +			hashWorker.Wait() +			if err == nil { +				err = hashError +			} +			return err +		} +	}  	return archiver.CopyFileWithTar  }  // copyWithTar returns a function which copies a directory tree from outside of  // any container into our working container, mapping permissions using the  // container's ID maps, possibly overridden using the passed-in chownOpts -func (b *Builder) copyWithTar(chownOpts *idtools.IDPair) func(src, dest string) error { +func (b *Builder) copyWithTar(chownOpts *idtools.IDPair, hasher io.Writer) func(src, dest string) error {  	convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)  	untarMappings := idtools.NewIDMappingsFromMaps(convertedUIDMap, convertedGIDMap)  	archiver := chrootarchive.NewArchiverWithChown(nil, chownOpts, untarMappings) +	if hasher != nil { +		originalUntar := archiver.Untar +		archiver.Untar = func(tarArchive io.Reader, dest string, options *archive.TarOptions) error { +			return originalUntar(io.TeeReader(tarArchive, hasher), dest, options) +		} +	}  	return archiver.CopyWithTar  }  // untarPath returns a function which extracts an archive in a specified  // location into our working container, mapping permissions using the  // container's ID maps, possibly overridden using the passed-in chownOpts -func (b *Builder) untarPath(chownOpts *idtools.IDPair) func(src, dest string) error { +func (b *Builder) untarPath(chownOpts *idtools.IDPair, hasher io.Writer) func(src, dest string) error {  	convertedUIDMap, convertedGIDMap := convertRuntimeIDMaps(b.IDMappingOptions.UIDMap, b.IDMappingOptions.GIDMap)  	untarMappings := idtools.NewIDMappingsFromMaps(convertedUIDMap, convertedGIDMap)  	archiver := chrootarchive.NewArchiverWithChown(nil, chownOpts, untarMappings) +	if hasher != nil { +		originalUntar := archiver.Untar +		archiver.Untar = func(tarArchive io.Reader, dest string, options *archive.TarOptions) error { +			return originalUntar(io.TeeReader(tarArchive, hasher), dest, options) +		} +	}  	return archiver.UntarPath  } diff --git a/vendor/github.com/projectatomic/buildah/util/util.go b/vendor/github.com/projectatomic/buildah/util/util.go index e8539f978..3f1d7530c 100644 --- a/vendor/github.com/projectatomic/buildah/util/util.go +++ b/vendor/github.com/projectatomic/buildah/util/util.go @@ -112,11 +112,6 @@ func ResolveName(name string, firstRegistry string, sc *types.SystemContext, sto  		logrus.Debugf("unable to read configured registries to complete %q: %v", name, err)  		registries = []string{}  	} -	if sc.DockerInsecureSkipTLSVerify { -		if unverifiedRegistries, err := sysregistries.GetInsecureRegistries(sc); err == nil { -			registries = append(registries, unverifiedRegistries...) -		} -	}  	// Create all of the combinations.  Some registries need an additional component added, so  	// use our lookaside map to keep track of them.  If there are no configured registries, we'll diff --git a/vendor/github.com/projectatomic/buildah/vendor.conf b/vendor/github.com/projectatomic/buildah/vendor.conf index 50f91b072..4a00ad05e 100644 --- a/vendor/github.com/projectatomic/buildah/vendor.conf +++ b/vendor/github.com/projectatomic/buildah/vendor.conf @@ -38,7 +38,7 @@ github.com/ostreedev/ostree-go aeb02c6b6aa2889db3ef62f7855650755befd460  github.com/pborman/uuid master  github.com/pkg/errors master  github.com/pquerna/ffjson d49c2bc1aa135aad0c6f4fc2056623ec78f5d5ac -github.com/projectatomic/libpod e686269da34ed4208f4ed517c0587ab38e8eaf2c +github.com/projectatomic/libpod 781eec27b52c842fc83c8b1c97fbf825065f3b0c  github.com/sirupsen/logrus master  github.com/syndtr/gocapability master  github.com/tchap/go-patricia master | 
