Dockerfile with hardened image and GitHub Action - #73
SebastianGode wants to merge 3 commits into
Conversation
|
recheck |
The CI has moved to GitHub Actions (docker-build.yml and the tag/release workflow). Remove the now-unused zuul.yaml so the project no longer declares Zuul check/gate pipelines.
|
@ecosquad-autoreview review |
There was a problem hiding this comment.
Review of PR #73 — Hardened image & GitHub Actions CI
Summary
The PR replaces the Zuul-based build/publish pipeline with GitHub Actions, and rewrites the Dockerfile to move from a Fedora 38 base to a Debian 13 Python base pulled from the internal DHI Artifactory mirror. It switches packaging deps from dnf names to Debian/trixie names, installs psycopg2 as a Debian package, creates a proper non-root user with a home directory, and drops the dead commented-out clone steps.
Overall the direction is reasonable (non-root user, pinned base, apt cleanup). CI checks (GitGuardian, CodeQL, build) pass. However there are a few correctness/robustness issues I want flagged before merge, one of which is likely to break the image at runtime.
Findings
Warning (should fix before merge)
-
setup.py installwith PEP 668 / externally-managed Python may fail or install into a broken state —Dockerfile:66RUN cd /usr/app/apimon && python3 setup.py installOn Debian 13 the system Python is externally managed. You set
PIP_BREAK_SYSTEM_PACKAGES=1and usepip ... --break-system-packagesfor the requirements, butpython3 setup.py installdoes not go through pip and is not affected byPIP_BREAK_SYSTEM_PACKAGES. More importantly,setup.py installis deprecated and, on modern setuptools, often fails or misbehaves. Worse: you switch toUSER apimonat the end, butsetup.py installruns as root and installs into/usr/lib/python3/...(system site-packages). That works, but it's inconsistent with the pip-based approach.
Fix: install the package the same way you install the requirements, e.g.RUN pip install --no-cache-dir --break-system-packages .(the repo has a proper
setup.py/setup.cfgwith console entry points, so a normal pip install of the project will generate theapimon-scheduler/apimon-executor/apimon-epmonscripts thatscripts/entrypoint.shrelies on.) This also avoids the deprecatedsetup.py installpath and keeps everything consistent with PEP 668 handling. -
Runtime deps installed via apt are duplicated/inconsistent with
requirements.txt—Dockerfileapt block
You installpython3-sqlalchemyandpython3-psycopg2via apt, butrequirements.txtalso pullssqlalchemyandpsycopg2-binarythrough pip (with--break-system-packages). This produces two conflicting installations of SQLAlchemy and two PostgreSQL drivers (psycopg2 from apt vs psycopg2-binary from pip), which can lead to version skew orImportError/ABI mismatches at runtime (e.g. apt psycopg2 compiled against a different libpq than what pip's build picks up).
Fix: pick one source of truth. Since you're already pip-installing the fullrequirements.txt, drop the apt versions ofpython3-sqlalchemyandpython3-psycopg2(keep only what has no pip equivalent, e.g.python3-dev,python3-dnspythonif needed by a system tool, etc.). Keep the apt set limited to native build/runtime deps that pip can't provide (git,gcc,ncat,procps,iproute2,xz-utils,passwd,python3-dev). -
build-argsuses the secretARTIFACTORY_URLvalue as the build arg, but the Dockerfile's default is a host, not a URL — all three workflows +Dockerfile:14
TheDockerfiledeclaresARG ARTIFACTORY_URL=artifactory.devops.telekom.deand uses it as a registry host inFROM ${ARTIFACTORY_URL}/dhi.io/python:.... The workflows passARTIFACTORY_URL=${{ secrets.ARTIFACTORY_URL }}. Ifsecrets.ARTIFACTORY_URLis a full URL (e.g.https://artifactory...orartifactory.../something), thenFROMwill break because a registry reference cannot contain a scheme or a path. Thedocker/login-actionalso usesregistry: ${{ secrets.ARTIFACTORY_URL }}, which expects just the host. Confirm the secret contains only the bare registry host (no scheme, no path). If it might contain a scheme, derive the host explicitly, e.g. store a dedicatedARTIFACTORY_REGISTRYsecret for the host and keep the URL separate, or strip the scheme in the workflow. This is easy to get subtly wrong and will fail the push jobs (the PR builds usepush: falseand thebuildcheck passed, so a malformed host may not have been exercised by thepush: truepath).
Suggestion (nice to have)
-
Consistent naming/structure between the two workflows —
.github/workflows/docker-build.ymlvsdocker-build-push-on-tag-or-release.yaml
docker-build.ymldefines both abuildjob and apush_if_mergedjob, but the trigger is onlypull_request(which does not include aclosed/merged event that carriesgithub.event.pull_request.merged == truein a useful way). Thepush_if_mergedjob will effectively never run, becausepull_requestevents are not delivered withmerged == true(a merged PR fires apushto the base, not apull_requestevent). If you intend "build on PR, push on merge", the push job should live in a workflow triggered onpushto the default branch, not onpull_request. The separate tag/release workflow already handles pushing; you may be able to droppush_if_mergedentirely and rely on the tag/release + default-branch push flow. Please confirm the intended publish trigger so there isn't a dead job. -
type=schedulein themetadata-actiontags — all three workflows
type=scheduleonly produces a tag when the job runs on a schedule; it's harmless here but adds a straylatest/date-ish tag on non-schedule runs in some versions. Not a blocker, just note it.
Checks
CI is green: GitGuardian (no secrets), CodeQL, and the build job all pass. push_if_merged is skipped as expected. Note the push paths (push: true in the tag/release workflow and push_if_merged) were not exercised in this PR, so findings #2 and #3 should be validated by running a real tag push before considering the pipeline fully verified.
Verdict
Request changes — the setup.py install / PEP 668 inconsistency (#1) and the duplicated SQLAlchemy/psycopg2 installs (#2) are likely to break the runtime image, and #3 needs confirmation of the ARTIFACTORY_URL secret format before the push jobs can be trusted.
sergiubz
left a comment
There was a problem hiding this comment.
Deleting zuul.yaml drops the otc-tox-pep8 and otc-tox-py311 jobs while the new workflows only build the image, so lint and unit tests no longer run on PRs; please add a tox job (pep8 + py311) before merging.
| # build arg (ARTIFACTORY_URL) so the build works both locally and in CI where | ||
| # the secret is injected. | ||
| ARG ARTIFACTORY_URL=artifactory.devops.telekom.de | ||
| FROM ${ARTIFACTORY_URL}/dhi.io/python:3.11-debian13-dev |
There was a problem hiding this comment.
The final image is the -dev base with gcc, python3-dev and git left installed, so the runtime ships the full build toolchain; use a multi-stage build and copy the installed app into a minimal runtime stage.
| apt-get clean && \ | ||
| rm -rf /var/lib/apt/lists/* | ||
|
|
||
| RUN git config --global user.email "apimon@test.com" |
There was a problem hiding this comment.
git config --global runs as root so it lands in /root/.gitconfig, which the apimon runtime user (HOME=/home/apimon) will not read; set it after USER apimon or write to /etc/gitconfig.
| "${{ secrets.SWR_URL }}/t-cloud-public/${{ env.PROJECT }}" | ||
| tags: | | ||
| type=schedule | ||
| type=ref,event=branch |
There was a problem hiding this comment.
On a merged pull_request these rules resolve to the head branch or pr-, not main or latest, so the published image gets a feature-branch tag; push on push to main or add type=raw,value=latest,enable={{is_default_branch}}.
No description provided.