Skip to content

Commit

Permalink
Merge pull request #311 from xicoalmeida/master
Browse files Browse the repository at this point in the history
Add instrumentations for containers created with Dockerfile
  • Loading branch information
Gianluca Arbezzano committed May 21, 2021
2 parents b5754a6 + df36248 commit 0d6a586
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 6 deletions.
25 changes: 19 additions & 6 deletions container.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,21 @@ type Container interface {

// ImageBuildInfo defines what is needed to build an image
type ImageBuildInfo interface {
GetContext() (io.Reader, error) // the path to the build context
GetDockerfile() string // the relative path to the Dockerfile, including the fileitself
ShouldBuildImage() bool // return true if the image needs to be built
GetContext() (io.Reader, error) // the path to the build context
GetDockerfile() string // the relative path to the Dockerfile, including the fileitself
ShouldPrintBuildLog() bool // allow build log to be printed to stdout
ShouldBuildImage() bool // return true if the image needs to be built
GetBuildArgs() map[string]*string // return the environment args used to build the from Dockerfile
}

// FromDockerfile represents the parameters needed to build an image from a Dockerfile
// rather than using a pre-built one
type FromDockerfile struct {
Context string // the path to the context of of the docker build
ContextArchive io.Reader // the tar archive file to send to docker that contains the build context
Dockerfile string // the path from the context to the Dockerfile for the image, defaults to "Dockerfile"
Context string // the path to the context of of the docker build
ContextArchive io.Reader // the tar archive file to send to docker that contains the build context
Dockerfile string // the path from the context to the Dockerfile for the image, defaults to "Dockerfile"
BuildArgs map[string]*string // enable user to pass build args to docker daemon
PrintBuildLog bool // enable user to print build log
}

// ContainerRequest represents the parameters used to get a running container
Expand Down Expand Up @@ -148,6 +152,11 @@ func (c *ContainerRequest) GetContext() (io.Reader, error) {
return buildContext, nil
}

// GetBuildArgs returns the env args to be used when creating from Dockerfile
func (c *ContainerRequest) GetBuildArgs() map[string]*string {
return c.FromDockerfile.BuildArgs
}

// GetDockerfile returns the Dockerfile from the ContainerRequest, defaults to "Dockerfile"
func (c *ContainerRequest) GetDockerfile() string {
f := c.FromDockerfile.Dockerfile
Expand All @@ -162,6 +171,10 @@ func (c *ContainerRequest) ShouldBuildImage() bool {
return c.FromDockerfile.Context != "" || c.FromDockerfile.ContextArchive != nil
}

func (c *ContainerRequest) ShouldPrintBuildLog() bool {
return c.FromDockerfile.PrintBuildLog
}

func (c *ContainerRequest) validateContextAndImage() error {
if c.FromDockerfile.Context != "" && c.Image != "" {
return errors.New("you cannot specify both an Image and Context in a ContainerRequest")
Expand Down
11 changes: 11 additions & 0 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import (
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/go-connections/nat"
"github.com/google/uuid"
"github.com/moby/term"
"github.com/pkg/errors"

"github.com/testcontainers/testcontainers-go/wait"
Expand Down Expand Up @@ -469,6 +471,7 @@ func (p *DockerProvider) BuildImage(ctx context.Context, img ImageBuildInfo) (st
}

buildOptions := types.ImageBuildOptions{
BuildArgs: img.GetBuildArgs(),
Dockerfile: img.GetDockerfile(),
Context: buildContext,
Tags: []string{repoTag},
Expand All @@ -479,6 +482,14 @@ func (p *DockerProvider) BuildImage(ctx context.Context, img ImageBuildInfo) (st
return "", err
}

if img.ShouldPrintBuildLog() {
termFd, isTerm := term.GetFdInfo(os.Stderr)
err = jsonmessage.DisplayJSONMessagesStream(resp.Body, os.Stderr, termFd, isTerm, nil)
if err != nil {
return "", err
}
}

// need to read the response from Docker, I think otherwise the image
// might not finish building before continuing to execute here
buf := new(bytes.Buffer)
Expand Down
110 changes: 110 additions & 0 deletions docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import (
"errors"
"fmt"
"github.com/stretchr/testify/assert"
"io/ioutil"
"math/rand"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
Expand Down Expand Up @@ -892,6 +894,114 @@ func Test_BuildContainerFromDockerfile(t *testing.T) {
}
}

func Test_BuildContainerFromDockerfileWithBuildArgs(t *testing.T) {
t.Log("getting ctx")
ctx := context.Background()

ba := "build args value"

t.Log("got ctx, creating container request")
req := ContainerRequest{
FromDockerfile: FromDockerfile{
Context: "./testresources",
Dockerfile: "args.Dockerfile",
BuildArgs: map[string]*string{
"FOO": &ba,
},
},
ExposedPorts: []string{"8080/tcp"},
WaitingFor: wait.ForLog("ready"),
}

genContainerReq := GenericContainerRequest{
ContainerRequest: req,
Started: true,
}

c, err := GenericContainer(ctx, genContainerReq)

if err != nil {
t.Fatal(err)
}

ep, err := c.Endpoint(ctx, "http")
if err != nil {
t.Fatal(err)
}

resp, err := http.Get(ep + "/env")

if err != nil {
t.Fatal(err)
}

body, err := ioutil.ReadAll(resp.Body)

if err != nil {
t.Fatal(err)
}

assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, ba, string(body))

defer func() {
t.Log("terminating container")
err := c.Terminate(ctx)
if err != nil {
t.Fatal(err)
}
t.Log("terminated container")
}()
}

func Test_BuildContainerFromDockerfileWithBuildLog(t *testing.T) {
rescueStdout := os.Stderr
r, w, _ := os.Pipe()
os.Stderr = w

t.Log("getting ctx")
ctx := context.Background()
t.Log("got ctx, creating container request")

req := ContainerRequest{
FromDockerfile: FromDockerfile{
Context: "./testresources",
Dockerfile: "buildlog.Dockerfile",
PrintBuildLog: true,
},
}

genContainerReq := GenericContainerRequest{
ContainerRequest: req,
Started: true,
}

c, err := GenericContainer(ctx, genContainerReq)

if err != nil {
t.Fatal(err)
}

defer func() {
t.Log("terminating container")
err := c.Terminate(ctx)
if err != nil {
t.Fatal(err)
}
t.Log("terminated container")
}()

w.Close()
out, _ := ioutil.ReadAll(r)
os.Stdout = rescueStdout
temp := strings.Split(string(out), "\n")

if temp[0] != "Step 1/1 : FROM alpine" {
t.Errorf("Expected stout firstline to be %s. Got '%s'.", "Step 1/2 : FROM alpine", temp[0])
}

}

func TestContainerCreationWaitsForLogAndPortContextTimeout(t *testing.T) {
ctx := context.Background()
req := ContainerRequest{
Expand Down
21 changes: 21 additions & 0 deletions docs/features/build_from_dockerfile.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@ req := ContainerRequest{
}
```

If your Dockerfile expects build args:

```Dockerfile
FROM alpine

ARG FOO

```
You can specify them like:

```go
req := ContainerRequest{
FromDockerfile: testcontainers.FromDockerfile{
Context: "/path/to/build/context",
Dockerfile: "CustomDockerfile",
BuildArgs: map[string]*string {
"FOO": "BAR",
},
},
}
```
## Dynamic Build Context

If you would like to send a build context that you created in code (maybe you have a dynamic Dockerfile), you can
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/go-sql-driver/mysql v1.6.0
github.com/google/uuid v1.2.0
github.com/moby/sys/mount v0.2.0 // indirect
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 // indirect
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.0
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
Expand Down Expand Up @@ -402,6 +403,8 @@ github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2J
github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
Expand Down Expand Up @@ -701,6 +704,7 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down
11 changes: 11 additions & 0 deletions testresources/args.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM golang:1.13-alpine

ARG FOO

ENV FOO=$FOO

WORKDIR /app

COPY echoserver.go .

CMD go run echoserver.go
1 change: 1 addition & 0 deletions testresources/buildlog.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FROM alpine
10 changes: 10 additions & 0 deletions testresources/echoserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import (
"os"
)

func envHandler() http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {

rw.Write([]byte(os.Getenv("FOO")))

rw.WriteHeader(http.StatusAccepted)
}
}

func echoHandler(destination *os.File) http.HandlerFunc {
return func(rw http.ResponseWriter, req *http.Request) {
echo := req.URL.Query()["echo"][0]
Expand All @@ -26,6 +35,7 @@ func main() {
mux := http.NewServeMux()
mux.HandleFunc("/stdout", echoHandler(os.Stdout))
mux.HandleFunc("/stderr", echoHandler(os.Stderr))
mux.HandleFunc("/env", envHandler())

ln, err := net.Listen("tcp", ":8080")
if err != nil {
Expand Down

0 comments on commit 0d6a586

Please sign in to comment.