commit 1b893558aa83ac6491e5ba416b493170a9045fec Author: Jingfang Liu Date: Mon Nov 12 10:26:12 2018 -0800 last change diff --git a/staging/src/k8s.io/cli-runtime/artifacts/kustomization/configMap.yaml b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/configMap.yaml new file mode 100644 index 0000000000..0008853094 --- /dev/null +++ b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/configMap.yaml @@ -0,0 +1,8 @@ + +apiVersion: v1 +kind: ConfigMap +metadata: + name: the-map +data: + altGreeting: "Good Morning!" + enableRisky: "false" diff --git a/staging/src/k8s.io/cli-runtime/artifacts/kustomization/deployment.yaml b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/deployment.yaml new file mode 100644 index 0000000000..6e79409080 --- /dev/null +++ b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: the-deployment +spec: + replicas: 3 + template: + metadata: + labels: + deployment: hello + spec: + containers: + - name: the-container + image: monopole/hello:1 + command: ["/hello", + "--port=8080", + "--enableRiskyFeature=$(ENABLE_RISKY)"] + ports: + - containerPort: 8080 + env: + - name: ALT_GREETING + valueFrom: + configMapKeyRef: + name: the-map + key: altGreeting + - name: ENABLE_RISKY + valueFrom: + configMapKeyRef: + name: the-map + key: enableRisky diff --git a/staging/src/k8s.io/cli-runtime/artifacts/kustomization/kustomization.yaml b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/kustomization.yaml new file mode 100644 index 0000000000..6e1e3202d5 --- /dev/null +++ b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/kustomization.yaml @@ -0,0 +1,5 @@ +nameprefix: test- + resources: +- deployment.yaml +- service.yaml +- configMap.yaml diff --git a/staging/src/k8s.io/cli-runtime/artifacts/kustomization/service.yaml b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/service.yaml new file mode 100644 index 0000000000..2942cdb7df --- /dev/null +++ b/staging/src/k8s.io/cli-runtime/artifacts/kustomization/service.yaml @@ -0,0 +1,13 @@ +kind: Service +apiVersion: v1 +metadata: + name: the-service +spec: + selector: + deployment: hello + type: LoadBalancer + ports: + - protocol: TCP + port: 8666 + targetPort: 8080 + \ No newline at end of file diff --git a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/BUILD b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/BUILD index 22b34de008..b91d1c0130 100644 --- a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/BUILD +++ b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/BUILD @@ -35,12 +35,15 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library", + "//staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps:go_default_library", "//staging/src/k8s.io/client-go/discovery:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library", "//staging/src/k8s.io/client-go/rest:go_default_library", "//staging/src/k8s.io/client-go/restmapper:go_default_library", "//vendor/golang.org/x/text/encoding/unicode:go_default_library", "//vendor/golang.org/x/text/transform:go_default_library", + "//vendor/sigs.k8s.io/kustomize/pkg/commands/build:go_default_library", + "//vendor/sigs.k8s.io/kustomize/pkg/fs:go_default_library", ], ) diff --git a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/builder_test.go b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/builder_test.go index 7fd526b33c..801f13f772 100644 --- a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/builder_test.go +++ b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/builder_test.go @@ -465,27 +465,48 @@ func TestPathBuilderWithMultipleInvalid(t *testing.T) { } func TestDirectoryBuilder(t *testing.T) { - b := newDefaultBuilder(). - FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: []string{"../../../artifacts/guestbook"}}). - NamespaceParam("test").DefaultNamespace() + tests := []struct { + directories []string + singleItem bool + number int + expectedNames []string + }{ + {[]string{"../../../artifacts/guestbook"}, false, 3, []string{"redis-master"}}, + {[]string{"../../../artifacts/kustomization"}, true, 3, []string{"test-the-deployment"}}, + {[]string{"../../../artifacts/guestbook", "../../../artifacts/kustomization"}, false, 6, []string{"redis-master", "test-the-deployment"}}, + } - test := &testVisitor{} - singleItemImplied := false + for _, tt := range tests { + b := newDefaultBuilder(). + FilenameParam(false, &FilenameOptions{Recursive: false, Filenames: tt.directories}). + NamespaceParam("test").DefaultNamespace() - err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle) - if err != nil || singleItemImplied || len(test.Infos) < 3 { - t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos) - } + test := &testVisitor{} + singleItemImplied := false - found := false - for _, info := range test.Infos { - if info.Name == "redis-master" && info.Namespace == "test" && info.Object != nil { - found = true - break + err := b.Do().IntoSingleItemImplied(&singleItemImplied).Visit(test.Handle) + if err != nil || singleItemImplied != tt.singleItem || len(test.Infos) < tt.number { + t.Fatalf("unexpected response: %v %t %#v", err, singleItemImplied, test.Infos) + } + + contained := func(name string) bool { + for _, info := range test.Infos { + if info.Name == name && info.Namespace == "test" && info.Object != nil { + return true + } + } + return false + } + + allFound := true + for _, name := range tt.expectedNames { + if !contained(name) { + allFound = false + } + } + if !allFound { + t.Errorf("unexpected responses: %#v", test.Infos) } - } - if !found { - t.Errorf("unexpected responses: %#v", test.Infos) } } diff --git a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/visitor.go b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/visitor.go index 32c1a691a5..d7a37e1cde 100644 --- a/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/visitor.go +++ b/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/resource/visitor.go @@ -20,10 +20,12 @@ import ( "bytes" "fmt" "io" + "io/ioutil" "net/http" "net/url" "os" "path/filepath" + "strings" "time" "golang.org/x/text/encoding/unicode" @@ -38,6 +40,9 @@ import ( utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/apimachinery/pkg/watch" + "k8s.io/cli-runtime/pkg/kustomize/k8sdeps" + "sigs.k8s.io/kustomize/pkg/commands/build" + "sigs.k8s.io/kustomize/pkg/fs" ) const ( @@ -452,7 +457,10 @@ func ExpandPathsToFileVisitors(mapper *mapper, paths string, recursive bool, ext if err != nil { return err } - + if isKustomizationDir(path) { + visitors = append(visitors, NewKustomizationVisitor(mapper, path, schema)) + return filepath.SkipDir + } if fi.IsDir() { if path != paths && !recursive { return filepath.SkipDir @@ -463,7 +471,10 @@ func ExpandPathsToFileVisitors(mapper *mapper, paths string, recursive bool, ext if path != paths && ignoreFile(path, extensions) { return nil } - + if strings.HasSuffix(path, "kustomization.yaml") { + visitors = append(visitors, NewKustomizationVisitor(mapper, filepath.Dir(path), schema)) + return nil + } visitor := &FileVisitor{ Path: path, StreamVisitor: NewStreamVisitor(nil, mapper, path, schema), @@ -479,6 +490,14 @@ func ExpandPathsToFileVisitors(mapper *mapper, paths string, recursive bool, ext return visitors, nil } +func isKustomizationDir(path string) bool { + if _, err := os.Stat(filepath.Join(path, "kustomization.yaml")); err == nil { + return true + } + return false +} + + // FileVisitor is wrapping around a StreamVisitor, to handle open/close files type FileVisitor struct { Path string @@ -507,6 +526,37 @@ func (v *FileVisitor) Visit(fn VisitorFunc) error { return v.StreamVisitor.Visit(fn) } +// KustomizationVisitor prorvides the output of kustomization build +type KustomizationVisitor struct { + Path string + *StreamVisitor +} + +// Visit in a KustomizationVisitor build the kustomization output +func (v *KustomizationVisitor) Visit(fn VisitorFunc) error { + fSys := fs.MakeRealFS() + f := k8sdeps.NewFactory() + var out bytes.Buffer + cmd := build.NewCmdBuild(&out, fSys, f.ResmapF, f.TransformerF) + cmd.SetArgs([]string{v.Path}) + // we want to silence usage, error output, and any future output from cobra + // we will get error output as a golang error from execute + cmd.SetOutput(ioutil.Discard) + _, err := cmd.ExecuteC() + if err != nil { + return err + } + v.StreamVisitor.Reader = bytes.NewReader(out.Bytes()) + return v.StreamVisitor.Visit(fn) +} + +func NewKustomizationVisitor(mapper *mapper, path string, schema ContentValidator) *KustomizationVisitor { + return &KustomizationVisitor{ + Path: path, + StreamVisitor: NewStreamVisitor(nil, mapper, path, schema), + } +} + // StreamVisitor reads objects from an io.Reader and walks them. A stream visitor can only be // visited once. // TODO: depends on objects being in JSON format before being passed to decode - need to implement