mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
fix(kyaml/yaml): minor nil safety fix for RNode.Content etc (#5985)
* Fix kyaml/yaml field access deref nil value for methods that look "nil-safe" This change is addressing observed panics within kustomize that obscure the actual failure. The primary observed problem case involves RNode.Content. * Fix test case * Fixes from review
This commit is contained in:
@@ -718,10 +718,11 @@ func (rn *RNode) MustString() string {
|
||||
|
||||
// Content returns Node Content field.
|
||||
func (rn *RNode) Content() []*yaml.Node {
|
||||
if rn == nil {
|
||||
yNode := rn.YNode()
|
||||
if yNode == nil {
|
||||
return nil
|
||||
}
|
||||
return rn.YNode().Content
|
||||
return yNode.Content
|
||||
}
|
||||
|
||||
// Fields returns the list of field names for a MappingNode.
|
||||
@@ -756,7 +757,11 @@ func (rn *RNode) FieldRNodes() ([]*RNode, error) {
|
||||
// Field returns a fieldName, fieldValue pair for MappingNodes.
|
||||
// Returns nil for non-MappingNodes.
|
||||
func (rn *RNode) Field(field string) *MapNode {
|
||||
if rn.YNode().Kind != yaml.MappingNode {
|
||||
yNode := rn.YNode()
|
||||
if yNode == nil {
|
||||
return nil
|
||||
}
|
||||
if yNode.Kind != yaml.MappingNode {
|
||||
return nil
|
||||
}
|
||||
var result *MapNode
|
||||
@@ -892,7 +897,11 @@ func (rn *RNode) ElementValuesList(keys []string) ([][]string, error) {
|
||||
// Element returns the element in the list which contains the field matching the value.
|
||||
// Returns nil for non-SequenceNodes or if no Element matches.
|
||||
func (rn *RNode) Element(key, value string) *RNode {
|
||||
if rn.YNode().Kind != yaml.SequenceNode {
|
||||
yNode := rn.YNode()
|
||||
if yNode == nil {
|
||||
return nil
|
||||
}
|
||||
if yNode.Kind != yaml.SequenceNode {
|
||||
return nil
|
||||
}
|
||||
elem, err := rn.Pipe(MatchElement(key, value))
|
||||
@@ -906,7 +915,11 @@ func (rn *RNode) Element(key, value string) *RNode {
|
||||
// corresponding values[i].
|
||||
// Returns nil for non-SequenceNodes or if no Element matches.
|
||||
func (rn *RNode) ElementList(keys []string, values []string) *RNode {
|
||||
if rn.YNode().Kind != yaml.SequenceNode {
|
||||
yNode := rn.YNode()
|
||||
if yNode == nil {
|
||||
return nil
|
||||
}
|
||||
if yNode.Kind != yaml.SequenceNode {
|
||||
return nil
|
||||
}
|
||||
elem, err := rn.Pipe(MatchElementList(keys, values))
|
||||
@@ -960,12 +973,17 @@ func (rn *RNode) GetAssociativeKey() string {
|
||||
|
||||
// MarshalJSON creates a byte slice from the RNode.
|
||||
func (rn *RNode) MarshalJSON() ([]byte, error) {
|
||||
yNode := rn.YNode()
|
||||
if yNode == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
|
||||
s, err := rn.String()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rn.YNode().Kind == SequenceNode {
|
||||
if yNode.Kind == SequenceNode {
|
||||
var a []interface{}
|
||||
if err := Unmarshal([]byte(s), &a); err != nil {
|
||||
return nil, err
|
||||
@@ -977,6 +995,7 @@ func (rn *RNode) MarshalJSON() ([]byte, error) {
|
||||
if err := Unmarshal([]byte(s), &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
|
||||
@@ -2356,3 +2356,70 @@ metadata:
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "hello-world-foo", fooAppName) // no field named 'foo.appname'
|
||||
}
|
||||
|
||||
func TestRNode_nilSafety(t *testing.T) {
|
||||
// Both of these scenarios should cause rn.YNode() to return nil.
|
||||
nodesToTest := [...]struct {
|
||||
name string
|
||||
rn *RNode
|
||||
}{
|
||||
{"nil *RNode receiver", nil},
|
||||
{"RNode with nil internal node", &RNode{value: nil}},
|
||||
}
|
||||
|
||||
t.Run("Content", func(t *testing.T) {
|
||||
for _, tc := range nodesToTest {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.NotPanics(t, func() {
|
||||
assert.Nil(t, tc.rn.Content())
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Field", func(t *testing.T) {
|
||||
for _, tc := range nodesToTest {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.NotPanics(t, func() {
|
||||
assert.Nil(t, tc.rn.Field("any-field"))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Element", func(t *testing.T) {
|
||||
for _, tc := range nodesToTest {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.NotPanics(t, func() {
|
||||
assert.Nil(t, tc.rn.Element("any-key", "any-value"))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ElementList", func(t *testing.T) {
|
||||
for _, tc := range nodesToTest {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
assert.NotPanics(t, func() {
|
||||
assert.Nil(t, tc.rn.ElementList([]string{"any-key"}, []string{"any-value"}))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MarshalJSON", func(t *testing.T) {
|
||||
for _, tc := range nodesToTest {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
jsonData []byte
|
||||
err error
|
||||
)
|
||||
assert.NotPanics(t, func() {
|
||||
jsonData, err = tc.rn.MarshalJSON()
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []byte("null"), jsonData)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user