mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-12 01:14:22 +00:00
refactor cmd/config to internal
This commit is contained in:
135
cmd/config/internal/commands/cat.go
Normal file
135
cmd/config/internal/commands/cat.go
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// GetCatRunner returns a command CatRunner.
|
||||
func GetCatRunner(name string) *CatRunner {
|
||||
r := &CatRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "cat DIR...",
|
||||
Short: commands.CatShort,
|
||||
Long: commands.CatLong,
|
||||
Example: commands.CatExamples,
|
||||
RunE: r.runE,
|
||||
}
|
||||
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,
|
||||
"annotate resources with their file origins.")
|
||||
c.Flags().StringVar(&r.WrapKind, "wrap-kind", "",
|
||||
"if set, wrap the output in this list type kind.")
|
||||
c.Flags().StringVar(&r.WrapApiVersion, "wrap-version", "",
|
||||
"if set, wrap the output in this list type apiVersion.")
|
||||
c.Flags().StringVar(&r.FunctionConfig, "function-config", "",
|
||||
"path to function config to put in ResourceList -- only if wrapped in a ResourceList.")
|
||||
c.Flags().StringSliceVar(&r.Styles, "style", []string{},
|
||||
"yaml styles to apply. may be 'TaggedStyle', 'DoubleQuotedStyle', 'LiteralStyle', "+
|
||||
"'FoldedStyle', 'FlowStyle'.")
|
||||
c.Flags().BoolVar(&r.StripComments, "strip-comments", false,
|
||||
"remove comments from yaml.")
|
||||
c.Flags().BoolVar(&r.IncludeLocal, "include-local", false,
|
||||
"if true, include local-config in the output.")
|
||||
c.Flags().BoolVar(&r.ExcludeNonLocal, "exclude-non-local", false,
|
||||
"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")
|
||||
r.Command = c
|
||||
return r
|
||||
}
|
||||
|
||||
func CatCommand(name string) *cobra.Command {
|
||||
return GetCatRunner(name).Command
|
||||
}
|
||||
|
||||
// CatRunner contains the run function
|
||||
type CatRunner struct {
|
||||
IncludeSubpackages bool
|
||||
Format bool
|
||||
KeepAnnotations bool
|
||||
WrapKind string
|
||||
WrapApiVersion string
|
||||
FunctionConfig string
|
||||
OutputDest string
|
||||
Styles []string
|
||||
StripComments bool
|
||||
IncludeLocal bool
|
||||
ExcludeNonLocal bool
|
||||
Command *cobra.Command
|
||||
}
|
||||
|
||||
func (r *CatRunner) runE(c *cobra.Command, args []string) error {
|
||||
// if there is a function-config specified, emit it
|
||||
var functionConfig *yaml.RNode
|
||||
if r.FunctionConfig != "" {
|
||||
configs, err := kio.LocalPackageReader{PackagePath: r.FunctionConfig,
|
||||
OmitReaderAnnotations: !r.KeepAnnotations}.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(configs) != 1 {
|
||||
return 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
|
||||
}
|
||||
|
||||
var outputs []kio.Writer
|
||||
outputs = append(outputs, kio.ByteWriter{
|
||||
Writer: out,
|
||||
KeepReaderAnnotations: r.KeepAnnotations,
|
||||
WrappingKind: r.WrapKind,
|
||||
WrappingAPIVersion: r.WrapApiVersion,
|
||||
FunctionConfig: functionConfig,
|
||||
Style: yaml.GetStyle(r.Styles...),
|
||||
})
|
||||
|
||||
return handleError(c, kio.Pipeline{Inputs: inputs, Filters: fltr, Outputs: outputs}.Execute())
|
||||
}
|
||||
570
cmd/config/internal/commands/cat_test.go
Normal file
570
cmd/config/internal/commands/cat_test.go
Normal file
@@ -0,0 +1,570 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
)
|
||||
|
||||
// TODO(pwittrock): write tests for reading / writing ResourceLists
|
||||
|
||||
func TestCmd_files(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-cat-test")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetCatRunner("")
|
||||
r.Command.SetArgs([]string{d})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, `kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bar
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
spec:
|
||||
replicas: 3
|
||||
`, b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestCmd_filesWithReconcilers(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-cat-test")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/image:version
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetCatRunner("")
|
||||
r.Command.SetArgs([]string{d, "--include-local"})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, `kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/image:version
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
spec:
|
||||
replicas: 3
|
||||
`, b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestCmd_filesWithoutNonReconcilers(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-cat-test")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetCatRunner("")
|
||||
r.Command.SetArgs([]string{d, "--include-local", "--exclude-non-local"})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, `apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
spec:
|
||||
replicas: 3
|
||||
`, b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestCmd_outputTruncateFile(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-cat-test")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "kustomize-cat-test-dest")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetCatRunner("")
|
||||
r.Command.SetArgs([]string{d, "--dest", f.Name()})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, ``, b.String()) {
|
||||
return
|
||||
}
|
||||
|
||||
actual, err := ioutil.ReadFile(f.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, `kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bar
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
spec:
|
||||
replicas: 3
|
||||
`, string(actual)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestCmd_outputCreateFile(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-cat-test")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
d2, err := ioutil.TempDir("", "kustomize-cat-test-dest")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(d2)
|
||||
f := filepath.Join(d2, "output.yaml")
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetCatRunner("")
|
||||
r.Command.SetArgs([]string{d, "--dest", f})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, ``, b.String()) {
|
||||
return
|
||||
}
|
||||
|
||||
actual, err := ioutil.ReadFile(f)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, `kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: bar
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
spec:
|
||||
replicas: 3
|
||||
`, string(actual)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
139
cmd/config/internal/commands/cmdwrap.go
Normal file
139
cmd/config/internal/commands/cmdwrap.go
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
)
|
||||
|
||||
// GetWrapRunner returns a command runner.
|
||||
func GetWrapRunner() *WrapRunner {
|
||||
r := &WrapRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "wrap CMD...",
|
||||
Short: "Wrap an executable so it implements the config fn interface",
|
||||
Long: `Wrap an executable so it implements the config fn interface
|
||||
|
||||
wrap simplifies writing config functions by:
|
||||
|
||||
- invoking an executable command converting an input ResourceList into environment
|
||||
- merging the output onto the original input as a set of patches
|
||||
- setting filenames on any Resources missing them
|
||||
|
||||
config function authors may use wrap by using it to invoke a command from a container image
|
||||
|
||||
The following are equivalent:
|
||||
|
||||
kyaml wrap -- CMD
|
||||
|
||||
kyaml xargs -- CMD | kyaml merge | kyaml fmt --set-filenames
|
||||
|
||||
Environment Variables:
|
||||
|
||||
KUST_OVERRIDE_DIR:
|
||||
|
||||
Path to a directory containing patches to apply to after merging.
|
||||
`,
|
||||
Example: `
|
||||
|
||||
`,
|
||||
RunE: r.runE,
|
||||
SilenceUsage: true,
|
||||
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
r.Command = c
|
||||
r.XArgs = GetXArgsRunner()
|
||||
c.Flags().BoolVar(&r.XArgs.EnvOnly,
|
||||
"env-only", true, "only set env vars, not arguments.")
|
||||
c.Flags().StringVar(&r.XArgs.WrapKind,
|
||||
"wrap-kind", "List", "wrap the input xargs give to the command in this type.")
|
||||
c.Flags().StringVar(&r.XArgs.WrapVersion,
|
||||
"wrap-version", "v1", "wrap the input xargs give to the command in this type.")
|
||||
return r
|
||||
}
|
||||
|
||||
// WrapRunner contains the run function
|
||||
type WrapRunner struct {
|
||||
Command *cobra.Command
|
||||
XArgs *XArgsRunner
|
||||
getEnv func(key string) string
|
||||
}
|
||||
|
||||
const (
|
||||
KustMergeEnv = "KUST_MERGE"
|
||||
KustOverrideDirEnv = "KUST_OVERRIDE_DIR"
|
||||
)
|
||||
|
||||
func WrapCommand() *cobra.Command {
|
||||
return GetWrapRunner().Command
|
||||
}
|
||||
|
||||
func (r *WrapRunner) runE(c *cobra.Command, args []string) error {
|
||||
if r.getEnv == nil {
|
||||
r.getEnv = os.Getenv
|
||||
}
|
||||
xargsIn := &bytes.Buffer{}
|
||||
if _, err := io.Copy(xargsIn, c.InOrStdin()); err != nil {
|
||||
return err
|
||||
}
|
||||
mergeInput := bytes.NewBuffer(xargsIn.Bytes())
|
||||
// Run the command
|
||||
xargsOut := &bytes.Buffer{}
|
||||
r.XArgs.Command.SetArgs(args)
|
||||
r.XArgs.Command.SetIn(xargsIn)
|
||||
r.XArgs.Command.SetOut(xargsOut)
|
||||
r.XArgs.Command.SetErr(os.Stderr)
|
||||
if err := r.XArgs.Command.Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// merge the results
|
||||
buff := &kio.PackageBuffer{}
|
||||
|
||||
var fltrs []kio.Filter
|
||||
var inputs []kio.Reader
|
||||
if r.getEnv(KustMergeEnv) == "" || r.getEnv(KustMergeEnv) == "true" || r.getEnv(KustMergeEnv) == "1" {
|
||||
inputs = append(inputs, &kio.ByteReader{Reader: mergeInput})
|
||||
fltrs = append(fltrs, &filters.MergeFilter{})
|
||||
}
|
||||
inputs = append(inputs, &kio.ByteReader{Reader: xargsOut})
|
||||
|
||||
if err := (kio.Pipeline{Inputs: inputs, Filters: fltrs, Outputs: []kio.Writer{buff}}).
|
||||
Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inputs, fltrs = []kio.Reader{buff}, nil
|
||||
if r.getEnv(KustOverrideDirEnv) != "" {
|
||||
// merge the overrides on top of the output
|
||||
fltrs = append(fltrs, filters.MergeFilter{})
|
||||
inputs = append(inputs,
|
||||
kio.LocalPackageReader{
|
||||
OmitReaderAnnotations: true, // don't set path annotations, as they would override
|
||||
PackagePath: r.getEnv(KustOverrideDirEnv)})
|
||||
}
|
||||
fltrs = append(fltrs,
|
||||
&filters.FileSetter{
|
||||
FilenamePattern: filepath.Join("config", filters.DefaultFilenamePattern)},
|
||||
&filters.FormatFilter{})
|
||||
|
||||
err := kio.Pipeline{
|
||||
Inputs: inputs,
|
||||
Filters: fltrs,
|
||||
Outputs: []kio.Writer{kio.ByteWriter{
|
||||
Sort: true,
|
||||
KeepReaderAnnotations: true,
|
||||
Writer: c.OutOrStdout(),
|
||||
WrappingKind: kio.ResourceListKind,
|
||||
WrappingAPIVersion: kio.ResourceListAPIVersion}}}.Execute()
|
||||
return handleError(c, err)
|
||||
}
|
||||
307
cmd/config/internal/commands/cmdwrap_test.go
Normal file
307
cmd/config/internal/commands/cmdwrap_test.go
Normal file
@@ -0,0 +1,307 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
input = `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
functionConfig:
|
||||
metadata:
|
||||
name: test
|
||||
spec:
|
||||
replicas: 11
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
app: nginx
|
||||
name: test
|
||||
spec:
|
||||
replicas: 5
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
name: test
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: test
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
image: nginx:v1.7
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
app: nginx
|
||||
name: test
|
||||
spec:
|
||||
ports:
|
||||
# This i the port.
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
name: http
|
||||
selector:
|
||||
app: nginx
|
||||
name: test
|
||||
`
|
||||
|
||||
output = `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
name: test
|
||||
app: nginx
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: config/test_deployment.yaml
|
||||
spec:
|
||||
replicas: 11
|
||||
selector:
|
||||
matchLabels:
|
||||
name: test
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: test
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
image: nginx:v1.7
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
name: test
|
||||
app: nginx
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: config/test_service.yaml
|
||||
spec:
|
||||
selector:
|
||||
name: test
|
||||
app: nginx
|
||||
ports:
|
||||
- name: http
|
||||
# This i the port.
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
`
|
||||
|
||||
outputNoMerge = `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
name: test
|
||||
app: nginx
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: config/test_deployment.yaml
|
||||
spec:
|
||||
replicas: 11
|
||||
selector:
|
||||
matchLabels:
|
||||
name: test
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: test
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
image: nginx:v1.7
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
name: test
|
||||
app: nginx
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: config/test_service.yaml
|
||||
spec:
|
||||
selector:
|
||||
name: test
|
||||
app: nginx
|
||||
ports:
|
||||
- name: http
|
||||
# This i the port.
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
`
|
||||
|
||||
outputOverride = `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
name: test
|
||||
app: nginx
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: config/test_deployment.yaml
|
||||
spec:
|
||||
replicas: 11
|
||||
selector:
|
||||
matchLabels:
|
||||
name: test
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: test
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
image: nginx:v1.9
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
name: test
|
||||
app: nginx
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: config/test_service.yaml
|
||||
spec:
|
||||
selector:
|
||||
name: test
|
||||
app: nginx
|
||||
ports:
|
||||
- name: http
|
||||
# This i the port.
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
`
|
||||
)
|
||||
|
||||
func TestCmd_wrap(t *testing.T) {
|
||||
_, dir, _, ok := runtime.Caller(0)
|
||||
if !assert.True(t, ok) {
|
||||
t.FailNow()
|
||||
}
|
||||
dir = filepath.Dir(dir)
|
||||
|
||||
c := GetWrapRunner()
|
||||
c.Command.SetIn(bytes.NewBufferString(input))
|
||||
out := &bytes.Buffer{}
|
||||
c.Command.SetOut(out)
|
||||
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
|
||||
c.Command.SetArgs(args)
|
||||
c.XArgs.Args = args
|
||||
|
||||
if !assert.NoError(t, c.Command.Execute()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert.Equal(t, output, out.String())
|
||||
}
|
||||
|
||||
func TestCmd_wrapNoMerge(t *testing.T) {
|
||||
_, dir, _, ok := runtime.Caller(0)
|
||||
if !assert.True(t, ok) {
|
||||
t.FailNow()
|
||||
}
|
||||
dir = filepath.Dir(dir)
|
||||
|
||||
c := GetWrapRunner()
|
||||
c.getEnv = func(key string) string {
|
||||
if key == KustMergeEnv {
|
||||
return "false"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
c.Command.SetIn(bytes.NewBufferString(input))
|
||||
out := &bytes.Buffer{}
|
||||
c.Command.SetOut(out)
|
||||
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
|
||||
c.Command.SetArgs(args)
|
||||
c.XArgs.Args = args
|
||||
if !assert.NoError(t, c.Command.Execute()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert.Equal(t, outputNoMerge, out.String())
|
||||
}
|
||||
|
||||
func TestCmd_wrapOverride(t *testing.T) {
|
||||
_, dir, _, ok := runtime.Caller(0)
|
||||
if !assert.True(t, ok) {
|
||||
t.FailNow()
|
||||
}
|
||||
dir = filepath.Dir(dir)
|
||||
|
||||
c := GetWrapRunner()
|
||||
c.getEnv = func(key string) string {
|
||||
if key == KustOverrideDirEnv {
|
||||
return filepath.Join(dir, "test")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
c.Command.SetIn(bytes.NewBufferString(input))
|
||||
out := &bytes.Buffer{}
|
||||
c.Command.SetOut(out)
|
||||
args := []string{"--", filepath.Join(dir, "test", "test.sh")}
|
||||
c.Command.SetArgs(args)
|
||||
c.XArgs.Args = args
|
||||
if !assert.NoError(t, c.Command.Execute()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
assert.Equal(t, outputOverride, out.String())
|
||||
}
|
||||
226
cmd/config/internal/commands/cmdxargs.go
Normal file
226
cmd/config/internal/commands/cmdxargs.go
Normal file
@@ -0,0 +1,226 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// GetXArgsRunner returns a command runner.
|
||||
func GetXArgsRunner() *XArgsRunner {
|
||||
r := &XArgsRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "xargs -- CMD...",
|
||||
Short: "Convert functionConfig to commandline flags and envs",
|
||||
Long: `Convert functionConfig to commandline flags and envs.
|
||||
|
||||
xargs reads a ResourceList from stdin and parses the functionConfig field. xargs then
|
||||
reads each of the fields under .spec and parses them as flags. If the fields have non-scalar
|
||||
values, then xargs encoded the values as yaml strings.
|
||||
|
||||
CMD:
|
||||
The command to run and pass the functionConfig as arguments.
|
||||
`,
|
||||
Example: `
|
||||
# given this example functionConfig in config.yaml
|
||||
kind: Foo
|
||||
spec:
|
||||
flag1: value1
|
||||
flag2: value2
|
||||
items:
|
||||
- 2
|
||||
- 1
|
||||
|
||||
# this command:
|
||||
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml run xargs -- app
|
||||
|
||||
# is equivalent to this command:
|
||||
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | app --flag1=value1 --flag2=value2 2 1
|
||||
|
||||
# echo: prints the app arguments
|
||||
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs -- echo
|
||||
--flag1=value1 --flag2=value2 2 1
|
||||
|
||||
# env: prints the app env
|
||||
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs -- env
|
||||
|
||||
# cat: prints the app stdin -- prints the package contents and functionConfig wrapped in a
|
||||
# ResourceList
|
||||
$ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml reconcile xargs --no-flags -- env
|
||||
|
||||
`,
|
||||
RunE: r.runE,
|
||||
SilenceUsage: true,
|
||||
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
r.Command = c
|
||||
r.Command.Flags().BoolVar(&r.EnvOnly, "env-only", false, "only add env vars, not flags")
|
||||
c.Flags().StringVar(&r.WrapKind, "wrap-kind", "List", "wrap the input xargs give to the command in this type.")
|
||||
c.Flags().StringVar(&r.WrapVersion, "wrap-version", "v1", "wrap the input xargs give to the command in this type.")
|
||||
return r
|
||||
}
|
||||
|
||||
// Runner contains the run function
|
||||
type XArgsRunner struct {
|
||||
Command *cobra.Command
|
||||
Args []string
|
||||
EnvOnly bool
|
||||
WrapKind string
|
||||
WrapVersion string
|
||||
}
|
||||
|
||||
func XArgsCommand() *cobra.Command {
|
||||
return GetXArgsRunner().Command
|
||||
}
|
||||
|
||||
func (r *XArgsRunner) runE(c *cobra.Command, _ []string) error {
|
||||
if len(r.Args) == 0 {
|
||||
r.Args = os.Args
|
||||
}
|
||||
cmdIndex := -1
|
||||
for i := range r.Args {
|
||||
if r.Args[i] == "--" {
|
||||
cmdIndex = i + 1
|
||||
break
|
||||
}
|
||||
}
|
||||
if cmdIndex < 0 {
|
||||
return fmt.Errorf("must specify -- before command")
|
||||
}
|
||||
r.Args = r.Args[cmdIndex:]
|
||||
run := exec.Command(r.Args[0])
|
||||
|
||||
if len(r.Args) > 1 {
|
||||
r.Args = r.Args[cmdIndex+1:]
|
||||
} else {
|
||||
r.Args = []string{}
|
||||
}
|
||||
run.Stdout = c.OutOrStdout()
|
||||
run.Stderr = c.ErrOrStderr()
|
||||
|
||||
rw := &kio.ByteReadWriter{
|
||||
Reader: c.InOrStdin(),
|
||||
}
|
||||
nodes, err := rw.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
env := os.Environ()
|
||||
|
||||
// append the config to the flags
|
||||
if err = func() error {
|
||||
if rw.FunctionConfig == nil {
|
||||
return nil
|
||||
}
|
||||
str, err := rw.FunctionConfig.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// add the API object to the env
|
||||
env = append(env, fmt.Sprintf("KUST_FUNCTION_CONFIG=%s", str))
|
||||
|
||||
// parse the fields
|
||||
meta := rw.FunctionConfig.Field("metadata")
|
||||
if meta != nil {
|
||||
err = meta.Value.VisitFields(func(node *yaml.MapNode) error {
|
||||
if !r.EnvOnly {
|
||||
r.Args = append(r.Args, fmt.Sprintf("--%s=%s",
|
||||
node.Key.YNode().Value, parseYNode(node.Value.YNode())))
|
||||
}
|
||||
env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(node.Key.YNode().Value),
|
||||
node.Value.YNode().Value))
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
spec := rw.FunctionConfig.Field("spec")
|
||||
if spec != nil {
|
||||
err = spec.Value.VisitFields(func(node *yaml.MapNode) error {
|
||||
if !r.EnvOnly {
|
||||
r.Args = append(r.Args, fmt.Sprintf("--%s=%s",
|
||||
node.Key.YNode().Value, parseYNode(node.Value.YNode())))
|
||||
}
|
||||
env = append(env, fmt.Sprintf("%s=%s", strings.ToUpper(node.Key.YNode().Value),
|
||||
node.Value.YNode().Value))
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !r.EnvOnly {
|
||||
items := rw.FunctionConfig.Field("items")
|
||||
if items != nil {
|
||||
err = items.Value.VisitElements(func(node *yaml.RNode) error {
|
||||
r.Args = append(r.Args, parseYNode(node.YNode()))
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.WrapKind != "" {
|
||||
if kind := rw.FunctionConfig.Field("kind"); !yaml.IsFieldEmpty(kind) {
|
||||
kind.Value.YNode().Value = r.WrapKind
|
||||
}
|
||||
rw.WrappingKind = r.WrapKind
|
||||
}
|
||||
if r.WrapVersion != "" {
|
||||
if version := rw.FunctionConfig.Field("apiVersion"); !yaml.IsFieldEmpty(version) {
|
||||
version.Value.YNode().Value = r.WrapVersion
|
||||
}
|
||||
rw.WrappingAPIVersion = r.WrapVersion
|
||||
}
|
||||
return nil
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
run.Args = append(run.Args, r.Args...)
|
||||
run.Env = append(run.Env, env...)
|
||||
|
||||
// write ResourceList to stdin
|
||||
if err = func() error {
|
||||
in, err := run.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
rw.Writer = in
|
||||
if r.WrapKind != kio.ResourceListKind {
|
||||
rw.FunctionConfig = nil
|
||||
}
|
||||
return rw.Write(nodes)
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return handleError(c, run.Run())
|
||||
}
|
||||
|
||||
func parseYNode(node *yaml.Node) string {
|
||||
node.Value = strings.TrimSpace(node.Value)
|
||||
for _, b := range node.Value {
|
||||
if unicode.IsSpace(b) {
|
||||
// wrap in '' -- contains whitespace
|
||||
return fmt.Sprintf("'%s'", node.Value)
|
||||
}
|
||||
}
|
||||
return node.Value
|
||||
}
|
||||
117
cmd/config/internal/commands/cmdxargs_test.go
Normal file
117
cmd/config/internal/commands/cmdxargs_test.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
)
|
||||
|
||||
const (
|
||||
flagsInput = `kind: ResourceList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
- apiVersion: apps/v1
|
||||
kind: Service
|
||||
spec: {}
|
||||
functionConfig:
|
||||
kind: Foo
|
||||
spec:
|
||||
a: b
|
||||
c: d
|
||||
e: f
|
||||
items:
|
||||
- 1
|
||||
- 3
|
||||
- 2
|
||||
- 4
|
||||
`
|
||||
|
||||
resourceInput = `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
- apiVersion: apps/v1
|
||||
kind: Service
|
||||
spec: {}
|
||||
functionConfig:
|
||||
kind: Foo
|
||||
`
|
||||
|
||||
resourceOutput = `apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
- apiVersion: apps/v1
|
||||
kind: Service
|
||||
spec: {}
|
||||
`
|
||||
)
|
||||
|
||||
func TestXArgs_flags(t *testing.T) {
|
||||
c := commands.GetXArgsRunner()
|
||||
c.Command.SetIn(bytes.NewBufferString(flagsInput))
|
||||
out := &bytes.Buffer{}
|
||||
c.Command.SetOut(out)
|
||||
c.Command.SetArgs([]string{"--", "echo"})
|
||||
|
||||
c.Args = []string{"--", "echo"}
|
||||
if !assert.NoError(t, c.Command.Execute()) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, `--a=b --c=d --e=f 1 3 2 4
|
||||
`, out.String())
|
||||
}
|
||||
|
||||
func TestXArgs_input(t *testing.T) {
|
||||
c := commands.GetXArgsRunner()
|
||||
c.Command.SetIn(bytes.NewBufferString(resourceInput))
|
||||
out := &bytes.Buffer{}
|
||||
c.Command.SetOut(out)
|
||||
c.Command.SetArgs([]string{"--", "cat"})
|
||||
|
||||
c.Args = []string{"--", "cat"}
|
||||
if !assert.NoError(t, c.Command.Execute()) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, resourceOutput, out.String())
|
||||
}
|
||||
|
||||
func TestCmd_env(t *testing.T) {
|
||||
c := commands.GetXArgsRunner()
|
||||
c.Command.SetIn(bytes.NewBufferString(flagsInput))
|
||||
out := &bytes.Buffer{}
|
||||
c.Command.SetOut(out)
|
||||
c.Command.SetArgs([]string{"--env-only", "--", "env"})
|
||||
|
||||
c.Args = []string{"--", "env"}
|
||||
if !assert.NoError(t, c.Command.Execute()) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Contains(t, out.String(), "\nA=b\nC=d\nE=f\n")
|
||||
}
|
||||
88
cmd/config/internal/commands/count.go
Normal file
88
cmd/config/internal/commands/count.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/sets"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func GetCountRunner(name string) *CountRunner {
|
||||
r := &CountRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "count DIR...",
|
||||
Short: commands.CountShort,
|
||||
Long: commands.CountLong,
|
||||
Example: commands.CountExamples,
|
||||
RunE: r.runE,
|
||||
}
|
||||
fixDocs(name, c)
|
||||
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
|
||||
"also print resources from subpackages.")
|
||||
c.Flags().BoolVar(&r.Kind, "kind", true,
|
||||
"count resources by kind.")
|
||||
|
||||
r.Command = c
|
||||
return r
|
||||
}
|
||||
|
||||
func CountCommand(name string) *cobra.Command {
|
||||
return GetCountRunner(name).Command
|
||||
}
|
||||
|
||||
// CountRunner contains the run function
|
||||
type CountRunner struct {
|
||||
IncludeSubpackages bool
|
||||
Kind bool
|
||||
Command *cobra.Command
|
||||
}
|
||||
|
||||
func (r *CountRunner) runE(c *cobra.Command, args []string) error {
|
||||
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 out []kio.Writer
|
||||
if r.Kind {
|
||||
out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error {
|
||||
count := map[string]int{}
|
||||
k := sets.String{}
|
||||
for _, n := range nodes {
|
||||
m, _ := n.GetMeta()
|
||||
count[m.Kind]++
|
||||
k.Insert(m.Kind)
|
||||
}
|
||||
order := k.List()
|
||||
sort.Strings(order)
|
||||
for _, k := range order {
|
||||
fmt.Fprintf(c.OutOrStdout(), "%s: %d\n", k, count[k])
|
||||
}
|
||||
|
||||
return nil
|
||||
}))
|
||||
|
||||
} else {
|
||||
out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error {
|
||||
fmt.Fprintf(c.OutOrStdout(), "%d\n", len(nodes))
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
return handleError(c, kio.Pipeline{
|
||||
Inputs: inputs,
|
||||
Outputs: out,
|
||||
}.Execute())
|
||||
}
|
||||
73
cmd/config/internal/commands/count_test.go
Normal file
73
cmd/config/internal/commands/count_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
)
|
||||
|
||||
func TestCountCommand_files(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-count-test")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetCountRunner("")
|
||||
r.Command.SetArgs([]string{d})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, "Deployment: 2\nService: 1\n", b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
93
cmd/config/internal/commands/fmt.go
Normal file
93
cmd/config/internal/commands/fmt.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
)
|
||||
|
||||
// FmtCmd returns a command FmtRunner.
|
||||
func GetFmtRunner(name string) *FmtRunner {
|
||||
r := &FmtRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "fmt",
|
||||
Short: commands.FmtShort,
|
||||
Long: commands.FmtLong,
|
||||
Example: commands.FmtExamples,
|
||||
RunE: r.runE,
|
||||
PreRunE: r.preRunE,
|
||||
}
|
||||
fixDocs(name, c)
|
||||
c.Flags().StringVar(&r.FilenamePattern, "pattern", filters.DefaultFilenamePattern,
|
||||
`pattern to use for generating filenames for resources -- may contain the following
|
||||
formatting substitution verbs {'%n': 'metadata.name', '%s': 'metadata.namespace', '%k': 'kind'}`)
|
||||
c.Flags().BoolVar(&r.SetFilenames, "set-filenames", false,
|
||||
`if true, set default filenames on Resources without them`)
|
||||
c.Flags().BoolVar(&r.KeepAnnotations, "keep-annotations", false,
|
||||
`if true, keep index and filename annotations set on Resources.`)
|
||||
c.Flags().BoolVar(&r.Override, "override", false,
|
||||
`if true, override existing filepath annotations.`)
|
||||
r.Command = c
|
||||
return r
|
||||
}
|
||||
|
||||
func FmtCommand(name string) *cobra.Command {
|
||||
return GetFmtRunner(name).Command
|
||||
}
|
||||
|
||||
// FmtRunner contains the run function
|
||||
type FmtRunner struct {
|
||||
Command *cobra.Command
|
||||
FilenamePattern string
|
||||
SetFilenames bool
|
||||
KeepAnnotations bool
|
||||
Override bool
|
||||
}
|
||||
|
||||
func (r *FmtRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
if r.SetFilenames {
|
||||
r.KeepAnnotations = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *FmtRunner) runE(c *cobra.Command, args []string) error {
|
||||
f := []kio.Filter{filters.FormatFilter{}}
|
||||
|
||||
// format with file names
|
||||
if r.SetFilenames {
|
||||
f = append(f, &filters.FileSetter{
|
||||
FilenamePattern: r.FilenamePattern,
|
||||
Override: r.Override,
|
||||
})
|
||||
}
|
||||
|
||||
// format stdin if there are no args
|
||||
if len(args) == 0 {
|
||||
rw := &kio.ByteReadWriter{
|
||||
Reader: c.InOrStdin(),
|
||||
Writer: c.OutOrStdout(),
|
||||
KeepReaderAnnotations: r.KeepAnnotations,
|
||||
}
|
||||
return handleError(c, kio.Pipeline{
|
||||
Inputs: []kio.Reader{rw}, Filters: f, Outputs: []kio.Writer{rw}}.Execute())
|
||||
}
|
||||
|
||||
for i := range args {
|
||||
path := args[i]
|
||||
rw := &kio.LocalPackageReadWriter{
|
||||
NoDeleteFiles: true,
|
||||
PackagePath: path,
|
||||
KeepReaderAnnotations: r.KeepAnnotations}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{rw}, Filters: f, Outputs: []kio.Writer{rw}}.Execute()
|
||||
if err != nil {
|
||||
return handleError(c, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
162
cmd/config/internal/commands/fmt_test.go
Normal file
162
cmd/config/internal/commands/fmt_test.go
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters/testyaml"
|
||||
)
|
||||
|
||||
// TestCmd_files verifies the fmt command formats the files
|
||||
func TestFmtCommand_files(t *testing.T) {
|
||||
f1, err := ioutil.TempFile("", "cmdfmt*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(f1.Name())
|
||||
err = ioutil.WriteFile(f1.Name(), testyaml.UnformattedYaml1, 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
f2, err := ioutil.TempFile("", "cmdfmt*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(f2.Name())
|
||||
err = ioutil.WriteFile(f2.Name(), testyaml.UnformattedYaml2, 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt the files
|
||||
r := commands.GetFmtRunner("")
|
||||
r.Command.SetArgs([]string{f1.Name(), f2.Name()})
|
||||
err = r.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// verify the contents
|
||||
b, err := ioutil.ReadFile(f1.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, string(testyaml.FormattedYaml1), string(b)) {
|
||||
return
|
||||
}
|
||||
|
||||
b, err = ioutil.ReadFile(f2.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, string(testyaml.FormattedYaml2), string(b)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestFmtCommand_stdin(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
r := commands.GetFmtRunner("")
|
||||
r.Command.SetOut(out)
|
||||
r.Command.SetIn(bytes.NewReader(testyaml.UnformattedYaml1))
|
||||
|
||||
// fmt the input
|
||||
err := r.Command.Execute()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// verify the output
|
||||
assert.Equal(t, string(testyaml.FormattedYaml1), out.String())
|
||||
}
|
||||
|
||||
// TestCmd_filesAndstdin verifies that if both files and stdin input are provided, only
|
||||
// the files are formatted and the input is ignored
|
||||
func TestFmtCmd_filesAndStdin(t *testing.T) {
|
||||
f1, err := ioutil.TempFile("", "cmdfmt*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(f1.Name(), testyaml.UnformattedYaml1, 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
f2, err := ioutil.TempFile("", "cmdfmt*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(f2.Name(), testyaml.UnformattedYaml2, 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
in := &bytes.Buffer{}
|
||||
r := commands.GetFmtRunner("")
|
||||
r.Command.SetOut(out)
|
||||
r.Command.SetIn(in)
|
||||
|
||||
// fmt the files
|
||||
r = commands.GetFmtRunner("")
|
||||
r.Command.SetArgs([]string{f1.Name(), f2.Name()})
|
||||
err = r.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// verify the output
|
||||
b, err := ioutil.ReadFile(f1.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, string(testyaml.FormattedYaml1), string(b)) {
|
||||
return
|
||||
}
|
||||
|
||||
b, err = ioutil.ReadFile(f2.Name())
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, string(testyaml.FormattedYaml2), string(b)) {
|
||||
return
|
||||
}
|
||||
err = r.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, "", out.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TestCmd_files verifies the fmt command formats the files
|
||||
func TestCmd_failFiles(t *testing.T) {
|
||||
// fmt the files
|
||||
r := commands.GetFmtRunner("")
|
||||
r.Command.SetArgs([]string{"notrealfile"})
|
||||
err := r.Command.Execute()
|
||||
assert.EqualError(t, err, "lstat notrealfile: no such file or directory")
|
||||
}
|
||||
|
||||
// TestCmd_files verifies the fmt command formats the files
|
||||
func TestCmd_failFileContents(t *testing.T) {
|
||||
out := &bytes.Buffer{}
|
||||
r := commands.GetFmtRunner("")
|
||||
r.Command.SetOut(out)
|
||||
r.Command.SetIn(strings.NewReader(`{`))
|
||||
|
||||
// fmt the input
|
||||
err := r.Command.Execute()
|
||||
|
||||
// expect an error
|
||||
assert.EqualError(t, err, "yaml: line 1: did not find expected node content")
|
||||
}
|
||||
125
cmd/config/internal/commands/grep.go
Normal file
125
cmd/config/internal/commands/grep.go
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
)
|
||||
|
||||
// Cmd returns a command GrepRunner.
|
||||
func GetGrepRunner(name string) *GrepRunner {
|
||||
r := &GrepRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "grep QUERY [DIR]...",
|
||||
Short: commands.GrepShort,
|
||||
Long: commands.GrepLong,
|
||||
Example: commands.GrepExamples,
|
||||
PreRunE: r.preRunE,
|
||||
RunE: r.runE,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
fixDocs(name, c)
|
||||
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
|
||||
"also print resources from subpackages.")
|
||||
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", true,
|
||||
"annotate resources with their file origins.")
|
||||
c.Flags().BoolVarP(&r.InvertMatch, "invert-match", "v", false,
|
||||
" Selected Resources are those not matching any of the specified patterns..")
|
||||
|
||||
r.Command = c
|
||||
return r
|
||||
}
|
||||
|
||||
func GrepCommand(name string) *cobra.Command {
|
||||
return GetGrepRunner(name).Command
|
||||
}
|
||||
|
||||
// GrepRunner contains the run function
|
||||
type GrepRunner struct {
|
||||
IncludeSubpackages bool
|
||||
KeepAnnotations bool
|
||||
Command *cobra.Command
|
||||
filters.GrepFilter
|
||||
Format bool
|
||||
}
|
||||
|
||||
func (r *GrepRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
r.GrepFilter.Compare = func(a, b string) (int, error) {
|
||||
qa, err := resource.ParseQuantity(a)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%s: %v", a, err)
|
||||
}
|
||||
qb, err := resource.ParseQuantity(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return qa.Cmp(qb), err
|
||||
}
|
||||
parts, err := parseFieldPath(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var last []string
|
||||
if strings.Contains(parts[len(parts)-1], ">=") {
|
||||
last = strings.Split(parts[len(parts)-1], ">=")
|
||||
r.MatchType = filters.GreaterThanEq
|
||||
} else if strings.Contains(parts[len(parts)-1], "<=") {
|
||||
last = strings.Split(parts[len(parts)-1], "<=")
|
||||
r.MatchType = filters.LessThanEq
|
||||
} else if strings.Contains(parts[len(parts)-1], ">") {
|
||||
last = strings.Split(parts[len(parts)-1], ">")
|
||||
r.MatchType = filters.GreaterThan
|
||||
} else if strings.Contains(parts[len(parts)-1], "<") {
|
||||
last = strings.Split(parts[len(parts)-1], "<")
|
||||
r.MatchType = filters.LessThan
|
||||
} else {
|
||||
last = strings.Split(parts[len(parts)-1], "=")
|
||||
r.MatchType = filters.Regexp
|
||||
}
|
||||
if len(last) > 2 {
|
||||
return fmt.Errorf(
|
||||
"ambiguous match -- multiple of ['<', '>', '<=', '>=', '=' in final path element: %s",
|
||||
parts[len(parts)-1])
|
||||
}
|
||||
|
||||
if len(last) > 1 {
|
||||
r.Value = last[1]
|
||||
}
|
||||
|
||||
r.Path = append(parts[:len(parts)-1], last[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *GrepRunner) runE(c *cobra.Command, args []string) error {
|
||||
var filters = []kio.Filter{r.GrepFilter}
|
||||
|
||||
var inputs []kio.Reader
|
||||
for _, a := range args[1:] {
|
||||
inputs = append(inputs, kio.LocalPackageReader{
|
||||
PackagePath: a,
|
||||
IncludeSubpackages: r.IncludeSubpackages,
|
||||
})
|
||||
}
|
||||
if len(inputs) == 0 {
|
||||
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
|
||||
}
|
||||
|
||||
return handleError(c, kio.Pipeline{
|
||||
Inputs: inputs,
|
||||
Filters: filters,
|
||||
Outputs: []kio.Writer{kio.ByteWriter{
|
||||
Writer: c.OutOrStdout(),
|
||||
KeepReaderAnnotations: r.KeepAnnotations,
|
||||
}},
|
||||
}.Execute())
|
||||
}
|
||||
272
cmd/config/internal/commands/grep_test.go
Normal file
272
cmd/config/internal/commands/grep_test.go
Normal file
@@ -0,0 +1,272 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
)
|
||||
|
||||
// TestGrepCommand_files verifies grep reads the files and filters them
|
||||
func TestGrepCommand_files(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-kyaml-test")
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetGrepRunner("")
|
||||
r.Command.SetArgs([]string{"metadata.name=foo", d})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, `kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`, b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TestCmd_stdin verifies the grep command reads stdin if no files are provided
|
||||
func TestGrepCmd_stdin(t *testing.T) {
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetGrepRunner("")
|
||||
r.Command.SetArgs([]string{"metadata.name=foo"})
|
||||
r.Command.SetOut(b)
|
||||
r.Command.SetIn(bytes.NewBufferString(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
---
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`))
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, `kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: 0
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: 1
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`, b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TestGrepCmd_errInputs verifies the grep command errors on invalid matches
|
||||
func TestGrepCmd_errInputs(t *testing.T) {
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetGrepRunner("")
|
||||
r.Command.SetArgs([]string{"metadata.name=foo=bar"})
|
||||
r.Command.SetOut(b)
|
||||
r.Command.SetIn(bytes.NewBufferString(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
`))
|
||||
err := r.Command.Execute()
|
||||
if !assert.Error(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Contains(t, err.Error(), "ambiguous match")
|
||||
|
||||
// fmt the files
|
||||
b = &bytes.Buffer{}
|
||||
r = commands.GetGrepRunner("")
|
||||
r.Command.SetArgs([]string{"spec.template.spec.containers[a[b=c].image=foo"})
|
||||
r.Command.SetOut(b)
|
||||
r.Command.SetIn(bytes.NewBufferString(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
`))
|
||||
err = r.Command.Execute()
|
||||
if !assert.Error(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Contains(t, err.Error(), "unrecognized path element:")
|
||||
}
|
||||
|
||||
// TestGrepCommand_escapeDots verifies the grep command correctly escapes '\.' in inputs
|
||||
func TestGrepCommand_escapeDots(t *testing.T) {
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetGrepRunner("")
|
||||
r.Command.SetArgs([]string{"spec.template.spec.containers[name=nginx].image=nginx:1\\.7\\.9",
|
||||
"--annotate=false"})
|
||||
r.Command.SetOut(b)
|
||||
r.Command.SetIn(bytes.NewBufferString(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx1.8
|
||||
name: nginx1.8
|
||||
annotations:
|
||||
app: nginx1.8
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.8.1
|
||||
---
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx1.7
|
||||
name: nginx1.7
|
||||
annotations:
|
||||
app: nginx1.7
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
`))
|
||||
err := r.Command.Execute()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, `kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx1.7
|
||||
name: nginx1.7
|
||||
annotations:
|
||||
app: nginx1.7
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
`, b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
68
cmd/config/internal/commands/merge.go
Normal file
68
cmd/config/internal/commands/merge.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
)
|
||||
|
||||
func GetMergeRunner(name string) *MergeRunner {
|
||||
r := &MergeRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "merge [SOURCE_DIR...] [DESTINATION_DIR]",
|
||||
Short: commands.MergeShort,
|
||||
Long: commands.MergeLong,
|
||||
Example: commands.MergeExamples,
|
||||
RunE: r.runE,
|
||||
}
|
||||
fixDocs(name, c)
|
||||
r.Command = c
|
||||
r.Command.Flags().BoolVar(&r.InvertOrder, "invert-order", false,
|
||||
"if true, merge Resources in the reverse order")
|
||||
return r
|
||||
}
|
||||
|
||||
func MergeCommand(name string) *cobra.Command {
|
||||
return GetMergeRunner(name).Command
|
||||
}
|
||||
|
||||
// MergeRunner contains the run function
|
||||
type MergeRunner struct {
|
||||
Command *cobra.Command
|
||||
InvertOrder bool
|
||||
}
|
||||
|
||||
func (r *MergeRunner) runE(c *cobra.Command, args []string) error {
|
||||
var inputs []kio.Reader
|
||||
// add the packages in reverse order -- the arg list should be highest precedence first
|
||||
// e.g. merge from -> to, but the MergeFilter is highest precedence last
|
||||
for i := len(args) - 1; i >= 0; i-- {
|
||||
inputs = append(inputs, kio.LocalPackageReader{PackagePath: args[i]})
|
||||
}
|
||||
// if there is no "from" package, read from stdin
|
||||
rw := &kio.ByteReadWriter{
|
||||
Reader: c.InOrStdin(),
|
||||
Writer: c.OutOrStdout(),
|
||||
KeepReaderAnnotations: true,
|
||||
}
|
||||
if len(inputs) < 2 {
|
||||
inputs = append(inputs, rw)
|
||||
}
|
||||
|
||||
// write to the "to" package if specified
|
||||
var outputs []kio.Writer
|
||||
if len(args) != 0 {
|
||||
outputs = append(outputs, kio.LocalPackageWriter{PackagePath: args[len(args)-1]})
|
||||
}
|
||||
// if there is no "to" package, write to stdout
|
||||
if len(outputs) == 0 {
|
||||
outputs = append(outputs, rw)
|
||||
}
|
||||
|
||||
filters := []kio.Filter{filters.MergeFilter{}, filters.FormatFilter{}}
|
||||
return handleError(c, kio.Pipeline{Inputs: inputs, Filters: filters, Outputs: outputs}.Execute())
|
||||
}
|
||||
56
cmd/config/internal/commands/run-fns.go
Normal file
56
cmd/config/internal/commands/run-fns.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||
)
|
||||
|
||||
// GetCatRunner returns a RunFnRunner.
|
||||
func GetRunFnRunner(name string) *RunFnRunner {
|
||||
r := &RunFnRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "run DIR",
|
||||
Aliases: []string{"run-fns"},
|
||||
Short: commands.RunFnsShort,
|
||||
Long: commands.RunFnsLong,
|
||||
Example: commands.RunFnsExamples,
|
||||
RunE: r.runE,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
fixDocs(name, c)
|
||||
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
|
||||
"also print resources from subpackages.")
|
||||
r.Command = c
|
||||
r.Command.Flags().BoolVar(
|
||||
&r.DryRun, "dry-run", false, "print results to stdout")
|
||||
r.Command.Flags().StringSliceVar(
|
||||
&r.FnPaths, "fn-path", []string{},
|
||||
"directories containing functions without configuration")
|
||||
r.Command.AddCommand(XArgsCommand())
|
||||
r.Command.AddCommand(WrapCommand())
|
||||
return r
|
||||
}
|
||||
|
||||
func RunFnCommand(name string) *cobra.Command {
|
||||
return GetRunFnRunner(name).Command
|
||||
}
|
||||
|
||||
// RunFnRunner contains the run function
|
||||
type RunFnRunner struct {
|
||||
IncludeSubpackages bool
|
||||
Command *cobra.Command
|
||||
DryRun bool
|
||||
FnPaths []string
|
||||
}
|
||||
|
||||
func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
|
||||
rec := runfn.RunFns{Path: args[0], FunctionPaths: r.FnPaths}
|
||||
if r.DryRun {
|
||||
rec.Output = c.OutOrStdout()
|
||||
}
|
||||
return handleError(c, rec.Execute())
|
||||
}
|
||||
15
cmd/config/internal/commands/test/override.yaml
Normal file
15
cmd/config/internal/commands/test/override.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: test
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: test
|
||||
image: nginx:v1.9
|
||||
51
cmd/config/internal/commands/test/test.sh
Executable file
51
cmd/config/internal/commands/test/test.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
|
||||
|
||||
cat <<End-of-message
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ${NAME}
|
||||
labels:
|
||||
app: nginx
|
||||
name: ${NAME}
|
||||
spec:
|
||||
replicas: ${REPLICAS}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
name: ${NAME}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: ${NAME}
|
||||
spec:
|
||||
containers:
|
||||
- name: ${NAME}
|
||||
image: nginx:v1.7
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ${NAME}
|
||||
labels:
|
||||
app: nginx
|
||||
name: ${NAME}
|
||||
spec:
|
||||
ports:
|
||||
# This i the port.
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
name: http
|
||||
selector:
|
||||
app: nginx
|
||||
name: ${NAME}
|
||||
End-of-message
|
||||
187
cmd/config/internal/commands/tree.go
Normal file
187
cmd/config/internal/commands/tree.go
Normal file
@@ -0,0 +1,187 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func GetTreeRunner(name string) *TreeRunner {
|
||||
r := &TreeRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "tree DIR",
|
||||
Short: commands.TreeShort,
|
||||
Long: commands.TreeLong,
|
||||
Example: commands.TreeExamples,
|
||||
RunE: r.runE,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
}
|
||||
fixDocs(name, c)
|
||||
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
|
||||
"also print resources from subpackages.")
|
||||
|
||||
// TODO(pwittrock): Figure out if these are the right things to expose, and consider making it
|
||||
// a list of options instead of individual flags
|
||||
c.Flags().BoolVar(&r.name, "name", false, "print name field")
|
||||
c.Flags().BoolVar(&r.resources, "resources", false, "print resources field")
|
||||
c.Flags().BoolVar(&r.ports, "ports", false, "print ports field")
|
||||
c.Flags().BoolVar(&r.images, "image", false, "print image field")
|
||||
c.Flags().BoolVar(&r.replicas, "replicas", false, "print replicas field")
|
||||
c.Flags().BoolVar(&r.args, "args", false, "print args field")
|
||||
c.Flags().BoolVar(&r.cmd, "command", false, "print command field")
|
||||
c.Flags().BoolVar(&r.env, "env", false, "print env field")
|
||||
c.Flags().BoolVar(&r.all, "all", false, "print all field infos")
|
||||
c.Flags().StringSliceVar(&r.fields, "field", []string{}, "print field")
|
||||
c.Flags().BoolVar(&r.includeLocal, "include-local", false,
|
||||
"if true, include local-config in the output.")
|
||||
c.Flags().BoolVar(&r.excludeNonLocal, "exclude-non-local", false,
|
||||
"if true, exclude non-local-config in the output.")
|
||||
c.Flags().StringVar(&r.structure, "graph-structure", "directory",
|
||||
"Graph structure to use for printing the tree. may be any of: "+
|
||||
strings.Join(kio.GraphStructures, ","))
|
||||
|
||||
r.Command = c
|
||||
return r
|
||||
}
|
||||
|
||||
func TreeCommand(name string) *cobra.Command {
|
||||
return GetTreeRunner(name).Command
|
||||
}
|
||||
|
||||
// TreeRunner contains the run function
|
||||
type TreeRunner struct {
|
||||
IncludeSubpackages bool
|
||||
Command *cobra.Command
|
||||
name bool
|
||||
resources bool
|
||||
ports bool
|
||||
images bool
|
||||
replicas bool
|
||||
all bool
|
||||
env bool
|
||||
args bool
|
||||
cmd bool
|
||||
fields []string
|
||||
includeLocal bool
|
||||
excludeNonLocal bool
|
||||
structure string
|
||||
}
|
||||
|
||||
func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
|
||||
var input kio.Reader
|
||||
var root = "."
|
||||
if len(args) == 1 {
|
||||
root = filepath.Clean(args[0])
|
||||
input = kio.LocalPackageReader{PackagePath: args[0]}
|
||||
} else {
|
||||
input = &kio.ByteReader{Reader: c.InOrStdin()}
|
||||
}
|
||||
|
||||
var fields []kio.TreeWriterField
|
||||
for _, field := range r.fields {
|
||||
path, err := parseFieldPath(field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fields = append(fields, newField(path...))
|
||||
}
|
||||
|
||||
if r.name || (r.all && !c.Flag("name").Changed) {
|
||||
fields = append(fields,
|
||||
newField("spec", "containers", "[name=.*]", "name"),
|
||||
newField("spec", "template", "spec", "containers", "[name=.*]", "name"),
|
||||
)
|
||||
}
|
||||
if r.images || (r.all && !c.Flag("image").Changed) {
|
||||
fields = append(fields,
|
||||
newField("spec", "containers", "[name=.*]", "image"),
|
||||
newField("spec", "template", "spec", "containers", "[name=.*]", "image"),
|
||||
)
|
||||
}
|
||||
|
||||
if r.cmd || (r.all && !c.Flag("command").Changed) {
|
||||
fields = append(fields,
|
||||
newField("spec", "containers", "[name=.*]", "command"),
|
||||
newField("spec", "template", "spec", "containers", "[name=.*]", "command"),
|
||||
)
|
||||
}
|
||||
if r.args || (r.all && !c.Flag("args").Changed) {
|
||||
fields = append(fields,
|
||||
newField("spec", "containers", "[name=.*]", "args"),
|
||||
newField("spec", "template", "spec", "containers", "[name=.*]", "args"),
|
||||
)
|
||||
}
|
||||
if r.env || (r.all && !c.Flag("env").Changed) {
|
||||
fields = append(fields,
|
||||
newField("spec", "containers", "[name=.*]", "env"),
|
||||
newField("spec", "template", "spec", "containers", "[name=.*]", "env"),
|
||||
)
|
||||
}
|
||||
|
||||
if r.replicas || (r.all && !c.Flag("replicas").Changed) {
|
||||
fields = append(fields,
|
||||
newField("spec", "replicas"),
|
||||
)
|
||||
}
|
||||
if r.resources || (r.all && !c.Flag("resources").Changed) {
|
||||
fields = append(fields,
|
||||
newField("spec", "containers", "[name=.*]", "resources"),
|
||||
newField("spec", "template", "spec", "containers", "[name=.*]", "resources"),
|
||||
)
|
||||
}
|
||||
if r.ports || (r.all && !c.Flag("ports").Changed) {
|
||||
fields = append(fields,
|
||||
newField("spec", "containers", "[name=.*]", "ports"),
|
||||
newField("spec", "template", "spec", "containers", "[name=.*]", "ports"),
|
||||
newField("spec", "ports"),
|
||||
)
|
||||
}
|
||||
|
||||
// show reconcilers in tree
|
||||
fltrs := []kio.Filter{&filters.IsLocalConfig{
|
||||
IncludeLocalConfig: r.includeLocal,
|
||||
ExcludeNonLocalConfig: r.excludeNonLocal,
|
||||
}}
|
||||
|
||||
return handleError(c, kio.Pipeline{
|
||||
Inputs: []kio.Reader{input},
|
||||
Filters: fltrs,
|
||||
Outputs: []kio.Writer{kio.TreeWriter{
|
||||
Root: root,
|
||||
Writer: c.OutOrStdout(),
|
||||
Fields: fields,
|
||||
Structure: kio.TreeStructure(r.structure)}},
|
||||
}.Execute())
|
||||
}
|
||||
|
||||
func newField(val ...string) kio.TreeWriterField {
|
||||
if strings.HasPrefix(strings.Join(val, "."), "spec.template.spec.containers") {
|
||||
return kio.TreeWriterField{
|
||||
Name: "spec.template.spec.containers",
|
||||
PathMatcher: yaml.PathMatcher{Path: val, StripComments: true},
|
||||
SubName: val[len(val)-1],
|
||||
}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(strings.Join(val, "."), "spec.containers") {
|
||||
return kio.TreeWriterField{
|
||||
Name: "spec.containers",
|
||||
PathMatcher: yaml.PathMatcher{Path: val, StripComments: true},
|
||||
SubName: val[len(val)-1],
|
||||
}
|
||||
}
|
||||
|
||||
return kio.TreeWriterField{
|
||||
Name: strings.Join(val, "."),
|
||||
PathMatcher: yaml.PathMatcher{Path: val, StripComments: true},
|
||||
}
|
||||
}
|
||||
355
cmd/config/internal/commands/tree_test.go
Normal file
355
cmd/config/internal/commands/tree_test.go
Normal file
@@ -0,0 +1,355 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
)
|
||||
|
||||
// TestCmd_files verifies fmt reads the files and filters them
|
||||
func TestTreeCommand_files(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-tree-test")
|
||||
defer os.RemoveAll(d)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetTreeRunner("")
|
||||
r.Command.SetArgs([]string{d})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, fmt.Sprintf(`%s
|
||||
├── [f1.yaml] Deployment foo
|
||||
├── [f1.yaml] Service foo
|
||||
└── [f2.yaml] Deployment bar
|
||||
`, d), b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeCommand_stdin(t *testing.T) {
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetTreeRunner("")
|
||||
r.Command.SetArgs([]string{})
|
||||
r.Command.SetIn(bytes.NewBufferString(`apiVersion: extensions/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo3
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
apiVersion: extensions/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo3
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo3
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo2
|
||||
namespace: default2
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx3
|
||||
name: foo
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx3
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: bar-package
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
name: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`))
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, fmt.Sprintf(`.
|
||||
├── [f1.yaml] Deployment default/foo
|
||||
├── [f1.yaml] Service default/foo
|
||||
├── [f1.yaml] Deployment default/foo3
|
||||
├── [f1.yaml] Deployment default/foo3
|
||||
├── [f1.yaml] Deployment default/foo3
|
||||
├── [f1.yaml] Deployment default2/foo2
|
||||
└── bar-package
|
||||
└── [f2.yaml] Deployment bar
|
||||
`), b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeCommand_includeReconcilers(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-tree-test")
|
||||
defer os.RemoveAll(d)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: gcr.io/example/reconciler:v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetTreeRunner("")
|
||||
r.Command.SetArgs([]string{d, "--include-local"})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, fmt.Sprintf(`%s
|
||||
├── [f1.yaml] Deployment foo
|
||||
├── [f1.yaml] Service foo
|
||||
├── [f2.yaml] Deployment bar
|
||||
└── [f2.yaml] Abstraction foo
|
||||
`, d), b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestTreeCommand_excludeNonReconcilers(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustmoize-tree-test")
|
||||
defer os.RemoveAll(d)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(filepath.Join(d, "f2.yaml"), []byte(`
|
||||
apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
configFn:
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
annotations:
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetTreeRunner("")
|
||||
r.Command.SetArgs([]string{d, "--include-local", "--exclude-non-local"})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, fmt.Sprintf(`%s
|
||||
└── [f2.yaml] Abstraction foo
|
||||
`, d), b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
74
cmd/config/internal/commands/util.go
Normal file
74
cmd/config/internal/commands/util.go
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// parseFieldPath parse a flag value into a field path
|
||||
func parseFieldPath(path string) ([]string, error) {
|
||||
// fixup '\.' so we don't split on it
|
||||
match := strings.ReplaceAll(path, "\\.", "$$$$")
|
||||
parts := strings.Split(match, ".")
|
||||
for i := range parts {
|
||||
parts[i] = strings.ReplaceAll(parts[i], "$$$$", ".")
|
||||
}
|
||||
|
||||
// split the list index from the list field
|
||||
var newParts []string
|
||||
for i := range parts {
|
||||
if !strings.Contains(parts[i], "[") {
|
||||
newParts = append(newParts, parts[i])
|
||||
continue
|
||||
}
|
||||
p := strings.Split(parts[i], "[")
|
||||
if len(p) != 2 {
|
||||
return nil, fmt.Errorf("unrecognized path element: %s. "+
|
||||
"Should be of the form 'list[field=value]'", parts[i])
|
||||
}
|
||||
p[1] = "[" + p[1]
|
||||
newParts = append(newParts, p[0], p[1])
|
||||
}
|
||||
return newParts, nil
|
||||
}
|
||||
|
||||
func handleError(c *cobra.Command, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if StackOnError {
|
||||
if err, ok := err.(*errors.Error); ok {
|
||||
fmt.Fprint(os.Stderr, fmt.Sprintf("%s", err.Stack()))
|
||||
}
|
||||
}
|
||||
|
||||
if ExitOnError {
|
||||
fmt.Fprintf(c.ErrOrStderr(), "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ExitOnError if true, will cause commands to call os.Exit instead of returning an error.
|
||||
// Used for skipping printing usage on failure.
|
||||
var ExitOnError bool
|
||||
|
||||
// StackOnError if true, will print a stack trace on failure.
|
||||
var StackOnError bool
|
||||
|
||||
const cmdName = "kustomize config"
|
||||
|
||||
// FixDocs replaces instances of old with new in the docs for c
|
||||
func fixDocs(new string, c *cobra.Command) {
|
||||
c.Use = strings.ReplaceAll(c.Use, cmdName, new)
|
||||
c.Short = strings.ReplaceAll(c.Short, cmdName, new)
|
||||
c.Long = strings.ReplaceAll(c.Long, cmdName, new)
|
||||
c.Example = strings.ReplaceAll(c.Example, cmdName, new)
|
||||
}
|
||||
Reference in New Issue
Block a user