1
16
17 package prober
18
19 import (
20 "bytes"
21 "context"
22 "errors"
23 "fmt"
24 "reflect"
25 "strings"
26 "testing"
27
28 v1 "k8s.io/api/core/v1"
29 "k8s.io/apimachinery/pkg/util/intstr"
30 "k8s.io/client-go/tools/record"
31 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
32 containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
33 "k8s.io/kubernetes/pkg/kubelet/prober/results"
34 "k8s.io/kubernetes/pkg/kubelet/util/ioutils"
35 "k8s.io/kubernetes/pkg/probe"
36 execprobe "k8s.io/kubernetes/pkg/probe/exec"
37 )
38
39 func TestGetURLParts(t *testing.T) {
40 testCases := []struct {
41 probe *v1.HTTPGetAction
42 ok bool
43 host string
44 port int
45 path string
46 }{
47 {&v1.HTTPGetAction{Host: "", Port: intstr.FromInt32(-1), Path: ""}, false, "", -1, ""},
48 {&v1.HTTPGetAction{Host: "", Port: intstr.FromString(""), Path: ""}, false, "", -1, ""},
49 {&v1.HTTPGetAction{Host: "", Port: intstr.FromString("-1"), Path: ""}, false, "", -1, ""},
50 {&v1.HTTPGetAction{Host: "", Port: intstr.FromString("not-found"), Path: ""}, false, "", -1, ""},
51 {&v1.HTTPGetAction{Host: "", Port: intstr.FromString("found"), Path: ""}, true, "127.0.0.1", 93, ""},
52 {&v1.HTTPGetAction{Host: "", Port: intstr.FromInt32(76), Path: ""}, true, "127.0.0.1", 76, ""},
53 {&v1.HTTPGetAction{Host: "", Port: intstr.FromString("118"), Path: ""}, true, "127.0.0.1", 118, ""},
54 {&v1.HTTPGetAction{Host: "hostname", Port: intstr.FromInt32(76), Path: "path"}, true, "hostname", 76, "path"},
55 }
56
57 for _, test := range testCases {
58 state := v1.PodStatus{PodIP: "127.0.0.1"}
59 container := v1.Container{
60 Ports: []v1.ContainerPort{{Name: "found", ContainerPort: 93}},
61 LivenessProbe: &v1.Probe{
62 ProbeHandler: v1.ProbeHandler{
63 HTTPGet: test.probe,
64 },
65 },
66 }
67
68 scheme := test.probe.Scheme
69 if scheme == "" {
70 scheme = v1.URISchemeHTTP
71 }
72 host := test.probe.Host
73 if host == "" {
74 host = state.PodIP
75 }
76 port, err := probe.ResolveContainerPort(test.probe.Port, &container)
77 if test.ok && err != nil {
78 t.Errorf("Unexpected error: %v", err)
79 }
80 path := test.probe.Path
81
82 if !test.ok && err == nil {
83 t.Errorf("Expected error for %+v, got %s%s:%d/%s", test, scheme, host, port, path)
84 }
85 if test.ok {
86 if host != test.host || port != test.port || path != test.path {
87 t.Errorf("Expected %s:%d/%s, got %s:%d/%s",
88 test.host, test.port, test.path, host, port, path)
89 }
90 }
91 }
92 }
93
94 func TestGetTCPAddrParts(t *testing.T) {
95 testCases := []struct {
96 probe *v1.TCPSocketAction
97 ok bool
98 host string
99 port int
100 }{
101 {&v1.TCPSocketAction{Port: intstr.FromInt32(-1)}, false, "", -1},
102 {&v1.TCPSocketAction{Port: intstr.FromString("")}, false, "", -1},
103 {&v1.TCPSocketAction{Port: intstr.FromString("-1")}, false, "", -1},
104 {&v1.TCPSocketAction{Port: intstr.FromString("not-found")}, false, "", -1},
105 {&v1.TCPSocketAction{Port: intstr.FromString("found")}, true, "1.2.3.4", 93},
106 {&v1.TCPSocketAction{Port: intstr.FromInt32(76)}, true, "1.2.3.4", 76},
107 {&v1.TCPSocketAction{Port: intstr.FromString("118")}, true, "1.2.3.4", 118},
108 }
109
110 for _, test := range testCases {
111 host := "1.2.3.4"
112 container := v1.Container{
113 Ports: []v1.ContainerPort{{Name: "found", ContainerPort: 93}},
114 LivenessProbe: &v1.Probe{
115 ProbeHandler: v1.ProbeHandler{
116 TCPSocket: test.probe,
117 },
118 },
119 }
120 port, err := probe.ResolveContainerPort(test.probe.Port, &container)
121 if !test.ok && err == nil {
122 t.Errorf("Expected error for %+v, got %s:%d", test, host, port)
123 }
124 if test.ok && err != nil {
125 t.Errorf("Unexpected error: %v", err)
126 }
127 if test.ok {
128 if host != test.host || port != test.port {
129 t.Errorf("Expected %s:%d, got %s:%d", test.host, test.port, host, port)
130 }
131 }
132 }
133 }
134
135 func TestProbe(t *testing.T) {
136 ctx := context.Background()
137 containerID := kubecontainer.ContainerID{Type: "test", ID: "foobar"}
138
139 execProbe := &v1.Probe{
140 ProbeHandler: v1.ProbeHandler{
141 Exec: &v1.ExecAction{},
142 },
143 }
144 tests := []struct {
145 probe *v1.Probe
146 env []v1.EnvVar
147 execError bool
148 expectError bool
149 execResult probe.Result
150 expectedResult results.Result
151 expectCommand []string
152 }{
153 {
154 probe: nil,
155 expectedResult: results.Success,
156 },
157 {
158 probe: &v1.Probe{},
159 expectError: true,
160 expectedResult: results.Failure,
161 },
162 {
163 probe: execProbe,
164 execResult: probe.Failure,
165 expectedResult: results.Failure,
166 },
167 {
168 probe: execProbe,
169 execResult: probe.Success,
170 expectedResult: results.Success,
171 },
172 {
173 probe: execProbe,
174 execResult: probe.Warning,
175 expectedResult: results.Success,
176 },
177 {
178 probe: execProbe,
179 execResult: probe.Unknown,
180 expectedResult: results.Failure,
181 },
182 {
183 probe: execProbe,
184 execError: true,
185 expectError: true,
186 execResult: probe.Unknown,
187 expectedResult: results.Failure,
188 },
189 {
190 probe: &v1.Probe{
191 ProbeHandler: v1.ProbeHandler{
192 Exec: &v1.ExecAction{
193 Command: []string{"/bin/bash", "-c", "some script"},
194 },
195 },
196 },
197 expectCommand: []string{"/bin/bash", "-c", "some script"},
198 execResult: probe.Success,
199 expectedResult: results.Success,
200 },
201 {
202 probe: &v1.Probe{
203 ProbeHandler: v1.ProbeHandler{
204 Exec: &v1.ExecAction{
205 Command: []string{"/bin/bash", "-c", "some $(A) $(B)"},
206 },
207 },
208 },
209 env: []v1.EnvVar{
210 {Name: "A", Value: "script"},
211 },
212 expectCommand: []string{"/bin/bash", "-c", "some script $(B)"},
213 execResult: probe.Success,
214 expectedResult: results.Success,
215 },
216 }
217
218 for i, test := range tests {
219 for _, probeType := range [...]probeType{liveness, readiness, startup} {
220 prober := &prober{
221 recorder: &record.FakeRecorder{},
222 }
223 testID := fmt.Sprintf("%d-%s", i, probeType)
224 testContainer := v1.Container{Env: test.env}
225 switch probeType {
226 case liveness:
227 testContainer.LivenessProbe = test.probe
228 case readiness:
229 testContainer.ReadinessProbe = test.probe
230 case startup:
231 testContainer.StartupProbe = test.probe
232 }
233 if test.execError {
234 prober.exec = fakeExecProber{test.execResult, errors.New("exec error")}
235 } else {
236 prober.exec = fakeExecProber{test.execResult, nil}
237 }
238
239 result, err := prober.probe(ctx, probeType, &v1.Pod{}, v1.PodStatus{}, testContainer, containerID)
240 if test.expectError && err == nil {
241 t.Errorf("[%s] Expected probe error but no error was returned.", testID)
242 }
243 if !test.expectError && err != nil {
244 t.Errorf("[%s] Didn't expect probe error but got: %v", testID, err)
245 }
246 if test.expectedResult != result {
247 t.Errorf("[%s] Expected result to be %v but was %v", testID, test.expectedResult, result)
248 }
249
250 if len(test.expectCommand) > 0 {
251 prober.exec = execprobe.New()
252 prober.runner = &containertest.FakeContainerCommandRunner{}
253 _, err := prober.probe(ctx, probeType, &v1.Pod{}, v1.PodStatus{}, testContainer, containerID)
254 if err != nil {
255 t.Errorf("[%s] Didn't expect probe error but got: %v", testID, err)
256 continue
257 }
258 if !reflect.DeepEqual(test.expectCommand, prober.runner.(*containertest.FakeContainerCommandRunner).Cmd) {
259 t.Errorf("[%s] unexpected probe arguments: %v", testID, prober.runner.(*containertest.FakeContainerCommandRunner).Cmd)
260 }
261 }
262 }
263 }
264 }
265
266 func TestNewExecInContainer(t *testing.T) {
267 ctx := context.Background()
268 limit := 1024
269 tenKilobyte := strings.Repeat("logs-123", 128*10)
270
271 tests := []struct {
272 name string
273 stdout string
274 expected string
275 err error
276 }{
277 {
278 name: "no error",
279 stdout: "foo",
280 expected: "foo",
281 err: nil,
282 },
283 {
284 name: "no error",
285 stdout: tenKilobyte,
286 expected: tenKilobyte[0:limit],
287 err: nil,
288 },
289 {
290 name: "error - make sure we get output",
291 stdout: "foo",
292 expected: "foo",
293 err: errors.New("bad"),
294 },
295 }
296
297 for _, test := range tests {
298 runner := &containertest.FakeContainerCommandRunner{
299 Stdout: test.stdout,
300 Err: test.err,
301 }
302 prober := &prober{
303 runner: runner,
304 }
305
306 container := v1.Container{}
307 containerID := kubecontainer.ContainerID{Type: "docker", ID: "containerID"}
308 cmd := []string{"/foo", "bar"}
309 exec := prober.newExecInContainer(ctx, container, containerID, cmd, 0)
310
311 var dataBuffer bytes.Buffer
312 writer := ioutils.LimitWriter(&dataBuffer, int64(limit))
313 exec.SetStderr(writer)
314 exec.SetStdout(writer)
315 err := exec.Start()
316 if err == nil {
317 err = exec.Wait()
318 }
319 actualOutput := dataBuffer.Bytes()
320
321 if e, a := containerID, runner.ContainerID; e != a {
322 t.Errorf("%s: container id: expected %v, got %v", test.name, e, a)
323 }
324 if e, a := cmd, runner.Cmd; !reflect.DeepEqual(e, a) {
325 t.Errorf("%s: cmd: expected %v, got %v", test.name, e, a)
326 }
327
328 if e, a := test.expected, string(actualOutput); e != a {
329 t.Errorf("%s: output: expected %q, got %q", test.name, e, a)
330 }
331 if e, a := fmt.Sprintf("%v", test.err), fmt.Sprintf("%v", err); e != a {
332 t.Errorf("%s: error: expected %s, got %s", test.name, e, a)
333 }
334 }
335 }
336
View as plain text