Removes PartitionStatefulSetStrategyType and Partition from UpdateStrategy and replaces them with a parameterized RollingUpdate strategy.

pull/6/head
Kenneth Owens 2017-06-12 10:06:09 -07:00
parent b84567a57e
commit b1ce1ffc55
9 changed files with 76 additions and 112 deletions

View File

@ -66,11 +66,8 @@ const (
type StatefulSetUpdateStrategy struct { type StatefulSetUpdateStrategy struct {
// Type indicates the type of the StatefulSetUpdateStrategy. // Type indicates the type of the StatefulSetUpdateStrategy.
Type StatefulSetUpdateStrategyType Type StatefulSetUpdateStrategyType
// Partition is used to communicate the ordinal at which to partition // RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType.
// the StatefulSet when Type is PartitionStatefulSetStrategyType. This RollingUpdate *RollingUpdateStatefulSetStrategy
// value must be set when Type is PartitionStatefulSetStrategyType,
// and it must be nil otherwise.
Partition *PartitionStatefulSetStrategy
} }
// StatefulSetUpdateStrategyType is a string enumeration type that enumerates // StatefulSetUpdateStrategyType is a string enumeration type that enumerates
@ -78,14 +75,6 @@ type StatefulSetUpdateStrategy struct {
type StatefulSetUpdateStrategyType string type StatefulSetUpdateStrategyType string
const ( const (
// PartitionStatefulSetStrategyType indicates that updates will only be
// applied to a partition of the StatefulSet. This is useful for canaries
// and phased roll outs. When a scale operation is performed with this
// strategy, new Pods will be created from the specification version indicated
// by the StatefulSet's currentRevision if there ordinal is less than the supplied
// Partition's ordinal. Otherwise, they will be created from the specification
// version indicated by the StatefulSet's updateRevision.
PartitionStatefulSetStrategyType StatefulSetUpdateStrategyType = "Partition"
// RollingUpdateStatefulSetStrategyType indicates that update will be // RollingUpdateStatefulSetStrategyType indicates that update will be
// applied to all Pods in the StatefulSet with respect to the StatefulSet // applied to all Pods in the StatefulSet with respect to the StatefulSet
// ordering constraints. When a scale operation is performed with this // ordering constraints. When a scale operation is performed with this
@ -100,12 +89,11 @@ const (
OnDeleteStatefulSetStrategyType = "OnDelete" OnDeleteStatefulSetStrategyType = "OnDelete"
) )
// PartitionStatefulSetStrategy contains the parameters used with the // RollingUpdateStatefulSetStrategy is used to communicate parameter for RollingUpdateStatefulSetStrategyType.
// PartitionStatefulSetStrategyType. type RollingUpdateStatefulSetStrategy struct {
type PartitionStatefulSetStrategy struct { // Partition indicates the ordinal at which the StatefulSet should be
// Ordinal indicates the ordinal at which the StatefulSet should be
// partitioned. // partitioned.
Ordinal int32 Partition int32
} }
// A StatefulSetSpec is the specification of a StatefulSet. // A StatefulSetSpec is the specification of a StatefulSet.

View File

@ -167,22 +167,23 @@ func Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec(in *apps.StatefulSe
func Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy(in *StatefulSetUpdateStrategy, out *apps.StatefulSetUpdateStrategy, s conversion.Scope) error { func Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy(in *StatefulSetUpdateStrategy, out *apps.StatefulSetUpdateStrategy, s conversion.Scope) error {
out.Type = apps.StatefulSetUpdateStrategyType(in.Type) out.Type = apps.StatefulSetUpdateStrategyType(in.Type)
if in.Partition != nil { if in.RollingUpdate != nil {
out.Partition = new(apps.PartitionStatefulSetStrategy) out.RollingUpdate = new(apps.RollingUpdateStatefulSetStrategy)
out.Partition.Ordinal = in.Partition.Ordinal out.RollingUpdate.Partition = *in.RollingUpdate.Partition
} else { } else {
out.Partition = nil out.RollingUpdate = nil
} }
return nil return nil
} }
func Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(in *apps.StatefulSetUpdateStrategy, out *StatefulSetUpdateStrategy, s conversion.Scope) error { func Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(in *apps.StatefulSetUpdateStrategy, out *StatefulSetUpdateStrategy, s conversion.Scope) error {
out.Type = StatefulSetUpdateStrategyType(in.Type) out.Type = StatefulSetUpdateStrategyType(in.Type)
if in.Partition != nil { if in.RollingUpdate != nil {
out.Partition = new(PartitionStatefulSetStrategy) out.RollingUpdate = new(RollingUpdateStatefulSetStrategy)
out.Partition.Ordinal = in.Partition.Ordinal out.RollingUpdate.Partition = new(int32)
*out.RollingUpdate.Partition = in.RollingUpdate.Partition
} else { } else {
out.Partition = nil out.RollingUpdate = nil
} }
return nil return nil
} }

View File

@ -53,6 +53,12 @@ func SetDefaults_StatefulSet(obj *StatefulSet) {
obj.Spec.RevisionHistoryLimit = new(int32) obj.Spec.RevisionHistoryLimit = new(int32)
*obj.Spec.RevisionHistoryLimit = 10 *obj.Spec.RevisionHistoryLimit = 10
} }
if obj.Spec.UpdateStrategy.Type == RollingUpdateStatefulSetStrategyType &&
obj.Spec.UpdateStrategy.RollingUpdate != nil &&
obj.Spec.UpdateStrategy.RollingUpdate.Partition == nil {
obj.Spec.UpdateStrategy.RollingUpdate.Partition = new(int32)
*obj.Spec.UpdateStrategy.RollingUpdate.Partition = 0
}
} }

View File

@ -118,11 +118,8 @@ const (
type StatefulSetUpdateStrategy struct { type StatefulSetUpdateStrategy struct {
// Type indicates the type of the StatefulSetUpdateStrategy. // Type indicates the type of the StatefulSetUpdateStrategy.
Type StatefulSetUpdateStrategyType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type,casttype=StatefulSetStrategyType"` Type StatefulSetUpdateStrategyType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type,casttype=StatefulSetStrategyType"`
// Partition is used to communicate the ordinal at which to partition // RollingUpdate is used to communicate parameters when Type is RollingUpdateStatefulSetStrategyType.
// the StatefulSet when Type is PartitionStatefulSetStrategyType. This RollingUpdate *RollingUpdateStatefulSetStrategy `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"`
// value must be set when Type is PartitionStatefulSetStrategyType,
// and it must be nil otherwise.
Partition *PartitionStatefulSetStrategy `json:"partition,omitempty" protobuf:"bytes,2,opt,name=partition"`
} }
// StatefulSetUpdateStrategyType is a string enumeration type that enumerates // StatefulSetUpdateStrategyType is a string enumeration type that enumerates
@ -130,14 +127,6 @@ type StatefulSetUpdateStrategy struct {
type StatefulSetUpdateStrategyType string type StatefulSetUpdateStrategyType string
const ( const (
// PartitionStatefulSetStrategyType indicates that updates will only be
// applied to a partition of the StatefulSet. This is useful for canaries
// and phased roll outs. When a scale operation is performed with this
// strategy, new Pods will be created from the specification version indicated
// by the StatefulSet's currentRevision if there ordinal is less than the supplied
// Partition's ordinal. Otherwise, they will be created from the specification
// version indicated by the StatefulSet's updateRevision.
PartitionStatefulSetStrategyType StatefulSetUpdateStrategyType = "Partition"
// RollingUpdateStatefulSetStrategyType indicates that update will be // RollingUpdateStatefulSetStrategyType indicates that update will be
// applied to all Pods in the StatefulSet with respect to the StatefulSet // applied to all Pods in the StatefulSet with respect to the StatefulSet
// ordering constraints. When a scale operation is performed with this // ordering constraints. When a scale operation is performed with this
@ -152,12 +141,11 @@ const (
OnDeleteStatefulSetStrategyType = "OnDelete" OnDeleteStatefulSetStrategyType = "OnDelete"
) )
// PartitionStatefulSetStrategy contains the parameters used with the // RollingUpdateStatefulSetStrategy is used to communicate parameter for RollingUpdateStatefulSetStrategyType.
// PartitionStatefulSetStrategyType. type RollingUpdateStatefulSetStrategy struct {
type PartitionStatefulSetStrategy struct { // Partition indicates the ordinal at which the StatefulSet should be
// Ordinal indicates the ordinal at which the StatefulSet should be
// partitioned. // partitioned.
Ordinal int32 `json:"ordinal" protobuf:"varint,1,opt,name=ordinal"` Partition *int32 `json:"partition,omitempty" protobuf:"varint,1,opt,name=partition"`
} }
// A StatefulSetSpec is the specification of a StatefulSet. // A StatefulSetSpec is the specification of a StatefulSet.

View File

@ -78,35 +78,29 @@ func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path) fi
switch spec.UpdateStrategy.Type { switch spec.UpdateStrategy.Type {
case "": case "":
allErrs = append(allErrs, field.Required(fldPath.Child("updateStrategy"), "")) allErrs = append(allErrs, field.Required(fldPath.Child("updateStrategy"), ""))
case apps.OnDeleteStatefulSetStrategyType, apps.RollingUpdateStatefulSetStrategyType: case apps.OnDeleteStatefulSetStrategyType:
if spec.UpdateStrategy.Partition != nil { if spec.UpdateStrategy.RollingUpdate != nil {
allErrs = append( allErrs = append(
allErrs, allErrs,
field.Invalid( field.Invalid(
fldPath.Child("updateStrategy").Child("partition"), fldPath.Child("updateStrategy").Child("rollingUpdate"),
spec.UpdateStrategy.Partition.Ordinal, spec.UpdateStrategy.RollingUpdate,
fmt.Sprintf("only allowed for updateStrategy '%s'", apps.PartitionStatefulSetStrategyType))) fmt.Sprintf("only allowed for updateStrategy '%s'", apps.RollingUpdateStatefulSetStrategyType)))
} }
case apps.PartitionStatefulSetStrategyType: case apps.RollingUpdateStatefulSetStrategyType:
if spec.UpdateStrategy.Partition == nil { if spec.UpdateStrategy.RollingUpdate != nil {
allErrs = append( allErrs = append(allErrs,
allErrs, apivalidation.ValidateNonnegativeField(
field.Required( int64(spec.UpdateStrategy.RollingUpdate.Partition),
fldPath.Child("updateStrategy").Child("partition"), fldPath.Child("updateStrategy").Child("rollingUpdate").Child("partition"))...)
fmt.Sprintf("required for updateStrategy '%s'", apps.PartitionStatefulSetStrategyType)))
break
} }
allErrs = append(allErrs,
apivalidation.ValidateNonnegativeField(
int64(spec.UpdateStrategy.Partition.Ordinal),
fldPath.Child("updateStrategy").Child("partition").Child("ordinal"))...)
default: default:
allErrs = append(allErrs, allErrs = append(allErrs,
field.Invalid(fldPath.Child("updateStrategy"), spec.UpdateStrategy, field.Invalid(fldPath.Child("updateStrategy"), spec.UpdateStrategy,
fmt.Sprintf("must be '%s', '%s', or '%s'", fmt.Sprintf("must be '%s' or '%s'",
apps.RollingUpdateStatefulSetStrategyType, apps.RollingUpdateStatefulSetStrategyType,
apps.OnDeleteStatefulSetStrategyType, apps.OnDeleteStatefulSetStrategyType)))
apps.PartitionStatefulSetStrategyType)))
} }
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...) allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...)

View File

@ -97,9 +97,9 @@ func TestValidateStatefulSet(t *testing.T) {
Template: validPodTemplate.Template, Template: validPodTemplate.Template,
Replicas: 3, Replicas: 3,
UpdateStrategy: apps.StatefulSetUpdateStrategy{ UpdateStrategy: apps.StatefulSetUpdateStrategy{
Type: apps.PartitionStatefulSetStrategyType, Type: apps.RollingUpdateStatefulSetStrategyType,
Partition: func() *apps.PartitionStatefulSetStrategy { RollingUpdate: func() *apps.RollingUpdateStatefulSetStrategy {
return &apps.PartitionStatefulSetStrategy{Ordinal: 2} return &apps.RollingUpdateStatefulSetStrategy{Partition: 2}
}()}, }()},
}, },
}, },
@ -259,7 +259,7 @@ func TestValidateStatefulSet(t *testing.T) {
UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: "foo"}, UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: "foo"},
}, },
}, },
"partitioned rolling update": { "negative parition": {
ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault}, ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
Spec: apps.StatefulSetSpec{ Spec: apps.StatefulSetSpec{
PodManagementPolicy: apps.OrderedReadyPodManagement, PodManagementPolicy: apps.OrderedReadyPodManagement,
@ -267,23 +267,11 @@ func TestValidateStatefulSet(t *testing.T) {
Template: validPodTemplate.Template, Template: validPodTemplate.Template,
Replicas: 3, Replicas: 3,
UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType, UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType,
Partition: func() *apps.PartitionStatefulSetStrategy { RollingUpdate: func() *apps.RollingUpdateStatefulSetStrategy {
return &apps.PartitionStatefulSetStrategy{Ordinal: 2} return &apps.RollingUpdateStatefulSetStrategy{Partition: -1}
}()}, }()},
}, },
}, },
"empty partition": {
ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
Spec: apps.StatefulSetSpec{
PodManagementPolicy: apps.OrderedReadyPodManagement,
Selector: &metav1.LabelSelector{MatchLabels: validLabels},
Template: validPodTemplate.Template,
Replicas: 3,
UpdateStrategy: apps.StatefulSetUpdateStrategy{
Type: apps.PartitionStatefulSetStrategyType,
Partition: nil},
},
},
} }
for k, v := range errorCases { for k, v := range errorCases {
errs := ValidateStatefulSet(&v) errs := ValidateStatefulSet(&v)
@ -304,8 +292,8 @@ func TestValidateStatefulSet(t *testing.T) {
field != "metadata.labels" && field != "metadata.labels" &&
field != "status.replicas" && field != "status.replicas" &&
field != "spec.updateStrategy" && field != "spec.updateStrategy" &&
field != "spec.updateStrategy.partition" && field != "spec.updateStrategy.rollingUpate" &&
field != "spec.updateStrategy.partition.ordinal" { field != "spec.updateStrategy.rollingUpdate.partition" {
t.Errorf("%s: missing prefix for: %v", k, errs[i]) t.Errorf("%s: missing prefix for: %v", k, errs[i])
} }
} }

View File

@ -483,8 +483,8 @@ func (ssc *defaultStatefulSetControl) updateStatefulSet(
// we compute the minimum ordinal of the target sequence for a destructive update based on the strategy. // we compute the minimum ordinal of the target sequence for a destructive update based on the strategy.
updateMin := 0 updateMin := 0
if set.Spec.UpdateStrategy.Type == apps.PartitionStatefulSetStrategyType { if set.Spec.UpdateStrategy.RollingUpdate != nil {
updateMin = int(set.Spec.UpdateStrategy.Partition.Ordinal) updateMin = int(*set.Spec.UpdateStrategy.RollingUpdate.Partition)
} }
// we terminate the Pod with the largest ordinal that does not match the update revision. // we terminate the Pod with the largest ordinal that does not match the update revision.
for target := len(replicas) - 1; target >= updateMin; target-- { for target := len(replicas) - 1; target >= updateMin; target-- {

View File

@ -1053,7 +1053,7 @@ func TestStatefulSetControlOnDeleteUpdate(t *testing.T) {
} }
} }
func TestStatefulSetControlPartitionUpdate(t *testing.T) { func TestStatefulSetControlRollingUpdateWithPartition(t *testing.T) {
type testcase struct { type testcase struct {
name string name string
partition int32 partition int32
@ -1066,9 +1066,9 @@ func TestStatefulSetControlPartitionUpdate(t *testing.T) {
testFn := func(test *testcase, t *testing.T) { testFn := func(test *testcase, t *testing.T) {
set := test.initial() set := test.initial()
set.Spec.UpdateStrategy = apps.StatefulSetUpdateStrategy{ set.Spec.UpdateStrategy = apps.StatefulSetUpdateStrategy{
Type: apps.PartitionStatefulSetStrategyType, Type: apps.RollingUpdateStatefulSetStrategyType,
Partition: func() *apps.PartitionStatefulSetStrategy { RollingUpdate: func() *apps.RollingUpdateStatefulSetStrategy {
return &apps.PartitionStatefulSetStrategy{Ordinal: test.partition} return &apps.RollingUpdateStatefulSetStrategy{Partition: &test.partition}
}(), }(),
} }
client := fake.NewSimpleClientset(set) client := fake.NewSimpleClientset(set)
@ -2068,28 +2068,28 @@ func updateComplete(set *apps.StatefulSet, pods []*v1.Pod) bool {
return false return false
} }
if set.Spec.UpdateStrategy.Type == apps.RollingUpdateStatefulSetStrategyType { switch set.Spec.UpdateStrategy.Type {
if set.Status.CurrentReplicas < *set.Spec.Replicas { case apps.OnDeleteStatefulSetStrategyType:
return false return true
} case apps.RollingUpdateStatefulSetStrategyType:
for i := range pods { if set.Spec.UpdateStrategy.RollingUpdate == nil || *set.Spec.UpdateStrategy.RollingUpdate.Partition <= 0 {
if getPodRevision(pods[i]) != set.Status.CurrentRevision { if set.Status.CurrentReplicas < *set.Spec.Replicas {
return false return false
} }
} for i := range pods {
} if getPodRevision(pods[i]) != set.Status.CurrentRevision {
if set.Spec.UpdateStrategy.Type == apps.PartitionStatefulSetStrategyType { return false
if set.Status.UpdatedReplicas < (*set.Spec.Replicas - set.Spec.UpdateStrategy.Partition.Ordinal) { }
return false }
} } else {
for i := 0; i < int(set.Spec.UpdateStrategy.Partition.Ordinal); i++ { partition := int(*set.Spec.UpdateStrategy.RollingUpdate.Partition)
if getPodRevision(pods[i]) != set.Status.CurrentRevision { if len(pods) < partition {
return false return false
} }
} for i := partition; i < len(pods); i++ {
for i := int(set.Spec.UpdateStrategy.Partition.Ordinal); i < int(*set.Spec.Replicas); i++ { if getPodRevision(pods[i]) != set.Status.UpdateRevision {
if getPodRevision(pods[i]) != set.Status.UpdateRevision { return false
return false }
} }
} }
} }

View File

@ -286,10 +286,9 @@ func newStatefulSetPod(set *apps.StatefulSet, ordinal int) *v1.Pod {
// the current revision. updateRevision is the name of the update revision. ordinal is the ordinal of the Pod. If the // the current revision. updateRevision is the name of the update revision. ordinal is the ordinal of the Pod. If the
// returned error is nil, the returned Pod is valid. // returned error is nil, the returned Pod is valid.
func newVersionedStatefulSetPod(currentSet, updateSet *apps.StatefulSet, currentRevision, updateRevision string, ordinal int) *v1.Pod { func newVersionedStatefulSetPod(currentSet, updateSet *apps.StatefulSet, currentRevision, updateRevision string, ordinal int) *v1.Pod {
if (currentSet.Spec.UpdateStrategy.Type == apps.RollingUpdateStatefulSetStrategyType && if currentSet.Spec.UpdateStrategy.Type == apps.RollingUpdateStatefulSetStrategyType &&
ordinal < int(currentSet.Status.CurrentReplicas)) || (currentSet.Spec.UpdateStrategy.RollingUpdate == nil && ordinal < int(currentSet.Status.CurrentReplicas)) ||
(currentSet.Spec.UpdateStrategy.Type == apps.PartitionStatefulSetStrategyType && (currentSet.Spec.UpdateStrategy.RollingUpdate != nil && ordinal < int(*currentSet.Spec.UpdateStrategy.RollingUpdate.Partition)) {
ordinal < int(currentSet.Spec.UpdateStrategy.Partition.Ordinal)) {
pod := newStatefulSetPod(currentSet, ordinal) pod := newStatefulSetPod(currentSet, ordinal)
setPodRevision(pod, currentRevision) setPodRevision(pod, currentRevision)
return pod return pod