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) { func(j *extensions.DaemonSetUpdateStrategy, c fuzz.Continue) {
c.FuzzNoCustom(j) // fuzz self without calling this function again c.FuzzNoCustom(j) // fuzz self without calling this function again
// Ensure that strategyType is one of valid values. // 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". // Rolling update config params. Present only if type = "RollingUpdate".
//--- //---
// TODO: Update this to follow our convention for oneOf, whatever we decide it // 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 // See https://github.com/kubernetes/kubernetes/issues/35345
// +optional // +optional
RollingUpdate *RollingUpdateDaemonSet RollingUpdate *RollingUpdateDaemonSet
@@ -449,10 +449,17 @@ type DaemonSetSpec struct {
// +optional // +optional
MinReadySeconds int32 MinReadySeconds int32
// DEPRECATED.
// A sequence number representing a specific generation of the template. // A sequence number representing a specific generation of the template.
// Populated by the system. It can be set only during the creation. // Populated by the system. It can be set only during the creation.
// +optional // +optional
TemplateGeneration int64 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. // DaemonSetStatus represents the current status of a daemon set.
@@ -492,6 +499,12 @@ type DaemonSetStatus struct {
// (ready for at least spec.minReadySeconds) // (ready for at least spec.minReadySeconds)
// +optional // +optional
NumberUnavailable int32 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 // +genclient=true
@@ -519,10 +532,16 @@ type DaemonSet struct {
} }
const ( const (
// DEPRECATED: DefaultDaemonSetUniqueLabelKey is used instead.
// DaemonSetTemplateGenerationKey is the key of the labels that is added // DaemonSetTemplateGenerationKey is the key of the labels that is added
// to daemon set pods to distinguish between old and new pod templates // to daemon set pods to distinguish between old and new pod templates
// during DaemonSet template update. // during DaemonSet template update.
DaemonSetTemplateGenerationKey string = "pod-template-generation" 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. // DaemonSetList is a collection of daemon sets.

View File

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

View File

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

View File

@@ -386,7 +386,7 @@ type DaemonSetUpdateStrategy struct {
// Rolling update config params. Present only if type = "RollingUpdate". // Rolling update config params. Present only if type = "RollingUpdate".
//--- //---
// TODO: Update this to follow our convention for oneOf, whatever we decide it // 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 // See https://github.com/kubernetes/kubernetes/issues/35345
// +optional // +optional
RollingUpdate *RollingUpdateDaemonSet `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"` RollingUpdate *RollingUpdateDaemonSet `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"`
@@ -449,10 +449,17 @@ type DaemonSetSpec struct {
// +optional // +optional
MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,4,opt,name=minReadySeconds"` MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,4,opt,name=minReadySeconds"`
// DEPRECATED.
// A sequence number representing a specific generation of the template. // A sequence number representing a specific generation of the template.
// Populated by the system. It can be set only during the creation. // Populated by the system. It can be set only during the creation.
// +optional // +optional
TemplateGeneration int64 `json:"templateGeneration,omitempty" protobuf:"varint,5,opt,name=templateGeneration"` 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. // DaemonSetStatus represents the current status of a daemon set.
@@ -495,6 +502,12 @@ type DaemonSetStatus struct {
// (ready for at least spec.minReadySeconds) // (ready for at least spec.minReadySeconds)
// +optional // +optional
NumberUnavailable int32 `json:"numberUnavailable,omitempty" protobuf:"varint,8,opt,name=numberUnavailable"` 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 // +genclient=true
@@ -522,10 +535,16 @@ type DaemonSet struct {
} }
const ( const (
// DEPRECATED: DefaultDaemonSetUniqueLabelKey is used instead.
// DaemonSetTemplateGenerationKey is the key of the labels that is added // DaemonSetTemplateGenerationKey is the key of the labels that is added
// to daemon set pods to distinguish between old and new pod templates // to daemon set pods to distinguish between old and new pod templates
// during DaemonSet template update. // during DaemonSet template update.
DaemonSetTemplateGenerationKey string = "pod-template-generation" 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. // DaemonSetList is a collection of daemon sets.

View File

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

View File

@@ -21,12 +21,13 @@ limitations under the License.
package v1beta1 package v1beta1
import ( import (
reflect "reflect"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion" conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
intstr "k8s.io/apimachinery/pkg/util/intstr" intstr "k8s.io/apimachinery/pkg/util/intstr"
api_v1 "k8s.io/kubernetes/pkg/api/v1" api_v1 "k8s.io/kubernetes/pkg/api/v1"
reflect "reflect"
) )
func init() { 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.UpdatedNumberScheduled), fldPath.Child("updatedNumberScheduled"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberAvailable), fldPath.Child("numberAvailable"))...) allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberAvailable), fldPath.Child("numberAvailable"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberUnavailable), fldPath.Child("numberUnavailable"))...) 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 return allErrs
} }
@@ -141,6 +144,13 @@ func validateDaemonSetStatus(status *extensions.DaemonSetStatus, fldPath *field.
func ValidateDaemonSetStatusUpdate(ds, oldDS *extensions.DaemonSet) field.ErrorList { func ValidateDaemonSetStatusUpdate(ds, oldDS *extensions.DaemonSet) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata")) allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, validateDaemonSetStatus(&ds.Status, field.NewPath("status"))...) 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 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, apivalidation.ValidateNonnegativeField(int64(spec.TemplateGeneration), fldPath.Child("templateGeneration"))...)
allErrs = append(allErrs, ValidateDaemonSetUpdateStrategy(&spec.UpdateStrategy, fldPath.Child("updateStrategy"))...) 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 return allErrs
} }

View File

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