Merge pull request #2959 from phanimarupaka/CatWithSubpkgs

Cat with subpackages
This commit is contained in:
Kubernetes Prow Robot
2020-09-09 10:49:08 -07:00
committed by GitHub
2 changed files with 227 additions and 43 deletions

View File

@@ -5,9 +5,11 @@ package commands
import (
"fmt"
"io"
"os"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
@@ -19,15 +21,14 @@ import (
func GetCatRunner(name string) *CatRunner {
r := &CatRunner{}
c := &cobra.Command{
Use: "cat DIR...",
Use: "cat DIR",
Short: commands.CatShort,
Long: commands.CatLong,
Example: commands.CatExamples,
RunE: r.runE,
Args: cobra.MaximumNArgs(1),
}
fixDocs(name, c)
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
c.Flags().BoolVar(&r.Format, "format", true,
"format resource config yaml before printing.")
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", false,
@@ -49,6 +50,8 @@ func GetCatRunner(name string) *CatRunner {
"if true, exclude non-local-config in the output.")
c.Flags().StringVar(&r.OutputDest, "dest", "",
"if specified, write output to a file rather than stdout")
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", true,
"print resources recursively in all the nested subpackages")
r.Command = c
return r
}
@@ -59,7 +62,6 @@ func CatCommand(name string) *cobra.Command {
// CatRunner contains the run function
type CatRunner struct {
IncludeSubpackages bool
Format bool
KeepAnnotations bool
WrapKind string
@@ -71,56 +73,101 @@ type CatRunner struct {
IncludeLocal bool
ExcludeNonLocal bool
Command *cobra.Command
RecurseSubPackages bool
}
func (r *CatRunner) runE(c *cobra.Command, args []string) error {
// if there is a function-config specified, emit it
var writer = c.OutOrStdout()
if r.OutputDest != "" {
o, err := os.Create(r.OutputDest)
if err != nil {
return errors.Wrap(err)
}
defer o.Close()
writer = o
}
if len(args) == 0 {
input := &kio.ByteReader{Reader: c.InOrStdin()}
// if there is a function-config specified, emit it
outputs, err := r.out(writer)
if err != nil {
return err
}
return handleError(c, kio.Pipeline{Inputs: []kio.Reader{input}, Filters: r.catFilters(), Outputs: outputs}.Execute())
}
e := executeCmdOnPkgs{
writer: writer,
needOpenAPI: false,
recurseSubPackages: r.RecurseSubPackages,
cmdRunner: r,
rootPkgPath: args[0],
}
return e.execute()
}
func (r *CatRunner) executeCmd(w io.Writer, pkgPath string) error {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
input := kio.LocalPackageReader{PackagePath: pkgPath, PackageFileName: openAPIFileName}
outputs, err := r.out(w)
if err != nil {
return err
}
err = kio.Pipeline{
Inputs: []kio.Reader{input},
Filters: r.catFilters(),
Outputs: outputs,
}.Execute()
if err != nil {
// return err if there is only package
if !r.RecurseSubPackages {
return err
} else {
// print error message and continue if there are multiple packages to annotate
fmt.Fprintf(w, "%s in package %q\n", err.Error(), pkgPath)
}
}
fmt.Fprintf(w, "---\n")
return nil
}
func (r *CatRunner) catFilters() []kio.Filter {
var fltrs []kio.Filter
// don't include reconcilers
fltrs = append(fltrs, &filters.IsLocalConfig{
IncludeLocalConfig: r.IncludeLocal,
ExcludeNonLocalConfig: r.ExcludeNonLocal,
})
if r.Format {
fltrs = append(fltrs, filters.FormatFilter{})
}
if r.StripComments {
fltrs = append(fltrs, filters.StripCommentsFilter{})
}
return fltrs
}
func (r *CatRunner) out(w io.Writer) ([]kio.Writer, error) {
var outputs []kio.Writer
var functionConfig *yaml.RNode
if r.FunctionConfig != "" {
configs, err := kio.LocalPackageReader{PackagePath: r.FunctionConfig,
OmitReaderAnnotations: !r.KeepAnnotations}.Read()
if err != nil {
return err
return outputs, err
}
if len(configs) != 1 {
return fmt.Errorf("expected exactly 1 functionConfig, found %d", len(configs))
return outputs, fmt.Errorf("expected exactly 1 functionConfig, found %d", len(configs))
}
functionConfig = configs[0]
}
var inputs []kio.Reader
for _, a := range args {
inputs = append(inputs, kio.LocalPackageReader{
PackagePath: a,
IncludeSubpackages: r.IncludeSubpackages,
})
}
if len(inputs) == 0 {
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
}
var fltr []kio.Filter
// don't include reconcilers
fltr = append(fltr, &filters.IsLocalConfig{
IncludeLocalConfig: r.IncludeLocal,
ExcludeNonLocalConfig: r.ExcludeNonLocal,
})
if r.Format {
fltr = append(fltr, filters.FormatFilter{})
}
if r.StripComments {
fltr = append(fltr, filters.StripCommentsFilter{})
}
var out = c.OutOrStdout()
if r.OutputDest != "" {
o, err := os.Create(r.OutputDest)
if err != nil {
return handleError(c, errors.Wrap(err))
}
defer o.Close()
out = o
}
// remove this annotation explicitly, the ByteWriter won't clear it by
// default because it doesn't set it
clear := []string{"config.kubernetes.io/path"}
@@ -128,9 +175,8 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
clear = nil
}
var outputs []kio.Writer
outputs = append(outputs, kio.ByteWriter{
Writer: out,
Writer: w,
KeepReaderAnnotations: r.KeepAnnotations,
WrappingKind: r.WrapKind,
WrappingAPIVersion: r.WrapApiVersion,
@@ -139,5 +185,5 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
ClearAnnotations: clear,
})
return handleError(c, kio.Pipeline{Inputs: inputs, Filters: fltr, Outputs: outputs}.Execute())
return outputs, nil
}

View File

@@ -8,10 +8,13 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/openapi"
)
// TODO(pwittrock): write tests for reading / writing ResourceLists
@@ -112,6 +115,7 @@ metadata:
app: nginx
spec:
replicas: 3
---
`, b.String()) {
return
}
@@ -223,6 +227,7 @@ metadata:
app: nginx
spec:
replicas: 3
---
`, b.String()) {
return
}
@@ -305,6 +310,7 @@ metadata:
image: gcr.io/example/reconciler:v1
spec:
replicas: 3
---
`, b.String()) {
return
}
@@ -420,6 +426,7 @@ metadata:
app: nginx
spec:
replicas: 3
---
`, string(actual)) {
return
}
@@ -536,7 +543,138 @@ metadata:
app: nginx
spec:
replicas: 3
---
`, string(actual)) {
return
}
}
func TestCatSubPackages(t *testing.T) {
var tests = []struct {
name string
dataset string
packagePath string
args []string
expected string
}{
{
name: "cat-recurse-subpackages",
dataset: "dataset-without-setters",
expected: `
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-deployment
namespace: myspace
spec:
replicas: 3
template:
spec:
containers:
- name: mysql
image: mysql:1.7.9
---
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: storage-deployment
namespace: myspace
spec:
replicas: 4
template:
spec:
containers:
- name: storage
image: storage:1.7.7
---
`,
},
{
name: "cat-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
args: []string{"-R=false"},
packagePath: "mysql",
expected: `
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-deployment
namespace: myspace
spec:
replicas: 3
template:
spec:
containers:
- name: mysql
image: mysql:1.7.9
---
`,
},
{
name: "cat-nested-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql/storage",
args: []string{"-R=false"},
expected: `
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: storage-deployment
namespace: myspace
spec:
replicas: 4
template:
spec:
containers:
- name: storage
image: storage:1.7.7
---`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
defer os.RemoveAll(baseDir)
runner := commands.GetCatRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
t.FailNow()
}
})
}
}