diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index ff0b18e74a6..fa2c6b2b821 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -51580,6 +51580,19 @@ } } }, + "io.k8s.kubernetes.pkg.apis.apps.v1beta1.PartitionStatefulSetStrategy": { + "description": "PartitionStatefulSetStrategy contains the parameters used with the PartitionStatefulSetStrategyType.", + "required": [ + "ordinal" + ], + "properties": { + "ordinal": { + "description": "Ordinal indicates the ordinal at which the StatefulSet should be partitioned.", + "type": "integer", + "format": "int32" + } + } + }, "io.k8s.kubernetes.pkg.apis.apps.v1beta1.RollbackConfig": { "properties": { "revision": { @@ -51747,6 +51760,11 @@ "type": "integer", "format": "int32" }, + "revisionHistoryLimit": { + "description": "revisionHistoryLimit is the maximum number of revisions that will be maintained in the StatefulSet's revision history. The revision history consists of all revisions not represented by a currently applied StatefulSetSpec version. The default value is 10.", + "type": "integer", + "format": "int32" + }, "selector": { "description": "selector is a label query over pods that should match the replica count. If empty, defaulted to labels on the pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector" @@ -51759,6 +51777,10 @@ "description": "template is the object that describes the pod that will be created if insufficient replicas are detected. Each pod stamped out by the StatefulSet will fulfill this Template, but have a unique identity from the rest of the StatefulSet.", "$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.PodTemplateSpec" }, + "updateStrategy": { + "description": "updateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template.", + "$ref": "#/definitions/io.k8s.kubernetes.pkg.apis.apps.v1beta1.StatefulSetUpdateStrategy" + }, "volumeClaimTemplates": { "description": "volumeClaimTemplates is a list of claims that pods are allowed to reference. The StatefulSet controller is responsible for mapping network identities to claims in a way that maintains the identity of a pod. Every claim in this list must have at least one matching (by name) volumeMount in one container in the template. A claim in this list takes precedence over any volumes in the template, with the same name.", "type": "array", @@ -51774,15 +51796,51 @@ "replicas" ], "properties": { + "currentReplicas": { + "description": "currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by currentRevision.", + "type": "integer", + "format": "int32" + }, + "currentRevision": { + "description": "currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [0,currentReplicas).", + "type": "string" + }, "observedGeneration": { - "description": "observedGeneration is the most recent generation observed by this StatefulSet.", + "description": "observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the StatefulSet's generation, which is updated on mutation by the API Server.", "type": "integer", "format": "int64" }, - "replicas": { - "description": "replicas is the number of actual replicas.", + "readyReplicas": { + "description": "readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition.", "type": "integer", "format": "int32" + }, + "replicas": { + "description": "replicas is the number of Pods created by the StatefulSet controller.", + "type": "integer", + "format": "int32" + }, + "updateRevision": { + "description": "updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas)", + "type": "string" + }, + "updatedReplicas": { + "description": "updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by updateRevision.", + "type": "integer", + "format": "int32" + } + } + }, + "io.k8s.kubernetes.pkg.apis.apps.v1beta1.StatefulSetUpdateStrategy": { + "description": "StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. It includes any additional parameters necessary to perform the update for the indicated strategy.", + "properties": { + "partition": { + "description": "Partition is used to communicate the ordinal at which to partition the StatefulSet when Type is PartitionStatefulSetStrategyType. This value must be set when Type is PartitionStatefulSetStrategyType, and it must be nil otherwise.", + "$ref": "#/definitions/io.k8s.kubernetes.pkg.apis.apps.v1beta1.PartitionStatefulSetStrategy" + }, + "type": { + "description": "Type indicates the type of the StatefulSetUpdateStrategy.", + "type": "string" } } }, diff --git a/api/swagger-spec/apps_v1beta1.json b/api/swagger-spec/apps_v1beta1.json index 704fe935d67..223ea8cd089 100644 --- a/api/swagger-spec/apps_v1beta1.json +++ b/api/swagger-spec/apps_v1beta1.json @@ -5915,6 +5915,15 @@ "podManagementPolicy": { "type": "string", "description": "podManagementPolicy controls how pods are created during initial scale up, when replacing pods on nodes, or when scaling down. The default policy is `OrderedReady`, where pods are created in increasing order (pod-0, then pod-1, etc) and the controller will wait until each pod is ready before continuing. When scaling down, the pods are removed in the opposite order. The alternative policy is `Parallel` which will create pods in parallel to match the desired scale without waiting, and on scale down will delete all pods at once." + }, + "updateStrategy": { + "$ref": "v1beta1.StatefulSetUpdateStrategy", + "description": "updateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template." + }, + "revisionHistoryLimit": { + "type": "integer", + "format": "int32", + "description": "revisionHistoryLimit is the maximum number of revisions that will be maintained in the StatefulSet's revision history. The revision history consists of all revisions not represented by a currently applied StatefulSetSpec version. The default value is 10." } } }, @@ -5998,6 +6007,34 @@ } } }, + "v1beta1.StatefulSetUpdateStrategy": { + "id": "v1beta1.StatefulSetUpdateStrategy", + "description": "StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. It includes any additional parameters necessary to perform the update for the indicated strategy.", + "properties": { + "type": { + "type": "string", + "description": "Type indicates the type of the StatefulSetUpdateStrategy." + }, + "partition": { + "$ref": "v1beta1.PartitionStatefulSetStrategy", + "description": "Partition is used to communicate the ordinal at which to partition the StatefulSet when Type is PartitionStatefulSetStrategyType. This value must be set when Type is PartitionStatefulSetStrategyType, and it must be nil otherwise." + } + } + }, + "v1beta1.PartitionStatefulSetStrategy": { + "id": "v1beta1.PartitionStatefulSetStrategy", + "description": "PartitionStatefulSetStrategy contains the parameters used with the PartitionStatefulSetStrategyType.", + "required": [ + "ordinal" + ], + "properties": { + "ordinal": { + "type": "integer", + "format": "int32", + "description": "Ordinal indicates the ordinal at which the StatefulSet should be partitioned." + } + } + }, "v1beta1.StatefulSetStatus": { "id": "v1beta1.StatefulSetStatus", "description": "StatefulSetStatus represents the current state of a StatefulSet.", @@ -6008,12 +6045,35 @@ "observedGeneration": { "type": "integer", "format": "int64", - "description": "observedGeneration is the most recent generation observed by this StatefulSet." + "description": "observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the StatefulSet's generation, which is updated on mutation by the API Server." }, "replicas": { "type": "integer", "format": "int32", - "description": "replicas is the number of actual replicas." + "description": "replicas is the number of Pods created by the StatefulSet controller." + }, + "readyReplicas": { + "type": "integer", + "format": "int32", + "description": "readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition." + }, + "currentReplicas": { + "type": "integer", + "format": "int32", + "description": "currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by currentRevision." + }, + "updatedReplicas": { + "type": "integer", + "format": "int32", + "description": "updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by updateRevision." + }, + "currentRevision": { + "type": "string", + "description": "currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [0,currentReplicas)." + }, + "updateRevision": { + "type": "string", + "description": "updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas)" } } }, diff --git a/cmd/kube-controller-manager/app/apps.go b/cmd/kube-controller-manager/app/apps.go index 2d8d722356f..c3e475d940c 100644 --- a/cmd/kube-controller-manager/app/apps.go +++ b/cmd/kube-controller-manager/app/apps.go @@ -33,6 +33,7 @@ func startStatefulSetController(ctx ControllerContext) (bool, error) { ctx.InformerFactory.Core().V1().Pods(), ctx.InformerFactory.Apps().V1beta1().StatefulSets(), ctx.InformerFactory.Core().V1().PersistentVolumeClaims(), + ctx.InformerFactory.Apps().V1beta1().ControllerRevisions(), ctx.ClientBuilder.ClientOrDie("statefulset-controller"), ).Run(1, ctx.Stop) return true, nil diff --git a/docs/api-reference/apps/v1beta1/definitions.html b/docs/api-reference/apps/v1beta1/definitions.html index 06c28a187be..4722d2f3b81 100755 --- a/docs/api-reference/apps/v1beta1/definitions.html +++ b/docs/api-reference/apps/v1beta1/definitions.html @@ -1564,6 +1564,20 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }

string

+ +

updateStrategy

+

updateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template.

+

false

+

v1beta1.StatefulSetUpdateStrategy

+ + + +

revisionHistoryLimit

+

revisionHistoryLimit is the maximum number of revisions that will be maintained in the StatefulSet’s revision history. The revision history consists of all revisions not represented by a currently applied StatefulSetSpec version. The default value is 10.

+

false

+

integer (int32)

+ + @@ -1818,6 +1832,40 @@ When an object is created, the system will populate this list with the current s + +
+

v1beta1.PartitionStatefulSetStrategy

+
+

PartitionStatefulSetStrategy contains the parameters used with the PartitionStatefulSetStrategyType.

+
+ +++++++ + + + + + + + + + + + + + + + + + + +
NameDescriptionRequiredSchemaDefault

ordinal

Ordinal indicates the ordinal at which the StatefulSet should be partitioned.

true

integer (int32)

+

v1.AzureFileVolumeSource

@@ -5017,18 +5065,53 @@ Examples:

observedGeneration

-

observedGeneration is the most recent generation observed by this StatefulSet.

+

observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the StatefulSet’s generation, which is updated on mutation by the API Server.

false

integer (int64)

replicas

-

replicas is the number of actual replicas.

+

replicas is the number of Pods created by the StatefulSet controller.

true

integer (int32)

+ +

readyReplicas

+

readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition.

+

false

+

integer (int32)

+ + + +

currentReplicas

+

currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by currentRevision.

+

false

+

integer (int32)

+ + + +

updatedReplicas

+

updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by updateRevision.

+

false

+

integer (int32)

+ + + +

currentRevision

+

currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [0,currentReplicas).

+

false

+

string

+ + + +

updateRevision

+

updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas)

+

false

+

string

+ + @@ -6464,6 +6547,47 @@ Examples:
+
+
+

v1beta1.StatefulSetUpdateStrategy

+
+

StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. It includes any additional parameters necessary to perform the update for the indicated strategy.

+
+ +++++++ + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescriptionRequiredSchemaDefault

type

Type indicates the type of the StatefulSetUpdateStrategy.

false

string

partition

Partition is used to communicate the ordinal at which to partition the StatefulSet when Type is PartitionStatefulSetStrategyType. This value must be set when Type is PartitionStatefulSetStrategyType, and it must be nil otherwise.

false

v1beta1.PartitionStatefulSetStrategy

+

v1.NodeAffinity

@@ -6614,7 +6738,7 @@ Examples:
diff --git a/hack/.linted_packages b/hack/.linted_packages index 1791c700020..26038e5d648 100644 --- a/hack/.linted_packages +++ b/hack/.linted_packages @@ -194,6 +194,7 @@ pkg/client/unversioned pkg/cloudprovider/providers pkg/cloudprovider/providers/azure pkg/cloudprovider/providers/cloudstack +pkg/controller/history pkg/controller/volume/attachdetach/cache pkg/controller/volume/attachdetach/populator pkg/controller/volume/attachdetach/reconciler diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index 69baf42ad03..d796a5fda38 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -723,6 +723,13 @@ func appsFuncs(t apitesting.TestingCommon) []interface{} { if len(s.Spec.PodManagementPolicy) == 0 { s.Spec.PodManagementPolicy = apps.OrderedReadyPodManagement } + if len(s.Spec.UpdateStrategy.Type) == 0 { + s.Spec.UpdateStrategy.Type = apps.RollingUpdateStatefulSetStrategyType + } + if s.Spec.RevisionHistoryLimit == nil { + s.Spec.RevisionHistoryLimit = new(int32) + *s.Spec.RevisionHistoryLimit = 10 + } }, } } diff --git a/pkg/apis/apps/types.go b/pkg/apis/apps/types.go index f1d21c1284e..47c621941fd 100644 --- a/pkg/apis/apps/types.go +++ b/pkg/apis/apps/types.go @@ -60,6 +60,54 @@ const ( ParallelPodManagement = "Parallel" ) +// StatefulSetUpdateStrategy indicates the strategy that the StatefulSet +// controller will use to perform updates. It includes any additional parameters +// necessary to perform the update for the indicated strategy. +type StatefulSetUpdateStrategy struct { + // Type indicates the type of the StatefulSetUpdateStrategy. + Type StatefulSetUpdateStrategyType + // Partition is used to communicate the ordinal at which to partition + // the StatefulSet when Type is PartitionStatefulSetStrategyType. This + // value must be set when Type is PartitionStatefulSetStrategyType, + // and it must be nil otherwise. + Partition *PartitionStatefulSetStrategy +} + +// StatefulSetUpdateStrategyType is a string enumeration type that enumerates +// all possible update strategies for the StatefulSet controller. +type StatefulSetUpdateStrategyType string + +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 + // applied to all Pods in the StatefulSet with respect to the StatefulSet + // ordering constraints. When a scale operation is performed with this + // strategy, new Pods will be created from the specification version indicated + // by the StatefulSet's updateRevision. + RollingUpdateStatefulSetStrategyType = "RollingUpdate" + // OnDeleteStatefulSetStrategyType triggers the legacy behavior. Version + // tracking and ordered rolling restarts are disabled. Pods are recreated + // from the StatefulSetSpec when they are manually deleted. When a scale + // operation is performed with this strategy,specification version indicated + // by the StatefulSet's currentRevision. + OnDeleteStatefulSetStrategyType = "OnDelete" +) + +// PartitionStatefulSetStrategy contains the parameters used with the +// PartitionStatefulSetStrategyType. +type PartitionStatefulSetStrategy struct { + // Ordinal indicates the ordinal at which the StatefulSet should be + // partitioned. + Ordinal int32 +} + // A StatefulSetSpec is the specification of a StatefulSet. type StatefulSetSpec struct { // Replicas is the desired number of replicas of the given Template. @@ -109,16 +157,47 @@ type StatefulSetSpec struct { // all pods at once. // +optional PodManagementPolicy PodManagementPolicyType + + // updateStrategy indicates the StatefulSetUpdateStrategy that will be + // employed to update Pods in the StatefulSet when a revision is made to + // Template. + UpdateStrategy StatefulSetUpdateStrategy + + // revisionHistoryLimit is the maximum number of revisions that will + // be maintained in the StatefulSet's revision history. The revision history + // consists of all revisions not represented by a currently applied + // StatefulSetSpec version. The default value is 10. + RevisionHistoryLimit *int32 } // StatefulSetStatus represents the current state of a StatefulSet. type StatefulSetStatus struct { - // most recent generation observed by this StatefulSet. + // observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the + // StatefulSet's generation, which is updated on mutation by the API Server. // +optional ObservedGeneration *int64 - // Replicas is the number of actual replicas. + // replicas is the number of Pods created by the StatefulSet controller. Replicas int32 + + // readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition. + ReadyReplicas int32 + + // currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by currentRevision. + CurrentReplicas int32 + + // updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by updateRevision. + UpdatedReplicas int32 + + // currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the + // sequence [0,currentReplicas). + CurrentRevision string + + // updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence + // [replicas-updatedReplicas,replicas) + UpdateRevision string } // StatefulSetList is a collection of StatefulSets. diff --git a/pkg/apis/apps/v1beta1/conversion.go b/pkg/apis/apps/v1beta1/conversion.go index a2e36890996..e11b8623bb7 100644 --- a/pkg/apis/apps/v1beta1/conversion.go +++ b/pkg/apis/apps/v1beta1/conversion.go @@ -37,6 +37,8 @@ func addConversionFuncs(scheme *runtime.Scheme) error { err := scheme.AddConversionFuncs( Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec, Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec, + Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy, + Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy, // extensions // TODO: below conversions should be dropped in favor of auto-generated // ones, see https://github.com/kubernetes/kubernetextensionsssues/39865 @@ -109,6 +111,15 @@ func Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(in *StatefulSetSpec } else { out.VolumeClaimTemplates = nil } + if err := Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { + return err + } + if in.RevisionHistoryLimit != nil { + out.RevisionHistoryLimit = new(int32) + *out.RevisionHistoryLimit = *in.RevisionHistoryLimit + } else { + out.RevisionHistoryLimit = nil + } out.ServiceName = in.ServiceName out.PodManagementPolicy = apps.PodManagementPolicyType(in.PodManagementPolicy) return nil @@ -140,8 +151,39 @@ func Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec(in *apps.StatefulSe } else { out.VolumeClaimTemplates = nil } + if in.RevisionHistoryLimit != nil { + out.RevisionHistoryLimit = new(int32) + *out.RevisionHistoryLimit = *in.RevisionHistoryLimit + } else { + out.RevisionHistoryLimit = nil + } out.ServiceName = in.ServiceName out.PodManagementPolicy = PodManagementPolicyType(in.PodManagementPolicy) + if err := Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { + return err + } + return nil +} + +func Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy(in *StatefulSetUpdateStrategy, out *apps.StatefulSetUpdateStrategy, s conversion.Scope) error { + out.Type = apps.StatefulSetUpdateStrategyType(in.Type) + if in.Partition != nil { + out.Partition = new(apps.PartitionStatefulSetStrategy) + out.Partition.Ordinal = in.Partition.Ordinal + } else { + out.Partition = nil + } + return nil +} + +func Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(in *apps.StatefulSetUpdateStrategy, out *StatefulSetUpdateStrategy, s conversion.Scope) error { + out.Type = StatefulSetUpdateStrategyType(in.Type) + if in.Partition != nil { + out.Partition = new(PartitionStatefulSetStrategy) + out.Partition.Ordinal = in.Partition.Ordinal + } else { + out.Partition = nil + } return nil } diff --git a/pkg/apis/apps/v1beta1/defaults.go b/pkg/apis/apps/v1beta1/defaults.go index 52b881d6644..895c8b90eb7 100644 --- a/pkg/apis/apps/v1beta1/defaults.go +++ b/pkg/apis/apps/v1beta1/defaults.go @@ -30,6 +30,10 @@ func SetDefaults_StatefulSet(obj *StatefulSet) { if len(obj.Spec.PodManagementPolicy) == 0 { obj.Spec.PodManagementPolicy = OrderedReadyPodManagement } + + if obj.Spec.UpdateStrategy.Type == "" { + obj.Spec.UpdateStrategy.Type = OnDeleteStatefulSetStrategyType + } labels := obj.Spec.Template.Labels if labels != nil { if obj.Spec.Selector == nil { @@ -45,6 +49,11 @@ func SetDefaults_StatefulSet(obj *StatefulSet) { obj.Spec.Replicas = new(int32) *obj.Spec.Replicas = 1 } + if obj.Spec.RevisionHistoryLimit == nil { + obj.Spec.RevisionHistoryLimit = new(int32) + *obj.Spec.RevisionHistoryLimit = 10 + } + } // SetDefaults_Deployment sets additional defaults compared to its counterpart diff --git a/pkg/apis/apps/v1beta1/generated.pb.go b/pkg/apis/apps/v1beta1/generated.pb.go index 690fa6522cd..c31ccd3f2de 100644 --- a/pkg/apis/apps/v1beta1/generated.pb.go +++ b/pkg/apis/apps/v1beta1/generated.pb.go @@ -34,6 +34,7 @@ limitations under the License. DeploymentSpec DeploymentStatus DeploymentStrategy + PartitionStatefulSetStrategy RollbackConfig RollingUpdateDeployment Scale @@ -43,6 +44,7 @@ limitations under the License. StatefulSetList StatefulSetSpec StatefulSetStatus + StatefulSetUpdateStrategy */ package v1beta1 @@ -108,43 +110,55 @@ func (m *DeploymentStrategy) Reset() { *m = DeploymentStrateg func (*DeploymentStrategy) ProtoMessage() {} func (*DeploymentStrategy) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{8} } +func (m *PartitionStatefulSetStrategy) Reset() { *m = PartitionStatefulSetStrategy{} } +func (*PartitionStatefulSetStrategy) ProtoMessage() {} +func (*PartitionStatefulSetStrategy) Descriptor() ([]byte, []int) { + return fileDescriptorGenerated, []int{9} +} + func (m *RollbackConfig) Reset() { *m = RollbackConfig{} } func (*RollbackConfig) ProtoMessage() {} -func (*RollbackConfig) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{9} } +func (*RollbackConfig) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{10} } func (m *RollingUpdateDeployment) Reset() { *m = RollingUpdateDeployment{} } func (*RollingUpdateDeployment) ProtoMessage() {} func (*RollingUpdateDeployment) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{10} + return fileDescriptorGenerated, []int{11} } func (m *Scale) Reset() { *m = Scale{} } func (*Scale) ProtoMessage() {} -func (*Scale) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{11} } +func (*Scale) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{12} } func (m *ScaleSpec) Reset() { *m = ScaleSpec{} } func (*ScaleSpec) ProtoMessage() {} -func (*ScaleSpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{12} } +func (*ScaleSpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{13} } func (m *ScaleStatus) Reset() { *m = ScaleStatus{} } func (*ScaleStatus) ProtoMessage() {} -func (*ScaleStatus) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{13} } +func (*ScaleStatus) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{14} } func (m *StatefulSet) Reset() { *m = StatefulSet{} } func (*StatefulSet) ProtoMessage() {} -func (*StatefulSet) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{14} } +func (*StatefulSet) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{15} } func (m *StatefulSetList) Reset() { *m = StatefulSetList{} } func (*StatefulSetList) ProtoMessage() {} -func (*StatefulSetList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{15} } +func (*StatefulSetList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{16} } func (m *StatefulSetSpec) Reset() { *m = StatefulSetSpec{} } func (*StatefulSetSpec) ProtoMessage() {} -func (*StatefulSetSpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{16} } +func (*StatefulSetSpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{17} } func (m *StatefulSetStatus) Reset() { *m = StatefulSetStatus{} } func (*StatefulSetStatus) ProtoMessage() {} -func (*StatefulSetStatus) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{17} } +func (*StatefulSetStatus) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{18} } + +func (m *StatefulSetUpdateStrategy) Reset() { *m = StatefulSetUpdateStrategy{} } +func (*StatefulSetUpdateStrategy) ProtoMessage() {} +func (*StatefulSetUpdateStrategy) Descriptor() ([]byte, []int) { + return fileDescriptorGenerated, []int{19} +} func init() { proto.RegisterType((*ControllerRevision)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.ControllerRevision") @@ -156,6 +170,7 @@ func init() { proto.RegisterType((*DeploymentSpec)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.DeploymentSpec") proto.RegisterType((*DeploymentStatus)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.DeploymentStatus") proto.RegisterType((*DeploymentStrategy)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.DeploymentStrategy") + proto.RegisterType((*PartitionStatefulSetStrategy)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.PartitionStatefulSetStrategy") proto.RegisterType((*RollbackConfig)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.RollbackConfig") proto.RegisterType((*RollingUpdateDeployment)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.RollingUpdateDeployment") proto.RegisterType((*Scale)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.Scale") @@ -165,6 +180,7 @@ func init() { proto.RegisterType((*StatefulSetList)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.StatefulSetList") proto.RegisterType((*StatefulSetSpec)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.StatefulSetSpec") proto.RegisterType((*StatefulSetStatus)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.StatefulSetStatus") + proto.RegisterType((*StatefulSetUpdateStrategy)(nil), "k8s.io.kubernetes.pkg.apis.apps.v1beta1.StatefulSetUpdateStrategy") } func (m *ControllerRevision) Marshal() (dAtA []byte, err error) { size := m.Size() @@ -583,6 +599,27 @@ func (m *DeploymentStrategy) MarshalTo(dAtA []byte) (int, error) { return i, nil } +func (m *PartitionStatefulSetStrategy) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PartitionStatefulSetStrategy) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0x8 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.Ordinal)) + return i, nil +} + func (m *RollbackConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -885,6 +922,19 @@ func (m *StatefulSetSpec) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintGenerated(dAtA, i, uint64(len(m.PodManagementPolicy))) i += copy(dAtA[i:], m.PodManagementPolicy) + dAtA[i] = 0x3a + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.UpdateStrategy.Size())) + n27, err := m.UpdateStrategy.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n27 + if m.RevisionHistoryLimit != nil { + dAtA[i] = 0x40 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(*m.RevisionHistoryLimit)) + } return i, nil } @@ -911,6 +961,55 @@ func (m *StatefulSetStatus) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x10 i++ i = encodeVarintGenerated(dAtA, i, uint64(m.Replicas)) + dAtA[i] = 0x18 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.ReadyReplicas)) + dAtA[i] = 0x20 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.CurrentReplicas)) + dAtA[i] = 0x28 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.UpdatedReplicas)) + dAtA[i] = 0x32 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.CurrentRevision))) + i += copy(dAtA[i:], m.CurrentRevision) + dAtA[i] = 0x3a + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.UpdateRevision))) + i += copy(dAtA[i:], m.UpdateRevision) + return i, nil +} + +func (m *StatefulSetUpdateStrategy) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StatefulSetUpdateStrategy) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0xa + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Type))) + i += copy(dAtA[i:], m.Type) + if m.Partition != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.Partition.Size())) + n28, err := m.Partition.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n28 + } return i, nil } @@ -1090,6 +1189,13 @@ func (m *DeploymentStrategy) Size() (n int) { return n } +func (m *PartitionStatefulSetStrategy) Size() (n int) { + var l int + _ = l + n += 1 + sovGenerated(uint64(m.Ordinal)) + return n +} + func (m *RollbackConfig) Size() (n int) { var l int _ = l @@ -1195,6 +1301,11 @@ func (m *StatefulSetSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) l = len(m.PodManagementPolicy) n += 1 + l + sovGenerated(uint64(l)) + l = m.UpdateStrategy.Size() + n += 1 + l + sovGenerated(uint64(l)) + if m.RevisionHistoryLimit != nil { + n += 1 + sovGenerated(uint64(*m.RevisionHistoryLimit)) + } return n } @@ -1205,6 +1316,25 @@ func (m *StatefulSetStatus) Size() (n int) { n += 1 + sovGenerated(uint64(*m.ObservedGeneration)) } n += 1 + sovGenerated(uint64(m.Replicas)) + n += 1 + sovGenerated(uint64(m.ReadyReplicas)) + n += 1 + sovGenerated(uint64(m.CurrentReplicas)) + n += 1 + sovGenerated(uint64(m.UpdatedReplicas)) + l = len(m.CurrentRevision) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.UpdateRevision) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *StatefulSetUpdateStrategy) Size() (n int) { + var l int + _ = l + l = len(m.Type) + n += 1 + l + sovGenerated(uint64(l)) + if m.Partition != nil { + l = m.Partition.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -1350,6 +1480,16 @@ func (this *DeploymentStrategy) String() string { }, "") return s } +func (this *PartitionStatefulSetStrategy) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PartitionStatefulSetStrategy{`, + `Ordinal:` + fmt.Sprintf("%v", this.Ordinal) + `,`, + `}`, + }, "") + return s +} func (this *RollbackConfig) String() string { if this == nil { return "nil" @@ -1449,6 +1589,8 @@ func (this *StatefulSetSpec) String() string { `VolumeClaimTemplates:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.VolumeClaimTemplates), "PersistentVolumeClaim", "k8s_io_kubernetes_pkg_api_v1.PersistentVolumeClaim", 1), `&`, ``, 1) + `,`, `ServiceName:` + fmt.Sprintf("%v", this.ServiceName) + `,`, `PodManagementPolicy:` + fmt.Sprintf("%v", this.PodManagementPolicy) + `,`, + `UpdateStrategy:` + strings.Replace(strings.Replace(this.UpdateStrategy.String(), "StatefulSetUpdateStrategy", "StatefulSetUpdateStrategy", 1), `&`, ``, 1) + `,`, + `RevisionHistoryLimit:` + valueToStringGenerated(this.RevisionHistoryLimit) + `,`, `}`, }, "") return s @@ -1460,6 +1602,22 @@ func (this *StatefulSetStatus) String() string { s := strings.Join([]string{`&StatefulSetStatus{`, `ObservedGeneration:` + valueToStringGenerated(this.ObservedGeneration) + `,`, `Replicas:` + fmt.Sprintf("%v", this.Replicas) + `,`, + `ReadyReplicas:` + fmt.Sprintf("%v", this.ReadyReplicas) + `,`, + `CurrentReplicas:` + fmt.Sprintf("%v", this.CurrentReplicas) + `,`, + `UpdatedReplicas:` + fmt.Sprintf("%v", this.UpdatedReplicas) + `,`, + `CurrentRevision:` + fmt.Sprintf("%v", this.CurrentRevision) + `,`, + `UpdateRevision:` + fmt.Sprintf("%v", this.UpdateRevision) + `,`, + `}`, + }, "") + return s +} +func (this *StatefulSetUpdateStrategy) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&StatefulSetUpdateStrategy{`, + `Type:` + fmt.Sprintf("%v", this.Type) + `,`, + `Partition:` + strings.Replace(fmt.Sprintf("%v", this.Partition), "PartitionStatefulSetStrategy", "PartitionStatefulSetStrategy", 1) + `,`, `}`, }, "") return s @@ -3016,6 +3174,75 @@ func (m *DeploymentStrategy) Unmarshal(dAtA []byte) error { } return nil } +func (m *PartitionStatefulSetStrategy) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PartitionStatefulSetStrategy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PartitionStatefulSetStrategy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ordinal", wireType) + } + m.Ordinal = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ordinal |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *RollbackConfig) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -4076,6 +4303,56 @@ func (m *StatefulSetSpec) Unmarshal(dAtA []byte) error { } m.PodManagementPolicy = PodManagementPolicyType(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdateStrategy", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.UpdateStrategy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionHistoryLimit", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.RevisionHistoryLimit = &v default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -4165,6 +4442,233 @@ func (m *StatefulSetStatus) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReadyReplicas", wireType) + } + m.ReadyReplicas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReadyReplicas |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CurrentReplicas", wireType) + } + m.CurrentReplicas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CurrentReplicas |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedReplicas", wireType) + } + m.UpdatedReplicas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UpdatedReplicas |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CurrentRevision", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CurrentRevision = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdateRevision", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UpdateRevision = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StatefulSetUpdateStrategy) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StatefulSetUpdateStrategy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StatefulSetUpdateStrategy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = StatefulSetUpdateStrategyType(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Partition", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Partition == nil { + m.Partition = &PartitionStatefulSetStrategy{} + } + if err := m.Partition.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -4296,108 +4800,119 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 1647 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x58, 0xcf, 0x4f, 0x1b, 0xc7, - 0x17, 0x67, 0x8d, 0x0d, 0x66, 0x08, 0x26, 0x0c, 0x7c, 0xc1, 0x5f, 0x52, 0x19, 0xe4, 0x43, 0x42, - 0xaa, 0x64, 0xdd, 0x90, 0x34, 0x3f, 0xa0, 0x8a, 0x8a, 0x49, 0x9a, 0xa6, 0x82, 0x82, 0xc6, 0x10, - 0x35, 0x69, 0x2a, 0x65, 0xbc, 0x9e, 0x2c, 0x1b, 0xf6, 0x97, 0x76, 0xc6, 0x6e, 0x7c, 0xeb, 0xa5, - 0x87, 0x4a, 0x3d, 0xf4, 0x1f, 0xa8, 0xda, 0x73, 0x55, 0xa9, 0xff, 0x06, 0x6a, 0x2f, 0x51, 0x4f, - 0x51, 0x0f, 0xa8, 0x90, 0xbf, 0xa1, 0x97, 0x9c, 0xaa, 0x99, 0x9d, 0xfd, 0xe5, 0xb5, 0xc1, 0x50, - 0x95, 0x4b, 0x6f, 0xde, 0x79, 0xef, 0x7d, 0xde, 0x8f, 0x79, 0xef, 0xcd, 0x7b, 0x06, 0xb7, 0x76, - 0x6f, 0x53, 0xd5, 0x70, 0x2a, 0xbb, 0xcd, 0x3a, 0xf1, 0x6c, 0xc2, 0x08, 0xad, 0xb8, 0xbb, 0x7a, - 0x05, 0xbb, 0x06, 0xad, 0x60, 0xd7, 0xa5, 0x95, 0xd6, 0xb5, 0x3a, 0x61, 0xf8, 0x5a, 0x45, 0x27, - 0x36, 0xf1, 0x30, 0x23, 0x0d, 0xd5, 0xf5, 0x1c, 0xe6, 0xc0, 0x4b, 0xbe, 0xa0, 0x1a, 0x09, 0xaa, - 0xee, 0xae, 0xae, 0x72, 0x41, 0x95, 0x0b, 0xaa, 0x52, 0x70, 0xf6, 0xaa, 0x6e, 0xb0, 0x9d, 0x66, - 0x5d, 0xd5, 0x1c, 0xab, 0xa2, 0x3b, 0xba, 0x53, 0x11, 0xf2, 0xf5, 0xe6, 0x73, 0xf1, 0x25, 0x3e, - 0xc4, 0x2f, 0x1f, 0x77, 0xf6, 0x86, 0x34, 0x08, 0xbb, 0x86, 0x85, 0xb5, 0x1d, 0xc3, 0x26, 0x5e, - 0x3b, 0x32, 0xc9, 0x22, 0x0c, 0x57, 0x5a, 0x29, 0x6b, 0x66, 0x2b, 0xbd, 0xa4, 0xbc, 0xa6, 0xcd, - 0x0c, 0x8b, 0xa4, 0x04, 0x6e, 0x1e, 0x27, 0x40, 0xb5, 0x1d, 0x62, 0xe1, 0x94, 0xdc, 0xf5, 0x5e, - 0x72, 0x4d, 0x66, 0x98, 0x15, 0xc3, 0x66, 0x94, 0x79, 0x29, 0xa1, 0x2b, 0x3d, 0x83, 0xdc, 0xcd, - 0x97, 0x3b, 0x47, 0x5c, 0x89, 0xeb, 0x98, 0x86, 0xd6, 0xee, 0x75, 0x29, 0xe5, 0xbf, 0x14, 0x00, - 0x57, 0x1d, 0x9b, 0x79, 0x8e, 0x69, 0x12, 0x0f, 0x91, 0x96, 0x41, 0x0d, 0xc7, 0x86, 0xcf, 0x40, - 0x9e, 0x07, 0xae, 0x81, 0x19, 0x2e, 0x2a, 0xf3, 0xca, 0xc2, 0xe8, 0xe2, 0x7b, 0xaa, 0xbc, 0xbe, - 0xb8, 0x1f, 0xd1, 0x05, 0x72, 0x6e, 0xb5, 0x75, 0x4d, 0xdd, 0xa8, 0xbf, 0x20, 0x1a, 0x5b, 0x27, - 0x0c, 0x57, 0xe1, 0xde, 0xfe, 0xdc, 0xc0, 0xe1, 0xfe, 0x1c, 0x88, 0xce, 0x50, 0x88, 0x0a, 0x37, - 0x40, 0x56, 0xa0, 0x67, 0x04, 0xfa, 0xd5, 0x9e, 0xe8, 0x32, 0xba, 0x2a, 0xc2, 0x5f, 0xde, 0x7f, - 0xc9, 0x88, 0xcd, 0xcd, 0xab, 0x9e, 0x93, 0xd0, 0xd9, 0x7b, 0x98, 0x61, 0x24, 0x80, 0xe0, 0x15, - 0x90, 0xf7, 0xa4, 0xf9, 0xc5, 0xc1, 0x79, 0x65, 0x61, 0xb0, 0x7a, 0x5e, 0x72, 0xe5, 0x03, 0xb7, - 0x50, 0xc8, 0x51, 0x7e, 0xad, 0x80, 0xe9, 0xb4, 0xdf, 0x6b, 0x06, 0x65, 0xf0, 0x69, 0xca, 0x77, - 0xb5, 0x3f, 0xdf, 0xb9, 0xb4, 0xf0, 0x3c, 0x54, 0x1c, 0x9c, 0xc4, 0xfc, 0x7e, 0x06, 0x72, 0x06, - 0x23, 0x16, 0x2d, 0x66, 0xe6, 0x07, 0x17, 0x46, 0x17, 0x97, 0xd5, 0x3e, 0xab, 0x42, 0x4d, 0x5b, - 0x5b, 0x1d, 0x93, 0x7a, 0x72, 0x0f, 0x39, 0x22, 0xf2, 0x81, 0xcb, 0x3f, 0x67, 0x00, 0xb8, 0x47, - 0x5c, 0xd3, 0x69, 0x5b, 0xc4, 0x66, 0x67, 0x70, 0x95, 0x8f, 0x41, 0x96, 0xba, 0x44, 0x93, 0x57, - 0x79, 0xab, 0x6f, 0x8f, 0x22, 0x23, 0x6b, 0x2e, 0xd1, 0xa2, 0x4b, 0xe5, 0x5f, 0x48, 0x40, 0x42, - 0x0c, 0x86, 0x28, 0xc3, 0xac, 0x49, 0xc5, 0x95, 0x8e, 0x2e, 0xde, 0x39, 0x0d, 0xb8, 0x00, 0xa8, - 0x16, 0x24, 0xfc, 0x90, 0xff, 0x8d, 0x24, 0x70, 0xf9, 0x60, 0x10, 0x4c, 0x46, 0xcc, 0xab, 0x8e, - 0xdd, 0x30, 0x18, 0x2f, 0x81, 0x65, 0x90, 0x65, 0x6d, 0x97, 0x88, 0x98, 0x8d, 0x54, 0x2f, 0x05, - 0xc6, 0x6d, 0xb5, 0x5d, 0xf2, 0x76, 0x7f, 0x6e, 0xa6, 0x8b, 0x08, 0x27, 0x21, 0x21, 0x04, 0x1f, - 0x85, 0x76, 0x67, 0x84, 0xf8, 0xdd, 0xa4, 0xf2, 0xb7, 0xfb, 0x73, 0x47, 0x56, 0xb8, 0x1a, 0x62, - 0x26, 0x8d, 0x85, 0x17, 0xc1, 0x90, 0x47, 0x30, 0x75, 0xec, 0x62, 0x56, 0xe0, 0x86, 0x4e, 0x21, - 0x71, 0x8a, 0x24, 0x15, 0x5e, 0x06, 0xc3, 0x16, 0xa1, 0x14, 0xeb, 0xa4, 0x98, 0x13, 0x8c, 0xe3, - 0x92, 0x71, 0x78, 0xdd, 0x3f, 0x46, 0x01, 0x1d, 0xbe, 0x00, 0x05, 0x13, 0x53, 0xb6, 0xed, 0x36, - 0x30, 0x23, 0x5b, 0x86, 0x45, 0x8a, 0x43, 0x22, 0xd4, 0xef, 0xf6, 0x97, 0x25, 0x5c, 0xa2, 0x3a, - 0x2d, 0xd1, 0x0b, 0x6b, 0x09, 0x24, 0xd4, 0x81, 0x0c, 0x5b, 0x00, 0xf2, 0x93, 0x2d, 0x0f, 0xdb, - 0xd4, 0x0f, 0x19, 0xd7, 0x37, 0x7c, 0x62, 0x7d, 0xb3, 0x52, 0x1f, 0x5c, 0x4b, 0xa1, 0xa1, 0x2e, - 0x1a, 0xca, 0x7b, 0x0a, 0x28, 0x44, 0x17, 0x76, 0x06, 0x55, 0xfe, 0x59, 0xb2, 0xca, 0xaf, 0x9f, - 0x22, 0x6d, 0x7b, 0x54, 0xf7, 0xb7, 0x83, 0x00, 0x46, 0x4c, 0xc8, 0x31, 0xcd, 0x3a, 0xd6, 0x76, - 0xe1, 0x3c, 0xc8, 0xda, 0xd8, 0x0a, 0xb2, 0x35, 0x2c, 0xa5, 0x4f, 0xb1, 0x45, 0x90, 0xa0, 0xc0, - 0x1f, 0x14, 0x00, 0x9b, 0xe2, 0x2a, 0x1a, 0x2b, 0xb6, 0xed, 0x30, 0xcc, 0xa3, 0x13, 0x18, 0x58, - 0x3b, 0x85, 0x81, 0x81, 0x6e, 0x75, 0x3b, 0x85, 0x7a, 0xdf, 0x66, 0x5e, 0x3b, 0xba, 0xa5, 0x34, - 0x03, 0xea, 0x62, 0x0a, 0xdc, 0x05, 0xc0, 0x93, 0x98, 0x5b, 0x8e, 0x2c, 0xf8, 0xfe, 0xbb, 0x49, - 0x60, 0xce, 0xaa, 0x63, 0x3f, 0x37, 0xf4, 0xa8, 0x65, 0xa1, 0x10, 0x12, 0xc5, 0xe0, 0x67, 0xef, - 0x83, 0x99, 0x1e, 0x76, 0xc3, 0xf3, 0x60, 0x70, 0x97, 0xb4, 0xfd, 0x50, 0x22, 0xfe, 0x13, 0x4e, - 0x81, 0x5c, 0x0b, 0x9b, 0x4d, 0xe2, 0x57, 0x33, 0xf2, 0x3f, 0x96, 0x32, 0xb7, 0x95, 0xf2, 0x1f, - 0xb9, 0x78, 0x66, 0xf1, 0xce, 0x05, 0x17, 0xf8, 0x43, 0xe4, 0x9a, 0x86, 0x86, 0xa9, 0xc0, 0xc8, - 0x55, 0xcf, 0xf9, 0x8f, 0x90, 0x7f, 0x86, 0x42, 0x2a, 0xfc, 0x02, 0xe4, 0x29, 0x31, 0x89, 0xc6, - 0x1c, 0x4f, 0x36, 0xcf, 0xeb, 0x7d, 0xe6, 0x20, 0xae, 0x13, 0xb3, 0x26, 0x45, 0x7d, 0xf8, 0xe0, - 0x0b, 0x85, 0x90, 0xf0, 0x73, 0x90, 0x67, 0xc4, 0x72, 0x4d, 0xcc, 0x88, 0x8c, 0xe6, 0xd5, 0xde, - 0xd1, 0xe4, 0xb0, 0x9b, 0x4e, 0x63, 0x4b, 0x0a, 0x88, 0x8e, 0x1c, 0x66, 0x78, 0x70, 0x8a, 0x42, - 0x40, 0x68, 0x80, 0x3c, 0x65, 0x7c, 0x92, 0xd0, 0xdb, 0xa2, 0x17, 0x9d, 0xe4, 0x29, 0x8b, 0xf7, - 0x66, 0x1f, 0x22, 0x52, 0x15, 0x9c, 0xa0, 0x10, 0x1e, 0xae, 0x80, 0x71, 0xcb, 0xb0, 0x11, 0xc1, - 0x8d, 0x76, 0x8d, 0x68, 0x8e, 0xdd, 0xa0, 0xa2, 0xa9, 0xe5, 0xaa, 0x33, 0x52, 0x68, 0x7c, 0x3d, - 0x49, 0x46, 0x9d, 0xfc, 0x70, 0x0d, 0x4c, 0x05, 0x4f, 0xff, 0xc7, 0x06, 0x65, 0x8e, 0xd7, 0x5e, - 0x33, 0x2c, 0x83, 0x89, 0x56, 0x97, 0xab, 0x16, 0x0f, 0xf7, 0xe7, 0xa6, 0x50, 0x17, 0x3a, 0xea, - 0x2a, 0xc5, 0xbb, 0xb0, 0x8b, 0x9b, 0x94, 0x34, 0x44, 0xeb, 0xca, 0x47, 0x5d, 0x78, 0x53, 0x9c, - 0x22, 0x49, 0x85, 0x7a, 0x22, 0xa1, 0xf3, 0xff, 0x2c, 0xa1, 0x0b, 0xbd, 0x93, 0x19, 0x6e, 0x83, - 0x19, 0xd7, 0x73, 0x74, 0x8f, 0x50, 0x7a, 0x8f, 0xe0, 0x86, 0x69, 0xd8, 0x24, 0x88, 0xd4, 0x88, - 0xf0, 0xf0, 0xc2, 0xe1, 0xfe, 0xdc, 0xcc, 0x66, 0x77, 0x16, 0xd4, 0x4b, 0xb6, 0xfc, 0x7b, 0x16, - 0x9c, 0xef, 0x7c, 0x47, 0xe1, 0x27, 0x00, 0x3a, 0x75, 0x4a, 0xbc, 0x16, 0x69, 0x3c, 0xf0, 0x87, - 0x49, 0x3e, 0x71, 0x29, 0x62, 0xe2, 0x0a, 0x2b, 0x7e, 0x23, 0xc5, 0x81, 0xba, 0x48, 0xf9, 0x33, - 0x9b, 0x2c, 0x95, 0x8c, 0x30, 0x34, 0x36, 0xb3, 0xa5, 0xca, 0x65, 0x05, 0x8c, 0xcb, 0xae, 0x11, - 0x10, 0x45, 0x5a, 0xc7, 0xf2, 0x60, 0x3b, 0x49, 0x46, 0x9d, 0xfc, 0xf0, 0x01, 0x98, 0xc0, 0x2d, - 0x6c, 0x98, 0xb8, 0x6e, 0x92, 0x10, 0x24, 0x2b, 0x40, 0xfe, 0x2f, 0x41, 0x26, 0x56, 0x3a, 0x19, - 0x50, 0x5a, 0x06, 0xae, 0x83, 0xc9, 0xa6, 0x9d, 0x86, 0xf2, 0xf3, 0xf2, 0x82, 0x84, 0x9a, 0xdc, - 0x4e, 0xb3, 0xa0, 0x6e, 0x72, 0xd0, 0x05, 0x40, 0x0b, 0x9e, 0x7c, 0x5a, 0x1c, 0x12, 0x3d, 0xf9, - 0x83, 0x53, 0xd4, 0x53, 0x38, 0x37, 0x44, 0xfd, 0x2f, 0x3c, 0xa2, 0x28, 0xa6, 0x03, 0x2e, 0x83, - 0x31, 0x8f, 0x57, 0x48, 0x68, 0xfa, 0xb0, 0x30, 0xfd, 0x7f, 0x52, 0x6c, 0x0c, 0xc5, 0x89, 0x28, - 0xc9, 0x0b, 0x97, 0x40, 0x41, 0x73, 0x4c, 0x53, 0x54, 0xc6, 0xaa, 0xd3, 0xb4, 0x99, 0x48, 0xee, - 0xc1, 0x2a, 0xe4, 0x33, 0xc0, 0x6a, 0x82, 0x82, 0x3a, 0x38, 0xcb, 0xbf, 0x29, 0xf1, 0x07, 0x2c, - 0x28, 0x77, 0xb8, 0x94, 0x18, 0xb7, 0x2e, 0x76, 0x8c, 0x5b, 0xd3, 0x69, 0x89, 0xd8, 0xb4, 0xd5, - 0x06, 0x63, 0xbc, 0x18, 0x0c, 0x5b, 0xf7, 0x13, 0x40, 0x36, 0xd3, 0x0f, 0x4f, 0x54, 0x6a, 0xa1, - 0x74, 0xec, 0x09, 0x9e, 0x10, 0x91, 0x88, 0x13, 0x51, 0x52, 0x53, 0xf9, 0x2e, 0x28, 0x24, 0xeb, - 0x34, 0xb1, 0x87, 0x28, 0xc7, 0xee, 0x21, 0x6f, 0x14, 0x30, 0xd3, 0x43, 0x3b, 0x34, 0x41, 0xc1, - 0xc2, 0x2f, 0x63, 0x39, 0x74, 0xec, 0xfc, 0xce, 0x57, 0x4a, 0xd5, 0x5f, 0x29, 0xd5, 0x87, 0x36, - 0xdb, 0xf0, 0x6a, 0xcc, 0x33, 0x6c, 0xdd, 0xbf, 0x97, 0xf5, 0x04, 0x16, 0xea, 0xc0, 0x86, 0x4f, - 0x40, 0xde, 0xc2, 0x2f, 0x6b, 0x4d, 0x4f, 0x0f, 0xe2, 0x77, 0x72, 0x3d, 0xe2, 0x25, 0x5a, 0x97, - 0x28, 0x28, 0xc4, 0x2b, 0x7f, 0x9f, 0x01, 0xb9, 0x9a, 0x86, 0x4d, 0x72, 0x06, 0xdb, 0xc8, 0x56, - 0x62, 0x1b, 0x59, 0xec, 0x3b, 0x07, 0x84, 0x7d, 0x3d, 0x17, 0x91, 0xa7, 0x1d, 0x8b, 0xc8, 0x8d, - 0x13, 0xe2, 0x1e, 0xbd, 0x83, 0xdc, 0x01, 0x23, 0xa1, 0xfa, 0x44, 0x53, 0x54, 0x8e, 0x6b, 0x8a, - 0xe5, 0x9f, 0x32, 0x60, 0x34, 0xa6, 0xe2, 0x64, 0xd2, 0xd0, 0x4d, 0x4c, 0x20, 0xbc, 0xeb, 0x54, - 0x4f, 0xe3, 0x98, 0x1a, 0x4c, 0x1f, 0xfe, 0xe0, 0x17, 0x3d, 0xe6, 0xe9, 0xa1, 0xe4, 0x2e, 0x28, - 0x30, 0xec, 0xe9, 0x84, 0x05, 0x34, 0x11, 0xd0, 0x91, 0x68, 0x85, 0xd8, 0x4a, 0x50, 0x51, 0x07, - 0xf7, 0xec, 0x32, 0x18, 0x4b, 0x28, 0x3b, 0xd1, 0xb4, 0xf6, 0x0b, 0x0f, 0x16, 0xc3, 0x8c, 0x3c, - 0x6f, 0x9a, 0x35, 0x72, 0x16, 0xbb, 0xf1, 0x93, 0x44, 0x36, 0xde, 0xee, 0x3f, 0xb8, 0x91, 0x95, - 0x3d, 0x73, 0xb2, 0xde, 0x91, 0x93, 0x4b, 0xa7, 0x42, 0x3f, 0x3a, 0x33, 0x7f, 0x55, 0xc0, 0x78, - 0x8c, 0xfb, 0x0c, 0x56, 0xa7, 0xc7, 0xc9, 0xd5, 0xe9, 0xc6, 0x69, 0x9c, 0xea, 0xb1, 0x3b, 0xfd, - 0x98, 0x4d, 0x38, 0xf3, 0x1f, 0x9a, 0xd6, 0xbf, 0x56, 0xc0, 0x54, 0xcb, 0x31, 0x9b, 0x16, 0x59, - 0x35, 0xb1, 0x61, 0x05, 0x1c, 0x7c, 0xf6, 0x39, 0x66, 0x3f, 0x15, 0x9a, 0x88, 0x47, 0x0d, 0xca, - 0x88, 0xcd, 0x1e, 0x45, 0x18, 0xd5, 0x77, 0xa4, 0xbe, 0xa9, 0x47, 0x5d, 0x80, 0x51, 0x57, 0x75, - 0xf0, 0x7d, 0x30, 0xca, 0x87, 0x40, 0x43, 0x23, 0x7c, 0x33, 0x95, 0xff, 0x4d, 0x4c, 0x4a, 0xa0, - 0xd1, 0x5a, 0x44, 0x42, 0x71, 0x3e, 0xb8, 0x03, 0x26, 0x5d, 0xa7, 0xb1, 0x8e, 0x6d, 0xac, 0x13, - 0xfe, 0x34, 0x6e, 0x8a, 0x3f, 0x35, 0xc5, 0xf4, 0x3e, 0x52, 0xbd, 0x19, 0x4c, 0x5b, 0x9b, 0x69, - 0x96, 0xb7, 0x7c, 0xec, 0x4d, 0x1f, 0x8b, 0xd9, 0xa1, 0x1b, 0x64, 0xf9, 0x1b, 0x05, 0x4c, 0xa4, - 0xaa, 0x03, 0x7e, 0x74, 0xc4, 0xcc, 0x3b, 0xfd, 0x6f, 0xcd, 0xbb, 0xd5, 0xcb, 0x7b, 0x07, 0xa5, - 0x81, 0x57, 0x07, 0xa5, 0x81, 0xd7, 0x07, 0xa5, 0x81, 0xaf, 0x0e, 0x4b, 0xca, 0xde, 0x61, 0x49, - 0x79, 0x75, 0x58, 0x52, 0xfe, 0x3c, 0x2c, 0x29, 0xdf, 0xbd, 0x29, 0x0d, 0x3c, 0x19, 0x96, 0xb9, - 0xff, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5d, 0x5b, 0xd8, 0xe7, 0x95, 0x17, 0x00, 0x00, + // 1818 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x58, 0x4f, 0x4f, 0x23, 0xc9, + 0x15, 0xa7, 0xfd, 0x07, 0x4c, 0xb1, 0x98, 0xa1, 0x20, 0xe0, 0x65, 0x37, 0x06, 0xf9, 0xb0, 0xcb, + 0x44, 0x4b, 0x3b, 0xc3, 0x4c, 0x76, 0x67, 0x20, 0x1a, 0x85, 0x66, 0xc8, 0x66, 0x22, 0x08, 0xa8, + 0x0c, 0xa3, 0xec, 0x64, 0x23, 0x6d, 0xb9, 0x5d, 0xd3, 0xf4, 0xd2, 0xff, 0xd4, 0x5d, 0x76, 0xc6, + 0xb7, 0x28, 0x52, 0x6e, 0x39, 0xe4, 0x0b, 0x44, 0xb9, 0x47, 0x91, 0xf2, 0x35, 0x50, 0x72, 0xc8, + 0x6a, 0x4f, 0xa3, 0x1c, 0x50, 0xf0, 0x7c, 0x84, 0x28, 0x97, 0x39, 0x45, 0x55, 0x5d, 0xfd, 0xdf, + 0x0d, 0xb6, 0xa3, 0x70, 0xc9, 0xcd, 0x5d, 0xef, 0xbd, 0xdf, 0x7b, 0x55, 0xf5, 0xde, 0xab, 0xdf, + 0x33, 0xf8, 0xec, 0xe2, 0xb1, 0x27, 0xeb, 0x76, 0xf3, 0xa2, 0xdb, 0x26, 0xae, 0x45, 0x28, 0xf1, + 0x9a, 0xce, 0x85, 0xd6, 0xc4, 0x8e, 0xee, 0x35, 0xb1, 0xe3, 0x78, 0xcd, 0xde, 0x83, 0x36, 0xa1, + 0xf8, 0x41, 0x53, 0x23, 0x16, 0x71, 0x31, 0x25, 0x1d, 0xd9, 0x71, 0x6d, 0x6a, 0xc3, 0x8f, 0x7d, + 0x43, 0x39, 0x32, 0x94, 0x9d, 0x0b, 0x4d, 0x66, 0x86, 0x32, 0x33, 0x94, 0x85, 0xe1, 0xda, 0x96, + 0xa6, 0xd3, 0xf3, 0x6e, 0x5b, 0x56, 0x6d, 0xb3, 0xa9, 0xd9, 0x9a, 0xdd, 0xe4, 0xf6, 0xed, 0xee, + 0x2b, 0xfe, 0xc5, 0x3f, 0xf8, 0x2f, 0x1f, 0x77, 0xed, 0x91, 0x08, 0x08, 0x3b, 0xba, 0x89, 0xd5, + 0x73, 0xdd, 0x22, 0x6e, 0x3f, 0x0a, 0xc9, 0x24, 0x14, 0x37, 0x7b, 0x99, 0x68, 0xd6, 0x9a, 0x79, + 0x56, 0x6e, 0xd7, 0xa2, 0xba, 0x49, 0x32, 0x06, 0x9f, 0xde, 0x66, 0xe0, 0xa9, 0xe7, 0xc4, 0xc4, + 0x19, 0xbb, 0x87, 0x79, 0x76, 0x5d, 0xaa, 0x1b, 0x4d, 0xdd, 0xa2, 0x1e, 0x75, 0x33, 0x46, 0x9f, + 0xe4, 0x1e, 0xf2, 0xb0, 0xbd, 0x3c, 0xb9, 0xe1, 0x4a, 0x1c, 0xdb, 0xd0, 0xd5, 0x7e, 0xde, 0xa5, + 0x34, 0xfe, 0x2d, 0x01, 0xb8, 0x6f, 0x5b, 0xd4, 0xb5, 0x0d, 0x83, 0xb8, 0x88, 0xf4, 0x74, 0x4f, + 0xb7, 0x2d, 0xf8, 0x15, 0xa8, 0xb0, 0x83, 0xeb, 0x60, 0x8a, 0x6b, 0xd2, 0x86, 0xb4, 0x39, 0xb7, + 0xfd, 0x7d, 0x59, 0x5c, 0x5f, 0x7c, 0x1f, 0xd1, 0x05, 0x32, 0x6d, 0xb9, 0xf7, 0x40, 0x3e, 0x6e, + 0x7f, 0x4d, 0x54, 0x7a, 0x44, 0x28, 0x56, 0xe0, 0xe5, 0xd5, 0xfa, 0xd4, 0xe0, 0x6a, 0x1d, 0x44, + 0x6b, 0x28, 0x44, 0x85, 0xc7, 0xa0, 0xc4, 0xd1, 0x0b, 0x1c, 0x7d, 0x2b, 0x17, 0x5d, 0x9c, 0xae, + 0x8c, 0xf0, 0xaf, 0x0e, 0x5e, 0x53, 0x62, 0xb1, 0xf0, 0x94, 0xf7, 0x04, 0x74, 0xe9, 0x19, 0xa6, + 0x18, 0x71, 0x20, 0xf8, 0x09, 0xa8, 0xb8, 0x22, 0xfc, 0x5a, 0x71, 0x43, 0xda, 0x2c, 0x2a, 0xf7, + 0x84, 0x56, 0x25, 0xd8, 0x16, 0x0a, 0x35, 0x1a, 0x6f, 0x24, 0xb0, 0x92, 0xdd, 0xf7, 0xa1, 0xee, + 0x51, 0xf8, 0x65, 0x66, 0xef, 0xf2, 0x68, 0x7b, 0x67, 0xd6, 0x7c, 0xe7, 0xa1, 0xe3, 0x60, 0x25, + 0xb6, 0xef, 0xaf, 0x40, 0x59, 0xa7, 0xc4, 0xf4, 0x6a, 0x85, 0x8d, 0xe2, 0xe6, 0xdc, 0xf6, 0xae, + 0x3c, 0x62, 0x55, 0xc8, 0xd9, 0x68, 0x95, 0x79, 0xe1, 0xa7, 0xfc, 0x9c, 0x21, 0x22, 0x1f, 0xb8, + 0xf1, 0xe7, 0x02, 0x00, 0xcf, 0x88, 0x63, 0xd8, 0x7d, 0x93, 0x58, 0xf4, 0x0e, 0xae, 0xf2, 0x0b, + 0x50, 0xf2, 0x1c, 0xa2, 0x8a, 0xab, 0xfc, 0x6c, 0xe4, 0x1d, 0x45, 0x41, 0xb6, 0x1c, 0xa2, 0x46, + 0x97, 0xca, 0xbe, 0x10, 0x87, 0x84, 0x18, 0x4c, 0x7b, 0x14, 0xd3, 0xae, 0xc7, 0xaf, 0x74, 0x6e, + 0xfb, 0xc9, 0x24, 0xe0, 0x1c, 0x40, 0xa9, 0x0a, 0xf8, 0x69, 0xff, 0x1b, 0x09, 0xe0, 0xc6, 0x75, + 0x11, 0x2c, 0x45, 0xca, 0xfb, 0xb6, 0xd5, 0xd1, 0x29, 0x2b, 0x81, 0x5d, 0x50, 0xa2, 0x7d, 0x87, + 0xf0, 0x33, 0x9b, 0x55, 0x3e, 0x0e, 0x82, 0x3b, 0xed, 0x3b, 0xe4, 0xdd, 0xd5, 0xfa, 0xea, 0x10, + 0x13, 0x26, 0x42, 0xdc, 0x08, 0xbe, 0x08, 0xe3, 0x2e, 0x70, 0xf3, 0xa7, 0x49, 0xe7, 0xef, 0xae, + 0xd6, 0x6f, 0xac, 0x70, 0x39, 0xc4, 0x4c, 0x06, 0x0b, 0x3f, 0x02, 0xd3, 0x2e, 0xc1, 0x9e, 0x6d, + 0xd5, 0x4a, 0x1c, 0x37, 0xdc, 0x14, 0xe2, 0xab, 0x48, 0x48, 0xe1, 0x7d, 0x30, 0x63, 0x12, 0xcf, + 0xc3, 0x1a, 0xa9, 0x95, 0xb9, 0xe2, 0x82, 0x50, 0x9c, 0x39, 0xf2, 0x97, 0x51, 0x20, 0x87, 0x5f, + 0x83, 0xaa, 0x81, 0x3d, 0x7a, 0xe6, 0x74, 0x30, 0x25, 0xa7, 0xba, 0x49, 0x6a, 0xd3, 0xfc, 0xa8, + 0xbf, 0x37, 0x5a, 0x96, 0x30, 0x0b, 0x65, 0x45, 0xa0, 0x57, 0x0f, 0x13, 0x48, 0x28, 0x85, 0x0c, + 0x7b, 0x00, 0xb2, 0x95, 0x53, 0x17, 0x5b, 0x9e, 0x7f, 0x64, 0xcc, 0xdf, 0xcc, 0xd8, 0xfe, 0xd6, + 0x84, 0x3f, 0x78, 0x98, 0x41, 0x43, 0x43, 0x3c, 0x34, 0x2e, 0x25, 0x50, 0x8d, 0x2e, 0xec, 0x0e, + 0xaa, 0xfc, 0xe7, 0xc9, 0x2a, 0x7f, 0x38, 0x41, 0xda, 0xe6, 0x54, 0xf7, 0xef, 0x8a, 0x00, 0x46, + 0x4a, 0xc8, 0x36, 0x8c, 0x36, 0x56, 0x2f, 0xe0, 0x06, 0x28, 0x59, 0xd8, 0x0c, 0xb2, 0x35, 0x2c, + 0xa5, 0x9f, 0x61, 0x93, 0x20, 0x2e, 0x81, 0x7f, 0x94, 0x00, 0xec, 0xf2, 0xab, 0xe8, 0xec, 0x59, + 0x96, 0x4d, 0x31, 0x3b, 0x9d, 0x20, 0xc0, 0xd6, 0x04, 0x01, 0x06, 0xbe, 0xe5, 0xb3, 0x0c, 0xea, + 0x81, 0x45, 0xdd, 0x7e, 0x74, 0x4b, 0x59, 0x05, 0x34, 0x24, 0x14, 0x78, 0x01, 0x80, 0x2b, 0x30, + 0x4f, 0x6d, 0x51, 0xf0, 0xa3, 0x77, 0x93, 0x20, 0x9c, 0x7d, 0xdb, 0x7a, 0xa5, 0x6b, 0x51, 0xcb, + 0x42, 0x21, 0x24, 0x8a, 0xc1, 0xaf, 0x1d, 0x80, 0xd5, 0x9c, 0xb8, 0xe1, 0x3d, 0x50, 0xbc, 0x20, + 0x7d, 0xff, 0x28, 0x11, 0xfb, 0x09, 0x97, 0x41, 0xb9, 0x87, 0x8d, 0x2e, 0xf1, 0xab, 0x19, 0xf9, + 0x1f, 0x3b, 0x85, 0xc7, 0x52, 0xe3, 0x1f, 0xe5, 0x78, 0x66, 0xb1, 0xce, 0x05, 0x37, 0xd9, 0x43, + 0xe4, 0x18, 0xba, 0x8a, 0x3d, 0x8e, 0x51, 0x56, 0xde, 0xf3, 0x1f, 0x21, 0x7f, 0x0d, 0x85, 0x52, + 0xf8, 0x4b, 0x50, 0xf1, 0x88, 0x41, 0x54, 0x6a, 0xbb, 0xa2, 0x79, 0x3e, 0x1c, 0x31, 0x07, 0x71, + 0x9b, 0x18, 0x2d, 0x61, 0xea, 0xc3, 0x07, 0x5f, 0x28, 0x84, 0x84, 0xbf, 0x00, 0x15, 0x4a, 0x4c, + 0xc7, 0xc0, 0x94, 0x88, 0xd3, 0xdc, 0xca, 0x3f, 0x4d, 0x06, 0x7b, 0x62, 0x77, 0x4e, 0x85, 0x01, + 0xef, 0xc8, 0x61, 0x86, 0x07, 0xab, 0x28, 0x04, 0x84, 0x3a, 0xa8, 0x78, 0x94, 0x31, 0x09, 0xad, + 0xcf, 0x7b, 0xd1, 0x38, 0x4f, 0x59, 0xbc, 0x37, 0xfb, 0x10, 0x91, 0xab, 0x60, 0x05, 0x85, 0xf0, + 0x70, 0x0f, 0x2c, 0x98, 0xba, 0x85, 0x08, 0xee, 0xf4, 0x5b, 0x44, 0xb5, 0xad, 0x8e, 0xc7, 0x9b, + 0x5a, 0x59, 0x59, 0x15, 0x46, 0x0b, 0x47, 0x49, 0x31, 0x4a, 0xeb, 0xc3, 0x43, 0xb0, 0x1c, 0x3c, + 0xfd, 0x3f, 0xd1, 0x3d, 0x6a, 0xbb, 0xfd, 0x43, 0xdd, 0xd4, 0x29, 0x6f, 0x75, 0x65, 0xa5, 0x36, + 0xb8, 0x5a, 0x5f, 0x46, 0x43, 0xe4, 0x68, 0xa8, 0x15, 0xeb, 0xc2, 0x0e, 0xee, 0x7a, 0xa4, 0xc3, + 0x5b, 0x57, 0x25, 0xea, 0xc2, 0x27, 0x7c, 0x15, 0x09, 0x29, 0xd4, 0x12, 0x09, 0x5d, 0xf9, 0xef, + 0x12, 0xba, 0x9a, 0x9f, 0xcc, 0xf0, 0x0c, 0xac, 0x3a, 0xae, 0xad, 0xb9, 0xc4, 0xf3, 0x9e, 0x11, + 0xdc, 0x31, 0x74, 0x8b, 0x04, 0x27, 0x35, 0xcb, 0x77, 0xf8, 0xc1, 0xe0, 0x6a, 0x7d, 0xf5, 0x64, + 0xb8, 0x0a, 0xca, 0xb3, 0x6d, 0x7c, 0x5b, 0x02, 0xf7, 0xd2, 0xef, 0x28, 0xfc, 0x29, 0x80, 0x76, + 0xdb, 0x23, 0x6e, 0x8f, 0x74, 0x3e, 0xf7, 0xc9, 0x24, 0x63, 0x5c, 0x12, 0x67, 0x5c, 0x61, 0xc5, + 0x1f, 0x67, 0x34, 0xd0, 0x10, 0x2b, 0x9f, 0xb3, 0x89, 0x52, 0x29, 0xf0, 0x40, 0x63, 0x9c, 0x2d, + 0x53, 0x2e, 0x7b, 0x60, 0x41, 0x74, 0x8d, 0x40, 0xc8, 0xd3, 0x3a, 0x96, 0x07, 0x67, 0x49, 0x31, + 0x4a, 0xeb, 0xc3, 0xcf, 0xc1, 0x22, 0xee, 0x61, 0xdd, 0xc0, 0x6d, 0x83, 0x84, 0x20, 0x25, 0x0e, + 0xf2, 0xbe, 0x00, 0x59, 0xdc, 0x4b, 0x2b, 0xa0, 0xac, 0x0d, 0x3c, 0x02, 0x4b, 0x5d, 0x2b, 0x0b, + 0xe5, 0xe7, 0xe5, 0x07, 0x02, 0x6a, 0xe9, 0x2c, 0xab, 0x82, 0x86, 0xd9, 0x41, 0x07, 0x00, 0x35, + 0x78, 0xf2, 0xbd, 0xda, 0x34, 0xef, 0xc9, 0x3f, 0x9c, 0xa0, 0x9e, 0x42, 0xde, 0x10, 0xf5, 0xbf, + 0x70, 0xc9, 0x43, 0x31, 0x1f, 0x70, 0x17, 0xcc, 0xbb, 0xac, 0x42, 0xc2, 0xd0, 0x67, 0x78, 0xe8, + 0xdf, 0x11, 0x66, 0xf3, 0x28, 0x2e, 0x44, 0x49, 0x5d, 0xb8, 0x03, 0xaa, 0xaa, 0x6d, 0x18, 0xbc, + 0x32, 0xf6, 0xed, 0xae, 0x45, 0x79, 0x72, 0x17, 0x15, 0xc8, 0x38, 0xc0, 0x7e, 0x42, 0x82, 0x52, + 0x9a, 0x8d, 0xbf, 0x49, 0xf1, 0x07, 0x2c, 0x28, 0x77, 0xb8, 0x93, 0xa0, 0x5b, 0x1f, 0xa5, 0xe8, + 0xd6, 0x4a, 0xd6, 0x22, 0xc6, 0xb6, 0xfa, 0x60, 0x9e, 0x15, 0x83, 0x6e, 0x69, 0x7e, 0x02, 0x88, + 0x66, 0xfa, 0xa3, 0xb1, 0x4a, 0x2d, 0xb4, 0x8e, 0x3d, 0xc1, 0x8b, 0xfc, 0x24, 0xe2, 0x42, 0x94, + 0xf4, 0xd4, 0x78, 0x0e, 0x3e, 0x3c, 0xc1, 0x2e, 0x0d, 0xb9, 0x1a, 0x79, 0xd5, 0x35, 0x5a, 0x24, + 0xda, 0xd6, 0x7d, 0x30, 0x63, 0xbb, 0x1d, 0xdd, 0xc2, 0x86, 0x78, 0x0b, 0x42, 0x22, 0x76, 0xec, + 0x2f, 0xa3, 0x40, 0xde, 0x78, 0x0a, 0xaa, 0xc9, 0x92, 0x4f, 0x8c, 0x34, 0xd2, 0xad, 0x23, 0xcd, + 0x5b, 0x09, 0xac, 0xe6, 0x6c, 0x04, 0x1a, 0xa0, 0x6a, 0xe2, 0xd7, 0xb1, 0x74, 0xbc, 0x75, 0x14, + 0x60, 0xd3, 0xa9, 0xec, 0x4f, 0xa7, 0xf2, 0x73, 0x8b, 0x1e, 0xbb, 0x2d, 0xea, 0xea, 0x96, 0xe6, + 0x5f, 0xf1, 0x51, 0x02, 0x0b, 0xa5, 0xb0, 0xe1, 0x4b, 0x50, 0x31, 0xf1, 0xeb, 0x56, 0xd7, 0xd5, + 0x82, 0xab, 0x18, 0xdf, 0x0f, 0x7f, 0xd4, 0x8e, 0x04, 0x0a, 0x0a, 0xf1, 0x1a, 0x7f, 0x28, 0x80, + 0x72, 0x4b, 0xc5, 0x06, 0xb9, 0x83, 0xc1, 0xe6, 0x34, 0x31, 0xd8, 0x6c, 0x8f, 0x9c, 0x4e, 0x3c, + 0xbe, 0xdc, 0x99, 0xe6, 0xcb, 0xd4, 0x4c, 0xf3, 0x68, 0x4c, 0xdc, 0x9b, 0xc7, 0x99, 0x27, 0x60, + 0x36, 0x74, 0x9f, 0xe8, 0xaf, 0xd2, 0x6d, 0xfd, 0xb5, 0xf1, 0xa7, 0x02, 0x98, 0x8b, 0xb9, 0x18, + 0xcf, 0x1a, 0x3a, 0x09, 0x32, 0xc3, 0x1a, 0x98, 0x32, 0xc9, 0xc6, 0xe4, 0x80, 0xc8, 0xf8, 0x1c, + 0x32, 0xe2, 0x05, 0x59, 0x7e, 0xf3, 0x14, 0x54, 0x29, 0x76, 0x35, 0x42, 0x03, 0x19, 0x3f, 0xd0, + 0xd9, 0x68, 0x1a, 0x39, 0x4d, 0x48, 0x51, 0x4a, 0x7b, 0x6d, 0x17, 0xcc, 0x27, 0x9c, 0x8d, 0x45, + 0xfc, 0xfe, 0xc2, 0x0e, 0x2b, 0x2a, 0xf8, 0x3b, 0xc8, 0xc6, 0x97, 0x89, 0x6c, 0x7c, 0x3c, 0xfa, + 0xe1, 0xc6, 0xda, 0x52, 0x5e, 0x4e, 0xb6, 0x53, 0x39, 0xb9, 0x33, 0x11, 0xfa, 0xcd, 0x99, 0xf9, + 0x57, 0x09, 0x2c, 0xc4, 0xb4, 0xef, 0x60, 0x0a, 0xfb, 0x22, 0x39, 0x85, 0x3d, 0x9a, 0x64, 0x53, + 0x39, 0x63, 0xd8, 0xbf, 0xca, 0x89, 0xcd, 0xfc, 0x1f, 0x11, 0xff, 0xdf, 0x4a, 0x60, 0xb9, 0x67, + 0x1b, 0x5d, 0x93, 0xec, 0x1b, 0x58, 0x37, 0x03, 0x0d, 0x46, 0xa3, 0x6e, 0x19, 0x75, 0xb9, 0x27, + 0xe2, 0x7a, 0xba, 0x47, 0x89, 0x45, 0x5f, 0x44, 0x18, 0xca, 0x87, 0xc2, 0xdf, 0xf2, 0x8b, 0x21, + 0xc0, 0x68, 0xa8, 0x3b, 0xf8, 0x03, 0x30, 0xc7, 0xf8, 0xa4, 0xae, 0x12, 0x36, 0xe4, 0x8a, 0xbf, + 0x39, 0x96, 0x04, 0xd0, 0x5c, 0x2b, 0x12, 0xa1, 0xb8, 0x1e, 0x3c, 0x07, 0x4b, 0x8e, 0xdd, 0x39, + 0xc2, 0x16, 0xd6, 0x08, 0x7b, 0x1a, 0x4f, 0xf8, 0xff, 0xa3, 0x7c, 0x10, 0x98, 0x55, 0x3e, 0x0d, + 0x88, 0xdb, 0x49, 0x56, 0xe5, 0x1d, 0x63, 0xd0, 0xd9, 0x65, 0x4e, 0x43, 0x86, 0x41, 0xc2, 0xdf, + 0x48, 0xa0, 0xea, 0xf3, 0xcf, 0x80, 0x0d, 0x88, 0x7f, 0x3a, 0x94, 0x49, 0xf2, 0xf0, 0x2c, 0x81, + 0x14, 0xf5, 0xb8, 0xe4, 0x3a, 0x4a, 0x79, 0xcc, 0x1d, 0x7c, 0x2a, 0x93, 0x0c, 0x3e, 0x8d, 0xbf, + 0x17, 0xc1, 0x62, 0xa6, 0xe0, 0xe1, 0x8f, 0x6f, 0x98, 0x08, 0x56, 0xfe, 0x67, 0xd3, 0x40, 0x86, + 0xc0, 0x16, 0xc7, 0x20, 0xb0, 0x7b, 0x60, 0x41, 0xed, 0xba, 0x2e, 0xb1, 0x68, 0x6a, 0x0a, 0x08, + 0x47, 0x89, 0xfd, 0xa4, 0x18, 0xa5, 0xf5, 0x87, 0x4d, 0x23, 0xe5, 0x31, 0xa7, 0x91, 0x78, 0x14, + 0x82, 0xe6, 0xf9, 0x79, 0x98, 0x8d, 0x42, 0xb0, 0xbd, 0xb4, 0x3e, 0x7b, 0x03, 0x7d, 0xd4, 0x10, + 0x61, 0x26, 0xf9, 0x06, 0x9e, 0x25, 0xa4, 0x28, 0xa5, 0xdd, 0xf8, 0x56, 0x02, 0xef, 0xe7, 0x66, + 0x19, 0xdc, 0x4b, 0x90, 0xf2, 0xad, 0x14, 0x29, 0xff, 0x6e, 0xae, 0x61, 0x8c, 0x9b, 0xbb, 0x60, + 0xd6, 0x09, 0x08, 0xb2, 0xe8, 0x75, 0x07, 0x23, 0xe7, 0xff, 0x4d, 0xd4, 0x5a, 0x99, 0x1f, 0x5c, + 0xad, 0xcf, 0x86, 0x1a, 0x28, 0x72, 0xa3, 0xdc, 0xbf, 0xbc, 0xae, 0x4f, 0x7d, 0x73, 0x5d, 0x9f, + 0x7a, 0x73, 0x5d, 0x9f, 0xfa, 0xf5, 0xa0, 0x2e, 0x5d, 0x0e, 0xea, 0xd2, 0x37, 0x83, 0xba, 0xf4, + 0xcf, 0x41, 0x5d, 0xfa, 0xfd, 0xdb, 0xfa, 0xd4, 0xcb, 0x19, 0xe1, 0xe1, 0x3f, 0x01, 0x00, 0x00, + 0xff, 0xff, 0x4f, 0x0f, 0xec, 0xcc, 0xce, 0x1a, 0x00, 0x00, } diff --git a/pkg/apis/apps/v1beta1/generated.proto b/pkg/apis/apps/v1beta1/generated.proto index 869d435dbd4..f0f2aa4be7c 100644 --- a/pkg/apis/apps/v1beta1/generated.proto +++ b/pkg/apis/apps/v1beta1/generated.proto @@ -224,6 +224,14 @@ message DeploymentStrategy { optional RollingUpdateDeployment rollingUpdate = 2; } +// PartitionStatefulSetStrategy contains the parameters used with the +// PartitionStatefulSetStrategyType. +message PartitionStatefulSetStrategy { + // Ordinal indicates the ordinal at which the StatefulSet should be + // partitioned. + optional int32 ordinal = 1; +} + message RollbackConfig { // The revision to rollback to. If set to 0, rollback to the last revision. // +optional @@ -378,15 +386,60 @@ message StatefulSetSpec { // all pods at once. // +optional optional string podManagementPolicy = 6; + + // updateStrategy indicates the StatefulSetUpdateStrategy that will be + // employed to update Pods in the StatefulSet when a revision is made to + // Template. + optional StatefulSetUpdateStrategy updateStrategy = 7; + + // revisionHistoryLimit is the maximum number of revisions that will + // be maintained in the StatefulSet's revision history. The revision history + // consists of all revisions not represented by a currently applied + // StatefulSetSpec version. The default value is 10. + optional int32 revisionHistoryLimit = 8; } // StatefulSetStatus represents the current state of a StatefulSet. message StatefulSetStatus { - // observedGeneration is the most recent generation observed by this StatefulSet. + // observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the + // StatefulSet's generation, which is updated on mutation by the API Server. // +optional optional int64 observedGeneration = 1; - // replicas is the number of actual replicas. + // replicas is the number of Pods created by the StatefulSet controller. optional int32 replicas = 2; + + // readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition. + optional int32 readyReplicas = 3; + + // currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by currentRevision. + optional int32 currentReplicas = 4; + + // updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by updateRevision. + optional int32 updatedReplicas = 5; + + // currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the + // sequence [0,currentReplicas). + optional string currentRevision = 6; + + // updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence + // [replicas-updatedReplicas,replicas) + optional string updateRevision = 7; +} + +// StatefulSetUpdateStrategy indicates the strategy that the StatefulSet +// controller will use to perform updates. It includes any additional parameters +// necessary to perform the update for the indicated strategy. +message StatefulSetUpdateStrategy { + // Type indicates the type of the StatefulSetUpdateStrategy. + optional string type = 1; + + // Partition is used to communicate the ordinal at which to partition + // the StatefulSet when Type is PartitionStatefulSetStrategyType. This + // value must be set when Type is PartitionStatefulSetStrategyType, + // and it must be nil otherwise. + optional PartitionStatefulSetStrategy partition = 2; } diff --git a/pkg/apis/apps/v1beta1/types.generated.go b/pkg/apis/apps/v1beta1/types.generated.go index c0c47f56fcf..f7ad602414c 100644 --- a/pkg/apis/apps/v1beta1/types.generated.go +++ b/pkg/apis/apps/v1beta1/types.generated.go @@ -1368,6 +1368,423 @@ func (x *PodManagementPolicyType) CodecDecodeSelf(d *codec1978.Decoder) { } } +func (x *StatefulSetUpdateStrategy) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [2]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = x.Type != "" + yyq2[1] = x.Partition != nil + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(2) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + x.Type.CodecEncodeSelf(e) + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("type")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + x.Type.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + if x.Partition == nil { + r.EncodeNil() + } else { + x.Partition.CodecEncodeSelf(e) + } + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("partition")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Partition == nil { + r.EncodeNil() + } else { + x.Partition.CodecEncodeSelf(e) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *StatefulSetUpdateStrategy) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *StatefulSetUpdateStrategy) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "type": + if r.TryDecodeAsNil() { + x.Type = "" + } else { + yyv4 := &x.Type + yyv4.CodecDecodeSelf(d) + } + case "partition": + if r.TryDecodeAsNil() { + if x.Partition != nil { + x.Partition = nil + } + } else { + if x.Partition == nil { + x.Partition = new(PartitionStatefulSetStrategy) + } + x.Partition.CodecDecodeSelf(d) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *StatefulSetUpdateStrategy) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj6 int + var yyb6 bool + var yyhl6 bool = l >= 0 + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Type = "" + } else { + yyv7 := &x.Type + yyv7.CodecDecodeSelf(d) + } + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.Partition != nil { + x.Partition = nil + } + } else { + if x.Partition == nil { + x.Partition = new(PartitionStatefulSetStrategy) + } + x.Partition.CodecDecodeSelf(d) + } + for { + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj6-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x StatefulSetUpdateStrategyType) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x)) + } +} + +func (x *StatefulSetUpdateStrategyType) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + *((*string)(x)) = r.DecodeString() + } +} + +func (x *PartitionStatefulSetStrategy) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [1]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(1) + } else { + yynn2 = 1 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym4 := z.EncBinary() + _ = yym4 + if false { + } else { + r.EncodeInt(int64(x.Ordinal)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("ordinal")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + r.EncodeInt(int64(x.Ordinal)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *PartitionStatefulSetStrategy) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *PartitionStatefulSetStrategy) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "ordinal": + if r.TryDecodeAsNil() { + x.Ordinal = 0 + } else { + yyv4 := &x.Ordinal + yym5 := z.DecBinary() + _ = yym5 + if false { + } else { + *((*int32)(yyv4)) = int32(r.DecodeInt(32)) + } + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *PartitionStatefulSetStrategy) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj6 int + var yyb6 bool + var yyhl6 bool = l >= 0 + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Ordinal = 0 + } else { + yyv7 := &x.Ordinal + yym8 := z.DecBinary() + _ = yym8 + if false { + } else { + *((*int32)(yyv7)) = int32(r.DecodeInt(32)) + } + } + for { + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj6-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + func (x *StatefulSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -1382,16 +1799,18 @@ func (x *StatefulSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [6]bool + var yyq2 [8]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[0] = x.Replicas != nil yyq2[1] = x.Selector != nil yyq2[3] = len(x.VolumeClaimTemplates) != 0 yyq2[5] = x.PodManagementPolicy != "" + yyq2[6] = true + yyq2[7] = x.RevisionHistoryLimit != nil var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(6) + r.EncodeArrayStart(8) } else { yynn2 = 2 for _, b := range yyq2 { @@ -1550,6 +1969,58 @@ func (x *StatefulSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { x.PodManagementPolicy.CodecEncodeSelf(e) } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[6] { + yy26 := &x.UpdateStrategy + yy26.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[6] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("updateStrategy")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy28 := &x.UpdateStrategy + yy28.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[7] { + if x.RevisionHistoryLimit == nil { + r.EncodeNil() + } else { + yy31 := *x.RevisionHistoryLimit + yym32 := z.EncBinary() + _ = yym32 + if false { + } else { + r.EncodeInt(int64(yy31)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[7] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("revisionHistoryLimit")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.RevisionHistoryLimit == nil { + r.EncodeNil() + } else { + yy33 := *x.RevisionHistoryLimit + yym34 := z.EncBinary() + _ = yym34 + if false { + } else { + r.EncodeInt(int64(yy33)) + } + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -1682,6 +2153,29 @@ func (x *StatefulSetSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { yyv13 := &x.PodManagementPolicy yyv13.CodecDecodeSelf(d) } + case "updateStrategy": + if r.TryDecodeAsNil() { + x.UpdateStrategy = StatefulSetUpdateStrategy{} + } else { + yyv14 := &x.UpdateStrategy + yyv14.CodecDecodeSelf(d) + } + case "revisionHistoryLimit": + if r.TryDecodeAsNil() { + if x.RevisionHistoryLimit != nil { + x.RevisionHistoryLimit = nil + } + } else { + if x.RevisionHistoryLimit == nil { + x.RevisionHistoryLimit = new(int32) + } + yym16 := z.DecBinary() + _ = yym16 + if false { + } else { + *((*int32)(x.RevisionHistoryLimit)) = int32(r.DecodeInt(32)) + } + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -1693,16 +2187,16 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj14 int - var yyb14 bool - var yyhl14 bool = l >= 0 - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + var yyj17 int + var yyb17 bool + var yyhl17 bool = l >= 0 + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1715,20 +2209,20 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if x.Replicas == nil { x.Replicas = new(int32) } - yym16 := z.DecBinary() - _ = yym16 + yym19 := z.DecBinary() + _ = yym19 if false { } else { *((*int32)(x.Replicas)) = int32(r.DecodeInt(32)) } } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1741,21 +2235,21 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if x.Selector == nil { x.Selector = new(pkg1_v1.LabelSelector) } - yym18 := z.DecBinary() - _ = yym18 + yym21 := z.DecBinary() + _ = yym21 if false { } else if z.HasExtensions() && z.DecExt(x.Selector) { } else { z.DecFallback(x.Selector, false) } } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1763,16 +2257,16 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.Template = pkg3_v1.PodTemplateSpec{} } else { - yyv19 := &x.Template - yyv19.CodecDecodeSelf(d) + yyv22 := &x.Template + yyv22.CodecDecodeSelf(d) } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1780,21 +2274,21 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.VolumeClaimTemplates = nil } else { - yyv20 := &x.VolumeClaimTemplates - yym21 := z.DecBinary() - _ = yym21 + yyv23 := &x.VolumeClaimTemplates + yym24 := z.DecBinary() + _ = yym24 if false { } else { - h.decSlicev1_PersistentVolumeClaim((*[]pkg3_v1.PersistentVolumeClaim)(yyv20), d) + h.decSlicev1_PersistentVolumeClaim((*[]pkg3_v1.PersistentVolumeClaim)(yyv23), d) } } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1802,21 +2296,21 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.ServiceName = "" } else { - yyv22 := &x.ServiceName - yym23 := z.DecBinary() - _ = yym23 + yyv25 := &x.ServiceName + yym26 := z.DecBinary() + _ = yym26 if false { } else { - *((*string)(yyv22)) = r.DecodeString() + *((*string)(yyv25)) = r.DecodeString() } } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1824,21 +2318,64 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.PodManagementPolicy = "" } else { - yyv24 := &x.PodManagementPolicy - yyv24.CodecDecodeSelf(d) + yyv27 := &x.PodManagementPolicy + yyv27.CodecDecodeSelf(d) + } + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l + } else { + yyb17 = r.CheckBreak() + } + if yyb17 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.UpdateStrategy = StatefulSetUpdateStrategy{} + } else { + yyv28 := &x.UpdateStrategy + yyv28.CodecDecodeSelf(d) + } + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l + } else { + yyb17 = r.CheckBreak() + } + if yyb17 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.RevisionHistoryLimit != nil { + x.RevisionHistoryLimit = nil + } + } else { + if x.RevisionHistoryLimit == nil { + x.RevisionHistoryLimit = new(int32) + } + yym30 := z.DecBinary() + _ = yym30 + if false { + } else { + *((*int32)(x.RevisionHistoryLimit)) = int32(r.DecodeInt(32)) + } } for { - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj14-1, "") + z.DecStructFieldNotFound(yyj17-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -1857,13 +2394,18 @@ func (x *StatefulSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [2]bool + var yyq2 [7]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[0] = x.ObservedGeneration != nil + yyq2[2] = x.ReadyReplicas != 0 + yyq2[3] = x.CurrentReplicas != 0 + yyq2[4] = x.UpdatedReplicas != 0 + yyq2[5] = x.CurrentRevision != "" + yyq2[6] = x.UpdateRevision != "" var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(2) + r.EncodeArrayStart(7) } else { yynn2 = 1 for _, b := range yyq2 { @@ -1928,6 +2470,131 @@ func (x *StatefulSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { r.EncodeInt(int64(x.Replicas)) } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym12 := z.EncBinary() + _ = yym12 + if false { + } else { + r.EncodeInt(int64(x.ReadyReplicas)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("readyReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym13 := z.EncBinary() + _ = yym13 + if false { + } else { + r.EncodeInt(int64(x.ReadyReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yym15 := z.EncBinary() + _ = yym15 + if false { + } else { + r.EncodeInt(int64(x.CurrentReplicas)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("currentReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym16 := z.EncBinary() + _ = yym16 + if false { + } else { + r.EncodeInt(int64(x.CurrentReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + yym18 := z.EncBinary() + _ = yym18 + if false { + } else { + r.EncodeInt(int64(x.UpdatedReplicas)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("updatedReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym19 := z.EncBinary() + _ = yym19 + if false { + } else { + r.EncodeInt(int64(x.UpdatedReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[5] { + yym21 := z.EncBinary() + _ = yym21 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.CurrentRevision)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[5] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("currentRevision")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym22 := z.EncBinary() + _ = yym22 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.CurrentRevision)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[6] { + yym24 := z.EncBinary() + _ = yym24 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.UpdateRevision)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[6] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("updateRevision")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym25 := z.EncBinary() + _ = yym25 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.UpdateRevision)) + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -2017,6 +2684,66 @@ func (x *StatefulSetStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) *((*int32)(yyv6)) = int32(r.DecodeInt(32)) } } + case "readyReplicas": + if r.TryDecodeAsNil() { + x.ReadyReplicas = 0 + } else { + yyv8 := &x.ReadyReplicas + yym9 := z.DecBinary() + _ = yym9 + if false { + } else { + *((*int32)(yyv8)) = int32(r.DecodeInt(32)) + } + } + case "currentReplicas": + if r.TryDecodeAsNil() { + x.CurrentReplicas = 0 + } else { + yyv10 := &x.CurrentReplicas + yym11 := z.DecBinary() + _ = yym11 + if false { + } else { + *((*int32)(yyv10)) = int32(r.DecodeInt(32)) + } + } + case "updatedReplicas": + if r.TryDecodeAsNil() { + x.UpdatedReplicas = 0 + } else { + yyv12 := &x.UpdatedReplicas + yym13 := z.DecBinary() + _ = yym13 + if false { + } else { + *((*int32)(yyv12)) = int32(r.DecodeInt(32)) + } + } + case "currentRevision": + if r.TryDecodeAsNil() { + x.CurrentRevision = "" + } else { + yyv14 := &x.CurrentRevision + yym15 := z.DecBinary() + _ = yym15 + if false { + } else { + *((*string)(yyv14)) = r.DecodeString() + } + } + case "updateRevision": + if r.TryDecodeAsNil() { + x.UpdateRevision = "" + } else { + yyv16 := &x.UpdateRevision + yym17 := z.DecBinary() + _ = yym17 + if false { + } else { + *((*string)(yyv16)) = r.DecodeString() + } + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -2028,16 +2755,16 @@ func (x *StatefulSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj8 int - var yyb8 bool - var yyhl8 bool = l >= 0 - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + var yyj18 int + var yyb18 bool + var yyhl18 bool = l >= 0 + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l } else { - yyb8 = r.CheckBreak() + yyb18 = r.CheckBreak() } - if yyb8 { + if yyb18 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -2050,20 +2777,20 @@ func (x *StatefulSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder if x.ObservedGeneration == nil { x.ObservedGeneration = new(int64) } - yym10 := z.DecBinary() - _ = yym10 + yym20 := z.DecBinary() + _ = yym20 if false { } else { *((*int64)(x.ObservedGeneration)) = int64(r.DecodeInt(64)) } } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l } else { - yyb8 = r.CheckBreak() + yyb18 = r.CheckBreak() } - if yyb8 { + if yyb18 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -2071,26 +2798,136 @@ func (x *StatefulSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder if r.TryDecodeAsNil() { x.Replicas = 0 } else { - yyv11 := &x.Replicas - yym12 := z.DecBinary() - _ = yym12 + yyv21 := &x.Replicas + yym22 := z.DecBinary() + _ = yym22 if false { } else { - *((*int32)(yyv11)) = int32(r.DecodeInt(32)) + *((*int32)(yyv21)) = int32(r.DecodeInt(32)) + } + } + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l + } else { + yyb18 = r.CheckBreak() + } + if yyb18 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ReadyReplicas = 0 + } else { + yyv23 := &x.ReadyReplicas + yym24 := z.DecBinary() + _ = yym24 + if false { + } else { + *((*int32)(yyv23)) = int32(r.DecodeInt(32)) + } + } + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l + } else { + yyb18 = r.CheckBreak() + } + if yyb18 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.CurrentReplicas = 0 + } else { + yyv25 := &x.CurrentReplicas + yym26 := z.DecBinary() + _ = yym26 + if false { + } else { + *((*int32)(yyv25)) = int32(r.DecodeInt(32)) + } + } + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l + } else { + yyb18 = r.CheckBreak() + } + if yyb18 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.UpdatedReplicas = 0 + } else { + yyv27 := &x.UpdatedReplicas + yym28 := z.DecBinary() + _ = yym28 + if false { + } else { + *((*int32)(yyv27)) = int32(r.DecodeInt(32)) + } + } + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l + } else { + yyb18 = r.CheckBreak() + } + if yyb18 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.CurrentRevision = "" + } else { + yyv29 := &x.CurrentRevision + yym30 := z.DecBinary() + _ = yym30 + if false { + } else { + *((*string)(yyv29)) = r.DecodeString() + } + } + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l + } else { + yyb18 = r.CheckBreak() + } + if yyb18 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.UpdateRevision = "" + } else { + yyv31 := &x.UpdateRevision + yym32 := z.DecBinary() + _ = yym32 + if false { + } else { + *((*string)(yyv31)) = r.DecodeString() } } for { - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l } else { - yyb8 = r.CheckBreak() + yyb18 = r.CheckBreak() } - if yyb8 { + if yyb18 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj8-1, "") + z.DecStructFieldNotFound(yyj18-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -7114,7 +7951,7 @@ func (x codecSelfer1234) decSliceStatefulSet(v *[]StatefulSet, d *codec1978.Deco yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 912) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 984) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] diff --git a/pkg/apis/apps/v1beta1/types.go b/pkg/apis/apps/v1beta1/types.go index f194f9f2a57..a8a7319310c 100644 --- a/pkg/apis/apps/v1beta1/types.go +++ b/pkg/apis/apps/v1beta1/types.go @@ -26,6 +26,7 @@ import ( const ( // StatefulSetInitAnnotation if present, and set to false, indicates that a Pod's readiness should be ignored. StatefulSetInitAnnotation = "pod.alpha.kubernetes.io/initialized" + StatefulSetRevisionLabel = "statefulset.beta.kubernetes.io/revision" ) // ScaleSpec describes the attributes of a scale subresource @@ -111,6 +112,54 @@ const ( ParallelPodManagement = "Parallel" ) +// StatefulSetUpdateStrategy indicates the strategy that the StatefulSet +// controller will use to perform updates. It includes any additional parameters +// necessary to perform the update for the indicated strategy. +type StatefulSetUpdateStrategy struct { + // Type indicates the type of the StatefulSetUpdateStrategy. + Type StatefulSetUpdateStrategyType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type,casttype=StatefulSetStrategyType"` + // Partition is used to communicate the ordinal at which to partition + // the StatefulSet when Type is PartitionStatefulSetStrategyType. This + // 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 +// all possible update strategies for the StatefulSet controller. +type StatefulSetUpdateStrategyType string + +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 + // applied to all Pods in the StatefulSet with respect to the StatefulSet + // ordering constraints. When a scale operation is performed with this + // strategy, new Pods will be created from the specification version indicated + // by the StatefulSet's updateRevision. + RollingUpdateStatefulSetStrategyType = "RollingUpdate" + // OnDeleteStatefulSetStrategyType triggers the legacy behavior. Version + // tracking and ordered rolling restarts are disabled. Pods are recreated + // from the StatefulSetSpec when they are manually deleted. When a scale + // operation is performed with this strategy,specification version indicated + // by the StatefulSet's currentRevision. + OnDeleteStatefulSetStrategyType = "OnDelete" +) + +// PartitionStatefulSetStrategy contains the parameters used with the +// PartitionStatefulSetStrategyType. +type PartitionStatefulSetStrategy struct { + // Ordinal indicates the ordinal at which the StatefulSet should be + // partitioned. + Ordinal int32 `json:"ordinal" protobuf:"varint,1,opt,name=ordinal"` +} + // A StatefulSetSpec is the specification of a StatefulSet. type StatefulSetSpec struct { // replicas is the desired number of replicas of the given Template. @@ -160,16 +209,47 @@ type StatefulSetSpec struct { // all pods at once. // +optional PodManagementPolicy PodManagementPolicyType `json:"podManagementPolicy,omitempty" protobuf:"bytes,6,opt,name=podManagementPolicy,casttype=PodManagementPolicyType"` + + // updateStrategy indicates the StatefulSetUpdateStrategy that will be + // employed to update Pods in the StatefulSet when a revision is made to + // Template. + UpdateStrategy StatefulSetUpdateStrategy `json:"updateStrategy,omitempty" protobuf:"bytes,7,opt,name=updateStrategy"` + + // revisionHistoryLimit is the maximum number of revisions that will + // be maintained in the StatefulSet's revision history. The revision history + // consists of all revisions not represented by a currently applied + // StatefulSetSpec version. The default value is 10. + RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,8,opt,name=revisionHistoryLimit"` } // StatefulSetStatus represents the current state of a StatefulSet. type StatefulSetStatus struct { - // observedGeneration is the most recent generation observed by this StatefulSet. + // observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the + // StatefulSet's generation, which is updated on mutation by the API Server. // +optional ObservedGeneration *int64 `json:"observedGeneration,omitempty" protobuf:"varint,1,opt,name=observedGeneration"` - // replicas is the number of actual replicas. + // replicas is the number of Pods created by the StatefulSet controller. Replicas int32 `json:"replicas" protobuf:"varint,2,opt,name=replicas"` + + // readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition. + ReadyReplicas int32 `json:"readyReplicas,omitempty" protobuf:"varint,3,opt,name=readyReplicas"` + + // currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by currentRevision. + CurrentReplicas int32 `json:"currentReplicas,omitempty" protobuf:"varint,4,opt,name=currentReplicas"` + + // updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by updateRevision. + UpdatedReplicas int32 `json:"updatedReplicas,omitempty" protobuf:"varint,5,opt,name=updatedReplicas"` + + // currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the + // sequence [0,currentReplicas). + CurrentRevision string `json:"currentRevision,omitempty" protobuf:"bytes,6,opt,name=currentRevision"` + + // updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence + // [replicas-updatedReplicas,replicas) + UpdateRevision string `json:"updateRevision,omitempty" protobuf:"bytes,7,opt,name=updateRevision"` } // StatefulSetList is a collection of StatefulSets. diff --git a/pkg/apis/apps/v1beta1/types_swagger_doc_generated.go b/pkg/apis/apps/v1beta1/types_swagger_doc_generated.go index 752356933e8..e5b403300a3 100644 --- a/pkg/apis/apps/v1beta1/types_swagger_doc_generated.go +++ b/pkg/apis/apps/v1beta1/types_swagger_doc_generated.go @@ -137,6 +137,15 @@ func (DeploymentStrategy) SwaggerDoc() map[string]string { return map_DeploymentStrategy } +var map_PartitionStatefulSetStrategy = map[string]string{ + "": "PartitionStatefulSetStrategy contains the parameters used with the PartitionStatefulSetStrategyType.", + "ordinal": "Ordinal indicates the ordinal at which the StatefulSet should be partitioned.", +} + +func (PartitionStatefulSetStrategy) SwaggerDoc() map[string]string { + return map_PartitionStatefulSetStrategy +} + var map_RollbackConfig = map[string]string{ "revision": "The revision to rollback to. If set to 0, rollback to the last revision.", } @@ -212,6 +221,8 @@ var map_StatefulSetSpec = map[string]string{ "volumeClaimTemplates": "volumeClaimTemplates is a list of claims that pods are allowed to reference. The StatefulSet controller is responsible for mapping network identities to claims in a way that maintains the identity of a pod. Every claim in this list must have at least one matching (by name) volumeMount in one container in the template. A claim in this list takes precedence over any volumes in the template, with the same name.", "serviceName": "serviceName is the name of the service that governs this StatefulSet. This service must exist before the StatefulSet, and is responsible for the network identity of the set. Pods get DNS/hostnames that follow the pattern: pod-specific-string.serviceName.default.svc.cluster.local where \"pod-specific-string\" is managed by the StatefulSet controller.", "podManagementPolicy": "podManagementPolicy controls how pods are created during initial scale up, when replacing pods on nodes, or when scaling down. The default policy is `OrderedReady`, where pods are created in increasing order (pod-0, then pod-1, etc) and the controller will wait until each pod is ready before continuing. When scaling down, the pods are removed in the opposite order. The alternative policy is `Parallel` which will create pods in parallel to match the desired scale without waiting, and on scale down will delete all pods at once.", + "updateStrategy": "updateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template.", + "revisionHistoryLimit": "revisionHistoryLimit is the maximum number of revisions that will be maintained in the StatefulSet's revision history. The revision history consists of all revisions not represented by a currently applied StatefulSetSpec version. The default value is 10.", } func (StatefulSetSpec) SwaggerDoc() map[string]string { @@ -220,12 +231,27 @@ func (StatefulSetSpec) SwaggerDoc() map[string]string { var map_StatefulSetStatus = map[string]string{ "": "StatefulSetStatus represents the current state of a StatefulSet.", - "observedGeneration": "observedGeneration is the most recent generation observed by this StatefulSet.", - "replicas": "replicas is the number of actual replicas.", + "observedGeneration": "observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the StatefulSet's generation, which is updated on mutation by the API Server.", + "replicas": "replicas is the number of Pods created by the StatefulSet controller.", + "readyReplicas": "readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition.", + "currentReplicas": "currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by currentRevision.", + "updatedReplicas": "updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by updateRevision.", + "currentRevision": "currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [0,currentReplicas).", + "updateRevision": "updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas)", } func (StatefulSetStatus) SwaggerDoc() map[string]string { return map_StatefulSetStatus } +var map_StatefulSetUpdateStrategy = map[string]string{ + "": "StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. It includes any additional parameters necessary to perform the update for the indicated strategy.", + "type": "Type indicates the type of the StatefulSetUpdateStrategy.", + "partition": "Partition is used to communicate the ordinal at which to partition the StatefulSet when Type is PartitionStatefulSetStrategyType. This value must be set when Type is PartitionStatefulSetStrategyType, and it must be nil otherwise.", +} + +func (StatefulSetUpdateStrategy) SwaggerDoc() map[string]string { + return map_StatefulSetUpdateStrategy +} + // AUTO-GENERATED FUNCTIONS END HERE diff --git a/pkg/apis/apps/v1beta1/zz_generated.conversion.go b/pkg/apis/apps/v1beta1/zz_generated.conversion.go index fe6da7594a1..f1975cc4b8c 100644 --- a/pkg/apis/apps/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/apps/v1beta1/zz_generated.conversion.go @@ -42,6 +42,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_apps_ControllerRevision_To_v1beta1_ControllerRevision, Convert_v1beta1_ControllerRevisionList_To_apps_ControllerRevisionList, Convert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList, + Convert_v1beta1_PartitionStatefulSetStrategy_To_apps_PartitionStatefulSetStrategy, + Convert_apps_PartitionStatefulSetStrategy_To_v1beta1_PartitionStatefulSetStrategy, Convert_v1beta1_StatefulSet_To_apps_StatefulSet, Convert_apps_StatefulSet_To_v1beta1_StatefulSet, Convert_v1beta1_StatefulSetList_To_apps_StatefulSetList, @@ -50,6 +52,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec, Convert_v1beta1_StatefulSetStatus_To_apps_StatefulSetStatus, Convert_apps_StatefulSetStatus_To_v1beta1_StatefulSetStatus, + Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy, + Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy, ) } @@ -123,6 +127,26 @@ func Convert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList(in *a return autoConvert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList(in, out, s) } +func autoConvert_v1beta1_PartitionStatefulSetStrategy_To_apps_PartitionStatefulSetStrategy(in *PartitionStatefulSetStrategy, out *apps.PartitionStatefulSetStrategy, s conversion.Scope) error { + out.Ordinal = in.Ordinal + return nil +} + +// Convert_v1beta1_PartitionStatefulSetStrategy_To_apps_PartitionStatefulSetStrategy is an autogenerated conversion function. +func Convert_v1beta1_PartitionStatefulSetStrategy_To_apps_PartitionStatefulSetStrategy(in *PartitionStatefulSetStrategy, out *apps.PartitionStatefulSetStrategy, s conversion.Scope) error { + return autoConvert_v1beta1_PartitionStatefulSetStrategy_To_apps_PartitionStatefulSetStrategy(in, out, s) +} + +func autoConvert_apps_PartitionStatefulSetStrategy_To_v1beta1_PartitionStatefulSetStrategy(in *apps.PartitionStatefulSetStrategy, out *PartitionStatefulSetStrategy, s conversion.Scope) error { + out.Ordinal = in.Ordinal + return nil +} + +// Convert_apps_PartitionStatefulSetStrategy_To_v1beta1_PartitionStatefulSetStrategy is an autogenerated conversion function. +func Convert_apps_PartitionStatefulSetStrategy_To_v1beta1_PartitionStatefulSetStrategy(in *apps.PartitionStatefulSetStrategy, out *PartitionStatefulSetStrategy, s conversion.Scope) error { + return autoConvert_apps_PartitionStatefulSetStrategy_To_v1beta1_PartitionStatefulSetStrategy(in, out, s) +} + func autoConvert_v1beta1_StatefulSet_To_apps_StatefulSet(in *StatefulSet, out *apps.StatefulSet, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(&in.Spec, &out.Spec, s); err != nil { @@ -208,6 +232,10 @@ func autoConvert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(in *StatefulSet out.VolumeClaimTemplates = *(*[]api.PersistentVolumeClaim)(unsafe.Pointer(&in.VolumeClaimTemplates)) out.ServiceName = in.ServiceName out.PodManagementPolicy = apps.PodManagementPolicyType(in.PodManagementPolicy) + if err := Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { + return err + } + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) return nil } @@ -222,12 +250,21 @@ func autoConvert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec(in *apps.Statef out.VolumeClaimTemplates = *(*[]api_v1.PersistentVolumeClaim)(unsafe.Pointer(&in.VolumeClaimTemplates)) out.ServiceName = in.ServiceName out.PodManagementPolicy = PodManagementPolicyType(in.PodManagementPolicy) + if err := Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { + return err + } + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) return nil } func autoConvert_v1beta1_StatefulSetStatus_To_apps_StatefulSetStatus(in *StatefulSetStatus, out *apps.StatefulSetStatus, s conversion.Scope) error { out.ObservedGeneration = (*int64)(unsafe.Pointer(in.ObservedGeneration)) out.Replicas = in.Replicas + out.ReadyReplicas = in.ReadyReplicas + out.CurrentReplicas = in.CurrentReplicas + out.UpdatedReplicas = in.UpdatedReplicas + out.CurrentRevision = in.CurrentRevision + out.UpdateRevision = in.UpdateRevision return nil } @@ -239,6 +276,11 @@ func Convert_v1beta1_StatefulSetStatus_To_apps_StatefulSetStatus(in *StatefulSet func autoConvert_apps_StatefulSetStatus_To_v1beta1_StatefulSetStatus(in *apps.StatefulSetStatus, out *StatefulSetStatus, s conversion.Scope) error { out.ObservedGeneration = (*int64)(unsafe.Pointer(in.ObservedGeneration)) out.Replicas = in.Replicas + out.ReadyReplicas = in.ReadyReplicas + out.CurrentReplicas = in.CurrentReplicas + out.UpdatedReplicas = in.UpdatedReplicas + out.CurrentRevision = in.CurrentRevision + out.UpdateRevision = in.UpdateRevision return nil } @@ -246,3 +288,15 @@ func autoConvert_apps_StatefulSetStatus_To_v1beta1_StatefulSetStatus(in *apps.St func Convert_apps_StatefulSetStatus_To_v1beta1_StatefulSetStatus(in *apps.StatefulSetStatus, out *StatefulSetStatus, s conversion.Scope) error { return autoConvert_apps_StatefulSetStatus_To_v1beta1_StatefulSetStatus(in, out, s) } + +func autoConvert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy(in *StatefulSetUpdateStrategy, out *apps.StatefulSetUpdateStrategy, s conversion.Scope) error { + out.Type = apps.StatefulSetUpdateStrategyType(in.Type) + out.Partition = (*apps.PartitionStatefulSetStrategy)(unsafe.Pointer(in.Partition)) + return nil +} + +func autoConvert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(in *apps.StatefulSetUpdateStrategy, out *StatefulSetUpdateStrategy, s conversion.Scope) error { + out.Type = StatefulSetUpdateStrategyType(in.Type) + out.Partition = (*PartitionStatefulSetStrategy)(unsafe.Pointer(in.Partition)) + return nil +} diff --git a/pkg/apis/apps/v1beta1/zz_generated.deepcopy.go b/pkg/apis/apps/v1beta1/zz_generated.deepcopy.go index efd5a79aca1..abf7ccfef52 100644 --- a/pkg/apis/apps/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/apps/v1beta1/zz_generated.deepcopy.go @@ -46,6 +46,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_DeploymentSpec, InType: reflect.TypeOf(&DeploymentSpec{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_DeploymentStatus, InType: reflect.TypeOf(&DeploymentStatus{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_DeploymentStrategy, InType: reflect.TypeOf(&DeploymentStrategy{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_PartitionStatefulSetStrategy, InType: reflect.TypeOf(&PartitionStatefulSetStrategy{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_RollbackConfig, InType: reflect.TypeOf(&RollbackConfig{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_RollingUpdateDeployment, InType: reflect.TypeOf(&RollingUpdateDeployment{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_Scale, InType: reflect.TypeOf(&Scale{})}, @@ -55,6 +56,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_StatefulSetList, InType: reflect.TypeOf(&StatefulSetList{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_StatefulSetSpec, InType: reflect.TypeOf(&StatefulSetSpec{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_StatefulSetStatus, InType: reflect.TypeOf(&StatefulSetStatus{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_StatefulSetUpdateStrategy, InType: reflect.TypeOf(&StatefulSetUpdateStrategy{})}, ) } @@ -251,6 +253,16 @@ func DeepCopy_v1beta1_DeploymentStrategy(in interface{}, out interface{}, c *con } } +// DeepCopy_v1beta1_PartitionStatefulSetStrategy is an autogenerated deepcopy function. +func DeepCopy_v1beta1_PartitionStatefulSetStrategy(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*PartitionStatefulSetStrategy) + out := out.(*PartitionStatefulSetStrategy) + *out = *in + return nil + } +} + // DeepCopy_v1beta1_RollbackConfig is an autogenerated deepcopy function. func DeepCopy_v1beta1_RollbackConfig(in interface{}, out interface{}, c *conversion.Cloner) error { { @@ -397,6 +409,14 @@ func DeepCopy_v1beta1_StatefulSetSpec(in interface{}, out interface{}, c *conver } } } + if err := DeepCopy_v1beta1_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, c); err != nil { + return err + } + if in.RevisionHistoryLimit != nil { + in, out := &in.RevisionHistoryLimit, &out.RevisionHistoryLimit + *out = new(int32) + **out = **in + } return nil } } @@ -415,3 +435,18 @@ func DeepCopy_v1beta1_StatefulSetStatus(in interface{}, out interface{}, c *conv return nil } } + +// DeepCopy_v1beta1_StatefulSetUpdateStrategy is an autogenerated deepcopy function. +func DeepCopy_v1beta1_StatefulSetUpdateStrategy(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*StatefulSetUpdateStrategy) + out := out.(*StatefulSetUpdateStrategy) + *out = *in + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(PartitionStatefulSetStrategy) + **out = **in + } + return nil + } +} diff --git a/pkg/apis/apps/validation/validation.go b/pkg/apis/apps/validation/validation.go index 7c37d6841c7..a51c3634f8b 100644 --- a/pkg/apis/apps/validation/validation.go +++ b/pkg/apis/apps/validation/validation.go @@ -75,6 +75,40 @@ func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path) fi allErrs = append(allErrs, field.Invalid(fldPath.Child("podManagementPolicy"), spec.PodManagementPolicy, fmt.Sprintf("must be '%s' or '%s'", apps.OrderedReadyPodManagement, apps.ParallelPodManagement))) } + switch spec.UpdateStrategy.Type { + case "": + allErrs = append(allErrs, field.Required(fldPath.Child("updateStrategy"), "")) + case apps.OnDeleteStatefulSetStrategyType, apps.RollingUpdateStatefulSetStrategyType: + if spec.UpdateStrategy.Partition != nil { + allErrs = append( + allErrs, + field.Invalid( + fldPath.Child("updateStrategy").Child("partition"), + spec.UpdateStrategy.Partition.Ordinal, + fmt.Sprintf("only allowed for updateStrategy '%s'", apps.PartitionStatefulSetStrategyType))) + } + case apps.PartitionStatefulSetStrategyType: + if spec.UpdateStrategy.Partition == nil { + allErrs = append( + allErrs, + field.Required( + fldPath.Child("updateStrategy").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: + allErrs = append(allErrs, + field.Invalid(fldPath.Child("updateStrategy"), spec.UpdateStrategy, + fmt.Sprintf("must be '%s', '%s', or '%s'", + apps.RollingUpdateStatefulSetStrategyType, + apps.OnDeleteStatefulSetStrategyType, + apps.PartitionStatefulSetStrategyType))) + } + allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...) if spec.Selector == nil { allErrs = append(allErrs, field.Required(fldPath.Child("selector"), "")) @@ -113,20 +147,21 @@ func ValidateStatefulSet(statefulSet *apps.StatefulSet) field.ErrorList { func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet) field.ErrorList { allErrs := apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata")) - // TODO: For now we're taking the safe route and disallowing all updates to - // spec except for Replicas, for scaling, and Template.Spec.containers.image - // for rolling-update. Enable others on a case by case basis. restoreReplicas := statefulSet.Spec.Replicas statefulSet.Spec.Replicas = oldStatefulSet.Spec.Replicas - restoreContainers := statefulSet.Spec.Template.Spec.Containers - statefulSet.Spec.Template.Spec.Containers = oldStatefulSet.Spec.Template.Spec.Containers + restoreTemplate := statefulSet.Spec.Template + statefulSet.Spec.Template = oldStatefulSet.Spec.Template + + restoreStrategy := statefulSet.Spec.UpdateStrategy + statefulSet.Spec.UpdateStrategy = oldStatefulSet.Spec.UpdateStrategy if !reflect.DeepEqual(statefulSet.Spec, oldStatefulSet.Spec) { - allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas' and 'containers' are forbidden.")) + allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden.")) } statefulSet.Spec.Replicas = restoreReplicas - statefulSet.Spec.Template.Spec.Containers = restoreContainers + statefulSet.Spec.Template = restoreTemplate + statefulSet.Spec.UpdateStrategy = restoreStrategy allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(statefulSet.Spec.Replicas), field.NewPath("spec", "replicas"))...) containerErrs, _ := apivalidation.ValidateContainerUpdates(statefulSet.Spec.Template.Spec.Containers, oldStatefulSet.Spec.Template.Spec.Containers, field.NewPath("spec").Child("template").Child("containers")) diff --git a/pkg/apis/apps/validation/validation_test.go b/pkg/apis/apps/validation/validation_test.go index 6e56de16f7e..b53d2fc888a 100644 --- a/pkg/apis/apps/validation/validation_test.go +++ b/pkg/apis/apps/validation/validation_test.go @@ -59,6 +59,7 @@ func TestValidateStatefulSet(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, { @@ -67,6 +68,39 @@ func TestValidateStatefulSet(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault}, + Spec: apps.StatefulSetSpec{ + PodManagementPolicy: apps.ParallelPodManagement, + Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault}, + Spec: apps.StatefulSetSpec{ + PodManagementPolicy: apps.OrderedReadyPodManagement, + Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.OnDeleteStatefulSetStrategyType}, + }, + }, + { + 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: func() *apps.PartitionStatefulSetStrategy { + return &apps.PartitionStatefulSetStrategy{Ordinal: 2} + }()}, }, }, } @@ -83,6 +117,7 @@ func TestValidateStatefulSet(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, "missing-namespace": { @@ -91,6 +126,7 @@ func TestValidateStatefulSet(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, "empty selector": { @@ -98,6 +134,7 @@ func TestValidateStatefulSet(t *testing.T) { Spec: apps.StatefulSetSpec{ PodManagementPolicy: apps.OrderedReadyPodManagement, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, "selector_doesnt_match": { @@ -106,6 +143,7 @@ func TestValidateStatefulSet(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, "invalid manifest": { @@ -113,6 +151,7 @@ func TestValidateStatefulSet(t *testing.T) { Spec: apps.StatefulSetSpec{ PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, "negative_replicas": { @@ -121,6 +160,7 @@ func TestValidateStatefulSet(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Replicas: -1, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, "invalid_label": { @@ -135,6 +175,7 @@ func TestValidateStatefulSet(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, "invalid_label 2": { @@ -148,6 +189,7 @@ func TestValidateStatefulSet(t *testing.T) { Spec: apps.StatefulSetSpec{ PodManagementPolicy: apps.OrderedReadyPodManagement, Template: invalidPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, "invalid_annotation": { @@ -162,6 +204,7 @@ func TestValidateStatefulSet(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, "invalid restart policy 1": { @@ -182,6 +225,7 @@ func TestValidateStatefulSet(t *testing.T) { Labels: validLabels, }, }, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, "invalid restart policy 2": { @@ -202,6 +246,42 @@ func TestValidateStatefulSet(t *testing.T) { Labels: validLabels, }, }, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, + }, + }, + "invalid udpate strategy": { + 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: "foo"}, + }, + }, + "partitioned rolling update": { + 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.RollingUpdateStatefulSetStrategyType, + Partition: func() *apps.PartitionStatefulSetStrategy { + return &apps.PartitionStatefulSetStrategy{Ordinal: 2} + }()}, + }, + }, + "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}, }, }, } @@ -222,7 +302,10 @@ func TestValidateStatefulSet(t *testing.T) { field != "spec.template.labels" && field != "metadata.annotations" && field != "metadata.labels" && - field != "status.replicas" { + field != "status.replicas" && + field != "spec.updateStrategy" && + field != "spec.updateStrategy.partition" && + field != "spec.updateStrategy.partition.ordinal" { t.Errorf("%s: missing prefix for: %v", k, errs[i]) } } @@ -280,6 +363,7 @@ func TestValidateStatefulSetUpdate(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, update: apps.StatefulSet{ @@ -289,6 +373,7 @@ func TestValidateStatefulSetUpdate(t *testing.T) { Replicas: 3, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, }, @@ -305,8 +390,9 @@ func TestValidateStatefulSetUpdate(t *testing.T) { old: apps.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault}, Spec: apps.StatefulSetSpec{ - Selector: &metav1.LabelSelector{MatchLabels: validLabels}, - Template: validPodTemplate.Template, + Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, update: apps.StatefulSet{ @@ -316,6 +402,7 @@ func TestValidateStatefulSetUpdate(t *testing.T) { Replicas: 2, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: readWriteVolumePodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, }, @@ -323,16 +410,18 @@ func TestValidateStatefulSetUpdate(t *testing.T) { old: apps.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, Spec: apps.StatefulSetSpec{ - Selector: &metav1.LabelSelector{MatchLabels: validLabels}, - Template: validPodTemplate.Template, + Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, update: apps.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, Spec: apps.StatefulSetSpec{ - Replicas: 3, - Selector: &metav1.LabelSelector{MatchLabels: validLabels}, - Template: validPodTemplate.Template, + Replicas: 3, + Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, }, @@ -340,8 +429,9 @@ func TestValidateStatefulSetUpdate(t *testing.T) { old: apps.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, Spec: apps.StatefulSetSpec{ - Selector: &metav1.LabelSelector{MatchLabels: validLabels}, - Template: validPodTemplate.Template, + Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, update: apps.StatefulSet{ @@ -351,24 +441,7 @@ func TestValidateStatefulSetUpdate(t *testing.T) { Replicas: 3, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, - }, - }, - }, - "updates to a field other than spec.Replicas": { - old: apps.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, - Spec: apps.StatefulSetSpec{ - Selector: &metav1.LabelSelector{MatchLabels: validLabels}, - Template: validPodTemplate.Template, - }, - }, - update: apps.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, - Spec: apps.StatefulSetSpec{ - PodManagementPolicy: apps.OrderedReadyPodManagement, - Replicas: 1, - Selector: &metav1.LabelSelector{MatchLabels: validLabels}, - Template: readWriteVolumePodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, }, @@ -376,8 +449,9 @@ func TestValidateStatefulSetUpdate(t *testing.T) { old: apps.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "", Namespace: metav1.NamespaceDefault}, Spec: apps.StatefulSetSpec{ - Selector: &metav1.LabelSelector{MatchLabels: validLabels}, - Template: validPodTemplate.Template, + Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, update: apps.StatefulSet{ @@ -387,6 +461,7 @@ func TestValidateStatefulSetUpdate(t *testing.T) { Replicas: 2, Selector: &metav1.LabelSelector{MatchLabels: invalidLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, }, @@ -397,14 +472,16 @@ func TestValidateStatefulSetUpdate(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, update: apps.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault}, Spec: apps.StatefulSetSpec{ - Replicas: 2, - Selector: &metav1.LabelSelector{MatchLabels: validLabels}, - Template: invalidPodTemplate.Template, + Replicas: 2, + Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + Template: invalidPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, }, @@ -423,6 +500,7 @@ func TestValidateStatefulSetUpdate(t *testing.T) { Replicas: -1, Selector: &metav1.LabelSelector{MatchLabels: validLabels}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, }, }, diff --git a/pkg/apis/apps/zz_generated.deepcopy.go b/pkg/apis/apps/zz_generated.deepcopy.go index 84e48618d00..763b4b1565e 100644 --- a/pkg/apis/apps/zz_generated.deepcopy.go +++ b/pkg/apis/apps/zz_generated.deepcopy.go @@ -38,10 +38,12 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { return scheme.AddGeneratedDeepCopyFuncs( conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_ControllerRevision, InType: reflect.TypeOf(&ControllerRevision{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_ControllerRevisionList, InType: reflect.TypeOf(&ControllerRevisionList{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_PartitionStatefulSetStrategy, InType: reflect.TypeOf(&PartitionStatefulSetStrategy{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_StatefulSet, InType: reflect.TypeOf(&StatefulSet{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_StatefulSetList, InType: reflect.TypeOf(&StatefulSetList{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_StatefulSetSpec, InType: reflect.TypeOf(&StatefulSetSpec{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_StatefulSetStatus, InType: reflect.TypeOf(&StatefulSetStatus{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_StatefulSetUpdateStrategy, InType: reflect.TypeOf(&StatefulSetUpdateStrategy{})}, ) } @@ -87,6 +89,16 @@ func DeepCopy_apps_ControllerRevisionList(in interface{}, out interface{}, c *co } } +// DeepCopy_apps_PartitionStatefulSetStrategy is an autogenerated deepcopy function. +func DeepCopy_apps_PartitionStatefulSetStrategy(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*PartitionStatefulSetStrategy) + out := out.(*PartitionStatefulSetStrategy) + *out = *in + return nil + } +} + // DeepCopy_apps_StatefulSet is an autogenerated deepcopy function. func DeepCopy_apps_StatefulSet(in interface{}, out interface{}, c *conversion.Cloner) error { { @@ -153,6 +165,14 @@ func DeepCopy_apps_StatefulSetSpec(in interface{}, out interface{}, c *conversio } } } + if err := DeepCopy_apps_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, c); err != nil { + return err + } + if in.RevisionHistoryLimit != nil { + in, out := &in.RevisionHistoryLimit, &out.RevisionHistoryLimit + *out = new(int32) + **out = **in + } return nil } } @@ -171,3 +191,18 @@ func DeepCopy_apps_StatefulSetStatus(in interface{}, out interface{}, c *convers return nil } } + +// DeepCopy_apps_StatefulSetUpdateStrategy is an autogenerated deepcopy function. +func DeepCopy_apps_StatefulSetUpdateStrategy(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*StatefulSetUpdateStrategy) + out := out.(*StatefulSetUpdateStrategy) + *out = *in + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(PartitionStatefulSetStrategy) + **out = **in + } + return nil + } +} diff --git a/pkg/controller/BUILD b/pkg/controller/BUILD index 9b4df49d7ef..beafe906298 100644 --- a/pkg/controller/BUILD +++ b/pkg/controller/BUILD @@ -111,6 +111,7 @@ filegroup( "//pkg/controller/disruption:all-srcs", "//pkg/controller/endpoint:all-srcs", "//pkg/controller/garbagecollector:all-srcs", + "//pkg/controller/history:all-srcs", "//pkg/controller/job:all-srcs", "//pkg/controller/namespace:all-srcs", "//pkg/controller/node:all-srcs", diff --git a/pkg/controller/history/BUILD b/pkg/controller/history/BUILD new file mode 100644 index 00000000000..e6e8d8f948d --- /dev/null +++ b/pkg/controller/history/BUILD @@ -0,0 +1,69 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_test( + name = "go_default_test", + srcs = ["controller_history_test.go"], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//pkg/api/testapi:go_default_library", + "//pkg/api/v1:go_default_library", + "//pkg/apis/apps/v1beta1:go_default_library", + "//pkg/client/clientset_generated/clientset/fake:go_default_library", + "//pkg/client/informers/informers_generated/externalversions:go_default_library", + "//pkg/controller:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", + "//vendor/k8s.io/client-go/testing:go_default_library", + ], +) + +go_library( + name = "go_default_library", + srcs = ["controller_history.go"], + tags = ["automanaged"], + deps = [ + "//pkg/apis/apps/v1beta1:go_default_library", + "//pkg/client/clientset_generated/clientset:go_default_library", + "//pkg/client/informers/informers_generated/externalversions/apps/v1beta1:go_default_library", + "//pkg/client/listers/apps/v1beta1:go_default_library", + "//pkg/client/retry:go_default_library", + "//pkg/controller:go_default_library", + "//pkg/util/hash:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library", + "//vendor/k8s.io/client-go/tools/cache:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/pkg/controller/history/OWNERS b/pkg/controller/history/OWNERS new file mode 100755 index 00000000000..4ff17cf2c72 --- /dev/null +++ b/pkg/controller/history/OWNERS @@ -0,0 +1,16 @@ +approvers: +- bprashanth +- enisoc +- foxish +- janetkuo +- kargakis +- kow3ns +- smarterclayton +reviewers: +- bprashanth +- enisoc +- foxish +- janetkuo +- kargakis +- kow3ns +- smarterclayton diff --git a/pkg/controller/history/controller_history.go b/pkg/controller/history/controller_history.go new file mode 100644 index 00000000000..55ae429d1d2 --- /dev/null +++ b/pkg/controller/history/controller_history.go @@ -0,0 +1,490 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package history + +import ( + "bytes" + "fmt" + "hash/fnv" + "sort" + "strconv" + + apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" + "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" + appsinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/apps/v1beta1" + appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1" + "k8s.io/kubernetes/pkg/controller" + hashutil "k8s.io/kubernetes/pkg/util/hash" + + apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/cache" + "k8s.io/kubernetes/pkg/client/retry" +) + +// ControllerRevisionHashLabel is the label used to indicate the hash value of a ControllerRevision's Data. +const ControllerRevisionHashLabel = "controller.kubernetes.io/hash" + +// ControllerRevisionName returns the Name for a ControllerRevision in the form prefix-hash. If the length +// of prefix is greater than 223 bytes, it is truncated to allow for a name that is no larger than 253 bytes. +func ControllerRevisionName(prefix string, hash uint32) string { + if len(prefix) > 223 { + prefix = prefix[:223] + } + return fmt.Sprintf("%s-%d", prefix, hash) +} + +// NewControllerRevision returns the a ControllerRevision with a ControllerRef pointing parent and indicating that +// parent is of parentKind. The ControllerRevision has labels matching selector, contains Data equal to data, and +// has a Revision equal to revision. If the returned error is nil, the returned ControllerRevision is valid. If the +// returned error is not nil, the returned ControllerRevision is invalid for use. +func NewControllerRevision(parent metav1.Object, + parentKind schema.GroupVersionKind, + selector labels.Selector, + data runtime.RawExtension, + revision int64) (*apps.ControllerRevision, error) { + labelMap, err := labels.ConvertSelectorToLabelsMap(selector.String()) + if err != nil { + return nil, err + } + blockOwnerDeletion := true + isController := true + cr := &apps.ControllerRevision{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labelMap, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: parentKind.GroupVersion().String(), + Kind: parentKind.Kind, + Name: parent.GetName(), + UID: parent.GetUID(), + BlockOwnerDeletion: &blockOwnerDeletion, + Controller: &isController, + }, + }, + }, + Data: data, + Revision: revision, + } + hash := HashControllerRevision(cr, nil) + cr.Name = ControllerRevisionName(parent.GetName(), hash) + cr.Labels[ControllerRevisionHashLabel] = strconv.FormatInt(int64(hash), 10) + return cr, nil +} + +// HashControllerRevision hashes the contents of revision's Data using FNV hashing. If probe is not nil, the byte value +// of probe is added written to the hash as well. +func HashControllerRevision(revision *apps.ControllerRevision, probe *uint32) uint32 { + hf := fnv.New32() + if len(revision.Data.Raw) > 0 { + hf.Write(revision.Data.Raw) + } + if revision.Data.Object != nil { + hashutil.DeepHashObject(hf, revision.Data.Object) + } + if probe != nil { + hf.Write([]byte(strconv.FormatInt(int64(*probe), 10))) + } + return hf.Sum32() + +} + +// SortControllerRevisions sorts revisions by their Revision. +func SortControllerRevisions(revisions []*apps.ControllerRevision) { + sort.Sort(byRevision(revisions)) +} + +// EqualRevision returns true if lhs and rhs are either both nil, or both point to non-nil ControllerRevisions that +// contain semantically equivalent data. Otherwise this method returns false. +func EqualRevision(lhs *apps.ControllerRevision, rhs *apps.ControllerRevision) bool { + var lhsHash, rhsHash *uint32 + if lhs == nil || rhs == nil { + return lhs == rhs + } + if hs, found := lhs.Labels[ControllerRevisionHashLabel]; found { + hash, err := strconv.ParseInt(hs, 10, 32) + if err == nil { + lhsHash = new(uint32) + *lhsHash = uint32(hash) + } + } + if hs, found := rhs.Labels[ControllerRevisionHashLabel]; found { + hash, err := strconv.ParseInt(hs, 10, 32) + if err == nil { + rhsHash = new(uint32) + *rhsHash = uint32(hash) + } + } + if lhsHash != nil && rhsHash != nil && *lhsHash != *rhsHash { + return false + } + return bytes.Equal(lhs.Data.Raw, rhs.Data.Raw) && apiequality.Semantic.DeepEqual(lhs.Data.Object, rhs.Data.Object) +} + +// FindEqualRevisions returns all ControllerRevisions in revisions that are equal to needle using EqualRevision as the +// equality test. The returned slice preserves the order of revisions. +func FindEqualRevisions(revisions []*apps.ControllerRevision, needle *apps.ControllerRevision) []*apps.ControllerRevision { + var eq []*apps.ControllerRevision + for i := range revisions { + if EqualRevision(revisions[i], needle) { + eq = append(eq, revisions[i]) + } + } + return eq +} + +// byRevision implements sort.Interface to allow ControllerRevisions to be sorted by Revision. +type byRevision []*apps.ControllerRevision + +func (br byRevision) Len() int { + return len(br) +} + +func (br byRevision) Less(i, j int) bool { + return br[i].Revision < br[j].Revision +} + +func (br byRevision) Swap(i, j int) { + br[i], br[j] = br[j], br[i] +} + +// Interface provides an interface allowing for management of a Controller's history as realized by recorded +// ControllerRevisions. An instance of Interface can be retrieved from NewHistory. Implementations must treat all +// pointer parameters as "in" parameter, and they must not be mutated. +type Interface interface { + // ListControllerRevisions lists all ControllerRevisions matching selector and owned by parent or no other + // controller. If the returned error is nil the returned slice of ControllerRevisions is valid. If the + // returned error is not nil, the returned slice is not valid. + ListControllerRevisions(parent metav1.Object, selector labels.Selector) ([]*apps.ControllerRevision, error) + // CreateControllerRevision attempts to create the revision as owned by parent via a ControllerRef. If name + // collision occurs, a unique identifier is added to the hash of the revision and it is renamed using + // ControllerRevisionName. Implementations may cease to attempt to retry creation after some number of attempts + // and return an error. If the returned error is not nil, creation failed. If the returned error is nil, the + // returned ControllerRevision has been created. + CreateControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) + // DeleteControllerRevision attempts to delete revision. If the returned error is not nil, deletion has failed. + DeleteControllerRevision(revision *apps.ControllerRevision) error + // UpdateControllerRevision updates revision such that its Revision is equal to newRevision. Implementations + // may retry on conflict. If the returned error is nil, the update was successful and returned ControllerRevision + // is valid. If the returned error is not nil, the update failed and the returned ControllerRevision is invalid. + UpdateControllerRevision(revision *apps.ControllerRevision, newRevision int64) (*apps.ControllerRevision, error) + // AdoptControllerRevision attempts to adopt revision by adding a ControllerRef indicating that the parent + // Object of parentKind is the owner of revision. If revision is already owned, an error is returned. If the + // resource patch fails, an error is returned. If no error is returned, the returned ControllerRevision is + // valid. + AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) + // ReleaseControllerRevision attempts to release parent's ownership of revision by removing parent from the + // OwnerReferences of revision. If an error is returned, parent remains the owner of revision. If no error is + // returned, the returned ControllerRevision is valid. + ReleaseControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) +} + +// NewHistory returns an instance of Interface that uses client to communicate with the API Server and lister to list +// ControllerRevisions. This method should be used to create an Interface for all scenarios other than testing. +func NewHistory(client clientset.Interface, lister appslisters.ControllerRevisionLister) Interface { + return &realHistory{client, lister} +} + +// NewFakeHistory returns an instance of Interface that uses informer to create, update, list, and delete +// ControllerRevisions. This method should be used to create an Interface for testing purposes. +func NewFakeHistory(informer appsinformers.ControllerRevisionInformer) Interface { + return &fakeHistory{informer.Informer().GetIndexer(), informer.Lister()} +} + +type realHistory struct { + client clientset.Interface + lister appslisters.ControllerRevisionLister +} + +func (rh *realHistory) ListControllerRevisions(parent metav1.Object, selector labels.Selector) ([]*apps.ControllerRevision, error) { + // List all revisions in the namespace that match the selector + history, err := rh.lister.ControllerRevisions(parent.GetNamespace()).List(selector) + if err != nil { + return nil, err + } + var owned []*apps.ControllerRevision + for i := range history { + ref := controller.GetControllerOf(history[i]) + if ref == nil || ref.UID == parent.GetUID() { + owned = append(owned, history[i]) + } + + } + return owned, err +} + +func (rh *realHistory) CreateControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { + // Initialize the probe to 0 + probe := uint32(0) + + // Clone the input + any, err := scheme.Scheme.DeepCopy(revision) + if err != nil { + return nil, err + } + clone := any.(*apps.ControllerRevision) + + // Continue to attempt to create the revision updating the name with a new hash on each iteration + for { + var hash uint32 + // The first attempt uses no probe to resolve collisions + if probe > 0 { + hash = HashControllerRevision(revision, &probe) + } else { + hash = HashControllerRevision(revision, nil) + } + // Update the revisions name and labels + clone.Name = ControllerRevisionName(parent.GetName(), hash) + created, err := rh.client.Apps().ControllerRevisions(parent.GetNamespace()).Create(clone) + if errors.IsAlreadyExists(err) { + probe++ + continue + } + return created, err + } +} + +func (rh *realHistory) UpdateControllerRevision(revision *apps.ControllerRevision, newRevision int64) (*apps.ControllerRevision, error) { + obj, err := scheme.Scheme.DeepCopy(revision) + if err != nil { + return nil, err + } + clone := obj.(*apps.ControllerRevision) + err = retry.RetryOnConflict(retry.DefaultBackoff, func() error { + if clone.Revision == newRevision { + return nil + } + clone.Revision = newRevision + updated, updateErr := rh.client.Apps().ControllerRevisions(clone.Namespace).Update(clone) + if updateErr == nil { + return nil + } + if updated != nil { + clone = updated + } + if updated, err := rh.lister.ControllerRevisions(clone.Namespace).Get(clone.Name); err == nil { + // make a copy so we don't mutate the shared cache + obj, err := scheme.Scheme.DeepCopy(updated) + if err != nil { + return err + } + clone = obj.(*apps.ControllerRevision) + } + return updateErr + }) + return clone, err +} + +func (rh *realHistory) DeleteControllerRevision(revision *apps.ControllerRevision) error { + return rh.client.Apps().ControllerRevisions(revision.Namespace).Delete(revision.Name, nil) +} + +func (rh *realHistory) AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { + // Return an error if the parent does not own the revision + if owner := controller.GetControllerOf(revision); owner != nil { + return nil, fmt.Errorf("attempt to adopt revision owned by %v", owner) + } + // Use strategic merge patch to add an owner reference indicating a controller ref + return rh.client.Apps().ControllerRevisions(parent.GetNamespace()).Patch(revision.GetName(), + types.StrategicMergePatchType, []byte(fmt.Sprintf( + `{"metadata":{"ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"uid":"%s"}}`, + parentKind.GroupVersion().String(), parentKind.Kind, + parent.GetName(), parent.GetUID(), revision.UID))) +} + +func (rh *realHistory) ReleaseControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { + // Use strategic merge patch to add an owner reference indicating a controller ref + released, err := rh.client.Apps().ControllerRevisions(revision.GetNamespace()).Patch(revision.GetName(), + types.StrategicMergePatchType, + []byte(fmt.Sprintf(`{"metadata":{"ownerReferences":[{"$patch":"delete","uid":"%s"}],"uid":"%s"}}`, parent.GetUID(), revision.UID))) + + if err != nil { + if errors.IsNotFound(err) { + // We ignore deleted revisions + return nil, nil + } + if errors.IsInvalid(err) { + // We ignore cases where the parent no longer owns the revision or where the revision has no + // owner. + return nil, nil + } + } + return released, err +} + +type fakeHistory struct { + indexer cache.Indexer + lister appslisters.ControllerRevisionLister +} + +func (fh *fakeHistory) ListControllerRevisions(parent metav1.Object, selector labels.Selector) ([]*apps.ControllerRevision, error) { + history, err := fh.lister.ControllerRevisions(parent.GetNamespace()).List(selector) + if err != nil { + return nil, err + } + + var owned []*apps.ControllerRevision + for i := range history { + ref := controller.GetControllerOf(history[i]) + if ref == nil || ref.UID == parent.GetUID() { + owned = append(owned, history[i]) + } + + } + return owned, err +} + +func (fh *fakeHistory) addRevision(revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision) + if err != nil { + return nil, err + } + obj, found, err := fh.indexer.GetByKey(key) + if err != nil { + return nil, err + } + if found { + foundRevision := obj.(*apps.ControllerRevision) + return foundRevision, errors.NewAlreadyExists(apps.Resource("controllerrevision"), revision.Name) + } + return revision, fh.indexer.Update(revision) +} + +func (fh *fakeHistory) CreateControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { + // Initialize the probe to 0 + probe := uint32(0) + + // Clone the input + any, err := scheme.Scheme.DeepCopy(revision) + if err != nil { + return nil, err + } + clone := any.(*apps.ControllerRevision) + clone.Namespace = parent.GetNamespace() + + // Continue to attempt to create the revision updating the name with a new hash on each iteration + for { + var hash uint32 + // The first attempt uses no probe to resolve collisions + if probe > 0 { + hash = HashControllerRevision(revision, &probe) + } else { + hash = HashControllerRevision(revision, nil) + } + // Update the revisions name and labels + clone.Name = ControllerRevisionName(parent.GetName(), hash) + created, err := fh.addRevision(clone) + if errors.IsAlreadyExists(err) { + probe++ + continue + } + return created, err + } +} + +func (fh *fakeHistory) DeleteControllerRevision(revision *apps.ControllerRevision) error { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision) + if err != nil { + return err + } + obj, found, err := fh.indexer.GetByKey(key) + if err != nil { + return err + } + if !found { + return errors.NewNotFound(apps.Resource("controllerrevisions"), revision.Name) + } + return fh.indexer.Delete(obj) +} + +func (fh *fakeHistory) UpdateControllerRevision(revision *apps.ControllerRevision, newRevision int64) (*apps.ControllerRevision, error) { + obj, err := scheme.Scheme.DeepCopy(revision) + if err != nil { + return nil, err + } + clone := obj.(*apps.ControllerRevision) + clone.Revision = newRevision + return clone, fh.indexer.Update(clone) +} + +func (fh *fakeHistory) AdoptControllerRevision(parent metav1.Object, parentKind schema.GroupVersionKind, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { + blockOwnerDeletion := true + isController := true + if owner := controller.GetControllerOf(revision); owner != nil { + return nil, fmt.Errorf("attempt to adopt revision owned by %v", owner) + } + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision) + if err != nil { + return nil, err + } + _, found, err := fh.indexer.GetByKey(key) + if err != nil { + return nil, err + } + if !found { + return nil, errors.NewNotFound(apps.Resource("controllerrevisions"), revision.Name) + } + obj2, err := scheme.Scheme.DeepCopy(revision) + if err != nil { + return nil, err + } + clone := obj2.(*apps.ControllerRevision) + clone.OwnerReferences = append(clone.OwnerReferences, metav1.OwnerReference{ + APIVersion: parentKind.GroupVersion().String(), + Kind: parentKind.Kind, + Name: parent.GetName(), + UID: parent.GetUID(), + BlockOwnerDeletion: &blockOwnerDeletion, + Controller: &isController, + }) + return clone, fh.indexer.Update(clone) + +} + +func (fh *fakeHistory) ReleaseControllerRevision(parent metav1.Object, revision *apps.ControllerRevision) (*apps.ControllerRevision, error) { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(revision) + if err != nil { + return nil, err + } + _, found, err := fh.indexer.GetByKey(key) + if err != nil { + return nil, err + } + if !found { + return nil, nil + } + obj2, err := scheme.Scheme.DeepCopy(revision) + if err != nil { + return nil, err + } + clone := obj2.(*apps.ControllerRevision) + refs := clone.OwnerReferences + clone.OwnerReferences = nil + for i := range refs { + if refs[i].UID != parent.GetUID() { + clone.OwnerReferences = append(clone.OwnerReferences, refs[i]) + } + } + return clone, fh.indexer.Update(clone) +} diff --git a/pkg/controller/history/controller_history_test.go b/pkg/controller/history/controller_history_test.go new file mode 100644 index 00000000000..aaf6fb5f91f --- /dev/null +++ b/pkg/controller/history/controller_history_test.go @@ -0,0 +1,1687 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package history + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "testing" + + "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/api/v1" + apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" + "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" + informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions" + "k8s.io/kubernetes/pkg/controller" + + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + + core "k8s.io/client-go/testing" +) + +func TestRealHistory_ListControllerRevisions(t *testing.T) { + type testcase struct { + name string + parent metav1.Object + selector labels.Selector + revisions []*apps.ControllerRevision + want map[string]bool + } + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + for i := range test.revisions { + informer.Informer().GetIndexer().Add(test.revisions[i]) + } + + history := NewHistory(client, informer.Lister()) + revisions, err := history.ListControllerRevisions(test.parent, test.selector) + if err != nil { + t.Errorf("%s: %s", test.name, err) + } + got := make(map[string]bool) + for i := range revisions { + got[revisions[i].Name] = true + } + if !reflect.DeepEqual(test.want, got) { + t.Errorf("%s: want %v got %v", test.name, test.want, got) + } + } + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + ss1Orphan, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 3) + if err != nil { + t.Fatal(err) + } + ss1Orphan.Namespace = ss1.Namespace + ss1Orphan.OwnerReferences = nil + + tests := []testcase{ + { + name: "selects none", + parent: &ss1.ObjectMeta, + selector: sel1, + revisions: nil, + want: map[string]bool{}, + }, + { + name: "selects all", + parent: &ss1.ObjectMeta, + selector: sel1, + revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2}, + want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true}, + }, + { + name: "doesn't select another Objects history", + parent: &ss1.ObjectMeta, + selector: sel1, + revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2, ss2Rev1}, + want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true}, + }, + { + name: "selects orphans", + parent: &ss1.ObjectMeta, + selector: sel1, + revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2, ss1Orphan}, + want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true, ss1Orphan.Name: true}, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestFakeHistory_ListControllerRevisions(t *testing.T) { + type testcase struct { + name string + parent metav1.Object + selector labels.Selector + revisions []*apps.ControllerRevision + want map[string]bool + } + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + for i := range test.revisions { + informer.Informer().GetIndexer().Add(test.revisions[i]) + } + + history := NewFakeHistory(informer) + revisions, err := history.ListControllerRevisions(test.parent, test.selector) + if err != nil { + t.Errorf("%s: %s", test.name, err) + } + got := make(map[string]bool) + for i := range revisions { + got[revisions[i].Name] = true + } + if !reflect.DeepEqual(test.want, got) { + t.Errorf("%s: want %v got %v", test.name, test.want, got) + } + } + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + ss1Orphan, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 3) + if err != nil { + t.Fatal(err) + } + ss1Orphan.Namespace = ss1.Namespace + ss1Orphan.OwnerReferences = nil + + tests := []testcase{ + { + name: "selects none", + parent: &ss1.ObjectMeta, + selector: sel1, + revisions: nil, + want: map[string]bool{}, + }, + { + name: "selects all", + parent: &ss1.ObjectMeta, + selector: sel1, + revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2}, + want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true}, + }, + { + name: "doesn't select another Objects history", + parent: &ss1.ObjectMeta, + selector: sel1, + revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2, ss2Rev1}, + want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true}, + }, + { + name: "selects orphans", + parent: &ss1.ObjectMeta, + selector: sel1, + revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2, ss1Orphan}, + want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true, ss1Orphan.Name: true}, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestRealHistory_CreateControllerRevision(t *testing.T) { + type testcase struct { + name string + parent metav1.Object + revision *apps.ControllerRevision + existing []struct { + parent metav1.Object + revision *apps.ControllerRevision + } + rename bool + } + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + history := NewHistory(client, informer.Lister()) + for i := range test.existing { + _, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision) + if err != nil { + t.Fatal(err) + } + } + created, err := history.CreateControllerRevision(test.parent, test.revision) + if err != nil { + t.Errorf("%s: %s", test.name, err) + } + if test.rename && created.Name == test.revision.Name { + t.Errorf("%s: wanted rename got %s %s", test.name, created.Name, test.revision.Name) + + } + if !test.rename && created.Name != test.revision.Name { + t.Errorf("%s: wanted %s got %s", test.name, test.revision.Name, created.Name) + } + } + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + tests := []testcase{ + { + name: "creates new", + parent: &ss1.ObjectMeta, + revision: ss1Rev1, + existing: nil, + + rename: false, + }, + { + name: "create doesn't conflict when parents differ", + parent: &ss2.ObjectMeta, + revision: ss2Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + + rename: false, + }, + { + name: "create renames on conflict", + parent: &ss1.ObjectMeta, + revision: ss1Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + rename: true, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestFakeHistory_CreateControllerRevision(t *testing.T) { + type testcase struct { + name string + parent metav1.Object + revision *apps.ControllerRevision + existing []struct { + parent metav1.Object + revision *apps.ControllerRevision + } + rename bool + } + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + history := NewFakeHistory(informer) + for i := range test.existing { + _, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision) + if err != nil { + t.Fatal(err) + } + } + created, err := history.CreateControllerRevision(test.parent, test.revision) + if err != nil { + t.Errorf("%s: %s", test.name, err) + } + if test.rename && created.Name == test.revision.Name { + t.Errorf("%s: wanted rename got %s %s", test.name, created.Name, test.revision.Name) + + } + if !test.rename && created.Name != test.revision.Name { + t.Errorf("%s: wanted %s got %s", test.name, test.revision.Name, created.Name) + } + } + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + tests := []testcase{ + { + name: "creates new", + parent: &ss1.ObjectMeta, + revision: ss1Rev1, + existing: nil, + + rename: false, + }, + { + name: "create doesn't conflict when parents differ", + parent: &ss2.ObjectMeta, + revision: ss2Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + + rename: false, + }, + { + name: "create renames on conflict", + parent: &ss1.ObjectMeta, + revision: ss1Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + rename: true, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestRealHistory_UpdateControllerRevision(t *testing.T) { + conflictAttempts := 0 + type testcase struct { + name string + revision *apps.ControllerRevision + newRevision int64 + existing []struct { + parent metav1.Object + revision *apps.ControllerRevision + } + reactor core.ReactionFunc + err bool + } + conflictSuccess := func(action core.Action) (bool, runtime.Object, error) { + defer func() { + conflictAttempts++ + }() + switch action.(type) { + + case core.UpdateActionImpl: + update := action.(core.UpdateAction) + if conflictAttempts < 2 { + return true, update.GetObject(), errors.NewConflict(update.GetResource().GroupResource(), "", fmt.Errorf("conflict")) + } + return true, update.GetObject(), nil + default: + return false, nil, nil + } + } + internalError := func(action core.Action) (bool, runtime.Object, error) { + switch action.(type) { + case core.UpdateActionImpl: + return true, nil, errors.NewInternalError(fmt.Errorf("internal error")) + default: + return false, nil, nil + } + } + + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + history := NewHistory(client, informer.Lister()) + for i := range test.existing { + _, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision) + if err != nil { + t.Fatal(err) + } + } + if test.reactor != nil { + client.PrependReactor("*", "*", test.reactor) + } + updated, err := history.UpdateControllerRevision(test.revision, test.newRevision) + if !test.err && err != nil { + t.Errorf("%s: %s", test.name, err) + } + if !test.err && updated.Revision != test.newRevision { + t.Errorf("%s: got %d want %d", test.name, updated.Revision, test.newRevision) + } + if test.err && err == nil { + t.Errorf("%s: expected error", test.name) + } + } + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + + tests := []testcase{ + { + name: "update succeeds", + revision: ss1Rev1, + newRevision: ss1Rev1.Revision + 1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + reactor: nil, + err: false, + }, + { + name: "update succeeds no noop", + revision: ss1Rev1, + newRevision: ss1Rev1.Revision, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + reactor: nil, + err: false, + }, { + name: "update fails on error", + revision: ss1Rev1, + newRevision: ss1Rev1.Revision + 10, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + reactor: internalError, + err: true, + }, + { + name: "update on succeeds on conflict", + revision: ss1Rev1, + newRevision: ss1Rev1.Revision + 1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + reactor: conflictSuccess, + err: false, + }, + } + for i := range tests { + conflictAttempts = 0 + testFn(&tests[i], t) + } +} + +func TestFakeHistory_UpdateControllerRevision(t *testing.T) { + type testcase struct { + name string + revision *apps.ControllerRevision + newRevision int64 + existing []struct { + parent metav1.Object + revision *apps.ControllerRevision + } + err bool + } + + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + history := NewFakeHistory(informer) + for i := range test.existing { + _, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision) + if err != nil { + t.Fatal(err) + } + } + updated, err := history.UpdateControllerRevision(test.revision, test.newRevision) + if !test.err && err != nil { + t.Errorf("%s: %s", test.name, err) + } + if !test.err && updated.Revision != test.newRevision { + t.Errorf("%s: got %d want %d", test.name, updated.Revision, test.newRevision) + } + if test.err && err == nil { + t.Errorf("%s: expected error", test.name) + } + } + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + tests := []testcase{ + { + name: "update succeeds", + revision: ss1Rev1, + newRevision: ss1Rev1.Revision + 1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + err: false, + }, + { + name: "update succeeds no noop", + revision: ss1Rev1, + newRevision: ss1Rev1.Revision, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + err: false, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestRealHistory_DeleteControllerRevision(t *testing.T) { + type testcase struct { + name string + revision *apps.ControllerRevision + existing []struct { + parent metav1.Object + revision *apps.ControllerRevision + } + err bool + } + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + history := NewHistory(client, informer.Lister()) + for i := range test.existing { + _, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision) + if err != nil { + t.Fatal(err) + } + } + err := history.DeleteControllerRevision(test.revision) + if !test.err && err != nil { + t.Errorf("%s: %s", test.name, err) + } + if test.err && err == nil { + t.Errorf("%s: expected error", test.name) + } + } + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + ss2Rev2, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss2Rev2.Namespace = ss2.Namespace + tests := []testcase{ + { + name: "delete empty fails", + revision: ss1Rev1, + existing: nil, + err: true, + }, + { + name: "delete existing succeeds", + revision: ss1Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + err: false, + }, { + name: "delete non-existing fails", + revision: ss1Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss2, + revision: ss2Rev1, + }, + { + parent: ss2, + revision: ss2Rev2, + }, + }, + err: true, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestFakeHistory_DeleteControllerRevision(t *testing.T) { + type testcase struct { + name string + revision *apps.ControllerRevision + existing []struct { + parent metav1.Object + revision *apps.ControllerRevision + } + err bool + } + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + history := NewFakeHistory(informer) + for i := range test.existing { + _, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision) + if err != nil { + t.Fatal(err) + } + } + err := history.DeleteControllerRevision(test.revision) + if !test.err && err != nil { + t.Errorf("%s: %s", test.name, err) + } + if test.err && err == nil { + t.Errorf("%s: expected error", test.name) + } + } + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + ss2Rev2, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss2Rev2.Namespace = ss2.Namespace + tests := []testcase{ + { + name: "delete empty fails", + revision: ss1Rev1, + existing: nil, + err: true, + }, + { + name: "delete existing succeeds", + revision: ss1Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + err: false, + }, { + name: "delete non-existing fails", + revision: ss1Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss2, + revision: ss2Rev1, + }, + { + parent: ss2, + revision: ss2Rev2, + }, + }, + err: true, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestRealHistory_AdoptControllerRevision(t *testing.T) { + type testcase struct { + name string + parent metav1.Object + revision *apps.ControllerRevision + existing []struct { + parent metav1.Object + revision *apps.ControllerRevision + } + err bool + } + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + client.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) { + switch action := action.(type) { + case core.PatchActionImpl: + var found *apps.ControllerRevision + for i := range test.existing { + if test.revision.Name == test.existing[i].revision.Name && + test.revision.Namespace == test.existing[i].revision.Namespace { + found = test.existing[i].revision + break + } + } + if found == nil { + return true, nil, errors.NewNotFound(apps.Resource("controllerrevisions"), test.revision.Name) + } + b, err := strategicpatch.StrategicMergePatch( + []byte(runtime.EncodeOrDie(testapi.Apps.Codec(), test.revision)), + action.GetPatch(), test.revision) + if err != nil { + return true, nil, err + } + obj, err := runtime.Decode(testapi.Apps.Codec(), b) + if err != nil { + return true, nil, err + } + patched, err := testapi.Apps.Converter().ConvertToVersion(obj, apps.SchemeGroupVersion) + if err != nil { + return true, nil, err + } + return true, patched, err + default: + return false, nil, nil + } + + }) + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + + history := NewHistory(client, informer.Lister()) + for i := range test.existing { + _, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision) + if err != nil { + t.Fatal(err) + } + } + adopted, err := history.AdoptControllerRevision(test.parent, parentKind, test.revision) + if !test.err && err != nil { + t.Errorf("%s: %s", test.name, err) + } + if !test.err && controller.GetControllerOf(adopted).UID != test.parent.GetUID() { + t.Errorf("%s: adoption failed", test.name) + } + if test.err && err == nil { + t.Errorf("%s: expected error", test.name) + } + } + + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss1Rev2.OwnerReferences = []metav1.OwnerReference{} + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + tests := []testcase{ + { + name: "adopting an orphan succeeds", + parent: ss1, + revision: ss1Rev2, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev2, + }, + }, + err: false, + }, + { + name: "adopting an owned revision fails", + parent: ss1, + revision: ss2Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss2, + revision: ss2Rev1, + }, + }, + err: true, + }, + { + name: "adopting a non-existent revision fails", + parent: ss1, + revision: ss1Rev2, + existing: nil, + err: true, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestFakeHistory_AdoptControllerRevision(t *testing.T) { + type testcase struct { + name string + parent metav1.Object + parentType *metav1.TypeMeta + revision *apps.ControllerRevision + existing []struct { + parent metav1.Object + revision *apps.ControllerRevision + } + err bool + } + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + + history := NewFakeHistory(informer) + for i := range test.existing { + _, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision) + if err != nil { + t.Fatal(err) + } + } + adopted, err := history.AdoptControllerRevision(test.parent, parentKind, test.revision) + if !test.err && err != nil { + t.Errorf("%s: %s", test.name, err) + } + if !test.err && controller.GetControllerOf(adopted).UID != test.parent.GetUID() { + t.Errorf("%s: adoption failed", test.name) + } + if test.err && err == nil { + t.Errorf("%s: expected error", test.name) + } + } + + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss1Rev2.OwnerReferences = []metav1.OwnerReference{} + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + tests := []testcase{ + { + name: "adopting an orphan succeeds", + parent: ss1, + parentType: &ss1.TypeMeta, + revision: ss1Rev2, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev2, + }, + }, + err: false, + }, + { + name: "adopting an owned revision fails", + parent: ss1, + parentType: &ss1.TypeMeta, + revision: ss2Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss2, + revision: ss2Rev1, + }, + }, + err: true, + }, + { + name: "adopting a non-existent revision fails", + parent: ss1, + parentType: &ss1.TypeMeta, + revision: ss1Rev2, + existing: nil, + err: true, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestRealHistory_ReleaseControllerRevision(t *testing.T) { + type testcase struct { + name string + parent metav1.Object + revision *apps.ControllerRevision + existing []struct { + parent metav1.Object + revision *apps.ControllerRevision + } + err bool + } + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + client.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) { + switch action := action.(type) { + case core.PatchActionImpl: + var found *apps.ControllerRevision + for i := range test.existing { + if test.revision.Name == test.existing[i].revision.Name && + test.revision.Namespace == test.existing[i].revision.Namespace { + found = test.existing[i].revision + break + } + } + if found == nil { + return true, nil, errors.NewNotFound(apps.Resource("controllerrevisions"), test.revision.Name) + } + if foundParent := controller.GetControllerOf(test.revision); foundParent == nil || + foundParent.UID != test.parent.GetUID() { + return true, nil, errors.NewInvalid( + test.revision.GroupVersionKind().GroupKind(), test.revision.Name, nil) + } + b, err := strategicpatch.StrategicMergePatch( + []byte(runtime.EncodeOrDie(testapi.Apps.Codec(), test.revision)), + action.GetPatch(), test.revision) + if err != nil { + return true, nil, err + } + obj, err := runtime.Decode(testapi.Apps.Codec(), b) + if err != nil { + return true, nil, err + } + patched, err := testapi.Apps.Converter().ConvertToVersion(obj, apps.SchemeGroupVersion) + if err != nil { + return true, nil, err + } + return true, patched, err + default: + return false, nil, nil + } + + }) + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + + history := NewHistory(client, informer.Lister()) + for i := range test.existing { + _, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision) + if err != nil { + t.Fatal(err) + } + } + adopted, err := history.ReleaseControllerRevision(test.parent, test.revision) + if !test.err { + if err != nil { + t.Errorf("%s: %s", test.name, err) + } + if adopted == nil { + return + } + if owner := controller.GetControllerOf(adopted); owner != nil && owner.UID == test.parent.GetUID() { + t.Errorf("%s: release failed", test.name) + } + } + if test.err && err == nil { + t.Errorf("%s: expected error", test.name) + } + } + + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss1Rev2.OwnerReferences = []metav1.OwnerReference{} + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + tests := []testcase{ + { + name: "releasing an owned revision succeeds", + parent: ss1, + revision: ss1Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + err: false, + }, + { + name: "releasing an orphan succeeds", + parent: ss1, + revision: ss1Rev2, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev2, + }, + }, + err: false, + }, + { + name: "releasing a revision owned by another controller succeeds", + parent: ss1, + revision: ss2Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss2, + revision: ss2Rev1, + }, + }, + err: false, + }, + { + name: "releasing a non-existent revision succeeds", + parent: ss1, + revision: ss1Rev1, + existing: nil, + err: false, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestFakeHistory_ReleaseControllerRevision(t *testing.T) { + type testcase struct { + name string + parent metav1.Object + revision *apps.ControllerRevision + existing []struct { + parent metav1.Object + revision *apps.ControllerRevision + } + err bool + } + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + informer := informerFactory.Apps().V1beta1().ControllerRevisions() + informerFactory.WaitForCacheSync(stop) + history := NewFakeHistory(informer) + for i := range test.existing { + _, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision) + if err != nil { + t.Fatal(err) + } + } + adopted, err := history.ReleaseControllerRevision(test.parent, test.revision) + if !test.err { + if err != nil { + t.Errorf("%s: %s", test.name, err) + } + if adopted == nil { + return + } + if owner := controller.GetControllerOf(adopted); owner != nil && owner.UID == test.parent.GetUID() { + t.Errorf("%s: release failed", test.name) + } + } + if test.err && err == nil { + t.Errorf("%s: expected error", test.name) + } + } + + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss1Rev2.OwnerReferences = []metav1.OwnerReference{} + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + tests := []testcase{ + { + name: "releasing an owned revision succeeds", + parent: ss1, + revision: ss1Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev1, + }, + }, + err: false, + }, + { + name: "releasing an orphan succeeds", + parent: ss1, + revision: ss1Rev2, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss1, + revision: ss1Rev2, + }, + }, + err: false, + }, + { + name: "releasing a revision owned by another controller succeeds", + parent: ss1, + revision: ss2Rev1, + existing: []struct { + parent metav1.Object + revision *apps.ControllerRevision + }{ + { + parent: ss2, + revision: ss2Rev1, + }, + }, + err: false, + }, + { + name: "releasing a non-existent revision succeeds", + parent: ss1, + revision: ss1Rev1, + existing: nil, + err: false, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestFindEqualRevisions(t *testing.T) { + type testcase struct { + name string + revision *apps.ControllerRevision + revisions []*apps.ControllerRevision + want map[string]bool + } + testFn := func(test *testcase, t *testing.T) { + found := FindEqualRevisions(test.revisions, test.revision) + if len(found) != len(test.want) { + t.Errorf("%s: want %d revisions found %d", test.name, len(test.want), len(found)) + } + for i := range found { + if !test.want[found[i].Name] { + t.Errorf("%s: wanted %s not found", test.name, found[i].Name) + } + + } + } + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector) + if err != nil { + t.Fatal(err) + } + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss1Rev2.OwnerReferences = []metav1.OwnerReference{} + ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss2Rev1.Namespace = ss2.Namespace + ss2Rev2, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss2Rev2.Namespace = ss2.Namespace + tests := []testcase{ + { + name: "finds equivalent", + revision: ss1Rev1, + revisions: []*apps.ControllerRevision{ss1Rev1, ss2Rev1, ss2Rev2}, + want: map[string]bool{ss1Rev1.Name: true}, + }, + { + name: "finds nothing when empty", + revision: ss1Rev1, + revisions: nil, + want: map[string]bool{}, + }, + { + name: "finds nothing with no matches", + revision: ss1Rev1, + revisions: []*apps.ControllerRevision{ss2Rev2, ss2Rev1}, + want: map[string]bool{}, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestSortControllerRevisions(t *testing.T) { + type testcase struct { + name string + revisions []*apps.ControllerRevision + want []string + } + testFn := func(test *testcase, t *testing.T) { + SortControllerRevisions(test.revisions) + for i := range test.revisions { + if test.revisions[i].Name != test.want[i] { + t.Errorf("%s: want %s at %d got %s", test.name, test.want[i], i, test.revisions[i].Name) + } + } + } + ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"}) + sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector) + if err != nil { + t.Fatal(err) + } + + ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1) + if err != nil { + t.Fatal(err) + } + ss1Rev1.Namespace = ss1.Namespace + ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev2.Namespace = ss1.Namespace + ss1Rev3, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2) + if err != nil { + t.Fatal(err) + } + ss1Rev3.Namespace = ss1.Namespace + + tests := []testcase{ + { + name: "out of order", + revisions: []*apps.ControllerRevision{ss1Rev2, ss1Rev1, ss1Rev3}, + want: []string{ss1Rev1.Name, ss1Rev2.Name, ss1Rev3.Name}, + }, + { + name: "sorted", + revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2, ss1Rev3}, + want: []string{ss1Rev1.Name, ss1Rev2.Name, ss1Rev3.Name}, + }, + { + name: "reversed", + revisions: []*apps.ControllerRevision{ss1Rev3, ss1Rev2, ss1Rev1}, + want: []string{ss1Rev1.Name, ss1Rev2.Name, ss1Rev3.Name}, + }, + { + name: "empty", + revisions: nil, + want: nil, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func newStatefulSet(replicas int, name string, uid types.UID, labels map[string]string) *apps.StatefulSet { + return &apps.StatefulSet{ + TypeMeta: metav1.TypeMeta{ + Kind: "StatefulSet", + APIVersion: "apps/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: v1.NamespaceDefault, + UID: uid, + }, + Spec: apps.StatefulSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: labels, + }, + Replicas: func() *int32 { i := int32(replicas); return &i }(), + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "nginx", + Image: "nginx", + VolumeMounts: []v1.VolumeMount{ + {Name: "datadir", MountPath: "/tmp/"}, + {Name: "home", MountPath: "/home"}, + }, + }, + }, + Volumes: []v1.Volume{{ + Name: "home", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: fmt.Sprintf("/tmp/%v", "home"), + }, + }}}, + }, + }, + VolumeClaimTemplates: []v1.PersistentVolumeClaim{ + { + ObjectMeta: metav1.ObjectMeta{Name: "datadir"}, + Spec: v1.PersistentVolumeClaimSpec{ + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceStorage: *resource.NewQuantity(1, resource.BinarySI), + }, + }, + }, + }, + }, + ServiceName: "governingsvc", + }, + } +} + +var parentKind = apps.SchemeGroupVersion.WithKind("StatefulSet") + +func rawTemplate(template *v1.PodTemplateSpec) runtime.RawExtension { + buf := new(bytes.Buffer) + enc := json.NewEncoder(buf) + if err := enc.Encode(template); err != nil { + panic(err) + } + return runtime.RawExtension{Raw: buf.Bytes()} +} diff --git a/pkg/controller/statefulset/BUILD b/pkg/controller/statefulset/BUILD index dd64bae616d..c92d595db24 100644 --- a/pkg/controller/statefulset/BUILD +++ b/pkg/controller/statefulset/BUILD @@ -14,6 +14,7 @@ go_library( "stateful_pod_control.go", "stateful_set.go", "stateful_set_control.go", + "stateful_set_status_updater.go", "stateful_set_utils.go", ], tags = ["automanaged"], @@ -29,12 +30,15 @@ go_library( "//pkg/client/listers/core/v1:go_default_library", "//pkg/client/retry:go_default_library", "//pkg/controller:go_default_library", + "//pkg/controller/history:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library", "//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library", @@ -50,6 +54,7 @@ go_test( srcs = [ "stateful_pod_control_test.go", "stateful_set_control_test.go", + "stateful_set_status_updater_test.go", "stateful_set_test.go", "stateful_set_utils_test.go", ], @@ -68,6 +73,7 @@ go_test( "//pkg/client/listers/apps/v1beta1:go_default_library", "//pkg/client/listers/core/v1:go_default_library", "//pkg/controller:go_default_library", + "//pkg/controller/history:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", @@ -77,6 +83,7 @@ go_test( "//vendor/k8s.io/client-go/testing:go_default_library", "//vendor/k8s.io/client-go/tools/cache:go_default_library", "//vendor/k8s.io/client-go/tools/record:go_default_library", + "//vendor/k8s.io/metrics/pkg/client/clientset_generated/clientset/scheme:go_default_library", ], ) diff --git a/pkg/controller/statefulset/stateful_pod_control.go b/pkg/controller/statefulset/stateful_pod_control.go index 2b80901966c..79ecde4702b 100644 --- a/pkg/controller/statefulset/stateful_pod_control.go +++ b/pkg/controller/statefulset/stateful_pod_control.go @@ -49,10 +49,6 @@ type StatefulPodControlInterface interface { // DeleteStatefulPod deletes a Pod in a StatefulSet. The pods PVCs are not deleted. If the delete is successful, // the returned error is nil. DeleteStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error - // UpdateStatefulSetStatus updates the status of a StatefulSet. set is an in-out parameter, and any - // updates made to the set are made visible as mutations to the parameter. If the method is successful, the - // returned error is nil, and set has its status updated. - UpdateStatefulSetStatus(set *apps.StatefulSet, replicas int32, generation int64) error } func NewRealStatefulPodControl( @@ -93,7 +89,6 @@ func (spc *realStatefulPodControl) CreateStatefulPod(set *apps.StatefulSet, pod func (spc *realStatefulPodControl) UpdateStatefulPod(set *apps.StatefulSet, pod *v1.Pod) error { attemptedUpdate := false - err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { // assume the Pod is consistent consistent := true @@ -149,30 +144,6 @@ func (spc *realStatefulPodControl) DeleteStatefulPod(set *apps.StatefulSet, pod return err } -func (spc *realStatefulPodControl) UpdateStatefulSetStatus(set *apps.StatefulSet, replicas int32, generation int64) error { - return retry.RetryOnConflict(retry.DefaultBackoff, func() error { - set.Status.Replicas = replicas - set.Status.ObservedGeneration = &generation - _, updateErr := spc.client.Apps().StatefulSets(set.Namespace).UpdateStatus(set) - if updateErr == nil { - return nil - } - - if updated, err := spc.setLister.StatefulSets(set.Namespace).Get(set.Name); err == nil { - // make a copy so we don't mutate the shared cache - if copy, err := scheme.Scheme.DeepCopy(updated); err == nil { - set = copy.(*apps.StatefulSet) - } else { - utilruntime.HandleError(fmt.Errorf("error copying updated StatefulSet: %v", err)) - } - } else { - utilruntime.HandleError(fmt.Errorf("error getting updated StatefulSet %s/%s from lister: %v", set.Namespace, set.Name, err)) - } - - return updateErr - }) -} - // recordPodEvent records an event for verb applied to a Pod in a StatefulSet. If err is nil the generated event will // have a reason of v1.EventTypeNormal. If err is not nil the generated event will have a reason of v1.EventTypeWarning. func (spc *realStatefulPodControl) recordPodEvent(verb string, set *apps.StatefulSet, pod *v1.Pod, err error) { diff --git a/pkg/controller/statefulset/stateful_pod_control_test.go b/pkg/controller/statefulset/stateful_pod_control_test.go index df557cd4228..0e376785b42 100644 --- a/pkg/controller/statefulset/stateful_pod_control_test.go +++ b/pkg/controller/statefulset/stateful_pod_control_test.go @@ -29,9 +29,7 @@ import ( "k8s.io/client-go/tools/record" "k8s.io/kubernetes/pkg/api/v1" - apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" - appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1" corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1" ) @@ -461,116 +459,6 @@ func TestStatefulPodControlDeleteFailure(t *testing.T) { } } -func TestStatefulPodControlUpdatesSetStatus(t *testing.T) { - recorder := record.NewFakeRecorder(10) - set := newStatefulSet(3) - fakeClient := &fake.Clientset{} - control := NewRealStatefulPodControl(fakeClient, nil, nil, nil, recorder) - fakeClient.AddReactor("update", "statefulsets", func(action core.Action) (bool, runtime.Object, error) { - update := action.(core.UpdateAction) - return true, update.GetObject(), nil - }) - if err := control.UpdateStatefulSetStatus(set, 2, 1); err != nil { - t.Errorf("Error returned on successful status update: %s", err) - } - if set.Status.Replicas != 2 { - t.Errorf("UpdateStatefulSetStatus mutated the sets replicas %d", set.Status.Replicas) - } - events := collectEvents(recorder.Events) - if eventCount := len(events); eventCount != 0 { - t.Errorf("Expected 0 events for successful status update %d", eventCount) - } -} - -func TestStatefulPodControlUpdatesObservedGeneration(t *testing.T) { - recorder := record.NewFakeRecorder(10) - set := newStatefulSet(3) - fakeClient := &fake.Clientset{} - control := NewRealStatefulPodControl(fakeClient, nil, nil, nil, recorder) - fakeClient.AddReactor("update", "statefulsets", func(action core.Action) (bool, runtime.Object, error) { - update := action.(core.UpdateAction) - sts := update.GetObject().(*apps.StatefulSet) - if sts.Status.ObservedGeneration == nil || *sts.Status.ObservedGeneration != int64(3) { - t.Errorf("expected observedGeneration to be synced with generation for statefulset %q", sts.Name) - } - return true, sts, nil - }) - if err := control.UpdateStatefulSetStatus(set, 2, 3); err != nil { - t.Errorf("Error returned on successful status update: %s", err) - } -} - -func TestStatefulPodControlUpdateReplicasFailure(t *testing.T) { - recorder := record.NewFakeRecorder(10) - set := newStatefulSet(3) - fakeClient := &fake.Clientset{} - indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) - indexer.Add(set) - setLister := appslisters.NewStatefulSetLister(indexer) - control := NewRealStatefulPodControl(fakeClient, setLister, nil, nil, recorder) - fakeClient.AddReactor("update", "statefulsets", func(action core.Action) (bool, runtime.Object, error) { - return true, nil, apierrors.NewInternalError(errors.New("API server down")) - }) - if err := control.UpdateStatefulSetStatus(set, 2, 1); err == nil { - t.Error("Failed update did not return error") - } - events := collectEvents(recorder.Events) - if eventCount := len(events); eventCount != 0 { - t.Errorf("Expected 0 events for successful status update %d", eventCount) - } -} - -func TestStatefulPodControlUpdateReplicasConflict(t *testing.T) { - recorder := record.NewFakeRecorder(10) - set := newStatefulSet(3) - conflict := false - fakeClient := &fake.Clientset{} - indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) - indexer.Add(set) - setLister := appslisters.NewStatefulSetLister(indexer) - control := NewRealStatefulPodControl(fakeClient, setLister, nil, nil, recorder) - fakeClient.AddReactor("update", "statefulsets", func(action core.Action) (bool, runtime.Object, error) { - update := action.(core.UpdateAction) - if !conflict { - conflict = true - return true, update.GetObject(), apierrors.NewConflict(action.GetResource().GroupResource(), set.Name, errors.New("Object already exists")) - } else { - return true, update.GetObject(), nil - } - }) - if err := control.UpdateStatefulSetStatus(set, 2, 1); err != nil { - t.Errorf("UpdateStatefulSetStatus returned an error: %s", err) - } - if set.Status.Replicas != 2 { - t.Errorf("UpdateStatefulSetStatus mutated the sets replicas %d", set.Status.Replicas) - } - events := collectEvents(recorder.Events) - if eventCount := len(events); eventCount != 0 { - t.Errorf("Expected 0 events for successful status update %d", eventCount) - } -} - -func TestStatefulPodControlUpdateReplicasConflictFailure(t *testing.T) { - recorder := record.NewFakeRecorder(10) - set := newStatefulSet(3) - fakeClient := &fake.Clientset{} - indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) - indexer.Add(set) - setLister := appslisters.NewStatefulSetLister(indexer) - control := NewRealStatefulPodControl(fakeClient, setLister, nil, nil, recorder) - fakeClient.AddReactor("update", "statefulsets", func(action core.Action) (bool, runtime.Object, error) { - update := action.(core.UpdateAction) - return true, update.GetObject(), apierrors.NewConflict(action.GetResource().GroupResource(), set.Name, errors.New("Object already exists")) - }) - if err := control.UpdateStatefulSetStatus(set, 2, 1); err == nil { - t.Error("UpdateStatefulSetStatus failed to return an error on get failure") - } - events := collectEvents(recorder.Events) - if eventCount := len(events); eventCount != 0 { - t.Errorf("Expected 0 events for successful status update %d", eventCount) - } -} - func collectEvents(source <-chan string) []string { done := false events := make([]string, 0) diff --git a/pkg/controller/statefulset/stateful_set.go b/pkg/controller/statefulset/stateful_set.go index f580e819552..7458e2eb7d3 100644 --- a/pkg/controller/statefulset/stateful_set.go +++ b/pkg/controller/statefulset/stateful_set.go @@ -40,6 +40,7 @@ import ( appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1" corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1" "k8s.io/kubernetes/pkg/controller" + "k8s.io/kubernetes/pkg/controller/history" "github.com/golang/glog" ) @@ -80,6 +81,7 @@ func NewStatefulSetController( podInformer coreinformers.PodInformer, setInformer appsinformers.StatefulSetInformer, pvcInformer coreinformers.PersistentVolumeClaimInformer, + revInformer appsinformers.ControllerRevisionInformer, kubeClient clientset.Interface, ) *StatefulSetController { eventBroadcaster := record.NewBroadcaster() @@ -95,8 +97,9 @@ func NewStatefulSetController( setInformer.Lister(), podInformer.Lister(), pvcInformer.Lister(), - recorder, - ), + recorder), + NewRealStatefulSetStatusUpdater(kubeClient, setInformer.Lister()), + history.NewHistory(kubeClient, revInformer.Lister()), ), pvcListerSynced: pvcInformer.Informer().HasSynced, queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "statefulset"), @@ -305,6 +308,32 @@ func (ssc *StatefulSetController) getPodsForStatefulSet(set *apps.StatefulSet, s return cm.ClaimPods(pods, filter) } +// adoptOrphanRevisions adopts any orphaned ControllerRevisions matched by set's Selector. +func (ssc *StatefulSetController) adoptOrphanRevisions(set *apps.StatefulSet) error { + revisions, err := ssc.control.ListRevisions(set) + if err != nil { + return err + } + hasOrphans := false + for i := range revisions { + if controller.GetControllerOf(revisions[i]) == nil { + hasOrphans = true + break + } + } + if hasOrphans { + fresh, err := ssc.kubeClient.AppsV1beta1().StatefulSets(set.Namespace).Get(set.Name, metav1.GetOptions{}) + if err != nil { + return err + } + if fresh.UID != set.UID { + return fmt.Errorf("original StatefulSet %v/%v is gone: got uid %v, wanted %v", set.Namespace, set.Name, fresh.UID, set.UID) + } + return ssc.control.AdoptOrphanRevisions(set, revisions) + } + return nil +} + // getStatefulSetsForPod returns a list of StatefulSets that potentially match // a given pod. func (ssc *StatefulSetController) getStatefulSetsForPod(pod *v1.Pod) []*apps.StatefulSet { @@ -406,6 +435,10 @@ func (ssc *StatefulSetController) sync(key string) error { return nil } + if err := ssc.adoptOrphanRevisions(set); err != nil { + return err + } + pods, err := ssc.getPodsForStatefulSet(set, selector) if err != nil { return err diff --git a/pkg/controller/statefulset/stateful_set_control.go b/pkg/controller/statefulset/stateful_set_control.go index a0923d45bbc..b70b943e398 100644 --- a/pkg/controller/statefulset/stateful_set_control.go +++ b/pkg/controller/statefulset/stateful_set_control.go @@ -17,12 +17,14 @@ limitations under the License. package statefulset import ( - "fmt" + "math" "sort" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" "k8s.io/kubernetes/pkg/api/v1" apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" + "k8s.io/kubernetes/pkg/controller/history" "github.com/golang/glog" ) @@ -36,18 +38,30 @@ type StatefulSetControlInterface interface { // Implementors should sink any errors that they do not wish to trigger a retry, and they may feel free to // exit exceptionally at any point provided they wish the update to be re-run at a later point in time. UpdateStatefulSet(set *apps.StatefulSet, pods []*v1.Pod) error + // ListRevisions returns a array of the ControllerRevisions that represent the revisions of set. If the returned + // error is nil, the returns slice of ControllerRevisions is valid. + ListRevisions(set *apps.StatefulSet) ([]*apps.ControllerRevision, error) + // AdoptOrphanRevisions adopts any orphaned ControllerRevisions that match set's Selector. If all adoptions are + // successful the returned error is nil. + AdoptOrphanRevisions(set *apps.StatefulSet, revisions []*apps.ControllerRevision) error } // NewDefaultStatefulSetControl returns a new instance of the default implementation StatefulSetControlInterface that // implements the documented semantics for StatefulSets. podControl is the PodControlInterface used to create, update, -// and delete Pods and to create PersistentVolumeClaims. You should use an instance returned from -// NewRealStatefulPodControl() for any scenario other than testing. -func NewDefaultStatefulSetControl(podControl StatefulPodControlInterface) StatefulSetControlInterface { - return &defaultStatefulSetControl{podControl} +// and delete Pods and to create PersistentVolumeClaims. statusUpdater is the StatefulSetStatusUpdaterInterface used +// to update the status of StatefulSets. You should use an instance returned from NewRealStatefulPodControl() for any +// scenario other than testing. +func NewDefaultStatefulSetControl( + podControl StatefulPodControlInterface, + statusUpdater StatefulSetStatusUpdaterInterface, + controllerHistory history.Interface) StatefulSetControlInterface { + return &defaultStatefulSetControl{podControl, statusUpdater, controllerHistory} } type defaultStatefulSetControl struct { - podControl StatefulPodControlInterface + podControl StatefulPodControlInterface + statusUpdater StatefulSetStatusUpdaterInterface + controllerHistory history.Interface } // UpdateStatefulSet executes the core logic loop for a stateful set, applying the predictable and @@ -57,20 +71,223 @@ type defaultStatefulSetControl struct { // in no particular order. Clients using the burst strategy should be careful to ensure they // understand the consistency implications of having unpredictable numbers of pods available. func (ssc *defaultStatefulSetControl) UpdateStatefulSet(set *apps.StatefulSet, pods []*v1.Pod) error { + + // list all revisions and sort them + revisions, err := ssc.ListRevisions(set) + if err != nil { + return err + } + history.SortControllerRevisions(revisions) + + // get the current, and update revisions + currentRevision, updateRevision, err := ssc.getStatefulSetRevisions(set, revisions) + if err != nil { + return err + } + + // perform the main update function and get the status + status, err := ssc.updateStatefulSet(set, currentRevision, updateRevision, pods) + if err != nil { + return err + } + + // update the set's status + err = ssc.updateStatefulSetStatus(set, status) + if err != nil { + return err + } + + glog.V(4).Infof("StatefulSet %s/%s pod status replicas=%d ready=%d current=%d updated=%d", + set.Namespace, + set.Name, + status.Replicas, + status.ReadyReplicas, + status.CurrentReplicas, + status.UpdatedReplicas) + + glog.V(4).Infof("StatefulSet %s/%s revisions current=%s update=%s", + set.Namespace, + set.Name, + status.CurrentRevision, + status.UpdateRevision) + + // maintain the set's revision history limit + return ssc.truncateHistory(set, pods, revisions, currentRevision, updateRevision) +} + +func (ssc *defaultStatefulSetControl) ListRevisions(set *apps.StatefulSet) ([]*apps.ControllerRevision, error) { + selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) + if err != nil { + return nil, err + } + return ssc.controllerHistory.ListControllerRevisions(set, selector) +} + +func (ssc *defaultStatefulSetControl) AdoptOrphanRevisions( + set *apps.StatefulSet, + revisions []*apps.ControllerRevision) error { + for i := range revisions { + adopted, err := ssc.controllerHistory.AdoptControllerRevision(set, controllerKind, revisions[i]) + if err != nil { + return err + } + revisions[i] = adopted + } + return nil +} + +// truncateHistory truncates any non-live ControllerRevisions in revisions from set's history. The UpdateRevision and +// CurrentRevision in set's Status are considered to be live. Any revisions associated with the Pods in pods are also +// considered to be live. Non-live revisions are deleted, starting with the revision with the lowest Revision, until +// only RevisionHistoryLimit revisions remain. If the returned error is nil the operation was successful. This method +// expects that revisions is sorted when supplied. +func (ssc *defaultStatefulSetControl) truncateHistory( + set *apps.StatefulSet, + pods []*v1.Pod, + revisions []*apps.ControllerRevision, + current *apps.ControllerRevision, + update *apps.ControllerRevision) error { + history := make([]*apps.ControllerRevision, 0, len(revisions)) + // mark all live revisions + live := map[string]bool{current.Name: true, update.Name: true} + for i := range pods { + live[getPodRevision(pods[i])] = true + } + // collect live revisions and historic revisions + for i := range revisions { + if !live[revisions[i].Name] { + history = append(history, revisions[i]) + } + } + historyLen := len(history) + historyLimit := int(*set.Spec.RevisionHistoryLimit) + if historyLen <= historyLimit { + return nil + } + // delete any non-live history to maintain the revision limit. + history = history[:(historyLen - historyLimit)] + for i := 0; i < len(history); i++ { + if err := ssc.controllerHistory.DeleteControllerRevision(history[i]); err != nil { + return err + } + } + return nil +} + +// getStatefulSetRevisions returns the current and update ControllerRevisions for set. This method may create a new revision, +// or modify the Revision of an existing revision if an update to set is detected. This method expects that revisions +// is sorted when supplied. +func (ssc *defaultStatefulSetControl) getStatefulSetRevisions( + set *apps.StatefulSet, + revisions []*apps.ControllerRevision) (*apps.ControllerRevision, *apps.ControllerRevision, error) { + var currentRevision, updateRevision *apps.ControllerRevision + + revisionCount := len(revisions) + history.SortControllerRevisions(revisions) + + // create a new revision from the current set + updateRevision, err := newRevision(set, nextRevision(revisions)) + if err != nil { + return nil, nil, err + } + + // find any equivalent revisions + equalRevisions := history.FindEqualRevisions(revisions, updateRevision) + equalCount := len(equalRevisions) + + if equalCount > 0 && history.EqualRevision(revisions[revisionCount-1], equalRevisions[equalCount-1]) { + // if the equivalent revision is immediately prior the update revision has not changed + updateRevision = revisions[revisionCount-1] + } else if equalCount > 0 { + // if the equivalent revision is not immediately prior we will roll back by incrementing the + // Revision of the equivalent revision + updateRevision, err = ssc.controllerHistory.UpdateControllerRevision( + equalRevisions[equalCount-1], + updateRevision.Revision) + if err != nil { + return nil, nil, err + } + } else { + //if there is no equivalent revision we create a new one + updateRevision, err = ssc.controllerHistory.CreateControllerRevision(set, updateRevision) + if err != nil { + return nil, nil, err + } + } + + // attempt to find the revision that corresponds to the current revision + for i := range revisions { + if revisions[i].Name == set.Status.CurrentRevision { + currentRevision = revisions[i] + } + } + + // if the current revision is nil we initialize the history by setting it to the update revision + if currentRevision == nil { + currentRevision = updateRevision + } + + return currentRevision, updateRevision, nil +} + +// updateStatefulSet performs the update function for a StatefulSet. This method creates, updates, and deletes Pods in +// the set in order to conform the system to the target state for the set. The target state always contains +// set.Spec.Replicas Pods with a Ready Condition. If the UpdateStrategy.Type for the set is +// RollingUpdateStatefulSetStrategyType then all Pods in the set must be at set.Status.CurrentRevision. +// If the UpdateStrategy.Type for the set is OnDeleteStatefulSetStrategyType, the target state implies nothing about +// the revisions of Pods in the set. If the UpdateStrategy.Type for the set is PartitionStatefulSetStrategyType, then +// all Pods with ordinal less than UpdateStrategy.Partition.Ordinal must be at Status.CurrentRevision and all other +// Pods must be at Status.UpdateRevision. If the returned error is nil, the returned StatefulSetStatus is valid and the +// update must be recorded. If the error is not nil, the method should be retried until successful. +func (ssc *defaultStatefulSetControl) updateStatefulSet( + set *apps.StatefulSet, + currentRevision *apps.ControllerRevision, + updateRevision *apps.ControllerRevision, + pods []*v1.Pod) (*apps.StatefulSetStatus, error) { + // get the current and update revisions of the set. + currentSet, err := applyRevision(set, currentRevision) + if err != nil { + return nil, err + } + updateSet, err := applyRevision(set, updateRevision) + if err != nil { + return nil, err + } + + // set the generation, and revisions in the returned status + status := apps.StatefulSetStatus{} + status.ObservedGeneration = new(int64) + *status.ObservedGeneration = set.Generation + status.CurrentRevision = currentRevision.Name + status.UpdateRevision = updateRevision.Name + replicaCount := int(*set.Spec.Replicas) // slice that will contain all Pods such that 0 <= getOrdinal(pod) < set.Spec.Replicas replicas := make([]*v1.Pod, replicaCount) // slice that will contain all Pods such that set.Spec.Replicas <= getOrdinal(pod) condemned := make([]*v1.Pod, 0, len(pods)) - ready := 0 unhealthy := 0 + firstUnhealthyOrdinal := math.MaxInt32 + var firstUnhealthyPod *v1.Pod // First we partition pods into two lists valid replicas and condemned Pods for i := range pods { - //count the number of running and ready replicas + status.Replicas++ + + // count the number of running and ready replicas if isRunningAndReady(pods[i]) { - ready++ + status.ReadyReplicas++ } + + // count the number of current and update replicas + if isCreated(pods[i]) && !isTerminating(pods[i]) { + if getPodRevision(pods[i]) == currentRevision.Name { + status.CurrentReplicas++ + } else if getPodRevision(pods[i]) == updateRevision.Name { + status.UpdatedReplicas++ + } + } + if ord := getOrdinal(pods[i]); 0 <= ord && ord < replicaCount { // if the ordinal of the pod is within the range of the current number of replicas, // insert it at the indirection of its ordinal @@ -83,45 +300,53 @@ func (ssc *defaultStatefulSetControl) UpdateStatefulSet(set *apps.StatefulSet, p // If the ordinal could not be parsed (ord < 0), ignore the Pod. } - // for any empty indices in the sequence [0,set.Spec.Replicas) create a new Pod + // for any empty indices in the sequence [0,set.Spec.Replicas) create a new Pod at the correct revision for ord := 0; ord < replicaCount; ord++ { if replicas[ord] == nil { - replicas[ord] = newStatefulSetPod(set, ord) - } - } - - // count the number of unhealthy pods - for i := range replicas { - if !isHealthy(replicas[i]) { - unhealthy++ - } - } - for i := range condemned { - if !isHealthy(condemned[i]) { - unhealthy++ + replicas[ord] = newVersionedStatefulSetPod( + currentSet, + updateSet, + currentRevision.Name, + updateRevision.Name, ord) } } // sort the condemned Pods by their ordinals sort.Sort(ascendingOrdinal(condemned)) - // if the current number of replicas has changed update the statefulSets replicas - if set.Status.Replicas != int32(ready) || set.Status.ObservedGeneration == nil || set.Generation > *set.Status.ObservedGeneration { - obj, err := scheme.Scheme.Copy(set) - if err != nil { - return fmt.Errorf("unable to copy set: %v", err) + // find the first unhealthy Pod + for i := range replicas { + if !isHealthy(replicas[i]) { + unhealthy++ + if ord := getOrdinal(replicas[i]); ord < firstUnhealthyOrdinal { + firstUnhealthyOrdinal = ord + firstUnhealthyPod = replicas[i] + } } - set = obj.(*apps.StatefulSet) + } - if err := ssc.podControl.UpdateStatefulSetStatus(set, int32(ready), set.Generation); err != nil { - return err + for i := range condemned { + if !isHealthy(condemned[i]) { + unhealthy++ + if ord := getOrdinal(condemned[i]); ord < firstUnhealthyOrdinal { + firstUnhealthyOrdinal = ord + firstUnhealthyPod = condemned[i] + } } } + if unhealthy > 0 { + glog.V(4).Infof("StatefulSet %s/%s has %d unhealthy Pods starting with %s", + set.Namespace, + set.Name, + unhealthy, + firstUnhealthyPod.Name) + } + // If the StatefulSet is being deleted, don't do anything other than updating // status. if set.DeletionTimestamp != nil { - return nil + return &status, nil } monotonic := !allowsBurst(set) @@ -130,20 +355,41 @@ func (ssc *defaultStatefulSetControl) UpdateStatefulSet(set *apps.StatefulSet, p for i := range replicas { // delete and recreate failed pods if isFailed(replicas[i]) { - glog.V(2).Infof("StatefulSet %s is recreating failed Pod %s", set.Name, replicas[i].Name) + glog.V(4).Infof("StatefulSet %s/%s is recreating failed Pod %s", + set.Namespace, + set.Name, + replicas[i].Name) if err := ssc.podControl.DeleteStatefulPod(set, replicas[i]); err != nil { - return err + return &status, err } - replicas[i] = newStatefulSetPod(set, i) + if getPodRevision(replicas[i]) == currentRevision.Name { + status.CurrentReplicas-- + } else if getPodRevision(replicas[i]) == updateRevision.Name { + status.UpdatedReplicas-- + } + status.Replicas-- + replicas[i] = newVersionedStatefulSetPod( + currentSet, + updateSet, + currentRevision.Name, + updateRevision.Name, + i) } // If we find a Pod that has not been created we create the Pod if !isCreated(replicas[i]) { if err := ssc.podControl.CreateStatefulPod(set, replicas[i]); err != nil { - return err + return &status, err } + status.Replicas++ + if getPodRevision(replicas[i]) == currentRevision.Name { + status.CurrentReplicas++ + } else if getPodRevision(replicas[i]) == updateRevision.Name { + status.UpdatedReplicas++ + } + // if the set does not allow bursting, return immediately if monotonic { - return nil + return &status, nil } // pod created, no more work possible for this round continue @@ -151,15 +397,23 @@ func (ssc *defaultStatefulSetControl) UpdateStatefulSet(set *apps.StatefulSet, p // If we find a Pod that is currently terminating, we must wait until graceful deletion // completes before we continue to make progress. if isTerminating(replicas[i]) && monotonic { - glog.V(2).Infof("StatefulSet %s is waiting for Pod %s to Terminate", set.Name, replicas[i].Name) - return nil + glog.V(4).Infof( + "StatefulSet %s/%s is waiting for Pod %s to Terminate", + set.Namespace, + set.Name, + replicas[i].Name) + return &status, nil } // If we have a Pod that has been created but is not running and ready we can not make progress. // We must ensure that all for each Pod, when we create it, all of its predecessors, with respect to its // ordinal, are Running and Ready. if !isRunningAndReady(replicas[i]) && monotonic { - glog.V(2).Infof("StatefulSet %s is waiting for Pod %s to be Running and Ready", set.Name, replicas[i].Name) - return nil + glog.V(4).Infof( + "StatefulSet %s/%s is waiting for Pod %s to be Running and Ready", + set.Namespace, + set.Name, + replicas[i].Name) + return &status, nil } // Enforce the StatefulSet invariants if identityMatches(set, replicas[i]) && storageMatches(set, replicas[i]) { @@ -168,31 +422,120 @@ func (ssc *defaultStatefulSetControl) UpdateStatefulSet(set *apps.StatefulSet, p // Make a deep copy so we don't mutate the shared cache copy, err := scheme.Scheme.DeepCopy(replicas[i]) if err != nil { - return err + return &status, err } replica := copy.(*v1.Pod) - if err := ssc.podControl.UpdateStatefulPod(set, replica); err != nil { - return err + if err := ssc.podControl.UpdateStatefulPod(updateSet, replica); err != nil { + return &status, err } } // At this point, all of the current Replicas are Running and Ready, we can consider termination. // We will wait for all predecessors to be Running and Ready prior to attempting a deletion. // We will terminate Pods in a monotonically decreasing order over [len(pods),set.Spec.Replicas). - // Note that we do not resurrect Pods in this interval. - if unhealthy > 0 && monotonic { - glog.V(2).Infof("StatefulSet %s is waiting on %d Pods", set.Name, unhealthy) - return nil - } + // Note that we do not resurrect Pods in this interval. Also not that scaling will take precedence over + // updates. for target := len(condemned) - 1; target >= 0; target-- { - glog.V(2).Infof("StatefulSet %s terminating Pod %s", set.Name, condemned[target]) + // wait for terminating pods to expire + if isTerminating(condemned[target]) { + glog.V(4).Infof( + "StatefulSet %s/%s is waiting for Pod %s to Terminate prior to scale down", + set.Namespace, + set.Name, + condemned[target].Name) + // block if we are in monotonic mode + if monotonic { + return &status, nil + } + continue + } + // if we are in monotonic mode and the condemned target is not the first unhealthy Pod block + if !isRunningAndReady(condemned[target]) && monotonic && condemned[target] != firstUnhealthyPod { + glog.V(4).Infof( + "StatefulSet %s/%s is waiting for Pod %s to be Running and Ready prior to scale down", + set.Namespace, + set.Name, + firstUnhealthyPod.Name) + return &status, nil + } + glog.V(4).Infof("StatefulSet %s/%s terminating Pod %s for scale dowm", + set.Namespace, + set.Name, + condemned[target].Name) + if err := ssc.podControl.DeleteStatefulPod(set, condemned[target]); err != nil { - return err + return &status, err + } + if getPodRevision(condemned[target]) == currentRevision.Name { + status.CurrentReplicas-- + } else if getPodRevision(condemned[target]) == updateRevision.Name { + status.UpdatedReplicas-- } if monotonic { - return nil + return &status, nil } } + + // for the OnDelete strategy we short circuit. Pods will be updated when they are manually deleted. + if set.Spec.UpdateStrategy.Type == apps.OnDeleteStatefulSetStrategyType { + return &status, nil + } + + // we compute the minimum ordinal of the target sequence for a destructive update based on the strategy. + updateMin := 0 + if set.Spec.UpdateStrategy.Type == apps.PartitionStatefulSetStrategyType { + updateMin = int(set.Spec.UpdateStrategy.Partition.Ordinal) + } + // we terminate the Pod with the largest ordinal that does not match the update revision. + for target := len(replicas) - 1; target >= updateMin; target-- { + // all replicas should be healthy before an update progresses we allow termination of the firstUnhealthy + // Pod in any state allow for rolling back a failed update. + if !isRunningAndReady(replicas[target]) && replicas[target] != firstUnhealthyPod { + glog.V(4).Infof( + "StatefulSet %s/%s is waiting for Pod %s to be Running and Ready prior to update", + set.Namespace, + set.Name, + firstUnhealthyPod.Name) + return &status, nil + } + if getPodRevision(replicas[target]) != updateRevision.Name { + glog.V(4).Infof("StatefulSet %s/%s terminating Pod %s for update", + set.Namespace, + set.Name, + replicas[target].Name) + err := ssc.podControl.DeleteStatefulPod(set, replicas[target]) + status.CurrentReplicas-- + return &status, err + } + } + return &status, nil +} + +// updateStatefulSetStatus updates set's Status to be equal to status. If status indicates a complete update, it is +// mutated to indicate completion. If status is semantically equivalent to set's Status no update is performed. If the +// returned error is nil, the update is successful. +func (ssc *defaultStatefulSetControl) updateStatefulSetStatus( + set *apps.StatefulSet, + status *apps.StatefulSetStatus) error { + + // complete any in progress rolling update if necessary + completeRollingUpdate(set, status) + + // if the status is not inconsistent do not perform an update + if !inconsistentStatus(set, status) { + return nil + } + + // copy set and update its status + obj, err := scheme.Scheme.Copy(set) + if err != nil { + return err + } + set = obj.(*apps.StatefulSet) + if err := ssc.statusUpdater.UpdateStatefulSetStatus(set, status); err != nil { + return err + } + return nil } diff --git a/pkg/controller/statefulset/stateful_set_control_test.go b/pkg/controller/statefulset/stateful_set_control_test.go index 2f21701c979..5ac8ae84a68 100644 --- a/pkg/controller/statefulset/stateful_set_control_test.go +++ b/pkg/controller/statefulset/stateful_set_control_test.go @@ -44,14 +44,17 @@ import ( appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1" corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1" "k8s.io/kubernetes/pkg/controller" + "k8s.io/kubernetes/pkg/controller/history" + "k8s.io/metrics/pkg/client/clientset_generated/clientset/scheme" ) type invariantFunc func(set *apps.StatefulSet, spc *fakeStatefulPodControl) error -func setupController(client clientset.Interface) (*fakeStatefulPodControl, StatefulSetControlInterface, chan struct{}) { +func setupController(client clientset.Interface) (*fakeStatefulPodControl, *fakeStatefulSetStatusUpdater, StatefulSetControlInterface, chan struct{}) { informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1beta1().StatefulSets()) - ssc := NewDefaultStatefulSetControl(spc) + ssu := newFakeStatefulSetStatusUpdater(informerFactory.Apps().V1beta1().StatefulSets()) + ssc := NewDefaultStatefulSetControl(spc, ssu, history.NewFakeHistory(informerFactory.Apps().V1beta1().ControllerRevisions())) stop := make(chan struct{}) informerFactory.Start(stop) @@ -59,8 +62,9 @@ func setupController(client clientset.Interface) (*fakeStatefulPodControl, State stop, informerFactory.Apps().V1beta1().StatefulSets().Informer().HasSynced, informerFactory.Core().V1().Pods().Informer().HasSynced, + informerFactory.Apps().V1beta1().ControllerRevisions().Informer().HasSynced, ) - return spc, ssc, stop + return spc, ssu, ssc, stop } func burst(set *apps.StatefulSet) *apps.StatefulSet { @@ -111,10 +115,10 @@ func TestStatefulSetControl(t *testing.T) { func CreatesPods(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) + spc, _, ssc, stop := setupController(client) defer close(stop) - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to turn up StatefulSet : %s", err) } var err error @@ -129,14 +133,14 @@ func CreatesPods(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) func ScalesUp(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) + spc, _, ssc, stop := setupController(client) defer close(stop) - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to turn up StatefulSet : %s", err) } *set.Spec.Replicas = 4 - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to scale StatefulSet : %s", err) } var err error @@ -151,14 +155,14 @@ func ScalesUp(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { func ScalesDown(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) + spc, _, ssc, stop := setupController(client) defer close(stop) - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to turn up StatefulSet : %s", err) } *set.Spec.Replicas = 0 - if err := scaleDownStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleDownStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to scale StatefulSet : %s", err) } if set.Status.Replicas != 0 { @@ -168,10 +172,10 @@ func ScalesDown(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { func ReplacesPods(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) + spc, _, ssc, stop := setupController(client) defer close(stop) - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to turn up StatefulSet : %s", err) } var err error @@ -238,9 +242,8 @@ func ReplacesPods(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) func RecreatesFailedPod(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { client := fake.NewSimpleClientset() - informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) - spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1beta1().StatefulSets()) - ssc := NewDefaultStatefulSetControl(spc) + spc, _, ssc, stop := setupController(client) + defer close(stop) selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) if err != nil { t.Error(err) @@ -278,7 +281,7 @@ func RecreatesFailedPod(t *testing.T, set *apps.StatefulSet, invariants invarian func SetsInitAnnotation(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) + spc, _, ssc, stop := setupController(client) defer close(stop) selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) @@ -317,7 +320,7 @@ func SetsInitAnnotation(t *testing.T, set *apps.StatefulSet, invariants invarian if pods, err = spc.setPodInitStatus(set, 0, true); err != nil { t.Error(err) } - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to turn up StatefulSet : %s", err) } set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) @@ -331,14 +334,14 @@ func SetsInitAnnotation(t *testing.T, set *apps.StatefulSet, invariants invarian func CreatePodFailure(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) + spc, _, ssc, stop := setupController(client) defer close(stop) spc.SetCreateStatefulPodError(apierrors.NewInternalError(errors.New("API server failed")), 2) - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); !apierrors.IsInternalError(err) { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); !apierrors.IsInternalError(err) { t.Errorf("StatefulSetControl did not return InternalError found %s", err) } - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to turn up StatefulSet : %s", err) } var err error @@ -353,12 +356,12 @@ func CreatePodFailure(t *testing.T, set *apps.StatefulSet, invariants invariantF func UpdatePodFailure(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) + spc, _, ssc, stop := setupController(client) defer close(stop) spc.SetUpdateStatefulPodError(apierrors.NewInternalError(errors.New("API server failed")), 0) // have to have 1 successful loop first - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Fatalf("Unexpected error: %v", err) } var err error @@ -388,70 +391,16 @@ func UpdatePodFailure(t *testing.T, set *apps.StatefulSet, invariants invariantF } } -func testDefaultStatefulSetControlBlocksOnTerminating(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { - client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) - defer close(stop) - spc.SetUpdateStatefulPodError(apierrors.NewInternalError(errors.New("API server failed")), 0) - - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { - t.Fatalf("Unexpected error: %v", err) - } - var err error - set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) - if err != nil { - t.Fatalf("Error getting updated StatefulSet: %v", err) - } - if set.Status.Replicas != 3 { - t.Fatal("Failed to scale StatefulSet to 3 replicas") - } - // scale the set and add a terminated pod - *set.Spec.Replicas = 4 - pods, err := spc.addTerminatingPod(set, 2) - if err != nil { - t.Fatal(err) - } - if err := ssc.UpdateStatefulSet(set, pods); err != nil { - t.Fatal(err) - } - pods, err = spc.podsLister.List(labels.Everything()) - if err != nil { - t.Fatalf("Error listing pods: %v", err) - } - if len(pods) != 3 { - t.Fatalf("Expected 3 pods, got %d", len(pods)) - } - sort.Sort(ascendingOrdinal(pods)) - spc.DeleteStatefulPod(set, pods[2]) - pods, err = spc.podsLister.List(labels.Everything()) - if err != nil { - t.Fatalf("Error listing pods: %v", err) - } - if len(pods) != 2 { - t.Fatalf("Expected 3 pods, got %d", len(pods)) - } - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { - t.Fatalf("Unexpected error: %v", err) - } - set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) - if err != nil { - t.Fatalf("Error getting updated StatefulSet: %v", err) - } - if set.Status.Replicas != 4 { - t.Fatal("Failed to scale StatefulSet to 3 replicas") - } -} - func UpdateSetStatusFailure(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) + spc, ssu, ssc, stop := setupController(client) defer close(stop) - spc.SetUpdateStatefulSetStatusError(apierrors.NewInternalError(errors.New("API server failed")), 2) + ssu.SetUpdateStatefulSetStatusError(apierrors.NewInternalError(errors.New("API server failed")), 2) - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); !apierrors.IsInternalError(err) { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); !apierrors.IsInternalError(err) { t.Errorf("StatefulSetControl did not return InternalError found %s", err) } - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to turn up StatefulSet : %s", err) } var err error @@ -466,7 +415,7 @@ func UpdateSetStatusFailure(t *testing.T, set *apps.StatefulSet, invariants inva func PodRecreateDeleteFailure(t *testing.T, set *apps.StatefulSet, invariants invariantFunc) { client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) + spc, _, ssc, stop := setupController(client) defer close(stop) selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) @@ -515,10 +464,10 @@ func TestStatefulSetControlScaleDownDeleteError(t *testing.T) { invariants := assertMonotonicInvariants set := newStatefulSet(3) client := fake.NewSimpleClientset(set) - spc, ssc, stop := setupController(client) + spc, _, ssc, stop := setupController(client) defer close(stop) - if err := scaleUpStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleUpStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to turn up StatefulSet : %s", err) } var err error @@ -528,10 +477,10 @@ func TestStatefulSetControlScaleDownDeleteError(t *testing.T) { } *set.Spec.Replicas = 0 spc.SetDeleteStatefulPodError(apierrors.NewInternalError(errors.New("API server failed")), 2) - if err := scaleDownStatefulSetControl(t, set, ssc, spc, invariants); !apierrors.IsInternalError(err) { + if err := scaleDownStatefulSetControl(set, ssc, spc, invariants); !apierrors.IsInternalError(err) { t.Errorf("StatefulSetControl failed to throw error on delete %s", err) } - if err := scaleDownStatefulSetControl(t, set, ssc, spc, invariants); err != nil { + if err := scaleDownStatefulSetControl(set, ssc, spc, invariants); err != nil { t.Errorf("Failed to turn down StatefulSet %s", err) } set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) @@ -543,6 +492,1050 @@ func TestStatefulSetControlScaleDownDeleteError(t *testing.T) { } } +func TestStatefulSetControl_getSetRevisions(t *testing.T) { + type testcase struct { + name string + existing []*apps.ControllerRevision + set *apps.StatefulSet + expectedCount int + expectedCurrent *apps.ControllerRevision + expectedUpdate *apps.ControllerRevision + err bool + } + + testFn := func(test *testcase, t *testing.T) { + client := fake.NewSimpleClientset() + informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) + spc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1beta1().StatefulSets()) + ssu := newFakeStatefulSetStatusUpdater(informerFactory.Apps().V1beta1().StatefulSets()) + ssc := defaultStatefulSetControl{spc, ssu, history.NewFakeHistory(informerFactory.Apps().V1beta1().ControllerRevisions())} + + stop := make(chan struct{}) + defer close(stop) + informerFactory.Start(stop) + cache.WaitForCacheSync( + stop, + informerFactory.Apps().V1beta1().StatefulSets().Informer().HasSynced, + informerFactory.Core().V1().Pods().Informer().HasSynced, + informerFactory.Apps().V1beta1().ControllerRevisions().Informer().HasSynced, + ) + for i := range test.existing { + ssc.controllerHistory.CreateControllerRevision(test.set, test.existing[i]) + } + revisions, err := ssc.ListRevisions(test.set) + if err != nil { + t.Fatal(err) + } + current, update, err := ssc.getStatefulSetRevisions(test.set, revisions) + revisions, err = ssc.ListRevisions(test.set) + if err != nil { + t.Fatal(err) + } + if len(revisions) != test.expectedCount { + t.Errorf("%s: want %d revisions got %d", test.name, test.expectedCount, len(revisions)) + } + if test.err && err != nil { + t.Errorf("%s: expected error", test.name) + } + if !test.err && !history.EqualRevision(current, test.expectedCurrent) { + t.Errorf("%s: for current want %v got %v", test.name, test.expectedCurrent, current) + } + if !test.err && !history.EqualRevision(update, test.expectedUpdate) { + t.Errorf("%s: for current want %v got %v", test.name, test.expectedUpdate, update) + } + if !test.err && test.expectedCurrent != nil && current != nil && test.expectedCurrent.Revision != current.Revision { + t.Errorf("%s: for current revision want %d got %d", test.name, test.expectedCurrent.Revision, current.Revision) + } + if !test.err && test.expectedUpdate != nil && update != nil && test.expectedUpdate.Revision != update.Revision { + t.Errorf("%s: for current revision want %d got %d", test.name, test.expectedUpdate.Revision, update.Revision) + } + } + + updateRevision := func(cr *apps.ControllerRevision, revision int64) *apps.ControllerRevision { + obj, err := scheme.Scheme.DeepCopy(cr) + if err != nil { + t.Fatal(err) + } + clone := obj.(*apps.ControllerRevision) + clone.Revision = revision + return clone + } + + set := newStatefulSet(3) + rev0 := newRevisionOrDie(set, 1) + set1 := copySet(set) + set1.Spec.Template.Spec.Containers[0].Image = "foo" + set1.Status.CurrentRevision = rev0.Name + rev1 := newRevisionOrDie(set1, 2) + set2 := copySet(set1) + set2.Spec.Template.Labels["new"] = "label" + set2.Status.CurrentRevision = rev0.Name + rev2 := newRevisionOrDie(set2, 3) + tests := []testcase{ + { + name: "creates initial revision", + existing: nil, + set: set, + expectedCount: 1, + expectedCurrent: rev0, + expectedUpdate: rev0, + err: false, + }, + { + name: "creates revision on update", + existing: []*apps.ControllerRevision{rev0}, + set: set1, + expectedCount: 2, + expectedCurrent: rev0, + expectedUpdate: rev1, + err: false, + }, + { + name: "must not recreate a new revision of same set", + existing: []*apps.ControllerRevision{rev0, rev1}, + set: set1, + expectedCount: 2, + expectedCurrent: rev0, + expectedUpdate: rev1, + err: false, + }, + { + name: "must rollback to a previous revision", + existing: []*apps.ControllerRevision{rev0, rev1, rev2}, + set: set1, + expectedCount: 3, + expectedCurrent: rev0, + expectedUpdate: updateRevision(rev1, 4), + err: false, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestStatefulSetControlRollingUpdate(t *testing.T) { + type testcase struct { + name string + invariants func(set *apps.StatefulSet, spc *fakeStatefulPodControl) error + initial func() *apps.StatefulSet + update func(set *apps.StatefulSet) *apps.StatefulSet + validate func(set *apps.StatefulSet, pods []*v1.Pod) error + } + + testFn := func(test *testcase, t *testing.T) { + set := test.initial() + client := fake.NewSimpleClientset(set) + spc, _, ssc, stop := setupController(client) + defer close(stop) + if err := scaleUpStatefulSetControl(set, ssc, spc, test.invariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err := spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set = test.update(set) + if err := updateStatefulSetControl(set, ssc, spc, assertUpdateInvariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + pods, err := spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + if err := test.validate(set, pods); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + } + + tests := []testcase{ + { + name: "monotonic image update", + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(3) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "monotonic image update and scale up", + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(3) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 5 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "monotonic image update and scale down", + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(5) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 3 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update", + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(3)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update and scale up", + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(3)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 5 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update and scale down", + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(5)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 3 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestStatefulSetControlOnDeleteUpdate(t *testing.T) { + type testcase struct { + name string + invariants func(set *apps.StatefulSet, spc *fakeStatefulPodControl) error + initial func() *apps.StatefulSet + update func(set *apps.StatefulSet) *apps.StatefulSet + validateUpdate func(set *apps.StatefulSet, pods []*v1.Pod) error + validateRestart func(set *apps.StatefulSet, pods []*v1.Pod) error + } + + originalImage := newStatefulSet(3).Spec.Template.Spec.Containers[0].Image + + testFn := func(test *testcase, t *testing.T) { + set := test.initial() + set.Spec.UpdateStrategy = apps.StatefulSetUpdateStrategy{Type: apps.OnDeleteStatefulSetStrategyType} + client := fake.NewSimpleClientset(set) + spc, _, ssc, stop := setupController(client) + defer close(stop) + if err := scaleUpStatefulSetControl(set, ssc, spc, test.invariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err := spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set = test.update(set) + if err := updateStatefulSetControl(set, ssc, spc, assertUpdateInvariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + + selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + pods, err := spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + if err := test.validateUpdate(set, pods); err != nil { + for i := range pods { + t.Log(pods[i].Name) + } + t.Fatalf("%s: %s", test.name, err) + + } + replicas := *set.Spec.Replicas + *set.Spec.Replicas = 0 + if err := scaleDownStatefulSetControl(set, ssc, spc, test.invariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + *set.Spec.Replicas = replicas + if err := scaleUpStatefulSetControl(set, ssc, spc, test.invariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + pods, err = spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + if err := test.validateRestart(set, pods); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + } + + tests := []testcase{ + { + name: "monotonic image update", + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(3) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRestart: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "monotonic image update and scale up", + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(3) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 5 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if i < 3 && pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + if i >= 3 && pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRestart: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "monotonic image update and scale down", + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(5) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 3 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRestart: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update", + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(3)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRestart: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update and scale up", + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(3)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 5 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if i < 3 && pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + if i >= 3 && pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRestart: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update and scale down", + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(5)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 3 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRestart: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestStatefulSetControlPartitionUpdate(t *testing.T) { + type testcase struct { + name string + partition int32 + invariants func(set *apps.StatefulSet, spc *fakeStatefulPodControl) error + initial func() *apps.StatefulSet + update func(set *apps.StatefulSet) *apps.StatefulSet + validate func(set *apps.StatefulSet, pods []*v1.Pod) error + } + + testFn := func(test *testcase, t *testing.T) { + set := test.initial() + set.Spec.UpdateStrategy = apps.StatefulSetUpdateStrategy{ + Type: apps.PartitionStatefulSetStrategyType, + Partition: func() *apps.PartitionStatefulSetStrategy { + return &apps.PartitionStatefulSetStrategy{Ordinal: test.partition} + }(), + } + client := fake.NewSimpleClientset(set) + spc, _, ssc, stop := setupController(client) + defer close(stop) + if err := scaleUpStatefulSetControl(set, ssc, spc, test.invariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err := spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set = test.update(set) + if err := updateStatefulSetControl(set, ssc, spc, assertUpdateInvariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + pods, err := spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + if err := test.validate(set, pods); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + } + + originalImage := newStatefulSet(3).Spec.Template.Spec.Containers[0].Image + + tests := []testcase{ + { + name: "monotonic image update", + invariants: assertMonotonicInvariants, + partition: 2, + initial: func() *apps.StatefulSet { + return newStatefulSet(3) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if i < 2 && pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + if i >= 2 && pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "monotonic image update and scale up", + partition: 2, + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(3) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 5 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if i < 2 && pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + if i >= 2 && pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update", + partition: 2, + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(3)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if i < 2 && pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + if i >= 2 && pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update and scale up", + invariants: assertBurstInvariants, + partition: 2, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(3)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 5 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if i < 2 && pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + if i >= 2 && pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestStatefulSetControlLimitsHistory(t *testing.T) { + type testcase struct { + name string + invariants func(set *apps.StatefulSet, spc *fakeStatefulPodControl) error + initial func() *apps.StatefulSet + } + + testFn := func(test *testcase, t *testing.T) { + set := test.initial() + client := fake.NewSimpleClientset(set) + spc, _, ssc, stop := setupController(client) + defer close(stop) + if err := scaleUpStatefulSetControl(set, ssc, spc, test.invariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err := spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + for i := 0; i < 10; i++ { + set.Spec.Template.Spec.Containers[0].Image = fmt.Sprintf("foo-%d", i) + if err := updateStatefulSetControl(set, ssc, spc, assertUpdateInvariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + pods, err := spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + err = ssc.UpdateStatefulSet(set, pods) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + revisions, err := ssc.ListRevisions(set) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + if len(revisions) > int(*set.Spec.RevisionHistoryLimit)+2 { + t.Fatalf("%s: %d greater than limit %d", test.name, len(revisions), *set.Spec.RevisionHistoryLimit) + } + } + } + + tests := []testcase{ + { + name: "monotonic update", + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(3) + }, + }, + { + name: "burst update", + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(3)) + }, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + +func TestStatefulSetControlRollback(t *testing.T) { + type testcase struct { + name string + invariants func(set *apps.StatefulSet, spc *fakeStatefulPodControl) error + initial func() *apps.StatefulSet + update func(set *apps.StatefulSet) *apps.StatefulSet + validateUpdate func(set *apps.StatefulSet, pods []*v1.Pod) error + validateRollback func(set *apps.StatefulSet, pods []*v1.Pod) error + } + + originalImage := newStatefulSet(3).Spec.Template.Spec.Containers[0].Image + + testFn := func(test *testcase, t *testing.T) { + set := test.initial() + client := fake.NewSimpleClientset(set) + spc, _, ssc, stop := setupController(client) + defer close(stop) + if err := scaleUpStatefulSetControl(set, ssc, spc, test.invariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err := spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set = test.update(set) + if err := updateStatefulSetControl(set, ssc, spc, assertUpdateInvariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + pods, err := spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + if err := test.validateUpdate(set, pods); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + revisions, err := ssc.ListRevisions(set) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + history.SortControllerRevisions(revisions) + set, err = applyRevision(set, revisions[0]) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + if err := updateStatefulSetControl(set, ssc, spc, assertUpdateInvariants); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + pods, err = spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + t.Fatalf("%s: %s", test.name, err) + } + if err := test.validateRollback(set, pods); err != nil { + t.Fatalf("%s: %s", test.name, err) + } + } + + tests := []testcase{ + { + name: "monotonic image update", + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(3) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRollback: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "monotonic image update and scale up", + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(3) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 5 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRollback: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "monotonic image update and scale down", + invariants: assertMonotonicInvariants, + initial: func() *apps.StatefulSet { + return newStatefulSet(5) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 3 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRollback: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update", + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(3)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRollback: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update and scale up", + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(3)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 5 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRollback: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + { + name: "burst image update and scale down", + invariants: assertBurstInvariants, + initial: func() *apps.StatefulSet { + return burst(newStatefulSet(5)) + }, + update: func(set *apps.StatefulSet) *apps.StatefulSet { + *set.Spec.Replicas = 3 + set.Spec.Template.Spec.Containers[0].Image = "foo" + return set + }, + validateUpdate: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != "foo" { + return fmt.Errorf("want pod %s image foo found %s", pods[i].Name, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + validateRollback: func(set *apps.StatefulSet, pods []*v1.Pod) error { + sort.Sort(ascendingOrdinal(pods)) + for i := range pods { + if pods[i].Spec.Containers[0].Image != originalImage { + return fmt.Errorf("want pod %s image %s found %s", pods[i].Name, originalImage, pods[i].Spec.Containers[0].Image) + } + } + return nil + }, + }, + } + for i := range tests { + testFn(&tests[i], t) + } +} + type requestTracker struct { requests int err error @@ -563,16 +1556,15 @@ func (rt *requestTracker) reset() { } type fakeStatefulPodControl struct { - podsLister corelisters.PodLister - claimsLister corelisters.PersistentVolumeClaimLister - setsLister appslisters.StatefulSetLister - podsIndexer cache.Indexer - claimsIndexer cache.Indexer - setsIndexer cache.Indexer - createPodTracker requestTracker - updatePodTracker requestTracker - deletePodTracker requestTracker - updateStatusTracker requestTracker + podsLister corelisters.PodLister + claimsLister corelisters.PersistentVolumeClaimLister + setsLister appslisters.StatefulSetLister + podsIndexer cache.Indexer + claimsIndexer cache.Indexer + setsIndexer cache.Indexer + createPodTracker requestTracker + updatePodTracker requestTracker + deletePodTracker requestTracker } func newFakeStatefulPodControl(podInformer coreinformers.PodInformer, setInformer appsinformers.StatefulSetInformer) *fakeStatefulPodControl { @@ -586,7 +1578,6 @@ func newFakeStatefulPodControl(podInformer coreinformers.PodInformer, setInforme setInformer.Informer().GetIndexer(), requestTracker{0, nil, 0}, requestTracker{0, nil, 0}, - requestTracker{0, nil, 0}, requestTracker{0, nil, 0}} } @@ -605,11 +1596,6 @@ func (spc *fakeStatefulPodControl) SetDeleteStatefulPodError(err error, after in spc.deletePodTracker.after = after } -func (spc *fakeStatefulPodControl) SetUpdateStatefulSetStatusError(err error, after int) { - spc.updateStatusTracker.err = err - spc.updateStatusTracker.after = after -} - func copyPod(pod *v1.Pod) *v1.Pod { obj, err := api.Scheme.Copy(pod) if err != nil { @@ -618,6 +1604,14 @@ func copyPod(pod *v1.Pod) *v1.Pod { return obj.(*v1.Pod) } +func copySet(set *apps.StatefulSet) *apps.StatefulSet { + obj, err := scheme.Scheme.Copy(set) + if err != nil { + panic(err) + } + return obj.(*apps.StatefulSet) +} + func (spc *fakeStatefulPodControl) setPodPending(set *apps.StatefulSet, ordinal int) ([]*v1.Pod, error) { selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) if err != nil { @@ -782,19 +1776,39 @@ func (spc *fakeStatefulPodControl) DeleteStatefulPod(set *apps.StatefulSet, pod return nil } -func (spc *fakeStatefulPodControl) UpdateStatefulSetStatus(set *apps.StatefulSet, replicas int32, generation int64) error { - defer spc.updateStatusTracker.inc() - if spc.updateStatusTracker.errorReady() { - defer spc.updateStatusTracker.reset() - return spc.updateStatusTracker.err +var _ StatefulPodControlInterface = &fakeStatefulPodControl{} + +type fakeStatefulSetStatusUpdater struct { + setsLister appslisters.StatefulSetLister + setsIndexer cache.Indexer + updateStatusTracker requestTracker +} + +func newFakeStatefulSetStatusUpdater(setInformer appsinformers.StatefulSetInformer) *fakeStatefulSetStatusUpdater { + return &fakeStatefulSetStatusUpdater{ + setInformer.Lister(), + setInformer.Informer().GetIndexer(), + requestTracker{0, nil, 0}, } - set.Status.Replicas = replicas - set.Status.ObservedGeneration = &generation - spc.setsIndexer.Update(set) +} + +func (ssu *fakeStatefulSetStatusUpdater) UpdateStatefulSetStatus(set *apps.StatefulSet, status *apps.StatefulSetStatus) error { + defer ssu.updateStatusTracker.inc() + if ssu.updateStatusTracker.errorReady() { + defer ssu.updateStatusTracker.reset() + return ssu.updateStatusTracker.err + } + set.Status = *status + ssu.setsIndexer.Update(set) return nil } -var _ StatefulPodControlInterface = &fakeStatefulPodControl{} +func (ssu *fakeStatefulSetStatusUpdater) SetUpdateStatefulSetStatusError(err error, after int) { + ssu.updateStatusTracker.err = err + ssu.updateStatusTracker.after = after +} + +var _ StatefulSetStatusUpdaterInterface = &fakeStatefulSetStatusUpdater{} func assertMonotonicInvariants(set *apps.StatefulSet, spc *fakeStatefulPodControl) error { selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) @@ -812,7 +1826,7 @@ func assertMonotonicInvariants(set *apps.StatefulSet, spc *fakeStatefulPodContro } if getOrdinal(pods[ord]) != ord { - return fmt.Errorf("pods %s deployed in the wrong order", pods[ord].Name) + return fmt.Errorf("pods %s deployed in the wrong order %d", pods[ord].Name, ord) } if !storageMatches(set, pods[ord]) { @@ -870,6 +1884,54 @@ func assertBurstInvariants(set *apps.StatefulSet, spc *fakeStatefulPodControl) e return nil } +func assertUpdateInvariants(set *apps.StatefulSet, spc *fakeStatefulPodControl) error { + selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) + if err != nil { + return err + } + pods, err := spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + return err + } + sort.Sort(ascendingOrdinal(pods)) + for ord := 0; ord < len(pods); ord++ { + + if !storageMatches(set, pods[ord]) { + return fmt.Errorf("pod %s does not match the storage specification of StatefulSet %s ", pods[ord].Name, set.Name) + } + + for _, claim := range getPersistentVolumeClaims(set, pods[ord]) { + claim, err := spc.claimsLister.PersistentVolumeClaims(set.Namespace).Get(claim.Name) + if err != nil { + return err + } + if claim == nil { + return fmt.Errorf("claim %s for Pod %s was not created", claim.Name, pods[ord].Name) + } + } + + if !identityMatches(set, pods[ord]) { + return fmt.Errorf("pod %s does not match the identity specification of StatefulSet %s ", pods[ord].Name, set.Name) + } + } + if set.Spec.UpdateStrategy.Type == apps.OnDeleteStatefulSetStrategyType { + return nil + } + if set.Spec.UpdateStrategy.Type == apps.RollingUpdateStatefulSetStrategyType { + for i := 0; i < int(set.Status.CurrentReplicas) && i < len(pods); i++ { + if want, got := set.Status.CurrentRevision, getPodRevision(pods[i]); want != got { + return fmt.Errorf("pod %s want current revision %s got %s", pods[i].Name, want, got) + } + } + for i, j := len(pods)-1, 0; j < int(set.Status.UpdatedReplicas); i, j = i-1, j+1 { + if want, got := set.Status.UpdateRevision, getPodRevision(pods[i]); want != got { + return fmt.Errorf("pod %s want update revision %s got %s", pods[i].Name, want, got) + } + } + } + return nil +} + func fakeResourceVersion(object interface{}) { obj, isObj := object.(metav1.Object) if !isObj { @@ -882,12 +1944,15 @@ func fakeResourceVersion(object interface{}) { } } -func scaleUpStatefulSetControl(t *testing.T, set *apps.StatefulSet, ssc StatefulSetControlInterface, spc *fakeStatefulPodControl, invariants invariantFunc) error { +func scaleUpStatefulSetControl(set *apps.StatefulSet, + ssc StatefulSetControlInterface, + spc *fakeStatefulPodControl, + invariants invariantFunc) error { selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) if err != nil { return err } - for set.Status.Replicas < *set.Spec.Replicas { + for set.Status.ReadyReplicas < *set.Spec.Replicas { pods, err := spc.podsLister.Pods(set.Namespace).List(selector) if err != nil { return err @@ -898,7 +1963,6 @@ func scaleUpStatefulSetControl(t *testing.T, set *apps.StatefulSet, ssc Stateful initialized := false for ord, pod := range pods { if pod.Status.Phase == "" { - t.Logf("found pod %s pending", pod.Name) if pods, err = spc.setPodPending(set, ord); err != nil { return err } @@ -915,12 +1979,10 @@ func scaleUpStatefulSetControl(t *testing.T, set *apps.StatefulSet, ssc Stateful pod := pods[ord] switch pod.Status.Phase { case v1.PodPending: - t.Logf("set pod %s running", pod.Name) if pods, err = spc.setPodRunning(set, ord); err != nil { return err } case v1.PodRunning: - t.Logf("set pod %s ready", pod.Name) if pods, err = spc.setPodReady(set, ord); err != nil { return err } @@ -944,7 +2006,7 @@ func scaleUpStatefulSetControl(t *testing.T, set *apps.StatefulSet, ssc Stateful return invariants(set, spc) } -func scaleDownStatefulSetControl(t *testing.T, set *apps.StatefulSet, ssc StatefulSetControlInterface, spc *fakeStatefulPodControl, invariants invariantFunc) error { +func scaleDownStatefulSetControl(set *apps.StatefulSet, ssc StatefulSetControlInterface, spc *fakeStatefulPodControl, invariants invariantFunc) error { selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) if err != nil { return err @@ -978,7 +2040,10 @@ func scaleDownStatefulSetControl(t *testing.T, set *apps.StatefulSet, ssc Statef return err } sort.Sort(ascendingOrdinal(pods)) - spc.podsIndexer.Delete(pods[ordinal]) + + if len(pods) > 0 { + spc.podsIndexer.Delete(pods[len(pods)-1]) + } } if err := ssc.UpdateStatefulSet(set, pods); err != nil { return err @@ -993,3 +2058,126 @@ func scaleDownStatefulSetControl(t *testing.T, set *apps.StatefulSet, ssc Statef } return invariants(set, spc) } + +func updateComplete(set *apps.StatefulSet, pods []*v1.Pod) bool { + sort.Sort(ascendingOrdinal(pods)) + if len(pods) != int(*set.Spec.Replicas) { + return false + } + if set.Status.ReadyReplicas != *set.Spec.Replicas { + return false + } + + if set.Spec.UpdateStrategy.Type == apps.RollingUpdateStatefulSetStrategyType { + if set.Status.CurrentReplicas < *set.Spec.Replicas { + return false + } + for i := range pods { + if getPodRevision(pods[i]) != set.Status.CurrentRevision { + return false + } + } + } + if set.Spec.UpdateStrategy.Type == apps.PartitionStatefulSetStrategyType { + if set.Status.UpdatedReplicas < (*set.Spec.Replicas - set.Spec.UpdateStrategy.Partition.Ordinal) { + return false + } + for i := 0; i < int(set.Spec.UpdateStrategy.Partition.Ordinal); i++ { + if getPodRevision(pods[i]) != set.Status.CurrentRevision { + return false + } + } + for i := int(set.Spec.UpdateStrategy.Partition.Ordinal); i < int(*set.Spec.Replicas); i++ { + if getPodRevision(pods[i]) != set.Status.UpdateRevision { + return false + } + } + } + return true +} + +func updateStatefulSetControl(set *apps.StatefulSet, + ssc StatefulSetControlInterface, + spc *fakeStatefulPodControl, + invariants invariantFunc) error { + selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) + if err != nil { + return err + } + pods, err := spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + return err + } + if err = ssc.UpdateStatefulSet(set, pods); err != nil { + return err + } + + set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + return err + } + pods, err = spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + return err + } + for !updateComplete(set, pods) { + pods, err = spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + return err + } + sort.Sort(ascendingOrdinal(pods)) + initialized := false + for ord, pod := range pods { + if pod.Status.Phase == "" { + if pods, err = spc.setPodPending(set, ord); err != nil { + return err + } + break + } + } + if initialized { + continue + } + + if len(pods) > 0 { + ord := int(rand.Int63n(int64(len(pods)))) + pod := pods[ord] + switch pod.Status.Phase { + case v1.PodPending: + if pods, err = spc.setPodRunning(set, ord); err != nil { + return err + } + case v1.PodRunning: + if pods, err = spc.setPodReady(set, ord); err != nil { + return err + } + default: + continue + } + } + + if err = ssc.UpdateStatefulSet(set, pods); err != nil { + return err + } + set, err = spc.setsLister.StatefulSets(set.Namespace).Get(set.Name) + if err != nil { + return err + } + if err := invariants(set, spc); err != nil { + return err + } + pods, err = spc.podsLister.Pods(set.Namespace).List(selector) + if err != nil { + return err + } + } + return invariants(set, spc) +} + +func newRevisionOrDie(set *apps.StatefulSet, revision int64) *apps.ControllerRevision { + rev, err := newRevision(set, revision) + if err != nil { + panic(err) + } + return rev +} diff --git a/pkg/controller/statefulset/stateful_set_status_updater.go b/pkg/controller/statefulset/stateful_set_status_updater.go new file mode 100644 index 00000000000..f1a2f5c268a --- /dev/null +++ b/pkg/controller/statefulset/stateful_set_status_updater.go @@ -0,0 +1,78 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package statefulset + +import ( + "fmt" + + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/kubernetes/scheme" + + apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" + "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" + appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1" + + "k8s.io/kubernetes/pkg/client/retry" +) + +// StatefulSetStatusUpdaterInterface is an interface used to update the StatefulSetStatus associated with a StatefulSet. +// For any use other than testing, clients should create an instance using NewRealStatefulSetStatusUpdater. +type StatefulSetStatusUpdaterInterface interface { + // UpdateStatefulSetStatus sets the set's Status to status. Implementations are required to retry on conflicts, + // but fail on other errors. If the returned error is nil set's Status has been successfully set to status. + UpdateStatefulSetStatus(set *apps.StatefulSet, status *apps.StatefulSetStatus) error +} + +// NewRealStatefulSetStatusUpdater returns a StatefulSetStatusUpdaterInterface that updates the Status of a StatefulSet, +// using the supplied client and setLister. +func NewRealStatefulSetStatusUpdater( + client clientset.Interface, + setLister appslisters.StatefulSetLister) StatefulSetStatusUpdaterInterface { + return &realStatefulSetStatusUpdater{client, setLister} +} + +type realStatefulSetStatusUpdater struct { + client clientset.Interface + setLister appslisters.StatefulSetLister +} + +func (ssu *realStatefulSetStatusUpdater) UpdateStatefulSetStatus( + set *apps.StatefulSet, + status *apps.StatefulSetStatus) error { + // don't wait due to limited number of clients, but backoff after the default number of steps + return retry.RetryOnConflict(retry.DefaultRetry, func() error { + set.Status = *status + _, updateErr := ssu.client.Apps().StatefulSets(set.Namespace).UpdateStatus(set) + if updateErr == nil { + return nil + } + if updated, err := ssu.setLister.StatefulSets(set.Namespace).Get(set.Name); err == nil { + // make a copy so we don't mutate the shared cache + if copy, err := scheme.Scheme.DeepCopy(updated); err == nil { + set = copy.(*apps.StatefulSet) + } else { + utilruntime.HandleError(fmt.Errorf("error copying updated StatefulSet: %v", err)) + } + } else { + utilruntime.HandleError(fmt.Errorf("error getting updated StatefulSet %s/%s from lister: %v", set.Namespace, set.Name, err)) + } + + return updateErr + }) +} + +var _ StatefulSetStatusUpdaterInterface = &realStatefulSetStatusUpdater{} diff --git a/pkg/controller/statefulset/stateful_set_status_updater_test.go b/pkg/controller/statefulset/stateful_set_status_updater_test.go new file mode 100644 index 00000000000..3bdefb8e66b --- /dev/null +++ b/pkg/controller/statefulset/stateful_set_status_updater_test.go @@ -0,0 +1,141 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package statefulset + +import ( + "errors" + "testing" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + + core "k8s.io/client-go/testing" + "k8s.io/client-go/tools/cache" + + apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" + "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" + appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1" +) + +func TestStatefulSetUpdaterUpdatesSetStatus(t *testing.T) { + set := newStatefulSet(3) + status := apps.StatefulSetStatus{ObservedGeneration: func() *int64 { + i := int64(1) + return &i + }(), Replicas: 2} + fakeClient := &fake.Clientset{} + updater := NewRealStatefulSetStatusUpdater(fakeClient, nil) + fakeClient.AddReactor("update", "statefulsets", func(action core.Action) (bool, runtime.Object, error) { + update := action.(core.UpdateAction) + return true, update.GetObject(), nil + }) + if err := updater.UpdateStatefulSetStatus(set, &status); err != nil { + t.Errorf("Error returned on successful status update: %s", err) + } + if set.Status.Replicas != 2 { + t.Errorf("UpdateStatefulSetStatus mutated the sets replicas %d", set.Status.Replicas) + } +} + +func TestStatefulSetStatusUpdaterUpdatesObservedGeneration(t *testing.T) { + set := newStatefulSet(3) + status := apps.StatefulSetStatus{ObservedGeneration: func() *int64 { + i := int64(3) + return &i + }(), Replicas: 2} + fakeClient := &fake.Clientset{} + updater := NewRealStatefulSetStatusUpdater(fakeClient, nil) + fakeClient.AddReactor("update", "statefulsets", func(action core.Action) (bool, runtime.Object, error) { + update := action.(core.UpdateAction) + sts := update.GetObject().(*apps.StatefulSet) + if sts.Status.ObservedGeneration == nil || *sts.Status.ObservedGeneration != int64(3) { + t.Errorf("expected observedGeneration to be synced with generation for statefulset %q", sts.Name) + } + return true, sts, nil + }) + if err := updater.UpdateStatefulSetStatus(set, &status); err != nil { + t.Errorf("Error returned on successful status update: %s", err) + } +} + +func TestStatefulSetStatusUpdaterUpdateReplicasFailure(t *testing.T) { + set := newStatefulSet(3) + status := apps.StatefulSetStatus{ObservedGeneration: func() *int64 { + i := int64(3) + return &i + }(), Replicas: 2} + fakeClient := &fake.Clientset{} + indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + indexer.Add(set) + setLister := appslisters.NewStatefulSetLister(indexer) + updater := NewRealStatefulSetStatusUpdater(fakeClient, setLister) + fakeClient.AddReactor("update", "statefulsets", func(action core.Action) (bool, runtime.Object, error) { + return true, nil, apierrors.NewInternalError(errors.New("API server down")) + }) + if err := updater.UpdateStatefulSetStatus(set, &status); err == nil { + t.Error("Failed update did not return error") + } +} + +func TestStatefulSetStatusUpdaterUpdateReplicasConflict(t *testing.T) { + set := newStatefulSet(3) + status := apps.StatefulSetStatus{ObservedGeneration: func() *int64 { + i := int64(3) + return &i + }(), Replicas: 2} + conflict := false + fakeClient := &fake.Clientset{} + indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + indexer.Add(set) + setLister := appslisters.NewStatefulSetLister(indexer) + updater := NewRealStatefulSetStatusUpdater(fakeClient, setLister) + fakeClient.AddReactor("update", "statefulsets", func(action core.Action) (bool, runtime.Object, error) { + update := action.(core.UpdateAction) + if !conflict { + conflict = true + return true, update.GetObject(), apierrors.NewConflict(action.GetResource().GroupResource(), set.Name, errors.New("Object already exists")) + } else { + return true, update.GetObject(), nil + } + }) + if err := updater.UpdateStatefulSetStatus(set, &status); err != nil { + t.Errorf("UpdateStatefulSetStatus returned an error: %s", err) + } + if set.Status.Replicas != 2 { + t.Errorf("UpdateStatefulSetStatus mutated the sets replicas %d", set.Status.Replicas) + } +} + +func TestStatefulSetStatusUpdaterUpdateReplicasConflictFailure(t *testing.T) { + set := newStatefulSet(3) + status := apps.StatefulSetStatus{ObservedGeneration: func() *int64 { + i := int64(3) + return &i + }(), Replicas: 2} + fakeClient := &fake.Clientset{} + indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) + indexer.Add(set) + setLister := appslisters.NewStatefulSetLister(indexer) + updater := NewRealStatefulSetStatusUpdater(fakeClient, setLister) + fakeClient.AddReactor("update", "statefulsets", func(action core.Action) (bool, runtime.Object, error) { + update := action.(core.UpdateAction) + return true, update.GetObject(), apierrors.NewConflict(action.GetResource().GroupResource(), set.Name, errors.New("Object already exists")) + }) + if err := updater.UpdateStatefulSetStatus(set, &status); err == nil { + t.Error("UpdateStatefulSetStatus failed to return an error on get failure") + } +} diff --git a/pkg/controller/statefulset/stateful_set_test.go b/pkg/controller/statefulset/stateful_set_test.go index c867cf4bbe3..bc1f46410ee 100644 --- a/pkg/controller/statefulset/stateful_set_test.go +++ b/pkg/controller/statefulset/stateful_set_test.go @@ -29,6 +29,7 @@ import ( "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions" "k8s.io/kubernetes/pkg/controller" + "k8s.io/kubernetes/pkg/controller/history" ) func alwaysReady() bool { return true } @@ -578,15 +579,18 @@ func newFakeStatefulSetController(initialObjects ...runtime.Object) (*StatefulSe client := fake.NewSimpleClientset(initialObjects...) informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc()) fpc := newFakeStatefulPodControl(informerFactory.Core().V1().Pods(), informerFactory.Apps().V1beta1().StatefulSets()) + ssu := newFakeStatefulSetStatusUpdater(informerFactory.Apps().V1beta1().StatefulSets()) ssc := NewStatefulSetController( informerFactory.Core().V1().Pods(), informerFactory.Apps().V1beta1().StatefulSets(), informerFactory.Core().V1().PersistentVolumeClaims(), + informerFactory.Apps().V1beta1().ControllerRevisions(), client, ) + ssh := history.NewFakeHistory(informerFactory.Apps().V1beta1().ControllerRevisions()) ssc.podListerSynced = alwaysReady ssc.setListerSynced = alwaysReady - ssc.control = NewDefaultStatefulSetControl(fpc) + ssc.control = NewDefaultStatefulSetControl(fpc, ssu, ssh) return ssc, fpc } @@ -614,7 +618,7 @@ func scaleUpStatefulSetController(set *apps.StatefulSet, ssc *StatefulSetControl if err != nil { return err } - for set.Status.Replicas < *set.Spec.Replicas { + for set.Status.ReadyReplicas < *set.Spec.Replicas { pods, err := spc.podsLister.Pods(set.Namespace).List(selector) ord := len(pods) - 1 pod := getPodAtOrdinal(pods, ord) diff --git a/pkg/controller/statefulset/stateful_set_utils.go b/pkg/controller/statefulset/stateful_set_utils.go index 198da850da8..2fa09349fea 100644 --- a/pkg/controller/statefulset/stateful_set_utils.go +++ b/pkg/controller/statefulset/stateful_set_utils.go @@ -17,15 +17,23 @@ limitations under the License. package statefulset import ( + "encoding/json" "fmt" "regexp" "strconv" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" podutil "k8s.io/kubernetes/pkg/api/v1/pod" apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" "k8s.io/kubernetes/pkg/controller" + "k8s.io/kubernetes/pkg/controller/history" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/strategicpatch" + + "k8s.io/client-go/kubernetes/scheme" "github.com/golang/glog" ) @@ -36,6 +44,7 @@ const maxUpdateRetries = 10 // updateConflictError is the error used to indicate that the maximum number of retries against the API server have // been attempted and we need to back off var updateConflictError = fmt.Errorf("aborting update after %d attempts", maxUpdateRetries) +var patchCodec = api.Codecs.LegacyCodec(apps.SchemeGroupVersion) // overlappingStatefulSets sorts a list of StatefulSets by creation timestamp, using their names as a tie breaker. // Generally used to tie break between StatefulSets that have overlapping selectors. @@ -246,6 +255,23 @@ func newControllerRef(set *apps.StatefulSet) *metav1.OwnerReference { } } +// setPodRevision sets the revision of Pod to revision by adding the StatefulSetRevisionLabel +func setPodRevision(pod *v1.Pod, revision string) { + if pod.Labels == nil { + pod.Labels = make(map[string]string) + } + pod.Labels[apps.StatefulSetRevisionLabel] = revision +} + +// getPodRevision gets the revision of Pod by inspecting the StatefulSetRevisionLabel. If pod has no revision the empty +// string is returned. +func getPodRevision(pod *v1.Pod) string { + if pod.Labels == nil { + return "" + } + return pod.Labels[apps.StatefulSetRevisionLabel] +} + // newStatefulSetPod returns a new Pod conforming to the set's Spec with an identity generated from ordinal. func newStatefulSetPod(set *apps.StatefulSet, ordinal int) *v1.Pod { pod, _ := controller.GetPodFromTemplate(&set.Spec.Template, set, newControllerRef(set)) @@ -255,6 +281,122 @@ func newStatefulSetPod(set *apps.StatefulSet, ordinal int) *v1.Pod { return pod } +// newVersionedStatefulSetPod creates a new Pod for a StatefulSet. currentSet is the representation of the set at the +// current revision. updateSet is the representation of the set at the updateRevision. currentRevision is the name of +// 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. +func newVersionedStatefulSetPod(currentSet, updateSet *apps.StatefulSet, currentRevision, updateRevision string, ordinal int) *v1.Pod { + if (currentSet.Spec.UpdateStrategy.Type == apps.RollingUpdateStatefulSetStrategyType && + ordinal < int(currentSet.Status.CurrentReplicas)) || + (currentSet.Spec.UpdateStrategy.Type == apps.PartitionStatefulSetStrategyType && + ordinal < int(currentSet.Spec.UpdateStrategy.Partition.Ordinal)) { + pod := newStatefulSetPod(currentSet, ordinal) + setPodRevision(pod, currentRevision) + return pod + } + pod := newStatefulSetPod(updateSet, ordinal) + setPodRevision(pod, updateRevision) + return pod +} + +// getPatch returns a strategic merge patch that can be applied to restore a StatefulSet to a +// previous version. If the returned error is nil the patch is valid. The current state that we save is just the +// PodSpecTemplate. We can modify this later to encompass more state (or less) and remain compatible with previously +// recorded patches. +func getPatch(set *apps.StatefulSet) ([]byte, error) { + str, err := runtime.Encode(patchCodec, set) + if err != nil { + return nil, err + } + var raw map[string]interface{} + json.Unmarshal([]byte(str), &raw) + objCopy := make(map[string]interface{}) + specCopy := make(map[string]interface{}) + spec := raw["spec"].(map[string]interface{}) + template := spec["template"].(map[string]interface{}) + specCopy["template"] = template + template["$patch"] = "replace" + objCopy["spec"] = specCopy + patch, err := json.Marshal(objCopy) + return patch, err +} + +// newRevision creates a new ControllerRevision containing a patch that reapplies the target state of set. +// The Revision of the returned ControllerRevision is set to revision. If the returned error is nil, the returned +// ControllerRevision is valid. StatefulSet revisions are stored as patches that re-apply the current state of set +// to a new StatefulSet using a strategic merge patch to replace the saved state of the new StatefulSet. +func newRevision(set *apps.StatefulSet, revision int64) (*apps.ControllerRevision, error) { + patch, err := getPatch(set) + if err != nil { + return nil, err + } + selector, err := metav1.LabelSelectorAsSelector(set.Spec.Selector) + if err != nil { + return nil, err + } + return history.NewControllerRevision(set, + controllerKind, + selector, + runtime.RawExtension{Raw: patch}, + revision) +} + +// applyRevision returns a new StatefulSet constructed by restoring the state in revision to set. If the returned error +// is nil, the returned StatefulSet is valid. +func applyRevision(set *apps.StatefulSet, revision *apps.ControllerRevision) (*apps.StatefulSet, error) { + obj, err := scheme.Scheme.DeepCopy(set) + if err != nil { + return nil, err + } + clone := obj.(*apps.StatefulSet) + patched, err := strategicpatch.StrategicMergePatch([]byte(runtime.EncodeOrDie(patchCodec, clone)), revision.Data.Raw, clone) + if err != nil { + return nil, err + } + err = json.Unmarshal(patched, clone) + if err != nil { + return nil, err + } + return clone, nil +} + +// nextRevision finds the next valid revision number based on revisions. If the length of revisions +// is 0 this is 1. Otherwise, it is 1 greater than the largest revision's Revision. This method +// assumes that revisions has been sorted by Revision. +func nextRevision(revisions []*apps.ControllerRevision) int64 { + count := len(revisions) + if count <= 0 { + return 1 + } + return revisions[count-1].Revision + 1 +} + +// inconsistentStatus returns true if the ObservedGeneration of status is greater than set's +// Generation or if any of the status's fields do not match those of set's status. +func inconsistentStatus(set *apps.StatefulSet, status *apps.StatefulSetStatus) bool { + return set.Status.ObservedGeneration == nil || + *status.ObservedGeneration > *set.Status.ObservedGeneration || + status.Replicas != set.Status.Replicas || + status.CurrentReplicas != set.Status.CurrentReplicas || + status.ReadyReplicas != set.Status.ReadyReplicas || + status.UpdatedReplicas != set.Status.UpdatedReplicas || + status.CurrentRevision != set.Status.CurrentRevision || + status.UpdateRevision != set.Status.UpdateRevision +} + +// completeRollingUpdate completes a rolling update when all of set's replica Pods have been updated +// to the updateRevision. status's currentRevision is set to updateRevision and its' updateRevision +// is set to the empty string. status's currentReplicas is set to updateReplicas and its updateReplicas +// are set to 0. +func completeRollingUpdate(set *apps.StatefulSet, status *apps.StatefulSetStatus) { + if set.Spec.UpdateStrategy.Type == apps.RollingUpdateStatefulSetStrategyType && + status.UpdatedReplicas == status.Replicas && + status.ReadyReplicas == status.Replicas { + status.CurrentReplicas = status.UpdatedReplicas + status.CurrentRevision = status.UpdateRevision + } +} + // ascendingOrdinal is a sort.Interface that Sorts a list of Pods based on the ordinals extracted // from the Pod. Pod's that have not been constructed by StatefulSet's have an ordinal of -1, and are therefore pushed // to the front of the list. diff --git a/pkg/controller/statefulset/stateful_set_utils_test.go b/pkg/controller/statefulset/stateful_set_utils_test.go index 956674c40bf..b27145e5ad1 100644 --- a/pkg/controller/statefulset/stateful_set_utils_test.go +++ b/pkg/controller/statefulset/stateful_set_utils_test.go @@ -32,6 +32,7 @@ import ( podutil "k8s.io/kubernetes/pkg/api/v1/pod" apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" "k8s.io/kubernetes/pkg/controller" + "k8s.io/kubernetes/pkg/controller/history" ) func TestGetParentNameAndOrdinal(t *testing.T) { @@ -287,6 +288,26 @@ func TestNewPodControllerRef(t *testing.T) { } } +func TestCreateApplyRevision(t *testing.T) { + set := newStatefulSet(1) + revision, err := newRevision(set, 1) + if err != nil { + t.Fatal(err) + } + set.Spec.Template.Spec.Containers[0].Name = "foo" + restoredSet, err := applyRevision(set, revision) + if err != nil { + t.Fatal(err) + } + restoredRevision, err := newRevision(restoredSet, 2) + if err != nil { + t.Fatal(err) + } + if !history.EqualRevision(revision, restoredRevision) { + t.Errorf("wanted %v got %v", string(revision.Data.Raw), string(restoredRevision.Data.Raw)) + } +} + func newPVC(name string) v1.PersistentVolumeClaim { return v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ @@ -354,6 +375,11 @@ func newStatefulSetWithVolumes(replicas int, name string, petMounts []v1.VolumeM Template: template, VolumeClaimTemplates: claims, ServiceName: "governingsvc", + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, + RevisionHistoryLimit: func() *int32 { + limit := int32(2) + return &limit + }(), }, } } diff --git a/pkg/kubectl/BUILD b/pkg/kubectl/BUILD index 134093b4fde..ce9878673a1 100644 --- a/pkg/kubectl/BUILD +++ b/pkg/kubectl/BUILD @@ -148,6 +148,7 @@ go_test( "//pkg/api/testapi:go_default_library", "//pkg/api/testing:go_default_library", "//pkg/api/v1:go_default_library", + "//pkg/apis/apps:go_default_library", "//pkg/apis/apps/v1beta1:go_default_library", "//pkg/apis/batch:go_default_library", "//pkg/apis/batch/v1:go_default_library", diff --git a/pkg/kubectl/history.go b/pkg/kubectl/history.go index ab85dad33eb..c058d5a301d 100644 --- a/pkg/kubectl/history.go +++ b/pkg/kubectl/history.go @@ -52,6 +52,8 @@ func HistoryViewerFor(kind schema.GroupKind, c clientset.Interface) (HistoryView switch kind { case extensions.Kind("Deployment"), apps.Kind("Deployment"): return &DeploymentHistoryViewer{c}, nil + case apps.Kind("StatefulSet"): + return &StatefulSetHistoryViewer{c}, nil case extensions.Kind("DaemonSet"): return &DaemonSetHistoryViewer{c}, nil } @@ -200,6 +202,58 @@ func (h *DaemonSetHistoryViewer) ViewHistory(namespace, name string, revision in }) } +type StatefulSetHistoryViewer struct { + c clientset.Interface +} + +func getOwner(revision apps.ControllerRevision) *metav1.OwnerReference { + ownerRefs := revision.GetOwnerReferences() + for i := range ownerRefs { + owner := &ownerRefs[i] + if owner.Controller != nil && *owner.Controller == true { + return owner + } + } + return nil +} + +// ViewHistory returns a list of the revision history of a statefulset +// TODO: this should be a describer +// TODO: needs to implement detailed revision view +func (h *StatefulSetHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) { + + sts, err := h.c.Apps().StatefulSets(namespace).Get(name, metav1.GetOptions{}) + if err != nil { + return "", fmt.Errorf("failed to retrieve statefulset %s", err) + } + selector, err := metav1.LabelSelectorAsSelector(sts.Spec.Selector) + if err != nil { + return "", fmt.Errorf("failed to retrieve statefulset history %s", err) + } + revisions, err := h.c.Apps().ControllerRevisions(namespace).List(metav1.ListOptions{LabelSelector: selector.String()}) + if err != nil { + return "", fmt.Errorf("failed to retrieve statefulset history %s", err) + } + if len(revisions.Items) <= 0 { + return "No rollout history found.", nil + } + revisionNumbers := make([]int64, len(revisions.Items)) + for i := range revisions.Items { + if owner := getOwner(revisions.Items[i]); owner != nil && owner.UID == sts.UID { + revisionNumbers[i] = revisions.Items[i].Revision + } + } + sliceutil.SortInts64(revisionNumbers) + + return tabbedString(func(out io.Writer) error { + fmt.Fprintf(out, "REVISION\n") + for _, r := range revisionNumbers { + fmt.Fprintf(out, "%d\n", r) + } + return nil + }) +} + // controlledHistories returns all ControllerRevisions controlled by the given DaemonSet // TODO: Use external version DaemonSet instead when #3955 is fixed func controlledHistories(c clientset.Interface, ds *extensions.DaemonSet) ([]*appsv1beta1.ControllerRevision, error) { diff --git a/pkg/kubectl/rollout_status.go b/pkg/kubectl/rollout_status.go index bb466075a88..37879cf11bf 100644 --- a/pkg/kubectl/rollout_status.go +++ b/pkg/kubectl/rollout_status.go @@ -24,6 +24,7 @@ import ( "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + appsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion" extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion" "k8s.io/kubernetes/pkg/controller/deployment/util" ) @@ -39,6 +40,8 @@ func StatusViewerFor(kind schema.GroupKind, c internalclientset.Interface) (Stat return &DeploymentStatusViewer{c.Extensions()}, nil case extensions.Kind("DaemonSet"): return &DaemonSetStatusViewer{c.Extensions()}, nil + case apps.Kind("StatefulSet"): + return &StatefulSetStatusViewer{c.Apps()}, nil } return nil, fmt.Errorf("no status viewer has been implemented for %v", kind) } @@ -51,6 +54,10 @@ type DaemonSetStatusViewer struct { c extensionsclient.DaemonSetsGetter } +type StatefulSetStatusViewer struct { + c appsclient.StatefulSetsGetter +} + // Status returns a message describing deployment status, and a bool value indicating if the status is considered done func (s *DeploymentStatusViewer) Status(namespace, name string, revision int64) (string, bool, error) { deployment, err := s.c.Deployments(namespace).Get(name, metav1.GetOptions{}) @@ -107,3 +114,34 @@ func (s *DaemonSetStatusViewer) Status(namespace, name string, revision int64) ( } return fmt.Sprintf("Waiting for daemon set spec update to be observed...\n"), false, nil } + +// Status returns a message describing statefulset status, and a bool value indicating if the status is considered done +func (s *StatefulSetStatusViewer) Status(namespace, name string, revision int64) (string, bool, error) { + sts, err := s.c.StatefulSets(namespace).Get(name, metav1.GetOptions{}) + if err != nil { + return "", false, err + } + if sts.Spec.UpdateStrategy.Type == apps.OnDeleteStatefulSetStrategyType { + return "", true, fmt.Errorf("%s updateStrategy does not have a Status`", apps.OnDeleteStatefulSetStrategyType) + } + if sts.Status.ObservedGeneration == nil || sts.Generation > *sts.Status.ObservedGeneration { + return "Waiting for statefulset spec update to be observed...\n", false, nil + } + if sts.Status.ReadyReplicas < sts.Spec.Replicas { + return fmt.Sprintf("Waiting for %d pods to be ready...\n", sts.Spec.Replicas-sts.Status.ReadyReplicas), false, nil + } + if sts.Spec.UpdateStrategy.Type == apps.PartitionStatefulSetStrategyType { + if sts.Status.UpdatedReplicas < (sts.Spec.Replicas - sts.Spec.UpdateStrategy.Partition.Ordinal) { + return fmt.Sprintf("Waiting for partitioned roll out to finish: %d out of %d new pods have been updated...\n", + sts.Status.UpdatedReplicas, (sts.Spec.Replicas - sts.Spec.UpdateStrategy.Partition.Ordinal)), false, nil + } + return fmt.Sprintf("partitioned roll out complete: %d new pods have been updated...\n", + sts.Status.UpdatedReplicas), false, nil + } + if sts.Status.UpdateRevision != sts.Status.CurrentRevision { + return fmt.Sprintf("waiting for statefulset rolling update to complete %d pods at revision %s...\n", + sts.Status.UpdatedReplicas, sts.Status.UpdateRevision), false, nil + } + return fmt.Sprintf("statefulset rolling update complete %d pods at revision %s...\n", sts.Status.CurrentReplicas, sts.Status.CurrentRevision), true, nil + +} diff --git a/pkg/kubectl/rollout_status_test.go b/pkg/kubectl/rollout_status_test.go index 1c3682de005..c84c5636776 100644 --- a/pkg/kubectl/rollout_status_test.go +++ b/pkg/kubectl/rollout_status_test.go @@ -17,9 +17,12 @@ limitations under the License. package kubectl import ( + "fmt" "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" ) @@ -231,6 +234,163 @@ func TestDaemonSetStatusViewerStatus(t *testing.T) { } } +func TestStatefulSetStatusViewerStatus(t *testing.T) { + tests := []struct { + name string + generation int64 + strategy apps.StatefulSetUpdateStrategy + status apps.StatefulSetStatus + msg string + done bool + err bool + }{ + { + name: "on delete returns an error", + generation: 1, + strategy: apps.StatefulSetUpdateStrategy{Type: apps.OnDeleteStatefulSetStrategyType}, + status: apps.StatefulSetStatus{ + ObservedGeneration: func() *int64 { + generation := int64(1) + return &generation + }(), + Replicas: 0, + ReadyReplicas: 1, + CurrentReplicas: 0, + UpdatedReplicas: 0, + }, + + msg: "", + done: true, + err: true, + }, + { + name: "unobserved update is not complete", + generation: 2, + strategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, + status: apps.StatefulSetStatus{ + ObservedGeneration: func() *int64 { + generation := int64(1) + return &generation + }(), + Replicas: 3, + ReadyReplicas: 3, + CurrentReplicas: 3, + UpdatedReplicas: 0, + }, + + msg: "Waiting for statefulset spec update to be observed...\n", + done: false, + err: false, + }, + { + name: "if all pods are not ready the update is not complete", + generation: 1, + strategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, + status: apps.StatefulSetStatus{ + ObservedGeneration: func() *int64 { + generation := int64(2) + return &generation + }(), + Replicas: 3, + ReadyReplicas: 2, + CurrentReplicas: 3, + UpdatedReplicas: 0, + }, + + msg: fmt.Sprintf("Waiting for %d pods to be ready...\n", 1), + done: false, + err: false, + }, + { + name: "partition update completes when all replicas above the partition are updated", + generation: 1, + strategy: apps.StatefulSetUpdateStrategy{Type: apps.PartitionStatefulSetStrategyType, + Partition: func() *apps.PartitionStatefulSetStrategy { + return &apps.PartitionStatefulSetStrategy{Ordinal: 2} + }()}, + status: apps.StatefulSetStatus{ + ObservedGeneration: func() *int64 { + generation := int64(2) + return &generation + }(), + Replicas: 3, + ReadyReplicas: 3, + CurrentReplicas: 2, + UpdatedReplicas: 1, + }, + + msg: fmt.Sprintf("partitioned roll out complete: %d new pods have been updated...\n", 1), + done: true, + err: false, + }, + { + name: "partition update is in progress if all pods above the partition have not been updated", + generation: 1, + strategy: apps.StatefulSetUpdateStrategy{Type: apps.PartitionStatefulSetStrategyType, + Partition: func() *apps.PartitionStatefulSetStrategy { + return &apps.PartitionStatefulSetStrategy{Ordinal: 2} + }()}, + status: apps.StatefulSetStatus{ + ObservedGeneration: func() *int64 { + generation := int64(2) + return &generation + }(), + Replicas: 3, + ReadyReplicas: 3, + CurrentReplicas: 3, + UpdatedReplicas: 0, + }, + + msg: fmt.Sprintf("Waiting for partitioned roll out to finish: %d out of %d new pods have been updated...\n", 0, 1), + done: true, + err: false, + }, + { + name: "update completes when all replicas are current", + generation: 1, + strategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, + status: apps.StatefulSetStatus{ + ObservedGeneration: func() *int64 { + generation := int64(2) + return &generation + }(), + Replicas: 3, + ReadyReplicas: 3, + CurrentReplicas: 3, + UpdatedReplicas: 3, + CurrentRevision: "foo", + UpdateRevision: "foo", + }, + + msg: fmt.Sprintf("statefulset rolling update complete %d pods at revision %s...\n", 3, "foo"), + done: true, + err: false, + }, + } + for i := range tests { + test := tests[i] + s := newStatefulSet(3) + s.Status = test.status + s.Spec.UpdateStrategy = test.strategy + s.Generation = test.generation + client := fake.NewSimpleClientset(s).Apps() + dsv := &StatefulSetStatusViewer{c: client} + msg, done, err := dsv.Status(s.Namespace, s.Name, 0) + if test.err && err == nil { + t.Fatalf("%s: expected error", test.name) + } + if !test.err && err != nil { + t.Fatalf("%s: %s", test.name, err) + } + if done && !test.done { + t.Errorf("%s: want done %v got %v", test.name, done, test.done) + } + if msg != test.msg { + t.Errorf("%s: want message %s got %s", test.name, test.msg, msg) + } + } +} + func TestDaemonSetStatusViewerStatusWithWrongUpdateStrategyType(t *testing.T) { d := &extensions.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ @@ -252,3 +412,36 @@ func TestDaemonSetStatusViewerStatusWithWrongUpdateStrategyType(t *testing.T) { t.Errorf("Status for daemon sets with UpdateStrategy type different than RollingUpdate should return error. Instead got: msg: %s\ndone: %t\n err: %v", msg, done, err) } } + +func newStatefulSet(replicas int32) *apps.StatefulSet { + return &apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + Namespace: metav1.NamespaceDefault, + Labels: map[string]string{"a": "b"}, + }, + Spec: apps.StatefulSetSpec{ + PodManagementPolicy: apps.OrderedReadyPodManagement, + Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}, + Template: api.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"a": "b"}, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "test", + Image: "test_image", + ImagePullPolicy: api.PullIfNotPresent, + }, + }, + RestartPolicy: api.RestartPolicyAlways, + DNSPolicy: api.DNSClusterFirst, + }, + }, + Replicas: replicas, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, + }, + Status: apps.StatefulSetStatus{}, + } +} diff --git a/pkg/registry/apps/statefulset/storage/storage_test.go b/pkg/registry/apps/statefulset/storage/storage_test.go index 949a208dfad..9d4001d82ea 100644 --- a/pkg/registry/apps/statefulset/storage/storage_test.go +++ b/pkg/registry/apps/statefulset/storage/storage_test.go @@ -76,7 +76,8 @@ func validNewStatefulSet() *apps.StatefulSet { DNSPolicy: api.DNSClusterFirst, }, }, - Replicas: 7, + Replicas: 7, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, Status: apps.StatefulSetStatus{}, } diff --git a/pkg/registry/apps/statefulset/strategy_test.go b/pkg/registry/apps/statefulset/strategy_test.go index 34010031b46..6c65b687e1f 100644 --- a/pkg/registry/apps/statefulset/strategy_test.go +++ b/pkg/registry/apps/statefulset/strategy_test.go @@ -54,6 +54,7 @@ func TestStatefulSetStrategy(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: &metav1.LabelSelector{MatchLabels: validSelector}, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, Status: apps.StatefulSetStatus{Replicas: 3}, } @@ -74,6 +75,7 @@ func TestStatefulSetStrategy(t *testing.T) { PodManagementPolicy: apps.OrderedReadyPodManagement, Selector: ps.Spec.Selector, Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, Status: apps.StatefulSetStatus{Replicas: 4}, } @@ -122,9 +124,10 @@ func TestStatefulSetStatusStrategy(t *testing.T) { oldPS := &apps.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "10"}, Spec: apps.StatefulSetSpec{ - Replicas: 3, - Selector: &metav1.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplate.Template, + Replicas: 3, + Selector: &metav1.LabelSelector{MatchLabels: validSelector}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, Status: apps.StatefulSetStatus{ Replicas: 1, @@ -133,9 +136,10 @@ func TestStatefulSetStatusStrategy(t *testing.T) { newPS := &apps.StatefulSet{ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "9"}, Spec: apps.StatefulSetSpec{ - Replicas: 1, - Selector: &metav1.LabelSelector{MatchLabels: validSelector}, - Template: validPodTemplate.Template, + Replicas: 1, + Selector: &metav1.LabelSelector{MatchLabels: validSelector}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, }, Status: apps.StatefulSetStatus{ Replicas: 2, diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go index ce536644674..2db7f3d03de 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go @@ -264,6 +264,7 @@ func init() { rbac.NewRule("get", "list", "watch").Groups(appsGroup).Resources("statefulsets").RuleOrDie(), rbac.NewRule("update").Groups(appsGroup).Resources("statefulsets/status").RuleOrDie(), rbac.NewRule("get", "create", "delete", "update", "patch").Groups(legacyGroup).Resources("pods").RuleOrDie(), + rbac.NewRule("get", "create", "delete", "update", "patch", "list", "watch").Groups(appsGroup).Resources("controllerrevisions").RuleOrDie(), rbac.NewRule("get", "create").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(), eventsRule(), }, diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml index 8f7d222fcf7..4f97e5416b4 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml @@ -975,6 +975,18 @@ items: - get - patch - update + - apiGroups: + - apps + resources: + - controllerrevisions + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/types.go b/staging/src/k8s.io/client-go/pkg/apis/apps/types.go index 656c8e30502..04e9b50084d 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/types.go +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/types.go @@ -60,6 +60,54 @@ const ( ParallelPodManagement = "Parallel" ) +// StatefulSetUpdateStrategy indicates the strategy that the StatefulSet +// controller will use to perform updates. It includes any additional parameters +// necessary to perform the update for the indicated strategy. +type StatefulSetUpdateStrategy struct { + // Type indicates the type of the StatefulSetUpdateStrategy. + Type StatefulSetUpdateStrategyType + // Partition is used to communicate the ordinal at which to partition + // the StatefulSet when Type is PartitionStatefulSetStrategyType. This + // value must be set when Type is PartitionStatefulSetStrategyType, + // and it must be nil otherwise. + Partition *PartitionStatefulSetStrategy +} + +// StatefulSetUpdateStrategyType is a string enumeration type that enumerates +// all possible update strategies for the StatefulSet controller. +type StatefulSetUpdateStrategyType string + +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 + // applied to all Pods in the StatefulSet with respect to the StatefulSet + // ordering constraints. When a scale operation is performed with this + // strategy, new Pods will be created from the specification version indicated + // by the StatefulSet's updateRevision. + RollingUpdateStatefulSetStrategyType = "RollingUpdate" + // OnDeleteStatefulSetStrategyType triggers the legacy behavior. Version + // tracking and ordered rolling restarts are disabled. Pods are recreated + // from the StatefulSetSpec when they are manually deleted. When a scale + // operation is performed with this strategy,specification version indicated + // by the StatefulSet's currentRevision. + OnDeleteStatefulSetStrategyType = "OnDelete" +) + +// PartitionStatefulSetStrategy contains the parameters used with the +// PartitionStatefulSetStrategyType. +type PartitionStatefulSetStrategy struct { + // Ordinal indicates the ordinal at which the StatefulSet should be + // partitioned. + Ordinal int32 +} + // A StatefulSetSpec is the specification of a StatefulSet. type StatefulSetSpec struct { // Replicas is the desired number of replicas of the given Template. @@ -109,16 +157,47 @@ type StatefulSetSpec struct { // all pods at once. // +optional PodManagementPolicy PodManagementPolicyType + + // updateStrategy indicates the StatefulSetUpdateStrategy that will be + // employed to update Pods in the StatefulSet when a revision is made to + // Template. + UpdateStrategy StatefulSetUpdateStrategy + + // revisionHistoryLimit is the maximum number of revisions that will + // be maintained in the StatefulSet's revision history. The revision history + // consists of all revisions not represented by a currently applied + // StatefulSetSpec version. The default value is 10. + RevisionHistoryLimit *int32 } // StatefulSetStatus represents the current state of a StatefulSet. type StatefulSetStatus struct { - // most recent generation observed by this StatefulSet. + // observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the + // StatefulSet's generation, which is updated on mutation by the API Server. // +optional ObservedGeneration *int64 - // Replicas is the number of actual replicas. + // replicas is the number of Pods created by the StatefulSet controller. Replicas int32 + + // readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition. + ReadyReplicas int32 + + // currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by currentRevision. + CurrentReplicas int32 + + // updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by updateRevision. + UpdatedReplicas int32 + + // currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the + // sequence [0,currentReplicas). + CurrentRevision string + + // updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence + // [replicas-updatedReplicas,replicas) + UpdateRevision string } // StatefulSetList is a collection of StatefulSets. diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/conversion.go b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/conversion.go index 9ab3ce2b09e..95fb06d48e9 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/conversion.go +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/conversion.go @@ -37,6 +37,8 @@ func addConversionFuncs(scheme *runtime.Scheme) error { err := scheme.AddConversionFuncs( Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec, Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec, + Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy, + Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy, // extensions // TODO: below conversions should be dropped in favor of auto-generated // ones, see https://github.com/kubernetes/kubernetextensionsssues/39865 @@ -109,6 +111,15 @@ func Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(in *StatefulSetSpec } else { out.VolumeClaimTemplates = nil } + if err := Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { + return err + } + if in.RevisionHistoryLimit != nil { + out.RevisionHistoryLimit = new(int32) + *out.RevisionHistoryLimit = *in.RevisionHistoryLimit + } else { + out.RevisionHistoryLimit = nil + } out.ServiceName = in.ServiceName out.PodManagementPolicy = apps.PodManagementPolicyType(in.PodManagementPolicy) return nil @@ -140,8 +151,39 @@ func Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec(in *apps.StatefulSe } else { out.VolumeClaimTemplates = nil } + if in.RevisionHistoryLimit != nil { + out.RevisionHistoryLimit = new(int32) + *out.RevisionHistoryLimit = *in.RevisionHistoryLimit + } else { + out.RevisionHistoryLimit = nil + } out.ServiceName = in.ServiceName out.PodManagementPolicy = PodManagementPolicyType(in.PodManagementPolicy) + if err := Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { + return err + } + return nil +} + +func Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy(in *StatefulSetUpdateStrategy, out *apps.StatefulSetUpdateStrategy, s conversion.Scope) error { + out.Type = apps.StatefulSetUpdateStrategyType(in.Type) + if in.Partition != nil { + out.Partition = new(apps.PartitionStatefulSetStrategy) + out.Partition.Ordinal = in.Partition.Ordinal + } else { + out.Partition = nil + } + return nil +} + +func Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(in *apps.StatefulSetUpdateStrategy, out *StatefulSetUpdateStrategy, s conversion.Scope) error { + out.Type = StatefulSetUpdateStrategyType(in.Type) + if in.Partition != nil { + out.Partition = new(PartitionStatefulSetStrategy) + out.Partition.Ordinal = in.Partition.Ordinal + } else { + out.Partition = nil + } return nil } diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/defaults.go b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/defaults.go index 52b881d6644..895c8b90eb7 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/defaults.go +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/defaults.go @@ -30,6 +30,10 @@ func SetDefaults_StatefulSet(obj *StatefulSet) { if len(obj.Spec.PodManagementPolicy) == 0 { obj.Spec.PodManagementPolicy = OrderedReadyPodManagement } + + if obj.Spec.UpdateStrategy.Type == "" { + obj.Spec.UpdateStrategy.Type = OnDeleteStatefulSetStrategyType + } labels := obj.Spec.Template.Labels if labels != nil { if obj.Spec.Selector == nil { @@ -45,6 +49,11 @@ func SetDefaults_StatefulSet(obj *StatefulSet) { obj.Spec.Replicas = new(int32) *obj.Spec.Replicas = 1 } + if obj.Spec.RevisionHistoryLimit == nil { + obj.Spec.RevisionHistoryLimit = new(int32) + *obj.Spec.RevisionHistoryLimit = 10 + } + } // SetDefaults_Deployment sets additional defaults compared to its counterpart diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/generated.pb.go b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/generated.pb.go index 40cee7e11d9..26cee98238c 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/generated.pb.go @@ -34,6 +34,7 @@ limitations under the License. DeploymentSpec DeploymentStatus DeploymentStrategy + PartitionStatefulSetStrategy RollbackConfig RollingUpdateDeployment Scale @@ -43,6 +44,7 @@ limitations under the License. StatefulSetList StatefulSetSpec StatefulSetStatus + StatefulSetUpdateStrategy */ package v1beta1 @@ -108,43 +110,55 @@ func (m *DeploymentStrategy) Reset() { *m = DeploymentStrateg func (*DeploymentStrategy) ProtoMessage() {} func (*DeploymentStrategy) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{8} } +func (m *PartitionStatefulSetStrategy) Reset() { *m = PartitionStatefulSetStrategy{} } +func (*PartitionStatefulSetStrategy) ProtoMessage() {} +func (*PartitionStatefulSetStrategy) Descriptor() ([]byte, []int) { + return fileDescriptorGenerated, []int{9} +} + func (m *RollbackConfig) Reset() { *m = RollbackConfig{} } func (*RollbackConfig) ProtoMessage() {} -func (*RollbackConfig) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{9} } +func (*RollbackConfig) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{10} } func (m *RollingUpdateDeployment) Reset() { *m = RollingUpdateDeployment{} } func (*RollingUpdateDeployment) ProtoMessage() {} func (*RollingUpdateDeployment) Descriptor() ([]byte, []int) { - return fileDescriptorGenerated, []int{10} + return fileDescriptorGenerated, []int{11} } func (m *Scale) Reset() { *m = Scale{} } func (*Scale) ProtoMessage() {} -func (*Scale) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{11} } +func (*Scale) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{12} } func (m *ScaleSpec) Reset() { *m = ScaleSpec{} } func (*ScaleSpec) ProtoMessage() {} -func (*ScaleSpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{12} } +func (*ScaleSpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{13} } func (m *ScaleStatus) Reset() { *m = ScaleStatus{} } func (*ScaleStatus) ProtoMessage() {} -func (*ScaleStatus) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{13} } +func (*ScaleStatus) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{14} } func (m *StatefulSet) Reset() { *m = StatefulSet{} } func (*StatefulSet) ProtoMessage() {} -func (*StatefulSet) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{14} } +func (*StatefulSet) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{15} } func (m *StatefulSetList) Reset() { *m = StatefulSetList{} } func (*StatefulSetList) ProtoMessage() {} -func (*StatefulSetList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{15} } +func (*StatefulSetList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{16} } func (m *StatefulSetSpec) Reset() { *m = StatefulSetSpec{} } func (*StatefulSetSpec) ProtoMessage() {} -func (*StatefulSetSpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{16} } +func (*StatefulSetSpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{17} } func (m *StatefulSetStatus) Reset() { *m = StatefulSetStatus{} } func (*StatefulSetStatus) ProtoMessage() {} -func (*StatefulSetStatus) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{17} } +func (*StatefulSetStatus) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{18} } + +func (m *StatefulSetUpdateStrategy) Reset() { *m = StatefulSetUpdateStrategy{} } +func (*StatefulSetUpdateStrategy) ProtoMessage() {} +func (*StatefulSetUpdateStrategy) Descriptor() ([]byte, []int) { + return fileDescriptorGenerated, []int{19} +} func init() { proto.RegisterType((*ControllerRevision)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.ControllerRevision") @@ -156,6 +170,7 @@ func init() { proto.RegisterType((*DeploymentSpec)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.DeploymentSpec") proto.RegisterType((*DeploymentStatus)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.DeploymentStatus") proto.RegisterType((*DeploymentStrategy)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.DeploymentStrategy") + proto.RegisterType((*PartitionStatefulSetStrategy)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.PartitionStatefulSetStrategy") proto.RegisterType((*RollbackConfig)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.RollbackConfig") proto.RegisterType((*RollingUpdateDeployment)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.RollingUpdateDeployment") proto.RegisterType((*Scale)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.Scale") @@ -165,6 +180,7 @@ func init() { proto.RegisterType((*StatefulSetList)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.StatefulSetList") proto.RegisterType((*StatefulSetSpec)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.StatefulSetSpec") proto.RegisterType((*StatefulSetStatus)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.StatefulSetStatus") + proto.RegisterType((*StatefulSetUpdateStrategy)(nil), "k8s.io.client-go.pkg.apis.apps.v1beta1.StatefulSetUpdateStrategy") } func (m *ControllerRevision) Marshal() (dAtA []byte, err error) { size := m.Size() @@ -583,6 +599,27 @@ func (m *DeploymentStrategy) MarshalTo(dAtA []byte) (int, error) { return i, nil } +func (m *PartitionStatefulSetStrategy) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PartitionStatefulSetStrategy) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0x8 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.Ordinal)) + return i, nil +} + func (m *RollbackConfig) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -885,6 +922,19 @@ func (m *StatefulSetSpec) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintGenerated(dAtA, i, uint64(len(m.PodManagementPolicy))) i += copy(dAtA[i:], m.PodManagementPolicy) + dAtA[i] = 0x3a + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.UpdateStrategy.Size())) + n27, err := m.UpdateStrategy.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n27 + if m.RevisionHistoryLimit != nil { + dAtA[i] = 0x40 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(*m.RevisionHistoryLimit)) + } return i, nil } @@ -911,6 +961,55 @@ func (m *StatefulSetStatus) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x10 i++ i = encodeVarintGenerated(dAtA, i, uint64(m.Replicas)) + dAtA[i] = 0x18 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.ReadyReplicas)) + dAtA[i] = 0x20 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.CurrentReplicas)) + dAtA[i] = 0x28 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.UpdatedReplicas)) + dAtA[i] = 0x32 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.CurrentRevision))) + i += copy(dAtA[i:], m.CurrentRevision) + dAtA[i] = 0x3a + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.UpdateRevision))) + i += copy(dAtA[i:], m.UpdateRevision) + return i, nil +} + +func (m *StatefulSetUpdateStrategy) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *StatefulSetUpdateStrategy) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + dAtA[i] = 0xa + i++ + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Type))) + i += copy(dAtA[i:], m.Type) + if m.Partition != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(m.Partition.Size())) + n28, err := m.Partition.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n28 + } return i, nil } @@ -1090,6 +1189,13 @@ func (m *DeploymentStrategy) Size() (n int) { return n } +func (m *PartitionStatefulSetStrategy) Size() (n int) { + var l int + _ = l + n += 1 + sovGenerated(uint64(m.Ordinal)) + return n +} + func (m *RollbackConfig) Size() (n int) { var l int _ = l @@ -1195,6 +1301,11 @@ func (m *StatefulSetSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) l = len(m.PodManagementPolicy) n += 1 + l + sovGenerated(uint64(l)) + l = m.UpdateStrategy.Size() + n += 1 + l + sovGenerated(uint64(l)) + if m.RevisionHistoryLimit != nil { + n += 1 + sovGenerated(uint64(*m.RevisionHistoryLimit)) + } return n } @@ -1205,6 +1316,25 @@ func (m *StatefulSetStatus) Size() (n int) { n += 1 + sovGenerated(uint64(*m.ObservedGeneration)) } n += 1 + sovGenerated(uint64(m.Replicas)) + n += 1 + sovGenerated(uint64(m.ReadyReplicas)) + n += 1 + sovGenerated(uint64(m.CurrentReplicas)) + n += 1 + sovGenerated(uint64(m.UpdatedReplicas)) + l = len(m.CurrentRevision) + n += 1 + l + sovGenerated(uint64(l)) + l = len(m.UpdateRevision) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + +func (m *StatefulSetUpdateStrategy) Size() (n int) { + var l int + _ = l + l = len(m.Type) + n += 1 + l + sovGenerated(uint64(l)) + if m.Partition != nil { + l = m.Partition.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -1350,6 +1480,16 @@ func (this *DeploymentStrategy) String() string { }, "") return s } +func (this *PartitionStatefulSetStrategy) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PartitionStatefulSetStrategy{`, + `Ordinal:` + fmt.Sprintf("%v", this.Ordinal) + `,`, + `}`, + }, "") + return s +} func (this *RollbackConfig) String() string { if this == nil { return "nil" @@ -1449,6 +1589,8 @@ func (this *StatefulSetSpec) String() string { `VolumeClaimTemplates:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.VolumeClaimTemplates), "PersistentVolumeClaim", "k8s_io_kubernetes_pkg_api_v1.PersistentVolumeClaim", 1), `&`, ``, 1) + `,`, `ServiceName:` + fmt.Sprintf("%v", this.ServiceName) + `,`, `PodManagementPolicy:` + fmt.Sprintf("%v", this.PodManagementPolicy) + `,`, + `UpdateStrategy:` + strings.Replace(strings.Replace(this.UpdateStrategy.String(), "StatefulSetUpdateStrategy", "StatefulSetUpdateStrategy", 1), `&`, ``, 1) + `,`, + `RevisionHistoryLimit:` + valueToStringGenerated(this.RevisionHistoryLimit) + `,`, `}`, }, "") return s @@ -1460,6 +1602,22 @@ func (this *StatefulSetStatus) String() string { s := strings.Join([]string{`&StatefulSetStatus{`, `ObservedGeneration:` + valueToStringGenerated(this.ObservedGeneration) + `,`, `Replicas:` + fmt.Sprintf("%v", this.Replicas) + `,`, + `ReadyReplicas:` + fmt.Sprintf("%v", this.ReadyReplicas) + `,`, + `CurrentReplicas:` + fmt.Sprintf("%v", this.CurrentReplicas) + `,`, + `UpdatedReplicas:` + fmt.Sprintf("%v", this.UpdatedReplicas) + `,`, + `CurrentRevision:` + fmt.Sprintf("%v", this.CurrentRevision) + `,`, + `UpdateRevision:` + fmt.Sprintf("%v", this.UpdateRevision) + `,`, + `}`, + }, "") + return s +} +func (this *StatefulSetUpdateStrategy) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&StatefulSetUpdateStrategy{`, + `Type:` + fmt.Sprintf("%v", this.Type) + `,`, + `Partition:` + strings.Replace(fmt.Sprintf("%v", this.Partition), "PartitionStatefulSetStrategy", "PartitionStatefulSetStrategy", 1) + `,`, `}`, }, "") return s @@ -3016,6 +3174,75 @@ func (m *DeploymentStrategy) Unmarshal(dAtA []byte) error { } return nil } +func (m *PartitionStatefulSetStrategy) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PartitionStatefulSetStrategy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PartitionStatefulSetStrategy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Ordinal", wireType) + } + m.Ordinal = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Ordinal |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *RollbackConfig) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -4076,6 +4303,56 @@ func (m *StatefulSetSpec) Unmarshal(dAtA []byte) error { } m.PodManagementPolicy = PodManagementPolicyType(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdateStrategy", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.UpdateStrategy.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RevisionHistoryLimit", wireType) + } + var v int32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.RevisionHistoryLimit = &v default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -4165,6 +4442,233 @@ func (m *StatefulSetStatus) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReadyReplicas", wireType) + } + m.ReadyReplicas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReadyReplicas |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CurrentReplicas", wireType) + } + m.CurrentReplicas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CurrentReplicas |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedReplicas", wireType) + } + m.UpdatedReplicas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UpdatedReplicas |= (int32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CurrentRevision", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CurrentRevision = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdateRevision", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UpdateRevision = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *StatefulSetUpdateStrategy) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: StatefulSetUpdateStrategy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: StatefulSetUpdateStrategy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = StatefulSetUpdateStrategyType(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Partition", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Partition == nil { + m.Partition = &PartitionStatefulSetStrategy{} + } + if err := m.Partition.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -4296,108 +4800,119 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 1647 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x58, 0xcf, 0x4f, 0x1b, 0xc7, - 0x17, 0x67, 0x8d, 0x0d, 0x66, 0x08, 0x26, 0x0c, 0x7c, 0xc1, 0x5f, 0x52, 0x19, 0xe4, 0x43, 0x42, - 0xaa, 0x64, 0xdd, 0x90, 0x34, 0x3f, 0xa0, 0x8a, 0x8a, 0x49, 0x9a, 0xa6, 0x82, 0x82, 0xc6, 0x10, - 0x35, 0x69, 0x2a, 0x65, 0xbc, 0x9e, 0x2c, 0x1b, 0xf6, 0x97, 0x76, 0xc6, 0x6e, 0x7c, 0xeb, 0xa5, - 0x87, 0x4a, 0x3d, 0xf4, 0x1f, 0xa8, 0xda, 0x73, 0x55, 0xa9, 0xff, 0x06, 0x6a, 0x2f, 0x51, 0x4f, - 0x51, 0x0f, 0xa8, 0x90, 0xbf, 0xa1, 0x97, 0x9c, 0xaa, 0x99, 0x9d, 0xfd, 0xe5, 0xb5, 0xc1, 0x50, - 0x95, 0x4b, 0x6f, 0xde, 0x79, 0xef, 0x7d, 0xde, 0x8f, 0x79, 0xef, 0xcd, 0x7b, 0x06, 0xb7, 0x76, - 0x6f, 0x53, 0xd5, 0x70, 0x2a, 0xbb, 0xcd, 0x3a, 0xf1, 0x6c, 0xc2, 0x08, 0xad, 0xb8, 0xbb, 0x7a, - 0x05, 0xbb, 0x06, 0xad, 0x60, 0xd7, 0xa5, 0x95, 0xd6, 0xb5, 0x3a, 0x61, 0xf8, 0x5a, 0x45, 0x27, - 0x36, 0xf1, 0x30, 0x23, 0x0d, 0xd5, 0xf5, 0x1c, 0xe6, 0xc0, 0x4b, 0xbe, 0xa0, 0x1a, 0x09, 0xaa, - 0xee, 0xae, 0xae, 0x72, 0x41, 0x95, 0x0b, 0xaa, 0x52, 0x70, 0xf6, 0xaa, 0x6e, 0xb0, 0x9d, 0x66, - 0x5d, 0xd5, 0x1c, 0xab, 0xa2, 0x3b, 0xba, 0x53, 0x11, 0xf2, 0xf5, 0xe6, 0x73, 0xf1, 0x25, 0x3e, - 0xc4, 0x2f, 0x1f, 0x77, 0xf6, 0x86, 0x34, 0x08, 0xbb, 0x86, 0x85, 0xb5, 0x1d, 0xc3, 0x26, 0x5e, - 0x3b, 0x32, 0xc9, 0x22, 0x0c, 0x57, 0x5a, 0x29, 0x6b, 0x66, 0x2b, 0xbd, 0xa4, 0xbc, 0xa6, 0xcd, - 0x0c, 0x8b, 0xa4, 0x04, 0x6e, 0x1e, 0x27, 0x40, 0xb5, 0x1d, 0x62, 0xe1, 0x94, 0xdc, 0xf5, 0x5e, - 0x72, 0x4d, 0x66, 0x98, 0x15, 0xc3, 0x66, 0x94, 0x79, 0x29, 0xa1, 0x2b, 0x3d, 0x83, 0xdc, 0xcd, - 0x97, 0x3b, 0x47, 0x5c, 0x89, 0xeb, 0x98, 0x86, 0xd6, 0xee, 0x75, 0x29, 0xe5, 0xbf, 0x14, 0x00, - 0x57, 0x1d, 0x9b, 0x79, 0x8e, 0x69, 0x12, 0x0f, 0x91, 0x96, 0x41, 0x0d, 0xc7, 0x86, 0xcf, 0x40, - 0x9e, 0x07, 0xae, 0x81, 0x19, 0x2e, 0x2a, 0xf3, 0xca, 0xc2, 0xe8, 0xe2, 0x7b, 0xaa, 0xbc, 0xbe, - 0xb8, 0x1f, 0xd1, 0x05, 0x72, 0x6e, 0xb5, 0x75, 0x4d, 0xdd, 0xa8, 0xbf, 0x20, 0x1a, 0x5b, 0x27, - 0x0c, 0x57, 0xe1, 0xde, 0xfe, 0xdc, 0xc0, 0xe1, 0xfe, 0x1c, 0x88, 0xce, 0x50, 0x88, 0x0a, 0x37, - 0x40, 0x56, 0xa0, 0x67, 0x04, 0xfa, 0xd5, 0x9e, 0xe8, 0x32, 0xba, 0x2a, 0xc2, 0x5f, 0xde, 0x7f, - 0xc9, 0x88, 0xcd, 0xcd, 0xab, 0x9e, 0x93, 0xd0, 0xd9, 0x7b, 0x98, 0x61, 0x24, 0x80, 0xe0, 0x15, - 0x90, 0xf7, 0xa4, 0xf9, 0xc5, 0xc1, 0x79, 0x65, 0x61, 0xb0, 0x7a, 0x5e, 0x72, 0xe5, 0x03, 0xb7, - 0x50, 0xc8, 0x51, 0x7e, 0xad, 0x80, 0xe9, 0xb4, 0xdf, 0x6b, 0x06, 0x65, 0xf0, 0x69, 0xca, 0x77, - 0xb5, 0x3f, 0xdf, 0xb9, 0xb4, 0xf0, 0x3c, 0x54, 0x1c, 0x9c, 0xc4, 0xfc, 0x7e, 0x06, 0x72, 0x06, - 0x23, 0x16, 0x2d, 0x66, 0xe6, 0x07, 0x17, 0x46, 0x17, 0x97, 0xd5, 0x3e, 0xab, 0x42, 0x4d, 0x5b, - 0x5b, 0x1d, 0x93, 0x7a, 0x72, 0x0f, 0x39, 0x22, 0xf2, 0x81, 0xcb, 0x3f, 0x67, 0x00, 0xb8, 0x47, - 0x5c, 0xd3, 0x69, 0x5b, 0xc4, 0x66, 0x67, 0x70, 0x95, 0x8f, 0x41, 0x96, 0xba, 0x44, 0x93, 0x57, - 0x79, 0xab, 0x6f, 0x8f, 0x22, 0x23, 0x6b, 0x2e, 0xd1, 0xa2, 0x4b, 0xe5, 0x5f, 0x48, 0x40, 0x42, - 0x0c, 0x86, 0x28, 0xc3, 0xac, 0x49, 0xc5, 0x95, 0x8e, 0x2e, 0xde, 0x39, 0x0d, 0xb8, 0x00, 0xa8, - 0x16, 0x24, 0xfc, 0x90, 0xff, 0x8d, 0x24, 0x70, 0xf9, 0x60, 0x10, 0x4c, 0x46, 0xcc, 0xab, 0x8e, - 0xdd, 0x30, 0x18, 0x2f, 0x81, 0x65, 0x90, 0x65, 0x6d, 0x97, 0x88, 0x98, 0x8d, 0x54, 0x2f, 0x05, - 0xc6, 0x6d, 0xb5, 0x5d, 0xf2, 0x76, 0x7f, 0x6e, 0xa6, 0x8b, 0x08, 0x27, 0x21, 0x21, 0x04, 0x1f, - 0x85, 0x76, 0x67, 0x84, 0xf8, 0xdd, 0xa4, 0xf2, 0xb7, 0xfb, 0x73, 0x47, 0x56, 0xb8, 0x1a, 0x62, - 0x26, 0x8d, 0x85, 0x17, 0xc1, 0x90, 0x47, 0x30, 0x75, 0xec, 0x62, 0x56, 0xe0, 0x86, 0x4e, 0x21, - 0x71, 0x8a, 0x24, 0x15, 0x5e, 0x06, 0xc3, 0x16, 0xa1, 0x14, 0xeb, 0xa4, 0x98, 0x13, 0x8c, 0xe3, - 0x92, 0x71, 0x78, 0xdd, 0x3f, 0x46, 0x01, 0x1d, 0xbe, 0x00, 0x05, 0x13, 0x53, 0xb6, 0xed, 0x36, - 0x30, 0x23, 0x5b, 0x86, 0x45, 0x8a, 0x43, 0x22, 0xd4, 0xef, 0xf6, 0x97, 0x25, 0x5c, 0xa2, 0x3a, - 0x2d, 0xd1, 0x0b, 0x6b, 0x09, 0x24, 0xd4, 0x81, 0x0c, 0x5b, 0x00, 0xf2, 0x93, 0x2d, 0x0f, 0xdb, - 0xd4, 0x0f, 0x19, 0xd7, 0x37, 0x7c, 0x62, 0x7d, 0xb3, 0x52, 0x1f, 0x5c, 0x4b, 0xa1, 0xa1, 0x2e, - 0x1a, 0xca, 0x7b, 0x0a, 0x28, 0x44, 0x17, 0x76, 0x06, 0x55, 0xfe, 0x59, 0xb2, 0xca, 0xaf, 0x9f, - 0x22, 0x6d, 0x7b, 0x54, 0xf7, 0xb7, 0x83, 0x00, 0x46, 0x4c, 0xc8, 0x31, 0xcd, 0x3a, 0xd6, 0x76, - 0xe1, 0x3c, 0xc8, 0xda, 0xd8, 0x0a, 0xb2, 0x35, 0x2c, 0xa5, 0x4f, 0xb1, 0x45, 0x90, 0xa0, 0xc0, - 0x1f, 0x14, 0x00, 0x9b, 0xe2, 0x2a, 0x1a, 0x2b, 0xb6, 0xed, 0x30, 0xcc, 0xa3, 0x13, 0x18, 0x58, - 0x3b, 0x85, 0x81, 0x81, 0x6e, 0x75, 0x3b, 0x85, 0x7a, 0xdf, 0x66, 0x5e, 0x3b, 0xba, 0xa5, 0x34, - 0x03, 0xea, 0x62, 0x0a, 0xdc, 0x05, 0xc0, 0x93, 0x98, 0x5b, 0x8e, 0x2c, 0xf8, 0xfe, 0xbb, 0x49, - 0x60, 0xce, 0xaa, 0x63, 0x3f, 0x37, 0xf4, 0xa8, 0x65, 0xa1, 0x10, 0x12, 0xc5, 0xe0, 0x67, 0xef, - 0x83, 0x99, 0x1e, 0x76, 0xc3, 0xf3, 0x60, 0x70, 0x97, 0xb4, 0xfd, 0x50, 0x22, 0xfe, 0x13, 0x4e, - 0x81, 0x5c, 0x0b, 0x9b, 0x4d, 0xe2, 0x57, 0x33, 0xf2, 0x3f, 0x96, 0x32, 0xb7, 0x95, 0xf2, 0x1f, - 0xb9, 0x78, 0x66, 0xf1, 0xce, 0x05, 0x17, 0xf8, 0x43, 0xe4, 0x9a, 0x86, 0x86, 0xa9, 0xc0, 0xc8, - 0x55, 0xcf, 0xf9, 0x8f, 0x90, 0x7f, 0x86, 0x42, 0x2a, 0xfc, 0x02, 0xe4, 0x29, 0x31, 0x89, 0xc6, - 0x1c, 0x4f, 0x36, 0xcf, 0xeb, 0x7d, 0xe6, 0x20, 0xae, 0x13, 0xb3, 0x26, 0x45, 0x7d, 0xf8, 0xe0, - 0x0b, 0x85, 0x90, 0xf0, 0x73, 0x90, 0x67, 0xc4, 0x72, 0x4d, 0xcc, 0x88, 0x8c, 0xe6, 0xd5, 0xde, - 0xd1, 0xe4, 0xb0, 0x9b, 0x4e, 0x63, 0x4b, 0x0a, 0x88, 0x8e, 0x1c, 0x66, 0x78, 0x70, 0x8a, 0x42, - 0x40, 0x68, 0x80, 0x3c, 0x65, 0x7c, 0x92, 0xd0, 0xdb, 0xa2, 0x17, 0x9d, 0xe4, 0x29, 0x8b, 0xf7, - 0x66, 0x1f, 0x22, 0x52, 0x15, 0x9c, 0xa0, 0x10, 0x1e, 0xae, 0x80, 0x71, 0xcb, 0xb0, 0x11, 0xc1, - 0x8d, 0x76, 0x8d, 0x68, 0x8e, 0xdd, 0xa0, 0xa2, 0xa9, 0xe5, 0xaa, 0x33, 0x52, 0x68, 0x7c, 0x3d, - 0x49, 0x46, 0x9d, 0xfc, 0x70, 0x0d, 0x4c, 0x05, 0x4f, 0xff, 0xc7, 0x06, 0x65, 0x8e, 0xd7, 0x5e, - 0x33, 0x2c, 0x83, 0x89, 0x56, 0x97, 0xab, 0x16, 0x0f, 0xf7, 0xe7, 0xa6, 0x50, 0x17, 0x3a, 0xea, - 0x2a, 0xc5, 0xbb, 0xb0, 0x8b, 0x9b, 0x94, 0x34, 0x44, 0xeb, 0xca, 0x47, 0x5d, 0x78, 0x53, 0x9c, - 0x22, 0x49, 0x85, 0x7a, 0x22, 0xa1, 0xf3, 0xff, 0x2c, 0xa1, 0x0b, 0xbd, 0x93, 0x19, 0x6e, 0x83, - 0x19, 0xd7, 0x73, 0x74, 0x8f, 0x50, 0x7a, 0x8f, 0xe0, 0x86, 0x69, 0xd8, 0x24, 0x88, 0xd4, 0x88, - 0xf0, 0xf0, 0xc2, 0xe1, 0xfe, 0xdc, 0xcc, 0x66, 0x77, 0x16, 0xd4, 0x4b, 0xb6, 0xfc, 0x7b, 0x16, - 0x9c, 0xef, 0x7c, 0x47, 0xe1, 0x27, 0x00, 0x3a, 0x75, 0x4a, 0xbc, 0x16, 0x69, 0x3c, 0xf0, 0x87, - 0x49, 0x3e, 0x71, 0x29, 0x62, 0xe2, 0x0a, 0x2b, 0x7e, 0x23, 0xc5, 0x81, 0xba, 0x48, 0xf9, 0x33, - 0x9b, 0x2c, 0x95, 0x8c, 0x30, 0x34, 0x36, 0xb3, 0xa5, 0xca, 0x65, 0x05, 0x8c, 0xcb, 0xae, 0x11, - 0x10, 0x45, 0x5a, 0xc7, 0xf2, 0x60, 0x3b, 0x49, 0x46, 0x9d, 0xfc, 0xf0, 0x01, 0x98, 0xc0, 0x2d, - 0x6c, 0x98, 0xb8, 0x6e, 0x92, 0x10, 0x24, 0x2b, 0x40, 0xfe, 0x2f, 0x41, 0x26, 0x56, 0x3a, 0x19, - 0x50, 0x5a, 0x06, 0xae, 0x83, 0xc9, 0xa6, 0x9d, 0x86, 0xf2, 0xf3, 0xf2, 0x82, 0x84, 0x9a, 0xdc, - 0x4e, 0xb3, 0xa0, 0x6e, 0x72, 0xd0, 0x05, 0x40, 0x0b, 0x9e, 0x7c, 0x5a, 0x1c, 0x12, 0x3d, 0xf9, - 0x83, 0x53, 0xd4, 0x53, 0x38, 0x37, 0x44, 0xfd, 0x2f, 0x3c, 0xa2, 0x28, 0xa6, 0x03, 0x2e, 0x83, - 0x31, 0x8f, 0x57, 0x48, 0x68, 0xfa, 0xb0, 0x30, 0xfd, 0x7f, 0x52, 0x6c, 0x0c, 0xc5, 0x89, 0x28, - 0xc9, 0x0b, 0x97, 0x40, 0x41, 0x73, 0x4c, 0x53, 0x54, 0xc6, 0xaa, 0xd3, 0xb4, 0x99, 0x48, 0xee, - 0xc1, 0x2a, 0xe4, 0x33, 0xc0, 0x6a, 0x82, 0x82, 0x3a, 0x38, 0xcb, 0xbf, 0x29, 0xf1, 0x07, 0x2c, - 0x28, 0x77, 0xb8, 0x94, 0x18, 0xb7, 0x2e, 0x76, 0x8c, 0x5b, 0xd3, 0x69, 0x89, 0xd8, 0xb4, 0xd5, - 0x06, 0x63, 0xbc, 0x18, 0x0c, 0x5b, 0xf7, 0x13, 0x40, 0x36, 0xd3, 0x0f, 0x4f, 0x54, 0x6a, 0xa1, - 0x74, 0xec, 0x09, 0x9e, 0x10, 0x91, 0x88, 0x13, 0x51, 0x52, 0x53, 0xf9, 0x2e, 0x28, 0x24, 0xeb, - 0x34, 0xb1, 0x87, 0x28, 0xc7, 0xee, 0x21, 0x6f, 0x14, 0x30, 0xd3, 0x43, 0x3b, 0x34, 0x41, 0xc1, - 0xc2, 0x2f, 0x63, 0x39, 0x74, 0xec, 0xfc, 0xce, 0x57, 0x4a, 0xd5, 0x5f, 0x29, 0xd5, 0x87, 0x36, - 0xdb, 0xf0, 0x6a, 0xcc, 0x33, 0x6c, 0xdd, 0xbf, 0x97, 0xf5, 0x04, 0x16, 0xea, 0xc0, 0x86, 0x4f, - 0x40, 0xde, 0xc2, 0x2f, 0x6b, 0x4d, 0x4f, 0x0f, 0xe2, 0x77, 0x72, 0x3d, 0xe2, 0x25, 0x5a, 0x97, - 0x28, 0x28, 0xc4, 0x2b, 0x7f, 0x9f, 0x01, 0xb9, 0x9a, 0x86, 0x4d, 0x72, 0x06, 0xdb, 0xc8, 0x56, - 0x62, 0x1b, 0x59, 0xec, 0x3b, 0x07, 0x84, 0x7d, 0x3d, 0x17, 0x91, 0xa7, 0x1d, 0x8b, 0xc8, 0x8d, - 0x13, 0xe2, 0x1e, 0xbd, 0x83, 0xdc, 0x01, 0x23, 0xa1, 0xfa, 0x44, 0x53, 0x54, 0x8e, 0x6b, 0x8a, - 0xe5, 0x9f, 0x32, 0x60, 0x34, 0xa6, 0xe2, 0x64, 0xd2, 0xd0, 0x4d, 0x4c, 0x20, 0xbc, 0xeb, 0x54, - 0x4f, 0xe3, 0x98, 0x1a, 0x4c, 0x1f, 0xfe, 0xe0, 0x17, 0x3d, 0xe6, 0xe9, 0xa1, 0xe4, 0x2e, 0x28, - 0x30, 0xec, 0xe9, 0x84, 0x05, 0x34, 0x11, 0xd0, 0x91, 0x68, 0x85, 0xd8, 0x4a, 0x50, 0x51, 0x07, - 0xf7, 0xec, 0x32, 0x18, 0x4b, 0x28, 0x3b, 0xd1, 0xb4, 0xf6, 0x0b, 0x0f, 0x16, 0xc3, 0x8c, 0x3c, - 0x6f, 0x9a, 0x35, 0x72, 0x16, 0xbb, 0xf1, 0x93, 0x44, 0x36, 0xde, 0xee, 0x3f, 0xb8, 0x91, 0x95, - 0x3d, 0x73, 0xb2, 0xde, 0x91, 0x93, 0x4b, 0xa7, 0x42, 0x3f, 0x3a, 0x33, 0x7f, 0x55, 0xc0, 0x78, - 0x8c, 0xfb, 0x0c, 0x56, 0xa7, 0xc7, 0xc9, 0xd5, 0xe9, 0xc6, 0x69, 0x9c, 0xea, 0xb1, 0x3b, 0xfd, - 0x98, 0x4d, 0x38, 0xf3, 0x1f, 0x9a, 0xd6, 0xbf, 0x56, 0xc0, 0x54, 0xcb, 0x31, 0x9b, 0x16, 0x59, - 0x35, 0xb1, 0x61, 0x05, 0x1c, 0x7c, 0xf6, 0x39, 0x66, 0x3f, 0x15, 0x9a, 0x88, 0x47, 0x0d, 0xca, - 0x88, 0xcd, 0x1e, 0x45, 0x18, 0xd5, 0x77, 0xa4, 0xbe, 0xa9, 0x47, 0x5d, 0x80, 0x51, 0x57, 0x75, - 0xf0, 0x7d, 0x30, 0xca, 0x87, 0x40, 0x43, 0x23, 0x7c, 0x33, 0x95, 0xff, 0x4d, 0x4c, 0x4a, 0xa0, - 0xd1, 0x5a, 0x44, 0x42, 0x71, 0x3e, 0xb8, 0x03, 0x26, 0x5d, 0xa7, 0xb1, 0x8e, 0x6d, 0xac, 0x13, - 0xfe, 0x34, 0x6e, 0x8a, 0x3f, 0x35, 0xc5, 0xf4, 0x3e, 0x52, 0xbd, 0x19, 0x4c, 0x5b, 0x9b, 0x69, - 0x96, 0xb7, 0x7c, 0xec, 0x4d, 0x1f, 0x8b, 0xd9, 0xa1, 0x1b, 0x64, 0xf9, 0x1b, 0x05, 0x4c, 0xa4, - 0xaa, 0x03, 0x7e, 0x74, 0xc4, 0xcc, 0x3b, 0xfd, 0x6f, 0xcd, 0xbb, 0xd5, 0xcb, 0x7b, 0x07, 0xa5, - 0x81, 0x57, 0x07, 0xa5, 0x81, 0xd7, 0x07, 0xa5, 0x81, 0xaf, 0x0e, 0x4b, 0xca, 0xde, 0x61, 0x49, - 0x79, 0x75, 0x58, 0x52, 0xfe, 0x3c, 0x2c, 0x29, 0xdf, 0xbd, 0x29, 0x0d, 0x3c, 0x19, 0x96, 0xb9, - 0xff, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5d, 0x5b, 0xd8, 0xe7, 0x95, 0x17, 0x00, 0x00, + // 1818 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x58, 0x4f, 0x4f, 0x23, 0xc9, + 0x15, 0xa7, 0xfd, 0x07, 0x4c, 0xb1, 0x98, 0xa1, 0x20, 0xe0, 0x65, 0x37, 0x06, 0xf9, 0xb0, 0xcb, + 0x44, 0x4b, 0x3b, 0xc3, 0x4c, 0x76, 0x67, 0x20, 0x1a, 0x85, 0x66, 0xc8, 0x66, 0x22, 0x08, 0xa8, + 0x0c, 0xa3, 0xec, 0x64, 0x23, 0x6d, 0xb9, 0x5d, 0xd3, 0xf4, 0xd2, 0xff, 0xd4, 0x5d, 0x76, 0xc6, + 0xb7, 0x28, 0x52, 0x6e, 0x39, 0xe4, 0x0b, 0x44, 0xb9, 0x47, 0x91, 0xf2, 0x35, 0x50, 0x72, 0xc8, + 0x6a, 0x4f, 0xa3, 0x1c, 0x50, 0xf0, 0x7c, 0x84, 0x28, 0x97, 0x39, 0x45, 0x55, 0x5d, 0xfd, 0xdf, + 0x0d, 0xb6, 0xa3, 0x70, 0xc9, 0xcd, 0x5d, 0xef, 0xbd, 0xdf, 0x7b, 0x55, 0xf5, 0xde, 0xab, 0xdf, + 0x33, 0xf8, 0xec, 0xe2, 0xb1, 0x27, 0xeb, 0x76, 0xf3, 0xa2, 0xdb, 0x26, 0xae, 0x45, 0x28, 0xf1, + 0x9a, 0xce, 0x85, 0xd6, 0xc4, 0x8e, 0xee, 0x35, 0xb1, 0xe3, 0x78, 0xcd, 0xde, 0x83, 0x36, 0xa1, + 0xf8, 0x41, 0x53, 0x23, 0x16, 0x71, 0x31, 0x25, 0x1d, 0xd9, 0x71, 0x6d, 0x6a, 0xc3, 0x8f, 0x7d, + 0x43, 0x39, 0x32, 0x94, 0x9d, 0x0b, 0x4d, 0x66, 0x86, 0x32, 0x33, 0x94, 0x85, 0xe1, 0xda, 0x96, + 0xa6, 0xd3, 0xf3, 0x6e, 0x5b, 0x56, 0x6d, 0xb3, 0xa9, 0xd9, 0x9a, 0xdd, 0xe4, 0xf6, 0xed, 0xee, + 0x2b, 0xfe, 0xc5, 0x3f, 0xf8, 0x2f, 0x1f, 0x77, 0xed, 0x91, 0x08, 0x08, 0x3b, 0xba, 0x89, 0xd5, + 0x73, 0xdd, 0x22, 0x6e, 0x3f, 0x0a, 0xc9, 0x24, 0x14, 0x37, 0x7b, 0x99, 0x68, 0xd6, 0x9a, 0x79, + 0x56, 0x6e, 0xd7, 0xa2, 0xba, 0x49, 0x32, 0x06, 0x9f, 0xde, 0x66, 0xe0, 0xa9, 0xe7, 0xc4, 0xc4, + 0x19, 0xbb, 0x87, 0x79, 0x76, 0x5d, 0xaa, 0x1b, 0x4d, 0xdd, 0xa2, 0x1e, 0x75, 0x33, 0x46, 0x9f, + 0xe4, 0x1e, 0xf2, 0xb0, 0xbd, 0x3c, 0xb9, 0xe1, 0x4a, 0x1c, 0xdb, 0xd0, 0xd5, 0x7e, 0xde, 0xa5, + 0x34, 0xfe, 0x2d, 0x01, 0xb8, 0x6f, 0x5b, 0xd4, 0xb5, 0x0d, 0x83, 0xb8, 0x88, 0xf4, 0x74, 0x4f, + 0xb7, 0x2d, 0xf8, 0x15, 0xa8, 0xb0, 0x83, 0xeb, 0x60, 0x8a, 0x6b, 0xd2, 0x86, 0xb4, 0x39, 0xb7, + 0xfd, 0x7d, 0x59, 0x5c, 0x5f, 0x7c, 0x1f, 0xd1, 0x05, 0x32, 0x6d, 0xb9, 0xf7, 0x40, 0x3e, 0x6e, + 0x7f, 0x4d, 0x54, 0x7a, 0x44, 0x28, 0x56, 0xe0, 0xe5, 0xd5, 0xfa, 0xd4, 0xe0, 0x6a, 0x1d, 0x44, + 0x6b, 0x28, 0x44, 0x85, 0xc7, 0xa0, 0xc4, 0xd1, 0x0b, 0x1c, 0x7d, 0x2b, 0x17, 0x5d, 0x9c, 0xae, + 0x8c, 0xf0, 0xaf, 0x0e, 0x5e, 0x53, 0x62, 0xb1, 0xf0, 0x94, 0xf7, 0x04, 0x74, 0xe9, 0x19, 0xa6, + 0x18, 0x71, 0x20, 0xf8, 0x09, 0xa8, 0xb8, 0x22, 0xfc, 0x5a, 0x71, 0x43, 0xda, 0x2c, 0x2a, 0xf7, + 0x84, 0x56, 0x25, 0xd8, 0x16, 0x0a, 0x35, 0x1a, 0x6f, 0x24, 0xb0, 0x92, 0xdd, 0xf7, 0xa1, 0xee, + 0x51, 0xf8, 0x65, 0x66, 0xef, 0xf2, 0x68, 0x7b, 0x67, 0xd6, 0x7c, 0xe7, 0xa1, 0xe3, 0x60, 0x25, + 0xb6, 0xef, 0xaf, 0x40, 0x59, 0xa7, 0xc4, 0xf4, 0x6a, 0x85, 0x8d, 0xe2, 0xe6, 0xdc, 0xf6, 0xae, + 0x3c, 0x62, 0x55, 0xc8, 0xd9, 0x68, 0x95, 0x79, 0xe1, 0xa7, 0xfc, 0x9c, 0x21, 0x22, 0x1f, 0xb8, + 0xf1, 0xe7, 0x02, 0x00, 0xcf, 0x88, 0x63, 0xd8, 0x7d, 0x93, 0x58, 0xf4, 0x0e, 0xae, 0xf2, 0x0b, + 0x50, 0xf2, 0x1c, 0xa2, 0x8a, 0xab, 0xfc, 0x6c, 0xe4, 0x1d, 0x45, 0x41, 0xb6, 0x1c, 0xa2, 0x46, + 0x97, 0xca, 0xbe, 0x10, 0x87, 0x84, 0x18, 0x4c, 0x7b, 0x14, 0xd3, 0xae, 0xc7, 0xaf, 0x74, 0x6e, + 0xfb, 0xc9, 0x24, 0xe0, 0x1c, 0x40, 0xa9, 0x0a, 0xf8, 0x69, 0xff, 0x1b, 0x09, 0xe0, 0xc6, 0x75, + 0x11, 0x2c, 0x45, 0xca, 0xfb, 0xb6, 0xd5, 0xd1, 0x29, 0x2b, 0x81, 0x5d, 0x50, 0xa2, 0x7d, 0x87, + 0xf0, 0x33, 0x9b, 0x55, 0x3e, 0x0e, 0x82, 0x3b, 0xed, 0x3b, 0xe4, 0xdd, 0xd5, 0xfa, 0xea, 0x10, + 0x13, 0x26, 0x42, 0xdc, 0x08, 0xbe, 0x08, 0xe3, 0x2e, 0x70, 0xf3, 0xa7, 0x49, 0xe7, 0xef, 0xae, + 0xd6, 0x6f, 0xac, 0x70, 0x39, 0xc4, 0x4c, 0x06, 0x0b, 0x3f, 0x02, 0xd3, 0x2e, 0xc1, 0x9e, 0x6d, + 0xd5, 0x4a, 0x1c, 0x37, 0xdc, 0x14, 0xe2, 0xab, 0x48, 0x48, 0xe1, 0x7d, 0x30, 0x63, 0x12, 0xcf, + 0xc3, 0x1a, 0xa9, 0x95, 0xb9, 0xe2, 0x82, 0x50, 0x9c, 0x39, 0xf2, 0x97, 0x51, 0x20, 0x87, 0x5f, + 0x83, 0xaa, 0x81, 0x3d, 0x7a, 0xe6, 0x74, 0x30, 0x25, 0xa7, 0xba, 0x49, 0x6a, 0xd3, 0xfc, 0xa8, + 0xbf, 0x37, 0x5a, 0x96, 0x30, 0x0b, 0x65, 0x45, 0xa0, 0x57, 0x0f, 0x13, 0x48, 0x28, 0x85, 0x0c, + 0x7b, 0x00, 0xb2, 0x95, 0x53, 0x17, 0x5b, 0x9e, 0x7f, 0x64, 0xcc, 0xdf, 0xcc, 0xd8, 0xfe, 0xd6, + 0x84, 0x3f, 0x78, 0x98, 0x41, 0x43, 0x43, 0x3c, 0x34, 0x2e, 0x25, 0x50, 0x8d, 0x2e, 0xec, 0x0e, + 0xaa, 0xfc, 0xe7, 0xc9, 0x2a, 0x7f, 0x38, 0x41, 0xda, 0xe6, 0x54, 0xf7, 0xef, 0x8a, 0x00, 0x46, + 0x4a, 0xc8, 0x36, 0x8c, 0x36, 0x56, 0x2f, 0xe0, 0x06, 0x28, 0x59, 0xd8, 0x0c, 0xb2, 0x35, 0x2c, + 0xa5, 0x9f, 0x61, 0x93, 0x20, 0x2e, 0x81, 0x7f, 0x94, 0x00, 0xec, 0xf2, 0xab, 0xe8, 0xec, 0x59, + 0x96, 0x4d, 0x31, 0x3b, 0x9d, 0x20, 0xc0, 0xd6, 0x04, 0x01, 0x06, 0xbe, 0xe5, 0xb3, 0x0c, 0xea, + 0x81, 0x45, 0xdd, 0x7e, 0x74, 0x4b, 0x59, 0x05, 0x34, 0x24, 0x14, 0x78, 0x01, 0x80, 0x2b, 0x30, + 0x4f, 0x6d, 0x51, 0xf0, 0xa3, 0x77, 0x93, 0x20, 0x9c, 0x7d, 0xdb, 0x7a, 0xa5, 0x6b, 0x51, 0xcb, + 0x42, 0x21, 0x24, 0x8a, 0xc1, 0xaf, 0x1d, 0x80, 0xd5, 0x9c, 0xb8, 0xe1, 0x3d, 0x50, 0xbc, 0x20, + 0x7d, 0xff, 0x28, 0x11, 0xfb, 0x09, 0x97, 0x41, 0xb9, 0x87, 0x8d, 0x2e, 0xf1, 0xab, 0x19, 0xf9, + 0x1f, 0x3b, 0x85, 0xc7, 0x52, 0xe3, 0x1f, 0xe5, 0x78, 0x66, 0xb1, 0xce, 0x05, 0x37, 0xd9, 0x43, + 0xe4, 0x18, 0xba, 0x8a, 0x3d, 0x8e, 0x51, 0x56, 0xde, 0xf3, 0x1f, 0x21, 0x7f, 0x0d, 0x85, 0x52, + 0xf8, 0x4b, 0x50, 0xf1, 0x88, 0x41, 0x54, 0x6a, 0xbb, 0xa2, 0x79, 0x3e, 0x1c, 0x31, 0x07, 0x71, + 0x9b, 0x18, 0x2d, 0x61, 0xea, 0xc3, 0x07, 0x5f, 0x28, 0x84, 0x84, 0xbf, 0x00, 0x15, 0x4a, 0x4c, + 0xc7, 0xc0, 0x94, 0x88, 0xd3, 0xdc, 0xca, 0x3f, 0x4d, 0x06, 0x7b, 0x62, 0x77, 0x4e, 0x85, 0x01, + 0xef, 0xc8, 0x61, 0x86, 0x07, 0xab, 0x28, 0x04, 0x84, 0x3a, 0xa8, 0x78, 0x94, 0x31, 0x09, 0xad, + 0xcf, 0x7b, 0xd1, 0x38, 0x4f, 0x59, 0xbc, 0x37, 0xfb, 0x10, 0x91, 0xab, 0x60, 0x05, 0x85, 0xf0, + 0x70, 0x0f, 0x2c, 0x98, 0xba, 0x85, 0x08, 0xee, 0xf4, 0x5b, 0x44, 0xb5, 0xad, 0x8e, 0xc7, 0x9b, + 0x5a, 0x59, 0x59, 0x15, 0x46, 0x0b, 0x47, 0x49, 0x31, 0x4a, 0xeb, 0xc3, 0x43, 0xb0, 0x1c, 0x3c, + 0xfd, 0x3f, 0xd1, 0x3d, 0x6a, 0xbb, 0xfd, 0x43, 0xdd, 0xd4, 0x29, 0x6f, 0x75, 0x65, 0xa5, 0x36, + 0xb8, 0x5a, 0x5f, 0x46, 0x43, 0xe4, 0x68, 0xa8, 0x15, 0xeb, 0xc2, 0x0e, 0xee, 0x7a, 0xa4, 0xc3, + 0x5b, 0x57, 0x25, 0xea, 0xc2, 0x27, 0x7c, 0x15, 0x09, 0x29, 0xd4, 0x12, 0x09, 0x5d, 0xf9, 0xef, + 0x12, 0xba, 0x9a, 0x9f, 0xcc, 0xf0, 0x0c, 0xac, 0x3a, 0xae, 0xad, 0xb9, 0xc4, 0xf3, 0x9e, 0x11, + 0xdc, 0x31, 0x74, 0x8b, 0x04, 0x27, 0x35, 0xcb, 0x77, 0xf8, 0xc1, 0xe0, 0x6a, 0x7d, 0xf5, 0x64, + 0xb8, 0x0a, 0xca, 0xb3, 0x6d, 0x7c, 0x5b, 0x02, 0xf7, 0xd2, 0xef, 0x28, 0xfc, 0x29, 0x80, 0x76, + 0xdb, 0x23, 0x6e, 0x8f, 0x74, 0x3e, 0xf7, 0xc9, 0x24, 0x63, 0x5c, 0x12, 0x67, 0x5c, 0x61, 0xc5, + 0x1f, 0x67, 0x34, 0xd0, 0x10, 0x2b, 0x9f, 0xb3, 0x89, 0x52, 0x29, 0xf0, 0x40, 0x63, 0x9c, 0x2d, + 0x53, 0x2e, 0x7b, 0x60, 0x41, 0x74, 0x8d, 0x40, 0xc8, 0xd3, 0x3a, 0x96, 0x07, 0x67, 0x49, 0x31, + 0x4a, 0xeb, 0xc3, 0xcf, 0xc1, 0x22, 0xee, 0x61, 0xdd, 0xc0, 0x6d, 0x83, 0x84, 0x20, 0x25, 0x0e, + 0xf2, 0xbe, 0x00, 0x59, 0xdc, 0x4b, 0x2b, 0xa0, 0xac, 0x0d, 0x3c, 0x02, 0x4b, 0x5d, 0x2b, 0x0b, + 0xe5, 0xe7, 0xe5, 0x07, 0x02, 0x6a, 0xe9, 0x2c, 0xab, 0x82, 0x86, 0xd9, 0x41, 0x07, 0x00, 0x35, + 0x78, 0xf2, 0xbd, 0xda, 0x34, 0xef, 0xc9, 0x3f, 0x9c, 0xa0, 0x9e, 0x42, 0xde, 0x10, 0xf5, 0xbf, + 0x70, 0xc9, 0x43, 0x31, 0x1f, 0x70, 0x17, 0xcc, 0xbb, 0xac, 0x42, 0xc2, 0xd0, 0x67, 0x78, 0xe8, + 0xdf, 0x11, 0x66, 0xf3, 0x28, 0x2e, 0x44, 0x49, 0x5d, 0xb8, 0x03, 0xaa, 0xaa, 0x6d, 0x18, 0xbc, + 0x32, 0xf6, 0xed, 0xae, 0x45, 0x79, 0x72, 0x17, 0x15, 0xc8, 0x38, 0xc0, 0x7e, 0x42, 0x82, 0x52, + 0x9a, 0x8d, 0xbf, 0x49, 0xf1, 0x07, 0x2c, 0x28, 0x77, 0xb8, 0x93, 0xa0, 0x5b, 0x1f, 0xa5, 0xe8, + 0xd6, 0x4a, 0xd6, 0x22, 0xc6, 0xb6, 0xfa, 0x60, 0x9e, 0x15, 0x83, 0x6e, 0x69, 0x7e, 0x02, 0x88, + 0x66, 0xfa, 0xa3, 0xb1, 0x4a, 0x2d, 0xb4, 0x8e, 0x3d, 0xc1, 0x8b, 0xfc, 0x24, 0xe2, 0x42, 0x94, + 0xf4, 0xd4, 0x78, 0x0e, 0x3e, 0x3c, 0xc1, 0x2e, 0x0d, 0xb9, 0x1a, 0x79, 0xd5, 0x35, 0x5a, 0x24, + 0xda, 0xd6, 0x7d, 0x30, 0x63, 0xbb, 0x1d, 0xdd, 0xc2, 0x86, 0x78, 0x0b, 0x42, 0x22, 0x76, 0xec, + 0x2f, 0xa3, 0x40, 0xde, 0x78, 0x0a, 0xaa, 0xc9, 0x92, 0x4f, 0x8c, 0x34, 0xd2, 0xad, 0x23, 0xcd, + 0x5b, 0x09, 0xac, 0xe6, 0x6c, 0x04, 0x1a, 0xa0, 0x6a, 0xe2, 0xd7, 0xb1, 0x74, 0xbc, 0x75, 0x14, + 0x60, 0xd3, 0xa9, 0xec, 0x4f, 0xa7, 0xf2, 0x73, 0x8b, 0x1e, 0xbb, 0x2d, 0xea, 0xea, 0x96, 0xe6, + 0x5f, 0xf1, 0x51, 0x02, 0x0b, 0xa5, 0xb0, 0xe1, 0x4b, 0x50, 0x31, 0xf1, 0xeb, 0x56, 0xd7, 0xd5, + 0x82, 0xab, 0x18, 0xdf, 0x0f, 0x7f, 0xd4, 0x8e, 0x04, 0x0a, 0x0a, 0xf1, 0x1a, 0x7f, 0x28, 0x80, + 0x72, 0x4b, 0xc5, 0x06, 0xb9, 0x83, 0xc1, 0xe6, 0x34, 0x31, 0xd8, 0x6c, 0x8f, 0x9c, 0x4e, 0x3c, + 0xbe, 0xdc, 0x99, 0xe6, 0xcb, 0xd4, 0x4c, 0xf3, 0x68, 0x4c, 0xdc, 0x9b, 0xc7, 0x99, 0x27, 0x60, + 0x36, 0x74, 0x9f, 0xe8, 0xaf, 0xd2, 0x6d, 0xfd, 0xb5, 0xf1, 0xa7, 0x02, 0x98, 0x8b, 0xb9, 0x18, + 0xcf, 0x1a, 0x3a, 0x09, 0x32, 0xc3, 0x1a, 0x98, 0x32, 0xc9, 0xc6, 0xe4, 0x80, 0xc8, 0xf8, 0x1c, + 0x32, 0xe2, 0x05, 0x59, 0x7e, 0xf3, 0x14, 0x54, 0x29, 0x76, 0x35, 0x42, 0x03, 0x19, 0x3f, 0xd0, + 0xd9, 0x68, 0x1a, 0x39, 0x4d, 0x48, 0x51, 0x4a, 0x7b, 0x6d, 0x17, 0xcc, 0x27, 0x9c, 0x8d, 0x45, + 0xfc, 0xfe, 0xc2, 0x0e, 0x2b, 0x2a, 0xf8, 0x3b, 0xc8, 0xc6, 0x97, 0x89, 0x6c, 0x7c, 0x3c, 0xfa, + 0xe1, 0xc6, 0xda, 0x52, 0x5e, 0x4e, 0xb6, 0x53, 0x39, 0xb9, 0x33, 0x11, 0xfa, 0xcd, 0x99, 0xf9, + 0x57, 0x09, 0x2c, 0xc4, 0xb4, 0xef, 0x60, 0x0a, 0xfb, 0x22, 0x39, 0x85, 0x3d, 0x9a, 0x64, 0x53, + 0x39, 0x63, 0xd8, 0xbf, 0xca, 0x89, 0xcd, 0xfc, 0x1f, 0x11, 0xff, 0xdf, 0x4a, 0x60, 0xb9, 0x67, + 0x1b, 0x5d, 0x93, 0xec, 0x1b, 0x58, 0x37, 0x03, 0x0d, 0x46, 0xa3, 0x6e, 0x19, 0x75, 0xb9, 0x27, + 0xe2, 0x7a, 0xba, 0x47, 0x89, 0x45, 0x5f, 0x44, 0x18, 0xca, 0x87, 0xc2, 0xdf, 0xf2, 0x8b, 0x21, + 0xc0, 0x68, 0xa8, 0x3b, 0xf8, 0x03, 0x30, 0xc7, 0xf8, 0xa4, 0xae, 0x12, 0x36, 0xe4, 0x8a, 0xbf, + 0x39, 0x96, 0x04, 0xd0, 0x5c, 0x2b, 0x12, 0xa1, 0xb8, 0x1e, 0x3c, 0x07, 0x4b, 0x8e, 0xdd, 0x39, + 0xc2, 0x16, 0xd6, 0x08, 0x7b, 0x1a, 0x4f, 0xf8, 0xff, 0xa3, 0x7c, 0x10, 0x98, 0x55, 0x3e, 0x0d, + 0x88, 0xdb, 0x49, 0x56, 0xe5, 0x1d, 0x63, 0xd0, 0xd9, 0x65, 0x4e, 0x43, 0x86, 0x41, 0xc2, 0xdf, + 0x48, 0xa0, 0xea, 0xf3, 0xcf, 0x80, 0x0d, 0x88, 0x7f, 0x3a, 0x94, 0x49, 0xf2, 0xf0, 0x2c, 0x81, + 0x14, 0xf5, 0xb8, 0xe4, 0x3a, 0x4a, 0x79, 0xcc, 0x1d, 0x7c, 0x2a, 0x93, 0x0c, 0x3e, 0x8d, 0xbf, + 0x17, 0xc1, 0x62, 0xa6, 0xe0, 0xe1, 0x8f, 0x6f, 0x98, 0x08, 0x56, 0xfe, 0x67, 0xd3, 0x40, 0x86, + 0xc0, 0x16, 0xc7, 0x20, 0xb0, 0x7b, 0x60, 0x41, 0xed, 0xba, 0x2e, 0xb1, 0x68, 0x6a, 0x0a, 0x08, + 0x47, 0x89, 0xfd, 0xa4, 0x18, 0xa5, 0xf5, 0x87, 0x4d, 0x23, 0xe5, 0x31, 0xa7, 0x91, 0x78, 0x14, + 0x82, 0xe6, 0xf9, 0x79, 0x98, 0x8d, 0x42, 0xb0, 0xbd, 0xb4, 0x3e, 0x7b, 0x03, 0x7d, 0xd4, 0x10, + 0x61, 0x26, 0xf9, 0x06, 0x9e, 0x25, 0xa4, 0x28, 0xa5, 0xdd, 0xf8, 0x56, 0x02, 0xef, 0xe7, 0x66, + 0x19, 0xdc, 0x4b, 0x90, 0xf2, 0xad, 0x14, 0x29, 0xff, 0x6e, 0xae, 0x61, 0x8c, 0x9b, 0xbb, 0x60, + 0xd6, 0x09, 0x08, 0xb2, 0xe8, 0x75, 0x07, 0x23, 0xe7, 0xff, 0x4d, 0xd4, 0x5a, 0x99, 0x1f, 0x5c, + 0xad, 0xcf, 0x86, 0x1a, 0x28, 0x72, 0xa3, 0xdc, 0xbf, 0xbc, 0xae, 0x4f, 0x7d, 0x73, 0x5d, 0x9f, + 0x7a, 0x73, 0x5d, 0x9f, 0xfa, 0xf5, 0xa0, 0x2e, 0x5d, 0x0e, 0xea, 0xd2, 0x37, 0x83, 0xba, 0xf4, + 0xcf, 0x41, 0x5d, 0xfa, 0xfd, 0xdb, 0xfa, 0xd4, 0xcb, 0x19, 0xe1, 0xe1, 0x3f, 0x01, 0x00, 0x00, + 0xff, 0xff, 0x4f, 0x0f, 0xec, 0xcc, 0xce, 0x1a, 0x00, 0x00, } diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/generated.proto b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/generated.proto index 5d548a7fdb7..ab1dc6dda4d 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/generated.proto +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/generated.proto @@ -224,6 +224,14 @@ message DeploymentStrategy { optional RollingUpdateDeployment rollingUpdate = 2; } +// PartitionStatefulSetStrategy contains the parameters used with the +// PartitionStatefulSetStrategyType. +message PartitionStatefulSetStrategy { + // Ordinal indicates the ordinal at which the StatefulSet should be + // partitioned. + optional int32 ordinal = 1; +} + message RollbackConfig { // The revision to rollback to. If set to 0, rollback to the last revision. // +optional @@ -378,15 +386,60 @@ message StatefulSetSpec { // all pods at once. // +optional optional string podManagementPolicy = 6; + + // updateStrategy indicates the StatefulSetUpdateStrategy that will be + // employed to update Pods in the StatefulSet when a revision is made to + // Template. + optional StatefulSetUpdateStrategy updateStrategy = 7; + + // revisionHistoryLimit is the maximum number of revisions that will + // be maintained in the StatefulSet's revision history. The revision history + // consists of all revisions not represented by a currently applied + // StatefulSetSpec version. The default value is 10. + optional int32 revisionHistoryLimit = 8; } // StatefulSetStatus represents the current state of a StatefulSet. message StatefulSetStatus { - // observedGeneration is the most recent generation observed by this StatefulSet. + // observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the + // StatefulSet's generation, which is updated on mutation by the API Server. // +optional optional int64 observedGeneration = 1; - // replicas is the number of actual replicas. + // replicas is the number of Pods created by the StatefulSet controller. optional int32 replicas = 2; + + // readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition. + optional int32 readyReplicas = 3; + + // currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by currentRevision. + optional int32 currentReplicas = 4; + + // updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by updateRevision. + optional int32 updatedReplicas = 5; + + // currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the + // sequence [0,currentReplicas). + optional string currentRevision = 6; + + // updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence + // [replicas-updatedReplicas,replicas) + optional string updateRevision = 7; +} + +// StatefulSetUpdateStrategy indicates the strategy that the StatefulSet +// controller will use to perform updates. It includes any additional parameters +// necessary to perform the update for the indicated strategy. +message StatefulSetUpdateStrategy { + // Type indicates the type of the StatefulSetUpdateStrategy. + optional string type = 1; + + // Partition is used to communicate the ordinal at which to partition + // the StatefulSet when Type is PartitionStatefulSetStrategyType. This + // value must be set when Type is PartitionStatefulSetStrategyType, + // and it must be nil otherwise. + optional PartitionStatefulSetStrategy partition = 2; } diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types.generated.go b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types.generated.go index c7ddf6f4169..9022e03974a 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types.generated.go +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types.generated.go @@ -1368,6 +1368,423 @@ func (x *PodManagementPolicyType) CodecDecodeSelf(d *codec1978.Decoder) { } } +func (x *StatefulSetUpdateStrategy) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [2]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = x.Type != "" + yyq2[1] = x.Partition != nil + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(2) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + x.Type.CodecEncodeSelf(e) + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("type")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + x.Type.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + if x.Partition == nil { + r.EncodeNil() + } else { + x.Partition.CodecEncodeSelf(e) + } + } else { + r.EncodeNil() + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("partition")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Partition == nil { + r.EncodeNil() + } else { + x.Partition.CodecEncodeSelf(e) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *StatefulSetUpdateStrategy) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *StatefulSetUpdateStrategy) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "type": + if r.TryDecodeAsNil() { + x.Type = "" + } else { + yyv4 := &x.Type + yyv4.CodecDecodeSelf(d) + } + case "partition": + if r.TryDecodeAsNil() { + if x.Partition != nil { + x.Partition = nil + } + } else { + if x.Partition == nil { + x.Partition = new(PartitionStatefulSetStrategy) + } + x.Partition.CodecDecodeSelf(d) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *StatefulSetUpdateStrategy) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj6 int + var yyb6 bool + var yyhl6 bool = l >= 0 + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Type = "" + } else { + yyv7 := &x.Type + yyv7.CodecDecodeSelf(d) + } + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.Partition != nil { + x.Partition = nil + } + } else { + if x.Partition == nil { + x.Partition = new(PartitionStatefulSetStrategy) + } + x.Partition.CodecDecodeSelf(d) + } + for { + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj6-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x StatefulSetUpdateStrategyType) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x)) + } +} + +func (x *StatefulSetUpdateStrategyType) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + *((*string)(x)) = r.DecodeString() + } +} + +func (x *PartitionStatefulSetStrategy) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [1]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(1) + } else { + yynn2 = 1 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yym4 := z.EncBinary() + _ = yym4 + if false { + } else { + r.EncodeInt(int64(x.Ordinal)) + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("ordinal")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym5 := z.EncBinary() + _ = yym5 + if false { + } else { + r.EncodeInt(int64(x.Ordinal)) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *PartitionStatefulSetStrategy) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *PartitionStatefulSetStrategy) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "ordinal": + if r.TryDecodeAsNil() { + x.Ordinal = 0 + } else { + yyv4 := &x.Ordinal + yym5 := z.DecBinary() + _ = yym5 + if false { + } else { + *((*int32)(yyv4)) = int32(r.DecodeInt(32)) + } + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *PartitionStatefulSetStrategy) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj6 int + var yyb6 bool + var yyhl6 bool = l >= 0 + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Ordinal = 0 + } else { + yyv7 := &x.Ordinal + yym8 := z.DecBinary() + _ = yym8 + if false { + } else { + *((*int32)(yyv7)) = int32(r.DecodeInt(32)) + } + } + for { + yyj6++ + if yyhl6 { + yyb6 = yyj6 > l + } else { + yyb6 = r.CheckBreak() + } + if yyb6 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj6-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + func (x *StatefulSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { var h codecSelfer1234 z, r := codec1978.GenHelperEncoder(e) @@ -1382,16 +1799,18 @@ func (x *StatefulSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [6]bool + var yyq2 [8]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[0] = x.Replicas != nil yyq2[1] = x.Selector != nil yyq2[3] = len(x.VolumeClaimTemplates) != 0 yyq2[5] = x.PodManagementPolicy != "" + yyq2[6] = true + yyq2[7] = x.RevisionHistoryLimit != nil var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(6) + r.EncodeArrayStart(8) } else { yynn2 = 2 for _, b := range yyq2 { @@ -1550,6 +1969,58 @@ func (x *StatefulSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { x.PodManagementPolicy.CodecEncodeSelf(e) } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[6] { + yy26 := &x.UpdateStrategy + yy26.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[6] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("updateStrategy")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy28 := &x.UpdateStrategy + yy28.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[7] { + if x.RevisionHistoryLimit == nil { + r.EncodeNil() + } else { + yy31 := *x.RevisionHistoryLimit + yym32 := z.EncBinary() + _ = yym32 + if false { + } else { + r.EncodeInt(int64(yy31)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[7] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("revisionHistoryLimit")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.RevisionHistoryLimit == nil { + r.EncodeNil() + } else { + yy33 := *x.RevisionHistoryLimit + yym34 := z.EncBinary() + _ = yym34 + if false { + } else { + r.EncodeInt(int64(yy33)) + } + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -1682,6 +2153,29 @@ func (x *StatefulSetSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { yyv13 := &x.PodManagementPolicy yyv13.CodecDecodeSelf(d) } + case "updateStrategy": + if r.TryDecodeAsNil() { + x.UpdateStrategy = StatefulSetUpdateStrategy{} + } else { + yyv14 := &x.UpdateStrategy + yyv14.CodecDecodeSelf(d) + } + case "revisionHistoryLimit": + if r.TryDecodeAsNil() { + if x.RevisionHistoryLimit != nil { + x.RevisionHistoryLimit = nil + } + } else { + if x.RevisionHistoryLimit == nil { + x.RevisionHistoryLimit = new(int32) + } + yym16 := z.DecBinary() + _ = yym16 + if false { + } else { + *((*int32)(x.RevisionHistoryLimit)) = int32(r.DecodeInt(32)) + } + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -1693,16 +2187,16 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj14 int - var yyb14 bool - var yyhl14 bool = l >= 0 - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + var yyj17 int + var yyb17 bool + var yyhl17 bool = l >= 0 + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1715,20 +2209,20 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if x.Replicas == nil { x.Replicas = new(int32) } - yym16 := z.DecBinary() - _ = yym16 + yym19 := z.DecBinary() + _ = yym19 if false { } else { *((*int32)(x.Replicas)) = int32(r.DecodeInt(32)) } } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1741,21 +2235,21 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if x.Selector == nil { x.Selector = new(pkg1_v1.LabelSelector) } - yym18 := z.DecBinary() - _ = yym18 + yym21 := z.DecBinary() + _ = yym21 if false { } else if z.HasExtensions() && z.DecExt(x.Selector) { } else { z.DecFallback(x.Selector, false) } } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1763,16 +2257,16 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.Template = pkg3_v1.PodTemplateSpec{} } else { - yyv19 := &x.Template - yyv19.CodecDecodeSelf(d) + yyv22 := &x.Template + yyv22.CodecDecodeSelf(d) } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1780,21 +2274,21 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.VolumeClaimTemplates = nil } else { - yyv20 := &x.VolumeClaimTemplates - yym21 := z.DecBinary() - _ = yym21 + yyv23 := &x.VolumeClaimTemplates + yym24 := z.DecBinary() + _ = yym24 if false { } else { - h.decSlicev1_PersistentVolumeClaim((*[]pkg3_v1.PersistentVolumeClaim)(yyv20), d) + h.decSlicev1_PersistentVolumeClaim((*[]pkg3_v1.PersistentVolumeClaim)(yyv23), d) } } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1802,21 +2296,21 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.ServiceName = "" } else { - yyv22 := &x.ServiceName - yym23 := z.DecBinary() - _ = yym23 + yyv25 := &x.ServiceName + yym26 := z.DecBinary() + _ = yym26 if false { } else { - *((*string)(yyv22)) = r.DecodeString() + *((*string)(yyv25)) = r.DecodeString() } } - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -1824,21 +2318,64 @@ func (x *StatefulSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.PodManagementPolicy = "" } else { - yyv24 := &x.PodManagementPolicy - yyv24.CodecDecodeSelf(d) + yyv27 := &x.PodManagementPolicy + yyv27.CodecDecodeSelf(d) + } + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l + } else { + yyb17 = r.CheckBreak() + } + if yyb17 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.UpdateStrategy = StatefulSetUpdateStrategy{} + } else { + yyv28 := &x.UpdateStrategy + yyv28.CodecDecodeSelf(d) + } + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l + } else { + yyb17 = r.CheckBreak() + } + if yyb17 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.RevisionHistoryLimit != nil { + x.RevisionHistoryLimit = nil + } + } else { + if x.RevisionHistoryLimit == nil { + x.RevisionHistoryLimit = new(int32) + } + yym30 := z.DecBinary() + _ = yym30 + if false { + } else { + *((*int32)(x.RevisionHistoryLimit)) = int32(r.DecodeInt(32)) + } } for { - yyj14++ - if yyhl14 { - yyb14 = yyj14 > l + yyj17++ + if yyhl17 { + yyb17 = yyj17 > l } else { - yyb14 = r.CheckBreak() + yyb17 = r.CheckBreak() } - if yyb14 { + if yyb17 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj14-1, "") + z.DecStructFieldNotFound(yyj17-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -1857,13 +2394,18 @@ func (x *StatefulSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [2]bool + var yyq2 [7]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[0] = x.ObservedGeneration != nil + yyq2[2] = x.ReadyReplicas != 0 + yyq2[3] = x.CurrentReplicas != 0 + yyq2[4] = x.UpdatedReplicas != 0 + yyq2[5] = x.CurrentRevision != "" + yyq2[6] = x.UpdateRevision != "" var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(2) + r.EncodeArrayStart(7) } else { yynn2 = 1 for _, b := range yyq2 { @@ -1928,6 +2470,131 @@ func (x *StatefulSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { r.EncodeInt(int64(x.Replicas)) } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym12 := z.EncBinary() + _ = yym12 + if false { + } else { + r.EncodeInt(int64(x.ReadyReplicas)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("readyReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym13 := z.EncBinary() + _ = yym13 + if false { + } else { + r.EncodeInt(int64(x.ReadyReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yym15 := z.EncBinary() + _ = yym15 + if false { + } else { + r.EncodeInt(int64(x.CurrentReplicas)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("currentReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym16 := z.EncBinary() + _ = yym16 + if false { + } else { + r.EncodeInt(int64(x.CurrentReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[4] { + yym18 := z.EncBinary() + _ = yym18 + if false { + } else { + r.EncodeInt(int64(x.UpdatedReplicas)) + } + } else { + r.EncodeInt(0) + } + } else { + if yyq2[4] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("updatedReplicas")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym19 := z.EncBinary() + _ = yym19 + if false { + } else { + r.EncodeInt(int64(x.UpdatedReplicas)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[5] { + yym21 := z.EncBinary() + _ = yym21 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.CurrentRevision)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[5] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("currentRevision")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym22 := z.EncBinary() + _ = yym22 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.CurrentRevision)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[6] { + yym24 := z.EncBinary() + _ = yym24 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.UpdateRevision)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[6] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("updateRevision")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym25 := z.EncBinary() + _ = yym25 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.UpdateRevision)) + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -2017,6 +2684,66 @@ func (x *StatefulSetStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) *((*int32)(yyv6)) = int32(r.DecodeInt(32)) } } + case "readyReplicas": + if r.TryDecodeAsNil() { + x.ReadyReplicas = 0 + } else { + yyv8 := &x.ReadyReplicas + yym9 := z.DecBinary() + _ = yym9 + if false { + } else { + *((*int32)(yyv8)) = int32(r.DecodeInt(32)) + } + } + case "currentReplicas": + if r.TryDecodeAsNil() { + x.CurrentReplicas = 0 + } else { + yyv10 := &x.CurrentReplicas + yym11 := z.DecBinary() + _ = yym11 + if false { + } else { + *((*int32)(yyv10)) = int32(r.DecodeInt(32)) + } + } + case "updatedReplicas": + if r.TryDecodeAsNil() { + x.UpdatedReplicas = 0 + } else { + yyv12 := &x.UpdatedReplicas + yym13 := z.DecBinary() + _ = yym13 + if false { + } else { + *((*int32)(yyv12)) = int32(r.DecodeInt(32)) + } + } + case "currentRevision": + if r.TryDecodeAsNil() { + x.CurrentRevision = "" + } else { + yyv14 := &x.CurrentRevision + yym15 := z.DecBinary() + _ = yym15 + if false { + } else { + *((*string)(yyv14)) = r.DecodeString() + } + } + case "updateRevision": + if r.TryDecodeAsNil() { + x.UpdateRevision = "" + } else { + yyv16 := &x.UpdateRevision + yym17 := z.DecBinary() + _ = yym17 + if false { + } else { + *((*string)(yyv16)) = r.DecodeString() + } + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -2028,16 +2755,16 @@ func (x *StatefulSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj8 int - var yyb8 bool - var yyhl8 bool = l >= 0 - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + var yyj18 int + var yyb18 bool + var yyhl18 bool = l >= 0 + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l } else { - yyb8 = r.CheckBreak() + yyb18 = r.CheckBreak() } - if yyb8 { + if yyb18 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -2050,20 +2777,20 @@ func (x *StatefulSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder if x.ObservedGeneration == nil { x.ObservedGeneration = new(int64) } - yym10 := z.DecBinary() - _ = yym10 + yym20 := z.DecBinary() + _ = yym20 if false { } else { *((*int64)(x.ObservedGeneration)) = int64(r.DecodeInt(64)) } } - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l } else { - yyb8 = r.CheckBreak() + yyb18 = r.CheckBreak() } - if yyb8 { + if yyb18 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -2071,26 +2798,136 @@ func (x *StatefulSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder if r.TryDecodeAsNil() { x.Replicas = 0 } else { - yyv11 := &x.Replicas - yym12 := z.DecBinary() - _ = yym12 + yyv21 := &x.Replicas + yym22 := z.DecBinary() + _ = yym22 if false { } else { - *((*int32)(yyv11)) = int32(r.DecodeInt(32)) + *((*int32)(yyv21)) = int32(r.DecodeInt(32)) + } + } + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l + } else { + yyb18 = r.CheckBreak() + } + if yyb18 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ReadyReplicas = 0 + } else { + yyv23 := &x.ReadyReplicas + yym24 := z.DecBinary() + _ = yym24 + if false { + } else { + *((*int32)(yyv23)) = int32(r.DecodeInt(32)) + } + } + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l + } else { + yyb18 = r.CheckBreak() + } + if yyb18 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.CurrentReplicas = 0 + } else { + yyv25 := &x.CurrentReplicas + yym26 := z.DecBinary() + _ = yym26 + if false { + } else { + *((*int32)(yyv25)) = int32(r.DecodeInt(32)) + } + } + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l + } else { + yyb18 = r.CheckBreak() + } + if yyb18 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.UpdatedReplicas = 0 + } else { + yyv27 := &x.UpdatedReplicas + yym28 := z.DecBinary() + _ = yym28 + if false { + } else { + *((*int32)(yyv27)) = int32(r.DecodeInt(32)) + } + } + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l + } else { + yyb18 = r.CheckBreak() + } + if yyb18 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.CurrentRevision = "" + } else { + yyv29 := &x.CurrentRevision + yym30 := z.DecBinary() + _ = yym30 + if false { + } else { + *((*string)(yyv29)) = r.DecodeString() + } + } + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l + } else { + yyb18 = r.CheckBreak() + } + if yyb18 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.UpdateRevision = "" + } else { + yyv31 := &x.UpdateRevision + yym32 := z.DecBinary() + _ = yym32 + if false { + } else { + *((*string)(yyv31)) = r.DecodeString() } } for { - yyj8++ - if yyhl8 { - yyb8 = yyj8 > l + yyj18++ + if yyhl18 { + yyb18 = yyj18 > l } else { - yyb8 = r.CheckBreak() + yyb18 = r.CheckBreak() } - if yyb8 { + if yyb18 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj8-1, "") + z.DecStructFieldNotFound(yyj18-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -7114,7 +7951,7 @@ func (x codecSelfer1234) decSliceStatefulSet(v *[]StatefulSet, d *codec1978.Deco yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 912) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 984) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types.go b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types.go index 02b9784a895..66b6b6bad8e 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types.go +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types.go @@ -26,6 +26,7 @@ import ( const ( // StatefulSetInitAnnotation if present, and set to false, indicates that a Pod's readiness should be ignored. StatefulSetInitAnnotation = "pod.alpha.kubernetes.io/initialized" + StatefulSetRevisionLabel = "statefulset.beta.kubernetes.io/revision" ) // ScaleSpec describes the attributes of a scale subresource @@ -111,6 +112,54 @@ const ( ParallelPodManagement = "Parallel" ) +// StatefulSetUpdateStrategy indicates the strategy that the StatefulSet +// controller will use to perform updates. It includes any additional parameters +// necessary to perform the update for the indicated strategy. +type StatefulSetUpdateStrategy struct { + // Type indicates the type of the StatefulSetUpdateStrategy. + Type StatefulSetUpdateStrategyType `json:"type,omitempty" protobuf:"bytes,1,opt,name=type,casttype=StatefulSetStrategyType"` + // Partition is used to communicate the ordinal at which to partition + // the StatefulSet when Type is PartitionStatefulSetStrategyType. This + // 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 +// all possible update strategies for the StatefulSet controller. +type StatefulSetUpdateStrategyType string + +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 + // applied to all Pods in the StatefulSet with respect to the StatefulSet + // ordering constraints. When a scale operation is performed with this + // strategy, new Pods will be created from the specification version indicated + // by the StatefulSet's updateRevision. + RollingUpdateStatefulSetStrategyType = "RollingUpdate" + // OnDeleteStatefulSetStrategyType triggers the legacy behavior. Version + // tracking and ordered rolling restarts are disabled. Pods are recreated + // from the StatefulSetSpec when they are manually deleted. When a scale + // operation is performed with this strategy,specification version indicated + // by the StatefulSet's currentRevision. + OnDeleteStatefulSetStrategyType = "OnDelete" +) + +// PartitionStatefulSetStrategy contains the parameters used with the +// PartitionStatefulSetStrategyType. +type PartitionStatefulSetStrategy struct { + // Ordinal indicates the ordinal at which the StatefulSet should be + // partitioned. + Ordinal int32 `json:"ordinal" protobuf:"varint,1,opt,name=ordinal"` +} + // A StatefulSetSpec is the specification of a StatefulSet. type StatefulSetSpec struct { // replicas is the desired number of replicas of the given Template. @@ -160,16 +209,47 @@ type StatefulSetSpec struct { // all pods at once. // +optional PodManagementPolicy PodManagementPolicyType `json:"podManagementPolicy,omitempty" protobuf:"bytes,6,opt,name=podManagementPolicy,casttype=PodManagementPolicyType"` + + // updateStrategy indicates the StatefulSetUpdateStrategy that will be + // employed to update Pods in the StatefulSet when a revision is made to + // Template. + UpdateStrategy StatefulSetUpdateStrategy `json:"updateStrategy,omitempty" protobuf:"bytes,7,opt,name=updateStrategy"` + + // revisionHistoryLimit is the maximum number of revisions that will + // be maintained in the StatefulSet's revision history. The revision history + // consists of all revisions not represented by a currently applied + // StatefulSetSpec version. The default value is 10. + RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,8,opt,name=revisionHistoryLimit"` } // StatefulSetStatus represents the current state of a StatefulSet. type StatefulSetStatus struct { - // observedGeneration is the most recent generation observed by this StatefulSet. + // observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the + // StatefulSet's generation, which is updated on mutation by the API Server. // +optional ObservedGeneration *int64 `json:"observedGeneration,omitempty" protobuf:"varint,1,opt,name=observedGeneration"` - // replicas is the number of actual replicas. + // replicas is the number of Pods created by the StatefulSet controller. Replicas int32 `json:"replicas" protobuf:"varint,2,opt,name=replicas"` + + // readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition. + ReadyReplicas int32 `json:"readyReplicas,omitempty" protobuf:"varint,3,opt,name=readyReplicas"` + + // currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by currentRevision. + CurrentReplicas int32 `json:"currentReplicas,omitempty" protobuf:"varint,4,opt,name=currentReplicas"` + + // updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version + // indicated by updateRevision. + UpdatedReplicas int32 `json:"updatedReplicas,omitempty" protobuf:"varint,5,opt,name=updatedReplicas"` + + // currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the + // sequence [0,currentReplicas). + CurrentRevision string `json:"currentRevision,omitempty" protobuf:"bytes,6,opt,name=currentRevision"` + + // updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence + // [replicas-updatedReplicas,replicas) + UpdateRevision string `json:"updateRevision,omitempty" protobuf:"bytes,7,opt,name=updateRevision"` } // StatefulSetList is a collection of StatefulSets. diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types_swagger_doc_generated.go index 752356933e8..e5b403300a3 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types_swagger_doc_generated.go @@ -137,6 +137,15 @@ func (DeploymentStrategy) SwaggerDoc() map[string]string { return map_DeploymentStrategy } +var map_PartitionStatefulSetStrategy = map[string]string{ + "": "PartitionStatefulSetStrategy contains the parameters used with the PartitionStatefulSetStrategyType.", + "ordinal": "Ordinal indicates the ordinal at which the StatefulSet should be partitioned.", +} + +func (PartitionStatefulSetStrategy) SwaggerDoc() map[string]string { + return map_PartitionStatefulSetStrategy +} + var map_RollbackConfig = map[string]string{ "revision": "The revision to rollback to. If set to 0, rollback to the last revision.", } @@ -212,6 +221,8 @@ var map_StatefulSetSpec = map[string]string{ "volumeClaimTemplates": "volumeClaimTemplates is a list of claims that pods are allowed to reference. The StatefulSet controller is responsible for mapping network identities to claims in a way that maintains the identity of a pod. Every claim in this list must have at least one matching (by name) volumeMount in one container in the template. A claim in this list takes precedence over any volumes in the template, with the same name.", "serviceName": "serviceName is the name of the service that governs this StatefulSet. This service must exist before the StatefulSet, and is responsible for the network identity of the set. Pods get DNS/hostnames that follow the pattern: pod-specific-string.serviceName.default.svc.cluster.local where \"pod-specific-string\" is managed by the StatefulSet controller.", "podManagementPolicy": "podManagementPolicy controls how pods are created during initial scale up, when replacing pods on nodes, or when scaling down. The default policy is `OrderedReady`, where pods are created in increasing order (pod-0, then pod-1, etc) and the controller will wait until each pod is ready before continuing. When scaling down, the pods are removed in the opposite order. The alternative policy is `Parallel` which will create pods in parallel to match the desired scale without waiting, and on scale down will delete all pods at once.", + "updateStrategy": "updateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template.", + "revisionHistoryLimit": "revisionHistoryLimit is the maximum number of revisions that will be maintained in the StatefulSet's revision history. The revision history consists of all revisions not represented by a currently applied StatefulSetSpec version. The default value is 10.", } func (StatefulSetSpec) SwaggerDoc() map[string]string { @@ -220,12 +231,27 @@ func (StatefulSetSpec) SwaggerDoc() map[string]string { var map_StatefulSetStatus = map[string]string{ "": "StatefulSetStatus represents the current state of a StatefulSet.", - "observedGeneration": "observedGeneration is the most recent generation observed by this StatefulSet.", - "replicas": "replicas is the number of actual replicas.", + "observedGeneration": "observedGeneration is the most recent generation observed for this StatefulSet. It corresponds to the StatefulSet's generation, which is updated on mutation by the API Server.", + "replicas": "replicas is the number of Pods created by the StatefulSet controller.", + "readyReplicas": "readyReplicas is the number of Pods created by the StatefulSet controller that have a Ready Condition.", + "currentReplicas": "currentReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by currentRevision.", + "updatedReplicas": "updatedReplicas is the number of Pods created by the StatefulSet controller from the StatefulSet version indicated by updateRevision.", + "currentRevision": "currentRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [0,currentReplicas).", + "updateRevision": "updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas)", } func (StatefulSetStatus) SwaggerDoc() map[string]string { return map_StatefulSetStatus } +var map_StatefulSetUpdateStrategy = map[string]string{ + "": "StatefulSetUpdateStrategy indicates the strategy that the StatefulSet controller will use to perform updates. It includes any additional parameters necessary to perform the update for the indicated strategy.", + "type": "Type indicates the type of the StatefulSetUpdateStrategy.", + "partition": "Partition is used to communicate the ordinal at which to partition the StatefulSet when Type is PartitionStatefulSetStrategyType. This value must be set when Type is PartitionStatefulSetStrategyType, and it must be nil otherwise.", +} + +func (StatefulSetUpdateStrategy) SwaggerDoc() map[string]string { + return map_StatefulSetUpdateStrategy +} + // AUTO-GENERATED FUNCTIONS END HERE diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/zz_generated.conversion.go b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/zz_generated.conversion.go index 5b39d12f100..a5ead25a6ce 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/zz_generated.conversion.go +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/zz_generated.conversion.go @@ -42,6 +42,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_apps_ControllerRevision_To_v1beta1_ControllerRevision, Convert_v1beta1_ControllerRevisionList_To_apps_ControllerRevisionList, Convert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList, + Convert_v1beta1_PartitionStatefulSetStrategy_To_apps_PartitionStatefulSetStrategy, + Convert_apps_PartitionStatefulSetStrategy_To_v1beta1_PartitionStatefulSetStrategy, Convert_v1beta1_StatefulSet_To_apps_StatefulSet, Convert_apps_StatefulSet_To_v1beta1_StatefulSet, Convert_v1beta1_StatefulSetList_To_apps_StatefulSetList, @@ -50,6 +52,8 @@ func RegisterConversions(scheme *runtime.Scheme) error { Convert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec, Convert_v1beta1_StatefulSetStatus_To_apps_StatefulSetStatus, Convert_apps_StatefulSetStatus_To_v1beta1_StatefulSetStatus, + Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy, + Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy, ) } @@ -123,6 +127,26 @@ func Convert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList(in *a return autoConvert_apps_ControllerRevisionList_To_v1beta1_ControllerRevisionList(in, out, s) } +func autoConvert_v1beta1_PartitionStatefulSetStrategy_To_apps_PartitionStatefulSetStrategy(in *PartitionStatefulSetStrategy, out *apps.PartitionStatefulSetStrategy, s conversion.Scope) error { + out.Ordinal = in.Ordinal + return nil +} + +// Convert_v1beta1_PartitionStatefulSetStrategy_To_apps_PartitionStatefulSetStrategy is an autogenerated conversion function. +func Convert_v1beta1_PartitionStatefulSetStrategy_To_apps_PartitionStatefulSetStrategy(in *PartitionStatefulSetStrategy, out *apps.PartitionStatefulSetStrategy, s conversion.Scope) error { + return autoConvert_v1beta1_PartitionStatefulSetStrategy_To_apps_PartitionStatefulSetStrategy(in, out, s) +} + +func autoConvert_apps_PartitionStatefulSetStrategy_To_v1beta1_PartitionStatefulSetStrategy(in *apps.PartitionStatefulSetStrategy, out *PartitionStatefulSetStrategy, s conversion.Scope) error { + out.Ordinal = in.Ordinal + return nil +} + +// Convert_apps_PartitionStatefulSetStrategy_To_v1beta1_PartitionStatefulSetStrategy is an autogenerated conversion function. +func Convert_apps_PartitionStatefulSetStrategy_To_v1beta1_PartitionStatefulSetStrategy(in *apps.PartitionStatefulSetStrategy, out *PartitionStatefulSetStrategy, s conversion.Scope) error { + return autoConvert_apps_PartitionStatefulSetStrategy_To_v1beta1_PartitionStatefulSetStrategy(in, out, s) +} + func autoConvert_v1beta1_StatefulSet_To_apps_StatefulSet(in *StatefulSet, out *apps.StatefulSet, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(&in.Spec, &out.Spec, s); err != nil { @@ -208,6 +232,10 @@ func autoConvert_v1beta1_StatefulSetSpec_To_apps_StatefulSetSpec(in *StatefulSet out.VolumeClaimTemplates = *(*[]api.PersistentVolumeClaim)(unsafe.Pointer(&in.VolumeClaimTemplates)) out.ServiceName = in.ServiceName out.PodManagementPolicy = apps.PodManagementPolicyType(in.PodManagementPolicy) + if err := Convert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { + return err + } + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) return nil } @@ -222,12 +250,21 @@ func autoConvert_apps_StatefulSetSpec_To_v1beta1_StatefulSetSpec(in *apps.Statef out.VolumeClaimTemplates = *(*[]api_v1.PersistentVolumeClaim)(unsafe.Pointer(&in.VolumeClaimTemplates)) out.ServiceName = in.ServiceName out.PodManagementPolicy = PodManagementPolicyType(in.PodManagementPolicy) + if err := Convert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, s); err != nil { + return err + } + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) return nil } func autoConvert_v1beta1_StatefulSetStatus_To_apps_StatefulSetStatus(in *StatefulSetStatus, out *apps.StatefulSetStatus, s conversion.Scope) error { out.ObservedGeneration = (*int64)(unsafe.Pointer(in.ObservedGeneration)) out.Replicas = in.Replicas + out.ReadyReplicas = in.ReadyReplicas + out.CurrentReplicas = in.CurrentReplicas + out.UpdatedReplicas = in.UpdatedReplicas + out.CurrentRevision = in.CurrentRevision + out.UpdateRevision = in.UpdateRevision return nil } @@ -239,6 +276,11 @@ func Convert_v1beta1_StatefulSetStatus_To_apps_StatefulSetStatus(in *StatefulSet func autoConvert_apps_StatefulSetStatus_To_v1beta1_StatefulSetStatus(in *apps.StatefulSetStatus, out *StatefulSetStatus, s conversion.Scope) error { out.ObservedGeneration = (*int64)(unsafe.Pointer(in.ObservedGeneration)) out.Replicas = in.Replicas + out.ReadyReplicas = in.ReadyReplicas + out.CurrentReplicas = in.CurrentReplicas + out.UpdatedReplicas = in.UpdatedReplicas + out.CurrentRevision = in.CurrentRevision + out.UpdateRevision = in.UpdateRevision return nil } @@ -246,3 +288,15 @@ func autoConvert_apps_StatefulSetStatus_To_v1beta1_StatefulSetStatus(in *apps.St func Convert_apps_StatefulSetStatus_To_v1beta1_StatefulSetStatus(in *apps.StatefulSetStatus, out *StatefulSetStatus, s conversion.Scope) error { return autoConvert_apps_StatefulSetStatus_To_v1beta1_StatefulSetStatus(in, out, s) } + +func autoConvert_v1beta1_StatefulSetUpdateStrategy_To_apps_StatefulSetUpdateStrategy(in *StatefulSetUpdateStrategy, out *apps.StatefulSetUpdateStrategy, s conversion.Scope) error { + out.Type = apps.StatefulSetUpdateStrategyType(in.Type) + out.Partition = (*apps.PartitionStatefulSetStrategy)(unsafe.Pointer(in.Partition)) + return nil +} + +func autoConvert_apps_StatefulSetUpdateStrategy_To_v1beta1_StatefulSetUpdateStrategy(in *apps.StatefulSetUpdateStrategy, out *StatefulSetUpdateStrategy, s conversion.Scope) error { + out.Type = StatefulSetUpdateStrategyType(in.Type) + out.Partition = (*PartitionStatefulSetStrategy)(unsafe.Pointer(in.Partition)) + return nil +} diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/zz_generated.deepcopy.go index a7feb4a82fe..475fb17d845 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/zz_generated.deepcopy.go @@ -46,6 +46,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_DeploymentSpec, InType: reflect.TypeOf(&DeploymentSpec{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_DeploymentStatus, InType: reflect.TypeOf(&DeploymentStatus{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_DeploymentStrategy, InType: reflect.TypeOf(&DeploymentStrategy{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_PartitionStatefulSetStrategy, InType: reflect.TypeOf(&PartitionStatefulSetStrategy{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_RollbackConfig, InType: reflect.TypeOf(&RollbackConfig{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_RollingUpdateDeployment, InType: reflect.TypeOf(&RollingUpdateDeployment{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_Scale, InType: reflect.TypeOf(&Scale{})}, @@ -55,6 +56,7 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_StatefulSetList, InType: reflect.TypeOf(&StatefulSetList{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_StatefulSetSpec, InType: reflect.TypeOf(&StatefulSetSpec{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_StatefulSetStatus, InType: reflect.TypeOf(&StatefulSetStatus{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_v1beta1_StatefulSetUpdateStrategy, InType: reflect.TypeOf(&StatefulSetUpdateStrategy{})}, ) } @@ -251,6 +253,16 @@ func DeepCopy_v1beta1_DeploymentStrategy(in interface{}, out interface{}, c *con } } +// DeepCopy_v1beta1_PartitionStatefulSetStrategy is an autogenerated deepcopy function. +func DeepCopy_v1beta1_PartitionStatefulSetStrategy(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*PartitionStatefulSetStrategy) + out := out.(*PartitionStatefulSetStrategy) + *out = *in + return nil + } +} + // DeepCopy_v1beta1_RollbackConfig is an autogenerated deepcopy function. func DeepCopy_v1beta1_RollbackConfig(in interface{}, out interface{}, c *conversion.Cloner) error { { @@ -397,6 +409,14 @@ func DeepCopy_v1beta1_StatefulSetSpec(in interface{}, out interface{}, c *conver } } } + if err := DeepCopy_v1beta1_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, c); err != nil { + return err + } + if in.RevisionHistoryLimit != nil { + in, out := &in.RevisionHistoryLimit, &out.RevisionHistoryLimit + *out = new(int32) + **out = **in + } return nil } } @@ -415,3 +435,18 @@ func DeepCopy_v1beta1_StatefulSetStatus(in interface{}, out interface{}, c *conv return nil } } + +// DeepCopy_v1beta1_StatefulSetUpdateStrategy is an autogenerated deepcopy function. +func DeepCopy_v1beta1_StatefulSetUpdateStrategy(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*StatefulSetUpdateStrategy) + out := out.(*StatefulSetUpdateStrategy) + *out = *in + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(PartitionStatefulSetStrategy) + **out = **in + } + return nil + } +} diff --git a/staging/src/k8s.io/client-go/pkg/apis/apps/zz_generated.deepcopy.go b/staging/src/k8s.io/client-go/pkg/apis/apps/zz_generated.deepcopy.go index 9e2bf52449c..5307cdf7986 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/apps/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/client-go/pkg/apis/apps/zz_generated.deepcopy.go @@ -38,10 +38,12 @@ func RegisterDeepCopies(scheme *runtime.Scheme) error { return scheme.AddGeneratedDeepCopyFuncs( conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_ControllerRevision, InType: reflect.TypeOf(&ControllerRevision{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_ControllerRevisionList, InType: reflect.TypeOf(&ControllerRevisionList{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_PartitionStatefulSetStrategy, InType: reflect.TypeOf(&PartitionStatefulSetStrategy{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_StatefulSet, InType: reflect.TypeOf(&StatefulSet{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_StatefulSetList, InType: reflect.TypeOf(&StatefulSetList{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_StatefulSetSpec, InType: reflect.TypeOf(&StatefulSetSpec{})}, conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_StatefulSetStatus, InType: reflect.TypeOf(&StatefulSetStatus{})}, + conversion.GeneratedDeepCopyFunc{Fn: DeepCopy_apps_StatefulSetUpdateStrategy, InType: reflect.TypeOf(&StatefulSetUpdateStrategy{})}, ) } @@ -87,6 +89,16 @@ func DeepCopy_apps_ControllerRevisionList(in interface{}, out interface{}, c *co } } +// DeepCopy_apps_PartitionStatefulSetStrategy is an autogenerated deepcopy function. +func DeepCopy_apps_PartitionStatefulSetStrategy(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*PartitionStatefulSetStrategy) + out := out.(*PartitionStatefulSetStrategy) + *out = *in + return nil + } +} + // DeepCopy_apps_StatefulSet is an autogenerated deepcopy function. func DeepCopy_apps_StatefulSet(in interface{}, out interface{}, c *conversion.Cloner) error { { @@ -153,6 +165,14 @@ func DeepCopy_apps_StatefulSetSpec(in interface{}, out interface{}, c *conversio } } } + if err := DeepCopy_apps_StatefulSetUpdateStrategy(&in.UpdateStrategy, &out.UpdateStrategy, c); err != nil { + return err + } + if in.RevisionHistoryLimit != nil { + in, out := &in.RevisionHistoryLimit, &out.RevisionHistoryLimit + *out = new(int32) + **out = **in + } return nil } } @@ -171,3 +191,18 @@ func DeepCopy_apps_StatefulSetStatus(in interface{}, out interface{}, c *convers return nil } } + +// DeepCopy_apps_StatefulSetUpdateStrategy is an autogenerated deepcopy function. +func DeepCopy_apps_StatefulSetUpdateStrategy(in interface{}, out interface{}, c *conversion.Cloner) error { + { + in := in.(*StatefulSetUpdateStrategy) + out := out.(*StatefulSetUpdateStrategy) + *out = *in + if in.Partition != nil { + in, out := &in.Partition, &out.Partition + *out = new(PartitionStatefulSetStrategy) + **out = **in + } + return nil + } +} diff --git a/test/e2e/framework/statefulset_utils.go b/test/e2e/framework/statefulset_utils.go index 4ab4ad4d29a..03303c2504d 100644 --- a/test/e2e/framework/statefulset_utils.go +++ b/test/e2e/framework/statefulset_utils.go @@ -312,6 +312,22 @@ func (s *StatefulSetTester) waitForRunning(numStatefulPods int32, ss *apps.State } } +// WaitForState periodically polls for the ss and its pods until the until function returns either true or an error +func (s *StatefulSetTester) WaitForState(ss *apps.StatefulSet, until func(*apps.StatefulSet, *v1.PodList) (bool, error)) { + pollErr := wait.PollImmediate(StatefulSetPoll, StatefulSetTimeout, + func() (bool, error) { + ssGet, err := s.c.Apps().StatefulSets(ss.Namespace).Get(ss.Name, metav1.GetOptions{}) + if err != nil { + return false, err + } + podList := s.GetPodList(ssGet) + return until(ssGet, podList) + }) + if pollErr != nil { + Failf("Failed waiting for pods to enter running: %v", pollErr) + } +} + // WaitForRunningAndReady waits for numStatefulPods in ss to be Running and Ready. func (s *StatefulSetTester) WaitForRunningAndReady(numStatefulPods int32, ss *apps.StatefulSet) { s.waitForRunning(numStatefulPods, ss, true) @@ -365,8 +381,33 @@ func (s *StatefulSetTester) SetHealthy(ss *apps.StatefulSet) { } } -// WaitForStatus waits for the ss.Status.Replicas to be equal to expectedReplicas -func (s *StatefulSetTester) WaitForStatus(ss *apps.StatefulSet, expectedReplicas int32) { +// WaitForStatusReadyReplicas waits for the ss.Status.ReadyReplicas to be equal to expectedReplicas +func (s *StatefulSetTester) WaitForStatusReadyReplicas(ss *apps.StatefulSet, expectedReplicas int32) { + Logf("Waiting for statefulset status.replicas updated to %d", expectedReplicas) + + ns, name := ss.Namespace, ss.Name + pollErr := wait.PollImmediate(StatefulSetPoll, StatefulSetTimeout, + func() (bool, error) { + ssGet, err := s.c.Apps().StatefulSets(ns).Get(name, metav1.GetOptions{}) + if err != nil { + return false, err + } + if *ssGet.Status.ObservedGeneration < ss.Generation { + return false, nil + } + if ssGet.Status.ReadyReplicas != expectedReplicas { + Logf("Waiting for stateful set status to become %d, currently %d", expectedReplicas, ssGet.Status.Replicas) + return false, nil + } + return true, nil + }) + if pollErr != nil { + Failf("Failed waiting for stateful set status.readyReplicas updated to %d: %v", expectedReplicas, pollErr) + } +} + +// WaitForStatusReplicas waits for the ss.Status.Replicas to be equal to expectedReplicas +func (s *StatefulSetTester) WaitForStatusReplicas(ss *apps.StatefulSet, expectedReplicas int32) { Logf("Waiting for statefulset status.replicas updated to %d", expectedReplicas) ns, name := ss.Namespace, ss.Name @@ -416,7 +457,7 @@ func DeleteAllStatefulSets(c clientset.Interface, ns string) { if err := sst.Scale(&ss, 0); err != nil { errList = append(errList, fmt.Sprintf("%v", err)) } - sst.WaitForStatus(&ss, 0) + sst.WaitForStatusReplicas(&ss, 0) Logf("Deleting statefulset %v", ss.Name) // Use OrphanDependents=false so it's deleted synchronously. // We already made sure the Pods are gone inside Scale(). @@ -561,6 +602,7 @@ func NewStatefulSet(name, ns, governingSvcName string, replicas int32, statefulP Volumes: vols, }, }, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, VolumeClaimTemplates: claims, ServiceName: governingSvcName, }, diff --git a/test/e2e/statefulset.go b/test/e2e/statefulset.go index 866d960b5a1..8a8f68ebebb 100644 --- a/test/e2e/statefulset.go +++ b/test/e2e/statefulset.go @@ -31,7 +31,6 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/watch" "k8s.io/kubernetes/pkg/api/v1" - podutil "k8s.io/kubernetes/pkg/api/v1/pod" apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" "k8s.io/kubernetes/pkg/controller" @@ -250,8 +249,11 @@ var _ = framework.KubeDescribe("StatefulSet", func() { It("should allow template updates", func() { By("Creating stateful set " + ssName + " in namespace " + ns) - *(ss.Spec.Replicas) = 2 - + testProbe := &v1.Probe{Handler: v1.Handler{HTTPGet: &v1.HTTPGetAction{ + Path: "/index.html", + Port: intstr.IntOrString{IntVal: 80}}}} + ss := framework.NewStatefulSet("ss2", ns, headlessSvcName, 2, nil, nil, labels) + ss.Spec.Template.Spec.Containers[0].ReadinessProbe = testProbe ss, err := c.Apps().StatefulSets(ns).Create(ss) Expect(err).NotTo(HaveOccurred()) @@ -268,79 +270,21 @@ var _ = framework.KubeDescribe("StatefulSet", func() { }) Expect(err).NotTo(HaveOccurred()) - updateIndex := 0 - By(fmt.Sprintf("Deleting stateful pod at index %d", updateIndex)) - sst.DeleteStatefulPodAtIndex(updateIndex, ss) - - By("Waiting for all stateful pods to be running again") - sst.WaitForRunningAndReady(*ss.Spec.Replicas, ss) - - By(fmt.Sprintf("Verifying stateful pod at index %d is updated", updateIndex)) - verify := func(pod *v1.Pod) { - podImage := pod.Spec.Containers[0].Image - Expect(podImage).To(Equal(newImage), fmt.Sprintf("Expected stateful pod image %s updated to %s", podImage, newImage)) - } - sst.VerifyPodAtIndex(updateIndex, ss, verify) - }) - - It("Scaling down before scale up is finished should wait until current pod will be running and ready before it will be removed", func() { - By("Creating stateful set " + ssName + " in namespace " + ns + ", and pausing scale operations after each pod") - testProbe := &v1.Probe{Handler: v1.Handler{HTTPGet: &v1.HTTPGetAction{ - Path: "/index.html", - Port: intstr.IntOrString{IntVal: 80}}}} - ss := framework.NewStatefulSet(ssName, ns, headlessSvcName, 1, nil, nil, labels) - ss.Spec.Template.Spec.Containers[0].ReadinessProbe = testProbe - framework.SetStatefulSetInitializedAnnotation(ss, "false") - ss, err := c.Apps().StatefulSets(ns).Create(ss) - Expect(err).NotTo(HaveOccurred()) - sst := framework.NewStatefulSetTester(c) - sst.WaitForRunningAndReady(1, ss) - - By("Scaling up stateful set " + ssName + " to 3 replicas and pausing after 2nd pod") - sst.SetHealthy(ss) - sst.UpdateReplicas(ss, 3) - sst.WaitForRunningAndReady(2, ss) - - By("Before scale up finished setting 2nd pod to be not ready by breaking readiness probe") - sst.BreakProbe(ss, testProbe) - sst.WaitForStatus(ss, 0) - sst.WaitForRunningAndNotReady(2, ss) - - By("Continue scale operation after the 2nd pod, and scaling down to 1 replica") - sst.SetHealthy(ss) - sst.UpdateReplicas(ss, 1) - - By("Verifying that the 2nd pod wont be removed if it is not running and ready") - sst.ConfirmStatefulPodCount(2, ss, 10*time.Second, true) - expectedPodName := ss.Name + "-1" - expectedPod, err := f.ClientSet.Core().Pods(ns).Get(expectedPodName, metav1.GetOptions{}) - Expect(err).NotTo(HaveOccurred()) - - By("Verifying the 2nd pod is removed only when it becomes running and ready") - sst.RestoreProbe(ss, testProbe) - watcher, err := f.ClientSet.Core().Pods(ns).Watch(metav1.SingleObject( - metav1.ObjectMeta{ - Name: expectedPod.Name, - ResourceVersion: expectedPod.ResourceVersion, - }, - )) - Expect(err).NotTo(HaveOccurred()) - _, err = watch.Until(framework.StatefulSetTimeout, watcher, func(event watch.Event) (bool, error) { - pod := event.Object.(*v1.Pod) - if event.Type == watch.Deleted && pod.Name == expectedPodName { - return false, fmt.Errorf("Pod %v was deleted before enter running", pod.Name) - } - framework.Logf("Observed event %v for pod %v. Phase %v, Pod is ready %v", - event.Type, pod.Name, pod.Status.Phase, podutil.IsPodReady(pod)) - if pod.Name != expectedPodName { + sst.WaitForState(ss, func(set *apps.StatefulSet, pods *v1.PodList) (bool, error) { + if len(pods.Items) < 2 { return false, nil } - if pod.Status.Phase == v1.PodRunning && podutil.IsPodReady(pod) { - return true, nil + for i := range pods.Items { + if pods.Items[i].Spec.Containers[0].Image != newImage { + framework.Logf("Waiting for pod %s to have image %s current image %s", + pods.Items[i].Name, + newImage, + pods.Items[i].Spec.Containers[0].Image) + return false, nil + } } - return false, nil + return true, nil }) - Expect(err).NotTo(HaveOccurred()) }) It("Scaling should happen in predictable order and halt if any stateful pod is unhealthy", func() { @@ -367,7 +311,7 @@ var _ = framework.KubeDescribe("StatefulSet", func() { By("Confirming that stateful set scale up will halt with unhealthy stateful pod") sst.BreakProbe(ss, testProbe) sst.WaitForRunningAndNotReady(*ss.Spec.Replicas, ss) - sst.WaitForStatus(ss, 0) + sst.WaitForStatusReadyReplicas(ss, 0) sst.UpdateReplicas(ss, 3) sst.ConfirmStatefulPodCount(1, ss, 10*time.Second, true) @@ -397,7 +341,7 @@ var _ = framework.KubeDescribe("StatefulSet", func() { Expect(err).NotTo(HaveOccurred()) sst.BreakProbe(ss, testProbe) - sst.WaitForStatus(ss, 0) + sst.WaitForStatusReadyReplicas(ss, 0) sst.WaitForRunningAndNotReady(3, ss) sst.UpdateReplicas(ss, 0) sst.ConfirmStatefulPodCount(3, ss, 10*time.Second, true) @@ -442,7 +386,7 @@ var _ = framework.KubeDescribe("StatefulSet", func() { By("Confirming that stateful set scale up will not halt with unhealthy stateful pod") sst.BreakProbe(ss, testProbe) sst.WaitForRunningAndNotReady(*ss.Spec.Replicas, ss) - sst.WaitForStatus(ss, 0) + sst.WaitForStatusReadyReplicas(ss, 0) sst.UpdateReplicas(ss, 3) sst.ConfirmStatefulPodCount(3, ss, 10*time.Second, false) @@ -452,7 +396,7 @@ var _ = framework.KubeDescribe("StatefulSet", func() { By("Scale down will not halt with unhealthy stateful pod") sst.BreakProbe(ss, testProbe) - sst.WaitForStatus(ss, 0) + sst.WaitForStatusReadyReplicas(ss, 0) sst.WaitForRunningAndNotReady(3, ss) sst.UpdateReplicas(ss, 0) sst.ConfirmStatefulPodCount(0, ss, 10*time.Second, false) @@ -460,7 +404,7 @@ var _ = framework.KubeDescribe("StatefulSet", func() { By("Scaling down stateful set " + ssName + " to 0 replicas and waiting until none of pods will run in namespace" + ns) sst.RestoreProbe(ss, testProbe) sst.Scale(ss, 0) - sst.WaitForStatus(ss, 0) + sst.WaitForStatusReadyReplicas(ss, 0) }) It("Should recreate evicted statefulset", func() {