2021-06-11 01:29:30 +10:00
|
|
|
/*
|
|
|
|
Copyright 2019 Google LLC All Rights Reserved.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
2019-04-18 10:18:29 -07:00
|
|
|
|
2019-04-26 15:25:40 -07:00
|
|
|
package options
|
2019-04-18 10:18:29 -07:00
|
|
|
|
|
|
|
import (
|
2021-11-05 13:26:09 -04:00
|
|
|
"errors"
|
2021-10-21 16:14:06 +11:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/google/go-containerregistry/pkg/name"
|
2019-04-18 10:18:29 -07:00
|
|
|
"github.com/spf13/cobra"
|
2021-10-21 16:14:06 +11:00
|
|
|
"github.com/spf13/viper"
|
|
|
|
"golang.org/x/tools/go/packages"
|
|
|
|
|
|
|
|
"github.com/google/ko/pkg/build"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// configDefaultBaseImage is the default base image if not specified in .ko.yaml.
|
2022-06-30 15:12:39 -04:00
|
|
|
configDefaultBaseImage = "ghcr.io/distroless/static:latest"
|
2019-04-18 10:18:29 -07:00
|
|
|
)
|
|
|
|
|
2019-09-11 10:07:02 -07:00
|
|
|
// BuildOptions represents options for the ko builder.
|
|
|
|
type BuildOptions struct {
|
2021-05-26 04:44:52 +10:00
|
|
|
// BaseImage enables setting the default base image programmatically.
|
|
|
|
// If non-empty, this takes precedence over the value in `.ko.yaml`.
|
2021-06-11 01:29:30 +10:00
|
|
|
BaseImage string
|
|
|
|
|
2021-10-21 16:14:06 +11:00
|
|
|
// BaseImageOverrides stores base image overrides for import paths.
|
|
|
|
BaseImageOverrides map[string]string
|
|
|
|
|
2021-06-11 01:29:30 +10:00
|
|
|
// WorkingDirectory allows for setting the working directory for invocations of the `go` tool.
|
|
|
|
// Empty string means the current working directory.
|
|
|
|
WorkingDirectory string
|
|
|
|
|
2019-09-11 10:07:02 -07:00
|
|
|
ConcurrentBuilds int
|
2019-04-18 10:18:29 -07:00
|
|
|
DisableOptimizations bool
|
2021-11-22 10:57:13 -08:00
|
|
|
SBOM string
|
2022-01-04 11:19:52 -08:00
|
|
|
Platforms []string
|
2021-03-03 13:03:31 -05:00
|
|
|
Labels []string
|
2021-05-26 04:44:52 +10:00
|
|
|
// UserAgent enables overriding the default value of the `User-Agent` HTTP
|
|
|
|
// request header used when retrieving the base image.
|
|
|
|
UserAgent string
|
2021-06-01 09:47:30 -07:00
|
|
|
|
|
|
|
InsecureRegistry bool
|
2021-08-27 02:33:01 +10:00
|
|
|
|
2021-11-17 14:03:48 +11:00
|
|
|
// Trimpath controls whether ko adds the `-trimpath` flag to `go build` by default.
|
|
|
|
// The `-trimpath` flags aids in achieving reproducible builds, but it removes path information that is useful for interactive debugging.
|
|
|
|
// Set this field to `false` and `DisableOptimizations` to `true` if you want to interactively debug the binary in the resulting image.
|
|
|
|
// `AddBuildOptions()` defaults this field to `true`.
|
|
|
|
Trimpath bool
|
|
|
|
|
2021-10-21 16:14:06 +11:00
|
|
|
// BuildConfigs stores the per-image build config from `.ko.yaml`.
|
2021-08-27 02:33:01 +10:00
|
|
|
BuildConfigs map[string]build.Config
|
2019-04-18 10:18:29 -07:00
|
|
|
}
|
|
|
|
|
2019-09-11 10:07:02 -07:00
|
|
|
func AddBuildOptions(cmd *cobra.Command, bo *BuildOptions) {
|
2021-08-11 16:54:13 -04:00
|
|
|
cmd.Flags().IntVarP(&bo.ConcurrentBuilds, "jobs", "j", 0,
|
|
|
|
"The maximum number of concurrent builds (default GOMAXPROCS)")
|
2019-09-11 10:07:02 -07:00
|
|
|
cmd.Flags().BoolVar(&bo.DisableOptimizations, "disable-optimizations", bo.DisableOptimizations,
|
2019-04-18 10:18:29 -07:00
|
|
|
"Disable optimizations when building Go code. Useful when you want to interactively debug the created container.")
|
2021-11-22 14:19:43 -08:00
|
|
|
cmd.Flags().StringVar(&bo.SBOM, "sbom", "spdx",
|
2022-05-05 20:38:26 +09:00
|
|
|
"The SBOM media type to use (none will disable SBOM synthesis and upload, also supports: spdx, cyclonedx, go.version-m).")
|
2022-01-04 11:19:52 -08:00
|
|
|
cmd.Flags().StringSliceVar(&bo.Platforms, "platform", []string{},
|
2020-12-21 16:53:00 -08:00
|
|
|
"Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*")
|
2021-03-03 13:03:31 -05:00
|
|
|
cmd.Flags().StringSliceVar(&bo.Labels, "image-label", []string{},
|
|
|
|
"Which labels (key=value) to add to the image.")
|
2021-11-17 14:03:48 +11:00
|
|
|
bo.Trimpath = true
|
2019-04-18 10:18:29 -07:00
|
|
|
}
|
2021-10-21 16:14:06 +11:00
|
|
|
|
|
|
|
// LoadConfig reads build configuration from defaults, environment variables, and the `.ko.yaml` config file.
|
|
|
|
func (bo *BuildOptions) LoadConfig() error {
|
|
|
|
v := viper.New()
|
|
|
|
if bo.WorkingDirectory == "" {
|
|
|
|
bo.WorkingDirectory = "."
|
|
|
|
}
|
|
|
|
// If omitted, use this base image.
|
|
|
|
v.SetDefault("defaultBaseImage", configDefaultBaseImage)
|
2022-02-08 11:28:53 -08:00
|
|
|
const configName = ".ko"
|
|
|
|
|
|
|
|
v.SetConfigName(configName) // .yaml is implicit
|
2021-10-21 16:14:06 +11:00
|
|
|
v.SetEnvPrefix("KO")
|
|
|
|
v.AutomaticEnv()
|
|
|
|
|
|
|
|
if override := os.Getenv("KO_CONFIG_PATH"); override != "" {
|
2022-06-16 15:21:29 -04:00
|
|
|
file, err := os.Stat(override)
|
2022-02-08 11:28:53 -08:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error looking for config file: %w", err)
|
|
|
|
}
|
2022-06-16 15:21:29 -04:00
|
|
|
var path string
|
|
|
|
if file.IsDir() {
|
|
|
|
path = filepath.Join(override, configName+".yaml")
|
|
|
|
file, err = os.Stat(path)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error looking for config file: %w", err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
path = override
|
|
|
|
}
|
|
|
|
|
2022-02-08 11:28:53 -08:00
|
|
|
if !file.Mode().IsRegular() {
|
|
|
|
return fmt.Errorf("config file %s is not a regular file", path)
|
|
|
|
}
|
2021-10-21 16:14:06 +11:00
|
|
|
v.AddConfigPath(override)
|
|
|
|
}
|
|
|
|
v.AddConfigPath(bo.WorkingDirectory)
|
|
|
|
|
|
|
|
if err := v.ReadInConfig(); err != nil {
|
2021-11-05 13:26:09 -04:00
|
|
|
if !errors.As(err, &viper.ConfigFileNotFoundError{}) {
|
|
|
|
return fmt.Errorf("error reading config file: %w", err)
|
2021-10-21 16:14:06 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if bo.BaseImage == "" {
|
|
|
|
ref := v.GetString("defaultBaseImage")
|
|
|
|
if _, err := name.ParseReference(ref); err != nil {
|
2021-11-05 13:26:09 -04:00
|
|
|
return fmt.Errorf("'defaultBaseImage': error parsing %q as image reference: %w", ref, err)
|
2021-10-21 16:14:06 +11:00
|
|
|
}
|
|
|
|
bo.BaseImage = ref
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(bo.BaseImageOverrides) == 0 {
|
|
|
|
baseImageOverrides := map[string]string{}
|
|
|
|
overrides := v.GetStringMapString("baseImageOverrides")
|
|
|
|
for key, value := range overrides {
|
|
|
|
if _, err := name.ParseReference(value); err != nil {
|
2021-11-05 13:26:09 -04:00
|
|
|
return fmt.Errorf("'baseImageOverrides': error parsing %q as image reference: %w", value, err)
|
2021-10-21 16:14:06 +11:00
|
|
|
}
|
|
|
|
baseImageOverrides[key] = value
|
|
|
|
}
|
|
|
|
bo.BaseImageOverrides = baseImageOverrides
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(bo.BuildConfigs) == 0 {
|
|
|
|
var builds []build.Config
|
|
|
|
if err := v.UnmarshalKey("builds", &builds); err != nil {
|
|
|
|
return fmt.Errorf("configuration section 'builds' cannot be parsed")
|
|
|
|
}
|
|
|
|
buildConfigs, err := createBuildConfigMap(bo.WorkingDirectory, builds)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not create build config map: %w", err)
|
|
|
|
}
|
|
|
|
bo.BuildConfigs = buildConfigs
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func createBuildConfigMap(workingDirectory string, configs []build.Config) (map[string]build.Config, error) {
|
|
|
|
buildConfigsByImportPath := make(map[string]build.Config)
|
|
|
|
for i, config := range configs {
|
2021-10-27 17:09:41 +02:00
|
|
|
// In case no ID is specified, use the index of the build config in
|
|
|
|
// the ko YAML file as a reference (debug help).
|
|
|
|
if config.ID == "" {
|
|
|
|
config.ID = fmt.Sprintf("#%d", i)
|
|
|
|
}
|
|
|
|
|
2021-10-21 16:14:06 +11:00
|
|
|
// Make sure to behave like GoReleaser by defaulting to the current
|
|
|
|
// directory in case the build or main field is not set, check
|
|
|
|
// https://goreleaser.com/customization/build/ for details
|
|
|
|
if config.Dir == "" {
|
|
|
|
config.Dir = "."
|
|
|
|
}
|
|
|
|
if config.Main == "" {
|
|
|
|
config.Main = "."
|
|
|
|
}
|
|
|
|
|
|
|
|
// baseDir is the directory where `go list` will be run to look for package information
|
|
|
|
baseDir := filepath.Join(workingDirectory, config.Dir)
|
|
|
|
|
|
|
|
// To behave like GoReleaser, check whether the configured `main` config value points to a
|
|
|
|
// source file, and if so, just use the directory it is in
|
|
|
|
path := config.Main
|
|
|
|
if fi, err := os.Stat(filepath.Join(baseDir, config.Main)); err == nil && fi.Mode().IsRegular() {
|
|
|
|
path = filepath.Dir(config.Main)
|
|
|
|
}
|
|
|
|
|
2021-10-27 17:09:41 +02:00
|
|
|
// Verify that the path actually leads to a local file (https://github.com/google/ko/issues/483)
|
|
|
|
if _, err := os.Stat(filepath.Join(baseDir, path)); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-10-21 16:14:06 +11:00
|
|
|
// By default, paths configured in the builds section are considered
|
|
|
|
// local import paths, therefore add a "./" equivalent as a prefix to
|
|
|
|
// the constructured import path
|
|
|
|
localImportPath := fmt.Sprint(".", string(filepath.Separator), path)
|
2021-12-16 11:21:02 -08:00
|
|
|
dir := filepath.Clean(baseDir)
|
|
|
|
if dir == "." {
|
2021-12-15 14:38:01 -05:00
|
|
|
dir = ""
|
|
|
|
}
|
|
|
|
pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName, Dir: dir}, localImportPath)
|
2021-10-21 16:14:06 +11:00
|
|
|
if err != nil {
|
2021-11-05 13:26:09 -04:00
|
|
|
return nil, fmt.Errorf("'builds': entry #%d does not contain a valid local import path (%s) for directory (%s): %w", i, localImportPath, baseDir, err)
|
2021-10-21 16:14:06 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(pkgs) != 1 {
|
|
|
|
return nil, fmt.Errorf("'builds': entry #%d results in %d local packages, only 1 is expected", i, len(pkgs))
|
|
|
|
}
|
|
|
|
importPath := pkgs[0].PkgPath
|
|
|
|
buildConfigsByImportPath[importPath] = config
|
|
|
|
}
|
|
|
|
|
|
|
|
return buildConfigsByImportPath, nil
|
|
|
|
}
|