Skip to content

Commit

Permalink
Add deps mount to mount dependencies from host
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinMind committed Dec 16, 2024
1 parent 9fb5aca commit 9ab7c82
Show file tree
Hide file tree
Showing 14 changed files with 465 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
backups
build*.py
buildx-bake-metadata.json
deps
deps/*
docker*.yml
docker/artifacts/*
docs/_build
Expand Down
5 changes: 5 additions & 0 deletions .github/actions/run-docker/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ inputs:
description: 'Skip data backup'
required: false
default: 'true'
olympia_deps:
description: 'Which dependencies to install at runtime? (development|production)'
required: false
default: 'development'

runs:
using: 'composite'
Expand All @@ -40,6 +44,7 @@ runs:
COMPOSE_FILE: ${{ inputs.compose_file }}
HOST_UID: ${{ steps.id.outputs.id }}
DATA_BACKUP_SKIP: ${{ inputs.data_backup_skip }}
OLYMPIA_DEPS: ${{ inputs.olympia_deps }}
run: |
# Start the specified services
make up
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/_test_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ jobs:
name: |
version: '${{ matrix.version }}' |
compose_file: '${{ matrix.compose_file }}'
olympia_deps: '${{ matrix.olympia_deps }}'
strategy:
fail-fast: false
matrix:
Expand All @@ -55,6 +56,9 @@ jobs:
compose_file:
- docker-compose.yml
- docker-compose.yml:docker-compose.ci.yml
olympia_deps:
- development
- production
steps:
- uses: actions/checkout@v4
- shell: bash
Expand All @@ -64,6 +68,7 @@ jobs:
Values passed to the action:
version: ${{ matrix.version }}
compose_file: ${{ matrix.compose_file }}
olympia_deps: ${{ matrix.olympia_deps }}
EOF
- name: ${{ matrix.version == 'local' && 'Uncached Build' || 'Pull' }} Check
uses: ./.github/actions/run-docker
Expand All @@ -74,6 +79,7 @@ jobs:
with:
version: ${{ matrix.version }}
compose_file: ${{ matrix.compose_file }}
olympia_deps: ${{ matrix.olympia_deps }}
run: make check
- name: Cached Build Check
uses: ./.github/actions/run-docker
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
backups
build*.py
buildx-bake-metadata.json
deps
deps/*
docker*.yml
docker/artifacts/*
docs/_build
Expand Down Expand Up @@ -56,3 +56,4 @@ tmp/*
!docker-compose.ci.yml
!docker-compose.private.yml
!private/README.md
!deps/.gitkeep
30 changes: 4 additions & 26 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,12 @@ ENV NPM_ARGS="--prefix ${NPM_CONFIG_PREFIX} --cache ${NPM_CACHE_DIR} --loglevel
# All we need in "base" is pip to be installed
#this let's other layers install packages using the correct version.
RUN \
--mount=type=bind,source=scripts/install_deps.py,target=${HOME}/scripts/install_deps.py \
# Files required to install pip dependencies
--mount=type=bind,source=./requirements/pip.txt,target=${HOME}/requirements/pip.txt \
--mount=type=cache,target=${PIP_CACHE_DIR},uid=${OLYMPIA_UID},gid=${OLYMPIA_UID} \
<<EOF
# Work arounds "Multiple .dist-info directories" issue.
rm -rf /deps/build/*
${PIP_COMMAND} install --progress-bar=off --no-deps --exists-action=w -r requirements/pip.txt
${HOME}/scripts/install_deps.py pip
EOF

# TODO: we should remove dependency on the environment variable
Expand All @@ -129,6 +128,7 @@ ENV DOCKER_TARGET=${DOCKER_TARGET}
FROM base AS pip_production

RUN \
--mount=type=bind,source=scripts/install_deps.py,target=${HOME}/scripts/install_deps.py \
# Files required to install pip dependencies
--mount=type=bind,source=./requirements/prod.txt,target=${HOME}/requirements/prod.txt \
# Files required to install npm dependencies
Expand All @@ -138,26 +138,7 @@ RUN \
--mount=type=cache,target=${PIP_CACHE_DIR},uid=${OLYMPIA_UID},gid=${OLYMPIA_UID} \
--mount=type=cache,target=${NPM_CACHE_DIR},uid=${OLYMPIA_UID},gid=${OLYMPIA_UID} \
<<EOF
${PIP_COMMAND} install --progress-bar=off --no-deps --exists-action=w -r requirements/prod.txt
npm ci ${NPM_ARGS} --include=prod
EOF

FROM pip_production AS pip_development

RUN \
# Files required to install pip dependencies
--mount=type=bind,source=./requirements/prod.txt,target=${HOME}/requirements/prod.txt \
--mount=type=bind,source=./requirements/dev.txt,target=${HOME}/requirements/dev.txt \
# Files required to install npm dependencies
--mount=type=bind,source=package.json,target=/deps/package.json \
--mount=type=bind,source=package-lock.json,target=/deps/package-lock.json \
# Mounts for caching dependencies
--mount=type=cache,target=${PIP_CACHE_DIR},uid=${OLYMPIA_UID},gid=${OLYMPIA_UID} \
--mount=type=cache,target=${NPM_CACHE_DIR},uid=${OLYMPIA_UID},gid=${OLYMPIA_UID} \
<<EOF
${PIP_COMMAND} install --progress-bar=off --no-deps --exists-action=w -r requirements/prod.txt
${PIP_COMMAND} install --progress-bar=off --no-deps --exists-action=w -r requirements/dev.txt
npm install ${NPM_ARGS} --no-save
${HOME}/scripts/install_deps.py prod
EOF

FROM base AS locales
Expand Down Expand Up @@ -213,9 +194,6 @@ SHELL ["/bin/sh", "-c"]

FROM sources AS development

# Copy dependencies from `pip_development`
COPY --from=pip_development --chown=olympia:olympia /deps /deps

FROM sources AS production

# Copy compiled locales from builder
Expand Down
20 changes: 14 additions & 6 deletions Makefile-docker
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ REQUIRED_FILES := \
/deps/package-lock.json \
/addons-server-docker-container \

# Build list of dependencies to install
DEPS = pip prod
# If we're running a development image, then we should install the development dependencies
ifeq ($(OLYMPIA_DEPS), development)
DEPS += dev
endif

.PHONY: help_redirect
help_redirect:
@$(MAKE) help --no-print-directory
Expand All @@ -28,12 +35,9 @@ check_debian_packages: ## check the existence of multiple debian packages

.PHONY: check_pip_packages
check_pip_packages: ## check the existence of multiple python packages
@ ./scripts/check_pip_packages.sh prod.txt
# "production" corresponds to the "propduction" DOCKER_TARGET defined in the Dockerfile
# When the target is "production" it means we cannot expect dev.txt dependencies to be installed.
@if [ "$(DOCKER_TARGET)" != "production" ]; then \
./scripts/check_pip_packages.sh dev.txt; \
fi
@for dep in $(DEPS); do \
./scripts/check_pip_packages.sh $$dep.txt; \
done

.PHONY: check_files
check_files: ## check the existence of multiple files
Expand Down Expand Up @@ -77,6 +81,10 @@ update_assets:
$(PYTHON_COMMAND) manage.py collectstatic --noinput


.PHONY: update_deps
update_deps: ## Update the dependencies
$(HOME)/scripts/install_deps.py $(DEPS)

# TOOD: remove this after we migrate addons-frontned to not depend on it.
.PHONY: setup-ui-tests
setup-ui-tests:
Expand Down
26 changes: 20 additions & 6 deletions Makefile-os
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ CLEAN_PATHS := \
version.json \
logs \
buildx-bake-metadata.json \
deps \

.PHONY: help_redirect
help_redirect:
Expand Down Expand Up @@ -150,19 +149,34 @@ docker_clean_build_cache: ## Remove buildx build cache
.PHONY: clean_docker
clean_docker: docker_compose_down docker_mysqld_volume_remove docker_clean_images docker_clean_volumes docker_clean_build_cache ## Remove all docker resources taking space on the host machine

# Explicitly run initialize via the web container as make can get confused
# both routing the command to the web container and
# routing the command to the proper target.
.PHONY: docker_compose_run
docker_compose_run: docker_mysqld_volume_create ## Run a command in the docker compose project
docker compose run \
--rm \
--no-deps \
$(DOCKER_RUN_ARGS) \
web \
$(ARGS)

.PHONY: docker_update_deps
docker_update_deps: ## Update the dependencies in the container based on the docker tag and target
mkdir -p deps
# Install the production dependencies always since we mount the ./deps directory
$(MAKE) docker_compose_run ARGS='make update_deps'


.PHONY: up_pre
up_pre: setup docker_pull_or_build ## Pre-up the environment, setup files, volumes and host state
up_pre: setup docker_pull_or_build docker_update_deps ## Pre-up the environment, setup files, volumes and host state

.PHONY: up_start
up_start: docker_mysqld_volume_create ## Start the docker containers
docker compose up $(DOCKER_COMPOSE_ARGS) $(ARGS)

.PHONY: up_post
up_post: docker_clean_images docker_clean_volumes ## Post-up the environment, setup files, volumes and host state
# Explicitly run initialize via the web container as make can get confused
# both routing the command to the web container and
# routing the command to the proper target.
docker compose exec --user olympia web make -f Makefile-docker initialize ARGS=$(shell echo "'$(INITIALIZE_ARGS)'")

.PHONY: up
up: up_pre up_start up_post ## Up the environment
Expand Down
Empty file added deps/.gitkeep
Empty file.
4 changes: 2 additions & 2 deletions docker-compose.ci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
services:
olympia_volumes:
environment:
- HOST_UID=9500
volumes:
- storage:/data/olympia/storage
- /data/olympia
- data_deps_production:/deps

worker:
extends:
Expand All @@ -23,3 +22,4 @@ services:

volumes:
storage:
data_deps_production:
17 changes: 13 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ x-env-mapping: &env
- HISTIGNORE=ls:exit:"cd .."
- HISTCONTROL=erasedups
- CIRCLECI
- HOST_UID
- DEBUG
- DATA_BACKUP_SKIP

x-site-static-mount: &site-static-mount
Expand Down Expand Up @@ -49,6 +47,7 @@ services:
# so we just sleep indefinitely instead.
command: ["sleep", "infinity"]
volumes:
- data_deps_development:/deps
- *site-static-mount
worker:
<<: *olympia
Expand All @@ -64,10 +63,15 @@ services:
]
volumes:
- .:/data/olympia
# Mount dependencies from the host
# make docker_update_deps ensures this volume has correct contents
- data_deps_development:/deps

extra_hosts:
- "olympia.test:127.0.0.1"
restart: on-failure:5
depends_on:
- olympia_volumes
- mysqld
- elasticsearch
- redis
Expand All @@ -85,8 +89,6 @@ services:
# and would otherwiser be deleted by mounting the cwd volume above
- data_static_build:/data/olympia/static-build
- *site-static-mount
depends_on:
- olympia_volumes

nginx:
image: nginx
Expand Down Expand Up @@ -199,6 +201,13 @@ volumes:
# Volume for rabbitmq/redis to avoid anonymous volumes
data_rabbitmq:
data_redis:
# Volume for dependencies
data_deps_development:
driver: local
driver_opts:
type: none
o: bind
device: ${PWD}/deps
data_mysqld:
# Keep this value in sync with Makefile-os
# External volumes must be manually created/destroyed
Expand Down
94 changes: 94 additions & 0 deletions scripts/install_deps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env python3

import os
import shutil
import subprocess
import sys


def copy_package_json():
"""Copy package.json files to deps directory if they exist."""
try:
shutil.copy('/data/olympia/package.json', '/deps')
shutil.copy('/data/olympia/package-lock.json', '/deps')
except (IOError, OSError):
pass # Ignore if files don't exist or can't be copied


def main(targets):
# Constants
ALLOWED_NPM_TARGETS = set(['prod', 'dev'])
DOCKER_TAG = os.environ.get('DOCKER_TAG', 'local')
DOCKER_TARGET = os.environ.get('DOCKER_TARGET', '')
OLYMPIA_DEPS = os.environ.get('OLYMPIA_DEPS', '')

if not targets:
raise ValueError('No targets specified')

print(
'Updating deps... \n',
f'targets: {", ".join(targets)} \n',
f'docker_tag: {DOCKER_TAG} \n',
f'docker_target: {DOCKER_TARGET} \n',
f'olympia_deps: {OLYMPIA_DEPS} \n',
)

# If we are installing production dependencies or on a non local image
# we always remove existing deps as we don't know what was previously
# installed or in the host ./deps directory before running this script
if 'local' not in DOCKER_TAG or OLYMPIA_DEPS == 'production':
print('Removing existing deps')
for item in os.listdir('/deps'):
item_path = os.path.join('/deps', item)
if os.path.isdir(item_path) and item != 'cache':
shutil.rmtree(item_path)
else:
print('Updating existing deps')

# Copy package.json files
copy_package_json()

# Prepare the includes lists
pip_includes = []
npm_includes = []

# PIP_COMMAND is set by the Dockerfile
pip_command = os.environ['PIP_COMMAND']
pip_args = pip_command.split() + [
'install',
'--progress-bar=off',
'--no-deps',
'--exists-action=w',
]

# NPM_ARGS is set by the Dockerfile
npm_args_env = os.environ['NPM_ARGS']
npm_args = [
'npm',
'install',
'--no-save',
'--no-audit',
'--no-fund',
] + npm_args_env.split()

# Add the relevant targets to the includes lists
for target in targets:
pip_includes.append(target)
pip_args.extend(['-r', f'requirements/{target}.txt'])
if target in ALLOWED_NPM_TARGETS:
npm_includes.append(target)
npm_args.extend(['--include', target])

if pip_includes:
# Install pip dependencies
print(f"Installing pip dependencies: {', '.join(pip_includes)} \n")
subprocess.run(pip_args, check=True)

if npm_includes:
# Install npm dependencies
print(f"Installing npm dependencies: {', '.join(npm_includes)} \n")
subprocess.run(npm_args, check=True)


if __name__ == '__main__':
main(sys.argv[1:])
Loading

0 comments on commit 9ab7c82

Please sign in to comment.