Skip to content

Commit

Permalink
Added field CollisionCount to StatefulSetStatus
Browse files Browse the repository at this point in the history
This is the first step towards fixing kubernetes#49909. The field will be used for collision avoidance when the StatefulSet controller creates name for the newest ControllerRevision, which will be done in a subsequent PR.
  • Loading branch information
liyinan926 committed Aug 1, 2017
1 parent 5ce3b35 commit 6e4b647
Show file tree
Hide file tree
Showing 9 changed files with 483 additions and 248 deletions.
6 changes: 6 additions & 0 deletions pkg/apis/apps/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ type StatefulSetStatus struct {
// updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence
// [replicas-updatedReplicas,replicas)
UpdateRevision string

// Count of hash collisions for the StatefulSet. The StatefulSet controller
// uses this field as a collision avoidance mechanism when it needs to
// create the name for the newest ControllerRevision.
// +optional
CollisionCount *int64
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down
30 changes: 30 additions & 0 deletions pkg/apis/apps/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,39 @@ func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet) fi
return allErrs
}

// ValidateStatefulSetStatus validates a StatefulSetStatus.
func ValidateStatefulSetStatus(status *apps.StatefulSetStatus, fieldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}

allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.Replicas), fieldPath.Child("replicas"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fieldPath.Child("readyReplicas"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentReplicas), fieldPath.Child("currentReplicas"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedReplicas), fieldPath.Child("updatedReplicas"))...)
if status.ObservedGeneration != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.ObservedGeneration), fieldPath.Child("observedGeneration"))...)
}
if status.CollisionCount != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.CollisionCount), fieldPath.Child("collisionCount"))...)
}

msg := "cannot be greater than status.replicas"
if status.ReadyReplicas > status.Replicas {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("readyReplicas"), status.ReadyReplicas, msg))
}
if status.CurrentReplicas > status.Replicas {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("currentReplicas"), status.CurrentReplicas, msg))
}
if status.UpdatedReplicas > status.Replicas {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("updatedReplicas"), status.UpdatedReplicas, msg))
}

return allErrs
}

// ValidateStatefulSetStatusUpdate tests if required fields in the StatefulSet are set.
func ValidateStatefulSetStatusUpdate(statefulSet, oldStatefulSet *apps.StatefulSet) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateStatefulSetStatus(&statefulSet.Status, field.NewPath("status"))...)
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata"))...)
// TODO: Validate status.
return allErrs
Expand Down
117 changes: 117 additions & 0 deletions pkg/apis/apps/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ import (
"strings"
"testing"

"github.com/davecgh/go-spew/spew"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apis/apps"
)
Expand Down Expand Up @@ -300,6 +303,120 @@ func TestValidateStatefulSet(t *testing.T) {
}
}

func TestValidateStatefulSetStatus(t *testing.T) {
minusOne := int64(-1)
tests := []struct {
name string
replicas int32
readyReplicas int32
currentReplicas int32
updatedReplicas int32
observedGeneration *int64
collisionCount *int64
expectedErr bool
}{
{
name: "valid status",
replicas: 3,
readyReplicas: 3,
currentReplicas: 2,
updatedReplicas: 1,
expectedErr: false,
},
{
name: "invalid replicas",
replicas: -1,
readyReplicas: 3,
currentReplicas: 2,
updatedReplicas: 1,
expectedErr: true,
},
{
name: "invalid readyReplicas",
replicas: 3,
readyReplicas: -1,
currentReplicas: 2,
updatedReplicas: 1,
expectedErr: true,
},
{
name: "invalid currentReplicas",
replicas: 3,
readyReplicas: 3,
currentReplicas: -1,
updatedReplicas: 1,
expectedErr: true,
},
{
name: "invalid updatedReplicas",
replicas: 3,
readyReplicas: 3,
currentReplicas: 2,
updatedReplicas: -1,
expectedErr: true,
},
{
name: "invalid observedGeneration",
replicas: 3,
readyReplicas: 3,
currentReplicas: 2,
updatedReplicas: 1,
observedGeneration: &minusOne,
expectedErr: true,
},
{
name: "invalid collisionCount",
replicas: 3,
readyReplicas: 3,
currentReplicas: 2,
updatedReplicas: 1,
collisionCount: &minusOne,
expectedErr: true,
},
{
name: "readyReplicas greater than replicas",
replicas: 3,
readyReplicas: 4,
currentReplicas: 2,
updatedReplicas: 1,
expectedErr: true,
},
{
name: "currentReplicas greater than replicas",
replicas: 3,
readyReplicas: 3,
currentReplicas: 4,
updatedReplicas: 1,
expectedErr: true,
},
{
name: "updatedReplicas greater than replicas",
replicas: 3,
readyReplicas: 3,
currentReplicas: 2,
updatedReplicas: 4,
expectedErr: true,
},
}

for _, test := range tests {
status := apps.StatefulSetStatus{
Replicas: test.replicas,
ReadyReplicas: test.readyReplicas,
CurrentReplicas: test.currentReplicas,
UpdatedReplicas: test.updatedReplicas,
ObservedGeneration: test.observedGeneration,
CollisionCount: test.collisionCount,
}

errs := ValidateStatefulSetStatus(&status, field.NewPath("status"))
if hasErr := len(errs) > 0; hasErr != test.expectedErr {
errString := spew.Sprintf("%#v", errs)
t.Errorf("%s: expected error: %t, got error: %t\nerrors: %s", test.name, test.expectedErr, hasErr, errString)
}
}
}

func TestValidateStatefulSetUpdate(t *testing.T) {
validLabels := map[string]string{"a": "b"}
validPodTemplate := api.PodTemplate{
Expand Down
Loading

0 comments on commit 6e4b647

Please sign in to comment.