Skip to content

Commit

Permalink
Use signal.NotifyContext and cmd.Context
Browse files Browse the repository at this point in the history
  • Loading branch information
imjasonh committed Nov 2, 2021
1 parent b1c35d2 commit b798a21
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 33 deletions.
6 changes: 5 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ limitations under the License.
package main

import (
"context"
"log"
"os"
"os/signal"

"github.com/google/go-containerregistry/pkg/logs"
"github.com/google/ko/pkg/commands"
Expand All @@ -28,7 +30,9 @@ func main() {
logs.Warn.SetOutput(os.Stderr)
logs.Progress.SetOutput(os.Stderr)

if err := commands.Root.Execute(); err != nil {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
if err := commands.Root.ExecuteContext(ctx); err != nil {
log.Fatal("error during command execution:", err)
}
}
4 changes: 1 addition & 3 deletions pkg/commands/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,7 @@ func addApply(topLevel *cobra.Command) {
if !isKubectlAvailable() {
return errors.New("error: kubectl is not available. kubectl must be installed to use ko apply")
}

// Cancel on signals.
ctx := createCancellableContext()
ctx := cmd.Context()

bo.InsecureRegistry = po.InsecureRegistry
builder, err := makeBuilder(ctx, bo)
Expand Down
5 changes: 3 additions & 2 deletions pkg/commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ func addBuild(topLevel *cobra.Command) {
# This always preserves import paths.
ko build --local github.com/foo/bar/cmd/baz github.com/foo/bar/cmd/blah`,
Args: cobra.MinimumNArgs(1),
RunE: func(_ *cobra.Command, args []string) error {
ctx := createCancellableContext()
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()

bo.InsecureRegistry = po.InsecureRegistry
builder, err := makeBuilder(ctx, bo)
if err != nil {
Expand Down
101 changes: 90 additions & 11 deletions pkg/commands/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ import (
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"

"github.com/google/go-containerregistry/pkg/authn"
Expand All @@ -33,6 +32,8 @@ import (
"github.com/google/go-containerregistry/pkg/v1/daemon"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/spf13/viper"
"golang.org/x/tools/go/packages"

"github.com/google/ko/pkg/build"
"github.com/google/ko/pkg/commands/options"
Expand Down Expand Up @@ -143,15 +144,93 @@ func getKoDataCreationTime() (*v1.Time, error) {
return getTimeFromEnv("KO_DATA_DATE_EPOCH")
}

func createCancellableContext() context.Context {
signals := make(chan os.Signal)
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background())
func createBuildConfigMap(workingDirectory string, configs []build.Config) (map[string]build.Config, error) {
buildConfigsByImportPath := make(map[string]build.Config)
for i, config := range configs {
// 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)
}

// 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)

pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName, Dir: baseDir}, localImportPath)
if err != nil {
return nil, fmt.Errorf("'builds': entry #%d does not contain a valid local import path (%s) for directory (%s): %v", i, localImportPath, baseDir, err)
}

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
}

// loadConfig reads build configuration from defaults, environment variables, and the `.ko.yaml` config file.
func loadConfig(workingDirectory string) error {
v := viper.New()
if workingDirectory == "" {
workingDirectory = "."
}
// If omitted, use this base image.
v.SetDefault("defaultBaseImage", configDefaultBaseImage)
v.SetConfigName(".ko") // .yaml is implicit
v.SetEnvPrefix("KO")
v.AutomaticEnv()

if override := os.Getenv("KO_CONFIG_PATH"); override != "" {
v.AddConfigPath(override)
}

go func() {
<-signals
cancel()
}()
v.AddConfigPath(workingDirectory)

if err := v.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return fmt.Errorf("error reading config file: %v", err)
}
}

return ctx
ref := v.GetString("defaultBaseImage")
if _, err := name.ParseReference(ref); err != nil {
return fmt.Errorf("'defaultBaseImage': error parsing %q as image reference: %v", ref, err)
}
defaultBaseImage = ref

baseImageOverrides = make(map[string]string)
overrides := v.GetStringMapString("baseImageOverrides")
for key, value := range overrides {
if _, err := name.ParseReference(value); err != nil {
return fmt.Errorf("'baseImageOverrides': error parsing %q as image reference: %v", value, err)
}
baseImageOverrides[key] = value
}

var builds []build.Config
if err := v.UnmarshalKey("builds", &builds); err != nil {
return fmt.Errorf("configuration section 'builds' cannot be parsed")
}
var err error
buildConfigs, err = createBuildConfigMap(workingDirectory, builds)
return err
}
4 changes: 1 addition & 3 deletions pkg/commands/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,7 @@ func addCreate(topLevel *cobra.Command) {
if !isKubectlAvailable() {
return errors.New("error: kubectl is not available. kubectl must be installed to use ko create")
}

// Cancel on signals.
ctx := createCancellableContext()
ctx := cmd.Context()

bo.InsecureRegistry = po.InsecureRegistry
builder, err := makeBuilder(ctx, bo)
Expand Down
18 changes: 8 additions & 10 deletions pkg/commands/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,25 @@ type runCmd func(*cobra.Command, []string) error
// passthru returns a runCmd that simply passes our CLI arguments
// through to a binary named command.
func passthru(command string) runCmd {
return func(_ *cobra.Command, _ []string) error {
return func(cmd *cobra.Command, _ []string) error {
if !isKubectlAvailable() {
return errors.New("error: kubectl is not available. kubectl must be installed to use ko delete")
}

// Cancel on signals.
ctx := createCancellableContext()
ctx := cmd.Context()

// Start building a command line invocation by passing
// through our arguments to command's CLI.
cmd := exec.CommandContext(ctx, command, os.Args[1:]...)
ecmd := exec.CommandContext(ctx, command, os.Args[1:]...)

// Pass through our environment
cmd.Env = os.Environ()
ecmd.Env = os.Environ()
// Pass through our stdfoo
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
ecmd.Stderr = os.Stderr
ecmd.Stdout = os.Stdout
ecmd.Stdin = os.Stdin

// Run it.
return cmd.Run()
return ecmd.Run()
}
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ If the image was not built using ko, or if it was built without embedding depend
ko deps docker.io/my-user/my-image:v3`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := createCancellableContext()
ctx := cmd.Context()

ref, err := name.ParseReference(args[0])
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion pkg/commands/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ func addResolve(topLevel *cobra.Command) {
ko resolve --local -f config/`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := createCancellableContext()
ctx := cmd.Context()

bo.InsecureRegistry = po.InsecureRegistry
builder, err := makeBuilder(ctx, bo)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func addRun(topLevel *cobra.Command) {
# You can also supply args and flags to the command.
ko run ./cmd/baz -- -v arg1 arg2 --yes`,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := createCancellableContext()
ctx := cmd.Context()

// Args after -- are for kubectl, so only consider importPaths before it.
importPaths := args
Expand Down

0 comments on commit b798a21

Please sign in to comment.