| /* |
| 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) |
| } |
| } |