# Multi-stage Dockerfile for PISCAL # Stage 1: Build stage - Compile PISCAL executable from Fortran sources FROM ubuntu:latest AS builder # Install build dependencies RUN set -xe \ && apt-get update \ && apt-get install --no-install-recommends -y \ make \ xutils-dev \ gfortran \ && apt-get autoclean -y \ && apt-get autoremove -y # Copy source files from multiple directories COPY leafres/testarea/ /build/leafres/testarea/ COPY dataassim/math/optimization/ /build/dataassim/math/optimization/ COPY dataassim/math/othersupmath/ /build/dataassim/math/othersupmath/ COPY dataassim/math/algebra/ /build/dataassim/math/algebra/ COPY dataassim/math/specialfuncs/ /build/dataassim/math/specialfuncs/ COPY dataassim/math/nonlinsystems/ /build/dataassim/math/nonlinsystems/ COPY leafres/testrun/Makefile /build/leafres/testrun/Makefile # Build the executable WORKDIR /build/leafres/testrun RUN make clean || true RUN make # Stage 2: Runtime stage - Create minimal application container FROM ubuntu:latest # Build arguments for configurable credentials and paths (v1 VM layout) ARG SSH_USERNAME=piscaladmin ARG SSH_PASSWORD=piscaladmin ARG SSH_GROUP=piscaladmin ARG STORAGE_PATH=/home/piscaladmin/LeafWeb_storage ARG PISCAL_EXECUTABLE=/home/piscaladmin/piscal_executable/piscal # Install runtime dependencies only RUN set -xe \ && apt-get update \ && apt-get upgrade -y \ && apt-get install --no-install-recommends -y \ openssh-server \ sudo \ iproute2 \ vim \ wget \ libgfortran5 \ && apt-get autoclean -y \ && apt-get autoremove -y # Configure SSH server with parameterized credentials RUN set -xe \ && groupadd ${SSH_GROUP} \ && useradd -g ${SSH_GROUP} -G sudo -m -s /bin/bash ${SSH_USERNAME} \ && echo "${SSH_USERNAME}:${SSH_PASSWORD}" | chpasswd RUN set -xe \ && sed -i -e 's/#PasswordAuthentication.*/PasswordAuthentication yes/g' /etc/ssh/sshd_config \ && sed -i -e 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config \ && sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd RUN set -xe \ && chown -R ${SSH_USERNAME}:${SSH_GROUP} /home/${SSH_USERNAME} # Fix for SSHD - "Missing privilege separation directory: /run/sshd" RUN set -xe \ && mkdir /run/sshd # v1 VM layout: LeafWeb contains scripts + project directories # Create LeafWeb (scripts and job dirs), piscal_executable, and storage RUN set -xe \ && mkdir -p /home/${SSH_USERNAME}/LeafWeb \ && mkdir -p /home/${SSH_USERNAME}/piscal_executable \ && chown -R ${SSH_USERNAME}:${SSH_GROUP} /home/${SSH_USERNAME}/LeafWeb \ && chown -R ${SSH_USERNAME}:${SSH_GROUP} /home/${SSH_USERNAME}/piscal_executable # Create storage directory structure with proper ownership RUN set -xe \ && mkdir -p ${STORAGE_PATH}/input \ && mkdir -p ${STORAGE_PATH}/output \ && chown -R ${SSH_USERNAME}:${SSH_GROUP} ${STORAGE_PATH} # Copy compiled executable and scripts to staging dir (COPY can't use ARG in dest) COPY --from=builder /build/leafres/testrun/piscal /tmp/piscal COPY piscal-manager/piscal_launcher.sh piscal-manager/piscal_manager.sh piscal-manager/subdir_year.sh piscal-manager/README.txt /tmp/leafweb/ # Install to user's home (supports custom SSH_USERNAME) RUN set -xe \ && cp /tmp/piscal /home/${SSH_USERNAME}/piscal_executable/piscal \ && chown ${SSH_USERNAME}:${SSH_GROUP} /home/${SSH_USERNAME}/piscal_executable/piscal \ && cp /tmp/leafweb/* /home/${SSH_USERNAME}/LeafWeb/ \ && echo "piscal_executable=\"${PISCAL_EXECUTABLE}\"" > /home/${SSH_USERNAME}/LeafWeb/piscal_launcher.cfg \ && echo "storage_directory=\"${STORAGE_PATH}\"" >> /home/${SSH_USERNAME}/LeafWeb/piscal_launcher.cfg \ && chown ${SSH_USERNAME}:${SSH_GROUP} /home/${SSH_USERNAME}/LeafWeb/* \ && find /home/${SSH_USERNAME}/LeafWeb -name "*.sh" -type f -exec sed -i 's/\r$//' {} \; \ && chmod +x /home/${SSH_USERNAME}/LeafWeb/*.sh || true \ && rm -rf /tmp/piscal /tmp/leafweb WORKDIR /home/${SSH_USERNAME}/LeafWeb EXPOSE 22 CMD ["/usr/sbin/sshd", "-D"]