Skip to content

Commit 95df153

Browse files
authored
Merge pull request #1013 from nkubala/test-runner
Add test runner
2 parents bd3fc88 + a7b9412 commit 95df153

File tree

14 files changed

+391
-9
lines changed

14 files changed

+391
-9
lines changed

deploy/skaffold/Dockerfile

+5-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8
4747
RUN apt-get update \
4848
&& apt-get install -y bazel
4949

50+
ENV CONTAINER_STRUCTURE_TEST_VERSION=1.5.0
51+
RUN curl -LO https://storage.googleapis.com/container-structure-test/v${CONTAINER_STRUCTURE_TEST_VERSION}/container-structure-test-linux-amd64 \
52+
&& chmod +x container-structure-test-linux-amd64 \
53+
&& mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test
54+
5055
ENV PATH /usr/local/go/bin:/go/bin:/google-cloud-sdk/bin:$PATH
5156

5257
FROM runtime_deps as builder
@@ -94,4 +99,3 @@ CMD ["make", "integration"]
9499
FROM runtime_deps as distribution
95100

96101
COPY --from=integration /usr/bin/skaffold /usr/bin/skaffold
97-

integration/examples/getting-started/skaffold.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ kind: Config
33
build:
44
artifacts:
55
- image: gcr.io/k8s-skaffold/skaffold-example
6+
test:
7+
- image: gcr.io/k8s-skaffold/skaffold-example
8+
structureTests:
9+
- ./test/*
610
deploy:
711
kubectl:
812
manifests:
@@ -12,3 +16,8 @@ profiles:
1216
build:
1317
googleCloudBuild:
1418
projectId: k8s-skaffold
19+
- name: test
20+
test:
21+
- image: gcr.io/k8s-skaffold/skaffold-example
22+
structureTests:
23+
- ./test/profile_structure_test.yaml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
schemaVersion: 2.0.0
2+
3+
fileExistenceTests:
4+
- name: 'no go binary'
5+
path: '/usr/bin/go'
6+
shouldExist: false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
schemaVersion: 2.0.0
2+
3+
fileExistenceTests:
4+
- name: 'no local go binary'
5+
path: /usr/local/bin/go'
6+
shouldExist: false

pkg/skaffold/runner/runner.go

+37-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
3939
kubectx "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/context"
4040
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
41+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/test"
4142
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/watch"
4243

4344
"github.com/pkg/errors"
@@ -51,6 +52,7 @@ var ErrorConfigurationChanged = errors.New("configuration changed")
5152
type SkaffoldRunner struct {
5253
build.Builder
5354
deploy.Deployer
55+
test.Tester
5456
tag.Tagger
5557

5658
opts *config.SkaffoldOptions
@@ -76,19 +78,25 @@ func NewForConfig(opts *config.SkaffoldOptions, cfg *latest.SkaffoldConfig) (*Sk
7678
return nil, errors.Wrap(err, "parsing skaffold build config")
7779
}
7880

81+
tester, err := getTester(&cfg.Test)
82+
if err != nil {
83+
return nil, errors.Wrap(err, "parsing skaffold test config")
84+
}
85+
7986
deployer, err := getDeployer(&cfg.Deploy, kubeContext, opts.Namespace)
8087
if err != nil {
8188
return nil, errors.Wrap(err, "parsing skaffold deploy config")
8289
}
8390

8491
deployer = deploy.WithLabels(deployer, opts, builder, deployer, tagger)
85-
builder, deployer = WithTimings(builder, deployer)
92+
builder, tester, deployer = WithTimings(builder, tester, deployer)
8693
if opts.Notification {
8794
deployer = WithNotification(deployer)
8895
}
8996

9097
return &SkaffoldRunner{
9198
Builder: builder,
99+
Tester: tester,
92100
Deployer: deployer,
93101
Tagger: tagger,
94102
opts: opts,
@@ -115,6 +123,10 @@ func getBuilder(cfg *latest.BuildConfig, kubeContext string) (build.Builder, err
115123
}
116124
}
117125

126+
func getTester(cfg *[]latest.TestCase) (test.Tester, error) {
127+
return test.NewTester(cfg)
128+
}
129+
118130
func getDeployer(cfg *latest.DeployConfig, kubeContext string, namespace string) (deploy.Deployer, error) {
119131
deployers := []deploy.Deployer{}
120132

@@ -171,13 +183,17 @@ func getTagger(t latest.TagPolicy, customTag string) (tag.Tagger, error) {
171183
}
172184
}
173185

174-
// Run builds artifacts and then deploys them.
186+
// Run builds artifacts, runs tests on built artifacts, and then deploys them.
175187
func (r *SkaffoldRunner) Run(ctx context.Context, out io.Writer, artifacts []*latest.Artifact) error {
176188
bRes, err := r.Build(ctx, out, r.Tagger, artifacts)
177189
if err != nil {
178190
return errors.Wrap(err, "build step")
179191
}
180192

193+
if err = r.Test(out, bRes); err != nil {
194+
return errors.Wrap(err, "test step")
195+
}
196+
181197
_, err = r.Deploy(ctx, out, bRes)
182198
if err != nil {
183199
return errors.Wrap(err, "deploy step")
@@ -241,12 +257,20 @@ func (r *SkaffoldRunner) Dev(ctx context.Context, out io.Writer, artifacts []*la
241257
}
242258

243259
r.updateBuiltImages(imageList, bRes)
260+
if err := r.Test(out, bRes); err != nil {
261+
logrus.Warnln("Skipping Deploy due to failed tests:", err)
262+
return nil
263+
}
244264

245265
if _, err = r.Deploy(ctx, out, r.builds); err != nil {
246266
logrus.Warnln("Skipping Deploy due to error:", err)
247267
return nil
248268
}
249269
case changed.needsRedeploy:
270+
if err := r.Test(out, r.builds); err != nil {
271+
logrus.Warnln("Skipping Deploy due to failed tests:", err)
272+
return nil
273+
}
250274
if _, err := r.Deploy(ctx, out, r.builds); err != nil {
251275
logrus.Warnln("Skipping Deploy due to error:", err)
252276
return nil
@@ -275,6 +299,14 @@ func (r *SkaffoldRunner) Dev(ctx context.Context, out io.Writer, artifacts []*la
275299
}
276300
}
277301

302+
// Watch test configuration
303+
if err := watcher.Register(
304+
func() ([]string, error) { return r.TestDependencies(), nil },
305+
func(watch.Events) { changed.needsRedeploy = true },
306+
); err != nil {
307+
return nil, errors.Wrap(err, "watching test files")
308+
}
309+
278310
// Watch deployment configuration
279311
if err := watcher.Register(
280312
func() ([]string, error) { return r.Dependencies() },
@@ -298,6 +330,9 @@ func (r *SkaffoldRunner) Dev(ctx context.Context, out io.Writer, artifacts []*la
298330
}
299331

300332
r.updateBuiltImages(imageList, bRes)
333+
if err := r.Test(out, bRes); err != nil {
334+
return nil, errors.Wrap(err, "exiting dev mode because the first test run failed")
335+
}
301336

302337
_, err = r.Deploy(ctx, out, r.builds)
303338
if err != nil {

pkg/skaffold/runner/runner_test.go

+69-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy"
3232
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes"
3333
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
34+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/test"
3435
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/watch"
3536
"github.com/GoogleContainerTools/skaffold/testutil"
3637
clientgo "k8s.io/client-go/kubernetes"
@@ -65,6 +66,23 @@ func (t *TestBuilder) Build(ctx context.Context, w io.Writer, tagger tag.Tagger,
6566
return builds, nil
6667
}
6768

69+
type TestTester struct {
70+
errors []error
71+
}
72+
73+
func (t *TestTester) Test(out io.Writer, builds []build.Artifact) error {
74+
if len(t.errors) > 0 {
75+
err := t.errors[0]
76+
t.errors = t.errors[1:]
77+
return err
78+
}
79+
return nil
80+
}
81+
82+
func (t *TestTester) TestDependencies() []string {
83+
return []string{}
84+
}
85+
6886
type TestDeployer struct {
6987
deployed []build.Artifact
7088
errors []error
@@ -140,6 +158,7 @@ func TestNewForConfig(t *testing.T) {
140158
config *latest.SkaffoldConfig
141159
shouldErr bool
142160
expectedBuilder build.Builder
161+
expectedTester test.Tester
143162
expectedDeployer deploy.Deployer
144163
}{
145164
{
@@ -158,6 +177,7 @@ func TestNewForConfig(t *testing.T) {
158177
},
159178
},
160179
expectedBuilder: &local.Builder{},
180+
expectedTester: &test.FullTester{},
161181
expectedDeployer: &deploy.KubectlDeployer{},
162182
},
163183
{
@@ -184,6 +204,7 @@ func TestNewForConfig(t *testing.T) {
184204
},
185205
shouldErr: true,
186206
expectedBuilder: &local.Builder{},
207+
expectedTester: &test.FullTester{},
187208
expectedDeployer: &deploy.KubectlDeployer{},
188209
},
189210
{
@@ -197,6 +218,7 @@ func TestNewForConfig(t *testing.T) {
197218
}},
198219
shouldErr: true,
199220
expectedBuilder: &local.Builder{},
221+
expectedTester: &test.FullTester{},
200222
expectedDeployer: &deploy.KubectlDeployer{},
201223
},
202224
{
@@ -218,9 +240,10 @@ func TestNewForConfig(t *testing.T) {
218240

219241
testutil.CheckError(t, test.shouldErr, err)
220242
if cfg != nil {
221-
b, d := WithTimings(test.expectedBuilder, test.expectedDeployer)
243+
b, _t, d := WithTimings(test.expectedBuilder, test.expectedTester, test.expectedDeployer)
222244

223245
testutil.CheckErrorAndTypeEquality(t, test.shouldErr, err, b, cfg.Builder)
246+
testutil.CheckErrorAndTypeEquality(t, test.shouldErr, err, _t, cfg.Tester)
224247
testutil.CheckErrorAndTypeEquality(t, test.shouldErr, err, d, cfg.Deployer)
225248
}
226249
})
@@ -232,13 +255,15 @@ func TestRun(t *testing.T) {
232255
description string
233256
config *latest.SkaffoldConfig
234257
builder build.Builder
258+
tester test.Tester
235259
deployer deploy.Deployer
236260
shouldErr bool
237261
}{
238262
{
239263
description: "run no error",
240264
config: &latest.SkaffoldConfig{},
241265
builder: &TestBuilder{},
266+
tester: &TestTester{},
242267
deployer: &TestDeployer{},
243268
},
244269
{
@@ -247,6 +272,7 @@ func TestRun(t *testing.T) {
247272
builder: &TestBuilder{
248273
errors: []error{fmt.Errorf("")},
249274
},
275+
tester: &TestTester{},
250276
shouldErr: true,
251277
},
252278
{
@@ -261,17 +287,42 @@ func TestRun(t *testing.T) {
261287
},
262288
},
263289
builder: &TestBuilder{},
290+
tester: &TestTester{},
264291
deployer: &TestDeployer{
265292
errors: []error{fmt.Errorf("")},
266293
},
267294
shouldErr: true,
268295
},
296+
{
297+
description: "run test error",
298+
config: &latest.SkaffoldConfig{
299+
Build: latest.BuildConfig{
300+
Artifacts: []*latest.Artifact{
301+
{
302+
ImageName: "test",
303+
},
304+
},
305+
},
306+
Test: []latest.TestCase{
307+
{
308+
ImageName: "test",
309+
StructureTests: []string{"fake_file.yaml"},
310+
},
311+
},
312+
},
313+
builder: &TestBuilder{},
314+
tester: &TestTester{
315+
errors: []error{fmt.Errorf("")},
316+
},
317+
shouldErr: true,
318+
},
269319
}
270320

271321
for _, test := range tests {
272322
t.Run(test.description, func(t *testing.T) {
273323
runner := &SkaffoldRunner{
274324
Builder: test.builder,
325+
Tester: test.tester,
275326
Deployer: test.deployer,
276327
Tagger: &tag.ChecksumTagger{},
277328
opts: &config.SkaffoldOptions{},
@@ -290,6 +341,7 @@ func TestDev(t *testing.T) {
290341
var tests = []struct {
291342
description string
292343
builder build.Builder
344+
tester test.Tester
293345
deployer deploy.Deployer
294346
watcherFactory watch.Factory
295347
shouldErr bool
@@ -306,23 +358,35 @@ func TestDev(t *testing.T) {
306358
{
307359
description: "fails to deploy the first time",
308360
builder: &TestBuilder{},
361+
tester: &TestTester{},
309362
deployer: &TestDeployer{
310363
errors: []error{fmt.Errorf("")},
311364
},
312365
watcherFactory: NewWatcherFactory(nil, nil),
313366
shouldErr: true,
314367
},
368+
{
369+
description: "fails to deploy due to failed tests",
370+
builder: &TestBuilder{},
371+
tester: &TestTester{
372+
errors: []error{fmt.Errorf("")},
373+
},
374+
watcherFactory: NewWatcherFactory(nil, nil),
375+
shouldErr: true,
376+
},
315377
{
316378
description: "ignore subsequent build errors",
317379
builder: &TestBuilder{
318380
errors: []error{nil, fmt.Errorf("")},
319381
},
382+
tester: &TestTester{},
320383
deployer: &TestDeployer{},
321384
watcherFactory: NewWatcherFactory(nil, nil, nil),
322385
},
323386
{
324387
description: "ignore subsequent deploy errors",
325388
builder: &TestBuilder{},
389+
tester: &TestTester{},
326390
deployer: &TestDeployer{
327391
errors: []error{nil, fmt.Errorf("")},
328392
},
@@ -331,6 +395,7 @@ func TestDev(t *testing.T) {
331395
{
332396
description: "fail to watch files",
333397
builder: &TestBuilder{},
398+
tester: &TestTester{},
334399
deployer: &TestDeployer{},
335400
watcherFactory: NewWatcherFactory(fmt.Errorf(""), nil),
336401
shouldErr: true,
@@ -341,6 +406,7 @@ func TestDev(t *testing.T) {
341406
t.Run(test.description, func(t *testing.T) {
342407
runner := &SkaffoldRunner{
343408
Builder: test.builder,
409+
Tester: test.tester,
344410
Deployer: test.deployer,
345411
Tagger: &tag.ChecksumTagger{},
346412
watchFactory: test.watcherFactory,
@@ -360,6 +426,7 @@ func TestBuildAndDeployAllArtifacts(t *testing.T) {
360426
defer resetClient()
361427

362428
builder := &TestBuilder{}
429+
tester := &TestTester{}
363430
deployer := &TestDeployer{}
364431
artifacts := []*latest.Artifact{
365432
{ImageName: "image1"},
@@ -368,6 +435,7 @@ func TestBuildAndDeployAllArtifacts(t *testing.T) {
368435

369436
runner := &SkaffoldRunner{
370437
Builder: builder,
438+
Tester: tester,
371439
Deployer: deployer,
372440
opts: &config.SkaffoldOptions{},
373441
}

0 commit comments

Comments
 (0)