Skip to content

Switch ldap-server build to hermetic mode #95

Description

@lubomir

Create a requirement.in file listing the dependencies, use pip compile --generate-hashes to created a version with hashes, and configure the build to prefetch the dependencies.


Agent-Generated Plan

Plan: Switch ldap-server build to hermetic mode

Design

The reviewer's feedback identifies a fundamental problem with the prior plan: using fedora-minimal as the base image requires microdnf to install python3 and python3-pip, which itself would need network access during the hermetic build. Locking RPM dependencies in addition to Python packages would be disproportionate complexity for a test-only image.

Chosen approach: switch the base image from registry.fedoraproject.org/fedora-minimal:44 to an official Python image. A Python base image already contains the interpreter and pip, so microdnf is no longer needed, removing the RPM prefetch requirement entirely. Only the pip dependencies (ldaptor and its transitive dependencies) need to be locked.


Affected Files

File Change
integration-tests/images/ldap-server/requirements.in Create — lists direct pip dependencies
integration-tests/images/ldap-server/requirements.txt Create — pip-compiled lock file with hashes
integration-tests/images/ldap-server/Containerfile Modify — switch base image; install from requirements.txt
.tekton/ldap-server-pull-request.yaml Modify — enable hermetic mode and set prefetch-input
.tekton/ldap-server-push.yaml Modify — enable hermetic mode and set prefetch-input

Implementation Steps

1. Create requirements.in

Create integration-tests/images/ldap-server/requirements.in with the two direct runtime dependencies:

ldaptor
twisted

2. Generate requirements.txt with hashes

Install pip-tools locally and run:

pip-compile --generate-hashes \
    --output-file integration-tests/images/ldap-server/requirements.txt \
    integration-tests/images/ldap-server/requirements.in

The resulting requirements.txt pins all transitive dependencies (e.g., automat, constantly, hyperlink, incremental, zope.interface) with cryptographic hashes. Both files are committed to the repository.

The requirements.txt is a static file that must be regenerated manually whenever requirements.in changes (or periodically to pick up security fixes). No CI automation is required by this issue.

3. Update the Containerfile

Replace the fedora-minimal base image with an official python image and install dependencies from the lock file:

FROM python:3.12-slim

RUN pip install --no-cache-dir --require-hashes -r /tmp/requirements.txt

COPY requirements.txt /tmp/requirements.txt
COPY server.py /app/server.py

EXPOSE 1389

USER 1001

CMD ["python3", "/app/server.py"]

Note on image choice: python:3.12-slim (Debian-slim) contains only pip and the interpreter — no additional system package manager calls are needed. The Cachi2 pip prefetch handles everything. If a UBI-based Python image is required for organizational policy reasons (e.g., registry.access.redhat.com/ubi9/python-312), that image already includes pip as well. The implementer should confirm the preferred base image. The plan uses python:3.12-slim as the default; switching to a UBI variant is a one-line change with no other consequences.

The COPY requirements.txt line must come before the RUN pip install line so the file is available at build time.

4. Enable hermetic mode in the Tekton pipelines

In both .tekton/ldap-server-pull-request.yaml and .tekton/ldap-server-push.yaml, set the pipeline-level params defaults so that hermetic mode is active without requiring a runtime override:

- name: hermetic
  value: "true"
- name: prefetch-input
  value: pip

These parameters are already threaded through to both the prefetch-dependencies task (via $(params.prefetch-input)) and the build-container task (via $(params.hermetic) and $(params.prefetch-input)). The prefetch-dependencies task in the ldap-server pipelines does not have a when guard (unlike the cts pipelines), so it will run unconditionally; setting prefetch-input to pip will instruct Cachi2 to prefetch PyPI packages. No structural change to the task ordering is needed.

The prefetch-input value pip tells Cachi2 to look for requirements.txt at the root of the build context (integration-tests/images/ldap-server/), which is where the file lives. No cachi2.yaml config file is required for this simple case.


Edge Cases and Constraints

  • Transitive dependency churn: ldaptor and twisted pull in several transitive packages. If the lock file is generated on a different platform or Python version than the build image, the hashes may not match. Generate requirements.txt using the same Python minor version as the base image (3.12) to avoid platform-specific wheel hash mismatches. Use --pip-args="--only-binary=:all:" if all dependencies have published wheels, or omit it if source distributions are acceptable.
  • USER 1001 compatibility: The official python:3.12-slim image does not pre-create user 1001. The CMD and EXPOSE statements do not require root, so the USER 1001 directive will still work (Kubernetes/OpenShift will run the process as UID 1001 even without a /etc/passwd entry). This is unchanged from the current Containerfile.
  • server.py location: The current Containerfile copies server.py to /app/server.py. The new Containerfile preserves this path.

Acceptance Criteria

  1. integration-tests/images/ldap-server/requirements.in exists and contains ldaptor and twisted as the only entries.
  2. integration-tests/images/ldap-server/requirements.txt exists and every package entry includes a --hash= annotation.
  3. integration-tests/images/ldap-server/Containerfile does not contain a reference to fedora-minimal or microdnf.
  4. integration-tests/images/ldap-server/Containerfile contains pip install using -r requirements.txt with --require-hashes.
  5. In .tekton/ldap-server-pull-request.yaml, the top-level spec.params block contains hermetic: "true" and prefetch-input: pip.
  6. In .tekton/ldap-server-push.yaml, the top-level spec.params block contains hermetic: "true" and prefetch-input: pip.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions