Skip to content

Commit

Permalink
Fix null YAML values being replaced by "null"
Browse files Browse the repository at this point in the history
Related issues:

* kubernetes-sigs#5031
* kubernetes-sigs#5171

After noting this behaviour was not present in
d89b448 a `git bisect` pointed to the
change 1b7db20. The issue with that
change is that upon seeing a `null` node it would replace it with a node
whose value was equivalent but without a `!!null` tag. This meant that
one application of a patch would have the desired approach: the result
would be `null` in the output, but on a second application of a similar
patch the field would be rendered as `"null"`.

To avoid this, define a special flag node that is `null` but never
removed during merges. The added `TestApplySmPatch_Idempotency` test
verifies this behaviour. However, this approach  will may change the
value of the node in the output, e.g. if originally there was `field: ~`
it would be replaced by `field: null`. The added test case in
`kyaml/yaml/merge2/scalar_test.go` demonstrates this behaviour (this
test currently fails as I expect the desired outcome is to preserve the
null marker)

See also kubernetes-sigs#5365 for an
alternative approach
  • Loading branch information
matthewhughes934 committed Jan 28, 2024
1 parent dd49bd4 commit 3532f6b
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 3 deletions.
35 changes: 35 additions & 0 deletions api/resource/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/api/internal/utils"
"sigs.k8s.io/kustomize/api/provider"
. "sigs.k8s.io/kustomize/api/resource"
Expand Down Expand Up @@ -281,6 +282,40 @@ spec:
`, string(bytes))
}

func TestApplySmPatch_Idempotency(t *testing.T) {
patchApplyCount := 2
resourceYaml := `apiVersion: v1
kind: Deployment
metadata:
labels: null
name: my-deployment
`
resource, err := factory.FromBytes([]byte(resourceYaml))
require.NoError(t, err)

noOpPatch, err := factory.FromBytes([]byte(`
apiVersion: v1
kind: Deployment
metadata:
name: my-deployment
`))
require.NoError(t, err)

for i := 0; i < patchApplyCount; i++ {
require.NoError(t, resource.ApplySmPatch(noOpPatch))

bytes, err := resource.AsYAML()
require.NoError(t, err)

require.Equal(
t,
resourceYaml,
string(bytes),
"resource should be unchanged after re-application of patch",
)
}
}

func TestApplySmPatchShouldOutputListItemsInCorrectOrder(t *testing.T) {
cases := []struct {
name string
Expand Down
5 changes: 4 additions & 1 deletion kyaml/yaml/fns.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import (
yaml "sigs.k8s.io/yaml/goyaml.v3"
)

// KeepNullNode is used to persist a "null" node
var KeepNullNode = NewRNode(&Node{Tag: NodeTagNull, Value: "null", Kind: yaml.ScalarNode})

// Append creates an ElementAppender
func Append(elements ...*yaml.Node) ElementAppender {
return ElementAppender{Elements: elements}
Expand Down Expand Up @@ -725,7 +728,7 @@ func (s FieldSetter) Filter(rn *RNode) (*RNode, error) {
}

// Clear the field if it is empty, or explicitly null
if s.Value == nil || s.Value.IsTaggedNull() {
if (s.Value == nil || s.Value.IsTaggedNull()) && s.Value != KeepNullNode {
return rn.Pipe(Clear(s.Name))
}

Expand Down
3 changes: 1 addition & 2 deletions kyaml/yaml/merge2/merge2.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ func (m Merger) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.R

// If Origin is missing, preserve explicitly set null in Dest ("null", "~", etc)
if nodes.Origin().IsNil() && !nodes.Dest().IsNil() && len(nodes.Dest().YNode().Value) > 0 {
// Return a new node so that it won't have a "!!null" tag and therefore won't be cleared.
return yaml.NewScalarRNode(nodes.Dest().YNode().Value), nil
return yaml.KeepNullNode, nil
}

return nodes.Origin(), nil
Expand Down
16 changes: 16 additions & 0 deletions kyaml/yaml/merge2/scalar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,22 @@ field: null
expected: `
kind: Deployment
field: null
`,
mergeOptions: yaml.MergeOptions{
ListIncreaseDirection: yaml.MergeOptionsListAppend,
},
},
{description: `keep scalar -- missing in src, null in dest, preserves null marker`,
source: `
kind: Deployment
`,
dest: `
kind: Deployment
field: ~
`,
expected: `
kind: Deployment
field: ~
`,
mergeOptions: yaml.MergeOptions{
ListIncreaseDirection: yaml.MergeOptionsListAppend,
Expand Down

0 comments on commit 3532f6b

Please sign in to comment.