Skip to content

Commit

Permalink
travis: run unit tests on MacOS
Browse files Browse the repository at this point in the history
This requires a little bit of working around MacOS-specific weirdnesses
(which are fine under POSIX but don't match Linux). The most obvious one
is the lack of 'readlink -f', but there's also the lack of sub-second
granularity of filesystem timesamps, and the default-follow behaviour of
link(2) when trying to hardlink a symlink.

Signed-off-by: Aleksa Sarai <asarai@suse.de>
  • Loading branch information
cyphar committed Jun 20, 2020
1 parent fc7a6dc commit bb6fe03
Show file tree
Hide file tree
Showing 19 changed files with 265 additions and 94 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ matrix:
# Clear DOCKER_IMAGE since we don't use it.
- DOCKER_IMAGE=""
script:
# TODO: Run the integration and unit tests, so we don't need to
# special-case MacOS here.
# TODO: Run the integration tests and rest of the CI, so we don't need
# to special-case MacOS here.
- make local-validate-build
- make local-test-unit
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ umociimage:
docker build -t $(UMOCI_IMAGE) --build-arg DOCKER_IMAGE=$(DOCKER_IMAGE) .

ifndef COVERAGE
COVERAGE := $(basename $(shell mktemp -u umoci.cov.XXXXXX))
COVERAGE := $(notdir $(shell mktemp -u umoci.cov.XXXXXX))
export COVERAGE
endif

.PHONY: test-unit
Expand Down
3 changes: 2 additions & 1 deletion hack/publish-site.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
# limitations under the License.

set -Eeuo pipefail
source "$(dirname "$BASH_SOURCE")/readlinkf.sh"

# Change to site root.
site_root="$(readlink -f "$(dirname "${BASH_SOURCE}")/../.site")"
site_root="$(readlinkf_posix "$(dirname "${BASH_SOURCE}")/../.site")"
cd "$site_root"

# Make sure that we've checked out submodules.
Expand Down
43 changes: 43 additions & 0 deletions hack/readlinkf.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash
# readlinkf: POSIX-compliant implementation of readlink -f.
# Author: Koichi Nakashima <koichi@nksm.name>
# Licensed under the Creative Commons Zero v1.0 Universal license.
# <https://creativecommons.org/publicdomain/zero/1.0/>

# Copied verbatim from v1.1.0 of <https://github.com/ko1nksm/readlinkf>.

# POSIX compliant version
readlinkf_posix() {
[ "${1:-}" ] || return 1
max_symlinks=40
CDPATH='' # to avoid changing to an unexpected directory

target=$1
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
[ -d "${target:-/}" ] && target="$target/"

cd -P . 2>/dev/null || return 1
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
if [ ! "$target" = "${target%/*}" ]; then
case $target in
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
esac
target=${target##*/}
fi

if [ ! -L "$target" ]; then
target="${PWD%/}${target:+/}${target}"
printf '%s\n' "${target:-/}"
return 0
fi

# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
# <file mode>, <number of links>, <owner name>, <group name>,
# <size>, <date and time>, <pathname of link>, <contents of link>
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
link=$(ls -dl -- "$target" 2>/dev/null) || break
target=${link#*" $target -> "}
done
return 1
}
3 changes: 2 additions & 1 deletion hack/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

set -Eeuo pipefail
source "$(dirname "$BASH_SOURCE")/readlinkf.sh"

## --->
# Project-specific options and functions. In *theory* you shouldn't need to
# touch anything else in this script in order to use this elsewhere.
project="umoci"
root="$(readlink -f "$(dirname "${BASH_SOURCE}")/..")"
root="$(readlinkf_posix "$(dirname "${BASH_SOURCE}")/..")"

# These functions allow you to configure how the defaults are computed.
function get_arch() { go env GOARCH || uname -m; }
Expand Down
8 changes: 5 additions & 3 deletions hack/test-integration.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
# limitations under the License.

set -Eeuxo pipefail
source "$(dirname "$BASH_SOURCE")/readlinkf.sh"

export COVER="${COVER:-0}"
export ROOT="$(readlinkf_posix "$(dirname "$BASH_SOURCE")/..")"

# Set up the root and coverage directories.
export ROOT="$(readlink -f "$(dirname "$(readlink -f "$BASH_SOURCE")")/..")"
# Set up the coverage directory.
COVERAGE="${COVERAGE:-}"
export COVER="${COVER:-0}"
if [ "$COVER" -eq 1 ]; then
export COVERAGE_DIR=$(mktemp -dt umoci-coverage.XXXXXX)
fi
Expand Down
7 changes: 4 additions & 3 deletions hack/test-unit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
# limitations under the License.

set -Eeuxo pipefail
source "$(dirname "$BASH_SOURCE")/readlinkf.sh"

export ROOT="$(readlinkf_posix "$(dirname "$BASH_SOURCE")/..")"

GO="${GO:-go}"
COVERAGE="${COVERAGE:-}"
PROJECT="${PROJECT:-github.com/opencontainers/umoci}"

# Set up the root and coverage directories.
export ROOT="$(readlink -f "$(dirname "$(readlink -f "$BASH_SOURCE")")/..")"

# Run the tests.
extra_args=()
if [ -n "$COVERAGE" ]
Expand Down
3 changes: 2 additions & 1 deletion hack/test-vendor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# limitations under the License.

set -Eeuxo pipefail
source "$(dirname "$BASH_SOURCE")/readlinkf.sh"

# Generate a hash-of-hashes for the entire vendor/ tree.
function gethash() {
Expand All @@ -26,7 +27,7 @@ function gethash() {
}

# Figure out root directory.
ROOT="$(readlink -f "$(dirname "$(readlink -f "$BASH_SOURCE")")/..")"
ROOT="$(readlinkf_posix "$(dirname "$BASH_SOURCE")/..")"
STASHED_ROOT="$(mktemp -dt umoci-vendor.XXXXXX)"

# Stash away old vendor tree, and restore it on-exit.
Expand Down
77 changes: 47 additions & 30 deletions oci/layer/tar_extract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"time"

rspec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/umoci/pkg/testutils"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -226,8 +228,8 @@ func TestUnpackEntryWhiteout(t *testing.T) {
{"HiddenDirInSubdir", "another/path/.hiddendir", true},
} {
t.Logf("running Test%s", test.name)
testMtime := time.Unix(123, 456)
testAtime := time.Unix(789, 111)
testMtime := testutils.Unix(123, 456)
testAtime := testutils.Unix(789, 111)

dir, err := ioutil.TempDir("", "umoci-TestUnpackEntryWhiteout")
if err != nil {
Expand Down Expand Up @@ -344,9 +346,9 @@ func TestUnpackOpaqueWhiteout(t *testing.T) {
Typeflag: ph.typeflag,
Mode: int64(mode),
Size: size,
ModTime: time.Unix(1210393, 4528036),
AccessTime: time.Unix(7892829, 2341211),
ChangeTime: time.Unix(8731293, 8218947),
ModTime: testutils.Unix(1210393, 4528036),
AccessTime: testutils.Unix(7892829, 2341211),
ChangeTime: testutils.Unix(8731293, 8218947),
}, r
}

Expand Down Expand Up @@ -590,6 +592,9 @@ func TestUnpackHardlink(t *testing.T) {
var (
hdr *tar.Header

// On MacOS, this might not work.
hardlinkToSymlinkSupported = true

ctrValue = []byte("some content we won't check")
regFile = "regular"
symFile = "link"
Expand Down Expand Up @@ -650,10 +655,17 @@ func TestUnpackHardlink(t *testing.T) {
Gid: os.Getgid() + 2020,
}
if err := te.UnpackEntry(dir, hdr, nil); err != nil {
t.Fatalf("hardlinkB: unexpected UnpackEntry error: %s", err)
// On Travis' setup, hardlinks to symlinks are not permitted under
// MacOS. That's fine.
if runtime.GOOS == "darwin" && errors.Is(err, unix.ENOTSUP) {
hardlinkToSymlinkSupported = false
t.Logf("hardlinks to symlinks unsupported -- skipping that part of the test")
} else {
t.Fatalf("hardlinkB: unexpected UnpackEntry error: %s", err)
}
}

// Quickly make sure that the contents are as expected.
// Make sure that the contents are as expected.
ctrValueGot, err := ioutil.ReadFile(filepath.Join(dir, regFile))
if err != nil {
t.Fatalf("regular file was not created: %s", err)
Expand All @@ -663,7 +675,7 @@ func TestUnpackHardlink(t *testing.T) {
}

// Now we have to check the inode numbers.
var regFi, symFi, hardAFi, hardBFi unix.Stat_t
var regFi, symFi, hardAFi unix.Stat_t

if err := unix.Lstat(filepath.Join(dir, regFile), &regFi); err != nil {
t.Fatalf("could not stat regular file: %s", err)
Expand All @@ -674,36 +686,41 @@ func TestUnpackHardlink(t *testing.T) {
if err := unix.Lstat(filepath.Join(dir, hardFileA), &hardAFi); err != nil {
t.Fatalf("could not stat hardlinkA: %s", err)
}
if err := unix.Lstat(filepath.Join(dir, hardFileB), &hardBFi); err != nil {
t.Fatalf("could not stat hardlinkB: %s", err)
}

// This test only runs on Linux anyway.

if regFi.Ino == symFi.Ino {
t.Errorf("regular and symlink have the same inode! ino=%d", regFi.Ino)
}
if hardAFi.Ino == hardBFi.Ino {
t.Errorf("both hardlinks have the same inode! ino=%d", hardAFi.Ino)
}
if hardAFi.Ino != regFi.Ino {
t.Errorf("hardlink to regular has a different inode: reg=%d hard=%d", regFi.Ino, hardAFi.Ino)
}
if hardBFi.Ino != symFi.Ino {
t.Errorf("hardlink to symlink has a different inode: sym=%d hard=%d", symFi.Ino, hardBFi.Ino)
}

// Double-check readlink.
linknameA, err := os.Readlink(filepath.Join(dir, symFile))
if err != nil {
t.Errorf("unexpected error reading symlink: %s", err)
}
linknameB, err := os.Readlink(filepath.Join(dir, hardFileB))
if err != nil {
t.Errorf("unexpected error reading hardlink to symlink: %s", err)
}
if linknameA != linknameB {
t.Errorf("hardlink to symlink doesn't match linkname: link=%s hard=%s", linknameA, linknameB)
if hardlinkToSymlinkSupported {
var hardBFi unix.Stat_t

if err := unix.Lstat(filepath.Join(dir, hardFileB), &hardBFi); err != nil {
t.Fatalf("could not stat hardlinkB: %s", err)
}

// Check inode numbers of hardlink-to-symlink.
if hardAFi.Ino == hardBFi.Ino {
t.Errorf("both hardlinks have the same inode! ino=%d", hardAFi.Ino)
}
if hardBFi.Ino != symFi.Ino {
t.Errorf("hardlink to symlink has a different inode: sym=%d hard=%d", symFi.Ino, hardBFi.Ino)
}

// Double-check readlink.
linknameA, err := os.Readlink(filepath.Join(dir, symFile))
if err != nil {
t.Errorf("unexpected error reading symlink: %s", err)
}
linknameB, err := os.Readlink(filepath.Join(dir, hardFileB))
if err != nil {
t.Errorf("unexpected error reading hardlink to symlink: %s", err)
}
if linknameA != linknameB {
t.Errorf("hardlink to symlink doesn't match linkname: link=%s hard=%s", linknameA, linknameB)
}
}

// Make sure that uid and gid don't apply to hardlinks.
Expand Down
19 changes: 12 additions & 7 deletions pkg/fseval/fseval_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,18 @@ func (fs osFsEval) Readlink(path string) (string, error) {
}

// Symlink is equivalent to os.Symlink.
func (fs osFsEval) Symlink(linkname, path string) error {
return os.Symlink(linkname, path)
}

// Link is equivalent to os.Link.
func (fs osFsEval) Link(linkname, path string) error {
return os.Link(linkname, path)
func (fs osFsEval) Symlink(target, linkname string) error {
return os.Symlink(target, linkname)
}

// Link is equivalent to unix.Link(..., ~AT_SYMLINK_FOLLOW).
func (fs osFsEval) Link(target, linkname string) error {
// We need to explicitly pass 0 as a flag because POSIX allows the default
// behaviour of link(2) when it comes to target being a symlink to be
// implementation-defined. Only linkat(2) allows us to guarantee the right
// behaviour.
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html>
return unix.Linkat(unix.AT_FDCWD, target, unix.AT_FDCWD, linkname, 0)
}

// Chmod is equivalent to os.Chmod.
Expand Down
8 changes: 4 additions & 4 deletions pkg/fseval/fseval_rootless.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,13 @@ func (fs unprivFsEval) Readlink(path string) (string, error) {
}

// Symlink is equivalent to unpriv.Symlink.
func (fs unprivFsEval) Symlink(linkname, path string) error {
return unpriv.Symlink(linkname, path)
func (fs unprivFsEval) Symlink(target, linkname string) error {
return unpriv.Symlink(target, linkname)
}

// Link is equivalent to unpriv.Link.
func (fs unprivFsEval) Link(linkname, path string) error {
return unpriv.Link(linkname, path)
func (fs unprivFsEval) Link(target, linkname string) error {
return unpriv.Link(target, linkname)
}

// Chmod is equivalent to unpriv.Chmod.
Expand Down
17 changes: 9 additions & 8 deletions pkg/system/utime_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"testing"
"time"

"github.com/opencontainers/umoci/pkg/testutils"
"golang.org/x/sys/unix"
)

Expand All @@ -42,8 +43,8 @@ func TestLutimesFile(t *testing.T) {
t.Fatal(err)
}

atime := time.Unix(125812851, 128518257)
mtime := time.Unix(257172893, 995216512)
atime := testutils.Unix(125812851, 128518257)
mtime := testutils.Unix(257172893, 995216512)

if err := unix.Lstat(path, &fiOld); err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -91,8 +92,8 @@ func TestLutimesDirectory(t *testing.T) {
t.Fatal(err)
}

atime := time.Unix(128551231, 273285257)
mtime := time.Unix(185726393, 752135712)
atime := testutils.Unix(128551231, 273285257)
mtime := testutils.Unix(185726393, 752135712)

if err := unix.Lstat(path, &fiOld); err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -140,8 +141,8 @@ func TestLutimesSymlink(t *testing.T) {
t.Fatal(err)
}

atime := time.Unix(128551231, 273285257)
mtime := time.Unix(185726393, 752135712)
atime := testutils.Unix(128551231, 273285257)
mtime := testutils.Unix(185726393, 752135712)

if err := unix.Lstat(path, &fiOld); err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -218,8 +219,8 @@ func TestLutimesRelative(t *testing.T) {
t.Fatal(err)
}

atime := time.Unix(134858232, 258921237)
mtime := time.Unix(171257291, 425815288)
atime := testutils.Unix(134858232, 258921237)
mtime := testutils.Unix(171257291, 425815288)

if err := unix.Lstat(path, &fiOld); err != nil {
t.Fatal(err)
Expand Down
Loading

0 comments on commit bb6fe03

Please sign in to comment.