Add collisionCount api field in DeploymentStatus

Signed-off-by: Michail Kargakis <mkargaki@redhat.com>
This commit is contained in:
Michail Kargakis 2017-04-21 16:29:17 +02:00
parent 4def5add11
commit 4aa8b1a66a
No known key found for this signature in database
GPG Key ID: EA0DEBF5DFA276E0
5 changed files with 130 additions and 3 deletions

View File

@ -356,6 +356,12 @@ type DeploymentStatus struct {
// +patchMergeKey=type
// +patchStrategy=merge
Conditions []DeploymentCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,6,rep,name=conditions"`
// Count of hash collisions for the Deployment. The Deployment controller uses this
// field as a collision avoidance mechanism when it needs to create the name for the
// newest ReplicaSet.
// +optional
CollisionCount *int64 `json:"collisionCount,omitempty"`
}
type DeploymentConditionType string

View File

@ -327,6 +327,12 @@ type DeploymentStatus struct {
// Represents the latest available observations of a deployment's current state.
Conditions []DeploymentCondition
// Count of hash collisions for the Deployment. The Deployment controller uses this
// field as a collision avoidance mechanism when it needs to create the name for the
// newest ReplicaSet.
// +optional
CollisionCount *int64
}
type DeploymentConditionType string

View File

@ -325,6 +325,12 @@ type DeploymentStatus struct {
// +patchMergeKey=type
// +patchStrategy=merge
Conditions []DeploymentCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,6,rep,name=conditions"`
// Count of hash collisions for the Deployment. The Deployment controller uses this
// field as a collision avoidance mechanism when it needs to create the name for the
// newest ReplicaSet.
// +optional
CollisionCount *int64 `json:"collisionCount,omitempty"`
}
type DeploymentConditionType string

View File

@ -346,6 +346,9 @@ func ValidateDeploymentStatus(status *extensions.DeploymentStatus, fldPath *fiel
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.ReadyReplicas), fldPath.Child("readyReplicas"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fldPath.Child("availableReplicas"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UnavailableReplicas), fldPath.Child("unavailableReplicas"))...)
if status.CollisionCount != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(*status.CollisionCount, fldPath.Child("collisionCount"))...)
}
msg := "cannot be greater than status.replicas"
if status.UpdatedReplicas > status.Replicas {
allErrs = append(allErrs, field.Invalid(fldPath.Child("updatedReplicas"), status.UpdatedReplicas, msg))
@ -372,10 +375,29 @@ func ValidateDeploymentUpdate(update, old *extensions.Deployment) field.ErrorLis
func ValidateDeploymentStatusUpdate(update, old *extensions.Deployment) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateDeploymentStatus(&update.Status, field.NewPath("status"))...)
fldPath := field.NewPath("status")
allErrs = append(allErrs, ValidateDeploymentStatus(&update.Status, fldPath)...)
if isDecremented(update.Status.CollisionCount, old.Status.CollisionCount) {
value := int64(0)
if update.Status.CollisionCount != nil {
value = *update.Status.CollisionCount
}
allErrs = append(allErrs, field.Invalid(fldPath.Child("collisionCount"), value, "cannot be decremented"))
}
return allErrs
}
// TODO: Move in "k8s.io/kubernetes/pkg/api/validation"
func isDecremented(update, old *int64) bool {
if update == nil && old != nil {
return true
}
if update == nil || old == nil {
return false
}
return *update < *old
}
func ValidateDeployment(obj *extensions.Deployment) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, field.NewPath("spec"))...)

View File

@ -21,6 +21,8 @@ import (
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation/field"
@ -1230,6 +1232,11 @@ func TestValidateDeployment(t *testing.T) {
}
}
func int64p(i int) *int64 {
i64 := int64(i)
return &i64
}
func TestValidateDeploymentStatus(t *testing.T) {
tests := []struct {
name string
@ -1239,6 +1246,7 @@ func TestValidateDeploymentStatus(t *testing.T) {
readyReplicas int32
availableReplicas int32
observedGeneration int64
collisionCount *int64
expectedErr bool
}{
@ -1335,6 +1343,13 @@ func TestValidateDeploymentStatus(t *testing.T) {
observedGeneration: 1,
expectedErr: false,
},
{
name: "invalid collisionCount",
replicas: 3,
observedGeneration: 1,
collisionCount: int64p(-3),
expectedErr: true,
},
}
for _, test := range tests {
@ -1344,10 +1359,82 @@ func TestValidateDeploymentStatus(t *testing.T) {
ReadyReplicas: test.readyReplicas,
AvailableReplicas: test.availableReplicas,
ObservedGeneration: test.observedGeneration,
CollisionCount: test.collisionCount,
}
if hasErr := len(ValidateDeploymentStatus(&status, field.NewPath("status"))) > 0; hasErr != test.expectedErr {
t.Errorf("%s: expected error: %t, got error: %t", test.name, test.expectedErr, hasErr)
errs := ValidateDeploymentStatus(&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 TestValidateDeploymentStatusUpdate(t *testing.T) {
tests := []struct {
name string
from, to extensions.DeploymentStatus
expectedErr bool
}{
{
name: "increase: valid update",
from: extensions.DeploymentStatus{
CollisionCount: nil,
},
to: extensions.DeploymentStatus{
CollisionCount: int64p(1),
},
expectedErr: false,
},
{
name: "stable: valid update",
from: extensions.DeploymentStatus{
CollisionCount: int64p(1),
},
to: extensions.DeploymentStatus{
CollisionCount: int64p(1),
},
expectedErr: false,
},
{
name: "unset: invalid update",
from: extensions.DeploymentStatus{
CollisionCount: int64p(1),
},
to: extensions.DeploymentStatus{
CollisionCount: nil,
},
expectedErr: true,
},
{
name: "decrease: invalid update",
from: extensions.DeploymentStatus{
CollisionCount: int64p(2),
},
to: extensions.DeploymentStatus{
CollisionCount: int64p(1),
},
expectedErr: true,
},
}
for _, test := range tests {
meta := metav1.ObjectMeta{Name: "foo", Namespace: metav1.NamespaceDefault, ResourceVersion: "1"}
from := &extensions.Deployment{
ObjectMeta: meta,
Status: test.from,
}
to := &extensions.Deployment{
ObjectMeta: meta,
Status: test.to,
}
errs := ValidateDeploymentStatusUpdate(to, from)
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)
}
}
}