1
16
17 package printers
18
19 import (
20 "bytes"
21 "strings"
22 "testing"
23
24 "k8s.io/api/core/v1"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/runtime"
27 )
28
29 func TestTemplate(t *testing.T) {
30 testCase := []struct {
31 name string
32 template string
33 obj runtime.Object
34 expectOut string
35 expectErr func(error) (string, bool)
36 }{
37 {
38 name: "support base64 decoding of secret data",
39 template: "{{ .data.username | base64decode }}",
40 obj: &v1.Secret{
41 Data: map[string][]byte{
42 "username": []byte("hunter"),
43 },
44 },
45 expectOut: "hunter",
46 },
47 {
48 name: "test error path for base64 decoding",
49 template: "{{ .data.username | base64decode }}",
50 obj: &badlyMarshaledSecret{},
51 expectErr: func(err error) (string, bool) {
52 matched := strings.Contains(err.Error(), "base64 decode")
53 return "a base64 decode error", matched
54 },
55 },
56 {
57 name: "template 'eq' should not throw error for numbers",
58 template: "{{ eq .count 1}}",
59 obj: &v1.Event{
60 Count: 1,
61 },
62 expectOut: "true",
63 },
64 }
65 for _, test := range testCase {
66 t.Run(test.name, func(t *testing.T) {
67 buffer := &bytes.Buffer{}
68
69 p, err := NewGoTemplatePrinter([]byte(test.template))
70 if err != nil {
71 if test.expectErr == nil {
72 t.Errorf("[%s]expected success but got:\n %v\n", test.name, err)
73 return
74 }
75 if expected, ok := test.expectErr(err); !ok {
76 t.Errorf("[%s]expect:\n %v\n but got:\n %v\n", test.name, expected, err)
77 }
78 return
79 }
80
81 err = p.PrintObj(test.obj, buffer)
82 if err != nil {
83 if test.expectErr == nil {
84 t.Errorf("[%s]expected success but got:\n %v\n", test.name, err)
85 return
86 }
87 if expected, ok := test.expectErr(err); !ok {
88 t.Errorf("[%s]expect:\n %v\n but got:\n %v\n", test.name, expected, err)
89 }
90 return
91 }
92
93 if test.expectErr != nil {
94 t.Errorf("[%s]expect:\n error\n but got:\n no error\n", test.name)
95 return
96 }
97
98 if test.expectOut != buffer.String() {
99 t.Errorf("[%s]expect:\n %v\n but got:\n %v\n", test.name, test.expectOut, buffer.String())
100 }
101 })
102 }
103 }
104
105 type badlyMarshaledSecret struct {
106 v1.Secret
107 }
108
109 func (a badlyMarshaledSecret) MarshalJSON() ([]byte, error) {
110 return []byte(`{"apiVersion":"v1","data":{"username":"--THIS IS NOT BASE64--"},"kind":"Secret"}`), nil
111 }
112
113 func TestTemplateStrings(t *testing.T) {
114
115 table := map[string]struct {
116 pod v1.Pod
117 expect string
118 }{
119 "nilInfo": {v1.Pod{}, "false"},
120 "emptyInfo": {v1.Pod{Status: v1.PodStatus{ContainerStatuses: []v1.ContainerStatus{}}}, "false"},
121 "fooExists": {
122 v1.Pod{
123 Status: v1.PodStatus{
124 ContainerStatuses: []v1.ContainerStatus{
125 {
126 Name: "foo",
127 },
128 },
129 },
130 },
131 "false",
132 },
133 "barExists": {
134 v1.Pod{
135 Status: v1.PodStatus{
136 ContainerStatuses: []v1.ContainerStatus{
137 {
138 Name: "bar",
139 },
140 },
141 },
142 },
143 "false",
144 },
145 "bothExist": {
146 v1.Pod{
147 Status: v1.PodStatus{
148 ContainerStatuses: []v1.ContainerStatus{
149 {
150 Name: "foo",
151 },
152 {
153 Name: "bar",
154 },
155 },
156 },
157 },
158 "false",
159 },
160 "barValid": {
161 v1.Pod{
162 Status: v1.PodStatus{
163 ContainerStatuses: []v1.ContainerStatus{
164 {
165 Name: "foo",
166 },
167 {
168 Name: "bar",
169 State: v1.ContainerState{
170 Running: &v1.ContainerStateRunning{
171 StartedAt: metav1.Time{},
172 },
173 },
174 },
175 },
176 },
177 },
178 "false",
179 },
180 "bothValid": {
181 v1.Pod{
182 Status: v1.PodStatus{
183 ContainerStatuses: []v1.ContainerStatus{
184 {
185 Name: "foo",
186 State: v1.ContainerState{
187 Running: &v1.ContainerStateRunning{
188 StartedAt: metav1.Time{},
189 },
190 },
191 },
192 {
193 Name: "bar",
194 State: v1.ContainerState{
195 Running: &v1.ContainerStateRunning{
196 StartedAt: metav1.Time{},
197 },
198 },
199 },
200 },
201 },
202 },
203 "true",
204 },
205 }
206
207 tmpl := `{{if (exists . "status" "containerStatuses")}}{{range .status.containerStatuses}}{{if (and (eq .name "foo") (exists . "state" "running"))}}true{{end}}{{end}}{{end}}`
208 printer, err := NewGoTemplatePrinter([]byte(tmpl))
209 if err != nil {
210 t.Fatalf("tmpl fail: %v", err)
211 }
212
213 for name, item := range table {
214 buffer := &bytes.Buffer{}
215 err = printer.PrintObj(&item.pod, buffer)
216 if err != nil {
217 t.Errorf("%v: unexpected err: %v", name, err)
218 continue
219 }
220 actual := buffer.String()
221 if len(actual) == 0 {
222 actual = "false"
223 }
224 if e := item.expect; e != actual {
225 t.Errorf("%v: expected %v, got %v", name, e, actual)
226 }
227 }
228 }
229
230 func TestTemplatePanic(t *testing.T) {
231 tmpl := `{{and ((index .currentState.info "foo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}`
232 printer, err := NewGoTemplatePrinter([]byte(tmpl))
233 if err != nil {
234 t.Fatalf("tmpl fail: %v", err)
235 }
236 buffer := &bytes.Buffer{}
237 err = printer.PrintObj(&v1.Pod{}, buffer)
238 if err == nil {
239 t.Fatalf("expected that template to crash")
240 }
241 if buffer.String() == "" {
242 t.Errorf("no debugging info was printed")
243 }
244 }
245
246 func TestTemplateSuccess(t *testing.T) {
247
248 templatePrinter, err := NewGoTemplatePrinter([]byte("{{.name}}"))
249 if err != nil {
250 t.Fatal(err)
251 }
252
253
254 om := func(name string) metav1.ObjectMeta { return metav1.ObjectMeta{Name: name} }
255 objects := []runtime.Object{
256 &v1.Pod{ObjectMeta: om("pod")},
257 &v1.PodList{},
258 &v1.PodList{Items: []v1.Pod{{}}},
259 &v1.Endpoints{
260 Subsets: []v1.EndpointSubset{{
261 Addresses: []v1.EndpointAddress{{IP: "127.0.0.1"}, {IP: "localhost"}},
262 Ports: []v1.EndpointPort{{Port: 8080}},
263 }}},
264 }
265
266 for _, obj := range objects {
267 b := &bytes.Buffer{}
268 if err := templatePrinter.PrintObj(obj, b); err != nil {
269 t.Errorf("Unexpected template error: %v", err)
270 }
271 }
272 }
273
274 func TestTemplateErrors(t *testing.T) {
275
276 templatePrinter, err := NewGoTemplatePrinter([]byte("{{len .items}}"))
277 if err != nil {
278 t.Fatal(err)
279 }
280
281
282 om := func(name string) metav1.ObjectMeta { return metav1.ObjectMeta{Name: name} }
283 objects := []runtime.Object{
284 &v1.Pod{ObjectMeta: om("pod")},
285 &v1.PodList{},
286 &v1.Endpoints{
287 Subsets: []v1.EndpointSubset{{
288 Addresses: []v1.EndpointAddress{{IP: "127.0.0.1"}, {IP: "localhost"}},
289 Ports: []v1.EndpointPort{{Port: 8080}},
290 }}},
291 }
292
293 for _, obj := range objects {
294 b := &bytes.Buffer{}
295 if err := templatePrinter.PrintObj(obj, b); err == nil {
296 t.Errorf("Expected template printer error; received none")
297 }
298 }
299 }
300
View as plain text