Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement(ci): Add release docker image #844

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
157 changes: 157 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: docker
on:
push:
# Build on main
branches:
- main
tags:
- '**'
# TODO: Remove pull_request before merging
leojonathanoh marked this conversation as resolved.
Show resolved Hide resolved
pull_request:
branches:
- main

permissions:
contents: read
packages: write
# Ensure cosign can request for Github's OIDC JWT ID token
# See: https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-cloud-providers#adding-permissions-settings
id-token: write

jobs:
# This job builds the binaries and uploads it as github artifacts.
# This allows us to use the same binaries for any release CI jobs.
build:
name: Build all linux architectures
runs-on: ubuntu-latest
steps:
- name: setup go
uses: actions/setup-go@v3
with:
go-version: ${{ env.GO_VERSION }}

- uses: actions/checkout@v3
with:
# Fetch all tags
fetch-depth: 0

- name: Build on all supported architectures
run: |
set -e
./scripts/release.sh

- uses: actions/upload-artifact@v3
with:
name: binaries-${{ github.sha }}
path: |
release*/*

# This job downloads the binaries previously uploaded as artifacts, and builds multi-arch images
build-docker-image:
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
# Fetch all tags
fetch-depth: 0

- name: Prepare
id: prep
run: |
set -e
TAG=$( git describe --tags --dirty ) # E.g. v1.2.0-23-g60ee190
echo "TAG=$TAG" >> $GITHUB_ENV

# This step generates the docker tags
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ github.repository }}
ghcr.io/${{ github.repository }}
# type=ref,event=pr generates tag(s) on PRs only. E.g. 'pr-123', 'pr-123-abc0123'
# type=ref,event=branch generates tag(s) on branch only. E.g. 'main-abc0123'
# type=semver generates tag(s) on tags only. E.g. 'v0', 'v0.0', 'v0.0.0', and 'latest'
tags: |
type=ref,event=pr
type=ref,suffix=-{{sha}},event=branch
type=semver,pattern=v{{major}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}.{{minor}}.{{patch}}
squeed marked this conversation as resolved.
Show resolved Hide resolved
# The rest of the org.opencontainers.image.xxx labels are dynamically generated
labels: |
org.opencontainers.image.description=CNI Plugins
org.opencontainers.image.licenses=Apache License 2.0

# See: https://github.com/docker/build-push-action/blob/v2.6.1/docs/advanced/cache.md#github-cache
- name: Set up QEMU
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2

- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-

- name: Login to Docker Hub registry
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- uses: actions/download-artifact@v3
with:
name: binaries-${{ github.sha }}

- run: |
ls -al release*/

- name: Build and push
id: build-and-push
# TODO: Remove pull_request before merging
if: github.event_name == 'pull_request' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
uses: docker/build-push-action@v3
with:
build-args: |
TAG=${{ env.TAG }}
context: '.'
file: Dockerfile
platforms: linux/amd64,linux/arm,linux/arm64,linux/mips64le,linux/ppc64le,linux/riscv64,linux/s390x
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max

- name: Install cosign
uses: sigstore/cosign-installer@1fc5bd396d372bee37d608f955b336615edf79c8 # v3.2.0
- name: Check install!
run: cosign version

# This signs the image using ACTIONS_ID_TOKEN_REQUEST_TOKEN
- name: Sign the published Docker image
run: echo "${{ steps.meta.outputs.tags }}" | xargs -I {} cosign sign --yes {}@${{ steps.build-and-push.outputs.digest }}

# Temp fix
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
42 changes: 42 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
FROM busybox as test

ADD docker-installer/install_cni_plugins.sh /script/install_cni_plugins.sh
ADD docker-installer/test_install_cni_plugins.sh /script/test_install_cni_plugins.sh
WORKDIR /script
RUN /script/test_install_cni_plugins.sh

FROM busybox as build
ARG TAG
# Get buildx automatic platform vars: https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
ARG TARGETPLATFORM
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
ARG BUILDPLATFORM
ARG BUILDOS
ARG BUILDARCH
ARG BUILDVARIANT
RUN echo TARGETPLATFORM=$TARGETPLATFORM
RUN echo TARGETOS=$TARGETOS
RUN echo TARGETARCH=$TARGETARCH
RUN echo TARGETVARIANT=$TARGETVARIANT
RUN echo BUILDPLATFORM=$BUILDPLATFORM
RUN echo BUILDOS=$BUILDOS
RUN echo BUILDARCH=$BUILDARCH
RUN echo BUILDVARIANT=$BUILDVARIANT
# Use buildx automatic platform vars
COPY release-$TAG/cni-plugins-$TARGETOS-$TARGETARCH-$TAG.tgz cni-plugins-$TARGETOS-$TARGETARCH-$TAG.tgz
COPY release-$TAG/cni-plugins-$TARGETOS-$TARGETARCH-$TAG.tgz.sha512 cni-plugins-$TARGETOS-$TARGETARCH-$TAG.tgz.sha512
RUN set -eux; \
sha512sum -c cni-plugins-$TARGETOS-$TARGETARCH-$TAG.tgz.sha512; \
mkdir -p /opt/cni/bin; \
tar -xvf cni-plugins-$TARGETOS-$TARGETARCH-$TAG.tgz -C /opt/cni/bin;

# This is the final, minimal container
FROM busybox as final
COPY docker-installer/install_cni_plugins.sh /script/install_cni_plugins.sh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may need LABEL command to specify some of label, as github document mentioned, such as org.opencontainers.image.source

As I worked similar stuff in another repo, this org.opencontainers.image.source is required.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tip. Any recommendation on which LABELs to add?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

managed to add all the org.opencontainers.image.xxx labels, hope i didn't miss anything.

COPY --from=build /opt/cni/bin /opt/cni/bin
ENV FORCE=
WORKDIR /opt/cni/bin
VOLUME /host/opt/cni/bin
CMD ["/script/install_cni_plugins.sh","/opt/cni/bin","/host/opt/cni/bin"]
9 changes: 9 additions & 0 deletions docker-installer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
This scripts are used in the docker image.

The docker image installs the plug-ins to /host/opt/cni/bin which should be bind-mounted to /opt/cni/bin on the host.

The installer-script keeps track of plug-ins it installs and ensures:
- that no existing plug-ins that have been installed or updated by someone else are overwriten
- that plug-ins installed by this script are updated if a new version of this image is used

A unit-test shell script for the installer script is part of the docker build process.
69 changes: 69 additions & 0 deletions docker-installer/install_cni_plugins.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/bin/sh

set -eu

if [ $# -ne 2 ]; then
echo "USAGE: $0 source-dir dest-dir"
echo "Env vars:"
echo " FORCE Overwrite existing binaries"
exit 1
fi

SRC=$1
DST=$2
FORCE=${FORCE:-}

install_cni_plugin() {
NAME=$1
MD5=$( md5sum $SRC/$NAME | awk '{ print $1 }' )
if [ -e $DST/$NAME ]; then
if [ ! -e $DST/$NAME.by_cni_installer_image ]; then
# The file already exists but there's no marker that
# it was installed by this installer -> keep untouched
if [ -z "$FORCE" ]; then
echo "* '$NAME' ignored (exists but not installed by me)"
return
fi
else
OTHER_MD5=$( md5sum $DST/$NAME | awk '{ print $1 }' )
INSTALLED_MD5=$( cat $DST/$NAME.by_cni_installer_image )

if [ "$OTHER_MD5" != "$INSTALLED_MD5" ]; then
# The file was previously installed by this installer
# but later changed -> keep untouched
if [ -z "$FORCE" ]; then
echo "* '$NAME' ignored (previously installed by me but changed by someone else)"
return
fi
fi

if [ "$OTHER_MD5" == "$MD5" ]; then
# The file was previously installed by this installer
# and is up-to-date
echo "* '$NAME' is up-to-date"
return
fi
fi

# The file was previously installed by this installer
# but needs an update
cp -a $SRC/$NAME $DST/.$NAME.new
mv $DST/.$NAME.new $DST/$NAME
echo $MD5 > $DST/$NAME.by_cni_installer_image
echo "* '$NAME' updated"

else
cp -a $SRC/$NAME $DST/.$NAME.new
mv $DST/.$NAME.new $DST/$NAME
echo $MD5 > $DST/$NAME.by_cni_installer_image
echo "* '$NAME' installed"
fi
}

echo "Installing CNI plug-ins to $DST"
echo

for FILE in $( find $SRC -maxdepth 1 -type f ); do
NAME=$( basename $FILE )
install_cni_plugin $NAME
done