spack containerize generates containers from envs  (#14202)
				
					
				
			This PR adds a new command to Spack:
```console
$ spack containerize -h
usage: spack containerize [-h] [--config CONFIG]
creates recipes to build images for different container runtimes
optional arguments:
  -h, --help       show this help message and exit
  --config CONFIG  configuration for the container recipe that will be generated
```
which takes an environment with an additional `container` section:
```yaml
spack:
  specs:
  - gromacs build_type=Release 
  - mpich
  - fftw precision=float
  packages:
    all:
      target: [broadwell]
  container:
    # Select the format of the recipe e.g. docker,
    # singularity or anything else that is currently supported
    format: docker
    
    # Select from a valid list of images
    base:
      image: "ubuntu:18.04"
      spack: prerelease
    # Additional system packages that are needed at runtime
    os_packages:
    - libgomp1
```
and turns it into a `Dockerfile` or a Singularity definition file, for instance:
```Dockerfile
# Build stage with Spack pre-installed and ready to be used
FROM spack/ubuntu-bionic:prerelease as builder
# What we want to install and how we want to install it
# is specified in a manifest file (spack.yaml)
RUN mkdir /opt/spack-environment \
&&  (echo "spack:" \
&&   echo "  specs:" \
&&   echo "  - gromacs build_type=Release" \
&&   echo "  - mpich" \
&&   echo "  - fftw precision=float" \
&&   echo "  packages:" \
&&   echo "    all:" \
&&   echo "      target:" \
&&   echo "      - broadwell" \
&&   echo "  config:" \
&&   echo "    install_tree: /opt/software" \
&&   echo "  concretization: together" \
&&   echo "  view: /opt/view") > /opt/spack-environment/spack.yaml
# Install the software, remove unecessary deps and strip executables
RUN cd /opt/spack-environment && spack install && spack autoremove -y
RUN find -L /opt/view/* -type f -exec readlink -f '{}' \; | \
    xargs file -i | \
    grep 'charset=binary' | \
    grep 'x-executable\|x-archive\|x-sharedlib' | \
    awk -F: '{print $1}' | xargs strip -s
# Modifications to the environment that are necessary to run
RUN cd /opt/spack-environment && \
    spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh
# Bare OS image to run the installed executables
FROM ubuntu:18.04
COPY --from=builder /opt/spack-environment /opt/spack-environment
COPY --from=builder /opt/software /opt/software
COPY --from=builder /opt/view /opt/view
COPY --from=builder /etc/profile.d/z10_spack_environment.sh /etc/profile.d/z10_spack_environment.sh
RUN apt-get -yqq update && apt-get -yqq upgrade                                   \
 && apt-get -yqq install libgomp1 \
 && rm -rf /var/lib/apt/lists/*
ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l"]
```
			
			
This commit is contained in:
		 Massimiliano Culpo
					Massimiliano Culpo
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						parent
						
							ed501eaab2
						
					
				
				
					commit
					9635ff3d20
				
			
							
								
								
									
										307
									
								
								lib/spack/docs/containers.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										307
									
								
								lib/spack/docs/containers.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,307 @@ | |||||||
|  | .. Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  |    Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  |  | ||||||
|  |    SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  |  | ||||||
|  | .. _containers: | ||||||
|  |  | ||||||
|  | ================ | ||||||
|  | Container Images | ||||||
|  | ================ | ||||||
|  |  | ||||||
|  | Spack can be an ideal tool to setup images for containers since all the | ||||||
|  | features discussed in :ref:`environments` can greatly help to manage | ||||||
|  | the installation of software during the image build process. Nonetheless, | ||||||
|  | building a production image from scratch still requires a lot of | ||||||
|  | boilerplate to: | ||||||
|  |  | ||||||
|  | - Get Spack working within the image, possibly running as root | ||||||
|  | - Minimize the physical size of the software installed | ||||||
|  | - Properly update the system software in the base image | ||||||
|  |  | ||||||
|  | To facilitate users with these tedious tasks, Spack provides a command | ||||||
|  | to automatically generate recipes for container images based on | ||||||
|  | Environments: | ||||||
|  |  | ||||||
|  | .. code-block:: console | ||||||
|  |  | ||||||
|  |    $ ls | ||||||
|  |    spack.yaml | ||||||
|  |  | ||||||
|  |    $ spack containerize | ||||||
|  |    # Build stage with Spack pre-installed and ready to be used | ||||||
|  |    FROM spack/centos7:latest as builder | ||||||
|  |  | ||||||
|  |    # What we want to install and how we want to install it | ||||||
|  |    # is specified in a manifest file (spack.yaml) | ||||||
|  |    RUN mkdir /opt/spack-environment \ | ||||||
|  |    &&  (echo "spack:" \ | ||||||
|  |    &&   echo "  specs:" \ | ||||||
|  |    &&   echo "  - gromacs+mpi" \ | ||||||
|  |    &&   echo "  - mpich" \ | ||||||
|  |    &&   echo "  concretization: together" \ | ||||||
|  |    &&   echo "  config:" \ | ||||||
|  |    &&   echo "    install_tree: /opt/software" \ | ||||||
|  |    &&   echo "  view: /opt/view") > /opt/spack-environment/spack.yaml | ||||||
|  |  | ||||||
|  |    # Install the software, remove unecessary deps | ||||||
|  |    RUN cd /opt/spack-environment && spack install && spack gc -y | ||||||
|  |  | ||||||
|  |    # Strip all the binaries | ||||||
|  |    RUN find -L /opt/view/* -type f -exec readlink -f '{}' \; | \ | ||||||
|  |        xargs file -i | \ | ||||||
|  |        grep 'charset=binary' | \ | ||||||
|  |        grep 'x-executable\|x-archive\|x-sharedlib' | \ | ||||||
|  |        awk -F: '{print $1}' | xargs strip -s | ||||||
|  |  | ||||||
|  |    # Modifications to the environment that are necessary to run | ||||||
|  |    RUN cd /opt/spack-environment && \ | ||||||
|  |        spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh | ||||||
|  |  | ||||||
|  |  | ||||||
|  |    # Bare OS image to run the installed executables | ||||||
|  |    FROM centos:7 | ||||||
|  |  | ||||||
|  |    COPY --from=builder /opt/spack-environment /opt/spack-environment | ||||||
|  |    COPY --from=builder /opt/software /opt/software | ||||||
|  |    COPY --from=builder /opt/view /opt/view | ||||||
|  |    COPY --from=builder /etc/profile.d/z10_spack_environment.sh /etc/profile.d/z10_spack_environment.sh | ||||||
|  |  | ||||||
|  |    RUN yum update -y && yum install -y epel-release && yum update -y                                   \ | ||||||
|  |     && yum install -y libgomp \ | ||||||
|  |     && rm -rf /var/cache/yum  && yum clean all | ||||||
|  |  | ||||||
|  |    RUN echo 'export PS1="\[$(tput bold)\]\[$(tput setaf 1)\][gromacs]\[$(tput setaf 2)\]\u\[$(tput sgr0)\]:\w $ \[$(tput sgr0)\]"' >> ~/.bashrc | ||||||
|  |  | ||||||
|  |  | ||||||
|  |    LABEL "app"="gromacs" | ||||||
|  |    LABEL "mpi"="mpich" | ||||||
|  |  | ||||||
|  |    ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l"] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | The bits that make this automation possible are discussed in details | ||||||
|  | below. All the images generated in this way will be based on | ||||||
|  | multi-stage builds with: | ||||||
|  |  | ||||||
|  | - A fat ``build`` stage containing common build tools and Spack itself | ||||||
|  | - A minimal ``final`` stage containing only the software requested by the user | ||||||
|  |  | ||||||
|  | ----------------- | ||||||
|  | Spack Base Images | ||||||
|  | ----------------- | ||||||
|  |  | ||||||
|  | Docker images with Spack preinstalled and ready to be used are | ||||||
|  | built on `Docker Hub <https://hub.docker.com/u/spack>`_ | ||||||
|  | at every push to ``develop`` or to a release branch. The OS that | ||||||
|  | are currently supported are summarized in the table below: | ||||||
|  |  | ||||||
|  | .. _containers-supported-os: | ||||||
|  |  | ||||||
|  | .. list-table:: Supported operating systems | ||||||
|  |    :header-rows: 1 | ||||||
|  |  | ||||||
|  |    * - Operating System | ||||||
|  |      - Base Image | ||||||
|  |      - Spack Image | ||||||
|  |    * - Ubuntu 16.04 | ||||||
|  |      - ``ubuntu:16.04`` | ||||||
|  |      - ``spack/ubuntu-xenial`` | ||||||
|  |    * - Ubuntu 18.04 | ||||||
|  |      - ``ubuntu:16.04`` | ||||||
|  |      - ``spack/ubuntu-bionic`` | ||||||
|  |    * - CentOS 6 | ||||||
|  |      - ``centos:6`` | ||||||
|  |      - ``spack/centos6`` | ||||||
|  |    * - CentOS 7 | ||||||
|  |      - ``centos:7`` | ||||||
|  |      - ``spack/centos7`` | ||||||
|  |  | ||||||
|  | All the images are tagged with the corresponding release of Spack: | ||||||
|  |  | ||||||
|  | .. image:: dockerhub_spack.png | ||||||
|  |  | ||||||
|  | with the exception of the ``latest`` tag that points to the HEAD | ||||||
|  | of the ``develop`` branch. These images are available for anyone | ||||||
|  | to use and take care of all the repetitive tasks that are necessary | ||||||
|  | to setup Spack within a container. All the container recipes generated | ||||||
|  | automatically by Spack use them as base images for their ``build`` stage. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ------------------------- | ||||||
|  | Environment Configuration | ||||||
|  | ------------------------- | ||||||
|  |  | ||||||
|  | Any Spack Environment can be used for the automatic generation of container | ||||||
|  | recipes. Sensible defaults are provided for things like the base image or the | ||||||
|  | version of Spack used in the image. If a finer tuning is needed it can be | ||||||
|  | obtained by adding the relevant metadata under the ``container`` attribute | ||||||
|  | of environments: | ||||||
|  |  | ||||||
|  | .. code-block:: yaml | ||||||
|  |  | ||||||
|  |    spack: | ||||||
|  |      specs: | ||||||
|  |      - gromacs+mpi | ||||||
|  |      - mpich | ||||||
|  |  | ||||||
|  |      container: | ||||||
|  |        # Select the format of the recipe e.g. docker, | ||||||
|  |        # singularity or anything else that is currently supported | ||||||
|  |        format: docker | ||||||
|  |  | ||||||
|  |        # Select from a valid list of images | ||||||
|  |        base: | ||||||
|  |          image: "centos:7" | ||||||
|  |          spack: develop | ||||||
|  |  | ||||||
|  |        # Whether or not to strip binaries | ||||||
|  |        strip: true | ||||||
|  |  | ||||||
|  |        # Additional system packages that are needed at runtime | ||||||
|  |        os_packages: | ||||||
|  |        - libgomp | ||||||
|  |  | ||||||
|  |        # Extra instructions | ||||||
|  |        extra_instructions: | ||||||
|  |          final: | | ||||||
|  |    RUN echo 'export PS1="\[$(tput bold)\]\[$(tput setaf 1)\][gromacs]\[$(tput setaf 2)\]\u\[$(tput sgr0)\]:\w $ \[$(tput sgr0)\]"' >> ~/.bashrc | ||||||
|  |  | ||||||
|  |        # Labels for the image | ||||||
|  |        labels: | ||||||
|  |          app: "gromacs" | ||||||
|  |          mpi: "mpich" | ||||||
|  |  | ||||||
|  | The tables below describe the configuration options that are currently supported: | ||||||
|  |  | ||||||
|  | .. list-table:: General configuration options for the ``container`` section of ``spack.yaml`` | ||||||
|  |    :header-rows: 1 | ||||||
|  |  | ||||||
|  |    * - Option Name | ||||||
|  |      - Description | ||||||
|  |      - Allowed Values | ||||||
|  |      - Required | ||||||
|  |    * - ``format`` | ||||||
|  |      - The format of the recipe | ||||||
|  |      - ``docker`` or ``singularity`` | ||||||
|  |      - Yes | ||||||
|  |    * - ``base:image`` | ||||||
|  |      - Base image for ``final`` stage | ||||||
|  |      - See :ref:`containers-supported-os` | ||||||
|  |      - Yes | ||||||
|  |    * - ``base:spack`` | ||||||
|  |      - Version of Spack | ||||||
|  |      - Valid tags for ``base:image`` | ||||||
|  |      - Yes | ||||||
|  |    * - ``strip`` | ||||||
|  |      - Whether to strip binaries | ||||||
|  |      - ``true`` (default) or ``false`` | ||||||
|  |      - No | ||||||
|  |    * - ``os_packages`` | ||||||
|  |      - System packages to be installed | ||||||
|  |      - Valid packages for the ``final`` OS | ||||||
|  |      - No | ||||||
|  |    * - ``extra_instructions:build`` | ||||||
|  |      - Extra instructions (e.g. `RUN`, `COPY`, etc.) at the end of the ``build`` stage | ||||||
|  |      - Anything understood by the current ``format`` | ||||||
|  |      - No | ||||||
|  |    * - ``extra_instructions:final`` | ||||||
|  |      - Extra instructions (e.g. `RUN`, `COPY`, etc.) at the end of the ``final`` stage | ||||||
|  |      - Anything understood by the current ``format`` | ||||||
|  |      - No | ||||||
|  |    * - ``labels`` | ||||||
|  |      - Labels to tag the image | ||||||
|  |      - Pairs of key-value strings | ||||||
|  |      - No | ||||||
|  |  | ||||||
|  | .. list-table:: Configuration options specific to Singularity | ||||||
|  |    :header-rows: 1 | ||||||
|  |  | ||||||
|  |    * - Option Name | ||||||
|  |      - Description | ||||||
|  |      - Allowed Values | ||||||
|  |      - Required | ||||||
|  |    * - ``singularity:runscript`` | ||||||
|  |      - Content of ``%runscript`` | ||||||
|  |      - Any valid script | ||||||
|  |      - No | ||||||
|  |    * - ``singularity:startscript`` | ||||||
|  |      - Content of ``%startscript`` | ||||||
|  |      - Any valid script | ||||||
|  |      - No | ||||||
|  |    * - ``singularity:test`` | ||||||
|  |      - Content of ``%test`` | ||||||
|  |      - Any valid script | ||||||
|  |      - No | ||||||
|  |    * - ``singularity:help`` | ||||||
|  |      - Description of the image | ||||||
|  |      - Description string | ||||||
|  |      - No | ||||||
|  |  | ||||||
|  | Once the Environment is properly configured a recipe for a container | ||||||
|  | image can be printed to standard output by issuing the following | ||||||
|  | command from the directory where the ``spack.yaml`` resides: | ||||||
|  |  | ||||||
|  | .. code-block:: console | ||||||
|  |  | ||||||
|  |    $ spack containerize | ||||||
|  |  | ||||||
|  | The example ``spack.yaml`` above would produce for instance the | ||||||
|  | following ``Dockerfile``: | ||||||
|  |  | ||||||
|  | .. code-block:: docker | ||||||
|  |  | ||||||
|  |    # Build stage with Spack pre-installed and ready to be used | ||||||
|  |    FROM spack/centos7:latest as builder | ||||||
|  |  | ||||||
|  |    # What we want to install and how we want to install it | ||||||
|  |    # is specified in a manifest file (spack.yaml) | ||||||
|  |    RUN mkdir /opt/spack-environment \ | ||||||
|  |    &&  (echo "spack:" \ | ||||||
|  |    &&   echo "  specs:" \ | ||||||
|  |    &&   echo "  - gromacs+mpi" \ | ||||||
|  |    &&   echo "  - mpich" \ | ||||||
|  |    &&   echo "  concretization: together" \ | ||||||
|  |    &&   echo "  config:" \ | ||||||
|  |    &&   echo "    install_tree: /opt/software" \ | ||||||
|  |    &&   echo "  view: /opt/view") > /opt/spack-environment/spack.yaml | ||||||
|  |  | ||||||
|  |    # Install the software, remove unecessary deps | ||||||
|  |    RUN cd /opt/spack-environment && spack install && spack gc -y | ||||||
|  |  | ||||||
|  |    # Strip all the binaries | ||||||
|  |    RUN find -L /opt/view/* -type f -exec readlink -f '{}' \; | \ | ||||||
|  |        xargs file -i | \ | ||||||
|  |        grep 'charset=binary' | \ | ||||||
|  |        grep 'x-executable\|x-archive\|x-sharedlib' | \ | ||||||
|  |        awk -F: '{print $1}' | xargs strip -s | ||||||
|  |  | ||||||
|  |    # Modifications to the environment that are necessary to run | ||||||
|  |    RUN cd /opt/spack-environment && \ | ||||||
|  |        spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh | ||||||
|  |  | ||||||
|  |  | ||||||
|  |    # Bare OS image to run the installed executables | ||||||
|  |    FROM centos:7 | ||||||
|  |  | ||||||
|  |    COPY --from=builder /opt/spack-environment /opt/spack-environment | ||||||
|  |    COPY --from=builder /opt/software /opt/software | ||||||
|  |    COPY --from=builder /opt/view /opt/view | ||||||
|  |    COPY --from=builder /etc/profile.d/z10_spack_environment.sh /etc/profile.d/z10_spack_environment.sh | ||||||
|  |  | ||||||
|  |    RUN yum update -y && yum install -y epel-release && yum update -y                                   \ | ||||||
|  |     && yum install -y libgomp \ | ||||||
|  |     && rm -rf /var/cache/yum  && yum clean all | ||||||
|  |  | ||||||
|  |    RUN echo 'export PS1="\[$(tput bold)\]\[$(tput setaf 1)\][gromacs]\[$(tput setaf 2)\]\u\[$(tput sgr0)\]:\w $ \[$(tput sgr0)\]"' >> ~/.bashrc | ||||||
|  |  | ||||||
|  |  | ||||||
|  |    LABEL "app"="gromacs" | ||||||
|  |    LABEL "mpi"="mpich" | ||||||
|  |  | ||||||
|  |    ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l"] | ||||||
|  |  | ||||||
|  | .. note:: | ||||||
|  |    Spack can also produce Singularity definition files to build the image. The | ||||||
|  |    minimum version of Singularity required to build a SIF (Singularity Image Format) | ||||||
|  |    from them is ``3.5.3``. | ||||||
							
								
								
									
										
											BIN
										
									
								
								lib/spack/docs/dockerhub_spack.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								lib/spack/docs/dockerhub_spack.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 88 KiB | 
| @@ -49,6 +49,8 @@ Spack uses a "manifest and lock" model similar to `Bundler gemfiles | |||||||
| managers. The user input file is named ``spack.yaml`` and the lock | managers. The user input file is named ``spack.yaml`` and the lock | ||||||
| file is named ``spack.lock`` | file is named ``spack.lock`` | ||||||
|  |  | ||||||
|  | .. _environments-using: | ||||||
|  |  | ||||||
| ------------------ | ------------------ | ||||||
| Using Environments | Using Environments | ||||||
| ------------------ | ------------------ | ||||||
|   | |||||||
| @@ -66,6 +66,7 @@ or refer to the full manual below. | |||||||
|    config_yaml |    config_yaml | ||||||
|    build_settings |    build_settings | ||||||
|    environments |    environments | ||||||
|  |    containers | ||||||
|    mirrors |    mirrors | ||||||
|    module_file_support |    module_file_support | ||||||
|    repositories |    repositories | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								lib/spack/spack/cmd/containerize.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								lib/spack/spack/cmd/containerize.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | import os | ||||||
|  | import os.path | ||||||
|  | import spack.container | ||||||
|  | 
 | ||||||
|  | description = ("creates recipes to build images for different" | ||||||
|  |                " container runtimes") | ||||||
|  | section = "container" | ||||||
|  | level = "long" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def containerize(parser, args): | ||||||
|  |     config_dir = args.env_dir or os.getcwd() | ||||||
|  |     config_file = os.path.abspath(os.path.join(config_dir, 'spack.yaml')) | ||||||
|  |     if not os.path.exists(config_file): | ||||||
|  |         msg = 'file not found: {0}' | ||||||
|  |         raise ValueError(msg.format(config_file)) | ||||||
|  | 
 | ||||||
|  |     config = spack.container.validate(config_file) | ||||||
|  | 
 | ||||||
|  |     recipe = spack.container.recipe(config) | ||||||
|  |     print(recipe) | ||||||
							
								
								
									
										81
									
								
								lib/spack/spack/container/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								lib/spack/spack/container/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | """Package that provides functions and classes to | ||||||
|  | generate container recipes from a Spack environment | ||||||
|  | """ | ||||||
|  | import warnings | ||||||
|  | 
 | ||||||
|  | import spack.environment | ||||||
|  | import spack.schema.env as env | ||||||
|  | import spack.util.spack_yaml as syaml | ||||||
|  | from .writers import recipe | ||||||
|  | 
 | ||||||
|  | __all__ = ['validate', 'recipe'] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def validate(configuration_file): | ||||||
|  |     """Validate a Spack environment YAML file that is being used to generate a | ||||||
|  |     recipe for a container. | ||||||
|  | 
 | ||||||
|  |     Since a few attributes of the configuration must have specific values for | ||||||
|  |     the container recipe, this function returns a sanitized copy of the | ||||||
|  |     configuration in the input file. If any modification is needed, a warning | ||||||
|  |     will be issued. | ||||||
|  | 
 | ||||||
|  |     Args: | ||||||
|  |         configuration_file (str): path to the Spack environment YAML file | ||||||
|  | 
 | ||||||
|  |     Returns: | ||||||
|  |         A sanitized copy of the configuration stored in the input file | ||||||
|  |     """ | ||||||
|  |     import jsonschema | ||||||
|  |     with open(configuration_file) as f: | ||||||
|  |         config = syaml.load(f) | ||||||
|  | 
 | ||||||
|  |     # Ensure we have a "container" attribute with sensible defaults set | ||||||
|  |     env_dict = spack.environment.config_dict(config) | ||||||
|  |     env_dict.setdefault('container', { | ||||||
|  |         'format': 'docker', | ||||||
|  |         'base': {'image': 'ubuntu:18.04', 'spack': 'develop'} | ||||||
|  |     }) | ||||||
|  |     env_dict['container'].setdefault('format', 'docker') | ||||||
|  |     env_dict['container'].setdefault( | ||||||
|  |         'base', {'image': 'ubuntu:18.04', 'spack': 'develop'} | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     # Remove attributes that are not needed / allowed in the | ||||||
|  |     # container recipe | ||||||
|  |     for subsection in ('cdash', 'gitlab_ci', 'modules'): | ||||||
|  |         if subsection in env_dict: | ||||||
|  |             msg = ('the subsection "{0}" in "{1}" is not used when generating' | ||||||
|  |                    ' container recipes and will be discarded') | ||||||
|  |             warnings.warn(msg.format(subsection, configuration_file)) | ||||||
|  |             env_dict.pop(subsection) | ||||||
|  | 
 | ||||||
|  |     # Set the default value of the concretization strategy to "together" and | ||||||
|  |     # warn if the user explicitly set another value | ||||||
|  |     env_dict.setdefault('concretization', 'together') | ||||||
|  |     if env_dict['concretization'] != 'together': | ||||||
|  |         msg = ('the "concretization" attribute of the environment is set ' | ||||||
|  |                'to "{0}" [the advised value is instead "together"]') | ||||||
|  |         warnings.warn(msg.format(env_dict['concretization'])) | ||||||
|  | 
 | ||||||
|  |     # Check if the install tree was explicitly set to a custom value and warn | ||||||
|  |     # that it will be overridden | ||||||
|  |     environment_config = env_dict.get('config', {}) | ||||||
|  |     if environment_config.get('install_tree', None): | ||||||
|  |         msg = ('the "config:install_tree" attribute has been set explicitly ' | ||||||
|  |                'and will be overridden in the container image') | ||||||
|  |         warnings.warn(msg) | ||||||
|  | 
 | ||||||
|  |     # Likewise for the view | ||||||
|  |     environment_view = env_dict.get('view', None) | ||||||
|  |     if environment_view: | ||||||
|  |         msg = ('the "view" attribute has been set explicitly ' | ||||||
|  |                'and will be overridden in the container image') | ||||||
|  |         warnings.warn(msg) | ||||||
|  | 
 | ||||||
|  |     jsonschema.validate(config, schema=env.schema) | ||||||
|  |     return config | ||||||
							
								
								
									
										50
									
								
								lib/spack/spack/container/images.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								lib/spack/spack/container/images.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | { | ||||||
|  |   "ubuntu:18.04": { | ||||||
|  |     "update": "apt-get -yqq update && apt-get -yqq upgrade", | ||||||
|  |     "install": "apt-get -yqq install", | ||||||
|  |     "clean": "rm -rf /var/lib/apt/lists/*", | ||||||
|  |     "environment": [], | ||||||
|  |     "build": "spack/ubuntu-bionic", | ||||||
|  |     "build_tags": { | ||||||
|  |       "develop": "latest", | ||||||
|  |       "0.14": "0.14", | ||||||
|  |       "0.14.0": "0.14.0" | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   "ubuntu:16.04": { | ||||||
|  |     "update": "apt-get -yqq update && apt-get -yqq upgrade", | ||||||
|  |     "install": "apt-get -yqq install", | ||||||
|  |     "clean": "rm -rf /var/lib/apt/lists/*", | ||||||
|  |     "environment": [], | ||||||
|  |     "build": "spack/ubuntu-xenial", | ||||||
|  |     "build_tags": { | ||||||
|  |       "develop": "latest", | ||||||
|  |       "0.14": "0.14", | ||||||
|  |       "0.14.0": "0.14.0" | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   "centos:7": { | ||||||
|  |     "update": "yum update -y && yum install -y epel-release && yum update -y", | ||||||
|  |     "install": "yum install -y", | ||||||
|  |     "clean": "rm -rf /var/cache/yum  && yum clean all", | ||||||
|  |     "environment": [], | ||||||
|  |     "build": "spack/centos7", | ||||||
|  |     "build_tags": { | ||||||
|  |       "develop": "latest", | ||||||
|  |       "0.14": "0.14", | ||||||
|  |       "0.14.0": "0.14.0" | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   "centos:6": { | ||||||
|  |     "update": "yum update -y && yum install -y epel-release && yum update -y", | ||||||
|  |     "install": "yum install -y", | ||||||
|  |     "clean": "rm -rf /var/cache/yum  && yum clean all", | ||||||
|  |     "environment": [], | ||||||
|  |     "build": "spack/centos6", | ||||||
|  |     "build_tags": { | ||||||
|  |       "develop": "latest", | ||||||
|  |       "0.14": "0.14", | ||||||
|  |       "0.14.0": "0.14.0" | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										72
									
								
								lib/spack/spack/container/images.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								lib/spack/spack/container/images.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | """Manages the details on the images used in the build and the run stage.""" | ||||||
|  | import json | ||||||
|  | import os.path | ||||||
|  | 
 | ||||||
|  | #: Global variable used to cache in memory the content of images.json | ||||||
|  | _data = None | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def data(): | ||||||
|  |     """Returns a dictionary with the static data on the images. | ||||||
|  | 
 | ||||||
|  |     The dictionary is read from a JSON file lazily the first time | ||||||
|  |     this function is called. | ||||||
|  |     """ | ||||||
|  |     global _data | ||||||
|  |     if not _data: | ||||||
|  |         json_dir = os.path.abspath(os.path.dirname(__file__)) | ||||||
|  |         json_file = os.path.join(json_dir, 'images.json') | ||||||
|  |         with open(json_file) as f: | ||||||
|  |             _data = json.load(f) | ||||||
|  |     return _data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def build_info(image, spack_version): | ||||||
|  |     """Returns the name of the build image and its tag. | ||||||
|  | 
 | ||||||
|  |     Args: | ||||||
|  |         image (str): image to be used at run-time. Should be of the form | ||||||
|  |             <image_name>:<image_tag> e.g. "ubuntu:18.04" | ||||||
|  |         spack_version (str): version of Spack that we want to use to build | ||||||
|  | 
 | ||||||
|  |     Returns: | ||||||
|  |         A tuple with (image_name, image_tag) for the build image | ||||||
|  |     """ | ||||||
|  |     # Don't handle error here, as a wrong image should have been | ||||||
|  |     # caught by the JSON schema | ||||||
|  |     image_data = data()[image] | ||||||
|  |     build_image = image_data['build'] | ||||||
|  | 
 | ||||||
|  |     # Try to check if we have a tag for this Spack version | ||||||
|  |     try: | ||||||
|  |         build_tag = image_data['build_tags'][spack_version] | ||||||
|  |     except KeyError: | ||||||
|  |         msg = ('the image "{0}" has no tag for Spack version "{1}" ' | ||||||
|  |                '[valid versions are {2}]') | ||||||
|  |         msg = msg.format(build_image, spack_version, | ||||||
|  |                          ', '.join(image_data['build_tags'].keys())) | ||||||
|  |         raise ValueError(msg) | ||||||
|  | 
 | ||||||
|  |     return build_image, build_tag | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def package_info(image): | ||||||
|  |     """Returns the commands used to update system repositories, install | ||||||
|  |     system packages and clean afterwards. | ||||||
|  | 
 | ||||||
|  |     Args: | ||||||
|  |         image (str): image to be used at run-time. Should be of the form | ||||||
|  |             <image_name>:<image_tag> e.g. "ubuntu:18.04" | ||||||
|  | 
 | ||||||
|  |     Returns: | ||||||
|  |         A tuple of (update, install, clean) commands. | ||||||
|  |     """ | ||||||
|  |     image_data = data()[image] | ||||||
|  |     update = image_data['update'] | ||||||
|  |     install = image_data['install'] | ||||||
|  |     clean = image_data['clean'] | ||||||
|  |     return update, install, clean | ||||||
							
								
								
									
										154
									
								
								lib/spack/spack/container/writers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								lib/spack/spack/container/writers/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,154 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | """Writers for different kind of recipes and related | ||||||
|  | convenience functions. | ||||||
|  | """ | ||||||
|  | import collections | ||||||
|  | import copy | ||||||
|  | 
 | ||||||
|  | import spack.environment | ||||||
|  | import spack.schema.env | ||||||
|  | import spack.tengine as tengine | ||||||
|  | import spack.util.spack_yaml as syaml | ||||||
|  | 
 | ||||||
|  | from spack.container.images import build_info, package_info | ||||||
|  | 
 | ||||||
|  | #: Caches all the writers that are currently supported | ||||||
|  | _writer_factory = {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def writer(name): | ||||||
|  |     """Decorator to register a factory for a recipe writer. | ||||||
|  | 
 | ||||||
|  |     Each factory should take a configuration dictionary and return a | ||||||
|  |     properly configured writer that, when called, prints the | ||||||
|  |     corresponding recipe. | ||||||
|  |     """ | ||||||
|  |     def _decorator(factory): | ||||||
|  |         _writer_factory[name] = factory | ||||||
|  |         return factory | ||||||
|  |     return _decorator | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def create(configuration): | ||||||
|  |     """Returns a writer that conforms to the configuration passed as input. | ||||||
|  | 
 | ||||||
|  |     Args: | ||||||
|  |         configuration: how to generate the current recipe | ||||||
|  |     """ | ||||||
|  |     name = spack.environment.config_dict(configuration)['container']['format'] | ||||||
|  |     return _writer_factory[name](configuration) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def recipe(configuration): | ||||||
|  |     """Returns a recipe that conforms to the configuration passed as input. | ||||||
|  | 
 | ||||||
|  |     Args: | ||||||
|  |         configuration: how to generate the current recipe | ||||||
|  |     """ | ||||||
|  |     return create(configuration)() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class PathContext(tengine.Context): | ||||||
|  |     """Generic context used to instantiate templates of recipes that | ||||||
|  |     install software in a common location and make it available | ||||||
|  |     directly via PATH. | ||||||
|  |     """ | ||||||
|  |     def __init__(self, config): | ||||||
|  |         self.config = spack.environment.config_dict(config) | ||||||
|  |         self.container_config = self.config['container'] | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def run(self): | ||||||
|  |         """Information related to the run image.""" | ||||||
|  |         image = self.container_config['base']['image'] | ||||||
|  |         Run = collections.namedtuple('Run', ['image']) | ||||||
|  |         return Run(image=image) | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def build(self): | ||||||
|  |         """Information related to the build image.""" | ||||||
|  | 
 | ||||||
|  |         # Map the final image to the correct build image | ||||||
|  |         run_image = self.container_config['base']['image'] | ||||||
|  |         spack_version = self.container_config['base']['spack'] | ||||||
|  |         image, tag = build_info(run_image, spack_version) | ||||||
|  | 
 | ||||||
|  |         Build = collections.namedtuple('Build', ['image', 'tag']) | ||||||
|  |         return Build(image=image, tag=tag) | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def strip(self): | ||||||
|  |         """Whether or not to strip binaries in the image""" | ||||||
|  |         return self.container_config.get('strip', True) | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def paths(self): | ||||||
|  |         """Important paths in the image""" | ||||||
|  |         Paths = collections.namedtuple('Paths', [ | ||||||
|  |             'environment', 'store', 'view' | ||||||
|  |         ]) | ||||||
|  |         return Paths( | ||||||
|  |             environment='/opt/spack-environment', | ||||||
|  |             store='/opt/software', | ||||||
|  |             view='/opt/view' | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def manifest(self): | ||||||
|  |         """The spack.yaml file that should be used in the image""" | ||||||
|  |         import jsonschema | ||||||
|  |         # Copy in the part of spack.yaml prescribed in the configuration file | ||||||
|  |         manifest = copy.deepcopy(self.config) | ||||||
|  |         manifest.pop('container') | ||||||
|  | 
 | ||||||
|  |         # Ensure that a few paths are where they need to be | ||||||
|  |         manifest.setdefault('config', syaml.syaml_dict()) | ||||||
|  |         manifest['config']['install_tree'] = self.paths.store | ||||||
|  |         manifest['view'] = self.paths.view | ||||||
|  |         manifest = {'spack': manifest} | ||||||
|  | 
 | ||||||
|  |         # Validate the manifest file | ||||||
|  |         jsonschema.validate(manifest, schema=spack.schema.env.schema) | ||||||
|  | 
 | ||||||
|  |         return syaml.dump(manifest, default_flow_style=False).strip() | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def os_packages(self): | ||||||
|  |         """Additional system packages that are needed at run-time.""" | ||||||
|  |         package_list = self.container_config.get('os_packages', None) | ||||||
|  |         if not package_list: | ||||||
|  |             return package_list | ||||||
|  | 
 | ||||||
|  |         image = self.container_config['base']['image'] | ||||||
|  |         update, install, clean = package_info(image) | ||||||
|  |         Packages = collections.namedtuple( | ||||||
|  |             'Packages', ['update', 'install', 'list', 'clean'] | ||||||
|  |         ) | ||||||
|  |         return Packages(update=update, install=install, | ||||||
|  |                         list=package_list, clean=clean) | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def extra_instructions(self): | ||||||
|  |         Extras = collections.namedtuple('Extra', ['build', 'final']) | ||||||
|  |         extras = self.container_config.get('extra_instructions', {}) | ||||||
|  |         build, final = extras.get('build', None), extras.get('final', None) | ||||||
|  |         return Extras(build=build, final=final) | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def labels(self): | ||||||
|  |         return self.container_config.get('labels', {}) | ||||||
|  | 
 | ||||||
|  |     def __call__(self): | ||||||
|  |         """Returns the recipe as a string""" | ||||||
|  |         env = tengine.make_environment() | ||||||
|  |         t = env.get_template(self.template_name) | ||||||
|  |         return t.render(**self.to_dict()) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Import after function definition all the modules in this package, | ||||||
|  | # so that registration of writers will happen automatically | ||||||
|  | import spack.container.writers.singularity  # noqa | ||||||
|  | import spack.container.writers.docker  # noqa | ||||||
							
								
								
									
										30
									
								
								lib/spack/spack/container/writers/docker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								lib/spack/spack/container/writers/docker.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | import spack.tengine as tengine | ||||||
|  | 
 | ||||||
|  | from . import writer, PathContext | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @writer('docker') | ||||||
|  | class DockerContext(PathContext): | ||||||
|  |     """Context used to instantiate a Dockerfile""" | ||||||
|  |     #: Name of the template used for Dockerfiles | ||||||
|  |     template_name = 'container/Dockerfile' | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def manifest(self): | ||||||
|  |         manifest_str = super(DockerContext, self).manifest | ||||||
|  |         # Docker doesn't support HEREDOC so we need to resort to | ||||||
|  |         # a horrible echo trick to have the manifest in the Dockerfile | ||||||
|  |         echoed_lines = [] | ||||||
|  |         for idx, line in enumerate(manifest_str.split('\n')): | ||||||
|  |             if idx == 0: | ||||||
|  |                 echoed_lines.append('&&  (echo "' + line + '" \\') | ||||||
|  |                 continue | ||||||
|  |             echoed_lines.append('&&   echo "' + line + '" \\') | ||||||
|  | 
 | ||||||
|  |         echoed_lines[-1] = echoed_lines[-1].replace(' \\', ')') | ||||||
|  | 
 | ||||||
|  |         return '\n'.join(echoed_lines) | ||||||
							
								
								
									
										33
									
								
								lib/spack/spack/container/writers/singularity.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								lib/spack/spack/container/writers/singularity.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | import spack.tengine as tengine | ||||||
|  | from . import writer, PathContext | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @writer('singularity') | ||||||
|  | class SingularityContext(PathContext): | ||||||
|  |     """Context used to instantiate a Singularity definition file""" | ||||||
|  |     #: Name of the template used for Singularity definition files | ||||||
|  |     template_name = 'container/singularity.def' | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def singularity_config(self): | ||||||
|  |         return self.container_config.get('singularity', {}) | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def runscript(self): | ||||||
|  |         return self.singularity_config.get('runscript', '') | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def startscript(self): | ||||||
|  |         return self.singularity_config.get('startscript', '') | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def test(self): | ||||||
|  |         return self.singularity_config.get('test', '') | ||||||
|  | 
 | ||||||
|  |     @tengine.context_property | ||||||
|  |     def help(self): | ||||||
|  |         return self.singularity_config.get('help', '') | ||||||
							
								
								
									
										82
									
								
								lib/spack/spack/schema/container.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								lib/spack/spack/schema/container.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | """Schema for the 'container' subsection of Spack environments.""" | ||||||
|  | 
 | ||||||
|  | #: Schema for the container attribute included in Spack environments | ||||||
|  | container_schema = { | ||||||
|  |     'type': 'object', | ||||||
|  |     'additionalProperties': False, | ||||||
|  |     'properties': { | ||||||
|  |         # The recipe formats that are currently supported by the command | ||||||
|  |         'format': { | ||||||
|  |             'type': 'string', | ||||||
|  |             'enum': ['docker', 'singularity'] | ||||||
|  |         }, | ||||||
|  |         # Describes the base image to start from and the version | ||||||
|  |         # of Spack to be used | ||||||
|  |         'base': { | ||||||
|  |             'type': 'object', | ||||||
|  |             'additionalProperties': False, | ||||||
|  |             'properties': { | ||||||
|  |                 'image': { | ||||||
|  |                     'type': 'string', | ||||||
|  |                     'enum': ['ubuntu:18.04', | ||||||
|  |                              'ubuntu:16.04', | ||||||
|  |                              'centos:7', | ||||||
|  |                              'centos:6'] | ||||||
|  |                 }, | ||||||
|  |                 'spack': { | ||||||
|  |                     'type': 'string', | ||||||
|  |                     'enum': ['develop', '0.14', '0.14.0'] | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             'required': ['image', 'spack'] | ||||||
|  |         }, | ||||||
|  |         # Whether or not to strip installed binaries | ||||||
|  |         'strip': { | ||||||
|  |             'type': 'boolean', | ||||||
|  |             'default': True | ||||||
|  |         }, | ||||||
|  |         # Additional system packages that are needed at runtime | ||||||
|  |         'os_packages': { | ||||||
|  |             'type': 'array', | ||||||
|  |             'items': { | ||||||
|  |                 'type': 'string' | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         # Add labels to the image | ||||||
|  |         'labels': { | ||||||
|  |             'type': 'object', | ||||||
|  |         }, | ||||||
|  |         # Add a custom extra section at the bottom of a stage | ||||||
|  |         'extra_instructions': { | ||||||
|  |             'type': 'object', | ||||||
|  |             'additionalProperties': False, | ||||||
|  |             'properties': { | ||||||
|  |                 'build': {'type': 'string'}, | ||||||
|  |                 'final': {'type': 'string'} | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         # Reserved for properties that are specific to each format | ||||||
|  |         'singularity': { | ||||||
|  |             'type': 'object', | ||||||
|  |             'additionalProperties': False, | ||||||
|  |             'default': {}, | ||||||
|  |             'properties': { | ||||||
|  |                 'runscript': {'type': 'string'}, | ||||||
|  |                 'startscript': {'type': 'string'}, | ||||||
|  |                 'test': {'type': 'string'}, | ||||||
|  |                 'help': {'type': 'string'} | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         'docker': { | ||||||
|  |             'type': 'object', | ||||||
|  |             'additionalProperties': False, | ||||||
|  |             'default': {}, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | properties = {'container': container_schema} | ||||||
| @@ -13,6 +13,7 @@ | |||||||
| import spack.schema.cdash | import spack.schema.cdash | ||||||
| import spack.schema.compilers | import spack.schema.compilers | ||||||
| import spack.schema.config | import spack.schema.config | ||||||
|  | import spack.schema.container | ||||||
| import spack.schema.gitlab_ci | import spack.schema.gitlab_ci | ||||||
| import spack.schema.mirrors | import spack.schema.mirrors | ||||||
| import spack.schema.modules | import spack.schema.modules | ||||||
| @@ -26,6 +27,7 @@ | |||||||
|     spack.schema.cdash.properties, |     spack.schema.cdash.properties, | ||||||
|     spack.schema.compilers.properties, |     spack.schema.compilers.properties, | ||||||
|     spack.schema.config.properties, |     spack.schema.config.properties, | ||||||
|  |     spack.schema.container.properties, | ||||||
|     spack.schema.gitlab_ci.properties, |     spack.schema.gitlab_ci.properties, | ||||||
|     spack.schema.mirrors.properties, |     spack.schema.mirrors.properties, | ||||||
|     spack.schema.modules.properties, |     spack.schema.modules.properties, | ||||||
|   | |||||||
| @@ -30,7 +30,9 @@ def test_packages_are_removed(config, mutable_database, capsys): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @pytest.mark.db | @pytest.mark.db | ||||||
| def test_gc_with_environment(config, mutable_database, capsys): | def test_gc_with_environment( | ||||||
|  |         config, mutable_database, mutable_mock_env_path, capsys | ||||||
|  | ): | ||||||
|     s = spack.spec.Spec('simple-inheritance') |     s = spack.spec.Spec('simple-inheritance') | ||||||
|     s.concretize() |     s.concretize() | ||||||
|     s.package.do_install(fake=True, explicit=True) |     s.package.do_install(fake=True, explicit=True) | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| # Copyright 2013-2019 Lawrence Livermore National Security, LLC and other | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
| # Spack Project Developers. See the top-level COPYRIGHT file for details. | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
| # | # | ||||||
| # SPDX-License-Identifier: (Apache-2.0 OR MIT) | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								lib/spack/spack/test/container/cli.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/spack/spack/test/container/cli.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | import llnl.util.filesystem as fs | ||||||
|  | import spack.main | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | containerize = spack.main.SpackCommand('containerize') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_command(configuration_dir, capsys): | ||||||
|  |     with capsys.disabled(): | ||||||
|  |         with fs.working_dir(configuration_dir): | ||||||
|  |             output = containerize() | ||||||
|  |     assert 'FROM spack/ubuntu-bionic' in output | ||||||
							
								
								
									
										43
									
								
								lib/spack/spack/test/container/conftest.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								lib/spack/spack/test/container/conftest.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | import pytest | ||||||
|  | 
 | ||||||
|  | import spack.util.spack_yaml as syaml | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.fixture() | ||||||
|  | def minimal_configuration(): | ||||||
|  |     return { | ||||||
|  |         'spack': { | ||||||
|  |             'specs': [ | ||||||
|  |                 'gromacs', | ||||||
|  |                 'mpich', | ||||||
|  |                 'fftw precision=float' | ||||||
|  |             ], | ||||||
|  |             'container': { | ||||||
|  |                 'format': 'docker', | ||||||
|  |                 'base': { | ||||||
|  |                     'image': 'ubuntu:18.04', | ||||||
|  |                     'spack': 'develop' | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.fixture() | ||||||
|  | def config_dumper(tmpdir): | ||||||
|  |     """Function that dumps an environment config in a temporary folder.""" | ||||||
|  |     def dumper(configuration): | ||||||
|  |         content = syaml.dump(configuration, default_flow_style=False) | ||||||
|  |         config_file = tmpdir / 'spack.yaml' | ||||||
|  |         config_file.write(content) | ||||||
|  |         return str(tmpdir) | ||||||
|  |     return dumper | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.fixture() | ||||||
|  | def configuration_dir(minimal_configuration, config_dumper): | ||||||
|  |     return config_dumper(minimal_configuration) | ||||||
							
								
								
									
										74
									
								
								lib/spack/spack/test/container/docker.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								lib/spack/spack/test/container/docker.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | 
 | ||||||
|  | import spack.container.writers as writers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_manifest(minimal_configuration): | ||||||
|  |     writer = writers.create(minimal_configuration) | ||||||
|  |     manifest_str = writer.manifest | ||||||
|  |     for line in manifest_str.split('\n'): | ||||||
|  |         assert 'echo' in line | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_build_and_run_images(minimal_configuration): | ||||||
|  |     writer = writers.create(minimal_configuration) | ||||||
|  | 
 | ||||||
|  |     # Test the output of run property | ||||||
|  |     run = writer.run | ||||||
|  |     assert run.image == 'ubuntu:18.04' | ||||||
|  | 
 | ||||||
|  |     # Test the output of the build property | ||||||
|  |     build = writer.build | ||||||
|  |     assert build.image == 'spack/ubuntu-bionic' | ||||||
|  |     assert build.tag == 'latest' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_packages(minimal_configuration): | ||||||
|  |     # In this minimal configuration we don't have packages | ||||||
|  |     writer = writers.create(minimal_configuration) | ||||||
|  |     assert writer.os_packages is None | ||||||
|  | 
 | ||||||
|  |     # If we add them a list should be returned | ||||||
|  |     pkgs = ['libgomp1'] | ||||||
|  |     minimal_configuration['spack']['container']['os_packages'] = pkgs | ||||||
|  |     writer = writers.create(minimal_configuration) | ||||||
|  |     p = writer.os_packages | ||||||
|  |     assert p.update | ||||||
|  |     assert p.install | ||||||
|  |     assert p.clean | ||||||
|  |     assert p.list == pkgs | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_ensure_render_works(minimal_configuration): | ||||||
|  |     # Here we just want to ensure that nothing is raised | ||||||
|  |     writer = writers.create(minimal_configuration) | ||||||
|  |     writer() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_strip_is_set_from_config(minimal_configuration): | ||||||
|  |     writer = writers.create(minimal_configuration) | ||||||
|  |     assert writer.strip is True | ||||||
|  | 
 | ||||||
|  |     minimal_configuration['spack']['container']['strip'] = False | ||||||
|  |     writer = writers.create(minimal_configuration) | ||||||
|  |     assert writer.strip is False | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_extra_instructions_is_set_from_config(minimal_configuration): | ||||||
|  |     writer = writers.create(minimal_configuration) | ||||||
|  |     assert writer.extra_instructions == (None, None) | ||||||
|  | 
 | ||||||
|  |     test_line = 'RUN echo Hello world!' | ||||||
|  |     e = minimal_configuration['spack']['container'] | ||||||
|  |     e['extra_instructions'] = {} | ||||||
|  |     e['extra_instructions']['build'] = test_line | ||||||
|  |     writer = writers.create(minimal_configuration) | ||||||
|  |     assert writer.extra_instructions == (test_line, None) | ||||||
|  | 
 | ||||||
|  |     e['extra_instructions']['final'] = test_line | ||||||
|  |     del e['extra_instructions']['build'] | ||||||
|  |     writer = writers.create(minimal_configuration) | ||||||
|  |     assert writer.extra_instructions == (None, test_line) | ||||||
							
								
								
									
										58
									
								
								lib/spack/spack/test/container/images.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								lib/spack/spack/test/container/images.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | import os.path | ||||||
|  | 
 | ||||||
|  | import pytest | ||||||
|  | 
 | ||||||
|  | import spack.container | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.parametrize('image,spack_version,expected', [ | ||||||
|  |     ('ubuntu:18.04', 'develop', ('spack/ubuntu-bionic', 'latest')), | ||||||
|  |     ('ubuntu:18.04', '0.14.0', ('spack/ubuntu-bionic', '0.14.0')), | ||||||
|  | ]) | ||||||
|  | def test_build_info(image, spack_version, expected): | ||||||
|  |     output = spack.container.images.build_info(image, spack_version) | ||||||
|  |     assert output == expected | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.parametrize('image,spack_version', [ | ||||||
|  |     ('ubuntu:18.04', 'doesnotexist') | ||||||
|  | ]) | ||||||
|  | def test_build_info_error(image, spack_version): | ||||||
|  |     with pytest.raises(ValueError, match=r"has no tag for"): | ||||||
|  |         spack.container.images.build_info(image, spack_version) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.parametrize('image', [ | ||||||
|  |     'ubuntu:18.04' | ||||||
|  | ]) | ||||||
|  | def test_package_info(image): | ||||||
|  |     update, install, clean = spack.container.images.package_info(image) | ||||||
|  |     assert update | ||||||
|  |     assert install | ||||||
|  |     assert clean | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.parametrize('extra_config,expected_msg', [ | ||||||
|  |     ({'modules': {'enable': ['tcl']}}, 'the subsection "modules" in'), | ||||||
|  |     ({'concretization': 'separately'}, 'the "concretization" attribute'), | ||||||
|  |     ({'config': {'install_tree': '/some/dir'}}, | ||||||
|  |      'the "config:install_tree" attribute has been set'), | ||||||
|  |     ({'view': '/some/dir'}, 'the "view" attribute has been set') | ||||||
|  | ]) | ||||||
|  | def test_validate( | ||||||
|  |         extra_config, expected_msg, minimal_configuration, config_dumper | ||||||
|  | ): | ||||||
|  |     minimal_configuration['spack'].update(extra_config) | ||||||
|  |     spack_yaml_dir = config_dumper(minimal_configuration) | ||||||
|  |     spack_yaml = os.path.join(spack_yaml_dir, 'spack.yaml') | ||||||
|  | 
 | ||||||
|  |     with pytest.warns(UserWarning) as w: | ||||||
|  |         spack.container.validate(spack_yaml) | ||||||
|  | 
 | ||||||
|  |     # Tests are designed to raise only one warning | ||||||
|  |     assert len(w) == 1 | ||||||
|  |     assert expected_msg in str(w.pop().message) | ||||||
							
								
								
									
										16
									
								
								lib/spack/spack/test/container/schema.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/spack/spack/test/container/schema.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | 
 | ||||||
|  | import spack.container | ||||||
|  | import spack.schema.container | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_images_in_schema(): | ||||||
|  |     properties = spack.schema.container.container_schema['properties'] | ||||||
|  |     allowed_images = set( | ||||||
|  |         properties['base']['properties']['image']['enum'] | ||||||
|  |     ) | ||||||
|  |     images_in_json = set(x for x in spack.container.images.data()) | ||||||
|  |     assert images_in_json == allowed_images | ||||||
							
								
								
									
										42
									
								
								lib/spack/spack/test/container/singularity.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								lib/spack/spack/test/container/singularity.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | # Copyright 2013-2020 Lawrence Livermore National Security, LLC and other | ||||||
|  | # Spack Project Developers. See the top-level COPYRIGHT file for details. | ||||||
|  | # | ||||||
|  | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  | import pytest | ||||||
|  | 
 | ||||||
|  | import spack.container.writers as writers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.fixture | ||||||
|  | def singularity_configuration(minimal_configuration): | ||||||
|  |     minimal_configuration['spack']['container']['format'] = 'singularity' | ||||||
|  |     return minimal_configuration | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_ensure_render_works(singularity_configuration): | ||||||
|  |     container_config = singularity_configuration['spack']['container'] | ||||||
|  |     assert container_config['format'] == 'singularity' | ||||||
|  |     # Here we just want to ensure that nothing is raised | ||||||
|  |     writer = writers.create(singularity_configuration) | ||||||
|  |     writer() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.parametrize('properties,expected', [ | ||||||
|  |     ({'runscript': '/opt/view/bin/h5ls'}, | ||||||
|  |      {'runscript': '/opt/view/bin/h5ls', | ||||||
|  |       'startscript': '', | ||||||
|  |       'test': '', | ||||||
|  |       'help': ''}) | ||||||
|  | ]) | ||||||
|  | def test_singularity_specific_properties( | ||||||
|  |         properties, expected, singularity_configuration | ||||||
|  | ): | ||||||
|  |     # Set the property in the configuration | ||||||
|  |     container_config = singularity_configuration['spack']['container'] | ||||||
|  |     for name, value in properties.items(): | ||||||
|  |         container_config.setdefault('singularity', {})[name] = value | ||||||
|  | 
 | ||||||
|  |     # Assert the properties return the expected values | ||||||
|  |     writer = writers.create(singularity_configuration) | ||||||
|  |     for name, value in expected.items(): | ||||||
|  |         assert getattr(writer, name) == value | ||||||
| @@ -313,7 +313,7 @@ _spack() { | |||||||
|     then |     then | ||||||
|         SPACK_COMPREPLY="-h --help -H --all-help --color -C --config-scope -d --debug --timestamp --pdb -e --env -D --env-dir -E --no-env --use-env-repo -k --insecure -l --enable-locks -L --disable-locks -m --mock -p --profile --sorted-profile --lines -v --verbose --stacktrace -V --version --print-shell-vars" |         SPACK_COMPREPLY="-h --help -H --all-help --color -C --config-scope -d --debug --timestamp --pdb -e --env -D --env-dir -E --no-env --use-env-repo -k --insecure -l --enable-locks -L --disable-locks -m --mock -p --profile --sorted-profile --lines -v --verbose --stacktrace -V --version --print-shell-vars" | ||||||
|     else |     else | ||||||
|         SPACK_COMPREPLY="activate add arch blame bootstrap build build-env buildcache cd checksum ci clean clone commands compiler compilers concretize config configure create deactivate debug dependencies dependents deprecate dev-build diy docs edit env extensions fetch find flake8 gc gpg graph help info install license list load location log-parse maintainers mirror module patch pkg providers pydoc python reindex remove rm repo resource restage setup spec stage test uninstall unload upload-s3 url verify versions view" |         SPACK_COMPREPLY="activate add arch blame bootstrap build build-env buildcache cd checksum ci clean clone commands compiler compilers concretize config configure containerize create deactivate debug dependencies dependents deprecate dev-build diy docs edit env extensions fetch find flake8 gc gpg graph help info install license list load location log-parse maintainers mirror module patch pkg providers pydoc python reindex remove rm repo resource restage setup spec stage test uninstall unload upload-s3 url verify versions view" | ||||||
|     fi |     fi | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -628,6 +628,10 @@ _spack_configure() { | |||||||
|     fi |     fi | ||||||
| } | } | ||||||
|  |  | ||||||
|  | _spack_containerize() { | ||||||
|  |     SPACK_COMPREPLY="-h --help" | ||||||
|  | } | ||||||
|  |  | ||||||
| _spack_create() { | _spack_create() { | ||||||
|     if $list_options |     if $list_options | ||||||
|     then |     then | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								share/spack/templates/container/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								share/spack/templates/container/Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | # Build stage with Spack pre-installed and ready to be used | ||||||
|  | FROM {{ build.image }}:{{ build.tag }} as builder | ||||||
|  |  | ||||||
|  | # What we want to install and how we want to install it | ||||||
|  | # is specified in a manifest file (spack.yaml) | ||||||
|  | RUN mkdir {{ paths.environment }} \ | ||||||
|  | {{ manifest }} > {{ paths.environment }}/spack.yaml | ||||||
|  |  | ||||||
|  | # Install the software, remove unecessary deps | ||||||
|  | RUN cd {{ paths.environment }} && spack install && spack gc -y | ||||||
|  | {% if strip %} | ||||||
|  |  | ||||||
|  | # Strip all the binaries | ||||||
|  | RUN find -L {{ paths.view }}/* -type f -exec readlink -f '{}' \; | \ | ||||||
|  |     xargs file -i | \ | ||||||
|  |     grep 'charset=binary' | \ | ||||||
|  |     grep 'x-executable\|x-archive\|x-sharedlib' | \ | ||||||
|  |     awk -F: '{print $1}' | xargs strip -s | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | # Modifications to the environment that are necessary to run | ||||||
|  | RUN cd {{ paths.environment }} && \ | ||||||
|  |     spack env activate --sh -d . >> /etc/profile.d/z10_spack_environment.sh | ||||||
|  |  | ||||||
|  | {% if extra_instructions.build %} | ||||||
|  | {{ extra_instructions.build }} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | # Bare OS image to run the installed executables | ||||||
|  | FROM {{ run.image }} | ||||||
|  |  | ||||||
|  | COPY --from=builder {{ paths.environment }} {{ paths.environment }} | ||||||
|  | COPY --from=builder {{ paths.store }} {{ paths.store }} | ||||||
|  | COPY --from=builder {{ paths.view }} {{ paths.view }} | ||||||
|  | COPY --from=builder /etc/profile.d/z10_spack_environment.sh /etc/profile.d/z10_spack_environment.sh | ||||||
|  |  | ||||||
|  | {% if os_packages %} | ||||||
|  | RUN {{ os_packages.update }}                                   \ | ||||||
|  |  && {{ os_packages.install }}{% for pkg in os_packages.list %} {{ pkg }}{% endfor %} \ | ||||||
|  |  && {{ os_packages.clean }} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | {% if extra_instructions.final %} | ||||||
|  | {{ extra_instructions.final }} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | {% for label, value in labels.items() %} | ||||||
|  | LABEL "{{ label }}"="{{ value }}" | ||||||
|  | {% endfor %} | ||||||
|  |  | ||||||
|  | ENTRYPOINT ["/bin/bash", "--rcfile", "/etc/profile", "-l"] | ||||||
							
								
								
									
										90
									
								
								share/spack/templates/container/singularity.def
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								share/spack/templates/container/singularity.def
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | |||||||
|  | Bootstrap: docker | ||||||
|  | From: {{ build.image }}:{{ build.tag }} | ||||||
|  | Stage: build | ||||||
|  |  | ||||||
|  | %post | ||||||
|  |   # Create the manifest file for the installation in /opt/spack-environment | ||||||
|  |   mkdir {{ paths.environment }} && cd {{ paths.environment }} | ||||||
|  |   cat << EOF > spack.yaml | ||||||
|  | {{ manifest }} | ||||||
|  | EOF | ||||||
|  |  | ||||||
|  |   # Install all the required software | ||||||
|  |   . /opt/spack/share/spack/setup-env.sh | ||||||
|  |   spack install | ||||||
|  |   spack gc -y | ||||||
|  |   spack env activate --sh -d . >> {{ paths.environment }}/environment_modifications.sh | ||||||
|  | {% if strip %} | ||||||
|  |  | ||||||
|  |   # Strip the binaries to reduce the size of the image | ||||||
|  |   find -L {{ paths.view }}/* -type f -exec readlink -f '{}' \; | \ | ||||||
|  |     xargs file -i | \ | ||||||
|  |     grep 'charset=binary' | \ | ||||||
|  |     grep 'x-executable\|x-archive\|x-sharedlib' | \ | ||||||
|  |     awk -F: '{print $1}' | xargs strip -s | ||||||
|  | {% endif %} | ||||||
|  | {% if extra_instructions.build %} | ||||||
|  | {{ extra_instructions.build }} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | {% if apps %} | ||||||
|  | {% for application, help_text in apps.items() %} | ||||||
|  |  | ||||||
|  | %apprun {{ application }} | ||||||
|  |     exec /opt/view/bin/{{ application }} "$@" | ||||||
|  |  | ||||||
|  | %apphelp {{ application }} | ||||||
|  |     {{help_text }} | ||||||
|  | {% endfor %} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | Bootstrap: docker | ||||||
|  | From: {{ run.image }} | ||||||
|  | Stage: final | ||||||
|  |  | ||||||
|  | %files from build | ||||||
|  |   {{ paths.environment }} /opt | ||||||
|  |   {{ paths.store }} /opt | ||||||
|  |   {{ paths.view }} /opt | ||||||
|  |   {{ paths.environment }}/environment_modifications.sh {{ paths.environment }}/environment_modifications.sh | ||||||
|  |  | ||||||
|  | %post | ||||||
|  | {% if os_packages.list %} | ||||||
|  |   # Update, install and cleanup of system packages | ||||||
|  |   {{ os_packages.update }} | ||||||
|  |   {{ os_packages.install }} {{ os_packages.list | join | replace('\n', ' ') }} | ||||||
|  |   {{ os_packages.clean }} | ||||||
|  | {% endif %} | ||||||
|  |   # Modify the environment without relying on sourcing shell specific files at startup | ||||||
|  |   cat {{ paths.environment }}/environment_modifications.sh >> $SINGULARITY_ENVIRONMENT | ||||||
|  | {% if extra_instructions.final %} | ||||||
|  | {{ extra_instructions.final }} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | {% if runscript %} | ||||||
|  | %runscript | ||||||
|  | {{ runscript }} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | {% if startscript %} | ||||||
|  | %startscript | ||||||
|  | {{ startscript }} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | {% if test %} | ||||||
|  | %test | ||||||
|  | {{ test }} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | {% if help %} | ||||||
|  | %help | ||||||
|  | {{ help }} | ||||||
|  | {% endif %} | ||||||
|  |  | ||||||
|  | {% if labels %} | ||||||
|  | %labels | ||||||
|  | {% for label, value in labels.items() %} | ||||||
|  |   {{ label }} {{ value }} | ||||||
|  | {% endfor %} | ||||||
|  | {% endif %} | ||||||
		Reference in New Issue
	
	Block a user