Skip to content

Commit aac5750

Browse files
authored
Handle empty input gracefully (#756)
* handle resource list being 0 bytes Signed-off-by: Mózes László Máté <laszlo.mozes@nokia.com> * handle empty function input gracefully Signed-off-by: Mózes László Máté <laszlo.mozes@nokia.com> * fix lint Signed-off-by: Mózes László Máté <laszlo.mozes@nokia.com> * address copilot comment Signed-off-by: Mózes László Máté <laszlo.mozes@nokia.com> --------- Signed-off-by: Mózes László Máté <laszlo.mozes@nokia.com>
1 parent ade572b commit aac5750

4 files changed

Lines changed: 72 additions & 3 deletions

File tree

go/fn/object.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ func IsMetaResource() func(*KubeObject) bool {
730730
}
731731

732732
func (o *KubeObject) IsEmpty() bool {
733-
return yaml.IsYNodeEmptyMap(o.obj.Node())
733+
return o == nil || o.obj == nil || yaml.IsYNodeEmptyMap(o.obj.Node())
734734
}
735735

736736
func NewEmptyKubeObject() *KubeObject {

go/fn/resourcelist.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package fn
1717

1818
import (
19+
"bytes"
1920
"fmt"
2021
"reflect"
2122
"sort"
@@ -90,9 +91,18 @@ func CheckResourceDuplication(rl *ResourceList) error {
9091
// or KRM fn output
9192
func ParseResourceList(in []byte) (*ResourceList, error) {
9293
rl := &ResourceList{}
94+
95+
// handle empty input
96+
if len(bytes.TrimSpace(in)) == 0 {
97+
rl.Items = make(KubeObjects, 0)
98+
rl.Results = make(Results, 0)
99+
rl.FunctionConfig = NewEmptyKubeObject()
100+
return rl, nil
101+
}
102+
93103
rlObj, err := ParseKubeObject(in)
94104
if err != nil {
95-
return nil, fmt.Errorf("failed to parse input bytes: %w", err)
105+
return nil, fmt.Errorf("failed to parse ResourceList as a single KubeObject: %w", err)
96106
}
97107
if rlObj.GetKind() != kio.ResourceListKind {
98108
return nil, fmt.Errorf("input was of unexpected kind %q; expected ResourceList", rlObj.GetKind())
@@ -172,7 +182,7 @@ func (rl *ResourceList) toYNode() (*yaml.Node, error) {
172182
return nil, err
173183
}
174184
}
175-
if !rl.FunctionConfig.IsEmpty() {
185+
if rl.FunctionConfig != nil && !rl.FunctionConfig.IsEmpty() {
176186
if err := reMap.SetNestedMap(rl.FunctionConfig.node(), "functionConfig"); err != nil {
177187
return nil, err
178188
}

go/fn/resourcelist_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
package fn
1616

1717
import (
18+
"fmt"
1819
"testing"
1920

2021
"github.com/google/go-cmp/cmp"
22+
"github.com/stretchr/testify/require"
23+
"sigs.k8s.io/kustomize/kyaml/kio"
2124
)
2225

2326
var dupResourceInput = []byte(`
@@ -78,3 +81,24 @@ results:
7881
t.Fatalf("unexpected diff: %v", cmp.Diff(expected, rl.Results))
7982
}
8083
}
84+
85+
func TestParseZeroBytesResourceList(t *testing.T) {
86+
rl, err := ParseResourceList([]byte{})
87+
require.NoError(t, err)
88+
require.NotNil(t, rl)
89+
}
90+
91+
// This caused a panic before
92+
func TestEmptyToYAML(t *testing.T) {
93+
rl := &ResourceList{}
94+
95+
var out []byte
96+
var err error
97+
require.NotPanics(t, func() {
98+
out, err = rl.ToYAML()
99+
})
100+
101+
require.NoError(t, err)
102+
expected := fmt.Sprintf("apiVersion: %s\nkind: %s\n", kio.ResourceListAPIVersion, kio.ResourceListKind)
103+
require.Equal(t, expected, string(out))
104+
}

go/fn/run_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2026 The kpt Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fn
16+
17+
import (
18+
"fmt"
19+
"testing"
20+
21+
"github.com/stretchr/testify/assert"
22+
"github.com/stretchr/testify/require"
23+
"sigs.k8s.io/kustomize/kyaml/kio"
24+
)
25+
26+
func TestRunEmptyInputBytes(t *testing.T) {
27+
var noOpFn ResourceListProcessorFunc = func(rl *ResourceList) (bool, error) {
28+
return true, nil
29+
}
30+
31+
output, err := Run(noOpFn, []byte{})
32+
require.NoError(t, err)
33+
expected := fmt.Appendf(nil, "apiVersion: %s\nkind: %s\n", kio.ResourceListAPIVersion, kio.ResourceListKind)
34+
assert.Equal(t, expected, output)
35+
}

0 commit comments

Comments
 (0)