github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/build/builder_mux.go (about) 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 "context" 21 "fmt" 22 "io" 23 "reflect" 24 25 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" 26 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph" 27 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/hooks" 28 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 29 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform" 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 31 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/tag" 32 ) 33 34 // BuilderMux encapsulates multiple build configs. 35 type BuilderMux struct { 36 builders []PipelineBuilder 37 byImageName map[string]PipelineBuilder 38 store ArtifactStore 39 concurrency int 40 } 41 42 // Config represents an interface for getting all config pipelines. 43 type Config interface { 44 GetPipelines() []latest.Pipeline 45 DefaultRepo() *string 46 MultiLevelRepo() *bool 47 GlobalConfig() string 48 BuildConcurrency() int 49 } 50 51 // NewBuilderMux returns an implementation of `build.BuilderMux`. 52 func NewBuilderMux(cfg Config, store ArtifactStore, builder func(p latest.Pipeline) (PipelineBuilder, error)) (*BuilderMux, error) { 53 pipelines := cfg.GetPipelines() 54 m := make(map[string]PipelineBuilder) 55 var pbs []PipelineBuilder 56 for _, p := range pipelines { 57 b, err := builder(p) 58 if err != nil { 59 return nil, fmt.Errorf("creating builder: %w", err) 60 } 61 pbs = append(pbs, b) 62 for _, a := range p.Build.Artifacts { 63 m[a.ImageName] = b 64 } 65 } 66 concurrency := getConcurrency(pbs, cfg.BuildConcurrency()) 67 return &BuilderMux{builders: pbs, byImageName: m, store: store, concurrency: concurrency}, nil 68 } 69 70 // Build executes the specific image builder for each artifact in the given artifact slice. 71 func (b *BuilderMux) Build(ctx context.Context, out io.Writer, tags tag.ImageTags, resolver platform.Resolver, artifacts []*latest.Artifact) ([]graph.Artifact, error) { 72 m := make(map[PipelineBuilder]bool) 73 for _, a := range artifacts { 74 m[b.byImageName[a.ImageName]] = true 75 } 76 77 for builder := range m { 78 if err := builder.PreBuild(ctx, out); err != nil { 79 return nil, err 80 } 81 } 82 83 builderF := func(ctx context.Context, out io.Writer, artifact *latest.Artifact, tag string, platforms platform.Matcher) (string, error) { 84 p := b.byImageName[artifact.ImageName] 85 pl, err := filterBuildEnvSupportedPlatforms(p.SupportedPlatforms(), platforms) 86 if err != nil { 87 return "", err 88 } 89 platforms = pl 90 91 artifactBuilder := p.Build(ctx, out, artifact) 92 hooksOpts, err := hooks.NewBuildEnvOpts(artifact, tag, p.PushImages()) 93 if err != nil { 94 return "", err 95 } 96 r := hooks.BuildRunner(artifact.LifecycleHooks, hooksOpts) 97 var built string 98 if err = r.RunPreHooks(ctx, out); err != nil { 99 return "", err 100 } 101 if built, err = artifactBuilder(ctx, out, artifact, tag, platforms); err != nil { 102 return "", err 103 } 104 if err = r.RunPostHooks(ctx, out); err != nil { 105 return "", err 106 } 107 return built, nil 108 } 109 ar, err := InOrder(ctx, out, tags, resolver, artifacts, builderF, b.concurrency, b.store) 110 if err != nil { 111 return nil, err 112 } 113 114 for builder := range m { 115 if errB := builder.PostBuild(ctx, out); errB != nil { 116 return nil, errB 117 } 118 } 119 120 return ar, nil 121 } 122 123 // Prune removes built images. 124 func (b *BuilderMux) Prune(ctx context.Context, writer io.Writer) error { 125 for _, builder := range b.builders { 126 if err := builder.Prune(ctx, writer); err != nil { 127 return err 128 } 129 } 130 return nil 131 } 132 133 // filterBuildEnvSupportedPlatforms filters the target platforms to those supported by the selected build environment (local/googleCloudBuild/cluster). 134 func filterBuildEnvSupportedPlatforms(supported platform.Matcher, target platform.Matcher) (platform.Matcher, error) { 135 if target.IsEmpty() { 136 return target, nil 137 } 138 pl := target.Intersect(supported) 139 if pl.IsEmpty() { 140 return platform.Matcher{}, fmt.Errorf("target build platforms %q not supported by current build environment. Supported platforms: %q", target, supported) 141 } 142 return pl, nil 143 } 144 145 func getConcurrency(pbs []PipelineBuilder, cliConcurrency int) int { 146 ctx := context.TODO() 147 if cliConcurrency >= 0 { 148 log.Entry(ctx).Infof("build concurrency set to cli concurrency %d", cliConcurrency) 149 return cliConcurrency 150 } 151 minConcurrency := -1 152 for i, b := range pbs { 153 concurrency := 1 154 if b.Concurrency() != nil { 155 concurrency = *b.Concurrency() 156 } 157 // set mux concurrency to be the minimum of all builders' concurrency. (concurrency = 0 means unlimited) 158 switch { 159 case minConcurrency < 0: 160 minConcurrency = concurrency 161 log.Entry(ctx).Infof("build concurrency first set to %d parsed from %s[%d]", minConcurrency, reflect.TypeOf(b).String(), i) 162 case concurrency > 0 && (minConcurrency == 0 || concurrency < minConcurrency): 163 minConcurrency = concurrency 164 log.Entry(ctx).Infof("build concurrency updated to %d parsed from %s[%d]", minConcurrency, reflect.TypeOf(b).String(), i) 165 default: 166 log.Entry(ctx).Infof("build concurrency value %d parsed from %s[%d] is ignored since it's not less than previously set value %d", concurrency, reflect.TypeOf(b).String(), i, minConcurrency) 167 } 168 } 169 if minConcurrency < 0 { 170 log.Entry(ctx).Infof("build concurrency set to default value of %d", minConcurrency) 171 // set default concurrency to 1 for local builder. For GCB and Cluster build the default value is 0 172 return constants.DefaultLocalConcurrency 173 } 174 log.Entry(ctx).Infof("final build concurrency value is %d", minConcurrency) 175 return minConcurrency 176 }