Skip to content

Commit 0a7200b

Browse files
PS4M. Add workflow to build mongot image on PRs
1 parent 688d3db commit 0a7200b

4 files changed

Lines changed: 358 additions & 0 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: dev-docker-image-cleanup
2+
3+
# When a PR is closed (merged or not), delete the per-PR dev image tag
4+
# (perconalab/percona-search-mongodb:pr-<number>) from Docker Hub so stale
5+
# dev tags don't accumulate. GitHub artifacts are left to expire on their own
6+
# retention window.
7+
8+
on:
9+
pull_request:
10+
types: [closed]
11+
branches:
12+
- main
13+
14+
env:
15+
REPO: perconalab/percona-search-mongodb
16+
17+
jobs:
18+
delete-tag:
19+
runs-on: ubuntu-latest
20+
timeout-minutes: 10
21+
env:
22+
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
23+
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
24+
steps:
25+
- name: Delete pr-<number> tag from Docker Hub
26+
if: env.DOCKERHUB_TOKEN != '' && env.DOCKERHUB_USERNAME != ''
27+
run: |
28+
set -e
29+
TAG="pr-${{ github.event.pull_request.number }}"
30+
echo "Deleting ${REPO}:${TAG}"
31+
32+
JWT=$(curl -sS -H "Content-Type: application/json" \
33+
-X POST \
34+
-d "{\"username\": \"${DOCKERHUB_USERNAME}\", \"password\": \"${DOCKERHUB_TOKEN}\"}" \
35+
https://hub.docker.com/v2/users/login/ | jq -r .token)
36+
37+
if [ -z "${JWT}" ] || [ "${JWT}" = "null" ]; then
38+
echo "Failed to obtain Docker Hub auth token" >&2
39+
exit 1
40+
fi
41+
42+
HTTP_CODE=$(curl -sS -o /dev/null -w "%{http_code}" \
43+
-X DELETE \
44+
-H "Authorization: JWT ${JWT}" \
45+
"https://hub.docker.com/v2/repositories/${REPO}/tags/${TAG}/")
46+
47+
case "${HTTP_CODE}" in
48+
204) echo "Deleted ${REPO}:${TAG}" ;;
49+
404) echo "Tag ${REPO}:${TAG} not found (already gone or never pushed); nothing to do" ;;
50+
*) echo "Unexpected response deleting tag: HTTP ${HTTP_CODE}" >&2; exit 1 ;;
51+
esac
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
name: dev-docker-image
2+
3+
# Builds mongot from source and packages it into a multi-arch (amd64 + arm64)
4+
# Docker image. For every event it:
5+
# * ALWAYS uploads a per-arch image as a GitHub artifact (docker save tarball)
6+
# * ADDITIONALLY, when Docker Hub credentials are available, pushes each arch
7+
# by digest and assembles a multi-arch manifest tag:
8+
# - pull_request -> perconalab/percona-search-mongodb:pr-<number>
9+
# - push to main / dispatch -> perconalab/percona-search-mongodb:dev
10+
# The pr-<number> tag is removed when the PR closes (see dev-docker-image-cleanup.yml).
11+
12+
on:
13+
workflow_dispatch:
14+
15+
pull_request:
16+
types: [opened, reopened, synchronize, ready_for_review]
17+
branches:
18+
- main
19+
20+
push:
21+
branches:
22+
- main
23+
24+
concurrency:
25+
group: ${{ github.workflow }}-${{ github.ref }}
26+
cancel-in-progress: true
27+
28+
env:
29+
IMAGE: perconalab/percona-search-mongodb
30+
31+
jobs:
32+
# Resolve the target tag once so every arch and the manifest agree.
33+
tag:
34+
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
35+
runs-on: ubuntu-latest
36+
outputs:
37+
tag: ${{ steps.meta.outputs.tag }}
38+
steps:
39+
- name: Compute image tag
40+
id: meta
41+
run: |
42+
if [ "${{ github.event_name }}" = "pull_request" ]; then
43+
TAG="pr-${{ github.event.pull_request.number }}"
44+
else
45+
# push to main (post-merge) or manual dispatch both refresh :dev
46+
TAG="dev"
47+
fi
48+
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
49+
echo "Image will be tagged: ${IMAGE}:${TAG}"
50+
51+
build:
52+
needs: tag
53+
strategy:
54+
fail-fast: false
55+
matrix:
56+
include:
57+
- arch: amd64
58+
runner: ubuntu-latest
59+
bazel_platform: linux_x86_64
60+
docker_platform: linux/amd64
61+
- arch: arm64
62+
runner: ubuntu-24.04-arm
63+
bazel_platform: linux_aarch64
64+
docker_platform: linux/arm64
65+
runs-on: ${{ matrix.runner }}
66+
timeout-minutes: 180
67+
permissions:
68+
contents: read
69+
env:
70+
TAG: ${{ needs.tag.outputs.tag }}
71+
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
72+
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
73+
steps:
74+
- name: Cleanup unnecessary software
75+
run: |
76+
df -h
77+
sudo rm -rf /usr/share/dotnet || true
78+
sudo rm -rf /usr/local/lib/android || true
79+
sudo rm -rf /opt/ghc || true
80+
sudo rm -rf /opt/hostedtoolcache/CodeQL || true
81+
df -h
82+
83+
- name: Checkout
84+
uses: actions/checkout@v4
85+
with:
86+
fetch-depth: 0
87+
88+
# Native per-arch bundle: amd64 builds on x86, arm64 builds on an arm
89+
# runner, so the bundled JDK and native libs are the correct architecture.
90+
- name: Build mongot community bundle
91+
run: |
92+
./scripts/tools/bazelisk/run.sh build \
93+
--platforms=//bazel/platforms:${{ matrix.bazel_platform }} \
94+
--//bazel/config:version=${TAG} \
95+
--embed_label=${TAG} \
96+
//deploy:mongot-community
97+
98+
- name: Assemble Docker build context
99+
run: |
100+
mkdir -p docker-context
101+
cp -L bazel-bin/deploy/mongot-community.tgz docker-context/mongot-community.tgz
102+
cp percona-packaging/docker/Dockerfile docker-context/Dockerfile
103+
cp percona-packaging/docker/mongot-entry.sh docker-context/mongot-entry.sh
104+
cp percona-packaging/conf/mongot.yml docker-context/mongot.yml
105+
ls -l docker-context
106+
107+
- name: Set up Docker Buildx
108+
uses: docker/setup-buildx-action@v3
109+
110+
# Single-arch, docker-loadable tarball. Always produced so PRs (including
111+
# forks without credentials) still get a usable image per architecture.
112+
- name: Build image tarball
113+
run: |
114+
docker buildx build docker-context \
115+
--platform ${{ matrix.docker_platform }} \
116+
--tag "${IMAGE}:${TAG}-${{ matrix.arch }}" \
117+
--output type=docker,dest=image.tar
118+
gzip -c image.tar > "percona-search-mongodb-${TAG}-${{ matrix.arch }}.tar.gz"
119+
ls -lh "percona-search-mongodb-${TAG}-${{ matrix.arch }}.tar.gz"
120+
121+
- name: Upload image artifact
122+
uses: actions/upload-artifact@v4
123+
with:
124+
name: percona-search-mongodb-${{ needs.tag.outputs.tag }}-${{ matrix.arch }}
125+
path: percona-search-mongodb-${{ needs.tag.outputs.tag }}-${{ matrix.arch }}.tar.gz
126+
if-no-files-found: error
127+
retention-days: 7
128+
129+
# The steps below run only when Docker Hub credentials are present.
130+
# Fork PRs never receive secrets, so these steps are skipped automatically.
131+
- name: Log in to Docker Hub
132+
if: env.DOCKERHUB_TOKEN != '' && env.DOCKERHUB_USERNAME != ''
133+
uses: docker/login-action@v3
134+
with:
135+
username: ${{ secrets.DOCKERHUB_USERNAME }}
136+
password: ${{ secrets.DOCKERHUB_TOKEN }}
137+
138+
# Push the arch image by digest (no per-arch tag); the manifest job stitches
139+
# the digests together into the final tag.
140+
- name: Push image by digest
141+
if: env.DOCKERHUB_TOKEN != '' && env.DOCKERHUB_USERNAME != ''
142+
run: |
143+
docker buildx build docker-context \
144+
--platform ${{ matrix.docker_platform }} \
145+
--output "type=image,name=${IMAGE},push-by-digest=true,name-canonical=true,push=true" \
146+
--metadata-file metadata.json
147+
digest="$(jq -r '."containerimage.digest"' metadata.json)"
148+
echo "Pushed ${IMAGE}@${digest} (${{ matrix.arch }})"
149+
mkdir -p digests
150+
echo "${digest}" > "digests/${{ matrix.arch }}"
151+
152+
- name: Upload digest
153+
if: env.DOCKERHUB_TOKEN != '' && env.DOCKERHUB_USERNAME != ''
154+
uses: actions/upload-artifact@v4
155+
with:
156+
name: digest-${{ matrix.arch }}
157+
path: digests/${{ matrix.arch }}
158+
if-no-files-found: error
159+
retention-days: 1
160+
161+
# Assemble the multi-arch manifest from the per-arch digests. Only runs when
162+
# credentials are configured; a no-op (still green) otherwise, so fork PRs pass.
163+
manifest:
164+
needs: [tag, build]
165+
runs-on: ubuntu-latest
166+
env:
167+
TAG: ${{ needs.tag.outputs.tag }}
168+
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
169+
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
170+
steps:
171+
- name: Download digests
172+
if: env.DOCKERHUB_TOKEN != '' && env.DOCKERHUB_USERNAME != ''
173+
uses: actions/download-artifact@v4
174+
with:
175+
pattern: digest-*
176+
path: digests
177+
merge-multiple: true
178+
179+
- name: Log in to Docker Hub
180+
if: env.DOCKERHUB_TOKEN != '' && env.DOCKERHUB_USERNAME != ''
181+
uses: docker/login-action@v3
182+
with:
183+
username: ${{ secrets.DOCKERHUB_USERNAME }}
184+
password: ${{ secrets.DOCKERHUB_TOKEN }}
185+
186+
- name: Create and push multi-arch manifest
187+
if: env.DOCKERHUB_TOKEN != '' && env.DOCKERHUB_USERNAME != ''
188+
run: |
189+
refs=""
190+
for f in digests/*; do
191+
refs="${refs} ${IMAGE}@$(cat "${f}")"
192+
done
193+
echo "Combining:${refs} -> ${IMAGE}:${TAG}"
194+
docker buildx imagetools create -t "${IMAGE}:${TAG}" ${refs}
195+
docker buildx imagetools inspect "${IMAGE}:${TAG}"
196+
197+
- name: Summary
198+
if: always()
199+
run: |
200+
{
201+
echo "### mongot dev image (multi-arch: amd64 + arm64)"
202+
echo ""
203+
echo "- Tag: \`${IMAGE}:${TAG}\`"
204+
echo "- Artifacts: \`percona-search-mongodb-${TAG}-amd64\`, \`percona-search-mongodb-${TAG}-arm64\` (load with \`docker load < *.tar.gz\`)"
205+
if [ -n "${DOCKERHUB_TOKEN}" ] && [ -n "${DOCKERHUB_USERNAME}" ]; then
206+
echo "- Pushed to Docker Hub: yes (\`docker pull ${IMAGE}:${TAG}\`)"
207+
else
208+
echo "- Pushed to Docker Hub: no (credentials unavailable, e.g. fork PR)"
209+
fi
210+
} >> "$GITHUB_STEP_SUMMARY"
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
FROM redhat/ubi9-minimal AS bundle
2+
3+
ARG BUNDLE=mongot-community.tgz
4+
ARG INSTALL_DIR=/usr/lib/percona-search-mongodb
5+
6+
RUN set -ex; \
7+
microdnf -y install tar gzip; \
8+
microdnf clean all
9+
10+
COPY ${BUNDLE} /tmp/mongot-community.tgz
11+
RUN set -ex; \
12+
mkdir -p "${INSTALL_DIR}"; \
13+
tar -xzf /tmp/mongot-community.tgz \
14+
-C "${INSTALL_DIR}" --strip-components=1; \
15+
chmod +x "${INSTALL_DIR}/mongot"; \
16+
chmod -R g+rX "${INSTALL_DIR}"
17+
18+
FROM redhat/ubi9-minimal
19+
20+
# Dev image: mongot (Percona Search for MongoDB) built from source in this repo
21+
LABEL name="Percona Search for MongoDB (dev)" \
22+
vendor="Percona" \
23+
summary="mongot built from percona-mongot source" \
24+
description="Development build of Percona Search for MongoDB (mongot). Built directly \
25+
from source instead of the published package. Not for production use." \
26+
org.opencontainers.image.title="Percona Search for MongoDB (dev)" \
27+
org.opencontainers.image.vendor="Percona" \
28+
org.opencontainers.image.source="https://github.com/percona/percona-mongot"
29+
30+
ARG INSTALL_DIR=/usr/lib/percona-search-mongodb
31+
32+
RUN set -ex; \
33+
microdnf -y update; \
34+
microdnf -y install \
35+
jq \
36+
oniguruma \
37+
shadow-utils \
38+
vim-minimal; \
39+
microdnf clean all; \
40+
rm -rf /var/cache/dnf /var/cache/yum
41+
42+
COPY --from=bundle --chown=1001:0 ${INSTALL_DIR} ${INSTALL_DIR}
43+
44+
RUN set -ex; \
45+
printf '#!/bin/sh\nexec %s/mongot "$@"\n' "${INSTALL_DIR}" > /usr/bin/mongot; \
46+
chmod +x /usr/bin/mongot
47+
48+
COPY mongot.yml /etc/mongot/mongot.yml
49+
50+
RUN set -ex; \
51+
useradd -u 1001 -r -g 0 -m -s /sbin/nologin \
52+
-c "Default Application User" mongodb; \
53+
mkdir -p /var/lib/mongot /var/log/mongot /etc/mongot/secrets; \
54+
chown -R 1001:0 /var/lib/mongot /var/log/mongot /etc/mongot; \
55+
chmod -R g+rwX /var/lib/mongot /var/log/mongot; \
56+
chmod -R g+rX /etc/mongot
57+
58+
COPY mongot-entry.sh /entrypoint.sh
59+
RUN chmod +x /entrypoint.sh
60+
61+
USER 1001
62+
63+
VOLUME ["/var/lib/mongot"]
64+
65+
# 27028 grpc query server, 9946 metrics, 8080 health endpoint
66+
EXPOSE 27028 9946 8080
67+
68+
ENTRYPOINT ["/entrypoint.sh"]
69+
CMD ["mongot", "--config", "/etc/mongot/mongot.yml"]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/sh
2+
#
3+
# Entry point for the Percona Search for MongoDB (mongot) dev image.
4+
#
5+
# This lets operators pass mongot options as arguments to the container, e.g.:
6+
#
7+
# docker run ... percona-search-mongodb --config /path/to/mongot.yml
8+
#
9+
# while a plain `docker run` still starts mongot with the bundled default
10+
# config supplied via CMD. Any other command (e.g. `sh`) is execed as-is
11+
# so the image stays debuggable.
12+
#
13+
# POSIX sh only (no bashisms), so it runs on minimal base images.
14+
set -e
15+
16+
# First argument is a flag -> the operator is passing mongot options; prepend
17+
# the binary so they don't have to restate it.
18+
case "${1:-}" in
19+
-*) set -- mongot "$@" ;;
20+
esac
21+
22+
# Normalize the bare `mongot` command to its absolute path.
23+
if [ "$1" = 'mongot' ]; then
24+
shift
25+
set -- /usr/bin/mongot "$@"
26+
fi
27+
28+
exec "$@"

0 commit comments

Comments
 (0)