Skip to content

Commit 2473331

Browse files
committed
Add tests for patch/resource reading.
1 parent fbc38d0 commit 2473331

File tree

4 files changed

+269
-45
lines changed

4 files changed

+269
-45
lines changed

pkg/resmap/resmap.go

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,16 @@ package resmap
2020
import (
2121
"bytes"
2222
"fmt"
23-
"io"
2423
"reflect"
2524
"sort"
26-
"strings"
2725

2826
"github.com/ghodss/yaml"
2927
"github.com/golang/glog"
3028
"github.com/pkg/errors"
3129
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
32-
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
3330
"sigs.k8s.io/kustomize/pkg/gvk"
3431
internal "sigs.k8s.io/kustomize/pkg/internal/error"
3532
"sigs.k8s.io/kustomize/pkg/loader"
36-
"sigs.k8s.io/kustomize/pkg/patch"
3733
"sigs.k8s.io/kustomize/pkg/resource"
3834
)
3935

@@ -154,24 +150,6 @@ func (m ResMap) FilterBy(inputId resource.ResId) ResMap {
154150
return result
155151
}
156152

157-
// NewResourceSliceFromPatches returns a slice of resources given a patch path slice from a kustomization file.
158-
func NewResourceSliceFromPatches(
159-
loader loader.Loader, paths []patch.StrategicMerge) ([]*resource.Resource, error) {
160-
var result []*resource.Resource
161-
for _, path := range paths {
162-
content, err := loader.Load(string(path))
163-
if err != nil {
164-
return nil, err
165-
}
166-
res, err := newResourceSliceFromBytes(content)
167-
if err != nil {
168-
return nil, internal.Handler(err, string(path))
169-
}
170-
result = append(result, res...)
171-
}
172-
return result, nil
173-
}
174-
175153
// NewResMapFromFiles returns a ResMap given a resource path slice.
176154
func NewResMapFromFiles(loader loader.Loader, paths []string) (ResMap, error) {
177155
var result []ResMap
@@ -191,7 +169,7 @@ func NewResMapFromFiles(loader loader.Loader, paths []string) (ResMap, error) {
191169

192170
// newResMapFromBytes decodes a list of objects in byte array format.
193171
func newResMapFromBytes(b []byte) (ResMap, error) {
194-
resources, err := newResourceSliceFromBytes(b)
172+
resources, err := resource.NewResourceSliceFromBytes(b)
195173
if err != nil {
196174
return nil, err
197175
}
@@ -219,23 +197,6 @@ func newResMapFromResourceSlice(resources []*resource.Resource) (ResMap, error)
219197
return result, nil
220198
}
221199

222-
func newResourceSliceFromBytes(in []byte) ([]*resource.Resource, error) {
223-
decoder := k8syaml.NewYAMLOrJSONDecoder(bytes.NewReader(in), 1024)
224-
var result []*resource.Resource
225-
var err error
226-
for err == nil || isEmptyYamlError(err) {
227-
var out unstructured.Unstructured
228-
err = decoder.Decode(&out)
229-
if err == nil {
230-
result = append(result, resource.NewResourceFromUnstruct(out))
231-
}
232-
}
233-
if err != io.EOF {
234-
return nil, err
235-
}
236-
return result, nil
237-
}
238-
239200
// MergeWithoutOverride combines multiple ResMap instances, failing on key collision
240201
// and skipping nil maps. In case if all of the maps are nil, an empty ResMap is returned.
241202
func MergeWithoutOverride(maps ...ResMap) (ResMap, error) {
@@ -303,7 +264,3 @@ func MergeWithOverride(maps ...ResMap) (ResMap, error) {
303264
}
304265
return result, nil
305266
}
306-
307-
func isEmptyYamlError(err error) bool {
308-
return strings.Contains(err.Error(), "is missing in 'null'")
309-
}

pkg/resource/resource.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,20 @@ limitations under the License.
1818
package resource
1919

2020
import (
21+
"bytes"
2122
"encoding/json"
2223
"fmt"
24+
"io"
2325
"strings"
2426

2527
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2628
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2729
"k8s.io/apimachinery/pkg/runtime"
30+
"k8s.io/apimachinery/pkg/util/yaml"
2831
"sigs.k8s.io/kustomize/pkg/gvk"
32+
internal "sigs.k8s.io/kustomize/pkg/internal/error"
33+
"sigs.k8s.io/kustomize/pkg/loader"
34+
"sigs.k8s.io/kustomize/pkg/patch"
2935
)
3036

3137
// Resource is an "Unstructured" (json/map form) Kubernetes API resource object
@@ -59,6 +65,56 @@ func NewResourceFromUnstruct(u unstructured.Unstructured) *Resource {
5965
return &Resource{Unstructured: u, b: BehaviorUnspecified}
6066
}
6167

68+
// NewResourceSliceFromPatches returns a slice of resources given a patch path
69+
// slice from a kustomization file.
70+
func NewResourceSliceFromPatches(
71+
ldr loader.Loader, paths []patch.StrategicMerge) ([]*Resource, error) {
72+
var result []*Resource
73+
for _, path := range paths {
74+
content, err := ldr.Load(string(path))
75+
if err != nil {
76+
return nil, err
77+
}
78+
res, err := NewResourceSliceFromBytes(content)
79+
if err != nil {
80+
return nil, internal.Handler(err, string(path))
81+
}
82+
result = append(result, res...)
83+
}
84+
return result, nil
85+
}
86+
87+
// NewResourceSliceFromBytes unmarshalls bytes into a Resource slice.
88+
func NewResourceSliceFromBytes(in []byte) ([]*Resource, error) {
89+
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(in), 1024)
90+
var result []*Resource
91+
var err error
92+
for err == nil || isEmptyYamlError(err) {
93+
var out unstructured.Unstructured
94+
err = decoder.Decode(&out)
95+
if err == nil {
96+
result = append(result, NewResourceFromUnstruct(out))
97+
}
98+
}
99+
if err != io.EOF {
100+
return nil, err
101+
}
102+
return result, nil
103+
}
104+
105+
func isEmptyYamlError(err error) bool {
106+
return strings.Contains(err.Error(), "is missing in 'null'")
107+
}
108+
109+
// String returns resource as JSON.
110+
func (r *Resource) String() string {
111+
bs, err := r.MarshalJSON()
112+
if err != nil {
113+
return "<" + err.Error() + ">"
114+
}
115+
return r.b.String() + ":" + strings.TrimSpace(string(bs))
116+
}
117+
62118
// Behavior returns the behavior for the resource.
63119
func (r *Resource) Behavior() GenerationBehavior {
64120
return r.b

pkg/resource/resource_test.go

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,220 @@ limitations under the License.
1717
package resource
1818

1919
import (
20+
"reflect"
2021
"testing"
22+
23+
"sigs.k8s.io/kustomize/pkg/internal/loadertest"
24+
"sigs.k8s.io/kustomize/pkg/patch"
2125
)
2226

27+
var testConfigMap = NewResourceFromMap(
28+
map[string]interface{}{
29+
"apiVersion": "v1",
30+
"kind": "ConfigMap",
31+
"metadata": map[string]interface{}{
32+
"name": "winnie",
33+
},
34+
})
35+
36+
const testConfigMapString = `unspecified:{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie"}}`
37+
38+
var testDeployment = NewResourceFromMap(
39+
map[string]interface{}{
40+
"apiVersion": "apps/v1",
41+
"kind": "Deployment",
42+
"metadata": map[string]interface{}{
43+
"name": "pooh",
44+
},
45+
})
46+
47+
const testDeploymentString = `unspecified:{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"pooh"}}`
48+
49+
func TestResourceString(t *testing.T) {
50+
tests := []struct {
51+
in *Resource
52+
s string
53+
}{
54+
{
55+
in: testConfigMap,
56+
s: testConfigMapString,
57+
},
58+
{
59+
in: testDeployment,
60+
s: testDeploymentString,
61+
},
62+
}
63+
for _, test := range tests {
64+
if test.in.String() != test.s {
65+
t.Fatalf("Expected %s == %s", test.in.String(), test.s)
66+
}
67+
}
68+
}
69+
70+
func TestNewResourceSliceFromPatches(t *testing.T) {
71+
patchGood1 := patch.StrategicMerge("/foo/patch1.yaml")
72+
patch1 := `
73+
apiVersion: apps/v1
74+
kind: Deployment
75+
metadata:
76+
name: pooh
77+
`
78+
patchGood2 := patch.StrategicMerge("/foo/patch2.yaml")
79+
patch2 := `
80+
apiVersion: v1
81+
kind: ConfigMap
82+
metadata:
83+
name: winnie
84+
---
85+
# some comment
86+
---
87+
---
88+
`
89+
patchBad := patch.StrategicMerge("/foo/patch3.yaml")
90+
patch3 := `
91+
WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOT: woot
92+
`
93+
l := loadertest.NewFakeLoader("/foo")
94+
l.AddFile(string(patchGood1), []byte(patch1))
95+
l.AddFile(string(patchGood2), []byte(patch2))
96+
l.AddFile(string(patchBad), []byte(patch3))
97+
98+
tests := []struct {
99+
name string
100+
input []patch.StrategicMerge
101+
expectedOut []*Resource
102+
expectedErr bool
103+
}{
104+
{
105+
name: "happy",
106+
input: []patch.StrategicMerge{patchGood1, patchGood2},
107+
expectedOut: []*Resource{testDeployment, testConfigMap},
108+
expectedErr: false,
109+
},
110+
{
111+
name: "badFileName",
112+
input: []patch.StrategicMerge{patchGood1, "doesNotExist"},
113+
expectedOut: []*Resource{},
114+
expectedErr: true,
115+
},
116+
{
117+
name: "badData",
118+
input: []patch.StrategicMerge{patchGood1, patchBad},
119+
expectedOut: []*Resource{},
120+
expectedErr: true,
121+
},
122+
}
123+
for _, test := range tests {
124+
rs, err := NewResourceSliceFromPatches(l, test.input)
125+
if test.expectedErr && err == nil {
126+
t.Fatalf("%v: should return error", test.name)
127+
}
128+
if !test.expectedErr && err != nil {
129+
t.Fatalf("%v: unexpected error: %s", test.name, err)
130+
}
131+
if len(rs) != len(test.expectedOut) {
132+
t.Fatalf("%s: length mismatch %d != %d",
133+
test.name, len(rs), len(test.expectedOut))
134+
}
135+
for i := range rs {
136+
if !reflect.DeepEqual(test.expectedOut[i], rs[i]) {
137+
t.Fatalf("%s: Got: %v\nexpected:%v",
138+
test.name, test.expectedOut[i], rs[i])
139+
}
140+
}
141+
}
142+
}
143+
144+
func TestNewResourceSliceFromBytes(t *testing.T) {
145+
tests := []struct {
146+
name string
147+
input []byte
148+
expectedOut []*Resource
149+
expectedErr bool
150+
}{
151+
{
152+
name: "garbage",
153+
input: []byte("garbageIn: garbageOut"),
154+
expectedOut: []*Resource{},
155+
expectedErr: true,
156+
},
157+
{
158+
name: "noBytes",
159+
input: []byte{},
160+
expectedOut: []*Resource{},
161+
expectedErr: false,
162+
},
163+
{
164+
name: "goodJson",
165+
input: []byte(`
166+
{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie"}}
167+
`),
168+
expectedOut: []*Resource{testConfigMap},
169+
expectedErr: false,
170+
},
171+
{
172+
name: "goodYaml1",
173+
input: []byte(`
174+
apiVersion: v1
175+
kind: ConfigMap
176+
metadata:
177+
name: winnie
178+
`),
179+
expectedOut: []*Resource{testConfigMap},
180+
expectedErr: false,
181+
},
182+
{
183+
name: "goodYaml2",
184+
input: []byte(`
185+
apiVersion: v1
186+
kind: ConfigMap
187+
metadata:
188+
name: winnie
189+
---
190+
apiVersion: v1
191+
kind: ConfigMap
192+
metadata:
193+
name: winnie
194+
`),
195+
expectedOut: []*Resource{testConfigMap, testConfigMap},
196+
expectedErr: false,
197+
},
198+
{
199+
name: "garbageInOneOfTwoObjects",
200+
input: []byte(`
201+
apiVersion: v1
202+
kind: ConfigMap
203+
metadata:
204+
name: winnie
205+
---
206+
WOOOOOOOOOOOOOOOOOOOOOOOOT: woot
207+
`),
208+
expectedOut: []*Resource{},
209+
expectedErr: true,
210+
},
211+
}
212+
213+
for _, test := range tests {
214+
rs, err := NewResourceSliceFromBytes(test.input)
215+
if test.expectedErr && err == nil {
216+
t.Fatalf("%v: should return error", test.name)
217+
}
218+
if !test.expectedErr && err != nil {
219+
t.Fatalf("%v: unexpected error: %s", test.name, err)
220+
}
221+
if len(rs) != len(test.expectedOut) {
222+
t.Fatalf("%s: length mismatch %d != %d",
223+
test.name, len(rs), len(test.expectedOut))
224+
}
225+
for i := range rs {
226+
if !reflect.DeepEqual(test.expectedOut[i], rs[i]) {
227+
t.Fatalf("%s: Got: %v\nexpected:%v",
228+
test.name, test.expectedOut[i], rs[i])
229+
}
230+
}
231+
}
232+
}
233+
23234
func TestGetFieldValue(t *testing.T) {
24235
res := NewResourceFromMap(map[string]interface{}{
25236
"Kind": "Service",

pkg/target/kusttarget.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ func (kt *KustTarget) loadCustomizedResMap() (resmap.ResMap, error) {
157157
kt.kustomization.PatchesStrategicMerge = patch.Append(
158158
kt.kustomization.PatchesStrategicMerge,
159159
kt.kustomization.Patches...)
160-
patches, err := resmap.NewResourceSliceFromPatches(
160+
patches, err := resource.NewResourceSliceFromPatches(
161161
kt.ldr, kt.kustomization.PatchesStrategicMerge)
162162
if err != nil {
163163
errs.Append(errors.Wrap(err, "NewResourceSliceFromPatches"))

0 commit comments

Comments
 (0)