Skip to content

Commit c7d43d8

Browse files
joelanfordfabianvf
authored andcommitted
Build, tag, and push operator base images during CI (operator-framework#832)
* Makefile,hack/docker,hack/tests: adding new docker scripts and Make targets * Makefile,pkg/scaffold: changing default image repo to quay.io/operator-framework * [travis deploy] .travis.yml: refactor into stages to build and push docker images * [travis deploy] Makefile,hack/: renaming make 'docker' targets to 'image' * [travis deploy] .travis.yml: renaming make 'docker' targets to 'image' * [travis deploy] .travis.yml,Makefile,hack/image: updates from PR feedback * [travis deploy] test/ansible-operator/cmd/ansible-operator/main.go: copying from github.com/water-hole/ansible-operator * [travis deploy] test/helm-operator/cmd/helm-operator/main.go: use production logger * [travis deploy] test/helm-operator/cmd/helm-operator/main.go: load watches from env, not hardcoded file * [travis deploy] Makefile,hack/tests: always build, test, and tag from local-only image tag * [travis deploy] pkg/scaffold/ansible,pkg/scaffold/helm: use image tags from latest release * [travis deploy] Makefile: add missing PHONY target for test/ci-helm * [travis deploy] test/ansible-operator/cmd/ansible-operator/main.go: watch all namespaces if WATCH_NAMESPACE is unset * [travis deploy] test/ansible-operator/cmd/ansible-operator/main.go: fixing ansible-operator compile error
1 parent 1053df1 commit c7d43d8

File tree

14 files changed

+335
-60
lines changed

14 files changed

+335
-60
lines changed

.travis.yml

+74-37
Original file line numberDiff line numberDiff line change
@@ -5,51 +5,88 @@ sudo: required
55
go:
66
- 1.10.3
77

8-
before_install:
9-
- |
10-
if [ "$TRAVIS_COMMIT_RANGE" != "" ] && ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qvE '(\.md)|(\.MD)|(\.png)|(\.pdf)|^(doc/)|^(MAINTAINERS)|^(LICENSE)'; then
11-
echo "Only doc files were updated, not running the CI."
12-
exit
13-
fi
8+
# The `x_base_steps` top-level key is unknown to travis,
9+
# so we can use it to create a bunch of common build step
10+
# YAML anchors which we use in our build jobs.
11+
x_base_steps:
12+
# before_install for jobs that require go builds
13+
- &go_before_install
14+
before_install:
15+
# hack/ci/check-doc-only-update.sh needs to be sourced so
16+
# that it can properly exit the test early with success
17+
- source hack/ci/check-doc-only-update.sh
18+
- curl -Lo dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && chmod +x dep && sudo mv dep /usr/local/bin/
19+
- travis_retry dep ensure
20+
21+
# Base go, ansbile, and helm job
22+
- &test
23+
stage: test
24+
env: CLUSTER=openshift
25+
<<: *go_before_install
26+
install:
27+
- make install
28+
- hack/ci/setup-openshift.sh
29+
after_success:
30+
- echo "Build succeeded, operator was generated, memcached operator is running on $CLUSTER, and unit/integration tests pass"
31+
after_failure:
32+
- echo "Build failed, operator failed to generate, memcached operator is not running on $CLUSTER, or unit/integration tests failed"
33+
- kubectl get all --all-namespaces
34+
- kubectl get events --all-namespaces --field-selector=type=Warning
35+
services:
36+
- docker
37+
38+
# Base deploy job
39+
- &deploy
40+
stage: deploy
41+
if: type != pull_request AND ( tag IS present OR branch = master OR commit_message =~ /\[travis deploy\]/ )
42+
<<: *go_before_install
43+
install: skip
44+
before_script:
45+
- git config remote.origin.fetch +refs/heads/*:refs/remotes/origin/*
46+
- git fetch --unshallow --tags
47+
after_success:
48+
- echo "Image build succeeded, and docker image tagged and pushed to repository"
49+
after_failure:
50+
- echo "Image build, docker image tagging, or docker image pushing to repository failed"
51+
services:
52+
- docker
1453

1554
jobs:
1655
include:
17-
- before_script: hack/ci/setup-openshift.sh
18-
env: CLUSTER=openshift
19-
script: make test/ci-go
56+
# Build and test go
57+
- <<: *test
2058
name: Go on OpenShift
21-
services:
22-
- docker
23-
- before_script: hack/ci/setup-openshift.sh
24-
env: CLUSTER=openshift
25-
script: make test/ci-ansible
59+
script: make test/ci-go
60+
61+
# Build and test ansible
62+
- <<: *test
2663
name: Ansible on OpenShift
27-
services:
28-
- docker
29-
- before_script: hack/ci/setup-openshift.sh
30-
env: CLUSTER=openshift
31-
script: make test/ci-helm
64+
before_script: sudo pip install ansible
65+
script: make test/ci-ansible
66+
67+
# Build and test helm
68+
- <<: *test
3269
name: Helm on OpenShift
33-
services:
34-
- docker
35-
- name: Markdown Link Checker
70+
script: make test/ci-helm
71+
72+
# Test markdown
73+
- stage: test
74+
name: Markdown Link Checker
3675
language: bash
37-
before_install: true
38-
install: true
76+
script: make test/markdown
3977
after_success: echo 'Markdown links are correct'
4078
after_failure: echo 'Incorrect markdown link detected'
41-
script: make test/markdown
42-
43-
install:
44-
- curl -Lo dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && chmod +x dep && sudo mv dep /usr/local/bin/
45-
- dep ensure
46-
- make install
47-
- sudo pip install ansible
4879

49-
after_success:
50-
- echo "Build succeeded, operator was generated, memcached operator is running on $CLUSTER, and unit/integration tests pass"
80+
# Build and deploy ansible-operator docker image
81+
- <<: *deploy
82+
name: Docker image for ansible-operator
83+
script:
84+
- make image/build/ansible
85+
- make image/push/ansible
5186

52-
after_failure:
53-
- echo "Build failed, operator failed to generate, memcached operator is not running on $CLUSTER, or unit/integration tests failed"
54-
- kubectl get all --all-namespaces
55-
- kubectl get events --all-namespaces --field-selector=type=Warning
87+
# Build and deploy helm-operator docker image
88+
- <<: *deploy
89+
name: Docker image for helm-operator
90+
script:
91+
- make image/build/helm
92+
- make image/push/helm

Makefile

+29-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ REPO = github.com/operator-framework/operator-sdk
1414
BUILD_PATH = $(REPO)/commands/operator-sdk
1515
PKGS = $(shell go list ./... | grep -v /vendor/)
1616

17+
ANSIBLE_BASE_IMAGE = quay.io/operator-framework/ansible-operator
18+
HELM_BASE_IMAGE = quay.io/operator-framework/helm-operator
19+
20+
ANSIBLE_IMAGE ?= $(ANSIBLE_BASE_IMAGE)
21+
HELM_IMAGE ?= $(HELM_BASE_IMAGE)
22+
1723
export CGO_ENABLED:=0
1824

1925
all: format test build/operator-sdk
@@ -84,13 +90,33 @@ test/e2e: test/e2e/go test/e2e/ansible test/e2e/helm
8490
test/e2e/go:
8591
./hack/tests/e2e-go.sh $(ARGS)
8692

87-
test/e2e/ansible:
93+
test/e2e/ansible: image/build/ansible
8894
./hack/tests/e2e-ansible.sh
8995

90-
test/e2e/helm:
96+
test/e2e/helm: image/build/helm
9197
./hack/tests/e2e-helm.sh
9298

9399
test/markdown:
94100
./hack/ci/marker --root=doc
95101

96-
.PHONY: test test/sanity test/unit test/subcommand test/e2e test/e2e/go test/e2e/ansible test/e2e/helm test/ci-go test/ci-ansible test/markdown
102+
.PHONY: test test/sanity test/unit test/subcommand test/e2e test/e2e/go test/e2e/ansible test/e2e/helm test/ci-go test/ci-ansible test/ci-helm test/markdown
103+
104+
image: image/build image/push
105+
106+
image/build: image/build/ansible image/build/helm
107+
108+
image/build/ansible:
109+
./hack/image/build-ansible-image.sh $(ANSIBLE_BASE_IMAGE):dev
110+
111+
image/build/helm:
112+
./hack/image/build-helm-image.sh $(HELM_BASE_IMAGE):dev
113+
114+
image/push: image/push/ansible image/push/helm
115+
116+
image/push/ansible:
117+
./hack/image/push-image-tags.sh $(ANSIBLE_BASE_IMAGE):dev $(ANSIBLE_IMAGE)
118+
119+
image/push/helm:
120+
./hack/image/push-image-tags.sh $(HELM_BASE_IMAGE):dev $(HELM_IMAGE)
121+
122+
.PHONY: image image/build image/build/ansible image/build/helm image/push image/push/ansible image/push/helm

hack/ci/check-doc-only-update.sh

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
3+
if [ "$TRAVIS_COMMIT_RANGE" != "" ] && ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qvE '(\.md)|(\.MD)|(\.png)|(\.pdf)|^(doc/)|^(MAINTAINERS)|^(LICENSE)'; then
4+
echo "Only doc files were updated, not running the CI."
5+
exit
6+
fi
7+

hack/image/build-ansible-image.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
3+
set -eux
4+
5+
# build operator binary and base image
6+
go build -o test/ansible-operator/ansible-operator test/ansible-operator/cmd/ansible-operator/main.go
7+
pushd test/ansible-operator
8+
docker build -t "$1" .
9+
popd

hack/image/build-helm-image.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env bash
2+
3+
set -eux
4+
5+
# build operator binary and base image
6+
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o test/helm-operator/helm-operator test/helm-operator/cmd/helm-operator/main.go
7+
pushd test/helm-operator
8+
docker build -t "$1" .
9+
popd

hack/image/push-image-tags.sh

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#!/usr/bin/env bash
2+
3+
source hack/lib/common.sh
4+
5+
semver_regex="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$"
6+
7+
#
8+
# push_image_tags <source_image> <push_image>
9+
#
10+
# push_image_tags tags the source docker image with zero or more
11+
# image tags based on TravisCI environment variables and the
12+
# presence of git tags in the repository of the current working
13+
# directory. If a second argument is present, it will be used as
14+
# the base image name in pushed image tags.
15+
#
16+
function push_image_tags() {
17+
source_image=$1; shift || fatal "${FUNCNAME} usage error"
18+
push_image=$1; shift || push_image=$source_image
19+
20+
print_image_info $source_image
21+
print_git_tags
22+
docker_login
23+
check_can_push || return 0
24+
25+
images=$(get_image_tags $push_image)
26+
27+
for image in $images; do
28+
docker tag "$source_image" "$image"
29+
docker push "$image"
30+
done
31+
}
32+
33+
#
34+
# print_image_info <image_name>
35+
#
36+
# print_image_info prints helpful information about a docker
37+
# image.
38+
#
39+
function print_image_info() {
40+
image_name=$1; shift || fatal "${FUNCNAME} usage error"
41+
image_id=$(docker inspect "$image_name" -f "{{.Id}}")
42+
image_created=$(docker inspect "$image_name" -f "{{.Created}}")
43+
44+
if [[ -n "$image_id" ]]; then
45+
echo "Docker image info:"
46+
echo " Name: $image_name"
47+
echo " ID: $image_id"
48+
echo " Created: $image_created"
49+
echo ""
50+
else
51+
echo "Could not find docker image \"$image_name\""
52+
return 1
53+
fi
54+
}
55+
56+
#
57+
# print_git_tags
58+
#
59+
# print_git_tags prints all tags present in the git repository.
60+
#
61+
function print_git_tags() {
62+
git_tags=$(git tag -l | sed 's|^| |')
63+
if [[ -n "$git_tags" ]]; then
64+
echo "Found git tags:"
65+
echo "$git_tags"
66+
echo ""
67+
fi
68+
}
69+
70+
#
71+
# docker_login <image_name>
72+
#
73+
# docker_login performs a docker login for the server of the provided
74+
# image if the DOCKER_USERNAME and DOCKER_PASSWORD environment variables
75+
# are set.
76+
#
77+
function docker_login() {
78+
if [[ -n "$DOCKER_USERNAME" && -n "$DOCKER_PASSWORD" ]]; then
79+
server=$(docker_server_for_image $image_name)
80+
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin "$server"
81+
fi
82+
}
83+
84+
#
85+
# check_can_push
86+
#
87+
# check_can_push performs various checks to determine whether images
88+
# built from this commit should be pushed. It prints a message and
89+
# returns a failure code if any check doesn't pass.
90+
#
91+
function check_can_push() {
92+
if [[ "$TRAVIS" != "true" ]]; then
93+
echo "Detected execution in a non-TravisCI environment. Skipping image push."
94+
return 1
95+
elif [[ "$TRAVIS_EVENT_TYPE" == "pull_request" ]]; then
96+
echo "Detected pull request commit. Skipping image push"
97+
return 1
98+
elif [[ ! -f "$HOME/.docker/config.json" ]]; then
99+
echo "Docker login credentials required to push. Skipping image push."
100+
return 1
101+
fi
102+
}
103+
104+
#
105+
# get_image_tags <image_name>
106+
#
107+
# get_image_tags returns a list of tags that are eligible to be pushed.
108+
# If an image name is passed as an argument, the full <name>:<tag> will
109+
# be returned for each eligible tag. The criteria is:
110+
# 1. Is TRAVIS_BRANCH set? => <image_name>:$TRAVIS_BRANCH
111+
# 2. Is TRAVIS_TAG highest semver release? => <image_name>:latest
112+
#
113+
function get_image_tags() {
114+
image_name=$1
115+
[[ -n "$image_name" ]] && image_name="${image_name}:"
116+
117+
# Tag `:$TRAVIS_BRANCH` if it is set.
118+
# Note that if the build is for a tag, $TRAVIS_BRANCH is set
119+
# to the tag, so this works in both cases
120+
if [[ -n "$TRAVIS_BRANCH" ]]; then
121+
echo "${image_name}${TRAVIS_BRANCH}"
122+
fi
123+
124+
# Tag `:latest` if $TRAVIS_TAG is the highest semver tag found in
125+
# the repository.
126+
if is_latest_tag "$TRAVIS_TAG"; then
127+
echo "${image_name}latest"
128+
fi
129+
}
130+
131+
#
132+
# docker_server_for_image <image_name>
133+
#
134+
# docker_server_for_image returns the server component of the image
135+
# name. If the image name does not contain a server component, an
136+
# empty string is returned.
137+
#
138+
function docker_server_for_image() {
139+
image_name=$1; shift || fatal "${FUNCNAME} usage error"
140+
IFS='/' read -r -a segments <<< "$image_name"
141+
if [[ "${#segments[@]}" -gt "2" ]]; then
142+
echo "${segments[0]}"
143+
else
144+
echo ""
145+
fi
146+
}
147+
148+
#
149+
# latest_git_version
150+
#
151+
# latest_git_version returns the highest semantic version
152+
# number found in the repository, with the form "vX.Y.Z".
153+
# Version numbers not matching the semver release format
154+
# are ignored.
155+
#
156+
function latest_git_version() {
157+
git tag -l | egrep "${semver_regex}" | sort -V | tail -1
158+
}
159+
160+
#
161+
# is_latest_tag <candidate>
162+
#
163+
# is_latest_tag returns whether the candidate tag matches
164+
# the latest tag from the git repository, based on semver.
165+
# To be the latest tag, the candidate must match the semver
166+
# release format.
167+
#
168+
function is_latest_tag() {
169+
candidate=$1; shift || fatal "${FUNCNAME} usage error"
170+
if ! [[ "$candidate" =~ $semver_regex ]]; then
171+
return 1
172+
fi
173+
174+
latest="$(latest_git_version)"
175+
[[ -z "$latest" || "$candidate" == "$latest" ]]
176+
}
177+
178+
push_image_tags "$@"

hack/lib/common.sh

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
3+
function log() { printf '%s\n' "$*"; }
4+
function error() { log "ERROR: $*" >&2; }
5+
function fatal() { error "$@"; exit 1; }

0 commit comments

Comments
 (0)