Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker image for optimade on ghcr.io #1171

Merged
merged 19 commits into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 24 additions & 7 deletions .docker/run.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
#!/bin/bash
set -ex
#!/usr/bin/env bash
# Bash script to run upon starting the Dockerfile.
#
# Any extra parameters supplied to this script will be passed on to `uvicorn`.
# This can be nice if using the image for development (adding `--reload` and more).
set -e

if [ "${MAIN}" == "main_index" ]; then
PORT=5001
else
if [ "${OPTIMADE_CONFIG_FILE}" == "/app/optimade_config.json" ]; then
echo "Using the demo config file."
echo "Set the environment variable OPTIMADE_CONFIG_FILE to override this behaviour."
echo "For more configuration options, please see https://www.optimade.org/optimade-python-tools/configuration/."
echo "Note, the variable should point to a bound path within the container."
fi

if [ -z "${OPTIMADE_LOG_LEVEL}" ]; then
export OPTIMADE_LOG_LEVEL=info
fi

if [ "${OPTIMADE_DEBUG}" == "1" ]; then
export OPTIMADE_LOG_LEVEL=debug
fi

# Determine the server to run (standard or index meta-db)
if [ -z "${MAIN}" ] || ( [ "${MAIN}" != "main_index" ] && [ "${MAIN}" != "main" ] ); then
MAIN="main"
PORT=5000
fi

uvicorn optimade.server.$MAIN:app --host 0.0.0.0 --port $PORT
uvicorn optimade.server.${MAIN}:app --host 0.0.0.0 --port 5000 --log-level ${OPTIMADE_LOG_LEVEL} "$@"
101 changes: 101 additions & 0 deletions .github/workflows/cd_container_image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Build and Publish Container Image

on:
workflow_call:
inputs:
release:
description: 'Whether or not this is a release.'
default: false
required: false
type: boolean
checkout_ref:
description: 'The git ref to checkout and use as source.'
default: master
required: false
type: string

jobs:
publish_container_image:
name: Publish Container image on GH Container Registry
if: github.repository_owner == 'Materials-Consortia'
runs-on: ubuntu-latest

env:
IMAGE_NAME: optimade

steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: true
ref: ${{ inputs.checkout_ref }}

- name: Retrieve version
run: |
# Get current OPTIMADE version from optimade/__init__.py
regex="^__version__ = (\"|')(.*)(\"|')$"
while IFS="" read -r line || [ -n "${line}" ]; do
if [[ "${line}" =~ $regex ]]; then
VERSION="${BASH_REMATCH[2]}"
fi
done < optimade/__init__.py

echo "VERSION=${VERSION}" >> $GITHUB_ENV
echo VERSION=${VERSION}

- name: Set source ref
run: |
if [[ "${{ inputs.release }}" == "true" ]]; then
# Use tag for metadata instead of commit SHA
SOURCE_REF=v${{ env.VERSION }}
else
SOURCE_REF=$(git rev-parse HEAD)
fi

echo "SOURCE_REF=${SOURCE_REF}" >> $GITHUB_ENV
echo SOURCE_REF=${SOURCE_REF}

- name: Build image
run: |
docker build \
--file Dockerfile \
--tag "${IMAGE_NAME}" \
--label "gh_actions_runnumber=${GITHUB_RUN_ID}" \
--label "org.openctonainers.image.title=OPTIMADE" \
--label "org.opencontainers.image.description=A server implementation for serving an OPTIMADE API." \
--label "org.opencontainers.image.source=https://github.com/${{ github.repository }}/tree/${{ env.SOURCE_REF }}" \
--label "org.opencontainers.image.documentation=https://github.com/${{ github.repository }}/blob/${{ env.SOURCE_REF }}/README.md" \
--label "org.opencontainers.image.licenses=MIT" \
--label "org.opencontainers.image.url=https://github.com/${{ github.repository }}/pkgs/container/${IMAGE_NAME}" \
--label "org.opencontainers.image.vendor=${{ github.repository_owner }}" \
--label "org.opencontainers.image.version=${{ env.VERSION }}" \
--label "org.opencontainers.image.base.name=docker.io/library/python:3.10-slim" \
.

- name: Create full image ID and tag with 'develop'
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/${IMAGE_NAME}

# Change all uppercase to lowercase
IMAGE_ID=$(echo ${IMAGE_ID} | tr '[A-Z]' '[a-z]')

echo "IMAGE_ID=${IMAGE_ID}" >> $GITHUB_ENV
echo IMAGE_ID=${IMAGE_ID}

# Tag with "develop"
docker tag ${IMAGE_NAME} ${IMAGE_ID}:develop

- name: Tag with version and `latest` - if it's a release
if: inputs.release
run: |
docker tag ${IMAGE_NAME} ${{ env.IMAGE_ID }}:${{ env.VERSION }}
docker tag ${IMAGE_NAME} ${{ env.IMAGE_ID }}:latest

- name: Log in to registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin

- name: Push image
run: |
docker image inspect ${IMAGE_NAME}
docker push --all-tags ${{ env.IMAGE_ID }}
12 changes: 12 additions & 0 deletions .github/workflows/cd_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,15 @@ jobs:
run: |
mike deploy --push --remote origin --branch gh-pages --update-aliases --config-file mkdocs.yml ${GITHUB_REF#refs/tags/v} stable
mike deploy --push --remote origin --branch gh-pages --update-aliases --config-file mkdocs.yml latest master

publish_container_image:
name: Publish container image
needs: publish
uses: ./.github/workflows/cd_container_image.yml
with:
release: true
checkout_ref: ${{ env.PUBLISH_UPDATE_BRANCH }}
secrets: inherit
permissions:
packages: write
contents: read
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ jobs:
with:
submodules: true

- name: Build the Docker images
run: docker-compose build
- name: Build the container image
run: docker build -f Dockerfile --build-arg CONFIG_FILE=.docker/docker_config.json --tag optimade .

- name: Start Docker image - server
run: |
docker-compose up optimade &
docker run --rm -d -p 3213:5000 --name optimade optimade
.github/utils/wait_for_it.sh localhost:3213 -t 120
sleep 15

Expand All @@ -119,7 +119,7 @@ jobs:

- name: Start Docker image - index server
run: |
docker-compose up optimade-index &
docker run --rm -d -p 3214:5000 --name optimade-index -e MAIN=main_index optimade
.github/utils/wait_for_it.sh localhost:3214 -t 120
sleep 15

Expand Down
25 changes: 22 additions & 3 deletions .github/workflows/ci_cd_updated_master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,28 @@ env:
GIT_USER_EMAIL: "dev@optimade.org"

jobs:
deploy-docs:
deploy_docs:
name: Deploy `latest` documentation
if: github.repository_owner == 'Materials-Consortia'
runs-on: ubuntu-latest
outputs:
release_run: ${{ steps.release_check.outputs.release_run }}

steps:
- name: Release check
id: release_check
run: |
COMMIT_MSG="$(gh api /repos/${{ github.repository}}/commits/${{ env.DEFAULT_REPO_BRANCH }} --jq '.commit.message')"
if [[ "${COMMIT_MSG}" =~ ^Release\ v.*\ -\ Changelog$ ]]; then
echo "In a release - do not run this job !"
echo "RELEASE_RUN=true" >> $GITHUB_ENV
RELEASE_RUN=true
else
echo "Not a release - update docs"
echo "RELEASE_RUN=false" >> $GITHUB_ENV
RELEASE_RUN=false
fi

echo "RELEASE_RUN=${RELEASE_RUN}" >> $GITHUB_ENV
echo "::set-output name=release_run::${RELEASE_RUN}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Expand Down Expand Up @@ -145,3 +151,16 @@ jobs:
env:
HD_OPTIMADE_CONFIG_FILE: /app/tests/test_config.json
HD_OPTIMADE_BASE_URL: https://optimade.herokuapp.com

publish_container_image:
name: Publish container image
needs: deploy_docs
uses: ./.github/workflows/cd_container_image.yml
if: needs.deploy_docs.outputs.release_run == 'false'
with:
release: false
checkout_ref: ${{ github.sha }}
secrets: inherit
permissions:
packages: write
contents: read
30 changes: 18 additions & 12 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
FROM python:3.7
FROM python:3.10-slim

# Prevent writing .pyc files on the import of source modules
# and set unbuffered mode to ensure logging outputs
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

WORKDIR /app

# copy repo contents
COPY setup.py README.md ./
# Copy repo contents
COPY setup.py setup.cfg LICENSE MANIFEST.in README.md .docker/run.sh ./
COPY optimade ./optimade
COPY providers/src/links/v1/providers.json ./optimade/server/data/
RUN pip install -e .[server]

ARG PORT=5000
EXPOSE ${PORT}

COPY .docker/run.sh ./
RUN apt-get purge -y --auto-remove \
&& rm -rf /var/lib/apt/lists/* \
&& pip install --no-cache-dir --trusted-host pypi.org --trusted-host files.pythonhosted.org -U pip setuptools wheel flit \
&& pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org .[server]

# Setup server configuration
ARG CONFIG_FILE=optimade_config.json
COPY ${CONFIG_FILE} ./config.json
ENV OPTIMADE_CONFIG_FILE /app/config.json
COPY ${CONFIG_FILE} ./optimade_config.json
ENV OPTIMADE_CONFIG_FILE=/app/optimade_config.json

CMD ["/app/run.sh"]
# Run app
EXPOSE 5000
ENTRYPOINT [ "/app/run.sh" ]
58 changes: 57 additions & 1 deletion INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ This package can be installed from PyPI, or by cloning the repository, depending

1. To use the `optimade` Python package as a library, (e.g., using the models for validation, parsing filters with the grammar, or using the command-line tool `optimade-validator` tool), it is recommended that you install the latest release of the package from PyPI with `pip install optimade`.
2. If you want to run, use or modify the reference server implementation, then it is recommended that you clone this repository and install it from your local files (with `pip install .`, or `pip install -e .` for an editable installation).
As an alternative, you can run the `optimade` container image (see the [Container image](#container-image) section below).

## The index meta-database

This package may be used to setup and run an [OPTIMADE index meta-database](https://github.com/Materials-Consortia/OPTIMADE/blob/develop/optimade.rst#index-meta-database).
Clone this repository and install the package locally with `pip install -e .[server]`.

!!! info
To avoid installing anything locally and instead use the docker image, please see the section [Container image](#container-image) below.

There is a built-in index meta-database set up to populate a `mongomock` in-memory database with resources from a static `json` file containing the `child` resources you, as a database provider, want to serve under this index meta-database.
The location of that `json` file is controllable using the `index_links_path` property of the configuration or setting via the environment variable `optimade_index_links_path`.

Expand All @@ -34,7 +38,7 @@ All contributed Python code, must use the [black](https://github.com/ambv/black)

```sh
# Clone this repository to your computer
git clone git@github.com:Materials-Consortia/optimade-python-tools.git
git clone --recursive git@github.com:Materials-Consortia/optimade-python-tools.git
cd optimade-python-tools

# Ensure a Python>=3.7 (virtual) environment (example below using Anaconda/Miniconda)
Expand Down Expand Up @@ -95,6 +99,7 @@ The easiest way to deploy these databases and run the tests is with Docker, as s
These commands should be run from a local optimade-python-tools directory.

The following command starts a local Elasticsearch v6 instance, runs the test suite, then stops and deletes the containers (required as the tests insert some data):

```shell
docker run -d --name elasticsearch_test -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:6.8.23 \
&& sleep 10 \
Expand All @@ -103,8 +108,59 @@ docker container stop elasticsearch_test; docker container rm elasticsearch_test
```

The following command starts a local MongoDB instance, runs the test suite, then stops and deletes the containers:

```shell
docker run -d --name mongo_test -p 27017:27017 -d mongo:4.4.6 \
&& OPTIMADE_DATABASE_BACKEND="mongodb" py.test; \
docker container stop mongo_test; docker container rm mongo_test
```

## Container image

### Retrieve the image

The [`optimade` container image](https://github.com/Materials-Consortia/optimade-python-tools/pkgs/container/optimade) is available from the [GitHub Container registry](https://ghcr.io).
To pull the latest version using [Docker](https://docs.docker.com/) run the following:

```shell
docker pull ghcr.io/materials-consortia/optimade:latest
CasperWA marked this conversation as resolved.
Show resolved Hide resolved
```

!!! note
The tag, `:latest`, can be left out, as the `latest` version will be pulled by default.

If you'd like to pull a specific version, this can be done by replacing `latest` in the command above with the version of choice, e.g., `0.17.1`.
To see which versions are available, please go [here](https://github.com/Materials-Consortia/optimade-python-tools/pkgs/container/optimade/versions).

You can also install the `develop` version.
This is an image built from the latest commit on the `master` branch and should never be used for production.

### Run a container

When starting a container from the image there are a few choices.
It is possible to run either a standard OPTIMADE server, or an [index meta-database](https://github.com/Materials-Consortia/OPTIMADE/blob/master/optimade.rst#index-meta-database) server from this image.
Note, these servers can be run in separate containers at the same time.
The key is setting the environment variable `MAIN`.

| **MAIN** | **Result** |
|:---:|:--- |
| `main` | Standard OPTIMADE server. |
| `main_index` | Index meta-database OPTIMADE server. |

Using Docker, the following command will run a container from the image:

```shell
# rm will remove container when it exits.
CasperWA marked this conversation as resolved.
Show resolved Hide resolved
# detach will run the server in the background.
# publish will run the server from the host port 8080.
# name will give the container a handy name for referencing later.
docker run \
--rm \
--detach \
--publish 8080:5000 \
--env MAIN=main \
--name my-optimade \
ghcr.io/materials-consortia/optimade:latest
```

The server should now be available at [localhost:8080](http://localhost:8080).
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- markdownlint-disable MD033 -->
<!-- markdownlint-disable-next-line MD041 -->
<img width="10%" align="left" src="images/optimade_logo_180x180.svg">

# OPTIMADE Python tools
Expand Down
29 changes: 0 additions & 29 deletions docker-compose.yml

This file was deleted.