...

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

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

     1  /*
     2  Copyright 2016 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 kuberuntime
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  
    25  	v1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/types"
    28  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    29  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    30  	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
    31  	runtimetesting "k8s.io/cri-api/pkg/apis/testing"
    32  	"k8s.io/kubernetes/pkg/features"
    33  	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
    34  	"k8s.io/utils/ptr"
    35  )
    36  
    37  type podStatusProviderFunc func(uid types.UID, name, namespace string) (*kubecontainer.PodStatus, error)
    38  
    39  func (f podStatusProviderFunc) GetPodStatus(_ context.Context, uid types.UID, name, namespace string) (*kubecontainer.PodStatus, error) {
    40  	return f(uid, name, namespace)
    41  }
    42  
    43  func TestIsInitContainerFailed(t *testing.T) {
    44  	tests := []struct {
    45  		status      *kubecontainer.Status
    46  		isFailed    bool
    47  		description string
    48  	}{
    49  		{
    50  			status: &kubecontainer.Status{
    51  				State:    kubecontainer.ContainerStateExited,
    52  				ExitCode: 1,
    53  			},
    54  			isFailed:    true,
    55  			description: "Init container in exited state and non-zero exit code should return true",
    56  		},
    57  		{
    58  			status: &kubecontainer.Status{
    59  				State: kubecontainer.ContainerStateUnknown,
    60  			},
    61  			isFailed:    true,
    62  			description: "Init container in unknown state should return true",
    63  		},
    64  		{
    65  			status: &kubecontainer.Status{
    66  				Reason:   "OOMKilled",
    67  				ExitCode: 0,
    68  			},
    69  			isFailed:    true,
    70  			description: "Init container which reason is OOMKilled should return true",
    71  		},
    72  		{
    73  			status: &kubecontainer.Status{
    74  				State:    kubecontainer.ContainerStateExited,
    75  				ExitCode: 0,
    76  			},
    77  			isFailed:    false,
    78  			description: "Init container in exited state and zero exit code should return false",
    79  		},
    80  		{
    81  			status: &kubecontainer.Status{
    82  				State: kubecontainer.ContainerStateRunning,
    83  			},
    84  			isFailed:    false,
    85  			description: "Init container in running state should return false",
    86  		},
    87  		{
    88  			status: &kubecontainer.Status{
    89  				State: kubecontainer.ContainerStateCreated,
    90  			},
    91  			isFailed:    false,
    92  			description: "Init container in created state should return false",
    93  		},
    94  	}
    95  	for i, test := range tests {
    96  		isFailed := isInitContainerFailed(test.status)
    97  		assert.Equal(t, test.isFailed, isFailed, "TestCase[%d]: %s", i, test.description)
    98  	}
    99  }
   100  
   101  func TestStableKey(t *testing.T) {
   102  	container := &v1.Container{
   103  		Name:  "test_container",
   104  		Image: "foo/image:v1",
   105  	}
   106  	pod := &v1.Pod{
   107  		ObjectMeta: metav1.ObjectMeta{
   108  			Name:      "test_pod",
   109  			Namespace: "test_pod_namespace",
   110  			UID:       "test_pod_uid",
   111  		},
   112  		Spec: v1.PodSpec{
   113  			Containers: []v1.Container{*container},
   114  		},
   115  	}
   116  	oldKey := getStableKey(pod, container)
   117  
   118  	// Updating the container image should change the key.
   119  	container.Image = "foo/image:v2"
   120  	newKey := getStableKey(pod, container)
   121  	assert.NotEqual(t, oldKey, newKey)
   122  }
   123  
   124  func TestToKubeContainer(t *testing.T) {
   125  	c := &runtimeapi.Container{
   126  		Id: "test-id",
   127  		Metadata: &runtimeapi.ContainerMetadata{
   128  			Name:    "test-name",
   129  			Attempt: 1,
   130  		},
   131  		Image:    &runtimeapi.ImageSpec{Image: "test-image"},
   132  		ImageId:  "test-image-id",
   133  		ImageRef: "test-image-ref",
   134  		State:    runtimeapi.ContainerState_CONTAINER_RUNNING,
   135  		Annotations: map[string]string{
   136  			containerHashLabel: "1234",
   137  		},
   138  	}
   139  	expect := &kubecontainer.Container{
   140  		ID: kubecontainer.ContainerID{
   141  			Type: runtimetesting.FakeRuntimeName,
   142  			ID:   "test-id",
   143  		},
   144  		Name:                "test-name",
   145  		ImageID:             "test-image-id",
   146  		ImageRef:            "test-image-ref",
   147  		Image:               "test-image",
   148  		ImageRuntimeHandler: "",
   149  		Hash:                uint64(0x1234),
   150  		State:               kubecontainer.ContainerStateRunning,
   151  	}
   152  
   153  	_, _, m, err := createTestRuntimeManager()
   154  	assert.NoError(t, err)
   155  	got, err := m.toKubeContainer(c)
   156  	assert.NoError(t, err)
   157  	assert.Equal(t, expect, got)
   158  
   159  	// unable to convert a nil pointer to a runtime container
   160  	_, err = m.toKubeContainer(nil)
   161  	assert.Error(t, err)
   162  	_, err = m.sandboxToKubeContainer(nil)
   163  	assert.Error(t, err)
   164  }
   165  
   166  func TestToKubeContainerWithRuntimeHandlerInImageSpecCri(t *testing.T) {
   167  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RuntimeClassInImageCriAPI, true)()
   168  	c := &runtimeapi.Container{
   169  		Id: "test-id",
   170  		Metadata: &runtimeapi.ContainerMetadata{
   171  			Name:    "test-name",
   172  			Attempt: 1,
   173  		},
   174  		Image:    &runtimeapi.ImageSpec{Image: "test-image", RuntimeHandler: "test-runtimeHandler"},
   175  		ImageId:  "test-image-id",
   176  		ImageRef: "test-image-ref",
   177  		State:    runtimeapi.ContainerState_CONTAINER_RUNNING,
   178  		Annotations: map[string]string{
   179  			containerHashLabel: "1234",
   180  		},
   181  	}
   182  	expect := &kubecontainer.Container{
   183  		ID: kubecontainer.ContainerID{
   184  			Type: runtimetesting.FakeRuntimeName,
   185  			ID:   "test-id",
   186  		},
   187  		Name:                "test-name",
   188  		ImageID:             "test-image-id",
   189  		ImageRef:            "test-image-ref",
   190  		Image:               "test-image",
   191  		ImageRuntimeHandler: "test-runtimeHandler",
   192  		Hash:                uint64(0x1234),
   193  		State:               kubecontainer.ContainerStateRunning,
   194  	}
   195  
   196  	_, _, m, err := createTestRuntimeManager()
   197  	assert.NoError(t, err)
   198  	got, err := m.toKubeContainer(c)
   199  	assert.NoError(t, err)
   200  	assert.Equal(t, expect, got)
   201  
   202  	// unable to convert a nil pointer to a runtime container
   203  	_, err = m.toKubeContainer(nil)
   204  	assert.Error(t, err)
   205  	_, err = m.sandboxToKubeContainer(nil)
   206  	assert.Error(t, err)
   207  }
   208  
   209  func TestGetImageUser(t *testing.T) {
   210  	_, i, m, err := createTestRuntimeManager()
   211  	assert.NoError(t, err)
   212  
   213  	type image struct {
   214  		name     string
   215  		uid      *runtimeapi.Int64Value
   216  		username string
   217  	}
   218  
   219  	type imageUserValues struct {
   220  		// getImageUser can return (*int64)(nil) so comparing with *uid will break
   221  		// type cannot be *int64 as Golang does not allow to take the address of a numeric constant"
   222  		uid      interface{}
   223  		username string
   224  		err      error
   225  	}
   226  
   227  	tests := []struct {
   228  		description             string
   229  		originalImage           image
   230  		expectedImageUserValues imageUserValues
   231  	}{
   232  		{
   233  			"image without username and uid should return (new(int64), \"\", nil)",
   234  			image{
   235  				name:     "test-image-ref1",
   236  				uid:      (*runtimeapi.Int64Value)(nil),
   237  				username: "",
   238  			},
   239  			imageUserValues{
   240  				uid:      int64(0),
   241  				username: "",
   242  				err:      nil,
   243  			},
   244  		},
   245  		{
   246  			"image with username and no uid should return ((*int64)nil, imageStatus.Username, nil)",
   247  			image{
   248  				name:     "test-image-ref2",
   249  				uid:      (*runtimeapi.Int64Value)(nil),
   250  				username: "testUser",
   251  			},
   252  			imageUserValues{
   253  				uid:      (*int64)(nil),
   254  				username: "testUser",
   255  				err:      nil,
   256  			},
   257  		},
   258  		{
   259  			"image with uid should return (*int64, \"\", nil)",
   260  			image{
   261  				name: "test-image-ref3",
   262  				uid: &runtimeapi.Int64Value{
   263  					Value: 2,
   264  				},
   265  				username: "whatever",
   266  			},
   267  			imageUserValues{
   268  				uid:      int64(2),
   269  				username: "",
   270  				err:      nil,
   271  			},
   272  		},
   273  	}
   274  
   275  	i.SetFakeImages([]string{"test-image-ref1", "test-image-ref2", "test-image-ref3"})
   276  	for j, test := range tests {
   277  		ctx := context.Background()
   278  		i.Images[test.originalImage.name].Username = test.originalImage.username
   279  		i.Images[test.originalImage.name].Uid = test.originalImage.uid
   280  
   281  		uid, username, err := m.getImageUser(ctx, test.originalImage.name)
   282  		assert.NoError(t, err, "TestCase[%d]", j)
   283  
   284  		if test.expectedImageUserValues.uid == (*int64)(nil) {
   285  			assert.Equal(t, test.expectedImageUserValues.uid, uid, "TestCase[%d]", j)
   286  		} else {
   287  			assert.Equal(t, test.expectedImageUserValues.uid, *uid, "TestCase[%d]", j)
   288  		}
   289  		assert.Equal(t, test.expectedImageUserValues.username, username, "TestCase[%d]", j)
   290  	}
   291  }
   292  
   293  func TestToRuntimeProtocol(t *testing.T) {
   294  	for _, test := range []struct {
   295  		name     string
   296  		protocol string
   297  		expected runtimeapi.Protocol
   298  	}{
   299  		{
   300  			name:     "TCP protocol",
   301  			protocol: "TCP",
   302  			expected: runtimeapi.Protocol_TCP,
   303  		},
   304  		{
   305  			name:     "UDP protocol",
   306  			protocol: "UDP",
   307  			expected: runtimeapi.Protocol_UDP,
   308  		},
   309  		{
   310  			name:     "SCTP protocol",
   311  			protocol: "SCTP",
   312  			expected: runtimeapi.Protocol_SCTP,
   313  		},
   314  		{
   315  			name:     "unknown protocol",
   316  			protocol: "unknown",
   317  			expected: runtimeapi.Protocol_TCP,
   318  		},
   319  	} {
   320  		t.Run(test.name, func(t *testing.T) {
   321  			if result := toRuntimeProtocol(v1.Protocol(test.protocol)); result != test.expected {
   322  				t.Errorf("expected %d but got %d", test.expected, result)
   323  			}
   324  		})
   325  	}
   326  }
   327  
   328  func TestToKubeContainerState(t *testing.T) {
   329  	for _, test := range []struct {
   330  		name     string
   331  		state    int32
   332  		expected kubecontainer.State
   333  	}{
   334  		{
   335  			name:     "container created",
   336  			state:    0,
   337  			expected: kubecontainer.ContainerStateCreated,
   338  		},
   339  		{
   340  			name:     "container running",
   341  			state:    1,
   342  			expected: kubecontainer.ContainerStateRunning,
   343  		},
   344  		{
   345  			name:     "container exited",
   346  			state:    2,
   347  			expected: kubecontainer.ContainerStateExited,
   348  		},
   349  		{
   350  			name:     "unknown state",
   351  			state:    3,
   352  			expected: kubecontainer.ContainerStateUnknown,
   353  		},
   354  		{
   355  			name:     "not supported state",
   356  			state:    4,
   357  			expected: kubecontainer.ContainerStateUnknown,
   358  		},
   359  	} {
   360  		t.Run(test.name, func(t *testing.T) {
   361  			if result := toKubeContainerState(runtimeapi.ContainerState(test.state)); result != test.expected {
   362  				t.Errorf("expected %s but got %s", test.expected, result)
   363  			}
   364  		})
   365  	}
   366  }
   367  
   368  func TestGetAppArmorProfile(t *testing.T) {
   369  	tests := []struct {
   370  		name               string
   371  		podProfile         *v1.AppArmorProfile
   372  		expectedProfile    *runtimeapi.SecurityProfile
   373  		expectedOldProfile string
   374  		expectError        bool
   375  	}{{
   376  		name:            "no appArmor",
   377  		expectedProfile: nil,
   378  	}, {
   379  		name:       "runtime default",
   380  		podProfile: &v1.AppArmorProfile{Type: v1.AppArmorProfileTypeRuntimeDefault},
   381  		expectedProfile: &runtimeapi.SecurityProfile{
   382  			ProfileType: runtimeapi.SecurityProfile_RuntimeDefault,
   383  		},
   384  		expectedOldProfile: "runtime/default",
   385  	}, {
   386  		name:       "unconfined",
   387  		podProfile: &v1.AppArmorProfile{Type: v1.AppArmorProfileTypeUnconfined},
   388  		expectedProfile: &runtimeapi.SecurityProfile{
   389  			ProfileType: runtimeapi.SecurityProfile_Unconfined,
   390  		},
   391  		expectedOldProfile: "unconfined",
   392  	}, {
   393  		name: "localhost",
   394  		podProfile: &v1.AppArmorProfile{
   395  			Type:             v1.AppArmorProfileTypeLocalhost,
   396  			LocalhostProfile: ptr.To("test"),
   397  		},
   398  		expectedProfile: &runtimeapi.SecurityProfile{
   399  			ProfileType:  runtimeapi.SecurityProfile_Localhost,
   400  			LocalhostRef: "test",
   401  		},
   402  		expectedOldProfile: "localhost/test",
   403  	}, {
   404  		name: "invalid localhost",
   405  		podProfile: &v1.AppArmorProfile{
   406  			Type: v1.AppArmorProfileTypeLocalhost,
   407  		},
   408  		expectError: true,
   409  	}, {
   410  		name: "invalid type",
   411  		podProfile: &v1.AppArmorProfile{
   412  			Type: "foo",
   413  		},
   414  		expectError: true,
   415  	}}
   416  
   417  	for _, test := range tests {
   418  		t.Run(test.name, func(t *testing.T) {
   419  			pod := v1.Pod{
   420  				ObjectMeta: metav1.ObjectMeta{
   421  					Name: "bar",
   422  				},
   423  				Spec: v1.PodSpec{
   424  					SecurityContext: &v1.PodSecurityContext{
   425  						AppArmorProfile: test.podProfile,
   426  					},
   427  					Containers: []v1.Container{{Name: "foo"}},
   428  				},
   429  			}
   430  
   431  			actual, actualOld, err := getAppArmorProfile(&pod, &pod.Spec.Containers[0])
   432  
   433  			if test.expectError {
   434  				assert.Error(t, err)
   435  			} else {
   436  				assert.NoError(t, err)
   437  			}
   438  
   439  			assert.Equal(t, test.expectedProfile, actual, "AppArmor profile")
   440  			assert.Equal(t, test.expectedOldProfile, actualOld, "old (deprecated) profile string")
   441  		})
   442  	}
   443  }
   444  

View as plain text