mirror of
https://github.com/ko-build/ko.git
synced 2024-12-12 08:54:09 +02:00
Merge pull request #58 from ImJasonH/strict
Implement "strict mode" which requires `ko://` prefix for import paths
This commit is contained in:
commit
b7eb9dfe48
@ -66,14 +66,12 @@ func TestGoBuildIsSupportedRefWithModules(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("random.Image() = %v", err)
|
||||
}
|
||||
|
||||
mod := &modInfo{
|
||||
Path: filepath.FromSlash("github.com/google/ko/cmd/ko/test"),
|
||||
Dir: ".",
|
||||
}
|
||||
|
||||
ng, err := NewGo(WithBaseImages(func(string) (v1.Image, error) { return base, nil }),
|
||||
withModuleInfo(mod))
|
||||
ng, err := NewGo(WithBaseImages(func(string) (v1.Image, error) { return base, nil }), withModuleInfo(mod))
|
||||
if err != nil {
|
||||
t.Fatalf("NewGo() = %v", err)
|
||||
}
|
||||
|
@ -47,7 +47,6 @@ func WithDisabledOptimizations() Option {
|
||||
|
||||
// withBuilder is a functional option for overriding the way go binaries
|
||||
// are built.
|
||||
// This is exposed for testing.
|
||||
func withBuilder(b builder) Option {
|
||||
return func(gbo *gobuildOpener) error {
|
||||
gbo.build = b
|
||||
|
@ -34,6 +34,7 @@ func addApply(topLevel *cobra.Command) {
|
||||
ta := &options.TagsOptions{}
|
||||
do := &options.DebugOptions{}
|
||||
so := &options.SelectorOptions{}
|
||||
sto := &options.StrictOptions{}
|
||||
apply := &cobra.Command{
|
||||
Use: "apply -f FILENAME",
|
||||
Short: "Apply the input files with image references resolved to built/pushed image digests.",
|
||||
@ -116,7 +117,7 @@ func addApply(topLevel *cobra.Command) {
|
||||
stdin.Write([]byte("---\n"))
|
||||
}
|
||||
// Once primed kick things off.
|
||||
resolveFilesToWriter(builder, publisher, fo, so, stdin)
|
||||
resolveFilesToWriter(builder, publisher, fo, so, sto, stdin)
|
||||
}()
|
||||
|
||||
// Run it.
|
||||
@ -131,6 +132,7 @@ func addApply(topLevel *cobra.Command) {
|
||||
options.AddTagsArg(apply, ta)
|
||||
options.AddDebugArg(apply, do)
|
||||
options.AddSelectorArg(apply, so)
|
||||
options.AddStrictArg(apply, sto)
|
||||
|
||||
// Collect the ko-specific apply flags before registering the kubectl global
|
||||
// flags so that we can ignore them when passing kubectl global flags through
|
||||
|
@ -34,6 +34,7 @@ func addCreate(topLevel *cobra.Command) {
|
||||
ta := &options.TagsOptions{}
|
||||
do := &options.DebugOptions{}
|
||||
so := &options.SelectorOptions{}
|
||||
sto := &options.StrictOptions{}
|
||||
create := &cobra.Command{
|
||||
Use: "create -f FILENAME",
|
||||
Short: "Create the input files with image references resolved to built/pushed image digests.",
|
||||
@ -116,7 +117,7 @@ func addCreate(topLevel *cobra.Command) {
|
||||
stdin.Write([]byte("---\n"))
|
||||
}
|
||||
// Once primed kick things off.
|
||||
resolveFilesToWriter(builder, publisher, fo, so, stdin)
|
||||
resolveFilesToWriter(builder, publisher, fo, so, sto, stdin)
|
||||
}()
|
||||
|
||||
// Run it.
|
||||
@ -131,6 +132,7 @@ func addCreate(topLevel *cobra.Command) {
|
||||
options.AddTagsArg(create, ta)
|
||||
options.AddDebugArg(create, do)
|
||||
options.AddSelectorArg(create, so)
|
||||
options.AddStrictArg(create, sto)
|
||||
|
||||
// Collect the ko-specific apply flags before registering the kubectl global
|
||||
// flags so that we can ignore them when passing kubectl global flags through
|
||||
|
@ -21,13 +21,13 @@ import (
|
||||
// LocalOptions represents options for the ko binary.
|
||||
type LocalOptions struct {
|
||||
// Local publishes images to a local docker daemon.
|
||||
Local bool
|
||||
Local bool
|
||||
InsecureRegistry bool
|
||||
}
|
||||
|
||||
func AddLocalArg(cmd *cobra.Command, lo *LocalOptions) {
|
||||
cmd.Flags().BoolVarP(&lo.Local, "local", "L", lo.Local,
|
||||
"Whether to publish images to a local docker daemon vs. a registry.")
|
||||
cmd.Flags().BoolVar(&lo.InsecureRegistry, "insecure-registry", lo.InsecureRegistry,
|
||||
cmd.Flags().BoolVar(&lo.InsecureRegistry, "insecure-registry", lo.InsecureRegistry,
|
||||
"Whether to skip TLS verification on the registry")
|
||||
}
|
||||
|
29
pkg/commands/options/strict.go
Normal file
29
pkg/commands/options/strict.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"
|
||||
)
|
||||
|
||||
// StrictOptions holds options to require strict references.
|
||||
type StrictOptions struct {
|
||||
Strict bool
|
||||
}
|
||||
|
||||
func AddStrictArg(cmd *cobra.Command, so *StrictOptions) {
|
||||
cmd.Flags().BoolVarP(&so.Strict, "strict", "", so.Strict,
|
||||
`If true, require package references to be explicitly prefixed with "ko://"`)
|
||||
}
|
@ -24,13 +24,13 @@ import (
|
||||
|
||||
// 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{}
|
||||
so := &options.SelectorOptions{}
|
||||
sto := &options.StrictOptions{}
|
||||
|
||||
resolve := &cobra.Command{
|
||||
Use: "resolve -f FILENAME",
|
||||
@ -66,7 +66,7 @@ func addResolve(topLevel *cobra.Command) {
|
||||
if err != nil {
|
||||
log.Fatalf("error creating publisher: %v", err)
|
||||
}
|
||||
resolveFilesToWriter(builder, publisher, fo, so, os.Stdout)
|
||||
resolveFilesToWriter(builder, publisher, fo, so, sto, os.Stdout)
|
||||
},
|
||||
}
|
||||
options.AddLocalArg(resolve, lo)
|
||||
@ -75,5 +75,6 @@ func addResolve(topLevel *cobra.Command) {
|
||||
options.AddTagsArg(resolve, ta)
|
||||
options.AddDebugArg(resolve, do)
|
||||
options.AddSelectorArg(resolve, so)
|
||||
options.AddStrictArg(resolve, sto)
|
||||
topLevel.AddCommand(resolve)
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ func makePublisher(no *options.NameOptions, lo *options.LocalOptions, ta *option
|
||||
// resolvedFuture represents a "future" for the bytes of a resolved file.
|
||||
type resolvedFuture chan []byte
|
||||
|
||||
func resolveFilesToWriter(builder *build.Caching, publisher publish.Interface, fo *options.FilenameOptions, so *options.SelectorOptions, out io.WriteCloser) {
|
||||
func resolveFilesToWriter(builder *build.Caching, publisher publish.Interface, fo *options.FilenameOptions, so *options.SelectorOptions, sto *options.StrictOptions, out io.WriteCloser) {
|
||||
defer out.Close()
|
||||
|
||||
// By having this as a channel, we can hook this up to a filesystem
|
||||
@ -194,7 +194,7 @@ func resolveFilesToWriter(builder *build.Caching, publisher publish.Interface, f
|
||||
recordingBuilder := &build.Recorder{
|
||||
Builder: builder,
|
||||
}
|
||||
b, err := resolveFile(f, recordingBuilder, publisher, so)
|
||||
b, err := resolveFile(f, recordingBuilder, publisher, so, sto)
|
||||
if err != nil {
|
||||
// Don't let build errors disrupt the watch.
|
||||
lg := log.Fatalf
|
||||
@ -239,7 +239,7 @@ func resolveFilesToWriter(builder *build.Caching, publisher publish.Interface, f
|
||||
}
|
||||
}
|
||||
|
||||
func resolveFile(f string, builder build.Interface, pub publish.Interface, so *options.SelectorOptions) (b []byte, err error) {
|
||||
func resolveFile(f string, builder build.Interface, pub publish.Interface, so *options.SelectorOptions, sto *options.StrictOptions) (b []byte, err error) {
|
||||
if f == "-" {
|
||||
b, err = ioutil.ReadAll(os.Stdin)
|
||||
} else {
|
||||
@ -256,5 +256,5 @@ func resolveFile(f string, builder build.Interface, pub publish.Interface, so *o
|
||||
}
|
||||
}
|
||||
|
||||
return resolve.ImageReferences(b, builder, pub)
|
||||
return resolve.ImageReferences(b, sto.Strict, builder, pub)
|
||||
}
|
||||
|
@ -28,11 +28,11 @@ import (
|
||||
|
||||
// defalt is intentionally misspelled to avoid keyword collision (and drive Jon nuts).
|
||||
type defalt struct {
|
||||
base string
|
||||
t http.RoundTripper
|
||||
auth authn.Authenticator
|
||||
namer Namer
|
||||
tags []string
|
||||
base string
|
||||
t http.RoundTripper
|
||||
auth authn.Authenticator
|
||||
namer Namer
|
||||
tags []string
|
||||
insecure bool
|
||||
}
|
||||
|
||||
@ -40,11 +40,11 @@ type defalt struct {
|
||||
type Option func(*defaultOpener) error
|
||||
|
||||
type defaultOpener struct {
|
||||
base string
|
||||
t http.RoundTripper
|
||||
auth authn.Authenticator
|
||||
namer Namer
|
||||
tags []string
|
||||
base string
|
||||
t http.RoundTripper
|
||||
auth authn.Authenticator
|
||||
namer Namer
|
||||
tags []string
|
||||
insecure bool
|
||||
}
|
||||
|
||||
|
@ -86,4 +86,4 @@ func Insecure(b bool) Option {
|
||||
i.insecure = b
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/google/ko/pkg/build"
|
||||
@ -28,7 +29,7 @@ import (
|
||||
|
||||
// ImageReferences resolves supported references to images within the input yaml
|
||||
// to published image digests.
|
||||
func ImageReferences(input []byte, builder build.Interface, publisher publish.Interface) ([]byte, error) {
|
||||
func ImageReferences(input []byte, strict bool, builder build.Interface, publisher publish.Interface) ([]byte, error) {
|
||||
// First, walk the input objects and collect a list of supported references
|
||||
refs := make(map[string]struct{})
|
||||
// The loop is to support multi-document yaml files.
|
||||
@ -45,8 +46,15 @@ func ImageReferences(input []byte, builder build.Interface, publisher publish.In
|
||||
}
|
||||
// This simply returns the replaced object, which we discard during the gathering phase.
|
||||
if _, err := replaceRecursive(obj, func(ref string) (string, error) {
|
||||
if builder.IsSupportedReference(ref) {
|
||||
refs[ref] = struct{}{}
|
||||
strictRef := strings.HasPrefix(ref, "ko://")
|
||||
if strict && !strictRef {
|
||||
return ref, nil
|
||||
}
|
||||
tref := strings.TrimPrefix(ref, "ko://")
|
||||
if builder.IsSupportedReference(tref) {
|
||||
refs[tref] = struct{}{}
|
||||
} else if strict && strictRef {
|
||||
return "", fmt.Errorf("Found strict reference %q but %s is not a valid import path", ref, tref)
|
||||
}
|
||||
return ref, nil
|
||||
}); err != nil {
|
||||
@ -93,6 +101,7 @@ func ImageReferences(input []byte, builder build.Interface, publisher publish.In
|
||||
if !builder.IsSupportedReference(ref) {
|
||||
return ref, nil
|
||||
}
|
||||
ref = strings.TrimPrefix(ref, "ko://")
|
||||
if val, ok := sm.Load(ref); ok {
|
||||
return val.(string), nil
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ func TestYAMLArrays(t *testing.T) {
|
||||
t.Fatalf("yaml.Marshal(%v) = %v", inputStructured, err)
|
||||
}
|
||||
|
||||
outYAML, err := ImageReferences(inputYAML, testBuilder, newFixedPublish(test.base, testHashes))
|
||||
outYAML, err := ImageReferences(inputYAML, false, testBuilder, newFixedPublish(test.base, testHashes))
|
||||
if err != nil {
|
||||
t.Fatalf("ImageReferences(%v) = %v", string(inputYAML), err)
|
||||
}
|
||||
@ -158,7 +158,7 @@ func TestYAMLMaps(t *testing.T) {
|
||||
t.Fatalf("yaml.Marshal(%v) = %v", inputStructured, err)
|
||||
}
|
||||
|
||||
outYAML, err := ImageReferences(inputYAML, testBuilder, newFixedPublish(base, testHashes))
|
||||
outYAML, err := ImageReferences(inputYAML, false, testBuilder, newFixedPublish(base, testHashes))
|
||||
if err != nil {
|
||||
t.Fatalf("ImageReferences(%v) = %v", string(inputYAML), err)
|
||||
}
|
||||
@ -226,7 +226,7 @@ func TestYAMLObject(t *testing.T) {
|
||||
t.Fatalf("yaml.Marshal(%v) = %v", inputStructured, err)
|
||||
}
|
||||
|
||||
outYAML, err := ImageReferences(inputYAML, testBuilder, newFixedPublish(base, testHashes))
|
||||
outYAML, err := ImageReferences(inputYAML, false, testBuilder, newFixedPublish(base, testHashes))
|
||||
if err != nil {
|
||||
t.Fatalf("ImageReferences(%v) = %v", string(inputYAML), err)
|
||||
}
|
||||
@ -242,8 +242,29 @@ func TestYAMLObject(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrict(t *testing.T) {
|
||||
refs := []string{
|
||||
"ko://" + fooRef,
|
||||
"ko://" + barRef,
|
||||
}
|
||||
buf := bytes.NewBuffer(nil)
|
||||
encoder := yaml.NewEncoder(buf)
|
||||
for _, input := range refs {
|
||||
if err := encoder.Encode(input); err != nil {
|
||||
t.Fatalf("Encode(%v) = %v", input, err)
|
||||
}
|
||||
}
|
||||
inputYAML := buf.Bytes()
|
||||
base := mustRepository("gcr.io/multi-pass")
|
||||
outYAML, err := ImageReferences(inputYAML, true, testBuilder, newFixedPublish(base, testHashes))
|
||||
if err != nil {
|
||||
t.Fatalf("ImageReferences: %v", err)
|
||||
}
|
||||
t.Log(string(outYAML))
|
||||
}
|
||||
|
||||
func TestMultiDocumentYAMLs(t *testing.T) {
|
||||
tests := []struct {
|
||||
for _, test := range []struct {
|
||||
desc string
|
||||
refs []string
|
||||
hashes []v1.Hash
|
||||
@ -253,9 +274,7 @@ func TestMultiDocumentYAMLs(t *testing.T) {
|
||||
refs: []string{fooRef, barRef},
|
||||
hashes: []v1.Hash{fooHash, barHash},
|
||||
base: mustRepository("gcr.io/multi-pass"),
|
||||
}}
|
||||
|
||||
for _, test := range tests {
|
||||
}} {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
encoder := yaml.NewEncoder(buf)
|
||||
@ -266,7 +285,7 @@ func TestMultiDocumentYAMLs(t *testing.T) {
|
||||
}
|
||||
inputYAML := buf.Bytes()
|
||||
|
||||
outYAML, err := ImageReferences(inputYAML, testBuilder, newFixedPublish(test.base, testHashes))
|
||||
outYAML, err := ImageReferences(inputYAML, false, testBuilder, newFixedPublish(test.base, testHashes))
|
||||
if err != nil {
|
||||
t.Fatalf("ImageReferences(%v) = %v", string(inputYAML), err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user