implement taints and tolerations

This commit is contained in:
Kevin
2016-03-31 11:42:57 +08:00
parent 952e8302fb
commit 52fb89ff73
34 changed files with 4623 additions and 69 deletions

View File

@@ -1954,60 +1954,6 @@ func TestValidatePod(t *testing.T) {
NodeName: "foobar",
},
},
}
for _, pod := range successCases {
if errs := ValidatePod(&pod); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
errorCases := map[string]api.Pod{
"bad name": {
ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
"bad namespace": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
"bad spec": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"},
Spec: api.PodSpec{
Containers: []api.Container{{}},
},
},
"bad label": {
ObjectMeta: api.ObjectMeta{
Name: "abc",
Namespace: "ns",
Labels: map[string]string{
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
}
for k, v := range errorCases {
if errs := ValidatePod(&v); len(errs) == 0 {
t.Errorf("expected failure for %q", k)
}
}
}
func TestValidateAffinity(t *testing.T) {
successCases := []api.Pod{
{ // Serialized affinity requirements in annotations.
ObjectMeta: api.ObjectMeta{
Name: "123",
@@ -2160,6 +2106,83 @@ func TestValidateAffinity(t *testing.T) {
DNSPolicy: api.DNSClusterFirst,
},
},
{ // populate tolerations equal operator in annotations.
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "foo",
"operator": "Equal",
"value": "bar",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
{ // populate tolerations exists operator in annotations.
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "foo",
"operator": "Exists",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
{ // empty operator is ok for toleration
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "foo",
"value": "bar",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
{ // empty efffect is ok for toleration
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "foo",
"operator": "Equal",
"value": "bar"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
}
for _, pod := range successCases {
if errs := ValidatePod(&pod); len(errs) != 0 {
@@ -2168,6 +2191,42 @@ func TestValidateAffinity(t *testing.T) {
}
errorCases := map[string]api.Pod{
"bad name": {
ObjectMeta: api.ObjectMeta{Name: "", Namespace: "ns"},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
"bad namespace": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: ""},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
"bad spec": {
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"},
Spec: api.PodSpec{
Containers: []api.Container{{}},
},
},
"bad label": {
ObjectMeta: api.ObjectMeta{
Name: "abc",
Namespace: "ns",
Labels: map[string]string{
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
},
},
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
},
},
"invalid json of node affinity in pod annotations": {
ObjectMeta: api.ObjectMeta{
Name: "123",
@@ -2476,6 +2535,66 @@ func TestValidateAffinity(t *testing.T) {
DNSPolicy: api.DNSClusterFirst,
},
},
"invalid toleration key": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "nospecialchars^=@",
"operator": "Equal",
"value": "bar",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
"invalid toleration operator": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "foo",
"operator": "In",
"value": "bar",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
"value must be empty when `operator` is 'Exists'": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.TolerationsAnnotationKey: `
[{
"key": "foo",
"operator": "Exists",
"value": "bar",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
},
},
}
for k, v := range errorCases {
if errs := ValidatePod(&v); len(errs) == 0 {
@@ -3885,6 +4004,32 @@ func TestValidateNode(t *testing.T) {
ExternalID: "external",
},
},
{
ObjectMeta: api.ObjectMeta{
Name: "dedicated-node1",
// Add a valid taint to a node
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "GPU",
"value": "true",
"effect": "NoSchedule"
}]`,
},
},
Status: api.NodeStatus{
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "something"},
},
Capacity: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
api.ResourceName(api.ResourceMemory): resource.MustParse("0"),
},
},
Spec: api.NodeSpec{
ExternalID: "external",
},
},
}
for _, successCase := range successCases {
if errs := ValidateNode(&successCase); len(errs) != 0 {
@@ -3936,6 +4081,119 @@ func TestValidateNode(t *testing.T) {
},
},
},
"missing-taint-key": {
ObjectMeta: api.ObjectMeta{
Name: "dedicated-node1",
// Add a taint with an empty key to a node
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "",
"value": "special-user-1",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.NodeSpec{
ExternalID: "external",
},
},
"bad-taint-key": {
ObjectMeta: api.ObjectMeta{
Name: "dedicated-node1",
// Add a taint with an empty key to a node
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "NoUppercaseOrSpecialCharsLike=Equals",
"value": "special-user-1",
"effect": "NoSchedule"
}]`,
},
},
Spec: api.NodeSpec{
ExternalID: "external",
},
},
"bad-taint-value": {
ObjectMeta: api.ObjectMeta{
Name: "dedicated-node2",
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "dedicated",
"value": "some\\bad\\value",
"effect": "NoSchedule"
}]`,
},
},
Status: api.NodeStatus{
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "something"},
},
Capacity: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
api.ResourceName(api.ResourceMemory): resource.MustParse("0"),
},
},
// Add a taint with an empty value to a node
Spec: api.NodeSpec{
ExternalID: "external",
},
},
"missing-taint-effect": {
ObjectMeta: api.ObjectMeta{
Name: "dedicated-node3",
// Add a taint with an empty effect to a node
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "dedicated",
"value": "special-user-3",
"effect": ""
}]`,
},
},
Status: api.NodeStatus{
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "something"},
},
Capacity: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
api.ResourceName(api.ResourceMemory): resource.MustParse("0"),
},
},
Spec: api.NodeSpec{
ExternalID: "external",
},
},
"invalide-taint-effect": {
ObjectMeta: api.ObjectMeta{
Name: "dedicated-node3",
// Add a taint with an empty effect to a node
Annotations: map[string]string{
api.TaintsAnnotationKey: `
[{
"key": "dedicated",
"value": "special-user-3",
"effect": "NoExecute"
}]`,
},
},
Status: api.NodeStatus{
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "something"},
},
Capacity: api.ResourceList{
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
api.ResourceName(api.ResourceMemory): resource.MustParse("0"),
},
},
Spec: api.NodeSpec{
ExternalID: "external",
},
},
}
for k, v := range errorCases {
errs := ValidateNode(&v)
@@ -3945,14 +4203,19 @@ func TestValidateNode(t *testing.T) {
for i := range errs {
field := errs[i].Field
expectedFields := map[string]bool{
"metadata.name": true,
"metadata.labels": true,
"metadata.annotations": true,
"metadata.namespace": true,
"spec.externalID": true,
"metadata.name": true,
"metadata.labels": true,
"metadata.annotations": true,
"metadata.namespace": true,
"spec.externalID": true,
"metadata.annotations.scheduler.alpha.kubernetes.io/taints[0].key": true,
"metadata.annotations.scheduler.alpha.kubernetes.io/taints[0].value": true,
"metadata.annotations.scheduler.alpha.kubernetes.io/taints[0].effect": true,
}
if expectedFields[field] == false {
t.Errorf("%s: missing prefix for: %v", k, errs[i])
if val, ok := expectedFields[field]; ok {
if !val {
t.Errorf("%s: missing prefix for: %v", k, errs[i])
}
}
}
}