Joe
Joe

Reputation: 3620

Create docker container to run `go test` with all module dependencies downloaded and cached

I want to test my Go code in a CI environment which requires using Docker. How do I create a Docker image that has all the dependencies listed in go.mod already downloaded and compiled so that docker run $IMG go test uses the cached artifacts?

The desired properties of this image:

Existing approaches

Parsing go.mod and using go get

From https://github.com/golang/go/issues/27719#issuecomment-578246826

This approach is close but it doesn't appear to use GOCACHE when I run go test. This also appears to choke on certain module paths, like gopkg.in/DataDog/dd-trace-go.v1:

FROM golang:1.13
WORKDIR /src
COPY go.mod ./
RUN set -eu \
  && go mod graph \
  | cut -d '@' -f 1 \
  | cut -d ' ' -f 2 \
  | sort -u \
  | sed -e 's#dd-trace-go.v1#&/ddtrace#' \
  | xargs go get -v
docker run --mount /src:/src $IMG go test ./...

Using DOCKER_BUILDKIT with a mount cache

Originally described in https://github.com/golang/go/issues/27719#issuecomment-514747274. This only works for go build. I can't use it for go test because the cache mount is unmounted after the RUN command so it doesn't exist in the created Docker image.

This also depends on experimental docker features.

# syntax = docker/dockerfile:experimental
FROM golang:1.13 as go-builder
ARG VERSION
WORKDIR /src
COPY . /src/
# With a mount cache, Docker will cache the target directories for future
# invocations of this RUN layer. Meaning, once this command is run once, all
# successive calls will use the already downloaded and already compiled assets.
RUN --mount=type=cache,target=/go/pkg/mod \
    --mount=type=cache,target=/root/.cache/go-build \
    go build ./server

Upvotes: 9

Views: 4130

Answers (2)

Namor81
Namor81

Reputation: 75

In your dockerfile if you run go get ./... it will download all the dependencies to your docker image based on the go.mod and go.sum. You can also add the --insecure flag to the go get command if you are dealing with internal self signed repositories. Very similar to go mod download. Aside from that you can have a shell script that actually initiates the Go test ./... and reports it out to your CI environment if that allows, I know that Gitlab allows this.

FROM golang:1.15-alpine AS builder
RUN apk add --update git gcc musl-dev
RUN apk update && apk add bash
COPY . /app
RUN go version

WORKDIR /app
ENV CGO_ENABLED=0
RUN git config --global http.sslVerify false
RUN go get ./...

WORKDIR /app
RUN chmod +x ./unitTest.sh
RUN ./unitTest.sh

WORKDIR /app/cmd/svr
RUN go build -o app
RUN chmod 700 app

FROM alpine:latest
WORKDIR /root/
ARG build_stamp
ARG git_commit
ARG build_number
ENV BUILD_STAMP=$build_stamp
ENV GIT_COMMIT=$git_commit
ENV BUILD_NUMBER=$build_number
COPY --from=builder /app/cmd/svr .
EXPOSE 8000
CMD ["./app"]

and the script

#!/usr/bin/env bash

TESTS=$(go test -v -covermode=count -coverprofile=count.txt ./...)
echo "$TESTS"

if echo "$TESTS" | grep -q "FAIL" ; then
  echo ""
  echo "One or more Unit Tests for app have Failed. Build will now fail. Pipeline will also fail..."
  echo ""
  exit 1
else
  echo ""
  echo "All Unit Tests for application have passed!"
  echo "Running Code Coverage..."
  echo ""
  COVERAGE=$(go tool cover -func=./count.txt)
  echo "$COVERAGE"
  exit 0
fi

Upvotes: 1

Arith
Arith

Reputation: 416

I often put COPY go.mod on the very begin of a Dockerfile, as it does not change that often.

FROM golang:1.14.3 as builder

WORKDIR /app

COPY go.mod .
COPY go.sum .

RUN go mod download

COPY . .
RUN go build -tags netgo -ldflags '-extldflags "-static"' -o app .

FROM centos:7  
WORKDIR /root

COPY --from=builder /app/app .

So, if you go mod does not change, the line RUN go mod download only run for the first time.

Upvotes: 1

Related Questions