diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index fe6a6522a31..0a812a5cd32 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -124,27 +124,6 @@ jobs: error: 'Subject too long (max 72)' - cross: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v2 - # We have to run this under Docker as Ubuntu (host) does not support all - # the architectures we want to compile test against, and Dockerfile uses - # Debian (which does). - # - # XXX: as currently this is the only job that is using Docker, we are - # building and using the runcimage locally. In case more jobs running - # under Docker will emerge, it will be good to have a separate make - # runcimage job and share its result (the docker image) with whoever - # needs it. - - uses: satackey/action-docker-layer-caching@v0.0.11 - continue-on-error: true - - name: build docker image - run: make runcimage - - name: cross - run: make cross - - cfmt: runs-on: ubuntu-20.04 steps: @@ -169,12 +148,21 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 - - name: install deps - run: | - sudo apt -qq update - sudo apt -qq install gperf - - name: make release - run: make release + # We have to run this under Docker as Ubuntu (host) does not support all + # the architectures we want to compile test against, and Dockerfile uses + # Debian (which does). + # + # XXX: as currently this is the only job that is using Docker, we are + # building and using the runcimage locally. In case more jobs running + # under Docker will emerge, it will be good to have a separate make + # runcimage job and share its result (the docker image) with whoever + # needs it. + - uses: satackey/action-docker-layer-caching@v0.0.11 + continue-on-error: true + - name: build docker image + run: make runcimage + - name: make releaseall + run: make releaseall - name: upload artifacts uses: actions/upload-artifact@v2 with: diff --git a/Dockerfile b/Dockerfile index d98c53d52bf..c4d81083e3b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ ARG GO_VERSION=1.17 ARG BATS_VERSION=v1.3.0 +ARG LIBSECCOMP_VERSION=2.5.2 FROM golang:${GO_VERSION}-bullseye ARG DEBIAN_FRONTEND=noninteractive @@ -21,15 +22,10 @@ RUN echo 'deb https://download.opensuse.org/repositories/devel:/tools:/criu/Debi curl \ gawk \ gcc \ + gperf \ iptables \ jq \ kmod \ - libseccomp-dev \ - libseccomp-dev:arm64 \ - libseccomp-dev:armel \ - libseccomp-dev:armhf \ - libseccomp-dev:ppc64el \ - libseccomp2 \ pkg-config \ python3-minimal \ sudo \ @@ -52,4 +48,10 @@ RUN cd /tmp \ && ./install.sh /usr/local \ && rm -rf /tmp/bats-core +# install libseccomp +ARG LIBSECCOMP_VERSION +COPY script/* /tmp/script/ +RUN mkdir -p /usr/local/src/libseccomp \ + && /tmp/script/seccomp.sh "$LIBSECCOMP_VERSION" /usr/local/src/libseccomp /usr/local/src/libseccomp/.env-file arm64 armel armhf ppc64le + WORKDIR /go/src/github.com/opencontainers/runc diff --git a/Makefile b/Makefile index 7ddf3fae2bd..4c68ac651a0 100644 --- a/Makefile +++ b/Makefile @@ -32,19 +32,23 @@ runc: all: runc recvtty sd-helper seccompagent -recvtty sd-helper: +recvtty sd-helper seccompagent: $(GO_BUILD) -o contrib/cmd/$@/$@ ./contrib/cmd/$@ -seccompagent: - $(GO_BUILD) -o contrib/cmd/seccompagent/seccompagent ./contrib/cmd/seccompagent - static: $(GO_BUILD_STATIC) -o runc . - $(GO_BUILD_STATIC) -o contrib/cmd/recvtty/recvtty ./contrib/cmd/recvtty - $(GO_BUILD_STATIC) -o contrib/cmd/sd-helper/sd-helper ./contrib/cmd/sd-helper -release: - script/release.sh -r release/$(VERSION) -v $(VERSION) +releaseall: RELEASE_ARGS := "-a arm64 -a armel -a armhf -a ppc64le" +releaseall: release + +release: runcimage + $(CONTAINER_ENGINE) run $(CONTAINER_ENGINE_RUN_FLAGS) \ + --rm -v $(CURDIR):/go/src/$(PROJECT) \ + -e RELEASE_ARGS=$(RELEASE_ARGS) \ + $(RUNC_IMAGE) make localrelease + +localrelease: + script/release.sh -r release/$(VERSION) -v $(VERSION) $(RELEASE_ARGS) dbuild: runcimage $(CONTAINER_ENGINE) run $(CONTAINER_ENGINE_RUN_FLAGS) \ @@ -115,6 +119,7 @@ clean: rm -f runc runc-* rm -f contrib/cmd/recvtty/recvtty rm -f contrib/cmd/sd-helper/sd-helper + rm -f contrib/cmd/seccompagent/seccompagent rm -rf release rm -rf man/man8 @@ -123,7 +128,9 @@ cfmt: indent -linux -l120 -il0 -ppi2 -cp1 -T size_t -T jmp_buf $(C_SRC) shellcheck: - shellcheck tests/integration/*.bats tests/integration/*.sh tests/integration/*.bash tests/*.sh script/release.sh + shellcheck tests/integration/*.bats tests/integration/*.sh \ + tests/integration/*.bash tests/*.sh \ + script/release.sh script/seccomp.sh script/lib.sh # TODO: add shellcheck for more sh files shfmt: @@ -140,20 +147,9 @@ verify-dependencies: vendor || (echo -e "git status:\n $$(git status -- go.mod go.sum vendor/)\nerror: vendor/, go.mod and/or go.sum not up to date. Run \"make vendor\" to update"; exit 1) \ && echo "all vendor files are up to date." -cross: runcimage - $(CONTAINER_ENGINE) run $(CONTAINER_ENGINE_RUN_FLAGS) \ - -e BUILDTAGS="$(BUILDTAGS)" --rm \ - -v $(CURDIR):/go/src/$(PROJECT) \ - $(RUNC_IMAGE) make localcross - -localcross: - CGO_ENABLED=1 GOARCH=arm GOARM=6 CC=arm-linux-gnueabi-gcc $(GO_BUILD) -o runc-armel . - CGO_ENABLED=1 GOARCH=arm GOARM=7 CC=arm-linux-gnueabihf-gcc $(GO_BUILD) -o runc-armhf . - CGO_ENABLED=1 GOARCH=arm64 CC=aarch64-linux-gnu-gcc $(GO_BUILD) -o runc-arm64 . - CGO_ENABLED=1 GOARCH=ppc64le CC=powerpc64le-linux-gnu-gcc $(GO_BUILD) -o runc-ppc64le . - -.PHONY: runc all recvtty sd-helper static release dbuild lint man runcimage \ +.PHONY: runc all recvtty sd-helper seccompagent static releaseall release \ + localrelease dbuild lint man runcimage \ test localtest unittest localunittest integration localintegration \ rootlessintegration localrootlessintegration shell install install-bash \ install-man clean cfmt shfmt shellcheck \ - vendor verify-dependencies cross localcross + vendor verify-dependencies diff --git a/script/lib.sh b/script/lib.sh new file mode 100644 index 00000000000..4000d2a94ec --- /dev/null +++ b/script/lib.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# set_cross_vars sets a few environment variables used for cross-compiling, +# based on the architecture specified in $1. +function set_cross_vars() { + GOARCH="$1" # default, may be overridden below + unset GOARM + + case $1 in + arm64) + HOST=aarch64-linux-gnu + ;; + armel) + HOST=arm-linux-gnueabi + GOARCH=arm + GOARM=6 + ;; + armhf) + HOST=arm-linux-gnueabihf + GOARCH=arm + GOARM=7 + ;; + ppc64le) + HOST=powerpc64le-linux-gnu + ;; + *) + echo "set_cross_vars: unsupported architecture: $1" >&2 + exit 1 + ;; + esac + + CC=$HOST-gcc + STRIP=$HOST-strip + + export HOST GOARM GOARCH CC STRIP +} diff --git a/script/release.sh b/script/release.sh index ae44b620178..7b260527177 100755 --- a/script/release.sh +++ b/script/release.sh @@ -21,28 +21,36 @@ set -e project="runc" root="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")" +# shellcheck source=./script/lib.sh +source "$root/script/lib.sh" + # This function takes an output path as an argument, where the built # (preferably static) binary should be placed. +# Parameters: +# $1 -- destination directory to place build artefacts to. +# $2 -- native architecture (a .suffix for a native binary file name). +# $@ -- additional architectures to cross-build for. function build_project() { + local libseccomp_version=2.5.2 local builddir builddir="$(dirname "$1")" - - # Due to libseccomp being LGPL we must include its sources, - # so download, install and build against it. - - local libseccomp_ver='2.5.1' - local tarball="libseccomp-${libseccomp_ver}.tar.gz" - local prefix - prefix="$(mktemp -d)" - wget "https://github.com/seccomp/libseccomp/releases/download/v${libseccomp_ver}/${tarball}"{,.asc} - tar xf "$tarball" - ( - cd "libseccomp-${libseccomp_ver}" - ./configure --prefix="$prefix" --libdir="$prefix/lib" \ - --enable-static --disable-shared - make install - ) - mv "$tarball"{,.asc} "$builddir" + shift + local native_arch="$1" + shift + local arches=("$@") + + # Assume that if /usr/local/src/libseccomp/.env-file exists, then + # we are run via Dockerfile, and seccomp is already built. + if [ -r /usr/local/src/libseccomp/.env-file ]; then + # shellcheck disable=SC1091 + source /usr/local/src/libseccomp/.env-file + # Copy the source tarball. + cp /usr/local/src/libseccomp/* "$builddir" + else + "$root/script/seccomp.sh" "$libseccomp_version" "$builddir" "./env-file" "${arches[@]}" + # shellcheck disable=SC1091 + source ./env-file + fi # For reproducible builds, add these to EXTRA_LDFLAGS: # -w to disable DWARF generation; @@ -52,10 +60,32 @@ function build_project() { # Add -a to go build flags to make sure it links against # the provided libseccomp, not the system one (otherwise # it can reuse cached pkg-config results). - make -C "$root" PKG_CONFIG_PATH="${prefix}/lib/pkgconfig" COMMIT_NO= EXTRA_FLAGS="-a" EXTRA_LDFLAGS="${ldflags}" static - rm -rf "$prefix" + local make_args=(COMMIT_NO= EXTRA_FLAGS="-a" EXTRA_LDFLAGS="${ldflags}" static) + + # Build natively. + make -C "$root" \ + PKG_CONFIG_PATH="${LIBSECCOMP_PREFIX}/lib/pkgconfig" \ + LD_LIBRARY_PATH="${LIBSECCOMP_PREFIX}/lib" \ + "${make_args[@]}" strip "$root/$project" - mv "$root/$project" "$1" + mv "$root/$project" "$builddir/$project.$native_arch" + rm -rf "${LIBSECCOMP_PREFIX}" + + # Cross-build for for other architectures. + local prefix arch + for arch in "${arches[@]}"; do + eval prefix=\$"LIBSECCOMP_PREFIX_$arch" + if [ -z "$prefix" ]; then + echo "LIBSECCOMP_PREFIX_$arch is empty (unsupported arch?)" >&2 + exit 1 + fi + set_cross_vars "$arch" + make -C "$root" \ + PKG_CONFIG_PATH="${prefix}/lib/pkgconfig" "${make_args[@]}" + "$STRIP" "$root/$project" + mv "$root/$project" "$builddir/$project.$arch" + rm -rf "$prefix" + done } # End of the easy-to-configure portion. @@ -63,7 +93,7 @@ function build_project() { # Print usage information. function usage() { - echo "usage: release.sh [-S ] [-c ] [-r ] [-v ]" >&2 + echo "usage: release.sh [-S ] [-c ] [-r ] [-v ] [-a ]" >&2 exit 1 } @@ -91,7 +121,9 @@ commit="HEAD" version="" releasedir="" hashcmd="" -while getopts "S:c:r:v:h:" opt; do +declare -a add_arches + +while getopts "S:c:r:v:h:a:" opt; do case "$opt" in S) keyid="$OPTARG" @@ -108,6 +140,9 @@ while getopts "S:c:r:v:h:" opt; do h) hashcmd="$OPTARG" ;; + a) + add_arches+=("$OPTARG") + ;; :) echo "Missing argument: -$OPTARG" >&2 usage @@ -122,7 +157,9 @@ done version="${version:-$(<"$root/VERSION")}" releasedir="${releasedir:-release/$version}" hashcmd="${hashcmd:-sha256sum}" -goarch="$(go env GOARCH || echo "amd64")" +native_arch="$(go env GOARCH || echo "amd64")" +# Suffixes of files to checksum/sign. +suffixes=("$native_arch" "${add_arches[@]}" tar.xz) log "creating $project release in '$releasedir'" log " version: $version" @@ -137,15 +174,16 @@ set -x rm -rf "$releasedir" && mkdir -p "$releasedir" # Build project. -build_project "$releasedir/$project.$goarch" +build_project "$releasedir/$project" "$native_arch" "${add_arches[@]}" # Generate new archive. git archive --format=tar --prefix="$project-$version/" "$commit" | xz >"$releasedir/$project.tar.xz" -# Generate sha256 checksums for both. +# Generate sha256 checksums for binaries and libseccomp tarball. ( cd "$releasedir" - "$hashcmd" "$project".{"$goarch",tar.xz} >"$project.$hashcmd" + # Add $project. prefix to all suffixes. + "$hashcmd" "${suffixes[@]/#/$project.}" >"$project.$hashcmd" ) # Set up the gpgflags. @@ -154,8 +192,9 @@ gpgflags=() gpg_cansign "${gpgflags[@]}" || bail "Could not find suitable GPG key, skipping signing step." # Sign everything. -gpg "${gpgflags[@]}" --detach-sign --armor "$releasedir/$project.$goarch" -gpg "${gpgflags[@]}" --detach-sign --armor "$releasedir/$project.tar.xz" +for sfx in "${suffixes[@]}"; do + gpg "${gpgflags[@]}" --detach-sign --armor "$releasedir/$project.$sfx" +done gpg "${gpgflags[@]}" --clear-sign --armor \ --output "$releasedir/$project.$hashcmd"{.tmp,} && mv "$releasedir/$project.$hashcmd"{.tmp,} diff --git a/script/seccomp.sh b/script/seccomp.sh new file mode 100755 index 00000000000..b80ed9174f4 --- /dev/null +++ b/script/seccomp.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +# shellcheck source=./script/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" + +# Due to libseccomp being LGPL we must include its sources, +# so download, install and build against it. +# Parameters: +# $1 -- libseccomp version to download and build. +# $2 -- destination directory to put the source tarball in. +# $3 -- file to append LIBSECCOMP_PREFIX*= environment variables to +# (can be sourced to get install paths). +# $@ -- additional architectures to cross-compile for. +function build_libseccomp() { + local ver="$1" + shift + local dest="$1" + shift + local varfile="$1" + shift + local arches=("$@") + local tar="libseccomp-${ver}.tar.gz" + + # Download and extract. + wget "https://github.com/seccomp/libseccomp/releases/download/v${ver}/${tar}"{,.asc} + local srcdir + srcdir="$(mktemp -d)" + tar xf "$tar" -C "$srcdir" + pushd "$srcdir/libseccomp-$ver" || return + + # Build and install natively. + local prefix + prefix="$(mktemp -d)" + ./configure \ + --prefix="$prefix" --libdir="$prefix/lib" \ + --enable-static --disable-shared + echo LIBSECCOMP_PREFIX="$prefix" >>"$varfile" + make install + make clean + + # Build and install for additional architectures. + local arch + for arch in "${arches[@]}"; do + prefix="$(mktemp -d)" + set_cross_vars "$arch" + ./configure --host "$HOST" \ + --prefix="$prefix" --libdir="$prefix/lib" \ + --enable-static --enable-shared + make install + make clean + echo "LIBSECCOMP_PREFIX_${arch}=$prefix" >>"$varfile" + done + + # Place the source tarball to $dest. + popd || return + mv "$tar"{,.asc} "$dest" +} + +if $# -lt 4; then + echo "Usage: seccomp.sh [ ...]" >&2 + exit 1 +fi + +build_libseccomp "$@"