summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel J Walsh <dwalsh@redhat.com>2018-06-07 01:00:07 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2018-06-13 12:49:32 +0000
commitbe217caa3856c76a6b997c203422715e13b0335a (patch)
tree49190e0813ba860ccc74d017ccf12562e009c6bc
parent95ea3d4f3a77d014fdd1be43411ba96a85091712 (diff)
downloadpodman-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/podman18
-rw-r--r--docs/podman-build.1.md124
-rw-r--r--vendor.conf2
-rw-r--r--vendor/github.com/projectatomic/buildah/add.go27
-rw-r--r--vendor/github.com/projectatomic/buildah/buildah.go4
-rw-r--r--vendor/github.com/projectatomic/buildah/commit.go4
-rw-r--r--vendor/github.com/projectatomic/buildah/image.go7
-rw-r--r--vendor/github.com/projectatomic/buildah/imagebuildah/build.go286
-rw-r--r--vendor/github.com/projectatomic/buildah/new.go3
-rw-r--r--vendor/github.com/projectatomic/buildah/pkg/cli/common.go20
-rw-r--r--vendor/github.com/projectatomic/buildah/run.go13
-rw-r--r--vendor/github.com/projectatomic/buildah/util.go51
-rw-r--r--vendor/github.com/projectatomic/buildah/util/util.go5
-rw-r--r--vendor/github.com/projectatomic/buildah/vendor.conf2
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