addons-server
addons-server copied to clipboard
Generate version.json as part of docker build:
Relates to: https://mozilla-hub.atlassian.net/browse/ADDSRV-720
Description
Changes required to run docker compose commands in CI and Locally with minimal configuration required from the developer.
Context
This PR is a prerequisite to completing the migration of our CircleCI to Github actions. Many jobs depend on all or some services running and this allows us to achieve compatible environments in local and ci with 1 set of configs.
Some of the key problems that were solved here are
Installing olympia
We install olympia using eggs. This has 2 important constraints on our docker flow. 1 we have to install olympia after the sources are in the container. the .egg-info directory will contain references to the source files and so they must be present when installing. 2) When we mount .:/data/olympia in local developmet we remove the egg-info directory and this breaks the installation. This has been masked because we "reinstall" everything multiple times in the development flow but surfaces when you try to implement docker compose in CI.
The solution is to override the /data/olympia/src/olympia.egg-info mount after mounting the root. This forces docker to use the folder already present in the image.
Mounting dependencies.
In local development we mount our local ./deps:/deps to the container. This directory on the host is typically empty and then fills up with files after we re install dependencies. This is slow and redundant work, especially in CI.
The solution here was to implement a multistage final build. In our docker file we install production dependencies in one stage and layer dev dependencies on another stage. In the final stage we can select "production" or "development" to copy over the required deps. This allows us to encapsulate all dependency management to the image. In CI you can just build the image and run it without the ./deps mount.
For local develepment we can copy files out of the image into the host and remount them when running the container to maintain the "visibility" of the container directory.
NOTE: we should revisit this as docker container folders and data volumes can be inspected at least in VS code without mounting to the host. We could potentially simplify and speed up local development by removing the mount all together.
Compose files
Our compose file was designed entirely for local development. Making it work in CI and local proved difficult with the single file approach. The solution was to use the "includes" directive allowing us to break apart the "Features" of our compose config and then re "compose" them in 3 final configurations:
- docker-compose.yml (default, used for local development)
- docker-compose.ci.yml (default in CI, doesn't mount any volumes)
- docker-compose.olympia.yml (mounts the olympia volume but not the "deps") (Becomes redundant if we remove the deps volume)
Version.json
Our current sentry implementation is expecting a version.json to exist to determine metadata about the current build. There is a fallback if the file doesn't exist to use git files. If you run in CI without mounting .:/data/olympia those files won't exist. Solution was to remove the fallback and unify the version.json creation as a definitive part of the build process. running make build_docker_image
will first create the version.json which is then copied into the image during build.
Bake
By moving so much of our configuration to docker-compose we can utilize buildx bake to build our image using the compose configuration. This is probably not "necessary" but simplifies our configuration between local and CI builds.
Dynamic image:version
We had previously hard coded our docker image to "mozilla/addons-server:latest" and weren't even building this image locally.
Solution here is to use dynamic DOCKER_<NAME> environment variables during both local development and CI. This allows us to specify the context before doing anything. Currently we have
DOCKER_IMAGE DOCKER_VERSION DOCKER_TARGET DOCKER_COMMIT
These are used for building and running the containers and let you configure exactly how you want it to run from a single source of truth.
OLYMPIA_UID
Our docker container has an olympia
user with the UID/GID 9500. This id is the owner of /deps and /data/olympia where most relevant files for our app reside.
Whenever you mount .:/data/olympia you are adding files from the host that are owned by the host user (probably not 9500) and so can create permission conflicts in the running container.
This is further beligered by the fact the our script for setting the olympia user ID in a running container can get stuck reading UID from the environment and not from an explicitly set value, since we are reading from the shell set UID, the .env file and the docker compose and any one of them could win out.
The solution is to rename the variable specifying the container UID to OLMYPIA_UID. This prevents getting conflated with the current user ID. Secondly we only set this ID when we mount the repo files. Otherwise we keep the 9500 ID. This allows us to control the behaviour via top level compose files.
Fix olympia.
I've migrated the ./fix_olympia_user.sh script to be the "entrypoint" for our containers. This simply guarantees that whenever you run the container, you will ensure the UID is correct for the olympia user without having to manually execute it prior.
Testing
docker-compose.yml
Run locally. First clean up your environment
git checkout migrate-ci-gha
git clean -xdf
make clean_docker
Run initialize
make initialize_docker
Expect the environment is setup and the app is running. smoke test to make sure it all works.
make shell
pytest -n auto -v src/olympia/amo/
Make sure you can run tests.
docker-compose.ci.yml
Spin down your environment if not already
docker compose down --rmi local --volumes
Build for CI
echo "COMPOSE_FILE=docker-compose.ci.yml" >> .env
make build_docker_image
Now up the containers
make docker_compose_up
Run a command
cat <<EOF | docker compose exec web sh
pytest -n auto -m src/olympia/amo/
EOF
This will run without mounting olympia. You can test by modifying the ./deps folder or any file in the repo and checking if the file is pristine in the container.
echo "Hello" > test.txt
docker compose exec web cat test.txt
Expect an error because you haven't mounted.
Change the target to production, expect error "supervisor not found"
echo "DOCKER_TARGET=production" >> .env
Or modify if already set.
make docker_compose_up
Check the logs of web
docker compose logs web
Expect the supervisor error because this is a dev dependency.
Running
make update_deps
will fix the issue.
docker-compose.olympia.yml
Run the same steps as for CI, but use docker-compose.olympia.yml
. Run the make extract_locales
and you should expect to see file updates in the host. However, there should be nothing in the ./deps folder as the volume is not mounted. Useful for running actions in CI that should modify files but not requiring access to deps on the host.