Skip to content

Commit 176cb17

Browse files
committed
Support short build logs
Signed-off-by: David Gageot <[email protected]>
1 parent eedf94c commit 176cb17

File tree

12 files changed

+204
-14
lines changed

12 files changed

+204
-14
lines changed

pkg/skaffold/build/cluster/cluster.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags,
4545
defer teardownDockerConfigSecret()
4646
}
4747

48-
return build.InParallel(ctx, out, tags, artifacts, b.buildArtifact, b.ClusterDetails.Concurrency)
48+
builder := build.WithLogFile(b.buildArtifact, b.suppressLogs)
49+
return build.InParallel(ctx, out, tags, artifacts, builder, b.ClusterDetails.Concurrency)
4950
}
5051

5152
func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) {

pkg/skaffold/build/cluster/types.go

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type Builder struct {
3535
kubeContext string
3636
timeout time.Duration
3737
insecureRegistries map[string]bool
38+
suppressLogs []string
3839
}
3940

4041
// NewBuilder creates a new Builder that builds artifacts on cluster.
@@ -50,6 +51,7 @@ func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) {
5051
timeout: timeout,
5152
kubeContext: runCtx.KubeContext,
5253
insecureRegistries: runCtx.InsecureRegistries,
54+
suppressLogs: runCtx.Opts.SuppressLogs,
5355
}, nil
5456
}
5557

pkg/skaffold/build/gcb/cloud_build.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ import (
4646

4747
// Build builds a list of artifacts with Google Cloud Build.
4848
func (b *Builder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact) ([]build.Artifact, error) {
49-
return build.InParallel(ctx, out, tags, artifacts, b.buildArtifactWithCloudBuild, b.GoogleCloudBuild.Concurrency)
49+
builder := build.WithLogFile(b.buildArtifactWithCloudBuild, b.suppressLogs)
50+
return build.InParallel(ctx, out, tags, artifacts, builder, b.GoogleCloudBuild.Concurrency)
5051
}
5152

5253
func (b *Builder) buildArtifactWithCloudBuild(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) {

pkg/skaffold/build/gcb/types.go

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ type Builder struct {
7979
*latest.GoogleCloudBuild
8080
skipTests bool
8181
insecureRegistries map[string]bool
82+
suppressLogs []string
8283
}
8384

8485
// NewBuilder creates a new Builder that builds artifacts with Google Cloud Build.
@@ -87,6 +88,7 @@ func NewBuilder(runCtx *runcontext.RunContext) *Builder {
8788
GoogleCloudBuild: runCtx.Cfg.Build.GoogleCloudBuild,
8889
skipTests: runCtx.Opts.SkipTests,
8990
insecureRegistries: runCtx.InsecureRegistries,
91+
suppressLogs: runCtx.Opts.SuppressLogs,
9092
}
9193
}
9294

pkg/skaffold/build/local/local.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ func (b *Builder) Build(ctx context.Context, out io.Writer, tags tag.ImageTags,
4242
}
4343
defer b.localDocker.Close()
4444

45-
return build.InParallel(ctx, out, tags, artifacts, b.buildArtifact, *b.cfg.Concurrency)
45+
builder := build.WithLogFile(b.buildArtifact, b.suppressLogs)
46+
return build.InParallel(ctx, out, tags, artifacts, builder, *b.cfg.Concurrency)
4647
}
4748

4849
func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) {

pkg/skaffold/build/local/types.go

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type Builder struct {
4343
kubeContext string
4444
builtImages []string
4545
insecureRegistries map[string]bool
46+
suppressLogs []string
4647
}
4748

4849
// external dependencies are wrapped
@@ -85,6 +86,7 @@ func NewBuilder(runCtx *runcontext.RunContext) (*Builder, error) {
8586
prune: runCtx.Opts.Prune(),
8687
pruneChildren: !runCtx.Opts.NoPruneChildren,
8788
insecureRegistries: runCtx.InsecureRegistries,
89+
suppressLogs: runCtx.Opts.SuppressLogs,
8890
}, nil
8991
}
9092

pkg/skaffold/build/logfile.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright 2020 The Skaffold Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package build
18+
19+
import (
20+
"bytes"
21+
"context"
22+
"fmt"
23+
"io"
24+
25+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/logfile"
26+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
27+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
28+
)
29+
30+
// WithLogFile wraps an `artifactBuilder` so that it optionnaly outputs its logs to a file.
31+
func WithLogFile(builder ArtifactBuilder, suppressedLogs []string) ArtifactBuilder {
32+
if !(util.StrSliceContains(suppressedLogs, "build") || util.StrSliceContains(suppressedLogs, "all")) {
33+
return builder
34+
}
35+
36+
return func(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error) {
37+
file, err := logfile.Create(artifact.ImageName + ".log")
38+
if err != nil {
39+
return "", fmt.Errorf("unable create log file for %s: %w", artifact.ImageName, err)
40+
}
41+
fmt.Fprintln(out, " - writing logs to:", file.Name())
42+
43+
// Print logs to a memory buffer and to a file.
44+
var buf bytes.Buffer
45+
w := io.MultiWriter(file, &buf)
46+
47+
// Run the build.
48+
digest, err := builder(ctx, w, artifact, tag)
49+
50+
// After the build finishes, close the log file. If the build failed, print the full log to the console.
51+
file.Close()
52+
if err != nil {
53+
buf.WriteTo(out)
54+
}
55+
56+
return digest, err
57+
}
58+
}

pkg/skaffold/build/logfile_test.go

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
Copyright 2020 The Skaffold Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package build
18+
19+
import (
20+
"bytes"
21+
"context"
22+
"errors"
23+
"fmt"
24+
"io"
25+
"path/filepath"
26+
"strings"
27+
"testing"
28+
29+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
30+
"github.com/GoogleContainerTools/skaffold/testutil"
31+
)
32+
33+
func TestWithLogFile(t *testing.T) {
34+
tests := []struct {
35+
description string
36+
builder ArtifactBuilder
37+
suppress []string
38+
shouldErr bool
39+
expectedDigest string
40+
logsFound []string
41+
logsNotFound []string
42+
}{
43+
{
44+
description: "all logs",
45+
builder: fakeBuilder,
46+
suppress: nil,
47+
shouldErr: false,
48+
expectedDigest: "digest",
49+
logsFound: []string{"building img with tag img:123"},
50+
logsNotFound: []string{" - writing logs to: ", filepath.Join("skaffold", "img.log")},
51+
},
52+
{
53+
description: "suppress build logs",
54+
builder: fakeBuilder,
55+
suppress: []string{"build"},
56+
shouldErr: false,
57+
expectedDigest: "digest",
58+
logsFound: []string{" - writing logs to: ", filepath.Join("skaffold", "img.log")},
59+
logsNotFound: []string{"building img with tag img:123"},
60+
},
61+
{
62+
description: "suppress all logs",
63+
builder: fakeBuilder,
64+
suppress: []string{"all"},
65+
shouldErr: false,
66+
expectedDigest: "digest",
67+
logsFound: []string{" - writing logs to: ", filepath.Join("skaffold", "img.log")},
68+
logsNotFound: []string{"building img with tag img:123"},
69+
},
70+
{
71+
description: "suppress only deploy logs",
72+
builder: fakeBuilder,
73+
suppress: []string{"deploy"},
74+
shouldErr: false,
75+
expectedDigest: "digest",
76+
logsFound: []string{"building img with tag img:123"},
77+
logsNotFound: []string{" - writing logs to: ", filepath.Join("skaffold", "img.log")},
78+
},
79+
{
80+
description: "failed build - all logs",
81+
builder: fakeFailingBuilder,
82+
suppress: nil,
83+
shouldErr: true,
84+
expectedDigest: "",
85+
logsFound: []string{"failed to build img with tag img:123"},
86+
logsNotFound: []string{" - writing logs to: ", filepath.Join("skaffold", "img.log")},
87+
},
88+
{
89+
description: "failed build - suppressed logs",
90+
builder: fakeFailingBuilder,
91+
suppress: []string{"build"},
92+
shouldErr: true,
93+
expectedDigest: "",
94+
logsFound: []string{" - writing logs to: ", filepath.Join("skaffold", "img.log"), "failed to build img with tag img:123"},
95+
},
96+
}
97+
for _, test := range tests {
98+
testutil.Run(t, test.description, func(t *testutil.T) {
99+
var out bytes.Buffer
100+
101+
builder := WithLogFile(test.builder, test.suppress)
102+
digest, err := builder(context.Background(), &out, &latest.Artifact{ImageName: "img"}, "img:123")
103+
104+
t.CheckErrorAndDeepEqual(test.shouldErr, err, test.expectedDigest, digest)
105+
for _, found := range test.logsFound {
106+
t.CheckContains(found, out.String())
107+
}
108+
for _, notFound := range test.logsNotFound {
109+
t.CheckFalse(strings.Contains(out.String(), notFound))
110+
}
111+
})
112+
}
113+
}
114+
115+
func fakeBuilder(_ context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) {
116+
fmt.Fprintln(out, "building", a.ImageName, "with tag", tag)
117+
return "digest", nil
118+
}
119+
120+
func fakeFailingBuilder(_ context.Context, out io.Writer, a *latest.Artifact, tag string) (string, error) {
121+
fmt.Fprintln(out, "failed to build", a.ImageName, "with tag", tag)
122+
return "", errors.New("bug")
123+
}

pkg/skaffold/build/parallel.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import (
3131

3232
const bufferedLinesPerArtifact = 10000
3333

34-
type artifactBuilder func(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error)
34+
type ArtifactBuilder func(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string) (string, error)
3535

3636
// For testing
3737
var (
@@ -40,7 +40,7 @@ var (
4040
)
4141

4242
// InParallel builds a list of artifacts in parallel but prints the logs in sequential order.
43-
func InParallel(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, buildArtifact artifactBuilder, concurrency int) ([]Artifact, error) {
43+
func InParallel(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, buildArtifact ArtifactBuilder, concurrency int) ([]Artifact, error) {
4444
if len(artifacts) == 0 {
4545
return nil, nil
4646
}
@@ -87,7 +87,7 @@ func InParallel(ctx context.Context, out io.Writer, tags tag.ImageTags, artifact
8787
return collectResults(out, artifacts, results, outputs)
8888
}
8989

90-
func runBuild(ctx context.Context, cw io.WriteCloser, tags tag.ImageTags, artifact *latest.Artifact, results *sync.Map, build artifactBuilder) {
90+
func runBuild(ctx context.Context, cw io.WriteCloser, tags tag.ImageTags, artifact *latest.Artifact, results *sync.Map, build ArtifactBuilder) {
9191
event.BuildInProgress(artifact.ImageName)
9292

9393
finalTag, err := getBuildResult(ctx, cw, tags, artifact, build)
@@ -110,7 +110,7 @@ func readOutputAndWriteToChannel(r io.Reader, lines chan string) {
110110
close(lines)
111111
}
112112

113-
func getBuildResult(ctx context.Context, cw io.Writer, tags tag.ImageTags, artifact *latest.Artifact, build artifactBuilder) (string, error) {
113+
func getBuildResult(ctx context.Context, cw io.Writer, tags tag.ImageTags, artifact *latest.Artifact, build ArtifactBuilder) (string, error) {
114114
color.Default.Fprintf(cw, "Building [%s]...\n", artifact.ImageName)
115115
tag, present := tags[artifact.ImageName]
116116
if !present {

pkg/skaffold/build/parallel_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import (
3535
func TestGetBuild(t *testing.T) {
3636
tests := []struct {
3737
description string
38-
buildArtifact artifactBuilder
38+
buildArtifact ArtifactBuilder
3939
tags tag.ImageTags
4040
expectedTag string
4141
expectedOut string
@@ -197,7 +197,7 @@ func TestCollectResults(t *testing.T) {
197197
func TestInParallel(t *testing.T) {
198198
tests := []struct {
199199
description string
200-
buildFunc artifactBuilder
200+
buildFunc ArtifactBuilder
201201
expected string
202202
}{
203203
{
@@ -302,14 +302,14 @@ func TestInParallelConcurrency(t *testing.T) {
302302
func TestInParallelForArgs(t *testing.T) {
303303
tests := []struct {
304304
description string
305-
inSeqFunc func(context.Context, io.Writer, tag.ImageTags, []*latest.Artifact, artifactBuilder) ([]Artifact, error)
306-
buildArtifact artifactBuilder
305+
inSeqFunc func(context.Context, io.Writer, tag.ImageTags, []*latest.Artifact, ArtifactBuilder) ([]Artifact, error)
306+
buildArtifact ArtifactBuilder
307307
artifactLen int
308308
expected []Artifact
309309
}{
310310
{
311311
description: "runs in sequence for 1 artifact",
312-
inSeqFunc: func(context.Context, io.Writer, tag.ImageTags, []*latest.Artifact, artifactBuilder) ([]Artifact, error) {
312+
inSeqFunc: func(context.Context, io.Writer, tag.ImageTags, []*latest.Artifact, ArtifactBuilder) ([]Artifact, error) {
313313
return []Artifact{{ImageName: "singleArtifact", Tag: "one"}}, nil
314314
},
315315
artifactLen: 1,

pkg/skaffold/build/sequence.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import (
2828
)
2929

3030
// InSequence builds a list of artifacts in sequence.
31-
func InSequence(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, buildArtifact artifactBuilder) ([]Artifact, error) {
31+
func InSequence(ctx context.Context, out io.Writer, tags tag.ImageTags, artifacts []*latest.Artifact, buildArtifact ArtifactBuilder) ([]Artifact, error) {
3232
var builds []Artifact
3333

3434
for _, artifact := range artifacts {

pkg/skaffold/build/sequence_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import (
3333
func TestInSequence(t *testing.T) {
3434
tests := []struct {
3535
description string
36-
buildArtifact artifactBuilder
36+
buildArtifact ArtifactBuilder
3737
tags tag.ImageTags
3838
expectedArtifacts []Artifact
3939
expectedOut string

0 commit comments

Comments
 (0)