Skip to content

Commit 28a7e8b

Browse files
committed
Run container-structure-test on remote images
Fixes #3907 Fixes #1082 Signed-off-by: David Gageot <[email protected]>
1 parent 07993d1 commit 28a7e8b

File tree

4 files changed

+93
-28
lines changed

4 files changed

+93
-28
lines changed

pkg/skaffold/runner/new.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,14 @@ func NewForConfig(runCtx *runcontext.RunContext) (*SkaffoldRunner, error) {
5252
return nil, fmt.Errorf("parsing build config: %w", err)
5353
}
5454

55-
tester := getTester(runCtx)
56-
syncer := getSyncer(runCtx)
57-
5855
imagesAreLocal := false
5956
if localBuilder, ok := builder.(*local.Builder); ok {
6057
imagesAreLocal = !localBuilder.PushImages()
6158
}
6259

60+
tester := getTester(runCtx, imagesAreLocal)
61+
syncer := getSyncer(runCtx)
62+
6363
depLister := func(ctx context.Context, artifact *latest.Artifact) ([]string, error) {
6464
buildDependencies, err := build.DependenciesForArtifact(ctx, artifact, runCtx.InsecureRegistries)
6565
if err != nil {
@@ -209,8 +209,8 @@ func getBuilder(runCtx *runcontext.RunContext) (build.Builder, error) {
209209
}
210210
}
211211

212-
func getTester(runCtx *runcontext.RunContext) test.Tester {
213-
return test.NewTester(runCtx)
212+
func getTester(runCtx *runcontext.RunContext, imagesAreLocal bool) test.Tester {
213+
return test.NewTester(runCtx, imagesAreLocal)
214214
}
215215

216216
func getSyncer(runCtx *runcontext.RunContext) sync.Syncer {

pkg/skaffold/test/test.go

+15-6
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,17 @@ import (
3232
// NewTester parses the provided test cases from the Skaffold config,
3333
// and returns a Tester instance with all the necessary test runners
3434
// to run all specified tests.
35-
func NewTester(runCtx *runcontext.RunContext) Tester {
36-
client, err := docker.NewAPIClient(runCtx)
35+
func NewTester(runCtx *runcontext.RunContext, imagesAreLocal bool) Tester {
36+
localDaemon, err := docker.NewAPIClient(runCtx)
3737
if err != nil {
3838
return nil
3939
}
4040

4141
return FullTester{
42-
testCases: runCtx.Cfg.Test,
43-
workingDir: runCtx.WorkingDir,
44-
extraEnv: client.ExtraEnv(),
42+
testCases: runCtx.Cfg.Test,
43+
workingDir: runCtx.WorkingDir,
44+
localDaemon: localDaemon,
45+
imagesAreLocal: imagesAreLocal,
4546
}
4647
}
4748

@@ -84,8 +85,16 @@ func (t FullTester) runStructureTests(ctx context.Context, out io.Writer, bRes [
8485
}
8586

8687
fqn := resolveArtifactImageTag(testCase.ImageName, bRes)
88+
if !t.imagesAreLocal {
89+
// The image is remote so we have to pull it locally.
90+
// `container-structure-test` currently can't do it:
91+
// https://github.com/GoogleContainerTools/container-structure-test/issues/253.
92+
if err := t.localDaemon.Pull(ctx, out, fqn); err != nil {
93+
return fmt.Errorf("unable to docker pull image %q: %w", fqn, err)
94+
}
95+
}
8796

88-
runner := structure.NewRunner(files, t.extraEnv)
97+
runner := structure.NewRunner(files, t.localDaemon.ExtraEnv())
8998
return runner.Test(ctx, out, fqn)
9099
}
91100

pkg/skaffold/test/test_test.go

+68-14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"testing"
2424

2525
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
26+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
2627
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/runner/runcontext"
2728
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
2829
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
@@ -32,7 +33,7 @@ import (
3233
func TestNoTestDependencies(t *testing.T) {
3334
runCtx := &runcontext.RunContext{}
3435

35-
deps, err := NewTester(runCtx).TestDependencies()
36+
deps, err := NewTester(runCtx, true).TestDependencies()
3637

3738
testutil.CheckErrorAndDeepEqual(t, false, err, 0, len(deps))
3839
}
@@ -53,7 +54,7 @@ func TestTestDependencies(t *testing.T) {
5354
},
5455
}
5556

56-
deps, err := NewTester(runCtx).TestDependencies()
57+
deps, err := NewTester(runCtx, true).TestDependencies()
5758

5859
expectedDeps := tmpDir.Paths("tests/test1.yaml", "tests/test2.yaml", "test3.yaml")
5960
testutil.CheckErrorAndDeepEqual(t, false, err, expectedDeps, deps)
@@ -68,7 +69,7 @@ func TestWrongPattern(t *testing.T) {
6869
},
6970
}
7071

71-
tester := NewTester(runCtx)
72+
tester := NewTester(runCtx, true)
7273

7374
_, err := tester.TestDependencies()
7475
testutil.CheckError(t, true, err)
@@ -80,19 +81,18 @@ func TestWrongPattern(t *testing.T) {
8081
func TestNoTest(t *testing.T) {
8182
runCtx := &runcontext.RunContext{}
8283

83-
err := NewTester(runCtx).Test(context.Background(), ioutil.Discard, nil)
84+
err := NewTester(runCtx, true).Test(context.Background(), ioutil.Discard, nil)
8485

8586
testutil.CheckError(t, false, err)
8687
}
8788

8889
func TestTestSuccess(t *testing.T) {
8990
testutil.Run(t, "", func(t *testutil.T) {
90-
tmpDir := t.NewTempDir()
91-
tmpDir.Touch("tests/test1.yaml", "tests/test2.yaml", "test3.yaml")
91+
tmpDir := t.NewTempDir().Touch("tests/test1.yaml", "tests/test2.yaml", "test3.yaml")
9292

9393
t.Override(&util.DefaultExecCommand, testutil.
94-
CmdRun("container-structure-test test -v warn --image TAG --config "+tmpDir.Path("tests/test1.yaml")+" --config "+tmpDir.Path("tests/test2.yaml")).
95-
AndRun("container-structure-test test -v warn --image TAG --config "+tmpDir.Path("test3.yaml")))
94+
CmdRun("container-structure-test test -v warn --image image:tag --config "+tmpDir.Path("tests/test1.yaml")+" --config "+tmpDir.Path("tests/test2.yaml")).
95+
AndRun("container-structure-test test -v warn --image image:tag --config "+tmpDir.Path("test3.yaml")))
9696

9797
runCtx := &runcontext.RunContext{
9898
WorkingDir: tmpDir.Root(),
@@ -111,26 +111,80 @@ func TestTestSuccess(t *testing.T) {
111111
},
112112
}
113113

114-
err := NewTester(runCtx).Test(context.Background(), ioutil.Discard, []build.Artifact{{
114+
imagesAreLocal := true
115+
err := NewTester(runCtx, imagesAreLocal).Test(context.Background(), ioutil.Discard, []build.Artifact{{
115116
ImageName: "image",
116-
Tag: "TAG",
117+
Tag: "image:tag",
117118
}})
118119

119120
t.CheckNoError(err)
120121
})
121122
}
122123

124+
func TestTestSuccessRemoteImage(t *testing.T) {
125+
testutil.Run(t, "", func(t *testutil.T) {
126+
t.NewTempDir().Touch("test.yaml").Chdir()
127+
t.Override(&util.DefaultExecCommand, testutil.CmdRun("container-structure-test test -v warn --image image:tag --config test.yaml"))
128+
t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) {
129+
return docker.NewLocalDaemon(&testutil.FakeAPIClient{}, nil, false, nil), nil
130+
})
131+
132+
runCtx := &runcontext.RunContext{
133+
Cfg: latest.Pipeline{
134+
Test: []*latest.TestCase{{
135+
ImageName: "image",
136+
StructureTests: []string{"test.yaml"},
137+
}},
138+
},
139+
}
140+
141+
imagesAreLocal := false
142+
err := NewTester(runCtx, imagesAreLocal).Test(context.Background(), ioutil.Discard, []build.Artifact{{
143+
ImageName: "image",
144+
Tag: "image:tag",
145+
}})
146+
147+
t.CheckNoError(err)
148+
})
149+
}
150+
151+
func TestTestFailureRemoteImage(t *testing.T) {
152+
testutil.Run(t, "", func(t *testutil.T) {
153+
t.NewTempDir().Touch("test.yaml").Chdir()
154+
t.Override(&util.DefaultExecCommand, testutil.CmdRun("container-structure-test test -v warn --image image:tag --config test.yaml"))
155+
t.Override(&docker.NewAPIClient, func(*runcontext.RunContext) (docker.LocalDaemon, error) {
156+
return docker.NewLocalDaemon(&testutil.FakeAPIClient{ErrImagePull: true}, nil, false, nil), nil
157+
})
158+
159+
runCtx := &runcontext.RunContext{
160+
Cfg: latest.Pipeline{
161+
Test: []*latest.TestCase{{
162+
ImageName: "image",
163+
StructureTests: []string{"test.yaml"},
164+
}},
165+
},
166+
}
167+
168+
imagesAreLocal := false
169+
err := NewTester(runCtx, imagesAreLocal).Test(context.Background(), ioutil.Discard, []build.Artifact{{
170+
ImageName: "image",
171+
Tag: "image:tag",
172+
}})
173+
174+
t.CheckErrorContains(`unable to docker pull image "image:tag"`, err)
175+
})
176+
}
177+
123178
func TestTestFailure(t *testing.T) {
124179
testutil.Run(t, "", func(t *testutil.T) {
125-
tmpDir := t.NewTempDir().Touch("test.yaml")
180+
t.NewTempDir().Touch("test.yaml").Chdir()
126181

127182
t.Override(&util.DefaultExecCommand, testutil.CmdRunErr(
128-
"container-structure-test test -v warn --image broken-image --config "+tmpDir.Path("test.yaml"),
183+
"container-structure-test test -v warn --image broken-image --config test.yaml",
129184
errors.New("FAIL"),
130185
))
131186

132187
runCtx := &runcontext.RunContext{
133-
WorkingDir: tmpDir.Root(),
134188
Cfg: latest.Pipeline{
135189
Test: []*latest.TestCase{
136190
{
@@ -141,7 +195,7 @@ func TestTestFailure(t *testing.T) {
141195
},
142196
}
143197

144-
err := NewTester(runCtx).Test(context.Background(), ioutil.Discard, []build.Artifact{{}})
198+
err := NewTester(runCtx, true).Test(context.Background(), ioutil.Discard, []build.Artifact{{}})
145199
t.CheckError(true, err)
146200
})
147201
}

pkg/skaffold/test/types.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"io"
2222

2323
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
24+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
2425
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
2526
)
2627

@@ -42,9 +43,10 @@ type Tester interface {
4243
// FullTester should always be the ONLY implementation of the Tester interface;
4344
// newly added testing implementations should implement the Runner interface.
4445
type FullTester struct {
45-
testCases []*latest.TestCase
46-
workingDir string
47-
extraEnv []string
46+
testCases []*latest.TestCase
47+
localDaemon docker.LocalDaemon
48+
workingDir string
49+
imagesAreLocal bool
4850
}
4951

5052
// Runner is the lowest-level test executor in Skaffold, responsible for

0 commit comments

Comments
 (0)