Introducing ReplicaSet, a new incarnation of Replication Controller.
This commit adds the API types, conversion, validation and defaulting code.
This commit is contained in:
@@ -1458,12 +1458,6 @@ func TestValidateScale(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func newInt(val int) *int {
|
||||
p := new(int)
|
||||
*p = val
|
||||
return p
|
||||
}
|
||||
|
||||
func TestValidateConfigMap(t *testing.T) {
|
||||
newConfigMap := func(name, namespace string, data map[string]string) extensions.ConfigMap {
|
||||
return extensions.ConfigMap{
|
||||
@@ -1565,3 +1559,474 @@ func TestValidateConfigMapUpdate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateReplicaSetStatusUpdate(t *testing.T) {
|
||||
validLabels := map[string]string{"a": "b"}
|
||||
validPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
type rcUpdateTest struct {
|
||||
old extensions.ReplicaSet
|
||||
update extensions.ReplicaSet
|
||||
}
|
||||
successCases := []rcUpdateTest{
|
||||
{
|
||||
old: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
Status: extensions.ReplicaSetStatus{
|
||||
Replicas: 2,
|
||||
},
|
||||
},
|
||||
update: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: 3,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
Status: extensions.ReplicaSetStatus{
|
||||
Replicas: 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
successCase.old.ObjectMeta.ResourceVersion = "1"
|
||||
successCase.update.ObjectMeta.ResourceVersion = "1"
|
||||
if errs := ValidateReplicaSetStatusUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
errorCases := map[string]rcUpdateTest{
|
||||
"negative replicas": {
|
||||
old: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
Status: extensions.ReplicaSetStatus{
|
||||
Replicas: 3,
|
||||
},
|
||||
},
|
||||
update: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: 2,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
Status: extensions.ReplicaSetStatus{
|
||||
Replicas: -3,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for testName, errorCase := range errorCases {
|
||||
if errs := ValidateReplicaSetStatusUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
|
||||
t.Errorf("expected failure: %s", testName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestValidateReplicaSetUpdate(t *testing.T) {
|
||||
validLabels := map[string]string{"a": "b"}
|
||||
validPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
readWriteVolumePodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||
invalidPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: invalidLabels,
|
||||
},
|
||||
},
|
||||
}
|
||||
type rcUpdateTest struct {
|
||||
old extensions.ReplicaSet
|
||||
update extensions.ReplicaSet
|
||||
}
|
||||
successCases := []rcUpdateTest{
|
||||
{
|
||||
old: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
update: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: 3,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
old: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
update: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: 1,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &readWriteVolumePodTemplate.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
successCase.old.ObjectMeta.ResourceVersion = "1"
|
||||
successCase.update.ObjectMeta.ResourceVersion = "1"
|
||||
if errs := ValidateReplicaSetUpdate(&successCase.update, &successCase.old); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
errorCases := map[string]rcUpdateTest{
|
||||
"more than one read/write": {
|
||||
old: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
update: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: 2,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &readWriteVolumePodTemplate.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid selector": {
|
||||
old: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
update: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: 2,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: invalidLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid pod": {
|
||||
old: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
update: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: 2,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &invalidPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
"negative replicas": {
|
||||
old: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
update: extensions.ReplicaSet{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: -1,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for testName, errorCase := range errorCases {
|
||||
if errs := ValidateReplicaSetUpdate(&errorCase.update, &errorCase.old); len(errs) == 0 {
|
||||
t.Errorf("expected failure: %s", testName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateReplicaSet(t *testing.T) {
|
||||
validLabels := map[string]string{"a": "b"}
|
||||
validPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
readWriteVolumePodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validLabels,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Volumes: []api.Volume{{Name: "gcepd", VolumeSource: api.VolumeSource{GCEPersistentDisk: &api.GCEPersistentDiskVolumeSource{PDName: "my-PD", FSType: "ext4", Partition: 1, ReadOnly: false}}}},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||
invalidPodTemplate := api.PodTemplate{
|
||||
Template: api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: invalidLabels,
|
||||
},
|
||||
},
|
||||
}
|
||||
successCases := []extensions.ReplicaSet{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc-123", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: 1,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &readWriteVolumePodTemplate.Template,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, successCase := range successCases {
|
||||
if errs := ValidateReplicaSet(&successCase); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
errorCases := map[string]extensions.ReplicaSet{
|
||||
"zero-length ID": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"missing-namespace": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc-123"},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"empty selector": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"selector_doesnt_match": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid manifest": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
},
|
||||
},
|
||||
"read-write persistent disk with > 1 pod": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc"},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: 2,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &readWriteVolumePodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"negative_replicas": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: api.NamespaceDefault},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Replicas: -1,
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
},
|
||||
},
|
||||
"invalid_label": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Labels: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid_label 2": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Labels: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Template: &invalidPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid_annotation": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
Annotations: map[string]string{
|
||||
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
|
||||
},
|
||||
},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &validPodTemplate.Template,
|
||||
},
|
||||
},
|
||||
"invalid restart policy 1": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyOnFailure,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validLabels,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"invalid restart policy 2": {
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "abc-123",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: extensions.ReplicaSetSpec{
|
||||
Selector: &extensions.LabelSelector{MatchLabels: validLabels},
|
||||
Template: &api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyNever,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validLabels,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
errs := ValidateReplicaSet(&v)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected failure for %s", k)
|
||||
}
|
||||
for i := range errs {
|
||||
field := errs[i].Field
|
||||
if !strings.HasPrefix(field, "spec.template.") &&
|
||||
field != "metadata.name" &&
|
||||
field != "metadata.namespace" &&
|
||||
field != "spec.selector" &&
|
||||
field != "spec.template" &&
|
||||
field != "GCEPersistentDisk.ReadOnly" &&
|
||||
field != "spec.replicas" &&
|
||||
field != "spec.template.labels" &&
|
||||
field != "metadata.annotations" &&
|
||||
field != "metadata.labels" &&
|
||||
field != "status.replicas" {
|
||||
t.Errorf("%s: missing prefix for: %v", k, errs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newInt(val int) *int {
|
||||
p := new(int)
|
||||
*p = val
|
||||
return p
|
||||
}
|
||||
|
Reference in New Issue
Block a user