Skip to content

Commit 9ec3ed4

Browse files
Send optional platform spec when creating container
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
1 parent d303350 commit 9ec3ed4

5 files changed

Lines changed: 178 additions & 21 deletions

File tree

container.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ type ContainerRequest struct {
9797
ReaperImage string // alternative reaper image
9898
AutoRemove bool // if set to true, the container will be removed from the host when stopped
9999
NetworkMode container.NetworkMode
100-
AlwaysPullImage bool // Always pull image
100+
AlwaysPullImage bool // Always pull image
101+
ImagePlatform string // ImagePlatform describes the platform which the image runs on.
101102
}
102103

103104
// ProviderType is an enum for the possible providers

docker.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"time"
1818

1919
"github.com/cenkalti/backoff/v4"
20+
"github.com/containerd/containerd/platforms"
2021
"github.com/docker/docker/api/types"
2122
"github.com/docker/docker/api/types/container"
2223
"github.com/docker/docker/api/types/network"
@@ -28,6 +29,7 @@ import (
2829
"github.com/magiconair/properties"
2930
"github.com/moby/term"
3031

32+
specs "github.com/opencontainers/image-spec/specs-go/v1"
3133
"github.com/testcontainers/testcontainers-go/wait"
3234
)
3335

@@ -698,30 +700,47 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
698700
}
699701

700702
var tag string
703+
var platform *specs.Platform
704+
701705
if req.ShouldBuildImage() {
702706
tag, err = p.BuildImage(ctx, &req)
703707
if err != nil {
704708
return nil, err
705709
}
706710
} else {
707711
tag = req.Image
712+
713+
if req.ImagePlatform != "" {
714+
p, err := platforms.Parse(req.ImagePlatform)
715+
if err != nil {
716+
return nil, fmt.Errorf("invalid platform %s: %w", req.ImagePlatform, err)
717+
}
718+
platform = &p
719+
}
720+
708721
var shouldPullImage bool
709722

710723
if req.AlwaysPullImage {
711724
shouldPullImage = true // If requested always attempt to pull image
712725
} else {
713-
_, _, err = p.client.ImageInspectWithRaw(ctx, tag)
726+
image, _, err := p.client.ImageInspectWithRaw(ctx, tag)
714727
if err != nil {
715728
if client.IsErrNotFound(err) {
716729
shouldPullImage = true
717730
} else {
718731
return nil, err
719732
}
720733
}
734+
if platform != nil && (image.Architecture != platform.Architecture || image.Os != platform.OS) {
735+
shouldPullImage = true
736+
}
721737
}
722738

723739
if shouldPullImage {
724-
pullOpt := types.ImagePullOptions{}
740+
pullOpt := types.ImagePullOptions{
741+
Platform: req.ImagePlatform, // may be empty
742+
}
743+
725744
if req.RegistryCred != "" {
726745
pullOpt.RegistryAuth = req.RegistryCred
727746
}
@@ -779,7 +798,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
779798
EndpointsConfig: endpointConfigs,
780799
}
781800

782-
resp, err := p.client.ContainerCreate(ctx, dockerInput, hostConfig, &networkingConfig, nil, req.Name)
801+
resp, err := p.client.ContainerCreate(ctx, dockerInput, hostConfig, &networkingConfig, platform, req.Name)
783802
if err != nil {
784803
return nil, err
785804
}

docker_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"time"
1919

2020
"github.com/stretchr/testify/assert"
21+
"github.com/stretchr/testify/require"
2122
"gotest.tools/v3/env"
2223
"gotest.tools/v3/fs"
2324

@@ -1494,6 +1495,63 @@ func TestContainerNonExistentImage(t *testing.T) {
14941495
})
14951496
}
14961497

1498+
func TestContainerCustomPlatformImage(t *testing.T) {
1499+
t.Run("error with a non-existent platform", func(t *testing.T) {
1500+
nonExistentPlatform := "windows/arm12"
1501+
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
1502+
defer cancel()
1503+
c, err := GenericContainer(ctx, GenericContainerRequest{
1504+
ContainerRequest: ContainerRequest{
1505+
Image: "redis:latest",
1506+
SkipReaper: true,
1507+
ImagePlatform: nonExistentPlatform,
1508+
},
1509+
Started: false,
1510+
})
1511+
1512+
t.Cleanup(func() {
1513+
if c != nil {
1514+
c.Terminate(ctx)
1515+
}
1516+
})
1517+
1518+
assert.Error(t, err)
1519+
})
1520+
1521+
t.Run("specific platform should be propagated", func(t *testing.T) {
1522+
ctx := context.Background()
1523+
1524+
c, err := GenericContainer(ctx, GenericContainerRequest{
1525+
ContainerRequest: ContainerRequest{
1526+
Image: "mysql:5.7",
1527+
SkipReaper: true,
1528+
ImagePlatform: "linux/amd64",
1529+
},
1530+
Started: false,
1531+
})
1532+
1533+
t.Cleanup(func() {
1534+
if c != nil {
1535+
c.Terminate(ctx)
1536+
}
1537+
})
1538+
1539+
assert.NoError(t, err)
1540+
1541+
dockerCli, err := client.NewEnvClient()
1542+
require.NoError(t, err)
1543+
1544+
dockerCli.NegotiateAPIVersion(ctx)
1545+
ctr, err := dockerCli.ContainerInspect(ctx, c.GetContainerID())
1546+
assert.NoError(t, err)
1547+
1548+
img, _, err := dockerCli.ImageInspectWithRaw(ctx, ctr.Image)
1549+
assert.NoError(t, err)
1550+
assert.Equal(t, "linux", img.Os)
1551+
assert.Equal(t, "amd64", img.Architecture)
1552+
})
1553+
}
1554+
14971555
func TestContainerWithCustomHostname(t *testing.T) {
14981556
ctx := context.Background()
14991557
name := fmt.Sprintf("some-nginx-%s-%d", t.Name(), rand.Int())

go.mod

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ module github.com/testcontainers/testcontainers-go
33
go 1.13
44

55
require (
6-
github.com/Microsoft/hcsshim v0.8.16 // indirect
76
github.com/cenkalti/backoff/v4 v4.1.2
7+
github.com/containerd/containerd v1.5.9
88
github.com/docker/docker v20.10.11+incompatible
99
github.com/docker/go-connections v0.4.0
1010
github.com/go-redis/redis v6.15.9+incompatible
@@ -16,11 +16,10 @@ require (
1616
github.com/moby/sys/mountinfo v0.5.0 // indirect
1717
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
1818
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
19-
github.com/opencontainers/runc v1.0.2 // indirect
19+
github.com/opencontainers/image-spec v1.0.2
2020
github.com/stretchr/testify v1.7.0
2121
golang.org/x/net v0.0.0-20211108170745-6635138e15ea // indirect
2222
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881
23-
google.golang.org/protobuf v1.27.1 // indirect
2423
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
2524
gotest.tools/v3 v3.0.3
2625
)

0 commit comments

Comments
 (0)