diff --git a/cmd/buildah/commit.go b/cmd/buildah/commit.go index 917d00b90..838382629 100644 --- a/cmd/buildah/commit.go +++ b/cmd/buildah/commit.go @@ -52,6 +52,10 @@ var ( Name: "iidfile", Usage: "Write the image ID to the file", }, + cli.BoolFlag{ + Name: "omit-timestamp", + Usage: "set created timestamp to epoch 0 to allow for deterministic builds", + }, cli.BoolFlag{ Name: "quiet, q", Usage: "don't output progress information when writing images", @@ -172,6 +176,7 @@ func commitCmd(c *cli.Context) error { IIDFile: c.String("iidfile"), Squash: c.Bool("squash"), BlobDirectory: c.String("blob-cache"), + OmitTimestamp: c.Bool("omit-timestamp"), } if !c.Bool("quiet") { options.ReportWriter = os.Stderr diff --git a/commit.go b/commit.go index 591ead4e6..72021b503 100644 --- a/commit.go +++ b/commit.go @@ -67,6 +67,10 @@ type CommitOptions struct { OnBuild []string // Parent is the base image that this image was created by. Parent string + + // OmitTimestamp forces epoch 0 as created timestamp to allow for + // deterministic, content-addressable builds. + OmitTimestamp bool } // PushOptions can be used to alter how an image is copied somewhere. @@ -140,7 +144,7 @@ func (b *Builder) Commit(ctx context.Context, dest types.ImageReference, options } } } - src, err := b.makeImageRef(options.PreferredManifestType, options.Parent, exportBaseLayers, options.Squash, options.BlobDirectory, options.Compression, options.HistoryTimestamp) + src, err := b.makeImageRef(options.PreferredManifestType, options.Parent, exportBaseLayers, options.Squash, options.BlobDirectory, options.Compression, options.HistoryTimestamp, options.OmitTimestamp) if err != nil { return imgID, nil, "", errors.Wrapf(err, "error computing layer digests and building metadata for container %q", b.ContainerID) } diff --git a/contrib/completions/bash/buildah b/contrib/completions/bash/buildah index ec105042f..49faccb92 100644 --- a/contrib/completions/bash/buildah +++ b/contrib/completions/bash/buildah @@ -317,6 +317,7 @@ return 1 --rm --squash --tls-verify + --omit-timestamp " local options_with_args=" diff --git a/docs/buildah-commit.md b/docs/buildah-commit.md index 678bf4c1e..cf4ca0607 100644 --- a/docs/buildah-commit.md +++ b/docs/buildah-commit.md @@ -76,6 +76,14 @@ Squash all of the new image's layers (including those inherited from a base imag Require HTTPS and verify certificates when talking to container registries (defaults to true) +**--omit-timestamp** *bool-value* + +Set the create timestamp to epoch 0 to allow for deterministic builds (defaults to false). +By default, the created timestamp is changed and written into the image manifest with every commit, +causing the image's sha256 hash to be different even if the sources are exactly the same otherwise. +When --omit-timestamp is set to true, the created timestamp is always set to the epoch and therefore not +changed, allowing the image's sha256 to remain the same. + ## EXAMPLE This example saves an image based on the container. diff --git a/image.go b/image.go index e4318b26d..b0876fb6d 100644 --- a/image.go +++ b/image.go @@ -635,7 +635,7 @@ func (i *containerImageSource) GetBlob(ctx context.Context, blob types.BlobInfo, return ioutils.NewReadCloserWrapper(layerFile, closer), size, nil } -func (b *Builder) makeImageRef(manifestType, parent string, exporting bool, squash bool, blobDirectory string, compress archive.Compression, historyTimestamp *time.Time) (types.ImageReference, error) { +func (b *Builder) makeImageRef(manifestType, parent string, exporting bool, squash bool, blobDirectory string, compress archive.Compression, historyTimestamp *time.Time, omitTimestamp bool) (types.ImageReference, error) { var name reference.Named container, err := b.store.Container(b.ContainerID) if err != nil { @@ -662,6 +662,10 @@ func (b *Builder) makeImageRef(manifestType, parent string, exporting bool, squa created = historyTimestamp.UTC() } + if omitTimestamp { + created = time.Unix(0, 0) + } + ref := &containerImageRef{ store: b.store, compression: compress,