This commit is contained in:
Jonathan Wong
2019-11-21 11:55:24 -08:00
40 changed files with 220 additions and 148 deletions

54
cmd/config/.golangci.yml Normal file
View File

@@ -0,0 +1,54 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
run:
deadline: 5m
linters:
# please, do not use `enable-all`: it's deprecated and will be removed soon.
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
enable:
- bodyclose
- deadcode
- depguard
- dogsled
- dupl
# - errcheck
# - funlen
# - gochecknoinits
# - goconst
# - gocritic
- gocyclo
- gofmt
- goimports
# - golint
- gosec
- gosimple
- govet
- ineffassign
- interfacer
# - lll
- misspell
- nakedret
# - scopelint
- staticcheck
- structcheck
# - stylecheck
- typecheck
- unconvert
# - unparam
- unused
- varcheck
# - whitespace
linters-settings:
dupl:
threshold: 400
lll:
line-length: 170
gocyclo:
min-complexity: 30
golint:
min-confidence: 0.85

View File

@@ -0,0 +1,2 @@
Copyright {{.Year}} {{.Holder}}
SPDX-License-Identifier: Apache-2.0

38
cmd/config/Makefile Normal file
View File

@@ -0,0 +1,38 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
.PHONY: generate license fix vet fmt test build tidy
GOPATH := $(shell go env GOPATH)
build:
go build -v -o $(GOPATH)/bin/kyaml .
all: generate license fix vet fmt test lint tidy
fix:
go fix ./...
fmt:
go fmt ./...
generate:
go generate ./...
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
go mod tidy
lint:
(which $(GOPATH)/bin/golangci-lint || go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1)
$(GOPATH)/bin/golangci-lint run ./...
test:
go test -cover ./...
vet:
go vet ./...

4
cmd/config/README.md Normal file
View File

@@ -0,0 +1,4 @@
# cmd/config
This package exists to expose kyaml filters directly as cli commands for the purposes
of development of the kyaml package and as a reference implementation for using the libraries.

130
cmd/config/cmd/cat.go Normal file
View File

@@ -0,0 +1,130 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"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() *CatRunner {
r := &CatRunner{}
c := &cobra.Command{
Use: "cat DIR...",
Short: "Print Resource Config from a local directory",
Long: `Print Resource Config from a local directory.
DIR:
Path to local directory.
`,
Example: `# print Resource config from a directory
kyaml cat my-dir/
# wrap Resource config from a directory in an ResourceList
kyaml cat my-dir/ --wrap-kind ResourceList --wrap-version config.kubernetes.io/v1alpha1 --function-config fn.yaml
# unwrap Resource config from a directory in an ResourceList
... | kyaml cat
`,
RunE: r.runE,
}
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.")
r.Command = c
return r
}
func CatCommand() *cobra.Command {
return GetCatRunner().Command
}
// CatRunner contains the run function
type CatRunner struct {
IncludeSubpackages bool
Format bool
KeepAnnotations bool
WrapKind string
WrapApiVersion string
FunctionConfig 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 outputs []kio.Writer
outputs = append(outputs, kio.ByteWriter{
Writer: c.OutOrStdout(),
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())
}

327
cmd/config/cmd/cat_test.go Normal file
View File

@@ -0,0 +1,327 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
)
// 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 := cmd.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 := cmd.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 := cmd.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
}
}

143
cmd/config/cmd/cmdwrap.go Normal file
View File

@@ -0,0 +1,143 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
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()
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,307 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
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/cmd/cmdxargs.go Normal file
View File

@@ -0,0 +1,226 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
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-fns 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 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
}

View File

@@ -0,0 +1,117 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
)
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 := cmd.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 := cmd.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 := cmd.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")
}

92
cmd/config/cmd/count.go Normal file
View File

@@ -0,0 +1,92 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//
package cmd
import (
"fmt"
"sort"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/sets"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func GetCountRunner() *CountRunner {
r := &CountRunner{}
c := &cobra.Command{
Use: "count DIR...",
Short: "Count Resources Config from a local directory",
Long: `Count Resources Config from a local directory.
DIR:
Path to local directory.
`,
Example: `# print Resource counts from a directory
kyaml count my-dir/
`,
RunE: r.runE,
}
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() *cobra.Command {
return GetCountRunner().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())
}

View File

@@ -0,0 +1,73 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
)
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 := cmd.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
}
}

58
cmd/config/cmd/docs.go Normal file
View File

@@ -0,0 +1,58 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
func GetDocsRunner() *DocsRunner {
r := &DocsRunner{}
c := &cobra.Command{
Use: "docs [API_TYPE]",
Short: "Print out documentation for API resource",
Long: `Print out documentation for API resource
The Docs command reads a JSON schema from the Kustomize API and outputs a pretiffied version of the documentation.
TODO:
<INSERT MORE DOCUMENTATION HERE>
For information on merge rules, run:
kyaml help docs
`,
Example: `kyaml docs kustomization`,
PreRunE: r.preRunE,
RunE: r.runE,
}
r.Command = c
r.Command.Flags().StringVar(&r.apiType, "api-type", "",
"API type to print out")
return r
}
// DocsCommand ...
func DocsCommand() *cobra.Command {
return GetDocsRunner().Command
}
// DocsRunner contains the run function
type DocsRunner struct {
Command *cobra.Command
apiType string
}
func (r *DocsRunner) preRunE(c *cobra.Command, args []string) error {
fmt.Println("Docs pre run. :)")
return nil
}
func (r *DocsRunner) runE(c *cobra.Command, args []string) error {
fmt.Println("Docs actual run. :-)")
fmt.Println(args[0])
return nil
}

127
cmd/config/cmd/fmt.go Normal file
View File

@@ -0,0 +1,127 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
)
// FmtCmd returns a command FmtRunner.
func GetFmtRunner() *FmtRunner {
r := &FmtRunner{}
c := &cobra.Command{
Use: "fmt",
Short: "Format yaml configuration files",
Long: `Format yaml configuration files
Fmt will format input by ordering fields and unordered list items in Kubernetes
objects. Inputs may be directories, files or stdin, and their contents must
include both apiVersion and kind fields.
- Stdin inputs are formatted and written to stdout
- File inputs (args) are formatted and written back to the file
- Directory inputs (args) are walked, each encountered .yaml and .yml file
acts as an input
For inputs which contain multiple yaml documents separated by \n---\n,
each document will be formatted and written back to the file in the original
order.
Field ordering roughly follows the ordering defined in the source Kubernetes
resource definitions (i.e. go structures), falling back on lexicographical
sorting for unrecognized fields.
Unordered list item ordering is defined for specific Resource types and
field paths.
- .spec.template.spec.containers (by element name)
- .webhooks.rules.operations (by element value)
`,
Example: `
# format file1.yaml and file2.yml
kyaml fmt file1.yaml file2.yml
# format all *.yaml and *.yml recursively traversing directories
kyaml fmt my-dir/
# format kubectl output
kubectl get -o yaml deployments | kyaml fmt
# format kustomize output
kustomize build | kyaml fmt
`,
RunE: r.runE,
PreRunE: r.preRunE,
}
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() *cobra.Command {
return GetFmtRunner().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/cmd/fmt_test.go Normal file
View File

@@ -0,0 +1,162 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
"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 := cmd.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 := cmd.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 := cmd.GetFmtRunner()
r.Command.SetOut(out)
r.Command.SetIn(in)
// fmt the files
r = cmd.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 := cmd.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 := cmd.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")
}

144
cmd/config/cmd/grep.go Normal file
View File

@@ -0,0 +1,144 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//
package cmd
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/api/resource"
)
// Cmd returns a command GrepRunner.
func GetGrepRunner() *GrepRunner {
r := &GrepRunner{}
c := &cobra.Command{
Use: "grep QUERY [DIR]...",
Short: "Search for matching Resources in a directory or from stdin",
Long: `Search for matching Resources in a directory or from stdin.
QUERY:
Query to match expressed as 'path.to.field=value'.
Maps and fields are matched as '.field-name' or '.map-key'
List elements are matched as '[list-elem-field=field-value]'
The value to match is expressed as '=value'
'.' as part of a key or value can be escaped as '\.'
DIR:
Path to local directory.
`,
Example: `# find Deployment Resources
kyaml grep "kind=Deployment" my-dir/
# find Resources named nginx
kyaml grep "metadata.name=nginx" my-dir/
# use tree to display matching Resources
kyaml grep "metadata.name=nginx" my-dir/ | kyaml tree
# look for Resources matching a specific container image
kyaml grep "spec.template.spec.containers[name=nginx].image=nginx:1\.7\.9" my-dir/ | kyaml tree
`,
PreRunE: r.preRunE,
RunE: r.runE,
Args: cobra.MinimumNArgs(1),
}
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() *cobra.Command {
return GetGrepRunner().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/cmd/grep_test.go Normal file
View File

@@ -0,0 +1,272 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
)
// 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 := cmd.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 := cmd.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 := cmd.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 = cmd.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 := cmd.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
}
}

80
cmd/config/cmd/merge.go Normal file
View File

@@ -0,0 +1,80 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
)
func GetMergeRunner() *MergeRunner {
r := &MergeRunner{}
c := &cobra.Command{
Use: "merge [SOURCE_DIR...] [DESTINATION_DIR]",
Short: "Merge Resource configuration files",
Long: `Merge Resource configuration files
Merge reads Kubernetes Resource yaml configuration files from stdin or sources packages and write
the result to stdout or a destination package.
Resources are merged using the Resource [apiVersion, kind, name, namespace] as the key. If any of
these are missing, merge will default the missing values to empty.
Resources specified later are high-precedence (the source) and Resources specified
earlier are lower-precedence (the destination).
For information on merge rules, run:
kyaml help merge
`,
Example: `cat resources_and_patches.yaml | kyaml merge > merged_resources.yaml`,
RunE: r.runE,
}
r.Command = c
r.Command.Flags().BoolVar(&r.InvertOrder, "invert-order", false,
"if true, merge Resources in the reverse order")
return r
}
func MergeCommand() *cobra.Command {
return GetMergeRunner().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())
}

98
cmd/config/cmd/run-fns.go Normal file
View File

@@ -0,0 +1,98 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kyaml/runfn"
)
// GetCatRunner returns a RunFnRunner.
func GetRunFnRunner() *RunFnRunner {
r := &RunFnRunner{}
c := &cobra.Command{
Use: "run-fns DIR",
Short: "Apply config functions to Resources.",
Long: `Apply config functions to Resources.
run-fns sequentially invokes all config functions in the directly, providing Resources
in the directory as input to the first function, and writing the output of the last
function back to the directory.
The ordering of functions is determined by the order they are encountered when walking the
directory. To clearly specify an ordering of functions, multiple functions may be
declared in the same file, separated by '---' (the functions will be invoked in the
order they appear in the file).
### Arguments:
DIR:
Path to local directory.
### Config Functions:
Config functions are specified as Kubernetes types containing a metadata.configFn.container.image
field. This fields tells run-fns how to invoke the container.
Example config function:
# in file example/fn.yaml
apiVersion: fn.example.com/v1beta1
kind: ExampleFunctionKind
metadata:
configFn:
container:
# function is invoked as a container running this image
image: gcr.io/example/examplefunction:v1.0.1
annotations:
config.kubernetes.io/local-config: "true" # tools should ignore this
spec:
configField: configValue
In the preceding example, 'kyaml run-fns example/' would identify the function by
the metadata.configFn field. It would then write all Resources in the directory to
a container stdin (running the gcr.io/example/examplefunction:v1.0.1 image). It
would then writer the container stdout back to example/, replacing the directory
file contents.
`,
Example: `
kyaml run-fns example/
`,
RunE: r.runE,
Args: cobra.ExactArgs(1),
}
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() *cobra.Command {
return GetRunFnRunner().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 rec.Execute()
}

View 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/cmd/test/test.sh Executable file
View 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

229
cmd/config/cmd/tree.go Normal file
View File

@@ -0,0 +1,229 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd
import (
"path/filepath"
"strings"
"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() *TreeRunner {
r := &TreeRunner{}
c := &cobra.Command{
Use: "tree DIR",
Short: "Display Resource structure from a directory or stdin",
Long: `Display Resource structure from a directory or stdin.
kyaml tree may be used to print Resources in a directory or cluster, preserving structure
Args:
DIR:
Path to local directory directory.
Resource fields may be printed as part of the Resources by specifying the fields as flags.
kyaml tree has build-in support for printing common fields, such as replicas, container images,
container names, etc.
kyaml tree supports printing arbitrary fields using the '--field' flag.
By default, kyaml tree uses the directory structure for the tree structure, however when printing
from the cluster, the Resource graph structure may be used instead.
`,
Example: `# print Resources using directory structure
kyaml tree my-dir/
# print replicas, container name, and container image and fields for Resources
kyaml tree my-dir --replicas --image --name
# print all common Resource fields
kyaml tree my-dir/ --all
# print the "foo"" annotation
kyaml tree my-dir/ --field "metadata.annotations.foo"
# print the "foo"" annotation
kubectl get all -o yaml | kyaml tree my-dir/ --structure=graph \
--field="status.conditions[type=Completed].status"
# print live Resources from a cluster using graph for structure
kubectl get all -o yaml | kyaml tree --replicas --name --image --structure=graph
# print live Resources using graph for structure
kubectl get all,applications,releasetracks -o yaml | kyaml tree --structure=graph \
--name --image --replicas \
--field="status.conditions[type=Completed].status" \
--field="status.conditions[type=Complete].status" \
--field="status.conditions[type=Ready].status" \
--field="status.conditions[type=ContainersReady].status"
`,
RunE: r.runE,
Args: cobra.MaximumNArgs(1),
}
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 'directory' or 'owners'.")
r.Command = c
return r
}
func TreeCommand() *cobra.Command {
return GetTreeRunner().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/cmd/tree_test.go Normal file
View File

@@ -0,0 +1,355 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package cmd_test
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/cmd"
)
// 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 := cmd.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 := cmd.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 := cmd.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 := cmd.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
}
}

64
cmd/config/cmd/util.go Normal file
View File

@@ -0,0 +1,64 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
//
package cmd
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

17
cmd/config/go.mod Normal file
View File

@@ -0,0 +1,17 @@
module sigs.k8s.io/kustomize/cmd/config
go 1.13
require (
github.com/go-errors/errors v1.0.1
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.4.0
sigs.k8s.io/kustomize/kyaml v0.0.0
sigs.k8s.io/kustomize/pseudo/k8s v0.0.0
)
replace (
sigs.k8s.io/kustomize/kyaml v0.0.0 => ../../kyaml
sigs.k8s.io/kustomize/pseudo/k8s v0.0.0 => ../../pseudo/k8s
)

147
cmd/config/go.sum Normal file
View File

@@ -0,0 +1,147 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.2/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d h1:LCPbGQ34PMrwad11aMZ+dbz5SAsq/0ySjRwQ8I9Qwd8=
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -0,0 +1,42 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package inpututil
import (
"sigs.k8s.io/kustomize/kyaml/yaml"
)
type MapInputsEFn func(*yaml.RNode, yaml.ResourceMeta) error
func MapInputsE(inputs []*yaml.RNode, fn MapInputsEFn) error {
for i := range inputs {
meta, err := inputs[i].GetMeta()
if err != nil {
return err
}
if err := fn(inputs[i], meta); err != nil {
return err
}
}
return nil
}
type MapInputsFn func(*yaml.RNode, yaml.ResourceMeta) ([]*yaml.RNode, error)
// runs the function against each input Resource, providing the parsed metadata
func MapInputs(inputs []*yaml.RNode, fn MapInputsFn) ([]*yaml.RNode, error) {
var outputs []*yaml.RNode
for i := range inputs {
meta, err := inputs[i].GetMeta()
if err != nil {
return nil, err
}
o, err := fn(inputs[i], meta)
if err != nil {
return nil, err
}
outputs = append(outputs, o...)
}
return outputs, nil
}

42
cmd/config/main.go Normal file
View File

@@ -0,0 +1,42 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"os"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/cmd"
"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
"sigs.k8s.io/kustomize/kyaml/yaml/merge3"
)
var root = &cobra.Command{
Use: "kyaml",
Short: "kyaml reference comand",
Long: `Description:
Reference implementation for using the kyaml libraries.
`,
Example: ``,
}
func main() {
root.PersistentFlags().BoolVar(&cmd.StackOnError, "stack-trace", false,
"print a stack-trace on failure")
cmd.ExitOnError = true
root.AddCommand(cmd.GrepCommand())
root.AddCommand(cmd.TreeCommand())
root.AddCommand(cmd.CatCommand())
root.AddCommand(cmd.FmtCommand())
root.AddCommand(cmd.MergeCommand())
root.AddCommand(cmd.CountCommand())
root.AddCommand(cmd.RunFnCommand())
root.AddCommand(&cobra.Command{Use: "merge", Long: merge2.Help})
root.AddCommand(&cobra.Command{Use: "merge3", Long: merge3.Help})
if err := root.Execute(); err != nil {
os.Exit(1)
}
}