Merge pull request #6178 from nalind/add-timestamp
add: add a new --timestamp flag
This commit is contained in:
commit
9986534eea
31
add.go
31
add.go
|
@ -95,8 +95,13 @@ type AddAndCopyOptions struct {
|
||||||
// RetryDelay is how long to wait before retrying attempts to retrieve
|
// RetryDelay is how long to wait before retrying attempts to retrieve
|
||||||
// remote contents.
|
// remote contents.
|
||||||
RetryDelay time.Duration
|
RetryDelay time.Duration
|
||||||
// Parents preserve parent directories of source content
|
// Parents specifies that we should preserve either all of the parent
|
||||||
|
// directories of source locations, or the ones which follow "/./" in
|
||||||
|
// the source paths for source locations which include such a
|
||||||
|
// component.
|
||||||
Parents bool
|
Parents bool
|
||||||
|
// Timestamp is a timestamp to override on all content as it is being read.
|
||||||
|
Timestamp *time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// gitURLFragmentSuffix matches fragments to use as Git reference and build
|
// gitURLFragmentSuffix matches fragments to use as Git reference and build
|
||||||
|
@ -123,7 +128,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, srcDigest digest.Digest, certPath string, insecureSkipTLSVerify types.OptionalBool) error {
|
func getURL(src string, chown *idtools.IDPair, mountpoint, renameTarget string, writer io.Writer, chmod *os.FileMode, srcDigest digest.Digest, certPath string, insecureSkipTLSVerify types.OptionalBool, timestamp *time.Time) error {
|
||||||
url, err := url.Parse(src)
|
url, err := url.Parse(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -154,15 +159,19 @@ func getURL(src string, chown *idtools.IDPair, mountpoint, renameTarget string,
|
||||||
name = path.Base(url.Path)
|
name = path.Base(url.Path)
|
||||||
}
|
}
|
||||||
// If there's a date on the content, use it. If not, use the Unix epoch
|
// If there's a date on the content, use it. If not, use the Unix epoch
|
||||||
// for compatibility.
|
// or a specified value for compatibility.
|
||||||
date := time.Unix(0, 0).UTC()
|
date := time.Unix(0, 0).UTC()
|
||||||
lastModified := response.Header.Get("Last-Modified")
|
if timestamp != nil {
|
||||||
if lastModified != "" {
|
date = timestamp.UTC()
|
||||||
d, err := time.Parse(time.RFC1123, lastModified)
|
} else {
|
||||||
if err != nil {
|
lastModified := response.Header.Get("Last-Modified")
|
||||||
return fmt.Errorf("parsing last-modified time: %w", err)
|
if lastModified != "" {
|
||||||
|
d, err := time.Parse(time.RFC1123, lastModified)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing last-modified time %q: %w", lastModified, err)
|
||||||
|
}
|
||||||
|
date = d.UTC()
|
||||||
}
|
}
|
||||||
date = d
|
|
||||||
}
|
}
|
||||||
// Figure out the size of the content.
|
// Figure out the size of the content.
|
||||||
size := response.ContentLength
|
size := response.ContentLength
|
||||||
|
@ -532,6 +541,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
|
||||||
StripSetuidBit: options.StripSetuidBit,
|
StripSetuidBit: options.StripSetuidBit,
|
||||||
StripSetgidBit: options.StripSetgidBit,
|
StripSetgidBit: options.StripSetgidBit,
|
||||||
StripStickyBit: options.StripStickyBit,
|
StripStickyBit: options.StripStickyBit,
|
||||||
|
Timestamp: options.Timestamp,
|
||||||
}
|
}
|
||||||
writer := io.WriteCloser(pipeWriter)
|
writer := io.WriteCloser(pipeWriter)
|
||||||
repositoryDir := filepath.Join(cloneDir, subdir)
|
repositoryDir := filepath.Join(cloneDir, subdir)
|
||||||
|
@ -540,7 +550,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
|
||||||
} else {
|
} else {
|
||||||
go func() {
|
go func() {
|
||||||
getErr = retry.IfNecessary(context.TODO(), func() error {
|
getErr = retry.IfNecessary(context.TODO(), func() error {
|
||||||
return getURL(src, chownFiles, mountPoint, renameTarget, pipeWriter, chmodDirsFiles, srcDigest, options.CertPath, options.InsecureSkipTLSVerify)
|
return getURL(src, chownFiles, mountPoint, renameTarget, pipeWriter, chmodDirsFiles, srcDigest, options.CertPath, options.InsecureSkipTLSVerify, options.Timestamp)
|
||||||
}, &retry.Options{
|
}, &retry.Options{
|
||||||
MaxRetry: options.MaxRetries,
|
MaxRetry: options.MaxRetries,
|
||||||
Delay: options.RetryDelay,
|
Delay: options.RetryDelay,
|
||||||
|
@ -696,6 +706,7 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
|
||||||
StripSetgidBit: options.StripSetgidBit,
|
StripSetgidBit: options.StripSetgidBit,
|
||||||
StripStickyBit: options.StripStickyBit,
|
StripStickyBit: options.StripStickyBit,
|
||||||
Parents: options.Parents,
|
Parents: options.Parents,
|
||||||
|
Timestamp: options.Timestamp,
|
||||||
}
|
}
|
||||||
getErr = copier.Get(contextDir, contextDir, getOptions, []string{globbedToGlobbable(globbed)}, writer)
|
getErr = copier.Get(contextDir, contextDir, getOptions, []string{globbedToGlobbable(globbed)}, writer)
|
||||||
closeErr = writer.Close()
|
closeErr = writer.Close()
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ type addCopyResults struct {
|
||||||
retryDelay string
|
retryDelay string
|
||||||
excludes []string
|
excludes []string
|
||||||
parents bool
|
parents bool
|
||||||
|
timestamp string
|
||||||
}
|
}
|
||||||
|
|
||||||
func createCommand(addCopy string, desc string, short string, opts *addCopyResults) *cobra.Command {
|
func createCommand(addCopy string, desc string, short string, opts *addCopyResults) *cobra.Command {
|
||||||
|
@ -95,6 +97,7 @@ func applyFlagVars(flags *pflag.FlagSet, opts *addCopyResults) {
|
||||||
if err := flags.MarkHidden("signature-policy"); err != nil {
|
if err := flags.MarkHidden("signature-policy"); err != nil {
|
||||||
panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err))
|
panic(fmt.Sprintf("error marking signature-policy as hidden: %v", err))
|
||||||
}
|
}
|
||||||
|
flags.StringVar(&opts.timestamp, "timestamp", "", "set timestamps on new content to `seconds` after the epoch")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -235,6 +238,16 @@ func addAndCopyCmd(c *cobra.Command, args []string, verb string, iopts addCopyRe
|
||||||
|
|
||||||
builder.ContentDigester.Restart()
|
builder.ContentDigester.Restart()
|
||||||
|
|
||||||
|
var timestamp *time.Time
|
||||||
|
if iopts.timestamp != "" {
|
||||||
|
u, err := strconv.ParseInt(iopts.timestamp, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing timestamp value %q: %w", iopts.timestamp, err)
|
||||||
|
}
|
||||||
|
t := time.Unix(u, 0).UTC()
|
||||||
|
timestamp = &t
|
||||||
|
}
|
||||||
|
|
||||||
options := buildah.AddAndCopyOptions{
|
options := buildah.AddAndCopyOptions{
|
||||||
Chmod: iopts.chmod,
|
Chmod: iopts.chmod,
|
||||||
Chown: iopts.chown,
|
Chown: iopts.chown,
|
||||||
|
@ -249,6 +262,7 @@ func addAndCopyCmd(c *cobra.Command, args []string, verb string, iopts addCopyRe
|
||||||
InsecureSkipTLSVerify: systemContext.DockerInsecureSkipTLSVerify,
|
InsecureSkipTLSVerify: systemContext.DockerInsecureSkipTLSVerify,
|
||||||
MaxRetries: iopts.retry,
|
MaxRetries: iopts.retry,
|
||||||
Parents: iopts.parents,
|
Parents: iopts.parents,
|
||||||
|
Timestamp: timestamp,
|
||||||
}
|
}
|
||||||
if iopts.contextdir != "" {
|
if iopts.contextdir != "" {
|
||||||
var excludes []string
|
var excludes []string
|
||||||
|
|
|
@ -391,6 +391,7 @@ type GetOptions struct {
|
||||||
NoDerefSymlinks bool // don't follow symlinks when globs match them
|
NoDerefSymlinks bool // don't follow symlinks when globs match them
|
||||||
IgnoreUnreadable bool // ignore errors reading items, instead of returning an error
|
IgnoreUnreadable bool // ignore errors reading items, instead of returning an error
|
||||||
NoCrossDevice bool // if a subdirectory is a mountpoint with a different device number, include it but skip its contents
|
NoCrossDevice bool // if a subdirectory is a mountpoint with a different device number, include it but skip its contents
|
||||||
|
Timestamp *time.Time // timestamp to force on all contents
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get produces an archive containing items that match the specified glob
|
// Get produces an archive containing items that match the specified glob
|
||||||
|
@ -1633,6 +1634,16 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str
|
||||||
if options.Rename != nil {
|
if options.Rename != nil {
|
||||||
hdr.Name = handleRename(options.Rename, hdr.Name)
|
hdr.Name = handleRename(options.Rename, hdr.Name)
|
||||||
}
|
}
|
||||||
|
if options.Timestamp != nil {
|
||||||
|
timestamp := options.Timestamp.UTC()
|
||||||
|
hdr.ModTime = timestamp
|
||||||
|
if !hdr.AccessTime.IsZero() {
|
||||||
|
hdr.AccessTime = timestamp
|
||||||
|
}
|
||||||
|
if !hdr.ChangeTime.IsZero() {
|
||||||
|
hdr.ChangeTime = timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
if err = tw.WriteHeader(hdr); err != nil {
|
if err = tw.WriteHeader(hdr); err != nil {
|
||||||
return fmt.Errorf("writing tar header from %q to pipe: %w", contentPath, err)
|
return fmt.Errorf("writing tar header from %q to pipe: %w", contentPath, err)
|
||||||
}
|
}
|
||||||
|
@ -1711,6 +1722,16 @@ func copierHandlerGetOne(srcfi os.FileInfo, symlinkTarget, name, contentPath str
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
}
|
}
|
||||||
|
if options.Timestamp != nil {
|
||||||
|
timestamp := options.Timestamp.UTC()
|
||||||
|
hdr.ModTime = timestamp
|
||||||
|
if !hdr.AccessTime.IsZero() {
|
||||||
|
hdr.AccessTime = timestamp
|
||||||
|
}
|
||||||
|
if !hdr.ChangeTime.IsZero() {
|
||||||
|
hdr.ChangeTime = timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
// output the header
|
// output the header
|
||||||
if err = tw.WriteHeader(hdr); err != nil {
|
if err = tw.WriteHeader(hdr); err != nil {
|
||||||
return fmt.Errorf("writing header for %s (%s): %w", contentPath, hdr.Name, err)
|
return fmt.Errorf("writing header for %s (%s): %w", contentPath, hdr.Name, err)
|
||||||
|
|
|
@ -177,7 +177,8 @@ type enumeratedFile struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
testDate = time.Unix(1485449953, 0)
|
testDate = time.Unix(1485449953, 0)
|
||||||
|
secondTestDate = time.Unix(1485449953*2, 0)
|
||||||
|
|
||||||
uid = os.Getuid()
|
uid = os.Getuid()
|
||||||
|
|
||||||
|
@ -890,6 +891,7 @@ func testGetMultiple(t *testing.T) {
|
||||||
renames map[string]string
|
renames map[string]string
|
||||||
noDerefSymlinks bool
|
noDerefSymlinks bool
|
||||||
parents bool
|
parents bool
|
||||||
|
timestamp *time.Time
|
||||||
}
|
}
|
||||||
getTestArchives := []struct {
|
getTestArchives := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -997,6 +999,16 @@ func testGetMultiple(t *testing.T) {
|
||||||
"subdir-f/hlink-b", // from subdir-e
|
"subdir-f/hlink-b", // from subdir-e
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "timestamped",
|
||||||
|
pattern: "file*",
|
||||||
|
items: []string{
|
||||||
|
"file-0",
|
||||||
|
"file-a",
|
||||||
|
"file-b",
|
||||||
|
},
|
||||||
|
timestamp: &secondTestDate,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "dot-with-wildcard-includes-and-excludes",
|
name: "dot-with-wildcard-includes-and-excludes",
|
||||||
pattern: ".",
|
pattern: ".",
|
||||||
|
@ -1520,6 +1532,7 @@ func testGetMultiple(t *testing.T) {
|
||||||
Rename: testCase.renames,
|
Rename: testCase.renames,
|
||||||
NoDerefSymlinks: testCase.noDerefSymlinks,
|
NoDerefSymlinks: testCase.noDerefSymlinks,
|
||||||
Parents: testCase.parents,
|
Parents: testCase.parents,
|
||||||
|
Timestamp: testCase.timestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run(fmt.Sprintf("topdir=%s,archive=%s,case=%s,pattern=%s", topdir, testArchive.name, testCase.name, testCase.pattern), func(t *testing.T) {
|
t.Run(fmt.Sprintf("topdir=%s,archive=%s,case=%s,pattern=%s", topdir, testArchive.name, testCase.name, testCase.pattern), func(t *testing.T) {
|
||||||
|
@ -1535,15 +1548,18 @@ func testGetMultiple(t *testing.T) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
getErr = Get(root, topdir, getOptions, []string{testCase.pattern}, pipeWriter)
|
getErr = Get(root, topdir, getOptions, []string{testCase.pattern}, pipeWriter)
|
||||||
pipeWriter.Close()
|
pipeWriter.Close()
|
||||||
wg.Done()
|
|
||||||
}()
|
}()
|
||||||
tr := tar.NewReader(pipeReader)
|
tr := tar.NewReader(pipeReader)
|
||||||
hdr, err := tr.Next()
|
hdr, err := tr.Next()
|
||||||
actualContents := []string{}
|
actualContents := []string{}
|
||||||
for err == nil {
|
for err == nil {
|
||||||
actualContents = append(actualContents, filepath.FromSlash(hdr.Name))
|
actualContents = append(actualContents, filepath.FromSlash(hdr.Name))
|
||||||
|
if testCase.timestamp != nil {
|
||||||
|
assert.Truef(t, testCase.timestamp.Equal(hdr.ModTime), "timestamp was supposed to be forced for %q", hdr.Name)
|
||||||
|
}
|
||||||
hdr, err = tr.Next()
|
hdr, err = tr.Next()
|
||||||
}
|
}
|
||||||
pipeReader.Close()
|
pipeReader.Close()
|
||||||
|
|
|
@ -83,6 +83,15 @@ from registries or retrieving content from HTTPS URLs.
|
||||||
|
|
||||||
Defaults to `2s`.
|
Defaults to `2s`.
|
||||||
|
|
||||||
|
**--timestamp** *seconds*
|
||||||
|
|
||||||
|
Set the timestamp ("mtime") for added content to exactly this number of seconds
|
||||||
|
since the epoch (Unix time 0, i.e., 00:00:00 UTC on 1 January 1970) to help
|
||||||
|
allow for deterministic builds.
|
||||||
|
|
||||||
|
The destination directory into which the content is being copied will most
|
||||||
|
likely reflect the time at which the content was added to it.
|
||||||
|
|
||||||
**--tls-verify** *bool-value*
|
**--tls-verify** *bool-value*
|
||||||
|
|
||||||
Require verification of certificates when retrieving sources from HTTPS
|
Require verification of certificates when retrieving sources from HTTPS
|
||||||
|
|
|
@ -87,6 +87,15 @@ Duration of delay between retry attempts in case of failure when performing pull
|
||||||
|
|
||||||
Defaults to `2s`.
|
Defaults to `2s`.
|
||||||
|
|
||||||
|
**--timestamp** *seconds*
|
||||||
|
|
||||||
|
Set the timestamp ("mtime") for added content to exactly this number of seconds
|
||||||
|
since the epoch (Unix time 0, i.e., 00:00:00 UTC on 1 January 1970) to help
|
||||||
|
allow for deterministic builds.
|
||||||
|
|
||||||
|
The destination directory into which the content is being copied will most
|
||||||
|
likely reflect the time at which the content was added to it.
|
||||||
|
|
||||||
**--tls-verify** *bool-value*
|
**--tls-verify** *bool-value*
|
||||||
|
|
||||||
Require verification of certificates when pulling images referred to with the
|
Require verification of certificates when pulling images referred to with the
|
||||||
|
|
|
@ -334,3 +334,61 @@ stuff/mystuff"
|
||||||
# Only that the add was actually successful.
|
# Only that the add was actually successful.
|
||||||
run_buildah add $cid /usr/libexec/catatonit/catatonit /catatonit
|
run_buildah add $cid /usr/libexec/catatonit/catatonit /catatonit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "add-with-timestamp" {
|
||||||
|
_prefetch busybox
|
||||||
|
url=https://raw.githubusercontent.com/containers/buildah/main/tests/bud/from-scratch/Dockerfile
|
||||||
|
timestamp=60
|
||||||
|
mkdir -p $TEST_SCRATCH_DIR/context
|
||||||
|
createrandom $TEST_SCRATCH_DIR/context/randomfile1
|
||||||
|
createrandom $TEST_SCRATCH_DIR/context/randomfile2
|
||||||
|
run_buildah from -q busybox
|
||||||
|
cid="$output"
|
||||||
|
# Add the content with more or less contemporary timestamps.
|
||||||
|
run_buildah copy "$cid" $TEST_SCRATCH_DIR/context/randomfile* /default
|
||||||
|
# Add a second copy that should get the same contemporary timestamps.
|
||||||
|
run_buildah copy "$cid" $TEST_SCRATCH_DIR/context/randomfile* /default2
|
||||||
|
# Add a third copy that we explicitly force timestamps for.
|
||||||
|
run_buildah copy --timestamp=$timestamp "$cid" $TEST_SCRATCH_DIR/context/randomfile* /explicit
|
||||||
|
run_buildah add --timestamp=$timestamp "$cid" "$url" /explicit
|
||||||
|
# Add a fourth copy that we forced the timestamps for out of band.
|
||||||
|
cp -v "${BUDFILES}"/from-scratch/Dockerfile $TEST_SCRATCH_DIR/context/
|
||||||
|
tar -cf $TEST_SCRATCH_DIR/tarball -C $TEST_SCRATCH_DIR/context randomfile1 randomfile2 Dockerfile
|
||||||
|
touch -d @$timestamp $TEST_SCRATCH_DIR/context/*
|
||||||
|
run_buildah copy "$cid" $TEST_SCRATCH_DIR/context/* /touched
|
||||||
|
# Add a fifth copy that we forced the timestamps for, from an archive.
|
||||||
|
run_buildah add --timestamp=$timestamp "$cid" $TEST_SCRATCH_DIR/tarball /archive
|
||||||
|
# Build the script to verify this inside of the rootfs.
|
||||||
|
cat > $TEST_SCRATCH_DIR/context/check-dates.sh <<-EOF
|
||||||
|
# Okay, at this point, default, default2, explicit, touched, and archive
|
||||||
|
# should all contain randomfile1, randomfile2, and Dockerfile.
|
||||||
|
# The copies in default and default2 should have contemporary timestamps for
|
||||||
|
# the random files, and a server-supplied timestamp or the epoch for the
|
||||||
|
# Dockerfile.
|
||||||
|
# The copies in explicit, touched, and archive should all have the same
|
||||||
|
# very old timestamps.
|
||||||
|
touch -d @$timestamp /tmp/reference-file
|
||||||
|
for f in /default/* /default2/* ; do
|
||||||
|
if test \$f -ot /tmp/reference-file ; then
|
||||||
|
echo expected \$f to be newer than /tmp/reference-file, but it was not
|
||||||
|
ls -l \$f /tmp/reference-file
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for f in /explicit/* /touched/* /archive/* ; do
|
||||||
|
if test \$f -nt /tmp/reference-file ; then
|
||||||
|
echo expected \$f and /tmp/reference-file to have the same datestamp
|
||||||
|
ls -l \$f /tmp/reference-file
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if test \$f -ot /tmp/reference-file ; then
|
||||||
|
echo expected \$f and /tmp/reference-file to have the same datestamp
|
||||||
|
ls -l \$f /tmp/reference-file
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exit 0
|
||||||
|
EOF
|
||||||
|
run_buildah copy --chmod=0755 "$cid" $TEST_SCRATCH_DIR/context/check-dates.sh /
|
||||||
|
run_buildah run "$cid" sh -x /check-dates.sh
|
||||||
|
}
|
||||||
|
|
|
@ -727,6 +727,9 @@ function skip_if_no_docker() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
########################
|
||||||
|
# skip_if_no_unshare #
|
||||||
|
########################
|
||||||
function skip_if_no_unshare() {
|
function skip_if_no_unshare() {
|
||||||
run which ${UNSHARE_BINARY:-unshare}
|
run which ${UNSHARE_BINARY:-unshare}
|
||||||
if [[ $status -ne 0 ]]; then
|
if [[ $status -ne 0 ]]; then
|
||||||
|
@ -749,6 +752,9 @@ function skip_if_no_unshare() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
######################
|
||||||
|
# start_git_daemon #
|
||||||
|
######################
|
||||||
function start_git_daemon() {
|
function start_git_daemon() {
|
||||||
daemondir=${TEST_SCRATCH_DIR}/git-daemon
|
daemondir=${TEST_SCRATCH_DIR}/git-daemon
|
||||||
mkdir -p ${daemondir}/repo
|
mkdir -p ${daemondir}/repo
|
||||||
|
@ -772,6 +778,9 @@ function start_git_daemon() {
|
||||||
GITPORT=$(cat ${TEST_SCRATCH_DIR}/git-daemon/port)
|
GITPORT=$(cat ${TEST_SCRATCH_DIR}/git-daemon/port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#####################
|
||||||
|
# stop_git_daemon #
|
||||||
|
#####################
|
||||||
function stop_git_daemon() {
|
function stop_git_daemon() {
|
||||||
if test -s ${TEST_SCRATCH_DIR}/git-daemon/pid ; then
|
if test -s ${TEST_SCRATCH_DIR}/git-daemon/pid ; then
|
||||||
kill $(cat ${TEST_SCRATCH_DIR}/git-daemon/pid)
|
kill $(cat ${TEST_SCRATCH_DIR}/git-daemon/pid)
|
||||||
|
@ -779,6 +788,9 @@ function stop_git_daemon() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
####################
|
||||||
|
# start_registry #
|
||||||
|
####################
|
||||||
# Bring up a registry server using buildah with vfs and chroot as a cheap
|
# Bring up a registry server using buildah with vfs and chroot as a cheap
|
||||||
# substitute for podman, accessible only to user $1 using password $2 on the
|
# substitute for podman, accessible only to user $1 using password $2 on the
|
||||||
# local system at a dynamically-allocated port.
|
# local system at a dynamically-allocated port.
|
||||||
|
@ -872,6 +884,9 @@ auth:
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
###################
|
||||||
|
# stop_registry #
|
||||||
|
###################
|
||||||
function stop_registry() {
|
function stop_registry() {
|
||||||
if test -n "${REGISTRY_PID}" ; then
|
if test -n "${REGISTRY_PID}" ; then
|
||||||
kill "${REGISTRY_PID}"
|
kill "${REGISTRY_PID}"
|
||||||
|
@ -885,3 +900,188 @@ function stop_registry() {
|
||||||
fi
|
fi
|
||||||
unset REGISTRY_DIR
|
unset REGISTRY_DIR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
###############################
|
||||||
|
# oci_image_manifest_digest #
|
||||||
|
###############################
|
||||||
|
# prints the digest of the form "sha256:xxx" of the manifest for the main image
|
||||||
|
# in an OCI layout in "$1"
|
||||||
|
function oci_image_manifest_digest() {
|
||||||
|
run jq -r '.manifests[0].digest' "$1"/index.json
|
||||||
|
assert $status = 0 "looking for the digest of the image manifest"
|
||||||
|
assert "$output" != ""
|
||||||
|
echo "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
#############################
|
||||||
|
# oci_image_config_digest #
|
||||||
|
#############################
|
||||||
|
# prints the digest of the form "sha256:xxx" of the config blob for the main
|
||||||
|
# image in an OCI layout in "$1"
|
||||||
|
function oci_image_config_digest() {
|
||||||
|
local digest=$(oci_image_manifest_digest "$1")
|
||||||
|
local alg=${digest%%:*}
|
||||||
|
local val=${digest##*:}
|
||||||
|
run jq -r '.config.digest' "$1"/blobs/"$alg"/"$val"
|
||||||
|
assert $status = 0 "looking for the digest of the image config"
|
||||||
|
assert "$output" != ""
|
||||||
|
echo "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
######################
|
||||||
|
# oci_image_config #
|
||||||
|
######################
|
||||||
|
# prints the relative path of the config blob for the main image in an OCI
|
||||||
|
# layout in "$1"
|
||||||
|
function oci_image_config() {
|
||||||
|
local diff_id=$(oci_image_config_digest "$@")
|
||||||
|
local alg=${diff_id%%:*}
|
||||||
|
local val=${diff_id##*:}
|
||||||
|
echo blobs/"$alg"/"$val"
|
||||||
|
}
|
||||||
|
|
||||||
|
########################
|
||||||
|
# oci_image_diff_ids #
|
||||||
|
########################
|
||||||
|
# prints the list of digests of the diff IDs for the main image in an OCI
|
||||||
|
# layout in "$1"
|
||||||
|
function oci_image_diff_ids() {
|
||||||
|
local digest=$(oci_image_config_digest "$1")
|
||||||
|
local alg=${digest%%:*}
|
||||||
|
local val=${digest##*:}
|
||||||
|
run jq -r '.rootfs.diff_ids[]' "$1"/blobs/"$alg"/"$val"
|
||||||
|
assert $status = 0 "looking for the diff IDs in the image config"
|
||||||
|
assert "$output" != ""
|
||||||
|
echo "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# oci_image_diff_id #
|
||||||
|
#######################
|
||||||
|
# prints a single diff ID for the main image in an OCI layout in "$1", choosing
|
||||||
|
# which one to print based on an index and arithmetic operands passed in
|
||||||
|
# subsequent arguments
|
||||||
|
function oci_image_diff_id() {
|
||||||
|
local diff_ids=($(oci_image_diff_ids "$1"))
|
||||||
|
shift
|
||||||
|
case "$*" in
|
||||||
|
-*) echo ${diff_ids[$((${#diff_ids[@]} "$@"))]} ;;
|
||||||
|
*) echo ${diff_ids[$(("$@"))]} ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
############################
|
||||||
|
# oci_image_last_diff_id #
|
||||||
|
############################
|
||||||
|
# prints the diff ID of the most recent layer for the main image in an OCI
|
||||||
|
# layout in "$1"
|
||||||
|
function oci_image_last_diff_id() {
|
||||||
|
local diff_id=($(oci_image_diff_id "$1" - 1))
|
||||||
|
echo "$diff_id"
|
||||||
|
}
|
||||||
|
|
||||||
|
####################
|
||||||
|
# oci_image_diff #
|
||||||
|
####################
|
||||||
|
# prints the relative path of a single layer diff for the main image in an OCI
|
||||||
|
# layout in "$1", choosing which one to print based on an index and arithmetic
|
||||||
|
# operands passed in subsequent arguments
|
||||||
|
function oci_image_diff() {
|
||||||
|
local diff_id=$(oci_image_diff_id "$@")
|
||||||
|
local alg=${diff_id%%:*}
|
||||||
|
local val=${diff_id##*:}
|
||||||
|
echo blobs/"$alg"/"$val"
|
||||||
|
}
|
||||||
|
|
||||||
|
#########################
|
||||||
|
# oci_image_last_diff #
|
||||||
|
#########################
|
||||||
|
# prints the relative path of the most recent layer for the main image in an
|
||||||
|
# OCI layout in "$1"
|
||||||
|
function oci_image_last_diff() {
|
||||||
|
local output=$(oci_image_diff "$1" - 1)
|
||||||
|
echo "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
#############################
|
||||||
|
# dir_image_config_digest #
|
||||||
|
#############################
|
||||||
|
# prints the digest of the form "sha256:xxx" of the config blob for the "dir"
|
||||||
|
# image in "$1"
|
||||||
|
function dir_image_config_digest() {
|
||||||
|
run jq -r '.config.digest' "$1"/manifest.json
|
||||||
|
assert $status = 0 "looking for the digest of the image config"
|
||||||
|
assert "$output" != ""
|
||||||
|
echo "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
########################
|
||||||
|
# dir_image_diff_ids #
|
||||||
|
########################
|
||||||
|
# prints the list of digests of the diff IDs for the "dir" image in "$1"
|
||||||
|
function dir_image_diff_ids() {
|
||||||
|
local digest=$(dir_image_config_digest "$1")
|
||||||
|
local alg=${digest%%:*}
|
||||||
|
local val=${digest##*:}
|
||||||
|
run jq -r '.rootfs.diff_ids[]' "$1"/"$val"
|
||||||
|
assert $status = 0 "looking for the diff IDs in the image config"
|
||||||
|
assert "$output" != ""
|
||||||
|
echo "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# dir_image_diff_id #
|
||||||
|
#######################
|
||||||
|
# prints a single diff ID for the "dir" image in "$1", choosing which one to
|
||||||
|
# print based on an index and arithmetic operands passed in subsequent
|
||||||
|
# arguments
|
||||||
|
function dir_image_diff_id() {
|
||||||
|
local diff_ids=($(dir_image_diff_ids "$1"))
|
||||||
|
shift
|
||||||
|
case "$*" in
|
||||||
|
-*) echo ${diff_ids[$((${#diff_ids[@]} "$@"))]} ;;
|
||||||
|
*) echo ${diff_ids[$(("$@"))]} ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
############################
|
||||||
|
# dir_image_last_diff_id #
|
||||||
|
############################
|
||||||
|
# prints the diff ID of the most recent layer for "dir" image in "$1"
|
||||||
|
function dir_image_last_diff_id() {
|
||||||
|
local diff_id=($(dir_image_diff_id "$1" - 1))
|
||||||
|
echo "$diff_id"
|
||||||
|
}
|
||||||
|
|
||||||
|
######################
|
||||||
|
# dir_image_config #
|
||||||
|
######################
|
||||||
|
# prints the relative path of the config blob for the "dir" image in "$1"
|
||||||
|
function dir_image_config() {
|
||||||
|
local diff_id=$(dir_image_config_digest "$@")
|
||||||
|
local alg=${diff_id%%:*}
|
||||||
|
local val=${diff_id##*:}
|
||||||
|
echo "$val"
|
||||||
|
}
|
||||||
|
|
||||||
|
####################
|
||||||
|
# dir_image_diff #
|
||||||
|
####################
|
||||||
|
# prints the relative path of a single layer diff for the "dir" image in "$1",
|
||||||
|
# choosing which one to print based on an index and arithmetic operands passed
|
||||||
|
# in subsequent arguments
|
||||||
|
function dir_image_diff() {
|
||||||
|
local diff_id=$(dir_image_diff_id "$@")
|
||||||
|
local alg=${diff_id%%:*}
|
||||||
|
local val=${diff_id##*:}
|
||||||
|
echo "$val"
|
||||||
|
}
|
||||||
|
|
||||||
|
#########################
|
||||||
|
# dir_image_last_diff #
|
||||||
|
#########################
|
||||||
|
# prints the relative path of the most recent layer for "dir" image in "$1"
|
||||||
|
function dir_image_last_diff() {
|
||||||
|
local output=$(dir_image_diff "$1")
|
||||||
|
echo "$output"
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue