Skip to content

Commit a8350cf

Browse files
chaenchrisburr
authored andcommitted
chore: replace container base images with pixi-managed environments
1 parent bfadb81 commit a8350cf

20 files changed

Lines changed: 22629 additions & 162 deletions

File tree

.dockerignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Pixi environments (can be very large)
2+
.pixi/
3+
4+
# Documentation build output
5+
site/
6+
7+
# Python build artifacts
8+
*.egg-info/
9+
__pycache__/

.github/workflows/deployment.yml

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ jobs:
9797

9898
docker:
9999
needs: deploy-pypi
100-
timeout-minutes: 30
100+
timeout-minutes: 30
101101
runs-on: ubuntu-latest
102102
steps:
103103
- name: Checkout
@@ -112,58 +112,46 @@ jobs:
112112
registry: ghcr.io
113113
username: ${{ github.actor }}
114114
password: ${{ secrets.GITHUB_TOKEN }}
115-
- name: Download diracx wheels
116-
uses: actions/download-artifact@v8
117-
with:
118-
name: diracx-whl
119-
- name: "Find wheels"
120-
id: find_wheel
121-
run: |
122-
# We need to copy them there to be able to access them in the RUN --mount
123-
cp diracx*.whl containers/client/
124-
cp diracx*.whl containers/services/
125-
for wheel_fn in *.whl; do
126-
pkg_name=$(basename "${wheel_fn}" | cut -d '-' -f 1)
127-
echo "${pkg_name}-wheel-name=$(ls "${pkg_name}"-*.whl)" >> $GITHUB_OUTPUT
128-
done
129115

130-
- name: Build and push client (release)
116+
- name: Build and push services (release)
131117
uses: docker/build-push-action@v7
132118
if: ${{ needs.deploy-pypi.outputs.create-release == 'true' }}
133119
with:
134-
context: containers/client/
135-
push: ${{ needs.deploy-pypi.outputs.create-release == 'true' }}
136-
tags: "ghcr.io/diracgrid/diracx/client:${{ needs.deploy-pypi.outputs.new-version }}"
120+
context: .
121+
file: containers/Dockerfile
122+
build-args: PIXI_ENV=container-services
123+
push: true
124+
tags: "ghcr.io/diracgrid/diracx/services:${{ needs.deploy-pypi.outputs.new-version }}"
137125
platforms: linux/amd64,linux/arm64
138-
build-args: EXTRA_PACKAGES_TO_INSTALL=DIRACCommon~=9.0.0
139-
- name: Build and push services (release)
126+
- name: Build and push client (release)
140127
uses: docker/build-push-action@v7
141128
if: ${{ needs.deploy-pypi.outputs.create-release == 'true' }}
142129
with:
143-
context: containers/services/
144-
push: ${{ needs.deploy-pypi.outputs.create-release == 'true' }}
145-
tags: "ghcr.io/diracgrid/diracx/services:${{ needs.deploy-pypi.outputs.new-version }}"
130+
context: .
131+
file: containers/Dockerfile
132+
build-args: PIXI_ENV=container-client
133+
push: true
134+
tags: "ghcr.io/diracgrid/diracx/client:${{ needs.deploy-pypi.outputs.new-version }}"
146135
platforms: linux/amd64,linux/arm64
147-
build-args: EXTRA_PACKAGES_TO_INSTALL=DIRACCommon~=9.0.0
148136

149-
- name: Build and push client (dev)
137+
- name: Build and push services (dev)
150138
uses: docker/build-push-action@v7
151139
with:
152-
context: containers/client/
140+
context: .
141+
file: containers/Dockerfile
142+
build-args: PIXI_ENV=container-services
153143
push: ${{ github.event_name != 'pull_request' && github.repository == 'DIRACGrid/diracx' && github.ref_name == 'main' }}
154-
tags: ghcr.io/diracgrid/diracx/client:dev
144+
tags: ghcr.io/diracgrid/diracx/services:dev
155145
platforms: linux/amd64,linux/arm64
156-
build-args: |
157-
EXTRA_PACKAGES_TO_INSTALL=git+https://github.com/DIRACGrid/DIRAC.git@integration#egg=diraccommon\&subdirectory=dirac-common
158-
- name: Build and push services (dev)
146+
- name: Build and push client (dev)
159147
uses: docker/build-push-action@v7
160148
with:
161-
context: containers/services/
149+
context: .
150+
file: containers/Dockerfile
151+
build-args: PIXI_ENV=container-client
162152
push: ${{ github.event_name != 'pull_request' && github.repository == 'DIRACGrid/diracx' && github.ref_name == 'main' }}
163-
tags: ghcr.io/diracgrid/diracx/services:dev
153+
tags: ghcr.io/diracgrid/diracx/client:dev
164154
platforms: linux/amd64,linux/arm64
165-
build-args: |
166-
EXTRA_PACKAGES_TO_INSTALL=git+https://github.com/DIRACGrid/DIRAC.git@integration#egg=diraccommon\&subdirectory=dirac-common
167155

168156
update-charts:
169157
name: Update Helm charts

.github/workflows/main.yml

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -121,44 +121,34 @@ jobs:
121121
with:
122122
cache: false
123123
environments: ${{ matrix.extension == 'diracx' && 'default' || 'default-gubbins' }}
124-
- name: Build gubbins wheels
125-
if: ${{ matrix.extension == 'gubbins' }}
126-
run: |
127-
for pkg_dir in $PWD/diracx-*; do
128-
echo "Building $pkg_dir"
129-
pixi exec python-build --outdir $PWD/extensions/containers/services/ $pkg_dir
130-
done
131-
# Also build the diracx metapackage
132-
pixi exec python-build --outdir $PWD/extensions/containers/services/ .
133-
# And build the gubbins package
134-
for pkg_dir in $PWD/extensions/gubbins/gubbins-*; do
135-
# Skip the testing package
136-
if [[ "${pkg_dir}" =~ .*testing.* ]];
137-
then
138-
echo "Do not build ${pkg_dir}";
139-
continue;
140-
fi
141-
echo "Building $pkg_dir"
142-
pixi exec python-build --outdir $PWD/extensions/containers/services/ $pkg_dir
143-
done
144124
- name: Set up Docker Buildx
145-
if: ${{ matrix.extension == 'gubbins' }}
146125
uses: docker/setup-buildx-action@v4
147-
- name: Build container for gubbins
148-
if: ${{ matrix.extension == 'gubbins' }}
126+
- name: Build services image
127+
uses: docker/build-push-action@v7
128+
with:
129+
context: .
130+
file: containers/Dockerfile
131+
build-args: |
132+
PIXI_ENV=${{ matrix.extension == 'diracx' && 'container-services' || 'gubbins-container-services' }}
133+
DIRACX_EXTENSIONS=${{ matrix.extension == 'diracx' && 'diracx' || 'gubbins,diracx' }}
134+
tags: ghcr.io/${{ matrix.extension == 'diracx' && 'diracgrid/diracx' || 'gubbins' }}/services:dev
135+
outputs: type=docker,dest=/tmp/services_image.tar
136+
- name: Build client image
149137
uses: docker/build-push-action@v7
150138
with:
151-
context: extensions/containers/services
152-
tags: gubbins/services:dev
153-
outputs: type=docker,dest=/tmp/gubbins_services_image.tar
139+
context: .
140+
file: containers/Dockerfile
154141
build-args: |
155-
EXTENSION_CUSTOM_SOURCES_TO_INSTALL=/bindmount/gubbins_db*.whl,/bindmount/gubbins_logic*.whl,/bindmount/gubbins_routers*.whl,/bindmount/gubbins_client*.whl
156-
- name: Load image
157-
if: ${{ matrix.extension == 'gubbins' }}
142+
PIXI_ENV=${{ matrix.extension == 'diracx' && 'container-client' || 'gubbins-container-client' }}
143+
DIRACX_EXTENSIONS=${{ matrix.extension == 'diracx' && 'diracx' || 'gubbins,diracx' }}
144+
tags: ghcr.io/${{ matrix.extension == 'diracx' && 'diracgrid/diracx' || 'gubbins' }}/client:dev
145+
outputs: type=docker,dest=/tmp/client_image.tar
146+
- name: Load images
158147
run: |
159-
docker load --input /tmp/gubbins_services_image.tar
160-
# Free up disk space by removing the tarball after loading the image
161-
rm -Rf /tmp/gubbins_services_image.tar
148+
docker load --input /tmp/services_image.tar
149+
rm -f /tmp/services_image.tar
150+
docker load --input /tmp/client_image.tar
151+
rm -f /tmp/client_image.tar
162152
docker builder prune -af || true
163153
docker image ls -a
164154
- name: Start demo
@@ -191,16 +181,26 @@ jobs:
191181
# Copy gubbins-charts to a temporary location and build dependencies
192182
cp -r ./extensions/gubbins-charts /tmp/
193183
../diracx-charts/.demo/helm dependency build /tmp/gubbins-charts
184+
# Replace the downloaded subchart with the locally-cloned one to
185+
# ensure we have the pixi-compatible ConfigMap entrypoint
186+
rm -f /tmp/gubbins-charts/charts/diracx-*.tgz
187+
../diracx-charts/.demo/helm package ../diracx-charts/diracx -d /tmp/gubbins-charts/charts/
194188
195189
demo_args+=("--extension-chart-path" "/tmp/gubbins-charts")
196190
demo_args+=("--ci-values" "./extensions/gubbins_values.yaml")
197-
demo_args+=("--load-docker-image" "gubbins/services:dev")
191+
demo_args+=("--load-docker-image" "ghcr.io/gubbins/services:dev")
192+
demo_args+=("--load-docker-image" "ghcr.io/gubbins/client:dev")
193+
demo_args+=("--prune-loaded-images")
198194
demo_source_dirs+=("/tmp/gubbins/")
199195
elif [ ${{ matrix.extension }} != 'diracx' ]; then
200196
echo "Unknown extension: ${{ matrix.extension }}"
201197
exit 1
202198
else
203199
demo_args+=("--set-value" "developer.autoReload=false")
200+
demo_args+=("--set-value" "global.imagePullPolicy=IfNotPresent")
201+
demo_args+=("--load-docker-image" "ghcr.io/diracgrid/diracx/services:dev")
202+
demo_args+=("--load-docker-image" "ghcr.io/diracgrid/diracx/client:dev")
203+
demo_args+=("--prune-loaded-images")
204204
fi
205205
206206
# Run the demo with the provided arguments

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ docs/source/_build
9797

9898
# pixi environments
9999
.pixi
100-
pixi.lock
101100
*.egg-info
102101
docs/templates/_builtin_markdown.jinja
103102

@@ -106,3 +105,7 @@ docs/templates/_builtin_markdown.jinja
106105

107106
# docs site
108107
site
108+
109+
# DiracX specific
110+
# No point in committing the pixi.lock for gubbins as it cannot work properly within the diracx repo
111+
extensions/gubbins/pixi.lock

.pre-commit-config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ repos:
2828
- id: check-yaml
2929
args: ["--unsafe"]
3030
- id: check-added-large-files
31+
exclude: pixi\.lock$
3132

3233
- repo: https://github.com/astral-sh/ruff-pre-commit
3334
rev: "v0.15.7"
@@ -66,6 +67,7 @@ repos:
6667
hooks:
6768
- id: codespell
6869
args: ["-w"]
70+
exclude: pixi\.lock$
6971

7072
- repo: https://github.com/adamchainz/blacken-docs
7173
rev: 1.20.0

containers/Dockerfile

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
FROM ghcr.io/prefix-dev/pixi:latest AS build
2+
3+
ARG PIXI_ENV=container-services
4+
ARG DIRACX_EXTENSIONS=diracx
5+
6+
WORKDIR /app
7+
COPY pixi.toml pixi.lock ./
8+
9+
# Copy source directories needed for path-based installs
10+
COPY .git/ .git/
11+
COPY diracx-api/ diracx-api/
12+
COPY diracx-cli/ diracx-cli/
13+
COPY diracx-client/ diracx-client/
14+
COPY diracx-core/ diracx-core/
15+
COPY diracx-db/ diracx-db/
16+
COPY diracx-logic/ diracx-logic/
17+
COPY diracx-routers/ diracx-routers/
18+
COPY diracx-testing/ diracx-testing/
19+
COPY pyproject.toml ./
20+
COPY README.md ./
21+
COPY extensions/gubbins/gubbins-api/ extensions/gubbins/gubbins-api/
22+
COPY extensions/gubbins/gubbins-cli/ extensions/gubbins/gubbins-cli/
23+
COPY extensions/gubbins/gubbins-client/ extensions/gubbins/gubbins-client/
24+
COPY extensions/gubbins/gubbins-core/ extensions/gubbins/gubbins-core/
25+
COPY extensions/gubbins/gubbins-db/ extensions/gubbins/gubbins-db/
26+
COPY extensions/gubbins/gubbins-logic/ extensions/gubbins/gubbins-logic/
27+
COPY extensions/gubbins/gubbins-routers/ extensions/gubbins/gubbins-routers/
28+
COPY extensions/gubbins/gubbins-testing/ extensions/gubbins/gubbins-testing/
29+
COPY extensions/gubbins/pyproject.toml extensions/gubbins/
30+
COPY extensions/gubbins/README.md extensions/gubbins/
31+
32+
# Switch to non-editable installs (same workaround as CI)
33+
RUN sed -i 's@editable = true@editable = false@g' pixi.toml
34+
35+
RUN pixi install --frozen -e ${PIXI_ENV}
36+
37+
# Generate activation script
38+
RUN pixi shell-hook -e ${PIXI_ENV} > /activate.sh
39+
40+
FROM ubuntu:24.04
41+
42+
ARG PIXI_ENV=container-services
43+
ARG DIRACX_EXTENSIONS=diracx
44+
ENV PIXI_ENV=${PIXI_ENV}
45+
ENV DIRACX_EXTENSIONS=${DIRACX_EXTENSIONS}
46+
47+
EXPOSE 8000
48+
49+
# Copy the installed environment
50+
COPY --from=build /app/.pixi/envs/${PIXI_ENV} /app/.pixi/envs/${PIXI_ENV}
51+
COPY --from=build /activate.sh /activate.sh
52+
COPY containers/entrypoint.sh /entrypoint.sh
53+
54+
# In many clusters the container is run as a random uid for security reasons.
55+
# If we mark the environment directory as group 0 and give it group write
56+
# permissions then we're still able to manage the environment from inside the
57+
# container.
58+
RUN chmod -R g=u /app/.pixi
59+
60+
# Compatibility shim: the Helm chart invokes this micromamba entrypoint path
61+
RUN printf '#!/bin/bash\nsource /activate.sh\nexec "$@"\n' > /usr/local/bin/_entrypoint.sh && \
62+
chmod +x /usr/local/bin/_entrypoint.sh
63+
64+
# Use tini as init (installed by pixi in the env)
65+
ENTRYPOINT ["/bin/bash", "-c", \
66+
"exec /app/.pixi/envs/${PIXI_ENV}/bin/tini -- /entrypoint.sh \"$@\"", "--"]

containers/client/Dockerfile

Lines changed: 0 additions & 12 deletions
This file was deleted.

containers/entrypoint.sh

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/bin/bash
2+
set -e
3+
4+
source /activate.sh
5+
6+
function install_sources() {
7+
extension_name=$1
8+
source_prefix=$2
9+
image_packages=$3
10+
11+
IFS=','
12+
to_install=()
13+
for dir in ${!source_prefix}; do
14+
for package_name in ${!image_packages}; do
15+
if [[ "${package_name}" == "." ]]; then
16+
wheel_name="${extension_name}"
17+
else
18+
wheel_name="${extension_name}_${package_name}"
19+
fi
20+
wheels=( $(find "${dir}" -name "${wheel_name}-*.whl") )
21+
if [[ ${#wheels[@]} -gt 1 ]]; then
22+
echo "ERROR: Multiple wheels found for ${package_name} in ${dir}"
23+
exit 1
24+
elif [[ ${#wheels[@]} -eq 1 ]]; then
25+
to_install+=("${wheels[0]}")
26+
else
27+
if [[ "${package_name}" == "." ]]; then
28+
src_dir=("${dir}")
29+
else
30+
src_dir=("${dir}-${package_name}")
31+
fi
32+
if [[ -f "${src_dir}/pyproject.toml" ]]; then
33+
to_install+=("${src_dir}")
34+
fi
35+
fi
36+
done
37+
done
38+
if [[ ${#to_install[@]} -gt 0 ]]; then
39+
pip install --no-deps "${to_install[@]}"
40+
fi
41+
}
42+
43+
44+
if [[ -n "${DIRACX_EXTENSIONS:-}" ]]; then
45+
# Install extensions in reverse order so that the base (diracx) is
46+
# installed last, allowing extension packages to override base packages.
47+
IFS=', ' read -r -a extension_array <<< "$DIRACX_EXTENSIONS"
48+
for (( idx=${#extension_array[@]}-1 ; idx>=0 ; idx-- )) ; do
49+
extension_name="${extension_array[idx]}"
50+
source_prefix="${extension_name^^}_CUSTOM_SOURCE_PREFIXES"
51+
image_packages="${extension_name^^}_IMAGE_PACKAGES"
52+
53+
if [[ -n "${!source_prefix:-}" ]]; then
54+
install_sources "${extension_name}" "${source_prefix}" "${image_packages}"
55+
fi
56+
done
57+
elif [[ -n "${DIRACX_CUSTOM_SOURCE_PREFIXES:-}" ]]; then
58+
# No extensions, just diracx
59+
install_sources "diracx" "DIRACX_CUSTOM_SOURCE_PREFIXES" "DIRACX_IMAGE_PACKAGES"
60+
fi
61+
62+
63+
exec "$@"

containers/services/Dockerfile

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)