forked from NOAA-OWP/ngencerf-server
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathDockerfile
More file actions
300 lines (276 loc) · 12.4 KB
/
Copy pathDockerfile
File metadata and controls
300 lines (276 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# syntax=docker/dockerfile:1.4
############################################################################
# Change/Verify these values when adopting this Dockerfile into another org:
# GH_ORG, IMAGE_NAMESPACE,
# DATA_ASSIMILATION_ORG, DATA_ASSIMILATION_REF,
# EWTS_ORG, EWTS_REF,
# MSW_MGR_ORG, MSW_MGR_REF,
# NGEN_ORG, NGEN_REF,
# NGEN_FORCING_ORG, NGEN_FORCING_REF
############################################################################
# Ownership / branding overrides
ARG GH_ORG=NGWPC
ARG IMAGE_NAMESPACE=ngwpc
# External repository sources (org and ref/branch overrides)
ARG DATA_ASSIMILATION_ORG=${GH_ORG}
ARG DATA_ASSIMILATION_REF=development
ARG EWTS_ORG=${GH_ORG}
ARG EWTS_REF=development
ARG MSW_MGR_ORG=${GH_ORG}
ARG MSW_MGR_REF=development
ARG NGEN_ORG=${GH_ORG}
ARG NGEN_REF=development
ARG NGEN_FORCING_ORG=${GH_ORG}
ARG NGEN_FORCING_REF=development
############################################################################
# Image selection
############################################################################
ARG BASE_REPO=python
ARG BASE_TAG=3.12-slim-bookworm
FROM ${BASE_REPO}:${BASE_TAG}
# Re-expose args after FROM for the remaining build stage
# Keeps whatever value was already set
ARG GH_ORG
ARG IMAGE_NAMESPACE
ARG DATA_ASSIMILATION_ORG
ARG DATA_ASSIMILATION_REF
ARG EWTS_ORG
ARG EWTS_REF
ARG MSW_MGR_ORG
ARG MSW_MGR_REF
ARG NGEN_ORG
ARG NGEN_REF
ARG NGEN_FORCING_ORG
ARG NGEN_FORCING_REF
# OCI Metadata Arguments
ARG BASE_REPO
ARG BASE_TAG
ARG BASE_NAME="${BASE_REPO}:${BASE_TAG}"
ARG BASE_DIGEST="unknown"
ARG BASE_REVISION="unknown"
ARG IMAGE_SOURCE="unknown"
ARG IMAGE_VENDOR="unknown"
ARG IMAGE_VERSION="unknown"
ARG IMAGE_REVISION="unknown"
ARG DATA_ASSIMILATION_REVISION="unknown"
ARG EWTS_REVISION="unknown"
ARG MSW_MGR_REVISION="unknown"
ARG NGEN_REVISION="unknown"
ARG NGEN_FORCING_REVISION="unknown"
# Image Labels: OCI-spec annotations followed by custom source-repo metadata.
# Single LABEL instruction per Docker best practices.
LABEL org.opencontainers.image.base.name="${BASE_NAME}" \
org.opencontainers.image.base.digest="${BASE_DIGEST}" \
org.opencontainers.image.source="${IMAGE_SOURCE}" \
org.opencontainers.image.vendor="${IMAGE_VENDOR}" \
org.opencontainers.image.version="${IMAGE_VERSION}" \
org.opencontainers.image.revision="${IMAGE_REVISION}" \
org.opencontainers.image.title="NGENCERF Server" \
org.opencontainers.image.description="Docker image for the NGENCERF server application" \
io.${IMAGE_NAMESPACE}.image.base.revision="${BASE_REVISION}" \
io.${IMAGE_NAMESPACE}.data.assimilation.org="${DATA_ASSIMILATION_ORG}" \
io.${IMAGE_NAMESPACE}.data.assimilation.ref="${DATA_ASSIMILATION_REF}" \
io.${IMAGE_NAMESPACE}.data.assimilation.revision="${DATA_ASSIMILATION_REVISION}" \
io.${IMAGE_NAMESPACE}.ewts.org="${EWTS_ORG}" \
io.${IMAGE_NAMESPACE}.ewts.ref="${EWTS_REF}" \
io.${IMAGE_NAMESPACE}.ewts.revision="${EWTS_REVISION}" \
io.${IMAGE_NAMESPACE}.msw.mgr.org="${MSW_MGR_ORG}" \
io.${IMAGE_NAMESPACE}.msw.mgr.ref="${MSW_MGR_REF}" \
io.${IMAGE_NAMESPACE}.msw.mgr.revision="${MSW_MGR_REVISION}" \
io.${IMAGE_NAMESPACE}.ngen.org="${NGEN_ORG}" \
io.${IMAGE_NAMESPACE}.ngen.ref="${NGEN_REF}" \
io.${IMAGE_NAMESPACE}.ngen.revision="${NGEN_REVISION}" \
io.${IMAGE_NAMESPACE}.ngen.forcing.org="${NGEN_FORCING_ORG}" \
io.${IMAGE_NAMESPACE}.ngen.forcing.ref="${NGEN_FORCING_REF}" \
io.${IMAGE_NAMESPACE}.ngen.forcing.revision="${NGEN_FORCING_REVISION}"
# Install build and runtime dependencies
RUN set -eux && \
apt-get update && \
apt-get install -y --no-install-recommends \
ca-certificates \
curl \
file \
findutils \
gcc \
g++ \
git \
jq \
libpq5 \
libpq-dev \
make \
openssl \
pkg-config \
xz-utils \
# GDAL/Fiona requirements
gdal-bin \
libgdal-dev \
libproj-dev \
proj-data && \
rm -rf /var/lib/apt/lists/*
# Install Python virtual environment
ENV VIRTUAL_ENV=/ngencerf/ngencerf-python
ENV PATH=${VIRTUAL_ENV}/bin:${PATH}
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache-bookworm \
set -eux && \
python -m venv ${VIRTUAL_ENV}
WORKDIR /ngencerf/ngencerf-server/
# Pre-copy requirements for better caching
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache-bookworm \
set -eux && \
pip install --upgrade pip && \
pip install -r requirements.txt && \
rm -f requirements.txt
# ── EWTS (Error and Warning Trapping System)
#
# The dev image only needs the Python EWTS runtime. The native EWTS
# libraries and ngen integration are built only in the production image.
ARG EWTS_CACHE_BUST=1
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache-bookworm \
set -eux && \
echo "EWTS cache bust: ${EWTS_CACHE_BUST}" && \
ewts_dir="$(mktemp -d)" && \
git clone --depth 1 -b "${EWTS_REF}" \
"https://github.com/${EWTS_ORG}/nwm-ewts.git" "${ewts_dir}" \
|| (git clone "https://github.com/${EWTS_ORG}/nwm-ewts.git" "${ewts_dir}" && \
cd "${ewts_dir}" && git checkout "${EWTS_REF}") && \
pip install "${ewts_dir}/runtime/python/ewts" && \
rm -rf "${ewts_dir}"
# Per-dependency cache-bust: CI passes the resolved commit SHA, so a stale
# Docker layer can't reinstall an older revision (see .github/workflows/cicd.yml)
ARG MSW_MGR_CACHE_BUST=1
ARG DATA_ASSIMILATION_CACHE_BUST=1
RUN --mount=type=cache,target=/root/.cache/pip,id=pip-cache-bookworm \
set -eux && \
echo "nwm-msw-mgr cache bust: ${MSW_MGR_CACHE_BUST}" && \
python -m pip install \
"git+https://github.com/${MSW_MGR_ORG}/nwm-msw-mgr.git@${MSW_MGR_REF}" && \
echo "nwm-data-assimilation cache bust: ${DATA_ASSIMILATION_CACHE_BUST}" && \
python -m pip install \
"git+https://github.com/${DATA_ASSIMILATION_ORG}/nwm-data-assimilation.git@${DATA_ASSIMILATION_REF}"
# Should parallel similar functionality in the run_cerf.sh
COPY .git .git
# Create git_info files for server, nwm-msw-mgr, and nwm-data-assimilation
RUN set -eux && \
# ----- Server git_info -----
# Ensure local tag metadata includes all remote tags before creating git_info.
git fetch --force --tags origin '+refs/tags/*:refs/tags/*' && \
# Get the remote URL from Git configuration
repo_url=$(git config --get remote.origin.url) && \
# Extract the repo name (everything after the last slash) and remove any trailing .git
key=${repo_url##*/} && \
key=${key%.git} && \
# Construct the file path using the derived key
GIT_INFO_PATH="${key}_git_info.json" && \
jq -n \
--arg commit_hash "$(git rev-parse HEAD)" \
--arg branch "$(git rev-parse --abbrev-ref HEAD)" \
--arg tags "$(git tag --points-at HEAD | tr '\n' ' ')" \
--arg author "$(git log -1 --pretty=format:'%an')" \
--arg commit_date "$(date -u -d @$(git log -1 --pretty=format:'%ct') +'%Y-%m-%d %H:%M:%S UTC')" \
--arg message "$(git log -1 --pretty=format:'%s' | tr '\n' ';')" \
--arg build_date "$(date -u +'%Y-%m-%d %H:%M:%S UTC')" \
"{\"$key\": {commit_hash: \$commit_hash, branch: \$branch, tags: \$tags, author: \$author, commit_date: \$commit_date, message: \$message, build_date: \$build_date}}" \
> "$GIT_INFO_PATH" && \
\
# ----- nwm-msw-mgr git_info -----
GIT_INFO_PATH="/ngencerf/ngencerf-server/nwm-msw-mgr_git_info.json" && \
tmpdir=$(mktemp -d) && \
git init "$tmpdir" && \
cd "$tmpdir" && \
git remote add origin "https://github.com/${MSW_MGR_ORG}/nwm-msw-mgr.git" && \
git fetch --force --tags origin '+refs/tags/*:refs/tags/*' && \
(git fetch --depth 1 origin "${MSW_MGR_REF}" \
|| git fetch --depth 1 origin "refs/tags/${MSW_MGR_REF}:refs/tags/${MSW_MGR_REF}" \
|| git fetch origin "${MSW_MGR_REF}" \
|| git fetch origin "refs/tags/${MSW_MGR_REF}:refs/tags/${MSW_MGR_REF}") && \
git checkout FETCH_HEAD && \
# Detect branch vs tag vs bare SHA for git_info metadata
branch=$(git branch -r --contains HEAD 2>/dev/null \
| grep -v '\->' | sed 's|origin/||' | head -n1 | xargs) && \
branch=${branch:-""} && \
tags=$(git tag --points-at HEAD 2>/dev/null | tr '\n' ' ') && \
jq -n \
--arg commit_hash "$(git rev-parse HEAD)" \
--arg branch "$branch" \
--arg tags "$tags" \
--arg author "$(git log -1 --pretty=format:'%an')" \
--arg commit_date "$(date -u -d @$(git log -1 --pretty=format:'%ct') +'%Y-%m-%d %H:%M:%S UTC')" \
--arg message "$(git log -1 --pretty=format:'%s' | tr '\n' ';')" \
--arg build_date "$(date -u +'%Y-%m-%d %H:%M:%S UTC')" \
'{"nwm-msw-mgr": {commit_hash: $commit_hash, branch: $branch, tags: $tags, author: $author, commit_date: $commit_date, message: $message, build_date: $build_date}}' \
> "$GIT_INFO_PATH" && \
cd / && \
rm -rf "$tmpdir" && \
\
# ----- nwm-data-assimilation git_info -----
GIT_INFO_PATH="/ngencerf/ngencerf-server/nwm-data-assimilation_git_info.json" && \
tmpdir=$(mktemp -d) && \
git init "$tmpdir" && \
cd "$tmpdir" && \
git remote add origin "https://github.com/${DATA_ASSIMILATION_ORG}/nwm-data-assimilation.git" && \
git fetch --force --tags origin '+refs/tags/*:refs/tags/*' && \
(git fetch --depth 1 origin "${DATA_ASSIMILATION_REF}" \
|| git fetch --depth 1 origin "refs/tags/${DATA_ASSIMILATION_REF}:refs/tags/${DATA_ASSIMILATION_REF}" \
|| git fetch origin "${DATA_ASSIMILATION_REF}" \
|| git fetch origin "refs/tags/${DATA_ASSIMILATION_REF}:refs/tags/${DATA_ASSIMILATION_REF}") && \
git checkout FETCH_HEAD && \
# Detect branch vs tag vs bare SHA for git_info metadata
branch=$(git branch -r --contains HEAD 2>/dev/null \
| grep -v '\->' | sed 's|origin/||' | head -n1 | xargs) && \
branch=${branch:-""} && \
tags=$(git tag --points-at HEAD 2>/dev/null | tr '\n' ' ') && \
jq -n \
--arg commit_hash "$(git rev-parse HEAD)" \
--arg branch "$branch" \
--arg tags "$tags" \
--arg author "$(git log -1 --pretty=format:'%an')" \
--arg commit_date "$(date -u -d @$(git log -1 --pretty=format:'%ct') +'%Y-%m-%d %H:%M:%S UTC')" \
--arg message "$(git log -1 --pretty=format:'%s' | tr '\n' ';')" \
--arg build_date "$(date -u +'%Y-%m-%d %H:%M:%S UTC')" \
'{"nwm-data-assimilation": {commit_hash: $commit_hash, branch: $branch, tags: $tags, author: $author, commit_date: $commit_date, message: $message, build_date: $build_date}}' \
> "$GIT_INFO_PATH" && \
cd / && \
rm -rf "$tmpdir"
# Remove .git directory
RUN rm -rf .git
# Copy application code
COPY . /ngencerf/ngencerf-server/
# Fetch bmi_forcing_templates into an internal, non-mounted path to be copied at runtime by runCerf.sh
# Cache-bust on the resolved ngen-forcing commit so a ref update isn't masked by a cached layer
ARG NGEN_FORCING_CACHE_BUST=1
RUN set -eux && \
echo "ngen-forcing cache bust: ${NGEN_FORCING_CACHE_BUST}" && \
PREBUILT_DIR="/ngencerf/prebuilt/bmi_forcing_templates" && \
NGEN_FORCING_URL="https://github.com/${NGEN_FORCING_ORG}/ngen-forcing.git" && \
\
echo "Preparing bmi_forcing_templates from ${NGEN_FORCING_URL}, ref (branch/tag/commit): ${NGEN_FORCING_REF}" && \
\
# Ensure prebuilt directory exists and is empty
rm -rf "$PREBUILT_DIR" && \
mkdir -p "$PREBUILT_DIR" && \
\
# Clone sparse repo; allow branch, tag, or commit refs
git clone --filter=blob:none --no-checkout --sparse \
"$NGEN_FORCING_URL" tmp-ngen-forcing && \
cd tmp-ngen-forcing && \
(git fetch --depth 1 origin "${NGEN_FORCING_REF}" \
|| git fetch --depth 1 origin "refs/tags/${NGEN_FORCING_REF}:refs/tags/${NGEN_FORCING_REF}" \
|| git fetch origin "${NGEN_FORCING_REF}" \
|| git fetch origin "refs/tags/${NGEN_FORCING_REF}:refs/tags/${NGEN_FORCING_REF}") && \
git checkout FETCH_HEAD && \
\
git sparse-checkout set \
NextGen_Forcings_Engine_BMI/BMI_NextGen_Configs/config_templates && \
\
# Copy *contents* of config_templates into PREBUILT_DIR
cp -a NextGen_Forcings_Engine_BMI/BMI_NextGen_Configs/config_templates/. \
"$PREBUILT_DIR"/ && \
\
cd /ngencerf/ngencerf-server && \
rm -rf tmp-ngen-forcing || true
# Copy additional configuration files
COPY ./cerfserver-docker.env /ngencerf/ngencerf-server/cerfserver.env
# Set the entry point and expose the application port
ENTRYPOINT [ "/ngencerf/ngencerf-server/runCerf.sh" ]
EXPOSE 8000