diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..b9bbfc0280 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Ignore GoLand (IntelliJ) files. +.idea/ + diff --git a/cmd/ko/commands.go b/cmd/ko/commands.go index c04a1dc899..7a0d1c4dec 100644 --- a/cmd/ko/commands.go +++ b/cmd/ko/commands.go @@ -70,6 +70,7 @@ func addKubeCommands(topLevel *cobra.Command) { no := &NameOptions{} fo := &FilenameOptions{} ta := &TagsOptions{} + do := &DebugOptions{} apply := &cobra.Command{ Use: "apply -f FILENAME", Short: "Apply the input files with image references resolved to built/pushed image digests.", @@ -144,7 +145,7 @@ func addKubeCommands(topLevel *cobra.Command) { stdin.Write([]byte("---\n")) } // Once primed kick things off. - resolveFilesToWriter(fo, no, lo, ta, stdin) + resolveFilesToWriter(fo, no, lo, ta, do, stdin) }() // Run it. @@ -157,6 +158,7 @@ func addKubeCommands(topLevel *cobra.Command) { addNamingArgs(apply, no) addFileArg(apply, fo) addTagsArg(apply, ta) + addDebugArg(apply, do) // Collect the ko-specific apply flags before registering the kubectl global // flags so that we can ignore them when passing kubectl global flags through @@ -197,13 +199,14 @@ func addKubeCommands(topLevel *cobra.Command) { ko resolve --local -f config/`, Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - resolveFilesToWriter(fo, no, lo, ta, os.Stdout) + resolveFilesToWriter(fo, no, lo, ta, do, os.Stdout) }, } addLocalArg(resolve, lo) addNamingArgs(resolve, no) addFileArg(resolve, fo) addTagsArg(resolve, ta) + addDebugArg(resolve, do) topLevel.AddCommand(resolve) publish := &cobra.Command{ @@ -237,12 +240,13 @@ func addKubeCommands(topLevel *cobra.Command) { ko publish --local github.com/foo/bar/cmd/baz github.com/foo/bar/cmd/blah`, Args: cobra.MinimumNArgs(1), Run: func(_ *cobra.Command, args []string) { - publishImages(args, no, lo, ta) + publishImages(args, no, lo, ta, do) }, } addLocalArg(publish, lo) addNamingArgs(publish, no) addTagsArg(publish, ta) + addDebugArg(publish, do) topLevel.AddCommand(publish) run := &cobra.Command{ @@ -259,7 +263,7 @@ func addKubeCommands(topLevel *cobra.Command) { # This supports relative import paths as well. ko run foo --image=./cmd/baz`, Run: func(cmd *cobra.Command, args []string) { - imgs := publishImages([]string{bo.Path}, no, lo, ta) + imgs := publishImages([]string{bo.Path}, no, lo, ta, do) // There's only one, but this is the simple way to access the // reference since the import path may have been qualified. @@ -293,6 +297,7 @@ func addKubeCommands(topLevel *cobra.Command) { addNamingArgs(run, no) addImageArg(run, bo) addTagsArg(run, ta) + addDebugArg(run, do) topLevel.AddCommand(run) } diff --git a/cmd/ko/debug.go b/cmd/ko/debug.go new file mode 100644 index 0000000000..cfd481582d --- /dev/null +++ b/cmd/ko/debug.go @@ -0,0 +1,29 @@ +// 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. + +package main + +import ( + "github.com/spf13/cobra" +) + +// DebugOptions holds options to improve debugging containers. +type DebugOptions struct { + DisableOptimizations bool +} + +func addDebugArg(cmd *cobra.Command, do *DebugOptions) { + cmd.Flags().BoolVar(&do.DisableOptimizations, "disable-optimizations", do.DisableOptimizations, + "Disable optimizations when building Go code. Useful when you want to interactively debug the created container.") +} diff --git a/cmd/ko/publish.go b/cmd/ko/publish.go index 15158922d5..867bb70b63 100644 --- a/cmd/ko/publish.go +++ b/cmd/ko/publish.go @@ -38,8 +38,8 @@ func qualifyLocalImport(importpath, gopathsrc, pwd string) (string, error) { return filepath.Join(strings.TrimPrefix(pwd, gopathsrc+string(filepath.Separator)), importpath), nil } -func publishImages(importpaths []string, no *NameOptions, lo *LocalOptions, ta *TagsOptions) map[string]name.Reference { - opt, err := gobuildOptions() +func publishImages(importpaths []string, no *NameOptions, lo *LocalOptions, ta *TagsOptions, do *DebugOptions) map[string]name.Reference { + opt, err := gobuildOptions(do) if err != nil { log.Fatalf("error setting up builder options: %v", err) } diff --git a/cmd/ko/resolve.go b/cmd/ko/resolve.go index 702b7060b1..9933afdcfc 100644 --- a/cmd/ko/resolve.go +++ b/cmd/ko/resolve.go @@ -30,7 +30,7 @@ import ( "github.com/mattmoor/dep-notify/pkg/graph" ) -func gobuildOptions() ([]build.Option, error) { +func gobuildOptions(do *DebugOptions) ([]build.Option, error) { creationTime, err := getCreationTime() if err != nil { return nil, err @@ -41,11 +41,14 @@ func gobuildOptions() ([]build.Option, error) { if creationTime != nil { opts = append(opts, build.WithCreationTime(*creationTime)) } + if do.DisableOptimizations { + opts = append(opts, build.WithDisabledOptimizations()) + } return opts, nil } -func makeBuilder() (*build.Caching, error) { - opt, err := gobuildOptions() +func makeBuilder(do *DebugOptions) (*build.Caching, error) { + opt, err := gobuildOptions(do) if err != nil { log.Fatalf("error setting up builder options: %v", err) } @@ -104,9 +107,9 @@ func makePublisher(no *NameOptions, lo *LocalOptions, ta *TagsOptions) (publish. // resolvedFuture represents a "future" for the bytes of a resolved file. type resolvedFuture chan []byte -func resolveFilesToWriter(fo *FilenameOptions, no *NameOptions, lo *LocalOptions, ta *TagsOptions, out io.WriteCloser) { +func resolveFilesToWriter(fo *FilenameOptions, no *NameOptions, lo *LocalOptions, ta *TagsOptions, do *DebugOptions, out io.WriteCloser) { defer out.Close() - builder, err := makeBuilder() + builder, err := makeBuilder(do) if err != nil { log.Fatalf("error creating builder: %v", err) } diff --git a/pkg/build/gobuild.go b/pkg/build/gobuild.go index 7300f5e22c..cf9c43829e 100644 --- a/pkg/build/gobuild.go +++ b/pkg/build/gobuild.go @@ -38,12 +38,13 @@ const ( // GetBase takes an importpath and returns a base v1.Image. type GetBase func(string) (v1.Image, error) -type builder func(string) (string, error) +type builder func(string, bool) (string, error) type gobuild struct { getBase GetBase creationTime v1.Time build builder + disableOptimizations bool } // Option is a functional option for NewGo. @@ -53,6 +54,7 @@ type gobuildOpener struct { getBase GetBase creationTime v1.Time build builder + disableOptimizations bool } func (gbo *gobuildOpener) Open() (Interface, error) { @@ -63,6 +65,7 @@ func (gbo *gobuildOpener) Open() (Interface, error) { getBase: gbo.getBase, creationTime: gbo.creationTime, build: gbo.build, + disableOptimizations: gbo.disableOptimizations, }, nil } @@ -94,14 +97,22 @@ func (*gobuild) IsSupportedReference(s string) bool { return p.IsCommand() } -func build(ip string) (string, error) { +func build(ip string, disableOptimizations bool) (string, error) { tmpDir, err := ioutil.TempDir("", "ko") if err != nil { return "", err } file := filepath.Join(tmpDir, "out") - cmd := exec.Command("go", "build", "-o", file, ip) + args := make([]string, 0, 6) + args = append(args, "build") + if disableOptimizations { + // Disable optimizations (-N) and inlining (-l). + args = append(args, "-gcflags", "all=-N -l") + } + args = append(args, "-o", file) + args = append(args, ip) + cmd := exec.Command("go", args...) // Last one wins // TODO(mattmoor): GOARCH=amd64 @@ -273,7 +284,7 @@ func tarKoData(importpath string) (*bytes.Buffer, error) { // Build implements build.Interface func (gb *gobuild) Build(s string) (v1.Image, error) { // Do the build into a temporary file. - file, err := gb.build(s) + file, err := gb.build(s, gb.disableOptimizations) if err != nil { return nil, err } diff --git a/pkg/build/gobuild_test.go b/pkg/build/gobuild_test.go index e7e0e83ad2..11d7f3f370 100644 --- a/pkg/build/gobuild_test.go +++ b/pkg/build/gobuild_test.go @@ -62,7 +62,7 @@ func TestGoBuildIsSupportedRef(t *testing.T) { } // A helper method we use to substitute for the default "build" method. -func writeTempFile(s string) (string, error) { +func writeTempFile(s string, _ bool) (string, error) { tmpDir, err := ioutil.TempDir("", "ko") if err != nil { return "", err diff --git a/pkg/build/options.go b/pkg/build/options.go index df6eb40a46..78e001a4ce 100644 --- a/pkg/build/options.go +++ b/pkg/build/options.go @@ -36,6 +36,15 @@ func WithCreationTime(t v1.Time) Option { } } +// WithDisabledOptimizations is a functional option for disabling optimizations +// when compiling. +func WithDisabledOptimizations() Option { + return func(gbo *gobuildOpener) error { + gbo.disableOptimizations = true + return nil + } +} + // withBuilder is a functional option for overriding the way go binaries // are built. This is exposed for testing. func withBuilder(b builder) Option {