mirror of
https://github.com/ko-build/ko.git
synced 2025-07-12 23:50:31 +02:00
Resolving #21 issue. Fixing conflicts after commands refactoring. Improving version command
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Ignore GoLand (IntelliJ) files.
|
||||||
|
.idea/
|
||||||
|
|
@ -1,306 +0,0 @@
|
|||||||
// Copyright 2018 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 (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
|
||||||
)
|
|
||||||
|
|
||||||
// runCmd is suitable for use with cobra.Command's Run field.
|
|
||||||
type runCmd func(*cobra.Command, []string)
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
// Start building a command line invocation by passing
|
|
||||||
// through our arguments to command's CLI.
|
|
||||||
cmd := exec.Command(command, os.Args[1:]...)
|
|
||||||
|
|
||||||
// Pass through our environment
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
// Pass through our stdfoo
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Stdin = os.Stdin
|
|
||||||
|
|
||||||
// Run it.
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
log.Fatalf("error executing %q command with args: %v; %v", command, os.Args[1:], err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addKubeCommands augments our CLI surface with a passthru delete command, and an apply
|
|
||||||
// command that realizes the promise of ko, as outlined here:
|
|
||||||
// https://github.com/google/go-containerregistry/issues/80
|
|
||||||
func addKubeCommands(topLevel *cobra.Command) {
|
|
||||||
topLevel.AddCommand(&cobra.Command{
|
|
||||||
Use: "delete",
|
|
||||||
Short: `See "kubectl help delete" for detailed usage.`,
|
|
||||||
Run: passthru("kubectl"),
|
|
||||||
// We ignore unknown flags to avoid importing everything Go exposes
|
|
||||||
// from our commands.
|
|
||||||
FParseErrWhitelist: cobra.FParseErrWhitelist{
|
|
||||||
UnknownFlags: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
topLevel.AddCommand(&cobra.Command{
|
|
||||||
Use: "version",
|
|
||||||
Short: `Print ko version.`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
version()
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
koApplyFlags := []string{}
|
|
||||||
lo := &LocalOptions{}
|
|
||||||
bo := &BinaryOptions{}
|
|
||||||
no := &NameOptions{}
|
|
||||||
fo := &FilenameOptions{}
|
|
||||||
ta := &TagsOptions{}
|
|
||||||
apply := &cobra.Command{
|
|
||||||
Use: "apply -f FILENAME",
|
|
||||||
Short: "Apply the input files with image references resolved to built/pushed image digests.",
|
|
||||||
Long: `This sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and then feeds the resulting yaml into "kubectl apply".`,
|
|
||||||
Example: `
|
|
||||||
# Build and publish import path references to a Docker
|
|
||||||
# Registry as:
|
|
||||||
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
|
||||||
# Then, feed the resulting yaml into "kubectl apply".
|
|
||||||
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
|
||||||
# --local was passed.
|
|
||||||
ko apply -f config/
|
|
||||||
|
|
||||||
# Build and publish import path references to a Docker
|
|
||||||
# Registry preserving import path names as:
|
|
||||||
# ${KO_DOCKER_REPO}/<import path>
|
|
||||||
# Then, feed the resulting yaml into "kubectl apply".
|
|
||||||
ko apply --preserve-import-paths -f config/
|
|
||||||
|
|
||||||
# Build and publish import path references to a Docker
|
|
||||||
# daemon as:
|
|
||||||
# ko.local/<import path>
|
|
||||||
# Then, feed the resulting yaml into "kubectl apply".
|
|
||||||
ko apply --local -f config/
|
|
||||||
|
|
||||||
# Apply from stdin:
|
|
||||||
cat config.yaml | ko apply -f -`,
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
// Create a set of ko-specific flags to ignore when passing through
|
|
||||||
// kubectl global flags.
|
|
||||||
ignoreSet := make(map[string]struct{})
|
|
||||||
for _, s := range koApplyFlags {
|
|
||||||
ignoreSet[s] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter out ko flags from what we will pass through to kubectl.
|
|
||||||
kubectlFlags := []string{}
|
|
||||||
cmd.Flags().Visit(func(flag *pflag.Flag) {
|
|
||||||
if _, ok := ignoreSet[flag.Name]; !ok {
|
|
||||||
kubectlFlags = append(kubectlFlags, "--"+flag.Name, flag.Value.String())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Issue a "kubectl apply" command reading from stdin,
|
|
||||||
// to which we will pipe the resolved files.
|
|
||||||
argv := []string{"apply", "-f", "-"}
|
|
||||||
argv = append(argv, kubectlFlags...)
|
|
||||||
kubectlCmd := exec.Command("kubectl", argv...)
|
|
||||||
|
|
||||||
// Pass through our environment
|
|
||||||
kubectlCmd.Env = os.Environ()
|
|
||||||
// Pass through our std{out,err} and make our resolved buffer stdin.
|
|
||||||
kubectlCmd.Stderr = os.Stderr
|
|
||||||
kubectlCmd.Stdout = os.Stdout
|
|
||||||
|
|
||||||
// Wire up kubectl stdin to resolveFilesToWriter.
|
|
||||||
stdin, err := kubectlCmd.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error piping to 'kubectl apply': %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
// kubectl buffers data before starting to apply it, which
|
|
||||||
// can lead to resources being created more slowly than desired.
|
|
||||||
// In the case of --watch, it can lead to resources not being
|
|
||||||
// applied at all until enough iteration has occurred. To work
|
|
||||||
// around this, we prime the stream with a bunch of empty objects
|
|
||||||
// which kubectl will discard.
|
|
||||||
// See https://github.com/google/go-containerregistry/pull/348
|
|
||||||
for i := 0; i < 1000; i++ {
|
|
||||||
stdin.Write([]byte("---\n"))
|
|
||||||
}
|
|
||||||
// Once primed kick things off.
|
|
||||||
resolveFilesToWriter(fo, no, lo, ta, stdin)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Run it.
|
|
||||||
if err := kubectlCmd.Run(); err != nil {
|
|
||||||
log.Fatalf("error executing 'kubectl apply': %v", err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
addLocalArg(apply, lo)
|
|
||||||
addNamingArgs(apply, no)
|
|
||||||
addFileArg(apply, fo)
|
|
||||||
addTagsArg(apply, ta)
|
|
||||||
|
|
||||||
// Collect the ko-specific apply flags before registering the kubectl global
|
|
||||||
// flags so that we can ignore them when passing kubectl global flags through
|
|
||||||
// to kubectl.
|
|
||||||
apply.Flags().VisitAll(func(flag *pflag.Flag) {
|
|
||||||
koApplyFlags = append(koApplyFlags, flag.Name)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Register the kubectl global flags.
|
|
||||||
kubeConfigFlags := genericclioptions.NewConfigFlags()
|
|
||||||
kubeConfigFlags.AddFlags(apply.Flags())
|
|
||||||
|
|
||||||
topLevel.AddCommand(apply)
|
|
||||||
|
|
||||||
resolve := &cobra.Command{
|
|
||||||
Use: "resolve -f FILENAME",
|
|
||||||
Short: "Print the input files with image references resolved to built/pushed image digests.",
|
|
||||||
Long: `This sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and prints the resulting yaml.`,
|
|
||||||
Example: `
|
|
||||||
# Build and publish import path references to a Docker
|
|
||||||
# Registry as:
|
|
||||||
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
|
||||||
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
|
||||||
# --local and --preserve-import-paths were passed.
|
|
||||||
ko resolve -f config/
|
|
||||||
|
|
||||||
# Build and publish import path references to a Docker
|
|
||||||
# Registry preserving import path names as:
|
|
||||||
# ${KO_DOCKER_REPO}/<import path>
|
|
||||||
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
|
||||||
# --local was passed.
|
|
||||||
ko resolve --preserve-import-paths -f config/
|
|
||||||
|
|
||||||
# Build and publish import path references to a Docker
|
|
||||||
# daemon as:
|
|
||||||
# ko.local/<import path>
|
|
||||||
# This always preserves import paths.
|
|
||||||
ko resolve --local -f config/`,
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
resolveFilesToWriter(fo, no, lo, ta, os.Stdout)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
addLocalArg(resolve, lo)
|
|
||||||
addNamingArgs(resolve, no)
|
|
||||||
addFileArg(resolve, fo)
|
|
||||||
addTagsArg(resolve, ta)
|
|
||||||
topLevel.AddCommand(resolve)
|
|
||||||
|
|
||||||
publish := &cobra.Command{
|
|
||||||
Use: "publish IMPORTPATH...",
|
|
||||||
Short: "Build and publish container images from the given importpaths.",
|
|
||||||
Long: `This sub-command builds the provided import paths into Go binaries, containerizes them, and publishes them.`,
|
|
||||||
Example: `
|
|
||||||
# Build and publish import path references to a Docker
|
|
||||||
# Registry as:
|
|
||||||
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
|
||||||
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
|
||||||
# --local and --preserve-import-paths were passed.
|
|
||||||
ko publish github.com/foo/bar/cmd/baz github.com/foo/bar/cmd/blah
|
|
||||||
|
|
||||||
# Build and publish a relative import path as:
|
|
||||||
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
|
||||||
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
|
||||||
# --local and --preserve-import-paths were passed.
|
|
||||||
ko publish ./cmd/blah
|
|
||||||
|
|
||||||
# Build and publish a relative import path as:
|
|
||||||
# ${KO_DOCKER_REPO}/<import path>
|
|
||||||
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
|
||||||
# --local was passed.
|
|
||||||
ko publish --preserve-import-paths ./cmd/blah
|
|
||||||
|
|
||||||
# Build and publish import path references to a Docker
|
|
||||||
# daemon as:
|
|
||||||
# ko.local/<import path>
|
|
||||||
# This always preserves import paths.
|
|
||||||
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)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
addLocalArg(publish, lo)
|
|
||||||
addNamingArgs(publish, no)
|
|
||||||
addTagsArg(publish, ta)
|
|
||||||
topLevel.AddCommand(publish)
|
|
||||||
|
|
||||||
run := &cobra.Command{
|
|
||||||
Use: "run NAME --image=IMPORTPATH",
|
|
||||||
Short: "A variant of `kubectl run` that containerizes IMPORTPATH first.",
|
|
||||||
Long: `This sub-command combines "ko publish" and "kubectl run" to support containerizing and running Go binaries on Kubernetes in a single command.`,
|
|
||||||
Example: `
|
|
||||||
# Publish the --image and run it on Kubernetes as:
|
|
||||||
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
|
||||||
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
|
||||||
# --local and --preserve-import-paths were passed.
|
|
||||||
ko run foo --image=github.com/foo/bar/cmd/baz
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
// There's only one, but this is the simple way to access the
|
|
||||||
// reference since the import path may have been qualified.
|
|
||||||
for k, v := range imgs {
|
|
||||||
log.Printf("Running %q", k)
|
|
||||||
// Issue a "kubectl run" command with our same arguments,
|
|
||||||
// but supply a second --image to override the one we intercepted.
|
|
||||||
argv := append(os.Args[1:], "--image", v.String())
|
|
||||||
kubectlCmd := exec.Command("kubectl", argv...)
|
|
||||||
|
|
||||||
// Pass through our environment
|
|
||||||
kubectlCmd.Env = os.Environ()
|
|
||||||
// Pass through our std*
|
|
||||||
kubectlCmd.Stderr = os.Stderr
|
|
||||||
kubectlCmd.Stdout = os.Stdout
|
|
||||||
kubectlCmd.Stdin = os.Stdin
|
|
||||||
|
|
||||||
// Run it.
|
|
||||||
if err := kubectlCmd.Run(); err != nil {
|
|
||||||
log.Fatalf("error executing \"kubectl run\": %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// We ignore unknown flags to avoid importing everything Go exposes
|
|
||||||
// from our commands.
|
|
||||||
FParseErrWhitelist: cobra.FParseErrWhitelist{
|
|
||||||
UnknownFlags: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
addLocalArg(run, lo)
|
|
||||||
addNamingArgs(run, no)
|
|
||||||
addImageArg(run, bo)
|
|
||||||
addTagsArg(run, ta)
|
|
||||||
|
|
||||||
topLevel.AddCommand(run)
|
|
||||||
}
|
|
@ -15,6 +15,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/google/ko/pkg/commands"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -29,7 +30,7 @@ func main() {
|
|||||||
cmd.Help()
|
cmd.Help()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
addKubeCommands(cmds)
|
commands.AddKubeCommands(cmds)
|
||||||
|
|
||||||
if err := cmds.Execute(); err != nil {
|
if err := cmds.Execute(); err != nil {
|
||||||
log.Fatalf("error during command execution: %v", err)
|
log.Fatalf("error during command execution: %v", err)
|
||||||
|
@ -25,6 +25,6 @@ VERSION=$1
|
|||||||
KO_ROOT="$(cd "$(dirname "$0")" && pwd)/.."
|
KO_ROOT="$(cd "$(dirname "$0")" && pwd)/.."
|
||||||
|
|
||||||
go get github.com/ahmetb/govvv
|
go get github.com/ahmetb/govvv
|
||||||
govvv build -o $KO_ROOT/build/ko $KO_ROOT/cmd/ko -version $VERSION
|
govvv build -o $KO_ROOT/build/ko $KO_ROOT/cmd/ko -pkg github.com/google/ko/pkg/commands -version $VERSION
|
||||||
git tag $VERSION
|
git tag $VERSION
|
||||||
git push origin $VERSION
|
git push origin $VERSION
|
||||||
|
@ -38,12 +38,13 @@ const (
|
|||||||
|
|
||||||
// GetBase takes an importpath and returns a base v1.Image.
|
// GetBase takes an importpath and returns a base v1.Image.
|
||||||
type GetBase func(string) (v1.Image, error)
|
type GetBase func(string) (v1.Image, error)
|
||||||
type builder func(string) (string, error)
|
type builder func(string, bool) (string, error)
|
||||||
|
|
||||||
type gobuild struct {
|
type gobuild struct {
|
||||||
getBase GetBase
|
getBase GetBase
|
||||||
creationTime v1.Time
|
creationTime v1.Time
|
||||||
build builder
|
build builder
|
||||||
|
disableOptimizations bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option is a functional option for NewGo.
|
// Option is a functional option for NewGo.
|
||||||
@ -53,6 +54,7 @@ type gobuildOpener struct {
|
|||||||
getBase GetBase
|
getBase GetBase
|
||||||
creationTime v1.Time
|
creationTime v1.Time
|
||||||
build builder
|
build builder
|
||||||
|
disableOptimizations bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gbo *gobuildOpener) Open() (Interface, error) {
|
func (gbo *gobuildOpener) Open() (Interface, error) {
|
||||||
@ -63,6 +65,7 @@ func (gbo *gobuildOpener) Open() (Interface, error) {
|
|||||||
getBase: gbo.getBase,
|
getBase: gbo.getBase,
|
||||||
creationTime: gbo.creationTime,
|
creationTime: gbo.creationTime,
|
||||||
build: gbo.build,
|
build: gbo.build,
|
||||||
|
disableOptimizations: gbo.disableOptimizations,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,14 +97,22 @@ func (*gobuild) IsSupportedReference(s string) bool {
|
|||||||
return p.IsCommand()
|
return p.IsCommand()
|
||||||
}
|
}
|
||||||
|
|
||||||
func build(ip string) (string, error) {
|
func build(ip string, disableOptimizations bool) (string, error) {
|
||||||
tmpDir, err := ioutil.TempDir("", "ko")
|
tmpDir, err := ioutil.TempDir("", "ko")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
file := filepath.Join(tmpDir, "out")
|
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
|
// Last one wins
|
||||||
// TODO(mattmoor): GOARCH=amd64
|
// TODO(mattmoor): GOARCH=amd64
|
||||||
@ -273,7 +284,7 @@ func tarKoData(importpath string) (*bytes.Buffer, error) {
|
|||||||
// Build implements build.Interface
|
// Build implements build.Interface
|
||||||
func (gb *gobuild) Build(s string) (v1.Image, error) {
|
func (gb *gobuild) Build(s string) (v1.Image, error) {
|
||||||
// Do the build into a temporary file.
|
// Do the build into a temporary file.
|
||||||
file, err := gb.build(s)
|
file, err := gb.build(s, gb.disableOptimizations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ func TestGoBuildIsSupportedRef(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A helper method we use to substitute for the default "build" method.
|
// 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")
|
tmpDir, err := ioutil.TempDir("", "ko")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -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
|
// withBuilder is a functional option for overriding the way go binaries
|
||||||
// are built. This is exposed for testing.
|
// are built. This is exposed for testing.
|
||||||
func withBuilder(b builder) Option {
|
func withBuilder(b builder) Option {
|
||||||
|
145
pkg/commands/apply.go
Normal file
145
pkg/commands/apply.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// Copyright 2018 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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/google/ko/pkg/commands/options"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// addApply augments our CLI surface with apply.
|
||||||
|
func addApply(topLevel *cobra.Command) {
|
||||||
|
koApplyFlags := []string{}
|
||||||
|
lo := &options.LocalOptions{}
|
||||||
|
no := &options.NameOptions{}
|
||||||
|
fo := &options.FilenameOptions{}
|
||||||
|
ta := &options.TagsOptions{}
|
||||||
|
do := &options.DebugOptions{}
|
||||||
|
apply := &cobra.Command{
|
||||||
|
Use: "apply -f FILENAME",
|
||||||
|
Short: "Apply the input files with image references resolved to built/pushed image digests.",
|
||||||
|
Long: `This sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and then feeds the resulting yaml into "kubectl apply".`,
|
||||||
|
Example: `
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# Registry as:
|
||||||
|
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
||||||
|
# Then, feed the resulting yaml into "kubectl apply".
|
||||||
|
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
||||||
|
# --local was passed.
|
||||||
|
ko apply -f config/
|
||||||
|
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# Registry preserving import path names as:
|
||||||
|
# ${KO_DOCKER_REPO}/<import path>
|
||||||
|
# Then, feed the resulting yaml into "kubectl apply".
|
||||||
|
ko apply --preserve-import-paths -f config/
|
||||||
|
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# daemon as:
|
||||||
|
# ko.local/<import path>
|
||||||
|
# Then, feed the resulting yaml into "kubectl apply".
|
||||||
|
ko apply --local -f config/
|
||||||
|
|
||||||
|
# Apply from stdin:
|
||||||
|
cat config.yaml | ko apply -f -`,
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
builder, err := makeBuilder(do)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating builder: %v", err)
|
||||||
|
}
|
||||||
|
publisher, err := makePublisher(no, lo, ta)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating publisher: %v", err)
|
||||||
|
}
|
||||||
|
// Create a set of ko-specific flags to ignore when passing through
|
||||||
|
// kubectl global flags.
|
||||||
|
ignoreSet := make(map[string]struct{})
|
||||||
|
for _, s := range koApplyFlags {
|
||||||
|
ignoreSet[s] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out ko flags from what we will pass through to kubectl.
|
||||||
|
kubectlFlags := []string{}
|
||||||
|
cmd.Flags().Visit(func(flag *pflag.Flag) {
|
||||||
|
if _, ok := ignoreSet[flag.Name]; !ok {
|
||||||
|
kubectlFlags = append(kubectlFlags, "--"+flag.Name, flag.Value.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Issue a "kubectl apply" command reading from stdin,
|
||||||
|
// to which we will pipe the resolved files.
|
||||||
|
argv := []string{"apply", "-f", "-"}
|
||||||
|
argv = append(argv, kubectlFlags...)
|
||||||
|
kubectlCmd := exec.Command("kubectl", argv...)
|
||||||
|
|
||||||
|
// Pass through our environment
|
||||||
|
kubectlCmd.Env = os.Environ()
|
||||||
|
// Pass through our std{out,err} and make our resolved buffer stdin.
|
||||||
|
kubectlCmd.Stderr = os.Stderr
|
||||||
|
kubectlCmd.Stdout = os.Stdout
|
||||||
|
|
||||||
|
// Wire up kubectl stdin to resolveFilesToWriter.
|
||||||
|
stdin, err := kubectlCmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error piping to 'kubectl apply': %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// kubectl buffers data before starting to apply it, which
|
||||||
|
// can lead to resources being created more slowly than desired.
|
||||||
|
// In the case of --watch, it can lead to resources not being
|
||||||
|
// applied at all until enough iteration has occurred. To work
|
||||||
|
// around this, we prime the stream with a bunch of empty objects
|
||||||
|
// which kubectl will discard.
|
||||||
|
// See https://github.com/google/go-containerregistry/pull/348
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
stdin.Write([]byte("---\n"))
|
||||||
|
}
|
||||||
|
// Once primed kick things off.
|
||||||
|
resolveFilesToWriter(builder, publisher, fo, stdin)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Run it.
|
||||||
|
if err := kubectlCmd.Run(); err != nil {
|
||||||
|
log.Fatalf("error executing 'kubectl apply': %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
options.AddLocalArg(apply, lo)
|
||||||
|
options.AddNamingArgs(apply, no)
|
||||||
|
options.AddFileArg(apply, fo)
|
||||||
|
options.AddTagsArg(apply, ta)
|
||||||
|
options.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
|
||||||
|
// to kubectl.
|
||||||
|
apply.Flags().VisitAll(func(flag *pflag.Flag) {
|
||||||
|
koApplyFlags = append(koApplyFlags, flag.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Register the kubectl global flags.
|
||||||
|
kubeConfigFlags := genericclioptions.NewConfigFlags()
|
||||||
|
kubeConfigFlags.AddFlags(apply.Flags())
|
||||||
|
|
||||||
|
topLevel.AddCommand(apply)
|
||||||
|
}
|
32
pkg/commands/commands.go
Normal file
32
pkg/commands/commands.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2018 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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddKubeCommands augments our CLI surface with a passthru delete command, and an apply
|
||||||
|
// command that realizes the promise of ko, as outlined here:
|
||||||
|
// https://github.com/google/go-containerregistry/issues/80
|
||||||
|
func AddKubeCommands(topLevel *cobra.Command) {
|
||||||
|
addDelete(topLevel)
|
||||||
|
addVersion(topLevel)
|
||||||
|
addCreate(topLevel)
|
||||||
|
addApply(topLevel)
|
||||||
|
addResolve(topLevel)
|
||||||
|
addPublish(topLevel)
|
||||||
|
addRun(topLevel)
|
||||||
|
}
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -21,12 +21,11 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/google/go-containerregistry/pkg/authn"
|
"github.com/google/go-containerregistry/pkg/authn"
|
||||||
"github.com/google/go-containerregistry/pkg/name"
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -51,7 +50,7 @@ func getCreationTime() (*v1.Time, error) {
|
|||||||
|
|
||||||
seconds, err := strconv.ParseInt(epoch, 10, 64)
|
seconds, err := strconv.ParseInt(epoch, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("the environment variable SOURCE_DATE_EPOCH is invalid. It's must be a number of seconds since January 1st 1970, 00:00 UTC, got %v", err)
|
return nil, fmt.Errorf("the environment variable SOURCE_DATE_EPOCH should be the number of seconds since January 1st 1970, 00:00 UTC, got: %v", err)
|
||||||
}
|
}
|
||||||
return &v1.Time{time.Unix(seconds, 0)}, nil
|
return &v1.Time{time.Unix(seconds, 0)}, nil
|
||||||
}
|
}
|
145
pkg/commands/create.go
Normal file
145
pkg/commands/create.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// Copyright 2018 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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/google/ko/pkg/commands/options"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// addCreate augments our CLI surface with apply.
|
||||||
|
func addCreate(topLevel *cobra.Command) {
|
||||||
|
koCreateFlags := []string{}
|
||||||
|
lo := &options.LocalOptions{}
|
||||||
|
no := &options.NameOptions{}
|
||||||
|
fo := &options.FilenameOptions{}
|
||||||
|
ta := &options.TagsOptions{}
|
||||||
|
do := &options.DebugOptions{}
|
||||||
|
create := &cobra.Command{
|
||||||
|
Use: "create -f FILENAME",
|
||||||
|
Short: "Create the input files with image references resolved to built/pushed image digests.",
|
||||||
|
Long: `This sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and then feeds the resulting yaml into "kubectl create".`,
|
||||||
|
Example: `
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# Registry as:
|
||||||
|
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
||||||
|
# Then, feed the resulting yaml into "kubectl create".
|
||||||
|
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
||||||
|
# --local was passed.
|
||||||
|
ko create -f config/
|
||||||
|
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# Registry preserving import path names as:
|
||||||
|
# ${KO_DOCKER_REPO}/<import path>
|
||||||
|
# Then, feed the resulting yaml into "kubectl create".
|
||||||
|
ko create --preserve-import-paths -f config/
|
||||||
|
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# daemon as:
|
||||||
|
# ko.local/<import path>
|
||||||
|
# Then, feed the resulting yaml into "kubectl create".
|
||||||
|
ko create --local -f config/
|
||||||
|
|
||||||
|
# Create from stdin:
|
||||||
|
cat config.yaml | ko create -f -`,
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
builder, err := makeBuilder(do)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating builder: %v", err)
|
||||||
|
}
|
||||||
|
publisher, err := makePublisher(no, lo, ta)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating publisher: %v", err)
|
||||||
|
}
|
||||||
|
// Create a set of ko-specific flags to ignore when passing through
|
||||||
|
// kubectl global flags.
|
||||||
|
ignoreSet := make(map[string]struct{})
|
||||||
|
for _, s := range koCreateFlags {
|
||||||
|
ignoreSet[s] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out ko flags from what we will pass through to kubectl.
|
||||||
|
kubectlFlags := []string{}
|
||||||
|
cmd.Flags().Visit(func(flag *pflag.Flag) {
|
||||||
|
if _, ok := ignoreSet[flag.Name]; !ok {
|
||||||
|
kubectlFlags = append(kubectlFlags, "--"+flag.Name, flag.Value.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Issue a "kubectl create" command reading from stdin,
|
||||||
|
// to which we will pipe the resolved files.
|
||||||
|
argv := []string{"create", "-f", "-"}
|
||||||
|
argv = append(argv, kubectlFlags...)
|
||||||
|
kubectlCmd := exec.Command("kubectl", argv...)
|
||||||
|
|
||||||
|
// Pass through our environment
|
||||||
|
kubectlCmd.Env = os.Environ()
|
||||||
|
// Pass through our std{out,err} and make our resolved buffer stdin.
|
||||||
|
kubectlCmd.Stderr = os.Stderr
|
||||||
|
kubectlCmd.Stdout = os.Stdout
|
||||||
|
|
||||||
|
// Wire up kubectl stdin to resolveFilesToWriter.
|
||||||
|
stdin, err := kubectlCmd.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error piping to 'kubectl create': %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// kubectl buffers data before starting to create it, which
|
||||||
|
// can lead to resources being created more slowly than desired.
|
||||||
|
// In the case of --watch, it can lead to resources not being
|
||||||
|
// applied at all until enough iteration has occurred. To work
|
||||||
|
// around this, we prime the stream with a bunch of empty objects
|
||||||
|
// which kubectl will discard.
|
||||||
|
// See https://github.com/google/go-containerregistry/pull/348
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
stdin.Write([]byte("---\n"))
|
||||||
|
}
|
||||||
|
// Once primed kick things off.
|
||||||
|
resolveFilesToWriter(builder, publisher, fo, stdin)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Run it.
|
||||||
|
if err := kubectlCmd.Run(); err != nil {
|
||||||
|
log.Fatalf("error executing 'kubectl create': %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
options.AddLocalArg(create, lo)
|
||||||
|
options.AddNamingArgs(create, no)
|
||||||
|
options.AddFileArg(create, fo)
|
||||||
|
options.AddTagsArg(create, ta)
|
||||||
|
options.AddDebugArg(create, 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
|
||||||
|
// to kubectl.
|
||||||
|
create.Flags().VisitAll(func(flag *pflag.Flag) {
|
||||||
|
koCreateFlags = append(koCreateFlags, flag.Name)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Register the kubectl global flags.
|
||||||
|
kubeConfigFlags := genericclioptions.NewConfigFlags()
|
||||||
|
kubeConfigFlags.AddFlags(create.Flags())
|
||||||
|
|
||||||
|
topLevel.AddCommand(create)
|
||||||
|
}
|
61
pkg/commands/delete.go
Normal file
61
pkg/commands/delete.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2018 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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// runCmd is suitable for use with cobra.Command's Run field.
|
||||||
|
type runCmd func(*cobra.Command, []string)
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// Start building a command line invocation by passing
|
||||||
|
// through our arguments to command's CLI.
|
||||||
|
cmd := exec.Command(command, os.Args[1:]...)
|
||||||
|
|
||||||
|
// Pass through our environment
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
// Pass through our stdfoo
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
|
||||||
|
// Run it.
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
log.Fatalf("error executing %q command with args: %v; %v", command, os.Args[1:], err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addDelete augments our CLI surface with publish.
|
||||||
|
func addDelete(topLevel *cobra.Command) {
|
||||||
|
topLevel.AddCommand(&cobra.Command{
|
||||||
|
Use: "delete",
|
||||||
|
Short: `See "kubectl help delete" for detailed usage.`,
|
||||||
|
Run: passthru("kubectl"),
|
||||||
|
// We ignore unknown flags to avoid importing everything Go exposes
|
||||||
|
// from our commands.
|
||||||
|
FParseErrWhitelist: cobra.FParseErrWhitelist{
|
||||||
|
UnknownFlags: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -24,7 +24,7 @@ type BinaryOptions struct {
|
|||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
func addImageArg(cmd *cobra.Command, lo *BinaryOptions) {
|
func AddImageArg(cmd *cobra.Command, lo *BinaryOptions) {
|
||||||
cmd.Flags().StringVarP(&lo.Path, "image", "i", lo.Path,
|
cmd.Flags().StringVarP(&lo.Path, "image", "i", lo.Path,
|
||||||
"The import path of the binary to publish.")
|
"The import path of the binary to publish.")
|
||||||
}
|
}
|
29
pkg/commands/options/debug.go
Normal file
29
pkg/commands/options/debug.go
Normal file
@ -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 options
|
||||||
|
|
||||||
|
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.")
|
||||||
|
}
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
@ -30,7 +30,7 @@ type FilenameOptions struct {
|
|||||||
Watch bool
|
Watch bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFileArg(cmd *cobra.Command, fo *FilenameOptions) {
|
func AddFileArg(cmd *cobra.Command, fo *FilenameOptions) {
|
||||||
// From pkg/kubectl
|
// From pkg/kubectl
|
||||||
cmd.Flags().StringSliceVarP(&fo.Filenames, "filename", "f", fo.Filenames,
|
cmd.Flags().StringSliceVarP(&fo.Filenames, "filename", "f", fo.Filenames,
|
||||||
"Filename, directory, or URL to files to use to create the resource")
|
"Filename, directory, or URL to files to use to create the resource")
|
||||||
@ -41,7 +41,7 @@ func addFileArg(cmd *cobra.Command, fo *FilenameOptions) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Based heavily on pkg/kubectl
|
// Based heavily on pkg/kubectl
|
||||||
func enumerateFiles(fo *FilenameOptions) chan string {
|
func EnumerateFiles(fo *FilenameOptions) chan string {
|
||||||
files := make(chan string)
|
files := make(chan string)
|
||||||
go func() {
|
go func() {
|
||||||
// When we're done enumerating files, close the channel
|
// When we're done enumerating files, close the channel
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
@ -31,7 +31,7 @@ type NameOptions struct {
|
|||||||
BaseImportPaths bool
|
BaseImportPaths bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func addNamingArgs(cmd *cobra.Command, no *NameOptions) {
|
func AddNamingArgs(cmd *cobra.Command, no *NameOptions) {
|
||||||
cmd.Flags().BoolVarP(&no.PreserveImportPaths, "preserve-import-paths", "P", no.PreserveImportPaths,
|
cmd.Flags().BoolVarP(&no.PreserveImportPaths, "preserve-import-paths", "P", no.PreserveImportPaths,
|
||||||
"Whether to preserve the full import path after KO_DOCKER_REPO.")
|
"Whether to preserve the full import path after KO_DOCKER_REPO.")
|
||||||
cmd.Flags().BoolVarP(&no.BaseImportPaths, "base-import-paths", "B", no.BaseImportPaths,
|
cmd.Flags().BoolVarP(&no.BaseImportPaths, "base-import-paths", "B", no.BaseImportPaths,
|
||||||
@ -52,7 +52,7 @@ func baseImportPaths(importpath string) string {
|
|||||||
return filepath.Base(importpath)
|
return filepath.Base(importpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeNamer(no *NameOptions) publish.Namer {
|
func MakeNamer(no *NameOptions) publish.Namer {
|
||||||
if no.PreserveImportPaths {
|
if no.PreserveImportPaths {
|
||||||
return preserveImportPath
|
return preserveImportPath
|
||||||
} else if no.BaseImportPaths {
|
} else if no.BaseImportPaths {
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -24,7 +24,7 @@ type LocalOptions struct {
|
|||||||
Local bool
|
Local bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func addLocalArg(cmd *cobra.Command, lo *LocalOptions) {
|
func AddLocalArg(cmd *cobra.Command, lo *LocalOptions) {
|
||||||
cmd.Flags().BoolVarP(&lo.Local, "local", "L", lo.Local,
|
cmd.Flags().BoolVarP(&lo.Local, "local", "L", lo.Local,
|
||||||
"Whether to publish images to a local docker daemon vs. a registry.")
|
"Whether to publish images to a local docker daemon vs. a registry.")
|
||||||
}
|
}
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -23,7 +23,7 @@ type TagsOptions struct {
|
|||||||
Tags []string
|
Tags []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTagsArg(cmd *cobra.Command, ta *TagsOptions) {
|
func AddTagsArg(cmd *cobra.Command, ta *TagsOptions) {
|
||||||
cmd.Flags().StringSliceVarP(&ta.Tags, "tags", "t", []string{"latest"},
|
cmd.Flags().StringSliceVarP(&ta.Tags, "tags", "t", []string{"latest"},
|
||||||
"Which tags to use for the produced image instead of the default 'latest' tag.")
|
"Which tags to use for the produced image instead of the default 'latest' tag.")
|
||||||
}
|
}
|
80
pkg/commands/publish.go
Normal file
80
pkg/commands/publish.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Copyright 2018 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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/google/ko/pkg/commands/options"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// addPublish augments our CLI surface with publish.
|
||||||
|
func addPublish(topLevel *cobra.Command) {
|
||||||
|
lo := &options.LocalOptions{}
|
||||||
|
no := &options.NameOptions{}
|
||||||
|
ta := &options.TagsOptions{}
|
||||||
|
do := &options.DebugOptions{}
|
||||||
|
|
||||||
|
publish := &cobra.Command{
|
||||||
|
Use: "publish IMPORTPATH...",
|
||||||
|
Short: "Build and publish container images from the given importpaths.",
|
||||||
|
Long: `This sub-command builds the provided import paths into Go binaries, containerizes them, and publishes them.`,
|
||||||
|
Example: `
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# Registry as:
|
||||||
|
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
||||||
|
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
||||||
|
# --local and --preserve-import-paths were passed.
|
||||||
|
ko publish github.com/foo/bar/cmd/baz github.com/foo/bar/cmd/blah
|
||||||
|
|
||||||
|
# Build and publish a relative import path as:
|
||||||
|
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
||||||
|
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
||||||
|
# --local and --preserve-import-paths were passed.
|
||||||
|
ko publish ./cmd/blah
|
||||||
|
|
||||||
|
# Build and publish a relative import path as:
|
||||||
|
# ${KO_DOCKER_REPO}/<import path>
|
||||||
|
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
||||||
|
# --local was passed.
|
||||||
|
ko publish --preserve-import-paths ./cmd/blah
|
||||||
|
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# daemon as:
|
||||||
|
# ko.local/<import path>
|
||||||
|
# This always preserves import paths.
|
||||||
|
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) {
|
||||||
|
builder, err := makeBuilder(do)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating builder: %v", err)
|
||||||
|
}
|
||||||
|
publisher, err := makePublisher(no, lo, ta)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating publisher: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := publishImages(args, publisher, builder); err != nil {
|
||||||
|
log.Fatalf("failed to publish images: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
options.AddLocalArg(publish, lo)
|
||||||
|
options.AddNamingArgs(publish, no)
|
||||||
|
options.AddTagsArg(publish, ta)
|
||||||
|
options.AddDebugArg(publish, do)
|
||||||
|
topLevel.AddCommand(publish)
|
||||||
|
}
|
@ -12,17 +12,15 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
gb "go/build"
|
gb "go/build"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/go-containerregistry/pkg/authn"
|
|
||||||
"github.com/google/go-containerregistry/pkg/name"
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
"github.com/google/ko/pkg/build"
|
"github.com/google/ko/pkg/build"
|
||||||
"github.com/google/ko/pkg/publish"
|
"github.com/google/ko/pkg/publish"
|
||||||
@ -38,15 +36,7 @@ func qualifyLocalImport(importpath, gopathsrc, pwd string) (string, error) {
|
|||||||
return filepath.Join(strings.TrimPrefix(pwd, gopathsrc+string(filepath.Separator)), importpath), nil
|
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 {
|
func publishImages(importpaths []string, pub publish.Interface, b build.Interface) (map[string]name.Reference, error) {
|
||||||
opt, err := gobuildOptions()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error setting up builder options: %v", err)
|
|
||||||
}
|
|
||||||
b, err := build.NewGo(opt...)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error creating go builder: %v", err)
|
|
||||||
}
|
|
||||||
imgs := make(map[string]name.Reference)
|
imgs := make(map[string]name.Reference)
|
||||||
for _, importpath := range importpaths {
|
for _, importpath := range importpaths {
|
||||||
if gb.IsLocalImport(importpath) {
|
if gb.IsLocalImport(importpath) {
|
||||||
@ -55,44 +45,27 @@ func publishImages(importpaths []string, no *NameOptions, lo *LocalOptions, ta *
|
|||||||
gopathsrc := filepath.Join(gb.Default.GOPATH, "src")
|
gopathsrc := filepath.Join(gb.Default.GOPATH, "src")
|
||||||
pwd, err := os.Getwd()
|
pwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error getting current working directory: %v", err)
|
return nil, fmt.Errorf("error getting current working directory: %v", err)
|
||||||
}
|
}
|
||||||
importpath, err = qualifyLocalImport(importpath, gopathsrc, pwd)
|
importpath, err = qualifyLocalImport(importpath, gopathsrc, pwd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !b.IsSupportedReference(importpath) {
|
if !b.IsSupportedReference(importpath) {
|
||||||
log.Fatalf("importpath %q is not supported", importpath)
|
return nil, fmt.Errorf("importpath %q is not supported", importpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
img, err := b.Build(importpath)
|
img, err := b.Build(importpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error building %q: %v", importpath, err)
|
return nil, fmt.Errorf("error building %q: %v", importpath, err)
|
||||||
}
|
|
||||||
var pub publish.Interface
|
|
||||||
repoName := os.Getenv("KO_DOCKER_REPO")
|
|
||||||
|
|
||||||
namer := makeNamer(no)
|
|
||||||
|
|
||||||
if lo.Local || repoName == publish.LocalDomain {
|
|
||||||
pub = publish.NewDaemon(namer, ta.Tags)
|
|
||||||
} else {
|
|
||||||
if _, err := name.NewRepository(repoName, name.WeakValidation); err != nil {
|
|
||||||
log.Fatalf("the environment variable KO_DOCKER_REPO must be set to a valid docker repository, got %v", err)
|
|
||||||
}
|
|
||||||
opts := []publish.Option{publish.WithAuthFromKeychain(authn.DefaultKeychain), publish.WithNamer(namer), publish.WithTags(ta.Tags)}
|
|
||||||
pub, err = publish.NewDefault(repoName, opts...)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error setting up default image publisher: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ref, err := pub.Publish(img, importpath)
|
ref, err := pub.Publish(img, importpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error publishing %s: %v", importpath, err)
|
return nil, fmt.Errorf("error publishing %s: %v", importpath, err)
|
||||||
}
|
}
|
||||||
imgs[importpath] = ref
|
imgs[importpath] = ref
|
||||||
}
|
}
|
||||||
return imgs
|
return imgs, nil
|
||||||
}
|
}
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package commands
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
77
pkg/commands/resolve.go
Normal file
77
pkg/commands/resolve.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2018 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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/google/ko/pkg/commands/options"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// addResolve augments our CLI surface with resolve.
|
||||||
|
func addResolve(topLevel *cobra.Command) {
|
||||||
|
|
||||||
|
lo := &options.LocalOptions{}
|
||||||
|
no := &options.NameOptions{}
|
||||||
|
fo := &options.FilenameOptions{}
|
||||||
|
ta := &options.TagsOptions{}
|
||||||
|
do := &options.DebugOptions{}
|
||||||
|
|
||||||
|
resolve := &cobra.Command{
|
||||||
|
Use: "resolve -f FILENAME",
|
||||||
|
Short: "Print the input files with image references resolved to built/pushed image digests.",
|
||||||
|
Long: `This sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and prints the resulting yaml.`,
|
||||||
|
Example: `
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# Registry as:
|
||||||
|
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
||||||
|
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
||||||
|
# --local and --preserve-import-paths were passed.
|
||||||
|
ko resolve -f config/
|
||||||
|
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# Registry preserving import path names as:
|
||||||
|
# ${KO_DOCKER_REPO}/<import path>
|
||||||
|
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
||||||
|
# --local was passed.
|
||||||
|
ko resolve --preserve-import-paths -f config/
|
||||||
|
|
||||||
|
# Build and publish import path references to a Docker
|
||||||
|
# daemon as:
|
||||||
|
# ko.local/<import path>
|
||||||
|
# This always preserves import paths.
|
||||||
|
ko resolve --local -f config/`,
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
builder, err := makeBuilder(do)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating builder: %v", err)
|
||||||
|
}
|
||||||
|
publisher, err := makePublisher(no, lo, ta)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating publisher: %v", err)
|
||||||
|
}
|
||||||
|
resolveFilesToWriter(builder, publisher, fo, os.Stdout)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
options.AddLocalArg(resolve, lo)
|
||||||
|
options.AddNamingArgs(resolve, no)
|
||||||
|
options.AddFileArg(resolve, fo)
|
||||||
|
options.AddTagsArg(resolve, ta)
|
||||||
|
options.AddDebugArg(resolve, do)
|
||||||
|
topLevel.AddCommand(resolve)
|
||||||
|
}
|
@ -12,9 +12,10 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -25,12 +26,13 @@ import (
|
|||||||
"github.com/google/go-containerregistry/pkg/authn"
|
"github.com/google/go-containerregistry/pkg/authn"
|
||||||
"github.com/google/go-containerregistry/pkg/name"
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
"github.com/google/ko/pkg/build"
|
"github.com/google/ko/pkg/build"
|
||||||
|
"github.com/google/ko/pkg/commands/options"
|
||||||
"github.com/google/ko/pkg/publish"
|
"github.com/google/ko/pkg/publish"
|
||||||
"github.com/google/ko/pkg/resolve"
|
"github.com/google/ko/pkg/resolve"
|
||||||
"github.com/mattmoor/dep-notify/pkg/graph"
|
"github.com/mattmoor/dep-notify/pkg/graph"
|
||||||
)
|
)
|
||||||
|
|
||||||
func gobuildOptions() ([]build.Option, error) {
|
func gobuildOptions(do *options.DebugOptions) ([]build.Option, error) {
|
||||||
creationTime, err := getCreationTime()
|
creationTime, err := getCreationTime()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -41,11 +43,14 @@ func gobuildOptions() ([]build.Option, error) {
|
|||||||
if creationTime != nil {
|
if creationTime != nil {
|
||||||
opts = append(opts, build.WithCreationTime(*creationTime))
|
opts = append(opts, build.WithCreationTime(*creationTime))
|
||||||
}
|
}
|
||||||
|
if do.DisableOptimizations {
|
||||||
|
opts = append(opts, build.WithDisabledOptimizations())
|
||||||
|
}
|
||||||
return opts, nil
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeBuilder() (*build.Caching, error) {
|
func makeBuilder(do *options.DebugOptions) (*build.Caching, error) {
|
||||||
opt, err := gobuildOptions()
|
opt, err := gobuildOptions(do)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error setting up builder options: %v", err)
|
log.Fatalf("error setting up builder options: %v", err)
|
||||||
}
|
}
|
||||||
@ -73,19 +78,22 @@ func makeBuilder() (*build.Caching, error) {
|
|||||||
return build.NewCaching(innerBuilder)
|
return build.NewCaching(innerBuilder)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePublisher(no *NameOptions, lo *LocalOptions, ta *TagsOptions) (publish.Interface, error) {
|
func makePublisher(no *options.NameOptions, lo *options.LocalOptions, ta *options.TagsOptions) (publish.Interface, error) {
|
||||||
// Create the publish.Interface that we will use to publish image references
|
// Create the publish.Interface that we will use to publish image references
|
||||||
// to either a docker daemon or a container image registry.
|
// to either a docker daemon or a container image registry.
|
||||||
innerPublisher, err := func() (publish.Interface, error) {
|
innerPublisher, err := func() (publish.Interface, error) {
|
||||||
namer := makeNamer(no)
|
namer := options.MakeNamer(no)
|
||||||
|
|
||||||
repoName := os.Getenv("KO_DOCKER_REPO")
|
repoName := os.Getenv("KO_DOCKER_REPO")
|
||||||
if lo.Local || repoName == publish.LocalDomain {
|
if lo.Local || repoName == publish.LocalDomain {
|
||||||
return publish.NewDaemon(namer, ta.Tags), nil
|
return publish.NewDaemon(namer, ta.Tags), nil
|
||||||
}
|
}
|
||||||
|
if repoName == "" {
|
||||||
|
return nil, errors.New("KO_DOCKER_REPO environment variable is unset")
|
||||||
|
}
|
||||||
_, err := name.NewRepository(repoName, name.WeakValidation)
|
_, err := name.NewRepository(repoName, name.WeakValidation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("the environment variable KO_DOCKER_REPO must be set to a valid docker repository, got %v", err)
|
return nil, fmt.Errorf("failed to parse environment variable KO_DOCKER_REPO=%q as repository: %v", repoName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return publish.NewDefault(repoName,
|
return publish.NewDefault(repoName,
|
||||||
@ -104,30 +112,21 @@ func makePublisher(no *NameOptions, lo *LocalOptions, ta *TagsOptions) (publish.
|
|||||||
// resolvedFuture represents a "future" for the bytes of a resolved file.
|
// resolvedFuture represents a "future" for the bytes of a resolved file.
|
||||||
type resolvedFuture chan []byte
|
type resolvedFuture chan []byte
|
||||||
|
|
||||||
func resolveFilesToWriter(fo *FilenameOptions, no *NameOptions, lo *LocalOptions, ta *TagsOptions, out io.WriteCloser) {
|
func resolveFilesToWriter(builder *build.Caching, publisher publish.Interface, fo *options.FilenameOptions, out io.WriteCloser) {
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
builder, err := makeBuilder()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error creating builder: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap publisher in a memoizing publisher implementation.
|
|
||||||
publisher, err := makePublisher(no, lo, ta)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error creating publisher: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// By having this as a channel, we can hook this up to a filesystem
|
// By having this as a channel, we can hook this up to a filesystem
|
||||||
// watcher and leave `fs` open to stream the names of yaml files
|
// watcher and leave `fs` open to stream the names of yaml files
|
||||||
// affected by code changes (including the modification of existing or
|
// affected by code changes (including the modification of existing or
|
||||||
// creation of new yaml files).
|
// creation of new yaml files).
|
||||||
fs := enumerateFiles(fo)
|
fs := options.EnumerateFiles(fo)
|
||||||
|
|
||||||
// This tracks filename -> []importpath
|
// This tracks filename -> []importpath
|
||||||
var sm sync.Map
|
var sm sync.Map
|
||||||
|
|
||||||
var g graph.Interface
|
var g graph.Interface
|
||||||
var errCh chan error
|
var errCh chan error
|
||||||
|
var err error
|
||||||
if fo.Watch {
|
if fo.Watch {
|
||||||
// Start a dep-notify process that on notifications scans the
|
// Start a dep-notify process that on notifications scans the
|
||||||
// file-to-recorded-build map and for each affected file resends
|
// file-to-recorded-build map and for each affected file resends
|
96
pkg/commands/run.go
Normal file
96
pkg/commands/run.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2018 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 commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/google/ko/pkg/commands/options"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// addRun augments our CLI surface with run.
|
||||||
|
func addRun(topLevel *cobra.Command) {
|
||||||
|
lo := &options.LocalOptions{}
|
||||||
|
bo := &options.BinaryOptions{}
|
||||||
|
no := &options.NameOptions{}
|
||||||
|
ta := &options.TagsOptions{}
|
||||||
|
do := &options.DebugOptions{}
|
||||||
|
|
||||||
|
run := &cobra.Command{
|
||||||
|
Use: "run NAME --image=IMPORTPATH",
|
||||||
|
Short: "A variant of `kubectl run` that containerizes IMPORTPATH first.",
|
||||||
|
Long: `This sub-command combines "ko publish" and "kubectl run" to support containerizing and running Go binaries on Kubernetes in a single command.`,
|
||||||
|
Example: `
|
||||||
|
# Publish the --image and run it on Kubernetes as:
|
||||||
|
# ${KO_DOCKER_REPO}/<package name>-<hash of import path>
|
||||||
|
# When KO_DOCKER_REPO is ko.local, it is the same as if
|
||||||
|
# --local and --preserve-import-paths were passed.
|
||||||
|
ko run foo --image=github.com/foo/bar/cmd/baz
|
||||||
|
|
||||||
|
# This supports relative import paths as well.
|
||||||
|
ko run foo --image=./cmd/baz`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
builder, err := makeBuilder(do)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating builder: %v", err)
|
||||||
|
}
|
||||||
|
publisher, err := makePublisher(no, lo, ta)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("error creating publisher: %v", err)
|
||||||
|
}
|
||||||
|
imgs, err := publishImages([]string{bo.Path}, publisher, builder)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to publish images: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's only one, but this is the simple way to access the
|
||||||
|
// reference since the import path may have been qualified.
|
||||||
|
for k, v := range imgs {
|
||||||
|
log.Printf("Running %q", k)
|
||||||
|
// Issue a "kubectl run" command with our same arguments,
|
||||||
|
// but supply a second --image to override the one we intercepted.
|
||||||
|
argv := append(os.Args[1:], "--image", v.String())
|
||||||
|
kubectlCmd := exec.Command("kubectl", argv...)
|
||||||
|
|
||||||
|
// Pass through our environment
|
||||||
|
kubectlCmd.Env = os.Environ()
|
||||||
|
// Pass through our std*
|
||||||
|
kubectlCmd.Stderr = os.Stderr
|
||||||
|
kubectlCmd.Stdout = os.Stdout
|
||||||
|
kubectlCmd.Stdin = os.Stdin
|
||||||
|
|
||||||
|
// Run it.
|
||||||
|
if err := kubectlCmd.Run(); err != nil {
|
||||||
|
log.Fatalf("error executing \"kubectl run\": %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// We ignore unknown flags to avoid importing everything Go exposes
|
||||||
|
// from our commands.
|
||||||
|
FParseErrWhitelist: cobra.FParseErrWhitelist{
|
||||||
|
UnknownFlags: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
options.AddLocalArg(run, lo)
|
||||||
|
options.AddNamingArgs(run, no)
|
||||||
|
options.AddImageArg(run, bo)
|
||||||
|
options.AddTagsArg(run, ta)
|
||||||
|
options.AddDebugArg(run, do)
|
||||||
|
|
||||||
|
topLevel.AddCommand(run)
|
||||||
|
}
|
@ -12,30 +12,38 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package main
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// provided by govvv in compile-time
|
// provided by govvv in compile-time
|
||||||
var Version string
|
var Version string
|
||||||
|
|
||||||
|
// addVersion augments our CLI surface with version.
|
||||||
|
func addVersion(topLevel *cobra.Command) {
|
||||||
|
topLevel.AddCommand(&cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: `Print ko version.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
version()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func version() {
|
func version() {
|
||||||
if Version == "" {
|
if Version == "" {
|
||||||
hash, err := gitRevParseHead()
|
hash, err := exec.Command("git", "rev-parse", "HEAD").Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error during command execution: %v", err)
|
log.Fatalf("error during command execution: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("version: %v", string(hash))
|
Version = strings.TrimSpace(string(hash))
|
||||||
} else {
|
}
|
||||||
fmt.Printf("version: %v\n", Version)
|
fmt.Printf("version: %v\n", Version)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func gitRevParseHead() ([]byte, error) {
|
|
||||||
cmd := exec.Command("git", "rev-parse", "HEAD")
|
|
||||||
return cmd.Output()
|
|
||||||
}
|
|
Reference in New Issue
Block a user