Skip to content

Commit 88dead6

Browse files
feat: allow saving specific platforms for an image (#3218)
* feat: allow saving specific platforms for an image * wip * fix: typo --------- Co-authored-by: Manuel de la Peña <[email protected]>
1 parent 44d970c commit 88dead6

File tree

4 files changed

+77
-6
lines changed

4 files changed

+77
-6
lines changed

docker.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1786,6 +1786,19 @@ func (p *DockerProvider) ListImages(ctx context.Context) ([]ImageInfo, error) {
17861786

17871787
// SaveImages exports a list of images as an uncompressed tar
17881788
func (p *DockerProvider) SaveImages(ctx context.Context, output string, images ...string) error {
1789+
return p.SaveImagesWithOpts(ctx, output, images)
1790+
}
1791+
1792+
// SaveImagesWithOpts exports a list of images as an uncompressed tar, passing options to the provider
1793+
func (p *DockerProvider) SaveImagesWithOpts(ctx context.Context, output string, images []string, opts ...SaveImageOption) error {
1794+
saveOpts := saveImageOptions{}
1795+
1796+
for _, opt := range opts {
1797+
if err := opt(&saveOpts); err != nil {
1798+
return fmt.Errorf("applying save image option: %w", err)
1799+
}
1800+
}
1801+
17891802
outputFile, err := os.Create(output)
17901803
if err != nil {
17911804
return fmt.Errorf("opening output file %w", err)
@@ -1794,7 +1807,7 @@ func (p *DockerProvider) SaveImages(ctx context.Context, output string, images .
17941807
_ = outputFile.Close()
17951808
}()
17961809

1797-
imageReader, err := p.client.ImageSave(ctx, images)
1810+
imageReader, err := p.client.ImageSave(ctx, images, saveOpts.dockerSaveOpts...)
17981811
if err != nil {
17991812
return fmt.Errorf("saving images %w", err)
18001813
}
@@ -1811,6 +1824,14 @@ func (p *DockerProvider) SaveImages(ctx context.Context, output string, images .
18111824
return nil
18121825
}
18131826

1827+
func SaveDockerImageWithPlatforms(platforms ...specs.Platform) SaveImageOption {
1828+
return func(opts *saveImageOptions) error {
1829+
opts.dockerSaveOpts = append(opts.dockerSaveOpts, client.ImageSaveWithPlatforms(platforms...))
1830+
1831+
return nil
1832+
}
1833+
}
1834+
18141835
// PullImage pulls image from registry
18151836
func (p *DockerProvider) PullImage(ctx context.Context, img string) error {
18161837
return p.attemptToPullImage(ctx, img, image.PullOptions{})

image.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package testcontainers
22

33
import (
44
"context"
5+
6+
"github.com/docker/docker/client"
57
)
68

79
// ImageInfo represents summary information of an image
@@ -10,9 +12,16 @@ type ImageInfo struct {
1012
Name string
1113
}
1214

15+
type saveImageOptions struct {
16+
dockerSaveOpts []client.ImageSaveOption
17+
}
18+
19+
type SaveImageOption func(*saveImageOptions) error
20+
1321
// ImageProvider allows manipulating images
1422
type ImageProvider interface {
1523
ListImages(context.Context) ([]ImageInfo, error)
1624
SaveImages(context.Context, string, ...string) error
25+
SaveImagesWithOpts(context.Context, string, []string, ...SaveImageOption) error
1726
PullImage(context.Context, string) error
1827
}

image_test.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"path/filepath"
77
"testing"
88

9+
"github.com/containerd/platforms"
910
"github.com/stretchr/testify/require"
1011

1112
"github.com/testcontainers/testcontainers-go/internal/core"
@@ -18,7 +19,8 @@ func TestImageList(t *testing.T) {
1819
require.NoErrorf(t, err, "failed to get provider")
1920

2021
defer func() {
21-
_ = provider.Close()
22+
err = provider.Close()
23+
require.NoError(t, err)
2224
}()
2325

2426
req := ContainerRequest{
@@ -51,7 +53,8 @@ func TestSaveImages(t *testing.T) {
5153
require.NoErrorf(t, err, "failed to get provider")
5254

5355
defer func() {
54-
_ = provider.Close()
56+
err = provider.Close()
57+
require.NoError(t, err)
5558
}()
5659

5760
req := ContainerRequest{
@@ -71,3 +74,38 @@ func TestSaveImages(t *testing.T) {
7174

7275
require.NotZerof(t, info.Size(), "output file is empty")
7376
}
77+
78+
func TestSaveImagesWithOpts(t *testing.T) {
79+
t.Setenv("DOCKER_HOST", core.MustExtractDockerHost(context.Background()))
80+
81+
provider, err := ProviderDocker.GetProvider()
82+
require.NoErrorf(t, err, "failed to get provider")
83+
84+
defer func() {
85+
err = provider.Close()
86+
require.NoError(t, err)
87+
}()
88+
89+
req := ContainerRequest{
90+
Image: "redis:latest",
91+
ImagePlatform: "linux/amd64",
92+
}
93+
94+
p, err := platforms.ParseAll([]string{"linux/amd64"})
95+
require.NoError(t, err)
96+
97+
ctr, err := provider.CreateContainer(context.Background(), req)
98+
CleanupContainer(t, ctr)
99+
require.NoErrorf(t, err, "creating test container")
100+
101+
output := filepath.Join(t.TempDir(), "images.tar")
102+
err = provider.SaveImagesWithOpts(
103+
context.Background(), output, []string{req.Image}, SaveDockerImageWithPlatforms(p...),
104+
)
105+
require.NoErrorf(t, err, "saving image %q", req.Image)
106+
107+
info, err := os.Stat(output)
108+
require.NoError(t, err)
109+
110+
require.NotZerof(t, info.Size(), "output file is empty")
111+
}

modules/k3s/k3s.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,11 @@ func unmarshal(bytes []byte) (*KubeConfigValue, error) {
194194
return &kubeConfig, nil
195195
}
196196

197-
// LoadImages loads images into the k3s container.
198197
func (c *K3sContainer) LoadImages(ctx context.Context, images ...string) error {
198+
return c.LoadImagesWithOpts(ctx, images)
199+
}
200+
201+
func (c *K3sContainer) LoadImagesWithOpts(ctx context.Context, images []string, opts ...testcontainers.SaveImageOption) error {
199202
provider, err := testcontainers.ProviderDocker.GetProvider()
200203
if err != nil {
201204
return fmt.Errorf("getting docker provider %w", err)
@@ -210,7 +213,7 @@ func (c *K3sContainer) LoadImages(ctx context.Context, images ...string) error {
210213
_ = os.Remove(imagesTar.Name())
211214
}()
212215

213-
err = provider.SaveImages(context.Background(), imagesTar.Name(), images...)
216+
err = provider.SaveImagesWithOpts(context.Background(), imagesTar.Name(), images, opts...)
214217
if err != nil {
215218
return fmt.Errorf("saving images %w", err)
216219
}
@@ -221,7 +224,7 @@ func (c *K3sContainer) LoadImages(ctx context.Context, images ...string) error {
221224
return fmt.Errorf("copying image to container %w", err)
222225
}
223226

224-
_, _, err = c.Exec(ctx, []string{"ctr", "-n=k8s.io", "images", "import", containerPath})
227+
_, _, err = c.Exec(ctx, []string{"ctr", "-n=k8s.io", "images", "import", "--all-platforms", containerPath})
225228
if err != nil {
226229
return fmt.Errorf("importing image %w", err)
227230
}

0 commit comments

Comments
 (0)