...

Source file src/k8s.io/kubernetes/pkg/kubelet/container/helpers_test.go

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

     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 container
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	"github.com/stretchr/testify/assert"
    26  
    27  	v1 "k8s.io/api/core/v1"
    28  	"k8s.io/apimachinery/pkg/api/resource"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  )
    31  
    32  func TestEnvVarsToMap(t *testing.T) {
    33  	vars := []EnvVar{
    34  		{
    35  			Name:  "foo",
    36  			Value: "bar",
    37  		},
    38  		{
    39  			Name:  "zoo",
    40  			Value: "baz",
    41  		},
    42  	}
    43  
    44  	varMap := envVarsToMap(vars)
    45  
    46  	if e, a := len(vars), len(varMap); e != a {
    47  		t.Errorf("Unexpected map length; expected: %d, got %d", e, a)
    48  	}
    49  
    50  	if a := varMap["foo"]; a != "bar" {
    51  		t.Errorf("Unexpected value of key 'foo': %v", a)
    52  	}
    53  
    54  	if a := varMap["zoo"]; a != "baz" {
    55  		t.Errorf("Unexpected value of key 'zoo': %v", a)
    56  	}
    57  }
    58  
    59  func TestExpandCommandAndArgs(t *testing.T) {
    60  	cases := []struct {
    61  		name            string
    62  		container       *v1.Container
    63  		envs            []EnvVar
    64  		expectedCommand []string
    65  		expectedArgs    []string
    66  	}{
    67  		{
    68  			name:      "none",
    69  			container: &v1.Container{},
    70  		},
    71  		{
    72  			name: "command expanded",
    73  			container: &v1.Container{
    74  				Command: []string{"foo", "$(VAR_TEST)", "$(VAR_TEST2)"},
    75  			},
    76  			envs: []EnvVar{
    77  				{
    78  					Name:  "VAR_TEST",
    79  					Value: "zoo",
    80  				},
    81  				{
    82  					Name:  "VAR_TEST2",
    83  					Value: "boo",
    84  				},
    85  			},
    86  			expectedCommand: []string{"foo", "zoo", "boo"},
    87  		},
    88  		{
    89  			name: "args expanded",
    90  			container: &v1.Container{
    91  				Args: []string{"zap", "$(VAR_TEST)", "$(VAR_TEST2)"},
    92  			},
    93  			envs: []EnvVar{
    94  				{
    95  					Name:  "VAR_TEST",
    96  					Value: "hap",
    97  				},
    98  				{
    99  					Name:  "VAR_TEST2",
   100  					Value: "trap",
   101  				},
   102  			},
   103  			expectedArgs: []string{"zap", "hap", "trap"},
   104  		},
   105  		{
   106  			name: "both expanded",
   107  			container: &v1.Container{
   108  				Command: []string{"$(VAR_TEST2)--$(VAR_TEST)", "foo", "$(VAR_TEST3)"},
   109  				Args:    []string{"foo", "$(VAR_TEST)", "$(VAR_TEST2)"},
   110  			},
   111  			envs: []EnvVar{
   112  				{
   113  					Name:  "VAR_TEST",
   114  					Value: "zoo",
   115  				},
   116  				{
   117  					Name:  "VAR_TEST2",
   118  					Value: "boo",
   119  				},
   120  				{
   121  					Name:  "VAR_TEST3",
   122  					Value: "roo",
   123  				},
   124  			},
   125  			expectedCommand: []string{"boo--zoo", "foo", "roo"},
   126  			expectedArgs:    []string{"foo", "zoo", "boo"},
   127  		},
   128  	}
   129  
   130  	for _, tc := range cases {
   131  		actualCommand, actualArgs := ExpandContainerCommandAndArgs(tc.container, tc.envs)
   132  
   133  		if e, a := tc.expectedCommand, actualCommand; !reflect.DeepEqual(e, a) {
   134  			t.Errorf("%v: unexpected command; expected %v, got %v", tc.name, e, a)
   135  		}
   136  
   137  		if e, a := tc.expectedArgs, actualArgs; !reflect.DeepEqual(e, a) {
   138  			t.Errorf("%v: unexpected args; expected %v, got %v", tc.name, e, a)
   139  		}
   140  
   141  	}
   142  }
   143  
   144  func TestExpandVolumeMountsWithSubpath(t *testing.T) {
   145  	cases := []struct {
   146  		name              string
   147  		container         *v1.Container
   148  		envs              []EnvVar
   149  		expectedSubPath   string
   150  		expectedMountPath string
   151  		expectedOk        bool
   152  	}{
   153  		{
   154  			name: "subpath with no expansion",
   155  			container: &v1.Container{
   156  				VolumeMounts: []v1.VolumeMount{{SubPathExpr: "foo"}},
   157  			},
   158  			expectedSubPath:   "foo",
   159  			expectedMountPath: "",
   160  			expectedOk:        true,
   161  		},
   162  		{
   163  			name: "volumes with expanded subpath",
   164  			container: &v1.Container{
   165  				VolumeMounts: []v1.VolumeMount{{SubPathExpr: "foo/$(POD_NAME)"}},
   166  			},
   167  			envs: []EnvVar{
   168  				{
   169  					Name:  "POD_NAME",
   170  					Value: "bar",
   171  				},
   172  			},
   173  			expectedSubPath:   "foo/bar",
   174  			expectedMountPath: "",
   175  			expectedOk:        true,
   176  		},
   177  		{
   178  			name: "volumes expanded with empty subpath",
   179  			container: &v1.Container{
   180  				VolumeMounts: []v1.VolumeMount{{SubPathExpr: ""}},
   181  			},
   182  			envs: []EnvVar{
   183  				{
   184  					Name:  "POD_NAME",
   185  					Value: "bar",
   186  				},
   187  			},
   188  			expectedSubPath:   "",
   189  			expectedMountPath: "",
   190  			expectedOk:        true,
   191  		},
   192  		{
   193  			name: "volumes expanded with no envs subpath",
   194  			container: &v1.Container{
   195  				VolumeMounts: []v1.VolumeMount{{SubPathExpr: "/foo/$(POD_NAME)"}},
   196  			},
   197  			expectedSubPath:   "/foo/$(POD_NAME)",
   198  			expectedMountPath: "",
   199  			expectedOk:        false,
   200  		},
   201  		{
   202  			name: "volumes expanded with leading environment variable",
   203  			container: &v1.Container{
   204  				VolumeMounts: []v1.VolumeMount{{SubPathExpr: "$(POD_NAME)/bar"}},
   205  			},
   206  			envs: []EnvVar{
   207  				{
   208  					Name:  "POD_NAME",
   209  					Value: "foo",
   210  				},
   211  			},
   212  			expectedSubPath:   "foo/bar",
   213  			expectedMountPath: "",
   214  			expectedOk:        true,
   215  		},
   216  		{
   217  			name: "volumes with volume and subpath",
   218  			container: &v1.Container{
   219  				VolumeMounts: []v1.VolumeMount{{MountPath: "/foo", SubPathExpr: "$(POD_NAME)/bar"}},
   220  			},
   221  			envs: []EnvVar{
   222  				{
   223  					Name:  "POD_NAME",
   224  					Value: "foo",
   225  				},
   226  			},
   227  			expectedSubPath:   "foo/bar",
   228  			expectedMountPath: "/foo",
   229  			expectedOk:        true,
   230  		},
   231  		{
   232  			name: "volumes with volume and no subpath",
   233  			container: &v1.Container{
   234  				VolumeMounts: []v1.VolumeMount{{MountPath: "/foo"}},
   235  			},
   236  			envs: []EnvVar{
   237  				{
   238  					Name:  "POD_NAME",
   239  					Value: "foo",
   240  				},
   241  			},
   242  			expectedSubPath:   "",
   243  			expectedMountPath: "/foo",
   244  			expectedOk:        true,
   245  		},
   246  		{
   247  			name: "subpaths with empty environment variable",
   248  			container: &v1.Container{
   249  				VolumeMounts: []v1.VolumeMount{{SubPathExpr: "foo/$(POD_NAME)/$(ANNOTATION)"}},
   250  			},
   251  			envs: []EnvVar{
   252  				{
   253  					Name:  "ANNOTATION",
   254  					Value: "",
   255  				},
   256  			},
   257  			expectedSubPath:   "foo/$(POD_NAME)/$(ANNOTATION)",
   258  			expectedMountPath: "",
   259  			expectedOk:        false,
   260  		},
   261  		{
   262  			name: "subpaths with missing env variables",
   263  			container: &v1.Container{
   264  				VolumeMounts: []v1.VolumeMount{{SubPathExpr: "foo/$(ODD_NAME)/$(POD_NAME)"}},
   265  			},
   266  			envs: []EnvVar{
   267  				{
   268  					Name:  "ODD_NAME",
   269  					Value: "bar",
   270  				},
   271  			},
   272  			expectedSubPath:   "foo/$(ODD_NAME)/$(POD_NAME)",
   273  			expectedMountPath: "",
   274  			expectedOk:        false,
   275  		},
   276  		{
   277  			name: "subpaths with empty expansion",
   278  			container: &v1.Container{
   279  				VolumeMounts: []v1.VolumeMount{{SubPathExpr: "$()"}},
   280  			},
   281  			expectedSubPath:   "$()",
   282  			expectedMountPath: "",
   283  			expectedOk:        false,
   284  		},
   285  		{
   286  			name: "subpaths with nested expandable envs",
   287  			container: &v1.Container{
   288  				VolumeMounts: []v1.VolumeMount{{SubPathExpr: "$(POD_NAME$(ANNOTATION))"}},
   289  			},
   290  			envs: []EnvVar{
   291  				{
   292  					Name:  "POD_NAME",
   293  					Value: "foo",
   294  				},
   295  				{
   296  					Name:  "ANNOTATION",
   297  					Value: "bar",
   298  				},
   299  			},
   300  			expectedSubPath:   "$(POD_NAME$(ANNOTATION))",
   301  			expectedMountPath: "",
   302  			expectedOk:        false,
   303  		},
   304  	}
   305  
   306  	for _, tc := range cases {
   307  		actualSubPath, err := ExpandContainerVolumeMounts(tc.container.VolumeMounts[0], tc.envs)
   308  		ok := err == nil
   309  		if e, a := tc.expectedOk, ok; !reflect.DeepEqual(e, a) {
   310  			t.Errorf("%v: unexpected validation failure of subpath; expected %v, got %v", tc.name, e, a)
   311  		}
   312  		if !ok {
   313  			// if ExpandContainerVolumeMounts returns an error, we don't care what the actualSubPath value is
   314  			continue
   315  		}
   316  		if e, a := tc.expectedSubPath, actualSubPath; !reflect.DeepEqual(e, a) {
   317  			t.Errorf("%v: unexpected subpath; expected %v, got %v", tc.name, e, a)
   318  		}
   319  		if e, a := tc.expectedMountPath, tc.container.VolumeMounts[0].MountPath; !reflect.DeepEqual(e, a) {
   320  			t.Errorf("%v: unexpected mountpath; expected %v, got %v", tc.name, e, a)
   321  		}
   322  	}
   323  
   324  }
   325  
   326  func TestGetContainerSpec(t *testing.T) {
   327  	for _, tc := range []struct {
   328  		name          string
   329  		havePod       *v1.Pod
   330  		haveName      string
   331  		wantContainer *v1.Container
   332  	}{
   333  		{
   334  			name: "regular container",
   335  			havePod: &v1.Pod{
   336  				Spec: v1.PodSpec{
   337  					Containers: []v1.Container{
   338  						{Name: "plain-ole-container"},
   339  					},
   340  					InitContainers: []v1.Container{
   341  						{Name: "init-container"},
   342  					},
   343  				},
   344  			},
   345  			haveName:      "plain-ole-container",
   346  			wantContainer: &v1.Container{Name: "plain-ole-container"},
   347  		},
   348  		{
   349  			name: "init container",
   350  			havePod: &v1.Pod{
   351  				Spec: v1.PodSpec{
   352  					Containers: []v1.Container{
   353  						{Name: "plain-ole-container"},
   354  					},
   355  					InitContainers: []v1.Container{
   356  						{Name: "init-container"},
   357  					},
   358  				},
   359  			},
   360  			haveName:      "init-container",
   361  			wantContainer: &v1.Container{Name: "init-container"},
   362  		},
   363  		{
   364  			name: "ephemeral container",
   365  			havePod: &v1.Pod{
   366  				Spec: v1.PodSpec{
   367  					Containers: []v1.Container{
   368  						{Name: "plain-ole-container"},
   369  					},
   370  					InitContainers: []v1.Container{
   371  						{Name: "init-container"},
   372  					},
   373  					EphemeralContainers: []v1.EphemeralContainer{
   374  						{EphemeralContainerCommon: v1.EphemeralContainerCommon{
   375  							Name: "debug-container",
   376  						}},
   377  					},
   378  				},
   379  			},
   380  			haveName:      "debug-container",
   381  			wantContainer: &v1.Container{Name: "debug-container"},
   382  		},
   383  	} {
   384  		t.Run(tc.name, func(t *testing.T) {
   385  			gotContainer := GetContainerSpec(tc.havePod, tc.haveName)
   386  			if diff := cmp.Diff(tc.wantContainer, gotContainer); diff != "" {
   387  				t.Fatalf("GetContainerSpec for %q returned diff (-want +got):%v", tc.name, diff)
   388  			}
   389  		})
   390  	}
   391  }
   392  
   393  func TestShouldContainerBeRestarted(t *testing.T) {
   394  	pod := &v1.Pod{
   395  		ObjectMeta: metav1.ObjectMeta{
   396  			UID:       "12345678",
   397  			Name:      "foo",
   398  			Namespace: "new",
   399  		},
   400  		Spec: v1.PodSpec{
   401  			Containers: []v1.Container{
   402  				{Name: "no-history"},
   403  				{Name: "alive"},
   404  				{Name: "succeed"},
   405  				{Name: "failed"},
   406  				{Name: "unknown"},
   407  			},
   408  		},
   409  	}
   410  	podStatus := &PodStatus{
   411  		ID:        pod.UID,
   412  		Name:      pod.Name,
   413  		Namespace: pod.Namespace,
   414  		ContainerStatuses: []*Status{
   415  			{
   416  				Name:  "alive",
   417  				State: ContainerStateRunning,
   418  			},
   419  			{
   420  				Name:     "succeed",
   421  				State:    ContainerStateExited,
   422  				ExitCode: 0,
   423  			},
   424  			{
   425  				Name:     "failed",
   426  				State:    ContainerStateExited,
   427  				ExitCode: 1,
   428  			},
   429  			{
   430  				Name:     "alive",
   431  				State:    ContainerStateExited,
   432  				ExitCode: 2,
   433  			},
   434  			{
   435  				Name:  "unknown",
   436  				State: ContainerStateUnknown,
   437  			},
   438  			{
   439  				Name:     "failed",
   440  				State:    ContainerStateExited,
   441  				ExitCode: 3,
   442  			},
   443  		},
   444  	}
   445  	policies := []v1.RestartPolicy{
   446  		v1.RestartPolicyNever,
   447  		v1.RestartPolicyOnFailure,
   448  		v1.RestartPolicyAlways,
   449  	}
   450  
   451  	// test policies
   452  	expected := map[string][]bool{
   453  		"no-history": {true, true, true},
   454  		"alive":      {false, false, false},
   455  		"succeed":    {false, false, true},
   456  		"failed":     {false, true, true},
   457  		"unknown":    {true, true, true},
   458  	}
   459  	for _, c := range pod.Spec.Containers {
   460  		for i, policy := range policies {
   461  			pod.Spec.RestartPolicy = policy
   462  			e := expected[c.Name][i]
   463  			r := ShouldContainerBeRestarted(&c, pod, podStatus)
   464  			if r != e {
   465  				t.Errorf("Restart for container %q with restart policy %q expected %t, got %t",
   466  					c.Name, policy, e, r)
   467  			}
   468  		}
   469  	}
   470  
   471  	// test deleted pod
   472  	pod.DeletionTimestamp = &metav1.Time{Time: time.Now()}
   473  	expected = map[string][]bool{
   474  		"no-history": {false, false, false},
   475  		"alive":      {false, false, false},
   476  		"succeed":    {false, false, false},
   477  		"failed":     {false, false, false},
   478  		"unknown":    {false, false, false},
   479  	}
   480  	for _, c := range pod.Spec.Containers {
   481  		for i, policy := range policies {
   482  			pod.Spec.RestartPolicy = policy
   483  			e := expected[c.Name][i]
   484  			r := ShouldContainerBeRestarted(&c, pod, podStatus)
   485  			if r != e {
   486  				t.Errorf("Restart for container %q with restart policy %q expected %t, got %t",
   487  					c.Name, policy, e, r)
   488  			}
   489  		}
   490  	}
   491  }
   492  
   493  func TestHasPrivilegedContainer(t *testing.T) {
   494  	newBoolPtr := func(b bool) *bool {
   495  		return &b
   496  	}
   497  	tests := map[string]struct {
   498  		securityContext *v1.SecurityContext
   499  		expected        bool
   500  	}{
   501  		"nil security context": {
   502  			securityContext: nil,
   503  			expected:        false,
   504  		},
   505  		"nil privileged": {
   506  			securityContext: &v1.SecurityContext{},
   507  			expected:        false,
   508  		},
   509  		"false privileged": {
   510  			securityContext: &v1.SecurityContext{Privileged: newBoolPtr(false)},
   511  			expected:        false,
   512  		},
   513  		"true privileged": {
   514  			securityContext: &v1.SecurityContext{Privileged: newBoolPtr(true)},
   515  			expected:        true,
   516  		},
   517  	}
   518  
   519  	for k, v := range tests {
   520  		pod := &v1.Pod{
   521  			Spec: v1.PodSpec{
   522  				Containers: []v1.Container{
   523  					{SecurityContext: v.securityContext},
   524  				},
   525  			},
   526  		}
   527  		actual := HasPrivilegedContainer(pod)
   528  		if actual != v.expected {
   529  			t.Errorf("%s expected %t but got %t", k, v.expected, actual)
   530  		}
   531  	}
   532  	// Test init containers as well.
   533  	for k, v := range tests {
   534  		pod := &v1.Pod{
   535  			Spec: v1.PodSpec{
   536  				InitContainers: []v1.Container{
   537  					{SecurityContext: v.securityContext},
   538  				},
   539  			},
   540  		}
   541  		actual := HasPrivilegedContainer(pod)
   542  		if actual != v.expected {
   543  			t.Errorf("%s expected %t but got %t", k, v.expected, actual)
   544  		}
   545  	}
   546  }
   547  
   548  func TestMakePortMappings(t *testing.T) {
   549  	port := func(name string, protocol v1.Protocol, containerPort, hostPort int32, ip string) v1.ContainerPort {
   550  		return v1.ContainerPort{
   551  			Name:          name,
   552  			Protocol:      protocol,
   553  			ContainerPort: containerPort,
   554  			HostPort:      hostPort,
   555  			HostIP:        ip,
   556  		}
   557  	}
   558  	portMapping := func(protocol v1.Protocol, containerPort, hostPort int, ip string) PortMapping {
   559  		return PortMapping{
   560  			Protocol:      protocol,
   561  			ContainerPort: containerPort,
   562  			HostPort:      hostPort,
   563  			HostIP:        ip,
   564  		}
   565  	}
   566  
   567  	tests := []struct {
   568  		container            *v1.Container
   569  		expectedPortMappings []PortMapping
   570  	}{
   571  		{
   572  			&v1.Container{
   573  				Name: "fooContainer",
   574  				Ports: []v1.ContainerPort{
   575  					port("", v1.ProtocolTCP, 80, 8080, "127.0.0.1"),
   576  					port("", v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
   577  					port("foo", v1.ProtocolUDP, 555, 5555, ""),
   578  					// Duplicated, should be ignored.
   579  					port("foo", v1.ProtocolUDP, 888, 8888, ""),
   580  					// Duplicated with different address family, shouldn't be ignored
   581  					port("", v1.ProtocolTCP, 80, 8080, "::"),
   582  					// No address family specified
   583  					port("", v1.ProtocolTCP, 1234, 5678, ""),
   584  				},
   585  			},
   586  			[]PortMapping{
   587  				portMapping(v1.ProtocolTCP, 80, 8080, "127.0.0.1"),
   588  				portMapping(v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
   589  				portMapping(v1.ProtocolUDP, 555, 5555, ""),
   590  				portMapping(v1.ProtocolTCP, 80, 8080, "::"),
   591  				portMapping(v1.ProtocolTCP, 1234, 5678, ""),
   592  			},
   593  		},
   594  		{
   595  			// The same container port can be mapped to different host ports
   596  			&v1.Container{
   597  				Name: "fooContainer",
   598  				Ports: []v1.ContainerPort{
   599  					port("", v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
   600  					port("", v1.ProtocolTCP, 4343, 4343, "192.168.0.1"),
   601  				},
   602  			},
   603  			[]PortMapping{
   604  				portMapping(v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
   605  				portMapping(v1.ProtocolTCP, 4343, 4343, "192.168.0.1"),
   606  			},
   607  		},
   608  		{
   609  			// The same container port AND same container host is not OK
   610  			&v1.Container{
   611  				Name: "fooContainer",
   612  				Ports: []v1.ContainerPort{
   613  					port("", v1.ProtocolTCP, 443, 4343, ""),
   614  					port("", v1.ProtocolTCP, 443, 4343, ""),
   615  				},
   616  			},
   617  			[]PortMapping{
   618  				portMapping(v1.ProtocolTCP, 443, 4343, ""),
   619  			},
   620  		},
   621  		{
   622  			// multihomed nodes - multiple IP scenario
   623  			&v1.Container{
   624  				Name: "fooContainer",
   625  				Ports: []v1.ContainerPort{
   626  					port("", v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
   627  					port("", v1.ProtocolTCP, 443, 4343, "172.16.0.1"),
   628  				},
   629  			},
   630  			[]PortMapping{
   631  				portMapping(v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
   632  				portMapping(v1.ProtocolTCP, 443, 4343, "172.16.0.1"),
   633  			},
   634  		},
   635  	}
   636  
   637  	for i, tt := range tests {
   638  		actual := MakePortMappings(tt.container)
   639  		assert.Equal(t, tt.expectedPortMappings, actual, "[%d]", i)
   640  	}
   641  }
   642  
   643  func TestHashContainer(t *testing.T) {
   644  	testCases := []struct {
   645  		name          string
   646  		image         string
   647  		args          []string
   648  		containerPort int32
   649  		expectedHash  uint64
   650  	}{
   651  		{
   652  			name:  "test_container",
   653  			image: "foo/image:v1",
   654  			args: []string{
   655  				"/bin/sh",
   656  				"-c",
   657  				"echo abc",
   658  			},
   659  			containerPort: int32(8001),
   660  			expectedHash:  uint64(0x3c42280f),
   661  		},
   662  	}
   663  
   664  	for _, tc := range testCases {
   665  		container := v1.Container{
   666  			Name:  tc.name,
   667  			Image: tc.image,
   668  			Args:  tc.args,
   669  			Ports: []v1.ContainerPort{{ContainerPort: tc.containerPort}},
   670  		}
   671  
   672  		hashVal := HashContainer(&container)
   673  		assert.Equal(t, tc.expectedHash, hashVal, "the hash value here should not be changed.")
   674  	}
   675  }
   676  
   677  func TestShouldRecordEvent(t *testing.T) {
   678  	var innerEventRecorder = &innerEventRecorder{
   679  		recorder: nil,
   680  	}
   681  
   682  	_, actual := innerEventRecorder.shouldRecordEvent(nil)
   683  	assert.Equal(t, false, actual)
   684  
   685  	var obj = &v1.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"}
   686  
   687  	_, actual = innerEventRecorder.shouldRecordEvent(obj)
   688  	assert.Equal(t, true, actual)
   689  
   690  	obj = &v1.ObjectReference{Namespace: "system", Name: "infra", FieldPath: "implicitly required container "}
   691  
   692  	_, actual = innerEventRecorder.shouldRecordEvent(obj)
   693  	assert.Equal(t, false, actual)
   694  
   695  	var nilObj *v1.ObjectReference = nil
   696  	_, actual = innerEventRecorder.shouldRecordEvent(nilObj)
   697  	assert.Equal(t, false, actual, "should not panic if the typed nil was used, see https://github.com/kubernetes/kubernetes/issues/95552")
   698  }
   699  
   700  func TestHasWindowsHostProcessContainer(t *testing.T) {
   701  	trueVar := true
   702  	falseVar := false
   703  	const containerName = "container"
   704  
   705  	testCases := []struct {
   706  		name           string
   707  		podSpec        *v1.PodSpec
   708  		expectedResult bool
   709  	}{
   710  		{
   711  			name: "hostprocess not set anywhere",
   712  			podSpec: &v1.PodSpec{
   713  				Containers: []v1.Container{{
   714  					Name: containerName,
   715  				}},
   716  			},
   717  			expectedResult: false,
   718  		},
   719  		{
   720  			name: "pod with hostprocess=false",
   721  			podSpec: &v1.PodSpec{
   722  				HostNetwork: true,
   723  				SecurityContext: &v1.PodSecurityContext{
   724  					WindowsOptions: &v1.WindowsSecurityContextOptions{
   725  						HostProcess: &falseVar,
   726  					},
   727  				},
   728  				Containers: []v1.Container{{
   729  					Name: containerName,
   730  				}},
   731  			},
   732  			expectedResult: false,
   733  		},
   734  		{
   735  			name: "pod with hostprocess=true",
   736  			podSpec: &v1.PodSpec{
   737  				HostNetwork: true,
   738  				SecurityContext: &v1.PodSecurityContext{
   739  					WindowsOptions: &v1.WindowsSecurityContextOptions{
   740  						HostProcess: &trueVar,
   741  					},
   742  				},
   743  				Containers: []v1.Container{{
   744  					Name: containerName,
   745  				}},
   746  			},
   747  			expectedResult: true,
   748  		},
   749  		{
   750  			name: "container with hostprocess=false",
   751  			podSpec: &v1.PodSpec{
   752  				HostNetwork: true,
   753  				Containers: []v1.Container{{
   754  					Name: containerName,
   755  					SecurityContext: &v1.SecurityContext{
   756  						WindowsOptions: &v1.WindowsSecurityContextOptions{
   757  							HostProcess: &falseVar,
   758  						},
   759  					},
   760  				}},
   761  			},
   762  			expectedResult: false,
   763  		},
   764  		{
   765  			name: "container with hostprocess=true",
   766  			podSpec: &v1.PodSpec{
   767  				HostNetwork: true,
   768  				Containers: []v1.Container{{
   769  					Name: containerName,
   770  					SecurityContext: &v1.SecurityContext{
   771  						WindowsOptions: &v1.WindowsSecurityContextOptions{
   772  							HostProcess: &trueVar,
   773  						},
   774  					},
   775  				}},
   776  			},
   777  			expectedResult: true,
   778  		},
   779  		{
   780  			name: "pod with hostprocess=false, container with hostprocess=true",
   781  			podSpec: &v1.PodSpec{
   782  				HostNetwork: true,
   783  				SecurityContext: &v1.PodSecurityContext{
   784  					WindowsOptions: &v1.WindowsSecurityContextOptions{
   785  						HostProcess: &falseVar,
   786  					},
   787  				},
   788  				Containers: []v1.Container{{
   789  					Name: containerName,
   790  					SecurityContext: &v1.SecurityContext{
   791  						WindowsOptions: &v1.WindowsSecurityContextOptions{
   792  							HostProcess: &trueVar,
   793  						},
   794  					},
   795  				}},
   796  			},
   797  			expectedResult: true,
   798  		},
   799  		{
   800  			name: "pod with hostprocess=true, container with hostprocess=flase",
   801  			podSpec: &v1.PodSpec{
   802  				HostNetwork: true,
   803  				SecurityContext: &v1.PodSecurityContext{
   804  					WindowsOptions: &v1.WindowsSecurityContextOptions{
   805  						HostProcess: &trueVar,
   806  					},
   807  				},
   808  				Containers: []v1.Container{{
   809  					Name: containerName,
   810  					SecurityContext: &v1.SecurityContext{
   811  						WindowsOptions: &v1.WindowsSecurityContextOptions{
   812  							HostProcess: &falseVar,
   813  						},
   814  					},
   815  				}},
   816  			},
   817  			expectedResult: false,
   818  		},
   819  		{
   820  			name: "containers with hostproces=mixed",
   821  			podSpec: &v1.PodSpec{
   822  				Containers: []v1.Container{
   823  					{
   824  						Name: containerName,
   825  						SecurityContext: &v1.SecurityContext{
   826  							WindowsOptions: &v1.WindowsSecurityContextOptions{
   827  								HostProcess: &falseVar,
   828  							},
   829  						},
   830  					},
   831  					{
   832  						Name: containerName,
   833  						SecurityContext: &v1.SecurityContext{
   834  							WindowsOptions: &v1.WindowsSecurityContextOptions{
   835  								HostProcess: &trueVar,
   836  							},
   837  						},
   838  					},
   839  				},
   840  			},
   841  			expectedResult: true,
   842  		},
   843  		{
   844  			name: "pod with hostProcess=false, containers with hostproces=mixed",
   845  			podSpec: &v1.PodSpec{
   846  				SecurityContext: &v1.PodSecurityContext{
   847  					WindowsOptions: &v1.WindowsSecurityContextOptions{
   848  						HostProcess: &falseVar,
   849  					},
   850  				},
   851  				Containers: []v1.Container{
   852  					{
   853  						Name: containerName,
   854  						SecurityContext: &v1.SecurityContext{
   855  							WindowsOptions: &v1.WindowsSecurityContextOptions{
   856  								HostProcess: &falseVar,
   857  							},
   858  						},
   859  					},
   860  					{
   861  						Name: containerName,
   862  						SecurityContext: &v1.SecurityContext{
   863  							WindowsOptions: &v1.WindowsSecurityContextOptions{
   864  								HostProcess: &trueVar,
   865  							},
   866  						},
   867  					},
   868  				},
   869  			},
   870  			expectedResult: true,
   871  		},
   872  		{
   873  			name: "pod with hostProcess=true, containers with hostproces=mixed",
   874  			podSpec: &v1.PodSpec{
   875  				SecurityContext: &v1.PodSecurityContext{
   876  					WindowsOptions: &v1.WindowsSecurityContextOptions{
   877  						HostProcess: &trueVar,
   878  					},
   879  				},
   880  				Containers: []v1.Container{
   881  					{
   882  						Name: containerName,
   883  						SecurityContext: &v1.SecurityContext{
   884  							WindowsOptions: &v1.WindowsSecurityContextOptions{
   885  								HostProcess: &falseVar,
   886  							},
   887  						},
   888  					},
   889  					{
   890  						Name: containerName,
   891  						SecurityContext: &v1.SecurityContext{
   892  							WindowsOptions: &v1.WindowsSecurityContextOptions{
   893  								HostProcess: &trueVar,
   894  							},
   895  						},
   896  					},
   897  				},
   898  			},
   899  			expectedResult: true,
   900  		},
   901  	}
   902  
   903  	for _, testCase := range testCases {
   904  		t.Run(testCase.name, func(t *testing.T) {
   905  			pod := &v1.Pod{}
   906  			pod.Spec = *testCase.podSpec
   907  			result := HasWindowsHostProcessContainer(pod)
   908  			assert.Equal(t, result, testCase.expectedResult)
   909  		})
   910  	}
   911  }
   912  
   913  func TestHashContainerWithoutResources(t *testing.T) {
   914  	cpu100m := resource.MustParse("100m")
   915  	cpu200m := resource.MustParse("200m")
   916  	mem100M := resource.MustParse("100Mi")
   917  	mem200M := resource.MustParse("200Mi")
   918  	cpuPolicyRestartNotRequired := v1.ContainerResizePolicy{ResourceName: v1.ResourceCPU, RestartPolicy: v1.NotRequired}
   919  	memPolicyRestartNotRequired := v1.ContainerResizePolicy{ResourceName: v1.ResourceMemory, RestartPolicy: v1.NotRequired}
   920  	cpuPolicyRestartRequired := v1.ContainerResizePolicy{ResourceName: v1.ResourceCPU, RestartPolicy: v1.RestartContainer}
   921  	memPolicyRestartRequired := v1.ContainerResizePolicy{ResourceName: v1.ResourceMemory, RestartPolicy: v1.RestartContainer}
   922  
   923  	type testCase struct {
   924  		name         string
   925  		container    *v1.Container
   926  		expectedHash uint64
   927  	}
   928  
   929  	tests := []testCase{
   930  		{
   931  			"Burstable pod with CPU policy restart required",
   932  			&v1.Container{
   933  				Name:  "foo",
   934  				Image: "bar",
   935  				Resources: v1.ResourceRequirements{
   936  					Limits:   v1.ResourceList{v1.ResourceCPU: cpu200m, v1.ResourceMemory: mem200M},
   937  					Requests: v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
   938  				},
   939  				ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartRequired, memPolicyRestartNotRequired},
   940  			},
   941  			0x5f62cb4c,
   942  		},
   943  		{
   944  			"Burstable pod with memory policy restart required",
   945  			&v1.Container{
   946  				Name:  "foo",
   947  				Image: "bar",
   948  				Resources: v1.ResourceRequirements{
   949  					Limits:   v1.ResourceList{v1.ResourceCPU: cpu200m, v1.ResourceMemory: mem200M},
   950  					Requests: v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
   951  				},
   952  				ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartNotRequired, memPolicyRestartRequired},
   953  			},
   954  			0xcdab9e00,
   955  		},
   956  		{
   957  			"Guaranteed pod with CPU policy restart required",
   958  			&v1.Container{
   959  				Name:  "foo",
   960  				Image: "bar",
   961  				Resources: v1.ResourceRequirements{
   962  					Limits:   v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
   963  					Requests: v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
   964  				},
   965  				ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartRequired, memPolicyRestartNotRequired},
   966  			},
   967  			0x5f62cb4c,
   968  		},
   969  		{
   970  			"Guaranteed pod with memory policy restart required",
   971  			&v1.Container{
   972  				Name:  "foo",
   973  				Image: "bar",
   974  				Resources: v1.ResourceRequirements{
   975  					Limits:   v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
   976  					Requests: v1.ResourceList{v1.ResourceCPU: cpu100m, v1.ResourceMemory: mem100M},
   977  				},
   978  				ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartNotRequired, memPolicyRestartRequired},
   979  			},
   980  			0xcdab9e00,
   981  		},
   982  	}
   983  	for _, tc := range tests {
   984  		t.Run(tc.name, func(t *testing.T) {
   985  			containerCopy := tc.container.DeepCopy()
   986  			hash := HashContainerWithoutResources(tc.container)
   987  			assert.Equal(t, tc.expectedHash, hash, "[%s]", tc.name)
   988  			assert.Equal(t, containerCopy, tc.container, "[%s]", tc.name)
   989  		})
   990  	}
   991  }
   992  

View as plain text