mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 05:03:09 +00:00
Add collisionCount api field in DeploymentStatus
Signed-off-by: Michail Kargakis <mkargaki@redhat.com>
This commit is contained in:
parent
4def5add11
commit
4aa8b1a66a
@ -356,6 +356,12 @@ type DeploymentStatus struct {
|
|||||||
// +patchMergeKey=type
|
// +patchMergeKey=type
|
||||||
// +patchStrategy=merge
|
// +patchStrategy=merge
|
||||||
Conditions []DeploymentCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,6,rep,name=conditions"`
|
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
|
type DeploymentConditionType string
|
||||||
|
@ -327,6 +327,12 @@ type DeploymentStatus struct {
|
|||||||
|
|
||||||
// Represents the latest available observations of a deployment's current state.
|
// Represents the latest available observations of a deployment's current state.
|
||||||
Conditions []DeploymentCondition
|
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
|
type DeploymentConditionType string
|
||||||
|
@ -325,6 +325,12 @@ type DeploymentStatus struct {
|
|||||||
// +patchMergeKey=type
|
// +patchMergeKey=type
|
||||||
// +patchStrategy=merge
|
// +patchStrategy=merge
|
||||||
Conditions []DeploymentCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,6,rep,name=conditions"`
|
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
|
type DeploymentConditionType string
|
||||||
|
@ -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.ReadyReplicas), fldPath.Child("readyReplicas"))...)
|
||||||
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fldPath.Child("availableReplicas"))...)
|
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.AvailableReplicas), fldPath.Child("availableReplicas"))...)
|
||||||
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UnavailableReplicas), fldPath.Child("unavailableReplicas"))...)
|
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"
|
msg := "cannot be greater than status.replicas"
|
||||||
if status.UpdatedReplicas > status.Replicas {
|
if status.UpdatedReplicas > status.Replicas {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("updatedReplicas"), status.UpdatedReplicas, msg))
|
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 {
|
func ValidateDeploymentStatusUpdate(update, old *extensions.Deployment) field.ErrorList {
|
||||||
allErrs := apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))
|
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
|
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 {
|
func ValidateDeployment(obj *extensions.Deployment) field.ErrorList {
|
||||||
allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata"))
|
allErrs := apivalidation.ValidateObjectMeta(&obj.ObjectMeta, true, ValidateDeploymentName, field.NewPath("metadata"))
|
||||||
allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, field.NewPath("spec"))...)
|
allErrs = append(allErrs, ValidateDeploymentSpec(&obj.Spec, field.NewPath("spec"))...)
|
||||||
|
@ -21,6 +21,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"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) {
|
func TestValidateDeploymentStatus(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -1239,6 +1246,7 @@ func TestValidateDeploymentStatus(t *testing.T) {
|
|||||||
readyReplicas int32
|
readyReplicas int32
|
||||||
availableReplicas int32
|
availableReplicas int32
|
||||||
observedGeneration int64
|
observedGeneration int64
|
||||||
|
collisionCount *int64
|
||||||
|
|
||||||
expectedErr bool
|
expectedErr bool
|
||||||
}{
|
}{
|
||||||
@ -1335,6 +1343,13 @@ func TestValidateDeploymentStatus(t *testing.T) {
|
|||||||
observedGeneration: 1,
|
observedGeneration: 1,
|
||||||
expectedErr: false,
|
expectedErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "invalid collisionCount",
|
||||||
|
replicas: 3,
|
||||||
|
observedGeneration: 1,
|
||||||
|
collisionCount: int64p(-3),
|
||||||
|
expectedErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -1344,10 +1359,82 @@ func TestValidateDeploymentStatus(t *testing.T) {
|
|||||||
ReadyReplicas: test.readyReplicas,
|
ReadyReplicas: test.readyReplicas,
|
||||||
AvailableReplicas: test.availableReplicas,
|
AvailableReplicas: test.availableReplicas,
|
||||||
ObservedGeneration: test.observedGeneration,
|
ObservedGeneration: test.observedGeneration,
|
||||||
|
CollisionCount: test.collisionCount,
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasErr := len(ValidateDeploymentStatus(&status, field.NewPath("status"))) > 0; hasErr != test.expectedErr {
|
errs := ValidateDeploymentStatus(&status, field.NewPath("status"))
|
||||||
t.Errorf("%s: expected error: %t, got error: %t", test.name, test.expectedErr, hasErr)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user