diff --git a/cmd/config/Makefile b/cmd/config/Makefile index 847f195cb..be9f2f345 100644 --- a/cmd/config/Makefile +++ b/cmd/config/Makefile @@ -8,7 +8,7 @@ GOBIN := $(shell go env GOPATH)/bin build: go build -v -o $(GOBIN)/config . -all: generate license fix vet fmt test lint tidy +all: generate build license fix vet fmt test lint tidy fix: go fix ./... @@ -17,6 +17,7 @@ fmt: go fmt ./... generate: + (which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/mdtogo) GOBIN=$(GOBIN) go generate ./... license: diff --git a/cmd/config/README.md b/cmd/config/README.md index b5984c93b..2dac84ef1 100644 --- a/cmd/config/README.md +++ b/cmd/config/README.md @@ -1,4 +1,21 @@ # cmd/config -This package exists to expose kyaml filters directly as cli commands for the purposes +This package exists to expose config filters directly as cli commands for the purposes of development of the kyaml package and as a reference implementation for using the libraries. + +## Docs: + +- [commands](docs/commands) +- [api-conventions](docs/api-conventions) + +## Build Command + +Build the `config` command under `GOBIN` + + $ make + +## Run Tests + +Run all tests, linting, etc and build + + $ make all \ No newline at end of file diff --git a/cmd/config/cmd/cat.go b/cmd/config/cmd/cat.go index b2ce1175b..2d2138416 100644 --- a/cmd/config/cmd/cat.go +++ b/cmd/config/cmd/cat.go @@ -7,33 +7,23 @@ import ( "fmt" "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/cmd/config/cmddocs/commands" "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 { +func GetCatRunner(name string) *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, + 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, @@ -59,8 +49,8 @@ kyaml cat my-dir/ --wrap-kind ResourceList --wrap-version config.kubernetes.io/v return r } -func CatCommand() *cobra.Command { - return GetCatRunner().Command +func CatCommand(name string) *cobra.Command { + return GetCatRunner(name).Command } // CatRunner contains the run function diff --git a/cmd/config/cmd/cat_test.go b/cmd/config/cmd/cat_test.go index 4fad23229..fd4786c16 100644 --- a/cmd/config/cmd/cat_test.go +++ b/cmd/config/cmd/cat_test.go @@ -76,7 +76,7 @@ spec: // fmt the files b := &bytes.Buffer{} - r := cmd.GetCatRunner() + r := cmd.GetCatRunner("") r.Command.SetArgs([]string{d}) r.Command.SetOut(b) if !assert.NoError(t, r.Command.Execute()) { @@ -182,7 +182,7 @@ spec: // fmt the files b := &bytes.Buffer{} - r := cmd.GetCatRunner() + r := cmd.GetCatRunner("") r.Command.SetArgs([]string{d, "--include-local"}) r.Command.SetOut(b) if !assert.NoError(t, r.Command.Execute()) { @@ -301,7 +301,7 @@ spec: // fmt the files b := &bytes.Buffer{} - r := cmd.GetCatRunner() + r := cmd.GetCatRunner("") r.Command.SetArgs([]string{d, "--include-local", "--exclude-non-local"}) r.Command.SetOut(b) if !assert.NoError(t, r.Command.Execute()) { diff --git a/cmd/config/cmd/count.go b/cmd/config/cmd/count.go index 9ff1160ed..2910de11a 100644 --- a/cmd/config/cmd/count.go +++ b/cmd/config/cmd/count.go @@ -8,26 +8,22 @@ import ( "sort" "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/cmd/config/cmddocs/commands" "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/sets" "sigs.k8s.io/kustomize/kyaml/yaml" ) -func GetCountRunner() *CountRunner { +func GetCountRunner(name string) *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, + 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, @@ -37,8 +33,8 @@ kyaml count my-dir/ return r } -func CountCommand() *cobra.Command { - return GetCountRunner().Command +func CountCommand(name string) *cobra.Command { + return GetCountRunner(name).Command } // CountRunner contains the run function diff --git a/cmd/config/cmd/count_test.go b/cmd/config/cmd/count_test.go index 9ee08448e..0643f5f68 100644 --- a/cmd/config/cmd/count_test.go +++ b/cmd/config/cmd/count_test.go @@ -60,7 +60,7 @@ spec: // fmt the files b := &bytes.Buffer{} - r := cmd.GetCountRunner() + r := cmd.GetCountRunner("") r.Command.SetArgs([]string{d}) r.Command.SetOut(b) if !assert.NoError(t, r.Command.Execute()) { diff --git a/cmd/config/cmd/fmt.go b/cmd/config/cmd/fmt.go index 0afdf0b85..1090f9908 100644 --- a/cmd/config/cmd/fmt.go +++ b/cmd/config/cmd/fmt.go @@ -5,57 +5,23 @@ package cmd import ( "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/cmd/config/cmddocs/commands" "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio/filters" ) // FmtCmd returns a command FmtRunner. -func GetFmtRunner() *FmtRunner { +func GetFmtRunner(name string) *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 -`, + 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'}`) @@ -69,8 +35,8 @@ formatting substitution verbs {'%n': 'metadata.name', '%s': 'metadata.namespace' return r } -func FmtCommand() *cobra.Command { - return GetFmtRunner().Command +func FmtCommand(name string) *cobra.Command { + return GetFmtRunner(name).Command } // FmtRunner contains the run function diff --git a/cmd/config/cmd/fmt_test.go b/cmd/config/cmd/fmt_test.go index 5ad7f82d4..3120b4435 100644 --- a/cmd/config/cmd/fmt_test.go +++ b/cmd/config/cmd/fmt_test.go @@ -38,7 +38,7 @@ func TestFmtCommand_files(t *testing.T) { } // fmt the files - r := cmd.GetFmtRunner() + r := cmd.GetFmtRunner("") r.Command.SetArgs([]string{f1.Name(), f2.Name()}) err = r.Command.Execute() if !assert.NoError(t, err) { @@ -65,7 +65,7 @@ func TestFmtCommand_files(t *testing.T) { func TestFmtCommand_stdin(t *testing.T) { out := &bytes.Buffer{} - r := cmd.GetFmtRunner() + r := cmd.GetFmtRunner("") r.Command.SetOut(out) r.Command.SetIn(bytes.NewReader(testyaml.UnformattedYaml1)) @@ -100,12 +100,12 @@ func TestFmtCmd_filesAndStdin(t *testing.T) { out := &bytes.Buffer{} in := &bytes.Buffer{} - r := cmd.GetFmtRunner() + r := cmd.GetFmtRunner("") r.Command.SetOut(out) r.Command.SetIn(in) // fmt the files - r = cmd.GetFmtRunner() + r = cmd.GetFmtRunner("") r.Command.SetArgs([]string{f1.Name(), f2.Name()}) err = r.Command.Execute() if !assert.NoError(t, err) { @@ -141,7 +141,7 @@ func TestFmtCmd_filesAndStdin(t *testing.T) { // TestCmd_files verifies the fmt command formats the files func TestCmd_failFiles(t *testing.T) { // fmt the files - r := cmd.GetFmtRunner() + r := cmd.GetFmtRunner("") r.Command.SetArgs([]string{"notrealfile"}) err := r.Command.Execute() assert.EqualError(t, err, "lstat notrealfile: no such file or directory") @@ -150,7 +150,7 @@ func TestCmd_failFiles(t *testing.T) { // TestCmd_files verifies the fmt command formats the files func TestCmd_failFileContents(t *testing.T) { out := &bytes.Buffer{} - r := cmd.GetFmtRunner() + r := cmd.GetFmtRunner("") r.Command.SetOut(out) r.Command.SetIn(strings.NewReader(`{`)) diff --git a/cmd/config/cmd/grep.go b/cmd/config/cmd/grep.go index 43e38b5b7..50076fa39 100644 --- a/cmd/config/cmd/grep.go +++ b/cmd/config/cmd/grep.go @@ -8,44 +8,25 @@ import ( "strings" "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/cmd/config/cmddocs/commands" "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 { +func GetGrepRunner(name string) *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 -`, + 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, @@ -57,8 +38,8 @@ kyaml grep "spec.template.spec.containers[name=nginx].image=nginx:1\.7\.9" my-di return r } -func GrepCommand() *cobra.Command { - return GetGrepRunner().Command +func GrepCommand(name string) *cobra.Command { + return GetGrepRunner(name).Command } // GrepRunner contains the run function diff --git a/cmd/config/cmd/grep_test.go b/cmd/config/cmd/grep_test.go index 78a6cce5c..35b92ce27 100644 --- a/cmd/config/cmd/grep_test.go +++ b/cmd/config/cmd/grep_test.go @@ -61,7 +61,7 @@ spec: // fmt the files b := &bytes.Buffer{} - r := cmd.GetGrepRunner() + r := cmd.GetGrepRunner("") r.Command.SetArgs([]string{"metadata.name=foo", d}) r.Command.SetOut(b) if !assert.NoError(t, r.Command.Execute()) { @@ -101,7 +101,7 @@ spec: func TestGrepCmd_stdin(t *testing.T) { // fmt the files b := &bytes.Buffer{} - r := cmd.GetGrepRunner() + r := cmd.GetGrepRunner("") r.Command.SetArgs([]string{"metadata.name=foo"}) r.Command.SetOut(b) r.Command.SetIn(bytes.NewBufferString(` @@ -166,7 +166,7 @@ spec: // TestGrepCmd_errInputs verifies the grep command errors on invalid matches func TestGrepCmd_errInputs(t *testing.T) { b := &bytes.Buffer{} - r := cmd.GetGrepRunner() + r := cmd.GetGrepRunner("") r.Command.SetArgs([]string{"metadata.name=foo=bar"}) r.Command.SetOut(b) r.Command.SetIn(bytes.NewBufferString(` @@ -188,7 +188,7 @@ spec: // fmt the files b = &bytes.Buffer{} - r = cmd.GetGrepRunner() + 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(` @@ -213,7 +213,7 @@ spec: func TestGrepCommand_escapeDots(t *testing.T) { // fmt the files b := &bytes.Buffer{} - r := cmd.GetGrepRunner() + r := cmd.GetGrepRunner("") r.Command.SetArgs([]string{"spec.template.spec.containers[name=nginx].image=nginx:1\\.7\\.9", "--annotate=false"}) r.Command.SetOut(b) diff --git a/cmd/config/cmd/merge.go b/cmd/config/cmd/merge.go index d1c4c76ad..413de9c82 100644 --- a/cmd/config/cmd/merge.go +++ b/cmd/config/cmd/merge.go @@ -5,41 +5,29 @@ package cmd import ( "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/cmd/config/cmddocs/commands" "sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio/filters" ) -func GetMergeRunner() *MergeRunner { +func GetMergeRunner(name string) *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`, + 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() *cobra.Command { - return GetMergeRunner().Command +func MergeCommand(name string) *cobra.Command { + return GetMergeRunner(name).Command } // MergeRunner contains the run function diff --git a/cmd/config/cmd/run-fns.go b/cmd/config/cmd/run-fns.go index 94431dac8..19f5eff25 100644 --- a/cmd/config/cmd/run-fns.go +++ b/cmd/config/cmd/run-fns.go @@ -5,64 +5,22 @@ package cmd import ( "github.com/spf13/cobra" + "sigs.k8s.io/kustomize/cmd/config/cmddocs/commands" "sigs.k8s.io/kustomize/kyaml/runfn" ) // GetCatRunner returns a RunFnRunner. -func GetRunFnRunner() *RunFnRunner { +func GetRunFnRunner(name string) *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), + Use: "run-fns DIR", + 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 @@ -76,8 +34,8 @@ kyaml run-fns example/ return r } -func RunFnCommand() *cobra.Command { - return GetRunFnRunner().Command +func RunFnCommand(name string) *cobra.Command { + return GetRunFnRunner(name).Command } // RunFnRunner contains the run function diff --git a/cmd/config/cmd/tree.go b/cmd/config/cmd/tree.go index b86558069..284c8ad46 100644 --- a/cmd/config/cmd/tree.go +++ b/cmd/config/cmd/tree.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "sigs.k8s.io/kustomize/cmd/config/cmddocs/commands" "sigs.k8s.io/kustomize/kyaml/kio/filters" "github.com/spf13/cobra" @@ -14,61 +15,17 @@ import ( "sigs.k8s.io/kustomize/kyaml/yaml" ) -func GetTreeRunner() *TreeRunner { +func GetTreeRunner(name string) *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), + 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.") @@ -95,8 +52,8 @@ kubectl get all,applications,releasetracks -o yaml | kyaml tree --structure=grap return r } -func TreeCommand() *cobra.Command { - return GetTreeRunner().Command +func TreeCommand(name string) *cobra.Command { + return GetTreeRunner(name).Command } // TreeRunner contains the run function diff --git a/cmd/config/cmd/tree_test.go b/cmd/config/cmd/tree_test.go index 3ea10c792..427b46428 100644 --- a/cmd/config/cmd/tree_test.go +++ b/cmd/config/cmd/tree_test.go @@ -74,7 +74,7 @@ spec: // fmt the files b := &bytes.Buffer{} - r := cmd.GetTreeRunner() + r := cmd.GetTreeRunner("") r.Command.SetArgs([]string{d}) r.Command.SetOut(b) if !assert.NoError(t, r.Command.Execute()) { @@ -93,7 +93,7 @@ spec: func TestTreeCommand_stdin(t *testing.T) { // fmt the files b := &bytes.Buffer{} - r := cmd.GetTreeRunner() + r := cmd.GetTreeRunner("") r.Command.SetArgs([]string{}) r.Command.SetIn(bytes.NewBufferString(`apiVersion: extensions/v1 kind: Deployment @@ -263,7 +263,7 @@ spec: // fmt the files b := &bytes.Buffer{} - r := cmd.GetTreeRunner() + r := cmd.GetTreeRunner("") r.Command.SetArgs([]string{d, "--include-local"}) r.Command.SetOut(b) if !assert.NoError(t, r.Command.Execute()) { @@ -340,7 +340,7 @@ spec: // fmt the files b := &bytes.Buffer{} - r := cmd.GetTreeRunner() + r := cmd.GetTreeRunner("") r.Command.SetArgs([]string{d, "--include-local", "--exclude-non-local"}) r.Command.SetOut(b) if !assert.NoError(t, r.Command.Execute()) { diff --git a/cmd/config/cmd/util.go b/cmd/config/cmd/util.go index cb2698caa..f561f415d 100644 --- a/cmd/config/cmd/util.go +++ b/cmd/config/cmd/util.go @@ -62,3 +62,13 @@ var ExitOnError bool // StackOnError if true, will print a stack trace on failure. var StackOnError bool + +const cmdName = "kyaml" + +// 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) +} diff --git a/cmd/config/cmddocs/api/docs.go b/cmd/config/cmddocs/api/docs.go new file mode 100644 index 000000000..a21db32e7 --- /dev/null +++ b/cmd/config/cmddocs/api/docs.go @@ -0,0 +1,144 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by "mdtogo"; DO NOT EDIT. +package api + +var Merge2Long = `# Merge (2-way) + + 2-way merges fields from a source to a destination, overriding the destination fields + where they differ. + + ### Merge Rules + + Fields are recursively merged using the following rules: + + - scalars + - if present only in the dest, it keeps its value + - if present in the src and is non-null, take the src value -- if ` + "`" + `null` + "`" + `, clear it + - example src: ` + "`" + `5` + "`" + `, dest: ` + "`" + `3` + "`" + ` => result: ` + "`" + `5` + "`" + ` + + - non-associative lists -- lists without a merge key + - if present only in the dest, it keeps its value + - if present in the src and is non-null, take the src value -- if ` + "`" + `null` + "`" + `, clear it + - example src: ` + "`" + `[1, 2, 3]` + "`" + `, dest: ` + "`" + `[a, b, c]` + "`" + ` => result: ` + "`" + `[1, 2, 3]` + "`" + ` + + - map keys and fields -- paired by the map-key / field-name + - if present only in the dest, it keeps its value + - if present only in the src, it is added to the dest + - if the field is present in both the src and dest, and the src value is + ` + "`" + `null` + "`" + `, the field is removed from the dest + - if the field is present in both the src and dest, the value is recursively merged + - example src: ` + "`" + `{'key1': 'value1', 'key2': 'value2'}` + "`" + `, + dest: ` + "`" + `{'key2': 'value0', 'key3': 'value3'}` + "`" + ` + => result: ` + "`" + `{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}` + "`" + ` + + - associative list elements -- paired by the associative key + - if present only in the dest, it keeps its value in the list + - if present only in the src, it is added to the dest list + - if the field is present in both the src and dest, the value is recursively merged + + ### Associative Keys + + Associative keys are used to identify "same" elements within 2 different lists, and merge them. + The following fields are recognized as associative keys: + + [` + "`" + `mountPath` + "`" + `, ` + "`" + `devicePath` + "`" + `, ` + "`" + `ip` + "`" + `, ` + "`" + `type` + "`" + `, ` + "`" + `topologyKey` + "`" + `, ` + "`" + `name` + "`" + `, ` + "`" + `containerPort` + "`" + `] + + Any lists where all of the elements contain associative keys will be merged as associative lists. + + ### Example + + > Source + + apiVersion: apps/v1 + kind: Deployment + spec: + replicas: 3 # scalar + template: + spec: + containers: # associative list -- (name) + - name: nginx + image: nginx:1.7 + command: ['new_run.sh', 'arg1'] # non-associative list + - name: sidecar2 + image: sidecar2:v1 + + > Destination + + apiVersion: apps/v1 + kind: Deployment + spec: + replicas: 1 + template: + spec: + containers: + - name: nginx + image: nginx:1.6 + command: ['old_run.sh', 'arg0'] + - name: sidecar1 + image: sidecar1:v1 + + > Result + + apiVersion: apps/v1 + kind: Deployment + spec: + replicas: 3 # scalar + template: + spec: + containers: # associative list -- (name) + - name: nginx + image: nginx:1.7 + command: ['new_run.sh', 'arg1'] # non-associative list + - name: sidecar1 + image: sidecar1:v1 + - name: sidecar2 + image: sidecar2:v1` + +var Merge3Long = `# Merge (3-way) + + 3-way merge identifies changes between an original source + updated source and merges the result + into a destination, overriding the destination fields where they have changed between + original and updated. + +### Resource MergeRules + + - Resources present in the original and deleted from the update are deleted. + - Resources missing from the original and added in the update are added. + - Resources present only in the dest are kept without changes. + - Resources present in both the update and the dest have their fields merged with the destination. + +### Field Merge Rules + + Fields are recursively merged using the following rules: + + - scalars + - if present in either dest or updated and ` + "`" + `null` + "`" + `, clear the value + - if unchanged between original and updated, keep dest value + - if changed between original and updated (added, deleted, changed), take the updated value + + - non-associative lists -- lists without a merge key + - if present in either dest or updated and ` + "`" + `null` + "`" + `, clear the value + - if unchanged between original and updated, keep dest value + - if changed between original and updated (added, deleted, changed), take the updated value + + - map keys and fields -- paired by the map-key / field-name + - if present in either dest or updated and ` + "`" + `null` + "`" + `, clear the value + - if present only in the dest, it keeps its value + - if not-present in the dest, add the delta between original-updated as a field + - otherwise recursively merge the value between original, updated, dest + + - associative list elements -- paired by the associative key + - if present only in the dest, it keeps its value + - if not-present in the dest, add the delta between original-updated as a field + - otherwise recursively merge the value between original, updated, dest + +### Associative Keys + + Associative keys are used to identify "same" elements within 2 different lists, and merge them. + The following fields are recognized as associative keys: + + [` + "`" + `mountPath` + "`" + `, ` + "`" + `devicePath` + "`" + `, ` + "`" + `ip` + "`" + `, ` + "`" + `type` + "`" + `, ` + "`" + `topologyKey` + "`" + `, ` + "`" + `name` + "`" + `, ` + "`" + `containerPort` + "`" + `] + + Any lists where all of the elements contain associative keys will be merged as associative lists.` diff --git a/cmd/config/cmddocs/commands/docs.go b/cmd/config/cmddocs/commands/docs.go new file mode 100644 index 000000000..dea7a3047 --- /dev/null +++ b/cmd/config/cmddocs/commands/docs.go @@ -0,0 +1,219 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by "mdtogo"; DO NOT EDIT. +package commands + +var CatShort = `Print Resource Config from a local directory.` +var CatLong = ` +Print Resource Config from a local directory. + + DIR: + Path to local directory. +` +var CatExamples = ` + # 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` + +var CountShort = `Count Resources Config from a local directory.` +var CountLong = ` +Count Resources Config from a local directory. + + DIR: + Path to local directory. +` +var CountExamples = ` + # print Resource counts from a directory + kyaml count my-dir/` + +var FmtShort = `Format yaml configuration files.` +var FmtLong = ` +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) +` +var FmtExamples = ` + # 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` + +var GrepShort = `Search for matching Resources in a directory or from stdin` +var GrepLong = ` + 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. +` +var GrepExamples = ` + # 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` + +var MergeShort = `Merge Resource configuration files` +var MergeLong = ` +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 docs merge +` +var MergeExamples = ` + cat resources_and_patches.yaml | kyaml merge > merged_resources.yaml` + +var RunFnsShort = `Apply config functions to Resources.` +var RunFnsLong = ` +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. + + See ` + "`" + `kyaml help docs-fn` + "`" + ` for more details on writing functions. +` +var RunFnsExamples = ` +kyaml run-fns example/` + +var TreeShort = `Display Resource structure from a directory or stdin.` +var TreeLong = ` +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. +` +var TreeExamples = ` + # 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"` diff --git a/cmd/config/docs/api-conventions/merge2.md b/cmd/config/docs/api-conventions/merge2.md new file mode 100644 index 000000000..06b19c259 --- /dev/null +++ b/cmd/config/docs/api-conventions/merge2.md @@ -0,0 +1,91 @@ +# Merge (2-way) + + 2-way merges fields from a source to a destination, overriding the destination fields + where they differ. + + ### Merge Rules + + Fields are recursively merged using the following rules: + + - scalars + - if present only in the dest, it keeps its value + - if present in the src and is non-null, take the src value -- if `null`, clear it + - example src: `5`, dest: `3` => result: `5` + + - non-associative lists -- lists without a merge key + - if present only in the dest, it keeps its value + - if present in the src and is non-null, take the src value -- if `null`, clear it + - example src: `[1, 2, 3]`, dest: `[a, b, c]` => result: `[1, 2, 3]` + + - map keys and fields -- paired by the map-key / field-name + - if present only in the dest, it keeps its value + - if present only in the src, it is added to the dest + - if the field is present in both the src and dest, and the src value is + `null`, the field is removed from the dest + - if the field is present in both the src and dest, the value is recursively merged + - example src: `{'key1': 'value1', 'key2': 'value2'}`, + dest: `{'key2': 'value0', 'key3': 'value3'}` + => result: `{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}` + + - associative list elements -- paired by the associative key + - if present only in the dest, it keeps its value in the list + - if present only in the src, it is added to the dest list + - if the field is present in both the src and dest, the value is recursively merged + + ### Associative Keys + + Associative keys are used to identify "same" elements within 2 different lists, and merge them. + The following fields are recognized as associative keys: + + [`mountPath`, `devicePath`, `ip`, `type`, `topologyKey`, `name`, `containerPort`] + + Any lists where all of the elements contain associative keys will be merged as associative lists. + + ### Example + + > Source + + apiVersion: apps/v1 + kind: Deployment + spec: + replicas: 3 # scalar + template: + spec: + containers: # associative list -- (name) + - name: nginx + image: nginx:1.7 + command: ['new_run.sh', 'arg1'] # non-associative list + - name: sidecar2 + image: sidecar2:v1 + + > Destination + + apiVersion: apps/v1 + kind: Deployment + spec: + replicas: 1 + template: + spec: + containers: + - name: nginx + image: nginx:1.6 + command: ['old_run.sh', 'arg0'] + - name: sidecar1 + image: sidecar1:v1 + + > Result + + apiVersion: apps/v1 + kind: Deployment + spec: + replicas: 3 # scalar + template: + spec: + containers: # associative list -- (name) + - name: nginx + image: nginx:1.7 + command: ['new_run.sh', 'arg1'] # non-associative list + - name: sidecar1 + image: sidecar1:v1 + - name: sidecar2 + image: sidecar2:v1 \ No newline at end of file diff --git a/cmd/config/docs/api-conventions/merge3.md b/cmd/config/docs/api-conventions/merge3.md new file mode 100644 index 000000000..a2905342b --- /dev/null +++ b/cmd/config/docs/api-conventions/merge3.md @@ -0,0 +1,46 @@ +# Merge (3-way) + + 3-way merge identifies changes between an original source + updated source and merges the result + into a destination, overriding the destination fields where they have changed between + original and updated. + +### Resource MergeRules + + - Resources present in the original and deleted from the update are deleted. + - Resources missing from the original and added in the update are added. + - Resources present only in the dest are kept without changes. + - Resources present in both the update and the dest have their fields merged with the destination. + +### Field Merge Rules + + Fields are recursively merged using the following rules: + + - scalars + - if present in either dest or updated and `null`, clear the value + - if unchanged between original and updated, keep dest value + - if changed between original and updated (added, deleted, changed), take the updated value + + - non-associative lists -- lists without a merge key + - if present in either dest or updated and `null`, clear the value + - if unchanged between original and updated, keep dest value + - if changed between original and updated (added, deleted, changed), take the updated value + + - map keys and fields -- paired by the map-key / field-name + - if present in either dest or updated and `null`, clear the value + - if present only in the dest, it keeps its value + - if not-present in the dest, add the delta between original-updated as a field + - otherwise recursively merge the value between original, updated, dest + + - associative list elements -- paired by the associative key + - if present only in the dest, it keeps its value + - if not-present in the dest, add the delta between original-updated as a field + - otherwise recursively merge the value between original, updated, dest + +### Associative Keys + + Associative keys are used to identify "same" elements within 2 different lists, and merge them. + The following fields are recognized as associative keys: + + [`mountPath`, `devicePath`, `ip`, `type`, `topologyKey`, `name`, `containerPort`] + + Any lists where all of the elements contain associative keys will be merged as associative lists. diff --git a/cmd/config/docs/commands/cat.md b/cmd/config/docs/commands/cat.md new file mode 100644 index 000000000..49bb7b245 --- /dev/null +++ b/cmd/config/docs/commands/cat.md @@ -0,0 +1,21 @@ +## cat + +Print Resource Config from a local directory. + +### Synopsis + +Print Resource Config from a local directory. + + DIR: + Path to local directory. + +### Examples + + # 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 diff --git a/cmd/config/docs/commands/count.md b/cmd/config/docs/commands/count.md new file mode 100644 index 000000000..a537c1a52 --- /dev/null +++ b/cmd/config/docs/commands/count.md @@ -0,0 +1,15 @@ +## count + +Count Resources Config from a local directory. + +### Synopsis + +Count Resources Config from a local directory. + + DIR: + Path to local directory. + +### Examples + + # print Resource counts from a directory + kyaml count my-dir/ \ No newline at end of file diff --git a/cmd/config/docs/commands/fmt.md b/cmd/config/docs/commands/fmt.md new file mode 100644 index 000000000..1539c2da5 --- /dev/null +++ b/cmd/config/docs/commands/fmt.md @@ -0,0 +1,44 @@ +## fmt + +Format yaml configuration files. + +### Synopsis + +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) + +### Examples + + # 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 \ No newline at end of file diff --git a/cmd/config/docs/commands/grep.md b/cmd/config/docs/commands/grep.md new file mode 100644 index 000000000..e21655567 --- /dev/null +++ b/cmd/config/docs/commands/grep.md @@ -0,0 +1,31 @@ +## grep + +Search for matching Resources in a directory or from stdin + +### Synopsis + + 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. + +### Examples + + # 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 \ No newline at end of file diff --git a/cmd/config/docs/commands/merge.md b/cmd/config/docs/commands/merge.md new file mode 100644 index 000000000..84f0f68df --- /dev/null +++ b/cmd/config/docs/commands/merge.md @@ -0,0 +1,24 @@ +## merge + +Merge Resource configuration files + +### Synopsis + +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 docs merge + +### Examples + + cat resources_and_patches.yaml | kyaml merge > merged_resources.yaml \ No newline at end of file diff --git a/cmd/config/docs/commands/run-fns.md b/cmd/config/docs/commands/run-fns.md new file mode 100644 index 000000000..1d40105f0 --- /dev/null +++ b/cmd/config/docs/commands/run-fns.md @@ -0,0 +1,53 @@ +## run-fns + +Apply config functions to Resources. + +### Synopsis + +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. + + See `kyaml help docs-fn` for more details on writing functions. + +### Examples + +kyaml run-fns example/ diff --git a/cmd/config/docs/commands/tree.md b/cmd/config/docs/commands/tree.md new file mode 100644 index 000000000..e64d1f08f --- /dev/null +++ b/cmd/config/docs/commands/tree.md @@ -0,0 +1,54 @@ +## tree + +Display Resource structure from a directory or stdin. + +### Synopsis + +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. + +### Examples + + # 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" diff --git a/cmd/config/main.go b/cmd/config/main.go index b805f99de..7bbca6894 100644 --- a/cmd/config/main.go +++ b/cmd/config/main.go @@ -1,6 +1,8 @@ // Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 +//go:generate $GOBIN/mdtogo docs/api-conventions cmddocs/api --full=true +//go:generate $GOBIN/mdtogo docs/commands cmddocs/commands package main import ( @@ -8,33 +10,39 @@ import ( "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" + "sigs.k8s.io/kustomize/cmd/config/cmddocs/api" ) var root = &cobra.Command{ - Use: "kyaml", - Short: "kyaml reference comand", - Long: `Description: - Reference implementation for using the kyaml libraries. -`, - Example: ``, + Use: "config", + Short: "Utilities for working with Resource Configuration.", + Long: `Utilities for working with Resource Configuration.`, } func main() { root.PersistentFlags().BoolVar(&cmd.StackOnError, "stack-trace", false, "print a stack-trace on failure") + name := "config" 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}) + root.AddCommand(cmd.GrepCommand(name)) + root.AddCommand(cmd.TreeCommand(name)) + root.AddCommand(cmd.CatCommand(name)) + root.AddCommand(cmd.FmtCommand(name)) + root.AddCommand(cmd.MergeCommand(name)) + root.AddCommand(cmd.CountCommand(name)) + root.AddCommand(cmd.RunFnCommand(name)) + + root.AddCommand(&cobra.Command{ + Use: "docs-merge", + Short: "Documentation for merging Resources (2-way merge).", + Long: api.Merge2Long, + }) + root.AddCommand(&cobra.Command{ + Use: "docs-merge3", + Short: "Documentation for merging Resources (3-way merge).", + Long: api.Merge3Long, + }) if err := root.Execute(); err != nil { os.Exit(1) diff --git a/kyaml/yaml/merge2/merge2.go b/kyaml/yaml/merge2/merge2.go index c6894fab8..ffedfbb72 100644 --- a/kyaml/yaml/merge2/merge2.go +++ b/kyaml/yaml/merge2/merge2.go @@ -10,97 +10,6 @@ import ( "sigs.k8s.io/kustomize/kyaml/yaml/walk" ) -const Help = ` -Description: - - merge merges fields from a source to a destination, overriding the destination fields - where they differ. - - ### Merge Rules - - Fields are recursively merged using the following rules: - - - scalars - - if present only in the dest, it keeps its value - - if present in the src and is non-null, take the src value -- if ` + "`null`" + `, clear it -` + " - example src: `5`, dest: `3` => result: `5`" + ` - - - non-associative lists -- lists without a merge key - - if present only in the dest, it keeps its value - - if present in the src and is non-null, take the src value -- if ` + "`null`" + `, clear it -` + " - example src: `[1, 2, 3]`, dest: `[a, b, c]` => result: `[1, 2, 3]`" + ` - - - map keys and fields -- paired by the map-key / field-name - - if present only in the dest, it keeps its value - - if present only in the src, it is added to the dest - - if the field is present in both the src and dest, and the src value is 'null', the field is removed from the dest - - if the field is present in both the src and dest, the value is recursively merged -` + " - example src: `{'key1': 'value1', 'key2': 'value2'}`, dest: `{'key2': 'value0', 'key3': 'value3'}` => result: `{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}`" + ` - - - associative list elements -- paired by the associative key - - if present only in the dest, it keeps its value in the list - - if present only in the src, it is added to the dest list - - if the field is present in both the src and dest, the value is recursively merged - - ### Associative Keys - - Associative keys are used to identify "same" elements within 2 different lists, and merge them. - The following fields are recognized as associative keys: - -` + "[`mountPath`, `devicePath`, `ip`, `type`, `topologyKey`, `name`, `containerPort`]" + ` - - Any lists where all of the elements contain associative keys will be merged as associative lists. - - ### Example - - > Source - - apiVersion: apps/v1 - kind: Deployment - spec: - replicas: 3 # scalar - template: - spec: - containers: # associative list -- (name) - - name: nginx - image: nginx:1.7 - command: ['new_run.sh', 'arg1'] # non-associative list - - name: sidecar2 - image: sidecar2:v1 - - > Destination - - apiVersion: apps/v1 - kind: Deployment - spec: - replicas: 1 - template: - spec: - containers: - - name: nginx - image: nginx:1.6 - command: ['old_run.sh', 'arg0'] - - name: sidecar1 - image: sidecar1:v1 - - > Result - - apiVersion: apps/v1 - kind: Deployment - spec: - replicas: 3 # scalar - template: - spec: - containers: # associative list -- (name) - - name: nginx - image: nginx:1.7 - command: ['new_run.sh', 'arg1'] # non-associative list - - name: sidecar1 - image: sidecar1:v1 - - name: sidecar2 - image: sidecar2:v1 -` - // Merge merges fields from src into dest. func Merge(src, dest *yaml.RNode) (*yaml.RNode, error) { return walk.Walker{Sources: []*yaml.RNode{dest, src}, Visitor: Merger{}}.Walk() diff --git a/kyaml/yaml/merge3/merge3.go b/kyaml/yaml/merge3/merge3.go index ea7db0879..d35fee5ca 100644 --- a/kyaml/yaml/merge3/merge3.go +++ b/kyaml/yaml/merge3/merge3.go @@ -10,55 +10,6 @@ import ( "sigs.k8s.io/kustomize/kyaml/yaml/walk" ) -const Help = ` -Description: - - merge3 identifies changes between an original source + updated source and merges the result - into a destination, overriding the destination fields where they have changed between - original and updated. - - ### Resource MergeRules - - - Resources present in the original and deleted from the update are deleted. - - Resources missing from the original and added in the update are added. - - Resources present only in the dest are kept without changes. - - Resources present in both the update and the dest are merged *original + update + dest => dest*. - - ### Field Merge Rules - - Fields are recursively merged using the following rules: - - - scalars - - if present in either dest or updated and 'null', clear the value - - if unchanged between original and updated, keep dest value - - if changed between original and updated (added, deleted, changed), take the updated value - - - non-associative lists -- lists without a merge key - - if present in either dest or updated and 'null', clear the value - - if unchanged between original and updated, keep dest value - - if changed between original and updated (added, deleted, changed), take the updated value - - - map keys and fields -- paired by the map-key / field-name - - if present in either dest or updated and 'null', clear the value - - if present only in the dest, it keeps its value - - if not-present in the dest, add the delta between original-updated as a field - - otherwise recursively merge the value between original, updated, dest - - - associative list elements -- paired by the associative key - - if present only in the dest, it keeps its value - - if not-present in the dest, add the delta between original-updated as a field - - otherwise recursively merge the value between original, updated, dest - - ### Associative Keys - - Associative keys are used to identify "same" elements within 2 different lists, and merge them. - The following fields are recognized as associative keys: - -` + "[`mountPath`, `devicePath`, `ip`, `type`, `topologyKey`, `name`, `containerPort`]" + ` - - Any lists where all of the elements contain associative keys will be merged as associative lists. -` - func Merge(dest, original, update *yaml.RNode) (*yaml.RNode, error) { // if update == nil && original != nil => declarative deletion