Implement ADD checksum flag #5135
See https://docs.docker.com/build/dockerfile/release-notes/#160. Fixes #5135 Signed-off-by: Jean-Francois Roy <jf@devklog.net>
This commit is contained in:
parent
1d30520e87
commit
99cad6ee1a
32
add.go
32
add.go
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/containers/storage/pkg/fileutils"
|
"github.com/containers/storage/pkg/fileutils"
|
||||||
"github.com/containers/storage/pkg/idtools"
|
"github.com/containers/storage/pkg/idtools"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
digest "github.com/opencontainers/go-digest"
|
||||||
"github.com/opencontainers/runc/libcontainer/userns"
|
"github.com/opencontainers/runc/libcontainer/userns"
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -35,6 +36,9 @@ type AddAndCopyOptions struct {
|
||||||
// newly-added content, potentially overriding permissions which would
|
// newly-added content, potentially overriding permissions which would
|
||||||
// otherwise be set to 0:0.
|
// otherwise be set to 0:0.
|
||||||
Chown string
|
Chown string
|
||||||
|
// Checksum is a standard container digest string (e.g. <algorithm>:<digest>)
|
||||||
|
// and is the expected hash of the content being copied.
|
||||||
|
Checksum string
|
||||||
// PreserveOwnership, if Chown is not set, tells us to avoid setting
|
// PreserveOwnership, if Chown is not set, tells us to avoid setting
|
||||||
// ownership of copied items to 0:0, instead using whatever ownership
|
// ownership of copied items to 0:0, instead using whatever ownership
|
||||||
// information is already set. Not meaningful for remote sources or
|
// information is already set. Not meaningful for remote sources or
|
||||||
|
@ -77,7 +81,7 @@ func sourceIsRemote(source string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getURL writes a tar archive containing the named content
|
// getURL writes a tar archive containing the named content
|
||||||
func getURL(src string, chown *idtools.IDPair, mountpoint, renameTarget string, writer io.Writer, chmod *os.FileMode) error {
|
func getURL(src string, chown *idtools.IDPair, mountpoint, renameTarget string, writer io.Writer, chmod *os.FileMode, srcDigest digest.Digest) error {
|
||||||
url, err := url.Parse(src)
|
url, err := url.Parse(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -110,7 +114,7 @@ func getURL(src string, chown *idtools.IDPair, mountpoint, renameTarget string,
|
||||||
}
|
}
|
||||||
// Figure out the size of the content.
|
// Figure out the size of the content.
|
||||||
size := response.ContentLength
|
size := response.ContentLength
|
||||||
responseBody := response.Body
|
var responseBody io.Reader = response.Body
|
||||||
if size < 0 {
|
if size < 0 {
|
||||||
// Create a temporary file and copy the content to it, so that
|
// Create a temporary file and copy the content to it, so that
|
||||||
// we can figure out how much content there is.
|
// we can figure out how much content there is.
|
||||||
|
@ -130,6 +134,11 @@ func getURL(src string, chown *idtools.IDPair, mountpoint, renameTarget string,
|
||||||
}
|
}
|
||||||
responseBody = f
|
responseBody = f
|
||||||
}
|
}
|
||||||
|
var digester digest.Digester
|
||||||
|
if srcDigest != "" {
|
||||||
|
digester = srcDigest.Algorithm().Digester()
|
||||||
|
responseBody = io.TeeReader(responseBody, digester.Hash())
|
||||||
|
}
|
||||||
// Write the output archive. Set permissions for compatibility.
|
// Write the output archive. Set permissions for compatibility.
|
||||||
tw := tar.NewWriter(writer)
|
tw := tar.NewWriter(writer)
|
||||||
defer tw.Close()
|
defer tw.Close()
|
||||||
|
@ -161,6 +170,12 @@ func getURL(src string, chown *idtools.IDPair, mountpoint, renameTarget string,
|
||||||
return fmt.Errorf("writing content from %q to tar stream: %w", src, err)
|
return fmt.Errorf("writing content from %q to tar stream: %w", src, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if digester != nil {
|
||||||
|
if responseDigest := digester.Digest(); responseDigest != srcDigest {
|
||||||
|
return fmt.Errorf("unexpected response digest for %q: %s, want %s", src, responseDigest, srcDigest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,9 +407,16 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
if sourceIsRemote(src) {
|
if sourceIsRemote(src) {
|
||||||
pipeReader, pipeWriter := io.Pipe()
|
pipeReader, pipeWriter := io.Pipe()
|
||||||
|
var srcDigest digest.Digest
|
||||||
|
if options.Checksum != "" {
|
||||||
|
srcDigest, err = digest.Parse(options.Checksum)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid checksum flag: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
getErr = getURL(src, chownFiles, mountPoint, renameTarget, pipeWriter, chmodDirsFiles)
|
getErr = getURL(src, chownFiles, mountPoint, renameTarget, pipeWriter, chmodDirsFiles, srcDigest)
|
||||||
pipeWriter.Close()
|
pipeWriter.Close()
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
|
@ -441,6 +463,10 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options.Checksum != "" {
|
||||||
|
return fmt.Errorf("checksum flag is not supported for local sources")
|
||||||
|
}
|
||||||
|
|
||||||
// Dig out the result of running glob+stat on this source spec.
|
// Dig out the result of running glob+stat on this source spec.
|
||||||
var localSourceStat *copier.StatsForGlob
|
var localSourceStat *copier.StatsForGlob
|
||||||
for _, st := range localSourceStats {
|
for _, st := range localSourceStats {
|
||||||
|
|
|
@ -22,6 +22,7 @@ type addCopyResults struct {
|
||||||
addHistory bool
|
addHistory bool
|
||||||
chmod string
|
chmod string
|
||||||
chown string
|
chown string
|
||||||
|
checksum string
|
||||||
quiet bool
|
quiet bool
|
||||||
ignoreFile string
|
ignoreFile string
|
||||||
contextdir string
|
contextdir string
|
||||||
|
@ -67,6 +68,7 @@ func applyFlagVars(flags *pflag.FlagSet, opts *addCopyResults) {
|
||||||
if err := flags.MarkHidden("cert-dir"); err != nil {
|
if err := flags.MarkHidden("cert-dir"); err != nil {
|
||||||
panic(fmt.Sprintf("error marking cert-dir as hidden: %v", err))
|
panic(fmt.Sprintf("error marking cert-dir as hidden: %v", err))
|
||||||
}
|
}
|
||||||
|
flags.StringVar(&opts.checksum, "checksum", "", "checksum the HTTP source content")
|
||||||
flags.StringVar(&opts.chown, "chown", "", "set the user and group ownership of the destination content")
|
flags.StringVar(&opts.chown, "chown", "", "set the user and group ownership of the destination content")
|
||||||
flags.StringVar(&opts.chmod, "chmod", "", "set the access permissions of the destination content")
|
flags.StringVar(&opts.chmod, "chmod", "", "set the access permissions of the destination content")
|
||||||
flags.StringVar(&opts.creds, "creds", "", "use `[username[:password]]` for accessing registries when pulling images")
|
flags.StringVar(&opts.creds, "creds", "", "use `[username[:password]]` for accessing registries when pulling images")
|
||||||
|
@ -235,6 +237,7 @@ func addAndCopyCmd(c *cobra.Command, args []string, verb string, iopts addCopyRe
|
||||||
options := buildah.AddAndCopyOptions{
|
options := buildah.AddAndCopyOptions{
|
||||||
Chmod: iopts.chmod,
|
Chmod: iopts.chmod,
|
||||||
Chown: iopts.chown,
|
Chown: iopts.chown,
|
||||||
|
Checksum: iopts.checksum,
|
||||||
ContextDir: contextdir,
|
ContextDir: contextdir,
|
||||||
IDMappingOptions: idMappingOptions,
|
IDMappingOptions: idMappingOptions,
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,11 @@ Defaults to false.
|
||||||
Note: You can also override the default value of --add-history by setting the
|
Note: You can also override the default value of --add-history by setting the
|
||||||
BUILDAH\_HISTORY environment variable. `export BUILDAH_HISTORY=true`
|
BUILDAH\_HISTORY environment variable. `export BUILDAH_HISTORY=true`
|
||||||
|
|
||||||
|
**--checksum** *checksum*
|
||||||
|
|
||||||
|
Checksum the source content. The value of *checksum* must be a standard
|
||||||
|
container digest string. Only supported for HTTP sources.
|
||||||
|
|
||||||
**--chmod** *permissions*
|
**--chmod** *permissions*
|
||||||
|
|
||||||
Sets the access permissions of the destination content. Accepts the numerical format.
|
Sets the access permissions of the destination content. Accepts the numerical format.
|
||||||
|
|
|
@ -21,6 +21,11 @@ Defaults to false.
|
||||||
Note: You can also override the default value of --add-history by setting the
|
Note: You can also override the default value of --add-history by setting the
|
||||||
BUILDAH\_HISTORY environment variable. `export BUILDAH_HISTORY=true`
|
BUILDAH\_HISTORY environment variable. `export BUILDAH_HISTORY=true`
|
||||||
|
|
||||||
|
**--checksum** *checksum*
|
||||||
|
|
||||||
|
Checksum the source content. The value of *checksum* must be a standard
|
||||||
|
container digest string. Only supported for HTTP sources.
|
||||||
|
|
||||||
**--chmod** *permissions*
|
**--chmod** *permissions*
|
||||||
|
|
||||||
Sets the access permissions of the destination content. Accepts the numerical format.
|
Sets the access permissions of the destination content. Accepts the numerical format.
|
||||||
|
|
|
@ -473,6 +473,7 @@ func (s *StageExecutor) Copy(excludes []string, copies ...imagebuilder.Copy) err
|
||||||
options := buildah.AddAndCopyOptions{
|
options := buildah.AddAndCopyOptions{
|
||||||
Chmod: copy.Chmod,
|
Chmod: copy.Chmod,
|
||||||
Chown: copy.Chown,
|
Chown: copy.Chown,
|
||||||
|
Checksum: copy.Checksum,
|
||||||
PreserveOwnership: preserveOwnership,
|
PreserveOwnership: preserveOwnership,
|
||||||
ContextDir: contextDir,
|
ContextDir: contextDir,
|
||||||
Excludes: copyExcludes,
|
Excludes: copyExcludes,
|
||||||
|
@ -1118,8 +1119,8 @@ func (s *StageExecutor) Execute(ctx context.Context, base string) (imgID string,
|
||||||
if command == "COPY" && (flag == "--chmod" || flag == "--chown" || flag == "--from") {
|
if command == "COPY" && (flag == "--chmod" || flag == "--chown" || flag == "--from") {
|
||||||
return "", nil, false, fmt.Errorf("COPY only supports the --chmod=<permissions> --chown=<uid:gid> and the --from=<image|stage> flags")
|
return "", nil, false, fmt.Errorf("COPY only supports the --chmod=<permissions> --chown=<uid:gid> and the --from=<image|stage> flags")
|
||||||
}
|
}
|
||||||
if command == "ADD" && (flag == "--chmod" || flag == "--chown") {
|
if command == "ADD" && (flag == "--chmod" || flag == "--chown" || flag == "--checksum") {
|
||||||
return "", nil, false, fmt.Errorf("ADD only supports the --chmod=<permissions> and the --chown=<uid:gid> flags")
|
return "", nil, false, fmt.Errorf("ADD only supports the --chmod=<permissions>, --chown=<uid:gid>, and --checksum=<checksum> flags")
|
||||||
}
|
}
|
||||||
if strings.Contains(flag, "--from") && command == "COPY" {
|
if strings.Contains(flag, "--from") && command == "COPY" {
|
||||||
arr := strings.Split(flag, "=")
|
arr := strings.Split(flag, "=")
|
||||||
|
|
|
@ -281,3 +281,28 @@ stuff/mystuff"
|
||||||
cmp $ubuntu/etc/passwd ${croot}/tmp/passwd
|
cmp $ubuntu/etc/passwd ${croot}/tmp/passwd
|
||||||
cmp $ubuntu/etc/passwd ${croot}/tmp/passwd2
|
cmp $ubuntu/etc/passwd ${croot}/tmp/passwd2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "add url with checksum flag" {
|
||||||
|
_prefetch busybox
|
||||||
|
run_buildah from --quiet $WITH_POLICY_JSON busybox
|
||||||
|
cid=$output
|
||||||
|
run_buildah add --checksum=sha256:4fd3aed66b5488b45fe83dd11842c2324fadcc38e1217bb45fbd28d660afdd39 $cid https://raw.githubusercontent.com/containers/buildah/bf3b55ba74102cc2503eccbaeffe011728d46b20/README.md /
|
||||||
|
run_buildah run $cid ls /README.md
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "add url with bad checksum" {
|
||||||
|
_prefetch busybox
|
||||||
|
run_buildah from --quiet $WITH_POLICY_JSON busybox
|
||||||
|
cid=$output
|
||||||
|
run_buildah 125 add --checksum=sha256:0000000000000000000000000000000000000000000000000000000000000000 $cid https://raw.githubusercontent.com/containers/buildah/bf3b55ba74102cc2503eccbaeffe011728d46b20/README.md /
|
||||||
|
expect_output --substring "unexpected response digest for \"https://raw.githubusercontent.com/containers/buildah/bf3b55ba74102cc2503eccbaeffe011728d46b20/README.md\": sha256:4fd3aed66b5488b45fe83dd11842c2324fadcc38e1217bb45fbd28d660afdd39, want sha256:0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "add path with checksum flag" {
|
||||||
|
_prefetch busybox
|
||||||
|
createrandom ${TEST_SCRATCH_DIR}/randomfile
|
||||||
|
run_buildah from --quiet $WITH_POLICY_JSON busybox
|
||||||
|
cid=$output
|
||||||
|
run_buildah 125 add --checksum=sha256:0000000000000000000000000000000000000000000000000000000000000000 $cid ${TEST_SCRATCH_DIR}/randomfile /
|
||||||
|
expect_output --substring "checksum flag is not supported for local sources"
|
||||||
|
}
|
||||||
|
|
|
@ -3088,7 +3088,7 @@ _EOF
|
||||||
imgName=alpine-image
|
imgName=alpine-image
|
||||||
ctrName=alpine-chown
|
ctrName=alpine-chown
|
||||||
run_buildah 125 build $WITH_POLICY_JSON --layers -t ${imgName} -f $BUDFILES/add-chown/Dockerfile.bad $BUDFILES/add-chown
|
run_buildah 125 build $WITH_POLICY_JSON --layers -t ${imgName} -f $BUDFILES/add-chown/Dockerfile.bad $BUDFILES/add-chown
|
||||||
expect_output --substring "ADD only supports the --chmod=<permissions> and the --chown=<uid:gid> flags"
|
expect_output --substring "ADD only supports the --chmod=<permissions>, --chown=<uid:gid>, and --checksum=<checksum> flags"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "bud with chmod add with bad chmod flag in Dockerfile with --layers" {
|
@test "bud with chmod add with bad chmod flag in Dockerfile with --layers" {
|
||||||
|
@ -3096,7 +3096,30 @@ _EOF
|
||||||
imgName=alpine-image
|
imgName=alpine-image
|
||||||
ctrName=alpine-chmod
|
ctrName=alpine-chmod
|
||||||
run_buildah 125 build $WITH_POLICY_JSON --layers -t ${imgName} -f $BUDFILES/add-chmod/Dockerfile.bad $BUDFILES/add-chmod
|
run_buildah 125 build $WITH_POLICY_JSON --layers -t ${imgName} -f $BUDFILES/add-chmod/Dockerfile.bad $BUDFILES/add-chmod
|
||||||
expect_output --substring "ADD only supports the --chmod=<permissions> and the --chown=<uid:gid> flags"
|
expect_output --substring "ADD only supports the --chmod=<permissions>, --chown=<uid:gid>, and --checksum=<checksum> flags"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "bud with ADD with checksum flag" {
|
||||||
|
_prefetch alpine
|
||||||
|
target=alpine-image
|
||||||
|
run_buildah build $WITH_POLICY_JSON -t alpine-image -f $BUDFILES/add-checksum/Containerfile $BUDFILES/add-checksum
|
||||||
|
run_buildah from --quiet $WITH_POLICY_JSON --name alpine-ctr alpine-image
|
||||||
|
run_buildah run alpine-ctr -- ls -l /README.md
|
||||||
|
expect_output --substring "README.md"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "bud with ADD with bad checksum" {
|
||||||
|
_prefetch alpine
|
||||||
|
target=alpine-image
|
||||||
|
run_buildah 125 build $WITH_POLICY_JSON -t ${target} -f $BUDFILES/add-checksum/Containerfile.bad-checksum $BUDFILES/add-checksum
|
||||||
|
expect_output --substring "unexpected response digest for \"https://raw.githubusercontent.com/containers/buildah/bf3b55ba74102cc2503eccbaeffe011728d46b20/README.md\": sha256:4fd3aed66b5488b45fe83dd11842c2324fadcc38e1217bb45fbd28d660afdd39, want sha256:0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "bud with ADD with bad checksum flag" {
|
||||||
|
_prefetch alpine
|
||||||
|
target=alpine-image
|
||||||
|
run_buildah 125 build $WITH_POLICY_JSON -t ${target} -f $BUDFILES/add-checksum/Containerfile.bad $BUDFILES/add-checksum
|
||||||
|
expect_output --substring "ADD only supports the --chmod=<permissions>, --chown=<uid:gid>, and --checksum=<checksum> flags"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "bud with ADD file construct" {
|
@test "bud with ADD file construct" {
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
FROM alpine
|
||||||
|
ADD --checksum=sha256:4fd3aed66b5488b45fe83dd11842c2324fadcc38e1217bb45fbd28d660afdd39 https://raw.githubusercontent.com/containers/buildah/bf3b55ba74102cc2503eccbaeffe011728d46b20/README.md /
|
|
@ -0,0 +1,2 @@
|
||||||
|
FROM alpine
|
||||||
|
ADD --checksum https://raw.githubusercontent.com/containers/buildah/bf3b55ba74102cc2503eccbaeffe011728d46b20/README.md /
|
|
@ -0,0 +1,2 @@
|
||||||
|
FROM alpine
|
||||||
|
ADD --checksum=sha256:0000000000000000000000000000000000000000000000000000000000000000 https://raw.githubusercontent.com/containers/buildah/bf3b55ba74102cc2503eccbaeffe011728d46b20/README.md /
|
Loading…
Reference in New Issue