Update DaemonSet API for rollback and history

1. Add revisionHistoryLimit (default 10), collisionCount, and validation code
2. Add daemonset-controller-hash label, and deprecate templateGeneration
This commit is contained in:
Janet Kuo
2017-05-16 12:50:15 -07:00
parent 78a9e4feba
commit 8275e8f017
9 changed files with 76 additions and 20 deletions

View File

@@ -553,6 +553,11 @@ func extensionFuncs(t apitesting.TestingCommon) []interface{} {
}
}
},
func(j *extensions.DaemonSetSpec, c fuzz.Continue) {
c.FuzzNoCustom(j) // fuzz self without calling this function again
rhl := int32(c.Rand.Int31())
j.RevisionHistoryLimit = &rhl
},
func(j *extensions.DaemonSetUpdateStrategy, c fuzz.Continue) {
c.FuzzNoCustom(j) // fuzz self without calling this function again
// Ensure that strategyType is one of valid values.

View File

@@ -386,7 +386,7 @@ type DaemonSetUpdateStrategy struct {
// Rolling update config params. Present only if type = "RollingUpdate".
//---
// TODO: Update this to follow our convention for oneOf, whatever we decide it
// to be. Same as DeploymentStrategy.RollingUpdate.
// to be. Same as Deployment `strategy.rollingUpdate`.
// See https://github.com/kubernetes/kubernetes/issues/35345
// +optional
RollingUpdate *RollingUpdateDaemonSet
@@ -449,10 +449,17 @@ type DaemonSetSpec struct {
// +optional
MinReadySeconds int32
// DEPRECATED.
// A sequence number representing a specific generation of the template.
// Populated by the system. It can be set only during the creation.
// +optional
TemplateGeneration int64
// The number of old history to retain to allow rollback.
// This is a pointer to distinguish between explicit zero and not specified.
// Defaults to 10.
// +optional
RevisionHistoryLimit *int32
}
// DaemonSetStatus represents the current status of a daemon set.
@@ -492,6 +499,12 @@ type DaemonSetStatus struct {
// (ready for at least spec.minReadySeconds)
// +optional
NumberUnavailable int32
// Count of hash collisions for the DaemonSet. The DaemonSet controller
// uses this field as a collision avoidance mechanism when it needs to
// create the name for the newest ControllerRevision.
// +optional
CollisionCount *int64
}
// +genclient=true
@@ -519,10 +532,16 @@ type DaemonSet struct {
}
const (
// DEPRECATED: DefaultDaemonSetUniqueLabelKey is used instead.
// DaemonSetTemplateGenerationKey is the key of the labels that is added
// to daemon set pods to distinguish between old and new pod templates
// during DaemonSet template update.
DaemonSetTemplateGenerationKey string = "pod-template-generation"
// DefaultDaemonSetUniqueLabelKey is the default label key that is added
// to existing DaemonSet pods to distinguish between old and new
// DaemonSet pods during DaemonSet template updates.
DefaultDaemonSetUniqueLabelKey string = "daemonset-controller-hash"
)
// DaemonSetList is a collection of daemon sets.

View File

@@ -56,6 +56,10 @@ func SetDefaults_DaemonSet(obj *DaemonSet) {
updateStrategy.RollingUpdate.MaxUnavailable = &maxUnavailable
}
}
if obj.Spec.RevisionHistoryLimit == nil {
obj.Spec.RevisionHistoryLimit = new(int32)
*obj.Spec.RevisionHistoryLimit = 10
}
}
func SetDefaults_Deployment(obj *Deployment) {

View File

@@ -32,7 +32,7 @@ import (
. "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)
func TestSetDefaultDaemonSet(t *testing.T) {
func TestSetDefaultDaemonSetSpec(t *testing.T) {
defaultLabels := map[string]string{"foo": "bar"}
period := int64(v1.DefaultTerminationGracePeriodSeconds)
defaultTemplate := v1.PodTemplateSpec{
@@ -78,6 +78,7 @@ func TestSetDefaultDaemonSet(t *testing.T) {
UpdateStrategy: DaemonSetUpdateStrategy{
Type: OnDeleteDaemonSetStrategyType,
},
RevisionHistoryLimit: newInt32(10),
},
},
},
@@ -90,6 +91,7 @@ func TestSetDefaultDaemonSet(t *testing.T) {
},
Spec: DaemonSetSpec{
Template: defaultTemplate,
RevisionHistoryLimit: newInt32(1),
},
},
expected: &DaemonSet{
@@ -106,6 +108,7 @@ func TestSetDefaultDaemonSet(t *testing.T) {
UpdateStrategy: DaemonSetUpdateStrategy{
Type: OnDeleteDaemonSetStrategyType,
},
RevisionHistoryLimit: newInt32(1),
},
},
},
@@ -117,19 +120,7 @@ func TestSetDefaultDaemonSet(t *testing.T) {
UpdateStrategy: DaemonSetUpdateStrategy{
Type: OnDeleteDaemonSetStrategyType,
},
},
},
},
{ // Update strategy.
original: &DaemonSet{
Spec: DaemonSetSpec{},
},
expected: &DaemonSet{
Spec: DaemonSetSpec{
Template: templateNoLabel,
UpdateStrategy: DaemonSetUpdateStrategy{
Type: OnDeleteDaemonSetStrategyType,
},
RevisionHistoryLimit: newInt32(10),
},
},
},
@@ -143,6 +134,7 @@ func TestSetDefaultDaemonSet(t *testing.T) {
UpdateStrategy: DaemonSetUpdateStrategy{
Type: OnDeleteDaemonSetStrategyType,
},
RevisionHistoryLimit: newInt32(10),
},
},
},

View File

@@ -386,7 +386,7 @@ type DaemonSetUpdateStrategy struct {
// Rolling update config params. Present only if type = "RollingUpdate".
//---
// TODO: Update this to follow our convention for oneOf, whatever we decide it
// to be. Same as DeploymentStrategy.RollingUpdate.
// to be. Same as Deployment `strategy.rollingUpdate`.
// See https://github.com/kubernetes/kubernetes/issues/35345
// +optional
RollingUpdate *RollingUpdateDaemonSet `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"`
@@ -449,10 +449,17 @@ type DaemonSetSpec struct {
// +optional
MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,4,opt,name=minReadySeconds"`
// DEPRECATED.
// A sequence number representing a specific generation of the template.
// Populated by the system. It can be set only during the creation.
// +optional
TemplateGeneration int64 `json:"templateGeneration,omitempty" protobuf:"varint,5,opt,name=templateGeneration"`
// The number of old history to retain to allow rollback.
// This is a pointer to distinguish between explicit zero and not specified.
// Defaults to 10.
// +optional
RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"`
}
// DaemonSetStatus represents the current status of a daemon set.
@@ -495,6 +502,12 @@ type DaemonSetStatus struct {
// (ready for at least spec.minReadySeconds)
// +optional
NumberUnavailable int32 `json:"numberUnavailable,omitempty" protobuf:"varint,8,opt,name=numberUnavailable"`
// Count of hash collisions for the DaemonSet. The DaemonSet controller
// uses this field as a collision avoidance mechanism when it needs to
// create the name for the newest ControllerRevision.
// +optional
CollisionCount *int64 `json:"collisionCount,omitempty" protobuf:"varint,9,opt,name=collisionCount"`
}
// +genclient=true
@@ -522,10 +535,16 @@ type DaemonSet struct {
}
const (
// DEPRECATED: DefaultDaemonSetUniqueLabelKey is used instead.
// DaemonSetTemplateGenerationKey is the key of the labels that is added
// to daemon set pods to distinguish between old and new pod templates
// during DaemonSet template update.
DaemonSetTemplateGenerationKey string = "pod-template-generation"
// DefaultDaemonSetUniqueLabelKey is the default label key that is added
// to existing DaemonSet pods to distinguish between old and new
// DaemonSet pods during DaemonSet template updates.
DefaultDaemonSetUniqueLabelKey string = "daemonset-controller-hash"
)
// DaemonSetList is a collection of daemon sets.

View File

@@ -21,6 +21,8 @@ limitations under the License.
package v1beta1
import (
unsafe "unsafe"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
@@ -28,7 +30,6 @@ import (
api "k8s.io/kubernetes/pkg/api"
api_v1 "k8s.io/kubernetes/pkg/api/v1"
extensions "k8s.io/kubernetes/pkg/apis/extensions"
unsafe "unsafe"
)
func init() {

View File

@@ -21,12 +21,13 @@ limitations under the License.
package v1beta1
import (
reflect "reflect"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
intstr "k8s.io/apimachinery/pkg/util/intstr"
api_v1 "k8s.io/kubernetes/pkg/api/v1"
reflect "reflect"
)
func init() {

View File

@@ -134,6 +134,9 @@ func validateDaemonSetStatus(status *extensions.DaemonSetStatus, fldPath *field.
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedNumberScheduled), fldPath.Child("updatedNumberScheduled"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberAvailable), fldPath.Child("numberAvailable"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberUnavailable), fldPath.Child("numberUnavailable"))...)
if status.CollisionCount != nil {
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.CollisionCount), fldPath.Child("collisionCount"))...)
}
return allErrs
}
@@ -141,6 +144,13 @@ func validateDaemonSetStatus(status *extensions.DaemonSetStatus, fldPath *field.
func ValidateDaemonSetStatusUpdate(ds, oldDS *extensions.DaemonSet) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, validateDaemonSetStatus(&ds.Status, field.NewPath("status"))...)
if isDecremented(ds.Status.CollisionCount, oldDS.Status.CollisionCount) {
value := int64(0)
if ds.Status.CollisionCount != nil {
value = *ds.Status.CollisionCount
}
allErrs = append(allErrs, field.Invalid(field.NewPath("status").Child("collisionCount"), value, "cannot be decremented"))
}
return allErrs
}
@@ -172,6 +182,10 @@ func ValidateDaemonSetSpec(spec *extensions.DaemonSetSpec, fldPath *field.Path)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.TemplateGeneration), fldPath.Child("templateGeneration"))...)
allErrs = append(allErrs, ValidateDaemonSetUpdateStrategy(&spec.UpdateStrategy, fldPath.Child("updateStrategy"))...)
if spec.RevisionHistoryLimit != nil {
// zero is a valid RevisionHistoryLimit
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...)
}
return allErrs
}

View File

@@ -21,12 +21,13 @@ limitations under the License.
package extensions
import (
reflect "reflect"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
intstr "k8s.io/apimachinery/pkg/util/intstr"
api "k8s.io/kubernetes/pkg/api"
reflect "reflect"
)
func init() {