Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add image-keep option for built images #1785

Merged
merged 5 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions container.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ type FromDockerfile struct {
BuildArgs map[string]*string // enable user to pass build args to docker daemon
PrintBuildLog bool // enable user to print build log
AuthConfigs map[string]registry.AuthConfig // Deprecated. Testcontainers will detect registry credentials automatically. Enable auth configs to be able to pull from an authenticated docker registry
// KeepImage describes whether DockerContainer.Terminate should not delete the
// container image. Useful for images that are built from a Dockerfile and take a
// long time to build. Keeping the image also Docker to reuse it.
KeepImage bool
}

type ContainerFile struct {
Expand Down Expand Up @@ -275,6 +279,10 @@ func (c *ContainerRequest) ShouldBuildImage() bool {
return c.FromDockerfile.Context != "" || c.FromDockerfile.ContextArchive != nil
}

func (c *ContainerRequest) ShouldKeepBuiltImage() bool {
return c.FromDockerfile.KeepImage
}

func (c *ContainerRequest) ShouldPrintBuildLog() bool {
return c.FromDockerfile.PrintBuildLog
}
Expand Down
9 changes: 6 additions & 3 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ type DockerContainer struct {
WaitingFor wait.Strategy
Image string

isRunning bool
imageWasBuilt bool
isRunning bool
imageWasBuilt bool
// keepBuiltImage makes Terminate not remove the image if imageWasBuilt.
mdelapenya marked this conversation as resolved.
Show resolved Hide resolved
keepBuiltImage bool
provider *DockerProvider
sessionID string
terminationSignal chan bool
Expand Down Expand Up @@ -273,7 +275,7 @@ func (c *DockerContainer) Terminate(ctx context.Context) error {
return err
}

if c.imageWasBuilt {
if c.imageWasBuilt && !c.keepBuiltImage {
_, err := c.provider.client.ImageRemove(ctx, c.Image, types.ImageRemoveOptions{
Force: true,
PruneChildren: true,
Expand Down Expand Up @@ -1072,6 +1074,7 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
WaitingFor: req.WaitingFor,
Image: tag,
imageWasBuilt: req.ShouldBuildImage(),
keepBuiltImage: req.ShouldKeepBuiltImage(),
sessionID: testcontainerssession.SessionID(),
provider: p,
terminationSignal: termSignal,
Expand Down
55 changes: 55 additions & 0 deletions docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"testing"
"time"

"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/api/types/volume"
Expand Down Expand Up @@ -2081,3 +2082,57 @@ func TestDockerProviderFindContainerByName(t *testing.T) {
require.NotNil(t, c)
assert.Contains(t, c.Names, c1Name)
}

func TestImageBuiltFromDockerfile_KeepBuiltImage(t *testing.T) {
tests := []struct {
keepBuiltImage bool
}{
{keepBuiltImage: true},
{keepBuiltImage: false},
}

for _, tt := range tests {
t.Run(fmt.Sprintf("keep_built_image_%t", tt.keepBuiltImage), func(t *testing.T) {
mdelapenya marked this conversation as resolved.
Show resolved Hide resolved
ctx := context.Background()
// Set up CLI.
provider, err := NewDockerProvider()
require.NoError(t, err, "get docker provider should not fail")
defer func() { _ = provider.Close() }()
cli := provider.Client()
// Create container.
c, err := GenericContainer(ctx, GenericContainerRequest{
ProviderType: providerType,
ContainerRequest: ContainerRequest{
FromDockerfile: FromDockerfile{
Context: "testdata",
Dockerfile: "echo.Dockerfile",
KeepImage: tt.keepBuiltImage,
},
},
})
require.NoError(t, err, "create container should not fail")
defer func() { _ = c.Terminate(context.Background()) }()
// Get the image ID.
containerName, err := c.Name(ctx)
require.NoError(t, err, "get container name should not fail")
containerDetails, err := cli.ContainerInspect(ctx, containerName)
require.NoError(t, err, "inspect container should not fail")
containerImage := containerDetails.Image
t.Cleanup(func() {
_, _ = cli.ImageRemove(ctx, containerImage, types.ImageRemoveOptions{
Force: true,
PruneChildren: true,
})
})
// Now, we terminate the container and check whether the image still exists.
err = c.Terminate(ctx)
require.NoError(t, err, "terminate container should not fail")
_, _, err = cli.ImageInspectWithRaw(ctx, containerImage)
if tt.keepBuiltImage {
assert.Nil(t, err, "image should still exist")
} else {
assert.NotNil(t, err, "image should not exist anymore")
}
})
}
}