mirror of https://github.com/k3s-io/k3s
603 lines
20 KiB
Go
603 lines
20 KiB
Go
/*
|
|
Copyright 2015 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package pod
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/util/intstr"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
)
|
|
|
|
func TestFindPort(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
containers []v1.Container
|
|
port intstr.IntOrString
|
|
expected int
|
|
pass bool
|
|
}{{
|
|
name: "valid int, no ports",
|
|
containers: []v1.Container{{}},
|
|
port: intstr.FromInt(93),
|
|
expected: 93,
|
|
pass: true,
|
|
}, {
|
|
name: "valid int, with ports",
|
|
containers: []v1.Container{{Ports: []v1.ContainerPort{{
|
|
Name: "",
|
|
ContainerPort: 11,
|
|
Protocol: "TCP",
|
|
}, {
|
|
Name: "p",
|
|
ContainerPort: 22,
|
|
Protocol: "TCP",
|
|
}}}},
|
|
port: intstr.FromInt(93),
|
|
expected: 93,
|
|
pass: true,
|
|
}, {
|
|
name: "valid str, no ports",
|
|
containers: []v1.Container{{}},
|
|
port: intstr.FromString("p"),
|
|
expected: 0,
|
|
pass: false,
|
|
}, {
|
|
name: "valid str, one ctr with ports",
|
|
containers: []v1.Container{{Ports: []v1.ContainerPort{{
|
|
Name: "",
|
|
ContainerPort: 11,
|
|
Protocol: "UDP",
|
|
}, {
|
|
Name: "p",
|
|
ContainerPort: 22,
|
|
Protocol: "TCP",
|
|
}, {
|
|
Name: "q",
|
|
ContainerPort: 33,
|
|
Protocol: "TCP",
|
|
}}}},
|
|
port: intstr.FromString("q"),
|
|
expected: 33,
|
|
pass: true,
|
|
}, {
|
|
name: "valid str, two ctr with ports",
|
|
containers: []v1.Container{{}, {Ports: []v1.ContainerPort{{
|
|
Name: "",
|
|
ContainerPort: 11,
|
|
Protocol: "UDP",
|
|
}, {
|
|
Name: "p",
|
|
ContainerPort: 22,
|
|
Protocol: "TCP",
|
|
}, {
|
|
Name: "q",
|
|
ContainerPort: 33,
|
|
Protocol: "TCP",
|
|
}}}},
|
|
port: intstr.FromString("q"),
|
|
expected: 33,
|
|
pass: true,
|
|
}, {
|
|
name: "valid str, two ctr with same port",
|
|
containers: []v1.Container{{}, {Ports: []v1.ContainerPort{{
|
|
Name: "",
|
|
ContainerPort: 11,
|
|
Protocol: "UDP",
|
|
}, {
|
|
Name: "p",
|
|
ContainerPort: 22,
|
|
Protocol: "TCP",
|
|
}, {
|
|
Name: "q",
|
|
ContainerPort: 22,
|
|
Protocol: "TCP",
|
|
}}}},
|
|
port: intstr.FromString("q"),
|
|
expected: 22,
|
|
pass: true,
|
|
}, {
|
|
name: "valid str, invalid protocol",
|
|
containers: []v1.Container{{}, {Ports: []v1.ContainerPort{{
|
|
Name: "a",
|
|
ContainerPort: 11,
|
|
Protocol: "snmp",
|
|
},
|
|
}}},
|
|
port: intstr.FromString("a"),
|
|
expected: 0,
|
|
pass: false,
|
|
}, {
|
|
name: "valid hostPort",
|
|
containers: []v1.Container{{}, {Ports: []v1.ContainerPort{{
|
|
Name: "a",
|
|
ContainerPort: 11,
|
|
HostPort: 81,
|
|
Protocol: "TCP",
|
|
},
|
|
}}},
|
|
port: intstr.FromString("a"),
|
|
expected: 11,
|
|
pass: true,
|
|
},
|
|
{
|
|
name: "invalid hostPort",
|
|
containers: []v1.Container{{}, {Ports: []v1.ContainerPort{{
|
|
Name: "a",
|
|
ContainerPort: 11,
|
|
HostPort: -1,
|
|
Protocol: "TCP",
|
|
},
|
|
}}},
|
|
port: intstr.FromString("a"),
|
|
expected: 11,
|
|
pass: true,
|
|
//this should fail but passes.
|
|
},
|
|
{
|
|
name: "invalid ContainerPort",
|
|
containers: []v1.Container{{}, {Ports: []v1.ContainerPort{{
|
|
Name: "a",
|
|
ContainerPort: -1,
|
|
Protocol: "TCP",
|
|
},
|
|
}}},
|
|
port: intstr.FromString("a"),
|
|
expected: -1,
|
|
pass: true,
|
|
//this should fail but passes
|
|
},
|
|
{
|
|
name: "HostIP Address",
|
|
containers: []v1.Container{{}, {Ports: []v1.ContainerPort{{
|
|
Name: "a",
|
|
ContainerPort: 11,
|
|
HostIP: "192.168.1.1",
|
|
Protocol: "TCP",
|
|
},
|
|
}}},
|
|
port: intstr.FromString("a"),
|
|
expected: 11,
|
|
pass: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
port, err := FindPort(&v1.Pod{Spec: v1.PodSpec{Containers: tc.containers}},
|
|
&v1.ServicePort{Protocol: "TCP", TargetPort: tc.port})
|
|
if err != nil && tc.pass {
|
|
t.Errorf("unexpected error for %s: %v", tc.name, err)
|
|
}
|
|
if err == nil && !tc.pass {
|
|
t.Errorf("unexpected non-error for %s: %d", tc.name, port)
|
|
}
|
|
if port != tc.expected {
|
|
t.Errorf("wrong result for %s: expected %d, got %d", tc.name, tc.expected, port)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPodSecrets(t *testing.T) {
|
|
// Stub containing all possible secret references in a pod.
|
|
// The names of the referenced secrets match struct paths detected by reflection.
|
|
pod := &v1.Pod{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{{
|
|
EnvFrom: []v1.EnvFromSource{{
|
|
SecretRef: &v1.SecretEnvSource{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.Containers[*].EnvFrom[*].SecretRef"}}}},
|
|
Env: []v1.EnvVar{{
|
|
ValueFrom: &v1.EnvVarSource{
|
|
SecretKeyRef: &v1.SecretKeySelector{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.Containers[*].Env[*].ValueFrom.SecretKeyRef"}}}}}}},
|
|
ImagePullSecrets: []v1.LocalObjectReference{{
|
|
Name: "Spec.ImagePullSecrets"}},
|
|
InitContainers: []v1.Container{{
|
|
EnvFrom: []v1.EnvFromSource{{
|
|
SecretRef: &v1.SecretEnvSource{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.InitContainers[*].EnvFrom[*].SecretRef"}}}},
|
|
Env: []v1.EnvVar{{
|
|
ValueFrom: &v1.EnvVarSource{
|
|
SecretKeyRef: &v1.SecretKeySelector{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef"}}}}}}},
|
|
Volumes: []v1.Volume{{
|
|
VolumeSource: v1.VolumeSource{
|
|
AzureFile: &v1.AzureFileVolumeSource{
|
|
SecretName: "Spec.Volumes[*].VolumeSource.AzureFile.SecretName"}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
CephFS: &v1.CephFSVolumeSource{
|
|
SecretRef: &v1.LocalObjectReference{
|
|
Name: "Spec.Volumes[*].VolumeSource.CephFS.SecretRef"}}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
Cinder: &v1.CinderVolumeSource{
|
|
SecretRef: &v1.LocalObjectReference{
|
|
Name: "Spec.Volumes[*].VolumeSource.Cinder.SecretRef"}}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
FlexVolume: &v1.FlexVolumeSource{
|
|
SecretRef: &v1.LocalObjectReference{
|
|
Name: "Spec.Volumes[*].VolumeSource.FlexVolume.SecretRef"}}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
Projected: &v1.ProjectedVolumeSource{
|
|
Sources: []v1.VolumeProjection{{
|
|
Secret: &v1.SecretProjection{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.Volumes[*].VolumeSource.Projected.Sources[*].Secret"}}}}}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
RBD: &v1.RBDVolumeSource{
|
|
SecretRef: &v1.LocalObjectReference{
|
|
Name: "Spec.Volumes[*].VolumeSource.RBD.SecretRef"}}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
Secret: &v1.SecretVolumeSource{
|
|
SecretName: "Spec.Volumes[*].VolumeSource.Secret.SecretName"}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
Secret: &v1.SecretVolumeSource{
|
|
SecretName: "Spec.Volumes[*].VolumeSource.Secret"}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
ScaleIO: &v1.ScaleIOVolumeSource{
|
|
SecretRef: &v1.LocalObjectReference{
|
|
Name: "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef"}}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
ISCSI: &v1.ISCSIVolumeSource{
|
|
SecretRef: &v1.LocalObjectReference{
|
|
Name: "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef"}}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
StorageOS: &v1.StorageOSVolumeSource{
|
|
SecretRef: &v1.LocalObjectReference{
|
|
Name: "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef"}}}}},
|
|
},
|
|
}
|
|
extractedNames := sets.NewString()
|
|
VisitPodSecretNames(pod, func(name string) bool {
|
|
extractedNames.Insert(name)
|
|
return true
|
|
})
|
|
|
|
// excludedSecretPaths holds struct paths to fields with "secret" in the name that are not actually references to secret API objects
|
|
excludedSecretPaths := sets.NewString(
|
|
"Spec.Volumes[*].VolumeSource.CephFS.SecretFile",
|
|
)
|
|
// expectedSecretPaths holds struct paths to fields with "secret" in the name that are references to secret API objects.
|
|
// every path here should be represented as an example in the Pod stub above, with the secret name set to the path.
|
|
expectedSecretPaths := sets.NewString(
|
|
"Spec.Containers[*].EnvFrom[*].SecretRef",
|
|
"Spec.Containers[*].Env[*].ValueFrom.SecretKeyRef",
|
|
"Spec.ImagePullSecrets",
|
|
"Spec.InitContainers[*].EnvFrom[*].SecretRef",
|
|
"Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef",
|
|
"Spec.Volumes[*].VolumeSource.AzureFile.SecretName",
|
|
"Spec.Volumes[*].VolumeSource.CephFS.SecretRef",
|
|
"Spec.Volumes[*].VolumeSource.Cinder.SecretRef",
|
|
"Spec.Volumes[*].VolumeSource.FlexVolume.SecretRef",
|
|
"Spec.Volumes[*].VolumeSource.Projected.Sources[*].Secret",
|
|
"Spec.Volumes[*].VolumeSource.RBD.SecretRef",
|
|
"Spec.Volumes[*].VolumeSource.Secret",
|
|
"Spec.Volumes[*].VolumeSource.Secret.SecretName",
|
|
"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef",
|
|
"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef",
|
|
"Spec.Volumes[*].VolumeSource.StorageOS.SecretRef",
|
|
)
|
|
secretPaths := collectResourcePaths(t, "secret", nil, "", reflect.TypeOf(&v1.Pod{}))
|
|
secretPaths = secretPaths.Difference(excludedSecretPaths)
|
|
if missingPaths := expectedSecretPaths.Difference(secretPaths); len(missingPaths) > 0 {
|
|
t.Logf("Missing expected secret paths:\n%s", strings.Join(missingPaths.List(), "\n"))
|
|
t.Error("Missing expected secret paths. Verify VisitPodSecretNames() is correctly finding the missing paths, then correct expectedSecretPaths")
|
|
}
|
|
if extraPaths := secretPaths.Difference(expectedSecretPaths); len(extraPaths) > 0 {
|
|
t.Logf("Extra secret paths:\n%s", strings.Join(extraPaths.List(), "\n"))
|
|
t.Error("Extra fields with 'secret' in the name found. Verify VisitPodSecretNames() is including these fields if appropriate, then correct expectedSecretPaths")
|
|
}
|
|
|
|
if missingNames := expectedSecretPaths.Difference(extractedNames); len(missingNames) > 0 {
|
|
t.Logf("Missing expected secret names:\n%s", strings.Join(missingNames.List(), "\n"))
|
|
t.Error("Missing expected secret names. Verify the pod stub above includes these references, then verify VisitPodSecretNames() is correctly finding the missing names")
|
|
}
|
|
if extraNames := extractedNames.Difference(expectedSecretPaths); len(extraNames) > 0 {
|
|
t.Logf("Extra secret names:\n%s", strings.Join(extraNames.List(), "\n"))
|
|
t.Error("Extra secret names extracted. Verify VisitPodSecretNames() is correctly extracting secret names")
|
|
}
|
|
}
|
|
|
|
// collectResourcePaths traverses the object, computing all the struct paths that lead to fields with resourcename in the name.
|
|
func collectResourcePaths(t *testing.T, resourcename string, path *field.Path, name string, tp reflect.Type) sets.String {
|
|
resourcename = strings.ToLower(resourcename)
|
|
resourcePaths := sets.NewString()
|
|
|
|
if tp.Kind() == reflect.Ptr {
|
|
resourcePaths.Insert(collectResourcePaths(t, resourcename, path, name, tp.Elem()).List()...)
|
|
return resourcePaths
|
|
}
|
|
|
|
if strings.Contains(strings.ToLower(name), resourcename) {
|
|
resourcePaths.Insert(path.String())
|
|
}
|
|
|
|
switch tp.Kind() {
|
|
case reflect.Ptr:
|
|
resourcePaths.Insert(collectResourcePaths(t, resourcename, path, name, tp.Elem()).List()...)
|
|
case reflect.Struct:
|
|
for i := 0; i < tp.NumField(); i++ {
|
|
field := tp.Field(i)
|
|
resourcePaths.Insert(collectResourcePaths(t, resourcename, path.Child(field.Name), field.Name, field.Type).List()...)
|
|
}
|
|
case reflect.Interface:
|
|
t.Errorf("cannot find %s fields in interface{} field %s", resourcename, path.String())
|
|
case reflect.Map:
|
|
resourcePaths.Insert(collectResourcePaths(t, resourcename, path.Key("*"), "", tp.Elem()).List()...)
|
|
case reflect.Slice:
|
|
resourcePaths.Insert(collectResourcePaths(t, resourcename, path.Key("*"), "", tp.Elem()).List()...)
|
|
default:
|
|
// all primitive types
|
|
}
|
|
|
|
return resourcePaths
|
|
}
|
|
|
|
func TestPodConfigmaps(t *testing.T) {
|
|
// Stub containing all possible ConfigMap references in a pod.
|
|
// The names of the referenced ConfigMaps match struct paths detected by reflection.
|
|
pod := &v1.Pod{
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{{
|
|
EnvFrom: []v1.EnvFromSource{{
|
|
ConfigMapRef: &v1.ConfigMapEnvSource{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.Containers[*].EnvFrom[*].ConfigMapRef"}}}},
|
|
Env: []v1.EnvVar{{
|
|
ValueFrom: &v1.EnvVarSource{
|
|
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}},
|
|
InitContainers: []v1.Container{{
|
|
EnvFrom: []v1.EnvFromSource{{
|
|
ConfigMapRef: &v1.ConfigMapEnvSource{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.InitContainers[*].EnvFrom[*].ConfigMapRef"}}}},
|
|
Env: []v1.EnvVar{{
|
|
ValueFrom: &v1.EnvVarSource{
|
|
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.InitContainers[*].Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}},
|
|
Volumes: []v1.Volume{{
|
|
VolumeSource: v1.VolumeSource{
|
|
Projected: &v1.ProjectedVolumeSource{
|
|
Sources: []v1.VolumeProjection{{
|
|
ConfigMap: &v1.ConfigMapProjection{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.Volumes[*].VolumeSource.Projected.Sources[*].ConfigMap"}}}}}}}, {
|
|
VolumeSource: v1.VolumeSource{
|
|
ConfigMap: &v1.ConfigMapVolumeSource{
|
|
LocalObjectReference: v1.LocalObjectReference{
|
|
Name: "Spec.Volumes[*].VolumeSource.ConfigMap"}}}}},
|
|
},
|
|
}
|
|
extractedNames := sets.NewString()
|
|
VisitPodConfigmapNames(pod, func(name string) bool {
|
|
extractedNames.Insert(name)
|
|
return true
|
|
})
|
|
|
|
// expectedPaths holds struct paths to fields with "ConfigMap" in the name that are references to ConfigMap API objects.
|
|
// every path here should be represented as an example in the Pod stub above, with the ConfigMap name set to the path.
|
|
expectedPaths := sets.NewString(
|
|
"Spec.Containers[*].EnvFrom[*].ConfigMapRef",
|
|
"Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef",
|
|
"Spec.InitContainers[*].EnvFrom[*].ConfigMapRef",
|
|
"Spec.InitContainers[*].Env[*].ValueFrom.ConfigMapKeyRef",
|
|
"Spec.Volumes[*].VolumeSource.Projected.Sources[*].ConfigMap",
|
|
"Spec.Volumes[*].VolumeSource.ConfigMap",
|
|
)
|
|
collectPaths := collectResourcePaths(t, "ConfigMap", nil, "", reflect.TypeOf(&v1.Pod{}))
|
|
if missingPaths := expectedPaths.Difference(collectPaths); len(missingPaths) > 0 {
|
|
t.Logf("Missing expected paths:\n%s", strings.Join(missingPaths.List(), "\n"))
|
|
t.Error("Missing expected paths. Verify VisitPodConfigmapNames() is correctly finding the missing paths, then correct expectedPaths")
|
|
}
|
|
if extraPaths := collectPaths.Difference(expectedPaths); len(extraPaths) > 0 {
|
|
t.Logf("Extra paths:\n%s", strings.Join(extraPaths.List(), "\n"))
|
|
t.Error("Extra fields with resource in the name found. Verify VisitPodConfigmapNames() is including these fields if appropriate, then correct expectedPaths")
|
|
}
|
|
|
|
if missingNames := expectedPaths.Difference(extractedNames); len(missingNames) > 0 {
|
|
t.Logf("Missing expected names:\n%s", strings.Join(missingNames.List(), "\n"))
|
|
t.Error("Missing expected names. Verify the pod stub above includes these references, then verify VisitPodConfigmapNames() is correctly finding the missing names")
|
|
}
|
|
if extraNames := extractedNames.Difference(expectedPaths); len(extraNames) > 0 {
|
|
t.Logf("Extra names:\n%s", strings.Join(extraNames.List(), "\n"))
|
|
t.Error("Extra names extracted. Verify VisitPodConfigmapNames() is correctly extracting resource names")
|
|
}
|
|
}
|
|
|
|
func newPod(now metav1.Time, ready bool, beforeSec int) *v1.Pod {
|
|
conditionStatus := v1.ConditionFalse
|
|
if ready {
|
|
conditionStatus = v1.ConditionTrue
|
|
}
|
|
return &v1.Pod{
|
|
Status: v1.PodStatus{
|
|
Conditions: []v1.PodCondition{
|
|
{
|
|
Type: v1.PodReady,
|
|
LastTransitionTime: metav1.NewTime(now.Time.Add(-1 * time.Duration(beforeSec) * time.Second)),
|
|
Status: conditionStatus,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestIsPodAvailable(t *testing.T) {
|
|
now := metav1.Now()
|
|
tests := []struct {
|
|
pod *v1.Pod
|
|
minReadySeconds int32
|
|
expected bool
|
|
}{
|
|
{
|
|
pod: newPod(now, false, 0),
|
|
minReadySeconds: 0,
|
|
expected: false,
|
|
},
|
|
{
|
|
pod: newPod(now, true, 0),
|
|
minReadySeconds: 1,
|
|
expected: false,
|
|
},
|
|
{
|
|
pod: newPod(now, true, 0),
|
|
minReadySeconds: 0,
|
|
expected: true,
|
|
},
|
|
{
|
|
pod: newPod(now, true, 51),
|
|
minReadySeconds: 50,
|
|
expected: true,
|
|
},
|
|
}
|
|
|
|
for i, test := range tests {
|
|
isAvailable := IsPodAvailable(test.pod, test.minReadySeconds, now)
|
|
if isAvailable != test.expected {
|
|
t.Errorf("[tc #%d] expected available pod: %t, got: %t", i, test.expected, isAvailable)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetContainerStatus(t *testing.T) {
|
|
type ExpectedStruct struct {
|
|
status v1.ContainerStatus
|
|
exists bool
|
|
}
|
|
|
|
tests := []struct {
|
|
status []v1.ContainerStatus
|
|
name string
|
|
expected ExpectedStruct
|
|
desc string
|
|
}{
|
|
{
|
|
status: []v1.ContainerStatus{{Name: "test1", Ready: false, Image: "image1"}, {Name: "test2", Ready: true, Image: "image1"}},
|
|
name: "test1",
|
|
expected: ExpectedStruct{status: v1.ContainerStatus{Name: "test1", Ready: false, Image: "image1"}, exists: true},
|
|
desc: "retrieve ContainerStatus with Name=\"test1\"",
|
|
},
|
|
{
|
|
status: []v1.ContainerStatus{{Name: "test2", Ready: false, Image: "image2"}},
|
|
name: "test1",
|
|
expected: ExpectedStruct{status: v1.ContainerStatus{}, exists: false},
|
|
desc: "no matching ContainerStatus with Name=\"test1\"",
|
|
},
|
|
{
|
|
status: []v1.ContainerStatus{{Name: "test3", Ready: false, Image: "image3"}},
|
|
name: "",
|
|
expected: ExpectedStruct{status: v1.ContainerStatus{}, exists: false},
|
|
desc: "retrieve an empty ContainerStatus with container name empty",
|
|
},
|
|
{
|
|
status: nil,
|
|
name: "",
|
|
expected: ExpectedStruct{status: v1.ContainerStatus{}, exists: false},
|
|
desc: "retrieve an empty ContainerStatus with status nil",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
resultStatus, exists := GetContainerStatus(test.status, test.name)
|
|
assert.Equal(t, test.expected.status, resultStatus, "GetContainerStatus: "+test.desc)
|
|
assert.Equal(t, test.expected.exists, exists, "GetContainerStatus: "+test.desc)
|
|
|
|
resultStatus = GetExistingContainerStatus(test.status, test.name)
|
|
assert.Equal(t, test.expected.status, resultStatus, "GetExistingContainerStatus: "+test.desc)
|
|
}
|
|
}
|
|
|
|
func TestUpdatePodCondition(t *testing.T) {
|
|
time := metav1.Now()
|
|
|
|
podStatus := v1.PodStatus{
|
|
Conditions: []v1.PodCondition{
|
|
{
|
|
Type: v1.PodReady,
|
|
Status: v1.ConditionTrue,
|
|
Reason: "successfully",
|
|
Message: "sync pod successfully",
|
|
LastProbeTime: time,
|
|
LastTransitionTime: metav1.NewTime(time.Add(1000)),
|
|
},
|
|
},
|
|
}
|
|
tests := []struct {
|
|
status *v1.PodStatus
|
|
conditions v1.PodCondition
|
|
expected bool
|
|
desc string
|
|
}{
|
|
{
|
|
status: &podStatus,
|
|
conditions: v1.PodCondition{
|
|
Type: v1.PodReady,
|
|
Status: v1.ConditionTrue,
|
|
Reason: "successfully",
|
|
Message: "sync pod successfully",
|
|
LastProbeTime: time,
|
|
LastTransitionTime: metav1.NewTime(time.Add(1000))},
|
|
expected: false,
|
|
desc: "all equal, no update",
|
|
},
|
|
{
|
|
status: &podStatus,
|
|
conditions: v1.PodCondition{
|
|
Type: v1.PodScheduled,
|
|
Status: v1.ConditionTrue,
|
|
Reason: "successfully",
|
|
Message: "sync pod successfully",
|
|
LastProbeTime: time,
|
|
LastTransitionTime: metav1.NewTime(time.Add(1000))},
|
|
expected: true,
|
|
desc: "not equal Type, should get updated",
|
|
},
|
|
{
|
|
status: &podStatus,
|
|
conditions: v1.PodCondition{
|
|
Type: v1.PodReady,
|
|
Status: v1.ConditionFalse,
|
|
Reason: "successfully",
|
|
Message: "sync pod successfully",
|
|
LastProbeTime: time,
|
|
LastTransitionTime: metav1.NewTime(time.Add(1000))},
|
|
expected: true,
|
|
desc: "not equal Status, should get updated",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
var resultStatus bool
|
|
resultStatus = UpdatePodCondition(test.status, &test.conditions)
|
|
|
|
assert.Equal(t, test.expected, resultStatus, test.desc)
|
|
}
|
|
}
|