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  }