...

Source file src/k8s.io/kubernetes/pkg/kubelet/prober/prober_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/prober

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    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  		{ // No probe
   154  			probe:          nil,
   155  			expectedResult: results.Success,
   156  		},
   157  		{ // No handler
   158  			probe:          &v1.Probe{},
   159  			expectError:    true,
   160  			expectedResult: results.Failure,
   161  		},
   162  		{ // Probe fails
   163  			probe:          execProbe,
   164  			execResult:     probe.Failure,
   165  			expectedResult: results.Failure,
   166  		},
   167  		{ // Probe succeeds
   168  			probe:          execProbe,
   169  			execResult:     probe.Success,
   170  			expectedResult: results.Success,
   171  		},
   172  		{ // Probe result is warning
   173  			probe:          execProbe,
   174  			execResult:     probe.Warning,
   175  			expectedResult: results.Success,
   176  		},
   177  		{ // Probe result is unknown
   178  			probe:          execProbe,
   179  			execResult:     probe.Unknown,
   180  			expectedResult: results.Failure,
   181  		},
   182  		{ // Probe has an error
   183  			probe:          execProbe,
   184  			execError:      true,
   185  			expectError:    true,
   186  			execResult:     probe.Unknown,
   187  			expectedResult: results.Failure,
   188  		},
   189  		{ // Probe arguments are passed through
   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  		{ // Probe arguments are passed through
   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  		// this isn't 100% foolproof as a bug in a real CommandRunner where it fails to copy to stdout/stderr wouldn't be caught by this test
   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