addons-server icon indicating copy to clipboard operation
addons-server copied to clipboard

Generate version.json as part of docker build:

Open KevinMind opened this issue 10 months ago • 0 comments

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.

KevinMind avatar Apr 24 '24 14:04 KevinMind