github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/build/ko/builder.go (about) 1 /* 2 Copyright 2021 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 ko 18 19 import ( 20 "context" 21 "fmt" 22 "path/filepath" 23 "strings" 24 25 "github.com/google/ko/pkg/build" 26 "github.com/google/ko/pkg/commands" 27 "github.com/google/ko/pkg/commands/options" 28 29 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform" 31 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 32 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" 33 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/version" 34 ) 35 36 func (b *Builder) newKoBuilder(ctx context.Context, a *latest.Artifact, platforms platform.Matcher) (build.Interface, error) { 37 bo, err := buildOptions(a, b.runMode, platforms) 38 if err != nil { 39 return nil, fmt.Errorf("could not construct ko build options: %v", err) 40 } 41 return commands.NewBuilder(ctx, bo) 42 } 43 44 func buildOptions(a *latest.Artifact, runMode config.RunMode, platforms platform.Matcher) (*options.BuildOptions, error) { 45 buildconfig, err := buildConfig(a) 46 if err != nil { 47 return nil, fmt.Errorf("could not create ko build config: %v", err) 48 } 49 imageLabels, err := labels(a) 50 if err != nil { 51 return nil, fmt.Errorf("could not expand image labels: %v", err) 52 } 53 return &options.BuildOptions{ 54 BaseImage: a.KoArtifact.BaseImage, 55 BuildConfigs: buildconfig, 56 ConcurrentBuilds: 1, // we could plug in Skaffold's max builds here, but it'd be incorrect if users build more than one artifact 57 DisableOptimizations: runMode == config.RunModes.Debug, 58 Labels: imageLabels, 59 Platforms: platforms.Array(), 60 SBOM: "none", // TODO: Need design for SBOM generation to consider other builders 61 Trimpath: runMode != config.RunModes.Debug, 62 UserAgent: version.UserAgentWithClient(), 63 WorkingDirectory: filepath.Join(a.Workspace, a.KoArtifact.Dir), 64 }, nil 65 } 66 67 // buildConfig creates the ko build config map based on the artifact config. 68 // A map entry is only required if the artifact config specifies fields that need to be part of ko build configs. 69 // If none of these are specified, we can provide an empty `BuildConfigs` map. 70 // In this case, ko falls back to build configs provided in `.ko.yaml`, or to the default zero config. 71 func buildConfig(a *latest.Artifact) (map[string]build.Config, error) { 72 buildconfigs := map[string]build.Config{} 73 if !koArtifactSpecifiesBuildConfig(*a.KoArtifact) { 74 return buildconfigs, nil 75 } 76 koImportpath, err := getImportPath(a) 77 if err != nil { 78 return nil, fmt.Errorf("could not determine import path of image %s: %v", a.ImageName, err) 79 } 80 env, err := expand(a.KoArtifact.Env) 81 if err != nil { 82 return nil, fmt.Errorf("could not expand env: %v", err) 83 } 84 flags, err := expand(a.KoArtifact.Flags) 85 if err != nil { 86 return nil, fmt.Errorf("could not expand build flags: %v", err) 87 } 88 ldflags, err := expand(a.KoArtifact.Ldflags) 89 if err != nil { 90 return nil, fmt.Errorf("could not expand linker flags: %v", err) 91 } 92 importpath := strings.TrimPrefix(koImportpath, build.StrictScheme) 93 buildconfigs[importpath] = build.Config{ 94 ID: a.ImageName, 95 Dir: ".", 96 Env: env, 97 Flags: flags, 98 Ldflags: ldflags, 99 Main: a.KoArtifact.Main, 100 } 101 return buildconfigs, nil 102 } 103 104 func koArtifactSpecifiesBuildConfig(k latest.KoArtifact) bool { 105 if k.Dir != "" && k.Dir != "." { 106 return true 107 } 108 if k.Main != "" && k.Main != "." { 109 return true 110 } 111 if len(k.Env) != 0 { 112 return true 113 } 114 if len(k.Flags) != 0 { 115 return true 116 } 117 if len(k.Ldflags) != 0 { 118 return true 119 } 120 return false 121 } 122 123 func labels(a *latest.Artifact) ([]string, error) { 124 rawLabels := map[string]*string{} 125 for k, v := range a.KoArtifact.Labels { 126 rawLabels[k] = util.StringPtr(v) 127 } 128 expandedLabels, err := util.EvaluateEnvTemplateMapWithEnv(rawLabels, nil) 129 if err != nil { 130 return nil, fmt.Errorf("unable to expand image labels: %w", err) 131 } 132 var labels []string 133 for k, v := range expandedLabels { 134 labels = append(labels, fmt.Sprintf("%s=%s", k, *v)) 135 } 136 return labels, nil 137 } 138 139 func expand(dryValues []string) ([]string, error) { 140 var expandedValues []string 141 for _, rawValue := range dryValues { 142 // support ko-style envvar templating syntax, see https://github.com/GoogleContainerTools/skaffold/issues/6916 143 rawValue = strings.ReplaceAll(rawValue, "{{.Env.", "{{.") 144 expandedValue, err := util.ExpandEnvTemplate(rawValue, nil) 145 if err != nil { 146 return nil, fmt.Errorf("could not expand %s", rawValue) 147 } 148 expandedValues = append(expandedValues, expandedValue) 149 } 150 return expandedValues, nil 151 }