mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
Handle parsing of bare sequence yaml nodes
This commit is contained in:
@@ -43,6 +43,13 @@ type ByteReadWriter struct {
|
||||
// Style is a style that is set on the Resource Node Document.
|
||||
Style yaml.Style
|
||||
|
||||
// WrapBareSeqNode wraps the bare sequence node document with map node,
|
||||
// kyaml uses reader annotations to track resources, it is not possible to
|
||||
// add them to bare sequence nodes, this option enables wrapping such bare
|
||||
// sequence nodes into map node with key yaml.BareSeqNodeWrappingKey
|
||||
// note that this wrapping is different and not related to ResourceList wrapping
|
||||
WrapBareSeqNode bool
|
||||
|
||||
FunctionConfig *yaml.RNode
|
||||
|
||||
Results *yaml.RNode
|
||||
@@ -57,6 +64,7 @@ func (rw *ByteReadWriter) Read() ([]*yaml.RNode, error) {
|
||||
Reader: rw.Reader,
|
||||
OmitReaderAnnotations: rw.OmitReaderAnnotations,
|
||||
PreserveSeqIndent: rw.PreserveSeqIndent,
|
||||
WrapBareSeqNode: rw.WrapBareSeqNode,
|
||||
}
|
||||
val, err := b.Read()
|
||||
if rw.FunctionConfig == nil {
|
||||
@@ -137,6 +145,13 @@ type ByteReader struct {
|
||||
// WrappingKind is set by Read(), and is the kind of the object that
|
||||
// the read objects were originally wrapped in.
|
||||
WrappingKind string
|
||||
|
||||
// WrapBareSeqNode wraps the bare sequence node document with map node,
|
||||
// kyaml uses reader annotations to track resources, it is not possible to
|
||||
// add them to bare sequence nodes, this option enables wrapping such bare
|
||||
// sequence nodes into map node with key yaml.BareSeqNodeWrappingKey
|
||||
// note that this wrapping is different and not related to ResourceList wrapping
|
||||
WrapBareSeqNode bool
|
||||
}
|
||||
|
||||
var _ Reader = &ByteReader{}
|
||||
@@ -208,6 +223,10 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if r.WrapBareSeqNode {
|
||||
// continue reading other resources if failed to parse a resource
|
||||
continue
|
||||
}
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
if yaml.IsMissingOrNull(node) {
|
||||
@@ -275,6 +294,14 @@ func (r *ByteReader) decode(originalYAML string, index int, decoder *yaml.Decode
|
||||
// sort the annotations by key so the output Resources is consistent (otherwise the
|
||||
// annotations will be in a random order)
|
||||
n := yaml.NewRNode(node)
|
||||
wrappedNode := yaml.NewRNode(&yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
})
|
||||
if r.WrapBareSeqNode && node.Kind == yaml.DocumentNode && node.Content[0].Kind == yaml.SequenceNode {
|
||||
wrappedNode.PipeE(yaml.SetField(yaml.BareSeqNodeWrappingKey, n))
|
||||
} else {
|
||||
wrappedNode = n
|
||||
}
|
||||
if r.SetAnnotations == nil {
|
||||
r.SetAnnotations = map[string]string{}
|
||||
}
|
||||
@@ -295,10 +322,10 @@ func (r *ByteReader) decode(originalYAML string, index int, decoder *yaml.Decode
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
_, err = n.Pipe(yaml.SetAnnotation(k, r.SetAnnotations[k]))
|
||||
_, err = wrappedNode.Pipe(yaml.SetAnnotation(k, r.SetAnnotations[k]))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
return yaml.NewRNode(node), nil
|
||||
return wrappedNode, nil
|
||||
}
|
||||
|
||||
@@ -589,3 +589,153 @@ env:
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteReadWriter_WrapBareSeqNode(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
readerErr string
|
||||
writerErr string
|
||||
input string
|
||||
wrapBareSeqNode bool
|
||||
expectedOutput string
|
||||
instance kio.ByteReadWriter
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "round_trip bare seq node simple",
|
||||
wrapBareSeqNode: true,
|
||||
input: `
|
||||
- foo
|
||||
- bar
|
||||
`,
|
||||
expectedOutput: `
|
||||
- foo
|
||||
- bar
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "round_trip bare seq node",
|
||||
wrapBareSeqNode: true,
|
||||
input: `# Use the old CRD because of the quantity validation issue:
|
||||
# https://github.com/kubeflow/kubeflow/issues/5722
|
||||
- op: replace
|
||||
path: /spec
|
||||
value:
|
||||
group: kubeflow.org
|
||||
names:
|
||||
kind: Notebook
|
||||
plural: notebooks
|
||||
singular: notebook
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: false
|
||||
`,
|
||||
expectedOutput: `# Use the old CRD because of the quantity validation issue:
|
||||
# https://github.com/kubeflow/kubeflow/issues/5722
|
||||
- op: replace
|
||||
path: /spec
|
||||
value:
|
||||
group: kubeflow.org
|
||||
names:
|
||||
kind: Notebook
|
||||
plural: notebooks
|
||||
singular: notebook
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: false
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "error round_trip bare seq node simple",
|
||||
wrapBareSeqNode: false,
|
||||
input: `
|
||||
- foo
|
||||
- bar
|
||||
`,
|
||||
readerErr: "wrong Node Kind for expected: MappingNode was SequenceNode",
|
||||
},
|
||||
{
|
||||
name: "error k fround_trip bare seq node",
|
||||
wrapBareSeqNode: false,
|
||||
input: `# Use the old CRD because of the quantity validation issue:
|
||||
# https://github.com/kubeflow/kubeflow/issues/5722
|
||||
- op: replace
|
||||
path: /spec
|
||||
value:
|
||||
group: kubeflow.org
|
||||
names:
|
||||
kind: Notebook
|
||||
plural: notebooks
|
||||
singular: notebook
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: false
|
||||
`,
|
||||
readerErr: "wrong Node Kind for expected: MappingNode was SequenceNode",
|
||||
},
|
||||
{
|
||||
name: "round_trip bare seq node json",
|
||||
wrapBareSeqNode: true,
|
||||
input: `[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--namespaced"}]`,
|
||||
expectedOutput: `[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--namespaced"}]`,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tc := testCases[i]
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var in, out bytes.Buffer
|
||||
in.WriteString(tc.input)
|
||||
w := tc.instance
|
||||
w.Writer = &out
|
||||
w.Reader = &in
|
||||
w.PreserveSeqIndent = true
|
||||
w.WrapBareSeqNode = tc.wrapBareSeqNode
|
||||
|
||||
nodes, err := w.Read()
|
||||
if tc.readerErr != "" {
|
||||
if !assert.Error(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Contains(t, err.Error(), tc.readerErr) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.WrappingKind = ""
|
||||
err = w.Write(nodes)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if tc.writerErr != "" {
|
||||
if !assert.Error(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Contains(t, err.Error(), tc.writerErr) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String())) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
@@ -113,7 +114,7 @@ func (w ByteWriter) Write(inputNodes []*yaml.RNode) error {
|
||||
} else {
|
||||
encoder.CompactSeqIndent()
|
||||
}
|
||||
if err := encoder.Encode(nodes[i].Document()); err != nil {
|
||||
if err := encoder.Encode(upWrapBareSequenceNode(nodes[i].Document())); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
@@ -181,3 +182,13 @@ func (w ByteWriter) shouldJSONEncodeSingleBareNode(nodes []*yaml.RNode) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// upWrapBareSequenceNode unwraps the bare sequence nodes wrapped by yaml.BareSeqNodeWrappingKey
|
||||
func upWrapBareSequenceNode(node *yaml.Node) *yaml.Node {
|
||||
rNode := yaml.NewRNode(node)
|
||||
seqNode, err := rNode.Pipe(yaml.Lookup(yaml.BareSeqNodeWrappingKey))
|
||||
if err == nil && !seqNode.IsNilOrEmpty() {
|
||||
return seqNode.YNode()
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
@@ -82,6 +82,13 @@ type LocalPackageReadWriter struct {
|
||||
|
||||
// FileSystem can be used to mock the disk file system.
|
||||
FileSystem filesys.FileSystemOrOnDisk
|
||||
|
||||
// WrapBareSeqNode wraps the bare sequence node document with map node,
|
||||
// kyaml uses reader annotations to track resources, it is not possible to
|
||||
// add them to bare sequence nodes, this option enables wrapping such bare
|
||||
// sequence nodes into map node with key yaml.BareSeqNodeWrappingKey
|
||||
// note that this wrapping is different and not related to ResourceList wrapping
|
||||
WrapBareSeqNode bool
|
||||
}
|
||||
|
||||
func (r *LocalPackageReadWriter) Read() ([]*yaml.RNode, error) {
|
||||
@@ -95,6 +102,7 @@ func (r *LocalPackageReadWriter) Read() ([]*yaml.RNode, error) {
|
||||
FileSkipFunc: r.FileSkipFunc,
|
||||
PreserveSeqIndent: r.PreserveSeqIndent,
|
||||
FileSystem: r.FileSystem,
|
||||
WrapBareSeqNode: r.WrapBareSeqNode,
|
||||
}.Read()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
@@ -193,6 +201,13 @@ type LocalPackageReader struct {
|
||||
|
||||
// FileSystem can be used to mock the disk file system.
|
||||
FileSystem filesys.FileSystemOrOnDisk
|
||||
|
||||
// WrapBareSeqNode wraps the bare sequence node document with map node,
|
||||
// kyaml uses reader annotations to track resources, it is not possible to
|
||||
// add them to bare sequence nodes, this option enables wrapping such bare
|
||||
// sequence nodes into map node with key yaml.BareSeqNodeWrappingKey
|
||||
// note that this wrapping is different and not related to ResourceList wrapping
|
||||
WrapBareSeqNode bool
|
||||
}
|
||||
|
||||
var _ Reader = LocalPackageReader{}
|
||||
@@ -287,6 +302,7 @@ func (r *LocalPackageReader) readFile(path string, _ os.FileInfo) ([]*yaml.RNode
|
||||
OmitReaderAnnotations: r.OmitReaderAnnotations,
|
||||
SetAnnotations: r.SetAnnotations,
|
||||
PreserveSeqIndent: r.PreserveSeqIndent,
|
||||
WrapBareSeqNode: r.WrapBareSeqNode,
|
||||
}
|
||||
return rr.Read()
|
||||
}
|
||||
|
||||
@@ -39,6 +39,13 @@ a: b #forth
|
||||
metadata:
|
||||
`)
|
||||
|
||||
var readFileE = []byte(`---
|
||||
a: b #first
|
||||
---
|
||||
- foo # second
|
||||
- bar
|
||||
`)
|
||||
|
||||
var pkgFile = []byte(``)
|
||||
|
||||
func TestLocalPackageReader_Read_empty(t *testing.T) {
|
||||
@@ -555,3 +562,41 @@ func testOnDiskAndOnMem(t *testing.T, files []mockFile, f func(t *testing.T, pat
|
||||
f(t, "/", fs)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLocalPackageReader_ReadBareSeqNodes(t *testing.T) {
|
||||
testOnDiskAndOnMem(t, []mockFile{
|
||||
{path: "a/b"},
|
||||
{path: "a/c"},
|
||||
{path: "e_test.yaml", content: readFileE},
|
||||
}, func(t *testing.T, path string, mockFS filesys.FileSystem) {
|
||||
rfr := LocalPackageReader{
|
||||
PackagePath: path,
|
||||
FileSystem: filesys.FileSystemOrOnDisk{FileSystem: mockFS},
|
||||
WrapBareSeqNode: true,
|
||||
}
|
||||
nodes, err := rfr.Read()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, nodes, 2)
|
||||
expected := []string{
|
||||
`a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
config.kubernetes.io/path: 'e_test.yaml'
|
||||
`,
|
||||
`bareSeqNodeWrappingKey:
|
||||
- foo # second
|
||||
- bar
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
config.kubernetes.io/path: 'e_test.yaml'
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
val, err := nodes[i].String()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected[i], val)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ const (
|
||||
WideSequenceStyle SequenceIndentStyle = "wide"
|
||||
CompactSequenceStyle SequenceIndentStyle = "compact"
|
||||
DefaultIndent = 2
|
||||
// BareSeqNodeWrappingKey kyaml uses reader annotations to track resources, it is not possible to
|
||||
// add them to bare sequence nodes, this key is used to wrap such bare
|
||||
// sequence nodes into map node, byteio_writer unwraps it while writing back
|
||||
BareSeqNodeWrappingKey = "bareSeqNodeWrappingKey"
|
||||
)
|
||||
|
||||
// SeqIndentType holds the indentation style for sequence nodes
|
||||
|
||||
Reference in New Issue
Block a user