Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/buildah/bud.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ func budCmd(c *cobra.Command, inputArgs []string, iopts budOptions) error {
MaxPullPushRetries: maxPullPushRetries,
NamespaceOptions: namespaceOptions,
NoCache: iopts.NoCache,
OmitTimestamp: iopts.OmitTimestamp,
OS: imageOS,
Out: stdout,
Output: output,
Expand Down
1 change: 1 addition & 0 deletions contrib/completions/bash/buildah
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ return 1
-h
--layers
--no-cache
--omit-timestamps
--pull
--pull-always
--pull-never
Expand Down
8 changes: 8 additions & 0 deletions docs/buildah-bud.md
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,14 @@ another process.

Do not use existing cached images for the container build. Build from the start with a new set of cached layers.

**--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.

**--os**="OS"

Set the OS of the image to the provided value instead of using the current operating system of the host.
Expand Down
36 changes: 31 additions & 5 deletions image.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package buildah

import (
"archive/tar"
"bytes"
"context"
"encoding/json"
Expand Down Expand Up @@ -356,7 +357,6 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
}
}
srcHasher := digest.Canonical.Digester()
reader := io.TeeReader(rc, srcHasher.Hash())
// Set up to write the possibly-recompressed blob.
layerFile, err := os.OpenFile(filepath.Join(path, "layer"), os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
Expand All @@ -367,14 +367,40 @@ func (i *containerImageRef) NewImageSource(ctx context.Context, sc *types.System
counter := ioutils.NewWriteCounter(layerFile)
multiWriter := io.MultiWriter(counter, destHasher.Hash())
// Compress the layer, if we're recompressing it.
writer, err := archive.CompressStream(multiWriter, i.compression)
writeCloser, err := archive.CompressStream(multiWriter, i.compression)
if err != nil {
layerFile.Close()
rc.Close()
return nil, errors.Wrapf(err, "error compressing %s", what)
}
size, err := io.Copy(writer, reader)
writer.Close()
writer := io.MultiWriter(writeCloser, srcHasher.Hash())
// Zero out timestamps in the layer, if we're doing that for
// history entries.
if i.created.Equal(time.Unix(0, 0)) {
nestedWriteCloser := ioutils.NewWriteCloserWrapper(writer, writeCloser.Close)
writeCloser = newTarFilterer(nestedWriteCloser, func(hdr *tar.Header) (bool, bool, io.Reader) {
// Changing a zeroed field to a non-zero field
// can affect the format that the library uses
// for writing the header, so only change
// fields that are already set to avoid
// changing the format (and as a result,
// changing the length) of the header that we
// write.
if !hdr.ModTime.IsZero() {
hdr.ModTime = i.created
}
if !hdr.AccessTime.IsZero() {
hdr.AccessTime = i.created
}
if !hdr.ChangeTime.IsZero() {
hdr.ChangeTime = i.created
}
return false, false, nil
})
writer = io.Writer(writeCloser)
}
size, err := io.Copy(writer, rc)
writeCloser.Close()
layerFile.Close()
rc.Close()
if err != nil {
Expand Down Expand Up @@ -679,7 +705,7 @@ func (b *Builder) makeImageRef(options CommitOptions, exporting bool) (types.Ima
}

if options.OmitTimestamp {
created = time.Unix(0, 0)
created = time.Unix(0, 0).UTC()
}

parent := ""
Expand Down
3 changes: 3 additions & 0 deletions imagebuildah/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ type BuildOptions struct {
SignBy string
// Architecture specifies the target architecture of the image to be built.
Architecture string
// OmitTimestamp forces epoch 0 as created timestamp to allow for
// deterministic, content-addressable builds.
OmitTimestamp bool
// OS is the specifies the operating system of the image to be built.
OS string
// MaxPullPushRetries is the maximum number of attempts we'll make to pull or push any one
Expand Down
2 changes: 2 additions & 0 deletions imagebuildah/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ type Executor struct {
devices []configs.Device
signBy string
architecture string
omitTimestamp bool
os string
maxPullPushRetries int
retryPullPushDelay time.Duration
Expand Down Expand Up @@ -200,6 +201,7 @@ func NewExecutor(store storage.Store, options BuildOptions, mainNode *parser.Nod
devices: devices,
signBy: options.SignBy,
architecture: options.Architecture,
omitTimestamp: options.OmitTimestamp,
os: options.OS,
maxPullPushRetries: options.MaxPullPushRetries,
retryPullPushDelay: options.PullPushRetryDelay,
Expand Down
1 change: 1 addition & 0 deletions imagebuildah/stage_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,7 @@ func (s *StageExecutor) commit(ctx context.Context, createdBy string, emptyLayer
SignBy: s.executor.signBy,
MaxRetries: s.executor.maxPullPushRetries,
RetryDelay: s.executor.retryPullPushDelay,
OmitTimestamp: s.executor.omitTimestamp,
}
imgID, _, manifestDigest, err := s.builder.Commit(ctx, imageRef, options)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions pkg/cli/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type BudResults struct {
Logfile string
Loglevel int
NoCache bool
OmitTimestamp bool
OS string
Platform string
Pull bool
Expand Down Expand Up @@ -168,6 +169,7 @@ func GetBudFlags(flags *BudResults) pflag.FlagSet {
fs.BoolVar(&flags.NoCache, "no-cache", false, "Do not use existing cached images for the container build. Build from the start with a new set of cached layers.")
fs.StringVar(&flags.Logfile, "logfile", "", "log to `file` instead of stdout/stderr")
fs.IntVar(&flags.Loglevel, "loglevel", 0, "adjust logging level (range from -2 to 3)")
fs.BoolVar(&flags.OmitTimestamp, "omit-timestamp", false, "set created timestamp to epoch 0 to allow for deterministic builds")
fs.StringVar(&flags.OS, "os", runtime.GOOS, "set the OS to the provided value instead of the current operating system of the host")
fs.StringVar(&flags.Platform, "platform", parse.DefaultPlatform(), "set the OS/ARCH to the provided value instead of the current operating system and architecture of the host (for example `linux/arm`)")
fs.BoolVar(&flags.Pull, "pull", true, "pull the image from the registry if newer or not present in store, if false, only pull the image if not present")
Expand Down