├── .circleci └── config.yml ├── .docs ├── Makefile ├── apidoc.sh ├── changelog.md ├── conf.py ├── index.rst └── source │ ├── modules.rst │ ├── scompose.client.rst │ ├── scompose.logger.rst │ ├── scompose.project.rst │ ├── scompose.rst │ ├── scompose.templates.rst │ ├── scompose.tests.rst │ ├── scompose.tests.testdata.rst │ └── scompose.utils.rst ├── .github ├── FUNDING.yml ├── dev-requirements.txt └── workflows │ └── main.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .pylintrc ├── CHANGELOG.md ├── CITATION.cff ├── LICENSE ├── MANIFEST.in ├── README.md ├── docs ├── .nojekyll ├── README.md ├── _coverpage.md ├── api │ ├── _sources │ │ ├── changelog.md.txt │ │ ├── index.rst.txt │ │ └── source │ │ │ ├── modules.rst.txt │ │ │ ├── scompose.client.rst.txt │ │ │ ├── scompose.logger.rst.txt │ │ │ ├── scompose.project.rst.txt │ │ │ ├── scompose.rst.txt │ │ │ ├── scompose.templates.rst.txt │ │ │ ├── scompose.tests.rst.txt │ │ │ ├── scompose.tests.testdata.rst.txt │ │ │ └── scompose.utils.rst.txt │ ├── assets │ │ ├── basic.css │ │ ├── css │ │ │ ├── badge_only.css │ │ │ ├── fonts │ │ │ │ ├── Roboto-Slab-Bold.woff │ │ │ │ ├── Roboto-Slab-Bold.woff2 │ │ │ │ ├── Roboto-Slab-Regular.woff │ │ │ │ ├── Roboto-Slab-Regular.woff2 │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ ├── fontawesome-webfont.woff2 │ │ │ │ ├── lato-bold-italic.woff │ │ │ │ ├── lato-bold-italic.woff2 │ │ │ │ ├── lato-bold.woff │ │ │ │ ├── lato-bold.woff2 │ │ │ │ ├── lato-normal-italic.woff │ │ │ │ ├── lato-normal-italic.woff2 │ │ │ │ ├── lato-normal.woff │ │ │ │ └── lato-normal.woff2 │ │ │ └── theme.css │ │ ├── doctools.js │ │ ├── documentation_options.js │ │ ├── file.png │ │ ├── jquery-3.5.1.js │ │ ├── jquery.js │ │ ├── js │ │ │ ├── badge_only.js │ │ │ ├── html5shiv-printshiv.min.js │ │ │ ├── html5shiv.min.js │ │ │ └── theme.js │ │ ├── language_data.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── underscore-1.13.1.js │ │ └── underscore.js │ ├── changelog.html │ ├── genindex.html │ ├── index.html │ ├── modules │ │ ├── index.html │ │ └── scompose │ │ │ ├── client.html │ │ │ ├── client │ │ │ ├── build.html │ │ │ ├── config.html │ │ │ ├── create.html │ │ │ ├── down.html │ │ │ ├── exec.html │ │ │ ├── logs.html │ │ │ ├── ps.html │ │ │ ├── restart.html │ │ │ ├── shell.html │ │ │ └── up.html │ │ │ ├── logger │ │ │ ├── message.html │ │ │ └── progress.html │ │ │ ├── project │ │ │ ├── instance.html │ │ │ └── project.html │ │ │ ├── templates.html │ │ │ └── utils.html │ ├── objects.inv │ ├── py-modindex.html │ ├── search.html │ ├── searchindex.js │ └── source │ │ ├── modules.html │ │ ├── scompose.client.html │ │ ├── scompose.html │ │ ├── scompose.logger.html │ │ ├── scompose.project.html │ │ ├── scompose.templates.html │ │ ├── scompose.tests.html │ │ ├── scompose.tests.testdata.html │ │ └── scompose.utils.html ├── commands.md ├── img │ ├── content.png │ ├── scompose.png │ ├── scompose.xcf │ └── upload.png ├── index.html └── spec │ ├── README.md │ ├── spec-1.0.md │ └── spec-2.0.md ├── paper ├── paper.bib ├── paper.md └── singularity-compose.png ├── pyproject.toml ├── scompose ├── __init__.py ├── client │ ├── __init__.py │ ├── build.py │ ├── check.py │ ├── config.py │ ├── create.py │ ├── down.py │ ├── exec.py │ ├── logs.py │ ├── ps.py │ ├── restart.py │ ├── run.py │ ├── shell.py │ └── up.py ├── config │ ├── __init__.py │ └── schema.py ├── logger │ ├── __init__.py │ ├── message.py │ └── progress.py ├── project │ ├── __init__.py │ ├── instance.py │ └── project.py ├── templates │ ├── __init__.py │ ├── hosts │ └── resolv.conf ├── tests │ ├── __init__.py │ ├── configs │ │ ├── cmd_args │ │ │ ├── Singularity │ │ │ └── singularity-compose.yml │ │ ├── config_merge │ │ │ ├── singularity-compose-1.yml │ │ │ └── singularity-compose-2.yml │ │ ├── depends_on │ │ │ ├── Singularity.first │ │ │ ├── Singularity.second │ │ │ ├── Singularity.third │ │ │ └── singularity-compose.yml │ │ └── wrong_depends_on │ │ │ ├── Singularity.first │ │ │ ├── Singularity.second │ │ │ ├── Singularity.third │ │ │ └── singularity-compose.yml │ ├── test_client.py │ ├── test_command_args.py │ ├── test_config.py │ ├── test_depends_on.py │ └── test_utils.py ├── utils │ └── __init__.py └── version.py ├── setup.cfg └── setup.py /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | # https://circleci.com/orbs/registry/orb/singularity/singularity 4 | # defaults to golang 1.17.1 and singularity 3.8.2 5 | orbs: 6 | singularity: singularity/singularity@1.0.11 7 | 8 | ################################################################################ 9 | # Workflows 10 | ################################################################################ 11 | 12 | workflows: 13 | version: 2 14 | test: 15 | jobs: 16 | - run-scompose-ci-tests: 17 | filters: 18 | branches: 19 | ignore: master 20 | 21 | executors: 22 | ubuntu-machine: 23 | machine: 24 | image: ubuntu-2004:202107-02 25 | 26 | ################################################################################ 27 | # Functions 28 | ################################################################################ 29 | 30 | install_scompose: &install_scompose 31 | name: Install Singularity Compose 32 | command: | 33 | cd ~/repo 34 | pip install .[all] 35 | 36 | test_scompose: &test_scompose 37 | name: Test Singularity Compose 38 | command: | 39 | cd ~/repo 40 | pip install .[all] 41 | pytest -sv scompose/tests/test_depends_on.py 42 | pytest -sv scompose/tests/test_client.py 43 | pytest -sv scompose/tests/test_utils.py 44 | pytest -sv scompose/tests/test_command_args.py 45 | pytest -sv scompose/tests/test_config.py 46 | 47 | install_dependencies: &install_dependencies 48 | name: Install Python dependencies 49 | command: | 50 | PYTHON_VERSION=3 51 | CONDA_PATH="$HOME/conda/Python${PYTHON_VERSION}" 52 | echo 'export PATH="'"$CONDA_PATH"'/bin:$PATH"' >> "$BASH_ENV" 53 | source "$BASH_ENV" 54 | if [ ! -d "$CONDA_PATH" ]; then 55 | CONDA_SCRIPT=Miniconda${PYTHON_VERSION}-latest-Linux-x86_64.sh 56 | wget https://repo.anaconda.com/miniconda/$CONDA_SCRIPT 57 | /bin/bash $CONDA_SCRIPT -b -p $CONDA_PATH 58 | else 59 | echo "Miniconda is already installed, continuing to build." 60 | fi 61 | python --version 62 | [ $(python -c'import sys;print(sys.version_info.major)') -eq $PYTHON_VERSION ] 63 | pip install --upgrade pytest setuptools 64 | 65 | ################################################################################ 66 | # Jobs 67 | ################################################################################ 68 | 69 | jobs: 70 | run-scompose-ci-tests: 71 | executor: "ubuntu-machine" 72 | working_directory: ~/repo 73 | steps: 74 | - checkout 75 | - restore_cache: 76 | keys: v3-dependencies 77 | - run: *install_dependencies 78 | - singularity/install-go 79 | - singularity/debian-install-3: 80 | singularity-version: 3.8.3 81 | - save_cache: 82 | paths: 83 | - ~/conda 84 | key: v3-dependencies 85 | - run: *install_scompose 86 | - run: *test_scompose 87 | -------------------------------------------------------------------------------- /.docs/apidoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # If the modules changed, the content of "source" should be backed up and 3 | # new files generated (to update) by doing: 4 | # 5 | # sphinx-apidoc -o source/ ../scompose 6 | 7 | HERE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 8 | BASE=$(dirname $HERE) 9 | cd $HERE 10 | cd $BASE && python setup.py install && cd $HERE && make html 11 | rm -rf $BASE/docs/api 12 | 13 | # Create new folders 14 | mkdir -p $BASE/docs/api 15 | 16 | # Rename folders 17 | find $HERE/_build/html -exec sed -i -e 's/_static/assets/g' {} \; 18 | find $HERE/_build/html -exec sed -i -e 's/_modules/modules/g' {} \; 19 | 20 | # Copy to new locations 21 | mv $HERE/_build/html/_static $BASE/docs/api/assets 22 | mv $HERE/_build/html/_modules $BASE/docs/api/modules 23 | cp -R $HERE/_build/html/* $BASE/docs/api 24 | -------------------------------------------------------------------------------- /.docs/changelog.md: -------------------------------------------------------------------------------- 1 | ../CHANGELOG.md 2 | -------------------------------------------------------------------------------- /.docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to Singularity Compose API's documentation! 2 | =================================================== 3 | 4 | Singularity Compose is an orchestration tool for Singularity instances. 5 | 6 | Contents: 7 | 8 | .. toctree:: 9 | :maxdepth: 3 10 | 11 | source/scompose.rst 12 | changelog.md 13 | 14 | 15 | Indices and tables 16 | ------------------ 17 | 18 | * :ref:`modindex` 19 | -------------------------------------------------------------------------------- /.docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | singularity-compose 2 | =================== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | scompose 8 | -------------------------------------------------------------------------------- /.docs/source/scompose.client.rst: -------------------------------------------------------------------------------- 1 | scompose.client package 2 | ======================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | scompose.client.build module 8 | ---------------------------- 9 | 10 | .. automodule:: scompose.client.build 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | scompose.client.config module 16 | ----------------------------- 17 | 18 | .. automodule:: scompose.client.config 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | scompose.client.create module 24 | ----------------------------- 25 | 26 | .. automodule:: scompose.client.create 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | scompose.client.down module 32 | --------------------------- 33 | 34 | .. automodule:: scompose.client.down 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | scompose.client.exec module 40 | --------------------------- 41 | 42 | .. automodule:: scompose.client.exec 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | scompose.client.logs module 48 | --------------------------- 49 | 50 | .. automodule:: scompose.client.logs 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | scompose.client.ps module 56 | ------------------------- 57 | 58 | .. automodule:: scompose.client.ps 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | scompose.client.restart module 64 | ------------------------------ 65 | 66 | .. automodule:: scompose.client.restart 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | scompose.client.shell module 72 | ---------------------------- 73 | 74 | .. automodule:: scompose.client.shell 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | 79 | scompose.client.up module 80 | ------------------------- 81 | 82 | .. automodule:: scompose.client.up 83 | :members: 84 | :undoc-members: 85 | :show-inheritance: 86 | 87 | 88 | Module contents 89 | --------------- 90 | 91 | .. automodule:: scompose.client 92 | :members: 93 | :undoc-members: 94 | :show-inheritance: 95 | -------------------------------------------------------------------------------- /.docs/source/scompose.logger.rst: -------------------------------------------------------------------------------- 1 | scompose.logger package 2 | ======================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | scompose.logger.message module 8 | ------------------------------ 9 | 10 | .. automodule:: scompose.logger.message 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | scompose.logger.progress module 16 | ------------------------------- 17 | 18 | .. automodule:: scompose.logger.progress 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: scompose.logger 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /.docs/source/scompose.project.rst: -------------------------------------------------------------------------------- 1 | scompose.project package 2 | ======================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | scompose.project.instance module 8 | -------------------------------- 9 | 10 | .. automodule:: scompose.project.instance 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | scompose.project.project module 16 | ------------------------------- 17 | 18 | .. automodule:: scompose.project.project 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: scompose.project 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /.docs/source/scompose.rst: -------------------------------------------------------------------------------- 1 | scompose package 2 | ================ 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | scompose.client 10 | scompose.logger 11 | scompose.project 12 | scompose.templates 13 | scompose.tests 14 | scompose.utils 15 | 16 | Submodules 17 | ---------- 18 | 19 | scompose.version module 20 | ----------------------- 21 | 22 | .. automodule:: scompose.version 23 | :members: 24 | :undoc-members: 25 | :show-inheritance: 26 | 27 | 28 | Module contents 29 | --------------- 30 | 31 | .. automodule:: scompose 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | -------------------------------------------------------------------------------- /.docs/source/scompose.templates.rst: -------------------------------------------------------------------------------- 1 | scompose.templates package 2 | ========================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: scompose.templates 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /.docs/source/scompose.tests.rst: -------------------------------------------------------------------------------- 1 | scompose.tests package 2 | ====================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | scompose.tests.testdata 10 | 11 | Submodules 12 | ---------- 13 | 14 | scompose.tests.test\_client module 15 | ---------------------------------- 16 | 17 | .. automodule:: scompose.tests.test_client 18 | :members: 19 | :undoc-members: 20 | :show-inheritance: 21 | 22 | scompose.tests.test\_utils module 23 | --------------------------------- 24 | 25 | .. automodule:: scompose.tests.test_utils 26 | :members: 27 | :undoc-members: 28 | :show-inheritance: 29 | 30 | 31 | Module contents 32 | --------------- 33 | 34 | .. automodule:: scompose.tests 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | -------------------------------------------------------------------------------- /.docs/source/scompose.tests.testdata.rst: -------------------------------------------------------------------------------- 1 | scompose.tests.testdata package 2 | =============================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: scompose.tests.testdata 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /.docs/source/scompose.utils.rst: -------------------------------------------------------------------------------- 1 | scompose.utils package 2 | ====================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: scompose.utils 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: vsoch 2 | -------------------------------------------------------------------------------- /.github/dev-requirements.txt: -------------------------------------------------------------------------------- 1 | pre-commit 2 | black==23.3.0 3 | isort 4 | flake8 5 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: singularity-compose ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches_ignore: [] 9 | 10 | jobs: 11 | tests: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: singularityhub/install-singularity@main 16 | - name: Install dependencies 17 | run: | 18 | python -m pip install --upgrade pip 19 | pip install pytest semver pytest-runner requests 20 | pip install .[all] 21 | 22 | - name: Run unit tests 23 | run: | 24 | which singularity 25 | sudo cp /usr/local/bin/singularity /usr/bin/singularity 26 | pytest -sv scompose/tests/test_depends_on.py 27 | pytest -sv scompose/tests/test_client.py 28 | pytest -sv scompose/tests/test_utils.py 29 | pytest -sv scompose/tests/test_config.py 30 | 31 | formatting: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v4 35 | 36 | - name: Setup black environment 37 | run: conda create --quiet --name black 38 | 39 | - name: Check Spelling 40 | uses: crate-ci/typos@7ad296c72fa8265059cc03d1eda562fbdfcd6df2 # v1.9.0 41 | with: 42 | files: ./docs/*.md ./docs/*/*.md ./README.md .docs/*.rst .docs/*/*.rst 43 | 44 | - name: Lint and format Python code 45 | run: | 46 | export PATH="/usr/share/miniconda/bin:$PATH" 47 | source activate black 48 | pip install -r .github/dev-requirements.txt 49 | pre-commit run --all-files 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | env 2 | .eggs 3 | .docs/_build 4 | dist 5 | build 6 | __pycache__ 7 | singularity_compose.egg-info/ 8 | *.sif 9 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: ".all-contributorsrc" 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v4.3.0 5 | hooks: 6 | - id: check-added-large-files 7 | - id: check-case-conflict 8 | - id: check-docstring-first 9 | - id: end-of-file-fixer 10 | - id: trailing-whitespace 11 | - id: mixed-line-ending 12 | 13 | - repo: local 14 | hooks: 15 | - id: black 16 | name: black 17 | language: python 18 | types: [python] 19 | entry: black 20 | 21 | - id: isort 22 | name: isort 23 | args: [--filter-files] 24 | language: python 25 | types: [python] 26 | entry: isort 27 | 28 | - id: flake8 29 | name: flake8 30 | language: python 31 | types: [python] 32 | entry: flake8 33 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | This is a manually generated log to track changes to the repository for each release. 4 | Each section should include general headers such as **Implemented enhancements** 5 | and **Merged pull requests**. Critical items to know are: 6 | 7 | - renamed commands 8 | - deprecated / removed commands 9 | - changed defaults 10 | - backward incompatible changes 11 | - migration guidance 12 | - changed behaviour 13 | 14 | The versions coincide with releases on pypi. 15 | 16 | ## [0.1.x](https://github.com/singularityhub/singularity-compose/tree/master) (0.1.x) 17 | - support for custom network type (0.1.19) 18 | - fix 'bridge' option for 'up' command (0.1.18) 19 | - add support for instance replicas (0.1.17) 20 | - fix check command validation (0.1.16) 21 | - fix a bug triggered when using startoptions in conjunction with network=false (0.1.15) 22 | - bind volumes can handle tilde expansion (0.1.14) 23 | - fix module import used by check command (0.1.13) 24 | - adding jsonschema validation and check command (0.1.12) 25 | - implement configuration override feature 26 | - implement `--preview` argument for the `check` command 27 | - add network->enable option on composer file (0.1.11) 28 | - add network->allocate_ip option on composer file (0.1.10) 29 | - version 2.0 of the spec with added fakeroot network, start, exec, and run options (0.1.0) 30 | - stop option added (equivalent functionality to down) 31 | - spython version 0.1.0 with Singularity 3.x or greater required 32 | 33 | ## [0.0.x](https://github.com/singularityhub/singularity-compose/tree/master) (0.0.x) 34 | - removed check for sudo when adding network flags (0.0.21) 35 | - singularity-compose down supporting timeout (0.0.20) 36 | - command, ability to associate arguments to the instance's startscript (0.0.19) 37 | - depends\_on, check circular dependencies at startup and shutdown in reverse order (0.0.18) 38 | - resolv.conf, etc.hosts generated if needed, network disabled non-sudo users (0.0.17) 39 | - resolv.conf needs to bind by default (0.0.16) 40 | - adding run command (0.0.15) 41 | - ensuring that builds are streamed (0.0.14) 42 | - adding more build options to build as build-flags (0.0.13) 43 | - when not using sudo, need to set --network=none, and catching exec error (0.0.12) 44 | - pyaml version should be for newer kind, and still check for attribute (0.0.11) 45 | - alpha release with simple (single container) working example (0.0.1) 46 | - dummy release (0.0.0) 47 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.1.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: Sochat 5 | given-names: Vanessa 6 | orcid: https://orcid.org/0000-0002-4387-3819 7 | title: "Singularity Compose: Orchestration for Singularity Instances" 8 | version: 0.1.0 9 | doi: 10.21105/joss.01578 10 | date-released: 2019-08-26 11 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md LICENSE 2 | recursive-include scompose * 3 | graft scompose 4 | global-exclude __pycache__ 5 | global-exclude *.py[co] 6 | global-exclude *.sif 7 | global-exclude *.simg 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Singularity Compose 2 | 3 | [![status](http://joss.theoj.org/papers/1fc2593b11b5e18df12efb59ed8757a0/status.svg)](http://joss.theoj.org/papers/1fc2593b11b5e18df12efb59ed8757a0) 4 | [![DOI](https://zenodo.org/badge/188852712.svg)](https://zenodo.org/badge/latestdoi/188852712) 5 | 6 | This is a simple orchestration library for Singularity containers, akin to 7 | Docker Compose. See the [specification](https://singularityhub.github.io/singularity-compose/#/spec/spec-1.0) 8 | and the [documentation](https://singularityhub.github.io/singularity-compose) for 9 | details, or more examples below. 10 | 11 | ## Examples 12 | 13 | See our [Singularity Compose Examples](https://www.github.com/singularityhub/singularity-compose-examples) repository for 14 | finding or contributing examples for using scompose. 15 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/.nojekyll -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | ![logo](img/scompose.png) 2 | 3 | # Singularity Compose docs 4 | 5 | > Singularity Compose for simple container orchestration 6 | 7 | - Definition of services 8 | - Interaction with instances 9 | - Logging and Networking 10 | 11 | 12 | 32 | 33 | [GitHub](https://github.com/singularityhub/singularity-compose) 34 | [Get Started](#singularity-compose) 35 | -------------------------------------------------------------------------------- /docs/api/_sources/changelog.md.txt: -------------------------------------------------------------------------------- 1 | ../CHANGELOG.md 2 | -------------------------------------------------------------------------------- /docs/api/_sources/index.rst.txt: -------------------------------------------------------------------------------- 1 | Welcome to Singularity Compose API's documentation! 2 | =================================================== 3 | 4 | Singularity Compose is an orchestration tool for Singularity instances. 5 | 6 | Contents: 7 | 8 | .. toctree:: 9 | :maxdepth: 3 10 | 11 | source/scompose.rst 12 | changelog.md 13 | 14 | 15 | Indices and tables 16 | ------------------ 17 | 18 | * :ref:`modindex` 19 | -------------------------------------------------------------------------------- /docs/api/_sources/source/modules.rst.txt: -------------------------------------------------------------------------------- 1 | singularity-compose 2 | =================== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | scompose 8 | -------------------------------------------------------------------------------- /docs/api/_sources/source/scompose.client.rst.txt: -------------------------------------------------------------------------------- 1 | scompose.client package 2 | ======================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | scompose.client.build module 8 | ---------------------------- 9 | 10 | .. automodule:: scompose.client.build 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | scompose.client.config module 16 | ----------------------------- 17 | 18 | .. automodule:: scompose.client.config 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | scompose.client.create module 24 | ----------------------------- 25 | 26 | .. automodule:: scompose.client.create 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | scompose.client.down module 32 | --------------------------- 33 | 34 | .. automodule:: scompose.client.down 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | scompose.client.exec module 40 | --------------------------- 41 | 42 | .. automodule:: scompose.client.exec 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | scompose.client.logs module 48 | --------------------------- 49 | 50 | .. automodule:: scompose.client.logs 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | scompose.client.ps module 56 | ------------------------- 57 | 58 | .. automodule:: scompose.client.ps 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | scompose.client.restart module 64 | ------------------------------ 65 | 66 | .. automodule:: scompose.client.restart 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | scompose.client.shell module 72 | ---------------------------- 73 | 74 | .. automodule:: scompose.client.shell 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | 79 | scompose.client.up module 80 | ------------------------- 81 | 82 | .. automodule:: scompose.client.up 83 | :members: 84 | :undoc-members: 85 | :show-inheritance: 86 | 87 | 88 | Module contents 89 | --------------- 90 | 91 | .. automodule:: scompose.client 92 | :members: 93 | :undoc-members: 94 | :show-inheritance: 95 | -------------------------------------------------------------------------------- /docs/api/_sources/source/scompose.logger.rst.txt: -------------------------------------------------------------------------------- 1 | scompose.logger package 2 | ======================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | scompose.logger.message module 8 | ------------------------------ 9 | 10 | .. automodule:: scompose.logger.message 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | scompose.logger.progress module 16 | ------------------------------- 17 | 18 | .. automodule:: scompose.logger.progress 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: scompose.logger 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/api/_sources/source/scompose.project.rst.txt: -------------------------------------------------------------------------------- 1 | scompose.project package 2 | ======================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | scompose.project.instance module 8 | -------------------------------- 9 | 10 | .. automodule:: scompose.project.instance 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | scompose.project.project module 16 | ------------------------------- 17 | 18 | .. automodule:: scompose.project.project 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: scompose.project 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/api/_sources/source/scompose.rst.txt: -------------------------------------------------------------------------------- 1 | scompose package 2 | ================ 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | scompose.client 10 | scompose.logger 11 | scompose.project 12 | scompose.templates 13 | scompose.tests 14 | scompose.utils 15 | 16 | Submodules 17 | ---------- 18 | 19 | scompose.version module 20 | ----------------------- 21 | 22 | .. automodule:: scompose.version 23 | :members: 24 | :undoc-members: 25 | :show-inheritance: 26 | 27 | 28 | Module contents 29 | --------------- 30 | 31 | .. automodule:: scompose 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | -------------------------------------------------------------------------------- /docs/api/_sources/source/scompose.templates.rst.txt: -------------------------------------------------------------------------------- 1 | scompose.templates package 2 | ========================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: scompose.templates 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/api/_sources/source/scompose.tests.rst.txt: -------------------------------------------------------------------------------- 1 | scompose.tests package 2 | ====================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | scompose.tests.testdata 10 | 11 | Submodules 12 | ---------- 13 | 14 | scompose.tests.test\_client module 15 | ---------------------------------- 16 | 17 | .. automodule:: scompose.tests.test_client 18 | :members: 19 | :undoc-members: 20 | :show-inheritance: 21 | 22 | scompose.tests.test\_utils module 23 | --------------------------------- 24 | 25 | .. automodule:: scompose.tests.test_utils 26 | :members: 27 | :undoc-members: 28 | :show-inheritance: 29 | 30 | 31 | Module contents 32 | --------------- 33 | 34 | .. automodule:: scompose.tests 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | -------------------------------------------------------------------------------- /docs/api/_sources/source/scompose.tests.testdata.rst.txt: -------------------------------------------------------------------------------- 1 | scompose.tests.testdata package 2 | =============================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: scompose.tests.testdata 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/api/_sources/source/scompose.utils.rst.txt: -------------------------------------------------------------------------------- 1 | scompose.utils package 2 | ====================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: scompose.utils 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /docs/api/assets/css/badge_only.css: -------------------------------------------------------------------------------- 1 | .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}} 2 | -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/Roboto-Slab-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/Roboto-Slab-Bold.woff -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/Roboto-Slab-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/Roboto-Slab-Bold.woff2 -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/Roboto-Slab-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/Roboto-Slab-Regular.woff -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/Roboto-Slab-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/Roboto-Slab-Regular.woff2 -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/lato-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/lato-bold-italic.woff -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/lato-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/lato-bold-italic.woff2 -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/lato-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/lato-bold.woff -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/lato-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/lato-bold.woff2 -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/lato-normal-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/lato-normal-italic.woff -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/lato-normal-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/lato-normal-italic.woff2 -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/lato-normal.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/lato-normal.woff -------------------------------------------------------------------------------- /docs/api/assets/css/fonts/lato-normal.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/css/fonts/lato-normal.woff2 -------------------------------------------------------------------------------- /docs/api/assets/documentation_options.js: -------------------------------------------------------------------------------- 1 | var DOCUMENTATION_OPTIONS = { 2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), 3 | VERSION: '1', 4 | LANGUAGE: 'None', 5 | COLLAPSE_INDEX: false, 6 | BUILDER: 'html', 7 | FILE_SUFFIX: '.html', 8 | LINK_SUFFIX: '.html', 9 | HAS_SOURCE: true, 10 | SOURCELINK_SUFFIX: '.txt', 11 | NAVIGATION_WITH_KEYS: false 12 | }; 13 | -------------------------------------------------------------------------------- /docs/api/assets/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/assets/file.png -------------------------------------------------------------------------------- /docs/api/assets/js/badge_only.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}}); 2 | -------------------------------------------------------------------------------- /docs/api/assets/js/html5shiv-printshiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML="",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document); 5 | -------------------------------------------------------------------------------- /docs/api/assets/js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); 5 | -------------------------------------------------------------------------------- /docs/api/assets/js/theme.js: -------------------------------------------------------------------------------- 1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t 2 | 3 | 4 | 5 | 6 | <no title> — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 48 | 49 |
53 | 54 |
55 |
56 |
57 | 64 |
65 |
66 |
67 |
68 | 69 |

../CHANGELOG.md

70 | 71 | 72 |
73 |
74 |
77 | 78 |
79 | 80 |
81 |

© Copyright 2019, Vanessa Sochat.

82 |
83 | 84 | Built with Sphinx using a 85 | theme 86 | provided by Read the Docs. 87 | 88 | 89 |
90 |
91 |
92 |
93 |
94 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /docs/api/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome to Singularity Compose API’s documentation! — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 48 | 49 |
53 | 54 |
55 |
56 |
57 |
    58 |
  • »
  • 59 |
  • Welcome to Singularity Compose API’s documentation!
  • 60 |
  • 61 | View page source 62 |
  • 63 |
64 |
65 |
66 |
67 |
68 | 69 |
70 |

Welcome to Singularity Compose API’s documentation!

71 |

Singularity Compose is an orchestration tool for Singularity instances.

72 |

Contents:

73 | 92 |
93 |

Indices and tables

94 | 97 |
98 |
99 | 100 | 101 |
102 |
103 |
106 | 107 |
108 | 109 |
110 |

© Copyright 2019, Vanessa Sochat.

111 |
112 | 113 | Built with Sphinx using a 114 | theme 115 | provided by Read the Docs. 116 | 117 | 118 |
119 |
120 |
121 |
122 |
123 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /docs/api/modules/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Overview: module code — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 |
    57 |
  • »
  • 58 |
  • Overview: module code
  • 59 |
  • 60 |
  • 61 |
62 |
63 |
64 | 89 |
90 | 91 |
92 | 93 |
94 |

© Copyright 2019, Vanessa Sochat.

95 |
96 | 97 | Built with Sphinx using a 98 | theme 99 | provided by Read the Docs. 100 | 101 | 102 |
103 |
104 |
105 |
106 |
107 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/client/build.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.client.build — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 | 64 |
65 |
66 |
67 |
68 | 69 |

Source code for scompose.client.build

 70 | """
 71 | 
 72 | Copyright (C) 2019-2021 Vanessa Sochat.
 73 | 
 74 | This Source Code Form is subject to the terms of the
 75 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 76 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 77 | 
 78 | 
 79 | """
 80 | 
 81 | from scompose.project import Project
 82 | 
 83 | 
 84 | 
[docs]def main(args, parser, extra): 85 | """Build or rebuild containers 86 | 87 | Containers are built once and then named as <project>_<service>, 88 | e.g. `folder_db`. If a Singularity recipe changes for a container folder, 89 | you can run "singularity-compose build" to rebuild it. 90 | """ 91 | # Initialize the project 92 | project = Project( 93 | filename=args.file, name=args.project_name, env_file=args.env_file 94 | ) 95 | 96 | # Builds any containers into folders 97 | project.build(args.names)
98 |
99 | 100 |
101 |
102 |
103 | 104 |
105 | 106 |
107 |

© Copyright 2019, Vanessa Sochat.

108 |
109 | 110 | Built with Sphinx using a 111 | theme 112 | provided by Read the Docs. 113 | 114 | 115 |
116 |
117 |
118 |
119 |
120 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/client/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.client.config — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 | 64 |
65 |
66 |
67 |
68 | 69 |

Source code for scompose.client.config

 70 | """
 71 | 
 72 | Copyright (C) 2019-2021 Vanessa Sochat.
 73 | 
 74 | This Source Code Form is subject to the terms of the
 75 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 76 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 77 | 
 78 | 
 79 | """
 80 | 
 81 | from scompose.project import Project
 82 | 
 83 | 
 84 | 
[docs]def main(args, parser, extra): 85 | """View or validate a configuration file 86 | 87 | This comes down to reading in the config to the project, at which 88 | case it is validated. We then print it for the user. 89 | """ 90 | # Initialize the project 91 | project = Project( 92 | filename=args.file, name=args.project_name, env_file=args.env_file 93 | ) 94 | 95 | # Builds any containers into folders 96 | project.view_config()
97 |
98 | 99 |
100 |
101 |
102 | 103 |
104 | 105 |
106 |

© Copyright 2019, Vanessa Sochat.

107 |
108 | 109 | Built with Sphinx using a 110 | theme 111 | provided by Read the Docs. 112 | 113 | 114 |
115 |
116 |
117 |
118 |
119 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/client/create.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.client.create — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 | 64 |
65 |
66 |
67 |
68 | 69 |

Source code for scompose.client.create

 70 | """
 71 | 
 72 | Copyright (C) 2019-2021 Vanessa Sochat.
 73 | 
 74 | This Source Code Form is subject to the terms of the
 75 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 76 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 77 | 
 78 | 
 79 | """
 80 | 
 81 | from scompose.project import Project
 82 | 
 83 | 
 84 | 
[docs]def main(args, parser, extra): 85 | """create one or more instances. If they don't exist, build first. 86 | 87 | This will build and bring up one or more named instances, or if None 88 | are provided, we create all of them. 89 | """ 90 | # Initialize the project 91 | project = Project( 92 | filename=args.file, name=args.project_name, env_file=args.env_file 93 | ) 94 | 95 | # Create instances, and if none specified, create all 96 | project.create( 97 | args.names, 98 | writable_tmpfs=not args.read_only, 99 | bridge=args.bridge, 100 | no_resolv=args.no_resolv, 101 | )
102 |
103 | 104 |
105 |
106 |
107 | 108 |
109 | 110 |
111 |

© Copyright 2019, Vanessa Sochat.

112 |
113 | 114 | Built with Sphinx using a 115 | theme 116 | provided by Read the Docs. 117 | 118 | 119 |
120 |
121 |
122 |
123 |
124 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/client/down.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.client.down — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 | 64 |
65 |
66 |
67 |
68 | 69 |

Source code for scompose.client.down

 70 | """
 71 | 
 72 | Copyright (C) 2019-2021 Vanessa Sochat.
 73 | 
 74 | This Source Code Form is subject to the terms of the
 75 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 76 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 77 | 
 78 | 
 79 | """
 80 | 
 81 | from scompose.project import Project
 82 | 
 83 | 
 84 | 
[docs]def main(args, parser, extra): 85 | """bring one or more instances down""" 86 | # Initialize the project 87 | project = Project( 88 | filename=args.file, name=args.project_name, env_file=args.env_file 89 | ) 90 | 91 | # Create instances, and if none specified, create all 92 | project.down(args.names, args.timeout)
93 |
94 | 95 |
96 |
97 |
98 | 99 |
100 | 101 |
102 |

© Copyright 2019, Vanessa Sochat.

103 |
104 | 105 | Built with Sphinx using a 106 | theme 107 | provided by Read the Docs. 108 | 109 | 110 |
111 |
112 |
113 |
114 |
115 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/client/exec.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.client.exec — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 | 64 |
65 |
66 |
67 |
68 | 69 |

Source code for scompose.client.exec

 70 | """
 71 | 
 72 | Copyright (C) 2019-2021 Vanessa Sochat.
 73 | 
 74 | This Source Code Form is subject to the terms of the
 75 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 76 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 77 | 
 78 | 
 79 | """
 80 | 
 81 | from scompose.project import Project
 82 | 
 83 | 
 84 | 
[docs]def main(args, parser, extra): 85 | """execute a command to an instance.""" 86 | # Initialize the project 87 | project = Project( 88 | filename=args.file, name=args.project_name, env_file=args.env_file 89 | ) 90 | 91 | project.execute(args.name[0], extra)
92 |
93 | 94 |
95 |
96 |
97 | 98 |
99 | 100 |
101 |

© Copyright 2019, Vanessa Sochat.

102 |
103 | 104 | Built with Sphinx using a 105 | theme 106 | provided by Read the Docs. 107 | 108 | 109 |
110 |
111 |
112 |
113 |
114 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/client/logs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.client.logs — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 | 64 |
65 |
66 |
67 |
68 | 69 |

Source code for scompose.client.logs

 70 | """
 71 | 
 72 | Copyright (C) 2019-2021 Vanessa Sochat.
 73 | 
 74 | This Source Code Form is subject to the terms of the
 75 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 76 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 77 | 
 78 | 
 79 | """
 80 | 
 81 | from scompose.project import Project
 82 | 
 83 | 
 84 | 
[docs]def main(args, parser, extra): 85 | """bring one or more instances down""" 86 | # Initialize the project 87 | project = Project( 88 | filename=args.file, name=args.project_name, env_file=args.env_file 89 | ) 90 | 91 | if args.clear: 92 | project.clear_logs(args.names) 93 | else: 94 | project.logs(args.names, args.tail)
95 |
96 | 97 |
98 |
99 |
100 | 101 |
102 | 103 |
104 |

© Copyright 2019, Vanessa Sochat.

105 |
106 | 107 | Built with Sphinx using a 108 | theme 109 | provided by Read the Docs. 110 | 111 | 112 |
113 |
114 |
115 |
116 |
117 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/client/ps.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.client.ps — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 | 64 |
65 |
66 |
67 |
68 | 69 |

Source code for scompose.client.ps

 70 | """
 71 | 
 72 | Copyright (C) 2019-2021 Vanessa Sochat.
 73 | 
 74 | This Source Code Form is subject to the terms of the
 75 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 76 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 77 | 
 78 | 
 79 | """
 80 | 
 81 | from scompose.project import Project
 82 | 
 83 | 
 84 | 
[docs]def main(args, parser, extra): 85 | """bring one or more instances down""" 86 | # Initialize the project 87 | project = Project( 88 | filename=args.file, name=args.project_name, env_file=args.env_file 89 | ) 90 | 91 | # Create instances, and if none specified, create all 92 | project.ps()
93 |
94 | 95 |
96 |
97 |
98 | 99 |
100 | 101 |
102 |

© Copyright 2019, Vanessa Sochat.

103 |
104 | 105 | Built with Sphinx using a 106 | theme 107 | provided by Read the Docs. 108 | 109 | 110 |
111 |
112 |
113 |
114 |
115 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/client/restart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.client.restart — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 | 64 |
65 |
66 |
67 |
68 | 69 |

Source code for scompose.client.restart

 70 | """
 71 | 
 72 | Copyright (C) 2019-2021 Vanessa Sochat.
 73 | 
 74 | This Source Code Form is subject to the terms of the
 75 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 76 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 77 | 
 78 | 
 79 | """
 80 | 
 81 | from scompose.project import Project
 82 | 
 83 | 
 84 | 
[docs]def main(args, parser, extra): 85 | """bring up one or more instances. They must exist. 86 | 87 | This will build and bring up one or more named instances, or if None 88 | are provided, we create all of them. 89 | """ 90 | # Initialize the project 91 | project = Project( 92 | filename=args.file, name=args.project_name, env_file=args.env_file 93 | ) 94 | 95 | # Create instances, and if none specified, create all 96 | project.down(args.names) 97 | 98 | # Create instances, and if none specified, create all 99 | project.up( 100 | args.names, 101 | writable_tmpfs=not args.read_only, 102 | bridge=args.bridge, 103 | no_resolv=args.no_resolv, 104 | )
105 |
106 | 107 |
108 |
109 |
110 | 111 |
112 | 113 |
114 |

© Copyright 2019, Vanessa Sochat.

115 |
116 | 117 | Built with Sphinx using a 118 | theme 119 | provided by Read the Docs. 120 | 121 | 122 |
123 |
124 |
125 |
126 |
127 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/client/shell.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.client.shell — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 | 64 |
65 |
66 |
67 |
68 | 69 |

Source code for scompose.client.shell

 70 | """
 71 | 
 72 | Copyright (C) 2019-2021 Vanessa Sochat.
 73 | 
 74 | This Source Code Form is subject to the terms of the
 75 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 76 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 77 | 
 78 | """
 79 | 
 80 | from scompose.project import Project
 81 | 
 82 | 
 83 | 
[docs]def main(args, parser, extra): 84 | """bring one or more instances down""" 85 | # Initialize the project 86 | project = Project( 87 | filename=args.file, name=args.project_name, env_file=args.env_file 88 | ) 89 | 90 | # Create instances, and if none specified, create all 91 | project.shell(args.name[0])
92 |
93 | 94 |
95 |
96 |
97 | 98 |
99 | 100 |
101 |

© Copyright 2019, Vanessa Sochat.

102 |
103 | 104 | Built with Sphinx using a 105 | theme 106 | provided by Read the Docs. 107 | 108 | 109 |
110 |
111 |
112 |
113 |
114 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/client/up.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.client.up — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 | 64 |
65 |
66 |
67 |
68 | 69 |

Source code for scompose.client.up

 70 | """
 71 | 
 72 | Copyright (C) 2019-2021 Vanessa Sochat.
 73 | 
 74 | This Source Code Form is subject to the terms of the
 75 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 76 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 77 | 
 78 | """
 79 | 
 80 | from scompose.project import Project
 81 | 
 82 | 
 83 | 
[docs]def main(args, parser, extra): 84 | """bring up one or more instances. They must exist. 85 | 86 | This will build and bring up one or more named instances, or if None 87 | are provided, we create all of them. 88 | """ 89 | # Initialize the project 90 | project = Project( 91 | filename=args.file, name=args.project_name, env_file=args.env_file 92 | ) 93 | 94 | # Create instances, and if none specified, create all 95 | project.up( 96 | args.names, 97 | writable_tmpfs=not args.read_only, 98 | bridge=args.bridge, 99 | no_resolv=args.no_resolv, 100 | )
101 |
102 | 103 |
104 |
105 |
106 | 107 |
108 | 109 |
110 |

© Copyright 2019, Vanessa Sochat.

111 |
112 | 113 | Built with Sphinx using a 114 | theme 115 | provided by Read the Docs. 116 | 117 | 118 |
119 |
120 |
121 |
122 |
123 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /docs/api/modules/scompose/templates.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.templates — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 47 | 48 |
52 | 53 |
54 |
55 |
56 |
    57 |
  • »
  • 58 |
  • Module code »
  • 59 |
  • scompose.templates
  • 60 |
  • 61 |
  • 62 |
63 |
64 |
65 |
66 |
67 | 68 |

Source code for scompose.templates

 69 | """
 70 | 
 71 | Copyright (C) 2019-2021 Vanessa Sochat.
 72 | 
 73 | This Source Code Form is subject to the terms of the
 74 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed
 75 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 76 | 
 77 | """
 78 | 
 79 | from scompose.logger import bot
 80 | from scompose.utils import get_installdir
 81 | 
 82 | import os
 83 | 
 84 | 
 85 | 
[docs]def get_template(name): 86 | """get a template by name from this directory. If does not exist, 87 | return None. 88 | """ 89 | here = get_installdir() 90 | template = os.path.join(here, "templates", name) 91 | if os.path.exists(template): 92 | return template 93 | bot.warning("%s does not exist." % template)
94 |
95 | 96 |
97 |
98 |
99 | 100 |
101 | 102 |
103 |

© Copyright 2019, Vanessa Sochat.

104 |
105 | 106 | Built with Sphinx using a 107 | theme 108 | provided by Read the Docs. 109 | 110 | 111 |
112 |
113 |
114 |
115 |
116 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /docs/api/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/api/objects.inv -------------------------------------------------------------------------------- /docs/api/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Search — Singularity Compose API 1 documentation 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 50 | 51 |
55 | 56 |
57 |
58 |
59 |
    60 |
  • »
  • 61 |
  • Search
  • 62 |
  • 63 |
  • 64 |
65 |
66 |
67 |
68 |
69 | 70 | 77 | 78 | 79 |
80 | 81 |
82 | 83 |
84 |
85 |
86 | 87 |
88 | 89 |
90 |

© Copyright 2019, Vanessa Sochat.

91 |
92 | 93 | Built with Sphinx using a 94 | theme 95 | provided by Read the Docs. 96 | 97 | 98 |
99 |
100 |
101 |
102 |
103 | 108 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /docs/api/source/scompose.tests.testdata.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | scompose.tests.testdata package — Singularity Compose API 1 documentation 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 70 | 71 |
75 | 76 |
77 |
78 |
79 | 88 |
89 |
90 |
91 |
92 | 93 |
94 |

scompose.tests.testdata package

95 |
96 |

Module contents

97 |
98 |
99 | 100 | 101 |
102 |
103 |
107 | 108 |
109 | 110 |
111 |

© Copyright 2019, Vanessa Sochat.

112 |
113 | 114 | Built with Sphinx using a 115 | theme 116 | provided by Read the Docs. 117 | 118 | 119 |
120 |
121 |
122 |
123 |
124 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /docs/img/content.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/img/content.png -------------------------------------------------------------------------------- /docs/img/scompose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/img/scompose.png -------------------------------------------------------------------------------- /docs/img/scompose.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/img/scompose.xcf -------------------------------------------------------------------------------- /docs/img/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/docs/img/upload.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Singularity Compose: Container Orchestration 6 | 7 | 8 | 9 | 10 | 21 | 22 | 23 |
24 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /docs/spec/README.md: -------------------------------------------------------------------------------- 1 | # Specifications 2 | 3 | This folder contains specifications for each version of a singularity-compose.yml file. 4 | 5 | - [2.0](spec/spec-2.0.md) is version 2 of the specification with added network, start, exec, and run options. See [the file here](https://github.com/singularityhub/singularity-compose/tree/master/docs/spec/spec-2.0.md). You should use version 0.1.0 or later for this spec. 6 | - [1.0](spec/spec-1.0.md) is version 1 (current) of the under development specification. See [the file here](https://github.com/singularityhub/singularity-compose/tree/master/docs/spec/spec-1.0.md). You should use a version less than 0.1.0 for this version. 7 | -------------------------------------------------------------------------------- /docs/spec/spec-1.0.md: -------------------------------------------------------------------------------- 1 | # Singularity Compose Version 1.0 2 | 3 | ## Overview 4 | 5 | The main format of the file is yaml. We must define a version and one or more 6 | instances under "instances." Here is a full example for reference. 7 | 8 | ```yaml 9 | version: "1.0" 10 | instances: 11 | 12 | nginx: 13 | build: 14 | context: ./nginx 15 | recipe: Singularity.nginx 16 | volumes: 17 | - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro 18 | - ./uwsgi_params.par:/etc/nginx/uwsgi_params.par:ro 19 | volumes_from: 20 | - app 21 | ports: 22 | - "80" 23 | 24 | db: 25 | image: postgres:9.4 26 | volumes: 27 | - db-data:/var/lib/postgresql/data 28 | 29 | app: 30 | build: 31 | context: ./app 32 | volumes: 33 | - .:/code 34 | - ./static:/var/www/static 35 | - ./images:/var/www/images 36 | ports: 37 | - "5000:80" 38 | depends_on: 39 | - nginx 40 | ``` 41 | 42 | Each of nginx, uwsgi, and db are instances to be built as containers, and run 43 | as instances. 44 | 45 | ## Networking 46 | 47 | Since Singularity does not (currently) have control over custom networking, 48 | all instance ports are mapped to the host (localhost) and we don't have any 49 | configuration settings to control this (how to handle ports?) 50 | 51 | ## Startscript arguments 52 | 53 | It is possible to use the `command` option to pass arguments to an instance's 54 | startscript. 55 | 56 | The following example shows how to pass the arguments `arg0 arg1 arg2` to the 57 | startscript of instance `app`, 58 | 59 | ```yaml 60 | app: 61 | build: 62 | context: ./app 63 | command: "arg0 arg1 arg2" 64 | ``` 65 | 66 | ## Environment 67 | 68 | While Singularity compose doesn't currently have support for an environment 69 | section, it's easy to add custom environments by way of binding an environment 70 | file to the instance! For example: 71 | 72 | ```yaml 73 | app: 74 | build: 75 | context: ./app 76 | volumes: 77 | - ./env_file.sh:/.singularity.d/env/env_file.sh 78 | ``` 79 | 80 | Any file that is found in the `.singularity.d/env` folder will be sourced. 81 | For example, you could define or export variables like so: 82 | 83 | ```bash 84 | #!/bin/bash 85 | MYNAME=dinosaur 86 | export MYNAME 87 | ``` 88 | 89 | Make sure to export variables. 90 | 91 | ## Instance 92 | 93 | An instance currently must be instantiated from a container built 94 | from a Singularity recipe in a named folder (the example above) 95 | alongside the singularity-compose.yml: 96 | 97 | ``` 98 | nginx: 99 | build: 100 | context: ./nginx 101 | recipe: Singularity.nginx 102 | volumes: 103 | - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro 104 | - ./uwsgi_params.par:/etc/nginx/uwsgi_params.par:ro 105 | volumes_from: 106 | - uwsgi 107 | ports: 108 | - "80" 109 | ``` 110 | 111 | or from a container unique resource identifier (uri) that can be pulled 112 | to a local directory with the same name as the section. 113 | 114 | ``` 115 | nginx: 116 | image: docker://vanessa/sregistry_nginx 117 | volumes: 118 | - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro 119 | - ./uwsgi_params.par:/etc/nginx/uwsgi_params.par:ro 120 | volumes_from: 121 | - uwsgi 122 | ports: 123 | - "80" 124 | ``` 125 | 126 | We build or pull a local container for reproducibility. The first approach, 127 | building from a local file, is recommended as you have full control over 128 | the environment and startscript, and there are likely few containers out in the 129 | wild (and ready to be pulled) that have the correct entry and start scripts 130 | for your application. In the case that you *do* want to pull 131 | a container (and use the existing startscript or entrypoint) you can do that 132 | as follows: 133 | 134 | ``` 135 | nginx: 136 | image: docker://vanessa/sregistry_nginx 137 | ... 138 | ``` 139 | 140 | Customization of an image (e.g., labels, help, post) is out of 141 | scope for singularity-compose, and you must build from a recipe instead. 142 | The fields for instances are discussed below: 143 | 144 | 145 | ### Fields 146 | 147 | |Name| Description | 148 | |----|-------------| 149 | |name|The name of the instance will be "nginx" unless the user provides a "name" 150 | field (not defined above).| 151 | |build| a section to define how and where to build the base container from.| 152 | |build.context| the folder with the Singularity file (and other relevant files). Must exist. 153 | |build.recipe| the Singularity recipe in the build context folder. It defaults to `Singularity`| 154 | |build.options| a list of one or more options (single strings for boolean, or key value pairs for arguments) to provide to build. This is where you could provide fakeroot.| 155 | |image| is looked for after a build entry. It can be a unique resource identifier, or container binary. | 156 | |volumes| one or more files or files to bind to the instance when it's started.| 157 | |volumes_from| shared volumes that are defined for other instances| 158 | |ports| currently not sure how I'm going to handle this!| 159 | |post| a section of post commands and arguments, run after instance creation | 160 | |post.commands| a list of commands to run (directly or a script) on the host | 161 | -------------------------------------------------------------------------------- /paper/paper.bib: -------------------------------------------------------------------------------- 1 | @ARTICLE{Kurtzer2017-xj, 2 | title = "Singularity: Scientific containers for mobility of compute", 3 | author = "Kurtzer, Gregory M and Sochat, Vanessa and Bauer, Michael W", 4 | abstract = "Here we present Singularity, software developed to bring 5 | containers and reproducibility to scientific computing. Using 6 | Singularity containers, developers can work in reproducible 7 | environments of their choosing and design, and these complete 8 | environments can easily be copied and executed on other 9 | platforms. Singularity is an open source initiative that 10 | harnesses the expertise of system and software engineers and 11 | researchers alike, and integrates seamlessly into common 12 | workflows for both of these groups. As its primary use case, 13 | Singularity brings mobility of computing to both users and HPC 14 | centers, providing a secure means to capture and distribute 15 | software and compute environments. This ability to create and 16 | deploy reproducible environments across these centers, a 17 | previously unmet need, makes Singularity a game changing 18 | development for computational science.", 19 | journal = "PLoS One", 20 | doi = "10.1371/journal.pone.0177459", 21 | publisher = "Public Library of Science", 22 | volume = 12, 23 | number = 5, 24 | pages = "e0177459", 25 | month = "11~" # may, 26 | year = 2017 27 | } 28 | 29 | 30 | @Software{SingularityCompose, 31 | title = "singularity-compose", 32 | booktitle = "Singularity Compose", 33 | author = "Sochat, Vanessa", 34 | abstract = "orchestration tool for Singularity container instances", 35 | howpublished = "\url{https://singularityhub.github.io/singularity-compose}", 36 | note = "Accessed: 2019-6-24", 37 | date = "2019-06-24" 38 | } 39 | 40 | 41 | @Software{SingularityComposeGithub, 42 | title = "Singularity Compose {Github}", 43 | booktitle = "Singularity Compose {Github}", 44 | author = "Sochat, Vanessa", 45 | abstract = "open source code for singularity-compose", 46 | howpublished = "\url{https://github.com/singularityhub/singularity-compose}", 47 | note = "Accessed: 2019-6-24", 48 | date = "2019-06-24" 49 | } 50 | 51 | 52 | 53 | @Software{DockerCompose, 54 | title = "Docker {Compose}", 55 | booktitle = "Docker Documentation", 56 | author = {{Docker, Inc.}}, 57 | abstract = "Introduction and Overview of Compose", 58 | howpublished = "\url{https://docs.docker.com/compose/}", 59 | note = "Accessed: 2019-6-24", 60 | date = "2019-06-24" 61 | } 62 | 63 | 64 | @Software{SingularityComposeExamples, 65 | title = "Singularity Compose Examples", 66 | booktitle = "Singularity Compose Examples", 67 | author = "Sochat, Vanessa", 68 | abstract = "open source examples for singularity-compose", 69 | howpublished = "\url{https://github.com/singularityhub/singularity-compose-examples}", 70 | note = "Accessed: 2019-6-24", 71 | date = "2019-06-24" 72 | } 73 | 74 | @MISC{Merkel2014-da, 75 | title = "Docker: Lightweight {Linux} Containers for Consistent Development 76 | and Deployment", 77 | author = "Merkel, Dirk", 78 | journal = "Linux J.", 79 | publisher = "Belltown Media", 80 | volume = 2014, 81 | number = 239, 82 | month = mar, 83 | year = 2014, 84 | address = "Houston, TX" 85 | } 86 | 87 | 88 | @MISC{Wikipedia_contributors2019-bw, 89 | title = "Kubernetes", 90 | booktitle = "Wikipedia, The Free Encyclopedia", 91 | author = "{Wikipedia contributors}", 92 | abstract = "Kubernetes (commonly stylized as k8s[3]) is an open-source 93 | container-orchestration system for automating application 94 | deployment, scaling, and management.[4] It was originally 95 | designed by Google, and is now maintained by the Cloud Native 96 | Computing Foundation. It aims to provide a ``platform for 97 | automating deployment, scaling, and operations of application 98 | containers across clusters of hosts''.[3] It works with a 99 | range of container tools, including Docker.[5] Many cloud 100 | services offer a Kubernetes-based platform or infrastructure 101 | as a service (PaaS or IaaS) on which Kubernetes can be 102 | deployed as a platform-providing service. Many vendors also 103 | provide their own branded Kubernetes distributions.", 104 | month = jun, 105 | year = 2019, 106 | howpublished = "\url{https://en.wikipedia.org/w/index.php?title=Kubernetes&oldid=903021989}", 107 | note = "Accessed: 2019-6-24" 108 | } 109 | 110 | @MISC{SingularityInstances, 111 | title = "Running Services --- {Singularity} container 3.2 documentation", 112 | author = "{Singularity contributors}", 113 | howpublished = "\url{https://sylabs.io/guides/3.2/user-guide/running_services.html?highlight=instances}", 114 | note = "Accessed: 2019-6-24", 115 | year = 2019 116 | } 117 | 118 | 119 | @MISC{Meyer2019-sd, 120 | title = "Sylabs Slides Singularity Updates Into Its Enterprise Pro 121 | Package", 122 | booktitle = "{SDxCentral}", 123 | author = "Meyer, Dan", 124 | abstract = "Sylabs pushed out an update to its enterprise-focused 125 | SingularityPRO container platform that takes advantage of its 126 | 3.x launch.", 127 | month = apr, 128 | year = 2019, 129 | howpublished = "\url{https://www.sdxcentral.com/articles/news/sylabs-slides-singularity-updates-into-its-enterprise-pro-package/2019/04/}", 130 | note = "Accessed: 2019-6-24" 131 | } 132 | -------------------------------------------------------------------------------- /paper/singularity-compose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/paper/singularity-compose.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | profile = "black" 3 | exclude = ["^env/"] 4 | 5 | [tool.isort] 6 | profile = "black" # needed for black/isort compatibility 7 | skip = [] 8 | -------------------------------------------------------------------------------- /scompose/__init__.py: -------------------------------------------------------------------------------- 1 | from .version import __version__ 2 | -------------------------------------------------------------------------------- /scompose/client/build.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | 10 | """ 11 | 12 | from scompose.project import Project 13 | 14 | 15 | def main(args, parser, extra): 16 | """Build or rebuild containers 17 | 18 | Containers are built once and then named as _, 19 | e.g. `folder_db`. If a Singularity recipe changes for a container folder, 20 | you can run "singularity-compose build" to rebuild it. 21 | """ 22 | # Initialize the project 23 | project = Project( 24 | filename=args.file, name=args.project_name, env_file=args.env_file 25 | ) 26 | 27 | # Builds any containers into folders 28 | project.build(args.names) 29 | -------------------------------------------------------------------------------- /scompose/client/check.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2021-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | """ 10 | 11 | import yaml 12 | 13 | from scompose.config import merge_config 14 | from scompose.config.schema import validate_config 15 | from scompose.logger import bot 16 | 17 | 18 | def main(args, parser, extra): 19 | """ 20 | Validate compose files for correctness. 21 | 22 | CLI Arguments 23 | ========== 24 | --preview flag to show combined configs. 25 | """ 26 | 27 | # validate compose files 28 | for f in args.file: 29 | valid = validate_config(f) 30 | if valid and not args.preview: 31 | bot.info("%s is valid." % f) 32 | elif not valid: 33 | bot.exit("%s is not valid." % f) 34 | 35 | if args.preview: 36 | # preview 37 | config = merge_config(args.file) 38 | print(yaml.dump(config, sort_keys=False)) 39 | -------------------------------------------------------------------------------- /scompose/client/config.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | 10 | """ 11 | 12 | from scompose.project import Project 13 | 14 | 15 | def main(args, parser, extra): 16 | """View or validate a configuration file 17 | 18 | This comes down to reading in the config to the project, at which 19 | case it is validated. We then print it for the user. 20 | """ 21 | # Initialize the project 22 | project = Project( 23 | filename=args.file, name=args.project_name, env_file=args.env_file 24 | ) 25 | 26 | # Builds any containers into folders 27 | project.view_config() 28 | -------------------------------------------------------------------------------- /scompose/client/create.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | 10 | """ 11 | 12 | from scompose.project import Project 13 | 14 | 15 | def main(args, parser, extra): 16 | """create one or more instances. If they don't exist, build first. 17 | 18 | This will build and bring up one or more named instances, or if None 19 | are provided, we create all of them. 20 | """ 21 | # Initialize the project 22 | project = Project( 23 | filename=args.file, name=args.project_name, env_file=args.env_file 24 | ) 25 | 26 | # Create instances, and if none specified, create all 27 | project.create( 28 | args.names, 29 | writable_tmpfs=not args.read_only, 30 | bridge=args.bridge, 31 | no_resolv=args.no_resolv, 32 | ) 33 | -------------------------------------------------------------------------------- /scompose/client/down.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | 10 | """ 11 | 12 | from scompose.project import Project 13 | 14 | 15 | def main(args, parser, extra): 16 | """bring one or more instances down""" 17 | # Initialize the project 18 | project = Project( 19 | filename=args.file, name=args.project_name, env_file=args.env_file 20 | ) 21 | 22 | # Create instances, and if none specified, create all 23 | project.down(args.names, args.timeout) 24 | -------------------------------------------------------------------------------- /scompose/client/exec.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | 10 | """ 11 | 12 | from scompose.project import Project 13 | 14 | 15 | def main(args, parser, extra): 16 | """execute a command to an instance.""" 17 | # Initialize the project 18 | project = Project( 19 | filename=args.file, name=args.project_name, env_file=args.env_file 20 | ) 21 | 22 | project.execute(args.name[0], extra) 23 | -------------------------------------------------------------------------------- /scompose/client/logs.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | 10 | """ 11 | 12 | from scompose.project import Project 13 | 14 | 15 | def main(args, parser, extra): 16 | """bring one or more instances down""" 17 | # Initialize the project 18 | project = Project( 19 | filename=args.file, name=args.project_name, env_file=args.env_file 20 | ) 21 | 22 | if args.clear: 23 | project.clear_logs(args.names) 24 | else: 25 | project.logs(args.names, args.tail) 26 | -------------------------------------------------------------------------------- /scompose/client/ps.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | 10 | """ 11 | 12 | from scompose.project import Project 13 | 14 | 15 | def main(args, parser, extra): 16 | """bring one or more instances down""" 17 | # Initialize the project 18 | project = Project( 19 | filename=args.file, name=args.project_name, env_file=args.env_file 20 | ) 21 | 22 | # Create instances, and if none specified, create all 23 | project.ps() 24 | -------------------------------------------------------------------------------- /scompose/client/restart.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | 10 | """ 11 | 12 | from scompose.project import Project 13 | 14 | 15 | def main(args, parser, extra): 16 | """bring up one or more instances. They must exist. 17 | 18 | This will build and bring up one or more named instances, or if None 19 | are provided, we create all of them. 20 | """ 21 | # Initialize the project 22 | project = Project( 23 | filename=args.file, name=args.project_name, env_file=args.env_file 24 | ) 25 | 26 | # Create instances, and if none specified, create all 27 | project.down(args.names) 28 | 29 | # Create instances, and if none specified, create all 30 | project.up( 31 | args.names, 32 | writable_tmpfs=not args.read_only, 33 | bridge=args.bridge, 34 | no_resolv=args.no_resolv, 35 | ) 36 | -------------------------------------------------------------------------------- /scompose/client/run.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | 10 | """ 11 | 12 | from scompose.project import Project 13 | 14 | 15 | def main(args, parser, extra): 16 | """execute a command to an instance.""" 17 | # Initialize the project 18 | project = Project( 19 | filename=args.file, name=args.project_name, env_file=args.env_file 20 | ) 21 | 22 | project.run(args.name[0]) 23 | -------------------------------------------------------------------------------- /scompose/client/shell.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | """ 10 | 11 | from scompose.project import Project 12 | 13 | 14 | def main(args, parser, extra): 15 | """bring one or more instances down""" 16 | # Initialize the project 17 | project = Project( 18 | filename=args.file, name=args.project_name, env_file=args.env_file 19 | ) 20 | 21 | # Create instances, and if none specified, create all 22 | project.shell(args.name[0]) 23 | -------------------------------------------------------------------------------- /scompose/client/up.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | """ 10 | 11 | from scompose.project import Project 12 | 13 | 14 | def main(args, parser, extra): 15 | """bring up one or more instances. They must exist. 16 | 17 | This will build and bring up one or more named instances, or if None 18 | are provided, we create all of them. 19 | """ 20 | # Initialize the project 21 | project = Project( 22 | filename=args.file, name=args.project_name, env_file=args.env_file 23 | ) 24 | 25 | # Create instances, and if none specified, create all 26 | project.up( 27 | args.names, 28 | writable_tmpfs=not args.read_only, 29 | bridge=args.bridge, 30 | no_resolv=args.no_resolv, 31 | ) 32 | -------------------------------------------------------------------------------- /scompose/config/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | """ 10 | 11 | import os 12 | 13 | from scompose.logger import bot 14 | from scompose.utils import read_yaml 15 | 16 | 17 | def merge_config(file_list): 18 | """ 19 | Given one or more config files, merge into one 20 | """ 21 | yaml_files = [] 22 | for f in file_list: 23 | try: 24 | # ensure file exists 25 | if not os.path.exists(f): 26 | bot.exit("%s does not exist." % f) 27 | 28 | # read yaml file 29 | yaml_files.append(read_yaml(f, quiet=True)) 30 | except Exception: # ParserError 31 | bot.exit("Cannot parse %s, invalid yaml." % f) 32 | 33 | # merge/override yaml properties where applicable 34 | return _deep_merge(yaml_files) 35 | 36 | 37 | def _deep_merge(yaml_files): 38 | """ 39 | Merge yaml files into a single dict 40 | """ 41 | base_yaml = None 42 | for idx, item in enumerate(yaml_files): 43 | if idx == 0: 44 | base_yaml = item 45 | else: 46 | base_yaml = _merge(base_yaml, item) 47 | 48 | return base_yaml 49 | 50 | 51 | def _merge(a, b): 52 | """ 53 | Merge dict b into a 54 | """ 55 | for key in b: 56 | if key in a: 57 | # merge dicts recursively 58 | if isinstance(a[key], dict) and isinstance(b[key], dict): 59 | a[key] = _merge(a[key], b[key]) 60 | # if types are equal, b takes precedence 61 | elif isinstance(a[key], type(b[key])): 62 | a[key] = b[key] 63 | # if nothing matches then this means a conflict of types which shouldn't exist in the first place 64 | else: 65 | bot.exit("key '%s': type mismatch in different files." % key) 66 | else: 67 | a[key] = b[key] 68 | return a 69 | -------------------------------------------------------------------------------- /scompose/config/schema.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2021-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | """ 10 | 11 | import sys 12 | 13 | from jsonschema.exceptions import ValidationError 14 | 15 | from scompose.utils import read_yaml 16 | 17 | # We don't require jsonschema, so catch import error and alert user 18 | try: 19 | from jsonschema import validate 20 | except ImportError as e: 21 | msg = "pip install jsonschema" 22 | sys.exit("jsonschema is required for checking and validation: %s\n %s" % (e, msg)) 23 | 24 | 25 | def validate_config(filepath): 26 | """ 27 | Validate a singularity-compose.yaml file. 28 | """ 29 | try: 30 | cfg = read_yaml(filepath, quiet=True) 31 | validate(cfg, compose_schema) 32 | return True 33 | except ValidationError: 34 | return False 35 | 36 | 37 | ## Singularity Compose Schema 38 | 39 | schema_url = "https://json-schema.org/draft-07/schema/#" 40 | 41 | # Common patterns of types 42 | string_list = {"type": "array", "items": {"type": "string"}} 43 | 44 | # Instance groups 45 | instance_build = { 46 | "type": "object", 47 | "properties": { 48 | "recipe": {"type": "string"}, 49 | "context": {"type": "string"}, 50 | "options": string_list, 51 | }, 52 | } 53 | 54 | instance_network = { 55 | "type": "object", 56 | "properties": { 57 | "allocate_ip": {"type": "boolean"}, 58 | "enable": {"type": "boolean"}, 59 | }, 60 | } 61 | 62 | 63 | instance_start = { 64 | "type": "object", 65 | "properties": { 66 | "args": {"type": ["string", "array"]}, 67 | "background": {"type": "boolean"}, 68 | "options": string_list, 69 | }, 70 | } 71 | 72 | instance_run = { 73 | "type": "object", 74 | "properties": { 75 | "args": {"type": ["string", "array"]}, 76 | "options": string_list, 77 | }, 78 | } 79 | 80 | instance_post = { 81 | "type": "object", 82 | "properties": { 83 | "commands": string_list, 84 | }, 85 | } 86 | 87 | instance_exec = { 88 | "type": "object", 89 | "properties": {"options": string_list, "command": {"type": "string"}}, 90 | "required": [ 91 | "command", 92 | ], 93 | } 94 | 95 | instance_deploy = { 96 | "type": "object", 97 | "properties": { 98 | "replicas": {"type": "number", "minimum": 1}, 99 | }, 100 | } 101 | 102 | # A single instance 103 | instance = { 104 | "type": "object", 105 | "properties": { 106 | "image": {"type": "string"}, 107 | "build": instance_build, 108 | "network": instance_network, 109 | "ports": string_list, 110 | "volumes": string_list, 111 | "volumes_from": string_list, 112 | "depends_on": string_list, 113 | "start": instance_start, 114 | "exec": instance_exec, 115 | "run": {"oneOf": [instance_run, {"type": "array"}]}, 116 | "post": instance_post, 117 | "deploy": instance_deploy, 118 | }, 119 | } 120 | 121 | 122 | # instances define container services 123 | instances = {"type": "object", "patternProperties": {"\\w[\\w-]*": instance}} 124 | 125 | properties = {"version": {"type": "string"}, "instances": instances} 126 | 127 | compose_schema = { 128 | "$schema": schema_url, 129 | "title": "Singularity Compose Schema", 130 | "type": "object", 131 | "required": [ 132 | "version", 133 | "instances", 134 | ], 135 | "properties": properties, 136 | "additionalProperties": False, 137 | } 138 | -------------------------------------------------------------------------------- /scompose/logger/__init__.py: -------------------------------------------------------------------------------- 1 | from .message import bot 2 | from .progress import ProgressBar 3 | -------------------------------------------------------------------------------- /scompose/logger/progress.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # clint.textui.progress 4 | # ~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | # A derivation of clint version, to not introduce a dependency and add custom functionality. 7 | # Credit to base code goes to https://github.com/kennethreitz/clint/blob/master/clint/textui/progress.py 8 | 9 | 10 | from __future__ import absolute_import 11 | 12 | import sys 13 | import time 14 | 15 | STREAM = sys.stderr 16 | 17 | BAR_TEMPLATE = "%s[%s%s] %i/%i MB - %s\r" 18 | BAR_FILLED_CHAR = "-" 19 | BAR_EMPTY_CHAR = " " 20 | 21 | # How long to wait before recalculating the ETA 22 | ETA_INTERVAL = 1 23 | # How many intervals (excluding the current one) to calculate the simple moving 24 | # average 25 | ETA_SMA_WINDOW = 9 26 | 27 | 28 | class ProgressBar: 29 | def __enter__(self): 30 | return self 31 | 32 | def __exit__(self, exc_type, exc_val, exc_tb): 33 | self.done() 34 | return False # we're not suppressing exceptions 35 | 36 | def __init__( 37 | self, 38 | label="", 39 | width=32, 40 | hide=None, 41 | empty_char=BAR_EMPTY_CHAR, 42 | filled_char=BAR_FILLED_CHAR, 43 | expected_size=None, 44 | every=1, 45 | ): 46 | self.label = label 47 | self.width = width 48 | self.hide = hide 49 | # Only show bar in terminals by default (better for piping, logging etc.) 50 | if hide is None: 51 | try: 52 | self.hide = not STREAM.isatty() 53 | except AttributeError: # output does not support isatty() 54 | self.hide = True 55 | self.empty_char = empty_char 56 | self.filled_char = filled_char 57 | self.expected_size = expected_size 58 | self.every = every 59 | self.start = time.time() 60 | self.ittimes = [] 61 | self.eta = 0 62 | self.etadelta = time.time() 63 | self.etadisp = self.format_time(self.eta) 64 | self.last_progress = 0 65 | if self.expected_size: 66 | self.show(0) 67 | 68 | def show(self, progress, count=None): 69 | if count is not None: 70 | self.expected_size = count 71 | if self.expected_size is None: 72 | raise Exception("expected_size not initialized") 73 | self.last_progress = progress 74 | if (time.time() - self.etadelta) > ETA_INTERVAL: 75 | self.etadelta = time.time() 76 | self.ittimes = self.ittimes[-ETA_SMA_WINDOW:] + [ 77 | -(self.start - time.time()) / (progress + 1) 78 | ] 79 | self.eta = ( 80 | sum(self.ittimes) 81 | / float(len(self.ittimes)) 82 | * (self.expected_size - progress) 83 | ) 84 | self.etadisp = self.format_time(self.eta) 85 | x = int(self.width * progress / self.expected_size) 86 | if not self.hide: 87 | if (progress % self.every) == 0 or ( # True every "every" updates 88 | progress == self.expected_size 89 | ): # And when we're done 90 | STREAM.write( 91 | BAR_TEMPLATE 92 | % ( 93 | self.label, 94 | self.filled_char * x, 95 | self.empty_char * (self.width - x), 96 | progress, 97 | self.expected_size, 98 | self.etadisp, 99 | ) 100 | ) 101 | STREAM.flush() 102 | 103 | def done(self): 104 | self.elapsed = time.time() - self.start 105 | elapsed_disp = self.format_time(self.elapsed) 106 | if not self.hide: 107 | # Print completed bar with elapsed time 108 | STREAM.write( 109 | BAR_TEMPLATE 110 | % ( 111 | self.label, 112 | self.filled_char * self.width, 113 | self.empty_char * 0, 114 | self.last_progress, 115 | self.expected_size, 116 | elapsed_disp, 117 | ) 118 | ) 119 | STREAM.write("\n") 120 | STREAM.flush() 121 | 122 | def format_time(self, seconds): 123 | return time.strftime("%H:%M:%S", time.gmtime(seconds)) 124 | 125 | 126 | def bar( 127 | it, 128 | label="", 129 | width=32, 130 | hide=None, 131 | empty_char=BAR_EMPTY_CHAR, 132 | filled_char=BAR_FILLED_CHAR, 133 | expected_size=None, 134 | every=1, 135 | ): 136 | """Progress iterator. Wrap your iterables with it.""" 137 | 138 | count = len(it) if expected_size is None else expected_size 139 | 140 | with ProgressBar( 141 | label=label, 142 | width=width, 143 | hide=hide, 144 | empty_char=BAR_EMPTY_CHAR, 145 | filled_char=BAR_FILLED_CHAR, 146 | expected_size=count, 147 | every=every, 148 | ) as pbar: 149 | for i, item in enumerate(it): 150 | yield item 151 | pbar.show(i + 1) 152 | -------------------------------------------------------------------------------- /scompose/project/__init__.py: -------------------------------------------------------------------------------- 1 | from .project import Project 2 | -------------------------------------------------------------------------------- /scompose/templates/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | """ 10 | 11 | import os 12 | 13 | from scompose.logger import bot 14 | from scompose.utils import get_installdir 15 | 16 | 17 | def get_template(name): 18 | """get a template by name from this directory. If does not exist, 19 | return None. 20 | """ 21 | here = get_installdir() 22 | template = os.path.join(here, "templates", name) 23 | if os.path.exists(template): 24 | return template 25 | bot.warning("%s does not exist." % template) 26 | -------------------------------------------------------------------------------- /scompose/templates/hosts: -------------------------------------------------------------------------------- 1 | 127.0.0.1 localhost 2 | 3 | # The following lines are desirable for IPv6 capable hosts 4 | ::1 ip6-localhost ip6-loopback 5 | fe00::0 ip6-localnet 6 | ff00::0 ip6-mcastprefix 7 | ff02::1 ip6-allnodes 8 | ff02::2 ip6-allrouters 9 | -------------------------------------------------------------------------------- /scompose/templates/resolv.conf: -------------------------------------------------------------------------------- 1 | # This is resolv.conf generated by singularity-compose. It is provided 2 | # to provide Google nameservers. If you don't want to have it generated 3 | # and bound by default, use the up --no-resolv argument. 4 | 5 | nameserver 8.8.8.8 6 | nameserver 8.8.4.4 7 | -------------------------------------------------------------------------------- /scompose/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singularityhub/singularity-compose/ca6fa1834e73e66442a0da01a2246cc86b077b3d/scompose/tests/__init__.py -------------------------------------------------------------------------------- /scompose/tests/configs/cmd_args/Singularity: -------------------------------------------------------------------------------- 1 | Bootstrap: docker 2 | From: busybox 3 | 4 | %startscript 5 | echo "Following arguments: $@" 6 | -------------------------------------------------------------------------------- /scompose/tests/configs/cmd_args/singularity-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.0" 2 | instances: 3 | echo: 4 | build: 5 | context: . 6 | recipe: Singularity 7 | start: 8 | args: "arg0 arg1 arg2" 9 | -------------------------------------------------------------------------------- /scompose/tests/configs/config_merge/singularity-compose-1.yml: -------------------------------------------------------------------------------- 1 | version: "2.0" 2 | instances: 3 | echo: 4 | build: 5 | context: . 6 | recipe: Singularity 7 | start: 8 | args: "arg0 arg1 arg2" 9 | -------------------------------------------------------------------------------- /scompose/tests/configs/config_merge/singularity-compose-2.yml: -------------------------------------------------------------------------------- 1 | version: "2.0" 2 | instances: 3 | echo: 4 | start: 5 | options: 6 | - fakeroot 7 | args: "arg0 arg1" 8 | 9 | hello: 10 | image: from_the_other_side.sif 11 | start: 12 | args: "how are you?" 13 | -------------------------------------------------------------------------------- /scompose/tests/configs/depends_on/Singularity.first: -------------------------------------------------------------------------------- 1 | Bootstrap: docker 2 | From: busybox 3 | 4 | %startscript 5 | printf "Present working directory is $PWD" 6 | printf "This is first" 7 | -------------------------------------------------------------------------------- /scompose/tests/configs/depends_on/Singularity.second: -------------------------------------------------------------------------------- 1 | Bootstrap: docker 2 | From: busybox 3 | 4 | %startscript 5 | printf "Present working directory is $PWD" 6 | printf "This is second" 7 | -------------------------------------------------------------------------------- /scompose/tests/configs/depends_on/Singularity.third: -------------------------------------------------------------------------------- 1 | Bootstrap: docker 2 | From: busybox 3 | 4 | %startscript 5 | printf "Present working directory is $PWD" 6 | printf "This is third" 7 | -------------------------------------------------------------------------------- /scompose/tests/configs/depends_on/singularity-compose.yml: -------------------------------------------------------------------------------- 1 | version: "1.0" 2 | instances: 3 | second: 4 | build: 5 | context: . 6 | recipe: Singularity.second 7 | depends_on: [first] 8 | third: 9 | build: 10 | context: . 11 | recipe: Singularity.third 12 | depends_on: [second] 13 | first: 14 | build: 15 | context: . 16 | recipe: Singularity.first 17 | -------------------------------------------------------------------------------- /scompose/tests/configs/wrong_depends_on/Singularity.first: -------------------------------------------------------------------------------- 1 | Bootstrap: docker 2 | From: busybox 3 | 4 | %startscript 5 | printf "Present working directory is $PWD" 6 | printf "This is first" 7 | -------------------------------------------------------------------------------- /scompose/tests/configs/wrong_depends_on/Singularity.second: -------------------------------------------------------------------------------- 1 | Bootstrap: docker 2 | From: busybox 3 | 4 | %startscript 5 | printf "Present working directory is $PWD" 6 | printf "This is second" 7 | -------------------------------------------------------------------------------- /scompose/tests/configs/wrong_depends_on/Singularity.third: -------------------------------------------------------------------------------- 1 | Bootstrap: docker 2 | From: busybox 3 | 4 | %startscript 5 | printf "Present working directory is $PWD" 6 | printf "This is third" 7 | -------------------------------------------------------------------------------- /scompose/tests/configs/wrong_depends_on/singularity-compose.yml: -------------------------------------------------------------------------------- 1 | version: "1.0" 2 | instances: 3 | first: 4 | build: 5 | context: . 6 | recipe: Singularity.first 7 | depends_on: [second] 8 | second: 9 | build: 10 | context: . 11 | recipe: Singularity.second 12 | depends_on: [second] 13 | third: 14 | build: 15 | context: . 16 | recipe: Singularity.third 17 | -------------------------------------------------------------------------------- /scompose/tests/test_client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | # This Source Code Form is subject to the terms of the 6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | import os 10 | from time import sleep 11 | 12 | import requests 13 | 14 | from scompose.project import Project 15 | from scompose.utils import run_command 16 | 17 | 18 | def test_commands(tmp_path): 19 | tmpdir = os.path.join(tmp_path, "repo") 20 | repo = "https://github.com/singularityhub/singularity-compose-examples" 21 | 22 | # Clone the example 23 | run_command(["git", "clone", repo, tmpdir]) 24 | 25 | # Test the simple apache example 26 | workdir = os.path.join(tmpdir, "v1.0", "apache-simple") 27 | os.chdir(workdir) 28 | 29 | # Check for required files 30 | assert "singularity-compose.yml" in os.listdir() 31 | 32 | print("Creating project...") 33 | 34 | # Loading project validates config 35 | project = Project() 36 | 37 | print("Testing build") 38 | assert "httpd.sif" not in os.listdir("httpd") 39 | project.build() 40 | assert "httpd.sif" in os.listdir("httpd") 41 | 42 | print("Testing view config") 43 | project.view_config() 44 | 45 | print("Testing up") 46 | project.up() 47 | assert "etc.hosts" in os.listdir() 48 | assert "resolv.conf" in os.listdir() 49 | 50 | print("Waiting for instance to start") 51 | sleep(10) 52 | 53 | print("Testing logs") 54 | project.logs(["httpd1"], tail=20) 55 | 56 | print("Clearing logs") 57 | project.clear_logs(["httpd1"]) 58 | project.logs(["httpd1"], tail=20) 59 | 60 | print("Testing ps") 61 | project.ps() 62 | 63 | print("Testing exec") 64 | project.execute("httpd1", ["echo", "MarsBar"]) 65 | 66 | # Ensure running 67 | print(requests.get("http://127.0.0.1").status_code) 68 | 69 | print("Testing down") 70 | project.down() 71 | 72 | print("Testing ip lookup") 73 | lookup = project.get_ip_lookup(["httpd1"]) 74 | assert "httpd1" in lookup 75 | assert lookup["httpd1"] == "10.22.0.2" 76 | -------------------------------------------------------------------------------- /scompose/tests/test_command_args.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | # This Source Code Form is subject to the terms of the 6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | import os 10 | import shutil 11 | from time import sleep 12 | 13 | from scompose.logger import bot 14 | from scompose.project import Project 15 | 16 | here = os.path.dirname(os.path.abspath(__file__)) 17 | 18 | 19 | def test_command_args(tmp_path): 20 | bot.clear() ## Clear previously logged messages 21 | 22 | cmd_args = os.path.join(here, "configs", "cmd_args") 23 | for filename in os.listdir(cmd_args): 24 | source = os.path.join(cmd_args, filename) 25 | dest = os.path.join(tmp_path, filename) 26 | print("Copying %s to %s" % (filename, dest)) 27 | shutil.copyfile(source, dest) 28 | 29 | # Test the simple apache example 30 | os.chdir(tmp_path) 31 | 32 | # Check for required files 33 | assert "singularity-compose.yml" in os.listdir() 34 | 35 | print("Creating project...") 36 | 37 | # Loading project validates config 38 | project = Project() 39 | 40 | print("Testing build") 41 | project.build() 42 | 43 | assert "echo.sif" in os.listdir(tmp_path) 44 | 45 | print("Testing view config") 46 | project.view_config() 47 | 48 | print("Testing up") 49 | project.up() 50 | 51 | print("Waiting for instances to start") 52 | sleep(10) 53 | 54 | print("Bringing down") 55 | project.down() 56 | 57 | log = bot.get_logs() 58 | assert "arg0 arg1 arg2" in log 59 | -------------------------------------------------------------------------------- /scompose/tests/test_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Copyright (C) 2017-2024 Vanessa Sochat. 4 | 5 | # This Source Code Form is subject to the terms of the 6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | import os 10 | 11 | here = os.path.dirname(os.path.abspath(__file__)) 12 | 13 | 14 | def test_merge(): 15 | print("Testing utils._merge") 16 | from scompose.config import _merge 17 | 18 | # No override 19 | a = {"a": 123} 20 | b = {"b": 456} 21 | assert _merge(a, b) == {"a": 123, "b": 456} 22 | 23 | # Override + merge 24 | a = {"a": 123} 25 | b = {"b": 456, "a": 789} 26 | assert _merge(a, b) == {"a": 789, "b": 456} 27 | 28 | # Override only 29 | a = {"a": 123} 30 | b = {"a": 789} 31 | assert _merge(a, b) == {"a": 789} 32 | 33 | # Dict merge 34 | a = {"a": 123, "b": {"c": "d"}} 35 | b = {"b": {"e": "f"}} 36 | assert _merge(a, b) == {"a": 123, "b": {"c": "d", "e": "f"}} 37 | 38 | # Dict merge + key override 39 | a = {"a": 123, "b": {"c": "d"}} 40 | b = {"b": {"c": "f"}} 41 | assert _merge(a, b) == {"a": 123, "b": {"c": "f"}} 42 | 43 | 44 | def test_deep_merge(): 45 | print("Testing utils._deep_merge") 46 | from scompose.config import _deep_merge 47 | from scompose.utils import read_yaml 48 | 49 | config_override = os.path.join(here, "configs", "config_merge") 50 | 51 | # single file 52 | yaml_files = [ 53 | read_yaml( 54 | os.path.join(config_override, "singularity-compose-1.yml"), quiet=True 55 | ) 56 | ] 57 | ret = _deep_merge(yaml_files) 58 | assert ret["instances"] == { 59 | "echo": { 60 | "build": {"context": ".", "recipe": "Singularity"}, 61 | "start": {"args": "arg0 arg1 arg2"}, 62 | } 63 | } 64 | 65 | # multiple files 66 | yaml_files = [ 67 | read_yaml( 68 | os.path.join(config_override, "singularity-compose-1.yml"), quiet=True 69 | ), 70 | read_yaml( 71 | os.path.join(config_override, "singularity-compose-2.yml"), quiet=True 72 | ), 73 | ] 74 | ret = _deep_merge(yaml_files) 75 | assert ret["instances"] == { 76 | "echo": { 77 | "build": {"context": ".", "recipe": "Singularity"}, 78 | "start": {"args": "arg0 arg1", "options": ["fakeroot"]}, 79 | }, 80 | "hello": { 81 | "image": "from_the_other_side.sif", 82 | "start": {"args": "how are you?"}, 83 | }, 84 | } 85 | 86 | 87 | def test_merge_config(): 88 | print("Testing utils.build_interpolated_config") 89 | from scompose.config import merge_config 90 | 91 | config_override = os.path.join(here, "configs", "config_merge") 92 | 93 | # single file 94 | file_list = [os.path.join(config_override, "singularity-compose-1.yml")] 95 | ret = merge_config(file_list) 96 | assert ret["instances"] == { 97 | "echo": { 98 | "build": {"context": ".", "recipe": "Singularity"}, 99 | "start": {"args": "arg0 arg1 arg2"}, 100 | } 101 | } 102 | 103 | # multiple files 104 | file_list = [ 105 | os.path.join(config_override, "singularity-compose-1.yml"), 106 | os.path.join(config_override, "singularity-compose-2.yml"), 107 | ] 108 | ret = merge_config(file_list) 109 | assert ret["instances"] == { 110 | "echo": { 111 | "build": {"context": ".", "recipe": "Singularity"}, 112 | "start": {"args": "arg0 arg1", "options": ["fakeroot"]}, 113 | }, 114 | "hello": { 115 | "image": "from_the_other_side.sif", 116 | "start": {"args": "how are you?"}, 117 | }, 118 | } 119 | -------------------------------------------------------------------------------- /scompose/tests/test_depends_on.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | # This Source Code Form is subject to the terms of the 6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | import os 10 | import shutil 11 | from time import sleep 12 | 13 | from scompose.logger import bot 14 | from scompose.project import Project 15 | 16 | here = os.path.dirname(os.path.abspath(__file__)) 17 | 18 | 19 | def test_circular_dependency(tmp_path): 20 | depends_on = os.path.join(here, "configs", "wrong_depends_on") 21 | for filename in os.listdir(depends_on): 22 | source = os.path.join(depends_on, filename) 23 | dest = os.path.join(tmp_path, filename) 24 | print("Copying %s to %s" % (filename, dest)) 25 | shutil.copyfile(source, dest) 26 | 27 | # Test the simple apache example 28 | os.chdir(tmp_path) 29 | 30 | # Check for required files 31 | assert "singularity-compose.yml" in os.listdir() 32 | 33 | print("Creating project...") 34 | 35 | # Loading project validates config 36 | project = Project() 37 | 38 | print("Testing build") 39 | project.build() 40 | 41 | for image in ["first.sif", "second.sif", "third.sif"]: 42 | assert image in os.listdir(tmp_path) 43 | 44 | print("Testing view config") 45 | project.view_config() 46 | 47 | try: 48 | print("Testing up") 49 | project.up() 50 | raise Exception("Up should have failed") 51 | except SystemExit: 52 | print("Up failed as expected") 53 | finally: 54 | print("Bringing down") 55 | project.down() 56 | 57 | 58 | def test_no_circular_dependency(tmp_path): 59 | bot.clear() ## Clear previously logged messages 60 | 61 | depends_on = os.path.join(here, "configs", "depends_on") 62 | for filename in os.listdir(depends_on): 63 | source = os.path.join(depends_on, filename) 64 | dest = os.path.join(tmp_path, filename) 65 | print("Copying %s to %s" % (filename, dest)) 66 | shutil.copyfile(source, dest) 67 | 68 | # Test the simple apache example 69 | os.chdir(tmp_path) 70 | 71 | # Check for required files 72 | assert "singularity-compose.yml" in os.listdir() 73 | 74 | print("Creating project...") 75 | 76 | # Loading project validates config 77 | project = Project() 78 | 79 | print("Testing build") 80 | project.build() 81 | 82 | for image in ["first.sif", "second.sif", "third.sif"]: 83 | assert image in os.listdir(tmp_path) 84 | 85 | print("Testing view config") 86 | project.view_config() 87 | 88 | # Test depends_on DAG order 89 | keys = list(project.instances.keys()) 90 | assert keys == ["first1", "second1", "third1"] 91 | 92 | print("Testing up") 93 | project.up() 94 | 95 | print("Waiting for instances to start") 96 | sleep(10) 97 | 98 | print("Bringing down") 99 | project.down() 100 | 101 | log = bot.get_logs() 102 | assert log.index("Creating first") < log.index("Creating second") and log.index( 103 | "Creating second" 104 | ) < log.index("Creating third") 105 | -------------------------------------------------------------------------------- /scompose/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Copyright (C) 2017-2024 Vanessa Sochat. 4 | 5 | # This Source Code Form is subject to the terms of the 6 | # Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | # with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | import os 10 | 11 | import pytest 12 | 13 | here = os.path.dirname(os.path.abspath(__file__)) 14 | 15 | 16 | def test_write_read_files(tmp_path): 17 | """test_write_read_files will test the functions write_file and read_file""" 18 | print("Testing utils.write_file...") 19 | from scompose.utils import write_file 20 | 21 | tmpfile = str(tmp_path / "written_file.txt") 22 | assert not os.path.exists(tmpfile) 23 | write_file(tmpfile, "hello!") 24 | assert os.path.exists(tmpfile) 25 | 26 | print("Testing utils.read_file...") 27 | from scompose.utils import read_file 28 | 29 | content = read_file(tmpfile)[0] 30 | assert content == "hello!" 31 | 32 | 33 | def test_write_bad_json(tmp_path): 34 | from scompose.utils import write_json 35 | 36 | bad_json = {"Wakkawakkawakka'}": [{True}, "2", 3]} 37 | tmpfile = str(tmp_path / "json_file.txt") 38 | assert not os.path.exists(tmpfile) 39 | with pytest.raises(TypeError): 40 | write_json(bad_json, tmpfile) 41 | 42 | 43 | def test_write_json(tmp_path): 44 | import json 45 | 46 | from scompose.utils import read_json, write_json 47 | 48 | good_json = {"Wakkawakkawakka": [True, "2", 3]} 49 | tmpfile = str(tmp_path / "good_json_file.txt") 50 | assert not os.path.exists(tmpfile) 51 | write_json(good_json, tmpfile) 52 | with open(tmpfile, "r") as f: 53 | content = json.loads(f.read()) 54 | assert isinstance(content, dict) 55 | assert "Wakkawakkawakka" in content 56 | content = read_json(tmpfile) 57 | assert "Wakkawakkawakka" in content 58 | 59 | 60 | def test_get_installdir(): 61 | """get install directory should return the base of where sregistry 62 | is installed 63 | """ 64 | print("Testing utils.get_installdir") 65 | from scompose.utils import get_installdir 66 | 67 | whereami = get_installdir() 68 | print(whereami) 69 | assert whereami.endswith("scompose") 70 | 71 | 72 | def test_run_command(): 73 | print("Testing utils.run_command") 74 | from scompose.utils import run_command 75 | 76 | result = run_command(["echo", "hello"]) 77 | assert result["message"] == "hello\n" 78 | assert result["return_code"] == 0 79 | 80 | 81 | def test_get_userhome(): 82 | print("Testing utils.get_userhome") 83 | from scompose.utils import get_userhome 84 | 85 | home = get_userhome() 86 | assert home in os.environ.get("HOME") 87 | 88 | 89 | def test_print_json(): 90 | print("Testing utils.print_json") 91 | from scompose.utils import print_json 92 | 93 | result = print_json({1: 1}) 94 | assert result == '{\n "1": 1\n}' 95 | -------------------------------------------------------------------------------- /scompose/utils/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | """ 10 | 11 | import errno 12 | import json 13 | import os 14 | import pwd 15 | import sys 16 | from subprocess import PIPE, STDOUT, Popen 17 | 18 | import yaml 19 | 20 | 21 | def get_installdir(): 22 | """get_installdir returns the installation directory of the application""" 23 | return os.path.abspath(os.path.dirname(os.path.dirname(__file__))) 24 | 25 | 26 | def get_userhome(): 27 | """get the user home based on the effective uid""" 28 | return pwd.getpwuid(os.getuid())[5] 29 | 30 | 31 | def run_command(cmd, sudo=False): 32 | """run_command uses subprocess to send a command to the terminal. 33 | 34 | Parameters 35 | ========== 36 | cmd: the command to send, should be a list for subprocess 37 | error_message: the error message to give to user if fails, 38 | if none specified, will alert that command failed. 39 | 40 | """ 41 | if sudo is True: 42 | cmd = ["sudo"] + cmd 43 | 44 | try: 45 | output = Popen(cmd, stderr=STDOUT, stdout=PIPE) 46 | 47 | except FileNotFoundError: 48 | cmd.pop(0) 49 | output = Popen(cmd, stderr=STDOUT, stdout=PIPE) 50 | 51 | t = output.communicate()[0], output.returncode 52 | output = {"message": t[0], "return_code": t[1]} 53 | 54 | if isinstance(output["message"], bytes): 55 | output["message"] = output["message"].decode("utf-8") 56 | 57 | return output 58 | 59 | 60 | ################################################################################ 61 | ## FOLDER OPERATIONS ########################################################### 62 | ################################################################################ 63 | 64 | 65 | def mkdir_p(path): 66 | """mkdir_p attempts to get the same functionality as mkdir -p 67 | :param path: the path to create. 68 | """ 69 | try: 70 | os.makedirs(path) 71 | except OSError as e: 72 | if e.errno == errno.EEXIST and os.path.isdir(path): 73 | pass 74 | else: 75 | print("Error creating path %s, exiting." % path) 76 | sys.exit(1) 77 | 78 | 79 | ################################################################################ 80 | ## FILE OPERATIONS ############################################################# 81 | ################################################################################ 82 | 83 | 84 | def write_file(filename, content, mode="w"): 85 | """write_file will open a file, "filename" and write content, "content" 86 | and properly close the file 87 | """ 88 | with open(filename, mode) as filey: 89 | filey.writelines(content) 90 | return filename 91 | 92 | 93 | def read_file(filename, mode="r", readlines=True): 94 | """write_file will open a file, "filename" and write content, "content" 95 | and properly close the file 96 | """ 97 | with open(filename, mode) as filey: 98 | if readlines is True: 99 | content = filey.readlines() 100 | else: 101 | content = filey.read() 102 | return content 103 | 104 | 105 | # Yaml 106 | 107 | 108 | def read_yaml(filename, mode="r", quiet=False): 109 | """read a yaml file, only including sections between dashes""" 110 | stream = read_file(filename, mode, readlines=False) 111 | return _read_yaml(stream, quiet=quiet) 112 | 113 | 114 | def write_yaml(yaml_dict, filename, mode="w"): 115 | """write a dictionary to yaml file 116 | 117 | Parameters 118 | ========== 119 | yaml_dict: the dict to print to yaml 120 | filename: the output file to write to 121 | pretty_print: if True, will use nicer formatting 122 | """ 123 | with open(filename, mode) as filey: 124 | filey.writelines(yaml.dump(yaml_dict)) 125 | return filename 126 | 127 | 128 | def _read_yaml(section, quiet=False): 129 | """read yaml from a string, either read from file (read_frontmatter) or 130 | from yml file proper (read_yaml) 131 | 132 | Parameters 133 | ========== 134 | section: a string of unparsed yaml content. 135 | """ 136 | metadata = {} 137 | 138 | # PyYaml vs pyaml have subtle differences 139 | if hasattr(yaml, "FullLoader"): 140 | docs = yaml.load_all(section, Loader=yaml.FullLoader) 141 | else: 142 | docs = yaml.load_all(section) 143 | 144 | for doc in docs: 145 | if isinstance(doc, dict): 146 | for k, v in doc.items(): 147 | if not quiet: 148 | print("%s: %s" % (k, v)) 149 | metadata[k] = v 150 | return metadata 151 | 152 | 153 | # Json 154 | 155 | 156 | def write_json(json_obj, filename, mode="w", print_pretty=True): 157 | """write_json will (optionally,pretty print) a json object to file 158 | 159 | Parameters 160 | ========== 161 | json_obj: the dict to print to json 162 | filename: the output file to write to 163 | pretty_print: if True, will use nicer formatting 164 | """ 165 | with open(filename, mode) as filey: 166 | if print_pretty: 167 | filey.writelines(print_json(json_obj)) 168 | else: 169 | filey.writelines(json.dumps(json_obj)) 170 | return filename 171 | 172 | 173 | def print_json(json_obj): 174 | """just dump the json in a "pretty print" format""" 175 | return json.dumps(json_obj, indent=4, separators=(",", ": ")) 176 | 177 | 178 | def read_json(filename, mode="r"): 179 | """read_json reads in a json file and returns 180 | the data structure as dict. 181 | """ 182 | with open(filename, mode) as filey: 183 | data = json.load(filey) 184 | return data 185 | -------------------------------------------------------------------------------- /scompose/version.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (C) 2019-2024 Vanessa Sochat. 4 | 5 | This Source Code Form is subject to the terms of the 6 | Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed 7 | with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | """ 10 | 11 | __version__ = "0.1.19" 12 | AUTHOR = "Vanessa Sochat" 13 | AUTHOR_EMAIL = "vsoch@users.noreply.github.com" 14 | NAME = "singularity-compose" 15 | PACKAGE_URL = "http://github.com/singularityhub/singularity-compose" 16 | KEYWORDS = "singularity, compose" 17 | DESCRIPTION = "simple orchestration for singularity containers" 18 | LICENSE = "LICENSE" 19 | 20 | ################################################################################ 21 | # Global requirements 22 | 23 | 24 | INSTALL_REQUIRES = ( 25 | ("spython", {"min_version": "0.2.11"}), 26 | ("pyaml", {"min_version": "5.1.1"}), 27 | ) 28 | 29 | INSTALL_REQUIRES_CHECKS = INSTALL_REQUIRES + (("jsonschema", {"min_version": None}),) 30 | 31 | TESTS_REQUIRES = (("pytest", {"min_version": "4.6.2"}),) 32 | 33 | INSTALL_REQUIRES_ALL = INSTALL_REQUIRES 34 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = benchmarks docs 3 | max-line-length = 100 4 | ignore = E1 E2 E5 W5 5 | per-file-ignores = 6 | scompose/utils/__init__.py:F401 7 | scompose/client/__init__.py:F401 8 | scompose/__init__.py:F401 9 | scompose/config/__init__.py:F401 10 | scompose/logger/__init__.py:F401 11 | scompose/project/__init__.py:F401 12 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import find_packages, setup 4 | 5 | ################################################################################ 6 | # HELPER FUNCTIONS ############################################################# 7 | ################################################################################ 8 | 9 | 10 | def get_lookup(): 11 | """get version by way of sregistry.version, returns a 12 | lookup dictionary with several global variables without 13 | needing to import singularity 14 | """ 15 | lookup = dict() 16 | version_file = os.path.join("scompose", "version.py") 17 | with open(version_file) as filey: 18 | exec(filey.read(), lookup) 19 | return lookup 20 | 21 | 22 | # Read in requirements 23 | def get_reqs(lookup=None, key="INSTALL_REQUIRES"): 24 | """get requirements, mean reading in requirements and versions from 25 | the lookup obtained with get_lookup""" 26 | 27 | if lookup is None: 28 | lookup = get_lookup() 29 | 30 | install_requires = [] 31 | for module in lookup[key]: 32 | module_name = module[0] 33 | module_meta = module[1] 34 | if "exact_version" in module_meta: 35 | dependency = "%s==%s" % (module_name, module_meta["exact_version"]) 36 | elif "min_version" in module_meta: 37 | if module_meta["min_version"] is None: 38 | dependency = module_name 39 | else: 40 | dependency = "%s>=%s" % (module_name, module_meta["min_version"]) 41 | install_requires.append(dependency) 42 | return install_requires 43 | 44 | 45 | # Make sure everything is relative to setup.py 46 | install_path = os.path.dirname(os.path.abspath(__file__)) 47 | os.chdir(install_path) 48 | 49 | # Get version information from the lookup 50 | lookup = get_lookup() 51 | VERSION = lookup["__version__"] 52 | NAME = lookup["NAME"] 53 | AUTHOR = lookup["AUTHOR"] 54 | AUTHOR_EMAIL = lookup["AUTHOR_EMAIL"] 55 | PACKAGE_URL = lookup["PACKAGE_URL"] 56 | KEYWORDS = lookup["KEYWORDS"] 57 | DESCRIPTION = lookup["DESCRIPTION"] 58 | LICENSE = lookup["LICENSE"] 59 | with open("README.md") as filey: 60 | LONG_DESCRIPTION = filey.read() 61 | 62 | ################################################################################ 63 | # MAIN ######################################################################### 64 | ################################################################################ 65 | 66 | 67 | if __name__ == "__main__": 68 | INSTALL_REQUIRES = get_reqs(lookup) 69 | INSTALL_REQUIRES_ALL = get_reqs(lookup, "INSTALL_REQUIRES_ALL") 70 | INSTALL_REQUIRES_CHECKS = get_reqs(lookup, "INSTALL_REQUIRES_CHECKS") 71 | TESTS_REQUIRES = get_reqs(lookup, "TESTS_REQUIRES") 72 | 73 | setup( 74 | name=NAME, 75 | version=VERSION, 76 | author=AUTHOR, 77 | author_email=AUTHOR_EMAIL, 78 | maintainer=AUTHOR, 79 | maintainer_email=AUTHOR_EMAIL, 80 | packages=find_packages(), 81 | include_package_data=True, 82 | zip_safe=False, 83 | url=PACKAGE_URL, 84 | license=LICENSE, 85 | description=DESCRIPTION, 86 | long_description=LONG_DESCRIPTION, 87 | long_description_content_type="text/markdown", 88 | keywords=KEYWORDS, 89 | install_requires=INSTALL_REQUIRES, 90 | setup_requires=["pytest-runner"], 91 | tests_require=TESTS_REQUIRES, 92 | extras_require={ 93 | "all": [INSTALL_REQUIRES_ALL], 94 | "checks": [INSTALL_REQUIRES_CHECKS], 95 | }, 96 | classifiers=[ 97 | "Intended Audience :: Science/Research", 98 | "Intended Audience :: Developers", 99 | "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", 100 | "Programming Language :: C", 101 | "Programming Language :: Python", 102 | "Topic :: Software Development", 103 | "Topic :: Scientific/Engineering", 104 | "Operating System :: Unix", 105 | "Programming Language :: Python :: 2.7", 106 | "Programming Language :: Python :: 3", 107 | ], 108 | entry_points={"console_scripts": ["singularity-compose=scompose.client:start"]}, 109 | ) 110 | --------------------------------------------------------------------------------