1
0
mirror of https://github.com/coturn/coturn.git synced 2025-10-25 13:00:59 +02:00

Rework Docker CI pipeline to be granular (#1092)

- build and test each platform separately on CI
- rework `Makefile` commands
- renew Docker tags description in README

Additionally:
- show output of failed tests
- ensure Docker images are not pulled in tests
- remove usage of deprecated `::set-output` GitHub Actions feature
This commit is contained in:
Kai Ren 2022-11-16 17:30:42 +01:00 committed by GitHub
parent 95373d3e2a
commit a999df65ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 452 additions and 254 deletions

View File

@ -1,8 +1,11 @@
name: Docker CI name: Docker CI
on: on:
pull_request:
push: push:
branches: ["master"]
tags: ["docker/*"]
pull_request:
branches: ["master"]
schedule: schedule:
- cron: "13 13 * * 3" - cron: "13 13 * * 3"
@ -11,29 +14,28 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
buildx:
############
# Building #
############
build:
strategy: strategy:
fail-fast: false
matrix: matrix:
include: dist: ["alpine", "debian"]
- dockerfile: debian arch:
cache: ${{ github.ref != 'refs/heads/master' - amd64
&& !startsWith(github.ref, 'refs/tags/docker/') }} - arm32v6
publish: ${{ github.event_name == 'push' - arm32v7
&& github.repository_owner == 'coturn' - arm64v8
&& (startsWith(github.ref, 'refs/tags/docker/') - ppc64le
|| github.ref == 'refs/heads/master') }} - s390x
- dockerfile: alpine
cache: ${{ github.ref != 'refs/heads/master'
&& !startsWith(github.ref, 'refs/tags/docker/') }}
publish: ${{ github.event_name == 'push'
&& github.repository_owner == 'coturn'
&& (startsWith(github.ref, 'refs/tags/docker/')
|| github.ref == 'refs/heads/master') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0 # for correct image labeling via `git describe --tags`
- uses: docker/setup-qemu-action@v2 - uses: docker/setup-qemu-action@v2
- uses: docker/setup-buildx-action@v2 - uses: docker/setup-buildx-action@v2
@ -52,100 +54,204 @@ jobs:
run: | run: |
test "${{ fromJSON(steps.git.outputs.result).ref }}" \ test "${{ fromJSON(steps.git.outputs.result).ref }}" \
== "$(grep -m1 'COTURN_VER ?=' Makefile | cut -d'=' -f2 | tr -d ' ')" == "$(grep -m1 'COTURN_VER ?=' Makefile | cut -d'=' -f2 | tr -d ' ')"
working-directory: ./docker/coturn working-directory: docker/coturn/
if: ${{ matrix.publish if: ${{ github.event_name == 'push'
&& github.ref != 'refs/heads/master' }} && startsWith(github.ref, 'refs/tags/docker/') }}
- uses: satackey/action-docker-layer-caching@v0.0.11 - run: make docker.image no-cache=yes
with: dockerfile=${{ matrix.dist }}
key: docker-${{ matrix.dockerfile }}-buildx-{hash} platform=linux/${{ matrix.arch }}
restore-keys: docker-${{ matrix.dockerfile }}-buildx-
continue-on-error: true
timeout-minutes: 10
if: ${{ matrix.cache }}
- name: Pre-build Docker images cache
run: make docker.build.cache DOCKERFILE=${{ matrix.dockerfile }}
no-cache=${{ (matrix.cache && 'no') || 'yes' }}
ref=${{ fromJSON(steps.git.outputs.result).ref }} ref=${{ fromJSON(steps.git.outputs.result).ref }}
working-directory: ./docker/coturn tag=build-${{ github.run_number }}-${{ matrix.dist }}-${{ matrix.arch }}
working-directory: docker/coturn/
- name: Test Docker images - run: make docker.tar to-file=.cache/image.tar
run: | tags=build-${{ github.run_number }}-${{ matrix.dist }}-${{ matrix.arch }}
# Enable experimental features of Docker Daemon to run multi-arch images. working-directory: docker/coturn/
echo "$(cat /etc/docker/daemon.json)" '{"experimental": true}' \ - uses: actions/upload-artifact@v3
| jq --slurp 'reduce .[] as $item ({}; . * $item)' \ with:
| sudo tee /etc/docker/daemon.json name: ${{ matrix.dist }}-${{ matrix.arch }}-${{ github.run_number }}
sudo systemctl restart docker path: docker/coturn/.cache/image.tar
retention-days: 1
make npm.install
make test.docker DOCKERFILE=${{ matrix.dockerfile }} \
platforms=@all build=yes \
ref=${{ fromJSON(steps.git.outputs.result).ref }} ###########
# Testing #
###########
test:
needs: ["build"]
strategy:
fail-fast: false
matrix:
dist: ["alpine", "debian"]
arch:
- amd64
- arm32v6
- arm32v7
- arm64v8
- ppc64le
- s390x
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v2
- run: make npm.install
working-directory: docker/coturn/
- name: Detect correct Git version for image tests
id: git
uses: actions/github-script@v6
with:
script: |
let out = {ref: 'HEAD', ver: ''};
if ('${{ github.ref }}'.startsWith('refs/tags/docker/')) {
out.ref = '${{ github.ref }}'.substring(17).split('-')[0];
out.ver = out.ref;
}
return out;
- uses: actions/download-artifact@v3
with:
name: ${{ matrix.dist }}-${{ matrix.arch }}-${{ github.run_number }}
path: docker/coturn/.cache/
- run: make docker.untar from-file=.cache/image.tar
working-directory: docker/coturn/
- run: make test.docker
platform=linux/${{ matrix.arch }}
tag=build-${{ github.run_number }}-${{ matrix.dist }}-${{ matrix.arch }}
env: env:
COTURN_VERSION: ${{ fromJSON(steps.git.outputs.result).ver }} COTURN_VERSION: ${{ fromJSON(steps.git.outputs.result).ver }}
working-directory: ./docker/coturn working-directory: docker/coturn/
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
if: ${{ matrix.publish }}
- name: Login to Quay.io
uses: docker/login-action@v2
with:
registry: quay.io
username: ${{ secrets.QUAYIO_ROBOT_USERNAME }}
password: ${{ secrets.QUAYIO_ROBOT_TOKEN }}
if: ${{ matrix.publish }}
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_BOT_USER }}
password: ${{ secrets.DOCKERHUB_BOT_PASS }}
if: ${{ matrix.publish }}
- name: Publish version Docker tags
run: make docker.push DOCKERFILE=${{ matrix.dockerfile }}
ref=${{ fromJSON(steps.git.outputs.result).ref }} #############
working-directory: ./docker/coturn # Releasing #
if: ${{ matrix.publish #############
&& github.ref != 'refs/heads/master' }}
- name: Publish edge Docker tags push:
run: make docker.push DOCKERFILE=${{ matrix.dockerfile }} if: ${{ github.event_name == 'push'
tags=edge-${{ matrix.dockerfile }} && github.repository_owner == 'coturn'
ref=${{ fromJSON(steps.git.outputs.result).ref }} && (startsWith(github.ref, 'refs/tags/')
working-directory: ./docker/coturn || github.ref == 'refs/heads/master') }}
if: ${{ matrix.publish needs: ["build", "test"]
&& github.ref == 'refs/heads/master' }} strategy:
fail-fast: false
max-parallel: 1
matrix:
registry: ["docker.io", "ghcr.io", "quay.io"]
dist: ["alpine", "debian"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Parse Docker image name from Git repository name
id: image
uses: actions-ecosystem/action-regex-match@v2
with:
text: ${{ github.repository }}
regex: '^${{ github.repository_owner }}/(.+)$'
- name: Parse semver versions from Git tag
id: semver
uses: actions-ecosystem/action-regex-match@v2
with:
text: ${{ github.ref }}
regex: '^refs/tags/docker/(((([0-9]+)\.[0-9]+)\.[0-9]+)-(.+))$'
if: ${{ startsWith(github.ref, 'refs/tags/') }}
- name: Form main Docker image tag
id: docker
run: echo "tag=${{ (startsWith(github.ref, 'refs/tags/')
&& steps.semver.outputs.group1)
|| 'edge' }}-${{ matrix.dist }}"
>> $GITHUB_OUTPUT
- uses: actions/download-artifact@v3
with:
path: docker/coturn/.cache/
- name: Login to ${{ matrix.registry }} container registry
uses: docker/login-action@v2
with:
registry: ${{ matrix.registry }}
username: ${{ (matrix.registry == 'docker.io'
&& secrets.DOCKERHUB_BOT_USER)
|| (matrix.registry == 'quay.io'
&& secrets.QUAYIO_ROBOT_USER)
|| github.repository_owner }}
password: ${{ (matrix.registry == 'docker.io'
&& secrets.DOCKERHUB_BOT_PASS)
|| (matrix.registry == 'quay.io'
&& secrets.QUAYIO_ROBOT_TOKEN)
|| secrets.GITHUB_TOKEN }}
- name: Tag and push single-platform images
run: |
for arch in amd64 \
arm32v6 \
arm32v7 \
arm64v8 \
ppc64le \
s390x
do
make docker.untar \
from-file=.cache/${{ matrix.dist }}-$arch-${{ github.run_number }}/image.tar
make docker.tags \
of=build-${{ github.run_number }}-${{ matrix.dist }}-$arch \
tags=${{ steps.docker.outputs.tag }}-$arch \
registries=${{ matrix.registry }}
make docker.push \
tags=${{ steps.docker.outputs.tag }}-$arch \
registries=${{ matrix.registry }}
done
working-directory: docker/coturn/
- name: Tag and push multi-platform images
run: make docker.manifest push=yes
registries=${{ matrix.registry }}
of='${{ steps.docker.outputs.tag }}-amd64
${{ steps.docker.outputs.tag }}-arm32v6
${{ steps.docker.outputs.tag }}-arm32v7
${{ steps.docker.outputs.tag }}-arm64v8
${{ steps.docker.outputs.tag }}-ppc64le
${{ steps.docker.outputs.tag }}-s390x'
tags=${{ (startsWith(github.ref, 'refs/tags/')
&& '')
|| steps.docker.outputs.tag }}
env:
DOCKERFILE: ${{ matrix.dist }} # for correct `tags` auto-detection
working-directory: docker/coturn/
# On GitHub Container Registry README is automatically updated on pushes. # On GitHub Container Registry README is automatically updated on pushes.
- name: Update README on Quay.io
uses: christian-korneck/update-container-description-action@v1
env:
DOCKER_APIKEY: ${{ secrets.QUAYIO_API_TOKEN }}
with:
provider: quay
destination_container_repo: quay.io/coturn/coturn
readme_file: docker/coturn/README.md
if: ${{ matrix.publish }}
- name: Update README on Docker Hub - name: Update README on Docker Hub
uses: christian-korneck/update-container-description-action@v1 uses: christian-korneck/update-container-description-action@v1
with:
provider: dockerhub
destination_container_repo: ${{ github.repository_owner }}/${{ steps.image.outputs.group1 }}
readme_file: docker/coturn/README.md
env: env:
DOCKER_USER: ${{ secrets.DOCKERHUB_BOT_USER }} DOCKER_USER: ${{ secrets.DOCKERHUB_BOT_USER }}
DOCKER_PASS: ${{ secrets.DOCKERHUB_BOT_PASS }} DOCKER_PASS: ${{ secrets.DOCKERHUB_BOT_PASS }}
if: ${{ matrix.registry == 'docker.io' }}
- name: Update README on Quay.io
uses: christian-korneck/update-container-description-action@v1
with: with:
provider: dockerhub provider: quay
destination_container_repo: coturn/coturn destination_container_repo: ${{ matrix.registry }}/${{ github.repository_owner }}/${{ steps.image.outputs.group1 }}
readme_file: docker/coturn/README.md readme_file: docker/coturn/README.md
if: ${{ matrix.publish }} env:
DOCKER_APIKEY: ${{ secrets.QUAYIO_API_TOKEN }}
if: ${{ matrix.registry == 'quay.io' }}
release: release-github:
needs: ["buildx"] name: release (GitHub)
if: ${{ github.event_name == 'push' if: ${{ github.event_name == 'push'
&& github.repository_owner == 'coturn' && github.repository_owner == 'coturn'
&& startsWith(github.ref, 'refs/tags/docker/') }} && startsWith(github.ref, 'refs/tags/') }}
needs: ["push"]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -156,9 +262,11 @@ jobs:
with: with:
text: ${{ github.ref }} text: ${{ github.ref }}
regex: '^refs/tags/docker/(((([0-9]+)\.[0-9]+)\.[0-9]+)-(.+))$' regex: '^refs/tags/docker/(((([0-9]+)\.[0-9]+)\.[0-9]+)-(.+))$'
- name: Parse CHANGELOG link - name: Parse CHANGELOG link
id: changelog id: changelog
run: echo ::set-output name=link::${{ github.server_url }}/${{ github.repository }}/blob/docker/${{ steps.semver.outputs.group1 }}/docker/coturn/CHANGELOG.md#$(sed -n '/^## \[${{ steps.semver.outputs.group1 }}\]/{s/^## \[\(.*\)\][^0-9]*\([0-9].*\)/\1--\2/;s/[^0-9a-z-]*//g;p;}' CHANGELOG.md) run: echo "link=${{ github.server_url }}/${{ github.repository }}/blob/docker/${{ steps.semver.outputs.group1 }}/docker/coturn/CHANGELOG.md#$(sed -n '/^## \[${{ steps.semver.outputs.group1 }}\]/{s/^## \[\(.*\)\][^0-9]*\([0-9].*\)/\1--\2/;s/[^0-9a-z-]*//g;p;}' CHANGELOG.md)"
>> $GITHUB_OUTPUT
working-directory: ./docker/coturn working-directory: ./docker/coturn
- name: Create GitHub release - name: Create GitHub release

View File

@ -1,3 +1,4 @@
/.cache/
/node_modules/ /node_modules/
/package-lock.json /package-lock.json
/yarn.lock /yarn.lock

View File

@ -199,7 +199,6 @@ Coturn TURN server Docker image changelog
[Alpine Linux]: https://www.alpinelinux.org [Alpine Linux]: https://www.alpinelinux.org
[Coturn]: https://haraka.github.io [Coturn]: https://haraka.github.io
[Debian Linux]: https://www.debian.org [Debian Linux]: https://www.debian.org

View File

@ -23,7 +23,7 @@ Contribution Guide
At the moment `coturn/coturn` Docker image's [workflow is automated][1] via [GitHub Actions] in the following manner: At the moment `coturn/coturn` Docker image's [workflow is automated][1] via [GitHub Actions] in the following manner:
- On each push the image is built and tested. - On each push the image is built and tested.
This helps to track image regressions due to changes in codebase. This helps to track image regressions due to changes in the codebase.
- Image is built and tested automatically from `master` branch on weekly basis. - Image is built and tested automatically from `master` branch on weekly basis.
This helps to track image regressions due to changes in parent OS images (`debian`, `alpine`), their system packages, and other dependencies. This helps to track image regressions due to changes in parent OS images (`debian`, `alpine`), their system packages, and other dependencies.
@ -54,7 +54,6 @@ To produce a new release (version tag) of `coturn/coturn` Docker image, perform
[CHANGELOG]: https://github.com/coturn/coturn/blob/master/docker/coturn/CHANGELOG.md [CHANGELOG]: https://github.com/coturn/coturn/blob/master/docker/coturn/CHANGELOG.md
[GitHub Actions]: https://docs.github.com/actions [GitHub Actions]: https://docs.github.com/actions
[GitHub Release]: https://github.com/coturn/coturn/releases [GitHub Release]: https://github.com/coturn/coturn/releases

View File

@ -10,6 +10,13 @@ space := $(empty) $(empty)
eq = $(if $(or $(1),$(2)),$(and $(findstring $(1),$(2)),\ eq = $(if $(or $(1),$(2)),$(and $(findstring $(1),$(2)),\
$(findstring $(2),$(1))),1) $(findstring $(2),$(1))),1)
# Maps platform identifier to the one accepted by Docker CLI.
dockerify = $(strip $(if $(call eq,$(1),linux/arm32v6),linux/arm/v6,\
$(if $(call eq,$(1),linux/arm32v7),linux/arm/v7,\
$(if $(call eq,$(1),linux/arm64v8),linux/arm64/v8,\
$(if $(call eq,$(1),linux/i386), linux/386,\
$(platform))))))
@ -23,10 +30,11 @@ COTURN_MAJ_VER = $(strip $(shell echo $(COTURN_VER) | cut -d '.' -f1))
BUILD_REV ?= 0 BUILD_REV ?= 0
NAMESPACES := coturn \
ghcr.io/coturn \
quay.io/coturn
NAME := coturn NAME := coturn
OWNER := $(or $(GITHUB_REPOSITORY_OWNER),coturn)
REGISTRIES := $(strip $(subst $(comma), ,\
$(shell grep -m1 'registry: \["' ../../.github/workflows/docker.yml \
| cut -d':' -f2 | tr -d '"][')))
ALL_IMAGES := \ ALL_IMAGES := \
debian:$(COTURN_VER)-r$(BUILD_REV)-debian,$(COTURN_VER)-debian,$(COTURN_MIN_VER)-debian,$(COTURN_MAJ_VER)-debian,debian,$(COTURN_VER)-r$(BUILD_REV),$(COTURN_VER),$(COTURN_MIN_VER),$(COTURN_MAJ_VER),latest \ debian:$(COTURN_VER)-r$(BUILD_REV)-debian,$(COTURN_VER)-debian,$(COTURN_MIN_VER)-debian,$(COTURN_MAJ_VER)-debian,debian,$(COTURN_VER)-r$(BUILD_REV),$(COTURN_VER),$(COTURN_MIN_VER),$(COTURN_MAJ_VER),latest \
alpine:$(COTURN_VER)-r$(BUILD_REV)-alpine,$(COTURN_VER)-alpine,$(COTURN_MIN_VER)-alpine,$(COTURN_MAJ_VER)-alpine,alpine alpine:$(COTURN_VER)-r$(BUILD_REV)-alpine,$(COTURN_VER)-alpine,$(COTURN_MIN_VER)-alpine,$(COTURN_MAJ_VER)-alpine,alpine
@ -39,14 +47,6 @@ TAGS ?= $(word 1,$(subst |, ,\
VERSION ?= $(word 1,$(subst -, ,$(TAGS)))-$(word 2,$(strip \ VERSION ?= $(word 1,$(subst -, ,$(TAGS)))-$(word 2,$(strip \
$(subst -, ,$(subst $(comma), ,$(TAGS))))) $(subst -, ,$(subst $(comma), ,$(TAGS)))))
PLATFORMS ?= linux/amd64 \
linux/arm64 \
linux/arm/v6 \
linux/arm/v7 \
linux/ppc64le \
linux/s390x
MAIN_PLATFORM ?= $(word 1,$(subst $(comma), ,$(PLATFORMS)))
@ -56,10 +56,14 @@ MAIN_PLATFORM ?= $(word 1,$(subst $(comma), ,$(PLATFORMS)))
image: docker.image image: docker.image
manifest: docker.manifest
push: docker.push push: docker.push
release: git.release release: git.release
tags: docker.tags
test: test.docker test: test.docker
@ -69,29 +73,28 @@ test: test.docker
# Docker commands # # Docker commands #
################### ###################
docker-namespaces = $(strip $(if $(call eq,$(namespaces),),\ docker-registries = $(strip \
$(NAMESPACES),$(subst $(comma), ,$(namespaces)))) $(or $(subst $(comma), ,$(registries)),$(REGISTRIES)))
docker-tags = $(subst $(comma), ,$(or $(tags),$(TAGS))) docker-tags = $(strip $(or $(subst $(comma), ,$(tags)),$(TAGS)))
docker-platforms = $(strip $(if $(call eq,$(platforms),),\
$(PLATFORMS),$(subst $(comma), ,$(platforms))))
# Runs `docker buildx build` command allowing to customize it for the purpose of
# re-tagging or pushing. # Build single-platform Docker image with the given tag.
define docker.buildx #
$(eval dockerfile := $(strip $(1))) # Usage:
$(eval namespace := $(strip $(2))) # make docker.image [dockerfile=(debian|alpine)]
$(eval tag := $(strip $(3))) # [tag=($(VERSION)|<docker-tag>)]] [no-cache=(no|yes)]
$(eval git-ref := $(strip $(4))) # [platform=<os>/<arch>]
$(eval platform := $(strip $(5))) # [ref=<git-ref>]
$(eval no-cache := $(strip $(6)))
$(eval args := $(strip $(7))) github_url := $(strip $(or $(GITHUB_SERVER_URL),https://github.com))
$(eval github_url := $(strip $(or $(GITHUB_SERVER_URL),https://github.com))) github_repo := $(strip $(or $(GITHUB_REPOSITORY),$(OWNER)/$(NAME)))
$(eval github_repo := $(strip $(or $(GITHUB_REPOSITORY),coturn/coturn)))
docker.image:
cd ../../ && \ cd ../../ && \
docker buildx build --force-rm $(args) \ docker buildx build --force-rm \
--platform $(platform) \ $(if $(call eq,$(platform),),,--platform $(call dockerify,$(platform)))\
$(if $(call eq,$(no-cache),yes),--no-cache --pull,) \ $(if $(call eq,$(no-cache),yes),--no-cache --pull,) \
$(if $(call eq,$(git-ref),),,--build-arg coturn_git_ref=$(git-ref)) \ $(if $(call eq,$(ref),),,--build-arg coturn_git_ref=$(ref)) \
--build-arg coturn_github_url=$(github_url) \ --build-arg coturn_github_url=$(github_url) \
--build-arg coturn_github_repo=$(github_repo) \ --build-arg coturn_github_repo=$(github_repo) \
--label org.opencontainers.image.source=$(github_url)/$(github_repo) \ --label org.opencontainers.image.source=$(github_url)/$(github_repo) \
@ -99,71 +102,108 @@ define docker.buildx
$(shell git show --pretty=format:%H --no-patch)) \ $(shell git show --pretty=format:%H --no-patch)) \
--label org.opencontainers.image.version=$(subst docker/,,$(strip \ --label org.opencontainers.image.version=$(subst docker/,,$(strip \
$(shell git describe --tags --dirty --match='docker/*'))) \ $(shell git describe --tags --dirty --match='docker/*'))) \
-f docker/coturn/$(dockerfile)/Dockerfile \ -f docker/coturn/$(or $(dockerfile),$(DOCKERFILE))/Dockerfile \
-t $(namespace)/$(NAME):$(tag) ./ --load -t $(OWNER)/$(NAME):$(or $(tag),$(VERSION)) ./
# Unite multiple single-platform Docker images as a multi-platform Docker image.
#
# WARNING: All the single-platform Docker images should be present on their
# remote registry. This is the limitation imposed by `docker manifest`
# command.
#
# make docker.manifest [amend=(yes|no)] [push=(no|yes)]
# [of=($(VERSION)|<docker-tag-1>[,<docker-tag-2>...])]
# [tags=($(TAGS)|<docker-tag-1>[,<docker-tag-2>...])]
# [registries=($(REGISTRIES)|<prefix-1>[,<prefix-2>...])]
docker.manifest:
$(foreach tag,$(subst $(comma), ,$(docker-tags)),\
$(foreach registry,$(subst $(comma), ,$(docker-registries)),\
$(call docker.manifest.create.do,$(or $(of),$(VERSION)),\
$(registry),$(tag))))
ifeq ($(push),yes)
$(foreach tag,$(subst $(comma), ,$(docker-tags)),\
$(foreach registry,$(subst $(comma), ,$(docker-registries)),\
$(call docker.manifest.push.do,$(registry),$(tag))))
endif
define docker.manifest.create.do
$(eval froms := $(strip $(1)))
$(eval repo := $(strip $(2)))
$(eval tag := $(strip $(3)))
docker manifest create $(if $(call eq,$(amend),no),,--amend) \
$(repo)/$(OWNER)/$(NAME):$(tag) \
$(foreach from,$(subst $(comma), ,$(froms)),\
$(repo)/$(OWNER)/$(NAME):$(from))
endef
define docker.manifest.push.do
$(eval repo := $(strip $(1)))
$(eval tag := $(strip $(2)))
docker manifest push $(repo)/$(OWNER)/$(NAME):$(tag)
endef endef
# Pre-build cache for Docker image builds. # Manually push single-platform Docker images to container registries.
#
# WARNING: This command doesn't apply tag to the built Docker image, just
# creates a build cache. To produce a Docker image with a tag, use
# `docker.tag` command right after running this one.
# #
# Usage: # Usage:
# make docker.build.cache [DOCKERFILE=(debian|alpine)] # make docker.push [tags=($(TAGS)|<docker-tag-1>[,<docker-tag-2>...])]
# [platforms=($(PLATFORMS)|<platform-1>[,<platform-2>...])] # [registries=($(REGISTRIES)|<prefix-1>[,<prefix-2>...])]
# [no-cache=(no|yes)]
# [ref=<git-ref>]
docker.build.cache:
$(call docker.buildx,$(DOCKERFILE),\
coturn,\
build-cache,\
$(ref),\
$(shell echo "$(docker-platforms)" | tr -s '[:blank:]' ','),\
$(no-cache),\
--output 'type=image$(comma)push=false')
# Build Docker image on the given platform with the given tag.
#
# Usage:
# make docker.image [DOCKERFILE=(debian|alpine)]
# [tag=($(VERSION)|<tag>)]
# [platform=($(MAIN_PLATFORM)|<platform>)]
# [no-cache=(no|yes)]
# [ref=<git-ref>]
docker.image:
$(call docker.buildx,$(DOCKERFILE),\
coturn,\
$(or $(tag),$(VERSION)),\
$(ref),\
$(or $(platform),$(MAIN_PLATFORM)),\
$(no-cache),\
--load)
# Push Docker images to their repositories (container registries),
# along with the required multi-arch manifests.
#
# Usage:
# make docker.push [DOCKERFILE=(debian|alpine)]
# [namespaces=($(NAMESPACES)|<prefix-1>[,<prefix-2>...])]
# [tags=($(TAGS)|<tag-1>[,<tag-2>...])]
# [platforms=($(PLATFORMS)|<platform-1>[,<platform-2>...])]
# [ref=<git-ref>]
docker.push: docker.push:
$(foreach namespace,$(docker-namespaces),\ $(foreach tag,$(subst $(comma), ,$(docker-tags)),\
$(foreach tag,$(docker-tags),\ $(foreach registry,$(subst $(comma), ,$(docker-registries)),\
$(call docker.buildx,$(DOCKERFILE),\ $(call docker.push.do,$(registry),$(tag))))
$(namespace),\ define docker.push.do
$(tag),\ $(eval repo := $(strip $(1)))
$(ref),\ $(eval tag := $(strip $(2)))
$(shell echo "$(docker-platforms)" | tr -s '[:blank:]' ','),,\ docker push $(repo)/$(OWNER)/$(NAME):$(tag)
--push))) endef
# Tag single-platform Docker image with the given tags.
#
# Usage:
# make docker.tags [of=($(VERSION)|<docker-tag>)]
# [tags=($(TAGS)|<docker-tag-1>[,<docker-tag-2>...])]
# [registries=($(REGISTRIES)|<prefix-1>[,<prefix-2>...])]
docker.tags:
$(foreach tag,$(subst $(comma), ,$(docker-tags)),\
$(foreach registry,$(subst $(comma), ,$(docker-registries)),\
$(call docker.tags.do,$(or $(of),$(VERSION)),$(registry),$(tag))))
define docker.tags.do
$(eval from := $(strip $(1)))
$(eval repo := $(strip $(2)))
$(eval to := $(strip $(3)))
docker tag $(OWNER)/$(NAME):$(from) $(repo)/$(OWNER)/$(NAME):$(to)
endef
# Save single-platform Docker images to a tarball file.
#
# Usage:
# make docker.tar [to-file=(.cache/image.tar|<file-path>)]
# [tags=($(VERSION)|<docker-tag-1>[,<docker-tag-2>...])]
docker-tar-file = $(or $(to-file),.cache/image.tar)
docker.tar:
@mkdir -p $(dir $(docker-tar-file))
docker save -o $(docker-tar-file) \
$(foreach tag,$(subst $(comma), ,$(or $(tags),$(VERSION))),\
$(OWNER)/$(NAME):$(tag))
docker.test: test.docker
# Load single-platform Docker images from a tarball file.
#
# Usage:
# make docker.untar [from-file=(.cache/image.tar|<file-path>)]
docker.untar:
docker load -i $(or $(from-file),.cache/image.tar)
@ -178,35 +218,21 @@ docker.push:
# https://github.com/bats-core/bats-core # https://github.com/bats-core/bats-core
# #
# Usage: # Usage:
# make test.docker # make test.docker [tag=($(VERSION)|<docker-tag>)]
# [tag=($(VERSION)|<tag>)] # [platform=(linux/amd64|<os>/<arch>)]
# [platforms=($(MAIN_PLATFORM)|@all|<platform-1>[,<platform-2>...])] # [with=ipv6]
# [( [build=no]
# | build=yes [DOCKERFILE=(debian|alpine)]
# [ref=<git-ref>] )]
# [with=ipv6]
test-docker-platforms = $(strip $(if $(call eq,$(platforms),),$(MAIN_PLATFORM),\
$(if $(call eq,$(platforms),@all),$(PLATFORMS),\
$(docker-platforms))))
test.docker: test.docker:
ifeq ($(wildcard node_modules/.bin/bats),) ifeq ($(wildcard node_modules/.bin/bats),)
@make npm.install @make npm.install
endif endif
$(foreach platform,$(test-docker-platforms),\ IMAGE=$(OWNER)/$(NAME):$(or $(tag),$(VERSION)) \
$(call test.docker.do,$(or $(tag),$(VERSION)),$(platform))) PLATFORM=$(or $(call dockerify,$(platform)),linux/amd64) \
define test.docker.do
$(eval tag := $(strip $(1)))
$(eval platform := $(strip $(2)))
$(if $(call eq,$(build),yes),\
@make docker.image DOCKERFILE=$(DOCKERFILE) \
no-cache=no tag=$(tag) platform=$(platform) ref=$(ref) ,)
IMAGE=coturn/$(NAME):$(tag) PLATFORM=$(platform) \
$(if $(call eq,$(with),ipv6),TEST_IPV6=1,) \ $(if $(call eq,$(with),ipv6),TEST_IPV6=1,) \
node_modules/.bin/bats \ node_modules/.bin/bats \
--timing $(if $(call eq,$(CI),),--pretty,--formatter tap) \ --timing $(if $(call eq,$(CI),),--pretty,--formatter tap) \
--print-output-on-failure \
tests/main.bats tests/main.bats
endef
@ -257,8 +283,9 @@ endif
# .PHONY section # # .PHONY section #
################## ##################
.PHONY: image push release test \ .PHONY: image manifest push release test \
docker.build.cache docker.image docker.push \ docker.image docker.manifest docker.push docker.tags docker.tar \
docker.test docker.untar \
git.release \ git.release \
npm.install \ npm.install \
test.docker test.docker

View File

@ -23,12 +23,7 @@ Coturn TURN server Docker image
## Supported platforms ## Supported platforms
- `linux/amd64` - `linux`: `amd64`, `arm32v6`, `arm32v7`, `arm64v8`, `ppc64le`, `s390x`
- `linux/arm64`
- `linux/arm/v6`
- `linux/arm/v7`
- `linux/ppc64le`
- `linux/s390x`
@ -134,38 +129,73 @@ docker run -d --network=host --mount type=tmpfs,destination=/var/lib/coturn cotu
## Image versions ## Image versions
### `X` ### `alpine`
Latest tag of `X` Coturn's major version. This image is based on the popular [Alpine Linux project][1], available in [the alpine official image][2]. [Alpine Linux][1] is much smaller than most distribution base images (~5MB), and thus leads to much slimmer images in general.
This variant is highly recommended when final image size being as small as possible is desired. The main caveat to note is that it does use [musl libc][4] instead of [glibc and friends][5], so certain software might run into issues depending on the depth of their libc requirements. However, most software doesn't have an issue with this, so this variant is usually a very safe choice. See [this Hacker News comment thread][6] for more discussion of the issues that might arise and some pro/con comparisons of using [Alpine][1]-based images.
### `X.Y` ### `<X>`
Latest tag of `X` Coturn's minor version. Latest tag of the latest major `X` Coturn version.
This is a multi-platform image.
### `X.Y.Z` or `X.Y.Z.W` ### `<X.Y>`
Latest tag version of a concrete `X.Y.Z` or `X.Y.Z.W` version of Coturn. Latest tag of the latest minor `X.Y` Coturn version.
This is a multi-platform image.
### `X.Y.Z-rN` or `X.Y.Z.W-rN` ### `<X.Y.Z>`/`<X.Y.Z.W>`
Concrete `N` image revision tag of a Coturn's concrete `X.Y.Z` or `X.Y.Z.W` version. Latest tag of the concrete `X.Y.Z` (or `X.Y.Z.W`) Coturn version.
This is a multi-platform image.
### `<X.Y.Z>-r<N>`/`<X.Y.Z.W>-r<N>`
Concrete `N` image revision tag of the concrete `X.Y.Z` (or `X.Y.Z.W`) Coturn version.
Once built, it's never updated.
This is a multi-platform image.
### `<X.Y.Z>-r<N>-<dist>`/`<X.Y.Z.W>-r<N>-<dist>`
Concrete `N` image revision tag of the concrete `X.Y.Z` (or `X.Y.Z.W`) Coturn version on the concrete `dist` (`alpine` or `debian`).
Once built, it's never updated.
This is a multi-platform image.
### `<X.Y.Z>-r<N>-<dist>-<arch>`/`<X.Y.Z.W>-r<N>-<dist>-<arch>`
Concrete `N` image revision tag of the concrete `X.Y.Z` (or `X.Y.Z.W`) Coturn version on the concrete `dist` (`alpine` or `debian`) and `arch`.
Once build, it's never updated. Once build, it's never updated.
This is a single-platform image.
### `alpine`
This image is based on the popular [Alpine Linux project][1], available in [the alpine official image][2]. Alpine Linux is much smaller than most distribution base images (~5MB), and thus leads to much slimmer images in general.
This variant is highly recommended when final image size being as small as possible is desired. The main caveat to note is that it does use [musl libc][4] instead of [glibc and friends][5], so certain software might run into issues depending on the depth of their libc requirements. However, most software doesn't have an issue with this, so this variant is usually a very safe choice. See [this Hacker News comment thread][6] for more discussion of the issues that might arise and some pro/con comparisons of using Alpine-based images.
### `edge` ### `edge-<dist>`
Contains build of Coturn's latest `master` branch. Latest tag of the latest `master` branch of Coturn on the concrete `dist` (`alpine` or `debian`).
This is a multi-platform image.
### `edge-<dist>-<arch>`
Latest tag of the latest `master` branch of Coturn on the concrete `dist` (`alpine` or `debian`) and `arch`.
This is a single-platform image.

View File

@ -1,5 +1,5 @@
{ {
"devDependencies": { "devDependencies": {
"bats": "^1.1" "bats": "^1.8"
} }
} }

View File

@ -2,17 +2,18 @@
@test "Built on correct arch" { @test "Built on correct arch" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
'uname -m' 'uname -m'
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
if [ "$PLATFORM" = "linux/amd64" ]; then if [ "$PLATFORM" = "linux/amd64" ]; then
[ "$output" = "x86_64" ] [ "$output" = "x86_64" ]
elif [ "$PLATFORM" = "linux/arm64" ]; then
[ "$output" = "aarch64" ]
elif [ "$PLATFORM" = "linux/arm/v6" ]; then elif [ "$PLATFORM" = "linux/arm/v6" ]; then
[ "$output" = "armv7l" ] [ "$output" = "armv7l" ]
elif [ "$PLATFORM" = "linux/arm/v7" ]; then elif [ "$PLATFORM" = "linux/arm/v7" ]; then
[ "$output" = "armv7l" ] [ "$output" = "armv7l" ]
elif [ "$PLATFORM" = "linux/arm64/v8" ]; then
[ "$output" = "aarch64" ]
else else
[ "$output" = "$(echo $PLATFORM | cut -d '/' -f2-)" ] [ "$output" = "$(echo $PLATFORM | cut -d '/' -f2-)" ]
fi fi
@ -20,13 +21,15 @@
@test "Coturn is installed" { @test "Coturn is installed" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
'which turnserver' 'which turnserver'
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
} }
@test "Coturn runs ok" { @test "Coturn runs ok" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
'turnserver -h' 'turnserver -h'
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
} }
@ -34,7 +37,8 @@
@test "Coturn has correct version" { @test "Coturn has correct version" {
[ -z "$COTURN_VERSION" ] && skip [ -z "$COTURN_VERSION" ] && skip
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep -m 1 'Version Coturn' \ "turnserver -o --log-file=stdout | grep -m 1 'Version Coturn' \
| cut -d ' ' -f2 \ | cut -d ' ' -f2 \
| cut -d '-' -f2" | cut -d '-' -f2"
@ -46,36 +50,55 @@
} }
@test "TLS supported" { @test "TLS supported" { # TODO: Remove on next Coturn version release.
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ [ ! "$COTURN_VERSION" = '4.6.0' ] && skip
run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep 'TLS supported'" "turnserver -o --log-file=stdout | grep 'TLS supported'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
} }
@test "DTLS supported" { @test "TLS 1.3 supported" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ [ "$COTURN_VERSION" = '4.6.0' ] && skip
run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep 'TLS 1.3 supported'"
[ "$status" -eq 0 ]
[ ! "$output" = '' ]
}
@test "DTLS supported" { # TODO: Remove on next Coturn version release.
[ ! "$COTURN_VERSION" = '4.6.0' ] && skip
run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep 'DTLS supported'" "turnserver -o --log-file=stdout | grep 'DTLS supported'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
} }
@test "DTLS 1.2 supported" { @test "DTLS 1.2 supported" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep 'DTLS 1.2 supported'" "turnserver -o --log-file=stdout | grep 'DTLS 1.2 supported'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
} }
@test "TURN/STUN ALPN supported" { @test "TURN/STUN ALPN supported" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep 'TURN/STUN ALPN supported'" "turnserver -o --log-file=stdout | grep 'TURN/STUN ALPN supported'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
} }
@test "oAuth supported" { @test "oAuth supported" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep '(oAuth) supported'" "turnserver -o --log-file=stdout | grep '(oAuth) supported'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
@ -83,35 +106,40 @@
@test "SQLite supported" { @test "SQLite supported" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep 'SQLite supported'" "turnserver -o --log-file=stdout | grep 'SQLite supported'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
} }
@test "Redis supported" { @test "Redis supported" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep 'Redis supported'" "turnserver -o --log-file=stdout | grep 'Redis supported'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
} }
@test "PostgreSQL supported" { @test "PostgreSQL supported" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep 'PostgreSQL supported'" "turnserver -o --log-file=stdout | grep 'PostgreSQL supported'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
} }
@test "MySQL supported" { @test "MySQL supported" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep 'MySQL supported'" "turnserver -o --log-file=stdout | grep 'MySQL supported'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
} }
@test "MongoDB supported" { @test "MongoDB supported" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout | grep 'MongoDB supported'" "turnserver -o --log-file=stdout | grep 'MongoDB supported'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
@ -120,7 +148,8 @@
@test "Prometheus supported" { @test "Prometheus supported" {
# Support of Prometheus is not displayed in the output, # Support of Prometheus is not displayed in the output,
# but using --prometheus flag does the job. # but using --prometheus flag does the job.
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
"turnserver -o --log-file=stdout --prometheus | grep 'Version Coturn'" "turnserver -o --log-file=stdout --prometheus | grep 'Version Coturn'"
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[ ! "$output" = '' ] [ ! "$output" = '' ]
@ -128,19 +157,22 @@
@test "detect-external-ip is present" { @test "detect-external-ip is present" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
'which detect-external-ip' 'which detect-external-ip'
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
} }
@test "detect-external-ip runs ok" { @test "detect-external-ip runs ok" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
'detect-external-ip' 'detect-external-ip'
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
} }
@test "detect-external-ip returns valid IPv4" { @test "detect-external-ip returns valid IPv4" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
'detect-external-ip --ipv4' 'detect-external-ip --ipv4'
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
@ -151,7 +183,8 @@
@test "detect-external-ip returns valid IPv6" { @test "detect-external-ip returns valid IPv6" {
[ -z "$TEST_IPV6" ] && skip [ -z "$TEST_IPV6" ] && skip
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
'detect-external-ip --ipv6' 'detect-external-ip --ipv6'
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
@ -160,7 +193,8 @@
} }
@test "detect-external-ip returns IPv4 by default" { @test "detect-external-ip returns IPv4 by default" {
run docker run --rm --platform $PLATFORM --entrypoint sh $IMAGE -c \ run docker run --rm --pull never --platform $PLATFORM \
--entrypoint sh $IMAGE -c \
'detect-external-ip --ipv4' 'detect-external-ip --ipv4'
[ "$status" -eq 0 ] [ "$status" -eq 0 ]