Migrated kCTF from Google Container Registry to Artifact Registry #823
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Update docker images | |
on: | |
push: | |
paths-ignore: | |
- 'docs/**' | |
- '*.md' | |
pull_request: | |
paths-ignore: | |
- 'docs/**' | |
- '*.md' | |
env: | |
GKE_PROJECT: ${{ secrets.GKE_PROJECT }} | |
GKE_ZONE: us-east1-c | |
GKE_CLUSTER: github-ci | |
GKE_REGISTRY: us.gcr.io | |
jobs: | |
build-docker: | |
runs-on: ubuntu-latest | |
if: github.event_name == 'push' | |
outputs: | |
challenge-modified: ${{ steps.set-modified.outputs.challenge-modified }} | |
healthcheck-modified: ${{ steps.set-modified.outputs.healthcheck-modified }} | |
gcsfuse-modified: ${{ steps.set-modified.outputs.gcsfuse-modified }} | |
certbot-modified: ${{ steps.set-modified.outputs.certbot-modified }} | |
challenge-digest: ${{ steps.push.outputs.challenge-digest }} | |
healthcheck-digest: ${{ steps.push.outputs.healthcheck-digest }} | |
gcsfuse-digest: ${{ steps.push.outputs.gcsfuse-digest }} | |
certbot-digest: ${{ steps.push.outputs.certbot-digest }} | |
strategy: | |
matrix: | |
image: ["challenge", "healthcheck", "gcsfuse", "certbot"] | |
steps: | |
- uses: actions/checkout@v2 | |
with: | |
fetch-depth: 0 | |
- id: modified | |
name: Check for modified paths | |
run: | | |
PATHS=(".github/workflows/update-images.yaml" "docker-images/${{ matrix.image }}/**") | |
BASE_SHA="$(git log -n1 --grep='Automated commit: update images.' --format=%H || echo '')" | |
echo "BASE_SHA=${BASE_SHA}" | |
if [ -z "${BASE_SHA}" ]; then | |
# we couldn't find any existing robot commit, just rebuild everything | |
echo "::set-output name=modified::true" | |
exit 0 | |
fi | |
CHANGED_FILES="$(git diff --name-only ${BASE_SHA} ${{ github.sha }})" | |
echo "CHANGED_FILES=${CHANGED_FILES}" | |
while IFS= read -r changed_file; do | |
for watched_path in "${PATHS[@]}"; do | |
if [[ $changed_file == $watched_path ]]; then | |
echo "modified=true: ${changed_file} matches ${watched_path}" | |
echo "::set-output name=modified::true" | |
exit 0 | |
fi | |
done | |
done <<< "${CHANGED_FILES}" | |
- id: set-modified | |
name: Set modified | |
run: | | |
echo "::set-output name=${{ matrix.image }}-modified::${{ steps.modified.outputs.modified }}" | |
- name: Build image | |
if: steps.modified.outputs.modified | |
run: | | |
cd "docker-images/${{ matrix.image }}" | |
docker build . --tag "${{ matrix.image }}" | |
- name: Set up Python | |
uses: actions/setup-python@v4 | |
with: | |
python-version: '3.7' | |
- name: 'Set up Cloud SDK auth' | |
uses: 'google-github-actions/auth@v0' | |
with: | |
# not using workload identity because we use gsutil | |
# TODO(evn) switch when supported | |
# https://github.com/google-github-actions/setup-gcloud#authorization | |
credentials_json: '${{ secrets.GKE_KEY }}' | |
- name: 'Set up Cloud SDK' | |
uses: 'google-github-actions/setup-gcloud@v0' | |
with: | |
version: '319.0.0' | |
service_account_email: ${{ secrets.GKE_EMAIL }} | |
- name: Configure docker to use the gcloud command-line tool as a credential helper | |
if: steps.modified.outputs.modified | |
run: | | |
gcloud auth configure-docker | |
- id: push | |
name: Push images | |
if: steps.modified.outputs.modified | |
run: | | |
IMAGE_GCR="gcr.io/${{ secrets.GCR_PROJECT }}/${{ matrix.image }}" | |
docker tag "${{ matrix.image }}" "$IMAGE_GCR" | |
DIGEST="$(docker push "$IMAGE_GCR" | grep 'digest: ' | sed 's/.*\(sha256:[^ ]*\).*/\1/')" | |
echo "::set-output name=${{ matrix.image }}-digest::${DIGEST}" | |
build-operator: | |
runs-on: ubuntu-latest | |
needs: | |
- build-docker | |
if: github.event_name == 'push' | |
outputs: | |
kctf-operator-modified: ${{ steps.set-modified.outputs.kctf-operator-modified }} | |
kctf-operator-digest: ${{ steps.push.outputs.kctf-operator-digest }} | |
steps: | |
- uses: actions/checkout@v2 | |
with: | |
fetch-depth: 0 | |
- id: modified | |
name: Check for modified paths | |
run: | | |
PATHS=(".github/workflows/update-images.yaml" "kctf-operator/**") | |
BASE_SHA="$(git log -n1 --grep='Automated commit: update images.' --format=%H || echo '')" | |
echo "BASE_SHA=${BASE_SHA}" | |
if [ -z "${BASE_SHA}" ]; then | |
# we couldn't find any existing robot commit, just rebuild everything | |
echo "::set-output name=modified::true" | |
exit 0 | |
fi | |
CHANGED_FILES="$(git diff --name-only ${BASE_SHA} ${{ github.sha }})" | |
echo "CHANGED_FILES=${CHANGED_FILES}" | |
while IFS= read -r changed_file; do | |
for watched_path in "${PATHS[@]}"; do | |
if [[ $changed_file == $watched_path ]]; then | |
echo "modified=true: ${changed_file} matches ${watched_path}" | |
echo "::set-output name=modified::true" | |
exit 0 | |
fi | |
done | |
done <<< "${CHANGED_FILES}" | |
- id: set-modified | |
name: Set modified | |
run: | | |
echo "::set-output name=kctf-operator-modified::${{ steps.modified.outputs.modified }}" | |
- name: 'Set up Cloud SDK auth' | |
uses: 'google-github-actions/auth@v0' | |
with: | |
# not using workload identity because we use gsutil | |
# TODO(evn) switch when supported | |
# https://github.com/google-github-actions/setup-gcloud#authorization | |
credentials_json: '${{ secrets.GKE_KEY }}' | |
- name: Set up Python | |
uses: actions/setup-python@v4 | |
with: | |
python-version: '3.7' | |
- name: Export gcloud related env variable | |
run: export CLOUDSDK_PYTHON="/usr/bin/python3" | |
- name: 'Set up Cloud SDK' | |
uses: 'google-github-actions/setup-gcloud@v0' | |
with: | |
version: '319.0.0' | |
service_account_email: ${{ secrets.GKE_EMAIL }} | |
- name: Configure docker to use the gcloud command-line tool as a credential helper | |
if: steps.modified.outputs.modified | |
run: | | |
gcloud auth configure-docker | |
- name: 'Setup go version necessary for operator' | |
uses: actions/setup-go@v2 | |
with: | |
go-version: '1.16.0' | |
- name: Build image | |
if: steps.modified.outputs.modified | |
run: | | |
cd kctf-operator | |
curl -L https://github.com/operator-framework/operator-sdk/releases/download/v1.15.0/operator-sdk_linux_amd64 -o operator-sdk | |
chmod u+x operator-sdk | |
sudo mv operator-sdk /usr/local/bin/ | |
make controller-gen | |
make manifests docker-build IMG=kctf-operator | |
- id: push | |
name: Push images | |
if: steps.modified.outputs.modified | |
run: | | |
IMAGE_GCR="gcr.io/${{ secrets.GCR_PROJECT }}/kctf-operator" | |
docker tag "kctf-operator" "$IMAGE_GCR" | |
DIGEST="$(docker push "$IMAGE_GCR" | grep 'digest: ' | sed 's/.*\(sha256:[^ ]*\).*/\1/')" | |
echo "::set-output name=kctf-operator-digest::${DIGEST}" | |
update-image-and-commit: | |
runs-on: ubuntu-latest | |
needs: | |
- build-docker | |
- build-operator | |
steps: | |
- uses: actions/checkout@v2 | |
- name: Update challenge image | |
if: needs.build-docker.outputs.challenge-modified | |
run: | | |
IMAGE="gcr.io/${{ secrets.GCR_PROJECT }}/challenge@${{ needs.build-docker.outputs.challenge-digest }}" | |
for dir in dist/challenge-templates/*; do | |
if [[ -e "${dir}/challenge/Dockerfile" ]]; then | |
sed -i "s#FROM gcr.io/${{ secrets.GCR_PROJECT }}/challenge.*#FROM ${IMAGE}#" "${dir}/challenge/Dockerfile" | |
fi | |
done | |
- name: Update healthcheck image | |
if: needs.build-docker.outputs.healthcheck-modified | |
run: | | |
IMAGE="gcr.io/${{ secrets.GCR_PROJECT }}/healthcheck@${{ needs.build-docker.outputs.healthcheck-digest }}" | |
for dir in dist/challenge-templates/*; do | |
if [[ -e "${dir}/healthcheck/Dockerfile" ]]; then | |
sed -i "s#FROM gcr.io/${{ secrets.GCR_PROJECT }}/healthcheck.*#FROM ${IMAGE}#" "${dir}/healthcheck/Dockerfile" | |
fi | |
done | |
- name: Update gcsfuse image | |
if: needs.build-docker.outputs.gcsfuse-modified | |
run: | | |
IMAGE="gcr.io/${{ secrets.GCR_PROJECT }}/gcsfuse@${{ needs.build-docker.outputs.gcsfuse-digest }}" | |
sed -i 's%const DOCKER_GCSFUSE_IMAGE = .*%const DOCKER_GCSFUSE_IMAGE = "'${IMAGE}'"%' kctf-operator/resources/constants.go | |
- name: Update certbot image | |
if: needs.build-docker.outputs.certbot-modified | |
run: | | |
IMAGE="gcr.io/${{ secrets.GCR_PROJECT }}/certbot@${{ needs.build-docker.outputs.certbot-digest }}" | |
sed -i 's%const DOCKER_CERTBOT_IMAGE = .*%const DOCKER_CERTBOT_IMAGE = "'${IMAGE}'"%' kctf-operator/resources/constants.go | |
- name: Update operator | |
if: needs.build-operator.outputs.kctf-operator-modified | |
run: | | |
IMAGE="gcr.io/${{ secrets.GCR_PROJECT }}/kctf-operator@${{ needs.build-operator.outputs.kctf-operator-digest }}" | |
sed -i "s#image: .*kctf-operator.*#image: ${IMAGE}#" dist/resources/operator.yaml | |
- name: Download kubectl | |
run: | | |
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl | |
chmod +x ./kubectl | |
sudo mv ./kubectl /usr/local/bin/kubectl | |
- name: Set up Python | |
uses: actions/setup-python@v4 | |
with: | |
python-version: '3.7' | |
- name: Export gcloud related env variable | |
run: export CLOUDSDK_PYTHON="/usr/bin/python3" | |
- name: 'Set up Cloud SDK auth' | |
uses: 'google-github-actions/auth@v0' | |
with: | |
# not using workload identity because we use gsutil | |
# TODO(evn) switch when supported | |
# https://github.com/google-github-actions/setup-gcloud#authorization | |
credentials_json: '${{ secrets.GKE_KEY }}' | |
- name: 'Set up Cloud SDK' | |
uses: 'google-github-actions/setup-gcloud@v0' | |
with: | |
service_account_email: ${{ secrets.GKE_EMAIL }} | |
install_components: 'gke-gcloud-auth-plugin' | |
- name: Configure docker to use the gcloud command-line tool as a credential helper | |
run: | | |
gcloud auth configure-docker | |
- name: Configure kCTF cluster | |
run: | | |
mkdir /tmp/samples | |
cp -R dist /tmp/samples/kctf | |
source /tmp/samples/kctf/activate | |
kctf cluster create --project $GKE_PROJECT --zone $GKE_ZONE --registry $GKE_REGISTRY --cluster-name $GKE_CLUSTER --domain-name kctf-ci.kctf.cloud test | |
# Try to delete any existing CRD in case this branch is older than what's running on the cluster | |
# Though, this is just a hotfix. We don't support downgrading and we should rather test using | |
# KIND in this workflow and then do a full cluster delete/create during merge. | |
kubectl delete crd/challenges.kctf.dev || true | |
kctf cluster start | |
- name: Deploy all tasks | |
run: | | |
source /tmp/samples/kctf/activate | |
cd /tmp/samples | |
for challenge_name in $(kubectl get challenges -o "jsonpath={.items[*].metadata.name}"); do | |
echo "deleting challenge ${challenge_name}" | |
kubectl delete "challenge/${challenge_name}" | |
done | |
for template in $(ls kctf/challenge-templates/); do | |
kctf chal create --template $template $template | |
echo "entering challenge directory ${template}" | |
pushd $template | |
if [[ -e "challenge/Makefile" ]]; then | |
make -C "challenge" | |
fi | |
sed -i "s/public: false/public: true/" challenge.yaml | |
CHALLENGE_NAME="$("${KCTF_BIN}/yq" eval '.metadata.name' challenge.yaml)" | |
echo "starting challenge ${CHALLENGE_NAME}" | |
kctf chal start | |
echo "challenge started, waiting for it to become available" | |
# We want to wait for the deployment to be available, but it | |
# might not have been created yet by the operator and wait will fail. | |
# So try to "kubectl get" the challenge a few times to make sure it exists. | |
# Ideally, we would expose the condition in the operator but I | |
# don't think that's currently possible. | |
for i in {1..5}; do | |
kubectl get "deployment/${CHALLENGE_NAME}" && break | |
echo "deployment/${CHALLENGE_NAME} doesn't exist yet, sleeping" | |
sleep 5 | |
done | |
kubectl wait --for=condition=available --timeout=5m "deployment/${CHALLENGE_NAME}" | |
echo "challenge availble, stopping" | |
kctf chal stop | |
popd | |
done | |
- name: Commit | |
env: | |
SUBMITTER: ${{ github.event.head_commit.author.email }} | |
run: | | |
# git add returns success for files that exist and haven't been modified | |
git add kctf-operator/resources/constants.go | |
git add dist/resources/operator.yaml | |
git add kctf-operator/config/crd/bases/kctf.dev_challenges.yaml | |
git add dist/resources/kctf.dev_challenges.yaml | |
for dir in dist/challenge-templates/* samples/*; do | |
if [[ ! -e "${dir}/challenge.yaml" ]]; then | |
continue | |
fi | |
git add "${dir}/challenge/Dockerfile" | |
# not all challenges might have healthchecks, ignore errors | |
git add "${dir}/healthcheck/Dockerfile" 2>&1 || true | |
done | |
git status | |
git config user.email "$SUBMITTER" | |
git config user.name "GitHub Action" | |
if git commit -m "Automated commit: update images."; then | |
git push | |
fi |