feat: add coredns mysql image build and updater

This commit is contained in:
bryanpedini committed 2026-11-09 22:57:47 +01:00
1 parent c3ab4cfb70
commit 6835e480c1
7 files changed
+195 -1

No files matched your search

+1
View File
@@ -0,0 +1 @@
.git
+66
View File
@@ -0,0 +1,66 @@
name: Build and update CoreDNS MySQL
on:
push:
branches: [main]
schedule:
- cron: '0 2 * * *'
workflow_dispatch:
concurrency:
group: coredns-mysql
cancel-in-progress: false
jobs:
image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Check upstream versions
if: github.event_name != 'push'
run: python3 scripts/update.py
- name: Build and test
run: |
set -eu
. ./versions.env
docker build --pull --build-arg COREDNS_VERSION="$COREDNS_VERSION" --build-arg MYSQL_PLUGIN_REV="$MYSQL_PLUGIN_REV" -f dockerfile -t coredns-mysql:ci .
scripts/test-image.sh coredns-mysql:ci "$COREDNS_VERSION"
- name: Commit and push updated versions
if: github.event_name != 'push'
env:
GIT_HOST: ${{ vars.GIT_HOST }}
GIT_NAME: ${{ vars.GIT_NAME }}
GIT_EMAIL: ${{ vars.GIT_EMAIL }}
REGISTRY_USER: ${{ vars.REGISTRY_USER }}
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
run: |
set -eu
if git diff --quiet -- versions.env; then exit 0; fi
test -n "$GIT_HOST" && test -n "$GIT_NAME" && test -n "$GIT_EMAIL" && test -n "$REGISTRY_USER" && test -n "$GIT_TOKEN"
git config user.name "$GIT_NAME"
git config user.email "$GIT_EMAIL"
git add versions.env
git commit -m 'chore: update upstream versions'
askpass=$(mktemp)
trap 'rm -f "$askpass"' EXIT
printf '%s\n' '#!/bin/sh' 'case "$1" in *Username*) printf "%s\\n" "$REGISTRY_USER" ;; *) printf "%s\\n" "$GIT_TOKEN" ;; esac' > "$askpass"
chmod 700 "$askpass"
GIT_ASKPASS="$askpass" GIT_TERMINAL_PROMPT=0 git push "https://${GIT_HOST}/${GITHUB_REPOSITORY}.git" HEAD:refs/heads/main
- name: Publish image
env:
GIT_HOST: ${{ vars.GIT_HOST }}
REGISTRY_USER: ${{ vars.REGISTRY_USER }}
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
run: |
set -eu
test -n "$GIT_HOST" && test -n "$REGISTRY_USER" && test -n "$GIT_TOKEN"
. ./versions.env
image="${GIT_HOST}/docker/coredns-mysql"
printf '%s' "$GIT_TOKEN" | docker login "$GIT_HOST" --username "$REGISTRY_USER" --password-stdin
trap 'docker logout "$GIT_HOST" >/dev/null 2>&1 || true' EXIT
docker tag coredns-mysql:ci "${image}:${COREDNS_VERSION}"
docker tag coredns-mysql:ci "${image}:latest"
docker push "${image}:${COREDNS_VERSION}"
docker push "${image}:latest"
+25 -1
View File
@@ -1 +1,25 @@
# coredns-mysql
# CoreDNS MySQL
A CoreDNS container with the [Cloud 66 MySQL plugin](https://github.com/cloud66-oss/coredns_mysql), built from pinned upstream revisions. The image contains the standard CoreDNS plugins as well as `mysql`.
## Image
The Gitea Actions workflow builds and tests the image before publishing `git.bjphoster.com/docker/coredns-mysql` with `latest` and the CoreDNS version as tags. Run with a Corefile mounted read-only:
```sh
docker run --rm -p 53:53/udp -p 53:53/tcp \
-v "$PWD/Corefile:/Corefile:ro" \
git.bjphoster.com/docker/coredns-mysql:latest -conf /Corefile
```
Configure the plugin and its database according to the [plugin documentation](https://github.com/cloud66-oss/coredns_mysql). Do not store database credentials in the Corefile or repository. The plugin supports DSN environment variables. This image does not initialize a database or change DNS delegation.
## Updates and CI
`.gitea/workflows/image.yaml` runs on pushes to `main`, manually, and daily. The scheduled and manual runs check the latest stable CoreDNS release and the plugin's default-branch commit. Changed revisions are built and tested before an update commit is pushed to Gitea and the image is published. The registry tags change only after a successful build and test. A failed build leaves the repository and published images unchanged. The runner needs Docker access and network access to GitHub and Gitea.
Configure Gitea Actions variables `GIT_HOST`, `GIT_NAME`, `GIT_EMAIL` and `REGISTRY_USER`, and secret `GIT_TOKEN`, with permission to push to this repository and its container registry. The `GIT_TOKEN` is also used for the updater's Git push. Scheduled changes may be reviewed in Git history. Workflow execution and publication begin only after the repository and workflow are pushed and Actions is enabled.
## License
MIT. See [LICENSE](LICENSE). CoreDNS and the MySQL plugin retain their own upstream licenses.
+21
View File
@@ -0,0 +1,21 @@
# SPDX-License-Identifier: MIT
# Copyright (C) 2026 Bryan Joshua Pedini
FROM golang:1.26-alpine AS build
RUN apk add --no-cache git ca-certificates
ARG COREDNS_VERSION
RUN test -n "$COREDNS_VERSION"
RUN git clone --depth 1 --branch "v${COREDNS_VERSION}" https://github.com/coredns/coredns.git /src
WORKDIR /src
RUN sed -i '/^file:file$/i mysql:github.com/cloud66-oss/coredns_mysql' plugin.cfg
ARG MYSQL_PLUGIN_REV
RUN test -n "$MYSQL_PLUGIN_REV"
RUN go get "github.com/cloud66-oss/coredns_mysql@${MYSQL_PLUGIN_REV}"
RUN go generate
RUN CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o /coredns .
FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /coredns /coredns
EXPOSE 53 53/udp
ENTRYPOINT ["/coredns"]
+56
View File
@@ -0,0 +1,56 @@
#!/bin/sh
# SPDX-License-Identifier: MIT
# Copyright (C) 2026 Bryan Joshua Pedini
set -eu
image=$1
version=$2
output=$(docker run --rm "$image" -plugins)
printf '%s\n' "$output" | grep -qx 'mysql'
docker run --rm "$image" -version | grep -F "CoreDNS-${version}"
network="coredns-ci-$$"
db="coredns-db-$$"
dns="coredns-dns-$$"
config=$(mktemp)
cleanup() {
docker rm -f "$dns" "$db" >/dev/null 2>&1 || true
docker network rm "$network" >/dev/null 2>&1 || true
rm -f "$config"
}
trap cleanup EXIT INT TERM
docker network create --internal "$network" >/dev/null
docker run -d --name "$db" --network "$network" --network-alias database -e MARIADB_ROOT_PASSWORD=test -e MARIADB_DATABASE=coredns mariadb:11 >/dev/null
ready=0
attempt=0
while [ "$attempt" -lt 60 ]; do
attempt=$((attempt + 1))
if docker exec "$db" mariadb -uroot -ptest -e 'SELECT 1' >/dev/null 2>&1; then ready=1; break; fi
sleep 1
done
[ "$ready" = 1 ] || { docker logs "$db"; exit 1; }
docker exec -i "$db" mariadb -uroot -ptest coredns <<'SQL'
CREATE TABLE coredns_records (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, zone VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, ttl INT DEFAULT NULL, content TEXT, record_type VARCHAR(255) NOT NULL);
INSERT INTO coredns_records (zone, name, ttl, content, record_type) VALUES ('example.test.', 'www', 30, '{"ip":"192.0.2.10"}', 'A');
SQL
printf '%s\n' '.:1053 {' ' mysql {' ' dsn root:test@tcp(database:3306)/coredns' ' }' ' errors' '}' > "$config"
docker create --name "$dns" --network "$network" --network-alias dns "$image" -conf /Corefile >/dev/null
docker cp "$config" "$dns:/Corefile"
docker start "$dns" >/dev/null
docker run --rm --network "$network" python:3.13-slim python -c '
import socket, struct, time
query = bytes.fromhex("123401000001000000000000") + b"\x03www\x07example\x04test\0" + struct.pack("!HH", 1, 1)
for attempt in range(30):
try:
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.settimeout(1)
sock.sendto(query, ("dns", 1053))
response, _ = sock.recvfrom(4096)
if response[:2] == b"\x12\x34" and response[3] & 15 == 0 and struct.unpack("!H", response[6:8])[0] and b"\xc0\x00\x02\x0a" in response:
print("MySQL-backed DNS query passed")
break
except OSError:
pass
time.sleep(1)
else:
raise SystemExit("MySQL-backed DNS query failed")
'
+24
View File
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: MIT
# Copyright (C) 2026 Bryan Joshua Pedini
import json
import re
import urllib.request
from pathlib import Path
def fetch(url):
request = urllib.request.Request(url, headers={"Accept": "application/vnd.github+json", "User-Agent": "coredns-mysql-updater"})
with urllib.request.urlopen(request, timeout=30) as response:
return json.load(response)
release = fetch("https://api.github.com/repos/coredns/coredns/releases/latest")
plugin = fetch("https://api.github.com/repos/cloud66-oss/coredns_mysql/commits/HEAD")
tag = release["tag_name"]
revision = plugin["sha"]
if not re.fullmatch(r"v[0-9]+\.[0-9]+\.[0-9]+", tag):
raise SystemExit(f"Unexpected CoreDNS release: {tag}")
if not re.fullmatch(r"[0-9a-f]{40}", revision):
raise SystemExit("Unexpected plugin revision")
Path("versions.env").write_text(f"COREDNS_VERSION={tag[1:]}\nMYSQL_PLUGIN_REV={revision}\n")
+2
View File
@@ -0,0 +1,2 @@
COREDNS_VERSION=1.15.0
MYSQL_PLUGIN_REV=de52e2924a6f6152d637e3efd08ac27cb9f4d431