Update go dependencies
This commit is contained in:
parent
15ffb51394
commit
bb4d483837
1621 changed files with 86368 additions and 284392 deletions
907
vendor/k8s.io/kubernetes/CHANGELOG-1.10.md
generated
vendored
907
vendor/k8s.io/kubernetes/CHANGELOG-1.10.md
generated
vendored
File diff suppressed because it is too large
Load diff
216
vendor/k8s.io/kubernetes/pkg/api/service/util_test.go
generated
vendored
216
vendor/k8s.io/kubernetes/pkg/api/service/util_test.go
generated
vendored
|
|
@ -1,216 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 service
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
netsets "k8s.io/kubernetes/pkg/util/net/sets"
|
||||
)
|
||||
|
||||
func TestGetLoadBalancerSourceRanges(t *testing.T) {
|
||||
checkError := func(v string) {
|
||||
annotations := make(map[string]string)
|
||||
annotations[api.AnnotationLoadBalancerSourceRangesKey] = v
|
||||
svc := api.Service{}
|
||||
svc.Annotations = annotations
|
||||
_, err := GetLoadBalancerSourceRanges(&svc)
|
||||
if err == nil {
|
||||
t.Errorf("Expected error parsing: %q", v)
|
||||
}
|
||||
svc = api.Service{}
|
||||
svc.Spec.LoadBalancerSourceRanges = strings.Split(v, ",")
|
||||
_, err = GetLoadBalancerSourceRanges(&svc)
|
||||
if err == nil {
|
||||
t.Errorf("Expected error parsing: %q", v)
|
||||
}
|
||||
}
|
||||
checkError("10.0.0.1/33")
|
||||
checkError("foo.bar")
|
||||
checkError("10.0.0.1/32,*")
|
||||
checkError("10.0.0.1/32,")
|
||||
checkError("10.0.0.1/32, ")
|
||||
checkError("10.0.0.1")
|
||||
|
||||
checkOK := func(v string) netsets.IPNet {
|
||||
annotations := make(map[string]string)
|
||||
annotations[api.AnnotationLoadBalancerSourceRangesKey] = v
|
||||
svc := api.Service{}
|
||||
svc.Annotations = annotations
|
||||
cidrs, err := GetLoadBalancerSourceRanges(&svc)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error parsing: %q", v)
|
||||
}
|
||||
svc = api.Service{}
|
||||
svc.Spec.LoadBalancerSourceRanges = strings.Split(v, ",")
|
||||
cidrs, err = GetLoadBalancerSourceRanges(&svc)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error parsing: %q", v)
|
||||
}
|
||||
return cidrs
|
||||
}
|
||||
cidrs := checkOK("192.168.0.1/32")
|
||||
if len(cidrs) != 1 {
|
||||
t.Errorf("Expected exactly one CIDR: %v", cidrs.StringSlice())
|
||||
}
|
||||
cidrs = checkOK("192.168.0.1/32,192.168.0.1/32")
|
||||
if len(cidrs) != 1 {
|
||||
t.Errorf("Expected exactly one CIDR (after de-dup): %v", cidrs.StringSlice())
|
||||
}
|
||||
cidrs = checkOK("192.168.0.1/32,192.168.0.2/32")
|
||||
if len(cidrs) != 2 {
|
||||
t.Errorf("Expected two CIDRs: %v", cidrs.StringSlice())
|
||||
}
|
||||
cidrs = checkOK(" 192.168.0.1/32 , 192.168.0.2/32 ")
|
||||
if len(cidrs) != 2 {
|
||||
t.Errorf("Expected two CIDRs: %v", cidrs.StringSlice())
|
||||
}
|
||||
// check LoadBalancerSourceRanges not specified
|
||||
svc := api.Service{}
|
||||
cidrs, err := GetLoadBalancerSourceRanges(&svc)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(cidrs) != 1 {
|
||||
t.Errorf("Expected exactly one CIDR: %v", cidrs.StringSlice())
|
||||
}
|
||||
if !IsAllowAll(cidrs) {
|
||||
t.Errorf("Expected default to be allow-all: %v", cidrs.StringSlice())
|
||||
}
|
||||
// check SourceRanges annotation is empty
|
||||
annotations := make(map[string]string)
|
||||
annotations[api.AnnotationLoadBalancerSourceRangesKey] = ""
|
||||
svc = api.Service{}
|
||||
svc.Annotations = annotations
|
||||
cidrs, err = GetLoadBalancerSourceRanges(&svc)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(cidrs) != 1 {
|
||||
t.Errorf("Expected exactly one CIDR: %v", cidrs.StringSlice())
|
||||
}
|
||||
if !IsAllowAll(cidrs) {
|
||||
t.Errorf("Expected default to be allow-all: %v", cidrs.StringSlice())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowAll(t *testing.T) {
|
||||
checkAllowAll := func(allowAll bool, cidrs ...string) {
|
||||
ipnets, err := netsets.ParseIPNets(cidrs...)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error parsing cidrs: %v", cidrs)
|
||||
}
|
||||
if allowAll != IsAllowAll(ipnets) {
|
||||
t.Errorf("IsAllowAll did not return expected value for %v", cidrs)
|
||||
}
|
||||
}
|
||||
checkAllowAll(false, "10.0.0.1/32")
|
||||
checkAllowAll(false, "10.0.0.1/32", "10.0.0.2/32")
|
||||
checkAllowAll(false, "10.0.0.1/32", "10.0.0.1/32")
|
||||
|
||||
checkAllowAll(true, "0.0.0.0/0")
|
||||
checkAllowAll(true, "192.168.0.0/0")
|
||||
checkAllowAll(true, "192.168.0.1/32", "0.0.0.0/0")
|
||||
}
|
||||
|
||||
func TestRequestsOnlyLocalTraffic(t *testing.T) {
|
||||
checkRequestsOnlyLocalTraffic := func(requestsOnlyLocalTraffic bool, service *api.Service) {
|
||||
res := RequestsOnlyLocalTraffic(service)
|
||||
if res != requestsOnlyLocalTraffic {
|
||||
t.Errorf("Expected requests OnlyLocal traffic = %v, got %v",
|
||||
requestsOnlyLocalTraffic, res)
|
||||
}
|
||||
}
|
||||
|
||||
checkRequestsOnlyLocalTraffic(false, &api.Service{})
|
||||
checkRequestsOnlyLocalTraffic(false, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
})
|
||||
checkRequestsOnlyLocalTraffic(false, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeNodePort,
|
||||
},
|
||||
})
|
||||
checkRequestsOnlyLocalTraffic(false, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeNodePort,
|
||||
ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeCluster,
|
||||
},
|
||||
})
|
||||
checkRequestsOnlyLocalTraffic(true, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeNodePort,
|
||||
ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal,
|
||||
},
|
||||
})
|
||||
checkRequestsOnlyLocalTraffic(false, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeLoadBalancer,
|
||||
ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeCluster,
|
||||
},
|
||||
})
|
||||
checkRequestsOnlyLocalTraffic(true, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeLoadBalancer,
|
||||
ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestNeedsHealthCheck(t *testing.T) {
|
||||
checkNeedsHealthCheck := func(needsHealthCheck bool, service *api.Service) {
|
||||
res := NeedsHealthCheck(service)
|
||||
if res != needsHealthCheck {
|
||||
t.Errorf("Expected needs health check = %v, got %v",
|
||||
needsHealthCheck, res)
|
||||
}
|
||||
}
|
||||
|
||||
checkNeedsHealthCheck(false, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
})
|
||||
checkNeedsHealthCheck(false, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeNodePort,
|
||||
ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeCluster,
|
||||
},
|
||||
})
|
||||
checkNeedsHealthCheck(false, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeNodePort,
|
||||
ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal,
|
||||
},
|
||||
})
|
||||
checkNeedsHealthCheck(false, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeLoadBalancer,
|
||||
ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeCluster,
|
||||
},
|
||||
})
|
||||
checkNeedsHealthCheck(true, &api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Type: api.ServiceTypeLoadBalancer,
|
||||
ExternalTrafficPolicy: api.ServiceExternalTrafficPolicyTypeLocal,
|
||||
},
|
||||
})
|
||||
}
|
||||
522
vendor/k8s.io/kubernetes/pkg/api/v1/pod/util_test.go
generated
vendored
522
vendor/k8s.io/kubernetes/pkg/api/v1/pod/util_test.go
generated
vendored
|
|
@ -1,522 +0,0 @@
|
|||
/*
|
||||
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{
|
||||
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.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 := collectSecretPaths(t, 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")
|
||||
}
|
||||
}
|
||||
|
||||
// collectSecretPaths traverses the object, computing all the struct paths that lead to fields with "secret" in the name.
|
||||
func collectSecretPaths(t *testing.T, path *field.Path, name string, tp reflect.Type) sets.String {
|
||||
secretPaths := sets.NewString()
|
||||
|
||||
if tp.Kind() == reflect.Ptr {
|
||||
secretPaths.Insert(collectSecretPaths(t, path, name, tp.Elem()).List()...)
|
||||
return secretPaths
|
||||
}
|
||||
|
||||
if strings.Contains(strings.ToLower(name), "secret") {
|
||||
secretPaths.Insert(path.String())
|
||||
}
|
||||
|
||||
switch tp.Kind() {
|
||||
case reflect.Ptr:
|
||||
secretPaths.Insert(collectSecretPaths(t, path, name, tp.Elem()).List()...)
|
||||
case reflect.Struct:
|
||||
for i := 0; i < tp.NumField(); i++ {
|
||||
field := tp.Field(i)
|
||||
secretPaths.Insert(collectSecretPaths(t, path.Child(field.Name), field.Name, field.Type).List()...)
|
||||
}
|
||||
case reflect.Interface:
|
||||
t.Errorf("cannot find secret fields in interface{} field %s", path.String())
|
||||
case reflect.Map:
|
||||
secretPaths.Insert(collectSecretPaths(t, path.Key("*"), "", tp.Elem()).List()...)
|
||||
case reflect.Slice:
|
||||
secretPaths.Insert(collectSecretPaths(t, path.Key("*"), "", tp.Elem()).List()...)
|
||||
default:
|
||||
// all primitive types
|
||||
}
|
||||
|
||||
return secretPaths
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
491
vendor/k8s.io/kubernetes/pkg/apis/core/helper/helpers_test.go
generated
vendored
491
vendor/k8s.io/kubernetes/pkg/apis/core/helper/helpers_test.go
generated
vendored
|
|
@ -1,491 +0,0 @@
|
|||
/*
|
||||
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 helper
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
func TestSemantic(t *testing.T) {
|
||||
table := []struct {
|
||||
a, b interface{}
|
||||
shouldEqual bool
|
||||
}{
|
||||
{resource.MustParse("0"), resource.Quantity{}, true},
|
||||
{resource.Quantity{}, resource.MustParse("0"), true},
|
||||
{resource.Quantity{}, resource.MustParse("1m"), false},
|
||||
{
|
||||
resource.NewQuantity(5, resource.BinarySI),
|
||||
resource.NewQuantity(5, resource.DecimalSI),
|
||||
true,
|
||||
},
|
||||
{resource.MustParse("2m"), resource.MustParse("1m"), false},
|
||||
}
|
||||
|
||||
for index, item := range table {
|
||||
if e, a := item.shouldEqual, Semantic.DeepEqual(item.a, item.b); e != a {
|
||||
t.Errorf("case[%d], expected %v, got %v.", index, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsStandardResource(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output bool
|
||||
}{
|
||||
{"cpu", true},
|
||||
{"memory", true},
|
||||
{"disk", false},
|
||||
{"blah", false},
|
||||
{"x.y.z", false},
|
||||
{"hugepages-2Mi", true},
|
||||
{"requests.hugepages-2Mi", true},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
if IsStandardResourceName(tc.input) != tc.output {
|
||||
t.Errorf("case[%d], input: %s, expected: %t, got: %t", i, tc.input, tc.output, !tc.output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsStandardContainerResource(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
output bool
|
||||
}{
|
||||
{"cpu", true},
|
||||
{"memory", true},
|
||||
{"disk", false},
|
||||
{"hugepages-2Mi", true},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
if IsStandardContainerResourceName(tc.input) != tc.output {
|
||||
t.Errorf("case[%d], input: %s, expected: %t, got: %t", i, tc.input, tc.output, !tc.output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddToNodeAddresses(t *testing.T) {
|
||||
testCases := []struct {
|
||||
existing []core.NodeAddress
|
||||
toAdd []core.NodeAddress
|
||||
expected []core.NodeAddress
|
||||
}{
|
||||
{
|
||||
existing: []core.NodeAddress{},
|
||||
toAdd: []core.NodeAddress{},
|
||||
expected: []core.NodeAddress{},
|
||||
},
|
||||
{
|
||||
existing: []core.NodeAddress{},
|
||||
toAdd: []core.NodeAddress{
|
||||
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: core.NodeHostName, Address: "localhost"},
|
||||
},
|
||||
expected: []core.NodeAddress{
|
||||
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: core.NodeHostName, Address: "localhost"},
|
||||
},
|
||||
},
|
||||
{
|
||||
existing: []core.NodeAddress{},
|
||||
toAdd: []core.NodeAddress{
|
||||
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
|
||||
},
|
||||
expected: []core.NodeAddress{
|
||||
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
existing: []core.NodeAddress{
|
||||
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: core.NodeInternalIP, Address: "10.1.1.1"},
|
||||
},
|
||||
toAdd: []core.NodeAddress{
|
||||
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: core.NodeHostName, Address: "localhost"},
|
||||
},
|
||||
expected: []core.NodeAddress{
|
||||
{Type: core.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: core.NodeInternalIP, Address: "10.1.1.1"},
|
||||
{Type: core.NodeHostName, Address: "localhost"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
AddToNodeAddresses(&tc.existing, tc.toAdd...)
|
||||
if !Semantic.DeepEqual(tc.expected, tc.existing) {
|
||||
t.Errorf("case[%d], expected: %v, got: %v", i, tc.expected, tc.existing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccessModesFromString(t *testing.T) {
|
||||
modes := GetAccessModesFromString("ROX")
|
||||
if !containsAccessMode(modes, core.ReadOnlyMany) {
|
||||
t.Errorf("Expected mode %s, but got %+v", core.ReadOnlyMany, modes)
|
||||
}
|
||||
|
||||
modes = GetAccessModesFromString("ROX,RWX")
|
||||
if !containsAccessMode(modes, core.ReadOnlyMany) {
|
||||
t.Errorf("Expected mode %s, but got %+v", core.ReadOnlyMany, modes)
|
||||
}
|
||||
if !containsAccessMode(modes, core.ReadWriteMany) {
|
||||
t.Errorf("Expected mode %s, but got %+v", core.ReadWriteMany, modes)
|
||||
}
|
||||
|
||||
modes = GetAccessModesFromString("RWO,ROX,RWX")
|
||||
if !containsAccessMode(modes, core.ReadOnlyMany) {
|
||||
t.Errorf("Expected mode %s, but got %+v", core.ReadOnlyMany, modes)
|
||||
}
|
||||
if !containsAccessMode(modes, core.ReadWriteMany) {
|
||||
t.Errorf("Expected mode %s, but got %+v", core.ReadWriteMany, modes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveDuplicateAccessModes(t *testing.T) {
|
||||
modes := []core.PersistentVolumeAccessMode{
|
||||
core.ReadWriteOnce, core.ReadOnlyMany, core.ReadOnlyMany, core.ReadOnlyMany,
|
||||
}
|
||||
modes = removeDuplicateAccessModes(modes)
|
||||
if len(modes) != 2 {
|
||||
t.Errorf("Expected 2 distinct modes in set but found %v", len(modes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeSelectorRequirementsAsSelector(t *testing.T) {
|
||||
matchExpressions := []core.NodeSelectorRequirement{{
|
||||
Key: "foo",
|
||||
Operator: core.NodeSelectorOpIn,
|
||||
Values: []string{"bar", "baz"},
|
||||
}}
|
||||
mustParse := func(s string) labels.Selector {
|
||||
out, e := labels.Parse(s)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return out
|
||||
}
|
||||
tc := []struct {
|
||||
in []core.NodeSelectorRequirement
|
||||
out labels.Selector
|
||||
expectErr bool
|
||||
}{
|
||||
{in: nil, out: labels.Nothing()},
|
||||
{in: []core.NodeSelectorRequirement{}, out: labels.Nothing()},
|
||||
{
|
||||
in: matchExpressions,
|
||||
out: mustParse("foo in (baz,bar)"),
|
||||
},
|
||||
{
|
||||
in: []core.NodeSelectorRequirement{{
|
||||
Key: "foo",
|
||||
Operator: core.NodeSelectorOpExists,
|
||||
Values: []string{"bar", "baz"},
|
||||
}},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
in: []core.NodeSelectorRequirement{{
|
||||
Key: "foo",
|
||||
Operator: core.NodeSelectorOpGt,
|
||||
Values: []string{"1"},
|
||||
}},
|
||||
out: mustParse("foo>1"),
|
||||
},
|
||||
{
|
||||
in: []core.NodeSelectorRequirement{{
|
||||
Key: "bar",
|
||||
Operator: core.NodeSelectorOpLt,
|
||||
Values: []string{"7"},
|
||||
}},
|
||||
out: mustParse("bar<7"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tc {
|
||||
out, err := NodeSelectorRequirementsAsSelector(tc.in)
|
||||
if err == nil && tc.expectErr {
|
||||
t.Errorf("[%v]expected error but got none.", i)
|
||||
}
|
||||
if err != nil && !tc.expectErr {
|
||||
t.Errorf("[%v]did not expect error but got: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(out, tc.out) {
|
||||
t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysctlsFromPodAnnotation(t *testing.T) {
|
||||
type Test struct {
|
||||
annotation string
|
||||
expectValue []core.Sysctl
|
||||
expectErr bool
|
||||
}
|
||||
for i, test := range []Test{
|
||||
{
|
||||
annotation: "",
|
||||
expectValue: nil,
|
||||
},
|
||||
{
|
||||
annotation: "foo.bar",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotation: "=123",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotation: "foo.bar=",
|
||||
expectValue: []core.Sysctl{{Name: "foo.bar", Value: ""}},
|
||||
},
|
||||
{
|
||||
annotation: "foo.bar=42",
|
||||
expectValue: []core.Sysctl{{Name: "foo.bar", Value: "42"}},
|
||||
},
|
||||
{
|
||||
annotation: "foo.bar=42,",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotation: "foo.bar=42,abc.def=1",
|
||||
expectValue: []core.Sysctl{{Name: "foo.bar", Value: "42"}, {Name: "abc.def", Value: "1"}},
|
||||
},
|
||||
} {
|
||||
sysctls, err := SysctlsFromPodAnnotation(test.annotation)
|
||||
if test.expectErr && err == nil {
|
||||
t.Errorf("[%v]expected error but got none", i)
|
||||
} else if !test.expectErr && err != nil {
|
||||
t.Errorf("[%v]did not expect error but got: %v", i, err)
|
||||
} else if !reflect.DeepEqual(sysctls, test.expectValue) {
|
||||
t.Errorf("[%v]expect value %v but got %v", i, test.expectValue, sysctls)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove when alpha support for topology constraints is removed
|
||||
func TestGetNodeAffinityFromAnnotations(t *testing.T) {
|
||||
testCases := []struct {
|
||||
annotations map[string]string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
annotations: nil,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{
|
||||
core.AlphaStorageNodeAffinityAnnotation: `{
|
||||
"requiredDuringSchedulingIgnoredDuringExecution": {
|
||||
"nodeSelectorTerms": [
|
||||
{ "matchExpressions": [
|
||||
{ "key": "test-key1",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
},
|
||||
{ "key": "test-key2",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
}
|
||||
]}
|
||||
]}
|
||||
}`,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{
|
||||
core.AlphaStorageNodeAffinityAnnotation: `[{
|
||||
"requiredDuringSchedulingIgnoredDuringExecution": {
|
||||
"nodeSelectorTerms": [
|
||||
{ "matchExpressions": [
|
||||
{ "key": "test-key1",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
},
|
||||
{ "key": "test-key2",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
}
|
||||
]}
|
||||
]}
|
||||
}]`,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{
|
||||
core.AlphaStorageNodeAffinityAnnotation: `{
|
||||
"requiredDuringSchedulingIgnoredDuringExecution": {
|
||||
"nodeSelectorTerms":
|
||||
"matchExpressions": [
|
||||
{ "key": "test-key1",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
},
|
||||
{ "key": "test-key2",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
}
|
||||
]}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
_, err := GetStorageNodeAffinityFromAnnotation(tc.annotations)
|
||||
if err == nil && tc.expectErr {
|
||||
t.Errorf("[%v]expected error but got none.", i)
|
||||
}
|
||||
if err != nil && !tc.expectErr {
|
||||
t.Errorf("[%v]did not expect error but got: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsHugePageResourceName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name core.ResourceName
|
||||
result bool
|
||||
}{
|
||||
{
|
||||
name: core.ResourceName("hugepages-2Mi"),
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: core.ResourceName("hugepages-1Gi"),
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
name: core.ResourceName("cpu"),
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
name: core.ResourceName("memory"),
|
||||
result: false,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
if testCase.result != IsHugePageResourceName(testCase.name) {
|
||||
t.Errorf("resource: %v expected result: %v", testCase.name, testCase.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHugePageResourceName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
pageSize resource.Quantity
|
||||
name core.ResourceName
|
||||
}{
|
||||
{
|
||||
pageSize: resource.MustParse("2Mi"),
|
||||
name: core.ResourceName("hugepages-2Mi"),
|
||||
},
|
||||
{
|
||||
pageSize: resource.MustParse("1Gi"),
|
||||
name: core.ResourceName("hugepages-1Gi"),
|
||||
},
|
||||
{
|
||||
// verify we do not regress our canonical representation
|
||||
pageSize: *resource.NewQuantity(int64(2097152), resource.BinarySI),
|
||||
name: core.ResourceName("hugepages-2Mi"),
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
if result := HugePageResourceName(testCase.pageSize); result != testCase.name {
|
||||
t.Errorf("pageSize: %v, expected: %v, but got: %v", testCase.pageSize.String(), testCase.name, result.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHugePageSizeFromResourceName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name core.ResourceName
|
||||
expectErr bool
|
||||
pageSize resource.Quantity
|
||||
}{
|
||||
{
|
||||
name: core.ResourceName("hugepages-2Mi"),
|
||||
pageSize: resource.MustParse("2Mi"),
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: core.ResourceName("hugepages-1Gi"),
|
||||
pageSize: resource.MustParse("1Gi"),
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: core.ResourceName("hugepages-bad"),
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
value, err := HugePageSizeFromResourceName(testCase.name)
|
||||
if testCase.expectErr && err == nil {
|
||||
t.Errorf("Expected an error for %v", testCase.name)
|
||||
} else if !testCase.expectErr && err != nil {
|
||||
t.Errorf("Unexpected error for %v, got %v", testCase.name, err)
|
||||
} else if testCase.pageSize.Value() != value.Value() {
|
||||
t.Errorf("Unexpected pageSize for resource %v got %v", testCase.name, value.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsOvercommitAllowed(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name core.ResourceName
|
||||
allowed bool
|
||||
}{
|
||||
{
|
||||
name: core.ResourceCPU,
|
||||
allowed: true,
|
||||
},
|
||||
{
|
||||
name: core.ResourceMemory,
|
||||
allowed: true,
|
||||
},
|
||||
{
|
||||
name: core.ResourceNvidiaGPU,
|
||||
allowed: false,
|
||||
},
|
||||
{
|
||||
name: HugePageResourceName(resource.MustParse("2Mi")),
|
||||
allowed: false,
|
||||
},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
if testCase.allowed != IsOvercommitAllowed(testCase.name) {
|
||||
t.Errorf("Unexpected result for %v", testCase.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
140
vendor/k8s.io/kubernetes/pkg/apis/core/install/install_test.go
generated
vendored
140
vendor/k8s.io/kubernetes/pkg/apis/core/install/install_test.go
generated
vendored
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 install
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
internal "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
func TestResourceVersioner(t *testing.T) {
|
||||
g, err := legacyscheme.Registry.Group(v1.GroupName)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
intf, err := g.DefaultInterfacesFor(v1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
accessor := intf.MetadataAccessor
|
||||
|
||||
pod := internal.Pod{ObjectMeta: metav1.ObjectMeta{ResourceVersion: "10"}}
|
||||
version, err := accessor.ResourceVersion(&pod)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if version != "10" {
|
||||
t.Errorf("unexpected version %v", version)
|
||||
}
|
||||
|
||||
podList := internal.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}}
|
||||
version, err = accessor.ResourceVersion(&podList)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if version != "10" {
|
||||
t.Errorf("unexpected version %v", version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodec(t *testing.T) {
|
||||
pod := internal.Pod{}
|
||||
// We do want to use package registered rather than testapi here, because we
|
||||
// want to test if the package install and package registered work as expected.
|
||||
data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.GroupOrDie(internal.GroupName).GroupVersion), &pod)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
other := internal.Pod{}
|
||||
if err := json.Unmarshal(data, &other); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if other.APIVersion != legacyscheme.Registry.GroupOrDie(internal.GroupName).GroupVersion.Version || other.Kind != "Pod" {
|
||||
t.Errorf("unexpected unmarshalled object %#v", other)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfacesFor(t *testing.T) {
|
||||
if _, err := legacyscheme.Registry.GroupOrDie(internal.GroupName).InterfacesFor(internal.SchemeGroupVersion); err == nil {
|
||||
t.Fatalf("unexpected non-error: %v", err)
|
||||
}
|
||||
for i, version := range legacyscheme.Registry.GroupOrDie(internal.GroupName).GroupVersions {
|
||||
if vi, err := legacyscheme.Registry.GroupOrDie(internal.GroupName).InterfacesFor(version); err != nil || vi == nil {
|
||||
t.Fatalf("%d: unexpected result: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRESTMapper(t *testing.T) {
|
||||
gv := schema.GroupVersion{Group: "", Version: "v1"}
|
||||
rcGVK := gv.WithKind("ReplicationController")
|
||||
podTemplateGVK := gv.WithKind("PodTemplate")
|
||||
|
||||
if gvk, err := legacyscheme.Registry.RESTMapper().KindFor(internal.SchemeGroupVersion.WithResource("replicationcontrollers")); err != nil || gvk != rcGVK {
|
||||
t.Errorf("unexpected version mapping: %v %v", gvk, err)
|
||||
}
|
||||
|
||||
if m, err := legacyscheme.Registry.GroupOrDie(internal.GroupName).RESTMapper.RESTMapping(podTemplateGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != podTemplateGVK || m.Resource != "podtemplates" {
|
||||
t.Errorf("unexpected version mapping: %#v %v", m, err)
|
||||
}
|
||||
|
||||
for _, version := range legacyscheme.Registry.GroupOrDie(internal.GroupName).GroupVersions {
|
||||
mapping, err := legacyscheme.Registry.GroupOrDie(internal.GroupName).RESTMapper.RESTMapping(rcGVK.GroupKind(), version.Version)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if mapping.Resource != "replicationControllers" && mapping.Resource != "replicationcontrollers" {
|
||||
t.Errorf("incorrect resource name: %#v", mapping)
|
||||
}
|
||||
if mapping.GroupVersionKind.GroupVersion() != version {
|
||||
t.Errorf("incorrect version: %v", mapping)
|
||||
}
|
||||
|
||||
interfaces, _ := legacyscheme.Registry.GroupOrDie(internal.GroupName).InterfacesFor(version)
|
||||
if mapping.ObjectConvertor != interfaces.ObjectConvertor {
|
||||
t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces)
|
||||
}
|
||||
|
||||
rc := &internal.ReplicationController{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
|
||||
name, err := mapping.MetadataAccessor.Name(rc)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if name != "foo" {
|
||||
t.Errorf("unable to retrieve object meta with: %v", mapping.MetadataAccessor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnversioned(t *testing.T) {
|
||||
for _, obj := range []runtime.Object{
|
||||
&metav1.Status{},
|
||||
} {
|
||||
if unversioned, ok := legacyscheme.Scheme.IsUnversioned(obj); !unversioned || !ok {
|
||||
t.Errorf("%v is expected to be unversioned", reflect.TypeOf(obj))
|
||||
}
|
||||
}
|
||||
}
|
||||
87
vendor/k8s.io/kubernetes/pkg/apis/core/pods/helpers_test.go
generated
vendored
87
vendor/k8s.io/kubernetes/pkg/apis/core/pods/helpers_test.go
generated
vendored
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 pods
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConvertDownwardAPIFieldLabel(t *testing.T) {
|
||||
testCases := []struct {
|
||||
version string
|
||||
label string
|
||||
value string
|
||||
expectedErr bool
|
||||
expectedLabel string
|
||||
expectedValue string
|
||||
}{
|
||||
{
|
||||
version: "v2",
|
||||
label: "metadata.name",
|
||||
value: "test-pod",
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
version: "v1",
|
||||
label: "invalid-label",
|
||||
value: "value",
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
version: "v1",
|
||||
label: "metadata.name",
|
||||
value: "test-pod",
|
||||
expectedLabel: "metadata.name",
|
||||
expectedValue: "test-pod",
|
||||
},
|
||||
{
|
||||
version: "v1",
|
||||
label: "metadata.annotations",
|
||||
value: "myValue",
|
||||
expectedLabel: "metadata.annotations",
|
||||
expectedValue: "myValue",
|
||||
},
|
||||
{
|
||||
version: "v1",
|
||||
label: "metadata.annotations['myKey']",
|
||||
value: "myValue",
|
||||
expectedLabel: "metadata.annotations['myKey']",
|
||||
expectedValue: "myValue",
|
||||
},
|
||||
{
|
||||
version: "v1",
|
||||
label: "spec.host",
|
||||
value: "127.0.0.1",
|
||||
expectedLabel: "spec.nodeName",
|
||||
expectedValue: "127.0.0.1",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
label, value, err := ConvertDownwardAPIFieldLabel(tc.version, tc.label, tc.value)
|
||||
if err != nil {
|
||||
if tc.expectedErr {
|
||||
continue
|
||||
}
|
||||
t.Errorf("ConvertDownwardAPIFieldLabel(%s, %s, %s) failed: %s",
|
||||
tc.version, tc.label, tc.value, err)
|
||||
}
|
||||
if tc.expectedLabel != label || tc.expectedValue != value {
|
||||
t.Errorf("ConvertDownwardAPIFieldLabel(%s, %s, %s) = (%s, %s, nil), expected (%s, %s, nil)",
|
||||
tc.version, tc.label, tc.value, label, value, tc.expectedLabel, tc.expectedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
120
vendor/k8s.io/kubernetes/pkg/apis/core/taint_test.go
generated
vendored
120
vendor/k8s.io/kubernetes/pkg/apis/core/taint_test.go
generated
vendored
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 core
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestTaintToString(t *testing.T) {
|
||||
testCases := []struct {
|
||||
taint *Taint
|
||||
expectedString string
|
||||
}{
|
||||
{
|
||||
taint: &Taint{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
expectedString: "foo=bar:NoSchedule",
|
||||
},
|
||||
{
|
||||
taint: &Taint{
|
||||
Key: "foo",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
expectedString: "foo:NoSchedule",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
if tc.expectedString != tc.taint.ToString() {
|
||||
t.Errorf("[%v] expected taint %v converted to %s, got %s", i, tc.taint, tc.expectedString, tc.taint.ToString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchTaint(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
taint *Taint
|
||||
taintToMatch Taint
|
||||
expectMatch bool
|
||||
}{
|
||||
{
|
||||
description: "two taints with the same key,value,effect should match",
|
||||
taint: &Taint{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
taintToMatch: Taint{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
expectMatch: true,
|
||||
},
|
||||
{
|
||||
description: "two taints with the same key,effect but different value should match",
|
||||
taint: &Taint{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
taintToMatch: Taint{
|
||||
Key: "foo",
|
||||
Value: "different-value",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
expectMatch: true,
|
||||
},
|
||||
{
|
||||
description: "two taints with the different key cannot match",
|
||||
taint: &Taint{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
taintToMatch: Taint{
|
||||
Key: "different-key",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
expectMatch: false,
|
||||
},
|
||||
{
|
||||
description: "two taints with the different effect cannot match",
|
||||
taint: &Taint{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
taintToMatch: Taint{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectPreferNoSchedule,
|
||||
},
|
||||
expectMatch: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if tc.expectMatch != tc.taint.MatchTaint(tc.taintToMatch) {
|
||||
t.Errorf("[%s] expect taint %s match taint %s", tc.description, tc.taint.ToString(), tc.taintToMatch.ToString())
|
||||
}
|
||||
}
|
||||
}
|
||||
136
vendor/k8s.io/kubernetes/pkg/apis/core/toleration_test.go
generated
vendored
136
vendor/k8s.io/kubernetes/pkg/apis/core/toleration_test.go
generated
vendored
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 core
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMatchToleration(t *testing.T) {
|
||||
|
||||
tolerationSeconds := int64(5)
|
||||
tolerationToMatchSeconds := int64(3)
|
||||
testCases := []struct {
|
||||
description string
|
||||
toleration *Toleration
|
||||
tolerationToMatch *Toleration
|
||||
expectMatch bool
|
||||
}{
|
||||
{
|
||||
description: "two taints with the same key,operator,value,effect should match",
|
||||
toleration: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
tolerationToMatch: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
expectMatch: true,
|
||||
},
|
||||
{
|
||||
description: "two taints with the different key cannot match",
|
||||
toleration: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
tolerationToMatch: &Toleration{
|
||||
Key: "different-key",
|
||||
Operator: "Exists",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
expectMatch: false,
|
||||
},
|
||||
{
|
||||
description: "two taints with the different operator cannot match",
|
||||
toleration: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
tolerationToMatch: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "different-operator",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
expectMatch: false,
|
||||
},
|
||||
{
|
||||
description: "two taints with the different value cannot match",
|
||||
toleration: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
tolerationToMatch: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Value: "different-value",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
expectMatch: false,
|
||||
},
|
||||
{
|
||||
description: "two taints with the different effect cannot match",
|
||||
toleration: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
},
|
||||
tolerationToMatch: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectPreferNoSchedule,
|
||||
},
|
||||
expectMatch: false,
|
||||
},
|
||||
{
|
||||
description: "two taints with the different tolerationSeconds should match",
|
||||
toleration: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
TolerationSeconds: &tolerationSeconds,
|
||||
},
|
||||
tolerationToMatch: &Toleration{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Value: "bar",
|
||||
Effect: TaintEffectNoSchedule,
|
||||
TolerationSeconds: &tolerationToMatchSeconds,
|
||||
},
|
||||
expectMatch: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if actual := tc.toleration.MatchToleration(tc.tolerationToMatch); actual != tc.expectMatch {
|
||||
t.Errorf("[%s] expect: %v , got: %v", tc.description, tc.expectMatch, !tc.expectMatch)
|
||||
}
|
||||
}
|
||||
}
|
||||
6
vendor/k8s.io/kubernetes/pkg/apis/core/types.go
generated
vendored
6
vendor/k8s.io/kubernetes/pkg/apis/core/types.go
generated
vendored
|
|
@ -1711,6 +1711,12 @@ type VolumeMount struct {
|
|||
type MountPropagationMode string
|
||||
|
||||
const (
|
||||
// MountPropagationNone means that the volume in a container will
|
||||
// not receive new mounts from the host or other containers, and filesystems
|
||||
// mounted inside the container won't be propagated to the host or other
|
||||
// containers.
|
||||
// Note that this mode corresponds to "private" in Linux terminology.
|
||||
MountPropagationNone MountPropagationMode = "None"
|
||||
// MountPropagationHostToContainer means that the volume in a container will
|
||||
// receive new mounts from the host or other containers, but filesystems
|
||||
// mounted inside the container won't be propagated to the host or other
|
||||
|
|
|
|||
348
vendor/k8s.io/kubernetes/pkg/apis/core/v1/conversion_test.go
generated
vendored
348
vendor/k8s.io/kubernetes/pkg/apis/core/v1/conversion_test.go
generated
vendored
|
|
@ -1,348 +0,0 @@
|
|||
/*
|
||||
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 v1_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/api/testing/fuzzer"
|
||||
metafuzzer "k8s.io/apimachinery/pkg/apis/meta/fuzzer"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
corefuzzer "k8s.io/kubernetes/pkg/apis/core/fuzzer"
|
||||
corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
utilpointer "k8s.io/kubernetes/pkg/util/pointer"
|
||||
|
||||
// enforce that all types are installed
|
||||
_ "k8s.io/kubernetes/pkg/api/testapi"
|
||||
)
|
||||
|
||||
func TestPodLogOptions(t *testing.T) {
|
||||
sinceSeconds := int64(1)
|
||||
sinceTime := metav1.NewTime(time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC).Local())
|
||||
tailLines := int64(2)
|
||||
limitBytes := int64(3)
|
||||
|
||||
versionedLogOptions := &v1.PodLogOptions{
|
||||
Container: "mycontainer",
|
||||
Follow: true,
|
||||
Previous: true,
|
||||
SinceSeconds: &sinceSeconds,
|
||||
SinceTime: &sinceTime,
|
||||
Timestamps: true,
|
||||
TailLines: &tailLines,
|
||||
LimitBytes: &limitBytes,
|
||||
}
|
||||
unversionedLogOptions := &core.PodLogOptions{
|
||||
Container: "mycontainer",
|
||||
Follow: true,
|
||||
Previous: true,
|
||||
SinceSeconds: &sinceSeconds,
|
||||
SinceTime: &sinceTime,
|
||||
Timestamps: true,
|
||||
TailLines: &tailLines,
|
||||
LimitBytes: &limitBytes,
|
||||
}
|
||||
expectedParameters := url.Values{
|
||||
"container": {"mycontainer"},
|
||||
"follow": {"true"},
|
||||
"previous": {"true"},
|
||||
"sinceSeconds": {"1"},
|
||||
"sinceTime": {"2000-01-01T12:34:56Z"},
|
||||
"timestamps": {"true"},
|
||||
"tailLines": {"2"},
|
||||
"limitBytes": {"3"},
|
||||
}
|
||||
|
||||
codec := runtime.NewParameterCodec(legacyscheme.Scheme)
|
||||
|
||||
// unversioned -> query params
|
||||
{
|
||||
actualParameters, err := codec.EncodeParameters(unversionedLogOptions, v1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualParameters, expectedParameters) {
|
||||
t.Fatalf("Expected\n%#v\ngot\n%#v", expectedParameters, actualParameters)
|
||||
}
|
||||
}
|
||||
|
||||
// versioned -> query params
|
||||
{
|
||||
actualParameters, err := codec.EncodeParameters(versionedLogOptions, v1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualParameters, expectedParameters) {
|
||||
t.Fatalf("Expected\n%#v\ngot\n%#v", expectedParameters, actualParameters)
|
||||
}
|
||||
}
|
||||
|
||||
// query params -> versioned
|
||||
{
|
||||
convertedLogOptions := &v1.PodLogOptions{}
|
||||
err := codec.DecodeParameters(expectedParameters, v1.SchemeGroupVersion, convertedLogOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(convertedLogOptions, versionedLogOptions) {
|
||||
t.Fatalf("Unexpected deserialization:\n%s", diff.ObjectGoPrintSideBySide(versionedLogOptions, convertedLogOptions))
|
||||
}
|
||||
}
|
||||
|
||||
// query params -> unversioned
|
||||
{
|
||||
convertedLogOptions := &core.PodLogOptions{}
|
||||
err := codec.DecodeParameters(expectedParameters, v1.SchemeGroupVersion, convertedLogOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(convertedLogOptions, unversionedLogOptions) {
|
||||
t.Fatalf("Unexpected deserialization:\n%s", diff.ObjectGoPrintSideBySide(unversionedLogOptions, convertedLogOptions))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPodSpecConversion tests that v1.ServiceAccount is an alias for
|
||||
// ServiceAccountName.
|
||||
func TestPodSpecConversion(t *testing.T) {
|
||||
name, other := "foo", "bar"
|
||||
|
||||
// Test internal -> v1. Should have both alias (DeprecatedServiceAccount)
|
||||
// and new field (ServiceAccountName).
|
||||
i := &core.PodSpec{
|
||||
ServiceAccountName: name,
|
||||
}
|
||||
v := v1.PodSpec{}
|
||||
if err := legacyscheme.Scheme.Convert(i, &v, nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if v.ServiceAccountName != name {
|
||||
t.Fatalf("want v1.ServiceAccountName %q, got %q", name, v.ServiceAccountName)
|
||||
}
|
||||
if v.DeprecatedServiceAccount != name {
|
||||
t.Fatalf("want v1.DeprecatedServiceAccount %q, got %q", name, v.DeprecatedServiceAccount)
|
||||
}
|
||||
|
||||
// Test v1 -> internal. Either DeprecatedServiceAccount, ServiceAccountName,
|
||||
// or both should translate to ServiceAccountName. ServiceAccountName wins
|
||||
// if both are set.
|
||||
testCases := []*v1.PodSpec{
|
||||
// New
|
||||
{ServiceAccountName: name},
|
||||
// Alias
|
||||
{DeprecatedServiceAccount: name},
|
||||
// Both: same
|
||||
{ServiceAccountName: name, DeprecatedServiceAccount: name},
|
||||
// Both: different
|
||||
{ServiceAccountName: name, DeprecatedServiceAccount: other},
|
||||
}
|
||||
for k, v := range testCases {
|
||||
got := core.PodSpec{}
|
||||
err := legacyscheme.Scheme.Convert(v, &got, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for case %d: %v", k, err)
|
||||
}
|
||||
if got.ServiceAccountName != name {
|
||||
t.Fatalf("want core.ServiceAccountName %q, got %q", name, got.ServiceAccountName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceListConversion(t *testing.T) {
|
||||
bigMilliQuantity := resource.NewQuantity(resource.MaxMilliValue, resource.DecimalSI)
|
||||
bigMilliQuantity.Add(resource.MustParse("12345m"))
|
||||
|
||||
tests := []struct {
|
||||
input v1.ResourceList
|
||||
expected core.ResourceList
|
||||
}{
|
||||
{ // No changes necessary.
|
||||
input: v1.ResourceList{
|
||||
v1.ResourceMemory: resource.MustParse("30M"),
|
||||
v1.ResourceCPU: resource.MustParse("100m"),
|
||||
v1.ResourceStorage: resource.MustParse("1G"),
|
||||
},
|
||||
expected: core.ResourceList{
|
||||
core.ResourceMemory: resource.MustParse("30M"),
|
||||
core.ResourceCPU: resource.MustParse("100m"),
|
||||
core.ResourceStorage: resource.MustParse("1G"),
|
||||
},
|
||||
},
|
||||
{ // Nano-scale values should be rounded up to milli-scale.
|
||||
input: v1.ResourceList{
|
||||
v1.ResourceCPU: resource.MustParse("3.000023m"),
|
||||
v1.ResourceMemory: resource.MustParse("500.000050m"),
|
||||
},
|
||||
expected: core.ResourceList{
|
||||
core.ResourceCPU: resource.MustParse("4m"),
|
||||
core.ResourceMemory: resource.MustParse("501m"),
|
||||
},
|
||||
},
|
||||
{ // Large values should still be accurate.
|
||||
input: v1.ResourceList{
|
||||
v1.ResourceCPU: *bigMilliQuantity.Copy(),
|
||||
v1.ResourceStorage: *bigMilliQuantity.Copy(),
|
||||
},
|
||||
expected: core.ResourceList{
|
||||
core.ResourceCPU: *bigMilliQuantity.Copy(),
|
||||
core.ResourceStorage: *bigMilliQuantity.Copy(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
output := core.ResourceList{}
|
||||
|
||||
// defaulting is a separate step from conversion that is applied when reading from the API or from etcd.
|
||||
// perform that step explicitly.
|
||||
corev1.SetDefaults_ResourceList(&test.input)
|
||||
|
||||
err := legacyscheme.Scheme.Convert(&test.input, &output, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error for case %d: %v", i, err)
|
||||
}
|
||||
if !apiequality.Semantic.DeepEqual(test.expected, output) {
|
||||
t.Errorf("unexpected conversion for case %d: Expected\n%+v;\nGot\n%+v", i, test.expected, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplicationControllerConversion(t *testing.T) {
|
||||
// If we start with a RC, we should always have round-trip fidelity.
|
||||
inputs := []*v1.ReplicationController{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "name",
|
||||
Namespace: "namespace",
|
||||
},
|
||||
Spec: v1.ReplicationControllerSpec{
|
||||
Replicas: utilpointer.Int32Ptr(1),
|
||||
MinReadySeconds: 32,
|
||||
Selector: map[string]string{"foo": "bar", "bar": "foo"},
|
||||
Template: &v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{"foo": "bar", "bar": "foo"},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "container",
|
||||
Image: "image",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v1.ReplicationControllerStatus{
|
||||
Replicas: 1,
|
||||
FullyLabeledReplicas: 2,
|
||||
ReadyReplicas: 3,
|
||||
AvailableReplicas: 4,
|
||||
ObservedGeneration: 5,
|
||||
Conditions: []v1.ReplicationControllerCondition{
|
||||
{
|
||||
Type: v1.ReplicationControllerReplicaFailure,
|
||||
Status: v1.ConditionTrue,
|
||||
LastTransitionTime: metav1.NewTime(time.Unix(123456789, 0)),
|
||||
Reason: "Reason",
|
||||
Message: "Message",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Add some fuzzed RCs.
|
||||
apiObjectFuzzer := fuzzer.FuzzerFor(fuzzer.MergeFuzzerFuncs(metafuzzer.Funcs, corefuzzer.Funcs), rand.NewSource(152), legacyscheme.Codecs)
|
||||
for i := 0; i < 100; i++ {
|
||||
rc := &v1.ReplicationController{}
|
||||
apiObjectFuzzer.Fuzz(rc)
|
||||
// Sometimes the fuzzer decides to leave Spec.Template nil.
|
||||
// We can't support that because Spec.Template is not a pointer in RS,
|
||||
// so it will round-trip as non-nil but empty.
|
||||
if rc.Spec.Template == nil {
|
||||
rc.Spec.Template = &v1.PodTemplateSpec{}
|
||||
}
|
||||
// Sometimes the fuzzer decides to insert an empty label key.
|
||||
// This doesn't round-trip properly because it's invalid.
|
||||
if rc.Spec.Selector != nil {
|
||||
delete(rc.Spec.Selector, "")
|
||||
}
|
||||
inputs = append(inputs, rc)
|
||||
}
|
||||
|
||||
// Round-trip the input RCs before converting to RS.
|
||||
for i := range inputs {
|
||||
inputs[i] = roundTrip(t, inputs[i]).(*v1.ReplicationController)
|
||||
}
|
||||
|
||||
for _, in := range inputs {
|
||||
rs := &extensions.ReplicaSet{}
|
||||
// Use in.DeepCopy() to avoid sharing pointers with `in`.
|
||||
if err := corev1.Convert_v1_ReplicationController_to_extensions_ReplicaSet(in.DeepCopy(), rs, nil); err != nil {
|
||||
t.Errorf("can't convert RC to RS: %v", err)
|
||||
continue
|
||||
}
|
||||
// Round-trip RS before converting back to RC.
|
||||
rs = roundTripRS(t, rs)
|
||||
out := &v1.ReplicationController{}
|
||||
if err := corev1.Convert_extensions_ReplicaSet_to_v1_ReplicationController(rs, out, nil); err != nil {
|
||||
t.Errorf("can't convert RS to RC: %v", err)
|
||||
continue
|
||||
}
|
||||
if !apiequality.Semantic.DeepEqual(in, out) {
|
||||
instr, _ := json.MarshalIndent(in, "", " ")
|
||||
outstr, _ := json.MarshalIndent(out, "", " ")
|
||||
t.Errorf("RC-RS conversion round-trip failed:\nin:\n%s\nout:\n%s", instr, outstr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func roundTripRS(t *testing.T, rs *extensions.ReplicaSet) *extensions.ReplicaSet {
|
||||
codec := legacyscheme.Codecs.LegacyCodec(extensionsv1beta1.SchemeGroupVersion)
|
||||
data, err := runtime.Encode(codec, rs)
|
||||
if err != nil {
|
||||
t.Errorf("%v\n %#v", err, rs)
|
||||
return nil
|
||||
}
|
||||
obj2, err := runtime.Decode(codec, data)
|
||||
if err != nil {
|
||||
t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), rs)
|
||||
return nil
|
||||
}
|
||||
obj3 := &extensions.ReplicaSet{}
|
||||
err = legacyscheme.Scheme.Convert(obj2, obj3, nil)
|
||||
if err != nil {
|
||||
t.Errorf("%v\nSource: %#v", err, obj2)
|
||||
return nil
|
||||
}
|
||||
return obj3
|
||||
}
|
||||
1386
vendor/k8s.io/kubernetes/pkg/apis/core/v1/defaults_test.go
generated
vendored
1386
vendor/k8s.io/kubernetes/pkg/apis/core/v1/defaults_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
655
vendor/k8s.io/kubernetes/pkg/apis/core/v1/helper/helpers_test.go
generated
vendored
655
vendor/k8s.io/kubernetes/pkg/apis/core/v1/helper/helpers_test.go
generated
vendored
|
|
@ -1,655 +0,0 @@
|
|||
/*
|
||||
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 helper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
func TestIsDefaultNamespaceResource(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resourceName v1.ResourceName
|
||||
expectVal bool
|
||||
}{
|
||||
{
|
||||
resourceName: "pod.alpha.kubernetes.io/opaque-int-resource-foo",
|
||||
expectVal: true,
|
||||
},
|
||||
{
|
||||
resourceName: "kubernetes.io/resource-foo",
|
||||
expectVal: true,
|
||||
},
|
||||
{
|
||||
resourceName: "foo",
|
||||
expectVal: true,
|
||||
},
|
||||
{
|
||||
resourceName: "a/b",
|
||||
expectVal: false,
|
||||
},
|
||||
{
|
||||
resourceName: "",
|
||||
expectVal: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("resourceName input=%s, expected value=%v", tc.resourceName, tc.expectVal), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := IsDefaultNamespaceResource(tc.resourceName)
|
||||
if v != tc.expectVal {
|
||||
t.Errorf("Got %v but expected %v", v, tc.expectVal)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHugePageSizeFromResourceName(t *testing.T) {
|
||||
expected100m, _ := resource.ParseQuantity("100m")
|
||||
testCases := []struct {
|
||||
resourceName v1.ResourceName
|
||||
expectVal resource.Quantity
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
resourceName: "pod.alpha.kubernetes.io/opaque-int-resource-foo",
|
||||
expectVal: resource.Quantity{},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
resourceName: "hugepages-",
|
||||
expectVal: resource.Quantity{},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
resourceName: "hugepages-100m",
|
||||
expectVal: expected100m,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
resourceName: "",
|
||||
expectVal: resource.Quantity{},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("resourceName input=%s, expected value=%v", tc.resourceName, tc.expectVal), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
v, err := HugePageSizeFromResourceName(tc.resourceName)
|
||||
if err == nil && tc.expectErr {
|
||||
t.Errorf("[%v]expected error but got none.", i)
|
||||
}
|
||||
if err != nil && !tc.expectErr {
|
||||
t.Errorf("[%v]did not expect error but got: %v", i, err)
|
||||
}
|
||||
if v != tc.expectVal {
|
||||
t.Errorf("Got %v but expected %v", v, tc.expectVal)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsOvercommitAllowed(t *testing.T) {
|
||||
testCases := []struct {
|
||||
resourceName v1.ResourceName
|
||||
expectVal bool
|
||||
}{
|
||||
{
|
||||
resourceName: "pod.alpha.kubernetes.io/opaque-int-resource-foo",
|
||||
expectVal: true,
|
||||
},
|
||||
{
|
||||
resourceName: "kubernetes.io/resource-foo",
|
||||
expectVal: true,
|
||||
},
|
||||
{
|
||||
resourceName: "alpha.kubernetes.io/nvidia-gpu",
|
||||
expectVal: false,
|
||||
},
|
||||
{
|
||||
resourceName: "hugepages-100m",
|
||||
expectVal: false,
|
||||
},
|
||||
{
|
||||
resourceName: "",
|
||||
expectVal: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(fmt.Sprintf("resourceName input=%s, expected value=%v", tc.resourceName, tc.expectVal), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := IsOvercommitAllowed(tc.resourceName)
|
||||
if v != tc.expectVal {
|
||||
t.Errorf("Got %v but expected %v", v, tc.expectVal)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddToNodeAddresses(t *testing.T) {
|
||||
testCases := []struct {
|
||||
existing []v1.NodeAddress
|
||||
toAdd []v1.NodeAddress
|
||||
expected []v1.NodeAddress
|
||||
}{
|
||||
{
|
||||
existing: []v1.NodeAddress{},
|
||||
toAdd: []v1.NodeAddress{},
|
||||
expected: []v1.NodeAddress{},
|
||||
},
|
||||
{
|
||||
existing: []v1.NodeAddress{},
|
||||
toAdd: []v1.NodeAddress{
|
||||
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: v1.NodeHostName, Address: "localhost"},
|
||||
},
|
||||
expected: []v1.NodeAddress{
|
||||
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: v1.NodeHostName, Address: "localhost"},
|
||||
},
|
||||
},
|
||||
{
|
||||
existing: []v1.NodeAddress{},
|
||||
toAdd: []v1.NodeAddress{
|
||||
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
|
||||
},
|
||||
expected: []v1.NodeAddress{
|
||||
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
existing: []v1.NodeAddress{
|
||||
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
|
||||
},
|
||||
toAdd: []v1.NodeAddress{
|
||||
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: v1.NodeHostName, Address: "localhost"},
|
||||
},
|
||||
expected: []v1.NodeAddress{
|
||||
{Type: v1.NodeExternalIP, Address: "1.1.1.1"},
|
||||
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
|
||||
{Type: v1.NodeHostName, Address: "localhost"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
AddToNodeAddresses(&tc.existing, tc.toAdd...)
|
||||
if !apiequality.Semantic.DeepEqual(tc.expected, tc.existing) {
|
||||
t.Errorf("case[%d], expected: %v, got: %v", i, tc.expected, tc.existing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccessModesFromString(t *testing.T) {
|
||||
modes := GetAccessModesFromString("ROX")
|
||||
if !containsAccessMode(modes, v1.ReadOnlyMany) {
|
||||
t.Errorf("Expected mode %s, but got %+v", v1.ReadOnlyMany, modes)
|
||||
}
|
||||
|
||||
modes = GetAccessModesFromString("ROX,RWX")
|
||||
if !containsAccessMode(modes, v1.ReadOnlyMany) {
|
||||
t.Errorf("Expected mode %s, but got %+v", v1.ReadOnlyMany, modes)
|
||||
}
|
||||
if !containsAccessMode(modes, v1.ReadWriteMany) {
|
||||
t.Errorf("Expected mode %s, but got %+v", v1.ReadWriteMany, modes)
|
||||
}
|
||||
|
||||
modes = GetAccessModesFromString("RWO,ROX,RWX")
|
||||
if !containsAccessMode(modes, v1.ReadOnlyMany) {
|
||||
t.Errorf("Expected mode %s, but got %+v", v1.ReadOnlyMany, modes)
|
||||
}
|
||||
if !containsAccessMode(modes, v1.ReadWriteMany) {
|
||||
t.Errorf("Expected mode %s, but got %+v", v1.ReadWriteMany, modes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveDuplicateAccessModes(t *testing.T) {
|
||||
modes := []v1.PersistentVolumeAccessMode{
|
||||
v1.ReadWriteOnce, v1.ReadOnlyMany, v1.ReadOnlyMany, v1.ReadOnlyMany,
|
||||
}
|
||||
modes = removeDuplicateAccessModes(modes)
|
||||
if len(modes) != 2 {
|
||||
t.Errorf("Expected 2 distinct modes in set but found %v", len(modes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeSelectorRequirementsAsSelector(t *testing.T) {
|
||||
matchExpressions := []v1.NodeSelectorRequirement{{
|
||||
Key: "foo",
|
||||
Operator: v1.NodeSelectorOpIn,
|
||||
Values: []string{"bar", "baz"},
|
||||
}}
|
||||
mustParse := func(s string) labels.Selector {
|
||||
out, e := labels.Parse(s)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return out
|
||||
}
|
||||
tc := []struct {
|
||||
in []v1.NodeSelectorRequirement
|
||||
out labels.Selector
|
||||
expectErr bool
|
||||
}{
|
||||
{in: nil, out: labels.Nothing()},
|
||||
{in: []v1.NodeSelectorRequirement{}, out: labels.Nothing()},
|
||||
{
|
||||
in: matchExpressions,
|
||||
out: mustParse("foo in (baz,bar)"),
|
||||
},
|
||||
{
|
||||
in: []v1.NodeSelectorRequirement{{
|
||||
Key: "foo",
|
||||
Operator: v1.NodeSelectorOpExists,
|
||||
Values: []string{"bar", "baz"},
|
||||
}},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
in: []v1.NodeSelectorRequirement{{
|
||||
Key: "foo",
|
||||
Operator: v1.NodeSelectorOpGt,
|
||||
Values: []string{"1"},
|
||||
}},
|
||||
out: mustParse("foo>1"),
|
||||
},
|
||||
{
|
||||
in: []v1.NodeSelectorRequirement{{
|
||||
Key: "bar",
|
||||
Operator: v1.NodeSelectorOpLt,
|
||||
Values: []string{"7"},
|
||||
}},
|
||||
out: mustParse("bar<7"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tc {
|
||||
out, err := NodeSelectorRequirementsAsSelector(tc.in)
|
||||
if err == nil && tc.expectErr {
|
||||
t.Errorf("[%v]expected error but got none.", i)
|
||||
}
|
||||
if err != nil && !tc.expectErr {
|
||||
t.Errorf("[%v]did not expect error but got: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(out, tc.out) {
|
||||
t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTolerationsTolerateTaintsWithFilter(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
tolerations []v1.Toleration
|
||||
taints []v1.Taint
|
||||
applyFilter taintsFilterFunc
|
||||
expectTolerated bool
|
||||
}{
|
||||
{
|
||||
description: "empty tolerations tolerate empty taints",
|
||||
tolerations: []v1.Toleration{},
|
||||
taints: []v1.Taint{},
|
||||
applyFilter: func(t *v1.Taint) bool { return true },
|
||||
expectTolerated: true,
|
||||
},
|
||||
{
|
||||
description: "non-empty tolerations tolerate empty taints",
|
||||
tolerations: []v1.Toleration{
|
||||
{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
taints: []v1.Taint{},
|
||||
applyFilter: func(t *v1.Taint) bool { return true },
|
||||
expectTolerated: true,
|
||||
},
|
||||
{
|
||||
description: "tolerations match all taints, expect tolerated",
|
||||
tolerations: []v1.Toleration{
|
||||
{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
taints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
applyFilter: func(t *v1.Taint) bool { return true },
|
||||
expectTolerated: true,
|
||||
},
|
||||
{
|
||||
description: "tolerations don't match taints, but no taints apply to the filter, expect tolerated",
|
||||
tolerations: []v1.Toleration{
|
||||
{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
taints: []v1.Taint{
|
||||
{
|
||||
Key: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
applyFilter: func(t *v1.Taint) bool { return false },
|
||||
expectTolerated: true,
|
||||
},
|
||||
{
|
||||
description: "no filterFunc indicated, means all taints apply to the filter, tolerations don't match taints, expect untolerated",
|
||||
tolerations: []v1.Toleration{
|
||||
{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
taints: []v1.Taint{
|
||||
{
|
||||
Key: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
applyFilter: nil,
|
||||
expectTolerated: false,
|
||||
},
|
||||
{
|
||||
description: "tolerations match taints, expect tolerated",
|
||||
tolerations: []v1.Toleration{
|
||||
{
|
||||
Key: "foo",
|
||||
Operator: "Exists",
|
||||
Effect: v1.TaintEffectNoExecute,
|
||||
},
|
||||
},
|
||||
taints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoExecute,
|
||||
},
|
||||
{
|
||||
Key: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
applyFilter: func(t *v1.Taint) bool { return t.Effect == v1.TaintEffectNoExecute },
|
||||
expectTolerated: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if tc.expectTolerated != TolerationsTolerateTaintsWithFilter(tc.tolerations, tc.taints, tc.applyFilter) {
|
||||
filteredTaints := []v1.Taint{}
|
||||
for _, taint := range tc.taints {
|
||||
if tc.applyFilter != nil && !tc.applyFilter(&taint) {
|
||||
continue
|
||||
}
|
||||
filteredTaints = append(filteredTaints, taint)
|
||||
}
|
||||
t.Errorf("[%s] expect tolerations %+v tolerate filtered taints %+v in taints %+v", tc.description, tc.tolerations, filteredTaints, tc.taints)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAvoidPodsFromNode(t *testing.T) {
|
||||
controllerFlag := true
|
||||
testCases := []struct {
|
||||
node *v1.Node
|
||||
expectValue v1.AvoidPods
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
node: &v1.Node{},
|
||||
expectValue: v1.AvoidPods{},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
node: &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
v1.PreferAvoidPodsAnnotationKey: `
|
||||
{
|
||||
"preferAvoidPods": [
|
||||
{
|
||||
"podSignature": {
|
||||
"podController": {
|
||||
"apiVersion": "v1",
|
||||
"kind": "ReplicationController",
|
||||
"name": "foo",
|
||||
"uid": "abcdef123456",
|
||||
"controller": true
|
||||
}
|
||||
},
|
||||
"reason": "some reason",
|
||||
"message": "some message"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectValue: v1.AvoidPods{
|
||||
PreferAvoidPods: []v1.PreferAvoidPodsEntry{
|
||||
{
|
||||
PodSignature: v1.PodSignature{
|
||||
PodController: &metav1.OwnerReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "ReplicationController",
|
||||
Name: "foo",
|
||||
UID: "abcdef123456",
|
||||
Controller: &controllerFlag,
|
||||
},
|
||||
},
|
||||
Reason: "some reason",
|
||||
Message: "some message",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
node: &v1.Node{
|
||||
// Missing end symbol of "podController" and "podSignature"
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
v1.PreferAvoidPodsAnnotationKey: `
|
||||
{
|
||||
"preferAvoidPods": [
|
||||
{
|
||||
"podSignature": {
|
||||
"podController": {
|
||||
"kind": "ReplicationController",
|
||||
"apiVersion": "v1"
|
||||
"reason": "some reason",
|
||||
"message": "some message"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectValue: v1.AvoidPods{},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
v, err := GetAvoidPodsFromNodeAnnotations(tc.node.Annotations)
|
||||
if err == nil && tc.expectErr {
|
||||
t.Errorf("[%v]expected error but got none.", i)
|
||||
}
|
||||
if err != nil && !tc.expectErr {
|
||||
t.Errorf("[%v]did not expect error but got: %v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(tc.expectValue, v) {
|
||||
t.Errorf("[%v]expect value %v but got %v with %v", i, tc.expectValue, v, v.PreferAvoidPods[0].PodSignature.PodController.Controller)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysctlsFromPodAnnotation(t *testing.T) {
|
||||
type Test struct {
|
||||
annotation string
|
||||
expectValue []v1.Sysctl
|
||||
expectErr bool
|
||||
}
|
||||
for i, test := range []Test{
|
||||
{
|
||||
annotation: "",
|
||||
expectValue: nil,
|
||||
},
|
||||
{
|
||||
annotation: "foo.bar",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotation: "=123",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotation: "foo.bar=",
|
||||
expectValue: []v1.Sysctl{{Name: "foo.bar", Value: ""}},
|
||||
},
|
||||
{
|
||||
annotation: "foo.bar=42",
|
||||
expectValue: []v1.Sysctl{{Name: "foo.bar", Value: "42"}},
|
||||
},
|
||||
{
|
||||
annotation: "foo.bar=42,",
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotation: "foo.bar=42,abc.def=1",
|
||||
expectValue: []v1.Sysctl{{Name: "foo.bar", Value: "42"}, {Name: "abc.def", Value: "1"}},
|
||||
},
|
||||
} {
|
||||
sysctls, err := SysctlsFromPodAnnotation(test.annotation)
|
||||
if test.expectErr && err == nil {
|
||||
t.Errorf("[%v]expected error but got none", i)
|
||||
} else if !test.expectErr && err != nil {
|
||||
t.Errorf("[%v]did not expect error but got: %v", i, err)
|
||||
} else if !reflect.DeepEqual(sysctls, test.expectValue) {
|
||||
t.Errorf("[%v]expect value %v but got %v", i, test.expectValue, sysctls)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove when alpha support for topology constraints is removed
|
||||
func TestGetNodeAffinityFromAnnotations(t *testing.T) {
|
||||
testCases := []struct {
|
||||
annotations map[string]string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
annotations: nil,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{
|
||||
v1.AlphaStorageNodeAffinityAnnotation: `{
|
||||
"requiredDuringSchedulingIgnoredDuringExecution": {
|
||||
"nodeSelectorTerms": [
|
||||
{ "matchExpressions": [
|
||||
{ "key": "test-key1",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
},
|
||||
{ "key": "test-key2",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
}
|
||||
]}
|
||||
]}
|
||||
}`,
|
||||
},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{
|
||||
v1.AlphaStorageNodeAffinityAnnotation: `[{
|
||||
"requiredDuringSchedulingIgnoredDuringExecution": {
|
||||
"nodeSelectorTerms": [
|
||||
{ "matchExpressions": [
|
||||
{ "key": "test-key1",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
},
|
||||
{ "key": "test-key2",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
}
|
||||
]}
|
||||
]}
|
||||
}]`,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
annotations: map[string]string{
|
||||
v1.AlphaStorageNodeAffinityAnnotation: `{
|
||||
"requiredDuringSchedulingIgnoredDuringExecution": {
|
||||
"nodeSelectorTerms":
|
||||
"matchExpressions": [
|
||||
{ "key": "test-key1",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
},
|
||||
{ "key": "test-key2",
|
||||
"operator": "In",
|
||||
"values": ["test-value1", "test-value2"]
|
||||
}
|
||||
]}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
_, err := GetStorageNodeAffinityFromAnnotation(tc.annotations)
|
||||
if err == nil && tc.expectErr {
|
||||
t.Errorf("[%v]expected error but got none.", i)
|
||||
}
|
||||
if err != nil && !tc.expectErr {
|
||||
t.Errorf("[%v]did not expect error but got: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
392
vendor/k8s.io/kubernetes/pkg/apis/core/validation/events_test.go
generated
vendored
392
vendor/k8s.io/kubernetes/pkg/apis/core/validation/events_test.go
generated
vendored
|
|
@ -1,392 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 validation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
func TestValidateEvent(t *testing.T) {
|
||||
table := []struct {
|
||||
*core.Event
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test1",
|
||||
Namespace: "foo",
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
Namespace: "bar",
|
||||
Kind: "Pod",
|
||||
},
|
||||
},
|
||||
false,
|
||||
}, {
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test2",
|
||||
Namespace: "aoeu-_-aoeu",
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
Namespace: "aoeu-_-aoeu",
|
||||
Kind: "Pod",
|
||||
},
|
||||
},
|
||||
false,
|
||||
}, {
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test3",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Node",
|
||||
},
|
||||
},
|
||||
true,
|
||||
}, {
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test4",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Namespace",
|
||||
},
|
||||
},
|
||||
true,
|
||||
}, {
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test5",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "extensions/v1beta1",
|
||||
Kind: "NoKind",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
},
|
||||
true,
|
||||
}, {
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test6",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "extensions/v1beta1",
|
||||
Kind: "Job",
|
||||
Namespace: "foo",
|
||||
},
|
||||
},
|
||||
false,
|
||||
}, {
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test7",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "extensions/v1beta1",
|
||||
Kind: "Job",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
},
|
||||
true,
|
||||
}, {
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test8",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "other/v1beta1",
|
||||
Kind: "Job",
|
||||
Namespace: "foo",
|
||||
},
|
||||
},
|
||||
false,
|
||||
}, {
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test9",
|
||||
Namespace: "foo",
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "other/v1beta1",
|
||||
Kind: "Job",
|
||||
Namespace: "foo",
|
||||
},
|
||||
},
|
||||
true,
|
||||
}, {
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test10",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "extensions",
|
||||
Kind: "Job",
|
||||
Namespace: "foo",
|
||||
},
|
||||
},
|
||||
false,
|
||||
}, {
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test11",
|
||||
Namespace: "foo",
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
// must register in v1beta1 to be true
|
||||
APIVersion: "extensions/v1beta1",
|
||||
Kind: "Job",
|
||||
Namespace: "foo",
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test12",
|
||||
Namespace: "foo",
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "other/v1beta1",
|
||||
Kind: "FooBar",
|
||||
Namespace: "bar",
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test13",
|
||||
Namespace: "",
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "other/v1beta1",
|
||||
Kind: "FooBar",
|
||||
Namespace: "bar",
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
&core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test14",
|
||||
Namespace: "foo",
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "other/v1beta1",
|
||||
Kind: "FooBar",
|
||||
Namespace: "",
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
if e, a := item.valid, len(ValidateEvent(item.Event)) == 0; e != a {
|
||||
t.Errorf("%v: expected %v, got %v: %v", item.Event.Name, e, a, ValidateEvent(item.Event))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateNewEvent(t *testing.T) {
|
||||
someTime := metav1.MicroTime{Time: time.Unix(1505828956, 0)}
|
||||
table := []struct {
|
||||
*core.Event
|
||||
valid bool
|
||||
msg string
|
||||
}{
|
||||
{
|
||||
Event: &core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Node",
|
||||
},
|
||||
EventTime: someTime,
|
||||
},
|
||||
valid: false,
|
||||
msg: "Old Event with EventTime should trigger new validation and fail",
|
||||
},
|
||||
{
|
||||
Event: &core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Node",
|
||||
},
|
||||
EventTime: someTime,
|
||||
ReportingController: "k8s.io/my-controller",
|
||||
ReportingInstance: "node-xyz",
|
||||
Action: "Do",
|
||||
Reason: "Because",
|
||||
},
|
||||
valid: true,
|
||||
msg: "Valid new Event",
|
||||
},
|
||||
{
|
||||
Event: &core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Node",
|
||||
},
|
||||
EventTime: someTime,
|
||||
ReportingController: "my-contr@ller",
|
||||
ReportingInstance: "node-xyz",
|
||||
Action: "Do",
|
||||
Reason: "Because",
|
||||
},
|
||||
valid: false,
|
||||
msg: "not qualified reportingController",
|
||||
},
|
||||
{
|
||||
Event: &core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Node",
|
||||
},
|
||||
EventTime: someTime,
|
||||
ReportingController: "k8s.io/my-controller",
|
||||
ReportingInstance: "node-xyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
|
||||
Action: "Do",
|
||||
Reason: "Because",
|
||||
},
|
||||
valid: false,
|
||||
msg: "too long reporting instance",
|
||||
},
|
||||
{
|
||||
Event: &core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Node",
|
||||
},
|
||||
EventTime: someTime,
|
||||
ReportingController: "k8s.io/my-controller",
|
||||
ReportingInstance: "node-xyz",
|
||||
Action: "Do",
|
||||
},
|
||||
valid: false,
|
||||
msg: "missing reason",
|
||||
},
|
||||
{
|
||||
Event: &core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: metav1.NamespaceSystem,
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Node",
|
||||
},
|
||||
EventTime: someTime,
|
||||
ReportingController: "k8s.io/my-controller",
|
||||
ReportingInstance: "node-xyz",
|
||||
Reason: "Because",
|
||||
},
|
||||
valid: false,
|
||||
msg: "missing action",
|
||||
},
|
||||
{
|
||||
Event: &core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Node",
|
||||
},
|
||||
EventTime: someTime,
|
||||
ReportingController: "k8s.io/my-controller",
|
||||
ReportingInstance: "node-xyz",
|
||||
Reason: "Because",
|
||||
},
|
||||
valid: false,
|
||||
msg: "missing namespace",
|
||||
},
|
||||
{
|
||||
Event: &core.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
},
|
||||
InvolvedObject: core.ObjectReference{
|
||||
APIVersion: "v1",
|
||||
Kind: "Node",
|
||||
},
|
||||
EventTime: someTime,
|
||||
ReportingController: "k8s.io/my-controller",
|
||||
ReportingInstance: "node-xyz",
|
||||
Action: "Do",
|
||||
Reason: "Because",
|
||||
Message: `zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz`,
|
||||
},
|
||||
valid: false,
|
||||
msg: "too long message",
|
||||
},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
if e, a := item.valid, len(ValidateEvent(item.Event)) == 0; e != a {
|
||||
t.Errorf("%v: expected %v, got %v: %v", item.msg, e, a, ValidateEvent(item.Event))
|
||||
}
|
||||
}
|
||||
}
|
||||
8
vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation.go
generated
vendored
8
vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation.go
generated
vendored
|
|
@ -1140,7 +1140,7 @@ func validateMountPropagation(mountPropagation *core.MountPropagationMode, conta
|
|||
return allErrs
|
||||
}
|
||||
|
||||
supportedMountPropagations := sets.NewString(string(core.MountPropagationBidirectional), string(core.MountPropagationHostToContainer))
|
||||
supportedMountPropagations := sets.NewString(string(core.MountPropagationBidirectional), string(core.MountPropagationHostToContainer), string(core.MountPropagationNone))
|
||||
if !supportedMountPropagations.Has(string(*mountPropagation)) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath, *mountPropagation, supportedMountPropagations.List()))
|
||||
}
|
||||
|
|
@ -1383,9 +1383,6 @@ func validateLocalVolumeSource(ls *core.LocalVolumeSource, fldPath *field.Path)
|
|||
return allErrs
|
||||
}
|
||||
|
||||
if !path.IsAbs(ls.Path) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, ls.Path, "must be an absolute path"))
|
||||
}
|
||||
allErrs = append(allErrs, validatePathNoBacksteps(ls.Path, fldPath.Child("path"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
|
@ -3017,9 +3014,6 @@ func ValidateNodeSelectorRequirement(rq core.NodeSelectorRequirement, fldPath *f
|
|||
func ValidateNodeSelectorTerm(term core.NodeSelectorTerm, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if len(term.MatchExpressions) == 0 {
|
||||
return append(allErrs, field.Required(fldPath.Child("matchExpressions"), "must have at least one node selector requirement"))
|
||||
}
|
||||
for j, req := range term.MatchExpressions {
|
||||
allErrs = append(allErrs, ValidateNodeSelectorRequirement(req, fldPath.Child("matchExpressions").Index(j))...)
|
||||
}
|
||||
|
|
|
|||
12688
vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation_test.go
generated
vendored
12688
vendor/k8s.io/kubernetes/pkg/apis/core/validation/validation_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
62
vendor/k8s.io/kubernetes/pkg/apis/extensions/helpers_test.go
generated
vendored
62
vendor/k8s.io/kubernetes/pkg/apis/extensions/helpers_test.go
generated
vendored
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 extensions
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPodAnnotationsFromSysctls(t *testing.T) {
|
||||
type Test struct {
|
||||
sysctls []string
|
||||
expectedValue string
|
||||
}
|
||||
for _, test := range []Test{
|
||||
{sysctls: []string{"a.b"}, expectedValue: "a.b"},
|
||||
{sysctls: []string{"a.b", "c.d"}, expectedValue: "a.b,c.d"},
|
||||
{sysctls: []string{"a.b", "a.b"}, expectedValue: "a.b,a.b"},
|
||||
{sysctls: []string{}, expectedValue: ""},
|
||||
{sysctls: nil, expectedValue: ""},
|
||||
} {
|
||||
a := PodAnnotationsFromSysctls(test.sysctls)
|
||||
if a != test.expectedValue {
|
||||
t.Errorf("wrong value for %v: got=%q wanted=%q", test.sysctls, a, test.expectedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSysctlsFromPodSecurityPolicyAnnotation(t *testing.T) {
|
||||
type Test struct {
|
||||
expectedValue []string
|
||||
annotation string
|
||||
}
|
||||
for _, test := range []Test{
|
||||
{annotation: "a.b", expectedValue: []string{"a.b"}},
|
||||
{annotation: "a.b,c.d", expectedValue: []string{"a.b", "c.d"}},
|
||||
{annotation: "a.b,a.b", expectedValue: []string{"a.b", "a.b"}},
|
||||
{annotation: "", expectedValue: []string{}},
|
||||
} {
|
||||
sysctls, err := SysctlsFromPodSecurityPolicyAnnotation(test.annotation)
|
||||
if err != nil {
|
||||
t.Errorf("error for %q: %v", test.annotation, err)
|
||||
}
|
||||
if !reflect.DeepEqual(sysctls, test.expectedValue) {
|
||||
t.Errorf("wrong value for %q: got=%v wanted=%v", test.annotation, sysctls, test.expectedValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
50
vendor/k8s.io/kubernetes/pkg/capabilities/capabilities_test.go
generated
vendored
50
vendor/k8s.io/kubernetes/pkg/capabilities/capabilities_test.go
generated
vendored
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 capabilities
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
defaultCap := Capabilities{
|
||||
AllowPrivileged: false,
|
||||
PrivilegedSources: PrivilegedSources{
|
||||
HostNetworkSources: []string{},
|
||||
HostPIDSources: []string{},
|
||||
HostIPCSources: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
res := Get()
|
||||
if !reflect.DeepEqual(defaultCap, res) {
|
||||
t.Fatalf("expected Capabilities: %#v, got a non-default: %#v", defaultCap, res)
|
||||
}
|
||||
|
||||
cap := Capabilities{
|
||||
PrivilegedSources: PrivilegedSources{
|
||||
HostNetworkSources: []string{"A", "B"},
|
||||
},
|
||||
}
|
||||
SetForTests(cap)
|
||||
|
||||
res = Get()
|
||||
if !reflect.DeepEqual(cap, res) {
|
||||
t.Fatalf("expected Capabilities: %#v , got a different: %#v", cap, res)
|
||||
}
|
||||
}
|
||||
181
vendor/k8s.io/kubernetes/pkg/controller/controller_ref_manager_test.go
generated
vendored
181
vendor/k8s.io/kubernetes/pkg/controller/controller_ref_manager_test.go
generated
vendored
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 controller
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/api/extensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var (
|
||||
productionLabel = map[string]string{"type": "production"}
|
||||
testLabel = map[string]string{"type": "testing"}
|
||||
productionLabelSelector = labels.Set{"type": "production"}.AsSelector()
|
||||
testLabelSelector = labels.Set{"type": "testing"}.AsSelector()
|
||||
controllerUID = "123"
|
||||
)
|
||||
|
||||
func newPod(podName string, label map[string]string, owner metav1.Object) *v1.Pod {
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: podName,
|
||||
Labels: label,
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Image: "foo/bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if owner != nil {
|
||||
pod.OwnerReferences = []metav1.OwnerReference{*metav1.NewControllerRef(owner, v1beta1.SchemeGroupVersion.WithKind("Fake"))}
|
||||
}
|
||||
return pod
|
||||
}
|
||||
|
||||
func TestClaimPods(t *testing.T) {
|
||||
controllerKind := schema.GroupVersionKind{}
|
||||
type test struct {
|
||||
name string
|
||||
manager *PodControllerRefManager
|
||||
pods []*v1.Pod
|
||||
filters []func(*v1.Pod) bool
|
||||
claimed []*v1.Pod
|
||||
released []*v1.Pod
|
||||
}
|
||||
var tests = []test{
|
||||
{
|
||||
name: "Claim pods with correct label",
|
||||
manager: NewPodControllerRefManager(&FakePodControl{},
|
||||
&v1.ReplicationController{},
|
||||
productionLabelSelector,
|
||||
controllerKind,
|
||||
func() error { return nil }),
|
||||
pods: []*v1.Pod{newPod("pod1", productionLabel, nil), newPod("pod2", testLabel, nil)},
|
||||
claimed: []*v1.Pod{newPod("pod1", productionLabel, nil)},
|
||||
},
|
||||
func() test {
|
||||
controller := v1.ReplicationController{}
|
||||
controller.UID = types.UID(controllerUID)
|
||||
now := metav1.Now()
|
||||
controller.DeletionTimestamp = &now
|
||||
return test{
|
||||
name: "Controller marked for deletion can not claim pods",
|
||||
manager: NewPodControllerRefManager(&FakePodControl{},
|
||||
&controller,
|
||||
productionLabelSelector,
|
||||
controllerKind,
|
||||
func() error { return nil }),
|
||||
pods: []*v1.Pod{newPod("pod1", productionLabel, nil), newPod("pod2", productionLabel, nil)},
|
||||
claimed: nil,
|
||||
}
|
||||
}(),
|
||||
func() test {
|
||||
controller := v1.ReplicationController{}
|
||||
controller.UID = types.UID(controllerUID)
|
||||
now := metav1.Now()
|
||||
controller.DeletionTimestamp = &now
|
||||
return test{
|
||||
name: "Controller marked for deletion can not claim new pods",
|
||||
manager: NewPodControllerRefManager(&FakePodControl{},
|
||||
&controller,
|
||||
productionLabelSelector,
|
||||
controllerKind,
|
||||
func() error { return nil }),
|
||||
pods: []*v1.Pod{newPod("pod1", productionLabel, &controller), newPod("pod2", productionLabel, nil)},
|
||||
claimed: []*v1.Pod{newPod("pod1", productionLabel, &controller)},
|
||||
}
|
||||
}(),
|
||||
func() test {
|
||||
controller := v1.ReplicationController{}
|
||||
controller2 := v1.ReplicationController{}
|
||||
controller.UID = types.UID(controllerUID)
|
||||
controller2.UID = types.UID("AAAAA")
|
||||
return test{
|
||||
name: "Controller can not claim pods owned by another controller",
|
||||
manager: NewPodControllerRefManager(&FakePodControl{},
|
||||
&controller,
|
||||
productionLabelSelector,
|
||||
controllerKind,
|
||||
func() error { return nil }),
|
||||
pods: []*v1.Pod{newPod("pod1", productionLabel, &controller), newPod("pod2", productionLabel, &controller2)},
|
||||
claimed: []*v1.Pod{newPod("pod1", productionLabel, &controller)},
|
||||
}
|
||||
}(),
|
||||
func() test {
|
||||
controller := v1.ReplicationController{}
|
||||
controller.UID = types.UID(controllerUID)
|
||||
return test{
|
||||
name: "Controller releases claimed pods when selector doesn't match",
|
||||
manager: NewPodControllerRefManager(&FakePodControl{},
|
||||
&controller,
|
||||
productionLabelSelector,
|
||||
controllerKind,
|
||||
func() error { return nil }),
|
||||
pods: []*v1.Pod{newPod("pod1", productionLabel, &controller), newPod("pod2", testLabel, &controller)},
|
||||
claimed: []*v1.Pod{newPod("pod1", productionLabel, &controller)},
|
||||
}
|
||||
}(),
|
||||
func() test {
|
||||
controller := v1.ReplicationController{}
|
||||
controller.UID = types.UID(controllerUID)
|
||||
podToDelete1 := newPod("pod1", productionLabel, &controller)
|
||||
podToDelete2 := newPod("pod2", productionLabel, nil)
|
||||
now := metav1.Now()
|
||||
podToDelete1.DeletionTimestamp = &now
|
||||
podToDelete2.DeletionTimestamp = &now
|
||||
|
||||
return test{
|
||||
name: "Controller does not claim orphaned pods marked for deletion",
|
||||
manager: NewPodControllerRefManager(&FakePodControl{},
|
||||
&controller,
|
||||
productionLabelSelector,
|
||||
controllerKind,
|
||||
func() error { return nil }),
|
||||
pods: []*v1.Pod{podToDelete1, podToDelete2},
|
||||
claimed: []*v1.Pod{podToDelete1},
|
||||
}
|
||||
}(),
|
||||
}
|
||||
for _, test := range tests {
|
||||
claimed, err := test.manager.ClaimPods(test.pods)
|
||||
if err != nil {
|
||||
t.Errorf("Test case `%s`, unexpected error: %v", test.name, err)
|
||||
} else if !reflect.DeepEqual(test.claimed, claimed) {
|
||||
t.Errorf("Test case `%s`, claimed wrong pods. Expected %v, got %v", test.name, podToStringSlice(test.claimed), podToStringSlice(claimed))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func podToStringSlice(pods []*v1.Pod) []string {
|
||||
var names []string
|
||||
for _, pod := range pods {
|
||||
names = append(names, pod.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
821
vendor/k8s.io/kubernetes/pkg/controller/controller_utils_test.go
generated
vendored
821
vendor/k8s.io/kubernetes/pkg/controller/controller_utils_test.go
generated
vendored
|
|
@ -1,821 +0,0 @@
|
|||
/*
|
||||
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 controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http/httptest"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
extensions "k8s.io/api/extensions/v1beta1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
"k8s.io/kubernetes/pkg/controller/testutil"
|
||||
"k8s.io/kubernetes/pkg/securitycontext"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// NewFakeControllerExpectationsLookup creates a fake store for PodExpectations.
|
||||
func NewFakeControllerExpectationsLookup(ttl time.Duration) (*ControllerExpectations, *clock.FakeClock) {
|
||||
fakeTime := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
|
||||
fakeClock := clock.NewFakeClock(fakeTime)
|
||||
ttlPolicy := &cache.TTLPolicy{Ttl: ttl, Clock: fakeClock}
|
||||
ttlStore := cache.NewFakeExpirationStore(
|
||||
ExpKeyFunc, nil, ttlPolicy, fakeClock)
|
||||
return &ControllerExpectations{ttlStore}, fakeClock
|
||||
}
|
||||
|
||||
func newReplicationController(replicas int) *v1.ReplicationController {
|
||||
rc := &v1.ReplicationController{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String()},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: uuid.NewUUID(),
|
||||
Name: "foobar",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
ResourceVersion: "18",
|
||||
},
|
||||
Spec: v1.ReplicationControllerSpec{
|
||||
Replicas: func() *int32 { i := int32(replicas); return &i }(),
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Template: &v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"name": "foo",
|
||||
"type": "production",
|
||||
},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Image: "foo/bar",
|
||||
TerminationMessagePath: v1.TerminationMessagePathDefault,
|
||||
ImagePullPolicy: v1.PullIfNotPresent,
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
},
|
||||
},
|
||||
RestartPolicy: v1.RestartPolicyAlways,
|
||||
DNSPolicy: v1.DNSDefault,
|
||||
NodeSelector: map[string]string{
|
||||
"baz": "blah",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return rc
|
||||
}
|
||||
|
||||
// create count pods with the given phase for the given rc (same selectors and namespace), and add them to the store.
|
||||
func newPodList(store cache.Store, count int, status v1.PodPhase, rc *v1.ReplicationController) *v1.PodList {
|
||||
pods := []v1.Pod{}
|
||||
for i := 0; i < count; i++ {
|
||||
newPod := v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("pod%d", i),
|
||||
Labels: rc.Spec.Selector,
|
||||
Namespace: rc.Namespace,
|
||||
},
|
||||
Status: v1.PodStatus{Phase: status},
|
||||
}
|
||||
if store != nil {
|
||||
store.Add(&newPod)
|
||||
}
|
||||
pods = append(pods, newPod)
|
||||
}
|
||||
return &v1.PodList{
|
||||
Items: pods,
|
||||
}
|
||||
}
|
||||
|
||||
func newReplicaSet(name string, replicas int) *extensions.ReplicaSet {
|
||||
return &extensions.ReplicaSet{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String()},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: uuid.NewUUID(),
|
||||
Name: name,
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
ResourceVersion: "18",
|
||||
},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: func() *int32 { i := int32(replicas); return &i }(),
|
||||
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||
Template: v1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"name": "foo",
|
||||
"type": "production",
|
||||
},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Image: "foo/bar",
|
||||
TerminationMessagePath: v1.TerminationMessagePathDefault,
|
||||
ImagePullPolicy: v1.PullIfNotPresent,
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
},
|
||||
},
|
||||
RestartPolicy: v1.RestartPolicyAlways,
|
||||
DNSPolicy: v1.DNSDefault,
|
||||
NodeSelector: map[string]string{
|
||||
"baz": "blah",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerExpectations(t *testing.T) {
|
||||
ttl := 30 * time.Second
|
||||
e, fakeClock := NewFakeControllerExpectationsLookup(ttl)
|
||||
// In practice we can't really have add and delete expectations since we only either create or
|
||||
// delete replicas in one rc pass, and the rc goes to sleep soon after until the expectations are
|
||||
// either fulfilled or timeout.
|
||||
adds, dels := 10, 30
|
||||
rc := newReplicationController(1)
|
||||
|
||||
// RC fires off adds and deletes at apiserver, then sets expectations
|
||||
rcKey, err := KeyFunc(rc)
|
||||
assert.NoError(t, err, "Couldn't get key for object %#v: %v", rc, err)
|
||||
|
||||
e.SetExpectations(rcKey, adds, dels)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < adds+1; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
// In prod this can happen either because of a failed create by the rc
|
||||
// or after having observed a create via informer
|
||||
e.CreationObserved(rcKey)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// There are still delete expectations
|
||||
assert.False(t, e.SatisfiedExpectations(rcKey), "Rc will sync before expectations are met")
|
||||
|
||||
for i := 0; i < dels+1; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
e.DeletionObserved(rcKey)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// Expectations have been surpassed
|
||||
podExp, exists, err := e.GetExpectations(rcKey)
|
||||
assert.NoError(t, err, "Could not get expectations for rc, exists %v and err %v", exists, err)
|
||||
assert.True(t, exists, "Could not get expectations for rc, exists %v and err %v", exists, err)
|
||||
|
||||
add, del := podExp.GetExpectations()
|
||||
assert.Equal(t, int64(-1), add, "Unexpected pod expectations %#v", podExp)
|
||||
assert.Equal(t, int64(-1), del, "Unexpected pod expectations %#v", podExp)
|
||||
assert.True(t, e.SatisfiedExpectations(rcKey), "Expectations are met but the rc will not sync")
|
||||
|
||||
// Next round of rc sync, old expectations are cleared
|
||||
e.SetExpectations(rcKey, 1, 2)
|
||||
podExp, exists, err = e.GetExpectations(rcKey)
|
||||
assert.NoError(t, err, "Could not get expectations for rc, exists %v and err %v", exists, err)
|
||||
assert.True(t, exists, "Could not get expectations for rc, exists %v and err %v", exists, err)
|
||||
add, del = podExp.GetExpectations()
|
||||
|
||||
assert.Equal(t, int64(1), add, "Unexpected pod expectations %#v", podExp)
|
||||
assert.Equal(t, int64(2), del, "Unexpected pod expectations %#v", podExp)
|
||||
|
||||
// Expectations have expired because of ttl
|
||||
fakeClock.Step(ttl + 1)
|
||||
assert.True(t, e.SatisfiedExpectations(rcKey),
|
||||
"Expectations should have expired but didn't")
|
||||
}
|
||||
|
||||
func TestUIDExpectations(t *testing.T) {
|
||||
uidExp := NewUIDTrackingControllerExpectations(NewControllerExpectations())
|
||||
rcList := []*v1.ReplicationController{
|
||||
newReplicationController(2),
|
||||
newReplicationController(1),
|
||||
newReplicationController(0),
|
||||
newReplicationController(5),
|
||||
}
|
||||
rcToPods := map[string][]string{}
|
||||
rcKeys := []string{}
|
||||
for i := range rcList {
|
||||
rc := rcList[i]
|
||||
rcName := fmt.Sprintf("rc-%v", i)
|
||||
rc.Name = rcName
|
||||
rc.Spec.Selector[rcName] = rcName
|
||||
podList := newPodList(nil, 5, v1.PodRunning, rc)
|
||||
rcKey, err := KeyFunc(rc)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't get key for object %#v: %v", rc, err)
|
||||
}
|
||||
rcKeys = append(rcKeys, rcKey)
|
||||
rcPodNames := []string{}
|
||||
for i := range podList.Items {
|
||||
p := &podList.Items[i]
|
||||
p.Name = fmt.Sprintf("%v-%v", p.Name, rc.Name)
|
||||
rcPodNames = append(rcPodNames, PodKey(p))
|
||||
}
|
||||
rcToPods[rcKey] = rcPodNames
|
||||
uidExp.ExpectDeletions(rcKey, rcPodNames)
|
||||
}
|
||||
for i := range rcKeys {
|
||||
j := rand.Intn(i + 1)
|
||||
rcKeys[i], rcKeys[j] = rcKeys[j], rcKeys[i]
|
||||
}
|
||||
for _, rcKey := range rcKeys {
|
||||
assert.False(t, uidExp.SatisfiedExpectations(rcKey),
|
||||
"Controller %v satisfied expectations before deletion", rcKey)
|
||||
|
||||
for _, p := range rcToPods[rcKey] {
|
||||
uidExp.DeletionObserved(rcKey, p)
|
||||
}
|
||||
|
||||
assert.True(t, uidExp.SatisfiedExpectations(rcKey),
|
||||
"Controller %v didn't satisfy expectations after deletion", rcKey)
|
||||
|
||||
uidExp.DeleteExpectations(rcKey)
|
||||
|
||||
assert.Nil(t, uidExp.GetUIDs(rcKey),
|
||||
"Failed to delete uid expectations for %v", rcKey)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePods(t *testing.T) {
|
||||
ns := metav1.NamespaceDefault
|
||||
body := runtime.EncodeOrDie(testapi.Default.Codec(), &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "empty_pod"}})
|
||||
fakeHandler := utiltesting.FakeHandler{
|
||||
StatusCode: 200,
|
||||
ResponseBody: string(body),
|
||||
}
|
||||
testServer := httptest.NewServer(&fakeHandler)
|
||||
defer testServer.Close()
|
||||
clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: testServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion}})
|
||||
|
||||
podControl := RealPodControl{
|
||||
KubeClient: clientset,
|
||||
Recorder: &record.FakeRecorder{},
|
||||
}
|
||||
|
||||
controllerSpec := newReplicationController(1)
|
||||
|
||||
// Make sure createReplica sends a POST to the apiserver with a pod from the controllers pod template
|
||||
err := podControl.CreatePods(ns, controllerSpec.Spec.Template, controllerSpec)
|
||||
assert.NoError(t, err, "unexpected error: %v", err)
|
||||
|
||||
expectedPod := v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: controllerSpec.Spec.Template.Labels,
|
||||
GenerateName: fmt.Sprintf("%s-", controllerSpec.Name),
|
||||
},
|
||||
Spec: controllerSpec.Spec.Template.Spec,
|
||||
}
|
||||
fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath("pods", metav1.NamespaceDefault, ""), "POST", nil)
|
||||
var actualPod = &v1.Pod{}
|
||||
err = json.Unmarshal([]byte(fakeHandler.RequestBody), actualPod)
|
||||
assert.NoError(t, err, "unexpected error: %v", err)
|
||||
assert.True(t, apiequality.Semantic.DeepDerivative(&expectedPod, actualPod),
|
||||
"Body: %s", fakeHandler.RequestBody)
|
||||
}
|
||||
|
||||
func TestActivePodFiltering(t *testing.T) {
|
||||
// This rc is not needed by the test, only the newPodList to give the pods labels/a namespace.
|
||||
rc := newReplicationController(0)
|
||||
podList := newPodList(nil, 5, v1.PodRunning, rc)
|
||||
podList.Items[0].Status.Phase = v1.PodSucceeded
|
||||
podList.Items[1].Status.Phase = v1.PodFailed
|
||||
expectedNames := sets.NewString()
|
||||
for _, pod := range podList.Items[2:] {
|
||||
expectedNames.Insert(pod.Name)
|
||||
}
|
||||
|
||||
var podPointers []*v1.Pod
|
||||
for i := range podList.Items {
|
||||
podPointers = append(podPointers, &podList.Items[i])
|
||||
}
|
||||
got := FilterActivePods(podPointers)
|
||||
gotNames := sets.NewString()
|
||||
for _, pod := range got {
|
||||
gotNames.Insert(pod.Name)
|
||||
}
|
||||
|
||||
assert.Equal(t, 0, expectedNames.Difference(gotNames).Len(),
|
||||
"expected %v, got %v", expectedNames.List(), gotNames.List())
|
||||
assert.Equal(t, 0, gotNames.Difference(expectedNames).Len(),
|
||||
"expected %v, got %v", expectedNames.List(), gotNames.List())
|
||||
}
|
||||
|
||||
func TestSortingActivePods(t *testing.T) {
|
||||
numPods := 9
|
||||
// This rc is not needed by the test, only the newPodList to give the pods labels/a namespace.
|
||||
rc := newReplicationController(0)
|
||||
podList := newPodList(nil, numPods, v1.PodRunning, rc)
|
||||
|
||||
pods := make([]*v1.Pod, len(podList.Items))
|
||||
for i := range podList.Items {
|
||||
pods[i] = &podList.Items[i]
|
||||
}
|
||||
// pods[0] is not scheduled yet.
|
||||
pods[0].Spec.NodeName = ""
|
||||
pods[0].Status.Phase = v1.PodPending
|
||||
// pods[1] is scheduled but pending.
|
||||
pods[1].Spec.NodeName = "bar"
|
||||
pods[1].Status.Phase = v1.PodPending
|
||||
// pods[2] is unknown.
|
||||
pods[2].Spec.NodeName = "foo"
|
||||
pods[2].Status.Phase = v1.PodUnknown
|
||||
// pods[3] is running but not ready.
|
||||
pods[3].Spec.NodeName = "foo"
|
||||
pods[3].Status.Phase = v1.PodRunning
|
||||
// pods[4] is running and ready but without LastTransitionTime.
|
||||
now := metav1.Now()
|
||||
pods[4].Spec.NodeName = "foo"
|
||||
pods[4].Status.Phase = v1.PodRunning
|
||||
pods[4].Status.Conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue}}
|
||||
pods[4].Status.ContainerStatuses = []v1.ContainerStatus{{RestartCount: 3}, {RestartCount: 0}}
|
||||
// pods[5] is running and ready and with LastTransitionTime.
|
||||
pods[5].Spec.NodeName = "foo"
|
||||
pods[5].Status.Phase = v1.PodRunning
|
||||
pods[5].Status.Conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue, LastTransitionTime: now}}
|
||||
pods[5].Status.ContainerStatuses = []v1.ContainerStatus{{RestartCount: 3}, {RestartCount: 0}}
|
||||
// pods[6] is running ready for a longer time than pods[5].
|
||||
then := metav1.Time{Time: now.AddDate(0, -1, 0)}
|
||||
pods[6].Spec.NodeName = "foo"
|
||||
pods[6].Status.Phase = v1.PodRunning
|
||||
pods[6].Status.Conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue, LastTransitionTime: then}}
|
||||
pods[6].Status.ContainerStatuses = []v1.ContainerStatus{{RestartCount: 3}, {RestartCount: 0}}
|
||||
// pods[7] has lower container restart count than pods[6].
|
||||
pods[7].Spec.NodeName = "foo"
|
||||
pods[7].Status.Phase = v1.PodRunning
|
||||
pods[7].Status.Conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue, LastTransitionTime: then}}
|
||||
pods[7].Status.ContainerStatuses = []v1.ContainerStatus{{RestartCount: 2}, {RestartCount: 1}}
|
||||
pods[7].CreationTimestamp = now
|
||||
// pods[8] is older than pods[7].
|
||||
pods[8].Spec.NodeName = "foo"
|
||||
pods[8].Status.Phase = v1.PodRunning
|
||||
pods[8].Status.Conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue, LastTransitionTime: then}}
|
||||
pods[8].Status.ContainerStatuses = []v1.ContainerStatus{{RestartCount: 2}, {RestartCount: 1}}
|
||||
pods[8].CreationTimestamp = then
|
||||
|
||||
getOrder := func(pods []*v1.Pod) []string {
|
||||
names := make([]string, len(pods))
|
||||
for i := range pods {
|
||||
names[i] = pods[i].Name
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
expected := getOrder(pods)
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
idx := rand.Perm(numPods)
|
||||
randomizedPods := make([]*v1.Pod, numPods)
|
||||
for j := 0; j < numPods; j++ {
|
||||
randomizedPods[j] = pods[idx[j]]
|
||||
}
|
||||
sort.Sort(ActivePods(randomizedPods))
|
||||
actual := getOrder(randomizedPods)
|
||||
|
||||
assert.EqualValues(t, expected, actual, "expected %v, got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestActiveReplicaSetsFiltering(t *testing.T) {
|
||||
var replicaSets []*extensions.ReplicaSet
|
||||
replicaSets = append(replicaSets, newReplicaSet("zero", 0))
|
||||
replicaSets = append(replicaSets, nil)
|
||||
replicaSets = append(replicaSets, newReplicaSet("foo", 1))
|
||||
replicaSets = append(replicaSets, newReplicaSet("bar", 2))
|
||||
expectedNames := sets.NewString()
|
||||
for _, rs := range replicaSets[2:] {
|
||||
expectedNames.Insert(rs.Name)
|
||||
}
|
||||
|
||||
got := FilterActiveReplicaSets(replicaSets)
|
||||
gotNames := sets.NewString()
|
||||
for _, rs := range got {
|
||||
gotNames.Insert(rs.Name)
|
||||
}
|
||||
|
||||
assert.Equal(t, 0, expectedNames.Difference(gotNames).Len(),
|
||||
"expected %v, got %v", expectedNames.List(), gotNames.List())
|
||||
assert.Equal(t, 0, gotNames.Difference(expectedNames).Len(),
|
||||
"expected %v, got %v", expectedNames.List(), gotNames.List())
|
||||
}
|
||||
|
||||
func TestComputeHash(t *testing.T) {
|
||||
collisionCount := int32(1)
|
||||
otherCollisionCount := int32(2)
|
||||
maxCollisionCount := int32(math.MaxInt32)
|
||||
tests := []struct {
|
||||
name string
|
||||
template *v1.PodTemplateSpec
|
||||
collisionCount *int32
|
||||
otherCollisionCount *int32
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
template: &v1.PodTemplateSpec{},
|
||||
collisionCount: &collisionCount,
|
||||
otherCollisionCount: &otherCollisionCount,
|
||||
},
|
||||
{
|
||||
name: "using math.MaxInt64",
|
||||
template: &v1.PodTemplateSpec{},
|
||||
collisionCount: nil,
|
||||
otherCollisionCount: &maxCollisionCount,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
hash := ComputeHash(test.template, test.collisionCount)
|
||||
otherHash := ComputeHash(test.template, test.otherCollisionCount)
|
||||
|
||||
assert.NotEqual(t, hash, otherHash, "expected different hashes but got the same: %d", hash)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveTaintOffNode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
nodeHandler *testutil.FakeNodeHandler
|
||||
nodeName string
|
||||
taintsToRemove []*v1.Taint
|
||||
expectedTaints []v1.Taint
|
||||
requestCount int
|
||||
}{
|
||||
{
|
||||
name: "remove one taint from node",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToRemove: []*v1.Taint{
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
expectedTaints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
},
|
||||
requestCount: 4,
|
||||
},
|
||||
{
|
||||
name: "remove multiple taints from node",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
{Key: "key3", Value: "value3", Effect: "NoSchedule"},
|
||||
{Key: "key4", Value: "value4", Effect: "NoExecute"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToRemove: []*v1.Taint{
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
{Key: "key3", Value: "value3", Effect: "NoSchedule"},
|
||||
},
|
||||
expectedTaints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key4", Value: "value4", Effect: "NoExecute"},
|
||||
},
|
||||
requestCount: 4,
|
||||
},
|
||||
{
|
||||
name: "remove no-exist taints from node",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToRemove: []*v1.Taint{
|
||||
{Key: "key3", Value: "value3", Effect: "NoSchedule"},
|
||||
},
|
||||
expectedTaints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
requestCount: 2,
|
||||
},
|
||||
{
|
||||
name: "remove taint from node without taints",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToRemove: []*v1.Taint{
|
||||
{Key: "key3", Value: "value3", Effect: "NoSchedule"},
|
||||
},
|
||||
expectedTaints: nil,
|
||||
requestCount: 2,
|
||||
},
|
||||
{
|
||||
name: "remove empty taint list from node without taints",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToRemove: []*v1.Taint{},
|
||||
expectedTaints: nil,
|
||||
requestCount: 2,
|
||||
},
|
||||
{
|
||||
name: "remove empty taint list from node",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToRemove: []*v1.Taint{},
|
||||
expectedTaints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
requestCount: 2,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
node, _ := test.nodeHandler.Get(test.nodeName, metav1.GetOptions{})
|
||||
err := RemoveTaintOffNode(test.nodeHandler, test.nodeName, node, test.taintsToRemove...)
|
||||
assert.NoError(t, err, "%s: RemoveTaintOffNode() error = %v", test.name, err)
|
||||
|
||||
node, _ = test.nodeHandler.Get(test.nodeName, metav1.GetOptions{})
|
||||
assert.EqualValues(t, test.expectedTaints, node.Spec.Taints,
|
||||
"%s: failed to remove taint off node: expected %+v, got %+v",
|
||||
test.name, test.expectedTaints, node.Spec.Taints)
|
||||
|
||||
assert.Equal(t, test.requestCount, test.nodeHandler.RequestCount,
|
||||
"%s: unexpected request count: expected %+v, got %+v",
|
||||
test.name, test.requestCount, test.nodeHandler.RequestCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddOrUpdateTaintOnNode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
nodeHandler *testutil.FakeNodeHandler
|
||||
nodeName string
|
||||
taintsToAdd []*v1.Taint
|
||||
expectedTaints []v1.Taint
|
||||
requestCount int
|
||||
}{
|
||||
{
|
||||
name: "add one taint on node",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToAdd: []*v1.Taint{
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
expectedTaints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
requestCount: 3,
|
||||
},
|
||||
{
|
||||
name: "add multiple taints to node",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToAdd: []*v1.Taint{
|
||||
{Key: "key3", Value: "value3", Effect: "NoSchedule"},
|
||||
{Key: "key4", Value: "value4", Effect: "NoExecute"},
|
||||
},
|
||||
expectedTaints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
{Key: "key3", Value: "value3", Effect: "NoSchedule"},
|
||||
{Key: "key4", Value: "value4", Effect: "NoExecute"},
|
||||
},
|
||||
requestCount: 3,
|
||||
},
|
||||
{
|
||||
name: "add exist taints to node",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToAdd: []*v1.Taint{
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
expectedTaints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
requestCount: 2,
|
||||
},
|
||||
{
|
||||
name: "add taint to node without taints",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToAdd: []*v1.Taint{
|
||||
{Key: "key3", Value: "value3", Effect: "NoSchedule"},
|
||||
},
|
||||
expectedTaints: []v1.Taint{
|
||||
{Key: "key3", Value: "value3", Effect: "NoSchedule"},
|
||||
},
|
||||
requestCount: 3,
|
||||
},
|
||||
{
|
||||
name: "add empty taint list to node without taints",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToAdd: []*v1.Taint{},
|
||||
expectedTaints: nil,
|
||||
requestCount: 1,
|
||||
},
|
||||
{
|
||||
name: "add empty taint list to node",
|
||||
nodeHandler: &testutil.FakeNodeHandler{
|
||||
Existing: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "node1",
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
||||
},
|
||||
nodeName: "node1",
|
||||
taintsToAdd: []*v1.Taint{},
|
||||
expectedTaints: []v1.Taint{
|
||||
{Key: "key1", Value: "value1", Effect: "NoSchedule"},
|
||||
{Key: "key2", Value: "value2", Effect: "NoExecute"},
|
||||
},
|
||||
requestCount: 1,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
err := AddOrUpdateTaintOnNode(test.nodeHandler, test.nodeName, test.taintsToAdd...)
|
||||
assert.NoError(t, err, "%s: AddOrUpdateTaintOnNode() error = %v", test.name, err)
|
||||
|
||||
node, _ := test.nodeHandler.Get(test.nodeName, metav1.GetOptions{})
|
||||
assert.EqualValues(t, test.expectedTaints, node.Spec.Taints,
|
||||
"%s: failed to add taint to node: expected %+v, got %+v",
|
||||
test.name, test.expectedTaints, node.Spec.Taints)
|
||||
|
||||
assert.Equal(t, test.requestCount, test.nodeHandler.RequestCount,
|
||||
"%s: unexpected request count: expected %+v, got %+v",
|
||||
test.name, test.requestCount, test.nodeHandler.RequestCount)
|
||||
}
|
||||
}
|
||||
241
vendor/k8s.io/kubernetes/pkg/fieldpath/fieldpath_test.go
generated
vendored
241
vendor/k8s.io/kubernetes/pkg/fieldpath/fieldpath_test.go
generated
vendored
|
|
@ -1,241 +0,0 @@
|
|||
/*
|
||||
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 fieldpath
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestExtractFieldPathAsString(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
fieldPath string
|
||||
obj interface{}
|
||||
expectedValue string
|
||||
expectedMessageFragment string
|
||||
}{
|
||||
{
|
||||
name: "not an API object",
|
||||
fieldPath: "metadata.name",
|
||||
obj: "",
|
||||
},
|
||||
{
|
||||
name: "ok - namespace",
|
||||
fieldPath: "metadata.namespace",
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "object-namespace",
|
||||
},
|
||||
},
|
||||
expectedValue: "object-namespace",
|
||||
},
|
||||
{
|
||||
name: "ok - name",
|
||||
fieldPath: "metadata.name",
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "object-name",
|
||||
},
|
||||
},
|
||||
expectedValue: "object-name",
|
||||
},
|
||||
{
|
||||
name: "ok - labels",
|
||||
fieldPath: "metadata.labels",
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{"key": "value"},
|
||||
},
|
||||
},
|
||||
expectedValue: "key=\"value\"",
|
||||
},
|
||||
{
|
||||
name: "ok - labels bslash n",
|
||||
fieldPath: "metadata.labels",
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{"key": "value\n"},
|
||||
},
|
||||
},
|
||||
expectedValue: "key=\"value\\n\"",
|
||||
},
|
||||
{
|
||||
name: "ok - annotations",
|
||||
fieldPath: "metadata.annotations",
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"builder": "john-doe"},
|
||||
},
|
||||
},
|
||||
expectedValue: "builder=\"john-doe\"",
|
||||
},
|
||||
{
|
||||
name: "ok - annotation",
|
||||
fieldPath: "metadata.annotations['spec.pod.beta.kubernetes.io/statefulset-index']",
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"spec.pod.beta.kubernetes.io/statefulset-index": "1"},
|
||||
},
|
||||
},
|
||||
expectedValue: "1",
|
||||
},
|
||||
{
|
||||
name: "ok - annotation",
|
||||
fieldPath: "metadata.annotations['Www.k8s.io/test']",
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"Www.k8s.io/test": "1"},
|
||||
},
|
||||
},
|
||||
expectedValue: "1",
|
||||
},
|
||||
{
|
||||
name: "invalid expression",
|
||||
fieldPath: "metadata.whoops",
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "object-namespace",
|
||||
},
|
||||
},
|
||||
expectedMessageFragment: "unsupported fieldPath",
|
||||
},
|
||||
{
|
||||
name: "invalid annotation key",
|
||||
fieldPath: "metadata.annotations['invalid~key']",
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"foo": "bar"},
|
||||
},
|
||||
},
|
||||
expectedMessageFragment: "invalid key subscript in metadata.annotations",
|
||||
},
|
||||
{
|
||||
name: "invalid label key",
|
||||
fieldPath: "metadata.labels['Www.k8s.io/test']",
|
||||
obj: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{"foo": "bar"},
|
||||
},
|
||||
},
|
||||
expectedMessageFragment: "invalid key subscript in metadata.labels",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actual, err := ExtractFieldPathAsString(tc.obj, tc.fieldPath)
|
||||
if err != nil {
|
||||
if tc.expectedMessageFragment != "" {
|
||||
if !strings.Contains(err.Error(), tc.expectedMessageFragment) {
|
||||
t.Errorf("%v: unexpected error message: %q, expected to contain %q", tc.name, err, tc.expectedMessageFragment)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("%v: unexpected error: %v", tc.name, err)
|
||||
}
|
||||
} else if tc.expectedMessageFragment != "" {
|
||||
t.Errorf("%v: expected error: %v", tc.name, tc.expectedMessageFragment)
|
||||
} else if e := tc.expectedValue; e != "" && e != actual {
|
||||
t.Errorf("%v: unexpected result; got %q, expected %q", tc.name, actual, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitMaybeSubscriptedPath(t *testing.T) {
|
||||
cases := []struct {
|
||||
fieldPath string
|
||||
expectedPath string
|
||||
expectedSubscript string
|
||||
expectedOK bool
|
||||
}{
|
||||
{
|
||||
fieldPath: "metadata.annotations['key']",
|
||||
expectedPath: "metadata.annotations",
|
||||
expectedSubscript: "key",
|
||||
expectedOK: true,
|
||||
},
|
||||
{
|
||||
fieldPath: "metadata.annotations['a[b']c']",
|
||||
expectedPath: "metadata.annotations",
|
||||
expectedSubscript: "a[b']c",
|
||||
expectedOK: true,
|
||||
},
|
||||
{
|
||||
fieldPath: "metadata.labels['['key']",
|
||||
expectedPath: "metadata.labels",
|
||||
expectedSubscript: "['key",
|
||||
expectedOK: true,
|
||||
},
|
||||
{
|
||||
fieldPath: "metadata.labels['key']']",
|
||||
expectedPath: "metadata.labels",
|
||||
expectedSubscript: "key']",
|
||||
expectedOK: true,
|
||||
},
|
||||
{
|
||||
fieldPath: "metadata.labels['']",
|
||||
expectedPath: "metadata.labels",
|
||||
expectedSubscript: "",
|
||||
expectedOK: true,
|
||||
},
|
||||
{
|
||||
fieldPath: "metadata.labels[' ']",
|
||||
expectedPath: "metadata.labels",
|
||||
expectedSubscript: " ",
|
||||
expectedOK: true,
|
||||
},
|
||||
{
|
||||
fieldPath: "metadata.labels[ 'key' ]",
|
||||
expectedOK: false,
|
||||
},
|
||||
{
|
||||
fieldPath: "metadata.labels[]",
|
||||
expectedOK: false,
|
||||
},
|
||||
{
|
||||
fieldPath: "metadata.labels[']",
|
||||
expectedOK: false,
|
||||
},
|
||||
{
|
||||
fieldPath: "metadata.labels['key']foo",
|
||||
expectedOK: false,
|
||||
},
|
||||
{
|
||||
fieldPath: "['key']",
|
||||
expectedOK: false,
|
||||
},
|
||||
{
|
||||
fieldPath: "metadata.labels",
|
||||
expectedOK: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
path, subscript, ok := SplitMaybeSubscriptedPath(tc.fieldPath)
|
||||
if !ok {
|
||||
if tc.expectedOK {
|
||||
t.Errorf("SplitMaybeSubscriptedPath(%q) expected to return (_, _, true)", tc.fieldPath)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if path != tc.expectedPath || subscript != tc.expectedSubscript {
|
||||
t.Errorf("SplitMaybeSubscriptedPath(%q) = (%q, %q, true), expect (%q, %q, true)",
|
||||
tc.fieldPath, path, subscript, tc.expectedPath, tc.expectedSubscript)
|
||||
}
|
||||
}
|
||||
}
|
||||
95
vendor/k8s.io/kubernetes/pkg/kubelet/active_deadline_test.go
generated
vendored
95
vendor/k8s.io/kubernetes/pkg/kubelet/active_deadline_test.go
generated
vendored
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 kubelet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/client-go/tools/record"
|
||||
)
|
||||
|
||||
// mockPodStatusProvider returns the status on the specified pod
|
||||
type mockPodStatusProvider struct {
|
||||
pods []*v1.Pod
|
||||
}
|
||||
|
||||
// GetPodStatus returns the status on the associated pod with matching uid (if found)
|
||||
func (m *mockPodStatusProvider) GetPodStatus(uid types.UID) (v1.PodStatus, bool) {
|
||||
for _, pod := range m.pods {
|
||||
if pod.UID == uid {
|
||||
return pod.Status, true
|
||||
}
|
||||
}
|
||||
return v1.PodStatus{}, false
|
||||
}
|
||||
|
||||
// TestActiveDeadlineHandler verifies the active deadline handler functions as expected.
|
||||
func TestActiveDeadlineHandler(t *testing.T) {
|
||||
pods := newTestPods(4)
|
||||
fakeClock := clock.NewFakeClock(time.Now())
|
||||
podStatusProvider := &mockPodStatusProvider{pods: pods}
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
handler, err := newActiveDeadlineHandler(podStatusProvider, fakeRecorder, fakeClock)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
now := metav1.Now()
|
||||
startTime := metav1.NewTime(now.Time.Add(-1 * time.Minute))
|
||||
|
||||
// this pod has exceeded its active deadline
|
||||
exceededActiveDeadlineSeconds := int64(30)
|
||||
pods[0].Status.StartTime = &startTime
|
||||
pods[0].Spec.ActiveDeadlineSeconds = &exceededActiveDeadlineSeconds
|
||||
|
||||
// this pod has not exceeded its active deadline
|
||||
notYetActiveDeadlineSeconds := int64(120)
|
||||
pods[1].Status.StartTime = &startTime
|
||||
pods[1].Spec.ActiveDeadlineSeconds = ¬YetActiveDeadlineSeconds
|
||||
|
||||
// this pod has no deadline
|
||||
pods[2].Status.StartTime = &startTime
|
||||
pods[2].Spec.ActiveDeadlineSeconds = nil
|
||||
|
||||
testCases := []struct {
|
||||
pod *v1.Pod
|
||||
expected bool
|
||||
}{{pods[0], true}, {pods[1], false}, {pods[2], false}, {pods[3], false}}
|
||||
|
||||
for i, testCase := range testCases {
|
||||
if actual := handler.ShouldSync(testCase.pod); actual != testCase.expected {
|
||||
t.Errorf("[%d] ShouldSync expected %#v, got %#v", i, testCase.expected, actual)
|
||||
}
|
||||
actual := handler.ShouldEvict(testCase.pod)
|
||||
if actual.Evict != testCase.expected {
|
||||
t.Errorf("[%d] ShouldEvict.Evict expected %#v, got %#v", i, testCase.expected, actual.Evict)
|
||||
}
|
||||
if testCase.expected {
|
||||
if actual.Reason != reason {
|
||||
t.Errorf("[%d] ShouldEvict.Reason expected %#v, got %#v", i, message, actual.Reason)
|
||||
}
|
||||
if actual.Message != message {
|
||||
t.Errorf("[%d] ShouldEvict.Message expected %#v, got %#v", i, message, actual.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
210
vendor/k8s.io/kubernetes/pkg/kubelet/container/cache_test.go
generated
vendored
210
vendor/k8s.io/kubernetes/pkg/kubelet/container/cache_test.go
generated
vendored
|
|
@ -1,210 +0,0 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
func newTestCache() *cache {
|
||||
c := NewCache()
|
||||
return c.(*cache)
|
||||
}
|
||||
|
||||
func TestCacheNotInitialized(t *testing.T) {
|
||||
cache := newTestCache()
|
||||
// If the global timestamp is not set, always return nil.
|
||||
d := cache.getIfNewerThan(types.UID("1234"), time.Time{})
|
||||
assert.True(t, d == nil, "should return nil since cache is not initialized")
|
||||
}
|
||||
|
||||
func getTestPodIDAndStatus(numContainers int) (types.UID, *PodStatus) {
|
||||
id := types.UID(strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||
name := fmt.Sprintf("cache-foo-%s", string(id))
|
||||
namespace := "ns"
|
||||
var status *PodStatus
|
||||
if numContainers > 0 {
|
||||
status = &PodStatus{ID: id, Name: name, Namespace: namespace}
|
||||
} else {
|
||||
status = &PodStatus{ID: id}
|
||||
}
|
||||
for i := 0; i < numContainers; i++ {
|
||||
status.ContainerStatuses = append(status.ContainerStatuses, &ContainerStatus{Name: string(i)})
|
||||
}
|
||||
return id, status
|
||||
}
|
||||
|
||||
func TestGetIfNewerThanWhenPodExists(t *testing.T) {
|
||||
cache := newTestCache()
|
||||
timestamp := time.Now()
|
||||
|
||||
cases := []struct {
|
||||
cacheTime time.Time
|
||||
modified time.Time
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
// Both the global cache timestamp and the modified time are newer
|
||||
// than the timestamp.
|
||||
cacheTime: timestamp.Add(time.Second),
|
||||
modified: timestamp,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
// Global cache timestamp is newer, but the pod entry modified
|
||||
// time is older than the given timestamp. This means that the
|
||||
// entry is up-to-date even though it hasn't changed for a while.
|
||||
cacheTime: timestamp.Add(time.Second),
|
||||
modified: timestamp.Add(-time.Second * 10),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
// Global cache timestamp is older, but the pod entry modified
|
||||
// time is newer than the given timestamp. This means that the
|
||||
// entry is up-to-date but the rest of the cache are still being
|
||||
// updated.
|
||||
cacheTime: timestamp.Add(-time.Second),
|
||||
modified: timestamp.Add(time.Second * 3),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
// Both the global cache timestamp and the modified time are older
|
||||
// than the given timestamp.
|
||||
cacheTime: timestamp.Add(-time.Second),
|
||||
modified: timestamp.Add(-time.Second),
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for i, c := range cases {
|
||||
podID, status := getTestPodIDAndStatus(2)
|
||||
cache.UpdateTime(c.cacheTime)
|
||||
cache.Set(podID, status, nil, c.modified)
|
||||
d := cache.getIfNewerThan(podID, timestamp)
|
||||
assert.Equal(t, c.expected, d != nil, "test[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodNewerThanWhenPodDoesNotExist(t *testing.T) {
|
||||
cache := newTestCache()
|
||||
cacheTime := time.Now()
|
||||
cache.UpdateTime(cacheTime)
|
||||
podID := types.UID("1234")
|
||||
|
||||
cases := []struct {
|
||||
timestamp time.Time
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
timestamp: cacheTime.Add(-time.Second),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
timestamp: cacheTime.Add(time.Second),
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for i, c := range cases {
|
||||
d := cache.getIfNewerThan(podID, c.timestamp)
|
||||
assert.Equal(t, c.expected, d != nil, "test[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheSetAndGet(t *testing.T) {
|
||||
cache := NewCache()
|
||||
cases := []struct {
|
||||
numContainers int
|
||||
error error
|
||||
}{
|
||||
{numContainers: 3, error: nil},
|
||||
{numContainers: 2, error: fmt.Errorf("unable to get status")},
|
||||
{numContainers: 0, error: nil},
|
||||
}
|
||||
for i, c := range cases {
|
||||
podID, status := getTestPodIDAndStatus(c.numContainers)
|
||||
cache.Set(podID, status, c.error, time.Time{})
|
||||
// Read back the status and error stored in cache and make sure they
|
||||
// match the original ones.
|
||||
actualStatus, actualErr := cache.Get(podID)
|
||||
assert.Equal(t, status, actualStatus, "test[%d]", i)
|
||||
assert.Equal(t, c.error, actualErr, "test[%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheGetPodDoesNotExist(t *testing.T) {
|
||||
cache := NewCache()
|
||||
podID, status := getTestPodIDAndStatus(0)
|
||||
// If the pod does not exist in cache, cache should return an status
|
||||
// object with id filled.
|
||||
actualStatus, actualErr := cache.Get(podID)
|
||||
assert.Equal(t, status, actualStatus)
|
||||
assert.Equal(t, nil, actualErr)
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
cache := &cache{pods: map[types.UID]*data{}}
|
||||
// Write a new pod status into the cache.
|
||||
podID, status := getTestPodIDAndStatus(3)
|
||||
cache.Set(podID, status, nil, time.Time{})
|
||||
actualStatus, actualErr := cache.Get(podID)
|
||||
assert.Equal(t, status, actualStatus)
|
||||
assert.Equal(t, nil, actualErr)
|
||||
// Delete the pod from cache, and verify that we get an empty status.
|
||||
cache.Delete(podID)
|
||||
expectedStatus := &PodStatus{ID: podID}
|
||||
actualStatus, actualErr = cache.Get(podID)
|
||||
assert.Equal(t, expectedStatus, actualStatus)
|
||||
assert.Equal(t, nil, actualErr)
|
||||
}
|
||||
|
||||
func verifyNotification(t *testing.T, ch chan *data, expectNotification bool) {
|
||||
if expectNotification {
|
||||
assert.True(t, len(ch) > 0, "Did not receive notification")
|
||||
} else {
|
||||
assert.True(t, len(ch) < 1, "Should not have triggered the notification")
|
||||
}
|
||||
// Drain the channel.
|
||||
for i := 0; i < len(ch); i++ {
|
||||
<-ch
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterNotification(t *testing.T) {
|
||||
cache := newTestCache()
|
||||
cacheTime := time.Now()
|
||||
cache.UpdateTime(cacheTime)
|
||||
|
||||
podID, status := getTestPodIDAndStatus(1)
|
||||
ch := cache.subscribe(podID, cacheTime.Add(time.Second))
|
||||
verifyNotification(t, ch, false)
|
||||
cache.Set(podID, status, nil, cacheTime.Add(time.Second))
|
||||
// The Set operation should've triggered the notification.
|
||||
verifyNotification(t, ch, true)
|
||||
|
||||
podID, _ = getTestPodIDAndStatus(1)
|
||||
|
||||
ch = cache.subscribe(podID, cacheTime.Add(time.Second))
|
||||
verifyNotification(t, ch, false)
|
||||
cache.UpdateTime(cacheTime.Add(time.Second * 2))
|
||||
// The advance of cache timestamp should've triggered the notification.
|
||||
verifyNotification(t, ch, true)
|
||||
}
|
||||
322
vendor/k8s.io/kubernetes/pkg/kubelet/container/helpers_test.go
generated
vendored
322
vendor/k8s.io/kubernetes/pkg/kubelet/container/helpers_test.go
generated
vendored
|
|
@ -1,322 +0,0 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestEnvVarsToMap(t *testing.T) {
|
||||
vars := []EnvVar{
|
||||
{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
},
|
||||
{
|
||||
Name: "zoo",
|
||||
Value: "baz",
|
||||
},
|
||||
}
|
||||
|
||||
varMap := EnvVarsToMap(vars)
|
||||
|
||||
if e, a := len(vars), len(varMap); e != a {
|
||||
t.Errorf("Unexpected map length; expected: %d, got %d", e, a)
|
||||
}
|
||||
|
||||
if a := varMap["foo"]; a != "bar" {
|
||||
t.Errorf("Unexpected value of key 'foo': %v", a)
|
||||
}
|
||||
|
||||
if a := varMap["zoo"]; a != "baz" {
|
||||
t.Errorf("Unexpected value of key 'zoo': %v", a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandCommandAndArgs(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
container *v1.Container
|
||||
envs []EnvVar
|
||||
expectedCommand []string
|
||||
expectedArgs []string
|
||||
}{
|
||||
{
|
||||
name: "none",
|
||||
container: &v1.Container{},
|
||||
},
|
||||
{
|
||||
name: "command expanded",
|
||||
container: &v1.Container{
|
||||
Command: []string{"foo", "$(VAR_TEST)", "$(VAR_TEST2)"},
|
||||
},
|
||||
envs: []EnvVar{
|
||||
{
|
||||
Name: "VAR_TEST",
|
||||
Value: "zoo",
|
||||
},
|
||||
{
|
||||
Name: "VAR_TEST2",
|
||||
Value: "boo",
|
||||
},
|
||||
},
|
||||
expectedCommand: []string{"foo", "zoo", "boo"},
|
||||
},
|
||||
{
|
||||
name: "args expanded",
|
||||
container: &v1.Container{
|
||||
Args: []string{"zap", "$(VAR_TEST)", "$(VAR_TEST2)"},
|
||||
},
|
||||
envs: []EnvVar{
|
||||
{
|
||||
Name: "VAR_TEST",
|
||||
Value: "hap",
|
||||
},
|
||||
{
|
||||
Name: "VAR_TEST2",
|
||||
Value: "trap",
|
||||
},
|
||||
},
|
||||
expectedArgs: []string{"zap", "hap", "trap"},
|
||||
},
|
||||
{
|
||||
name: "both expanded",
|
||||
container: &v1.Container{
|
||||
Command: []string{"$(VAR_TEST2)--$(VAR_TEST)", "foo", "$(VAR_TEST3)"},
|
||||
Args: []string{"foo", "$(VAR_TEST)", "$(VAR_TEST2)"},
|
||||
},
|
||||
envs: []EnvVar{
|
||||
{
|
||||
Name: "VAR_TEST",
|
||||
Value: "zoo",
|
||||
},
|
||||
{
|
||||
Name: "VAR_TEST2",
|
||||
Value: "boo",
|
||||
},
|
||||
{
|
||||
Name: "VAR_TEST3",
|
||||
Value: "roo",
|
||||
},
|
||||
},
|
||||
expectedCommand: []string{"boo--zoo", "foo", "roo"},
|
||||
expectedArgs: []string{"foo", "zoo", "boo"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actualCommand, actualArgs := ExpandContainerCommandAndArgs(tc.container, tc.envs)
|
||||
|
||||
if e, a := tc.expectedCommand, actualCommand; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v: unexpected command; expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
|
||||
if e, a := tc.expectedArgs, actualArgs; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v: unexpected args; expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldContainerBeRestarted(t *testing.T) {
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "12345678",
|
||||
Name: "foo",
|
||||
Namespace: "new",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "no-history"},
|
||||
{Name: "alive"},
|
||||
{Name: "succeed"},
|
||||
{Name: "failed"},
|
||||
{Name: "unknown"},
|
||||
},
|
||||
},
|
||||
}
|
||||
podStatus := &PodStatus{
|
||||
ID: pod.UID,
|
||||
Name: pod.Name,
|
||||
Namespace: pod.Namespace,
|
||||
ContainerStatuses: []*ContainerStatus{
|
||||
{
|
||||
Name: "alive",
|
||||
State: ContainerStateRunning,
|
||||
},
|
||||
{
|
||||
Name: "succeed",
|
||||
State: ContainerStateExited,
|
||||
ExitCode: 0,
|
||||
},
|
||||
{
|
||||
Name: "failed",
|
||||
State: ContainerStateExited,
|
||||
ExitCode: 1,
|
||||
},
|
||||
{
|
||||
Name: "alive",
|
||||
State: ContainerStateExited,
|
||||
ExitCode: 2,
|
||||
},
|
||||
{
|
||||
Name: "unknown",
|
||||
State: ContainerStateUnknown,
|
||||
},
|
||||
{
|
||||
Name: "failed",
|
||||
State: ContainerStateExited,
|
||||
ExitCode: 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
policies := []v1.RestartPolicy{
|
||||
v1.RestartPolicyNever,
|
||||
v1.RestartPolicyOnFailure,
|
||||
v1.RestartPolicyAlways,
|
||||
}
|
||||
expected := map[string][]bool{
|
||||
"no-history": {true, true, true},
|
||||
"alive": {false, false, false},
|
||||
"succeed": {false, false, true},
|
||||
"failed": {false, true, true},
|
||||
"unknown": {true, true, true},
|
||||
}
|
||||
for _, c := range pod.Spec.Containers {
|
||||
for i, policy := range policies {
|
||||
pod.Spec.RestartPolicy = policy
|
||||
e := expected[c.Name][i]
|
||||
r := ShouldContainerBeRestarted(&c, pod, podStatus)
|
||||
if r != e {
|
||||
t.Errorf("Restart for container %q with restart policy %q expected %t, got %t",
|
||||
c.Name, policy, e, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasPrivilegedContainer(t *testing.T) {
|
||||
newBoolPtr := func(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
tests := map[string]struct {
|
||||
securityContext *v1.SecurityContext
|
||||
expected bool
|
||||
}{
|
||||
"nil security context": {
|
||||
securityContext: nil,
|
||||
expected: false,
|
||||
},
|
||||
"nil privileged": {
|
||||
securityContext: &v1.SecurityContext{},
|
||||
expected: false,
|
||||
},
|
||||
"false privileged": {
|
||||
securityContext: &v1.SecurityContext{Privileged: newBoolPtr(false)},
|
||||
expected: false,
|
||||
},
|
||||
"true privileged": {
|
||||
securityContext: &v1.SecurityContext{Privileged: newBoolPtr(true)},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
pod := &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{SecurityContext: v.securityContext},
|
||||
},
|
||||
},
|
||||
}
|
||||
actual := HasPrivilegedContainer(pod)
|
||||
if actual != v.expected {
|
||||
t.Errorf("%s expected %t but got %t", k, v.expected, actual)
|
||||
}
|
||||
}
|
||||
// Test init containers as well.
|
||||
for k, v := range tests {
|
||||
pod := &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
InitContainers: []v1.Container{
|
||||
{SecurityContext: v.securityContext},
|
||||
},
|
||||
},
|
||||
}
|
||||
actual := HasPrivilegedContainer(pod)
|
||||
if actual != v.expected {
|
||||
t.Errorf("%s expected %t but got %t", k, v.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakePortMappings(t *testing.T) {
|
||||
port := func(name string, protocol v1.Protocol, containerPort, hostPort int32, ip string) v1.ContainerPort {
|
||||
return v1.ContainerPort{
|
||||
Name: name,
|
||||
Protocol: protocol,
|
||||
ContainerPort: containerPort,
|
||||
HostPort: hostPort,
|
||||
HostIP: ip,
|
||||
}
|
||||
}
|
||||
portMapping := func(name string, protocol v1.Protocol, containerPort, hostPort int, ip string) PortMapping {
|
||||
return PortMapping{
|
||||
Name: name,
|
||||
Protocol: protocol,
|
||||
ContainerPort: containerPort,
|
||||
HostPort: hostPort,
|
||||
HostIP: ip,
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
container *v1.Container
|
||||
expectedPortMappings []PortMapping
|
||||
}{
|
||||
{
|
||||
&v1.Container{
|
||||
Name: "fooContainer",
|
||||
Ports: []v1.ContainerPort{
|
||||
port("", v1.ProtocolTCP, 80, 8080, "127.0.0.1"),
|
||||
port("", v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
|
||||
port("foo", v1.ProtocolUDP, 555, 5555, ""),
|
||||
// Duplicated, should be ignored.
|
||||
port("foo", v1.ProtocolUDP, 888, 8888, ""),
|
||||
// Duplicated, should be ignored.
|
||||
port("", v1.ProtocolTCP, 80, 8888, ""),
|
||||
},
|
||||
},
|
||||
[]PortMapping{
|
||||
portMapping("fooContainer-TCP:80", v1.ProtocolTCP, 80, 8080, "127.0.0.1"),
|
||||
portMapping("fooContainer-TCP:443", v1.ProtocolTCP, 443, 4343, "192.168.0.1"),
|
||||
portMapping("fooContainer-foo", v1.ProtocolUDP, 555, 5555, ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
actual := MakePortMappings(tt.container)
|
||||
assert.Equal(t, tt.expectedPortMappings, actual, "[%d]", i)
|
||||
}
|
||||
}
|
||||
213
vendor/k8s.io/kubernetes/pkg/kubelet/container/ref_test.go
generated
vendored
213
vendor/k8s.io/kubernetes/pkg/kubelet/container/ref_test.go
generated
vendored
|
|
@ -1,213 +0,0 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
)
|
||||
|
||||
func TestFieldPath(t *testing.T) {
|
||||
pod := &v1.Pod{Spec: v1.PodSpec{Containers: []v1.Container{
|
||||
{Name: "foo"},
|
||||
{Name: "bar"},
|
||||
{Name: ""},
|
||||
{Name: "baz"},
|
||||
}}}
|
||||
table := map[string]struct {
|
||||
pod *v1.Pod
|
||||
container *v1.Container
|
||||
path string
|
||||
success bool
|
||||
}{
|
||||
"basic": {pod, &v1.Container{Name: "foo"}, "spec.containers{foo}", true},
|
||||
"basic2": {pod, &v1.Container{Name: "baz"}, "spec.containers{baz}", true},
|
||||
"emptyName": {pod, &v1.Container{Name: ""}, "spec.containers[2]", true},
|
||||
"basicSamePointer": {pod, &pod.Spec.Containers[0], "spec.containers{foo}", true},
|
||||
"missing": {pod, &v1.Container{Name: "qux"}, "", false},
|
||||
}
|
||||
|
||||
for name, item := range table {
|
||||
res, err := fieldPath(item.pod, item.container)
|
||||
if item.success == false {
|
||||
if err == nil {
|
||||
t.Errorf("%v: unexpected non-error", name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if e, a := item.path, res; e != a {
|
||||
t.Errorf("%v: wanted %v, got %v", name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateContainerRef(t *testing.T) {
|
||||
var (
|
||||
okPod = v1.Pod{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Pod",
|
||||
APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "ok",
|
||||
Namespace: "test-ns",
|
||||
UID: "bar",
|
||||
ResourceVersion: "42",
|
||||
SelfLink: "/api/" + legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() + "/pods/foo",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "by-name",
|
||||
},
|
||||
{},
|
||||
},
|
||||
},
|
||||
}
|
||||
noSelfLinkPod = okPod
|
||||
defaultedSelfLinkPod = okPod
|
||||
)
|
||||
noSelfLinkPod.Kind = ""
|
||||
noSelfLinkPod.APIVersion = ""
|
||||
noSelfLinkPod.ObjectMeta.SelfLink = ""
|
||||
defaultedSelfLinkPod.ObjectMeta.SelfLink = "/api/" + legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() + "/pods/ok"
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
pod *v1.Pod
|
||||
container *v1.Container
|
||||
expected *v1.ObjectReference
|
||||
success bool
|
||||
}{
|
||||
{
|
||||
name: "by-name",
|
||||
pod: &okPod,
|
||||
container: &v1.Container{
|
||||
Name: "by-name",
|
||||
},
|
||||
expected: &v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
Name: "ok",
|
||||
Namespace: "test-ns",
|
||||
UID: "bar",
|
||||
ResourceVersion: "42",
|
||||
FieldPath: ".spec.containers{by-name}",
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "no-name",
|
||||
pod: &okPod,
|
||||
container: &v1.Container{},
|
||||
expected: &v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
Name: "ok",
|
||||
Namespace: "test-ns",
|
||||
UID: "bar",
|
||||
ResourceVersion: "42",
|
||||
FieldPath: ".spec.containers[1]",
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "no-selflink",
|
||||
pod: &noSelfLinkPod,
|
||||
container: &v1.Container{},
|
||||
expected: nil,
|
||||
success: false,
|
||||
},
|
||||
{
|
||||
name: "defaulted-selflink",
|
||||
pod: &defaultedSelfLinkPod,
|
||||
container: &v1.Container{
|
||||
Name: "by-name",
|
||||
},
|
||||
expected: &v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
Name: "ok",
|
||||
Namespace: "test-ns",
|
||||
UID: "bar",
|
||||
ResourceVersion: "42",
|
||||
FieldPath: ".spec.containers{by-name}",
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "implicitly-required",
|
||||
pod: &okPod,
|
||||
container: &v1.Container{
|
||||
Name: "net",
|
||||
},
|
||||
expected: &v1.ObjectReference{
|
||||
Kind: "Pod",
|
||||
APIVersion: legacyscheme.Registry.GroupOrDie(v1.GroupName).GroupVersion.String(),
|
||||
Name: "ok",
|
||||
Namespace: "test-ns",
|
||||
UID: "bar",
|
||||
ResourceVersion: "42",
|
||||
FieldPath: "implicitly required container net",
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actual, err := GenerateContainerRef(tc.pod, tc.container)
|
||||
if err != nil {
|
||||
if tc.success {
|
||||
t.Errorf("%v: unexpected error: %v", tc.name, err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !tc.success {
|
||||
t.Errorf("%v: unexpected success", tc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
if e, a := tc.expected.Kind, actual.Kind; e != a {
|
||||
t.Errorf("%v: kind: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.expected.APIVersion, actual.APIVersion; e != a {
|
||||
t.Errorf("%v: apiVersion: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.expected.Name, actual.Name; e != a {
|
||||
t.Errorf("%v: name: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.expected.Namespace, actual.Namespace; e != a {
|
||||
t.Errorf("%v: namespace: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.expected.UID, actual.UID; e != a {
|
||||
t.Errorf("%v: uid: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
if e, a := tc.expected.ResourceVersion, actual.ResourceVersion; e != a {
|
||||
t.Errorf("%v: kind: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
74
vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache_test.go
generated
vendored
74
vendor/k8s.io/kubernetes/pkg/kubelet/container/runtime_cache_test.go
generated
vendored
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
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 container_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
ctest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
)
|
||||
|
||||
func comparePods(t *testing.T, expected []*ctest.FakePod, actual []*Pod) {
|
||||
if len(expected) != len(actual) {
|
||||
t.Errorf("expected %d pods, got %d instead", len(expected), len(actual))
|
||||
}
|
||||
for i := range expected {
|
||||
if !reflect.DeepEqual(expected[i].Pod, actual[i]) {
|
||||
t.Errorf("expected %#v, got %#v", expected[i].Pod, actual[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPods(t *testing.T) {
|
||||
runtime := &ctest.FakeRuntime{}
|
||||
expected := []*ctest.FakePod{{Pod: &Pod{ID: "1111"}}, {Pod: &Pod{ID: "2222"}}, {Pod: &Pod{ID: "3333"}}}
|
||||
runtime.PodList = expected
|
||||
cache := NewTestRuntimeCache(runtime)
|
||||
actual, err := cache.GetPods()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
|
||||
comparePods(t, expected, actual)
|
||||
}
|
||||
|
||||
func TestForceUpdateIfOlder(t *testing.T) {
|
||||
runtime := &ctest.FakeRuntime{}
|
||||
cache := NewTestRuntimeCache(runtime)
|
||||
|
||||
// Cache old pods.
|
||||
oldpods := []*ctest.FakePod{{Pod: &Pod{ID: "1111"}}}
|
||||
runtime.PodList = oldpods
|
||||
cache.UpdateCacheWithLock()
|
||||
|
||||
// Update the runtime to new pods.
|
||||
newpods := []*ctest.FakePod{{Pod: &Pod{ID: "1111"}}, {Pod: &Pod{ID: "2222"}}, {Pod: &Pod{ID: "3333"}}}
|
||||
runtime.PodList = newpods
|
||||
|
||||
// An older timestamp should not force an update.
|
||||
cache.ForceUpdateIfOlder(time.Now().Add(-20 * time.Minute))
|
||||
actual := cache.GetCachedPods()
|
||||
comparePods(t, oldpods, actual)
|
||||
|
||||
// A newer timestamp should force an update.
|
||||
cache.ForceUpdateIfOlder(time.Now().Add(20 * time.Second))
|
||||
actual = cache.GetCachedPods()
|
||||
comparePods(t, newpods, actual)
|
||||
}
|
||||
68
vendor/k8s.io/kubernetes/pkg/kubelet/container/sync_result_test.go
generated
vendored
68
vendor/k8s.io/kubernetes/pkg/kubelet/container/sync_result_test.go
generated
vendored
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
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 container
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPodSyncResult(t *testing.T) {
|
||||
okResults := []*SyncResult{
|
||||
NewSyncResult(StartContainer, "container_0"),
|
||||
NewSyncResult(SetupNetwork, "pod"),
|
||||
}
|
||||
errResults := []*SyncResult{
|
||||
NewSyncResult(KillContainer, "container_1"),
|
||||
NewSyncResult(TeardownNetwork, "pod"),
|
||||
}
|
||||
errResults[0].Fail(errors.New("error_0"), "message_0")
|
||||
errResults[1].Fail(errors.New("error_1"), "message_1")
|
||||
|
||||
// If the PodSyncResult doesn't contain error result, it should not be error
|
||||
result := PodSyncResult{}
|
||||
result.AddSyncResult(okResults...)
|
||||
if result.Error() != nil {
|
||||
t.Errorf("PodSyncResult should not be error: %v", result)
|
||||
}
|
||||
|
||||
// If the PodSyncResult contains error result, it should be error
|
||||
result = PodSyncResult{}
|
||||
result.AddSyncResult(okResults...)
|
||||
result.AddSyncResult(errResults...)
|
||||
if result.Error() == nil {
|
||||
t.Errorf("PodSyncResult should be error: %q", result)
|
||||
}
|
||||
|
||||
// If the PodSyncResult is failed, it should be error
|
||||
result = PodSyncResult{}
|
||||
result.AddSyncResult(okResults...)
|
||||
result.Fail(errors.New("error"))
|
||||
if result.Error() == nil {
|
||||
t.Errorf("PodSyncResult should be error: %q", result)
|
||||
}
|
||||
|
||||
// If the PodSyncResult is added an error PodSyncResult, it should be error
|
||||
errResult := PodSyncResult{}
|
||||
errResult.AddSyncResult(errResults...)
|
||||
result = PodSyncResult{}
|
||||
result.AddSyncResult(okResults...)
|
||||
result.AddPodSyncResult(errResult)
|
||||
if result.Error() == nil {
|
||||
t.Errorf("PodSyncResult should be error: %q", result)
|
||||
}
|
||||
}
|
||||
5
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet.go
generated
vendored
5
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet.go
generated
vendored
|
|
@ -237,6 +237,7 @@ type Dependencies struct {
|
|||
DockerClientConfig *dockershim.ClientConfig
|
||||
EventClient v1core.EventsGetter
|
||||
HeartbeatClient v1core.CoreV1Interface
|
||||
OnHeartbeatFailure func()
|
||||
KubeClient clientset.Interface
|
||||
ExternalKubeClient clientset.Interface
|
||||
Mounter mount.Interface
|
||||
|
|
@ -493,6 +494,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
|||
nodeName: nodeName,
|
||||
kubeClient: kubeDeps.KubeClient,
|
||||
heartbeatClient: kubeDeps.HeartbeatClient,
|
||||
onRepeatedHeartbeatFailure: kubeDeps.OnHeartbeatFailure,
|
||||
rootDirectory: rootDirectory,
|
||||
resyncInterval: kubeCfg.SyncFrequency.Duration,
|
||||
sourcesReady: config.NewSourcesReady(kubeDeps.PodConfig.SeenAllSources),
|
||||
|
|
@ -935,6 +937,9 @@ type Kubelet struct {
|
|||
iptClient utilipt.Interface
|
||||
rootDirectory string
|
||||
|
||||
// onRepeatedHeartbeatFailure is called when a heartbeat operation fails more than once. optional.
|
||||
onRepeatedHeartbeatFailure func()
|
||||
|
||||
// podWorkers handle syncing Pods in response to events.
|
||||
podWorkers PodWorkers
|
||||
|
||||
|
|
|
|||
69
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_getters_test.go
generated
vendored
69
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_getters_test.go
generated
vendored
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 kubelet
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestKubeletDirs(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
||||
defer testKubelet.Cleanup()
|
||||
kubelet := testKubelet.kubelet
|
||||
root := kubelet.rootDirectory
|
||||
|
||||
var exp, got string
|
||||
|
||||
got = kubelet.getPodsDir()
|
||||
exp = filepath.Join(root, "pods")
|
||||
assert.Equal(t, exp, got)
|
||||
|
||||
got = kubelet.getPluginsDir()
|
||||
exp = filepath.Join(root, "plugins")
|
||||
assert.Equal(t, exp, got)
|
||||
|
||||
got = kubelet.getPluginDir("foobar")
|
||||
exp = filepath.Join(root, "plugins/foobar")
|
||||
assert.Equal(t, exp, got)
|
||||
|
||||
got = kubelet.getPodDir("abc123")
|
||||
exp = filepath.Join(root, "pods/abc123")
|
||||
assert.Equal(t, exp, got)
|
||||
|
||||
got = kubelet.getPodVolumesDir("abc123")
|
||||
exp = filepath.Join(root, "pods/abc123/volumes")
|
||||
assert.Equal(t, exp, got)
|
||||
|
||||
got = kubelet.getPodVolumeDir("abc123", "plugin", "foobar")
|
||||
exp = filepath.Join(root, "pods/abc123/volumes/plugin/foobar")
|
||||
assert.Equal(t, exp, got)
|
||||
|
||||
got = kubelet.getPodPluginsDir("abc123")
|
||||
exp = filepath.Join(root, "pods/abc123/plugins")
|
||||
assert.Equal(t, exp, got)
|
||||
|
||||
got = kubelet.getPodPluginDir("abc123", "foobar")
|
||||
exp = filepath.Join(root, "pods/abc123/plugins/foobar")
|
||||
assert.Equal(t, exp, got)
|
||||
|
||||
got = kubelet.getPodContainerDir("abc123", "def456")
|
||||
exp = filepath.Join(root, "pods/abc123/containers/def456")
|
||||
assert.Equal(t, exp, got)
|
||||
}
|
||||
206
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_network_test.go
generated
vendored
206
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_network_test.go
generated
vendored
|
|
@ -1,206 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 kubelet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNetworkHostGetsPodNotFound(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, true)
|
||||
defer testKubelet.Cleanup()
|
||||
nh := networkHost{testKubelet.kubelet}
|
||||
|
||||
actualPod, _ := nh.GetPodByName("", "")
|
||||
if actualPod != nil {
|
||||
t.Fatalf("Was expected nil, received %v instead", actualPod)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkHostGetsKubeClient(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, true)
|
||||
defer testKubelet.Cleanup()
|
||||
nh := networkHost{testKubelet.kubelet}
|
||||
|
||||
if nh.GetKubeClient() != testKubelet.fakeKubeClient {
|
||||
t.Fatalf("NetworkHost client does not match testKubelet's client")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkHostGetsRuntime(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, true)
|
||||
defer testKubelet.Cleanup()
|
||||
nh := networkHost{testKubelet.kubelet}
|
||||
|
||||
if nh.GetRuntime() != testKubelet.fakeRuntime {
|
||||
t.Fatalf("NetworkHost runtime does not match testKubelet's runtime")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkHostSupportsLegacyFeatures(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, true)
|
||||
defer testKubelet.Cleanup()
|
||||
nh := networkHost{testKubelet.kubelet}
|
||||
|
||||
if nh.SupportsLegacyFeatures() == false {
|
||||
t.Fatalf("SupportsLegacyFeatures should not be false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoOpHostGetsName(t *testing.T) {
|
||||
nh := NoOpLegacyHost{}
|
||||
pod, err := nh.GetPodByName("", "")
|
||||
if pod != nil && err != true {
|
||||
t.Fatalf("noOpLegacyHost getpodbyname expected to be nil and true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoOpHostGetsKubeClient(t *testing.T) {
|
||||
nh := NoOpLegacyHost{}
|
||||
if nh.GetKubeClient() != nil {
|
||||
t.Fatalf("noOpLegacyHost client expected to be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoOpHostGetsRuntime(t *testing.T) {
|
||||
nh := NoOpLegacyHost{}
|
||||
if nh.GetRuntime() != nil {
|
||||
t.Fatalf("noOpLegacyHost runtime expected to be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoOpHostSupportsLegacyFeatures(t *testing.T) {
|
||||
nh := NoOpLegacyHost{}
|
||||
if nh.SupportsLegacyFeatures() != false {
|
||||
t.Fatalf("noOpLegacyHost legacy features expected to be false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeIPParam(t *testing.T) {
|
||||
type test struct {
|
||||
nodeIP string
|
||||
success bool
|
||||
testName string
|
||||
}
|
||||
tests := []test{
|
||||
{
|
||||
nodeIP: "",
|
||||
success: false,
|
||||
testName: "IP not set",
|
||||
},
|
||||
{
|
||||
nodeIP: "127.0.0.1",
|
||||
success: false,
|
||||
testName: "IPv4 loopback address",
|
||||
},
|
||||
{
|
||||
nodeIP: "::1",
|
||||
success: false,
|
||||
testName: "IPv6 loopback address",
|
||||
},
|
||||
{
|
||||
nodeIP: "224.0.0.1",
|
||||
success: false,
|
||||
testName: "multicast IPv4 address",
|
||||
},
|
||||
{
|
||||
nodeIP: "ff00::1",
|
||||
success: false,
|
||||
testName: "multicast IPv6 address",
|
||||
},
|
||||
{
|
||||
nodeIP: "169.254.0.1",
|
||||
success: false,
|
||||
testName: "IPv4 link-local unicast address",
|
||||
},
|
||||
{
|
||||
nodeIP: "fe80::0202:b3ff:fe1e:8329",
|
||||
success: false,
|
||||
testName: "IPv6 link-local unicast address",
|
||||
},
|
||||
{
|
||||
nodeIP: "0.0.0.0",
|
||||
success: false,
|
||||
testName: "Unspecified IPv4 address",
|
||||
},
|
||||
{
|
||||
nodeIP: "::",
|
||||
success: false,
|
||||
testName: "Unspecified IPv6 address",
|
||||
},
|
||||
{
|
||||
nodeIP: "1.2.3.4",
|
||||
success: false,
|
||||
testName: "IPv4 address that doesn't belong to host",
|
||||
},
|
||||
}
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
assert.Error(t, err, fmt.Sprintf(
|
||||
"Unable to obtain a list of the node's unicast interface addresses."))
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
if ip.IsLoopback() || ip.IsLinkLocalUnicast() {
|
||||
break
|
||||
}
|
||||
successTest := test{
|
||||
nodeIP: ip.String(),
|
||||
success: true,
|
||||
testName: fmt.Sprintf("Success test case for address %s", ip.String()),
|
||||
}
|
||||
tests = append(tests, successTest)
|
||||
}
|
||||
for _, test := range tests {
|
||||
err := validateNodeIP(net.ParseIP(test.nodeIP))
|
||||
if test.success {
|
||||
assert.NoError(t, err, "test %s", test.testName)
|
||||
} else {
|
||||
assert.Error(t, err, fmt.Sprintf("test %s", test.testName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetIPTablesMark(t *testing.T) {
|
||||
tests := []struct {
|
||||
bit int
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
14,
|
||||
"0x00004000/0x00004000",
|
||||
},
|
||||
{
|
||||
15,
|
||||
"0x00008000/0x00008000",
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
res := getIPTablesMark(tc.bit)
|
||||
assert.Equal(t, tc.expect, res, "input %d", tc.bit)
|
||||
}
|
||||
}
|
||||
3
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_node_status.go
generated
vendored
3
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_node_status.go
generated
vendored
|
|
@ -371,6 +371,9 @@ func (kl *Kubelet) syncNodeStatus() {
|
|||
func (kl *Kubelet) updateNodeStatus() error {
|
||||
for i := 0; i < nodeStatusUpdateRetry; i++ {
|
||||
if err := kl.tryUpdateNodeStatus(i); err != nil {
|
||||
if i > 0 && kl.onRepeatedHeartbeatFailure != nil {
|
||||
kl.onRepeatedHeartbeatFailure()
|
||||
}
|
||||
glog.Errorf("Error updating node status, will retry: %v", err)
|
||||
} else {
|
||||
return nil
|
||||
|
|
|
|||
1418
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_node_status_test.go
generated
vendored
1418
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_node_status_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
26
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_pods.go
generated
vendored
26
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_pods.go
generated
vendored
|
|
@ -110,23 +110,6 @@ func (kl *Kubelet) makeGPUDevices(pod *v1.Pod, container *v1.Container) ([]kubec
|
|||
return devices, nil
|
||||
}
|
||||
|
||||
func makeAbsolutePath(goos, path string) string {
|
||||
if goos != "windows" {
|
||||
return "/" + path
|
||||
}
|
||||
// These are all for windows
|
||||
// If there is a colon, give up.
|
||||
if strings.Contains(path, ":") {
|
||||
return path
|
||||
}
|
||||
// If there is a slash, but no drive, add 'c:'
|
||||
if strings.HasPrefix(path, "/") || strings.HasPrefix(path, "\\") {
|
||||
return "c:" + path
|
||||
}
|
||||
// Otherwise, add 'c:\'
|
||||
return "c:\\" + path
|
||||
}
|
||||
|
||||
// makeBlockVolumes maps the raw block devices specified in the path of the container
|
||||
// Experimental
|
||||
func (kl *Kubelet) makeBlockVolumes(pod *v1.Pod, container *v1.Container, podVolumes kubecontainer.VolumeMap, blkutil volumepathhandler.BlockVolumePathHandler) ([]kubecontainer.DeviceInfo, error) {
|
||||
|
|
@ -239,6 +222,7 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h
|
|||
VolumePath: volumePath,
|
||||
PodDir: podDir,
|
||||
ContainerName: container.Name,
|
||||
ReadOnly: mount.ReadOnly || vol.Mounter.GetAttributes().ReadOnly,
|
||||
})
|
||||
if err != nil {
|
||||
// Don't pass detailed error back to the user because it could give information about host filesystem
|
||||
|
|
@ -255,7 +239,7 @@ func makeMounts(pod *v1.Pod, podDir string, container *v1.Container, hostName, h
|
|||
}
|
||||
}
|
||||
if !filepath.IsAbs(containerPath) {
|
||||
containerPath = makeAbsolutePath(runtime.GOOS, containerPath)
|
||||
containerPath = volumeutil.MakeAbsolutePath(runtime.GOOS, containerPath)
|
||||
}
|
||||
|
||||
propagation, err := translateMountPropagation(mount.MountPropagation)
|
||||
|
|
@ -301,12 +285,14 @@ func translateMountPropagation(mountMode *v1.MountPropagationMode) (runtimeapi.M
|
|||
}
|
||||
switch {
|
||||
case mountMode == nil:
|
||||
// HostToContainer is the default
|
||||
return runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, nil
|
||||
// PRIVATE is the default
|
||||
return runtimeapi.MountPropagation_PROPAGATION_PRIVATE, nil
|
||||
case *mountMode == v1.MountPropagationHostToContainer:
|
||||
return runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, nil
|
||||
case *mountMode == v1.MountPropagationBidirectional:
|
||||
return runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL, nil
|
||||
case *mountMode == v1.MountPropagationNone:
|
||||
return runtimeapi.MountPropagation_PROPAGATION_PRIVATE, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid MountPropagation mode: %q", mountMode)
|
||||
}
|
||||
|
|
|
|||
2551
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_pods_test.go
generated
vendored
2551
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_pods_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
102
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_pods_windows_test.go
generated
vendored
102
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_pods_windows_test.go
generated
vendored
|
|
@ -1,102 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 2017 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 kubelet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/api/core/v1"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
)
|
||||
|
||||
func TestMakeMountsWindows(t *testing.T) {
|
||||
container := v1.Container{
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
MountPath: "c:/etc/hosts",
|
||||
Name: "disk",
|
||||
ReadOnly: false,
|
||||
},
|
||||
{
|
||||
MountPath: "c:/mnt/path3",
|
||||
Name: "disk",
|
||||
ReadOnly: true,
|
||||
},
|
||||
{
|
||||
MountPath: "c:/mnt/path4",
|
||||
Name: "disk4",
|
||||
ReadOnly: false,
|
||||
},
|
||||
{
|
||||
MountPath: "c:/mnt/path5",
|
||||
Name: "disk5",
|
||||
ReadOnly: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
podVolumes := kubecontainer.VolumeMap{
|
||||
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "c:/mnt/disk"}},
|
||||
"disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "c:/mnt/host"}},
|
||||
"disk5": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "c:/var/lib/kubelet/podID/volumes/empty/disk5"}},
|
||||
}
|
||||
|
||||
pod := v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
HostNetwork: true,
|
||||
},
|
||||
}
|
||||
|
||||
fm := &mount.FakeMounter{}
|
||||
mounts, _, _ := makeMounts(&pod, "/pod", &container, "fakepodname", "", "", podVolumes, fm)
|
||||
|
||||
expectedMounts := []kubecontainer.Mount{
|
||||
{
|
||||
Name: "disk",
|
||||
ContainerPath: "c:/etc/hosts",
|
||||
HostPath: "c:/mnt/disk",
|
||||
ReadOnly: false,
|
||||
SELinuxRelabel: false,
|
||||
},
|
||||
{
|
||||
Name: "disk",
|
||||
ContainerPath: "c:/mnt/path3",
|
||||
HostPath: "c:/mnt/disk",
|
||||
ReadOnly: true,
|
||||
SELinuxRelabel: false,
|
||||
},
|
||||
{
|
||||
Name: "disk4",
|
||||
ContainerPath: "c:/mnt/path4",
|
||||
HostPath: "c:/mnt/host",
|
||||
ReadOnly: false,
|
||||
SELinuxRelabel: false,
|
||||
},
|
||||
{
|
||||
Name: "disk5",
|
||||
ContainerPath: "c:/mnt/path5",
|
||||
HostPath: "c:/var/lib/kubelet/podID/volumes/empty/disk5",
|
||||
ReadOnly: false,
|
||||
SELinuxRelabel: false,
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expectedMounts, mounts, "mounts of container %+v", container)
|
||||
}
|
||||
111
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_resources_test.go
generated
vendored
111
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_resources_test.go
generated
vendored
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 kubelet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
|
||||
"k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestPodResourceLimitsDefaulting(t *testing.T) {
|
||||
cpuCores := resource.MustParse("10")
|
||||
memoryCapacity := resource.MustParse("10Gi")
|
||||
tk := newTestKubelet(t, true)
|
||||
defer tk.Cleanup()
|
||||
tk.fakeCadvisor.On("VersionInfo").Return(&cadvisorapi.VersionInfo{}, nil)
|
||||
tk.fakeCadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{
|
||||
NumCores: int(cpuCores.Value()),
|
||||
MemoryCapacity: uint64(memoryCapacity.Value()),
|
||||
}, nil)
|
||||
tk.fakeCadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
|
||||
tk.fakeCadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{}, nil)
|
||||
tk.kubelet.nodeInfo = &testNodeInfo{
|
||||
nodes: []*v1.Node{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: string(tk.kubelet.nodeName),
|
||||
},
|
||||
Status: v1.NodeStatus{
|
||||
Allocatable: v1.ResourceList{
|
||||
v1.ResourceCPU: resource.MustParse("6"),
|
||||
v1.ResourceMemory: resource.MustParse("4Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
cases := []struct {
|
||||
pod *v1.Pod
|
||||
expected *v1.Pod
|
||||
}{
|
||||
{
|
||||
pod: getPod("0", "0"),
|
||||
expected: getPod("6", "4Gi"),
|
||||
},
|
||||
{
|
||||
pod: getPod("1", "0"),
|
||||
expected: getPod("1", "4Gi"),
|
||||
},
|
||||
{
|
||||
pod: getPod("", ""),
|
||||
expected: getPod("6", "4Gi"),
|
||||
},
|
||||
{
|
||||
pod: getPod("0", "1Mi"),
|
||||
expected: getPod("6", "1Mi"),
|
||||
},
|
||||
}
|
||||
as := assert.New(t)
|
||||
for idx, tc := range cases {
|
||||
actual, _, err := tk.kubelet.defaultPodLimitsForDownwardAPI(tc.pod, nil)
|
||||
as.Nil(err, "failed to default pod limits: %v", err)
|
||||
if !apiequality.Semantic.DeepEqual(tc.expected, actual) {
|
||||
as.Fail("test case [%d] failed. Expected: %+v, Got: %+v", idx, tc.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getPod(cpuLimit, memoryLimit string) *v1.Pod {
|
||||
resources := v1.ResourceRequirements{}
|
||||
if cpuLimit != "" || memoryLimit != "" {
|
||||
resources.Limits = make(v1.ResourceList)
|
||||
}
|
||||
if cpuLimit != "" {
|
||||
resources.Limits[v1.ResourceCPU] = resource.MustParse(cpuLimit)
|
||||
}
|
||||
if memoryLimit != "" {
|
||||
resources.Limits[v1.ResourceMemory] = resource.MustParse(memoryLimit)
|
||||
}
|
||||
return &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "foo",
|
||||
Resources: resources,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
2239
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_test.go
generated
vendored
2239
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
470
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_volumes_test.go
generated
vendored
470
vendor/k8s.io/kubernetes/pkg/kubelet/kubelet_volumes_test.go
generated
vendored
|
|
@ -1,470 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 kubelet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||
"k8s.io/kubernetes/pkg/volume/util"
|
||||
)
|
||||
|
||||
func TestListVolumesForPod(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
||||
defer testKubelet.Cleanup()
|
||||
kubelet := testKubelet.kubelet
|
||||
|
||||
pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "vol1",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "vol2",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
stopCh := runVolumeManager(kubelet)
|
||||
defer close(stopCh)
|
||||
|
||||
kubelet.podManager.SetPods([]*v1.Pod{pod})
|
||||
err := kubelet.volumeManager.WaitForAttachAndMount(pod)
|
||||
assert.NoError(t, err)
|
||||
|
||||
podName := util.GetUniquePodName(pod)
|
||||
|
||||
volumesToReturn, volumeExsit := kubelet.ListVolumesForPod(types.UID(podName))
|
||||
assert.True(t, volumeExsit, "expected to find volumes for pod %q", podName)
|
||||
|
||||
outerVolumeSpecName1 := "vol1"
|
||||
assert.NotNil(t, volumesToReturn[outerVolumeSpecName1], "key %s", outerVolumeSpecName1)
|
||||
|
||||
outerVolumeSpecName2 := "vol2"
|
||||
assert.NotNil(t, volumesToReturn[outerVolumeSpecName2], "key %s", outerVolumeSpecName2)
|
||||
|
||||
}
|
||||
|
||||
func TestPodVolumesExist(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
||||
defer testKubelet.Cleanup()
|
||||
kubelet := testKubelet.kubelet
|
||||
|
||||
pods := []*v1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
UID: "pod1uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "vol1",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod2",
|
||||
UID: "pod2uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "vol2",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod3",
|
||||
UID: "pod3uid",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "vol3",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
stopCh := runVolumeManager(kubelet)
|
||||
defer close(stopCh)
|
||||
|
||||
kubelet.podManager.SetPods(pods)
|
||||
for _, pod := range pods {
|
||||
err := kubelet.volumeManager.WaitForAttachAndMount(pod)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
for _, pod := range pods {
|
||||
podVolumesExist := kubelet.podVolumesExist(pod.UID)
|
||||
assert.True(t, podVolumesExist, "pod %q", pod.UID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVolumeAttachAndMountControllerDisabled(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
||||
defer testKubelet.Cleanup()
|
||||
kubelet := testKubelet.kubelet
|
||||
|
||||
pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "vol1",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
stopCh := runVolumeManager(kubelet)
|
||||
defer close(stopCh)
|
||||
|
||||
kubelet.podManager.SetPods([]*v1.Pod{pod})
|
||||
err := kubelet.volumeManager.WaitForAttachAndMount(pod)
|
||||
assert.NoError(t, err)
|
||||
|
||||
podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
|
||||
util.GetUniquePodName(pod))
|
||||
|
||||
expectedPodVolumes := []string{"vol1"}
|
||||
assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
|
||||
for _, name := range expectedPodVolumes {
|
||||
assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
|
||||
}
|
||||
assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
|
||||
assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
|
||||
1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifyAttachCallCount(
|
||||
1 /* expectedAttachCallCount */, testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
|
||||
1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifySetUpCallCount(
|
||||
1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
|
||||
}
|
||||
|
||||
func TestVolumeUnmountAndDetachControllerDisabled(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
|
||||
defer testKubelet.Cleanup()
|
||||
kubelet := testKubelet.kubelet
|
||||
|
||||
pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "vol1",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
stopCh := runVolumeManager(kubelet)
|
||||
defer close(stopCh)
|
||||
|
||||
// Add pod
|
||||
kubelet.podManager.SetPods([]*v1.Pod{pod})
|
||||
|
||||
// Verify volumes attached
|
||||
err := kubelet.volumeManager.WaitForAttachAndMount(pod)
|
||||
assert.NoError(t, err)
|
||||
|
||||
podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
|
||||
util.GetUniquePodName(pod))
|
||||
|
||||
expectedPodVolumes := []string{"vol1"}
|
||||
assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
|
||||
for _, name := range expectedPodVolumes {
|
||||
assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
|
||||
}
|
||||
|
||||
assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
|
||||
assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
|
||||
1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifyAttachCallCount(
|
||||
1 /* expectedAttachCallCount */, testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
|
||||
1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifySetUpCallCount(
|
||||
1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
|
||||
|
||||
// Remove pod
|
||||
kubelet.podManager.SetPods([]*v1.Pod{})
|
||||
|
||||
assert.NoError(t, waitForVolumeUnmount(kubelet.volumeManager, pod))
|
||||
|
||||
// Verify volumes unmounted
|
||||
podVolumes = kubelet.volumeManager.GetMountedVolumesForPod(
|
||||
util.GetUniquePodName(pod))
|
||||
|
||||
assert.Len(t, podVolumes, 0,
|
||||
"Expected volumes to be unmounted and detached. But some volumes are still mounted: %#v", podVolumes)
|
||||
|
||||
assert.NoError(t, volumetest.VerifyTearDownCallCount(
|
||||
1 /* expectedTearDownCallCount */, testKubelet.volumePlugin))
|
||||
|
||||
// Verify volumes detached and no longer reported as in use
|
||||
assert.NoError(t, waitForVolumeDetach(v1.UniqueVolumeName("fake/vol1"), kubelet.volumeManager))
|
||||
assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
|
||||
assert.NoError(t, volumetest.VerifyDetachCallCount(
|
||||
1 /* expectedDetachCallCount */, testKubelet.volumePlugin))
|
||||
}
|
||||
|
||||
func TestVolumeAttachAndMountControllerEnabled(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
|
||||
defer testKubelet.Cleanup()
|
||||
kubelet := testKubelet.kubelet
|
||||
kubeClient := testKubelet.fakeKubeClient
|
||||
kubeClient.AddReactor("get", "nodes",
|
||||
func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname},
|
||||
Status: v1.NodeStatus{
|
||||
VolumesAttached: []v1.AttachedVolume{
|
||||
{
|
||||
Name: "fake/vol1",
|
||||
DevicePath: "fake/path",
|
||||
},
|
||||
}},
|
||||
Spec: v1.NodeSpec{ExternalID: testKubeletHostname},
|
||||
}, nil
|
||||
})
|
||||
kubeClient.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, fmt.Errorf("no reaction implemented for %s", action)
|
||||
})
|
||||
|
||||
pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "vol1",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
stopCh := runVolumeManager(kubelet)
|
||||
defer close(stopCh)
|
||||
|
||||
kubelet.podManager.SetPods([]*v1.Pod{pod})
|
||||
|
||||
// Fake node status update
|
||||
go simulateVolumeInUseUpdate(
|
||||
v1.UniqueVolumeName("fake/vol1"),
|
||||
stopCh,
|
||||
kubelet.volumeManager)
|
||||
|
||||
assert.NoError(t, kubelet.volumeManager.WaitForAttachAndMount(pod))
|
||||
|
||||
podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
|
||||
util.GetUniquePodName(pod))
|
||||
|
||||
expectedPodVolumes := []string{"vol1"}
|
||||
assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
|
||||
for _, name := range expectedPodVolumes {
|
||||
assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
|
||||
}
|
||||
assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
|
||||
assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
|
||||
1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifyZeroAttachCalls(testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
|
||||
1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifySetUpCallCount(
|
||||
1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
|
||||
}
|
||||
|
||||
func TestVolumeUnmountAndDetachControllerEnabled(t *testing.T) {
|
||||
testKubelet := newTestKubelet(t, true /* controllerAttachDetachEnabled */)
|
||||
defer testKubelet.Cleanup()
|
||||
kubelet := testKubelet.kubelet
|
||||
kubeClient := testKubelet.fakeKubeClient
|
||||
kubeClient.AddReactor("get", "nodes",
|
||||
func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: testKubeletHostname},
|
||||
Status: v1.NodeStatus{
|
||||
VolumesAttached: []v1.AttachedVolume{
|
||||
{
|
||||
Name: "fake/vol1",
|
||||
DevicePath: "fake/path",
|
||||
},
|
||||
}},
|
||||
Spec: v1.NodeSpec{ExternalID: testKubeletHostname},
|
||||
}, nil
|
||||
})
|
||||
kubeClient.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, fmt.Errorf("no reaction implemented for %s", action)
|
||||
})
|
||||
|
||||
pod := podWithUIDNameNsSpec("12345678", "foo", "test", v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "vol1",
|
||||
VolumeSource: v1.VolumeSource{
|
||||
GCEPersistentDisk: &v1.GCEPersistentDiskVolumeSource{
|
||||
PDName: "fake-device",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
stopCh := runVolumeManager(kubelet)
|
||||
defer close(stopCh)
|
||||
|
||||
// Add pod
|
||||
kubelet.podManager.SetPods([]*v1.Pod{pod})
|
||||
|
||||
// Fake node status update
|
||||
go simulateVolumeInUseUpdate(
|
||||
v1.UniqueVolumeName("fake/vol1"),
|
||||
stopCh,
|
||||
kubelet.volumeManager)
|
||||
|
||||
// Verify volumes attached
|
||||
assert.NoError(t, kubelet.volumeManager.WaitForAttachAndMount(pod))
|
||||
|
||||
podVolumes := kubelet.volumeManager.GetMountedVolumesForPod(
|
||||
util.GetUniquePodName(pod))
|
||||
|
||||
expectedPodVolumes := []string{"vol1"}
|
||||
assert.Len(t, podVolumes, len(expectedPodVolumes), "Volumes for pod %+v", pod)
|
||||
for _, name := range expectedPodVolumes {
|
||||
assert.Contains(t, podVolumes, name, "Volumes for pod %+v", pod)
|
||||
}
|
||||
|
||||
assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
|
||||
assert.NoError(t, volumetest.VerifyWaitForAttachCallCount(
|
||||
1 /* expectedWaitForAttachCallCount */, testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifyZeroAttachCalls(testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifyMountDeviceCallCount(
|
||||
1 /* expectedMountDeviceCallCount */, testKubelet.volumePlugin))
|
||||
assert.NoError(t, volumetest.VerifySetUpCallCount(
|
||||
1 /* expectedSetUpCallCount */, testKubelet.volumePlugin))
|
||||
|
||||
// Remove pod
|
||||
kubelet.podManager.SetPods([]*v1.Pod{})
|
||||
|
||||
assert.NoError(t, waitForVolumeUnmount(kubelet.volumeManager, pod))
|
||||
|
||||
// Verify volumes unmounted
|
||||
podVolumes = kubelet.volumeManager.GetMountedVolumesForPod(
|
||||
util.GetUniquePodName(pod))
|
||||
|
||||
assert.Len(t, podVolumes, 0,
|
||||
"Expected volumes to be unmounted and detached. But some volumes are still mounted: %#v", podVolumes)
|
||||
|
||||
assert.NoError(t, volumetest.VerifyTearDownCallCount(
|
||||
1 /* expectedTearDownCallCount */, testKubelet.volumePlugin))
|
||||
|
||||
// Verify volumes detached and no longer reported as in use
|
||||
assert.NoError(t, waitForVolumeDetach(v1.UniqueVolumeName("fake/vol1"), kubelet.volumeManager))
|
||||
assert.True(t, testKubelet.volumePlugin.GetNewAttacherCallCount() >= 1, "Expected plugin NewAttacher to be called at least once")
|
||||
assert.NoError(t, volumetest.VerifyZeroDetachCallCount(testKubelet.volumePlugin))
|
||||
}
|
||||
|
||||
type stubVolume struct {
|
||||
path string
|
||||
volume.MetricsNil
|
||||
}
|
||||
|
||||
func (f *stubVolume) GetPath() string {
|
||||
return f.path
|
||||
}
|
||||
|
||||
func (f *stubVolume) GetAttributes() volume.Attributes {
|
||||
return volume.Attributes{}
|
||||
}
|
||||
|
||||
func (f *stubVolume) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *stubVolume) SetUp(fsGroup *int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *stubVolume) SetUpAt(dir string, fsGroup *int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type stubBlockVolume struct {
|
||||
dirPath string
|
||||
volName string
|
||||
}
|
||||
|
||||
func (f *stubBlockVolume) GetGlobalMapPath(spec *volume.Spec) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (f *stubBlockVolume) GetPodDeviceMapPath() (string, string) {
|
||||
return f.dirPath, f.volName
|
||||
}
|
||||
|
||||
func (f *stubBlockVolume) SetUpDevice() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
func (f *stubBlockVolume) TearDownDevice(mapPath string, devicePath string) error {
|
||||
return nil
|
||||
}
|
||||
38
vendor/k8s.io/kubernetes/pkg/kubelet/oom_watcher_test.go
generated
vendored
38
vendor/k8s.io/kubernetes/pkg/kubelet/oom_watcher_test.go
generated
vendored
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
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 kubelet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/client-go/tools/record"
|
||||
cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
|
||||
)
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
mockCadvisor := &cadvisortest.Fake{}
|
||||
node := &v1.ObjectReference{}
|
||||
oomWatcher := NewOOMWatcher(mockCadvisor, fakeRecorder)
|
||||
assert.NoError(t, oomWatcher.Start(node))
|
||||
|
||||
// TODO: Improve this test once cadvisor exports events.EventChannel as an interface
|
||||
// and thereby allow using a mock version of cadvisor.
|
||||
}
|
||||
203
vendor/k8s.io/kubernetes/pkg/kubelet/pod_container_deletor_test.go
generated
vendored
203
vendor/k8s.io/kubernetes/pkg/kubelet/pod_container_deletor_test.go
generated
vendored
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 kubelet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
)
|
||||
|
||||
func TestGetContainersToDeleteInPodWithFilter(t *testing.T) {
|
||||
pod := kubecontainer.PodStatus{
|
||||
ContainerStatuses: []*kubecontainer.ContainerStatus{
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "1"},
|
||||
Name: "foo",
|
||||
CreatedAt: time.Now(),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "2"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(time.Second),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "3"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(2 * time.Second),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "4"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(3 * time.Second),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "5"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(4 * time.Second),
|
||||
State: kubecontainer.ContainerStateRunning,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
containersToKeep int
|
||||
expectedContainersToDelete containerStatusbyCreatedList
|
||||
}{
|
||||
{
|
||||
0,
|
||||
[]*kubecontainer.ContainerStatus{pod.ContainerStatuses[3], pod.ContainerStatuses[2], pod.ContainerStatuses[1]},
|
||||
},
|
||||
{
|
||||
1,
|
||||
[]*kubecontainer.ContainerStatus{pod.ContainerStatuses[2], pod.ContainerStatuses[1]},
|
||||
},
|
||||
{
|
||||
2,
|
||||
[]*kubecontainer.ContainerStatus{pod.ContainerStatuses[1]},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
candidates := getContainersToDeleteInPod("4", &pod, test.containersToKeep)
|
||||
if !reflect.DeepEqual(candidates, test.expectedContainersToDelete) {
|
||||
t.Errorf("expected %v got %v", test.expectedContainersToDelete, candidates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetContainersToDeleteInPod(t *testing.T) {
|
||||
pod := kubecontainer.PodStatus{
|
||||
ContainerStatuses: []*kubecontainer.ContainerStatus{
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "1"},
|
||||
Name: "foo",
|
||||
CreatedAt: time.Now(),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "2"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(time.Second),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "3"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(2 * time.Second),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "4"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(3 * time.Second),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "5"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(4 * time.Second),
|
||||
State: kubecontainer.ContainerStateRunning,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
containersToKeep int
|
||||
expectedContainersToDelete containerStatusbyCreatedList
|
||||
}{
|
||||
{
|
||||
0,
|
||||
[]*kubecontainer.ContainerStatus{pod.ContainerStatuses[3], pod.ContainerStatuses[2], pod.ContainerStatuses[1], pod.ContainerStatuses[0]},
|
||||
},
|
||||
{
|
||||
1,
|
||||
[]*kubecontainer.ContainerStatus{pod.ContainerStatuses[2], pod.ContainerStatuses[1], pod.ContainerStatuses[0]},
|
||||
},
|
||||
{
|
||||
2,
|
||||
[]*kubecontainer.ContainerStatus{pod.ContainerStatuses[1], pod.ContainerStatuses[0]},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
candidates := getContainersToDeleteInPod("", &pod, test.containersToKeep)
|
||||
if !reflect.DeepEqual(candidates, test.expectedContainersToDelete) {
|
||||
t.Errorf("expected %v got %v", test.expectedContainersToDelete, candidates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetContainersToDeleteInPodWithNoMatch(t *testing.T) {
|
||||
pod := kubecontainer.PodStatus{
|
||||
ContainerStatuses: []*kubecontainer.ContainerStatus{
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "1"},
|
||||
Name: "foo",
|
||||
CreatedAt: time.Now(),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "2"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(time.Second),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "3"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(2 * time.Second),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "4"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(3 * time.Second),
|
||||
State: kubecontainer.ContainerStateExited,
|
||||
},
|
||||
{
|
||||
ID: kubecontainer.ContainerID{Type: "test", ID: "5"},
|
||||
Name: "bar",
|
||||
CreatedAt: time.Now().Add(4 * time.Second),
|
||||
State: kubecontainer.ContainerStateRunning,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
filterID string
|
||||
expectedContainersToDelete containerStatusbyCreatedList
|
||||
}{
|
||||
{
|
||||
"abc",
|
||||
[]*kubecontainer.ContainerStatus{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
candidates := getContainersToDeleteInPod(test.filterID, &pod, len(pod.ContainerStatuses))
|
||||
if !reflect.DeepEqual(candidates, test.expectedContainersToDelete) {
|
||||
t.Errorf("expected %v got %v", test.expectedContainersToDelete, candidates)
|
||||
}
|
||||
}
|
||||
}
|
||||
355
vendor/k8s.io/kubernetes/pkg/kubelet/pod_workers_test.go
generated
vendored
355
vendor/k8s.io/kubernetes/pkg/kubelet/pod_workers_test.go
generated
vendored
|
|
@ -1,355 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 kubelet
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/client-go/tools/record"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/queue"
|
||||
)
|
||||
|
||||
// fakePodWorkers runs sync pod function in serial, so we can have
|
||||
// deterministic behaviour in testing.
|
||||
type fakePodWorkers struct {
|
||||
syncPodFn syncPodFnType
|
||||
cache kubecontainer.Cache
|
||||
t TestingInterface
|
||||
}
|
||||
|
||||
func (f *fakePodWorkers) UpdatePod(options *UpdatePodOptions) {
|
||||
status, err := f.cache.Get(options.Pod.UID)
|
||||
if err != nil {
|
||||
f.t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if err := f.syncPodFn(syncPodOptions{
|
||||
mirrorPod: options.MirrorPod,
|
||||
pod: options.Pod,
|
||||
podStatus: status,
|
||||
updateType: options.UpdateType,
|
||||
killPodOptions: options.KillPodOptions,
|
||||
}); err != nil {
|
||||
f.t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakePodWorkers) ForgetNonExistingPodWorkers(desiredPods map[types.UID]empty) {}
|
||||
|
||||
func (f *fakePodWorkers) ForgetWorker(uid types.UID) {}
|
||||
|
||||
type TestingInterface interface {
|
||||
Errorf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
func newPod(uid, name string) *v1.Pod {
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: types.UID(uid),
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// syncPodRecord is a record of a sync pod call
|
||||
type syncPodRecord struct {
|
||||
name string
|
||||
updateType kubetypes.SyncPodType
|
||||
}
|
||||
|
||||
func createPodWorkers() (*podWorkers, map[types.UID][]syncPodRecord) {
|
||||
lock := sync.Mutex{}
|
||||
processed := make(map[types.UID][]syncPodRecord)
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
fakeRuntime := &containertest.FakeRuntime{}
|
||||
fakeCache := containertest.NewFakeCache(fakeRuntime)
|
||||
podWorkers := newPodWorkers(
|
||||
func(options syncPodOptions) error {
|
||||
func() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
pod := options.pod
|
||||
processed[pod.UID] = append(processed[pod.UID], syncPodRecord{
|
||||
name: pod.Name,
|
||||
updateType: options.updateType,
|
||||
})
|
||||
}()
|
||||
return nil
|
||||
},
|
||||
fakeRecorder,
|
||||
queue.NewBasicWorkQueue(&clock.RealClock{}),
|
||||
time.Second,
|
||||
time.Second,
|
||||
fakeCache,
|
||||
)
|
||||
return podWorkers, processed
|
||||
}
|
||||
|
||||
func drainWorkers(podWorkers *podWorkers, numPods int) {
|
||||
for {
|
||||
stillWorking := false
|
||||
podWorkers.podLock.Lock()
|
||||
for i := 0; i < numPods; i++ {
|
||||
if podWorkers.isWorking[types.UID(string(i))] {
|
||||
stillWorking = true
|
||||
}
|
||||
}
|
||||
podWorkers.podLock.Unlock()
|
||||
if !stillWorking {
|
||||
break
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePod(t *testing.T) {
|
||||
podWorkers, processed := createPodWorkers()
|
||||
|
||||
numPods := 20
|
||||
for i := 0; i < numPods; i++ {
|
||||
for j := i; j < numPods; j++ {
|
||||
podWorkers.UpdatePod(&UpdatePodOptions{
|
||||
Pod: newPod(string(j), string(i)),
|
||||
UpdateType: kubetypes.SyncPodCreate,
|
||||
})
|
||||
}
|
||||
}
|
||||
drainWorkers(podWorkers, numPods)
|
||||
|
||||
if len(processed) != numPods {
|
||||
t.Errorf("Not all pods processed: %v", len(processed))
|
||||
return
|
||||
}
|
||||
for i := 0; i < numPods; i++ {
|
||||
uid := types.UID(i)
|
||||
if len(processed[uid]) < 1 || len(processed[uid]) > i+1 {
|
||||
t.Errorf("Pod %v processed %v times", i, len(processed[uid]))
|
||||
continue
|
||||
}
|
||||
|
||||
// PodWorker guarantees the first and the last event will be processed
|
||||
first := 0
|
||||
last := len(processed[uid]) - 1
|
||||
if processed[uid][first].name != string(0) {
|
||||
t.Errorf("Pod %v: incorrect order %v, %v", i, first, processed[uid][first])
|
||||
|
||||
}
|
||||
if processed[uid][last].name != string(i) {
|
||||
t.Errorf("Pod %v: incorrect order %v, %v", i, last, processed[uid][last])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePodDoesNotForgetSyncPodKill(t *testing.T) {
|
||||
podWorkers, processed := createPodWorkers()
|
||||
numPods := 20
|
||||
for i := 0; i < numPods; i++ {
|
||||
pod := newPod(string(i), string(i))
|
||||
podWorkers.UpdatePod(&UpdatePodOptions{
|
||||
Pod: pod,
|
||||
UpdateType: kubetypes.SyncPodCreate,
|
||||
})
|
||||
podWorkers.UpdatePod(&UpdatePodOptions{
|
||||
Pod: pod,
|
||||
UpdateType: kubetypes.SyncPodKill,
|
||||
})
|
||||
podWorkers.UpdatePod(&UpdatePodOptions{
|
||||
Pod: pod,
|
||||
UpdateType: kubetypes.SyncPodUpdate,
|
||||
})
|
||||
}
|
||||
drainWorkers(podWorkers, numPods)
|
||||
if len(processed) != numPods {
|
||||
t.Errorf("Not all pods processed: %v", len(processed))
|
||||
return
|
||||
}
|
||||
for i := 0; i < numPods; i++ {
|
||||
uid := types.UID(i)
|
||||
// each pod should be processed two times (create, kill, but not update)
|
||||
syncPodRecords := processed[uid]
|
||||
if len(syncPodRecords) < 2 {
|
||||
t.Errorf("Pod %v processed %v times, but expected at least 2", i, len(syncPodRecords))
|
||||
continue
|
||||
}
|
||||
if syncPodRecords[0].updateType != kubetypes.SyncPodCreate {
|
||||
t.Errorf("Pod %v event was %v, but expected %v", i, syncPodRecords[0].updateType, kubetypes.SyncPodCreate)
|
||||
}
|
||||
if syncPodRecords[1].updateType != kubetypes.SyncPodKill {
|
||||
t.Errorf("Pod %v event was %v, but expected %v", i, syncPodRecords[1].updateType, kubetypes.SyncPodKill)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestForgetNonExistingPodWorkers(t *testing.T) {
|
||||
podWorkers, _ := createPodWorkers()
|
||||
|
||||
numPods := 20
|
||||
for i := 0; i < numPods; i++ {
|
||||
podWorkers.UpdatePod(&UpdatePodOptions{
|
||||
Pod: newPod(string(i), "name"),
|
||||
UpdateType: kubetypes.SyncPodUpdate,
|
||||
})
|
||||
}
|
||||
drainWorkers(podWorkers, numPods)
|
||||
|
||||
if len(podWorkers.podUpdates) != numPods {
|
||||
t.Errorf("Incorrect number of open channels %v", len(podWorkers.podUpdates))
|
||||
}
|
||||
|
||||
desiredPods := map[types.UID]empty{}
|
||||
desiredPods[types.UID(2)] = empty{}
|
||||
desiredPods[types.UID(14)] = empty{}
|
||||
podWorkers.ForgetNonExistingPodWorkers(desiredPods)
|
||||
if len(podWorkers.podUpdates) != 2 {
|
||||
t.Errorf("Incorrect number of open channels %v", len(podWorkers.podUpdates))
|
||||
}
|
||||
if _, exists := podWorkers.podUpdates[types.UID(2)]; !exists {
|
||||
t.Errorf("No updates channel for pod 2")
|
||||
}
|
||||
if _, exists := podWorkers.podUpdates[types.UID(14)]; !exists {
|
||||
t.Errorf("No updates channel for pod 14")
|
||||
}
|
||||
|
||||
podWorkers.ForgetNonExistingPodWorkers(map[types.UID]empty{})
|
||||
if len(podWorkers.podUpdates) != 0 {
|
||||
t.Errorf("Incorrect number of open channels %v", len(podWorkers.podUpdates))
|
||||
}
|
||||
}
|
||||
|
||||
type simpleFakeKubelet struct {
|
||||
pod *v1.Pod
|
||||
mirrorPod *v1.Pod
|
||||
podStatus *kubecontainer.PodStatus
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func (kl *simpleFakeKubelet) syncPod(options syncPodOptions) error {
|
||||
kl.pod, kl.mirrorPod, kl.podStatus = options.pod, options.mirrorPod, options.podStatus
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kl *simpleFakeKubelet) syncPodWithWaitGroup(options syncPodOptions) error {
|
||||
kl.pod, kl.mirrorPod, kl.podStatus = options.pod, options.mirrorPod, options.podStatus
|
||||
kl.wg.Done()
|
||||
return nil
|
||||
}
|
||||
|
||||
// byContainerName sort the containers in a running pod by their names.
|
||||
type byContainerName kubecontainer.Pod
|
||||
|
||||
func (b byContainerName) Len() int { return len(b.Containers) }
|
||||
|
||||
func (b byContainerName) Swap(i, j int) {
|
||||
b.Containers[i], b.Containers[j] = b.Containers[j], b.Containers[i]
|
||||
}
|
||||
|
||||
func (b byContainerName) Less(i, j int) bool {
|
||||
return b.Containers[i].Name < b.Containers[j].Name
|
||||
}
|
||||
|
||||
// TestFakePodWorkers verifies that the fakePodWorkers behaves the same way as the real podWorkers
|
||||
// for their invocation of the syncPodFn.
|
||||
func TestFakePodWorkers(t *testing.T) {
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
fakeRuntime := &containertest.FakeRuntime{}
|
||||
fakeCache := containertest.NewFakeCache(fakeRuntime)
|
||||
|
||||
kubeletForRealWorkers := &simpleFakeKubelet{}
|
||||
kubeletForFakeWorkers := &simpleFakeKubelet{}
|
||||
|
||||
realPodWorkers := newPodWorkers(kubeletForRealWorkers.syncPodWithWaitGroup, fakeRecorder, queue.NewBasicWorkQueue(&clock.RealClock{}), time.Second, time.Second, fakeCache)
|
||||
fakePodWorkers := &fakePodWorkers{kubeletForFakeWorkers.syncPod, fakeCache, t}
|
||||
|
||||
tests := []struct {
|
||||
pod *v1.Pod
|
||||
mirrorPod *v1.Pod
|
||||
}{
|
||||
{
|
||||
&v1.Pod{},
|
||||
&v1.Pod{},
|
||||
},
|
||||
{
|
||||
podWithUIDNameNs("12345678", "foo", "new"),
|
||||
podWithUIDNameNs("12345678", "fooMirror", "new"),
|
||||
},
|
||||
{
|
||||
podWithUIDNameNs("98765", "bar", "new"),
|
||||
podWithUIDNameNs("98765", "barMirror", "new"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
kubeletForRealWorkers.wg.Add(1)
|
||||
realPodWorkers.UpdatePod(&UpdatePodOptions{
|
||||
Pod: tt.pod,
|
||||
MirrorPod: tt.mirrorPod,
|
||||
UpdateType: kubetypes.SyncPodUpdate,
|
||||
})
|
||||
fakePodWorkers.UpdatePod(&UpdatePodOptions{
|
||||
Pod: tt.pod,
|
||||
MirrorPod: tt.mirrorPod,
|
||||
UpdateType: kubetypes.SyncPodUpdate,
|
||||
})
|
||||
|
||||
kubeletForRealWorkers.wg.Wait()
|
||||
|
||||
if !reflect.DeepEqual(kubeletForRealWorkers.pod, kubeletForFakeWorkers.pod) {
|
||||
t.Errorf("%d: Expected: %#v, Actual: %#v", i, kubeletForRealWorkers.pod, kubeletForFakeWorkers.pod)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(kubeletForRealWorkers.mirrorPod, kubeletForFakeWorkers.mirrorPod) {
|
||||
t.Errorf("%d: Expected: %#v, Actual: %#v", i, kubeletForRealWorkers.mirrorPod, kubeletForFakeWorkers.mirrorPod)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(kubeletForRealWorkers.podStatus, kubeletForFakeWorkers.podStatus) {
|
||||
t.Errorf("%d: Expected: %#v, Actual: %#v", i, kubeletForRealWorkers.podStatus, kubeletForFakeWorkers.podStatus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestKillPodNowFunc tests the blocking kill pod function works with pod workers as expected.
|
||||
func TestKillPodNowFunc(t *testing.T) {
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
podWorkers, processed := createPodWorkers()
|
||||
killPodFunc := killPodNow(podWorkers, fakeRecorder)
|
||||
pod := newPod("test", "test")
|
||||
gracePeriodOverride := int64(0)
|
||||
err := killPodFunc(pod, v1.PodStatus{Phase: v1.PodFailed, Reason: "reason", Message: "message"}, &gracePeriodOverride)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(processed) != 1 {
|
||||
t.Errorf("len(processed) expected: %v, actual: %v", 1, len(processed))
|
||||
return
|
||||
}
|
||||
syncPodRecords := processed[pod.UID]
|
||||
if len(syncPodRecords) != 1 {
|
||||
t.Errorf("Pod processed %v times, but expected %v", len(syncPodRecords), 1)
|
||||
}
|
||||
if syncPodRecords[0].updateType != kubetypes.SyncPodKill {
|
||||
t.Errorf("Pod update type was %v, but expected %v", syncPodRecords[0].updateType, kubetypes.SyncPodKill)
|
||||
}
|
||||
}
|
||||
69
vendor/k8s.io/kubernetes/pkg/kubelet/reason_cache_test.go
generated
vendored
69
vendor/k8s.io/kubernetes/pkg/kubelet/reason_cache_test.go
generated
vendored
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 kubelet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
)
|
||||
|
||||
func TestReasonCache(t *testing.T) {
|
||||
// Create test sync result
|
||||
syncResult := kubecontainer.PodSyncResult{}
|
||||
results := []*kubecontainer.SyncResult{
|
||||
// reason cache should be set for SyncResult with StartContainer action and error
|
||||
kubecontainer.NewSyncResult(kubecontainer.StartContainer, "container_1"),
|
||||
// reason cache should not be set for SyncResult with StartContainer action but without error
|
||||
kubecontainer.NewSyncResult(kubecontainer.StartContainer, "container_2"),
|
||||
// reason cache should not be set for SyncResult with other actions
|
||||
kubecontainer.NewSyncResult(kubecontainer.KillContainer, "container_3"),
|
||||
}
|
||||
results[0].Fail(kubecontainer.ErrRunContainer, "message_1")
|
||||
results[2].Fail(kubecontainer.ErrKillContainer, "message_3")
|
||||
syncResult.AddSyncResult(results...)
|
||||
uid := types.UID("pod_1")
|
||||
|
||||
reasonCache := NewReasonCache()
|
||||
reasonCache.Update(uid, syncResult)
|
||||
assertReasonInfo(t, reasonCache, uid, results[0], true)
|
||||
assertReasonInfo(t, reasonCache, uid, results[1], false)
|
||||
assertReasonInfo(t, reasonCache, uid, results[2], false)
|
||||
|
||||
reasonCache.Remove(uid, results[0].Target.(string))
|
||||
assertReasonInfo(t, reasonCache, uid, results[0], false)
|
||||
}
|
||||
|
||||
func assertReasonInfo(t *testing.T, cache *ReasonCache, uid types.UID, result *kubecontainer.SyncResult, found bool) {
|
||||
name := result.Target.(string)
|
||||
actualReason, ok := cache.Get(uid, name)
|
||||
if ok && !found {
|
||||
t.Fatalf("unexpected cache hit: %v, %q", actualReason.Err, actualReason.Message)
|
||||
}
|
||||
if !ok && found {
|
||||
t.Fatalf("corresponding reason info not found")
|
||||
}
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
reason := result.Error
|
||||
message := result.Message
|
||||
if actualReason.Err != reason || actualReason.Message != message {
|
||||
t.Errorf("expected %v %q, got %v %q", reason, message, actualReason.Err, actualReason.Message)
|
||||
}
|
||||
}
|
||||
175
vendor/k8s.io/kubernetes/pkg/kubelet/runonce_test.go
generated
vendored
175
vendor/k8s.io/kubernetes/pkg/kubelet/runonce_test.go
generated
vendored
|
|
@ -1,175 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 kubelet
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cadvisorapi "github.com/google/cadvisor/info/v1"
|
||||
cadvisorapiv2 "github.com/google/cadvisor/info/v2"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/tools/record"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig"
|
||||
cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
"k8s.io/kubernetes/pkg/kubelet/configmap"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
containertest "k8s.io/kubernetes/pkg/kubelet/container/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/eviction"
|
||||
"k8s.io/kubernetes/pkg/kubelet/network"
|
||||
nettest "k8s.io/kubernetes/pkg/kubelet/network/testing"
|
||||
kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
|
||||
podtest "k8s.io/kubernetes/pkg/kubelet/pod/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/secret"
|
||||
"k8s.io/kubernetes/pkg/kubelet/server/stats"
|
||||
"k8s.io/kubernetes/pkg/kubelet/status"
|
||||
statustest "k8s.io/kubernetes/pkg/kubelet/status/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/volumemanager"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||
)
|
||||
|
||||
func TestRunOnce(t *testing.T) {
|
||||
cadvisor := &cadvisortest.Mock{}
|
||||
cadvisor.On("MachineInfo").Return(&cadvisorapi.MachineInfo{}, nil)
|
||||
cadvisor.On("ImagesFsInfo").Return(cadvisorapiv2.FsInfo{
|
||||
Usage: 400,
|
||||
Capacity: 1000,
|
||||
Available: 600,
|
||||
}, nil)
|
||||
cadvisor.On("RootFsInfo").Return(cadvisorapiv2.FsInfo{
|
||||
Usage: 9,
|
||||
Capacity: 10,
|
||||
}, nil)
|
||||
fakeSecretManager := secret.NewFakeManager()
|
||||
fakeConfigMapManager := configmap.NewFakeManager()
|
||||
podManager := kubepod.NewBasicPodManager(
|
||||
podtest.NewFakeMirrorClient(), fakeSecretManager, fakeConfigMapManager)
|
||||
fakeRuntime := &containertest.FakeRuntime{}
|
||||
basePath, err := utiltesting.MkTmpdir("kubelet")
|
||||
if err != nil {
|
||||
t.Fatalf("can't make a temp rootdir %v", err)
|
||||
}
|
||||
defer os.RemoveAll(basePath)
|
||||
kb := &Kubelet{
|
||||
rootDirectory: basePath,
|
||||
recorder: &record.FakeRecorder{},
|
||||
cadvisor: cadvisor,
|
||||
nodeInfo: testNodeInfo{},
|
||||
statusManager: status.NewManager(nil, podManager, &statustest.FakePodDeletionSafetyProvider{}),
|
||||
podManager: podManager,
|
||||
os: &containertest.FakeOS{},
|
||||
containerRuntime: fakeRuntime,
|
||||
reasonCache: NewReasonCache(),
|
||||
clock: clock.RealClock{},
|
||||
kubeClient: &fake.Clientset{},
|
||||
hostname: testKubeletHostname,
|
||||
nodeName: testKubeletHostname,
|
||||
runtimeState: newRuntimeState(time.Second),
|
||||
}
|
||||
kb.containerManager = cm.NewStubContainerManager()
|
||||
|
||||
plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil}
|
||||
kb.volumePluginMgr, err =
|
||||
NewInitializedVolumePluginMgr(kb, fakeSecretManager, fakeConfigMapManager, []volume.VolumePlugin{plug}, nil /* prober */)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to initialize VolumePluginMgr: %v", err)
|
||||
}
|
||||
kb.volumeManager = volumemanager.NewVolumeManager(
|
||||
true,
|
||||
kb.nodeName,
|
||||
kb.podManager,
|
||||
kb.statusManager,
|
||||
kb.kubeClient,
|
||||
kb.volumePluginMgr,
|
||||
fakeRuntime,
|
||||
kb.mounter,
|
||||
kb.getPodsDir(),
|
||||
kb.recorder,
|
||||
false, /* experimentalCheckNodeCapabilitiesBeforeMount */
|
||||
false /* keepTerminatedPodVolumes */)
|
||||
|
||||
kb.networkPlugin, _ = network.InitNetworkPlugin([]network.NetworkPlugin{}, "", nettest.NewFakeHost(nil), kubeletconfig.HairpinNone, "", network.UseDefaultMTU)
|
||||
// TODO: Factor out "StatsProvider" from Kubelet so we don't have a cyclic dependency
|
||||
volumeStatsAggPeriod := time.Second * 10
|
||||
kb.resourceAnalyzer = stats.NewResourceAnalyzer(kb, volumeStatsAggPeriod)
|
||||
nodeRef := &v1.ObjectReference{
|
||||
Kind: "Node",
|
||||
Name: string(kb.nodeName),
|
||||
UID: types.UID(kb.nodeName),
|
||||
Namespace: "",
|
||||
}
|
||||
fakeKillPodFunc := func(pod *v1.Pod, podStatus v1.PodStatus, gracePeriodOverride *int64) error {
|
||||
return nil
|
||||
}
|
||||
evictionManager, evictionAdmitHandler := eviction.NewManager(kb.resourceAnalyzer, eviction.Config{}, fakeKillPodFunc, nil, nil, kb.recorder, nodeRef, kb.clock)
|
||||
|
||||
kb.evictionManager = evictionManager
|
||||
kb.admitHandlers.AddPodAdmitHandler(evictionAdmitHandler)
|
||||
kb.mounter = &mount.FakeMounter{}
|
||||
if err := kb.setupDataDirs(); err != nil {
|
||||
t.Errorf("Failed to init data dirs: %v", err)
|
||||
}
|
||||
|
||||
pods := []*v1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "12345678",
|
||||
Name: "foo",
|
||||
Namespace: "new",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
podManager.SetPods(pods)
|
||||
// The original test here is totally meaningless, because fakeruntime will always return an empty podStatus. While
|
||||
// the original logic of isPodRunning happens to return true when podstatus is empty, so the test can always pass.
|
||||
// Now the logic in isPodRunning is changed, to let the test pass, we set the podstatus directly in fake runtime.
|
||||
// This is also a meaningless test, because the isPodRunning will also always return true after setting this. However,
|
||||
// because runonce is never used in kubernetes now, we should deprioritize the cleanup work.
|
||||
// TODO(random-liu) Fix the test, make it meaningful.
|
||||
fakeRuntime.PodStatus = kubecontainer.PodStatus{
|
||||
ContainerStatuses: []*kubecontainer.ContainerStatus{
|
||||
{
|
||||
Name: "bar",
|
||||
State: kubecontainer.ContainerStateRunning,
|
||||
},
|
||||
},
|
||||
}
|
||||
results, err := kb.runOnce(pods, time.Millisecond)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if results[0].Err != nil {
|
||||
t.Errorf("unexpected run pod error: %v", results[0].Err)
|
||||
}
|
||||
if results[0].Pod.Name != "foo" {
|
||||
t.Errorf("unexpected pod: %q", results[0].Pod.Name)
|
||||
}
|
||||
}
|
||||
119
vendor/k8s.io/kubernetes/pkg/kubelet/types/labels_test.go
generated
vendored
119
vendor/k8s.io/kubernetes/pkg/kubelet/types/labels_test.go
generated
vendored
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetContainerName(t *testing.T) {
|
||||
var cases = []struct {
|
||||
labels map[string]string
|
||||
containerName string
|
||||
}{
|
||||
{
|
||||
labels: map[string]string{
|
||||
"io.kubernetes.container.name": "c1",
|
||||
},
|
||||
containerName: "c1",
|
||||
},
|
||||
{
|
||||
labels: map[string]string{
|
||||
"io.kubernetes.container.name": "c2",
|
||||
},
|
||||
containerName: "c2",
|
||||
},
|
||||
}
|
||||
for _, data := range cases {
|
||||
containerName := GetContainerName(data.labels)
|
||||
assert.Equal(t, data.containerName, containerName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodName(t *testing.T) {
|
||||
var cases = []struct {
|
||||
labels map[string]string
|
||||
podName string
|
||||
}{
|
||||
{
|
||||
labels: map[string]string{
|
||||
"io.kubernetes.pod.name": "p1",
|
||||
},
|
||||
podName: "p1",
|
||||
},
|
||||
{
|
||||
labels: map[string]string{
|
||||
"io.kubernetes.pod.name": "p2",
|
||||
},
|
||||
podName: "p2",
|
||||
},
|
||||
}
|
||||
for _, data := range cases {
|
||||
podName := GetPodName(data.labels)
|
||||
assert.Equal(t, data.podName, podName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodUID(t *testing.T) {
|
||||
var cases = []struct {
|
||||
labels map[string]string
|
||||
podUID string
|
||||
}{
|
||||
{
|
||||
labels: map[string]string{
|
||||
"io.kubernetes.pod.uid": "uid1",
|
||||
},
|
||||
podUID: "uid1",
|
||||
},
|
||||
{
|
||||
labels: map[string]string{
|
||||
"io.kubernetes.pod.uid": "uid2",
|
||||
},
|
||||
podUID: "uid2",
|
||||
},
|
||||
}
|
||||
for _, data := range cases {
|
||||
podUID := GetPodUID(data.labels)
|
||||
assert.Equal(t, data.podUID, podUID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodNamespace(t *testing.T) {
|
||||
var cases = []struct {
|
||||
labels map[string]string
|
||||
podNamespace string
|
||||
}{
|
||||
{
|
||||
labels: map[string]string{
|
||||
"io.kubernetes.pod.namespace": "ns1",
|
||||
},
|
||||
podNamespace: "ns1",
|
||||
},
|
||||
{
|
||||
labels: map[string]string{
|
||||
"io.kubernetes.pod.namespace": "ns2",
|
||||
},
|
||||
podNamespace: "ns2",
|
||||
},
|
||||
}
|
||||
for _, data := range cases {
|
||||
podNamespace := GetPodNamespace(data.labels)
|
||||
assert.Equal(t, data.podNamespace, podNamespace)
|
||||
}
|
||||
}
|
||||
178
vendor/k8s.io/kubernetes/pkg/kubelet/types/pod_update_test.go
generated
vendored
178
vendor/k8s.io/kubernetes/pkg/kubelet/types/pod_update_test.go
generated
vendored
|
|
@ -1,178 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestGetValidatedSources(t *testing.T) {
|
||||
// Empty.
|
||||
sources, err := GetValidatedSources([]string{""})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sources, 0)
|
||||
|
||||
// Success.
|
||||
sources, err = GetValidatedSources([]string{FileSource, ApiserverSource})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sources, 2)
|
||||
|
||||
// All.
|
||||
sources, err = GetValidatedSources([]string{AllSource})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, sources, 3)
|
||||
|
||||
// Unknown source.
|
||||
sources, err = GetValidatedSources([]string{"taco"})
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetPodSource(t *testing.T) {
|
||||
cases := []struct {
|
||||
pod v1.Pod
|
||||
expected string
|
||||
errExpected bool
|
||||
}{
|
||||
{
|
||||
pod: v1.Pod{},
|
||||
expected: "",
|
||||
errExpected: true,
|
||||
},
|
||||
{
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
"kubernetes.io/config.source": "host-ipc-sources",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "host-ipc-sources",
|
||||
errExpected: false,
|
||||
},
|
||||
}
|
||||
for i, data := range cases {
|
||||
source, err := GetPodSource(&data.pod)
|
||||
if data.errExpected {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, data.expected, source, "test[%d]", i)
|
||||
t.Logf("Test case [%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
cases := []struct {
|
||||
sp SyncPodType
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
sp: SyncPodCreate,
|
||||
expected: "create",
|
||||
},
|
||||
{
|
||||
sp: SyncPodUpdate,
|
||||
expected: "update",
|
||||
},
|
||||
{
|
||||
sp: SyncPodSync,
|
||||
expected: "sync",
|
||||
},
|
||||
{
|
||||
sp: SyncPodKill,
|
||||
expected: "kill",
|
||||
},
|
||||
{
|
||||
sp: 50,
|
||||
expected: "unknown",
|
||||
},
|
||||
}
|
||||
for i, data := range cases {
|
||||
syncPodString := data.sp.String()
|
||||
assert.Equal(t, data.expected, syncPodString, "test[%d]", i)
|
||||
t.Logf("Test case [%d]", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCriticalPod(t *testing.T) {
|
||||
cases := []struct {
|
||||
pod v1.Pod
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
Namespace: "ns",
|
||||
Annotations: map[string]string{
|
||||
"scheduler.alpha.kubernetes.io/critical-pod": "",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod2",
|
||||
Namespace: "ns",
|
||||
Annotations: map[string]string{
|
||||
"scheduler.alpha.kubernetes.io/critical-pod": "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod3",
|
||||
Namespace: "kube-system",
|
||||
Annotations: map[string]string{
|
||||
"scheduler.alpha.kubernetes.io/critical-pod": "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod4",
|
||||
Namespace: "kube-system",
|
||||
Annotations: map[string]string{
|
||||
"scheduler.alpha.kubernetes.io/critical-pod": "",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for i, data := range cases {
|
||||
actual := IsCriticalPod(&data.pod)
|
||||
if actual != data.expected {
|
||||
t.Errorf("IsCriticalPod result wrong:\nexpected: %v\nactual: %v for test[%d] with Annotations: %v",
|
||||
data.expected, actual, i, data.pod.Annotations)
|
||||
}
|
||||
}
|
||||
}
|
||||
138
vendor/k8s.io/kubernetes/pkg/kubelet/types/types_test.go
generated
vendored
138
vendor/k8s.io/kubernetes/pkg/kubelet/types/types_test.go
generated
vendored
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 types
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestConvertToTimestamp(t *testing.T) {
|
||||
timestamp := "2017-02-17T15:34:49.830882016+08:00"
|
||||
convertedTimeStamp := ConvertToTimestamp(timestamp).GetString()
|
||||
assert.Equal(t, timestamp, convertedTimeStamp)
|
||||
}
|
||||
|
||||
func TestLen(t *testing.T) {
|
||||
var cases = []struct {
|
||||
statuses SortedContainerStatuses
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
statuses: SortedContainerStatuses{{Name: "first"}},
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
statuses: SortedContainerStatuses{{Name: "first"}, {Name: "second"}},
|
||||
expected: 2,
|
||||
},
|
||||
}
|
||||
for _, data := range cases {
|
||||
assert.Equal(t, data.expected, data.statuses.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwap(t *testing.T) {
|
||||
var cases = []struct {
|
||||
statuses SortedContainerStatuses
|
||||
expected SortedContainerStatuses
|
||||
}{
|
||||
{
|
||||
statuses: SortedContainerStatuses{{Name: "first"}, {Name: "second"}},
|
||||
expected: SortedContainerStatuses{{Name: "second"}, {Name: "first"}},
|
||||
},
|
||||
}
|
||||
for _, data := range cases {
|
||||
data.statuses.Swap(0, 1)
|
||||
if !reflect.DeepEqual(data.statuses, data.expected) {
|
||||
t.Errorf(
|
||||
"failed Swap:\n\texpected: %v\n\t actual: %v",
|
||||
data.expected,
|
||||
data.statuses,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLess(t *testing.T) {
|
||||
var cases = []struct {
|
||||
statuses SortedContainerStatuses
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
statuses: SortedContainerStatuses{{Name: "first"}, {Name: "second"}},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
statuses: SortedContainerStatuses{{Name: "second"}, {Name: "first"}},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
for _, data := range cases {
|
||||
actual := data.statuses.Less(0, 1)
|
||||
if actual != data.expected {
|
||||
t.Errorf(
|
||||
"failed Less:\n\texpected: %t\n\t actual: %t",
|
||||
data.expected,
|
||||
actual,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSortInitContainerStatuses(t *testing.T) {
|
||||
pod := v1.Pod{
|
||||
Spec: v1.PodSpec{},
|
||||
}
|
||||
var cases = []struct {
|
||||
containers []v1.Container
|
||||
statuses []v1.ContainerStatus
|
||||
sortedStatuses []v1.ContainerStatus
|
||||
}{
|
||||
{
|
||||
containers: []v1.Container{{Name: "first"}, {Name: "second"}, {Name: "third"}, {Name: "fourth"}},
|
||||
statuses: []v1.ContainerStatus{{Name: "first"}, {Name: "second"}, {Name: "third"}, {Name: "fourth"}},
|
||||
sortedStatuses: []v1.ContainerStatus{{Name: "first"}, {Name: "second"}, {Name: "third"}, {Name: "fourth"}},
|
||||
},
|
||||
{
|
||||
containers: []v1.Container{{Name: "first"}, {Name: "second"}, {Name: "third"}, {Name: "fourth"}},
|
||||
statuses: []v1.ContainerStatus{{Name: "second"}, {Name: "first"}, {Name: "fourth"}, {Name: "third"}},
|
||||
sortedStatuses: []v1.ContainerStatus{{Name: "first"}, {Name: "second"}, {Name: "third"}, {Name: "fourth"}},
|
||||
},
|
||||
{
|
||||
containers: []v1.Container{{Name: "first"}, {Name: "second"}, {Name: "third"}, {Name: "fourth"}},
|
||||
statuses: []v1.ContainerStatus{{Name: "fourth"}, {Name: "first"}},
|
||||
sortedStatuses: []v1.ContainerStatus{{Name: "first"}, {Name: "fourth"}},
|
||||
},
|
||||
{
|
||||
containers: []v1.Container{{Name: "first"}, {Name: "second"}, {Name: "third"}, {Name: "fourth"}},
|
||||
statuses: []v1.ContainerStatus{{Name: "first"}, {Name: "third"}},
|
||||
sortedStatuses: []v1.ContainerStatus{{Name: "first"}, {Name: "third"}},
|
||||
},
|
||||
}
|
||||
for _, data := range cases {
|
||||
pod.Spec.InitContainers = data.containers
|
||||
SortInitContainerStatuses(&pod, data.statuses)
|
||||
if !reflect.DeepEqual(data.statuses, data.sortedStatuses) {
|
||||
t.Errorf("SortInitContainerStatuses result wrong:\nContainers order: %v\nExpected order: %v\nReturne order: %v",
|
||||
data.containers, data.sortedStatuses, data.statuses)
|
||||
}
|
||||
}
|
||||
}
|
||||
35
vendor/k8s.io/kubernetes/pkg/kubelet/util/format/resources_test.go
generated
vendored
35
vendor/k8s.io/kubernetes/pkg/kubelet/util/format/resources_test.go
generated
vendored
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 format
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
func TestResourceList(t *testing.T) {
|
||||
resourceList := v1.ResourceList{}
|
||||
resourceList[v1.ResourceCPU] = resource.MustParse("100m")
|
||||
resourceList[v1.ResourceMemory] = resource.MustParse("5Gi")
|
||||
actual := ResourceList(resourceList)
|
||||
expected := "cpu=100m,memory=5Gi"
|
||||
if actual != expected {
|
||||
t.Errorf("Unexpected result, actual: %v, expected: %v", actual, expected)
|
||||
}
|
||||
}
|
||||
222
vendor/k8s.io/kubernetes/pkg/kubelet/util/sliceutils/sliceutils_test.go
generated
vendored
222
vendor/k8s.io/kubernetes/pkg/kubelet/util/sliceutils/sliceutils_test.go
generated
vendored
|
|
@ -1,222 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 sliceutils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestStringInSlice(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
s string
|
||||
list []string
|
||||
er bool
|
||||
}{
|
||||
{"first", []string{"first", "second"}, true},
|
||||
{"FIRST", []string{"first", "second"}, false},
|
||||
{"third", []string{"first", "second"}, false},
|
||||
{"first", nil, false},
|
||||
|
||||
{"", []string{"first", "second"}, false},
|
||||
{"", []string{"first", "second", ""}, true},
|
||||
{"", nil, false},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := StringInSlice(fooTest.s, fooTest.list)
|
||||
if r != fooTest.er {
|
||||
t.Errorf("returned %t but expected %t for s=%s & list=%s", r, fooTest.er, fooTest.s, fooTest.list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildPodsByCreationTime() PodsByCreationTime {
|
||||
return []*v1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo1",
|
||||
Namespace: v1.NamespaceDefault,
|
||||
CreationTimestamp: metav1.Time{
|
||||
Time: time.Now(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo2",
|
||||
Namespace: v1.NamespaceDefault,
|
||||
CreationTimestamp: metav1.Time{
|
||||
Time: time.Now().Add(time.Hour * 1),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo3",
|
||||
Namespace: v1.NamespaceDefault,
|
||||
CreationTimestamp: metav1.Time{
|
||||
Time: time.Now().Add(time.Hour * 2),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodsByCreationTimeLen(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
pods PodsByCreationTime
|
||||
el int
|
||||
}{
|
||||
{[]*v1.Pod{}, 0},
|
||||
{buildPodsByCreationTime(), 3},
|
||||
{[]*v1.Pod{nil, {}}, 2},
|
||||
{nil, 0},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.pods.Len()
|
||||
if r != fooTest.el {
|
||||
t.Errorf("returned %d but expected %d for the len of PodsByCreationTime=%s", r, fooTest.el, fooTest.pods)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodsByCreationTimeSwap(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
pods PodsByCreationTime
|
||||
i int
|
||||
j int
|
||||
}{
|
||||
{buildPodsByCreationTime(), 0, 1},
|
||||
{buildPodsByCreationTime(), 2, 1},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
fooi := fooTest.pods[fooTest.i]
|
||||
fooj := fooTest.pods[fooTest.j]
|
||||
fooTest.pods.Swap(fooTest.i, fooTest.j)
|
||||
if fooi.GetName() != fooTest.pods[fooTest.j].GetName() || fooj.GetName() != fooTest.pods[fooTest.i].GetName() {
|
||||
t.Errorf("failed to swap for %v", fooTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodsByCreationTimeLess(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
pods PodsByCreationTime
|
||||
i int
|
||||
j int
|
||||
er bool
|
||||
}{
|
||||
// ascending order
|
||||
{buildPodsByCreationTime(), 0, 2, true},
|
||||
{buildPodsByCreationTime(), 1, 0, false},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.pods.Less(fooTest.i, fooTest.j)
|
||||
if r != fooTest.er {
|
||||
t.Errorf("returned %t but expected %t for the foo=%s", r, fooTest.er, fooTest.pods)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildByImageSize() ByImageSize {
|
||||
return []kubecontainer.Image{
|
||||
{
|
||||
ID: "1",
|
||||
RepoTags: []string{"foo-tag11", "foo-tag12"},
|
||||
RepoDigests: []string{"foo-rd11", "foo-rd12"},
|
||||
Size: 1,
|
||||
},
|
||||
{
|
||||
ID: "2",
|
||||
RepoTags: []string{"foo-tag21", "foo-tag22"},
|
||||
RepoDigests: []string{"foo-rd21", "foo-rd22"},
|
||||
Size: 2,
|
||||
},
|
||||
{
|
||||
ID: "3",
|
||||
RepoTags: []string{"foo-tag31", "foo-tag32"},
|
||||
RepoDigests: []string{"foo-rd31", "foo-rd32"},
|
||||
Size: 3,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestByImageSizeLen(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
images ByImageSize
|
||||
el int
|
||||
}{
|
||||
{[]kubecontainer.Image{}, 0},
|
||||
{buildByImageSize(), 3},
|
||||
{nil, 0},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.images.Len()
|
||||
if r != fooTest.el {
|
||||
t.Errorf("returned %d but expected %d for the len of ByImageSize=%v", r, fooTest.el, fooTest.images)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestByImageSizeSwap(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
images ByImageSize
|
||||
i int
|
||||
j int
|
||||
}{
|
||||
{buildByImageSize(), 0, 1},
|
||||
{buildByImageSize(), 2, 1},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
fooi := fooTest.images[fooTest.i]
|
||||
fooj := fooTest.images[fooTest.j]
|
||||
fooTest.images.Swap(fooTest.i, fooTest.j)
|
||||
if fooi.ID != fooTest.images[fooTest.j].ID || fooj.ID != fooTest.images[fooTest.i].ID {
|
||||
t.Errorf("failed to swap for %v", fooTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestByImageSizeLess(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
images ByImageSize
|
||||
i int
|
||||
j int
|
||||
er bool
|
||||
}{
|
||||
// descending order
|
||||
{buildByImageSize(), 0, 2, false},
|
||||
{buildByImageSize(), 1, 0, true},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.images.Less(fooTest.i, fooTest.j)
|
||||
if r != fooTest.er {
|
||||
t.Errorf("returned %t but expected %t for the foo=%v", r, fooTest.er, fooTest.images)
|
||||
}
|
||||
}
|
||||
}
|
||||
64
vendor/k8s.io/kubernetes/pkg/kubelet/util/util_test.go
generated
vendored
64
vendor/k8s.io/kubernetes/pkg/kubelet/util/util_test.go
generated
vendored
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseEndpoint(t *testing.T) {
|
||||
tests := []struct {
|
||||
endpoint string
|
||||
expectError bool
|
||||
expectedProtocol string
|
||||
expectedAddr string
|
||||
}{
|
||||
{
|
||||
endpoint: "unix:///tmp/s1.sock",
|
||||
expectedProtocol: "unix",
|
||||
expectedAddr: "/tmp/s1.sock",
|
||||
},
|
||||
{
|
||||
endpoint: "tcp://localhost:15880",
|
||||
expectedProtocol: "tcp",
|
||||
expectedAddr: "localhost:15880",
|
||||
},
|
||||
{
|
||||
endpoint: "tcp1://abc",
|
||||
expectedProtocol: "tcp1",
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
endpoint: "a b c",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
protocol, addr, err := parseEndpoint(test.endpoint)
|
||||
assert.Equal(t, test.expectedProtocol, protocol)
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during parsing %q", test.endpoint)
|
||||
continue
|
||||
}
|
||||
assert.Nil(t, err, "Expect no error during parsing %q", test.endpoint)
|
||||
assert.Equal(t, test.expectedAddr, addr)
|
||||
}
|
||||
|
||||
}
|
||||
223
vendor/k8s.io/kubernetes/pkg/master/client_ca_hook_test.go
generated
vendored
223
vendor/k8s.io/kubernetes/pkg/master/client_ca_hook_test.go
generated
vendored
|
|
@ -1,223 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 master
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
clienttesting "k8s.io/client-go/testing"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||
)
|
||||
|
||||
func TestWriteClientCAs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
hook ClientCARegistrationHook
|
||||
preexistingObjs []runtime.Object
|
||||
expectedConfigMaps map[string]*api.ConfigMap
|
||||
expectUpdate bool
|
||||
}{
|
||||
{
|
||||
name: "basic",
|
||||
hook: ClientCARegistrationHook{
|
||||
ClientCA: []byte("foo"),
|
||||
RequestHeaderUsernameHeaders: []string{"alfa", "bravo", "charlie"},
|
||||
RequestHeaderGroupHeaders: []string{"delta"},
|
||||
RequestHeaderExtraHeaderPrefixes: []string{"echo", "foxtrot"},
|
||||
RequestHeaderCA: []byte("bar"),
|
||||
RequestHeaderAllowedNames: []string{"first", "second"},
|
||||
},
|
||||
expectedConfigMaps: map[string]*api.ConfigMap{
|
||||
"extension-apiserver-authentication": {
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||
Data: map[string]string{
|
||||
"client-ca-file": "foo",
|
||||
"requestheader-username-headers": `["alfa","bravo","charlie"]`,
|
||||
"requestheader-group-headers": `["delta"]`,
|
||||
"requestheader-extra-headers-prefix": `["echo","foxtrot"]`,
|
||||
"requestheader-client-ca-file": "bar",
|
||||
"requestheader-allowed-names": `["first","second"]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "skip extension-apiserver-authentication",
|
||||
hook: ClientCARegistrationHook{
|
||||
RequestHeaderCA: []byte("bar"),
|
||||
RequestHeaderAllowedNames: []string{"first", "second"},
|
||||
},
|
||||
expectedConfigMaps: map[string]*api.ConfigMap{
|
||||
"extension-apiserver-authentication": {
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||
Data: map[string]string{
|
||||
"requestheader-username-headers": `null`,
|
||||
"requestheader-group-headers": `null`,
|
||||
"requestheader-extra-headers-prefix": `null`,
|
||||
"requestheader-client-ca-file": "bar",
|
||||
"requestheader-allowed-names": `["first","second"]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "skip extension-apiserver-authentication",
|
||||
hook: ClientCARegistrationHook{
|
||||
ClientCA: []byte("foo"),
|
||||
},
|
||||
expectedConfigMaps: map[string]*api.ConfigMap{
|
||||
"extension-apiserver-authentication": {
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||
Data: map[string]string{
|
||||
"client-ca-file": "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty allowed names",
|
||||
hook: ClientCARegistrationHook{
|
||||
RequestHeaderCA: []byte("bar"),
|
||||
},
|
||||
expectedConfigMaps: map[string]*api.ConfigMap{
|
||||
"extension-apiserver-authentication": {
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||
Data: map[string]string{
|
||||
"requestheader-username-headers": `null`,
|
||||
"requestheader-group-headers": `null`,
|
||||
"requestheader-extra-headers-prefix": `null`,
|
||||
"requestheader-client-ca-file": "bar",
|
||||
"requestheader-allowed-names": `null`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "overwrite extension-apiserver-authentication",
|
||||
hook: ClientCARegistrationHook{
|
||||
ClientCA: []byte("foo"),
|
||||
},
|
||||
preexistingObjs: []runtime.Object{
|
||||
&api.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||
Data: map[string]string{
|
||||
"client-ca-file": "other",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedConfigMaps: map[string]*api.ConfigMap{
|
||||
"extension-apiserver-authentication": {
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||
Data: map[string]string{
|
||||
"client-ca-file": "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectUpdate: true,
|
||||
},
|
||||
{
|
||||
name: "overwrite extension-apiserver-authentication requestheader",
|
||||
hook: ClientCARegistrationHook{
|
||||
RequestHeaderUsernameHeaders: []string{},
|
||||
RequestHeaderGroupHeaders: []string{},
|
||||
RequestHeaderExtraHeaderPrefixes: []string{},
|
||||
RequestHeaderCA: []byte("bar"),
|
||||
RequestHeaderAllowedNames: []string{},
|
||||
},
|
||||
preexistingObjs: []runtime.Object{
|
||||
&api.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||
Data: map[string]string{
|
||||
"requestheader-username-headers": `null`,
|
||||
"requestheader-group-headers": `null`,
|
||||
"requestheader-extra-headers-prefix": `null`,
|
||||
"requestheader-client-ca-file": "something",
|
||||
"requestheader-allowed-names": `null`,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedConfigMaps: map[string]*api.ConfigMap{
|
||||
"extension-apiserver-authentication": {
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||
Data: map[string]string{
|
||||
"requestheader-username-headers": `[]`,
|
||||
"requestheader-group-headers": `[]`,
|
||||
"requestheader-extra-headers-prefix": `[]`,
|
||||
"requestheader-client-ca-file": "bar",
|
||||
"requestheader-allowed-names": `[]`,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectUpdate: true,
|
||||
},
|
||||
{
|
||||
name: "namespace exists",
|
||||
hook: ClientCARegistrationHook{
|
||||
ClientCA: []byte("foo"),
|
||||
},
|
||||
preexistingObjs: []runtime.Object{
|
||||
&api.Namespace{ObjectMeta: metav1.ObjectMeta{Name: metav1.NamespaceSystem}},
|
||||
},
|
||||
expectedConfigMaps: map[string]*api.ConfigMap{
|
||||
"extension-apiserver-authentication": {
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceSystem, Name: "extension-apiserver-authentication"},
|
||||
Data: map[string]string{
|
||||
"client-ca-file": "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
client := fake.NewSimpleClientset(test.preexistingObjs...)
|
||||
test.hook.tryToWriteClientCAs(client.Core())
|
||||
|
||||
actualConfigMaps, updated := getFinalConfiMaps(client)
|
||||
if !reflect.DeepEqual(test.expectedConfigMaps, actualConfigMaps) {
|
||||
t.Errorf("%s: %v", test.name, diff.ObjectReflectDiff(test.expectedConfigMaps, actualConfigMaps))
|
||||
continue
|
||||
}
|
||||
if test.expectUpdate != updated {
|
||||
t.Errorf("%s: expected %v, got %v", test.name, test.expectUpdate, updated)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFinalConfiMaps(client *fake.Clientset) (map[string]*api.ConfigMap, bool) {
|
||||
ret := map[string]*api.ConfigMap{}
|
||||
updated := false
|
||||
|
||||
for _, action := range client.Actions() {
|
||||
if action.Matches("create", "configmaps") {
|
||||
obj := action.(clienttesting.CreateAction).GetObject().(*api.ConfigMap)
|
||||
ret[obj.Name] = obj
|
||||
}
|
||||
if action.Matches("update", "configmaps") {
|
||||
updated = true
|
||||
obj := action.(clienttesting.UpdateAction).GetObject().(*api.ConfigMap)
|
||||
ret[obj.Name] = obj
|
||||
}
|
||||
}
|
||||
return ret, updated
|
||||
}
|
||||
948
vendor/k8s.io/kubernetes/pkg/master/controller_test.go
generated
vendored
948
vendor/k8s.io/kubernetes/pkg/master/controller_test.go
generated
vendored
|
|
@ -1,948 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 master
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
core "k8s.io/client-go/testing"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||
"k8s.io/kubernetes/pkg/master/reconcilers"
|
||||
)
|
||||
|
||||
func TestReconcileEndpoints(t *testing.T) {
|
||||
ns := metav1.NamespaceDefault
|
||||
om := func(name string) metav1.ObjectMeta {
|
||||
return metav1.ObjectMeta{Namespace: ns, Name: name}
|
||||
}
|
||||
reconcile_tests := []struct {
|
||||
testName string
|
||||
serviceName string
|
||||
ip string
|
||||
endpointPorts []api.EndpointPort
|
||||
additionalMasters int
|
||||
endpoints *api.EndpointsList
|
||||
expectUpdate *api.Endpoints // nil means none expected
|
||||
expectCreate *api.Endpoints // nil means none expected
|
||||
}{
|
||||
{
|
||||
testName: "no existing endpoints",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
endpoints: nil,
|
||||
expectCreate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints satisfy",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints satisfy but too many",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "4.3.2.1"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints satisfy but too many + extra masters",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
additionalMasters: 3,
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{
|
||||
{IP: "1.2.3.4"},
|
||||
{IP: "4.3.2.1"},
|
||||
{IP: "4.3.2.2"},
|
||||
{IP: "4.3.2.3"},
|
||||
{IP: "4.3.2.4"},
|
||||
},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{
|
||||
{IP: "1.2.3.4"},
|
||||
{IP: "4.3.2.2"},
|
||||
{IP: "4.3.2.3"},
|
||||
{IP: "4.3.2.4"},
|
||||
},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints satisfy but too many + extra masters + delete first",
|
||||
serviceName: "foo",
|
||||
ip: "4.3.2.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
additionalMasters: 3,
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{
|
||||
{IP: "1.2.3.4"},
|
||||
{IP: "4.3.2.1"},
|
||||
{IP: "4.3.2.2"},
|
||||
{IP: "4.3.2.3"},
|
||||
{IP: "4.3.2.4"},
|
||||
},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{
|
||||
{IP: "4.3.2.1"},
|
||||
{IP: "4.3.2.2"},
|
||||
{IP: "4.3.2.3"},
|
||||
{IP: "4.3.2.4"},
|
||||
},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints satisfy and endpoint addresses length less than master count",
|
||||
serviceName: "foo",
|
||||
ip: "4.3.2.2",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
additionalMasters: 3,
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{
|
||||
{IP: "4.3.2.1"},
|
||||
{IP: "4.3.2.2"},
|
||||
},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: nil,
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints current IP missing and address length less than master count",
|
||||
serviceName: "foo",
|
||||
ip: "4.3.2.2",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
additionalMasters: 3,
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{
|
||||
{IP: "4.3.2.1"},
|
||||
},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{
|
||||
{IP: "4.3.2.1"},
|
||||
{IP: "4.3.2.2"},
|
||||
},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints wrong name",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("bar"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectCreate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints wrong IP",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "4.3.2.1"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints wrong port",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 9090, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints wrong protocol",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "UDP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints wrong port name",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "baz", Port: 8080, Protocol: "TCP"}},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "baz", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints extra service ports satisfy",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP"},
|
||||
{Name: "bar", Port: 1000, Protocol: "TCP"},
|
||||
{Name: "baz", Port: 1010, Protocol: "TCP"},
|
||||
},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP"},
|
||||
{Name: "bar", Port: 1000, Protocol: "TCP"},
|
||||
{Name: "baz", Port: 1010, Protocol: "TCP"},
|
||||
},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints extra service ports missing port",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP"},
|
||||
{Name: "bar", Port: 1000, Protocol: "TCP"},
|
||||
},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP"},
|
||||
{Name: "bar", Port: 1000, Protocol: "TCP"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range reconcile_tests {
|
||||
fakeClient := fake.NewSimpleClientset()
|
||||
if test.endpoints != nil {
|
||||
fakeClient = fake.NewSimpleClientset(test.endpoints)
|
||||
}
|
||||
reconciler := reconcilers.NewMasterCountEndpointReconciler(test.additionalMasters+1, fakeClient.Core())
|
||||
err := reconciler.ReconcileEndpoints(test.serviceName, net.ParseIP(test.ip), test.endpointPorts, true)
|
||||
if err != nil {
|
||||
t.Errorf("case %q: unexpected error: %v", test.testName, err)
|
||||
}
|
||||
|
||||
updates := []core.UpdateAction{}
|
||||
for _, action := range fakeClient.Actions() {
|
||||
if action.GetVerb() != "update" {
|
||||
continue
|
||||
}
|
||||
updates = append(updates, action.(core.UpdateAction))
|
||||
}
|
||||
if test.expectUpdate != nil {
|
||||
if len(updates) != 1 {
|
||||
t.Errorf("case %q: unexpected updates: %v", test.testName, updates)
|
||||
} else if e, a := test.expectUpdate, updates[0].GetObject(); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("case %q: expected update:\n%#v\ngot:\n%#v\n", test.testName, e, a)
|
||||
}
|
||||
}
|
||||
if test.expectUpdate == nil && len(updates) > 0 {
|
||||
t.Errorf("case %q: no update expected, yet saw: %v", test.testName, updates)
|
||||
}
|
||||
|
||||
creates := []core.CreateAction{}
|
||||
for _, action := range fakeClient.Actions() {
|
||||
if action.GetVerb() != "create" {
|
||||
continue
|
||||
}
|
||||
creates = append(creates, action.(core.CreateAction))
|
||||
}
|
||||
if test.expectCreate != nil {
|
||||
if len(creates) != 1 {
|
||||
t.Errorf("case %q: unexpected creates: %v", test.testName, creates)
|
||||
} else if e, a := test.expectCreate, creates[0].GetObject(); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("case %q: expected create:\n%#v\ngot:\n%#v\n", test.testName, e, a)
|
||||
}
|
||||
}
|
||||
if test.expectCreate == nil && len(creates) > 0 {
|
||||
t.Errorf("case %q: no create expected, yet saw: %v", test.testName, creates)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
non_reconcile_tests := []struct {
|
||||
testName string
|
||||
serviceName string
|
||||
ip string
|
||||
endpointPorts []api.EndpointPort
|
||||
additionalMasters int
|
||||
endpoints *api.EndpointsList
|
||||
expectUpdate *api.Endpoints // nil means none expected
|
||||
expectCreate *api.Endpoints // nil means none expected
|
||||
}{
|
||||
{
|
||||
testName: "existing endpoints extra service ports missing port no update",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP"},
|
||||
{Name: "bar", Port: 1000, Protocol: "TCP"},
|
||||
},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: nil,
|
||||
},
|
||||
{
|
||||
testName: "existing endpoints extra service ports, wrong ports, wrong IP",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP"},
|
||||
{Name: "bar", Port: 1000, Protocol: "TCP"},
|
||||
},
|
||||
endpoints: &api.EndpointsList{
|
||||
Items: []api.Endpoints{{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "4.3.2.1"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
expectUpdate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "no existing endpoints",
|
||||
serviceName: "foo",
|
||||
ip: "1.2.3.4",
|
||||
endpointPorts: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
endpoints: nil,
|
||||
expectCreate: &api.Endpoints{
|
||||
ObjectMeta: om("foo"),
|
||||
Subsets: []api.EndpointSubset{{
|
||||
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
|
||||
Ports: []api.EndpointPort{{Name: "foo", Port: 8080, Protocol: "TCP"}},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range non_reconcile_tests {
|
||||
fakeClient := fake.NewSimpleClientset()
|
||||
if test.endpoints != nil {
|
||||
fakeClient = fake.NewSimpleClientset(test.endpoints)
|
||||
}
|
||||
reconciler := reconcilers.NewMasterCountEndpointReconciler(test.additionalMasters+1, fakeClient.Core())
|
||||
err := reconciler.ReconcileEndpoints(test.serviceName, net.ParseIP(test.ip), test.endpointPorts, false)
|
||||
if err != nil {
|
||||
t.Errorf("case %q: unexpected error: %v", test.testName, err)
|
||||
}
|
||||
|
||||
updates := []core.UpdateAction{}
|
||||
for _, action := range fakeClient.Actions() {
|
||||
if action.GetVerb() != "update" {
|
||||
continue
|
||||
}
|
||||
updates = append(updates, action.(core.UpdateAction))
|
||||
}
|
||||
if test.expectUpdate != nil {
|
||||
if len(updates) != 1 {
|
||||
t.Errorf("case %q: unexpected updates: %v", test.testName, updates)
|
||||
} else if e, a := test.expectUpdate, updates[0].GetObject(); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("case %q: expected update:\n%#v\ngot:\n%#v\n", test.testName, e, a)
|
||||
}
|
||||
}
|
||||
if test.expectUpdate == nil && len(updates) > 0 {
|
||||
t.Errorf("case %q: no update expected, yet saw: %v", test.testName, updates)
|
||||
}
|
||||
|
||||
creates := []core.CreateAction{}
|
||||
for _, action := range fakeClient.Actions() {
|
||||
if action.GetVerb() != "create" {
|
||||
continue
|
||||
}
|
||||
creates = append(creates, action.(core.CreateAction))
|
||||
}
|
||||
if test.expectCreate != nil {
|
||||
if len(creates) != 1 {
|
||||
t.Errorf("case %q: unexpected creates: %v", test.testName, creates)
|
||||
} else if e, a := test.expectCreate, creates[0].GetObject(); !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("case %q: expected create:\n%#v\ngot:\n%#v\n", test.testName, e, a)
|
||||
}
|
||||
}
|
||||
if test.expectCreate == nil && len(creates) > 0 {
|
||||
t.Errorf("case %q: no create expected, yet saw: %v", test.testName, creates)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCreateOrUpdateMasterService(t *testing.T) {
|
||||
ns := metav1.NamespaceDefault
|
||||
om := func(name string) metav1.ObjectMeta {
|
||||
return metav1.ObjectMeta{Namespace: ns, Name: name}
|
||||
}
|
||||
|
||||
create_tests := []struct {
|
||||
testName string
|
||||
serviceName string
|
||||
servicePorts []api.ServicePort
|
||||
serviceType api.ServiceType
|
||||
expectCreate *api.Service // nil means none expected
|
||||
}{
|
||||
{
|
||||
testName: "service does not exist",
|
||||
serviceName: "foo",
|
||||
servicePorts: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
serviceType: api.ServiceTypeClusterIP,
|
||||
expectCreate: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range create_tests {
|
||||
master := Controller{}
|
||||
fakeClient := fake.NewSimpleClientset()
|
||||
master.ServiceClient = fakeClient.Core()
|
||||
master.CreateOrUpdateMasterServiceIfNeeded(test.serviceName, net.ParseIP("1.2.3.4"), test.servicePorts, test.serviceType, false)
|
||||
creates := []core.CreateAction{}
|
||||
for _, action := range fakeClient.Actions() {
|
||||
if action.GetVerb() == "create" {
|
||||
creates = append(creates, action.(core.CreateAction))
|
||||
}
|
||||
}
|
||||
if test.expectCreate != nil {
|
||||
if len(creates) != 1 {
|
||||
t.Errorf("case %q: unexpected creations: %v", test.testName, creates)
|
||||
} else {
|
||||
obj := creates[0].GetObject()
|
||||
if e, a := test.expectCreate.Spec, obj.(*api.Service).Spec; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("case %q: expected create:\n%#v\ngot:\n%#v\n", test.testName, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
if test.expectCreate == nil && len(creates) > 1 {
|
||||
t.Errorf("case %q: no create expected, yet saw: %v", test.testName, creates)
|
||||
}
|
||||
}
|
||||
|
||||
reconcile_tests := []struct {
|
||||
testName string
|
||||
serviceName string
|
||||
servicePorts []api.ServicePort
|
||||
serviceType api.ServiceType
|
||||
service *api.Service
|
||||
expectUpdate *api.Service // nil means none expected
|
||||
}{
|
||||
{
|
||||
testName: "service definition wrong port",
|
||||
serviceName: "foo",
|
||||
servicePorts: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
serviceType: api.ServiceTypeClusterIP,
|
||||
service: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8000, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
expectUpdate: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "service definition missing port",
|
||||
serviceName: "foo",
|
||||
servicePorts: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
{Name: "baz", Port: 1000, Protocol: "TCP", TargetPort: intstr.FromInt(1000)},
|
||||
},
|
||||
serviceType: api.ServiceTypeClusterIP,
|
||||
service: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
expectUpdate: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
{Name: "baz", Port: 1000, Protocol: "TCP", TargetPort: intstr.FromInt(1000)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "service definition incorrect port",
|
||||
serviceName: "foo",
|
||||
servicePorts: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
serviceType: api.ServiceTypeClusterIP,
|
||||
service: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "bar", Port: 1000, Protocol: "UDP", TargetPort: intstr.FromInt(1000)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
expectUpdate: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "service definition incorrect port name",
|
||||
serviceName: "foo",
|
||||
servicePorts: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
serviceType: api.ServiceTypeClusterIP,
|
||||
service: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 1000, Protocol: "UDP", TargetPort: intstr.FromInt(1000)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
expectUpdate: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "service definition incorrect target port",
|
||||
serviceName: "foo",
|
||||
servicePorts: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
serviceType: api.ServiceTypeClusterIP,
|
||||
service: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(1000)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
expectUpdate: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "service definition incorrect protocol",
|
||||
serviceName: "foo",
|
||||
servicePorts: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
serviceType: api.ServiceTypeClusterIP,
|
||||
service: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "UDP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
expectUpdate: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "service definition has incorrect type",
|
||||
serviceName: "foo",
|
||||
servicePorts: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
serviceType: api.ServiceTypeClusterIP,
|
||||
service: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeNodePort,
|
||||
},
|
||||
},
|
||||
expectUpdate: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "service definition satisfies",
|
||||
serviceName: "foo",
|
||||
servicePorts: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
serviceType: api.ServiceTypeClusterIP,
|
||||
service: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
expectUpdate: nil,
|
||||
},
|
||||
}
|
||||
for _, test := range reconcile_tests {
|
||||
master := Controller{}
|
||||
fakeClient := fake.NewSimpleClientset(test.service)
|
||||
master.ServiceClient = fakeClient.Core()
|
||||
err := master.CreateOrUpdateMasterServiceIfNeeded(test.serviceName, net.ParseIP("1.2.3.4"), test.servicePorts, test.serviceType, true)
|
||||
if err != nil {
|
||||
t.Errorf("case %q: unexpected error: %v", test.testName, err)
|
||||
}
|
||||
updates := []core.UpdateAction{}
|
||||
for _, action := range fakeClient.Actions() {
|
||||
if action.GetVerb() == "update" {
|
||||
updates = append(updates, action.(core.UpdateAction))
|
||||
}
|
||||
}
|
||||
if test.expectUpdate != nil {
|
||||
if len(updates) != 1 {
|
||||
t.Errorf("case %q: unexpected updates: %v", test.testName, updates)
|
||||
} else {
|
||||
obj := updates[0].GetObject()
|
||||
if e, a := test.expectUpdate.Spec, obj.(*api.Service).Spec; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("case %q: expected update:\n%#v\ngot:\n%#v\n", test.testName, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
if test.expectUpdate == nil && len(updates) > 0 {
|
||||
t.Errorf("case %q: no update expected, yet saw: %v", test.testName, updates)
|
||||
}
|
||||
}
|
||||
|
||||
non_reconcile_tests := []struct {
|
||||
testName string
|
||||
serviceName string
|
||||
servicePorts []api.ServicePort
|
||||
serviceType api.ServiceType
|
||||
service *api.Service
|
||||
expectUpdate *api.Service // nil means none expected
|
||||
}{
|
||||
{
|
||||
testName: "service definition wrong port, no expected update",
|
||||
serviceName: "foo",
|
||||
servicePorts: []api.ServicePort{
|
||||
{Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt(8080)},
|
||||
},
|
||||
serviceType: api.ServiceTypeClusterIP,
|
||||
service: &api.Service{
|
||||
ObjectMeta: om("foo"),
|
||||
Spec: api.ServiceSpec{
|
||||
Ports: []api.ServicePort{
|
||||
{Name: "foo", Port: 1000, Protocol: "TCP", TargetPort: intstr.FromInt(1000)},
|
||||
},
|
||||
Selector: nil,
|
||||
ClusterIP: "1.2.3.4",
|
||||
SessionAffinity: api.ServiceAffinityClientIP,
|
||||
Type: api.ServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
expectUpdate: nil,
|
||||
},
|
||||
}
|
||||
for _, test := range non_reconcile_tests {
|
||||
master := Controller{}
|
||||
fakeClient := fake.NewSimpleClientset(test.service)
|
||||
master.ServiceClient = fakeClient.Core()
|
||||
err := master.CreateOrUpdateMasterServiceIfNeeded(test.serviceName, net.ParseIP("1.2.3.4"), test.servicePorts, test.serviceType, false)
|
||||
if err != nil {
|
||||
t.Errorf("case %q: unexpected error: %v", test.testName, err)
|
||||
}
|
||||
updates := []core.UpdateAction{}
|
||||
for _, action := range fakeClient.Actions() {
|
||||
if action.GetVerb() == "update" {
|
||||
updates = append(updates, action.(core.UpdateAction))
|
||||
}
|
||||
}
|
||||
if test.expectUpdate != nil {
|
||||
if len(updates) != 1 {
|
||||
t.Errorf("case %q: unexpected updates: %v", test.testName, updates)
|
||||
} else {
|
||||
obj := updates[0].GetObject()
|
||||
if e, a := test.expectUpdate.Spec, obj.(*api.Service).Spec; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("case %q: expected update:\n%#v\ngot:\n%#v\n", test.testName, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
if test.expectUpdate == nil && len(updates) > 0 {
|
||||
t.Errorf("case %q: no update expected, yet saw: %v", test.testName, updates)
|
||||
}
|
||||
}
|
||||
}
|
||||
187
vendor/k8s.io/kubernetes/pkg/master/import_known_versions_test.go
generated
vendored
187
vendor/k8s.io/kubernetes/pkg/master/import_known_versions_test.go
generated
vendored
|
|
@ -1,187 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 master
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
)
|
||||
|
||||
func TestGroupVersions(t *testing.T) {
|
||||
// legacyUnsuffixedGroups contains the groups released prior to deciding that kubernetes API groups should be dns-suffixed
|
||||
// new groups should be suffixed with ".k8s.io" (https://github.com/kubernetes/kubernetes/pull/31887#issuecomment-244462396)
|
||||
legacyUnsuffixedGroups := sets.NewString(
|
||||
"",
|
||||
"apps",
|
||||
"autoscaling",
|
||||
"batch",
|
||||
"componentconfig",
|
||||
"extensions",
|
||||
"policy",
|
||||
)
|
||||
|
||||
// No new groups should be added to the legacyUnsuffixedGroups exclusion list
|
||||
if len(legacyUnsuffixedGroups) != 7 {
|
||||
t.Errorf("No additional unnamespaced groups should be created")
|
||||
}
|
||||
|
||||
for _, gv := range legacyscheme.Registry.RegisteredGroupVersions() {
|
||||
if !strings.HasSuffix(gv.Group, ".k8s.io") && !legacyUnsuffixedGroups.Has(gv.Group) {
|
||||
t.Errorf("Group %s does not have the standard kubernetes API group suffix of .k8s.io", gv.Group)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeTags(t *testing.T) {
|
||||
for gvk, knownType := range legacyscheme.Scheme.AllKnownTypes() {
|
||||
if gvk.Version == runtime.APIVersionInternal {
|
||||
ensureNoTags(t, gvk, knownType, nil)
|
||||
} else {
|
||||
ensureTags(t, gvk, knownType, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These types are registered in external versions, and therefore include json tags,
|
||||
// but are also registered in internal versions (or referenced from internal types),
|
||||
// so we explicitly allow tags for them
|
||||
var typesAllowedTags = map[reflect.Type]bool{
|
||||
reflect.TypeOf(intstr.IntOrString{}): true,
|
||||
reflect.TypeOf(metav1.Time{}): true,
|
||||
reflect.TypeOf(metav1.MicroTime{}): true,
|
||||
reflect.TypeOf(metav1.Duration{}): true,
|
||||
reflect.TypeOf(metav1.TypeMeta{}): true,
|
||||
reflect.TypeOf(metav1.ListMeta{}): true,
|
||||
reflect.TypeOf(metav1.ObjectMeta{}): true,
|
||||
reflect.TypeOf(metav1.OwnerReference{}): true,
|
||||
reflect.TypeOf(metav1.LabelSelector{}): true,
|
||||
reflect.TypeOf(metav1.GetOptions{}): true,
|
||||
reflect.TypeOf(metav1.ExportOptions{}): true,
|
||||
reflect.TypeOf(metav1.ListOptions{}): true,
|
||||
reflect.TypeOf(metav1.DeleteOptions{}): true,
|
||||
reflect.TypeOf(metav1.GroupVersionKind{}): true,
|
||||
reflect.TypeOf(metav1.GroupVersionResource{}): true,
|
||||
reflect.TypeOf(metav1.Status{}): true,
|
||||
}
|
||||
|
||||
func ensureNoTags(t *testing.T, gvk schema.GroupVersionKind, tp reflect.Type, parents []reflect.Type) {
|
||||
if _, ok := typesAllowedTags[tp]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
parents = append(parents, tp)
|
||||
|
||||
switch tp.Kind() {
|
||||
case reflect.Map, reflect.Slice, reflect.Ptr:
|
||||
ensureNoTags(t, gvk, tp.Elem(), parents)
|
||||
|
||||
case reflect.String, reflect.Bool, reflect.Float32, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uintptr, reflect.Uint32, reflect.Uint64, reflect.Interface:
|
||||
// no-op
|
||||
|
||||
case reflect.Struct:
|
||||
for i := 0; i < tp.NumField(); i++ {
|
||||
f := tp.Field(i)
|
||||
if f.PkgPath != "" {
|
||||
continue // Ignore unexported fields
|
||||
}
|
||||
jsonTag := f.Tag.Get("json")
|
||||
protoTag := f.Tag.Get("protobuf")
|
||||
if len(jsonTag) > 0 || len(protoTag) > 0 {
|
||||
t.Errorf("Internal types should not have json or protobuf tags. %#v has tag on field %v: %v", gvk, f.Name, f.Tag)
|
||||
for i, tp := range parents {
|
||||
t.Logf("%s%v:", strings.Repeat(" ", i), tp)
|
||||
}
|
||||
}
|
||||
|
||||
ensureNoTags(t, gvk, f.Type, parents)
|
||||
}
|
||||
|
||||
default:
|
||||
t.Errorf("Unexpected type %v in %#v", tp.Kind(), gvk)
|
||||
for i, tp := range parents {
|
||||
t.Logf("%s%v:", strings.Repeat(" ", i), tp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
marshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
|
||||
unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
|
||||
)
|
||||
|
||||
// These fields are limited exceptions to the standard JSON naming structure.
|
||||
// Additions should only be made if a non-standard field name was released and cannot be changed for compatibility reasons.
|
||||
var allowedNonstandardJSONNames = map[reflect.Type]string{
|
||||
reflect.TypeOf(v1.DaemonEndpoint{}): "Port",
|
||||
}
|
||||
|
||||
func ensureTags(t *testing.T, gvk schema.GroupVersionKind, tp reflect.Type, parents []reflect.Type) {
|
||||
// This type handles its own encoding/decoding and doesn't need json tags
|
||||
if tp.Implements(marshalerType) && (tp.Implements(unmarshalerType) || reflect.PtrTo(tp).Implements(unmarshalerType)) {
|
||||
return
|
||||
}
|
||||
|
||||
parents = append(parents, tp)
|
||||
|
||||
switch tp.Kind() {
|
||||
case reflect.Map, reflect.Slice, reflect.Ptr:
|
||||
ensureTags(t, gvk, tp.Elem(), parents)
|
||||
|
||||
case reflect.String, reflect.Bool, reflect.Float32, reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uintptr, reflect.Uint32, reflect.Uint64, reflect.Interface:
|
||||
// no-op
|
||||
|
||||
case reflect.Struct:
|
||||
for i := 0; i < tp.NumField(); i++ {
|
||||
f := tp.Field(i)
|
||||
jsonTag := f.Tag.Get("json")
|
||||
if len(jsonTag) == 0 {
|
||||
t.Errorf("External types should have json tags. %#v tags on field %v are: %s", gvk, f.Name, f.Tag)
|
||||
for i, tp := range parents {
|
||||
t.Logf("%s%v", strings.Repeat(" ", i), tp)
|
||||
}
|
||||
}
|
||||
|
||||
jsonTagName := strings.Split(jsonTag, ",")[0]
|
||||
if len(jsonTagName) > 0 && (jsonTagName[0] < 'a' || jsonTagName[0] > 'z') && jsonTagName != "-" && allowedNonstandardJSONNames[tp] != jsonTagName {
|
||||
t.Errorf("External types should have json names starting with lowercase letter. %#v has json tag on field %v with name %s", gvk, f.Name, jsonTagName)
|
||||
t.Log(tp)
|
||||
t.Log(allowedNonstandardJSONNames[tp])
|
||||
for i, tp := range parents {
|
||||
t.Logf("%s%v", strings.Repeat(" ", i), tp)
|
||||
}
|
||||
}
|
||||
|
||||
ensureTags(t, gvk, f.Type, parents)
|
||||
}
|
||||
|
||||
default:
|
||||
t.Errorf("Unexpected type %v in %#v", tp.Kind(), gvk)
|
||||
for i, tp := range parents {
|
||||
t.Logf("%s%v:", strings.Repeat(" ", i), tp)
|
||||
}
|
||||
}
|
||||
}
|
||||
97
vendor/k8s.io/kubernetes/pkg/master/master_openapi_test.go
generated
vendored
97
vendor/k8s.io/kubernetes/pkg/master/master_openapi_test.go
generated
vendored
|
|
@ -1,97 +0,0 @@
|
|||
// +build !race
|
||||
|
||||
/*
|
||||
Copyright 2014 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 master
|
||||
|
||||
// This test file is separated from master_test.go so we would be able to disable
|
||||
// race check for it. TestValidOpenAPISpec will became extremely slow if -race
|
||||
// flag exists, and will cause the tests to timeout.
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
openapigen "k8s.io/kubernetes/pkg/generated/openapi"
|
||||
|
||||
"github.com/go-openapi/loads"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// TestValidOpenAPISpec verifies that the open api is added
|
||||
// at the proper endpoint and the spec is valid.
|
||||
func TestValidOpenAPISpec(t *testing.T) {
|
||||
etcdserver, config, sharedInformers, assert := setUp(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
config.GenericConfig.EnableIndex = true
|
||||
config.GenericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapigen.GetOpenAPIDefinitions, legacyscheme.Scheme)
|
||||
config.GenericConfig.OpenAPIConfig.Info = &spec.Info{
|
||||
InfoProps: spec.InfoProps{
|
||||
Title: "Kubernetes",
|
||||
Version: "unversioned",
|
||||
},
|
||||
}
|
||||
config.GenericConfig.SwaggerConfig = genericapiserver.DefaultSwaggerConfig()
|
||||
|
||||
master, err := config.Complete(sharedInformers).New(genericapiserver.EmptyDelegate)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in bringing up the master: %v", err)
|
||||
}
|
||||
|
||||
// make sure swagger.json is not registered before calling PrepareRun.
|
||||
server := httptest.NewServer(apirequest.WithRequestContext(master.GenericAPIServer.Handler.Director, master.GenericAPIServer.RequestContextMapper()))
|
||||
defer server.Close()
|
||||
resp, err := http.Get(server.URL + "/swagger.json")
|
||||
if !assert.NoError(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
assert.Equal(http.StatusNotFound, resp.StatusCode)
|
||||
|
||||
master.GenericAPIServer.PrepareRun()
|
||||
|
||||
resp, err = http.Get(server.URL + "/swagger.json")
|
||||
if !assert.NoError(err) {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
assert.Equal(http.StatusOK, resp.StatusCode)
|
||||
|
||||
// as json schema
|
||||
var sch spec.Schema
|
||||
if assert.NoError(decodeResponse(resp, &sch)) {
|
||||
validator := validate.NewSchemaValidator(spec.MustLoadSwagger20Schema(), nil, "", strfmt.Default)
|
||||
res := validator.Validate(&sch)
|
||||
assert.NoError(res.AsError())
|
||||
}
|
||||
|
||||
// Validate OpenApi spec
|
||||
doc, err := loads.Spec(server.URL + "/swagger.json")
|
||||
if assert.NoError(err) {
|
||||
validator := validate.NewSpecValidator(doc.Schema(), strfmt.Default)
|
||||
res, warns := validator.Validate(doc)
|
||||
assert.NoError(res.AsError())
|
||||
if !warns.IsValid() {
|
||||
t.Logf("Open API spec on root has some warnings : %v", warns)
|
||||
}
|
||||
}
|
||||
}
|
||||
360
vendor/k8s.io/kubernetes/pkg/master/master_test.go
generated
vendored
360
vendor/k8s.io/kubernetes/pkg/master/master_test.go
generated
vendored
|
|
@ -1,360 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 master
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
certificatesapiv1beta1 "k8s.io/api/certificates/v1beta1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/options"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/certificates"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/storage"
|
||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||
"k8s.io/kubernetes/pkg/master/reconcilers"
|
||||
certificatesrest "k8s.io/kubernetes/pkg/registry/certificates/rest"
|
||||
corerest "k8s.io/kubernetes/pkg/registry/core/rest"
|
||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||
kubeversion "k8s.io/kubernetes/pkg/version"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// setUp is a convience function for setting up for (most) tests.
|
||||
func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, informers.SharedInformerFactory, *assert.Assertions) {
|
||||
server, storageConfig := etcdtesting.NewUnsecuredEtcd3TestClientServer(t)
|
||||
|
||||
config := &Config{
|
||||
GenericConfig: genericapiserver.NewConfig(legacyscheme.Codecs),
|
||||
ExtraConfig: ExtraConfig{
|
||||
APIResourceConfigSource: DefaultAPIResourceConfigSource(),
|
||||
APIServerServicePort: 443,
|
||||
MasterCount: 1,
|
||||
EndpointReconcilerType: reconcilers.MasterCountReconcilerType,
|
||||
},
|
||||
}
|
||||
|
||||
resourceEncoding := serverstorage.NewDefaultResourceEncodingConfig(legacyscheme.Registry)
|
||||
resourceEncoding.SetVersionEncoding(api.GroupName, legacyscheme.Registry.GroupOrDie(api.GroupName).GroupVersion, schema.GroupVersion{Group: api.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetVersionEncoding(autoscaling.GroupName, *testapi.Autoscaling.GroupVersion(), schema.GroupVersion{Group: autoscaling.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), schema.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
|
||||
// FIXME (soltysh): this GroupVersionResource override should be configurable
|
||||
resourceEncoding.SetResourceEncoding(schema.GroupResource{Group: "batch", Resource: "cronjobs"}, schema.GroupVersion{Group: batch.GroupName, Version: "v1beta1"}, schema.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetResourceEncoding(schema.GroupResource{Group: "storage.k8s.io", Resource: "volumeattachments"}, schema.GroupVersion{Group: storage.GroupName, Version: "v1beta1"}, schema.GroupVersion{Group: storage.GroupName, Version: runtime.APIVersionInternal})
|
||||
|
||||
resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), schema.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), schema.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetVersionEncoding(rbac.GroupName, *testapi.Rbac.GroupVersion(), schema.GroupVersion{Group: rbac.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetVersionEncoding(certificates.GroupName, *testapi.Certificates.GroupVersion(), schema.GroupVersion{Group: certificates.GroupName, Version: runtime.APIVersionInternal})
|
||||
storageFactory := serverstorage.NewDefaultStorageFactory(*storageConfig, testapi.StorageMediaType(), legacyscheme.Codecs, resourceEncoding, DefaultAPIResourceConfigSource(), nil)
|
||||
|
||||
etcdOptions := options.NewEtcdOptions(storageConfig)
|
||||
// unit tests don't need watch cache and it leaks lots of goroutines with etcd testing functions during unit tests
|
||||
etcdOptions.EnableWatchCache = false
|
||||
err := etcdOptions.ApplyWithStorageFactoryTo(storageFactory, config.GenericConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
kubeVersion := kubeversion.Get()
|
||||
config.GenericConfig.Version = &kubeVersion
|
||||
config.ExtraConfig.StorageFactory = storageFactory
|
||||
config.GenericConfig.LoopbackClientConfig = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs}}
|
||||
config.GenericConfig.PublicAddress = net.ParseIP("192.168.10.4")
|
||||
config.GenericConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
|
||||
config.GenericConfig.RequestContextMapper = genericapirequest.NewRequestContextMapper()
|
||||
config.GenericConfig.LoopbackClientConfig = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs}}
|
||||
config.GenericConfig.EnableMetrics = true
|
||||
config.ExtraConfig.EnableCoreControllers = false
|
||||
config.ExtraConfig.KubeletClientConfig = kubeletclient.KubeletClientConfig{Port: 10250}
|
||||
config.ExtraConfig.ProxyTransport = utilnet.SetTransportDefaults(&http.Transport{
|
||||
Dial: func(network, addr string) (net.Conn, error) { return nil, nil },
|
||||
TLSClientConfig: &tls.Config{},
|
||||
})
|
||||
|
||||
clientset, err := kubernetes.NewForConfig(config.GenericConfig.LoopbackClientConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create client set due to %v", err)
|
||||
}
|
||||
sharedInformers := informers.NewSharedInformerFactory(clientset, config.GenericConfig.LoopbackClientConfig.Timeout)
|
||||
|
||||
return server, *config, sharedInformers, assert.New(t)
|
||||
}
|
||||
|
||||
// TestLegacyRestStorageStrategies ensures that all Storage objects which are using the generic registry Store have
|
||||
// their various strategies properly wired up. This surfaced as a bug where strategies defined Export functions, but
|
||||
// they were never used outside of unit tests because the export strategies were not assigned inside the Store.
|
||||
func TestLegacyRestStorageStrategies(t *testing.T) {
|
||||
_, etcdserver, masterCfg, _ := newMaster(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
storageProvider := corerest.LegacyRESTStorageProvider{
|
||||
StorageFactory: masterCfg.ExtraConfig.StorageFactory,
|
||||
ProxyTransport: masterCfg.ExtraConfig.ProxyTransport,
|
||||
KubeletClientConfig: masterCfg.ExtraConfig.KubeletClientConfig,
|
||||
EventTTL: masterCfg.ExtraConfig.EventTTL,
|
||||
ServiceIPRange: masterCfg.ExtraConfig.ServiceIPRange,
|
||||
ServiceNodePortRange: masterCfg.ExtraConfig.ServiceNodePortRange,
|
||||
LoopbackClientConfig: masterCfg.GenericConfig.LoopbackClientConfig,
|
||||
}
|
||||
|
||||
_, apiGroupInfo, err := storageProvider.NewLegacyRESTStorage(masterCfg.GenericConfig.RESTOptionsGetter)
|
||||
if err != nil {
|
||||
t.Errorf("failed to create legacy REST storage: %v", err)
|
||||
}
|
||||
|
||||
// Any new stores with export logic will need to be added here:
|
||||
exceptions := registrytest.StrategyExceptions{
|
||||
// Only these stores should have an export strategy defined:
|
||||
HasExportStrategy: []string{
|
||||
"secrets",
|
||||
"limitRanges",
|
||||
"nodes",
|
||||
"podTemplates",
|
||||
},
|
||||
}
|
||||
|
||||
strategyErrors := registrytest.ValidateStorageStrategies(apiGroupInfo.VersionedResourcesStorageMap["v1"], exceptions)
|
||||
for _, err := range strategyErrors {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificatesRestStorageStrategies(t *testing.T) {
|
||||
_, etcdserver, masterCfg, _ := newMaster(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
certStorageProvider := certificatesrest.RESTStorageProvider{}
|
||||
apiGroupInfo, _ := certStorageProvider.NewRESTStorage(masterCfg.ExtraConfig.APIResourceConfigSource, masterCfg.GenericConfig.RESTOptionsGetter)
|
||||
|
||||
exceptions := registrytest.StrategyExceptions{
|
||||
HasExportStrategy: []string{
|
||||
"certificatesigningrequests",
|
||||
},
|
||||
}
|
||||
|
||||
strategyErrors := registrytest.ValidateStorageStrategies(
|
||||
apiGroupInfo.VersionedResourcesStorageMap[certificatesapiv1beta1.SchemeGroupVersion.Version], exceptions)
|
||||
for _, err := range strategyErrors {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
|
||||
etcdserver, config, sharedInformers, assert := setUp(t)
|
||||
|
||||
master, err := config.Complete(sharedInformers).New(genericapiserver.EmptyDelegate)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in bringing up the master: %v", err)
|
||||
}
|
||||
|
||||
return master, etcdserver, config, assert
|
||||
}
|
||||
|
||||
// TestVersion tests /version
|
||||
func TestVersion(t *testing.T) {
|
||||
s, etcdserver, _, _ := newMaster(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/version", nil)
|
||||
resp := httptest.NewRecorder()
|
||||
s.GenericAPIServer.Handler.ServeHTTP(resp, req)
|
||||
if resp.Code != 200 {
|
||||
t.Fatalf("expected http 200, got: %d", resp.Code)
|
||||
}
|
||||
|
||||
var info version.Info
|
||||
err := json.NewDecoder(resp.Body).Decode(&info)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(kubeversion.Get(), info) {
|
||||
t.Errorf("Expected %#v, Got %#v", kubeversion.Get(), info)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeEndpointReconciler struct{}
|
||||
|
||||
func (*fakeEndpointReconciler) ReconcileEndpoints(serviceName string, ip net.IP, endpointPorts []api.EndpointPort, reconcilePorts bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeNodeList(nodes []string, nodeResources apiv1.NodeResources) *apiv1.NodeList {
|
||||
list := apiv1.NodeList{
|
||||
Items: make([]apiv1.Node, len(nodes)),
|
||||
}
|
||||
for i := range nodes {
|
||||
list.Items[i].Name = nodes[i]
|
||||
list.Items[i].Status.Capacity = nodeResources.Capacity
|
||||
}
|
||||
return &list
|
||||
}
|
||||
|
||||
// TestGetNodeAddresses verifies that proper results are returned
|
||||
// when requesting node addresses.
|
||||
func TestGetNodeAddresses(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
fakeNodeClient := fake.NewSimpleClientset(makeNodeList([]string{"node1", "node2"}, apiv1.NodeResources{})).Core().Nodes()
|
||||
addressProvider := nodeAddressProvider{fakeNodeClient}
|
||||
|
||||
// Fail case (no addresses associated with nodes)
|
||||
nodes, _ := fakeNodeClient.List(metav1.ListOptions{})
|
||||
addrs, err := addressProvider.externalAddresses()
|
||||
|
||||
assert.Error(err, "addresses should have caused an error as there are no addresses.")
|
||||
assert.Equal([]string(nil), addrs)
|
||||
|
||||
// Pass case with External type IP
|
||||
nodes, _ = fakeNodeClient.List(metav1.ListOptions{})
|
||||
for index := range nodes.Items {
|
||||
nodes.Items[index].Status.Addresses = []apiv1.NodeAddress{{Type: apiv1.NodeExternalIP, Address: "127.0.0.1"}}
|
||||
fakeNodeClient.Update(&nodes.Items[index])
|
||||
}
|
||||
addrs, err = addressProvider.externalAddresses()
|
||||
assert.NoError(err, "addresses should not have returned an error.")
|
||||
assert.Equal([]string{"127.0.0.1", "127.0.0.1"}, addrs)
|
||||
}
|
||||
|
||||
func decodeResponse(resp *http.Response, obj interface{}) error {
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(data, obj); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Because we need to be backwards compatible with release 1.1, at endpoints
|
||||
// that exist in release 1.1, the responses should have empty APIVersion.
|
||||
func TestAPIVersionOfDiscoveryEndpoints(t *testing.T) {
|
||||
master, etcdserver, _, assert := newMaster(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
server := httptest.NewServer(genericapirequest.WithRequestContext(master.GenericAPIServer.Handler.GoRestfulContainer.ServeMux, master.GenericAPIServer.RequestContextMapper()))
|
||||
|
||||
// /api exists in release-1.1
|
||||
resp, err := http.Get(server.URL + "/api")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
apiVersions := metav1.APIVersions{}
|
||||
assert.NoError(decodeResponse(resp, &apiVersions))
|
||||
assert.Equal(apiVersions.APIVersion, "")
|
||||
|
||||
// /api/v1 exists in release-1.1
|
||||
resp, err = http.Get(server.URL + "/api/v1")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
resourceList := metav1.APIResourceList{}
|
||||
assert.NoError(decodeResponse(resp, &resourceList))
|
||||
assert.Equal(resourceList.APIVersion, "")
|
||||
|
||||
// /apis exists in release-1.1
|
||||
resp, err = http.Get(server.URL + "/apis")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
groupList := metav1.APIGroupList{}
|
||||
assert.NoError(decodeResponse(resp, &groupList))
|
||||
assert.Equal(groupList.APIVersion, "")
|
||||
|
||||
// /apis/extensions exists in release-1.1
|
||||
resp, err = http.Get(server.URL + "/apis/extensions")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
group := metav1.APIGroup{}
|
||||
assert.NoError(decodeResponse(resp, &group))
|
||||
assert.Equal(group.APIVersion, "")
|
||||
|
||||
// /apis/extensions/v1beta1 exists in release-1.1
|
||||
resp, err = http.Get(server.URL + "/apis/extensions/v1beta1")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
resourceList = metav1.APIResourceList{}
|
||||
assert.NoError(decodeResponse(resp, &resourceList))
|
||||
assert.Equal(resourceList.APIVersion, "")
|
||||
|
||||
// /apis/autoscaling doesn't exist in release-1.1, so the APIVersion field
|
||||
// should be non-empty in the results returned by the server.
|
||||
resp, err = http.Get(server.URL + "/apis/autoscaling")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
group = metav1.APIGroup{}
|
||||
assert.NoError(decodeResponse(resp, &group))
|
||||
assert.Equal(group.APIVersion, "v1")
|
||||
|
||||
// apis/autoscaling/v1 doesn't exist in release-1.1, so the APIVersion field
|
||||
// should be non-empty in the results returned by the server.
|
||||
|
||||
resp, err = http.Get(server.URL + "/apis/autoscaling/v1")
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
resourceList = metav1.APIResourceList{}
|
||||
assert.NoError(decodeResponse(resp, &resourceList))
|
||||
assert.Equal(resourceList.APIVersion, "v1")
|
||||
|
||||
}
|
||||
|
||||
func TestNoAlphaVersionsEnabledByDefault(t *testing.T) {
|
||||
config := DefaultAPIResourceConfigSource()
|
||||
for gv, enable := range config.GroupVersionConfigs {
|
||||
if enable && strings.Contains(gv.Version, "alpha") {
|
||||
t.Errorf("Alpha API version %s enabled by default", gv.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
838
vendor/k8s.io/kubernetes/pkg/scheduler/scheduler_test.go
generated
vendored
838
vendor/k8s.io/kubernetes/pkg/scheduler/scheduler_test.go
generated
vendored
|
|
@ -1,838 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 scheduler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
clientcache "k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
"k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
|
||||
"k8s.io/kubernetes/pkg/scheduler/algorithm"
|
||||
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
|
||||
"k8s.io/kubernetes/pkg/scheduler/core"
|
||||
"k8s.io/kubernetes/pkg/scheduler/schedulercache"
|
||||
schedulertesting "k8s.io/kubernetes/pkg/scheduler/testing"
|
||||
"k8s.io/kubernetes/pkg/scheduler/util"
|
||||
"k8s.io/kubernetes/pkg/scheduler/volumebinder"
|
||||
)
|
||||
|
||||
type fakeBinder struct {
|
||||
b func(binding *v1.Binding) error
|
||||
}
|
||||
|
||||
func (fb fakeBinder) Bind(binding *v1.Binding) error { return fb.b(binding) }
|
||||
|
||||
type fakePodConditionUpdater struct{}
|
||||
|
||||
func (fc fakePodConditionUpdater) Update(pod *v1.Pod, podCondition *v1.PodCondition) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakePodPreemptor struct{}
|
||||
|
||||
func (fp fakePodPreemptor) GetUpdatedPod(pod *v1.Pod) (*v1.Pod, error) {
|
||||
return pod, nil
|
||||
}
|
||||
|
||||
func (fp fakePodPreemptor) DeletePod(pod *v1.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fp fakePodPreemptor) SetNominatedNodeName(pod *v1.Pod, nomNodeName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fp fakePodPreemptor) RemoveNominatedNodeName(pod *v1.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func podWithID(id, desiredHost string) *v1.Pod {
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: id,
|
||||
UID: types.UID(id),
|
||||
SelfLink: util.Test.SelfLink(string(v1.ResourcePods), id),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: desiredHost,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func deletingPod(id string) *v1.Pod {
|
||||
deletionTimestamp := metav1.Now()
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: id,
|
||||
UID: types.UID(id),
|
||||
SelfLink: util.Test.SelfLink(string(v1.ResourcePods), id),
|
||||
DeletionTimestamp: &deletionTimestamp,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func podWithPort(id, desiredHost string, port int) *v1.Pod {
|
||||
pod := podWithID(id, desiredHost)
|
||||
pod.Spec.Containers = []v1.Container{
|
||||
{Name: "ctr", Ports: []v1.ContainerPort{{HostPort: int32(port)}}},
|
||||
}
|
||||
return pod
|
||||
}
|
||||
|
||||
func podWithResources(id, desiredHost string, limits v1.ResourceList, requests v1.ResourceList) *v1.Pod {
|
||||
pod := podWithID(id, desiredHost)
|
||||
pod.Spec.Containers = []v1.Container{
|
||||
{Name: "ctr", Resources: v1.ResourceRequirements{Limits: limits, Requests: requests}},
|
||||
}
|
||||
return pod
|
||||
}
|
||||
|
||||
type mockScheduler struct {
|
||||
machine string
|
||||
err error
|
||||
}
|
||||
|
||||
func (es mockScheduler) Schedule(pod *v1.Pod, ml algorithm.NodeLister) (string, error) {
|
||||
return es.machine, es.err
|
||||
}
|
||||
|
||||
func (es mockScheduler) Predicates() map[string]algorithm.FitPredicate {
|
||||
return nil
|
||||
}
|
||||
func (es mockScheduler) Prioritizers() []algorithm.PriorityConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es mockScheduler) Preempt(pod *v1.Pod, nodeLister algorithm.NodeLister, scheduleErr error) (*v1.Node, []*v1.Pod, []*v1.Pod, error) {
|
||||
return nil, nil, nil, nil
|
||||
}
|
||||
|
||||
func TestScheduler(t *testing.T) {
|
||||
eventBroadcaster := record.NewBroadcaster()
|
||||
eventBroadcaster.StartLogging(t.Logf).Stop()
|
||||
errS := errors.New("scheduler")
|
||||
errB := errors.New("binder")
|
||||
testNode := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}}
|
||||
|
||||
table := []struct {
|
||||
injectBindError error
|
||||
sendPod *v1.Pod
|
||||
algo algorithm.ScheduleAlgorithm
|
||||
expectErrorPod *v1.Pod
|
||||
expectForgetPod *v1.Pod
|
||||
expectAssumedPod *v1.Pod
|
||||
expectError error
|
||||
expectBind *v1.Binding
|
||||
eventReason string
|
||||
}{
|
||||
{
|
||||
sendPod: podWithID("foo", ""),
|
||||
algo: mockScheduler{testNode.Name, nil},
|
||||
expectBind: &v1.Binding{ObjectMeta: metav1.ObjectMeta{Name: "foo", UID: types.UID("foo")}, Target: v1.ObjectReference{Kind: "Node", Name: testNode.Name}},
|
||||
expectAssumedPod: podWithID("foo", testNode.Name),
|
||||
eventReason: "Scheduled",
|
||||
}, {
|
||||
sendPod: podWithID("foo", ""),
|
||||
algo: mockScheduler{testNode.Name, errS},
|
||||
expectError: errS,
|
||||
expectErrorPod: podWithID("foo", ""),
|
||||
eventReason: "FailedScheduling",
|
||||
}, {
|
||||
sendPod: podWithID("foo", ""),
|
||||
algo: mockScheduler{testNode.Name, nil},
|
||||
expectBind: &v1.Binding{ObjectMeta: metav1.ObjectMeta{Name: "foo", UID: types.UID("foo")}, Target: v1.ObjectReference{Kind: "Node", Name: testNode.Name}},
|
||||
expectAssumedPod: podWithID("foo", testNode.Name),
|
||||
injectBindError: errB,
|
||||
expectError: errB,
|
||||
expectErrorPod: podWithID("foo", testNode.Name),
|
||||
expectForgetPod: podWithID("foo", testNode.Name),
|
||||
eventReason: "FailedScheduling",
|
||||
}, {
|
||||
sendPod: deletingPod("foo"),
|
||||
algo: mockScheduler{"", nil},
|
||||
eventReason: "FailedScheduling",
|
||||
},
|
||||
}
|
||||
|
||||
for i, item := range table {
|
||||
var gotError error
|
||||
var gotPod *v1.Pod
|
||||
var gotForgetPod *v1.Pod
|
||||
var gotAssumedPod *v1.Pod
|
||||
var gotBinding *v1.Binding
|
||||
configurator := &FakeConfigurator{
|
||||
Config: &Config{
|
||||
SchedulerCache: &schedulertesting.FakeCache{
|
||||
ForgetFunc: func(pod *v1.Pod) {
|
||||
gotForgetPod = pod
|
||||
},
|
||||
AssumeFunc: func(pod *v1.Pod) {
|
||||
gotAssumedPod = pod
|
||||
},
|
||||
},
|
||||
NodeLister: schedulertesting.FakeNodeLister(
|
||||
[]*v1.Node{&testNode},
|
||||
),
|
||||
Algorithm: item.algo,
|
||||
GetBinder: func(pod *v1.Pod) Binder {
|
||||
return fakeBinder{func(b *v1.Binding) error {
|
||||
gotBinding = b
|
||||
return item.injectBindError
|
||||
}}
|
||||
},
|
||||
PodConditionUpdater: fakePodConditionUpdater{},
|
||||
Error: func(p *v1.Pod, err error) {
|
||||
gotPod = p
|
||||
gotError = err
|
||||
},
|
||||
NextPod: func() *v1.Pod {
|
||||
return item.sendPod
|
||||
},
|
||||
Recorder: eventBroadcaster.NewRecorder(legacyscheme.Scheme, v1.EventSource{Component: "scheduler"}),
|
||||
VolumeBinder: volumebinder.NewFakeVolumeBinder(&persistentvolume.FakeVolumeBinderConfig{AllBound: true}),
|
||||
},
|
||||
}
|
||||
|
||||
s, _ := NewFromConfigurator(configurator, nil...)
|
||||
called := make(chan struct{})
|
||||
events := eventBroadcaster.StartEventWatcher(func(e *v1.Event) {
|
||||
if e, a := item.eventReason, e.Reason; e != a {
|
||||
t.Errorf("%v: expected %v, got %v", i, e, a)
|
||||
}
|
||||
close(called)
|
||||
})
|
||||
s.scheduleOne()
|
||||
<-called
|
||||
if e, a := item.expectAssumedPod, gotAssumedPod; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v: assumed pod: wanted %v, got %v", i, e, a)
|
||||
}
|
||||
if e, a := item.expectErrorPod, gotPod; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v: error pod: wanted %v, got %v", i, e, a)
|
||||
}
|
||||
if e, a := item.expectForgetPod, gotForgetPod; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v: forget pod: wanted %v, got %v", i, e, a)
|
||||
}
|
||||
if e, a := item.expectError, gotError; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v: error: wanted %v, got %v", i, e, a)
|
||||
}
|
||||
if e, a := item.expectBind, gotBinding; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%v: error: %s", i, diff.ObjectDiff(e, a))
|
||||
}
|
||||
events.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchedulerNoPhantomPodAfterExpire(t *testing.T) {
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc)
|
||||
scache := schedulercache.New(100*time.Millisecond, stop)
|
||||
pod := podWithPort("pod.Name", "", 8080)
|
||||
node := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}}
|
||||
scache.AddNode(&node)
|
||||
nodeLister := schedulertesting.FakeNodeLister([]*v1.Node{&node})
|
||||
predicateMap := map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts}
|
||||
scheduler, bindingChan, _ := setupTestSchedulerWithOnePodOnNode(t, queuedPodStore, scache, nodeLister, predicateMap, pod, &node)
|
||||
|
||||
waitPodExpireChan := make(chan struct{})
|
||||
timeout := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
return
|
||||
default:
|
||||
}
|
||||
pods, err := scache.List(labels.Everything())
|
||||
if err != nil {
|
||||
t.Fatalf("cache.List failed: %v", err)
|
||||
}
|
||||
if len(pods) == 0 {
|
||||
close(waitPodExpireChan)
|
||||
return
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
// waiting for the assumed pod to expire
|
||||
select {
|
||||
case <-waitPodExpireChan:
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
close(timeout)
|
||||
t.Fatalf("timeout timeout in waiting pod expire after %v", wait.ForeverTestTimeout)
|
||||
}
|
||||
|
||||
// We use conflicted pod ports to incur fit predicate failure if first pod not removed.
|
||||
secondPod := podWithPort("bar", "", 8080)
|
||||
queuedPodStore.Add(secondPod)
|
||||
scheduler.scheduleOne()
|
||||
select {
|
||||
case b := <-bindingChan:
|
||||
expectBinding := &v1.Binding{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar", UID: types.UID("bar")},
|
||||
Target: v1.ObjectReference{Kind: "Node", Name: node.Name},
|
||||
}
|
||||
if !reflect.DeepEqual(expectBinding, b) {
|
||||
t.Errorf("binding want=%v, get=%v", expectBinding, b)
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Fatalf("timeout in binding after %v", wait.ForeverTestTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchedulerNoPhantomPodAfterDelete(t *testing.T) {
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc)
|
||||
scache := schedulercache.New(10*time.Minute, stop)
|
||||
firstPod := podWithPort("pod.Name", "", 8080)
|
||||
node := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}}
|
||||
scache.AddNode(&node)
|
||||
nodeLister := schedulertesting.FakeNodeLister([]*v1.Node{&node})
|
||||
predicateMap := map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts}
|
||||
scheduler, bindingChan, errChan := setupTestSchedulerWithOnePodOnNode(t, queuedPodStore, scache, nodeLister, predicateMap, firstPod, &node)
|
||||
|
||||
// We use conflicted pod ports to incur fit predicate failure.
|
||||
secondPod := podWithPort("bar", "", 8080)
|
||||
queuedPodStore.Add(secondPod)
|
||||
// queuedPodStore: [bar:8080]
|
||||
// cache: [(assumed)foo:8080]
|
||||
|
||||
scheduler.scheduleOne()
|
||||
select {
|
||||
case err := <-errChan:
|
||||
expectErr := &core.FitError{
|
||||
Pod: secondPod,
|
||||
NumAllNodes: 1,
|
||||
FailedPredicates: core.FailedPredicateMap{node.Name: []algorithm.PredicateFailureReason{predicates.ErrPodNotFitsHostPorts}},
|
||||
}
|
||||
if !reflect.DeepEqual(expectErr, err) {
|
||||
t.Errorf("err want=%v, get=%v", expectErr, err)
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Fatalf("timeout in fitting after %v", wait.ForeverTestTimeout)
|
||||
}
|
||||
|
||||
// We mimic the workflow of cache behavior when a pod is removed by user.
|
||||
// Note: if the schedulercache timeout would be super short, the first pod would expire
|
||||
// and would be removed itself (without any explicit actions on schedulercache). Even in that case,
|
||||
// explicitly AddPod will as well correct the behavior.
|
||||
firstPod.Spec.NodeName = node.Name
|
||||
if err := scache.AddPod(firstPod); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if err := scache.RemovePod(firstPod); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
queuedPodStore.Add(secondPod)
|
||||
scheduler.scheduleOne()
|
||||
select {
|
||||
case b := <-bindingChan:
|
||||
expectBinding := &v1.Binding{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar", UID: types.UID("bar")},
|
||||
Target: v1.ObjectReference{Kind: "Node", Name: node.Name},
|
||||
}
|
||||
if !reflect.DeepEqual(expectBinding, b) {
|
||||
t.Errorf("binding want=%v, get=%v", expectBinding, b)
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Fatalf("timeout in binding after %v", wait.ForeverTestTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
// Scheduler should preserve predicate constraint even if binding was longer
|
||||
// than cache ttl
|
||||
func TestSchedulerErrorWithLongBinding(t *testing.T) {
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
|
||||
firstPod := podWithPort("foo", "", 8080)
|
||||
conflictPod := podWithPort("bar", "", 8080)
|
||||
pods := map[string]*v1.Pod{firstPod.Name: firstPod, conflictPod.Name: conflictPod}
|
||||
for _, test := range []struct {
|
||||
Expected map[string]bool
|
||||
CacheTTL time.Duration
|
||||
BindingDuration time.Duration
|
||||
}{
|
||||
{
|
||||
Expected: map[string]bool{firstPod.Name: true},
|
||||
CacheTTL: 100 * time.Millisecond,
|
||||
BindingDuration: 300 * time.Millisecond,
|
||||
},
|
||||
{
|
||||
Expected: map[string]bool{firstPod.Name: true},
|
||||
CacheTTL: 10 * time.Second,
|
||||
BindingDuration: 300 * time.Millisecond,
|
||||
},
|
||||
} {
|
||||
queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc)
|
||||
scache := schedulercache.New(test.CacheTTL, stop)
|
||||
|
||||
node := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}}
|
||||
scache.AddNode(&node)
|
||||
|
||||
nodeLister := schedulertesting.FakeNodeLister([]*v1.Node{&node})
|
||||
predicateMap := map[string]algorithm.FitPredicate{"PodFitsHostPorts": predicates.PodFitsHostPorts}
|
||||
|
||||
scheduler, bindingChan := setupTestSchedulerLongBindingWithRetry(
|
||||
queuedPodStore, scache, nodeLister, predicateMap, stop, test.BindingDuration)
|
||||
scheduler.Run()
|
||||
queuedPodStore.Add(firstPod)
|
||||
queuedPodStore.Add(conflictPod)
|
||||
|
||||
resultBindings := map[string]bool{}
|
||||
waitChan := time.After(5 * time.Second)
|
||||
for finished := false; !finished; {
|
||||
select {
|
||||
case b := <-bindingChan:
|
||||
resultBindings[b.Name] = true
|
||||
p := pods[b.Name]
|
||||
p.Spec.NodeName = b.Target.Name
|
||||
scache.AddPod(p)
|
||||
case <-waitChan:
|
||||
finished = true
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(resultBindings, test.Expected) {
|
||||
t.Errorf("Result binding are not equal to expected. %v != %v", resultBindings, test.Expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// queuedPodStore: pods queued before processing.
|
||||
// cache: scheduler cache that might contain assumed pods.
|
||||
func setupTestSchedulerWithOnePodOnNode(t *testing.T, queuedPodStore *clientcache.FIFO, scache schedulercache.Cache,
|
||||
nodeLister schedulertesting.FakeNodeLister, predicateMap map[string]algorithm.FitPredicate, pod *v1.Pod, node *v1.Node) (*Scheduler, chan *v1.Binding, chan error) {
|
||||
|
||||
scheduler, bindingChan, errChan := setupTestScheduler(queuedPodStore, scache, nodeLister, predicateMap, nil)
|
||||
|
||||
queuedPodStore.Add(pod)
|
||||
// queuedPodStore: [foo:8080]
|
||||
// cache: []
|
||||
|
||||
scheduler.scheduleOne()
|
||||
// queuedPodStore: []
|
||||
// cache: [(assumed)foo:8080]
|
||||
|
||||
select {
|
||||
case b := <-bindingChan:
|
||||
expectBinding := &v1.Binding{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: pod.Name, UID: types.UID(pod.Name)},
|
||||
Target: v1.ObjectReference{Kind: "Node", Name: node.Name},
|
||||
}
|
||||
if !reflect.DeepEqual(expectBinding, b) {
|
||||
t.Errorf("binding want=%v, get=%v", expectBinding, b)
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Fatalf("timeout after %v", wait.ForeverTestTimeout)
|
||||
}
|
||||
return scheduler, bindingChan, errChan
|
||||
}
|
||||
|
||||
func TestSchedulerFailedSchedulingReasons(t *testing.T) {
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc)
|
||||
scache := schedulercache.New(10*time.Minute, stop)
|
||||
|
||||
// Design the baseline for the pods, and we will make nodes that dont fit it later.
|
||||
var cpu = int64(4)
|
||||
var mem = int64(500)
|
||||
podWithTooBigResourceRequests := podWithResources("bar", "", v1.ResourceList{
|
||||
v1.ResourceCPU: *(resource.NewQuantity(cpu, resource.DecimalSI)),
|
||||
v1.ResourceMemory: *(resource.NewQuantity(mem, resource.DecimalSI)),
|
||||
}, v1.ResourceList{
|
||||
v1.ResourceCPU: *(resource.NewQuantity(cpu, resource.DecimalSI)),
|
||||
v1.ResourceMemory: *(resource.NewQuantity(mem, resource.DecimalSI)),
|
||||
})
|
||||
|
||||
// create several nodes which cannot schedule the above pod
|
||||
nodes := []*v1.Node{}
|
||||
for i := 0; i < 100; i++ {
|
||||
uid := fmt.Sprintf("machine%v", i)
|
||||
node := v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: uid, UID: types.UID(uid)},
|
||||
Status: v1.NodeStatus{
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourceCPU: *(resource.NewQuantity(cpu/2, resource.DecimalSI)),
|
||||
v1.ResourceMemory: *(resource.NewQuantity(mem/5, resource.DecimalSI)),
|
||||
v1.ResourcePods: *(resource.NewQuantity(10, resource.DecimalSI)),
|
||||
},
|
||||
Allocatable: v1.ResourceList{
|
||||
v1.ResourceCPU: *(resource.NewQuantity(cpu/2, resource.DecimalSI)),
|
||||
v1.ResourceMemory: *(resource.NewQuantity(mem/5, resource.DecimalSI)),
|
||||
v1.ResourcePods: *(resource.NewQuantity(10, resource.DecimalSI)),
|
||||
}},
|
||||
}
|
||||
scache.AddNode(&node)
|
||||
nodes = append(nodes, &node)
|
||||
}
|
||||
nodeLister := schedulertesting.FakeNodeLister(nodes)
|
||||
predicateMap := map[string]algorithm.FitPredicate{
|
||||
"PodFitsResources": predicates.PodFitsResources,
|
||||
}
|
||||
|
||||
// Create expected failure reasons for all the nodes. Hopefully they will get rolled up into a non-spammy summary.
|
||||
failedPredicatesMap := core.FailedPredicateMap{}
|
||||
for _, node := range nodes {
|
||||
failedPredicatesMap[node.Name] = []algorithm.PredicateFailureReason{
|
||||
predicates.NewInsufficientResourceError(v1.ResourceCPU, 4000, 0, 2000),
|
||||
predicates.NewInsufficientResourceError(v1.ResourceMemory, 500, 0, 100),
|
||||
}
|
||||
}
|
||||
scheduler, _, errChan := setupTestScheduler(queuedPodStore, scache, nodeLister, predicateMap, nil)
|
||||
|
||||
queuedPodStore.Add(podWithTooBigResourceRequests)
|
||||
scheduler.scheduleOne()
|
||||
select {
|
||||
case err := <-errChan:
|
||||
expectErr := &core.FitError{
|
||||
Pod: podWithTooBigResourceRequests,
|
||||
NumAllNodes: len(nodes),
|
||||
FailedPredicates: failedPredicatesMap,
|
||||
}
|
||||
if len(fmt.Sprint(expectErr)) > 150 {
|
||||
t.Errorf("message is too spammy ! %v ", len(fmt.Sprint(expectErr)))
|
||||
}
|
||||
if !reflect.DeepEqual(expectErr, err) {
|
||||
t.Errorf("\n err \nWANT=%+v,\nGOT=%+v", expectErr, err)
|
||||
}
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Fatalf("timeout after %v", wait.ForeverTestTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
// queuedPodStore: pods queued before processing.
|
||||
// scache: scheduler cache that might contain assumed pods.
|
||||
func setupTestScheduler(queuedPodStore *clientcache.FIFO, scache schedulercache.Cache, nodeLister schedulertesting.FakeNodeLister, predicateMap map[string]algorithm.FitPredicate, recorder record.EventRecorder) (*Scheduler, chan *v1.Binding, chan error) {
|
||||
algo := core.NewGenericScheduler(
|
||||
scache,
|
||||
nil,
|
||||
nil,
|
||||
predicateMap,
|
||||
algorithm.EmptyPredicateMetadataProducer,
|
||||
[]algorithm.PriorityConfig{},
|
||||
algorithm.EmptyPriorityMetadataProducer,
|
||||
[]algorithm.SchedulerExtender{},
|
||||
nil,
|
||||
schedulertesting.FakePersistentVolumeClaimLister{},
|
||||
false)
|
||||
bindingChan := make(chan *v1.Binding, 1)
|
||||
errChan := make(chan error, 1)
|
||||
configurator := &FakeConfigurator{
|
||||
Config: &Config{
|
||||
SchedulerCache: scache,
|
||||
NodeLister: nodeLister,
|
||||
Algorithm: algo,
|
||||
GetBinder: func(pod *v1.Pod) Binder {
|
||||
return fakeBinder{func(b *v1.Binding) error {
|
||||
bindingChan <- b
|
||||
return nil
|
||||
}}
|
||||
},
|
||||
NextPod: func() *v1.Pod {
|
||||
return clientcache.Pop(queuedPodStore).(*v1.Pod)
|
||||
},
|
||||
Error: func(p *v1.Pod, err error) {
|
||||
errChan <- err
|
||||
},
|
||||
Recorder: &record.FakeRecorder{},
|
||||
PodConditionUpdater: fakePodConditionUpdater{},
|
||||
PodPreemptor: fakePodPreemptor{},
|
||||
VolumeBinder: volumebinder.NewFakeVolumeBinder(&persistentvolume.FakeVolumeBinderConfig{AllBound: true}),
|
||||
},
|
||||
}
|
||||
|
||||
if recorder != nil {
|
||||
configurator.Config.Recorder = recorder
|
||||
}
|
||||
|
||||
sched, _ := NewFromConfigurator(configurator, nil...)
|
||||
|
||||
return sched, bindingChan, errChan
|
||||
}
|
||||
|
||||
func setupTestSchedulerLongBindingWithRetry(queuedPodStore *clientcache.FIFO, scache schedulercache.Cache, nodeLister schedulertesting.FakeNodeLister, predicateMap map[string]algorithm.FitPredicate, stop chan struct{}, bindingTime time.Duration) (*Scheduler, chan *v1.Binding) {
|
||||
algo := core.NewGenericScheduler(
|
||||
scache,
|
||||
nil,
|
||||
nil,
|
||||
predicateMap,
|
||||
algorithm.EmptyPredicateMetadataProducer,
|
||||
[]algorithm.PriorityConfig{},
|
||||
algorithm.EmptyPriorityMetadataProducer,
|
||||
[]algorithm.SchedulerExtender{},
|
||||
nil,
|
||||
schedulertesting.FakePersistentVolumeClaimLister{},
|
||||
false)
|
||||
bindingChan := make(chan *v1.Binding, 2)
|
||||
configurator := &FakeConfigurator{
|
||||
Config: &Config{
|
||||
SchedulerCache: scache,
|
||||
NodeLister: nodeLister,
|
||||
Algorithm: algo,
|
||||
GetBinder: func(pod *v1.Pod) Binder {
|
||||
return fakeBinder{func(b *v1.Binding) error {
|
||||
time.Sleep(bindingTime)
|
||||
bindingChan <- b
|
||||
return nil
|
||||
}}
|
||||
},
|
||||
WaitForCacheSync: func() bool {
|
||||
return true
|
||||
},
|
||||
NextPod: func() *v1.Pod {
|
||||
return clientcache.Pop(queuedPodStore).(*v1.Pod)
|
||||
},
|
||||
Error: func(p *v1.Pod, err error) {
|
||||
queuedPodStore.AddIfNotPresent(p)
|
||||
},
|
||||
Recorder: &record.FakeRecorder{},
|
||||
PodConditionUpdater: fakePodConditionUpdater{},
|
||||
PodPreemptor: fakePodPreemptor{},
|
||||
StopEverything: stop,
|
||||
VolumeBinder: volumebinder.NewFakeVolumeBinder(&persistentvolume.FakeVolumeBinderConfig{AllBound: true}),
|
||||
},
|
||||
}
|
||||
|
||||
sched, _ := NewFromConfigurator(configurator, nil...)
|
||||
|
||||
return sched, bindingChan
|
||||
}
|
||||
|
||||
func setupTestSchedulerWithVolumeBinding(fakeVolumeBinder *volumebinder.VolumeBinder, stop <-chan struct{}, broadcaster record.EventBroadcaster) (*Scheduler, chan *v1.Binding, chan error) {
|
||||
testNode := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "machine1", UID: types.UID("machine1")}}
|
||||
nodeLister := schedulertesting.FakeNodeLister([]*v1.Node{&testNode})
|
||||
queuedPodStore := clientcache.NewFIFO(clientcache.MetaNamespaceKeyFunc)
|
||||
queuedPodStore.Add(podWithID("foo", ""))
|
||||
scache := schedulercache.New(10*time.Minute, stop)
|
||||
scache.AddNode(&testNode)
|
||||
|
||||
predicateMap := map[string]algorithm.FitPredicate{
|
||||
predicates.CheckVolumeBindingPred: predicates.NewVolumeBindingPredicate(fakeVolumeBinder),
|
||||
}
|
||||
|
||||
recorder := broadcaster.NewRecorder(legacyscheme.Scheme, v1.EventSource{Component: "scheduler"})
|
||||
s, bindingChan, errChan := setupTestScheduler(queuedPodStore, scache, nodeLister, predicateMap, recorder)
|
||||
s.config.VolumeBinder = fakeVolumeBinder
|
||||
return s, bindingChan, errChan
|
||||
}
|
||||
|
||||
// This is a workaround because golint complains that errors cannot
|
||||
// end with punctuation. However, the real predicate error message does
|
||||
// end with a period.
|
||||
func makePredicateError(failReason string) error {
|
||||
s := fmt.Sprintf("0/1 nodes are available: %v.", failReason)
|
||||
return fmt.Errorf(s)
|
||||
}
|
||||
|
||||
func TestSchedulerWithVolumeBinding(t *testing.T) {
|
||||
order := []string{predicates.CheckVolumeBindingPred, predicates.GeneralPred}
|
||||
predicates.SetPredicatesOrdering(order)
|
||||
findErr := fmt.Errorf("find err")
|
||||
assumeErr := fmt.Errorf("assume err")
|
||||
bindErr := fmt.Errorf("bind err")
|
||||
|
||||
eventBroadcaster := record.NewBroadcaster()
|
||||
eventBroadcaster.StartLogging(t.Logf).Stop()
|
||||
|
||||
// This can be small because we wait for pod to finish scheduling first
|
||||
chanTimeout := 2 * time.Second
|
||||
|
||||
utilfeature.DefaultFeatureGate.Set("VolumeScheduling=true")
|
||||
defer utilfeature.DefaultFeatureGate.Set("VolumeScheduling=false")
|
||||
|
||||
table := map[string]struct {
|
||||
expectError error
|
||||
expectPodBind *v1.Binding
|
||||
expectAssumeCalled bool
|
||||
expectBindCalled bool
|
||||
eventReason string
|
||||
volumeBinderConfig *persistentvolume.FakeVolumeBinderConfig
|
||||
}{
|
||||
"all-bound": {
|
||||
volumeBinderConfig: &persistentvolume.FakeVolumeBinderConfig{
|
||||
AllBound: true,
|
||||
FindUnboundSatsified: true,
|
||||
FindBoundSatsified: true,
|
||||
},
|
||||
expectAssumeCalled: true,
|
||||
expectPodBind: &v1.Binding{ObjectMeta: metav1.ObjectMeta{Name: "foo", UID: types.UID("foo")}, Target: v1.ObjectReference{Kind: "Node", Name: "machine1"}},
|
||||
|
||||
eventReason: "Scheduled",
|
||||
},
|
||||
"bound,invalid-pv-affinity": {
|
||||
volumeBinderConfig: &persistentvolume.FakeVolumeBinderConfig{
|
||||
AllBound: true,
|
||||
FindUnboundSatsified: true,
|
||||
FindBoundSatsified: false,
|
||||
},
|
||||
eventReason: "FailedScheduling",
|
||||
expectError: makePredicateError("1 node(s) had volume node affinity conflict"),
|
||||
},
|
||||
"unbound,no-matches": {
|
||||
volumeBinderConfig: &persistentvolume.FakeVolumeBinderConfig{
|
||||
FindUnboundSatsified: false,
|
||||
FindBoundSatsified: true,
|
||||
},
|
||||
eventReason: "FailedScheduling",
|
||||
expectError: makePredicateError("1 node(s) didn't find available persistent volumes to bind"),
|
||||
},
|
||||
"bound-and-unbound-unsatisfied": {
|
||||
volumeBinderConfig: &persistentvolume.FakeVolumeBinderConfig{
|
||||
FindUnboundSatsified: false,
|
||||
FindBoundSatsified: false,
|
||||
},
|
||||
eventReason: "FailedScheduling",
|
||||
expectError: makePredicateError("1 node(s) didn't find available persistent volumes to bind, 1 node(s) had volume node affinity conflict"),
|
||||
},
|
||||
"unbound,found-matches": {
|
||||
volumeBinderConfig: &persistentvolume.FakeVolumeBinderConfig{
|
||||
FindUnboundSatsified: true,
|
||||
FindBoundSatsified: true,
|
||||
AssumeBindingRequired: true,
|
||||
},
|
||||
expectAssumeCalled: true,
|
||||
expectBindCalled: true,
|
||||
eventReason: "FailedScheduling",
|
||||
expectError: fmt.Errorf("Volume binding started, waiting for completion"),
|
||||
},
|
||||
"unbound,found-matches,already-bound": {
|
||||
volumeBinderConfig: &persistentvolume.FakeVolumeBinderConfig{
|
||||
FindUnboundSatsified: true,
|
||||
FindBoundSatsified: true,
|
||||
AssumeBindingRequired: false,
|
||||
},
|
||||
expectAssumeCalled: true,
|
||||
expectBindCalled: false,
|
||||
eventReason: "FailedScheduling",
|
||||
expectError: fmt.Errorf("Volume binding started, waiting for completion"),
|
||||
},
|
||||
"predicate-error": {
|
||||
volumeBinderConfig: &persistentvolume.FakeVolumeBinderConfig{
|
||||
FindErr: findErr,
|
||||
},
|
||||
eventReason: "FailedScheduling",
|
||||
expectError: findErr,
|
||||
},
|
||||
"assume-error": {
|
||||
volumeBinderConfig: &persistentvolume.FakeVolumeBinderConfig{
|
||||
FindUnboundSatsified: true,
|
||||
FindBoundSatsified: true,
|
||||
AssumeErr: assumeErr,
|
||||
},
|
||||
expectAssumeCalled: true,
|
||||
eventReason: "FailedScheduling",
|
||||
expectError: assumeErr,
|
||||
},
|
||||
"bind-error": {
|
||||
volumeBinderConfig: &persistentvolume.FakeVolumeBinderConfig{
|
||||
FindUnboundSatsified: true,
|
||||
FindBoundSatsified: true,
|
||||
AssumeBindingRequired: true,
|
||||
BindErr: bindErr,
|
||||
},
|
||||
expectAssumeCalled: true,
|
||||
expectBindCalled: true,
|
||||
eventReason: "FailedScheduling",
|
||||
expectError: bindErr,
|
||||
},
|
||||
}
|
||||
|
||||
for name, item := range table {
|
||||
stop := make(chan struct{})
|
||||
fakeVolumeBinder := volumebinder.NewFakeVolumeBinder(item.volumeBinderConfig)
|
||||
internalBinder, ok := fakeVolumeBinder.Binder.(*persistentvolume.FakeVolumeBinder)
|
||||
if !ok {
|
||||
t.Fatalf("Failed to get fake volume binder")
|
||||
}
|
||||
s, bindingChan, errChan := setupTestSchedulerWithVolumeBinding(fakeVolumeBinder, stop, eventBroadcaster)
|
||||
|
||||
eventChan := make(chan struct{})
|
||||
events := eventBroadcaster.StartEventWatcher(func(e *v1.Event) {
|
||||
if e, a := item.eventReason, e.Reason; e != a {
|
||||
t.Errorf("%v: expected %v, got %v", name, e, a)
|
||||
}
|
||||
close(eventChan)
|
||||
})
|
||||
|
||||
go fakeVolumeBinder.Run(s.bindVolumesWorker, stop)
|
||||
|
||||
s.scheduleOne()
|
||||
|
||||
// Wait for pod to succeed or fail scheduling
|
||||
select {
|
||||
case <-eventChan:
|
||||
case <-time.After(wait.ForeverTestTimeout):
|
||||
t.Fatalf("%v: scheduling timeout after %v", name, wait.ForeverTestTimeout)
|
||||
}
|
||||
|
||||
events.Stop()
|
||||
|
||||
// Wait for scheduling to return an error
|
||||
select {
|
||||
case err := <-errChan:
|
||||
if item.expectError == nil || !reflect.DeepEqual(item.expectError.Error(), err.Error()) {
|
||||
t.Errorf("%v: \n err \nWANT=%+v,\nGOT=%+v", name, item.expectError, err)
|
||||
}
|
||||
case <-time.After(chanTimeout):
|
||||
if item.expectError != nil {
|
||||
t.Errorf("%v: did not receive error after %v", name, chanTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for pod to succeed binding
|
||||
select {
|
||||
case b := <-bindingChan:
|
||||
if !reflect.DeepEqual(item.expectPodBind, b) {
|
||||
t.Errorf("%v: \n err \nWANT=%+v,\nGOT=%+v", name, item.expectPodBind, b)
|
||||
}
|
||||
case <-time.After(chanTimeout):
|
||||
if item.expectPodBind != nil {
|
||||
t.Errorf("%v: did not receive pod binding after %v", name, chanTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
if item.expectAssumeCalled != internalBinder.AssumeCalled {
|
||||
t.Errorf("%v: expectedAssumeCall %v", name, item.expectAssumeCalled)
|
||||
}
|
||||
|
||||
if item.expectBindCalled != internalBinder.BindCalled {
|
||||
t.Errorf("%v: expectedBindCall %v", name, item.expectBindCalled)
|
||||
}
|
||||
|
||||
close(stop)
|
||||
}
|
||||
}
|
||||
198
vendor/k8s.io/kubernetes/pkg/security/apparmor/validate_test.go
generated
vendored
198
vendor/k8s.io/kubernetes/pkg/security/apparmor/validate_test.go
generated
vendored
|
|
@ -1,198 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 apparmor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetAppArmorFS(t *testing.T) {
|
||||
// This test only passes on systems running AppArmor with the default configuration.
|
||||
// The test should be manually run if modifying the getAppArmorFS function.
|
||||
t.Skip()
|
||||
|
||||
const expectedPath = "/sys/kernel/security/apparmor"
|
||||
actualPath, err := getAppArmorFS()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expectedPath, actualPath)
|
||||
}
|
||||
|
||||
func TestValidateHost(t *testing.T) {
|
||||
// This test only passes on systems running AppArmor with the default configuration.
|
||||
// The test should be manually run if modifying the getAppArmorFS function.
|
||||
t.Skip()
|
||||
|
||||
assert.NoError(t, validateHost("docker"))
|
||||
assert.Error(t, validateHost("rkt"))
|
||||
}
|
||||
|
||||
func TestValidateProfile(t *testing.T) {
|
||||
loadedProfiles := map[string]bool{
|
||||
"docker-default": true,
|
||||
"foo-bar": true,
|
||||
"baz": true,
|
||||
"/usr/sbin/ntpd": true,
|
||||
"/usr/lib/connman/scripts/dhclient-script": true,
|
||||
"/usr/lib/NetworkManager/nm-dhcp-client.action": true,
|
||||
"/usr/bin/evince-previewer//sanitized_helper": true,
|
||||
}
|
||||
tests := []struct {
|
||||
profile string
|
||||
expectValid bool
|
||||
}{
|
||||
{"", true},
|
||||
{ProfileRuntimeDefault, true},
|
||||
{ProfileNameUnconfined, true},
|
||||
{"baz", false}, // Missing local prefix.
|
||||
{ProfileNamePrefix + "/usr/sbin/ntpd", true},
|
||||
{ProfileNamePrefix + "foo-bar", true},
|
||||
{ProfileNamePrefix + "unloaded", false}, // Not loaded.
|
||||
{ProfileNamePrefix + "", false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := validateProfile(test.profile, loadedProfiles)
|
||||
if test.expectValid {
|
||||
assert.NoError(t, err, "Profile %s should be valid", test.profile)
|
||||
} else {
|
||||
assert.Error(t, err, fmt.Sprintf("Profile %s should not be valid", test.profile))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateBadHost(t *testing.T) {
|
||||
hostErr := errors.New("expected host error")
|
||||
v := &validator{
|
||||
validateHostErr: hostErr,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
profile string
|
||||
expectValid bool
|
||||
}{
|
||||
{"", true},
|
||||
{ProfileRuntimeDefault, false},
|
||||
{ProfileNamePrefix + "docker-default", false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := v.Validate(getPodWithProfile(test.profile))
|
||||
if test.expectValid {
|
||||
assert.NoError(t, err, "Pod with profile %q should be valid", test.profile)
|
||||
} else {
|
||||
assert.Equal(t, hostErr, err, "Pod with profile %q should trigger a host validation error", test.profile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateValidHost(t *testing.T) {
|
||||
v := &validator{
|
||||
appArmorFS: "./testdata/",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
profile string
|
||||
expectValid bool
|
||||
}{
|
||||
{"", true},
|
||||
{ProfileRuntimeDefault, true},
|
||||
{ProfileNamePrefix + "docker-default", true},
|
||||
{ProfileNamePrefix + "foo-container", true},
|
||||
{ProfileNamePrefix + "/usr/sbin/ntpd", true},
|
||||
{"docker-default", false},
|
||||
{ProfileNamePrefix + "foo", false},
|
||||
{ProfileNamePrefix + "", false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := v.Validate(getPodWithProfile(test.profile))
|
||||
if test.expectValid {
|
||||
assert.NoError(t, err, "Pod with profile %q should be valid", test.profile)
|
||||
} else {
|
||||
assert.Error(t, err, fmt.Sprintf("Pod with profile %q should trigger a validation error", test.profile))
|
||||
}
|
||||
}
|
||||
|
||||
// Test multi-container pod.
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: map[string]string{
|
||||
ContainerAnnotationKeyPrefix + "init": ProfileNamePrefix + "foo-container",
|
||||
ContainerAnnotationKeyPrefix + "test1": ProfileRuntimeDefault,
|
||||
ContainerAnnotationKeyPrefix + "test2": ProfileNamePrefix + "docker-default",
|
||||
},
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
InitContainers: []v1.Container{
|
||||
{Name: "init"},
|
||||
},
|
||||
Containers: []v1.Container{
|
||||
{Name: "test1"},
|
||||
{Name: "test2"},
|
||||
{Name: "no-profile"},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.NoError(t, v.Validate(pod), "Multi-container pod should validate")
|
||||
for k, val := range pod.Annotations {
|
||||
pod.Annotations[k] = val + "-bad"
|
||||
assert.Error(t, v.Validate(pod), fmt.Sprintf("Multi-container pod with invalid profile %s:%s", k, pod.Annotations[k]))
|
||||
pod.Annotations[k] = val // Restore.
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseProfileName(t *testing.T) {
|
||||
tests := []struct{ line, expected string }{
|
||||
{"foo://bar/baz (kill)", "foo://bar/baz"},
|
||||
{"foo-bar (enforce)", "foo-bar"},
|
||||
{"/usr/foo/bar/baz (complain)", "/usr/foo/bar/baz"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
name := parseProfileName(test.line)
|
||||
assert.Equal(t, test.expected, name, "Parsing %s", test.line)
|
||||
}
|
||||
}
|
||||
|
||||
func getPodWithProfile(profile string) *v1.Pod {
|
||||
annotations := map[string]string{
|
||||
ContainerAnnotationKeyPrefix + "test": profile,
|
||||
}
|
||||
if profile == "" {
|
||||
annotations = map[string]string{
|
||||
"foo": "bar",
|
||||
}
|
||||
}
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Annotations: annotations,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
184
vendor/k8s.io/kubernetes/pkg/serviceaccount/claims_test.go
generated
vendored
184
vendor/k8s.io/kubernetes/pkg/serviceaccount/claims_test.go
generated
vendored
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 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 serviceaccount
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
|
||||
"gopkg.in/square/go-jose.v2/jwt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
now = func() time.Time {
|
||||
// epoch time: 1514764800
|
||||
return time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClaims(t *testing.T) {
|
||||
sa := core.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "myns",
|
||||
Name: "mysvcacct",
|
||||
UID: "mysvcacct-uid",
|
||||
},
|
||||
}
|
||||
pod := &core.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "myns",
|
||||
Name: "mypod",
|
||||
UID: "mypod-uid",
|
||||
},
|
||||
}
|
||||
sec := &core.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "myns",
|
||||
Name: "mysecret",
|
||||
UID: "mysecret-uid",
|
||||
},
|
||||
}
|
||||
cs := []struct {
|
||||
// input
|
||||
sa core.ServiceAccount
|
||||
pod *core.Pod
|
||||
sec *core.Secret
|
||||
exp int64
|
||||
aud []string
|
||||
// desired
|
||||
sc *jwt.Claims
|
||||
pc *privateClaims
|
||||
}{
|
||||
{
|
||||
// pod and secret
|
||||
sa: sa,
|
||||
pod: pod,
|
||||
sec: sec,
|
||||
// really fast
|
||||
exp: 0,
|
||||
// nil audience
|
||||
aud: nil,
|
||||
|
||||
sc: &jwt.Claims{
|
||||
Subject: "system:serviceaccount:myns:mysvcacct",
|
||||
IssuedAt: jwt.NumericDate(1514764800),
|
||||
NotBefore: jwt.NumericDate(1514764800),
|
||||
Expiry: jwt.NumericDate(1514764800),
|
||||
},
|
||||
pc: &privateClaims{
|
||||
Kubernetes: kubernetes{
|
||||
Namespace: "myns",
|
||||
Svcacct: ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
|
||||
Pod: &ref{Name: "mypod", UID: "mypod-uid"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// pod
|
||||
sa: sa,
|
||||
pod: pod,
|
||||
// empty audience
|
||||
aud: []string{},
|
||||
exp: 100,
|
||||
|
||||
sc: &jwt.Claims{
|
||||
Subject: "system:serviceaccount:myns:mysvcacct",
|
||||
IssuedAt: jwt.NumericDate(1514764800),
|
||||
NotBefore: jwt.NumericDate(1514764800),
|
||||
Expiry: jwt.NumericDate(1514764800 + 100),
|
||||
},
|
||||
pc: &privateClaims{
|
||||
Kubernetes: kubernetes{
|
||||
Namespace: "myns",
|
||||
Svcacct: ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
|
||||
Pod: &ref{Name: "mypod", UID: "mypod-uid"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// secret
|
||||
sa: sa,
|
||||
sec: sec,
|
||||
exp: 100,
|
||||
// single member audience
|
||||
aud: []string{"1"},
|
||||
|
||||
sc: &jwt.Claims{
|
||||
Subject: "system:serviceaccount:myns:mysvcacct",
|
||||
Audience: []string{"1"},
|
||||
IssuedAt: jwt.NumericDate(1514764800),
|
||||
NotBefore: jwt.NumericDate(1514764800),
|
||||
Expiry: jwt.NumericDate(1514764800 + 100),
|
||||
},
|
||||
pc: &privateClaims{
|
||||
Kubernetes: kubernetes{
|
||||
Namespace: "myns",
|
||||
Svcacct: ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
|
||||
Secret: &ref{Name: "mysecret", UID: "mysecret-uid"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// no obj binding
|
||||
sa: sa,
|
||||
exp: 100,
|
||||
// multimember audience
|
||||
aud: []string{"1", "2"},
|
||||
|
||||
sc: &jwt.Claims{
|
||||
Subject: "system:serviceaccount:myns:mysvcacct",
|
||||
Audience: []string{"1", "2"},
|
||||
IssuedAt: jwt.NumericDate(1514764800),
|
||||
NotBefore: jwt.NumericDate(1514764800),
|
||||
Expiry: jwt.NumericDate(1514764800 + 100),
|
||||
},
|
||||
pc: &privateClaims{
|
||||
Kubernetes: kubernetes{
|
||||
Namespace: "myns",
|
||||
Svcacct: ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, c := range cs {
|
||||
t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
|
||||
// comparing json spews has the benefit over
|
||||
// reflect.DeepEqual that we are also asserting that
|
||||
// claims structs are json serializable
|
||||
spew := func(obj interface{}) string {
|
||||
b, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
t.Fatalf("err, couldn't marshal claims: %v", err)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
sc, pc := Claims(c.sa, c.pod, c.sec, c.exp, c.aud)
|
||||
if spew(sc) != spew(c.sc) {
|
||||
t.Errorf("standard claims differed\n\tsaw:\t%s\n\twant:\t%s", spew(sc), spew(c.sc))
|
||||
}
|
||||
if spew(pc) != spew(c.pc) {
|
||||
t.Errorf("private claims differed\n\tsaw: %s\n\twant: %s", spew(pc), spew(c.pc))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
325
vendor/k8s.io/kubernetes/pkg/serviceaccount/jwt_test.go
generated
vendored
325
vendor/k8s.io/kubernetes/pkg/serviceaccount/jwt_test.go
generated
vendored
|
|
@ -1,325 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 serviceaccount_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
|
||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
||||
)
|
||||
|
||||
const otherPublicKey = `-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArXz0QkIG1B5Bj2/W69GH
|
||||
rsm5e+RC3kE+VTgocge0atqlLBek35tRqLgUi3AcIrBZ/0YctMSWDVcRt5fkhWwe
|
||||
Lqjj6qvAyNyOkrkBi1NFDpJBjYJtuKHgRhNxXbOzTSNpdSKXTfOkzqv56MwHOP25
|
||||
yP/NNAODUtr92D5ySI5QX8RbXW+uDn+ixul286PBW/BCrE4tuS88dA0tYJPf8LCu
|
||||
sqQOwlXYH/rNUg4Pyl9xxhR5DIJR0OzNNfChjw60zieRIt2LfM83fXhwk8IxRGkc
|
||||
gPZm7ZsipmfbZK2Tkhnpsa4QxDg7zHJPMsB5kxRXW0cQipXcC3baDyN9KBApNXa0
|
||||
PwIDAQAB
|
||||
-----END PUBLIC KEY-----`
|
||||
|
||||
const rsaPublicKey = `-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA249XwEo9k4tM8fMxV7zx
|
||||
OhcrP+WvXn917koM5Qr2ZXs4vo26e4ytdlrV0bQ9SlcLpQVSYjIxNfhTZdDt+ecI
|
||||
zshKuv1gKIxbbLQMOuK1eA/4HALyEkFgmS/tleLJrhc65tKPMGD+pKQ/xhmzRuCG
|
||||
51RoiMgbQxaCyYxGfNLpLAZK9L0Tctv9a0mJmGIYnIOQM4kC1A1I1n3EsXMWmeJU
|
||||
j7OTh/AjjCnMnkgvKT2tpKxYQ59PgDgU8Ssc7RDSmSkLxnrv+OrN80j6xrw0OjEi
|
||||
B4Ycr0PqfzZcvy8efTtFQ/Jnc4Bp1zUtFXt7+QeevePtQ2EcyELXE0i63T1CujRM
|
||||
WwIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
`
|
||||
|
||||
const rsaPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA249XwEo9k4tM8fMxV7zxOhcrP+WvXn917koM5Qr2ZXs4vo26
|
||||
e4ytdlrV0bQ9SlcLpQVSYjIxNfhTZdDt+ecIzshKuv1gKIxbbLQMOuK1eA/4HALy
|
||||
EkFgmS/tleLJrhc65tKPMGD+pKQ/xhmzRuCG51RoiMgbQxaCyYxGfNLpLAZK9L0T
|
||||
ctv9a0mJmGIYnIOQM4kC1A1I1n3EsXMWmeJUj7OTh/AjjCnMnkgvKT2tpKxYQ59P
|
||||
gDgU8Ssc7RDSmSkLxnrv+OrN80j6xrw0OjEiB4Ycr0PqfzZcvy8efTtFQ/Jnc4Bp
|
||||
1zUtFXt7+QeevePtQ2EcyELXE0i63T1CujRMWwIDAQABAoIBAHJx8GqyCBDNbqk7
|
||||
e7/hI9iE1S10Wwol5GH2RWxqX28cYMKq+8aE2LI1vPiXO89xOgelk4DN6urX6xjK
|
||||
ZBF8RRIMQy/e/O2F4+3wl+Nl4vOXV1u6iVXMsD6JRg137mqJf1Fr9elg1bsaRofL
|
||||
Q7CxPoB8dhS+Qb+hj0DhlqhgA9zG345CQCAds0ZYAZe8fP7bkwrLqZpMn7Dz9WVm
|
||||
++YgYYKjuE95kPuup/LtWfA9rJyE/Fws8/jGvRSpVn1XglMLSMKhLd27sE8ZUSV0
|
||||
2KUzbfRGE0+AnRULRrjpYaPu0XQ2JjdNvtkjBnv27RB89W9Gklxq821eH1Y8got8
|
||||
FZodjxECgYEA93pz7AQZ2xDs67d1XLCzpX84GxKzttirmyj3OIlxgzVHjEMsvw8v
|
||||
sjFiBU5xEEQDosrBdSknnlJqyiq1YwWG/WDckr13d8G2RQWoySN7JVmTQfXcLoTu
|
||||
YGRiiTuoEi3ab3ZqrgGrFgX7T/cHuasbYvzCvhM2b4VIR3aSxU2DTUMCgYEA4x7J
|
||||
T/ErP6GkU5nKstu/mIXwNzayEO1BJvPYsy7i7EsxTm3xe/b8/6cYOz5fvJLGH5mT
|
||||
Q8YvuLqBcMwZardrYcwokD55UvNLOyfADDFZ6l3WntIqbA640Ok2g1X4U8J09xIq
|
||||
ZLIWK1yWbbvi4QCeN5hvWq47e8sIj5QHjIIjRwkCgYEAyNqjltxFN9zmzPDa2d24
|
||||
EAvOt3pYTYBQ1t9KtqImdL0bUqV6fZ6PsWoPCgt+DBuHb+prVPGP7Bkr/uTmznU/
|
||||
+AlTO+12NsYLbr2HHagkXE31DEXE7CSLa8RNjN/UKtz4Ohq7vnowJvG35FCz/mb3
|
||||
FUHbtHTXa2+bGBUOTf/5Hw0CgYBxw0r9EwUhw1qnUYJ5op7OzFAtp+T7m4ul8kCa
|
||||
SCL8TxGsgl+SQ34opE775dtYfoBk9a0RJqVit3D8yg71KFjOTNAIqHJm/Vyyjc+h
|
||||
i9rJDSXiuczsAVfLtPVMRfS0J9QkqeG4PIfkQmVLI/CZ2ZBmsqEcX+eFs4ZfPLun
|
||||
Qsxe2QKBgGuPilIbLeIBDIaPiUI0FwU8v2j8CEQBYvoQn34c95hVQsig/o5z7zlo
|
||||
UsO0wlTngXKlWdOcCs1kqEhTLrstf48djDxAYAxkw40nzeJOt7q52ib/fvf4/UBy
|
||||
X024wzbiw1q07jFCyfQmODzURAx1VNT7QVUMdz/N8vy47/H40AZJ
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
||||
|
||||
// openssl ecparam -name prime256v1 -genkey -noout -out ecdsa256.pem
|
||||
const ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIEZmTmUhuanLjPA2CLquXivuwBDHTt5XYwgIr/kA1LtRoAoGCCqGSM49
|
||||
AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0
|
||||
/IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
|
||||
-----END EC PRIVATE KEY-----`
|
||||
|
||||
// openssl ec -in ecdsa256.pem -pubout -out ecdsa256pub.pem
|
||||
const ecdsaPublicKey = `-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPL
|
||||
X2i8uIp/C/ASqiIGUeeKQtX0/IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
|
||||
-----END PUBLIC KEY-----`
|
||||
|
||||
func getPrivateKey(data string) interface{} {
|
||||
key, _ := certutil.ParsePrivateKeyPEM([]byte(data))
|
||||
return key
|
||||
}
|
||||
|
||||
func getPublicKey(data string) interface{} {
|
||||
keys, _ := certutil.ParsePublicKeysPEM([]byte(data))
|
||||
return keys[0]
|
||||
}
|
||||
func TestTokenGenerateAndValidate(t *testing.T) {
|
||||
expectedUserName := "system:serviceaccount:test:my-service-account"
|
||||
expectedUserUID := "12345"
|
||||
|
||||
// Related API objects
|
||||
serviceAccount := &v1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-service-account",
|
||||
UID: "12345",
|
||||
Namespace: "test",
|
||||
},
|
||||
}
|
||||
rsaSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-rsa-secret",
|
||||
Namespace: "test",
|
||||
},
|
||||
}
|
||||
ecdsaSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-ecdsa-secret",
|
||||
Namespace: "test",
|
||||
},
|
||||
}
|
||||
|
||||
// Generate the RSA token
|
||||
rsaGenerator := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, getPrivateKey(rsaPrivateKey))
|
||||
rsaToken, err := rsaGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *rsaSecret))
|
||||
if err != nil {
|
||||
t.Fatalf("error generating token: %v", err)
|
||||
}
|
||||
if len(rsaToken) == 0 {
|
||||
t.Fatalf("no token generated")
|
||||
}
|
||||
rsaSecret.Data = map[string][]byte{
|
||||
"token": []byte(rsaToken),
|
||||
}
|
||||
|
||||
// Generate the ECDSA token
|
||||
ecdsaGenerator := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, getPrivateKey(ecdsaPrivateKey))
|
||||
ecdsaToken, err := ecdsaGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *ecdsaSecret))
|
||||
if err != nil {
|
||||
t.Fatalf("error generating token: %v", err)
|
||||
}
|
||||
if len(ecdsaToken) == 0 {
|
||||
t.Fatalf("no token generated")
|
||||
}
|
||||
ecdsaSecret.Data = map[string][]byte{
|
||||
"token": []byte(ecdsaToken),
|
||||
}
|
||||
|
||||
// Generate signer with same keys as RSA signer but different issuer
|
||||
badIssuerGenerator := serviceaccount.JWTTokenGenerator("foo", getPrivateKey(rsaPrivateKey))
|
||||
badIssuerToken, err := badIssuerGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *rsaSecret))
|
||||
if err != nil {
|
||||
t.Fatalf("error generating token: %v", err)
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
Client clientset.Interface
|
||||
Keys []interface{}
|
||||
Token string
|
||||
|
||||
ExpectedErr bool
|
||||
ExpectedOK bool
|
||||
ExpectedUserName string
|
||||
ExpectedUserUID string
|
||||
ExpectedGroups []string
|
||||
}{
|
||||
"no keys": {
|
||||
Token: rsaToken,
|
||||
Client: nil,
|
||||
Keys: []interface{}{},
|
||||
ExpectedErr: false,
|
||||
ExpectedOK: false,
|
||||
},
|
||||
"invalid keys (rsa)": {
|
||||
Token: rsaToken,
|
||||
Client: nil,
|
||||
Keys: []interface{}{getPublicKey(otherPublicKey), getPublicKey(ecdsaPublicKey)},
|
||||
ExpectedErr: true,
|
||||
ExpectedOK: false,
|
||||
},
|
||||
"invalid keys (ecdsa)": {
|
||||
Token: ecdsaToken,
|
||||
Client: nil,
|
||||
Keys: []interface{}{getPublicKey(otherPublicKey), getPublicKey(rsaPublicKey)},
|
||||
ExpectedErr: true,
|
||||
ExpectedOK: false,
|
||||
},
|
||||
"valid key (rsa)": {
|
||||
Token: rsaToken,
|
||||
Client: nil,
|
||||
Keys: []interface{}{getPublicKey(rsaPublicKey)},
|
||||
ExpectedErr: false,
|
||||
ExpectedOK: true,
|
||||
ExpectedUserName: expectedUserName,
|
||||
ExpectedUserUID: expectedUserUID,
|
||||
ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"},
|
||||
},
|
||||
"valid key, invalid issuer (rsa)": {
|
||||
Token: badIssuerToken,
|
||||
Client: nil,
|
||||
Keys: []interface{}{getPublicKey(rsaPublicKey)},
|
||||
ExpectedErr: false,
|
||||
ExpectedOK: false,
|
||||
},
|
||||
"valid key (ecdsa)": {
|
||||
Token: ecdsaToken,
|
||||
Client: nil,
|
||||
Keys: []interface{}{getPublicKey(ecdsaPublicKey)},
|
||||
ExpectedErr: false,
|
||||
ExpectedOK: true,
|
||||
ExpectedUserName: expectedUserName,
|
||||
ExpectedUserUID: expectedUserUID,
|
||||
ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"},
|
||||
},
|
||||
"rotated keys (rsa)": {
|
||||
Token: rsaToken,
|
||||
Client: nil,
|
||||
Keys: []interface{}{getPublicKey(otherPublicKey), getPublicKey(ecdsaPublicKey), getPublicKey(rsaPublicKey)},
|
||||
ExpectedErr: false,
|
||||
ExpectedOK: true,
|
||||
ExpectedUserName: expectedUserName,
|
||||
ExpectedUserUID: expectedUserUID,
|
||||
ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"},
|
||||
},
|
||||
"rotated keys (ecdsa)": {
|
||||
Token: ecdsaToken,
|
||||
Client: nil,
|
||||
Keys: []interface{}{getPublicKey(otherPublicKey), getPublicKey(rsaPublicKey), getPublicKey(ecdsaPublicKey)},
|
||||
ExpectedErr: false,
|
||||
ExpectedOK: true,
|
||||
ExpectedUserName: expectedUserName,
|
||||
ExpectedUserUID: expectedUserUID,
|
||||
ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"},
|
||||
},
|
||||
"valid lookup": {
|
||||
Token: rsaToken,
|
||||
Client: fake.NewSimpleClientset(serviceAccount, rsaSecret, ecdsaSecret),
|
||||
Keys: []interface{}{getPublicKey(rsaPublicKey)},
|
||||
ExpectedErr: false,
|
||||
ExpectedOK: true,
|
||||
ExpectedUserName: expectedUserName,
|
||||
ExpectedUserUID: expectedUserUID,
|
||||
ExpectedGroups: []string{"system:serviceaccounts", "system:serviceaccounts:test"},
|
||||
},
|
||||
"invalid secret lookup": {
|
||||
Token: rsaToken,
|
||||
Client: fake.NewSimpleClientset(serviceAccount),
|
||||
Keys: []interface{}{getPublicKey(rsaPublicKey)},
|
||||
ExpectedErr: true,
|
||||
ExpectedOK: false,
|
||||
},
|
||||
"invalid serviceaccount lookup": {
|
||||
Token: rsaToken,
|
||||
Client: fake.NewSimpleClientset(rsaSecret, ecdsaSecret),
|
||||
Keys: []interface{}{getPublicKey(rsaPublicKey)},
|
||||
ExpectedErr: true,
|
||||
ExpectedOK: false,
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range testCases {
|
||||
getter := serviceaccountcontroller.NewGetterFromClient(tc.Client)
|
||||
authenticator := serviceaccount.JWTTokenAuthenticator(serviceaccount.LegacyIssuer, tc.Keys, serviceaccount.NewLegacyValidator(tc.Client != nil, getter))
|
||||
|
||||
// An invalid, non-JWT token should always fail
|
||||
if _, ok, err := authenticator.AuthenticateToken("invalid token"); err != nil || ok {
|
||||
t.Errorf("%s: Expected err=nil, ok=false for non-JWT token", k)
|
||||
continue
|
||||
}
|
||||
|
||||
user, ok, err := authenticator.AuthenticateToken(tc.Token)
|
||||
if (err != nil) != tc.ExpectedErr {
|
||||
t.Errorf("%s: Expected error=%v, got %v", k, tc.ExpectedErr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if ok != tc.ExpectedOK {
|
||||
t.Errorf("%s: Expected ok=%v, got %v", k, tc.ExpectedOK, ok)
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil || !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if user.GetName() != tc.ExpectedUserName {
|
||||
t.Errorf("%s: Expected username=%v, got %v", k, tc.ExpectedUserName, user.GetName())
|
||||
continue
|
||||
}
|
||||
if user.GetUID() != tc.ExpectedUserUID {
|
||||
t.Errorf("%s: Expected userUID=%v, got %v", k, tc.ExpectedUserUID, user.GetUID())
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(user.GetGroups(), tc.ExpectedGroups) {
|
||||
t.Errorf("%s: Expected groups=%v, got %v", k, tc.ExpectedGroups, user.GetGroups())
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeSplitUsername(t *testing.T) {
|
||||
username := apiserverserviceaccount.MakeUsername("ns", "name")
|
||||
ns, name, err := apiserverserviceaccount.SplitUsername(username)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
if ns != "ns" || name != "name" {
|
||||
t.Errorf("Expected ns/name, got %s/%s", ns, name)
|
||||
}
|
||||
|
||||
invalid := []string{"test", "system:serviceaccount", "system:serviceaccount:", "system:serviceaccount:ns", "system:serviceaccount:ns:name:extra"}
|
||||
for _, n := range invalid {
|
||||
_, _, err := apiserverserviceaccount.SplitUsername("test")
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for %s", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
103
vendor/k8s.io/kubernetes/pkg/serviceaccount/util_test.go
generated
vendored
103
vendor/k8s.io/kubernetes/pkg/serviceaccount/util_test.go
generated
vendored
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 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 serviceaccount
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestIsServiceAccountToken(t *testing.T) {
|
||||
|
||||
secretIns := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "token-secret-1",
|
||||
Namespace: "default",
|
||||
UID: "23456",
|
||||
ResourceVersion: "1",
|
||||
Annotations: map[string]string{
|
||||
v1.ServiceAccountNameKey: "default",
|
||||
v1.ServiceAccountUIDKey: "12345",
|
||||
},
|
||||
},
|
||||
Type: v1.SecretTypeServiceAccountToken,
|
||||
Data: map[string][]byte{
|
||||
"token": []byte("ABC"),
|
||||
"ca.crt": []byte("CA Data"),
|
||||
"namespace": []byte("default"),
|
||||
},
|
||||
}
|
||||
|
||||
saIns := &v1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default",
|
||||
UID: "12345",
|
||||
Namespace: "default",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
}
|
||||
|
||||
saInsNameNotEqual := &v1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "non-default",
|
||||
UID: "12345",
|
||||
Namespace: "default",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
}
|
||||
|
||||
saInsUIDNotEqual := &v1.ServiceAccount{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "default",
|
||||
UID: "67890",
|
||||
Namespace: "default",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
}
|
||||
|
||||
tests := map[string]struct {
|
||||
secret *v1.Secret
|
||||
sa *v1.ServiceAccount
|
||||
expect bool
|
||||
}{
|
||||
"correct service account": {
|
||||
secret: secretIns,
|
||||
sa: saIns,
|
||||
expect: true,
|
||||
},
|
||||
"service account name not equal": {
|
||||
secret: secretIns,
|
||||
sa: saInsNameNotEqual,
|
||||
expect: false,
|
||||
},
|
||||
"service account uid not equal": {
|
||||
secret: secretIns,
|
||||
sa: saInsUIDNotEqual,
|
||||
expect: false,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range tests {
|
||||
actual := IsServiceAccountToken(v.secret, v.sa)
|
||||
if actual != v.expect {
|
||||
t.Errorf("%s failed, expected %t but received %t", k, v.expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
149
vendor/k8s.io/kubernetes/pkg/util/file/file_test.go
generated
vendored
149
vendor/k8s.io/kubernetes/pkg/util/file/file_test.go
generated
vendored
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 file
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func RecoverEnv(wd, tmpDir string) {
|
||||
os.Chdir(wd)
|
||||
os.RemoveAll(tmpDir)
|
||||
}
|
||||
|
||||
func TestFileUtils(t *testing.T) {
|
||||
fs := &afero.Afero{Fs: afero.NewOsFs()}
|
||||
// Create tmp dir
|
||||
tmpDir, err := fs.TempDir(os.TempDir(), "util_file_test_")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to create temp dir.")
|
||||
}
|
||||
|
||||
// create tmp file
|
||||
tmpFile, err := fs.TempFile(tmpDir, "test_file_exists_")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to create temp file.")
|
||||
}
|
||||
|
||||
// create tmp sym link
|
||||
tmpSymlinkName := filepath.Join(tmpDir, "test_file_exists_sym_link")
|
||||
err = os.Symlink(tmpFile.Name(), tmpSymlinkName)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to create sym link.")
|
||||
}
|
||||
|
||||
// create tmp sub dir
|
||||
tmpSubDir, err := fs.TempDir(tmpDir, "sub_")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to create temp sub dir.")
|
||||
}
|
||||
|
||||
// record the current dir
|
||||
currentDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to get current dir.")
|
||||
}
|
||||
|
||||
// change the work dir to temp dir
|
||||
err = os.Chdir(tmpDir)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to test: failed to change work dir.")
|
||||
}
|
||||
|
||||
// recover test environment
|
||||
defer RecoverEnv(currentDir, tmpDir)
|
||||
|
||||
t.Run("TestFileExists", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fileName string
|
||||
expectedError bool
|
||||
expectedValue bool
|
||||
}{
|
||||
{"file_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), false, false},
|
||||
{"file_exists", tmpFile.Name(), false, true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
realValued, realError := FileExists(test.fileName)
|
||||
if test.expectedError {
|
||||
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.fileName, test.name)
|
||||
} else {
|
||||
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.fileName, test.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TestFileOrSymlinkExists", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fileName string
|
||||
expectedError bool
|
||||
expectedValue bool
|
||||
}{
|
||||
{"file_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), false, false},
|
||||
{"file_exists", tmpFile.Name(), false, true},
|
||||
{"symlink_exists", tmpSymlinkName, false, true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
realValued, realError := FileOrSymlinkExists(test.fileName)
|
||||
if test.expectedError {
|
||||
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.fileName, test.name)
|
||||
} else {
|
||||
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.fileName, test.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TestReadDirNoStat", func(t *testing.T) {
|
||||
_, tmpFileSimpleName := filepath.Split(tmpFile.Name())
|
||||
_, tmpSymlinkSimpleName := filepath.Split(tmpSymlinkName)
|
||||
_, tmpSubDirSimpleName := filepath.Split(tmpSubDir)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
dirName string
|
||||
expectedError bool
|
||||
expectedValue []string
|
||||
}{
|
||||
{"dir_not_exists", filepath.Join(tmpDir, "file_not_exist_case"), true, []string{}},
|
||||
{"dir_is_empty", "", false, []string{tmpFileSimpleName, tmpSymlinkSimpleName, tmpSubDirSimpleName}},
|
||||
{"dir_exists", tmpDir, false, []string{tmpFileSimpleName, tmpSymlinkSimpleName, tmpSubDirSimpleName}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
realValued, realError := ReadDirNoStat(test.dirName)
|
||||
|
||||
// execute sort action before compare
|
||||
sort.Strings(realValued)
|
||||
sort.Strings(test.expectedValue)
|
||||
|
||||
if test.expectedError {
|
||||
assert.Errorf(t, realError, "Failed to test with '%s': %s", test.dirName, test.name)
|
||||
} else {
|
||||
assert.EqualValuesf(t, test.expectedValue, realValued, "Failed to test with '%s': %s", test.dirName, test.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
147
vendor/k8s.io/kubernetes/pkg/util/hash/hash_test.go
generated
vendored
147
vendor/k8s.io/kubernetes/pkg/util/hash/hash_test.go
generated
vendored
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
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 hash
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/adler32"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
type A struct {
|
||||
x int
|
||||
y string
|
||||
}
|
||||
|
||||
type B struct {
|
||||
x []int
|
||||
y map[string]bool
|
||||
}
|
||||
|
||||
type C struct {
|
||||
x int
|
||||
y string
|
||||
}
|
||||
|
||||
func (c C) String() string {
|
||||
return fmt.Sprintf("%d:%s", c.x, c.y)
|
||||
}
|
||||
|
||||
func TestDeepHashObject(t *testing.T) {
|
||||
successCases := []func() interface{}{
|
||||
func() interface{} { return 8675309 },
|
||||
func() interface{} { return "Jenny, I got your number" },
|
||||
func() interface{} { return []string{"eight", "six", "seven"} },
|
||||
func() interface{} { return [...]int{5, 3, 0, 9} },
|
||||
func() interface{} { return map[int]string{8: "8", 6: "6", 7: "7"} },
|
||||
func() interface{} { return map[string]int{"5": 5, "3": 3, "0": 0, "9": 9} },
|
||||
func() interface{} { return A{867, "5309"} },
|
||||
func() interface{} { return &A{867, "5309"} },
|
||||
func() interface{} {
|
||||
return B{[]int{8, 6, 7}, map[string]bool{"5": true, "3": true, "0": true, "9": true}}
|
||||
},
|
||||
func() interface{} { return map[A]bool{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
|
||||
func() interface{} { return map[C]bool{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
|
||||
func() interface{} { return map[*A]bool{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
|
||||
func() interface{} { return map[*C]bool{{8675309, "Jenny"}: true, {9765683, "!Jenny"}: false} },
|
||||
}
|
||||
|
||||
for _, tc := range successCases {
|
||||
hasher1 := adler32.New()
|
||||
DeepHashObject(hasher1, tc())
|
||||
hash1 := hasher1.Sum32()
|
||||
DeepHashObject(hasher1, tc())
|
||||
hash2 := hasher1.Sum32()
|
||||
if hash1 != hash2 {
|
||||
t.Fatalf("hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash1, hash2)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
hasher2 := adler32.New()
|
||||
|
||||
DeepHashObject(hasher1, tc())
|
||||
hash1a := hasher1.Sum32()
|
||||
DeepHashObject(hasher2, tc())
|
||||
hash2a := hasher2.Sum32()
|
||||
|
||||
if hash1a != hash1 {
|
||||
t.Errorf("repeated hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash1, hash1a)
|
||||
}
|
||||
if hash2a != hash2 {
|
||||
t.Errorf("repeated hash of the same object (%q) produced different results: %d vs %d", toString(tc()), hash2, hash2a)
|
||||
}
|
||||
if hash1a != hash2a {
|
||||
t.Errorf("hash of the same object produced (%q) different results: %d vs %d", toString(tc()), hash1a, hash2a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toString(obj interface{}) string {
|
||||
return spew.Sprintf("%#v", obj)
|
||||
}
|
||||
|
||||
type wheel struct {
|
||||
radius uint32
|
||||
}
|
||||
|
||||
type unicycle struct {
|
||||
primaryWheel *wheel
|
||||
licencePlateID string
|
||||
tags map[string]string
|
||||
}
|
||||
|
||||
func TestDeepObjectPointer(t *testing.T) {
|
||||
// Arrange
|
||||
wheel1 := wheel{radius: 17}
|
||||
wheel2 := wheel{radius: 22}
|
||||
wheel3 := wheel{radius: 17}
|
||||
|
||||
myUni1 := unicycle{licencePlateID: "blah", primaryWheel: &wheel1, tags: map[string]string{"color": "blue", "name": "john"}}
|
||||
myUni2 := unicycle{licencePlateID: "blah", primaryWheel: &wheel2, tags: map[string]string{"color": "blue", "name": "john"}}
|
||||
myUni3 := unicycle{licencePlateID: "blah", primaryWheel: &wheel3, tags: map[string]string{"color": "blue", "name": "john"}}
|
||||
|
||||
// Run it more than once to verify determinism of hasher.
|
||||
for i := 0; i < 100; i++ {
|
||||
hasher1 := adler32.New()
|
||||
hasher2 := adler32.New()
|
||||
hasher3 := adler32.New()
|
||||
// Act
|
||||
DeepHashObject(hasher1, myUni1)
|
||||
hash1 := hasher1.Sum32()
|
||||
DeepHashObject(hasher1, myUni1)
|
||||
hash1a := hasher1.Sum32()
|
||||
DeepHashObject(hasher2, myUni2)
|
||||
hash2 := hasher2.Sum32()
|
||||
DeepHashObject(hasher3, myUni3)
|
||||
hash3 := hasher3.Sum32()
|
||||
|
||||
// Assert
|
||||
if hash1 != hash1a {
|
||||
t.Errorf("repeated hash of the same object produced different results: %d vs %d", hash1, hash1a)
|
||||
}
|
||||
|
||||
if hash1 == hash2 {
|
||||
t.Errorf("hash1 (%d) and hash2(%d) must be different because they have different values for wheel size", hash1, hash2)
|
||||
}
|
||||
|
||||
if hash1 != hash3 {
|
||||
t.Errorf("hash1 (%d) and hash3(%d) must be the same because although they point to different objects, they have the same values for wheel size", hash1, hash3)
|
||||
}
|
||||
}
|
||||
}
|
||||
1
vendor/k8s.io/kubernetes/pkg/util/mount/BUILD
generated
vendored
1
vendor/k8s.io/kubernetes/pkg/util/mount/BUILD
generated
vendored
|
|
@ -102,6 +102,7 @@ go_test(
|
|||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
|
|
|
|||
165
vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount_test.go
generated
vendored
165
vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount_test.go
generated
vendored
|
|
@ -1,165 +0,0 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2017 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 mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
sourcePath = "/mnt/srv"
|
||||
destinationPath = "/mnt/dst"
|
||||
fsType = "xfs"
|
||||
mountOptions = []string{"vers=1", "foo=bar"}
|
||||
)
|
||||
|
||||
func TestMount(t *testing.T) {
|
||||
exec := NewFakeExec(func(cmd string, args ...string) ([]byte, error) {
|
||||
if cmd != "mount" {
|
||||
t.Errorf("expected mount command, got %q", cmd)
|
||||
}
|
||||
// mount -t fstype -o options source target
|
||||
expectedArgs := []string{"-t", fsType, "-o", strings.Join(mountOptions, ","), sourcePath, destinationPath}
|
||||
if !reflect.DeepEqual(expectedArgs, args) {
|
||||
t.Errorf("expected arguments %q, got %q", strings.Join(expectedArgs, " "), strings.Join(args, " "))
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
wrappedMounter := &fakeMounter{t}
|
||||
mounter := NewExecMounter(exec, wrappedMounter)
|
||||
|
||||
mounter.Mount(sourcePath, destinationPath, fsType, mountOptions)
|
||||
}
|
||||
|
||||
func TestBindMount(t *testing.T) {
|
||||
cmdCount := 0
|
||||
exec := NewFakeExec(func(cmd string, args ...string) ([]byte, error) {
|
||||
cmdCount++
|
||||
if cmd != "mount" {
|
||||
t.Errorf("expected mount command, got %q", cmd)
|
||||
}
|
||||
var expectedArgs []string
|
||||
switch cmdCount {
|
||||
case 1:
|
||||
// mount -t fstype -o "bind" source target
|
||||
expectedArgs = []string{"-t", fsType, "-o", "bind", sourcePath, destinationPath}
|
||||
case 2:
|
||||
// mount -t fstype -o "remount,opts" source target
|
||||
expectedArgs = []string{"-t", fsType, "-o", "remount," + strings.Join(mountOptions, ","), sourcePath, destinationPath}
|
||||
}
|
||||
if !reflect.DeepEqual(expectedArgs, args) {
|
||||
t.Errorf("expected arguments %q, got %q", strings.Join(expectedArgs, " "), strings.Join(args, " "))
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
wrappedMounter := &fakeMounter{t}
|
||||
mounter := NewExecMounter(exec, wrappedMounter)
|
||||
bindOptions := append(mountOptions, "bind")
|
||||
mounter.Mount(sourcePath, destinationPath, fsType, bindOptions)
|
||||
}
|
||||
|
||||
func TestUnmount(t *testing.T) {
|
||||
exec := NewFakeExec(func(cmd string, args ...string) ([]byte, error) {
|
||||
if cmd != "umount" {
|
||||
t.Errorf("expected unmount command, got %q", cmd)
|
||||
}
|
||||
// unmount $target
|
||||
expectedArgs := []string{destinationPath}
|
||||
if !reflect.DeepEqual(expectedArgs, args) {
|
||||
t.Errorf("expected arguments %q, got %q", strings.Join(expectedArgs, " "), strings.Join(args, " "))
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
wrappedMounter := &fakeMounter{t}
|
||||
mounter := NewExecMounter(exec, wrappedMounter)
|
||||
|
||||
mounter.Unmount(destinationPath)
|
||||
}
|
||||
|
||||
/* Fake wrapped mounter */
|
||||
type fakeMounter struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
// Mount() of wrapped mounter should never be called. We call exec instead.
|
||||
fm.t.Errorf("Unexpected wrapped mount call")
|
||||
return fmt.Errorf("Unexpected wrapped mount call")
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) Unmount(target string) error {
|
||||
// umount() of wrapped mounter should never be called. We call exec instead.
|
||||
fm.t.Errorf("Unexpected wrapped mount call")
|
||||
return fmt.Errorf("Unexpected wrapped mount call")
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) List() ([]MountPoint, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (fm *fakeMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
|
||||
return false
|
||||
}
|
||||
func (fm *fakeMounter) IsNotMountPoint(file string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
func (fm *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
func (fm *fakeMounter) DeviceOpened(pathname string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
func (fm *fakeMounter) PathIsDevice(pathname string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
func (fm *fakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
func (fm *fakeMounter) MakeRShared(path string) error {
|
||||
return nil
|
||||
}
|
||||
func (fm *fakeMounter) MakeFile(pathname string) error {
|
||||
return nil
|
||||
}
|
||||
func (fm *fakeMounter) MakeDir(pathname string) error {
|
||||
return nil
|
||||
}
|
||||
func (fm *fakeMounter) ExistsPath(pathname string) bool {
|
||||
return false
|
||||
}
|
||||
func (fm *fakeMounter) GetFileType(pathname string) (FileType, error) {
|
||||
return FileTypeFile, nil
|
||||
}
|
||||
func (fm *fakeMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||
return subPath.Path, nil, nil
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) CleanSubPaths(podDir string, volumeName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||
return nil
|
||||
}
|
||||
12
vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
generated
vendored
12
vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
generated
vendored
|
|
@ -58,8 +58,10 @@ func (f *FakeMounter) Mount(source string, target string, fstype string, options
|
|||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
// find 'bind' option
|
||||
opts := []string{}
|
||||
|
||||
for _, option := range options {
|
||||
// find 'bind' option
|
||||
if option == "bind" {
|
||||
// This is a bind-mount. In order to mimic linux behaviour, we must
|
||||
// use the original device of the bind-mount as the real source.
|
||||
|
|
@ -78,7 +80,11 @@ func (f *FakeMounter) Mount(source string, target string, fstype string, options
|
|||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
// find 'ro' option
|
||||
if option == "ro" {
|
||||
// reuse MountPoint.Opts field to mark mount as readonly
|
||||
opts = append(opts, "ro")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +94,7 @@ func (f *FakeMounter) Mount(source string, target string, fstype string, options
|
|||
absTarget = target
|
||||
}
|
||||
|
||||
f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype})
|
||||
f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype, Opts: opts})
|
||||
glog.V(5).Infof("Fake mounter: mounted %s to %s", source, absTarget)
|
||||
f.Log = append(f.Log, FakeAction{Action: FakeActionMount, Target: absTarget, Source: source, FSType: fstype})
|
||||
return nil
|
||||
|
|
|
|||
45
vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
generated
vendored
45
vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
generated
vendored
|
|
@ -19,6 +19,7 @@ limitations under the License.
|
|||
package mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
|
@ -123,6 +124,8 @@ type Subpath struct {
|
|||
PodDir string
|
||||
// Name of the container
|
||||
ContainerName string
|
||||
// True if the mount needs to be readonly
|
||||
ReadOnly bool
|
||||
}
|
||||
|
||||
// Exec executes command where mount utilities are. This can be either the host,
|
||||
|
|
@ -281,7 +284,13 @@ func IsNotMountPoint(mounter Interface, file string) (bool, error) {
|
|||
// The list equals:
|
||||
// options - 'bind' + 'remount' (no duplicate)
|
||||
func isBind(options []string) (bool, []string) {
|
||||
bindRemountOpts := []string{"remount"}
|
||||
// Because we have an FD opened on the subpath bind mount, the "bind" option
|
||||
// needs to be included, otherwise the mount target will error as busy if you
|
||||
// remount as readonly.
|
||||
//
|
||||
// As a consequence, all read only bind mounts will no longer change the underlying
|
||||
// volume mount to be read only.
|
||||
bindRemountOpts := []string{"bind", "remount"}
|
||||
bind := false
|
||||
|
||||
if len(options) != 0 {
|
||||
|
|
@ -337,3 +346,37 @@ func startsWithBackstep(rel string) bool {
|
|||
// normalize to / and check for ../
|
||||
return rel == ".." || strings.HasPrefix(filepath.ToSlash(rel), "../")
|
||||
}
|
||||
|
||||
// getFileType checks for file/directory/socket and block/character devices
|
||||
func getFileType(pathname string) (FileType, error) {
|
||||
var pathType FileType
|
||||
info, err := os.Stat(pathname)
|
||||
if os.IsNotExist(err) {
|
||||
return pathType, fmt.Errorf("path %q does not exist", pathname)
|
||||
}
|
||||
// err in call to os.Stat
|
||||
if err != nil {
|
||||
return pathType, err
|
||||
}
|
||||
|
||||
// checks whether the mode is the target mode
|
||||
isSpecificMode := func(mode, targetMode os.FileMode) bool {
|
||||
return mode&targetMode == targetMode
|
||||
}
|
||||
|
||||
mode := info.Mode()
|
||||
if mode.IsDir() {
|
||||
return FileTypeDirectory, nil
|
||||
} else if mode.IsRegular() {
|
||||
return FileTypeFile, nil
|
||||
} else if isSpecificMode(mode, os.ModeSocket) {
|
||||
return FileTypeSocket, nil
|
||||
} else if isSpecificMode(mode, os.ModeDevice) {
|
||||
if isSpecificMode(mode, os.ModeCharDevice) {
|
||||
return FileTypeCharDev, nil
|
||||
}
|
||||
return FileTypeBlockDev, nil
|
||||
}
|
||||
|
||||
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
|
||||
}
|
||||
|
|
|
|||
33
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
generated
vendored
33
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
generated
vendored
|
|
@ -423,31 +423,7 @@ func (mounter *Mounter) MakeRShared(path string) error {
|
|||
}
|
||||
|
||||
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
|
||||
var pathType FileType
|
||||
finfo, err := os.Stat(pathname)
|
||||
if os.IsNotExist(err) {
|
||||
return pathType, fmt.Errorf("path %q does not exist", pathname)
|
||||
}
|
||||
// err in call to os.Stat
|
||||
if err != nil {
|
||||
return pathType, err
|
||||
}
|
||||
|
||||
mode := finfo.Sys().(*syscall.Stat_t).Mode
|
||||
switch mode & syscall.S_IFMT {
|
||||
case syscall.S_IFSOCK:
|
||||
return FileTypeSocket, nil
|
||||
case syscall.S_IFBLK:
|
||||
return FileTypeBlockDev, nil
|
||||
case syscall.S_IFCHR:
|
||||
return FileTypeCharDev, nil
|
||||
case syscall.S_IFDIR:
|
||||
return FileTypeDirectory, nil
|
||||
case syscall.S_IFREG:
|
||||
return FileTypeFile, nil
|
||||
}
|
||||
|
||||
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
|
||||
return getFileType(pathname)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) MakeDir(pathname string) error {
|
||||
|
|
@ -801,8 +777,13 @@ func doBindSubPath(mounter Interface, subpath Subpath, kubeletPid int) (hostPath
|
|||
mountSource := fmt.Sprintf("/proc/%d/fd/%v", kubeletPid, fd)
|
||||
|
||||
// Do the bind mount
|
||||
options := []string{"bind"}
|
||||
if subpath.ReadOnly {
|
||||
options = append(options, "ro")
|
||||
}
|
||||
|
||||
glog.V(5).Infof("bind mounting %q at %q", mountSource, bindPathTarget)
|
||||
if err = mounter.Mount(mountSource, bindPathTarget, "" /*fstype*/, []string{"bind"}); err != nil {
|
||||
if err = mounter.Mount(mountSource, bindPathTarget, "" /*fstype*/, options); err != nil {
|
||||
return "", fmt.Errorf("error mounting %s: %s", subpath.Path, err)
|
||||
}
|
||||
|
||||
|
|
|
|||
1661
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux_test.go
generated
vendored
1661
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
26
vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows.go
generated
vendored
26
vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows.go
generated
vendored
|
|
@ -201,31 +201,7 @@ func (mounter *Mounter) MakeRShared(path string) error {
|
|||
|
||||
// GetFileType checks for sockets/block/character devices
|
||||
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
|
||||
var pathType FileType
|
||||
info, err := os.Stat(pathname)
|
||||
if os.IsNotExist(err) {
|
||||
return pathType, fmt.Errorf("path %q does not exist", pathname)
|
||||
}
|
||||
// err in call to os.Stat
|
||||
if err != nil {
|
||||
return pathType, err
|
||||
}
|
||||
|
||||
mode := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
|
||||
switch mode & syscall.S_IFMT {
|
||||
case syscall.S_IFSOCK:
|
||||
return FileTypeSocket, nil
|
||||
case syscall.S_IFBLK:
|
||||
return FileTypeBlockDev, nil
|
||||
case syscall.S_IFCHR:
|
||||
return FileTypeCharDev, nil
|
||||
case syscall.S_IFDIR:
|
||||
return FileTypeDirectory, nil
|
||||
case syscall.S_IFREG:
|
||||
return FileTypeFile, nil
|
||||
}
|
||||
|
||||
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
|
||||
return getFileType(pathname)
|
||||
}
|
||||
|
||||
// MakeFile creates a new directory
|
||||
|
|
|
|||
553
vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows_test.go
generated
vendored
553
vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows_test.go
generated
vendored
|
|
@ -1,553 +0,0 @@
|
|||
// +build windows
|
||||
|
||||
/*
|
||||
Copyright 2017 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 mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNormalizeWindowsPath(t *testing.T) {
|
||||
path := `/var/lib/kubelet/pods/146f8428-83e7-11e7-8dd4-000d3a31dac4/volumes/kubernetes.io~azure-disk`
|
||||
normalizedPath := normalizeWindowsPath(path)
|
||||
if normalizedPath != `c:\var\lib\kubelet\pods\146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~azure-disk` {
|
||||
t.Errorf("normizeWindowsPath test failed, normalizedPath : %q", normalizedPath)
|
||||
}
|
||||
|
||||
path = `/var/lib/kubelet/pods/146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~azure-disk`
|
||||
normalizedPath = normalizeWindowsPath(path)
|
||||
if normalizedPath != `c:\var\lib\kubelet\pods\146f8428-83e7-11e7-8dd4-000d3a31dac4\volumes\kubernetes.io~azure-disk` {
|
||||
t.Errorf("normizeWindowsPath test failed, normalizedPath : %q", normalizedPath)
|
||||
}
|
||||
|
||||
path = `/`
|
||||
normalizedPath = normalizeWindowsPath(path)
|
||||
if normalizedPath != `c:\` {
|
||||
t.Errorf("normizeWindowsPath test failed, normalizedPath : %q", normalizedPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDiskNumber(t *testing.T) {
|
||||
diskNum := "0"
|
||||
if err := ValidateDiskNumber(diskNum); err != nil {
|
||||
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
|
||||
}
|
||||
|
||||
diskNum = "99"
|
||||
if err := ValidateDiskNumber(diskNum); err != nil {
|
||||
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
|
||||
}
|
||||
|
||||
diskNum = "ab"
|
||||
if err := ValidateDiskNumber(diskNum); err == nil {
|
||||
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
|
||||
}
|
||||
|
||||
diskNum = "100"
|
||||
if err := ValidateDiskNumber(diskNum); err == nil {
|
||||
t.Errorf("TestValidateDiskNumber test failed, disk number : %s", diskNum)
|
||||
}
|
||||
}
|
||||
|
||||
func makeLink(link, target string) error {
|
||||
if output, err := exec.Command("cmd", "/c", "mklink", "/D", link, target).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("mklink failed: %v, link(%q) target(%q) output: %q", err, link, target, string(output))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeLink(link string) error {
|
||||
if output, err := exec.Command("cmd", "/c", "rmdir", link).CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("rmdir failed: %v, output: %q", err, string(output))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setEquivalent(set1, set2 []string) bool {
|
||||
map1 := make(map[string]bool)
|
||||
map2 := make(map[string]bool)
|
||||
for _, s := range set1 {
|
||||
map1[s] = true
|
||||
}
|
||||
for _, s := range set2 {
|
||||
map2[s] = true
|
||||
}
|
||||
|
||||
for s := range map1 {
|
||||
if !map2[s] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for s := range map2 {
|
||||
if !map1[s] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// this func must run in admin mode, otherwise it will fail
|
||||
func TestGetMountRefs(t *testing.T) {
|
||||
fm := &FakeMounter{MountPoints: []MountPoint{}}
|
||||
mountPath := `c:\secondmountpath`
|
||||
expectedRefs := []string{`c:\`, `c:\firstmountpath`, mountPath}
|
||||
|
||||
// remove symbolic links first
|
||||
for i := 1; i < len(expectedRefs); i++ {
|
||||
removeLink(expectedRefs[i])
|
||||
}
|
||||
|
||||
// create symbolic links
|
||||
for i := 1; i < len(expectedRefs); i++ {
|
||||
if err := makeLink(expectedRefs[i], expectedRefs[i-1]); err != nil {
|
||||
t.Errorf("makeLink failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if refs, err := GetMountRefs(fm, mountPath); err != nil || !setEquivalent(expectedRefs, refs) {
|
||||
t.Errorf("getMountRefs(%q) = %v, error: %v; expected %v", mountPath, refs, err, expectedRefs)
|
||||
}
|
||||
|
||||
// remove symbolic links
|
||||
for i := 1; i < len(expectedRefs); i++ {
|
||||
if err := removeLink(expectedRefs[i]); err != nil {
|
||||
t.Errorf("removeLink failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoSafeMakeDir(t *testing.T) {
|
||||
const testingVolumePath = `c:\tmp\DoSafeMakeDirTest`
|
||||
os.MkdirAll(testingVolumePath, 0755)
|
||||
defer os.RemoveAll(testingVolumePath)
|
||||
|
||||
tests := []struct {
|
||||
volumePath string
|
||||
subPath string
|
||||
expectError bool
|
||||
symlinkTarget string
|
||||
}{
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: ``,
|
||||
expectError: true,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `x`),
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `symlink`),
|
||||
expectError: false,
|
||||
symlinkTarget: `c:\tmp`,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `symlink\c\d`),
|
||||
expectError: true,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `symlink\y926`),
|
||||
expectError: true,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\symlink`),
|
||||
expectError: false,
|
||||
symlinkTarget: `c:\tmp`,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\x\symlink`),
|
||||
expectError: false,
|
||||
symlinkTarget: filepath.Join(testingVolumePath, `a`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if len(test.volumePath) > 0 && len(test.subPath) > 0 && len(test.symlinkTarget) > 0 {
|
||||
// make all parent sub directories
|
||||
if parent := filepath.Dir(test.subPath); parent != "." {
|
||||
os.MkdirAll(parent, 0755)
|
||||
}
|
||||
|
||||
// make last element as symlink
|
||||
linkPath := test.subPath
|
||||
if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
|
||||
if err := makeLink(linkPath, test.symlinkTarget); err != nil {
|
||||
t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := doSafeMakeDir(test.subPath, test.volumePath, os.FileMode(0755))
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
|
||||
continue
|
||||
}
|
||||
assert.Nil(t, err, "Expect no error during doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
|
||||
if _, err := os.Stat(test.subPath); os.IsNotExist(err) {
|
||||
t.Errorf("subPath should exists after doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockAndCheckSubPath(t *testing.T) {
|
||||
const testingVolumePath = `c:\tmp\LockAndCheckSubPathTest`
|
||||
|
||||
tests := []struct {
|
||||
volumePath string
|
||||
subPath string
|
||||
expectedHandleCount int
|
||||
expectError bool
|
||||
symlinkTarget string
|
||||
}{
|
||||
{
|
||||
volumePath: `c:\`,
|
||||
subPath: ``,
|
||||
expectedHandleCount: 0,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: ``,
|
||||
subPath: `a`,
|
||||
expectedHandleCount: 0,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a`),
|
||||
expectedHandleCount: 1,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
|
||||
expectedHandleCount: 4,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `symlink`),
|
||||
expectedHandleCount: 0,
|
||||
expectError: true,
|
||||
symlinkTarget: `c:\tmp`,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\symlink`),
|
||||
expectedHandleCount: 0,
|
||||
expectError: true,
|
||||
symlinkTarget: `c:\tmp`,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\d\symlink`),
|
||||
expectedHandleCount: 2,
|
||||
expectError: false,
|
||||
symlinkTarget: filepath.Join(testingVolumePath, `a\b`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if len(test.volumePath) > 0 && len(test.subPath) > 0 {
|
||||
os.MkdirAll(test.volumePath, 0755)
|
||||
if len(test.symlinkTarget) == 0 {
|
||||
// make all intermediate sub directories
|
||||
os.MkdirAll(test.subPath, 0755)
|
||||
} else {
|
||||
// make all parent sub directories
|
||||
if parent := filepath.Dir(test.subPath); parent != "." {
|
||||
os.MkdirAll(parent, 0755)
|
||||
}
|
||||
|
||||
// make last element as symlink
|
||||
linkPath := test.subPath
|
||||
if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
|
||||
if err := makeLink(linkPath, test.symlinkTarget); err != nil {
|
||||
t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileHandles, err := lockAndCheckSubPath(test.volumePath, test.subPath)
|
||||
unlockPath(fileHandles)
|
||||
assert.Equal(t, test.expectedHandleCount, len(fileHandles))
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
|
||||
continue
|
||||
}
|
||||
assert.Nil(t, err, "Expect no error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
|
||||
}
|
||||
|
||||
// remove dir will happen after closing all file handles
|
||||
assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
|
||||
}
|
||||
|
||||
func TestLockAndCheckSubPathWithoutSymlink(t *testing.T) {
|
||||
const testingVolumePath = `c:\tmp\LockAndCheckSubPathWithoutSymlinkTest`
|
||||
|
||||
tests := []struct {
|
||||
volumePath string
|
||||
subPath string
|
||||
expectedHandleCount int
|
||||
expectError bool
|
||||
symlinkTarget string
|
||||
}{
|
||||
{
|
||||
volumePath: `c:\`,
|
||||
subPath: ``,
|
||||
expectedHandleCount: 0,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: ``,
|
||||
subPath: `a`,
|
||||
expectedHandleCount: 0,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a`),
|
||||
expectedHandleCount: 1,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
|
||||
expectedHandleCount: 4,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `symlink`),
|
||||
expectedHandleCount: 1,
|
||||
expectError: true,
|
||||
symlinkTarget: `c:\tmp`,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\symlink`),
|
||||
expectedHandleCount: 4,
|
||||
expectError: true,
|
||||
symlinkTarget: `c:\tmp`,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\d\symlink`),
|
||||
expectedHandleCount: 5,
|
||||
expectError: true,
|
||||
symlinkTarget: filepath.Join(testingVolumePath, `a\b`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if len(test.volumePath) > 0 && len(test.subPath) > 0 {
|
||||
os.MkdirAll(test.volumePath, 0755)
|
||||
if len(test.symlinkTarget) == 0 {
|
||||
// make all intermediate sub directories
|
||||
os.MkdirAll(test.subPath, 0755)
|
||||
} else {
|
||||
// make all parent sub directories
|
||||
if parent := filepath.Dir(test.subPath); parent != "." {
|
||||
os.MkdirAll(parent, 0755)
|
||||
}
|
||||
|
||||
// make last element as symlink
|
||||
linkPath := test.subPath
|
||||
if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
|
||||
if err := makeLink(linkPath, test.symlinkTarget); err != nil {
|
||||
t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileHandles, err := lockAndCheckSubPathWithoutSymlink(test.volumePath, test.subPath)
|
||||
unlockPath(fileHandles)
|
||||
assert.Equal(t, test.expectedHandleCount, len(fileHandles))
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
|
||||
continue
|
||||
}
|
||||
assert.Nil(t, err, "Expect no error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
|
||||
}
|
||||
|
||||
// remove dir will happen after closing all file handles
|
||||
assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
|
||||
}
|
||||
|
||||
func TestFindExistingPrefix(t *testing.T) {
|
||||
const testingVolumePath = `c:\tmp\FindExistingPrefixTest`
|
||||
|
||||
tests := []struct {
|
||||
base string
|
||||
pathname string
|
||||
expectError bool
|
||||
expectedExistingPath string
|
||||
expectedToCreateDirs []string
|
||||
createSubPathBeforeTest bool
|
||||
}{
|
||||
{
|
||||
base: `c:\tmp\a`,
|
||||
pathname: `c:\tmp\b`,
|
||||
expectError: true,
|
||||
expectedExistingPath: "",
|
||||
expectedToCreateDirs: []string{},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
{
|
||||
base: ``,
|
||||
pathname: `c:\tmp\b`,
|
||||
expectError: true,
|
||||
expectedExistingPath: "",
|
||||
expectedToCreateDirs: []string{},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
{
|
||||
base: `c:\tmp\a`,
|
||||
pathname: `d:\tmp\b`,
|
||||
expectError: true,
|
||||
expectedExistingPath: "",
|
||||
expectedToCreateDirs: []string{},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
{
|
||||
base: testingVolumePath,
|
||||
pathname: testingVolumePath,
|
||||
expectError: false,
|
||||
expectedExistingPath: testingVolumePath,
|
||||
expectedToCreateDirs: []string{},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
{
|
||||
base: testingVolumePath,
|
||||
pathname: filepath.Join(testingVolumePath, `a\b`),
|
||||
expectError: false,
|
||||
expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
|
||||
expectedToCreateDirs: []string{},
|
||||
createSubPathBeforeTest: true,
|
||||
},
|
||||
{
|
||||
base: testingVolumePath,
|
||||
pathname: filepath.Join(testingVolumePath, `a\b\c\`),
|
||||
expectError: false,
|
||||
expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
|
||||
expectedToCreateDirs: []string{`c`},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
{
|
||||
base: testingVolumePath,
|
||||
pathname: filepath.Join(testingVolumePath, `a\b\c\d`),
|
||||
expectError: false,
|
||||
expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
|
||||
expectedToCreateDirs: []string{`c`, `d`},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if test.createSubPathBeforeTest {
|
||||
os.MkdirAll(test.pathname, 0755)
|
||||
}
|
||||
|
||||
existingPath, toCreate, err := findExistingPrefix(test.base, test.pathname)
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during findExistingPrefix(%s, %s)", test.base, test.pathname)
|
||||
continue
|
||||
}
|
||||
assert.Nil(t, err, "Expect no error during findExistingPrefix(%s, %s)", test.base, test.pathname)
|
||||
|
||||
assert.Equal(t, test.expectedExistingPath, existingPath, "Expect result not equal with findExistingPrefix(%s, %s) return: %q, expected: %q",
|
||||
test.base, test.pathname, existingPath, test.expectedExistingPath)
|
||||
|
||||
assert.Equal(t, test.expectedToCreateDirs, toCreate, "Expect result not equal with findExistingPrefix(%s, %s) return: %q, expected: %q",
|
||||
test.base, test.pathname, toCreate, test.expectedToCreateDirs)
|
||||
|
||||
}
|
||||
// remove dir will happen after closing all file handles
|
||||
assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
|
||||
}
|
||||
|
||||
func TestPathWithinBase(t *testing.T) {
|
||||
tests := []struct {
|
||||
fullPath string
|
||||
basePath string
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
fullPath: `c:\tmp\a\b\c`,
|
||||
basePath: `c:\tmp`,
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\tmp1`,
|
||||
basePath: `c:\tmp2`,
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\tmp`,
|
||||
basePath: `c:\tmp`,
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\tmp`,
|
||||
basePath: `c:\tmp\a\b\c`,
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\kubelet\pods\uuid\volumes\kubernetes.io~configmap\config\..timestamp\file.txt`,
|
||||
basePath: `c:\kubelet\pods\uuid\volumes\kubernetes.io~configmap\config`,
|
||||
expectedResult: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result := pathWithinBase(test.fullPath, test.basePath)
|
||||
assert.Equal(t, result, test.expectedResult, "Expect result not equal with pathWithinBase(%s, %s) return: %q, expected: %q",
|
||||
test.fullPath, test.basePath, result, test.expectedResult)
|
||||
}
|
||||
}
|
||||
7
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
generated
vendored
7
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
generated
vendored
|
|
@ -235,8 +235,13 @@ func (n *NsenterMounter) MakeRShared(path string) error {
|
|||
|
||||
func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) {
|
||||
var pathType FileType
|
||||
outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput()
|
||||
outputBytes, err := mounter.ne.Exec("stat", []string{"-L", "--printf=%F", pathname}).CombinedOutput()
|
||||
if err != nil {
|
||||
if strings.Contains(string(outputBytes), "No such file") {
|
||||
err = fmt.Errorf("%s does not exist", pathname)
|
||||
} else {
|
||||
err = fmt.Errorf("stat %s error: %v", pathname, string(outputBytes))
|
||||
}
|
||||
return pathType, err
|
||||
}
|
||||
|
||||
|
|
|
|||
191
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_test.go
generated
vendored
191
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_test.go
generated
vendored
|
|
@ -1,191 +0,0 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2017 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 mount
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseFindMnt(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
target string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
// standard mount name, e.g. for AWS
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/aws-ebs/mounts/aws/us-east-1d/vol-020f82b0759f72389 ext4\n",
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/aws-ebs/mounts/aws/us-east-1d/vol-020f82b0759f72389",
|
||||
false,
|
||||
},
|
||||
{
|
||||
// mount name with space, e.g. vSphere
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[datastore1] kubevols/kubernetes-dynamic-pvc-4aacaa9b-6ba5-11e7-8f64-0050569f1b82.vmdk ext2\n",
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[datastore1] kubevols/kubernetes-dynamic-pvc-4aacaa9b-6ba5-11e7-8f64-0050569f1b82.vmdk",
|
||||
false,
|
||||
},
|
||||
{
|
||||
// hypotetic mount with several spaces
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[ d a t a s t o r e 1 ] kubevols/kubernetes-dynamic-pvc-4aacaa9b-6ba5-11e7-8f64-0050569f1b82.vmdk ext2\n",
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/[ d a t a s t o r e 1 ] kubevols/kubernetes-dynamic-pvc-4aacaa9b-6ba5-11e7-8f64-0050569f1b82.vmdk",
|
||||
false,
|
||||
},
|
||||
{
|
||||
// invalid output - no filesystem type
|
||||
"/var/lib/kubelet/plugins/kubernetes.io/vsphere-volume/mounts/blabla",
|
||||
"",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
target, err := parseFindMnt(test.input)
|
||||
if test.expectError && err == nil {
|
||||
t.Errorf("test %d expected error, got nil", i)
|
||||
}
|
||||
if !test.expectError && err != nil {
|
||||
t.Errorf("test %d returned error: %s", i, err)
|
||||
}
|
||||
if target != test.target {
|
||||
t.Errorf("test %d expected %q, got %q", i, test.target, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPidOnHost(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "get_pid_on_host_tests")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
procFile string
|
||||
expectedPid int
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "valid status file",
|
||||
procFile: `Name: cat
|
||||
Umask: 0002
|
||||
State: R (running)
|
||||
Tgid: 15041
|
||||
Ngid: 0
|
||||
Pid: 15041
|
||||
PPid: 22699
|
||||
TracerPid: 0
|
||||
Uid: 1000 1000 1000 1000
|
||||
Gid: 1000 1000 1000 1000
|
||||
FDSize: 256
|
||||
Groups: 10 135 156 157 158 973 984 1000 1001
|
||||
NStgid: 15041
|
||||
NSpid: 15041
|
||||
NSpgid: 15041
|
||||
NSsid: 22699
|
||||
VmPeak: 115016 kB
|
||||
VmSize: 115016 kB
|
||||
VmLck: 0 kB
|
||||
VmPin: 0 kB
|
||||
VmHWM: 816 kB
|
||||
VmRSS: 816 kB
|
||||
RssAnon: 64 kB
|
||||
RssFile: 752 kB
|
||||
RssShmem: 0 kB
|
||||
VmData: 312 kB
|
||||
VmStk: 136 kB
|
||||
VmExe: 32 kB
|
||||
VmLib: 2060 kB
|
||||
VmPTE: 44 kB
|
||||
VmPMD: 12 kB
|
||||
VmSwap: 0 kB
|
||||
HugetlbPages: 0 kB
|
||||
Threads: 1
|
||||
SigQ: 2/60752
|
||||
SigPnd: 0000000000000000
|
||||
ShdPnd: 0000000000000000
|
||||
SigBlk: 0000000000000000
|
||||
SigIgn: 0000000000000000
|
||||
SigCgt: 0000000000000000
|
||||
CapInh: 0000000000000000
|
||||
CapPrm: 0000000000000000
|
||||
CapEff: 0000000000000000
|
||||
CapBnd: 0000003fffffffff
|
||||
CapAmb: 0000000000000000
|
||||
NoNewPrivs: 0
|
||||
Seccomp: 0
|
||||
Cpus_allowed: ff
|
||||
Cpus_allowed_list: 0-7
|
||||
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
|
||||
Mems_allowed_list: 0
|
||||
voluntary_ctxt_switches: 0
|
||||
nonvoluntary_ctxt_switches: 0
|
||||
`,
|
||||
expectedPid: 15041,
|
||||
},
|
||||
{
|
||||
name: "no Pid:",
|
||||
procFile: `Name: cat
|
||||
Umask: 0002
|
||||
State: R (running)
|
||||
Tgid: 15041
|
||||
Ngid: 0
|
||||
PPid: 22699
|
||||
`,
|
||||
expectedPid: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "invalid Pid:",
|
||||
procFile: `Name: cat
|
||||
Umask: 0002
|
||||
State: R (running)
|
||||
Tgid: 15041
|
||||
Ngid: 0
|
||||
Pid: invalid
|
||||
PPid: 22699
|
||||
`,
|
||||
expectedPid: 0,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
filename := path.Join(tempDir, strconv.Itoa(i))
|
||||
err := ioutil.WriteFile(filename, []byte(test.procFile), 0666)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
mounter := NsenterMounter{}
|
||||
pid, err := mounter.getPidOnHost(filename)
|
||||
if err != nil && !test.expectError {
|
||||
t.Errorf("Test %q: unexpected error: %s", test.name, err)
|
||||
}
|
||||
if err == nil && test.expectError {
|
||||
t.Errorf("Test %q: expected error, got none", test.name)
|
||||
}
|
||||
if pid != test.expectedPid {
|
||||
t.Errorf("Test %q: expected pid %d, got %d", test.name, test.expectedPid, pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
242
vendor/k8s.io/kubernetes/pkg/util/mount/safe_format_and_mount_test.go
generated
vendored
242
vendor/k8s.io/kubernetes/pkg/util/mount/safe_format_and_mount_test.go
generated
vendored
|
|
@ -1,242 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
fakeexec "k8s.io/utils/exec/testing"
|
||||
)
|
||||
|
||||
type ErrorMounter struct {
|
||||
*FakeMounter
|
||||
errIndex int
|
||||
err []error
|
||||
}
|
||||
|
||||
func (mounter *ErrorMounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
i := mounter.errIndex
|
||||
mounter.errIndex++
|
||||
if mounter.err != nil && mounter.err[i] != nil {
|
||||
return mounter.err[i]
|
||||
}
|
||||
return mounter.FakeMounter.Mount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
type ExecArgs struct {
|
||||
command string
|
||||
args []string
|
||||
output string
|
||||
err error
|
||||
}
|
||||
|
||||
func TestSafeFormatAndMount(t *testing.T) {
|
||||
if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
|
||||
t.Skipf("not supported on GOOS=%s", runtime.GOOS)
|
||||
}
|
||||
mntDir, err := ioutil.TempDir(os.TempDir(), "mount")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tmp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(mntDir)
|
||||
tests := []struct {
|
||||
description string
|
||||
fstype string
|
||||
mountOptions []string
|
||||
execScripts []ExecArgs
|
||||
mountErrs []error
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "Test a read only mount",
|
||||
fstype: "ext4",
|
||||
mountOptions: []string{"ro"},
|
||||
},
|
||||
{
|
||||
description: "Test a normal mount",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Test 'fsck' fails with exit status 4",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 4}},
|
||||
},
|
||||
expectedError: fmt.Errorf("'fsck' found errors on device /dev/foo but could not correct them: ."),
|
||||
},
|
||||
{
|
||||
description: "Test 'fsck' fails with exit status 1 (errors found and corrected)",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Test 'fsck' fails with exit status other than 1 and 4 (likely unformatted device)",
|
||||
fstype: "ext4",
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 8}},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and fails",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "DEVNAME=/dev/foo\nTYPE=ext4\n", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("unknown filesystem type '(null)'"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and confirms unformatted disk, format fails",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", fmt.Errorf("formatting failed")},
|
||||
},
|
||||
expectedError: fmt.Errorf("formatting failed"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount fails",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), fmt.Errorf("Still cannot mount")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("Still cannot mount"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount passes",
|
||||
fstype: "ext4",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and confirms unformatted disk, format passes, second mount passes with ext3",
|
||||
fstype: "ext3",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext3", []string{"-F", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "test that none ext4 fs does not get called with ext4 options.",
|
||||
fstype: "xfs",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.xfs", []string{"/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called and reports ext4 partition",
|
||||
fstype: "ext3",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'")},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "DEVNAME=/dev/foo\nPTTYPE=dos\n", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("failed to mount the volume as \"ext3\", it already contains unknown data, probably partitions. Mount error: unknown filesystem type '(null)'"),
|
||||
},
|
||||
{
|
||||
description: "Test that 'blkid' is called but has some usage or other errors (an exit code of 4 is returned)",
|
||||
fstype: "xfs",
|
||||
mountErrs: []error{fmt.Errorf("unknown filesystem type '(null)'"), nil},
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 4}},
|
||||
{"mkfs.xfs", []string{"/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("exit 4"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
execCallCount := 0
|
||||
execCallback := func(cmd string, args ...string) ([]byte, error) {
|
||||
if len(test.execScripts) <= execCallCount {
|
||||
t.Errorf("Unexpected command: %s %v", cmd, args)
|
||||
return nil, nil
|
||||
}
|
||||
script := test.execScripts[execCallCount]
|
||||
execCallCount++
|
||||
if script.command != cmd {
|
||||
t.Errorf("Unexpected command %s. Expecting %s", cmd, script.command)
|
||||
}
|
||||
for j := range args {
|
||||
if args[j] != script.args[j] {
|
||||
t.Errorf("Unexpected args %v. Expecting %v", args, script.args)
|
||||
}
|
||||
}
|
||||
return []byte(script.output), script.err
|
||||
}
|
||||
|
||||
fakeMounter := ErrorMounter{&FakeMounter{}, 0, test.mountErrs}
|
||||
fakeExec := NewFakeExec(execCallback)
|
||||
mounter := SafeFormatAndMount{
|
||||
Interface: &fakeMounter,
|
||||
Exec: fakeExec,
|
||||
}
|
||||
|
||||
device := "/dev/foo"
|
||||
dest := mntDir
|
||||
err := mounter.FormatAndMount(device, dest, test.fstype, test.mountOptions)
|
||||
if test.expectedError == nil {
|
||||
if err != nil {
|
||||
t.Errorf("test \"%s\" unexpected non-error: %v", test.description, err)
|
||||
}
|
||||
|
||||
// Check that something was mounted on the directory
|
||||
isNotMountPoint, err := fakeMounter.IsLikelyNotMountPoint(dest)
|
||||
if err != nil || isNotMountPoint {
|
||||
t.Errorf("test \"%s\" the directory was not mounted", test.description)
|
||||
}
|
||||
|
||||
//check that the correct device was mounted
|
||||
mountedDevice, _, err := GetDeviceNameFromMount(fakeMounter.FakeMounter, dest)
|
||||
if err != nil || mountedDevice != device {
|
||||
t.Errorf("test \"%s\" the correct device was not mounted", test.description)
|
||||
}
|
||||
} else {
|
||||
if err == nil || test.expectedError.Error() != err.Error() {
|
||||
t.Errorf("test \"%s\" unexpected error: \n [%v]. \nExpecting [%v]", test.description, err, test.expectedError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
286
vendor/k8s.io/kubernetes/pkg/util/net/net_test.go
generated
vendored
286
vendor/k8s.io/kubernetes/pkg/util/net/net_test.go
generated
vendored
|
|
@ -1,286 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 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 net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsIPv6String(t *testing.T) {
|
||||
testCases := []struct {
|
||||
ip string
|
||||
expectIPv6 bool
|
||||
}{
|
||||
{
|
||||
ip: "127.0.0.1",
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: "192.168.0.0",
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: "1.2.3.4",
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: "bad ip",
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: "::1",
|
||||
expectIPv6: true,
|
||||
},
|
||||
{
|
||||
ip: "fd00::600d:f00d",
|
||||
expectIPv6: true,
|
||||
},
|
||||
{
|
||||
ip: "2001:db8::5",
|
||||
expectIPv6: true,
|
||||
},
|
||||
}
|
||||
for i := range testCases {
|
||||
isIPv6 := IsIPv6String(testCases[i].ip)
|
||||
if isIPv6 != testCases[i].expectIPv6 {
|
||||
t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIPv6(t *testing.T) {
|
||||
testCases := []struct {
|
||||
ip net.IP
|
||||
expectIPv6 bool
|
||||
}{
|
||||
{
|
||||
ip: net.IPv4zero,
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: net.IPv4bcast,
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("127.0.0.1"),
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("10.20.40.40"),
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("172.17.3.0"),
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: nil,
|
||||
expectIPv6: false,
|
||||
},
|
||||
{
|
||||
ip: net.IPv6loopback,
|
||||
expectIPv6: true,
|
||||
},
|
||||
{
|
||||
ip: net.IPv6zero,
|
||||
expectIPv6: true,
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("fd00::600d:f00d"),
|
||||
expectIPv6: true,
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("2001:db8::5"),
|
||||
expectIPv6: true,
|
||||
},
|
||||
}
|
||||
for i := range testCases {
|
||||
isIPv6 := IsIPv6(testCases[i].ip)
|
||||
if isIPv6 != testCases[i].expectIPv6 {
|
||||
t.Errorf("[%d] Expect ipv6 %v, got %v", i+1, testCases[i].expectIPv6, isIPv6)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsIPv6CIDR(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
cidr string
|
||||
expectResult bool
|
||||
}{
|
||||
{
|
||||
desc: "ipv4 CIDR 1",
|
||||
cidr: "10.0.0.0/8",
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
desc: "ipv4 CIDR 2",
|
||||
cidr: "192.168.0.0/16",
|
||||
expectResult: false,
|
||||
},
|
||||
{
|
||||
desc: "ipv6 CIDR 1",
|
||||
cidr: "::/1",
|
||||
expectResult: true,
|
||||
},
|
||||
{
|
||||
desc: "ipv6 CIDR 2",
|
||||
cidr: "2000::/10",
|
||||
expectResult: true,
|
||||
},
|
||||
{
|
||||
desc: "ipv6 CIDR 3",
|
||||
cidr: "2001:db8::/32",
|
||||
expectResult: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
res := IsIPv6CIDR(tc.cidr)
|
||||
if res != tc.expectResult {
|
||||
t.Errorf("%v: want IsIPv6CIDR=%v, got %v", tc.desc, tc.expectResult, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterIncorrectIPVersion(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
isIPv6 bool
|
||||
ipStrings []string
|
||||
expectCorrects []string
|
||||
expectIncorrects []string
|
||||
}{
|
||||
{
|
||||
desc: "all ipv4 strings in ipv4 mode",
|
||||
isIPv6: false,
|
||||
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
|
||||
expectCorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
|
||||
expectIncorrects: nil,
|
||||
},
|
||||
{
|
||||
desc: "all ipv6 strings in ipv4 mode",
|
||||
isIPv6: false,
|
||||
ipStrings: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
|
||||
expectCorrects: nil,
|
||||
expectIncorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
|
||||
},
|
||||
{
|
||||
desc: "mixed versions in ipv4 mode",
|
||||
isIPv6: false,
|
||||
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1", "::1", "fd00::600d:f00d", "2001:db8::5"},
|
||||
expectCorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
|
||||
expectIncorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
|
||||
},
|
||||
{
|
||||
desc: "all ipv4 strings in ipv6 mode",
|
||||
isIPv6: true,
|
||||
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
|
||||
expectCorrects: nil,
|
||||
expectIncorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
|
||||
},
|
||||
{
|
||||
desc: "all ipv6 strings in ipv6 mode",
|
||||
isIPv6: true,
|
||||
ipStrings: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
|
||||
expectCorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
|
||||
expectIncorrects: nil,
|
||||
},
|
||||
{
|
||||
desc: "mixed versions in ipv6 mode",
|
||||
isIPv6: true,
|
||||
ipStrings: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1", "::1", "fd00::600d:f00d", "2001:db8::5"},
|
||||
expectCorrects: []string{"::1", "fd00::600d:f00d", "2001:db8::5"},
|
||||
expectIncorrects: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
corrects, incorrects := FilterIncorrectIPVersion(tc.ipStrings, tc.isIPv6)
|
||||
if !reflect.DeepEqual(tc.expectCorrects, corrects) {
|
||||
t.Errorf("%v: want corrects=%v, got %v", tc.desc, tc.expectCorrects, corrects)
|
||||
}
|
||||
if !reflect.DeepEqual(tc.expectIncorrects, incorrects) {
|
||||
t.Errorf("%v: want incorrects=%v, got %v", tc.desc, tc.expectIncorrects, incorrects)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterIncorrectCIDRVersion(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
isIPv6 bool
|
||||
cidrStrings []string
|
||||
expectCorrects []string
|
||||
expectIncorrects []string
|
||||
}{
|
||||
{
|
||||
desc: "all ipv4 strings in ipv4 mode",
|
||||
isIPv6: false,
|
||||
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1"},
|
||||
expectCorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
|
||||
expectIncorrects: nil,
|
||||
},
|
||||
{
|
||||
desc: "all ipv6 strings in ipv4 mode",
|
||||
isIPv6: false,
|
||||
cidrStrings: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
|
||||
expectCorrects: nil,
|
||||
expectIncorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
|
||||
},
|
||||
{
|
||||
desc: "mixed versions in ipv4 mode",
|
||||
isIPv6: false,
|
||||
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1", "2001:db8::/32", "2001:0db8:0123:4567::/64"},
|
||||
expectCorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
|
||||
expectIncorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
|
||||
},
|
||||
{
|
||||
desc: "all ipv4 strings in ipv6 mode",
|
||||
isIPv6: true,
|
||||
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1"},
|
||||
expectCorrects: nil,
|
||||
expectIncorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
|
||||
},
|
||||
{
|
||||
desc: "all ipv6 strings in ipv6 mode",
|
||||
isIPv6: true,
|
||||
cidrStrings: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
|
||||
expectCorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
|
||||
expectIncorrects: nil,
|
||||
},
|
||||
{
|
||||
desc: "mixed versions in ipv6 mode",
|
||||
isIPv6: true,
|
||||
cidrStrings: []string{"0.0.0.0/1", "1.0.0.0/1", "2001:db8::/32", "2001:0db8:0123:4567::/64"},
|
||||
expectCorrects: []string{"2001:db8::/32", "2001:0db8:0123:4567::/64"},
|
||||
expectIncorrects: []string{"0.0.0.0/1", "1.0.0.0/1"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
corrects, incorrects := FilterIncorrectCIDRVersion(tc.cidrStrings, tc.isIPv6)
|
||||
if !reflect.DeepEqual(tc.expectCorrects, corrects) {
|
||||
t.Errorf("%v: want corrects=%v, got %v", tc.desc, tc.expectCorrects, corrects)
|
||||
}
|
||||
if !reflect.DeepEqual(tc.expectIncorrects, incorrects) {
|
||||
t.Errorf("%v: want incorrects=%v, got %v", tc.desc, tc.expectIncorrects, incorrects)
|
||||
}
|
||||
}
|
||||
}
|
||||
155
vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet_test.go
generated
vendored
155
vendor/k8s.io/kubernetes/pkg/util/net/sets/ipnet_test.go
generated
vendored
|
|
@ -1,155 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 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 sets
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func parseIPNet(s string) *net.IPNet {
|
||||
_, net, err := net.ParseCIDR(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return net
|
||||
}
|
||||
|
||||
func TestIPNets(t *testing.T) {
|
||||
s := IPNet{}
|
||||
s2 := IPNet{}
|
||||
if len(s) != 0 {
|
||||
t.Errorf("Expected len=0: %d", len(s))
|
||||
}
|
||||
a := parseIPNet("1.0.0.0/8")
|
||||
b := parseIPNet("2.0.0.0/8")
|
||||
c := parseIPNet("3.0.0.0/8")
|
||||
d := parseIPNet("4.0.0.0/8")
|
||||
|
||||
s.Insert(a, b)
|
||||
if len(s) != 2 {
|
||||
t.Errorf("Expected len=2: %d", len(s))
|
||||
}
|
||||
s.Insert(c)
|
||||
if s.Has(d) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
if !s.Has(a) {
|
||||
t.Errorf("Missing contents: %#v", s)
|
||||
}
|
||||
s.Delete(a)
|
||||
if s.Has(a) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
s.Insert(a)
|
||||
if s.HasAll(a, b, d) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
if !s.HasAll(a, b) {
|
||||
t.Errorf("Missing contents: %#v", s)
|
||||
}
|
||||
s2.Insert(a, b, d)
|
||||
if s.IsSuperset(s2) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
s2.Delete(d)
|
||||
if !s.IsSuperset(s2) {
|
||||
t.Errorf("Missing contents: %#v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPNetSetDeleteMultiples(t *testing.T) {
|
||||
s := IPNet{}
|
||||
a := parseIPNet("1.0.0.0/8")
|
||||
b := parseIPNet("2.0.0.0/8")
|
||||
c := parseIPNet("3.0.0.0/8")
|
||||
|
||||
s.Insert(a, b, c)
|
||||
if len(s) != 3 {
|
||||
t.Errorf("Expected len=3: %d", len(s))
|
||||
}
|
||||
|
||||
s.Delete(a, c)
|
||||
if len(s) != 1 {
|
||||
t.Errorf("Expected len=1: %d", len(s))
|
||||
}
|
||||
if s.Has(a) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
if s.Has(c) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
if !s.Has(b) {
|
||||
t.Errorf("Missing contents: %#v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewIPSet(t *testing.T) {
|
||||
s, err := ParseIPNets("1.0.0.0/8", "2.0.0.0/8", "3.0.0.0/8")
|
||||
if err != nil {
|
||||
t.Errorf("error parsing IPNets: %v", err)
|
||||
}
|
||||
if len(s) != 3 {
|
||||
t.Errorf("Expected len=3: %d", len(s))
|
||||
}
|
||||
a := parseIPNet("1.0.0.0/8")
|
||||
b := parseIPNet("2.0.0.0/8")
|
||||
c := parseIPNet("3.0.0.0/8")
|
||||
|
||||
if !s.Has(a) || !s.Has(b) || !s.Has(c) {
|
||||
t.Errorf("Unexpected contents: %#v", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPNetSetDifference(t *testing.T) {
|
||||
l, err := ParseIPNets("1.0.0.0/8", "2.0.0.0/8", "3.0.0.0/8")
|
||||
if err != nil {
|
||||
t.Errorf("error parsing IPNets: %v", err)
|
||||
}
|
||||
r, err := ParseIPNets("1.0.0.0/8", "2.0.0.0/8", "4.0.0.0/8", "5.0.0.0/8")
|
||||
if err != nil {
|
||||
t.Errorf("error parsing IPNets: %v", err)
|
||||
}
|
||||
c := l.Difference(r)
|
||||
d := r.Difference(l)
|
||||
if len(c) != 1 {
|
||||
t.Errorf("Expected len=1: %d", len(c))
|
||||
}
|
||||
if !c.Has(parseIPNet("3.0.0.0/8")) {
|
||||
t.Errorf("Unexpected contents: %#v", c)
|
||||
}
|
||||
if len(d) != 2 {
|
||||
t.Errorf("Expected len=2: %d", len(d))
|
||||
}
|
||||
if !d.Has(parseIPNet("4.0.0.0/8")) || !d.Has(parseIPNet("5.0.0.0/8")) {
|
||||
t.Errorf("Unexpected contents: %#v", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPNetSetList(t *testing.T) {
|
||||
s, err := ParseIPNets("3.0.0.0/8", "1.0.0.0/8", "2.0.0.0/8")
|
||||
if err != nil {
|
||||
t.Errorf("error parsing IPNets: %v", err)
|
||||
}
|
||||
l := s.StringSlice()
|
||||
sort.Strings(l)
|
||||
if !reflect.DeepEqual(l, []string{"1.0.0.0/8", "2.0.0.0/8", "3.0.0.0/8"}) {
|
||||
t.Errorf("List gave unexpected result: %#v", l)
|
||||
}
|
||||
}
|
||||
51
vendor/k8s.io/kubernetes/pkg/util/parsers/parsers_test.go
generated
vendored
51
vendor/k8s.io/kubernetes/pkg/util/parsers/parsers_test.go
generated
vendored
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 parsers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Based on Docker test case removed in:
|
||||
// https://github.com/docker/docker/commit/4352da7803d182a6013a5238ce20a7c749db979a
|
||||
func TestParseImageName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Input string
|
||||
Repo string
|
||||
Tag string
|
||||
Digest string
|
||||
}{
|
||||
{Input: "root", Repo: "docker.io/library/root", Tag: "latest"},
|
||||
{Input: "root:tag", Repo: "docker.io/library/root", Tag: "tag"},
|
||||
{Input: "root@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "docker.io/library/root", Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
|
||||
{Input: "user/repo", Repo: "docker.io/user/repo", Tag: "latest"},
|
||||
{Input: "user/repo:tag", Repo: "docker.io/user/repo", Tag: "tag"},
|
||||
{Input: "user/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "docker.io/user/repo", Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
|
||||
{Input: "url:5000/repo", Repo: "url:5000/repo", Tag: "latest"},
|
||||
{Input: "url:5000/repo:tag", Repo: "url:5000/repo", Tag: "tag"},
|
||||
{Input: "url:5000/repo@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", Repo: "url:5000/repo", Digest: "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
|
||||
}
|
||||
for _, testCase := range testCases {
|
||||
repo, tag, digest, err := ParseImageName(testCase.Input)
|
||||
if err != nil {
|
||||
t.Errorf("ParseImageName(%s) failed: %v", testCase.Input, err)
|
||||
} else if repo != testCase.Repo || tag != testCase.Tag || digest != testCase.Digest {
|
||||
t.Errorf("Expected repo: %q, tag: %q and digest: %q, got %q, %q and %q", testCase.Repo, testCase.Tag, testCase.Digest,
|
||||
repo, tag, digest)
|
||||
}
|
||||
}
|
||||
}
|
||||
66
vendor/k8s.io/kubernetes/pkg/util/pointer/pointer_test.go
generated
vendored
66
vendor/k8s.io/kubernetes/pkg/util/pointer/pointer_test.go
generated
vendored
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 pointer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAllPtrFieldsNil(t *testing.T) {
|
||||
testCases := []struct {
|
||||
obj interface{}
|
||||
expected bool
|
||||
}{
|
||||
{struct{}{}, true},
|
||||
{struct{ Foo int }{12345}, true},
|
||||
{&struct{ Foo int }{12345}, true},
|
||||
{struct{ Foo *int }{nil}, true},
|
||||
{&struct{ Foo *int }{nil}, true},
|
||||
{struct {
|
||||
Foo int
|
||||
Bar *int
|
||||
}{12345, nil}, true},
|
||||
{&struct {
|
||||
Foo int
|
||||
Bar *int
|
||||
}{12345, nil}, true},
|
||||
{struct {
|
||||
Foo *int
|
||||
Bar *int
|
||||
}{nil, nil}, true},
|
||||
{&struct {
|
||||
Foo *int
|
||||
Bar *int
|
||||
}{nil, nil}, true},
|
||||
{struct{ Foo *int }{new(int)}, false},
|
||||
{&struct{ Foo *int }{new(int)}, false},
|
||||
{struct {
|
||||
Foo *int
|
||||
Bar *int
|
||||
}{nil, new(int)}, false},
|
||||
{&struct {
|
||||
Foo *int
|
||||
Bar *int
|
||||
}{nil, new(int)}, false},
|
||||
{(*struct{})(nil), true},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
if AllPtrFieldsNil(tc.obj) != tc.expected {
|
||||
t.Errorf("case[%d]: expected %t, got %t", i, tc.expected, !tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
687
vendor/k8s.io/kubernetes/pkg/util/taints/taints_test.go
generated
vendored
687
vendor/k8s.io/kubernetes/pkg/util/taints/taints_test.go
generated
vendored
|
|
@ -1,687 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 taints
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func TestTaintsVar(t *testing.T) {
|
||||
cases := []struct {
|
||||
f string
|
||||
err bool
|
||||
t []api.Taint
|
||||
}{
|
||||
{
|
||||
f: "",
|
||||
t: []api.Taint(nil),
|
||||
},
|
||||
{
|
||||
f: "--t=foo=bar:NoSchedule",
|
||||
t: []api.Taint{{Key: "foo", Value: "bar", Effect: "NoSchedule"}},
|
||||
},
|
||||
{
|
||||
f: "--t=foo=bar:NoSchedule,bing=bang:PreferNoSchedule",
|
||||
t: []api.Taint{
|
||||
{Key: "foo", Value: "bar", Effect: api.TaintEffectNoSchedule},
|
||||
{Key: "bing", Value: "bang", Effect: api.TaintEffectPreferNoSchedule},
|
||||
},
|
||||
},
|
||||
{
|
||||
f: "--t=dedicated-for=user1:NoExecute",
|
||||
t: []api.Taint{{Key: "dedicated-for", Value: "user1", Effect: "NoExecute"}},
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
args := append([]string{"test"}, strings.Fields(c.f)...)
|
||||
cli := pflag.NewFlagSet("test", pflag.ContinueOnError)
|
||||
var taints []api.Taint
|
||||
cli.Var(NewTaintsVar(&taints), "t", "bar")
|
||||
|
||||
err := cli.Parse(args)
|
||||
if err == nil && c.err {
|
||||
t.Errorf("[%v] expected error", i)
|
||||
continue
|
||||
}
|
||||
if err != nil && !c.err {
|
||||
t.Errorf("[%v] unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(c.t, taints) {
|
||||
t.Errorf("[%v] unexpected taints:\n\texpected:\n\t\t%#v\n\tgot:\n\t\t%#v", i, c.t, taints)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAddOrUpdateTaint(t *testing.T) {
|
||||
node := &v1.Node{}
|
||||
|
||||
taint := &v1.Taint{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
}
|
||||
|
||||
checkResult := func(testCaseName string, newNode *v1.Node, expectedTaint *v1.Taint, result, expectedResult bool, err error) {
|
||||
if err != nil {
|
||||
t.Errorf("[%s] should not raise error but got %v", testCaseName, err)
|
||||
}
|
||||
if result != expectedResult {
|
||||
t.Errorf("[%s] should return %t, but got: %t", testCaseName, expectedResult, result)
|
||||
}
|
||||
if len(newNode.Spec.Taints) != 1 || !reflect.DeepEqual(newNode.Spec.Taints[0], *expectedTaint) {
|
||||
t.Errorf("[%s] node should only have one taint: %v, but got: %v", testCaseName, *expectedTaint, newNode.Spec.Taints)
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new Taint.
|
||||
newNode, result, err := AddOrUpdateTaint(node, taint)
|
||||
checkResult("Add New Taint", newNode, taint, result, true, err)
|
||||
|
||||
// Update a Taint.
|
||||
taint.Value = "bar_1"
|
||||
newNode, result, err = AddOrUpdateTaint(node, taint)
|
||||
checkResult("Update Taint", newNode, taint, result, true, err)
|
||||
|
||||
// Add a duplicate Taint.
|
||||
node = newNode
|
||||
newNode, result, err = AddOrUpdateTaint(node, taint)
|
||||
checkResult("Add Duplicate Taint", newNode, taint, result, false, err)
|
||||
}
|
||||
|
||||
func TestTaintExists(t *testing.T) {
|
||||
testingTaints := []v1.Taint{
|
||||
{
|
||||
Key: "foo_1",
|
||||
Value: "bar_1",
|
||||
Effect: v1.TaintEffectNoExecute,
|
||||
},
|
||||
{
|
||||
Key: "foo_2",
|
||||
Value: "bar_2",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
taintToFind *v1.Taint
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
name: "taint exists",
|
||||
taintToFind: &v1.Taint{Key: "foo_1", Value: "bar_1", Effect: v1.TaintEffectNoExecute},
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "different key",
|
||||
taintToFind: &v1.Taint{Key: "no_such_key", Value: "bar_1", Effect: v1.TaintEffectNoExecute},
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "different effect",
|
||||
taintToFind: &v1.Taint{Key: "foo_1", Value: "bar_1", Effect: v1.TaintEffectNoSchedule},
|
||||
expectedResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
result := TaintExists(testingTaints, c.taintToFind)
|
||||
|
||||
if result != c.expectedResult {
|
||||
t.Errorf("[%s] unexpected results: %v", c.name, result)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveTaint(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
node *v1.Node
|
||||
taintToRemove *v1.Taint
|
||||
expectedTaints []v1.Taint
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
name: "remove taint unsuccessfully",
|
||||
node: &v1.Node{
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
taintToRemove: &v1.Taint{
|
||||
Key: "foo_1",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
expectedTaints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "remove taint successfully",
|
||||
node: &v1.Node{
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
taintToRemove: &v1.Taint{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
expectedTaints: []v1.Taint{},
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "remove taint from node with no taint",
|
||||
node: &v1.Node{
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{},
|
||||
},
|
||||
},
|
||||
taintToRemove: &v1.Taint{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
expectedTaints: []v1.Taint{},
|
||||
expectedResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
newNode, result, err := RemoveTaint(c.node, c.taintToRemove)
|
||||
if err != nil {
|
||||
t.Errorf("[%s] should not raise error but got: %v", c.name, err)
|
||||
}
|
||||
if result != c.expectedResult {
|
||||
t.Errorf("[%s] should return %t, but got: %t", c.name, c.expectedResult, result)
|
||||
}
|
||||
if !reflect.DeepEqual(newNode.Spec.Taints, c.expectedTaints) {
|
||||
t.Errorf("[%s] the new node object should have taints %v, but got: %v", c.name, c.expectedTaints, newNode.Spec.Taints)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteTaint(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
taints []v1.Taint
|
||||
taintToDelete *v1.Taint
|
||||
expectedTaints []v1.Taint
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
name: "delete taint with different name",
|
||||
taints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
taintToDelete: &v1.Taint{Key: "foo_1", Effect: v1.TaintEffectNoSchedule},
|
||||
expectedTaints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "delete taint with different effect",
|
||||
taints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
taintToDelete: &v1.Taint{Key: "foo", Effect: v1.TaintEffectNoExecute},
|
||||
expectedTaints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "delete taint successfully",
|
||||
taints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
taintToDelete: &v1.Taint{Key: "foo", Effect: v1.TaintEffectNoSchedule},
|
||||
expectedTaints: []v1.Taint{},
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "delete taint from empty taint array",
|
||||
taints: []v1.Taint{},
|
||||
taintToDelete: &v1.Taint{Key: "foo", Effect: v1.TaintEffectNoSchedule},
|
||||
expectedTaints: []v1.Taint{},
|
||||
expectedResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
taints, result := DeleteTaint(c.taints, c.taintToDelete)
|
||||
if result != c.expectedResult {
|
||||
t.Errorf("[%s] should return %t, but got: %t", c.name, c.expectedResult, result)
|
||||
}
|
||||
if !reflect.DeepEqual(taints, c.expectedTaints) {
|
||||
t.Errorf("[%s] the result taints should be %v, but got: %v", c.name, c.expectedTaints, taints)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteTaintByKey(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
taints []v1.Taint
|
||||
taintKey string
|
||||
expectedTaints []v1.Taint
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
name: "delete taint unsuccessfully",
|
||||
taints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
taintKey: "foo_1",
|
||||
expectedTaints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "delete taint successfully",
|
||||
taints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
taintKey: "foo",
|
||||
expectedTaints: []v1.Taint{},
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "delete taint from empty taint array",
|
||||
taints: []v1.Taint{},
|
||||
taintKey: "foo",
|
||||
expectedTaints: []v1.Taint{},
|
||||
expectedResult: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
taints, result := DeleteTaintsByKey(c.taints, c.taintKey)
|
||||
if result != c.expectedResult {
|
||||
t.Errorf("[%s] should return %t, but got: %t", c.name, c.expectedResult, result)
|
||||
}
|
||||
if !reflect.DeepEqual(c.expectedTaints, taints) {
|
||||
t.Errorf("[%s] the result taints should be %v, but got: %v", c.name, c.expectedTaints, taints)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckIfTaintsAlreadyExists(t *testing.T) {
|
||||
oldTaints := []v1.Taint{
|
||||
{
|
||||
Key: "foo_1",
|
||||
Value: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
{
|
||||
Key: "foo_2",
|
||||
Value: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
{
|
||||
Key: "foo_3",
|
||||
Value: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
taintsToCheck []v1.Taint
|
||||
expectedResult string
|
||||
}{
|
||||
{
|
||||
name: "empty array",
|
||||
taintsToCheck: []v1.Taint{},
|
||||
expectedResult: "",
|
||||
},
|
||||
{
|
||||
name: "no match",
|
||||
taintsToCheck: []v1.Taint{
|
||||
{
|
||||
Key: "foo_1",
|
||||
Effect: v1.TaintEffectNoExecute,
|
||||
},
|
||||
},
|
||||
expectedResult: "",
|
||||
},
|
||||
{
|
||||
name: "match one taint",
|
||||
taintsToCheck: []v1.Taint{
|
||||
{
|
||||
Key: "foo_2",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedResult: "foo_2",
|
||||
},
|
||||
{
|
||||
name: "match two taints",
|
||||
taintsToCheck: []v1.Taint{
|
||||
{
|
||||
Key: "foo_2",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
{
|
||||
Key: "foo_3",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedResult: "foo_2,foo_3",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
result := CheckIfTaintsAlreadyExists(oldTaints, c.taintsToCheck)
|
||||
if result != c.expectedResult {
|
||||
t.Errorf("[%s] should return '%s', but got: '%s'", c.name, c.expectedResult, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReorganizeTaints(t *testing.T) {
|
||||
node := &v1.Node{
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
overwrite bool
|
||||
taintsToAdd []v1.Taint
|
||||
taintsToDelete []v1.Taint
|
||||
expectedTaints []v1.Taint
|
||||
expectedOperation string
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "no changes with overwrite is true",
|
||||
overwrite: true,
|
||||
taintsToAdd: []v1.Taint{},
|
||||
taintsToDelete: []v1.Taint{},
|
||||
expectedTaints: node.Spec.Taints,
|
||||
expectedOperation: MODIFIED,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "no changes with overwrite is false",
|
||||
overwrite: false,
|
||||
taintsToAdd: []v1.Taint{},
|
||||
taintsToDelete: []v1.Taint{},
|
||||
expectedTaints: node.Spec.Taints,
|
||||
expectedOperation: UNTAINTED,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "add new taint",
|
||||
overwrite: false,
|
||||
taintsToAdd: []v1.Taint{
|
||||
{
|
||||
Key: "foo_1",
|
||||
Effect: v1.TaintEffectNoExecute,
|
||||
},
|
||||
},
|
||||
taintsToDelete: []v1.Taint{},
|
||||
expectedTaints: append([]v1.Taint{{Key: "foo_1", Effect: v1.TaintEffectNoExecute}}, node.Spec.Taints...),
|
||||
expectedOperation: TAINTED,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "delete taint with effect",
|
||||
overwrite: false,
|
||||
taintsToAdd: []v1.Taint{},
|
||||
taintsToDelete: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedTaints: []v1.Taint{},
|
||||
expectedOperation: UNTAINTED,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "delete taint with no effect",
|
||||
overwrite: false,
|
||||
taintsToAdd: []v1.Taint{},
|
||||
taintsToDelete: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
},
|
||||
},
|
||||
expectedTaints: []v1.Taint{},
|
||||
expectedOperation: UNTAINTED,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "delete non-exist taint",
|
||||
overwrite: false,
|
||||
taintsToAdd: []v1.Taint{},
|
||||
taintsToDelete: []v1.Taint{
|
||||
{
|
||||
Key: "foo_1",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedTaints: node.Spec.Taints,
|
||||
expectedOperation: UNTAINTED,
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "add new taint and delete old one",
|
||||
overwrite: false,
|
||||
taintsToAdd: []v1.Taint{
|
||||
{
|
||||
Key: "foo_1",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
taintsToDelete: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedTaints: []v1.Taint{
|
||||
{
|
||||
Key: "foo_1",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedOperation: MODIFIED,
|
||||
expectedErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
operation, taints, err := ReorganizeTaints(node, c.overwrite, c.taintsToAdd, c.taintsToDelete)
|
||||
if c.expectedErr && err == nil {
|
||||
t.Errorf("[%s] expect to see an error, but did not get one", c.name)
|
||||
} else if !c.expectedErr && err != nil {
|
||||
t.Errorf("[%s] expect not to see an error, but got one: %v", c.name, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(c.expectedTaints, taints) {
|
||||
t.Errorf("[%s] expect to see taint list %#v, but got: %#v", c.name, c.expectedTaints, taints)
|
||||
}
|
||||
|
||||
if c.expectedOperation != operation {
|
||||
t.Errorf("[%s] expect to see operation %s, but got: %s", c.name, c.expectedOperation, operation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTaints(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
spec []string
|
||||
expectedTaints []v1.Taint
|
||||
expectedTaintsToRemove []v1.Taint
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "invalid spec format",
|
||||
spec: []string{"foo=abc"},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid spec effect for adding taint",
|
||||
spec: []string{"foo=abc:invalid_effect"},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid spec effect for deleting taint",
|
||||
spec: []string{"foo:invalid_effect-"},
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "add new taints",
|
||||
spec: []string{"foo=abc:NoSchedule", "bar=abc:NoSchedule"},
|
||||
expectedTaints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "abc",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
{
|
||||
Key: "bar",
|
||||
Value: "abc",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "delete taints",
|
||||
spec: []string{"foo:NoSchedule-", "bar:NoSchedule-"},
|
||||
expectedTaintsToRemove: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
{
|
||||
Key: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "add taints and delete taints",
|
||||
spec: []string{"foo=abc:NoSchedule", "bar=abc:NoSchedule", "foo:NoSchedule-", "bar:NoSchedule-"},
|
||||
expectedTaints: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "abc",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
{
|
||||
Key: "bar",
|
||||
Value: "abc",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedTaintsToRemove: []v1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
{
|
||||
Key: "bar",
|
||||
Effect: v1.TaintEffectNoSchedule,
|
||||
},
|
||||
},
|
||||
expectedErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
taints, taintsToRemove, err := ParseTaints(c.spec)
|
||||
if c.expectedErr && err == nil {
|
||||
t.Errorf("[%s] expected error, but got nothing", c.name)
|
||||
}
|
||||
if !c.expectedErr && err != nil {
|
||||
t.Errorf("[%s] expected no error, but got: %v", c.name, err)
|
||||
}
|
||||
if !reflect.DeepEqual(c.expectedTaints, taints) {
|
||||
t.Errorf("[%s] expected returen taints as %v, but got: %v", c.name, c.expectedTaints, taints)
|
||||
}
|
||||
if !reflect.DeepEqual(c.expectedTaintsToRemove, taintsToRemove) {
|
||||
t.Errorf("[%s] expected return taints to be removed as %v, but got: %v", c.name, c.expectedTaintsToRemove, taintsToRemove)
|
||||
}
|
||||
}
|
||||
}
|
||||
112
vendor/k8s.io/kubernetes/pkg/volume/metrics_du_test.go
generated
vendored
112
vendor/k8s.io/kubernetes/pkg/volume/metrics_du_test.go
generated
vendored
|
|
@ -1,112 +0,0 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
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 volume_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
. "k8s.io/kubernetes/pkg/volume"
|
||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||
)
|
||||
|
||||
func getExpectedBlockSize(path string) int64 {
|
||||
statfs := &unix.Statfs_t{}
|
||||
err := unix.Statfs(path, statfs)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return int64(statfs.Bsize)
|
||||
}
|
||||
|
||||
// TestMetricsDuGetCapacity tests that MetricsDu can read disk usage
|
||||
// for path
|
||||
func TestMetricsDuGetCapacity(t *testing.T) {
|
||||
tmpDir, err := utiltesting.MkTmpdir("metrics_du_test")
|
||||
if err != nil {
|
||||
t.Fatalf("Can't make a tmp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
metrics := NewMetricsDu(tmpDir)
|
||||
|
||||
expectedEmptyDirUsage, err := volumetest.FindEmptyDirectoryUsageOnTmpfs()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error finding expected empty directory usage on tmpfs: %v", err)
|
||||
}
|
||||
|
||||
actual, err := metrics.GetMetrics()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when calling GetMetrics %v", err)
|
||||
}
|
||||
if e, a := expectedEmptyDirUsage.Value(), actual.Used.Value(); e != a {
|
||||
t.Errorf("Unexpected value for empty directory; expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
// TODO(pwittroc): Figure out a way to test these values for correctness, maybe by formatting and mounting a file
|
||||
// as a filesystem
|
||||
if a := actual.Capacity.Value(); a <= 0 {
|
||||
t.Errorf("Expected Capacity %d to be greater than 0.", a)
|
||||
}
|
||||
if a := actual.Available.Value(); a <= 0 {
|
||||
t.Errorf("Expected Available %d to be greater than 0.", a)
|
||||
}
|
||||
|
||||
// Write a file and expect Used to increase
|
||||
ioutil.WriteFile(filepath.Join(tmpDir, "f1"), []byte("Hello World"), os.ModeTemporary)
|
||||
actual, err = metrics.GetMetrics()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when calling GetMetrics %v", err)
|
||||
}
|
||||
if e, a := (expectedEmptyDirUsage.Value() + getExpectedBlockSize(filepath.Join(tmpDir, "f1"))), actual.Used.Value(); e != a {
|
||||
t.Errorf("Unexpected Used for directory with file. Expected %v, got %d.", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMetricsDuRequireInit tests that if MetricsDu is not initialized with a path, GetMetrics
|
||||
// returns an error
|
||||
func TestMetricsDuRequirePath(t *testing.T) {
|
||||
metrics := NewMetricsDu("")
|
||||
actual, err := metrics.GetMetrics()
|
||||
expected := &Metrics{}
|
||||
if !volumetest.MetricsEqualIgnoreTimestamp(actual, expected) {
|
||||
t.Errorf("Expected empty Metrics from uninitialized MetricsDu, actual %v", *actual)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("Expected error when calling GetMetrics on uninitialized MetricsDu, actual nil")
|
||||
}
|
||||
}
|
||||
|
||||
// TestMetricsDuRealDirectory tests that if MetricsDu is initialized to a non-existent path, GetMetrics
|
||||
// returns an error
|
||||
func TestMetricsDuRequireRealDirectory(t *testing.T) {
|
||||
metrics := NewMetricsDu("/not/a/real/directory")
|
||||
actual, err := metrics.GetMetrics()
|
||||
expected := &Metrics{}
|
||||
if !volumetest.MetricsEqualIgnoreTimestamp(actual, expected) {
|
||||
t.Errorf("Expected empty Metrics from incorrectly initialized MetricsDu, actual %v", *actual)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("Expected error when calling GetMetrics on incorrectly initialized MetricsDu, actual nil")
|
||||
}
|
||||
}
|
||||
33
vendor/k8s.io/kubernetes/pkg/volume/metrics_nil_test.go
generated
vendored
33
vendor/k8s.io/kubernetes/pkg/volume/metrics_nil_test.go
generated
vendored
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
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 volume
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMetricsNilGetCapacity(t *testing.T) {
|
||||
metrics := &MetricsNil{}
|
||||
actual, err := metrics.GetMetrics()
|
||||
expected := &Metrics{}
|
||||
if *actual != *expected {
|
||||
t.Errorf("Expected empty Metrics, actual %v", *actual)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("Expected error when calling GetMetrics, actual nil")
|
||||
}
|
||||
}
|
||||
67
vendor/k8s.io/kubernetes/pkg/volume/metrics_statfs_test.go
generated
vendored
67
vendor/k8s.io/kubernetes/pkg/volume/metrics_statfs_test.go
generated
vendored
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 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 volume_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
. "k8s.io/kubernetes/pkg/volume"
|
||||
volumetest "k8s.io/kubernetes/pkg/volume/testing"
|
||||
)
|
||||
|
||||
func TestGetMetricsStatFS(t *testing.T) {
|
||||
metrics := NewMetricsStatFS("")
|
||||
actual, err := metrics.GetMetrics()
|
||||
expected := &Metrics{}
|
||||
if !volumetest.MetricsEqualIgnoreTimestamp(actual, expected) {
|
||||
t.Errorf("Expected empty Metrics from uninitialized MetricsStatFS, actual %v", *actual)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("Expected error when calling GetMetrics on uninitialized MetricsStatFS, actual nil")
|
||||
}
|
||||
|
||||
metrics = NewMetricsStatFS("/not/a/real/directory")
|
||||
actual, err = metrics.GetMetrics()
|
||||
if !volumetest.MetricsEqualIgnoreTimestamp(actual, expected) {
|
||||
t.Errorf("Expected empty Metrics from incorrectly initialized MetricsStatFS, actual %v", *actual)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("Expected error when calling GetMetrics on incorrectly initialized MetricsStatFS, actual nil")
|
||||
}
|
||||
|
||||
tmpDir, err := utiltesting.MkTmpdir("metric_statfs_test")
|
||||
if err != nil {
|
||||
t.Fatalf("Can't make a tmp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
metrics = NewMetricsStatFS(tmpDir)
|
||||
actual, err = metrics.GetMetrics()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error when calling GetMetrics %v", err)
|
||||
}
|
||||
|
||||
if a := actual.Capacity.Value(); a <= 0 {
|
||||
t.Errorf("Expected Capacity %d to be greater than 0.", a)
|
||||
}
|
||||
if a := actual.Available.Value(); a <= 0 {
|
||||
t.Errorf("Expected Available %d to be greater than 0.", a)
|
||||
}
|
||||
|
||||
}
|
||||
167
vendor/k8s.io/kubernetes/pkg/volume/plugins_test.go
generated
vendored
167
vendor/k8s.io/kubernetes/pkg/volume/plugins_test.go
generated
vendored
|
|
@ -1,167 +0,0 @@
|
|||
/*
|
||||
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 volume
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
const testPluginName = "kubernetes.io/testPlugin"
|
||||
|
||||
func TestSpecSourceConverters(t *testing.T) {
|
||||
v := &v1.Volume{
|
||||
Name: "foo",
|
||||
VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
|
||||
}
|
||||
|
||||
converted := NewSpecFromVolume(v)
|
||||
if converted.Volume.EmptyDir == nil {
|
||||
t.Errorf("Unexpected nil EmptyDir: %#v", converted)
|
||||
}
|
||||
if v.Name != converted.Name() {
|
||||
t.Errorf("Expected %v but got %v", converted.Name(), v.Name)
|
||||
}
|
||||
|
||||
pv := &v1.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar"},
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{}},
|
||||
},
|
||||
}
|
||||
|
||||
converted = NewSpecFromPersistentVolume(pv, false)
|
||||
if converted.PersistentVolume.Spec.AWSElasticBlockStore == nil {
|
||||
t.Errorf("Unexpected nil AWSElasticBlockStore: %#v", converted)
|
||||
}
|
||||
if pv.Name != converted.Name() {
|
||||
t.Errorf("Expected %v but got %v", converted.Name(), pv.Name)
|
||||
}
|
||||
}
|
||||
|
||||
type testPlugins struct {
|
||||
}
|
||||
|
||||
func (plugin *testPlugins) Init(host VolumeHost) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *testPlugins) GetPluginName() string {
|
||||
return testPluginName
|
||||
}
|
||||
|
||||
func (plugin *testPlugins) GetVolumeName(spec *Spec) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (plugin *testPlugins) CanSupport(spec *Spec) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (plugin *testPlugins) RequiresRemount() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (plugin *testPlugins) SupportsMountOption() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (plugin *testPlugins) SupportsBulkVolumeVerification() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (plugin *testPlugins) NewMounter(spec *Spec, podRef *v1.Pod, opts VolumeOptions) (Mounter, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (plugin *testPlugins) NewUnmounter(name string, podUID types.UID) (Unmounter, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (plugin *testPlugins) ConstructVolumeSpec(volumeName, mountPath string) (*Spec, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func newTestPlugin() []VolumePlugin {
|
||||
return []VolumePlugin{&testPlugins{}}
|
||||
}
|
||||
|
||||
func TestVolumePluginMgrFunc(t *testing.T) {
|
||||
vpm := VolumePluginMgr{}
|
||||
var prober DynamicPluginProber = nil // TODO (#51147) inject mock
|
||||
vpm.InitPlugins(newTestPlugin(), prober, nil)
|
||||
|
||||
plug, err := vpm.FindPluginByName(testPluginName)
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin by name")
|
||||
}
|
||||
if plug.GetPluginName() != testPluginName {
|
||||
t.Errorf("Wrong name: %s", plug.GetPluginName())
|
||||
}
|
||||
|
||||
plug, err = vpm.FindPluginBySpec(nil)
|
||||
if err == nil {
|
||||
t.Errorf("Should return error if volume spec is nil")
|
||||
}
|
||||
|
||||
volumeSpec := &Spec{}
|
||||
plug, err = vpm.FindPluginBySpec(volumeSpec)
|
||||
if err != nil {
|
||||
t.Errorf("Should return test plugin if volume spec is not nil")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ValidatePodTemplate(t *testing.T) {
|
||||
pod := &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: "vol",
|
||||
VolumeSource: v1.VolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var want error
|
||||
if got := ValidateRecyclerPodTemplate(pod); got != want {
|
||||
t.Errorf("isPodTemplateValid(%v) returned (%v), want (%v)", pod.String(), got.Error(), want)
|
||||
}
|
||||
|
||||
// Check that the default recycle pod template is valid
|
||||
pod = NewPersistentVolumeRecyclerPodTemplate()
|
||||
want = nil
|
||||
if got := ValidateRecyclerPodTemplate(pod); got != want {
|
||||
t.Errorf("isPodTemplateValid(%v) returned (%v), want (%v)", pod.String(), got.Error(), want)
|
||||
}
|
||||
|
||||
pod = &v1.Pod{
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: "pv-recycler",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
// want = an error
|
||||
if got := ValidateRecyclerPodTemplate(pod); got == nil {
|
||||
t.Errorf("isPodTemplateValid(%v) returned (%v), want (%v)", pod.String(), got, "Error: pod specification does not contain any volume(s).")
|
||||
}
|
||||
}
|
||||
2
vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer.go
generated
vendored
2
vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer.go
generated
vendored
|
|
@ -230,7 +230,7 @@ func validatePayload(payload map[string]FileProjection) (map[string]FileProjecti
|
|||
return nil, err
|
||||
}
|
||||
|
||||
cleanPayload[path.Clean(k)] = content
|
||||
cleanPayload[filepath.Clean(k)] = content
|
||||
}
|
||||
|
||||
return cleanPayload, nil
|
||||
|
|
|
|||
813
vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer_test.go
generated
vendored
813
vendor/k8s.io/kubernetes/pkg/volume/util/atomic_writer_test.go
generated
vendored
|
|
@ -1,813 +0,0 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2016 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 util
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utiltesting "k8s.io/client-go/util/testing"
|
||||
)
|
||||
|
||||
func TestNewAtomicWriter(t *testing.T) {
|
||||
targetDir, err := utiltesting.MkTmpdir("atomic-write")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating tmp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(targetDir)
|
||||
|
||||
_, err = NewAtomicWriter(targetDir, "-test-")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating writer for existing target dir: %v", err)
|
||||
}
|
||||
|
||||
nonExistentDir, err := utiltesting.MkTmpdir("atomic-write")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating tmp dir: %v", err)
|
||||
}
|
||||
err = os.Remove(nonExistentDir)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error ensuring dir %v does not exist: %v", nonExistentDir, err)
|
||||
}
|
||||
|
||||
_, err = NewAtomicWriter(nonExistentDir, "-test-")
|
||||
if err == nil {
|
||||
t.Fatalf("unexpected success creating writer for nonexistent target dir: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePath(t *testing.T) {
|
||||
maxPath := strings.Repeat("a", maxPathLength+1)
|
||||
maxFile := strings.Repeat("a", maxFileNameLength+1)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
path string
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
name: "valid 1",
|
||||
path: "i/am/well/behaved.txt",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "valid 2",
|
||||
path: "keepyourheaddownandfollowtherules.txt",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "max path length",
|
||||
path: maxPath,
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "max file length",
|
||||
path: maxFile,
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "absolute failure",
|
||||
path: "/dev/null",
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "reserved path",
|
||||
path: "..sneaky.txt",
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "contains doubledot 1",
|
||||
path: "hello/there/../../../../../../etc/passwd",
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "contains doubledot 2",
|
||||
path: "hello/../etc/somethingbad",
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
path: "",
|
||||
valid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
err := validatePath(tc.path)
|
||||
if tc.valid && err != nil {
|
||||
t.Errorf("%v: unexpected failure: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !tc.valid && err == nil {
|
||||
t.Errorf("%v: unexpected success", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathsToRemove(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
payload1 map[string]FileProjection
|
||||
payload2 map[string]FileProjection
|
||||
expected sets.String
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
payload1: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
payload2: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
},
|
||||
expected: sets.NewString("bar.txt"),
|
||||
},
|
||||
{
|
||||
name: "simple 2",
|
||||
payload1: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"zip/bar.txt": {Mode: 0644, Data: []byte("zip/b}ar")},
|
||||
},
|
||||
payload2: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
},
|
||||
expected: sets.NewString("zip/bar.txt", "zip"),
|
||||
},
|
||||
{
|
||||
name: "subdirs 1",
|
||||
payload1: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"zip/zap/bar.txt": {Mode: 0644, Data: []byte("zip/bar")},
|
||||
},
|
||||
payload2: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
},
|
||||
expected: sets.NewString("zip/zap/bar.txt", "zip", "zip/zap"),
|
||||
},
|
||||
{
|
||||
name: "subdirs 2",
|
||||
payload1: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"zip/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/b}ar")},
|
||||
},
|
||||
payload2: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
},
|
||||
expected: sets.NewString("zip/1/2/3/4/bar.txt", "zip", "zip/1", "zip/1/2", "zip/1/2/3", "zip/1/2/3/4"),
|
||||
},
|
||||
{
|
||||
name: "subdirs 3",
|
||||
payload1: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"zip/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/b}ar")},
|
||||
"zap/a/b/c/bar.txt": {Mode: 0644, Data: []byte("zap/bar")},
|
||||
},
|
||||
payload2: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
},
|
||||
expected: sets.NewString("zip/1/2/3/4/bar.txt", "zip", "zip/1", "zip/1/2", "zip/1/2/3", "zip/1/2/3/4", "zap", "zap/a", "zap/a/b", "zap/a/b/c", "zap/a/b/c/bar.txt"),
|
||||
},
|
||||
{
|
||||
name: "subdirs 4",
|
||||
payload1: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"zap/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/bar")},
|
||||
"zap/1/2/c/bar.txt": {Mode: 0644, Data: []byte("zap/bar")},
|
||||
"zap/1/2/magic.txt": {Mode: 0644, Data: []byte("indigo")},
|
||||
},
|
||||
payload2: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"zap/1/2/magic.txt": {Mode: 0644, Data: []byte("indigo")},
|
||||
},
|
||||
expected: sets.NewString("zap/1/2/3/4/bar.txt", "zap/1/2/3", "zap/1/2/3/4", "zap/1/2/3/4/bar.txt", "zap/1/2/c", "zap/1/2/c/bar.txt"),
|
||||
},
|
||||
{
|
||||
name: "subdirs 5",
|
||||
payload1: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"zap/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/bar")},
|
||||
"zap/1/2/c/bar.txt": {Mode: 0644, Data: []byte("zap/bar")},
|
||||
},
|
||||
payload2: map[string]FileProjection{
|
||||
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"zap/1/2/magic.txt": {Mode: 0644, Data: []byte("indigo")},
|
||||
},
|
||||
expected: sets.NewString("zap/1/2/3/4/bar.txt", "zap/1/2/3", "zap/1/2/3/4", "zap/1/2/3/4/bar.txt", "zap/1/2/c", "zap/1/2/c/bar.txt"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
targetDir, err := utiltesting.MkTmpdir("atomic-write")
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error creating tmp dir: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
defer os.RemoveAll(targetDir)
|
||||
|
||||
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
||||
err = writer.Write(tc.payload1)
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error writing: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
dataDirPath := path.Join(targetDir, dataDirName)
|
||||
oldTsDir, err := os.Readlink(dataDirPath)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
t.Errorf("Data symlink does not exist: %v", dataDirPath)
|
||||
continue
|
||||
} else if err != nil {
|
||||
t.Errorf("Unable to read symlink %v: %v", dataDirPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
actual, err := writer.pathsToRemove(tc.payload2, path.Join(targetDir, oldTsDir))
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error determining paths to remove: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if e, a := tc.expected, actual; !e.Equal(a) {
|
||||
t.Errorf("%v: unexpected paths to remove:\nexpected: %v\n got: %v", tc.name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteOnce(t *testing.T) {
|
||||
// $1 if you can tell me what this binary is
|
||||
encodedMysteryBinary := `f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAAB
|
||||
AAAAAAAAAAEAAAAFAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAfQAAAAAAAAB9AAAAAAAAAAAA
|
||||
IAAAAAAAsDyZDwU=`
|
||||
|
||||
mysteryBinaryBytes := make([]byte, base64.StdEncoding.DecodedLen(len(encodedMysteryBinary)))
|
||||
numBytes, err := base64.StdEncoding.Decode(mysteryBinaryBytes, []byte(encodedMysteryBinary))
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error decoding binary payload: %v", err)
|
||||
}
|
||||
|
||||
if numBytes != 125 {
|
||||
t.Fatalf("Unexpected decoded binary size: expected 125, got %v", numBytes)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
payload map[string]FileProjection
|
||||
success bool
|
||||
}{
|
||||
{
|
||||
name: "invalid payload 1",
|
||||
payload: map[string]FileProjection{
|
||||
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||
"..bar": {Mode: 0644, Data: []byte("bar")},
|
||||
"binary.bin": {Mode: 0644, Data: mysteryBinaryBytes},
|
||||
},
|
||||
success: false,
|
||||
},
|
||||
{
|
||||
name: "invalid payload 2",
|
||||
payload: map[string]FileProjection{
|
||||
"foo/../bar": {Mode: 0644, Data: []byte("foo")},
|
||||
},
|
||||
success: false,
|
||||
},
|
||||
{
|
||||
name: "basic 1",
|
||||
payload: map[string]FileProjection{
|
||||
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "basic 2",
|
||||
payload: map[string]FileProjection{
|
||||
"binary.bin": {Mode: 0644, Data: mysteryBinaryBytes},
|
||||
".binary.bin": {Mode: 0644, Data: mysteryBinaryBytes},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "basic mode 1",
|
||||
payload: map[string]FileProjection{
|
||||
"foo": {Mode: 0777, Data: []byte("foo")},
|
||||
"bar": {Mode: 0400, Data: []byte("bar")},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "dotfiles",
|
||||
payload: map[string]FileProjection{
|
||||
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||
".dotfile": {Mode: 0644, Data: []byte("dotfile")},
|
||||
".dotfile.file": {Mode: 0644, Data: []byte("dotfile.file")},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "dotfiles mode",
|
||||
payload: map[string]FileProjection{
|
||||
"foo": {Mode: 0407, Data: []byte("foo")},
|
||||
"bar": {Mode: 0440, Data: []byte("bar")},
|
||||
".dotfile": {Mode: 0777, Data: []byte("dotfile")},
|
||||
".dotfile.file": {Mode: 0666, Data: []byte("dotfile.file")},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "subdirectories 1",
|
||||
payload: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "subdirectories mode 1",
|
||||
payload: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0400, Data: []byte("foo/bar")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "subdirectories 2",
|
||||
payload: map[string]FileProjection{
|
||||
"foo//bar.txt": {Mode: 0644, Data: []byte("foo//bar")},
|
||||
"bar///bar/zab.txt": {Mode: 0644, Data: []byte("bar/../bar/zab.txt")},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "subdirectories 3",
|
||||
payload: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
|
||||
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt")},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "kitchen sink",
|
||||
payload: map[string]FileProjection{
|
||||
"foo.log": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar.zap": {Mode: 0644, Data: []byte("bar")},
|
||||
".dotfile": {Mode: 0644, Data: []byte("dotfile")},
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
|
||||
"bar/zib/zab.txt": {Mode: 0400, Data: []byte("bar/zib/zab.txt")},
|
||||
"1/2/3/4/5/6/7/8/9/10/.dotfile.lib": {Mode: 0777, Data: []byte("1-2-3-dotfile")},
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
targetDir, err := utiltesting.MkTmpdir("atomic-write")
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error creating tmp dir: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
defer os.RemoveAll(targetDir)
|
||||
|
||||
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
||||
err = writer.Write(tc.payload)
|
||||
if err != nil && tc.success {
|
||||
t.Errorf("%v: unexpected error writing payload: %v", tc.name, err)
|
||||
continue
|
||||
} else if err == nil && !tc.success {
|
||||
t.Errorf("%v: unexpected success", tc.name)
|
||||
continue
|
||||
} else if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
checkVolumeContents(targetDir, tc.name, tc.payload, t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
first map[string]FileProjection
|
||||
next map[string]FileProjection
|
||||
shouldWrite bool
|
||||
}{
|
||||
{
|
||||
name: "update",
|
||||
first: map[string]FileProjection{
|
||||
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"foo": {Mode: 0644, Data: []byte("foo2")},
|
||||
"bar": {Mode: 0640, Data: []byte("bar2")},
|
||||
},
|
||||
shouldWrite: true,
|
||||
},
|
||||
{
|
||||
name: "no update",
|
||||
first: map[string]FileProjection{
|
||||
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
shouldWrite: false,
|
||||
},
|
||||
{
|
||||
name: "no update 2",
|
||||
first: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
shouldWrite: false,
|
||||
},
|
||||
{
|
||||
name: "add 1",
|
||||
first: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
"blu/zip.txt": {Mode: 0644, Data: []byte("zip")},
|
||||
},
|
||||
shouldWrite: true,
|
||||
},
|
||||
{
|
||||
name: "add 2",
|
||||
first: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
"blu/two/2/3/4/5/zip.txt": {Mode: 0644, Data: []byte("zip")},
|
||||
},
|
||||
shouldWrite: true,
|
||||
},
|
||||
{
|
||||
name: "add 3",
|
||||
first: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
"bar/2/3/4/5/zip.txt": {Mode: 0644, Data: []byte("zip")},
|
||||
},
|
||||
shouldWrite: true,
|
||||
},
|
||||
{
|
||||
name: "delete 1",
|
||||
first: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
},
|
||||
shouldWrite: true,
|
||||
},
|
||||
{
|
||||
name: "delete 2",
|
||||
first: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/1/2/3/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
},
|
||||
shouldWrite: true,
|
||||
},
|
||||
{
|
||||
name: "delete 3",
|
||||
first: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
|
||||
"bar/1/2/3/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
|
||||
},
|
||||
shouldWrite: true,
|
||||
},
|
||||
{
|
||||
name: "delete 4",
|
||||
first: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
|
||||
"bar/1/2/3/4/5/6zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
|
||||
},
|
||||
shouldWrite: true,
|
||||
},
|
||||
{
|
||||
name: "delete all",
|
||||
first: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
|
||||
"bar/1/2/3/4/5/6zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
next: map[string]FileProjection{},
|
||||
shouldWrite: true,
|
||||
},
|
||||
{
|
||||
name: "add and delete 1",
|
||||
first: map[string]FileProjection{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||
},
|
||||
next: map[string]FileProjection{
|
||||
"bar/baz.txt": {Mode: 0644, Data: []byte("baz")},
|
||||
},
|
||||
shouldWrite: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
targetDir, err := utiltesting.MkTmpdir("atomic-write")
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error creating tmp dir: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
defer os.RemoveAll(targetDir)
|
||||
|
||||
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
||||
|
||||
err = writer.Write(tc.first)
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error writing: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
checkVolumeContents(targetDir, tc.name, tc.first, t)
|
||||
if !tc.shouldWrite {
|
||||
continue
|
||||
}
|
||||
|
||||
err = writer.Write(tc.next)
|
||||
if err != nil {
|
||||
if tc.shouldWrite {
|
||||
t.Errorf("%v: unexpected error writing: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
} else if !tc.shouldWrite {
|
||||
t.Errorf("%v: unexpected success", tc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
checkVolumeContents(targetDir, tc.name, tc.next, t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleUpdates(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
payloads []map[string]FileProjection
|
||||
}{
|
||||
{
|
||||
name: "update 1",
|
||||
payloads: []map[string]FileProjection{
|
||||
{
|
||||
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
{
|
||||
"foo": {Mode: 0400, Data: []byte("foo2")},
|
||||
"bar": {Mode: 0400, Data: []byte("bar2")},
|
||||
},
|
||||
{
|
||||
"foo": {Mode: 0600, Data: []byte("foo3")},
|
||||
"bar": {Mode: 0600, Data: []byte("bar3")},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "update 2",
|
||||
payloads: []map[string]FileProjection{
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||
},
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||
"bar/zab.txt": {Mode: 0400, Data: []byte("bar/zab.txt2")},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "clear sentinel",
|
||||
payloads: []map[string]FileProjection{
|
||||
{
|
||||
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||
},
|
||||
{
|
||||
"foo": {Mode: 0644, Data: []byte("foo2")},
|
||||
"bar": {Mode: 0644, Data: []byte("bar2")},
|
||||
},
|
||||
{
|
||||
"foo": {Mode: 0644, Data: []byte("foo3")},
|
||||
"bar": {Mode: 0644, Data: []byte("bar3")},
|
||||
},
|
||||
{
|
||||
"foo": {Mode: 0644, Data: []byte("foo4")},
|
||||
"bar": {Mode: 0644, Data: []byte("bar4")},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "subdirectories 2",
|
||||
payloads: []map[string]FileProjection{
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
|
||||
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt")},
|
||||
},
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
|
||||
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
|
||||
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add 1",
|
||||
payloads: []map[string]FileProjection{
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||
"bar//zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
|
||||
"bar/zib////zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt")},
|
||||
},
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
|
||||
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
|
||||
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
|
||||
"add/new/keys.txt": {Mode: 0644, Data: []byte("addNewKeys")},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add 2",
|
||||
payloads: []map[string]FileProjection{
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
|
||||
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
|
||||
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
|
||||
"add/new/keys.txt": {Mode: 0644, Data: []byte("addNewKeys")},
|
||||
},
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
|
||||
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
|
||||
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
|
||||
"add/new/keys.txt": {Mode: 0644, Data: []byte("addNewKeys")},
|
||||
"add/new/keys2.txt": {Mode: 0644, Data: []byte("addNewKeys2")},
|
||||
"add/new/keys3.txt": {Mode: 0644, Data: []byte("addNewKeys3")},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove 1",
|
||||
payloads: []map[string]FileProjection{
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||
"bar//zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
|
||||
"zip/zap/zup/fop.txt": {Mode: 0644, Data: []byte("zip/zap/zup/fop.txt")},
|
||||
},
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
|
||||
},
|
||||
{
|
||||
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
targetDir, err := utiltesting.MkTmpdir("atomic-write")
|
||||
if err != nil {
|
||||
t.Errorf("%v: unexpected error creating tmp dir: %v", tc.name, err)
|
||||
continue
|
||||
}
|
||||
defer os.RemoveAll(targetDir)
|
||||
|
||||
writer := &AtomicWriter{targetDir: targetDir, logContext: "-test-"}
|
||||
|
||||
for _, payload := range tc.payloads {
|
||||
writer.Write(payload)
|
||||
|
||||
checkVolumeContents(targetDir, tc.name, payload, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkVolumeContents(targetDir, tcName string, payload map[string]FileProjection, t *testing.T) {
|
||||
dataDirPath := path.Join(targetDir, dataDirName)
|
||||
// use filepath.Walk to reconstruct the payload, then deep equal
|
||||
observedPayload := make(map[string]FileProjection)
|
||||
visitor := func(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
relativePath := strings.TrimPrefix(path, dataDirPath)
|
||||
relativePath = strings.TrimPrefix(relativePath, "/")
|
||||
if strings.HasPrefix(relativePath, "..") {
|
||||
return nil
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mode := int32(fileInfo.Mode())
|
||||
|
||||
observedPayload[relativePath] = FileProjection{Data: content, Mode: mode}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
d, err := ioutil.ReadDir(targetDir)
|
||||
if err != nil {
|
||||
t.Errorf("Unable to read dir %v: %v", targetDir, err)
|
||||
return
|
||||
}
|
||||
for _, info := range d {
|
||||
if strings.HasPrefix(info.Name(), "..") {
|
||||
continue
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
p := path.Join(targetDir, info.Name())
|
||||
actual, err := os.Readlink(p)
|
||||
if err != nil {
|
||||
t.Errorf("Unable to read symlink %v: %v", p, err)
|
||||
continue
|
||||
}
|
||||
if err := filepath.Walk(path.Join(targetDir, actual), visitor); err != nil {
|
||||
t.Errorf("%v: unexpected error walking directory: %v", tcName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanPathPayload := make(map[string]FileProjection, len(payload))
|
||||
for k, v := range payload {
|
||||
cleanPathPayload[path.Clean(k)] = v
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(cleanPathPayload, observedPayload) {
|
||||
t.Errorf("%v: payload and observed payload do not match.", tcName)
|
||||
}
|
||||
}
|
||||
160
vendor/k8s.io/kubernetes/pkg/volume/util/device_util_linux_test.go
generated
vendored
160
vendor/k8s.io/kubernetes/pkg/volume/util/device_util_linux_test.go
generated
vendored
|
|
@ -1,160 +0,0 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2016 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 util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type mockOsIOHandler struct{}
|
||||
|
||||
func (handler *mockOsIOHandler) ReadDir(dirname string) ([]os.FileInfo, error) {
|
||||
switch dirname {
|
||||
case "/sys/block/dm-1/slaves":
|
||||
f1 := &fakeFileInfo{
|
||||
name: "sda",
|
||||
}
|
||||
f2 := &fakeFileInfo{
|
||||
name: "sdb",
|
||||
}
|
||||
return []os.FileInfo{f1, f2}, nil
|
||||
case "/sys/block/":
|
||||
f1 := &fakeFileInfo{
|
||||
name: "sda",
|
||||
}
|
||||
f2 := &fakeFileInfo{
|
||||
name: "dm-1",
|
||||
}
|
||||
return []os.FileInfo{f1, f2}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (handler *mockOsIOHandler) Lstat(name string) (os.FileInfo, error) {
|
||||
links := map[string]string{
|
||||
"/sys/block/dm-1/slaves/sda": "sda",
|
||||
"/dev/sda": "sda",
|
||||
}
|
||||
if dev, ok := links[name]; ok {
|
||||
return &fakeFileInfo{name: dev}, nil
|
||||
}
|
||||
return nil, errors.New("Not Implemented for Mock")
|
||||
}
|
||||
|
||||
func (handler *mockOsIOHandler) EvalSymlinks(path string) (string, error) {
|
||||
links := map[string]string{
|
||||
"/returns/a/dev": "/dev/sde",
|
||||
"/returns/non/dev": "/sys/block",
|
||||
"/dev/disk/by-path/127.0.0.1:3260-eui.02004567A425678D-lun-0": "/dev/sda",
|
||||
"/dev/disk/by-path/127.0.0.3:3260-eui.03004567A425678D-lun-0": "/dev/sdb",
|
||||
"/dev/dm-2": "/dev/dm-2",
|
||||
"/dev/dm-3": "/dev/dm-3",
|
||||
"/dev/sdc": "/dev/sdc",
|
||||
"/dev/sde": "/dev/sde",
|
||||
}
|
||||
return links[path], nil
|
||||
}
|
||||
|
||||
func (handler *mockOsIOHandler) WriteFile(filename string, data []byte, perm os.FileMode) error {
|
||||
return errors.New("Not Implemented for Mock")
|
||||
}
|
||||
|
||||
type fakeFileInfo struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (fi *fakeFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
|
||||
func (fi *fakeFileInfo) Size() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (fi *fakeFileInfo) Mode() os.FileMode {
|
||||
return 777
|
||||
}
|
||||
|
||||
func (fi *fakeFileInfo) ModTime() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
func (fi *fakeFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (fi *fakeFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestFindMultipathDeviceForDevice(t *testing.T) {
|
||||
mockDeviceUtil := NewDeviceHandler(&mockOsIOHandler{})
|
||||
dev := mockDeviceUtil.FindMultipathDeviceForDevice("/dev/disk/by-path/127.0.0.1:3260-eui.02004567A425678D-lun-0")
|
||||
if dev != "/dev/dm-1" {
|
||||
t.Fatalf("mpio device not found dm-1 expected got [%s]", dev)
|
||||
}
|
||||
dev = mockDeviceUtil.FindMultipathDeviceForDevice("/dev/disk/by-path/empty")
|
||||
if dev != "" {
|
||||
t.Fatalf("mpio device not found '' expected got [%s]", dev)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindDeviceForPath(t *testing.T) {
|
||||
io := &mockOsIOHandler{}
|
||||
|
||||
disk, err := findDeviceForPath("/dev/sde", io)
|
||||
if err != nil {
|
||||
t.Fatalf("error finding device for path /dev/sde:%v", err)
|
||||
}
|
||||
if disk != "sde" {
|
||||
t.Fatalf("disk [%s] didn't match expected sde", disk)
|
||||
}
|
||||
disk, err = findDeviceForPath("/returns/a/dev", io)
|
||||
if err != nil {
|
||||
t.Fatalf("error finding device for path /returns/a/dev:%v", err)
|
||||
}
|
||||
if disk != "sde" {
|
||||
t.Fatalf("disk [%s] didn't match expected sde", disk)
|
||||
}
|
||||
_, err = findDeviceForPath("/returns/non/dev", io)
|
||||
if err == nil {
|
||||
t.Fatalf("link is to incorrect dev")
|
||||
}
|
||||
|
||||
_, err = findDeviceForPath("/path/doesnt/exist", &osIOHandler{})
|
||||
if err == nil {
|
||||
t.Fatalf("path shouldn't exist but still doesn't give an error")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFindSlaveDevicesOnMultipath(t *testing.T) {
|
||||
mockDeviceUtil := NewDeviceHandler(&mockOsIOHandler{})
|
||||
devices := mockDeviceUtil.FindSlaveDevicesOnMultipath("/dev/dm-1")
|
||||
if !reflect.DeepEqual(devices, []string{"/dev/sda", "/dev/sdb"}) {
|
||||
t.Fatalf("failed to find devices managed by mpio device. /dev/sda, /dev/sdb expected got [%s]", devices)
|
||||
}
|
||||
dev := mockDeviceUtil.FindSlaveDevicesOnMultipath("/dev/sdc")
|
||||
if len(dev) != 0 {
|
||||
t.Fatalf("mpio device not found '' expected got [%s]", dev)
|
||||
}
|
||||
}
|
||||
233
vendor/k8s.io/kubernetes/pkg/volume/util/nested_volumes_test.go
generated
vendored
233
vendor/k8s.io/kubernetes/pkg/volume/util/nested_volumes_test.go
generated
vendored
|
|
@ -1,233 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 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 util
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
type testCases struct {
|
||||
name string
|
||||
err bool
|
||||
expected sets.String
|
||||
volname string
|
||||
pod v1.Pod
|
||||
}
|
||||
|
||||
func TestGetNestedMountpoints(t *testing.T) {
|
||||
var (
|
||||
testNamespace = "test_namespace"
|
||||
testPodUID = types.UID("test_pod_uid")
|
||||
)
|
||||
|
||||
tc := []testCases{
|
||||
{
|
||||
name: "Simple Pod",
|
||||
err: false,
|
||||
expected: sets.NewString(),
|
||||
volname: "vol1",
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: testNamespace,
|
||||
UID: testPodUID,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{MountPath: "/dir", Name: "vol1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Simple Nested Pod",
|
||||
err: false,
|
||||
expected: sets.NewString("nested"),
|
||||
volname: "vol1",
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: testNamespace,
|
||||
UID: testPodUID,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{MountPath: "/dir", Name: "vol1"},
|
||||
{MountPath: "/dir/nested", Name: "vol2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Unsorted Nested Pod",
|
||||
err: false,
|
||||
expected: sets.NewString("nested", "nested2"),
|
||||
volname: "vol1",
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: testNamespace,
|
||||
UID: testPodUID,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{MountPath: "/dir/nested/double", Name: "vol3"},
|
||||
{MountPath: "/ignore", Name: "vol4"},
|
||||
{MountPath: "/dir/nested", Name: "vol2"},
|
||||
{MountPath: "/ignore2", Name: "vol5"},
|
||||
{MountPath: "/dir", Name: "vol1"},
|
||||
{MountPath: "/dir/nested2", Name: "vol3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple vol1 mounts Pod",
|
||||
err: false,
|
||||
expected: sets.NewString("nested", "nested2"),
|
||||
volname: "vol1",
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: testNamespace,
|
||||
UID: testPodUID,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{MountPath: "/dir", Name: "vol1"},
|
||||
{MountPath: "/dir/nested", Name: "vol2"},
|
||||
{MountPath: "/ignore", Name: "vol4"},
|
||||
{MountPath: "/other", Name: "vol1"},
|
||||
{MountPath: "/other/nested2", Name: "vol3"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Big Pod",
|
||||
err: false,
|
||||
volname: "vol1",
|
||||
expected: sets.NewString("sub1/sub2/sub3", "sub1/sub2/sub4", "sub1/sub2/sub6", "sub"),
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: testNamespace,
|
||||
UID: testPodUID,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{MountPath: "/mnt", Name: "vol1"},
|
||||
{MountPath: "/ignore", Name: "vol2"},
|
||||
{MountPath: "/mnt/sub1/sub2/sub3", Name: "vol3"},
|
||||
{MountPath: "/mnt/sub1/sub2/sub4", Name: "vol4"},
|
||||
{MountPath: "/mnt/sub1/sub2/sub4/skip", Name: "vol5"},
|
||||
{MountPath: "/mnt/sub1/sub2/sub4/skip2", Name: "vol5a"},
|
||||
{MountPath: "/mnt/sub1/sub2/sub6", Name: "vol6"},
|
||||
{MountPath: "/mnt7", Name: "vol7"},
|
||||
},
|
||||
},
|
||||
},
|
||||
InitContainers: []v1.Container{
|
||||
{
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{MountPath: "/mnt/dir", Name: "vol1"},
|
||||
{MountPath: "/mnt/dir_ignore", Name: "vol8"},
|
||||
{MountPath: "/ignore", Name: "vol9"},
|
||||
{MountPath: "/mnt/dir/sub", Name: "vol11"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Naughty Pod",
|
||||
err: true,
|
||||
expected: nil,
|
||||
volname: "vol1",
|
||||
pod: v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: testNamespace,
|
||||
UID: testPodUID,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{MountPath: "foo/../../dir", Name: "vol1"},
|
||||
{MountPath: "foo/../../dir/skip", Name: "vol10"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tc {
|
||||
dir, err := ioutil.TempDir("", "TestMakeNestedMountpoints.")
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error trying to create temp directory: %v", err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
rootdir := path.Join(dir, "vol")
|
||||
err = os.Mkdir(rootdir, 0755)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error trying to create temp root directory: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
dirs, err := getNestedMountpoints(test.volname, rootdir, test.pod)
|
||||
if test.err {
|
||||
if err == nil {
|
||||
t.Errorf("%v: expected error, got nil", test.name)
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("%v: expected no error, got %v", test.name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
actual := sets.NewString(dirs...)
|
||||
if !test.expected.Equal(actual) {
|
||||
t.Errorf("%v: unexpected nested directories created:\nexpected: %v\n got: %v", test.name, test.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
235
vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/recycler_client_test.go
generated
vendored
235
vendor/k8s.io/kubernetes/pkg/volume/util/recyclerclient/recycler_client_test.go
generated
vendored
|
|
@ -1,235 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 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 recyclerclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
type testcase struct {
|
||||
// Input of the test
|
||||
name string
|
||||
existingPod *v1.Pod
|
||||
createPod *v1.Pod
|
||||
// eventSequence is list of events that are simulated during recycling. It
|
||||
// can be either event generated by a recycler pod or a state change of
|
||||
// the pod. (see newPodEvent and newEvent below).
|
||||
eventSequence []watch.Event
|
||||
|
||||
// Expected output.
|
||||
// expectedEvents is list of events that were sent to the volume that was
|
||||
// recycled.
|
||||
expectedEvents []mockEvent
|
||||
expectedError string
|
||||
}
|
||||
|
||||
func newPodEvent(eventtype watch.EventType, name string, phase v1.PodPhase, message string) watch.Event {
|
||||
return watch.Event{
|
||||
Type: eventtype,
|
||||
Object: newPod(name, phase, message),
|
||||
}
|
||||
}
|
||||
|
||||
func newEvent(eventtype, message string) watch.Event {
|
||||
return watch.Event{
|
||||
Type: watch.Added,
|
||||
Object: &v1.Event{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
Reason: "MockEvent",
|
||||
Message: message,
|
||||
Type: eventtype,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newPod(name string, phase v1.PodPhase, message string) *v1.Pod {
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
Name: name,
|
||||
},
|
||||
Status: v1.PodStatus{
|
||||
Phase: phase,
|
||||
Message: message,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecyclerPod(t *testing.T) {
|
||||
tests := []testcase{
|
||||
{
|
||||
// Test recycler success with some events
|
||||
name: "RecyclerSuccess",
|
||||
createPod: newPod("podRecyclerSuccess", v1.PodPending, ""),
|
||||
eventSequence: []watch.Event{
|
||||
// Pod gets Running and Succeeded
|
||||
newPodEvent(watch.Added, "podRecyclerSuccess", v1.PodPending, ""),
|
||||
newEvent(v1.EventTypeNormal, "Successfully assigned recycler-for-podRecyclerSuccess to 127.0.0.1"),
|
||||
newEvent(v1.EventTypeNormal, "pulling image \"k8s.gcr.io/busybox\""),
|
||||
newEvent(v1.EventTypeNormal, "Successfully pulled image \"k8s.gcr.io/busybox\""),
|
||||
newEvent(v1.EventTypeNormal, "Created container with docker id 83d929aeac82"),
|
||||
newEvent(v1.EventTypeNormal, "Started container with docker id 83d929aeac82"),
|
||||
newPodEvent(watch.Modified, "podRecyclerSuccess", v1.PodRunning, ""),
|
||||
newPodEvent(watch.Modified, "podRecyclerSuccess", v1.PodSucceeded, ""),
|
||||
},
|
||||
expectedEvents: []mockEvent{
|
||||
{v1.EventTypeNormal, "Successfully assigned recycler-for-podRecyclerSuccess to 127.0.0.1"},
|
||||
{v1.EventTypeNormal, "pulling image \"k8s.gcr.io/busybox\""},
|
||||
{v1.EventTypeNormal, "Successfully pulled image \"k8s.gcr.io/busybox\""},
|
||||
{v1.EventTypeNormal, "Created container with docker id 83d929aeac82"},
|
||||
{v1.EventTypeNormal, "Started container with docker id 83d929aeac82"},
|
||||
},
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
// Test recycler failure with some events
|
||||
name: "RecyclerFailure",
|
||||
createPod: newPod("podRecyclerFailure", v1.PodPending, ""),
|
||||
eventSequence: []watch.Event{
|
||||
// Pod gets Running and Succeeded
|
||||
newPodEvent(watch.Added, "podRecyclerFailure", v1.PodPending, ""),
|
||||
newEvent(v1.EventTypeNormal, "Successfully assigned recycler-for-podRecyclerFailure to 127.0.0.1"),
|
||||
newEvent(v1.EventTypeWarning, "Unable to mount volumes for pod \"recycler-for-podRecyclerFailure_default(3c9809e5-347c-11e6-a79b-3c970e965218)\": timeout expired waiting for volumes to attach/mount"),
|
||||
newEvent(v1.EventTypeWarning, "Error syncing pod, skipping: timeout expired waiting for volumes to attach/mount for pod \"default\"/\"recycler-for-podRecyclerFailure\". list of unattached/unmounted"),
|
||||
newPodEvent(watch.Modified, "podRecyclerFailure", v1.PodRunning, ""),
|
||||
newPodEvent(watch.Modified, "podRecyclerFailure", v1.PodFailed, "Pod was active on the node longer than specified deadline"),
|
||||
},
|
||||
expectedEvents: []mockEvent{
|
||||
{v1.EventTypeNormal, "Successfully assigned recycler-for-podRecyclerFailure to 127.0.0.1"},
|
||||
{v1.EventTypeWarning, "Unable to mount volumes for pod \"recycler-for-podRecyclerFailure_default(3c9809e5-347c-11e6-a79b-3c970e965218)\": timeout expired waiting for volumes to attach/mount"},
|
||||
{v1.EventTypeWarning, "Error syncing pod, skipping: timeout expired waiting for volumes to attach/mount for pod \"default\"/\"recycler-for-podRecyclerFailure\". list of unattached/unmounted"},
|
||||
},
|
||||
expectedError: "failed to recycle volume: Pod was active on the node longer than specified deadline",
|
||||
},
|
||||
{
|
||||
// Recycler pod gets deleted
|
||||
name: "RecyclerDeleted",
|
||||
createPod: newPod("podRecyclerDeleted", v1.PodPending, ""),
|
||||
eventSequence: []watch.Event{
|
||||
// Pod gets Running and Succeeded
|
||||
newPodEvent(watch.Added, "podRecyclerDeleted", v1.PodPending, ""),
|
||||
newEvent(v1.EventTypeNormal, "Successfully assigned recycler-for-podRecyclerDeleted to 127.0.0.1"),
|
||||
newPodEvent(watch.Deleted, "podRecyclerDeleted", v1.PodPending, ""),
|
||||
},
|
||||
expectedEvents: []mockEvent{
|
||||
{v1.EventTypeNormal, "Successfully assigned recycler-for-podRecyclerDeleted to 127.0.0.1"},
|
||||
},
|
||||
expectedError: "failed to recycle volume: recycler pod was deleted",
|
||||
},
|
||||
{
|
||||
// Another recycler pod is already running
|
||||
name: "RecyclerRunning",
|
||||
existingPod: newPod("podOldRecycler", v1.PodRunning, ""),
|
||||
createPod: newPod("podNewRecycler", v1.PodFailed, "mock message"),
|
||||
eventSequence: []watch.Event{},
|
||||
expectedError: "old recycler pod found, will retry later",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Logf("Test %q", test.name)
|
||||
client := &mockRecyclerClient{
|
||||
events: test.eventSequence,
|
||||
pod: test.existingPod,
|
||||
}
|
||||
err := internalRecycleVolumeByWatchingPodUntilCompletion(test.createPod.Name, test.createPod, client)
|
||||
receivedError := ""
|
||||
if err != nil {
|
||||
receivedError = err.Error()
|
||||
}
|
||||
if receivedError != test.expectedError {
|
||||
t.Errorf("Test %q failed, expected error %q, got %q", test.name, test.expectedError, receivedError)
|
||||
continue
|
||||
}
|
||||
if !client.deletedCalled {
|
||||
t.Errorf("Test %q failed, expected deferred client.Delete to be called on recycler pod", test.name)
|
||||
continue
|
||||
}
|
||||
for i, expectedEvent := range test.expectedEvents {
|
||||
if len(client.receivedEvents) <= i {
|
||||
t.Errorf("Test %q failed, expected event %d: %q not received", test.name, i, expectedEvent.message)
|
||||
continue
|
||||
}
|
||||
receivedEvent := client.receivedEvents[i]
|
||||
if expectedEvent.eventtype != receivedEvent.eventtype {
|
||||
t.Errorf("Test %q failed, event %d does not match: expected eventtype %q, got %q", test.name, i, expectedEvent.eventtype, receivedEvent.eventtype)
|
||||
}
|
||||
if expectedEvent.message != receivedEvent.message {
|
||||
t.Errorf("Test %q failed, event %d does not match: expected message %q, got %q", test.name, i, expectedEvent.message, receivedEvent.message)
|
||||
}
|
||||
}
|
||||
for i := len(test.expectedEvents); i < len(client.receivedEvents); i++ {
|
||||
t.Errorf("Test %q failed, unexpected event received: %s, %q", test.name, client.receivedEvents[i].eventtype, client.receivedEvents[i].message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type mockRecyclerClient struct {
|
||||
pod *v1.Pod
|
||||
deletedCalled bool
|
||||
receivedEvents []mockEvent
|
||||
events []watch.Event
|
||||
}
|
||||
|
||||
type mockEvent struct {
|
||||
eventtype, message string
|
||||
}
|
||||
|
||||
func (c *mockRecyclerClient) CreatePod(pod *v1.Pod) (*v1.Pod, error) {
|
||||
if c.pod == nil {
|
||||
c.pod = pod
|
||||
return c.pod, nil
|
||||
}
|
||||
// Simulate "already exists" error
|
||||
return nil, errors.NewAlreadyExists(api.Resource("pods"), pod.Name)
|
||||
}
|
||||
|
||||
func (c *mockRecyclerClient) GetPod(name, namespace string) (*v1.Pod, error) {
|
||||
if c.pod != nil {
|
||||
return c.pod, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("pod does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *mockRecyclerClient) DeletePod(name, namespace string) error {
|
||||
c.deletedCalled = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *mockRecyclerClient) WatchPod(name, namespace string, stopChannel chan struct{}) (<-chan watch.Event, error) {
|
||||
eventCh := make(chan watch.Event, 0)
|
||||
go func() {
|
||||
for _, e := range c.events {
|
||||
eventCh <- e
|
||||
}
|
||||
}()
|
||||
return eventCh, nil
|
||||
}
|
||||
|
||||
func (c *mockRecyclerClient) Event(eventtype, message string) {
|
||||
c.receivedEvents = append(c.receivedEvents, mockEvent{eventtype, message})
|
||||
}
|
||||
167
vendor/k8s.io/kubernetes/pkg/volume/util/resize_util_test.go
generated
vendored
167
vendor/k8s.io/kubernetes/pkg/volume/util/resize_util_test.go
generated
vendored
|
|
@ -1,167 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 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 util
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type conditionMergeTestCase struct {
|
||||
description string
|
||||
pvc *v1.PersistentVolumeClaim
|
||||
newConditions []v1.PersistentVolumeClaimCondition
|
||||
finalCondtions []v1.PersistentVolumeClaimCondition
|
||||
}
|
||||
|
||||
func TestMergeResizeCondition(t *testing.T) {
|
||||
currentTime := metav1.Now()
|
||||
|
||||
pvc := getPVC([]v1.PersistentVolumeClaimCondition{
|
||||
{
|
||||
Type: v1.PersistentVolumeClaimResizing,
|
||||
Status: v1.ConditionTrue,
|
||||
LastTransitionTime: currentTime,
|
||||
},
|
||||
})
|
||||
|
||||
noConditionPVC := getPVC([]v1.PersistentVolumeClaimCondition{})
|
||||
|
||||
conditionFalseTime := metav1.Now()
|
||||
newTime := metav1.NewTime(time.Now().Add(1 * time.Hour))
|
||||
|
||||
testCases := []conditionMergeTestCase{
|
||||
{
|
||||
description: "when removing all conditions",
|
||||
pvc: pvc.DeepCopy(),
|
||||
newConditions: []v1.PersistentVolumeClaimCondition{},
|
||||
finalCondtions: []v1.PersistentVolumeClaimCondition{},
|
||||
},
|
||||
{
|
||||
description: "adding new condition",
|
||||
pvc: pvc.DeepCopy(),
|
||||
newConditions: []v1.PersistentVolumeClaimCondition{
|
||||
{
|
||||
Type: v1.PersistentVolumeClaimFileSystemResizePending,
|
||||
Status: v1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
finalCondtions: []v1.PersistentVolumeClaimCondition{
|
||||
{
|
||||
Type: v1.PersistentVolumeClaimFileSystemResizePending,
|
||||
Status: v1.ConditionTrue,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "adding same condition with new timestamp",
|
||||
pvc: pvc.DeepCopy(),
|
||||
newConditions: []v1.PersistentVolumeClaimCondition{
|
||||
{
|
||||
Type: v1.PersistentVolumeClaimResizing,
|
||||
Status: v1.ConditionTrue,
|
||||
LastTransitionTime: newTime,
|
||||
},
|
||||
},
|
||||
finalCondtions: []v1.PersistentVolumeClaimCondition{
|
||||
{
|
||||
Type: v1.PersistentVolumeClaimResizing,
|
||||
Status: v1.ConditionTrue,
|
||||
LastTransitionTime: currentTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "adding same condition but with different status",
|
||||
pvc: pvc.DeepCopy(),
|
||||
newConditions: []v1.PersistentVolumeClaimCondition{
|
||||
{
|
||||
Type: v1.PersistentVolumeClaimResizing,
|
||||
Status: v1.ConditionFalse,
|
||||
LastTransitionTime: conditionFalseTime,
|
||||
},
|
||||
},
|
||||
finalCondtions: []v1.PersistentVolumeClaimCondition{
|
||||
{
|
||||
Type: v1.PersistentVolumeClaimResizing,
|
||||
Status: v1.ConditionFalse,
|
||||
LastTransitionTime: conditionFalseTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "when no condition exists on pvc",
|
||||
pvc: noConditionPVC.DeepCopy(),
|
||||
newConditions: []v1.PersistentVolumeClaimCondition{
|
||||
{
|
||||
Type: v1.PersistentVolumeClaimResizing,
|
||||
Status: v1.ConditionTrue,
|
||||
LastTransitionTime: currentTime,
|
||||
},
|
||||
},
|
||||
finalCondtions: []v1.PersistentVolumeClaimCondition{
|
||||
{
|
||||
Type: v1.PersistentVolumeClaimResizing,
|
||||
Status: v1.ConditionTrue,
|
||||
LastTransitionTime: currentTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, testcase := range testCases {
|
||||
updatePVC := MergeResizeConditionOnPVC(testcase.pvc, testcase.newConditions)
|
||||
|
||||
updateConditions := updatePVC.Status.Conditions
|
||||
if !reflect.DeepEqual(updateConditions, testcase.finalCondtions) {
|
||||
t.Errorf("Expected updated conditions for test %s to be %v but got %v",
|
||||
testcase.description,
|
||||
testcase.finalCondtions, updateConditions)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getPVC(conditions []v1.PersistentVolumeClaimCondition) *v1.PersistentVolumeClaim {
|
||||
pvc := &v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "resize"},
|
||||
Spec: v1.PersistentVolumeClaimSpec{
|
||||
AccessModes: []v1.PersistentVolumeAccessMode{
|
||||
v1.ReadWriteOnce,
|
||||
v1.ReadOnlyMany,
|
||||
},
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceName(v1.ResourceStorage): resource.MustParse("2Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v1.PersistentVolumeClaimStatus{
|
||||
Phase: v1.ClaimBound,
|
||||
Conditions: conditions,
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourceStorage: resource.MustParse("2Gi"),
|
||||
},
|
||||
},
|
||||
}
|
||||
return pvc
|
||||
}
|
||||
31
vendor/k8s.io/kubernetes/pkg/volume/util/util.go
generated
vendored
31
vendor/k8s.io/kubernetes/pkg/volume/util/util.go
generated
vendored
|
|
@ -21,6 +21,7 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
|
|
@ -289,10 +290,12 @@ func checkAlphaNodeAffinity(pv *v1.PersistentVolume, nodeLabels map[string]strin
|
|||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse MatchExpressions: %v", err)
|
||||
}
|
||||
if !selector.Matches(labels.Set(nodeLabels)) {
|
||||
return fmt.Errorf("NodeSelectorTerm %+v does not match node labels", term.MatchExpressions)
|
||||
if selector.Matches(labels.Set(nodeLabels)) {
|
||||
// Terms are ORed, so only one needs to match
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("No matching NodeSelectorTerms")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -310,10 +313,12 @@ func checkVolumeNodeAffinity(pv *v1.PersistentVolume, nodeLabels map[string]stri
|
|||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse MatchExpressions: %v", err)
|
||||
}
|
||||
if !selector.Matches(labels.Set(nodeLabels)) {
|
||||
return fmt.Errorf("NodeSelectorTerm %+v does not match node labels", term.MatchExpressions)
|
||||
if selector.Matches(labels.Set(nodeLabels)) {
|
||||
// Terms are ORed, so only one needs to match
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("No matching NodeSelectorTerms")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -749,3 +754,21 @@ func CheckVolumeModeFilesystem(volumeSpec *volume.Spec) (bool, error) {
|
|||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// MakeAbsolutePath convert path to absolute path according to GOOS
|
||||
func MakeAbsolutePath(goos, path string) string {
|
||||
if goos != "windows" {
|
||||
return filepath.Clean("/" + path)
|
||||
}
|
||||
// These are all for windows
|
||||
// If there is a colon, give up.
|
||||
if strings.Contains(path, ":") {
|
||||
return path
|
||||
}
|
||||
// If there is a slash, but no drive, add 'c:'
|
||||
if strings.HasPrefix(path, "/") || strings.HasPrefix(path, "\\") {
|
||||
return "c:" + path
|
||||
}
|
||||
// Otherwise, add 'c:\'
|
||||
return "c:\\" + path
|
||||
}
|
||||
|
|
|
|||
1091
vendor/k8s.io/kubernetes/pkg/volume/util/util_test.go
generated
vendored
1091
vendor/k8s.io/kubernetes/pkg/volume/util/util_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
284
vendor/k8s.io/kubernetes/third_party/forked/golang/expansion/expand_test.go
generated
vendored
284
vendor/k8s.io/kubernetes/third_party/forked/golang/expansion/expand_test.go
generated
vendored
|
|
@ -1,284 +0,0 @@
|
|||
package expansion
|
||||
|
||||
import (
|
||||
"testing"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
func TestMapReference(t *testing.T) {
|
||||
envs := []api.EnvVar{
|
||||
{
|
||||
Name: "FOO",
|
||||
Value: "bar",
|
||||
},
|
||||
{
|
||||
Name: "ZOO",
|
||||
Value: "$(FOO)-1",
|
||||
},
|
||||
{
|
||||
Name: "BLU",
|
||||
Value: "$(ZOO)-2",
|
||||
},
|
||||
}
|
||||
|
||||
declaredEnv := map[string]string{
|
||||
"FOO": "bar",
|
||||
"ZOO": "$(FOO)-1",
|
||||
"BLU": "$(ZOO)-2",
|
||||
}
|
||||
|
||||
serviceEnv := map[string]string{}
|
||||
|
||||
mapping := MappingFuncFor(declaredEnv, serviceEnv)
|
||||
|
||||
for _, env := range envs {
|
||||
declaredEnv[env.Name] = Expand(env.Value, mapping)
|
||||
}
|
||||
|
||||
expectedEnv := map[string]string{
|
||||
"FOO": "bar",
|
||||
"ZOO": "bar-1",
|
||||
"BLU": "bar-1-2",
|
||||
}
|
||||
|
||||
for k, v := range expectedEnv {
|
||||
if e, a := v, declaredEnv[k]; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
} else {
|
||||
delete(declaredEnv, k)
|
||||
}
|
||||
}
|
||||
|
||||
if len(declaredEnv) != 0 {
|
||||
t.Errorf("Unexpected keys in declared env: %v", declaredEnv)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapping(t *testing.T) {
|
||||
context := map[string]string{
|
||||
"VAR_A": "A",
|
||||
"VAR_B": "B",
|
||||
"VAR_C": "C",
|
||||
"VAR_REF": "$(VAR_A)",
|
||||
"VAR_EMPTY": "",
|
||||
}
|
||||
mapping := MappingFuncFor(context)
|
||||
|
||||
doExpansionTest(t, mapping)
|
||||
}
|
||||
|
||||
func TestMappingDual(t *testing.T) {
|
||||
context := map[string]string{
|
||||
"VAR_A": "A",
|
||||
"VAR_EMPTY": "",
|
||||
}
|
||||
context2 := map[string]string{
|
||||
"VAR_B": "B",
|
||||
"VAR_C": "C",
|
||||
"VAR_REF": "$(VAR_A)",
|
||||
}
|
||||
mapping := MappingFuncFor(context, context2)
|
||||
|
||||
doExpansionTest(t, mapping)
|
||||
}
|
||||
|
||||
func doExpansionTest(t *testing.T, mapping func(string) string) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "whole string",
|
||||
input: "$(VAR_A)",
|
||||
expected: "A",
|
||||
},
|
||||
{
|
||||
name: "repeat",
|
||||
input: "$(VAR_A)-$(VAR_A)",
|
||||
expected: "A-A",
|
||||
},
|
||||
{
|
||||
name: "beginning",
|
||||
input: "$(VAR_A)-1",
|
||||
expected: "A-1",
|
||||
},
|
||||
{
|
||||
name: "middle",
|
||||
input: "___$(VAR_B)___",
|
||||
expected: "___B___",
|
||||
},
|
||||
{
|
||||
name: "end",
|
||||
input: "___$(VAR_C)",
|
||||
expected: "___C",
|
||||
},
|
||||
{
|
||||
name: "compound",
|
||||
input: "$(VAR_A)_$(VAR_B)_$(VAR_C)",
|
||||
expected: "A_B_C",
|
||||
},
|
||||
{
|
||||
name: "escape & expand",
|
||||
input: "$$(VAR_B)_$(VAR_A)",
|
||||
expected: "$(VAR_B)_A",
|
||||
},
|
||||
{
|
||||
name: "compound escape",
|
||||
input: "$$(VAR_A)_$$(VAR_B)",
|
||||
expected: "$(VAR_A)_$(VAR_B)",
|
||||
},
|
||||
{
|
||||
name: "mixed in escapes",
|
||||
input: "f000-$$VAR_A",
|
||||
expected: "f000-$VAR_A",
|
||||
},
|
||||
{
|
||||
name: "backslash escape ignored",
|
||||
input: "foo\\$(VAR_C)bar",
|
||||
expected: "foo\\Cbar",
|
||||
},
|
||||
{
|
||||
name: "backslash escape ignored",
|
||||
input: "foo\\\\$(VAR_C)bar",
|
||||
expected: "foo\\\\Cbar",
|
||||
},
|
||||
{
|
||||
name: "lots of backslashes",
|
||||
input: "foo\\\\\\\\$(VAR_A)bar",
|
||||
expected: "foo\\\\\\\\Abar",
|
||||
},
|
||||
{
|
||||
name: "nested var references",
|
||||
input: "$(VAR_A$(VAR_B))",
|
||||
expected: "$(VAR_A$(VAR_B))",
|
||||
},
|
||||
{
|
||||
name: "nested var references second type",
|
||||
input: "$(VAR_A$(VAR_B)",
|
||||
expected: "$(VAR_A$(VAR_B)",
|
||||
},
|
||||
{
|
||||
name: "value is a reference",
|
||||
input: "$(VAR_REF)",
|
||||
expected: "$(VAR_A)",
|
||||
},
|
||||
{
|
||||
name: "value is a reference x 2",
|
||||
input: "%%$(VAR_REF)--$(VAR_REF)%%",
|
||||
expected: "%%$(VAR_A)--$(VAR_A)%%",
|
||||
},
|
||||
{
|
||||
name: "empty var",
|
||||
input: "foo$(VAR_EMPTY)bar",
|
||||
expected: "foobar",
|
||||
},
|
||||
{
|
||||
name: "unterminated expression",
|
||||
input: "foo$(VAR_Awhoops!",
|
||||
expected: "foo$(VAR_Awhoops!",
|
||||
},
|
||||
{
|
||||
name: "expression without operator",
|
||||
input: "f00__(VAR_A)__",
|
||||
expected: "f00__(VAR_A)__",
|
||||
},
|
||||
{
|
||||
name: "shell special vars pass through",
|
||||
input: "$?_boo_$!",
|
||||
expected: "$?_boo_$!",
|
||||
},
|
||||
{
|
||||
name: "bare operators are ignored",
|
||||
input: "$VAR_A",
|
||||
expected: "$VAR_A",
|
||||
},
|
||||
{
|
||||
name: "undefined vars are passed through",
|
||||
input: "$(VAR_DNE)",
|
||||
expected: "$(VAR_DNE)",
|
||||
},
|
||||
{
|
||||
name: "multiple (even) operators, var undefined",
|
||||
input: "$$$$$$(BIG_MONEY)",
|
||||
expected: "$$$(BIG_MONEY)",
|
||||
},
|
||||
{
|
||||
name: "multiple (even) operators, var defined",
|
||||
input: "$$$$$$(VAR_A)",
|
||||
expected: "$$$(VAR_A)",
|
||||
},
|
||||
{
|
||||
name: "multiple (odd) operators, var undefined",
|
||||
input: "$$$$$$$(GOOD_ODDS)",
|
||||
expected: "$$$$(GOOD_ODDS)",
|
||||
},
|
||||
{
|
||||
name: "multiple (odd) operators, var defined",
|
||||
input: "$$$$$$$(VAR_A)",
|
||||
expected: "$$$A",
|
||||
},
|
||||
{
|
||||
name: "missing open expression",
|
||||
input: "$VAR_A)",
|
||||
expected: "$VAR_A)",
|
||||
},
|
||||
{
|
||||
name: "shell syntax ignored",
|
||||
input: "${VAR_A}",
|
||||
expected: "${VAR_A}",
|
||||
},
|
||||
{
|
||||
name: "trailing incomplete expression not consumed",
|
||||
input: "$(VAR_B)_______$(A",
|
||||
expected: "B_______$(A",
|
||||
},
|
||||
{
|
||||
name: "trailing incomplete expression, no content, is not consumed",
|
||||
input: "$(VAR_C)_______$(",
|
||||
expected: "C_______$(",
|
||||
},
|
||||
{
|
||||
name: "operator at end of input string is preserved",
|
||||
input: "$(VAR_A)foobarzab$",
|
||||
expected: "Afoobarzab$",
|
||||
},
|
||||
{
|
||||
name: "shell escaped incomplete expr",
|
||||
input: "foo-\\$(VAR_A",
|
||||
expected: "foo-\\$(VAR_A",
|
||||
},
|
||||
{
|
||||
name: "lots of $( in middle",
|
||||
input: "--$($($($($--",
|
||||
expected: "--$($($($($--",
|
||||
},
|
||||
{
|
||||
name: "lots of $( in beginning",
|
||||
input: "$($($($($--foo$(",
|
||||
expected: "$($($($($--foo$(",
|
||||
},
|
||||
{
|
||||
name: "lots of $( at end",
|
||||
input: "foo0--$($($($(",
|
||||
expected: "foo0--$($($($(",
|
||||
},
|
||||
{
|
||||
name: "escaped operators in variable names are not escaped",
|
||||
input: "$(foo$$var)",
|
||||
expected: "$(foo$$var)",
|
||||
},
|
||||
{
|
||||
name: "newline not expanded",
|
||||
input: "\n",
|
||||
expected: "\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
expanded := Expand(tc.input, mapping)
|
||||
if e, a := tc.expected, expanded; e != a {
|
||||
t.Errorf("%v: expected %q, got %q", tc.name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue