mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-29 09:40:49 +00:00
Merge pull request #2043 from pwittrock/master
Fix various cmd/config issues
This commit is contained in:
@@ -80,6 +80,8 @@ func NewConfigCommand(name string) *cobra.Command {
|
||||
root.AddCommand(commands.SetCommand(name))
|
||||
root.AddCommand(commands.ListSettersCommand(name))
|
||||
root.AddCommand(commands.CreateSetterCommand(name))
|
||||
root.AddCommand(commands.SinkCommand(name))
|
||||
root.AddCommand(commands.SourceCommand(name))
|
||||
|
||||
root.AddCommand(&cobra.Command{
|
||||
Use: "docs-merge",
|
||||
|
||||
@@ -22,7 +22,7 @@ order they appear in the file).
|
||||
|
||||
#### Config Functions:
|
||||
|
||||
Config functions are specified as Kubernetes types containing a metadata.annotations.[config.k8s.io/function]
|
||||
Config functions are specified as Kubernetes types containing a metadata.annotations.[config.kubernetes.io/function]
|
||||
field specifying an image for the container to run. This image tells run how to invoke the container.
|
||||
|
||||
Example config function:
|
||||
@@ -32,7 +32,7 @@ order they appear in the file).
|
||||
kind: ExampleFunctionKind
|
||||
metadata:
|
||||
annotations:
|
||||
config.k8s.io/function: |
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
# function is invoked as a container running this image
|
||||
image: gcr.io/example/examplefunction:v1.0.1
|
||||
@@ -41,7 +41,7 @@ order they appear in the file).
|
||||
configField: configValue
|
||||
|
||||
In the preceding example, 'kustomize config run example/' would identify the function by
|
||||
the metadata.annotations.[config.k8s.io/function] field. It would then write all Resources in the directory to
|
||||
the metadata.annotations.[config.kubernetes.io/function] 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 write the container stdout back to example/, replacing the directory
|
||||
file contents.
|
||||
|
||||
18
cmd/config/docs/commands/sink.md
Normal file
18
cmd/config/docs/commands/sink.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## sink
|
||||
|
||||
[Alpha] Implement a Sink by writing input to a local directory.
|
||||
|
||||
### Synopsis
|
||||
|
||||
[Alpha] Implement a Sink by writing input to a local directory.
|
||||
|
||||
kustomize config sink DIR
|
||||
|
||||
DIR:
|
||||
Path to local directory.
|
||||
|
||||
`sink` writes its input to a directory
|
||||
|
||||
### Examples
|
||||
|
||||
kustomize config source DIR/ | your-function | kustomize config sink DIR/
|
||||
21
cmd/config/docs/commands/source.md
Normal file
21
cmd/config/docs/commands/source.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## source
|
||||
|
||||
[Alpha] Implement a Source by reading a local directory.
|
||||
|
||||
### Synopsis
|
||||
|
||||
[Alpha] Implement a Source by reading a local directory.
|
||||
|
||||
kustomize config source DIR
|
||||
|
||||
DIR:
|
||||
Path to local directory.
|
||||
|
||||
`source` emits configuration to act as input to a function
|
||||
|
||||
### Examples
|
||||
|
||||
# emity configuration directory as input source to a function
|
||||
kustomize config source DIR/
|
||||
|
||||
kustomize config source DIR/ | your-function | kustomize config sink DIR/
|
||||
@@ -121,6 +121,13 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
|
||||
out = o
|
||||
}
|
||||
|
||||
// remove this annotation explicitly, the ByteWriter won't clear it by
|
||||
// default because it doesn't set it
|
||||
clear := []string{"config.kubernetes.io/path"}
|
||||
if r.KeepAnnotations {
|
||||
clear = nil
|
||||
}
|
||||
|
||||
var outputs []kio.Writer
|
||||
outputs = append(outputs, kio.ByteWriter{
|
||||
Writer: out,
|
||||
@@ -129,6 +136,7 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
|
||||
WrappingAPIVersion: r.WrapApiVersion,
|
||||
FunctionConfig: functionConfig,
|
||||
Style: yaml.GetStyle(r.Styles...),
|
||||
ClearAnnotations: clear,
|
||||
})
|
||||
|
||||
return handleError(c, kio.Pipeline{Inputs: inputs, Filters: fltr, Outputs: outputs}.Execute())
|
||||
|
||||
@@ -90,8 +90,6 @@ metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -100,8 +98,6 @@ metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -114,8 +110,6 @@ metadata:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
`, b.String()) {
|
||||
@@ -196,8 +190,6 @@ metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -206,8 +198,6 @@ metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -218,8 +208,6 @@ 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
|
||||
@@ -233,8 +221,6 @@ metadata:
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
`, b.String()) {
|
||||
@@ -314,8 +300,6 @@ 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
|
||||
@@ -414,8 +398,6 @@ metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -424,8 +406,6 @@ metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -438,8 +418,6 @@ metadata:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
`, string(actual)) {
|
||||
@@ -536,8 +514,6 @@ metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -546,8 +522,6 @@ metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -560,8 +534,6 @@ metadata:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
`, string(actual)) {
|
||||
|
||||
@@ -76,7 +76,6 @@ metadata:
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -87,7 +86,6 @@ metadata:
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
|
||||
48
cmd/config/internal/commands/sink.go
Normal file
48
cmd/config/internal/commands/sink.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
// GetSinkRunner returns a command for Sink.
|
||||
func GetSinkRunner(name string) *SinkRunner {
|
||||
r := &SinkRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "sink DIR",
|
||||
Short: commands.SinkShort,
|
||||
Long: commands.SinkLong,
|
||||
Example: commands.SinkExamples,
|
||||
RunE: r.runE,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
fixDocs(name, c)
|
||||
r.Command = c
|
||||
return r
|
||||
}
|
||||
|
||||
func SinkCommand(name string) *cobra.Command {
|
||||
return GetSinkRunner(name).Command
|
||||
}
|
||||
|
||||
// SinkRunner contains the run function
|
||||
type SinkRunner struct {
|
||||
Command *cobra.Command
|
||||
}
|
||||
|
||||
func (r *SinkRunner) runE(c *cobra.Command, args []string) error {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: c.InOrStdin()}},
|
||||
Outputs: []kio.Writer{
|
||||
&kio.LocalPackageWriter{
|
||||
PackagePath: args[0],
|
||||
ClearAnnotations: []string{"config.kubernetes.io/path"},
|
||||
},
|
||||
},
|
||||
}.Execute()
|
||||
return handleError(c, err)
|
||||
}
|
||||
140
cmd/config/internal/commands/sink_test.go
Normal file
140
cmd/config/internal/commands/sink_test.go
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
)
|
||||
|
||||
func TestSinkCommand(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-source-test")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetSinkRunner("")
|
||||
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
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/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
- apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
config.kubernetes.io/local-config: "true"
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
`))
|
||||
r.Command.SetArgs([]string{d})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actual, err := ioutil.ReadFile(filepath.Join(d, "f1.yaml"))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
expected := `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
|
||||
`
|
||||
if !assert.Equal(t, expected, string(actual)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actual, err = ioutil.ReadFile(filepath.Join(d, "f2.yaml"))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
expected = `apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
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
|
||||
`
|
||||
if !assert.Equal(t, expected, string(actual)) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
77
cmd/config/internal/commands/source.go
Normal file
77
cmd/config/internal/commands/source.go
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// GetSourceRunner returns a command for Source.
|
||||
func GetSourceRunner(name string) *SourceRunner {
|
||||
r := &SourceRunner{}
|
||||
c := &cobra.Command{
|
||||
Use: "source DIR",
|
||||
Short: commands.SourceShort,
|
||||
Long: commands.SourceLong,
|
||||
Example: commands.SourceExamples,
|
||||
RunE: r.runE,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
fixDocs(name, c)
|
||||
c.Flags().StringVar(&r.WrapKind, "wrap-kind", kio.ResourceListKind,
|
||||
"output using this format.")
|
||||
c.Flags().StringVar(&r.WrapApiVersion, "wrap-version", kio.ResourceListAPIVersion,
|
||||
"output using this format.")
|
||||
c.Flags().StringVar(&r.FunctionConfig, "function-config", "",
|
||||
"path to function config.")
|
||||
r.Command = c
|
||||
_ = c.MarkFlagFilename("function-config", "yaml", "json", "yml")
|
||||
return r
|
||||
}
|
||||
|
||||
func SourceCommand(name string) *cobra.Command {
|
||||
return GetSourceRunner(name).Command
|
||||
}
|
||||
|
||||
// SourceRunner contains the run function
|
||||
type SourceRunner struct {
|
||||
WrapKind string
|
||||
WrapApiVersion string
|
||||
FunctionConfig string
|
||||
Command *cobra.Command
|
||||
}
|
||||
|
||||
func (r *SourceRunner) 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}.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 outputs []kio.Writer
|
||||
outputs = append(outputs, kio.ByteWriter{
|
||||
Writer: c.OutOrStdout(),
|
||||
KeepReaderAnnotations: true,
|
||||
WrappingKind: r.WrapKind,
|
||||
WrappingAPIVersion: r.WrapApiVersion,
|
||||
FunctionConfig: functionConfig,
|
||||
})
|
||||
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{kio.LocalPackageReader{PackagePath: args[0]}},
|
||||
Outputs: outputs}.Execute()
|
||||
return handleError(c, err)
|
||||
}
|
||||
136
cmd/config/internal/commands/source_test.go
Normal file
136
cmd/config/internal/commands/source_test.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package commands_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||
)
|
||||
|
||||
func TestSourceCommand(t *testing.T) {
|
||||
d, err := ioutil.TempDir("", "kustomize-source-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/function: |
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
config.kubernetes.io/local-config: "true"
|
||||
spec:
|
||||
replicas: 3
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
`), 0600)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt the files
|
||||
b := &bytes.Buffer{}
|
||||
r := commands.GetSourceRunner("")
|
||||
r.Command.SetArgs([]string{d})
|
||||
r.Command.SetOut(b)
|
||||
if !assert.NoError(t, r.Command.Execute()) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
name: foo
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/index: '0'
|
||||
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/path: 'f1.yaml'
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
- apiVersion: v1
|
||||
kind: Abstraction
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
image: gcr.io/example/reconciler:v1
|
||||
config.kubernetes.io/local-config: "true"
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: bar
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'f2.yaml'
|
||||
spec:
|
||||
replicas: 3
|
||||
`, b.String()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -118,7 +117,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -132,7 +130,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -146,7 +143,6 @@ metadata:
|
||||
namespace: default2
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -160,7 +156,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx3
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -171,8 +166,7 @@ metadata:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: bar-package
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
config.kubernetes.io/path: bar-package/f2.yaml
|
||||
name: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -183,7 +177,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
selector:
|
||||
|
||||
@@ -220,7 +220,7 @@ order they appear in the file).
|
||||
|
||||
#### Config Functions:
|
||||
|
||||
Config functions are specified as Kubernetes types containing a metadata.annotations.[config.k8s.io/function]
|
||||
Config functions are specified as Kubernetes types containing a metadata.annotations.[config.kubernetes.io/function]
|
||||
field specifying an image for the container to run. This image tells run how to invoke the container.
|
||||
|
||||
Example config function:
|
||||
@@ -230,7 +230,7 @@ order they appear in the file).
|
||||
kind: ExampleFunctionKind
|
||||
metadata:
|
||||
annotations:
|
||||
config.k8s.io/function: |
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
# function is invoked as a container running this image
|
||||
image: gcr.io/example/examplefunction:v1.0.1
|
||||
@@ -239,7 +239,7 @@ order they appear in the file).
|
||||
configField: configValue
|
||||
|
||||
In the preceding example, 'kustomize config run example/' would identify the function by
|
||||
the metadata.annotations.[config.k8s.io/function] field. It would then write all Resources in the directory to
|
||||
the metadata.annotations.[config.kubernetes.io/function] 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 write the container stdout back to example/, replacing the directory
|
||||
file contents.
|
||||
@@ -332,6 +332,37 @@ var SetExamples = `
|
||||
name: test-app2 # {"description":"test environment","type":"string","x-kustomize":{"setBy":"dev","partialFieldSetters":[{"name":"name-prefix","value":"test"}]}}
|
||||
...`
|
||||
|
||||
var SinkShort = `[Alpha] Implement a Sink by writing input to a local directory.`
|
||||
var SinkLong = `
|
||||
[Alpha] Implement a Sink by writing input to a local directory.
|
||||
|
||||
kustomize config sink DIR
|
||||
|
||||
DIR:
|
||||
Path to local directory.
|
||||
|
||||
` + "`" + `sink` + "`" + ` writes its input to a directory
|
||||
`
|
||||
var SinkExamples = `
|
||||
kustomize config source DIR/ | your-function | kustomize config sink DIR/`
|
||||
|
||||
var SourceShort = `[Alpha] Implement a Source by reading a local directory.`
|
||||
var SourceLong = `
|
||||
[Alpha] Implement a Source by reading a local directory.
|
||||
|
||||
kustomize config source DIR
|
||||
|
||||
DIR:
|
||||
Path to local directory.
|
||||
|
||||
` + "`" + `source` + "`" + ` emits configuration to act as input to a function
|
||||
`
|
||||
var SourceExamples = `
|
||||
# emity configuration directory as input source to a function
|
||||
kustomize config source DIR/
|
||||
|
||||
kustomize config source DIR/ | your-function | kustomize config sink DIR/`
|
||||
|
||||
var TreeShort = `[Alpha] Display Resource structure from a directory or stdin.`
|
||||
var TreeLong = `
|
||||
[Alpha] Display Resource structure from a directory or stdin.
|
||||
|
||||
@@ -9,6 +9,7 @@ package main
|
||||
import (
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/cmd/config/complete"
|
||||
"sigs.k8s.io/kustomize/cmd/config/configcobra"
|
||||
"sigs.k8s.io/kustomize/kyaml/commandutil"
|
||||
)
|
||||
@@ -16,7 +17,10 @@ import (
|
||||
func main() {
|
||||
// enable the config commands
|
||||
os.Setenv(commandutil.EnableAlphaCommmandsEnvName, "true")
|
||||
if err := configcobra.NewConfigCommand("").Execute(); err != nil {
|
||||
cmd := configcobra.NewConfigCommand("")
|
||||
complete.Complete(cmd).Complete("config")
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ Resource configuration, and looks for invalid configuration.
|
||||
## Function invocation
|
||||
|
||||
The function is invoked by authoring a [local Resource](local-resource)
|
||||
with `metadata.annotations.[config.k8s.io/function]` and running:
|
||||
with `metadata.annotations.[config.kubernetes.io/function]` and running:
|
||||
|
||||
kustomize config run local-resource/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ apiVersion: examples.config.kubernetes.io/v1beta1
|
||||
kind: Validator
|
||||
metadata:
|
||||
annotations:
|
||||
config.k8s.io/function: |
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
|
||||
---
|
||||
|
||||
@@ -25,7 +25,7 @@ function input, and writing the function output.
|
||||
## Function invocation
|
||||
|
||||
The function is invoked by authoring a [local Resource](local-resource)
|
||||
with `metadata.annotations.[config.k8s.io/function]` and running:
|
||||
with `metadata.annotations.[config.kubernetes.io/function]` and running:
|
||||
|
||||
kustomize config run local-resource/
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ kind: Nginx
|
||||
metadata:
|
||||
name: demo
|
||||
annotations:
|
||||
config.k8s.io/function: |
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
image: gcr.io/kustomize-functions/example-nginx:v0.1.0
|
||||
spec:
|
||||
|
||||
@@ -20,7 +20,7 @@ heavy lifting of implementing the function interface.
|
||||
## Function invocation
|
||||
|
||||
The function is invoked by authoring a [local Resource](local-resource)
|
||||
with `metadata.annotations.[config.k8s.io/function]` and running:
|
||||
with `metadata.annotations.[config.kubernetes.io/function]` and running:
|
||||
|
||||
kustomize config run local-resource/
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ kind: CockroachDB
|
||||
metadata:
|
||||
name: demo
|
||||
annotations:
|
||||
config.k8s.io/function: |
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
image: gcr.io/kustomize-functions/example-cockroachdb:v0.1.0
|
||||
spec:
|
||||
|
||||
@@ -18,7 +18,7 @@ the `API` struct definition in [main.go](image/main.go) for documentation.
|
||||
## Function invocation
|
||||
|
||||
The function is invoked by authoring a [local Resource](local-resource)
|
||||
with `metadata.annotations.[config.k8s.io/function]` and running:
|
||||
with `metadata.annotations.[config.kubernetes.io/function]` and running:
|
||||
|
||||
kustomize config run local-resource/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ apiVersion: examples.config.kubernetes.io/v1beta1
|
||||
kind: Kubeval
|
||||
metadata:
|
||||
annotations:
|
||||
config.k8s.io/function: |
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
image: gcr.io/kustomize-functions/example-validator-kubeval:v0.1.0
|
||||
spec:
|
||||
|
||||
@@ -15,7 +15,7 @@ Resource configuration, and looks for invalid configuration.
|
||||
## Function invocation
|
||||
|
||||
The function is invoked by authoring a [local Resource](local-resource)
|
||||
with `metadata.annotations.[config.k8s.io/function]` and running:
|
||||
with `metadata.annotations.[config.kubernetes.io/function]` and running:
|
||||
|
||||
kustomize config run local-resource/
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ apiVersion: examples.config.kubernetes.io/v1beta1
|
||||
kind: Validator
|
||||
metadata:
|
||||
annotations:
|
||||
config.k8s.io/function: |
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
image: gcr.io/kustomize-functions/example-validator:v0.1.0
|
||||
---
|
||||
|
||||
@@ -6,35 +6,17 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/complete"
|
||||
"sigs.k8s.io/kustomize/kustomize/v3/internal/commands"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := commands.NewDefaultCommand()
|
||||
completion(cmd)
|
||||
complete.Complete(cmd).Complete("kustomize")
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// completion performs shell completion if kustomize is being called to provide
|
||||
// shell completion commands.
|
||||
func completion(cmd *cobra.Command) {
|
||||
// bash shell completion passes the command name as the first argument
|
||||
// do this after configuring cmd so it has all the subcommands
|
||||
if len(os.Args) > 1 {
|
||||
// use the base name in case kustomize is called with an absolute path
|
||||
name := filepath.Base(os.Args[1])
|
||||
if name == "kustomize" {
|
||||
// complete calls kustomize with itself as an argument
|
||||
complete.Complete(cmd).Complete("kustomize")
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,6 +194,13 @@ func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
const (
|
||||
FunctionAnnotationKey = "config.kubernetes.io/function"
|
||||
oldFunctionAnnotationKey = "config.k8s.io/function"
|
||||
)
|
||||
|
||||
var functionAnnotationKeys = []string{FunctionAnnotationKey, oldFunctionAnnotationKey}
|
||||
|
||||
// GetContainerName returns the container image for an API if one exists
|
||||
func GetContainerName(n *yaml.RNode) (string, string) {
|
||||
meta, _ := n.GetMeta()
|
||||
@@ -201,11 +208,14 @@ func GetContainerName(n *yaml.RNode) (string, string) {
|
||||
// path to the function, this will be mounted into the container
|
||||
path := meta.Annotations[kioutil.PathAnnotation]
|
||||
|
||||
functionAnnotation := meta.Annotations["config.k8s.io/function"]
|
||||
if functionAnnotation != "" {
|
||||
annotationContent, _ := yaml.Parse(functionAnnotation)
|
||||
image, _ := annotationContent.Pipe(yaml.Lookup("container", "image"))
|
||||
return image.YNode().Value, path
|
||||
// check previous keys for backwards compatibility
|
||||
for _, s := range functionAnnotationKeys {
|
||||
functionAnnotation := meta.Annotations[s]
|
||||
if functionAnnotation != "" {
|
||||
annotationContent, _ := yaml.Parse(functionAnnotation)
|
||||
image, _ := annotationContent.Pipe(yaml.Lookup("container", "image"))
|
||||
return image.YNode().Value, path
|
||||
}
|
||||
}
|
||||
|
||||
container := meta.Annotations["config.kubernetes.io/container"]
|
||||
|
||||
@@ -333,12 +333,12 @@ metadata:
|
||||
c, _ = GetContainerName(n)
|
||||
assert.Equal(t, "gcr.io/foo/bar:something", c)
|
||||
|
||||
// container from config.k8s.io/function annotation
|
||||
// container from config.kubernetes.io/function annotation
|
||||
n, err = yaml.Parse(`apiVersion: v1
|
||||
kind: MyThing
|
||||
metadata:
|
||||
annotations:
|
||||
config.k8s.io/function: |
|
||||
config.kubernetes.io/function: |
|
||||
container:
|
||||
image: gcr.io/foo/bar:something
|
||||
`)
|
||||
|
||||
@@ -20,9 +20,6 @@ const (
|
||||
|
||||
// PathAnnotation records the path to the file the Resource was read from
|
||||
PathAnnotation AnnotationKey = "config.kubernetes.io/path"
|
||||
|
||||
// PackageAnnotation records the name of the package the Resource was read from
|
||||
PackageAnnotation AnnotationKey = "config.kubernetes.io/package"
|
||||
)
|
||||
|
||||
func GetFileAnnotations(rn *yaml.RNode) (string, string, error) {
|
||||
|
||||
@@ -263,7 +263,6 @@ func (r *LocalPackageReader) initReaderAnnotations(path string, _ os.FileInfo) {
|
||||
r.SetAnnotations = map[string]string{}
|
||||
}
|
||||
if !r.OmitReaderAnnotations {
|
||||
r.SetAnnotations[kioutil.PackageAnnotation] = filepath.Dir(path)
|
||||
r.SetAnnotations[kioutil.PathAnnotation] = path
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,14 +109,12 @@ func TestLocalPackageReader_Read_pkg(t *testing.T) {
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
`,
|
||||
`# second thing
|
||||
@@ -128,7 +126,6 @@ g:
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'b_test.yaml'
|
||||
`,
|
||||
}
|
||||
@@ -171,14 +168,12 @@ func TestLocalPackageReader_Read_file(t *testing.T) {
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/package: '.'
|
||||
config.kubernetes.io/path: 'a_test.yaml'
|
||||
`,
|
||||
}
|
||||
@@ -270,14 +265,12 @@ func TestLocalPackageReader_Read_nestedDirs(t *testing.T) {
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/package: 'a${SEP}b'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/package: 'a${SEP}b'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`# second thing
|
||||
@@ -289,7 +282,6 @@ g:
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/package: 'a${SEP}b'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}b_test.yaml'
|
||||
`,
|
||||
}
|
||||
@@ -328,14 +320,12 @@ func TestLocalPackageReader_Read_matchRegex(t *testing.T) {
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/package: 'a${SEP}b'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/package: 'a${SEP}b'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
}
|
||||
@@ -371,14 +361,12 @@ func TestLocalPackageReader_Read_skipSubpackage(t *testing.T) {
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/package: 'a${SEP}b'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/package: 'a${SEP}b'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
}
|
||||
@@ -414,14 +402,12 @@ func TestLocalPackageReader_Read_includeSubpackage(t *testing.T) {
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/package: 'a${SEP}b'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/package: 'a${SEP}b'
|
||||
config.kubernetes.io/path: 'a${SEP}b${SEP}a_test.yaml'
|
||||
`,
|
||||
`# second thing
|
||||
@@ -433,7 +419,6 @@ g:
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/package: 'a${SEP}c'
|
||||
config.kubernetes.io/path: 'a${SEP}c${SEP}c_test.yaml'
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ func (r LocalPackageWriter) Write(nodes []*yaml.RNode) error {
|
||||
}
|
||||
|
||||
if !r.KeepReaderAnnotations {
|
||||
r.ClearAnnotations = append(r.ClearAnnotations, kioutil.PackageAnnotation)
|
||||
r.ClearAnnotations = append(r.ClearAnnotations, kioutil.PathAnnotation)
|
||||
}
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ func (p TreeWriter) index(nodes []*yaml.RNode) map[string][]*yaml.RNode {
|
||||
// not a resource
|
||||
continue
|
||||
}
|
||||
pkg := meta.Annotations[kioutil.PackageAnnotation]
|
||||
pkg := filepath.Dir(meta.Annotations[kioutil.PathAnnotation])
|
||||
indexByPackage[pkg] = append(indexByPackage[pkg], nodes[i])
|
||||
}
|
||||
return indexByPackage
|
||||
|
||||
@@ -21,8 +21,7 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx3
|
||||
config.kubernetes.io/package: foo-package/3
|
||||
config.kubernetes.io/path: f3.yaml
|
||||
config.kubernetes.io/path: foo-package/3/f3.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -34,8 +33,7 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: foo-package
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
config.kubernetes.io/path: foo-package/f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -45,8 +43,7 @@ metadata:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: bar-package
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
config.kubernetes.io/path: bar-package/f2.yaml
|
||||
name: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -57,8 +54,7 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: foo-package
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
config.kubernetes.io/path: foo-package/f1.yaml
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
@@ -94,7 +90,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx3
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f3.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -107,8 +102,7 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: foo-package
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
config.kubernetes.io/path: foo-package/f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
@@ -118,8 +112,8 @@ metadata:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: bar-package
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
config.kubernetes.io/package:
|
||||
config.kubernetes.io/path: bar-package/f2.yaml
|
||||
name: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -167,7 +161,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -181,7 +174,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -195,7 +187,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -209,7 +200,6 @@ metadata:
|
||||
namespace: default2
|
||||
annotations:
|
||||
app: nginx2
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -223,7 +213,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx3
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
replicas: 1
|
||||
@@ -234,8 +223,7 @@ metadata:
|
||||
app: nginx
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: bar-package
|
||||
config.kubernetes.io/path: f2.yaml
|
||||
config.kubernetes.io/path: bar-package/f2.yaml
|
||||
name: bar
|
||||
spec:
|
||||
replicas: 3
|
||||
@@ -246,7 +234,6 @@ metadata:
|
||||
namespace: default
|
||||
annotations:
|
||||
app: nginx
|
||||
config.kubernetes.io/package: .
|
||||
config.kubernetes.io/path: f1.yaml
|
||||
spec:
|
||||
selector:
|
||||
@@ -550,13 +537,12 @@ metadata:
|
||||
}
|
||||
|
||||
if !assert.Equal(t, `
|
||||
└──
|
||||
├── [.] Service myapp-staging/cockroachdb
|
||||
├── [.] StatefulSet myapp-staging/cockroachdb
|
||||
├── [.] Pod myapp-staging/cockroachdb-0
|
||||
├── [.] Pod myapp-staging/cockroachdb-1
|
||||
├── [.] Pod myapp-staging/cockroachdb-2
|
||||
└── [.] Application myapp-staging/myapp
|
||||
├── [.] Service myapp-staging/cockroachdb
|
||||
├── [.] StatefulSet myapp-staging/cockroachdb
|
||||
├── [.] Pod myapp-staging/cockroachdb-0
|
||||
├── [.] Pod myapp-staging/cockroachdb-1
|
||||
├── [.] Pod myapp-staging/cockroachdb-2
|
||||
└── [.] Application myapp-staging/myapp
|
||||
`, out.String()) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package yaml
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -70,6 +69,14 @@ func IsFieldEmpty(node *MapNode) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// GetValue returns underlying yaml.Node Value field
|
||||
func GetValue(node *RNode) string {
|
||||
if IsEmpty(node) {
|
||||
return ""
|
||||
}
|
||||
return node.YNode().Value
|
||||
}
|
||||
|
||||
func IsFieldNull(node *MapNode) bool {
|
||||
return node != nil && node.Value != nil && node.Value.YNode() != nil &&
|
||||
node.Value.YNode().Tag == NullNodeTag
|
||||
@@ -280,23 +287,79 @@ func (r *ResourceIdentifier) GetKind() string {
|
||||
|
||||
var ErrMissingMetadata = fmt.Errorf("missing Resource metadata")
|
||||
|
||||
// GetMeta returns the ResourceMeta for a RNode
|
||||
// Field names
|
||||
const (
|
||||
AnnotationsField = "annotations"
|
||||
APIVersionField = "apiVersion"
|
||||
KindField = "kind"
|
||||
MetadataField = "metadata"
|
||||
NameField = "name"
|
||||
NamespaceField = "namespace"
|
||||
LabelsField = "labels"
|
||||
)
|
||||
|
||||
// GetMeta returns the ResourceMeta for an RNode
|
||||
func (rn *RNode) GetMeta() (ResourceMeta, error) {
|
||||
if IsEmpty(rn) {
|
||||
return ResourceMeta{}, nil
|
||||
}
|
||||
missingMeta := true
|
||||
n := rn
|
||||
if n.YNode().Kind == DocumentNode {
|
||||
// get the content is this is the document node
|
||||
n = NewRNode(n.Content()[0])
|
||||
}
|
||||
|
||||
// don't decode into the struct directly or it will fail on UTF-8 issues
|
||||
// which appear in comments
|
||||
m := ResourceMeta{}
|
||||
b := &bytes.Buffer{}
|
||||
e := NewEncoder(b)
|
||||
if err := e.Encode(rn.YNode()); err != nil {
|
||||
return m, errors.Wrap(err)
|
||||
|
||||
// TODO: consider optimizing this parsing
|
||||
if f := n.Field(APIVersionField); !IsFieldEmpty(f) {
|
||||
m.APIVersion = GetValue(f.Value)
|
||||
missingMeta = false
|
||||
}
|
||||
if err := e.Close(); err != nil {
|
||||
return m, errors.Wrap(err)
|
||||
if f := n.Field(KindField); !IsFieldEmpty(f) {
|
||||
m.Kind = GetValue(f.Value)
|
||||
missingMeta = false
|
||||
}
|
||||
d := yaml.NewDecoder(b)
|
||||
d.KnownFields(false) // only want to parse the metadata
|
||||
if err := d.Decode(&m); err != nil {
|
||||
return m, errors.Wrap(err)
|
||||
|
||||
mf := n.Field(MetadataField)
|
||||
if IsFieldEmpty(mf) {
|
||||
if missingMeta {
|
||||
return m, ErrMissingMetadata
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
if reflect.DeepEqual(m, ResourceMeta{}) {
|
||||
meta := mf.Value
|
||||
|
||||
if f := meta.Field(NameField); !IsFieldEmpty(f) {
|
||||
m.Name = f.Value.YNode().Value
|
||||
missingMeta = false
|
||||
}
|
||||
if f := meta.Field(NamespaceField); !IsFieldEmpty(f) {
|
||||
m.Namespace = GetValue(f.Value)
|
||||
missingMeta = false
|
||||
}
|
||||
|
||||
if f := meta.Field(LabelsField); !IsFieldEmpty(f) {
|
||||
m.Labels = map[string]string{}
|
||||
_ = f.Value.VisitFields(func(node *MapNode) error {
|
||||
m.Labels[GetValue(node.Key)] = GetValue(node.Value)
|
||||
return nil
|
||||
})
|
||||
missingMeta = false
|
||||
}
|
||||
if f := meta.Field(AnnotationsField); !IsFieldEmpty(f) {
|
||||
m.Annotations = map[string]string{}
|
||||
_ = f.Value.VisitFields(func(node *MapNode) error {
|
||||
m.Annotations[GetValue(node.Key)] = GetValue(node.Value)
|
||||
return nil
|
||||
})
|
||||
missingMeta = false
|
||||
}
|
||||
|
||||
if missingMeta {
|
||||
return m, ErrMissingMetadata
|
||||
}
|
||||
return m, nil
|
||||
|
||||
49
kyaml/yaml/types_test.go
Normal file
49
kyaml/yaml/types_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Test that non-UTF8 characters in comments don't cause failures
|
||||
func TestRNode_GetMeta_UTF16(t *testing.T) {
|
||||
sr, err := Parse(`apiVersion: rbac.istio.io/v1alpha1
|
||||
kind: ServiceRole
|
||||
metadata:
|
||||
name: wildcard
|
||||
namespace: default
|
||||
# If set to [“*”], it refers to all services in the namespace
|
||||
annotations:
|
||||
foo: bar
|
||||
spec:
|
||||
rules:
|
||||
# There is one service in default namespace, should not result in a validation error
|
||||
# If set to [“*”], it refers to all services in the namespace
|
||||
- services: ["*"]
|
||||
methods: ["GET", "HEAD"]
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
actual, err := sr.GetMeta()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := ResourceMeta{
|
||||
APIVersion: "rbac.istio.io/v1alpha1",
|
||||
Kind: "ServiceRole",
|
||||
ObjectMeta: ObjectMeta{
|
||||
Name: "wildcard",
|
||||
Namespace: "default",
|
||||
Annotations: map[string]string{"foo": "bar"},
|
||||
},
|
||||
}
|
||||
if !assert.Equal(t, expected, actual) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user