medusa-project / book-tracker

Medusa Book Tracker
0 stars 0 forks source link

Get tests running in Docker #23

Closed adolski closed 1 year ago

adolski commented 1 year ago

I can't remember if you had Docker experience already, @gaurijo, but if not, one way to learn about it is through the Docker documentation. You also have access to LinkedIn Learning courses through the U of I, and it looks like they have quite a few on Docker.

The basic idea is, instead of running rails test from our shell, we want to spin up the Book Tracker in a container and run rails test in that. Alongside the Book Tracker container we will need another container for PostgreSQL, so we will want to use docker compose to orchestrate these multiple containers.

The Book Tracker is already containerized for running in AWS ECS. But the image is built for running the web app and not the tests. It would be possible to use the same image for both, but, in other projects, the way I've set it up is with separate images, generated by separate Dockerfiles.

An example of another app that is setup with separate web app & test containers, and works in ECS and GitHub Actions, is metaslurp. In this project there is a root-level Dockerfile (for the web app) and a separate test Dockerfile in docker/metaslurp-test/Dockerfile. The docker-compose.yml file is used for testing, and it spins up three separate containers, one for the main app and two for its dependent services.

One thing I'm not sure about yet is how to build an x86 container image from an ARM Mac, but I think that's a solvable problem once we get to it.

gaurijo commented 1 year ago

I dabbled with Docker for a project several months ago but didn't experience setting it up myself, so I'll definitely take a look at the resources you linked. Thanks!

gaurijo commented 1 year ago

@adolski I have made some decent progress (i think), with a docker image built for the tests but am figuring out the best way to utilize Docker Compose to run everything at once. Right now in my docker-compose.yml file, I have two services (book-tracker and postgres db), but I'm not sure if I need to split this up into three services (book-tracker app, book-tracker tests, and postgres db?) or if the way I have it set up covers both the web app and the tests:

version: '3'

services:
  postgres: 
    hostname: postgres
    image: postgres:14-alpine
    environment:
      POSTGRES_DB: book_tracker
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres 

  book-tracker:
    build: 
      context: . 
      dockerfile: docker/book-tracker-test/Dockerfile
    environment:
      RAILS_ENV: test
    ports: 
      - "3000:3000"
    depends_on:
      - postgres 

Some notes/questions:

  1. I chose postgres:14-alpine based on seeing that I have postgres v 14running, but unsure if that's relevant
  2. Inside the book-tracker service, I'm calling on the tests, confirming what port the db should be exposed to, and added a depends_on so that the book-tracker service knows to wait for postgres connection; do I also need to include any commands here?
  3. When I check docker ps I get the following output:
    CONTAINER ID   IMAGE                COMMAND                  CREATED          STATUS          PORTS      NAMES
    a8655e516656   postgres:14-alpine   "docker-entrypoint.s…"   12 minutes ago   Up 12 minutes   5432/tcp   book-tracker-postgres-1
  4. However when I run docker-compose up I get several errors aboutport 5432 that I'm confused by
    could not connect to server: Connection refused (ActiveRecord::ConnectionNotEstablished)
    book-tracker-book-tracker-1  |  Is the server running on host "localhost" (127.0.0.1) and accepting
    book-tracker-book-tracker-1  |  TCP/IP connections on port 5432?

I'll continue reading through the docs/watching docker videos, but wanted to see if you might notice anything here that's an immediate red flag.

adolski commented 1 year ago

The way I've dealt with this in other projects is to break out the development and test versions of the app into separate docker-compose files. (We don't really even need a development one for Book Tracker.) For example, in this project, there are three different compose files:

  1. docker-compose.yml contains all of the services that both versions of the app depend on, but not the app itself
  2. docker-compose.development.yml contains only the development version of the app
  3. docker-compose.test.yml contains only the test version of the app

Then docker compose can be invoked like:

docker compose -f docker-compose.yml -f docker-compose.test.yml up --build --exit-code-from ideals-test


Postgres 14 should be fine, unless any errors turn up, but I doubt they will.

And the project link above, and the docker-compose.test.yml file within it should answer the other questions. (I should have linked to it before, sorry.)

gaurijo commented 1 year ago

@adolski Thank you! I'll see how it goes splitting it up like this.

Should I follow the structure of how it's set up in Ideals, where all Dockerfiles are nested inside the docker directory? The way book-tracker is currently set up, has one Dockerfile living in the same directory as the project root.

adolski commented 1 year ago

I think it's better to put them in a subdirectory, because it reduces clutter in the root directory, and moreso if you ever have to add more of them (for other services).

gaurijo commented 1 year ago

Update:

  1. I reinstalled the latest version of Docker Desktop and it's running (for now!)
  2. Still running into issues with Docker Compose:

I set up a subdirectory for the dockerfiles: /docker/book-tracker-app/Dockerfileand /docker/book-tracker-test/Dockerfile

Here's how I set up the test Dockerfile:

FROM ruby:3.0.3-slim 

ENV RAILS_ENV=test
ENV RAILS_LOG_TO_STDOUT=true 
ENV RAILS_MAX_THREADS=5
ENV RAILS_SERVE_STATIC_FILES=true 

RUN apt-get update && apt-get install -y \
  build-essential \
  curl \
  git \
  libpq-dev 

RUN mkdir app 
WORKDIR /app

COPY Gemfile Gemfile.lock ./
RUN gem install bundler \ 
    && bundle config set without 'development' \
    && bundle install 

COPY . ./

CMD ["bin/rails", "test"]

Here is my docker-compose.yml:

version: '3'

services:
  postgres: 
    hostname: postgres
    image: postgres:14-alpine
    environment:
      POSTGRES_DB: book_tracker
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres 
    ports:
      - "5432:5432"

  book-tracker:
    build: 
      context: . 
      dockerfile: docker/book-tracker-app/Dockerfile
    ports:
      - "3000:3000"
    depends_on:
      - postgres 

Here is the docker-compose.test.yml:

version: '3'

services:
  book-tracker-test:
    build:
      context: .
      dockerfile: docker/book-tracker-test/Dockerfile 
    depends_on:
      - postgres
    command: bash -c "sleep 40 && bin/rails db:prepare & bin/rails test"

I also updated my config/database.yml to include 'host:postgres' in the default config block:

default: &default
  adapter:  postgresql
  encoding: unicode
  host: postgres 
  pool:     <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout:  5000
  min_messages: warning
  template: template0

Finally, upon running docker compose -f docker-compose.yml -f docker-compose.test.yml up here's the output I'm getting (I'm running into an error with the postgres username and password not being authenticated, which I'm having trouble figuring out why)

Image

Image

Image

adolski commented 1 year ago

It looks like Book Tracker in the container is trying to connect to Postgres using your (gaurijoshi's) credentials.

One thing that is missing from the Dockerfile, I think, is a Rails configuration file. In the metaslurp-test Dockerfile, there is this line near the end:

RUN cp config/credentials/ci.yml config/credentials/test.yml

config/credentials/ci.yml contains the database connection info, among all the other settings. Since the containerized Book Tracker is running the test Rails environment, it should pick up the config/credentials/test.yml file and use the credentials from that (which are postgres/postgres).

I think what's happening is that, since that file doesn't exist in the container, the variables in database.yml are getting assigned to empty hashes, which cause the test environment settings to be blank, so it tries to fall back to using your Mac account's username.

gaurijo commented 1 year ago

I think you're right!

Image

RUN cp <location_of_file_on_host> <container_id or container_name>:<file_destination>
adolski commented 1 year ago

Your config/credentials/test.yml file is different from the one that you want to be using in the container. It contains settings for your desktop environment, but some of the settings in the container (like the postgres credentials) will be different. That's the reason for the separate ci.yml file that gets copied over test.yml in the image. Since this app doesn't have a ci.yml file, you will have to create one. You could base it on your test.yml file, and then just change whatever settings you need to.

gaurijo commented 1 year ago

Thanks for clarifying, this makes a lot more sense to me now.

I created a ci.yml file based off my test.ymland looking at how it was set up in Metaslurp. Here is what it looks like (similar to metaslurp, I didn't include the secret_key_base). I'm still getting the same error with password authentication failing for 'gaurijoshi'. I've made sure the credentials in the ci.yml are set to postgres/postgres and are now being copied over the test.yml in the image with the RUN cp <ci.yml> <test.yml> command inside my test dockerfile.

I wonder if I might just need to restart Docker, if everything else looks like it's set up correctly, but I'm not confident that is the case here:

Image

adolski commented 1 year ago

Hmm. Are you sure the containers are getting rebuilt when you run them, i.e. are you passing --build to the docker compose command? And are you sure the test.yml is making it into the image? Those are my only ideas.

If you continue to have trouble, if you can open a PR with your work in progress, I can take a look at it. (Is that #24?)

gaurijo commented 1 year ago

I'll double check if the containers are in fact getting rebuilt and get back to you, but yes# 24 is my current work in progress for issue. Thanks!

gaurijo commented 1 year ago

So I made sure I'm passing --build in my docker compose command, and am getting a "failed to solve: read-only file system". Is this a disk space issue? I tried making more room but it keeps coming up.

Image

I also tried running docker build prune to get rid of any dangling build cache, but get a similar error.

Image

And same if I just run the docker compose command without the --buildflag:

Image

gaurijo commented 1 year ago

Update: The tests are now running in Docker successfully! (The 2 errors are pointing toward the User model tests).

Image

adolski commented 1 year ago

Great job!