Skip to content

Commit

Permalink
Optionally call RNode.deAnchor from ByteReader
Browse files Browse the repository at this point in the history
  • Loading branch information
monopole committed Sep 22, 2021
1 parent c47fc48 commit 448bad2
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 26 deletions.
16 changes: 12 additions & 4 deletions api/resource/factory.go
Expand Up @@ -143,7 +143,7 @@ func (rf *Factory) resourcesFromRNodes(
return
}

func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) {
func (rf *Factory) RNodesFromBytes(b []byte) ([]*yaml.RNode, error) {
nodes, err := kio.FromBytes(b)
if err != nil {
return nil, err
Expand All @@ -152,9 +152,17 @@ func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) {
if err != nil {
return nil, err
}
return rf.inlineAnyEmbeddedLists(nodes)
}

// inlineAnyEmbeddedLists scans the RNode slice for nodes named FooList.
// Such nodes are expected to be lists of resources, each of type Foo.
// These lists are replaced in the result by their inlined resources.
func (rf *Factory) inlineAnyEmbeddedLists(
nodes []*yaml.RNode) (result []*yaml.RNode, err error) {
var n0 *yaml.RNode
for len(nodes) > 0 {
n0 := nodes[0]
nodes = nodes[1:]
n0, nodes = nodes[0], nodes[1:]
kind := n0.GetKind()
if !strings.HasSuffix(kind, "List") {
result = append(result, n0)
Expand All @@ -164,7 +172,7 @@ func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) {
var m map[string]interface{}
m, err = n0.Map()
if err != nil {
return nil, err
return nil, fmt.Errorf("trouble expanding list of %s; %w", kind, err)
}
items, ok := m["items"]
if !ok {
Expand Down
7 changes: 2 additions & 5 deletions api/resource/factory_test.go
Expand Up @@ -639,17 +639,14 @@ data:
feeling: *color-used
`),
exp: expected{
// TODO(#3675) : the anchor should be replaced.
// Anchors are replaced in the List above due to a different code path
// (when the list is inlined).
out: []string{`
apiVersion: v1
kind: ConfigMap
metadata:
name: wildcard
data:
color: &color-used blue
feeling: *color-used
color: blue
feeling: blue
`},
},
},
Expand Down
12 changes: 12 additions & 0 deletions kyaml/kio/byteio_reader.go
Expand Up @@ -102,6 +102,7 @@ func ParseAll(inputs ...string) ([]*yaml.RNode, error) {
func FromBytes(bs []byte) ([]*yaml.RNode, error) {
return (&ByteReader{
OmitReaderAnnotations: true,
AnchorsAweigh: true,
Reader: bytes.NewBuffer(bs),
}).Read()
}
Expand Down Expand Up @@ -152,6 +153,10 @@ type ByteReader struct {
// sequence nodes into map node with key yaml.BareSeqNodeWrappingKey
// note that this wrapping is different and not related to ResourceList wrapping
WrapBareSeqNode bool

// AnchorsAweigh set to true attempts to replace all YAML anchor aliases
// with their definitions (anchor values) immediately after the read.
AnchorsAweigh bool
}

var _ Reader = &ByteReader{}
Expand Down Expand Up @@ -269,6 +274,13 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
// increment the index annotation value
index++
}
if r.AnchorsAweigh {
for _, n := range output {
if err = n.DeAnchor(); err != nil {
return nil, err
}
}
}
return output, nil
}

Expand Down
100 changes: 83 additions & 17 deletions kyaml/kio/byteio_reader_test.go
Expand Up @@ -758,7 +758,7 @@ items:
metadata:
name: deployment-b
spec:
<<: *hostAliases
*hostAliases
`),
exp: expected{
sOut: []string{`
Expand All @@ -769,7 +769,7 @@ items:
kind: Deployment
metadata:
name: deployment-a
spec: &hostAliases
spec:
template:
spec:
hostAliases:
Expand All @@ -781,7 +781,12 @@ items:
metadata:
name: deployment-b
spec:
!!merge <<: *hostAliases
template:
spec:
hostAliases:
- hostnames:
- a.example.com
ip: 8.8.8.8
`},
},
},
Expand All @@ -808,27 +813,26 @@ items:
}
}

// This test shows the lower level (go-yaml) representation of a small doc
// with an anchor. The anchor structure is there, in the sense that an
// alias pointer is readily available when a node's kind is an AliasNode.
// I.e. the anchor mapping name -> object was noted during unmarshalling.
// However, at the time of writing github.com/go-yaml/yaml/encoder.go
// doesn't appear to have an option to perform anchor replacements when
// encoding. It emits anchor definitions and references (aliases) intact.
func TestByteReader_AnchorBehavior(t *testing.T) {
// Show the low level (go-yaml) representation of a small doc with a
// YAML anchor and alias after reading it with anchor expansion on or off.
func TestByteReader_AnchorsAweigh(t *testing.T) {
const input = `
data:
color: &color-used blue
feeling: *color-used
`
expected := strings.TrimSpace(`
expectedWithAnchorsAweigh := strings.TrimSpace(`
data:
color: &color-used blue
feeling: *color-used
color: blue
feeling: blue
`)
var rNode *yaml.RNode
{
rNodes, err := FromBytes([]byte(input))
rNodes, err := (&ByteReader{
OmitReaderAnnotations: true,
AnchorsAweigh: false,
Reader: bytes.NewBuffer([]byte(input)),
}).Read()
assert.NoError(t, err)
assert.Equal(t, 1, len(rNodes))
rNode = rNodes[0]
Expand Down Expand Up @@ -879,9 +883,71 @@ data:
assert.NotNil(t, yNodes[3].Alias)
}

yaml, err := rNode.String()
str, err := rNode.String()
assert.NoError(t, err)
// The string version matches the input (it still has anchors and aliases).
assert.Equal(t, strings.TrimSpace(input), strings.TrimSpace(str))

// Do same thing again, but this time set AnchorsAweigh true.
{
rNodes, err := (&ByteReader{
OmitReaderAnnotations: true,
AnchorsAweigh: true,
Reader: bytes.NewBuffer([]byte(input)),
}).Read()
assert.NoError(t, err)
assert.Equal(t, 1, len(rNodes))
rNode = rNodes[0]
}
// Confirm internal representation.
{
yNode := rNode.YNode()

// The high level object is a map of "data" to some value.
assert.Equal(t, yaml.NodeTagMap, yNode.Tag)

yNodes := yNode.Content
assert.Equal(t, 2, len(yNodes))

// Confirm that the key is "data".
assert.Equal(t, yaml.NodeTagString, yNodes[0].Tag)
assert.Equal(t, "data", yNodes[0].Value)

assert.Equal(t, yaml.NodeTagMap, yNodes[1].Tag)

// The value of the "data" key.
yNodes = yNodes[1].Content
// Expect two name-value pairs.
assert.Equal(t, 4, len(yNodes))

assert.Equal(t, yaml.ScalarNode, yNodes[0].Kind)
assert.Equal(t, yaml.NodeTagString, yNodes[0].Tag)
assert.Equal(t, "color", yNodes[0].Value)
assert.Equal(t, "", yNodes[0].Anchor)
assert.Nil(t, yNodes[0].Alias)

assert.Equal(t, yaml.ScalarNode, yNodes[1].Kind)
assert.Equal(t, yaml.NodeTagString, yNodes[1].Tag)
assert.Equal(t, "blue", yNodes[1].Value)
assert.Empty(t, yNodes[1].Anchor)
assert.Nil(t, yNodes[1].Alias)

assert.Equal(t, yaml.ScalarNode, yNodes[2].Kind)
assert.Equal(t, yaml.NodeTagString, yNodes[2].Tag)
assert.Equal(t, "feeling", yNodes[2].Value)
assert.Equal(t, "", yNodes[2].Anchor)
assert.Nil(t, yNodes[2].Alias)

assert.Equal(t, yaml.ScalarNode, yNodes[3].Kind)
assert.Equal(t, yaml.NodeTagString, yNodes[3].Tag)
assert.Equal(t, "blue", yNodes[3].Value)
assert.Equal(t, "", yNodes[3].Anchor)
assert.Nil(t, yNodes[3].Alias)
}

str, err = rNode.String()
assert.NoError(t, err)
assert.Equal(t, expected, strings.TrimSpace(yaml))
assert.Equal(t, expectedWithAnchorsAweigh, strings.TrimSpace(str))
}

// TestByteReader_AddSeqIndentAnnotation tests if the internal.config.kubernetes.io/seqindent
Expand Down

0 comments on commit 448bad2

Please sign in to comment.