Skip to content

Commit

Permalink
Cirrus: Automate releasing of tested binaries
Browse files Browse the repository at this point in the history
It's desirable to make archives available of builds containing actual
tested content.  While not official distro-releases, these will enable
third-party testing, experimentation, and development for both branches
(e.g. "master") and pull requests (e.g. "pr3106").

* Add a Makefile targets for archiving both regular podman binaries
  and the remote-client.  Encode release metadata within these
  archives so that their exact source can be identified.

* Fix bug with cross-compiling remote clients for the Windows and Darwin
  platforms.

* Add unit-testing of cross-compiles for Windows and Darwin platforms.

* A few small CI-script typo-fixes

* Add a script which operates in two modes:

  1. Call Makefile targets which produce release archives.
     Upload the archive to Cirrus-CI's built-in caching system
     using reproducible cache keys.

  2. Utilize reproduced cache keys to attempt download of cache
     from each tasks.  When successful, parse the file's
     release metadata, using it to name the archive file.  Upload
     all recovered archives to a publicly accessible storage bucket
     for future reference.

* Update the main testing task to call the script in mode #1 for
  all primary platforms.

* Add a new `$SPECIALMODE` task to call the script in mode #1 for
  Windows and Darwin targets.

* Add a new 'release' task to the CI system, dependent upon all other
  tasks.  This new tasks executes the script in mode #2.

* Update CI documentation

Signed-off-by: Chris Evich <[email protected]>
  • Loading branch information
cevich committed Jul 3, 2019
1 parent f5593d3 commit 1ef8637
Show file tree
Hide file tree
Showing 11 changed files with 314 additions and 46 deletions.
50 changes: 50 additions & 0 deletions .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ testing_task:
unit_test_script: '$SCRIPT_BASE/unit_test.sh |& ${TIMESTAMP}'
integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}'
system_test_script: '$SCRIPT_BASE/system_test.sh |& ${TIMESTAMP}'
cache_release_archive_script: >-
[[ "$TEST_REMOTE_CLIENT" == "false" ]] || \
$SCRIPT_BASE/cache_release_archive.sh |& ${TIMESTAMP}
on_failure:
failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'
Expand Down Expand Up @@ -379,6 +382,29 @@ special_testing_in_podman_task:
<<: *standardlogs


special_testing_cross_task:

depends_on:
- "gating"
- "varlink_api"
- "vendor"

only_if: $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*'

env:
matrix:
SPECIALMODE: 'windows' # See docs
SPECIALMODE: 'darwin'

timeout_in: 20m

setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}'
cache_release_archive_script: '$SCRIPT_BASE/cache_release_archive.sh |& ${TIMESTAMP}'

on_failure:
failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh'


# Test building of new cache-images for future PR testing, in this PR.
test_build_cache_images_task:

Expand Down Expand Up @@ -464,6 +490,7 @@ success_task:
- "testing"
- "special_testing_rootless"
- "special_testing_in_podman"
- "special_testing_cross"
- "test_build_cache_images"
- "verify_test_built_images"
- "build_without_cgo"
Expand All @@ -479,3 +506,26 @@ success_task:
memory: 1

success_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/success.sh |& ${TIMESTAMP}'


release_task:

# TODO: Uncomment both to not affect pass/fail status of entire job?
# allow_failures: $CI == "true"
# skip_notifications: $CI == "true"

depends_on:
- "success"

gce_instance:
image_name: "${IMAGE_BUILDER_CACHE_IMAGE_NAME}"

timeout_in: 30m

env:
CIRRUS_CLONE_DEPTH: 1 # source is not used, only Makefile
GCPJSON: ENCRYPTED[789d8f7e9a5972ce350fd8e60f1032ccbf4a35c3938b604774b711aad280e12c21faf10e25af1e0ba33597ffb9e39e46]
GCPNAME: ENCRYPTED[417d50488a4bd197bcc925ba6574de5823b97e68db1a17e3a5fde4bcf26576987345e75f8d9ea1c15a156b4612c072a1]
GCPROJECT: ENCRYPTED[7c80e728e046b1c76147afd156a32c1c57d4a1ac1eab93b7e68e718c61ca8564fc61fef815952b8ae0a64e7034b8fe4f]

uncache_release_archives_script: '$SCRIPT_BASE/uncache_release_archives.sh |& ${TIMESTAMP}'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ __pycache__
/cmd/podman/varlink/iopodman.go
.gopathok
test/e2e/e2e.coverprofile
/podman*zip
29 changes: 26 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,16 @@ LDFLAGS_PODMAN ?= $(LDFLAGS) \
-X $(LIBPOD).etcDir=$(ETCDIR)
#Update to LIBSECCOMP_COMMIT should reflect in Dockerfile too.
LIBSECCOMP_COMMIT := release-2.3

# Rarely if ever should integration tests take more than 50min,
# caller may override in special circumstances if needed.
GINKGOTIMEOUT ?= -timeout=90m

RELEASE_VERSION ?= $(shell git fetch --tags && git describe HEAD 2> /dev/null)
RELEASE_DIST ?= $(shell ( source /etc/os-release; echo $$ID ))
RELEASE_DIST_VER ?= $(shell ( source /etc/os-release; echo $$VERSION_ID | cut -d '.' -f 1))
RELEASE_ARCH ?= $(shell go env GOARCH 2> /dev/null)
RELEASE_BASENAME := $(shell basename $(PROJECT))

# If GOPATH not specified, use one in the local directory
ifeq ($(GOPATH),)
export GOPATH := $(CURDIR)/_output
Expand Down Expand Up @@ -148,10 +153,10 @@ podman-remote: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman on
$(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS) remoteclient" -o bin/$@ $(PROJECT)/cmd/podman

podman-remote-darwin: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman on remote OSX environment
GOOS=darwin $(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "remoteclient containers_image_openpgp exclude_graphdriver_devicemapper" -o bin/$@ $(PROJECT)/cmd/podman
CGO_ENABLED=0 GOOS=darwin $(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "remoteclient containers_image_openpgp exclude_graphdriver_devicemapper" -o bin/$@ $(PROJECT)/cmd/podman

podman-remote-windows: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman for a remote windows environment
GOOS=windows $(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "remoteclient containers_image_openpgp exclude_graphdriver_devicemapper" -o bin/$@.exe $(PROJECT)/cmd/podman
CGO_ENABLED=0 GOOS=windows $(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "remoteclient containers_image_openpgp exclude_graphdriver_devicemapper" -o bin/$@.exe $(PROJECT)/cmd/podman

local-cross: $(CROSS_BUILD_TARGETS) ## Cross local compilation

Expand All @@ -165,6 +170,7 @@ clean: ## Clean artifacts
rm -rf \
.gopathok \
_output \
podman*.zip \
bin \
build \
test/checkseccomp/checkseccomp \
Expand Down Expand Up @@ -250,6 +256,23 @@ vagrant-check:

binaries: varlink_generate podman podman-remote ## Build podman

# Zip archives are supported on all platforms + allows embedding metadata
podman.zip: binaries docs
$(eval TMPDIR := $(shell mktemp -d -p '' $@_XXXX))
test -n "$(TMPDIR)"
$(MAKE) install "DESTDIR=$(TMPDIR)" "PREFIX=$(TMPDIR)/usr"
# Encoded RELEASE_INFO format depended upon by CI tooling
# X-RELEASE-INFO format depended upon by CI tooling
cd "$(TMPDIR)" && echo \
"X-RELEASE-INFO: $(RELEASE_BASENAME) $(RELEASE_VERSION) $(RELEASE_DIST) $(RELEASE_DIST_VER) $(RELEASE_ARCH)" | \
zip --recurse-paths --archive-comment "$(CURDIR)/$@" "./"
-rm -rf "$(TMPDIR)"

podman-remote-%.zip: podman-remote-%
# Don't label darwin/windows cros-compiles with local distribution & version
echo "X-RELEASE-INFO: podman-remote $(RELEASE_VERSION) $* cc $(RELEASE_ARCH)" | \
zip --archive-comment "$(CURDIR)/$@" ./bin/$<*

install.catatonit:
./hack/install_catatonit.sh

Expand Down
36 changes: 34 additions & 2 deletions contrib/cirrus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ which alter this behavior. Within each task, each script executes in sequence,
so long as any previous script exited successfully. The overall state of each
task (pass or fail) is set based on the exit status of the last script to execute.


### ``gating`` Task

***N/B: Steps below are performed by automation***
Expand Down Expand Up @@ -64,6 +63,12 @@ task (pass or fail) is set based on the exit status of the last script to execut
but this script normally completes in less than an hour.


### ``special_testing_cross`` Task

Confirm that cross-compile of podman-remote functions for both `windows`
and `darwin` targets.


### ``test_build_cache_images_task`` Task

Modifying the contents of cache-images is tested by making changes to
Expand Down Expand Up @@ -142,8 +147,22 @@ the magic ``***CIRRUS: TEST IMAGES***`` string. Keeping it and
`--force` pushing would needlessly cause Cirrus-CI to build
and test images again.

### `release` Task

Gathers up zip files uploaded by other tasks, from the local Cirrus-CI caching service.
Depending on the execution context (a PR or a branch), this task uploads the files
found to storage buckets at:

* [https://storage.cloud.google.com/libpod-pr-releases](https://storage.cloud.google.com/libpod-pr-releases)
* [https://storage.cloud.google.com/libpod-master-releases](https://storage.cloud.google.com/libpod-master-releases)

### Base-images
***Note:*** Repeated builds from the same PR or branch, will clobber previous archives
*by design*. This is intended so that the "latest" archive is always
available at a consistent URL. The precise details regarding a particular
build is encoded within the zip-archive comment.


## Base-images

Base-images are VM disk-images specially prepared for executing as GCE VMs.
In particular, they run services on startup similar in purpose/function
Expand Down Expand Up @@ -236,3 +255,16 @@ console output. Simply set the ``TTYDEV`` parameter, for example:
$ make libpod_base_images ... TTYDEV=$(tty)
...
```

## `$SPECIALMODE`

Some tasks alter their behavior based on this value. A summary of supported
values follows:

* `none`: Operate as normal, this is the default value if unspecified.
* `rootless`: Causes a random, ordinary user account to be created
and utilized for testing.
* `in_podman`: Causes testing to occur within a container executed by
podman on the host.
* `windows`: See **darwin**
* `darwin`: Signals the ``special_testing_cross`` task to cross-compile the remote client.
140 changes: 140 additions & 0 deletions contrib/cirrus/cache_release_archive.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/bin/bash

set -eo pipefail

source $(dirname $0)/lib.sh

req_env_var GOSRC

RELEASE_ARCHIVE_NAMES=""

handle_archive() { # Assumed to be called with set +e
TASK_NUMBER=$1
PR_OR_BRANCH=$2
CACHE_URL=$3
ARCHIVE_NAME="$(basename $CACHE_URL)"
req_env_var TASK_NUMBER PR_OR_BRANCH CACHE_URL ARCHIVE_NAME

cd /tmp
curl -sO "$CACHE_URL" || return $(warn 0 "Couldn't download file, skipping.")
[[ -r "/tmp/$ARCHIVE_NAME" ]] || return $(warn 0 "Unreadable archive '/tmp/$ARCHIVE_NAME', skipping.")

ZIPCOMMENT=$(unzip -qqz "$ARCHIVE_NAME" 2>/dev/null) # noisy bugger
if [[ "$?" -ne "0" ]] || [[ -z "$ZIPCOMMENT" ]]
then
return $(warn 0 "Could not unzip metadata from downloaded '/tmp/$ARCHIVE_NAME', skipping.")
fi

RELEASE_INFO=$(echo "$ZIPCOMMENT" | grep -m 1 'X-RELEASE-INFO:' | sed -r -e 's/X-RELEASE-INFO:\s*(.+)/\1/')
if [[ "$?" -ne "0" ]] || [[ -z "$RELEASE_INFO" ]]
then
return $(warn 0 "Metadata empty or invalid: '$ZIPCOMMENT', skipping.")
fi

# e.g. libpod v1.3.1-166-g60df124e fedora 29 amd64
# or libpod v1.3.1-166-g60df124e amd64
FIELDS="RELEASE_BASENAME RELEASE_VERSION RELEASE_DIST RELEASE_DIST_VER RELEASE_ARCH"
read $FIELDS <<< $RELEASE_INFO
for f in $FIELDS
do
[[ -n "${!f}" ]] || return $(warn 0 "Expecting $f to be non-empty in metadata: '$RELEASE_INFO', skipping.")
done

echo -n "Preparing $RELEASE_BASENAME archive: "
# Drop version number to enable "latest" representation
# (version available w/in zip-file comment)
RELEASE_ARCHIVE_NAME="${RELEASE_BASENAME}-${PR_OR_BRANCH}-${RELEASE_DIST}-${RELEASE_DIST_VER}-${RELEASE_ARCH}.zip"
# Allow uploading all gathered files in parallel, later with gsutil.
mv -v "$ARCHIVE_NAME" "/$RELEASE_ARCHIVE_NAME"
RELEASE_ARCHIVE_NAMES="$RELEASE_ARCHIVE_NAMES $RELEASE_ARCHIVE_NAME"
}

make_release() {
ARCHIVE_NAME="$1"
req_env_var ARCHIVE_NAME

# There's no actual testing of windows/darwin targets yet
# but we still want to cross-compile and publish binaries
if [[ "$SPECIALMODE" == "windows" ]] || [[ "$SPECIALMODE" == "darwin" ]]
then
RELFILE="podman-remote-${SPECIALMODE}.zip"
elif [[ "$SPECIALMODE" == "none" ]]
then
RELFILE="podman.zip"
else
die 55 "$(basename $0) unable to handle \$SPECIALMODE=$SPECIALMODE for $ARCHIVE_NAME"
fi
echo "Calling make $RELFILE"
cd $GOSRC
make "$RELFILE"
echo "Renaming archive so it can be identified/downloaded for publishing"
mv -v "$RELFILE" "$ARCHIVE_NAME"
echo "Success!"
}

[[ "$CI" == "true" ]] || \
die 56 "$0 requires a Cirrus-CI cross-task cache to function"

cd $GOSRC
# Same script re-used for both uploading and downloading to avoid duplication
if [[ "$(basename $0)" == "cache_release_archive.sh" ]]
then
# ref: https://cirrus-ci.org/guide/writing-tasks/#environment-variables
req_env_var CI_NODE_INDEX CIRRUS_BUILD_ID
# Use unique names for uncache_release_archives.sh to find/download them all
ARCHIVE_NAME="build-${CIRRUS_BUILD_ID}-task-${CI_NODE_INDEX}.zip"
make_release "$ARCHIVE_NAME"

# ref: https://cirrus-ci.org/guide/writing-tasks/#http-cache
URL="http://$CIRRUS_HTTP_CACHE_HOST/${ARCHIVE_NAME}"
echo "Uploading $ARCHIVE_NAME to Cirrus-CI cache at $URL"
curl -s -X POST --data-binary "@$ARCHIVE_NAME" "$URL"
elif [[ "$(basename $0)" == "uncache_release_archives.sh" ]]
then
req_env_var CIRRUS_BUILD_ID CI_NODE_TOTAL GCPJSON GCPNAME GCPROJECT
[[ "${CI_NODE_INDEX}" -eq "$[CI_NODE_TOTAL-1]" ]] || \
die 8 "The release task must be executed last to guarantee archive cache is complete"

if [[ -n "$CIRRUS_PR" ]]
then
PR_OR_BRANCH="pr$CIRRUS_PR"
BUCKET="libpod-pr-releases"
elif [[ -n "$CIRRUS_BRANCH" ]]
then
PR_OR_BRANCH="$CIRRUS_BRANCH"
BUCKET="libpod-$CIRRUS_BRANCH-releases"
else
die 10 "Expecting either \$CIRRUS_PR or \$CIRRUS_BRANCH to be non-empty."
fi

echo "Blindly downloading Cirrus-CI cache files for task (some will fail)."
set +e # Don't stop looping until all task's cache is attempted
for (( task_number = 0 ; task_number < $CI_NODE_TOTAL ; task_number++ ))
do
ARCHIVE_NAME="build-${CIRRUS_BUILD_ID}-task-${task_number}.zip"
URL="http://$CIRRUS_HTTP_CACHE_HOST/${ARCHIVE_NAME}"
echo "Attempting to download cached archive from $URL"
handle_archive "$task_number" "$PR_OR_BRANCH" "$URL"
echo "----------------------------------------"
done
set -e

[[ -n "$RELEASE_ARCHIVE_NAMES" ]] || \
die 67 "Error: No release archives found in CI cache, expecting at least one."

echo "Preparing to upload release archives."
gcloud config set project "$GCPROJECT"
echo "$GCPJSON" > /tmp/gcp.json
gcloud auth activate-service-account --key-file=/tmp/gcp.json
rm /tmp/gcp.json
# handle_archive() placed all uploadable files under /
gsutil -m cp /*.zip "gs://$BUCKET" # Upload in parallel
echo "Successfully uploaded archives:"
for ARCHIVE_NAME in $RELEASE_ARCHIVE_NAMES
do
echo " https://storage.cloud.google.com/$BUCKET/$ARCHIVE_NAME"
done
echo "These will remain available until automatic pruning by bucket policy."
else
die 9 "I don't know what to do when called $0"
fi
Loading

0 comments on commit 1ef8637

Please sign in to comment.