Docker on Amazon Web Services
上QQ阅读APP看书,第一时间看更新

Copying application source and running tests

The final steps in the test stage are to copy the application source into the container and add support for running tests:

# Test stage
FROM alpine AS test
LABEL application=todobackend

# Install basic utilities
RUN apk add --no-cache bash git

# Install build dependencies
RUN apk add --no-cache gcc python3-dev libffi-dev musl-dev linux-headers mariadb-dev
RUN pip3 install wheel

# Copy requirements
COPY /src/requirements* /build/
WORKDIR /build

# Build and install requirements
RUN pip3 wheel -r requirements_test.txt --no-cache-dir --no-input
RUN pip3 install -r requirements_test.txt -f /build --no-index --no-cache-dir

# Copy source code
COPY /src /app
WORKDIR /app

# Test entrypoint
CMD ["python3", "manage.py", "test", "--noinput", "--settings=todobackend.settings_test"]

In the preceding example, you first copy the entire /src folder to a folder called /app, and then change the working directory to /app. You might be wondering why we didn't just copy all of the application source earlier when we copied the requirements files. The answer here is that we are implementing a caching optimization, as your requirements files require the building of application dependencies, and by building them in a separate, earlier layer, if the requirements files remain the same (which they tend to do), Docker can leverage cached versions of the most recent layers that were built, rather than having to build and install application dependencies each time your image is built.

Finally, we add the CMD directive, which defines the default command that will be executed should a container based from this image be created and executed. Note that we specify the same python3 manage.py test command we used in the previous chapter to run our application tests locally.

You might wonder why we didn't just run our tests in the image using the   RUN  directive. The answer here is that you may want to collect artifacts as part of the build process, such as test reports, which are much easier to copy from a container that you spawn from a Docker image, than during the actual image-build process.

At this point, we have defined the first stage of our Docker build process, which will create a ready-to-test self-contained environment complete with the required operating-system dependencies, application dependencies and application source code. To build the image, you can run the docker build command, tagging the image with a name of todobackend-test:

> docker build --target test -t todobackend-test .
Sending build context to Docker daemon 311.8kB
Step 1/12 : FROM alpine AS test
---> 3fd9065eaf02
Step 2/12 : LABEL application=todobackend
---> Using cache
---> afdd1dee07d7
Step 3/12 : RUN apk add --no-cache bash git
---> Using cache
---> d9cd912ffa68
Step 4/12 : RUN apk add --no-cache gcc python3-dev libffi-dev musl-dev linux-headers mariadb-dev
---> Using cache
---> 89113207b0b8
Step 5/12 : RUN pip3 install wheel
---> Using cache
---> a866d3b1f3e0
Step 6/12 : COPY /src/requirements* /build/
---> Using cache
---> efc869447227
Step 7/12 : WORKDIR /build
---> Using cache
---> 53ced29de259
Step 8/12 : RUN pip3 wheel -r requirements_test.txt --no-cache-dir --no-input
---> Using cache
---> ba6d114360b9
Step 9/12 : RUN pip3 install -r requirements_test.txt -f /build --no-index --no-cache-dir
---> Using cache
---> ba0ebdace940
Step 10/12 : COPY /src /app
---> Using cache
---> 9ae5c85bc7cb
Step 11/12 : WORKDIR /app
---> Using cache
---> aedd8073c9e6
Step 12/12 : CMD ["python3", "manage.py", "test", "--noinput", "--settings=todobackend.settings_test"]
---> Using cache
---> 3ed637e47056
Successfully built 3ed637e47056
Successfully tagged todobackend-test:latest

In the preceding example, the --target flag allows you to target a specific stage in a multi-stage Dockerfile.  Although we only have a single stage at the moment, this flag allows us to build only the test stage in the event we have multiple stages in the Dockerfile. By convention, the docker build command looks for a Dockerfile file in the directory where you run the command, and the period at the end of the command specifies the current directory (i.e. the application repository root in this example) as the build context that should be copied to the Docker Engine when building the image.

With the image built and tagged with an image name of todobackend in your local Docker Engine, you can now start a container from the image, which by default will run the python3 manage.py test command as specified by the CMD directive:

todobackend>  docker run -it --rm todobackend-test
Creating test database for alias 'default'...

Ensure we can create a new todo item
- item has correct title
- item was created
- received 201 created status code
- received location header hyperlink

Ensure we can delete all todo items
- all items were deleted
- received 204 no content status code

Ensure we can delete a todo item
- received 204 no content status code
- the item was deleted

Ensure we can update an existing todo item using PATCH
- item was updated
- received 200 ok status code

Ensure we can update an existing todo item using PUT
- item was updated
- received 200 created status code
----------------------------------------------------------------------
XML: /app/unittests.xml
Name Stmts Miss Cover
-----------------------------------------------------
todo/__init__.py 0 0 100%
todo/admin.py 1 1 0%
todo/migrations/0001_initial.py 5 0 100%
todo/migrations/__init__.py 0 0 100%
todo/models.py 6 6 0%
todo/serializers.py 7 0 100%
todo/urls.py 6 0 100%
todo/views.py 17 0 100%
-----------------------------------------------------
TOTAL 42 7 83%
----------------------------------------------------------------------
Ran 12 tests in 0.433s

OK

Destroying test database for alias 'default'...

The -it flag specifies to run the container with an interactive terminal, and the --rm flag will automatically delete the container once it exits. Note that the tests all pass successfully, so we know the application built in the image is in a good state, at least in terms of the current tests that have been defined for the application.