Skip to content

Commit

Permalink
Merge pull request #395 from hairyhenderson/add-container-platform-spec
Browse files Browse the repository at this point in the history
Send optional platform spec when creating container
  • Loading branch information
gianarb authored Feb 2, 2022
2 parents 8be67da + 9ec3ed4 commit a5da993
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 21 deletions.
3 changes: 2 additions & 1 deletion container.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ type ContainerRequest struct {
ReaperImage string // alternative reaper image
AutoRemove bool // if set to true, the container will be removed from the host when stopped
NetworkMode container.NetworkMode
AlwaysPullImage bool // Always pull image
AlwaysPullImage bool // Always pull image
ImagePlatform string // ImagePlatform describes the platform which the image runs on.
}

type (
Expand Down
25 changes: 22 additions & 3 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"time"

"github.com/cenkalti/backoff/v4"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
Expand All @@ -28,6 +29,7 @@ import (
"github.com/magiconair/properties"
"github.com/moby/term"

specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/testcontainers/testcontainers-go/wait"
)

Expand Down Expand Up @@ -748,30 +750,47 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
}

var tag string
var platform *specs.Platform

if req.ShouldBuildImage() {
tag, err = p.BuildImage(ctx, &req)
if err != nil {
return nil, err
}
} else {
tag = req.Image

if req.ImagePlatform != "" {
p, err := platforms.Parse(req.ImagePlatform)
if err != nil {
return nil, fmt.Errorf("invalid platform %s: %w", req.ImagePlatform, err)
}
platform = &p
}

var shouldPullImage bool

if req.AlwaysPullImage {
shouldPullImage = true // If requested always attempt to pull image
} else {
_, _, err = p.client.ImageInspectWithRaw(ctx, tag)
image, _, err := p.client.ImageInspectWithRaw(ctx, tag)
if err != nil {
if client.IsErrNotFound(err) {
shouldPullImage = true
} else {
return nil, err
}
}
if platform != nil && (image.Architecture != platform.Architecture || image.Os != platform.OS) {
shouldPullImage = true
}
}

if shouldPullImage {
pullOpt := types.ImagePullOptions{}
pullOpt := types.ImagePullOptions{
Platform: req.ImagePlatform, // may be empty
}

if req.RegistryCred != "" {
pullOpt.RegistryAuth = req.RegistryCred
}
Expand Down Expand Up @@ -829,7 +848,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
EndpointsConfig: endpointConfigs,
}

resp, err := p.client.ContainerCreate(ctx, dockerInput, hostConfig, &networkingConfig, nil, req.Name)
resp, err := p.client.ContainerCreate(ctx, dockerInput, hostConfig, &networkingConfig, platform, req.Name)
if err != nil {
return nil, err
}
Expand Down
58 changes: 58 additions & 0 deletions docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gotest.tools/v3/env"
"gotest.tools/v3/fs"

Expand Down Expand Up @@ -1494,6 +1495,63 @@ func TestContainerNonExistentImage(t *testing.T) {
})
}

func TestContainerCustomPlatformImage(t *testing.T) {
t.Run("error with a non-existent platform", func(t *testing.T) {
nonExistentPlatform := "windows/arm12"
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
c, err := GenericContainer(ctx, GenericContainerRequest{
ContainerRequest: ContainerRequest{
Image: "redis:latest",
SkipReaper: true,
ImagePlatform: nonExistentPlatform,
},
Started: false,
})

t.Cleanup(func() {
if c != nil {
c.Terminate(ctx)
}
})

assert.Error(t, err)
})

t.Run("specific platform should be propagated", func(t *testing.T) {
ctx := context.Background()

c, err := GenericContainer(ctx, GenericContainerRequest{
ContainerRequest: ContainerRequest{
Image: "mysql:5.7",
SkipReaper: true,
ImagePlatform: "linux/amd64",
},
Started: false,
})

t.Cleanup(func() {
if c != nil {
c.Terminate(ctx)
}
})

assert.NoError(t, err)

dockerCli, err := client.NewEnvClient()
require.NoError(t, err)

dockerCli.NegotiateAPIVersion(ctx)
ctr, err := dockerCli.ContainerInspect(ctx, c.GetContainerID())
assert.NoError(t, err)

img, _, err := dockerCli.ImageInspectWithRaw(ctx, ctr.Image)
assert.NoError(t, err)
assert.Equal(t, "linux", img.Os)
assert.Equal(t, "amd64", img.Architecture)
})
}

func TestContainerWithCustomHostname(t *testing.T) {
ctx := context.Background()
name := fmt.Sprintf("some-nginx-%s-%d", t.Name(), rand.Int())
Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ module github.com/testcontainers/testcontainers-go
go 1.13

require (
github.com/Microsoft/hcsshim v0.8.16 // indirect
github.com/cenkalti/backoff/v4 v4.1.2
github.com/containerd/containerd v1.5.9
github.com/docker/docker v20.10.11+incompatible
github.com/docker/go-connections v0.4.0
github.com/go-redis/redis v6.15.9+incompatible
Expand All @@ -16,11 +16,10 @@ require (
github.com/moby/sys/mountinfo v0.5.0 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
github.com/opencontainers/runc v1.0.2 // indirect
github.com/opencontainers/image-spec v1.0.2
github.com/stretchr/testify v1.7.0
golang.org/x/net v0.0.0-20211108170745-6635138e15ea // indirect
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gotest.tools/v3 v3.0.3
)
Loading

0 comments on commit a5da993

Please sign in to comment.