From 8275e8f017008e94cf187480536b01e97e3a7d21 Mon Sep 17 00:00:00 2001 From: Janet Kuo Date: Tue, 16 May 2017 12:50:15 -0700 Subject: [PATCH 1/5] Update DaemonSet API for rollback and history 1. Add revisionHistoryLimit (default 10), collisionCount, and validation code 2. Add daemonset-controller-hash label, and deprecate templateGeneration --- pkg/api/testing/fuzzer.go | 5 +++++ pkg/apis/extensions/types.go | 21 +++++++++++++++++- pkg/apis/extensions/v1beta1/defaults.go | 4 ++++ pkg/apis/extensions/v1beta1/defaults_test.go | 22 ++++++------------- pkg/apis/extensions/v1beta1/types.go | 21 +++++++++++++++++- .../v1beta1/zz_generated.conversion.go | 3 ++- .../v1beta1/zz_generated.deepcopy.go | 3 ++- pkg/apis/extensions/validation/validation.go | 14 ++++++++++++ pkg/apis/extensions/zz_generated.deepcopy.go | 3 ++- 9 files changed, 76 insertions(+), 20 deletions(-) diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index e3884e42e85..cccd950ba15 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -553,6 +553,11 @@ func extensionFuncs(t apitesting.TestingCommon) []interface{} { } } }, + func(j *extensions.DaemonSetSpec, c fuzz.Continue) { + c.FuzzNoCustom(j) // fuzz self without calling this function again + rhl := int32(c.Rand.Int31()) + j.RevisionHistoryLimit = &rhl + }, func(j *extensions.DaemonSetUpdateStrategy, c fuzz.Continue) { c.FuzzNoCustom(j) // fuzz self without calling this function again // Ensure that strategyType is one of valid values. diff --git a/pkg/apis/extensions/types.go b/pkg/apis/extensions/types.go index 40442bce9d6..f57ae557cf0 100644 --- a/pkg/apis/extensions/types.go +++ b/pkg/apis/extensions/types.go @@ -386,7 +386,7 @@ type DaemonSetUpdateStrategy struct { // Rolling update config params. Present only if type = "RollingUpdate". //--- // TODO: Update this to follow our convention for oneOf, whatever we decide it - // to be. Same as DeploymentStrategy.RollingUpdate. + // to be. Same as Deployment `strategy.rollingUpdate`. // See https://github.com/kubernetes/kubernetes/issues/35345 // +optional RollingUpdate *RollingUpdateDaemonSet @@ -449,10 +449,17 @@ type DaemonSetSpec struct { // +optional MinReadySeconds int32 + // DEPRECATED. // A sequence number representing a specific generation of the template. // Populated by the system. It can be set only during the creation. // +optional TemplateGeneration int64 + + // The number of old history to retain to allow rollback. + // This is a pointer to distinguish between explicit zero and not specified. + // Defaults to 10. + // +optional + RevisionHistoryLimit *int32 } // DaemonSetStatus represents the current status of a daemon set. @@ -492,6 +499,12 @@ type DaemonSetStatus struct { // (ready for at least spec.minReadySeconds) // +optional NumberUnavailable int32 + + // Count of hash collisions for the DaemonSet. The DaemonSet controller + // uses this field as a collision avoidance mechanism when it needs to + // create the name for the newest ControllerRevision. + // +optional + CollisionCount *int64 } // +genclient=true @@ -519,10 +532,16 @@ type DaemonSet struct { } const ( + // DEPRECATED: DefaultDaemonSetUniqueLabelKey is used instead. // DaemonSetTemplateGenerationKey is the key of the labels that is added // to daemon set pods to distinguish between old and new pod templates // during DaemonSet template update. DaemonSetTemplateGenerationKey string = "pod-template-generation" + + // DefaultDaemonSetUniqueLabelKey is the default label key that is added + // to existing DaemonSet pods to distinguish between old and new + // DaemonSet pods during DaemonSet template updates. + DefaultDaemonSetUniqueLabelKey string = "daemonset-controller-hash" ) // DaemonSetList is a collection of daemon sets. diff --git a/pkg/apis/extensions/v1beta1/defaults.go b/pkg/apis/extensions/v1beta1/defaults.go index 06186652331..083db1238f6 100644 --- a/pkg/apis/extensions/v1beta1/defaults.go +++ b/pkg/apis/extensions/v1beta1/defaults.go @@ -56,6 +56,10 @@ func SetDefaults_DaemonSet(obj *DaemonSet) { updateStrategy.RollingUpdate.MaxUnavailable = &maxUnavailable } } + if obj.Spec.RevisionHistoryLimit == nil { + obj.Spec.RevisionHistoryLimit = new(int32) + *obj.Spec.RevisionHistoryLimit = 10 + } } func SetDefaults_Deployment(obj *Deployment) { diff --git a/pkg/apis/extensions/v1beta1/defaults_test.go b/pkg/apis/extensions/v1beta1/defaults_test.go index c6b840bbd2c..46be0c2c99c 100644 --- a/pkg/apis/extensions/v1beta1/defaults_test.go +++ b/pkg/apis/extensions/v1beta1/defaults_test.go @@ -32,7 +32,7 @@ import ( . "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" ) -func TestSetDefaultDaemonSet(t *testing.T) { +func TestSetDefaultDaemonSetSpec(t *testing.T) { defaultLabels := map[string]string{"foo": "bar"} period := int64(v1.DefaultTerminationGracePeriodSeconds) defaultTemplate := v1.PodTemplateSpec{ @@ -78,6 +78,7 @@ func TestSetDefaultDaemonSet(t *testing.T) { UpdateStrategy: DaemonSetUpdateStrategy{ Type: OnDeleteDaemonSetStrategyType, }, + RevisionHistoryLimit: newInt32(10), }, }, }, @@ -89,7 +90,8 @@ func TestSetDefaultDaemonSet(t *testing.T) { }, }, Spec: DaemonSetSpec{ - Template: defaultTemplate, + Template: defaultTemplate, + RevisionHistoryLimit: newInt32(1), }, }, expected: &DaemonSet{ @@ -106,6 +108,7 @@ func TestSetDefaultDaemonSet(t *testing.T) { UpdateStrategy: DaemonSetUpdateStrategy{ Type: OnDeleteDaemonSetStrategyType, }, + RevisionHistoryLimit: newInt32(1), }, }, }, @@ -117,19 +120,7 @@ func TestSetDefaultDaemonSet(t *testing.T) { UpdateStrategy: DaemonSetUpdateStrategy{ Type: OnDeleteDaemonSetStrategyType, }, - }, - }, - }, - { // Update strategy. - original: &DaemonSet{ - Spec: DaemonSetSpec{}, - }, - expected: &DaemonSet{ - Spec: DaemonSetSpec{ - Template: templateNoLabel, - UpdateStrategy: DaemonSetUpdateStrategy{ - Type: OnDeleteDaemonSetStrategyType, - }, + RevisionHistoryLimit: newInt32(10), }, }, }, @@ -143,6 +134,7 @@ func TestSetDefaultDaemonSet(t *testing.T) { UpdateStrategy: DaemonSetUpdateStrategy{ Type: OnDeleteDaemonSetStrategyType, }, + RevisionHistoryLimit: newInt32(10), }, }, }, diff --git a/pkg/apis/extensions/v1beta1/types.go b/pkg/apis/extensions/v1beta1/types.go index a97bb8427ca..e750f560635 100644 --- a/pkg/apis/extensions/v1beta1/types.go +++ b/pkg/apis/extensions/v1beta1/types.go @@ -386,7 +386,7 @@ type DaemonSetUpdateStrategy struct { // Rolling update config params. Present only if type = "RollingUpdate". //--- // TODO: Update this to follow our convention for oneOf, whatever we decide it - // to be. Same as DeploymentStrategy.RollingUpdate. + // to be. Same as Deployment `strategy.rollingUpdate`. // See https://github.com/kubernetes/kubernetes/issues/35345 // +optional RollingUpdate *RollingUpdateDaemonSet `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"` @@ -449,10 +449,17 @@ type DaemonSetSpec struct { // +optional MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,4,opt,name=minReadySeconds"` + // DEPRECATED. // A sequence number representing a specific generation of the template. // Populated by the system. It can be set only during the creation. // +optional TemplateGeneration int64 `json:"templateGeneration,omitempty" protobuf:"varint,5,opt,name=templateGeneration"` + + // The number of old history to retain to allow rollback. + // This is a pointer to distinguish between explicit zero and not specified. + // Defaults to 10. + // +optional + RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"` } // DaemonSetStatus represents the current status of a daemon set. @@ -495,6 +502,12 @@ type DaemonSetStatus struct { // (ready for at least spec.minReadySeconds) // +optional NumberUnavailable int32 `json:"numberUnavailable,omitempty" protobuf:"varint,8,opt,name=numberUnavailable"` + + // Count of hash collisions for the DaemonSet. The DaemonSet controller + // uses this field as a collision avoidance mechanism when it needs to + // create the name for the newest ControllerRevision. + // +optional + CollisionCount *int64 `json:"collisionCount,omitempty" protobuf:"varint,9,opt,name=collisionCount"` } // +genclient=true @@ -522,10 +535,16 @@ type DaemonSet struct { } const ( + // DEPRECATED: DefaultDaemonSetUniqueLabelKey is used instead. // DaemonSetTemplateGenerationKey is the key of the labels that is added // to daemon set pods to distinguish between old and new pod templates // during DaemonSet template update. DaemonSetTemplateGenerationKey string = "pod-template-generation" + + // DefaultDaemonSetUniqueLabelKey is the default label key that is added + // to existing DaemonSet pods to distinguish between old and new + // DaemonSet pods during DaemonSet template updates. + DefaultDaemonSetUniqueLabelKey string = "daemonset-controller-hash" ) // DaemonSetList is a collection of daemon sets. diff --git a/pkg/apis/extensions/v1beta1/zz_generated.conversion.go b/pkg/apis/extensions/v1beta1/zz_generated.conversion.go index 2fa3e94e2cd..b904ec7a5f0 100644 --- a/pkg/apis/extensions/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/extensions/v1beta1/zz_generated.conversion.go @@ -21,6 +21,8 @@ limitations under the License. package v1beta1 import ( + unsafe "unsafe" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" @@ -28,7 +30,6 @@ import ( api "k8s.io/kubernetes/pkg/api" api_v1 "k8s.io/kubernetes/pkg/api/v1" extensions "k8s.io/kubernetes/pkg/apis/extensions" - unsafe "unsafe" ) func init() { diff --git a/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go b/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go index 3d2d35f090f..cdbd523b62d 100644 --- a/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go @@ -21,12 +21,13 @@ limitations under the License. package v1beta1 import ( + reflect "reflect" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" intstr "k8s.io/apimachinery/pkg/util/intstr" api_v1 "k8s.io/kubernetes/pkg/api/v1" - reflect "reflect" ) func init() { diff --git a/pkg/apis/extensions/validation/validation.go b/pkg/apis/extensions/validation/validation.go index 514faa2adbc..4be86af92dd 100644 --- a/pkg/apis/extensions/validation/validation.go +++ b/pkg/apis/extensions/validation/validation.go @@ -134,6 +134,9 @@ func validateDaemonSetStatus(status *extensions.DaemonSetStatus, fldPath *field. allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.UpdatedNumberScheduled), fldPath.Child("updatedNumberScheduled"))...) allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberAvailable), fldPath.Child("numberAvailable"))...) allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.NumberUnavailable), fldPath.Child("numberUnavailable"))...) + if status.CollisionCount != nil { + allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*status.CollisionCount), fldPath.Child("collisionCount"))...) + } return allErrs } @@ -141,6 +144,13 @@ func validateDaemonSetStatus(status *extensions.DaemonSetStatus, fldPath *field. func ValidateDaemonSetStatusUpdate(ds, oldDS *extensions.DaemonSet) field.ErrorList { allErrs := apivalidation.ValidateObjectMetaUpdate(&ds.ObjectMeta, &oldDS.ObjectMeta, field.NewPath("metadata")) allErrs = append(allErrs, validateDaemonSetStatus(&ds.Status, field.NewPath("status"))...) + if isDecremented(ds.Status.CollisionCount, oldDS.Status.CollisionCount) { + value := int64(0) + if ds.Status.CollisionCount != nil { + value = *ds.Status.CollisionCount + } + allErrs = append(allErrs, field.Invalid(field.NewPath("status").Child("collisionCount"), value, "cannot be decremented")) + } return allErrs } @@ -172,6 +182,10 @@ func ValidateDaemonSetSpec(spec *extensions.DaemonSetSpec, fldPath *field.Path) allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.TemplateGeneration), fldPath.Child("templateGeneration"))...) allErrs = append(allErrs, ValidateDaemonSetUpdateStrategy(&spec.UpdateStrategy, fldPath.Child("updateStrategy"))...) + if spec.RevisionHistoryLimit != nil { + // zero is a valid RevisionHistoryLimit + allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(*spec.RevisionHistoryLimit), fldPath.Child("revisionHistoryLimit"))...) + } return allErrs } diff --git a/pkg/apis/extensions/zz_generated.deepcopy.go b/pkg/apis/extensions/zz_generated.deepcopy.go index aa2c01c6d2f..b6aa4d1dee1 100644 --- a/pkg/apis/extensions/zz_generated.deepcopy.go +++ b/pkg/apis/extensions/zz_generated.deepcopy.go @@ -21,12 +21,13 @@ limitations under the License. package extensions import ( + reflect "reflect" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" intstr "k8s.io/apimachinery/pkg/util/intstr" api "k8s.io/kubernetes/pkg/api" - reflect "reflect" ) func init() { From 4e6f70ff67ec435289984c9a162f0e75eb0180b8 Mon Sep 17 00:00:00 2001 From: Janet Kuo Date: Tue, 23 May 2017 15:20:07 -0700 Subject: [PATCH 2/5] Autogen: run hack/update-all.sh --- api/openapi-spec/swagger.json | 12 +- api/swagger-spec/extensions_v1beta1.json | 12 +- .../apps/v1beta1/definitions.html | 2 +- .../apps/v1beta1/operations.html | 2 +- .../extensions/v1beta1/definitions.html | 18 +- federation/apis/openapi-spec/swagger.json | 12 +- .../apis/swagger-spec/extensions_v1beta1.json | 12 +- .../extensions/v1beta1/definitions.html | 18 +- pkg/apis/extensions/v1beta1/generated.pb.go | 489 ++++++++++-------- pkg/apis/extensions/v1beta1/generated.proto | 15 +- .../extensions/v1beta1/types.generated.go | 454 ++++++++++------ pkg/apis/extensions/v1beta1/types.go | 2 +- .../v1beta1/types_swagger_doc_generated.go | 14 +- .../v1beta1/zz_generated.conversion.go | 7 +- .../v1beta1/zz_generated.deepcopy.go | 16 +- pkg/apis/extensions/zz_generated.deepcopy.go | 16 +- pkg/controller/daemon/BUILD | 2 + pkg/controller/deployment/util/BUILD | 2 - .../listers/extensions/v1beta1/BUILD | 1 + .../extensions/v1beta1/daemonset_expansion.go | 36 ++ .../client-go/pkg/apis/extensions/types.go | 21 +- .../pkg/apis/extensions/v1beta1/defaults.go | 4 + .../apis/extensions/v1beta1/generated.pb.go | 489 ++++++++++-------- .../apis/extensions/v1beta1/generated.proto | 15 +- .../extensions/v1beta1/types.generated.go | 454 ++++++++++------ .../pkg/apis/extensions/v1beta1/types.go | 21 +- .../v1beta1/types_swagger_doc_generated.go | 14 +- .../v1beta1/zz_generated.conversion.go | 4 + .../v1beta1/zz_generated.deepcopy.go | 13 + .../apis/extensions/zz_generated.deepcopy.go | 13 + 30 files changed, 1427 insertions(+), 763 deletions(-) diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index 6876d07a009..3a9b8d4a380 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -52564,6 +52564,11 @@ "type": "integer", "format": "int32" }, + "revisionHistoryLimit": { + "description": "The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", + "type": "integer", + "format": "int32" + }, "selector": { "description": "A label query over pods that are managed by the daemon set. Must match in order to be controlled. If empty, defaulted to labels on 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" @@ -52573,7 +52578,7 @@ "$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.PodTemplateSpec" }, "templateGeneration": { - "description": "A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.", + "description": "DEPRECATED. A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.", "type": "integer", "format": "int64" }, @@ -52592,6 +52597,11 @@ "numberReady" ], "properties": { + "collisionCount": { + "description": "Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", + "type": "integer", + "format": "int64" + }, "currentNumberScheduled": { "description": "The number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod. More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", "type": "integer", diff --git a/api/swagger-spec/extensions_v1beta1.json b/api/swagger-spec/extensions_v1beta1.json index 9cf8f7cabe5..41eca7e1ebb 100644 --- a/api/swagger-spec/extensions_v1beta1.json +++ b/api/swagger-spec/extensions_v1beta1.json @@ -7117,7 +7117,12 @@ "templateGeneration": { "type": "integer", "format": "int64", - "description": "A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation." + "description": "DEPRECATED. A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation." + }, + "revisionHistoryLimit": { + "type": "integer", + "format": "int32", + "description": "The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10." } } }, @@ -9159,6 +9164,11 @@ "type": "integer", "format": "int32", "description": "The number of nodes that should be running the daemon pod and have none of the daemon pod running and available (ready for at least spec.minReadySeconds)" + }, + "collisionCount": { + "type": "integer", + "format": "int64", + "description": "Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision." } } }, diff --git a/docs/api-reference/apps/v1beta1/definitions.html b/docs/api-reference/apps/v1beta1/definitions.html index 3d065820b2a..e98d17d7384 100755 --- a/docs/api-reference/apps/v1beta1/definitions.html +++ b/docs/api-reference/apps/v1beta1/definitions.html @@ -6607,7 +6607,7 @@ Examples:
diff --git a/docs/api-reference/apps/v1beta1/operations.html b/docs/api-reference/apps/v1beta1/operations.html index 06e8b35e0f2..a6e64a6a715 100755 --- a/docs/api-reference/apps/v1beta1/operations.html +++ b/docs/api-reference/apps/v1beta1/operations.html @@ -6634,7 +6634,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; } diff --git a/docs/api-reference/extensions/v1beta1/definitions.html b/docs/api-reference/extensions/v1beta1/definitions.html index 986de03ebce..8ad2ea99852 100755 --- a/docs/api-reference/extensions/v1beta1/definitions.html +++ b/docs/api-reference/extensions/v1beta1/definitions.html @@ -689,6 +689,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }

integer (int32)

+ +

collisionCount

+

Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.

+

false

+

integer (int64)

+ + @@ -2362,11 +2369,18 @@ When an object is created, the system will populate this list with the current s

templateGeneration

-

A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.

+

DEPRECATED. A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.

false

integer (int64)

+ +

revisionHistoryLimit

+

The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.

+

false

+

integer (int32)

+ + @@ -8134,7 +8148,7 @@ Both these may change in the future. Incoming requests are matched against the h diff --git a/federation/apis/openapi-spec/swagger.json b/federation/apis/openapi-spec/swagger.json index c2d8c6187d6..95a565d1a64 100644 --- a/federation/apis/openapi-spec/swagger.json +++ b/federation/apis/openapi-spec/swagger.json @@ -12964,6 +12964,11 @@ "type": "integer", "format": "int32" }, + "revisionHistoryLimit": { + "description": "The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", + "type": "integer", + "format": "int32" + }, "selector": { "description": "A label query over pods that are managed by the daemon set. Must match in order to be controlled. If empty, defaulted to labels on 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" @@ -12973,7 +12978,7 @@ "$ref": "#/definitions/io.k8s.kubernetes.pkg.api.v1.PodTemplateSpec" }, "templateGeneration": { - "description": "A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.", + "description": "DEPRECATED. A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.", "type": "integer", "format": "int64" }, @@ -12992,6 +12997,11 @@ "numberReady" ], "properties": { + "collisionCount": { + "description": "Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", + "type": "integer", + "format": "int64" + }, "currentNumberScheduled": { "description": "The number of nodes that are running at least 1 daemon pod and are supposed to run the daemon pod. More info: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/", "type": "integer", diff --git a/federation/apis/swagger-spec/extensions_v1beta1.json b/federation/apis/swagger-spec/extensions_v1beta1.json index 98582acdc0c..41a4c4bfc75 100644 --- a/federation/apis/swagger-spec/extensions_v1beta1.json +++ b/federation/apis/swagger-spec/extensions_v1beta1.json @@ -4861,7 +4861,12 @@ "templateGeneration": { "type": "integer", "format": "int64", - "description": "A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation." + "description": "DEPRECATED. A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation." + }, + "revisionHistoryLimit": { + "type": "integer", + "format": "int32", + "description": "The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10." } } }, @@ -6903,6 +6908,11 @@ "type": "integer", "format": "int32", "description": "The number of nodes that should be running the daemon pod and have none of the daemon pod running and available (ready for at least spec.minReadySeconds)" + }, + "collisionCount": { + "type": "integer", + "format": "int64", + "description": "Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision." } } }, diff --git a/federation/docs/api-reference/extensions/v1beta1/definitions.html b/federation/docs/api-reference/extensions/v1beta1/definitions.html index b15e8bd661c..5611455c551 100755 --- a/federation/docs/api-reference/extensions/v1beta1/definitions.html +++ b/federation/docs/api-reference/extensions/v1beta1/definitions.html @@ -671,6 +671,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }

integer (int32)

+ +

collisionCount

+

Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.

+

false

+

integer (int64)

+ + @@ -2193,11 +2200,18 @@ When an object is created, the system will populate this list with the current s

templateGeneration

-

A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.

+

DEPRECATED. A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.

false

integer (int64)

+ +

revisionHistoryLimit

+

The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.

+

false

+

integer (int32)

+ + @@ -7204,7 +7218,7 @@ Both these may change in the future. Incoming requests are matched against the h diff --git a/pkg/apis/extensions/v1beta1/generated.pb.go b/pkg/apis/extensions/v1beta1/generated.pb.go index f2397c0bc0d..c8c11725390 100644 --- a/pkg/apis/extensions/v1beta1/generated.pb.go +++ b/pkg/apis/extensions/v1beta1/generated.pb.go @@ -689,6 +689,11 @@ func (m *DaemonSetSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x28 i++ i = encodeVarintGenerated(dAtA, i, uint64(m.TemplateGeneration)) + if m.RevisionHistoryLimit != nil { + dAtA[i] = 0x30 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(*m.RevisionHistoryLimit)) + } return i, nil } @@ -731,6 +736,11 @@ func (m *DaemonSetStatus) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x40 i++ i = encodeVarintGenerated(dAtA, i, uint64(m.NumberUnavailable)) + if m.CollisionCount != nil { + dAtA[i] = 0x48 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(*m.CollisionCount)) + } return i, nil } @@ -2795,6 +2805,9 @@ func (m *DaemonSetSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) n += 1 + sovGenerated(uint64(m.MinReadySeconds)) n += 1 + sovGenerated(uint64(m.TemplateGeneration)) + if m.RevisionHistoryLimit != nil { + n += 1 + sovGenerated(uint64(*m.RevisionHistoryLimit)) + } return n } @@ -2809,6 +2822,9 @@ func (m *DaemonSetStatus) Size() (n int) { n += 1 + sovGenerated(uint64(m.UpdatedNumberScheduled)) n += 1 + sovGenerated(uint64(m.NumberAvailable)) n += 1 + sovGenerated(uint64(m.NumberUnavailable)) + if m.CollisionCount != nil { + n += 1 + sovGenerated(uint64(*m.CollisionCount)) + } return n } @@ -3605,6 +3621,7 @@ func (this *DaemonSetSpec) String() string { `UpdateStrategy:` + strings.Replace(strings.Replace(this.UpdateStrategy.String(), "DaemonSetUpdateStrategy", "DaemonSetUpdateStrategy", 1), `&`, ``, 1) + `,`, `MinReadySeconds:` + fmt.Sprintf("%v", this.MinReadySeconds) + `,`, `TemplateGeneration:` + fmt.Sprintf("%v", this.TemplateGeneration) + `,`, + `RevisionHistoryLimit:` + valueToStringGenerated(this.RevisionHistoryLimit) + `,`, `}`, }, "") return s @@ -3622,6 +3639,7 @@ func (this *DaemonSetStatus) String() string { `UpdatedNumberScheduled:` + fmt.Sprintf("%v", this.UpdatedNumberScheduled) + `,`, `NumberAvailable:` + fmt.Sprintf("%v", this.NumberAvailable) + `,`, `NumberUnavailable:` + fmt.Sprintf("%v", this.NumberUnavailable) + `,`, + `CollisionCount:` + valueToStringGenerated(this.CollisionCount) + `,`, `}`, }, "") return s @@ -5102,6 +5120,26 @@ func (m *DaemonSetSpec) Unmarshal(dAtA []byte) error { break } } + case 6: + 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:]) @@ -5304,6 +5342,26 @@ func (m *DaemonSetStatus) Unmarshal(dAtA []byte) error { break } } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CollisionCount", wireType) + } + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.CollisionCount = &v default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -11873,219 +11931,220 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 3412 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5b, 0xdd, 0x6f, 0x1c, 0x57, - 0x15, 0xcf, 0x78, 0xbd, 0xf1, 0xfa, 0x6c, 0x6c, 0xc7, 0xd7, 0xae, 0xb3, 0x75, 0x5b, 0x3b, 0x1d, - 0x44, 0x9b, 0x42, 0xbb, 0xa6, 0x29, 0x29, 0x6d, 0xda, 0xa6, 0xf5, 0xda, 0xf9, 0x30, 0xb2, 0x9d, - 0xed, 0xdd, 0x75, 0x44, 0xd3, 0x2f, 0xc6, 0xbb, 0xd7, 0xeb, 0x49, 0xe6, 0xab, 0x33, 0x77, 0x5c, - 0xaf, 0x2a, 0xa0, 0x12, 0xa2, 0x0f, 0x3c, 0x20, 0x78, 0x41, 0x45, 0x82, 0x47, 0x1e, 0x78, 0x81, - 0xf6, 0x01, 0x0a, 0x7f, 0x01, 0x79, 0x40, 0xa8, 0x48, 0x20, 0x55, 0xa8, 0x58, 0xc4, 0x15, 0xfd, - 0x07, 0xa0, 0x2f, 0x79, 0x42, 0xf7, 0xce, 0x9d, 0xef, 0x19, 0x27, 0xbb, 0x4e, 0x56, 0x48, 0xbc, - 0xed, 0xdc, 0x7b, 0xce, 0xef, 0x7c, 0xdc, 0x73, 0xcf, 0x3d, 0xf7, 0x63, 0xe1, 0x85, 0xeb, 0xcf, - 0x38, 0x55, 0xd5, 0x5c, 0xb8, 0xee, 0x6e, 0x12, 0xdb, 0x20, 0x94, 0x38, 0x0b, 0xd6, 0xf5, 0xce, - 0x82, 0x62, 0xa9, 0xce, 0x02, 0xd9, 0xa5, 0xc4, 0x70, 0x54, 0xd3, 0x70, 0x16, 0x76, 0x9e, 0xdc, - 0x24, 0x54, 0x79, 0x72, 0xa1, 0x43, 0x0c, 0x62, 0x2b, 0x94, 0xb4, 0xab, 0x96, 0x6d, 0x52, 0x13, - 0x3d, 0xe1, 0xb1, 0x57, 0x43, 0xf6, 0xaa, 0x75, 0xbd, 0x53, 0x65, 0xec, 0xd5, 0x90, 0xbd, 0x2a, - 0xd8, 0x67, 0x9f, 0xe8, 0xa8, 0x74, 0xdb, 0xdd, 0xac, 0xb6, 0x4c, 0x7d, 0xa1, 0x63, 0x76, 0xcc, - 0x05, 0x8e, 0xb2, 0xe9, 0x6e, 0xf1, 0x2f, 0xfe, 0xc1, 0x7f, 0x79, 0xe8, 0xb3, 0x5f, 0x17, 0xca, - 0x29, 0x96, 0xaa, 0x2b, 0xad, 0x6d, 0xd5, 0x20, 0x76, 0xd7, 0x57, 0x6f, 0xc1, 0x26, 0x8e, 0xe9, - 0xda, 0x2d, 0x92, 0xd4, 0xe9, 0x40, 0x2e, 0x67, 0x41, 0x27, 0x54, 0x59, 0xd8, 0x49, 0x59, 0x32, - 0xbb, 0x90, 0xc7, 0x65, 0xbb, 0x06, 0x55, 0xf5, 0xb4, 0x98, 0xa7, 0x6f, 0xc7, 0xe0, 0xb4, 0xb6, - 0x89, 0xae, 0xa4, 0xf8, 0x9e, 0xca, 0xe3, 0x73, 0xa9, 0xaa, 0x2d, 0xa8, 0x06, 0x75, 0xa8, 0x9d, - 0x62, 0x7a, 0x3c, 0x77, 0x98, 0xb2, 0x6c, 0x79, 0xf6, 0x80, 0x41, 0xb5, 0x4c, 0x4d, 0x6d, 0x75, - 0xf3, 0x06, 0x54, 0xae, 0x02, 0x2c, 0xd6, 0x57, 0xae, 0x10, 0x9b, 0x0d, 0x1c, 0x3a, 0x09, 0xc3, - 0x86, 0xa2, 0x93, 0x8a, 0x74, 0x52, 0x3a, 0x35, 0x5a, 0x3b, 0x76, 0x63, 0x6f, 0xfe, 0xc8, 0xfe, - 0xde, 0xfc, 0xf0, 0xba, 0xa2, 0x13, 0xcc, 0x7b, 0xe4, 0x9f, 0x49, 0x70, 0xff, 0x92, 0xeb, 0x50, - 0x53, 0x5f, 0x23, 0xd4, 0x56, 0x5b, 0x4b, 0xae, 0x6d, 0x13, 0x83, 0x36, 0xa8, 0x42, 0x5d, 0xe7, - 0xf6, 0xfc, 0xe8, 0x2a, 0x14, 0x77, 0x14, 0xcd, 0x25, 0x95, 0xa1, 0x93, 0xd2, 0xa9, 0xf2, 0xe9, - 0x6a, 0x55, 0x04, 0x54, 0xd4, 0x3b, 0x7e, 0x48, 0x55, 0xfd, 0x21, 0xaf, 0xbe, 0xec, 0x2a, 0x06, - 0x55, 0x69, 0xb7, 0x36, 0x2d, 0x20, 0x8f, 0x09, 0xb9, 0x57, 0x18, 0x16, 0xf6, 0x20, 0xe5, 0x1f, - 0x49, 0xf0, 0x50, 0xae, 0x6e, 0xab, 0xaa, 0x43, 0x91, 0x0e, 0x45, 0x95, 0x12, 0xdd, 0xa9, 0x48, - 0x27, 0x0b, 0xa7, 0xca, 0xa7, 0x2f, 0x55, 0x7b, 0x0a, 0xe7, 0x6a, 0x2e, 0x78, 0x6d, 0x4c, 0xe8, - 0x55, 0x5c, 0x61, 0xf0, 0xd8, 0x93, 0x22, 0xff, 0x44, 0x02, 0x14, 0xe5, 0x69, 0x2a, 0x76, 0x87, - 0xd0, 0x3b, 0xf0, 0xd2, 0x2b, 0x87, 0xf3, 0xd2, 0x94, 0x80, 0x2c, 0x7b, 0x02, 0x63, 0x4e, 0x7a, - 0x57, 0x82, 0x99, 0xb4, 0x4e, 0xdc, 0x3b, 0x5b, 0x71, 0xef, 0x2c, 0x1e, 0xc2, 0x3b, 0x1e, 0x6a, - 0x8e, 0x5b, 0x7e, 0x3b, 0x04, 0xa3, 0xcb, 0x0a, 0xd1, 0x4d, 0xa3, 0x41, 0x28, 0xfa, 0x36, 0x94, - 0xd8, 0x1c, 0x6d, 0x2b, 0x54, 0xe1, 0x1e, 0x29, 0x9f, 0xfe, 0xda, 0x41, 0xe6, 0x3a, 0x55, 0x46, - 0x5d, 0xdd, 0x79, 0xb2, 0x7a, 0x79, 0xf3, 0x1a, 0x69, 0xd1, 0x35, 0x42, 0x95, 0x1a, 0x12, 0x72, - 0x20, 0x6c, 0xc3, 0x01, 0x2a, 0x7a, 0x03, 0x86, 0x1d, 0x8b, 0xb4, 0x84, 0x33, 0x9f, 0xef, 0xd1, - 0xac, 0x40, 0xd3, 0x86, 0x45, 0x5a, 0xe1, 0x68, 0xb1, 0x2f, 0xcc, 0x71, 0xd1, 0x16, 0x1c, 0x75, - 0x78, 0x18, 0x54, 0x0a, 0x5c, 0xc2, 0xb9, 0xbe, 0x25, 0x78, 0xc1, 0x34, 0x2e, 0x64, 0x1c, 0xf5, - 0xbe, 0xb1, 0x40, 0x97, 0xff, 0x24, 0xc1, 0x58, 0x40, 0xcb, 0x47, 0xec, 0xb5, 0x94, 0xef, 0xaa, - 0x77, 0xe6, 0x3b, 0xc6, 0xcd, 0x3d, 0x77, 0x5c, 0xc8, 0x2a, 0xf9, 0x2d, 0x11, 0xbf, 0xbd, 0xee, - 0xc7, 0xc3, 0x10, 0x8f, 0x87, 0x67, 0xfa, 0x35, 0x2b, 0x27, 0x0c, 0x3e, 0x2b, 0x44, 0xcc, 0x61, - 0xee, 0x44, 0xaf, 0x43, 0xc9, 0x21, 0x1a, 0x69, 0x51, 0xd3, 0x16, 0xe6, 0x3c, 0x75, 0x87, 0xe6, - 0x28, 0x9b, 0x44, 0x6b, 0x08, 0xd6, 0xda, 0x31, 0x66, 0x8f, 0xff, 0x85, 0x03, 0x48, 0xf4, 0x2a, - 0x94, 0x28, 0xd1, 0x2d, 0x4d, 0xa1, 0xfe, 0xc4, 0x7a, 0x22, 0xdf, 0x24, 0x06, 0x5b, 0x37, 0xdb, - 0x4d, 0xc1, 0xc0, 0x07, 0x3f, 0x70, 0x96, 0xdf, 0x8a, 0x03, 0x40, 0xf4, 0x9e, 0x04, 0xe3, 0xae, - 0xd5, 0x66, 0xa4, 0x94, 0x25, 0xd8, 0x4e, 0x57, 0x44, 0xc3, 0x85, 0x7e, 0xdd, 0xb6, 0x11, 0x43, - 0xab, 0xcd, 0x08, 0xe1, 0xe3, 0xf1, 0x76, 0x9c, 0x90, 0x8a, 0x16, 0x61, 0x42, 0x57, 0x0d, 0x4c, - 0x94, 0x76, 0xb7, 0x41, 0x5a, 0xa6, 0xd1, 0x76, 0x2a, 0xc3, 0x27, 0xa5, 0x53, 0xc5, 0xda, 0x09, - 0x01, 0x30, 0xb1, 0x16, 0xef, 0xc6, 0x49, 0x7a, 0xf4, 0x4d, 0x40, 0xbe, 0x5d, 0x17, 0xbd, 0xf5, - 0x42, 0x35, 0x8d, 0x4a, 0xf1, 0xa4, 0x74, 0xaa, 0x50, 0x9b, 0x15, 0x28, 0xa8, 0x99, 0xa2, 0xc0, - 0x19, 0x5c, 0xf2, 0x7f, 0x86, 0x61, 0x22, 0x11, 0xe0, 0xe8, 0x0a, 0xcc, 0xb4, 0xbc, 0xf4, 0xb9, - 0xee, 0xea, 0x9b, 0xc4, 0x6e, 0xb4, 0xb6, 0x49, 0xdb, 0xd5, 0x48, 0x9b, 0x8f, 0x7a, 0xb1, 0x36, - 0x27, 0x64, 0xcc, 0x2c, 0x65, 0x52, 0xe1, 0x1c, 0x6e, 0xa6, 0xb7, 0xc1, 0x9b, 0xd6, 0x54, 0xc7, - 0x09, 0x30, 0x87, 0x38, 0x66, 0xa0, 0xf7, 0x7a, 0x8a, 0x02, 0x67, 0x70, 0x31, 0x1d, 0xdb, 0xc4, - 0x51, 0x6d, 0xd2, 0x4e, 0xea, 0x58, 0x88, 0xeb, 0xb8, 0x9c, 0x49, 0x85, 0x73, 0xb8, 0xd1, 0x19, - 0x28, 0x7b, 0xd2, 0xb8, 0xc7, 0xc5, 0xd0, 0x04, 0x09, 0x7b, 0x3d, 0xec, 0xc2, 0x51, 0x3a, 0x66, - 0x9a, 0xb9, 0xe9, 0x10, 0x7b, 0x87, 0xb4, 0xf3, 0x87, 0xe4, 0x72, 0x8a, 0x02, 0x67, 0x70, 0x31, - 0xd3, 0xbc, 0x98, 0x49, 0x99, 0x76, 0x34, 0x6e, 0xda, 0x46, 0x26, 0x15, 0xce, 0xe1, 0x66, 0x91, - 0xe7, 0xa9, 0xbc, 0xb8, 0xa3, 0xa8, 0x9a, 0xb2, 0xa9, 0x91, 0xca, 0x48, 0x3c, 0xf2, 0xd6, 0xe3, - 0xdd, 0x38, 0x49, 0x8f, 0x2e, 0xc2, 0xa4, 0xd7, 0xb4, 0x61, 0x28, 0x01, 0x48, 0x89, 0x83, 0xdc, - 0x2f, 0x40, 0x26, 0xd7, 0x93, 0x04, 0x38, 0xcd, 0x23, 0xff, 0x5d, 0x82, 0x13, 0x39, 0x33, 0x09, - 0xbd, 0x08, 0xc3, 0xb4, 0x6b, 0xf9, 0xeb, 0xef, 0x57, 0xfd, 0x8c, 0xde, 0xec, 0x5a, 0xe4, 0xd6, - 0xde, 0xfc, 0x03, 0x39, 0x6c, 0xac, 0x1b, 0x73, 0x46, 0xf4, 0x5d, 0x18, 0xb3, 0x4d, 0x4d, 0x53, - 0x8d, 0x8e, 0x47, 0x22, 0xb2, 0xc9, 0xf9, 0x1e, 0x67, 0x3a, 0x8e, 0x62, 0x84, 0xd9, 0x72, 0x72, - 0x7f, 0x6f, 0x7e, 0x2c, 0xd6, 0x87, 0xe3, 0xe2, 0xe4, 0xdf, 0x0f, 0x01, 0x2c, 0x13, 0x4b, 0x33, - 0xbb, 0x3a, 0x31, 0x06, 0xb1, 0x82, 0xbe, 0x19, 0x5b, 0x41, 0x5f, 0xe8, 0x35, 0xa3, 0x05, 0xaa, - 0xe6, 0x2e, 0xa1, 0x9d, 0xc4, 0x12, 0xfa, 0x62, 0xff, 0x22, 0x0e, 0x5e, 0x43, 0x6f, 0x16, 0x60, - 0x2a, 0x24, 0x5e, 0x32, 0x8d, 0xb6, 0xca, 0xe7, 0xc4, 0x73, 0xb1, 0x98, 0x78, 0x34, 0x11, 0x13, - 0x27, 0x32, 0x58, 0x22, 0xf1, 0x70, 0x25, 0xd0, 0x7e, 0x88, 0xb3, 0x9f, 0x8b, 0x0b, 0xbf, 0xb5, - 0x37, 0x7f, 0x60, 0x3d, 0x5f, 0x0d, 0x30, 0xe3, 0xca, 0xa2, 0x47, 0xe0, 0xa8, 0x4d, 0x14, 0xc7, - 0x34, 0x78, 0x9a, 0x18, 0x0d, 0x8d, 0xc2, 0xbc, 0x15, 0x8b, 0x5e, 0xf4, 0x18, 0x8c, 0xe8, 0xc4, - 0x71, 0x94, 0x0e, 0xe1, 0x19, 0x61, 0xb4, 0x36, 0x21, 0x08, 0x47, 0xd6, 0xbc, 0x66, 0xec, 0xf7, - 0xa3, 0x6b, 0x30, 0xae, 0x29, 0x8e, 0x08, 0xed, 0xa6, 0xaa, 0x13, 0x3e, 0xe7, 0xcb, 0xa7, 0xbf, - 0x72, 0x67, 0x11, 0xc3, 0x38, 0xc2, 0x95, 0x68, 0x35, 0x86, 0x84, 0x13, 0xc8, 0x68, 0x07, 0x10, - 0x6b, 0x69, 0xda, 0x8a, 0xe1, 0x78, 0x2e, 0x63, 0xf2, 0x46, 0x7a, 0x96, 0x17, 0xe4, 0xb7, 0xd5, - 0x14, 0x1a, 0xce, 0x90, 0x20, 0xff, 0x59, 0x82, 0xf1, 0x70, 0xc0, 0x06, 0x50, 0x28, 0xbd, 0x11, - 0x2f, 0x94, 0x9e, 0xed, 0x3b, 0x78, 0x73, 0x2a, 0xa5, 0xf7, 0x0b, 0x80, 0x42, 0x22, 0x96, 0x1a, - 0x36, 0x95, 0xd6, 0xf5, 0x3b, 0xd8, 0x47, 0xfc, 0x52, 0x02, 0x24, 0x92, 0xf5, 0xa2, 0x61, 0x98, - 0x94, 0xe7, 0x7f, 0x5f, 0xcd, 0x57, 0xfa, 0x56, 0xd3, 0xd7, 0xa0, 0xba, 0x91, 0xc2, 0x3e, 0x6f, - 0x50, 0xbb, 0x1b, 0x8e, 0x58, 0x9a, 0x00, 0x67, 0x28, 0x84, 0xde, 0x02, 0xb0, 0x05, 0x66, 0xd3, - 0x14, 0x29, 0xe0, 0x85, 0x3e, 0xb2, 0x29, 0x03, 0x58, 0x32, 0x8d, 0x2d, 0xb5, 0x13, 0x26, 0x34, - 0x1c, 0x00, 0xe3, 0x88, 0x90, 0xd9, 0xf3, 0x70, 0x22, 0x47, 0x7b, 0x74, 0x1c, 0x0a, 0xd7, 0x49, - 0xd7, 0x73, 0x2b, 0x66, 0x3f, 0xd1, 0x74, 0x74, 0x3f, 0x36, 0x2a, 0xb6, 0x52, 0x67, 0x87, 0x9e, - 0x91, 0xe4, 0xcf, 0x8b, 0xd1, 0x58, 0xe3, 0x55, 0xec, 0x29, 0x28, 0xd9, 0xc4, 0xd2, 0xd4, 0x96, - 0xe2, 0x88, 0x7a, 0x86, 0x17, 0xa4, 0x58, 0xb4, 0xe1, 0xa0, 0x37, 0x56, 0xef, 0x0e, 0xdd, 0xdb, - 0x7a, 0xb7, 0x70, 0xb7, 0xeb, 0x5d, 0x13, 0x4a, 0x8e, 0x5f, 0xe8, 0x0e, 0x73, 0xf0, 0xc5, 0x43, - 0xe4, 0x6c, 0x51, 0xe3, 0x06, 0x02, 0x83, 0xea, 0x36, 0x10, 0x92, 0x55, 0xd7, 0x16, 0x7b, 0xac, - 0x6b, 0x57, 0x61, 0xda, 0x26, 0x3b, 0x2a, 0x53, 0xe3, 0x92, 0xea, 0x50, 0xd3, 0xee, 0xae, 0xaa, - 0xba, 0x4a, 0x45, 0xd9, 0x53, 0xd9, 0xdf, 0x9b, 0x9f, 0xc6, 0x19, 0xfd, 0x38, 0x93, 0x8b, 0x65, - 0x67, 0x4b, 0x71, 0x1d, 0xd2, 0xe6, 0x29, 0xad, 0x14, 0x66, 0xe7, 0x3a, 0x6f, 0xc5, 0xa2, 0x17, - 0xe9, 0xb1, 0xe0, 0x2e, 0xdd, 0x8d, 0xe0, 0x1e, 0xcf, 0x0f, 0x6c, 0xb4, 0x01, 0x27, 0x2c, 0xdb, - 0xec, 0xd8, 0xc4, 0x71, 0x96, 0x89, 0xd2, 0xd6, 0x54, 0x83, 0xf8, 0xfe, 0x1a, 0xe5, 0x76, 0x3e, - 0xb0, 0xbf, 0x37, 0x7f, 0xa2, 0x9e, 0x4d, 0x82, 0xf3, 0x78, 0xe5, 0x4f, 0x86, 0xe1, 0x78, 0x72, - 0x95, 0xcd, 0xa9, 0x4a, 0xa5, 0xbe, 0xaa, 0xd2, 0xc7, 0x23, 0xd3, 0xc6, 0x2b, 0xd9, 0x83, 0x68, - 0xc8, 0x98, 0x3a, 0x8b, 0x30, 0x21, 0xf2, 0x88, 0xdf, 0x29, 0xea, 0xf2, 0x20, 0x1a, 0x36, 0xe2, - 0xdd, 0x38, 0x49, 0xcf, 0x6a, 0xcd, 0xb0, 0x84, 0xf4, 0x41, 0x86, 0xe3, 0xb5, 0xe6, 0x62, 0x92, - 0x00, 0xa7, 0x79, 0xd0, 0x1a, 0x4c, 0xb9, 0x46, 0x1a, 0xca, 0x8b, 0xce, 0x07, 0x04, 0xd4, 0xd4, - 0x46, 0x9a, 0x04, 0x67, 0xf1, 0xa1, 0x1d, 0x80, 0x96, 0x5f, 0x10, 0x38, 0x95, 0xa3, 0x3c, 0x57, - 0xd7, 0xfa, 0x9e, 0x5b, 0x41, 0x6d, 0x11, 0x66, 0xc4, 0xa0, 0xc9, 0xc1, 0x11, 0x49, 0xe8, 0x39, - 0x18, 0xb3, 0xf9, 0xc6, 0xc3, 0x37, 0xc0, 0x2b, 0xde, 0xef, 0x13, 0x6c, 0x63, 0x38, 0xda, 0x89, - 0xe3, 0xb4, 0xe8, 0x2c, 0x8c, 0xb7, 0x58, 0x8d, 0xca, 0xd4, 0x58, 0x32, 0x5d, 0x83, 0xf2, 0x40, - 0x2f, 0xd4, 0x10, 0xab, 0x13, 0x96, 0x62, 0x3d, 0x38, 0x41, 0x29, 0xff, 0x45, 0x8a, 0x2e, 0x6f, - 0x41, 0x99, 0x7e, 0x36, 0x56, 0x92, 0x3d, 0x92, 0x28, 0xc9, 0x66, 0xd2, 0x1c, 0x91, 0x8a, 0xec, - 0x7b, 0xd9, 0x15, 0xfa, 0x85, 0x43, 0x55, 0xe8, 0xe1, 0x32, 0x7d, 0xfb, 0x12, 0xfd, 0x43, 0x09, - 0x66, 0x2e, 0x34, 0x2e, 0xda, 0xa6, 0x6b, 0xf9, 0xea, 0x5d, 0xb6, 0x3c, 0x3f, 0x7f, 0x03, 0x86, - 0x6d, 0x57, 0xf3, 0xed, 0xfa, 0x92, 0x6f, 0x17, 0x76, 0x35, 0x66, 0xd7, 0x54, 0x82, 0xcb, 0x33, - 0x8a, 0x31, 0xa0, 0x37, 0xe0, 0xa8, 0xad, 0x18, 0x1d, 0xe2, 0x2f, 0xe0, 0x4f, 0xf7, 0x68, 0xcd, - 0xca, 0x32, 0x66, 0xec, 0x91, 0x32, 0x92, 0xa3, 0x61, 0x81, 0x2a, 0xff, 0x42, 0x82, 0x89, 0x4b, - 0xcd, 0x66, 0x7d, 0xc5, 0xe0, 0x19, 0xa0, 0xae, 0xd0, 0x6d, 0x56, 0x63, 0x58, 0x0a, 0xdd, 0x4e, - 0xd6, 0x18, 0xac, 0x0f, 0xf3, 0x1e, 0xb4, 0x0d, 0x23, 0x2c, 0xf3, 0x10, 0xa3, 0xdd, 0xe7, 0xf6, - 0x40, 0x88, 0xab, 0x79, 0x20, 0x61, 0xed, 0x2a, 0x1a, 0xb0, 0x0f, 0x2f, 0xbf, 0x03, 0xd3, 0x11, - 0xf5, 0x98, 0xbf, 0xf8, 0xc9, 0x26, 0x6a, 0x41, 0x91, 0x69, 0xe2, 0x9f, 0x5b, 0xf6, 0x7a, 0xfc, - 0x96, 0x30, 0x39, 0xac, 0xc1, 0xd8, 0x97, 0x83, 0x3d, 0x6c, 0x79, 0x0d, 0xc6, 0x2e, 0x99, 0x0e, - 0xad, 0x9b, 0x36, 0xe5, 0x6e, 0x43, 0x0f, 0x41, 0x41, 0x57, 0x0d, 0xb1, 0xc2, 0x97, 0x05, 0x4f, - 0x81, 0xad, 0x41, 0xac, 0x9d, 0x77, 0x2b, 0xbb, 0x22, 0x93, 0x85, 0xdd, 0xca, 0x2e, 0x66, 0xed, - 0xf2, 0x45, 0x18, 0x11, 0xc3, 0x11, 0x05, 0x2a, 0x1c, 0x0c, 0x54, 0xc8, 0x00, 0xfa, 0xcd, 0x10, - 0x8c, 0x08, 0xed, 0x07, 0xb0, 0x11, 0x7c, 0x2d, 0xb6, 0x11, 0x3c, 0xdb, 0xdf, 0x48, 0xe7, 0xee, - 0x02, 0xdb, 0x89, 0x5d, 0xe0, 0xf3, 0x7d, 0xe2, 0x1f, 0xbc, 0x05, 0xfc, 0x40, 0x82, 0xf1, 0x78, - 0xcc, 0xa1, 0x33, 0x50, 0x66, 0xeb, 0x91, 0xda, 0x22, 0xeb, 0x61, 0x41, 0x1d, 0x1c, 0xca, 0x34, - 0xc2, 0x2e, 0x1c, 0xa5, 0x43, 0x9d, 0x80, 0x8d, 0x85, 0x85, 0x70, 0x4a, 0xbe, 0xcb, 0x5d, 0xaa, - 0x6a, 0x55, 0xef, 0xc2, 0xa7, 0xba, 0x62, 0xd0, 0xcb, 0x76, 0x83, 0xda, 0xaa, 0xd1, 0x49, 0x09, - 0xe2, 0x31, 0x16, 0x45, 0x96, 0x6f, 0x48, 0x50, 0x16, 0x2a, 0x0f, 0x60, 0x3b, 0xf3, 0x6a, 0x7c, - 0x3b, 0xf3, 0x74, 0x9f, 0xf3, 0x39, 0x7b, 0x2f, 0xf3, 0x51, 0x68, 0x0a, 0x9b, 0xc1, 0x2c, 0xc1, - 0x6c, 0x9b, 0x0e, 0x4d, 0x26, 0x18, 0x36, 0xd7, 0x30, 0xef, 0x41, 0x3f, 0x90, 0xe0, 0xb8, 0x9a, - 0x98, 0xf3, 0xc2, 0xd7, 0x2f, 0xf6, 0xa7, 0x5a, 0x00, 0x53, 0xab, 0x08, 0x79, 0xc7, 0x93, 0x3d, - 0x38, 0x25, 0x52, 0x76, 0x21, 0x45, 0x85, 0x14, 0x18, 0xde, 0xa6, 0xd4, 0x12, 0x83, 0xb0, 0xd4, - 0x7f, 0xe6, 0x09, 0x55, 0x2a, 0x71, 0xf3, 0x9b, 0xcd, 0x3a, 0xe6, 0xd0, 0xf2, 0xaf, 0x87, 0x02, - 0x87, 0x35, 0xbc, 0x49, 0x12, 0xe4, 0x5b, 0xe9, 0x6e, 0xe4, 0xdb, 0x72, 0x56, 0xae, 0x45, 0xdf, - 0x82, 0x02, 0xd5, 0xfa, 0xdd, 0xd0, 0x0a, 0x09, 0xcd, 0xd5, 0x46, 0x98, 0xb0, 0x9a, 0xab, 0x0d, - 0xcc, 0x20, 0xd1, 0x9b, 0x50, 0x64, 0xab, 0x19, 0x9b, 0xe3, 0x85, 0xfe, 0x73, 0x08, 0xf3, 0x57, - 0x18, 0x61, 0xec, 0xcb, 0xc1, 0x1e, 0xae, 0xfc, 0x0e, 0x8c, 0xc5, 0x12, 0x01, 0xba, 0x06, 0xc7, - 0x34, 0x53, 0x69, 0xd7, 0x14, 0x4d, 0x31, 0x5a, 0xc4, 0x4e, 0xa6, 0xc6, 0xec, 0xbd, 0xd0, 0x6a, - 0x84, 0x43, 0x24, 0x94, 0xe0, 0xf2, 0x31, 0xda, 0x87, 0x63, 0xd8, 0xb2, 0x02, 0x10, 0x5a, 0x8f, - 0xe6, 0xa1, 0xc8, 0x42, 0xd8, 0x5b, 0x99, 0x46, 0x6b, 0xa3, 0x4c, 0x57, 0x16, 0xd9, 0x0e, 0xf6, - 0xda, 0xd1, 0x69, 0x00, 0x87, 0xb4, 0x6c, 0x42, 0x79, 0xde, 0xf1, 0x4e, 0x8f, 0x82, 0x0c, 0xdc, - 0x08, 0x7a, 0x70, 0x84, 0x4a, 0xfe, 0x9b, 0x04, 0x63, 0xeb, 0x84, 0xbe, 0x6d, 0xda, 0xd7, 0xeb, - 0xfc, 0x72, 0x77, 0x00, 0x79, 0x7f, 0x33, 0x96, 0xf7, 0x5f, 0xea, 0x71, 0xcc, 0x62, 0xda, 0xe6, - 0x65, 0x7f, 0xf9, 0x5f, 0x12, 0x54, 0x62, 0x94, 0xd1, 0x34, 0x41, 0xa0, 0x68, 0x99, 0x36, 0xf5, - 0xd7, 0xf8, 0x43, 0x69, 0xc0, 0x52, 0x6a, 0x64, 0x95, 0x67, 0xb0, 0xd8, 0x43, 0x67, 0x76, 0x6e, - 0xd9, 0xa6, 0x2e, 0xe2, 0xfe, 0x70, 0x52, 0x08, 0xb1, 0x43, 0x3b, 0x2f, 0xd8, 0xa6, 0x8e, 0x39, - 0xb6, 0xfc, 0x57, 0x09, 0x26, 0x63, 0x94, 0x03, 0x48, 0xe9, 0x4a, 0x3c, 0xa5, 0x3f, 0x7f, 0x18, - 0xc3, 0x72, 0x12, 0xfb, 0x17, 0x49, 0xb3, 0x98, 0x03, 0xd0, 0x16, 0x94, 0x2d, 0xb3, 0xdd, 0xb8, - 0x0b, 0xb7, 0x7a, 0x13, 0x6c, 0x85, 0xac, 0x87, 0x58, 0x38, 0x0a, 0x8c, 0x76, 0x61, 0xd2, 0x50, - 0x74, 0xe2, 0x58, 0x4a, 0x8b, 0x34, 0xee, 0xc2, 0x99, 0xca, 0x7d, 0xfc, 0xa6, 0x21, 0x89, 0x88, - 0xd3, 0x42, 0xe4, 0xdf, 0xa5, 0xec, 0x36, 0x6d, 0x8a, 0x5e, 0x86, 0x12, 0x7f, 0x60, 0xd1, 0x32, - 0x35, 0xb1, 0xb4, 0x9d, 0x61, 0x43, 0x53, 0x17, 0x6d, 0xb7, 0xf6, 0xe6, 0xbf, 0x7c, 0xe0, 0x91, - 0xb0, 0x4f, 0x88, 0x03, 0x18, 0xb4, 0x0e, 0xc3, 0xd6, 0x61, 0xca, 0x0c, 0xbe, 0xb0, 0xf0, 0xda, - 0x82, 0xe3, 0xc8, 0xff, 0x4e, 0x2a, 0xce, 0x97, 0x97, 0x6b, 0x77, 0x6d, 0xc0, 0x82, 0xb2, 0x26, - 0x77, 0xd0, 0x6c, 0x18, 0x11, 0xab, 0xac, 0x88, 0xcb, 0x8b, 0x87, 0x89, 0xcb, 0xe8, 0xca, 0x10, - 0x6c, 0x22, 0xfc, 0x46, 0x5f, 0x90, 0xfc, 0x0f, 0x09, 0x26, 0xb9, 0x42, 0x2d, 0xd7, 0x56, 0x69, - 0x77, 0x60, 0x19, 0x74, 0x2b, 0x96, 0x41, 0x97, 0x7b, 0x34, 0x34, 0xa5, 0x71, 0x6e, 0x16, 0xfd, - 0x54, 0x82, 0xfb, 0x52, 0xd4, 0x03, 0xc8, 0x30, 0x24, 0x9e, 0x61, 0x5e, 0x3a, 0xac, 0x81, 0x39, - 0x59, 0xe6, 0x87, 0xe5, 0x0c, 0xf3, 0x78, 0xe0, 0x9e, 0x06, 0xb0, 0x6c, 0x75, 0x47, 0xd5, 0x48, - 0x47, 0x5c, 0x24, 0x97, 0xc2, 0x21, 0xa9, 0x07, 0x3d, 0x38, 0x42, 0x85, 0xbe, 0x03, 0x33, 0x6d, - 0xb2, 0xa5, 0xb8, 0x1a, 0x5d, 0x6c, 0xb7, 0x97, 0x14, 0x4b, 0xd9, 0x54, 0x35, 0x95, 0xaa, 0x62, - 0x87, 0x3d, 0x5a, 0x3b, 0xef, 0x5d, 0xf0, 0x66, 0x51, 0xdc, 0xda, 0x9b, 0x7f, 0xf4, 0xe0, 0x4b, - 0x1d, 0x9f, 0xb8, 0x8b, 0x73, 0x84, 0xa0, 0xef, 0x4b, 0x50, 0xb1, 0xc9, 0x5b, 0xae, 0x6a, 0x93, - 0xf6, 0xb2, 0x6d, 0x5a, 0x31, 0x0d, 0x0a, 0x5c, 0x83, 0x8b, 0xfb, 0x7b, 0xf3, 0x15, 0x9c, 0x43, - 0xd3, 0x8b, 0x0e, 0xb9, 0x82, 0x10, 0x85, 0x29, 0x45, 0xd3, 0xcc, 0xb7, 0x49, 0xdc, 0x03, 0xc3, - 0x5c, 0x7e, 0x6d, 0x7f, 0x6f, 0x7e, 0x6a, 0x31, 0xdd, 0xdd, 0x8b, 0xe8, 0x2c, 0x78, 0xb4, 0x00, - 0x23, 0x3b, 0xa6, 0xe6, 0xea, 0xc4, 0xa9, 0x14, 0xb9, 0x24, 0x96, 0x71, 0x47, 0xae, 0x78, 0x4d, - 0xb7, 0xf6, 0xe6, 0x8f, 0x5e, 0x68, 0xf0, 0xa3, 0x0f, 0x9f, 0x8a, 0xed, 0xd1, 0x58, 0xcd, 0x24, - 0xa6, 0x3c, 0x3f, 0xb3, 0x2d, 0x85, 0x39, 0xe6, 0x52, 0xd8, 0x85, 0xa3, 0x74, 0x48, 0x87, 0xd1, - 0x6d, 0xb1, 0x6f, 0x77, 0x2a, 0x23, 0x7d, 0xad, 0x7e, 0xb1, 0x7d, 0x7f, 0x6d, 0x52, 0x88, 0x1c, - 0xf5, 0x9b, 0x1d, 0x1c, 0x4a, 0x40, 0x8f, 0xc1, 0x08, 0xff, 0x58, 0x59, 0xe6, 0x07, 0x60, 0xa5, - 0x30, 0x13, 0x5d, 0xf2, 0x9a, 0xb1, 0xdf, 0xef, 0x93, 0xae, 0xd4, 0x97, 0xf8, 0xc1, 0x6c, 0x82, - 0x74, 0xa5, 0xbe, 0x84, 0xfd, 0x7e, 0x64, 0xc1, 0x88, 0x43, 0x56, 0x55, 0xc3, 0xdd, 0xad, 0x40, - 0x5f, 0x57, 0xcd, 0x8d, 0xf3, 0x9c, 0x3b, 0x71, 0x14, 0x15, 0x4a, 0x14, 0xfd, 0xd8, 0x17, 0x83, - 0x76, 0x61, 0xd4, 0x76, 0x8d, 0x45, 0x67, 0xc3, 0x21, 0x76, 0xa5, 0xcc, 0x65, 0xf6, 0x9a, 0x9c, - 0xb1, 0xcf, 0x9f, 0x94, 0x1a, 0x78, 0x30, 0xa0, 0xc0, 0xa1, 0x30, 0xf4, 0x73, 0x09, 0x90, 0xe3, - 0x5a, 0x96, 0x46, 0x74, 0x62, 0x50, 0x45, 0xe3, 0xa7, 0x61, 0x4e, 0xe5, 0x18, 0xd7, 0xa1, 0xde, - 0xab, 0xdd, 0x29, 0xa0, 0xa4, 0x32, 0xc1, 0x31, 0x75, 0x9a, 0x14, 0x67, 0xe8, 0xc1, 0x86, 0x62, - 0xcb, 0xe1, 0xbf, 0x2b, 0x63, 0x7d, 0x0d, 0x45, 0xf6, 0xa9, 0x60, 0x38, 0x14, 0xa2, 0x1f, 0xfb, - 0x62, 0xd0, 0x15, 0x98, 0xb1, 0x89, 0xd2, 0xbe, 0x6c, 0x68, 0x5d, 0x6c, 0x9a, 0xf4, 0x82, 0xaa, - 0x11, 0xa7, 0xeb, 0x50, 0xa2, 0x57, 0xc6, 0x79, 0xd8, 0x04, 0xcf, 0x35, 0x70, 0x26, 0x15, 0xce, - 0xe1, 0x46, 0x2f, 0xc1, 0x71, 0x31, 0x31, 0x79, 0x68, 0xf2, 0x13, 0xb4, 0x09, 0x3e, 0x15, 0xa7, - 0xd9, 0x8e, 0x78, 0x31, 0xd1, 0x87, 0x53, 0xd4, 0xfc, 0x1d, 0x82, 0x38, 0x01, 0x1e, 0xcc, 0x4b, - 0xbe, 0xc3, 0xbd, 0x43, 0x08, 0x55, 0xbd, 0x67, 0xef, 0x10, 0x22, 0x22, 0x0e, 0x3e, 0x84, 0xfa, - 0x62, 0x08, 0xa6, 0x42, 0xe2, 0x3b, 0x7e, 0x87, 0x90, 0xc1, 0x32, 0x80, 0x77, 0x08, 0xd9, 0x17, - 0xf9, 0x85, 0x7b, 0x7d, 0x91, 0x7f, 0x0f, 0xde, 0x3f, 0xf0, 0xb7, 0x01, 0xa1, 0x13, 0xff, 0xf7, - 0xdf, 0x06, 0x84, 0xba, 0xe6, 0x14, 0x44, 0x7f, 0x18, 0x8a, 0x1a, 0xf4, 0x7f, 0x74, 0x01, 0x7d, - 0xf8, 0x77, 0x8e, 0xf2, 0xa7, 0x05, 0x38, 0x9e, 0x9c, 0xb1, 0xb1, 0x7b, 0x48, 0xe9, 0xb6, 0xf7, - 0x90, 0x75, 0x98, 0xde, 0x72, 0x35, 0xad, 0xcb, 0x1d, 0x12, 0xb9, 0x8c, 0xf4, 0xce, 0xfd, 0x1f, - 0x14, 0x9c, 0xd3, 0x17, 0x32, 0x68, 0x70, 0x26, 0x67, 0xce, 0x9d, 0x6a, 0xa1, 0xaf, 0x3b, 0xd5, - 0xd4, 0x95, 0xde, 0x70, 0x0f, 0x57, 0x7a, 0x99, 0xf7, 0xa3, 0xc5, 0x3e, 0xee, 0x47, 0xef, 0xc6, - 0x85, 0x66, 0x46, 0xe2, 0xbb, 0xdd, 0x85, 0xa6, 0xfc, 0x20, 0xcc, 0x0a, 0x36, 0xca, 0xef, 0x1a, - 0x0d, 0x6a, 0x9b, 0x9a, 0x46, 0xec, 0x65, 0x57, 0xd7, 0xbb, 0xf2, 0x39, 0x18, 0x8f, 0xdf, 0xaa, - 0x7b, 0x23, 0xef, 0x5d, 0xf4, 0x8b, 0xdb, 0x98, 0xc8, 0xc8, 0x7b, 0xed, 0x38, 0xa0, 0x90, 0xdf, - 0x93, 0x60, 0x26, 0xfb, 0x05, 0x1f, 0xd2, 0x60, 0x5c, 0x57, 0x76, 0xa3, 0x4f, 0x18, 0xa5, 0x3e, - 0xf7, 0xec, 0xfc, 0xfa, 0x74, 0x2d, 0x86, 0x85, 0x13, 0xd8, 0xf2, 0x67, 0x12, 0x9c, 0xc8, 0xb9, - 0xa8, 0x1c, 0xac, 0x26, 0xe8, 0x2a, 0x94, 0x74, 0x65, 0xb7, 0xe1, 0xda, 0x1d, 0xd2, 0xf7, 0x29, - 0x05, 0xcf, 0x25, 0x6b, 0x02, 0x05, 0x07, 0x78, 0xf2, 0x87, 0x12, 0x54, 0xf2, 0x2a, 0x4a, 0x74, - 0x26, 0x76, 0xa5, 0xfa, 0x70, 0xe2, 0x4a, 0x75, 0x32, 0xc5, 0x37, 0xa0, 0x0b, 0xd5, 0x8f, 0x24, - 0x98, 0xc9, 0xae, 0xbc, 0xd1, 0x53, 0x31, 0x8d, 0xe7, 0x13, 0x1a, 0x4f, 0x24, 0xb8, 0x84, 0xbe, - 0xdb, 0x30, 0x2e, 0xea, 0x73, 0x01, 0x23, 0xbc, 0xfc, 0xf8, 0xc1, 0x59, 0x55, 0x80, 0xf9, 0x95, - 0x26, 0x1f, 0xc9, 0x78, 0x1b, 0x4e, 0xe0, 0xca, 0xbf, 0x1a, 0x82, 0x62, 0xa3, 0xa5, 0x68, 0x64, - 0x00, 0x45, 0xdd, 0xd5, 0x58, 0x51, 0xd7, 0xeb, 0xbf, 0x0c, 0xb8, 0x96, 0xb9, 0xf5, 0xdc, 0x66, - 0xa2, 0x9e, 0x3b, 0xdb, 0x17, 0xfa, 0xc1, 0xa5, 0xdc, 0xb3, 0x30, 0x1a, 0x28, 0xd1, 0xdb, 0xea, - 0x21, 0x7f, 0x30, 0x04, 0xe5, 0x88, 0x88, 0x1e, 0xd7, 0x9e, 0x9d, 0xd8, 0xea, 0xdd, 0xcf, 0x1f, - 0x9a, 0x22, 0xb2, 0xab, 0xfe, 0xfa, 0xed, 0xbd, 0xe0, 0x0b, 0x5f, 0x62, 0xa5, 0x97, 0xf5, 0x73, - 0x30, 0x4e, 0xf9, 0xff, 0x7b, 0x82, 0x53, 0xc2, 0x02, 0x8f, 0xe2, 0xe0, 0x5d, 0x68, 0x33, 0xd6, - 0x8b, 0x13, 0xd4, 0xb3, 0xcf, 0xc1, 0x58, 0x4c, 0x58, 0x4f, 0x0f, 0xee, 0xfe, 0x28, 0xc1, 0xc3, - 0xb7, 0xdd, 0xd5, 0xa1, 0x5a, 0x6c, 0x7a, 0x55, 0x13, 0xd3, 0x6b, 0x2e, 0x1f, 0x60, 0x80, 0xcf, - 0x2d, 0x7e, 0x3a, 0x04, 0xa8, 0xb9, 0xad, 0xda, 0xed, 0xba, 0x62, 0xd3, 0x2e, 0x16, 0xff, 0xe2, - 0x1a, 0xc0, 0x84, 0x3b, 0x03, 0xe5, 0x36, 0x71, 0x5a, 0xb6, 0xca, 0x9d, 0x25, 0xf6, 0x0a, 0xc1, - 0x49, 0xca, 0x72, 0xd8, 0x85, 0xa3, 0x74, 0xa8, 0x03, 0xa5, 0x1d, 0xef, 0x7f, 0x82, 0xfe, 0xdd, - 0x5d, 0xaf, 0xc5, 0x6c, 0xf8, 0x4f, 0xc3, 0x30, 0xbe, 0x44, 0x83, 0x83, 0x03, 0x70, 0xf9, 0x7d, - 0x09, 0x66, 0xd2, 0x8e, 0x59, 0x66, 0xaa, 0xdf, 0x7b, 0xe7, 0x3c, 0x08, 0xc3, 0x1c, 0x9d, 0x79, - 0xe5, 0x98, 0x77, 0x66, 0xce, 0x24, 0x63, 0xde, 0x2a, 0x7f, 0x2e, 0xc1, 0x6c, 0xb6, 0x6a, 0x03, - 0xd8, 0x4a, 0x5c, 0x8b, 0x6f, 0x25, 0x7a, 0x3d, 0x78, 0xc8, 0xd6, 0x3b, 0x67, 0x5b, 0xb1, 0x97, - 0x39, 0x06, 0x03, 0x30, 0x72, 0x2b, 0x6e, 0xe4, 0xe2, 0xa1, 0x8d, 0xcc, 0x36, 0xb0, 0xf6, 0xd8, - 0x8d, 0x9b, 0x73, 0x47, 0x3e, 0xbe, 0x39, 0x77, 0xe4, 0x93, 0x9b, 0x73, 0x47, 0xde, 0xdd, 0x9f, - 0x93, 0x6e, 0xec, 0xcf, 0x49, 0x1f, 0xef, 0xcf, 0x49, 0xff, 0xdc, 0x9f, 0x93, 0x7e, 0xfc, 0xd9, - 0xdc, 0x91, 0xab, 0x23, 0x02, 0xf3, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x6d, 0x3f, 0x0c, 0xbe, - 0x34, 0x3d, 0x00, 0x00, + // 3427 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5b, 0xcd, 0x6f, 0x1c, 0xc7, + 0x95, 0x57, 0x73, 0x38, 0xe4, 0xf0, 0x8d, 0x48, 0x8a, 0x45, 0x9a, 0x1a, 0xd3, 0x36, 0x29, 0xf7, + 0x62, 0x6d, 0x79, 0xd7, 0x1e, 0xae, 0xe5, 0x95, 0xd7, 0x96, 0x6d, 0xd9, 0x1c, 0x52, 0x1f, 0x5c, + 0x90, 0xd4, 0xb8, 0x66, 0x28, 0xac, 0xe5, 0xaf, 0x6d, 0xce, 0x14, 0x87, 0x2d, 0xf5, 0x97, 0xbb, + 0xab, 0x69, 0x0e, 0x8c, 0xdd, 0x35, 0xb0, 0x88, 0x0f, 0x39, 0x04, 0xc9, 0x25, 0x70, 0x80, 0xe4, + 0x98, 0x43, 0x2e, 0x89, 0x7d, 0x48, 0x9c, 0xfc, 0x05, 0xd1, 0x21, 0x08, 0x1c, 0x20, 0x01, 0x8c, + 0xc0, 0x21, 0x22, 0x1a, 0xf1, 0x3f, 0x10, 0xf8, 0xa2, 0x53, 0x50, 0xd5, 0xd5, 0xdf, 0xdd, 0x94, + 0x66, 0x48, 0x0d, 0x02, 0xe4, 0x36, 0x5d, 0xf5, 0xde, 0xef, 0x7d, 0xd4, 0xab, 0x57, 0xaf, 0x3e, + 0x06, 0x5e, 0xb9, 0xf5, 0x82, 0x53, 0x55, 0xcd, 0xc5, 0x5b, 0xee, 0x16, 0xb1, 0x0d, 0x42, 0x89, + 0xb3, 0x68, 0xdd, 0xea, 0x2c, 0x2a, 0x96, 0xea, 0x2c, 0x92, 0x3d, 0x4a, 0x0c, 0x47, 0x35, 0x0d, + 0x67, 0x71, 0xf7, 0xd9, 0x2d, 0x42, 0x95, 0x67, 0x17, 0x3b, 0xc4, 0x20, 0xb6, 0x42, 0x49, 0xbb, + 0x6a, 0xd9, 0x26, 0x35, 0xd1, 0x33, 0x1e, 0x7b, 0x35, 0x64, 0xaf, 0x5a, 0xb7, 0x3a, 0x55, 0xc6, + 0x5e, 0x0d, 0xd9, 0xab, 0x82, 0x7d, 0xee, 0x99, 0x8e, 0x4a, 0x77, 0xdc, 0xad, 0x6a, 0xcb, 0xd4, + 0x17, 0x3b, 0x66, 0xc7, 0x5c, 0xe4, 0x28, 0x5b, 0xee, 0x36, 0xff, 0xe2, 0x1f, 0xfc, 0x97, 0x87, + 0x3e, 0xf7, 0xef, 0x42, 0x39, 0xc5, 0x52, 0x75, 0xa5, 0xb5, 0xa3, 0x1a, 0xc4, 0xee, 0xfa, 0xea, + 0x2d, 0xda, 0xc4, 0x31, 0x5d, 0xbb, 0x45, 0x92, 0x3a, 0x1d, 0xca, 0xe5, 0x2c, 0xea, 0x84, 0x2a, + 0x8b, 0xbb, 0x29, 0x4b, 0xe6, 0x16, 0xf3, 0xb8, 0x6c, 0xd7, 0xa0, 0xaa, 0x9e, 0x16, 0xf3, 0xfc, + 0xbd, 0x18, 0x9c, 0xd6, 0x0e, 0xd1, 0x95, 0x14, 0xdf, 0x73, 0x79, 0x7c, 0x2e, 0x55, 0xb5, 0x45, + 0xd5, 0xa0, 0x0e, 0xb5, 0x53, 0x4c, 0x4f, 0xe7, 0x0e, 0x53, 0x96, 0x2d, 0x2f, 0x1e, 0x32, 0xa8, + 0x96, 0xa9, 0xa9, 0xad, 0x6e, 0xde, 0x80, 0xca, 0x55, 0x80, 0xa5, 0xfa, 0xea, 0x75, 0x62, 0xb3, + 0x81, 0x43, 0x67, 0x60, 0xd8, 0x50, 0x74, 0x52, 0x91, 0xce, 0x48, 0x67, 0xc7, 0x6a, 0x27, 0x6f, + 0xef, 0x2f, 0x9c, 0x38, 0xd8, 0x5f, 0x18, 0xde, 0x50, 0x74, 0x82, 0x79, 0x8f, 0xfc, 0x03, 0x09, + 0x1e, 0x5e, 0x76, 0x1d, 0x6a, 0xea, 0xeb, 0x84, 0xda, 0x6a, 0x6b, 0xd9, 0xb5, 0x6d, 0x62, 0xd0, + 0x06, 0x55, 0xa8, 0xeb, 0xdc, 0x9b, 0x1f, 0xdd, 0x80, 0xe2, 0xae, 0xa2, 0xb9, 0xa4, 0x32, 0x74, + 0x46, 0x3a, 0x5b, 0x3e, 0x57, 0xad, 0x8a, 0x80, 0x8a, 0x7a, 0xc7, 0x0f, 0xa9, 0xaa, 0x3f, 0xe4, + 0xd5, 0xd7, 0x5d, 0xc5, 0xa0, 0x2a, 0xed, 0xd6, 0x66, 0x04, 0xe4, 0x49, 0x21, 0xf7, 0x3a, 0xc3, + 0xc2, 0x1e, 0xa4, 0xfc, 0x1d, 0x09, 0x1e, 0xcb, 0xd5, 0x6d, 0x4d, 0x75, 0x28, 0xd2, 0xa1, 0xa8, + 0x52, 0xa2, 0x3b, 0x15, 0xe9, 0x4c, 0xe1, 0x6c, 0xf9, 0xdc, 0xd5, 0x6a, 0x4f, 0xe1, 0x5c, 0xcd, + 0x05, 0xaf, 0x8d, 0x0b, 0xbd, 0x8a, 0xab, 0x0c, 0x1e, 0x7b, 0x52, 0xe4, 0xef, 0x49, 0x80, 0xa2, + 0x3c, 0x4d, 0xc5, 0xee, 0x10, 0x7a, 0x1f, 0x5e, 0x7a, 0xe3, 0x68, 0x5e, 0x9a, 0x16, 0x90, 0x65, + 0x4f, 0x60, 0xcc, 0x49, 0x1f, 0x4a, 0x30, 0x9b, 0xd6, 0x89, 0x7b, 0x67, 0x3b, 0xee, 0x9d, 0xa5, + 0x23, 0x78, 0xc7, 0x43, 0xcd, 0x71, 0xcb, 0xcf, 0x87, 0x60, 0x6c, 0x45, 0x21, 0xba, 0x69, 0x34, + 0x08, 0x45, 0xff, 0x0d, 0x25, 0x36, 0x47, 0xdb, 0x0a, 0x55, 0xb8, 0x47, 0xca, 0xe7, 0xfe, 0xed, + 0x30, 0x73, 0x9d, 0x2a, 0xa3, 0xae, 0xee, 0x3e, 0x5b, 0xbd, 0xb6, 0x75, 0x93, 0xb4, 0xe8, 0x3a, + 0xa1, 0x4a, 0x0d, 0x09, 0x39, 0x10, 0xb6, 0xe1, 0x00, 0x15, 0xbd, 0x03, 0xc3, 0x8e, 0x45, 0x5a, + 0xc2, 0x99, 0x2f, 0xf7, 0x68, 0x56, 0xa0, 0x69, 0xc3, 0x22, 0xad, 0x70, 0xb4, 0xd8, 0x17, 0xe6, + 0xb8, 0x68, 0x1b, 0x46, 0x1c, 0x1e, 0x06, 0x95, 0x02, 0x97, 0x70, 0xb1, 0x6f, 0x09, 0x5e, 0x30, + 0x4d, 0x08, 0x19, 0x23, 0xde, 0x37, 0x16, 0xe8, 0xf2, 0x6f, 0x24, 0x18, 0x0f, 0x68, 0xf9, 0x88, + 0xbd, 0x95, 0xf2, 0x5d, 0xf5, 0xfe, 0x7c, 0xc7, 0xb8, 0xb9, 0xe7, 0x4e, 0x09, 0x59, 0x25, 0xbf, + 0x25, 0xe2, 0xb7, 0xb7, 0xfd, 0x78, 0x18, 0xe2, 0xf1, 0xf0, 0x42, 0xbf, 0x66, 0xe5, 0x84, 0xc1, + 0xa7, 0xc3, 0x11, 0x73, 0x98, 0x3b, 0xd1, 0xdb, 0x50, 0x72, 0x88, 0x46, 0x5a, 0xd4, 0xb4, 0x85, + 0x39, 0xcf, 0xdd, 0xa7, 0x39, 0xca, 0x16, 0xd1, 0x1a, 0x82, 0xb5, 0x76, 0x92, 0xd9, 0xe3, 0x7f, + 0xe1, 0x00, 0x12, 0xbd, 0x09, 0x25, 0x4a, 0x74, 0x4b, 0x53, 0xa8, 0x3f, 0xb1, 0x9e, 0xc9, 0x37, + 0x89, 0xc1, 0xd6, 0xcd, 0x76, 0x53, 0x30, 0xf0, 0xc1, 0x0f, 0x9c, 0xe5, 0xb7, 0xe2, 0x00, 0x10, + 0x7d, 0x24, 0xc1, 0x84, 0x6b, 0xb5, 0x19, 0x29, 0x65, 0x09, 0xb6, 0xd3, 0x15, 0xd1, 0x70, 0xb9, + 0x5f, 0xb7, 0x6d, 0xc6, 0xd0, 0x6a, 0xb3, 0x42, 0xf8, 0x44, 0xbc, 0x1d, 0x27, 0xa4, 0xa2, 0x25, + 0x98, 0xd4, 0x55, 0x03, 0x13, 0xa5, 0xdd, 0x6d, 0x90, 0x96, 0x69, 0xb4, 0x9d, 0xca, 0xf0, 0x19, + 0xe9, 0x6c, 0xb1, 0x76, 0x5a, 0x00, 0x4c, 0xae, 0xc7, 0xbb, 0x71, 0x92, 0x1e, 0xfd, 0x27, 0x20, + 0xdf, 0xae, 0x2b, 0xde, 0x7a, 0xa1, 0x9a, 0x46, 0xa5, 0x78, 0x46, 0x3a, 0x5b, 0xa8, 0xcd, 0x09, + 0x14, 0xd4, 0x4c, 0x51, 0xe0, 0x0c, 0x2e, 0xb4, 0x06, 0x33, 0x36, 0xd9, 0x55, 0x99, 0x8d, 0x57, + 0x55, 0x87, 0x9a, 0x76, 0x77, 0x4d, 0xd5, 0x55, 0x5a, 0x19, 0xe1, 0x3a, 0x55, 0x0e, 0xf6, 0x17, + 0x66, 0x70, 0x46, 0x3f, 0xce, 0xe4, 0x92, 0x3f, 0x29, 0xc2, 0x64, 0x62, 0xba, 0xa0, 0xeb, 0x30, + 0xdb, 0xf2, 0x92, 0xf1, 0x86, 0xab, 0x6f, 0x11, 0xbb, 0xd1, 0xda, 0x21, 0x6d, 0x57, 0x23, 0x6d, + 0x1e, 0x43, 0xc5, 0xda, 0xbc, 0xd0, 0x78, 0x76, 0x39, 0x93, 0x0a, 0xe7, 0x70, 0x33, 0x2f, 0x18, + 0xbc, 0x69, 0x5d, 0x75, 0x9c, 0x00, 0x73, 0x88, 0x63, 0x06, 0x5e, 0xd8, 0x48, 0x51, 0xe0, 0x0c, + 0x2e, 0xa6, 0x63, 0x9b, 0x38, 0xaa, 0x4d, 0xda, 0x49, 0x1d, 0x0b, 0x71, 0x1d, 0x57, 0x32, 0xa9, + 0x70, 0x0e, 0x37, 0x3a, 0x0f, 0x65, 0x4f, 0x1a, 0x1f, 0x3f, 0x31, 0xd0, 0x41, 0xfa, 0xdf, 0x08, + 0xbb, 0x70, 0x94, 0x8e, 0x99, 0x66, 0x6e, 0x39, 0xc4, 0xde, 0x25, 0xed, 0xfc, 0x01, 0xbe, 0x96, + 0xa2, 0xc0, 0x19, 0x5c, 0xcc, 0x34, 0x2f, 0x02, 0x53, 0xa6, 0x8d, 0xc4, 0x4d, 0xdb, 0xcc, 0xa4, + 0xc2, 0x39, 0xdc, 0x2c, 0x8e, 0x3d, 0x95, 0x97, 0x76, 0x15, 0x55, 0x53, 0xb6, 0x34, 0x52, 0x19, + 0x8d, 0xc7, 0xf1, 0x46, 0xbc, 0x1b, 0x27, 0xe9, 0xd1, 0x15, 0x98, 0xf2, 0x9a, 0x36, 0x0d, 0x25, + 0x00, 0x29, 0x71, 0x90, 0x87, 0x05, 0xc8, 0xd4, 0x46, 0x92, 0x00, 0xa7, 0x79, 0xd0, 0x05, 0x98, + 0x68, 0x99, 0x9a, 0xc6, 0xe3, 0x71, 0xd9, 0x74, 0x0d, 0x5a, 0x19, 0xe3, 0xbe, 0x42, 0x6c, 0x3e, + 0x2e, 0xc7, 0x7a, 0x70, 0x82, 0x52, 0xfe, 0xa3, 0x04, 0xa7, 0x73, 0xe6, 0x34, 0x7a, 0x15, 0x86, + 0x69, 0xd7, 0xf2, 0x2b, 0x81, 0x7f, 0xf5, 0xd7, 0x96, 0x66, 0xd7, 0x22, 0x77, 0xf7, 0x17, 0x1e, + 0xc9, 0x61, 0x63, 0xdd, 0x98, 0x33, 0xa2, 0xff, 0x85, 0x71, 0x9b, 0x89, 0x33, 0x3a, 0x1e, 0x89, + 0xc8, 0x6b, 0x97, 0x7a, 0xcc, 0x39, 0x38, 0x8a, 0x11, 0xe6, 0xed, 0xa9, 0x83, 0xfd, 0x85, 0xf1, + 0x58, 0x1f, 0x8e, 0x8b, 0x93, 0x7f, 0x39, 0x04, 0xb0, 0x42, 0x2c, 0xcd, 0xec, 0xea, 0xc4, 0x18, + 0xc4, 0x5a, 0xfe, 0x6e, 0x6c, 0x2d, 0x7f, 0xa5, 0xd7, 0xdc, 0x1a, 0xa8, 0x9a, 0xbb, 0x98, 0x77, + 0x12, 0x8b, 0xf9, 0xab, 0xfd, 0x8b, 0x38, 0x7c, 0x35, 0xbf, 0x53, 0x80, 0xe9, 0x90, 0x78, 0xd9, + 0x34, 0xda, 0x2a, 0x9f, 0x4f, 0x2f, 0xc5, 0x62, 0xe2, 0xc9, 0x44, 0x4c, 0x9c, 0xce, 0x60, 0x89, + 0xc4, 0xc3, 0xf5, 0x40, 0xfb, 0x21, 0xce, 0x7e, 0x31, 0x2e, 0xfc, 0xee, 0xfe, 0xc2, 0xa1, 0x3b, + 0x8b, 0x6a, 0x80, 0x19, 0x57, 0x16, 0x3d, 0x01, 0x23, 0x36, 0x51, 0x1c, 0xd3, 0xe0, 0x29, 0x66, + 0x2c, 0x34, 0x0a, 0xf3, 0x56, 0x2c, 0x7a, 0xd1, 0x53, 0x30, 0xaa, 0x13, 0xc7, 0x51, 0x3a, 0x84, + 0x67, 0x93, 0xb1, 0xda, 0xa4, 0x20, 0x1c, 0x5d, 0xf7, 0x9a, 0xb1, 0xdf, 0x8f, 0x6e, 0xc2, 0x84, + 0xa6, 0x38, 0x22, 0xb4, 0x9b, 0xaa, 0x4e, 0x78, 0xbe, 0x28, 0x9f, 0xfb, 0x97, 0xfb, 0x8b, 0x18, + 0xc6, 0x11, 0xae, 0x89, 0x6b, 0x31, 0x24, 0x9c, 0x40, 0x46, 0xbb, 0x80, 0x58, 0x4b, 0xd3, 0x56, + 0x0c, 0xc7, 0x73, 0x19, 0x93, 0x37, 0xda, 0xb3, 0xbc, 0x20, 0x37, 0xae, 0xa5, 0xd0, 0x70, 0x86, + 0x04, 0xf9, 0xb7, 0x12, 0x4c, 0x84, 0x03, 0x36, 0x80, 0x92, 0xed, 0x9d, 0x78, 0xc9, 0xf6, 0x62, + 0xdf, 0xc1, 0x9b, 0x53, 0xb3, 0x7d, 0x5c, 0x00, 0x14, 0x12, 0xb1, 0xd4, 0xb0, 0xa5, 0xb4, 0x6e, + 0xdd, 0xc7, 0x8e, 0xe6, 0xc7, 0x12, 0x20, 0x91, 0xe8, 0x97, 0x0c, 0xc3, 0xa4, 0x7c, 0xed, 0xf0, + 0xd5, 0x7c, 0xa3, 0x6f, 0x35, 0x7d, 0x0d, 0xaa, 0x9b, 0x29, 0xec, 0x4b, 0x06, 0xb5, 0xbb, 0xe1, + 0x88, 0xa5, 0x09, 0x70, 0x86, 0x42, 0xe8, 0x3d, 0x00, 0x5b, 0x60, 0x36, 0x4d, 0x91, 0x02, 0x5e, + 0xe9, 0x23, 0x9b, 0x32, 0x80, 0x65, 0xd3, 0xd8, 0x56, 0x3b, 0x61, 0x42, 0xc3, 0x01, 0x30, 0x8e, + 0x08, 0x99, 0xbb, 0x04, 0xa7, 0x73, 0xb4, 0x47, 0xa7, 0xa0, 0x70, 0x8b, 0x74, 0x3d, 0xb7, 0x62, + 0xf6, 0x13, 0xcd, 0x44, 0x77, 0x86, 0x63, 0x62, 0x53, 0x77, 0x61, 0xe8, 0x05, 0x49, 0xfe, 0xba, + 0x18, 0x8d, 0x35, 0x5e, 0x4f, 0x9f, 0x85, 0x92, 0x4d, 0x2c, 0x4d, 0x6d, 0x29, 0x8e, 0xa8, 0x85, + 0x78, 0x69, 0x8c, 0x45, 0x1b, 0x0e, 0x7a, 0x63, 0x95, 0xf7, 0xd0, 0x83, 0xad, 0xbc, 0x0b, 0xc7, + 0x5d, 0x79, 0x9b, 0x50, 0x72, 0xfc, 0x92, 0x7b, 0x98, 0x83, 0x2f, 0x1d, 0x21, 0x67, 0x8b, 0x6a, + 0x3b, 0x10, 0x18, 0xd4, 0xd9, 0x81, 0x90, 0xac, 0x0a, 0xbb, 0xd8, 0x63, 0x85, 0x7d, 0xac, 0x55, + 0x31, 0xcb, 0xce, 0x96, 0xe2, 0x3a, 0xa4, 0xcd, 0x53, 0x5a, 0x29, 0xcc, 0xce, 0x75, 0xde, 0x8a, + 0x45, 0x2f, 0xd2, 0x63, 0xc1, 0x5d, 0x3a, 0x8e, 0xe0, 0x9e, 0xc8, 0x0f, 0x6c, 0xb4, 0x09, 0xa7, + 0x2d, 0xdb, 0xec, 0xd8, 0xc4, 0x71, 0x56, 0x88, 0xd2, 0xd6, 0x54, 0x83, 0xf8, 0xfe, 0x1a, 0xe3, + 0x76, 0x3e, 0x72, 0xb0, 0xbf, 0x70, 0xba, 0x9e, 0x4d, 0x82, 0xf3, 0x78, 0xe5, 0x2f, 0x86, 0xe1, + 0x54, 0x72, 0x95, 0xcd, 0xa9, 0x68, 0xa5, 0xbe, 0x2a, 0xda, 0xa7, 0x23, 0xd3, 0xc6, 0x2b, 0xf7, + 0x83, 0x68, 0xc8, 0x98, 0x3a, 0x4b, 0x30, 0x29, 0xf2, 0x88, 0xdf, 0x29, 0x6a, 0xfa, 0x20, 0x1a, + 0x36, 0xe3, 0xdd, 0x38, 0x49, 0xcf, 0xea, 0xd4, 0xb0, 0xfc, 0xf4, 0x41, 0x86, 0xe3, 0x75, 0xea, + 0x52, 0x92, 0x00, 0xa7, 0x79, 0xd0, 0x3a, 0x4c, 0xbb, 0x46, 0x1a, 0xca, 0x8b, 0xce, 0x47, 0x04, + 0xd4, 0xf4, 0x66, 0x9a, 0x04, 0x67, 0xf1, 0xa1, 0x5d, 0x80, 0x96, 0x5f, 0x10, 0x38, 0x95, 0x11, + 0x9e, 0xab, 0x6b, 0x7d, 0xcf, 0xad, 0xa0, 0xb6, 0x08, 0x33, 0x62, 0xd0, 0xe4, 0xe0, 0x88, 0x24, + 0xf4, 0x12, 0x8c, 0xdb, 0x7c, 0xd3, 0xe2, 0x1b, 0xe0, 0x15, 0xfe, 0x0f, 0x09, 0xb6, 0x71, 0x1c, + 0xed, 0xc4, 0x71, 0xda, 0x8c, 0x5a, 0xbd, 0x74, 0xdf, 0xb5, 0xfa, 0xef, 0xa4, 0xe8, 0xf2, 0x16, + 0x94, 0xe9, 0x17, 0x62, 0x25, 0xd9, 0x13, 0x89, 0x92, 0x6c, 0x36, 0xcd, 0x11, 0xa9, 0xc8, 0xfe, + 0x2f, 0xbb, 0x42, 0xbf, 0x7c, 0xa4, 0x0a, 0x3d, 0x5c, 0xa6, 0xef, 0x5d, 0xa2, 0x7f, 0x2a, 0xc1, + 0xec, 0xe5, 0xc6, 0x15, 0xdb, 0x74, 0x2d, 0x5f, 0xbd, 0x6b, 0x96, 0xe7, 0xe7, 0xff, 0x80, 0x61, + 0xdb, 0xd5, 0x7c, 0xbb, 0xfe, 0xc9, 0xb7, 0x0b, 0xbb, 0x1a, 0xb3, 0x6b, 0x3a, 0xc1, 0xe5, 0x19, + 0xc5, 0x18, 0xd0, 0x3b, 0x30, 0x62, 0x2b, 0x46, 0x87, 0xf8, 0x0b, 0xf8, 0xf3, 0x3d, 0x5a, 0xb3, + 0xba, 0x82, 0x19, 0x7b, 0xa4, 0x8c, 0xe4, 0x68, 0x58, 0xa0, 0xca, 0x3f, 0x92, 0x60, 0xf2, 0x6a, + 0xb3, 0x59, 0x5f, 0x35, 0x78, 0x06, 0xa8, 0x2b, 0x74, 0x87, 0xd5, 0x18, 0x96, 0x42, 0x77, 0x92, + 0x35, 0x06, 0xeb, 0xc3, 0xbc, 0x07, 0xed, 0xc0, 0x28, 0xcb, 0x3c, 0xc4, 0x68, 0xf7, 0xb9, 0x3d, + 0x10, 0xe2, 0x6a, 0x1e, 0x48, 0x58, 0xbb, 0x8a, 0x06, 0xec, 0xc3, 0xcb, 0x1f, 0xc0, 0x4c, 0x44, + 0x3d, 0xe6, 0x2f, 0x7e, 0xc6, 0x8a, 0x5a, 0x50, 0x64, 0x9a, 0xf8, 0x27, 0xa8, 0xbd, 0x1e, 0x04, + 0x26, 0x4c, 0x0e, 0x6b, 0x30, 0xf6, 0xe5, 0x60, 0x0f, 0x5b, 0x5e, 0x87, 0xf1, 0xab, 0xa6, 0x43, + 0xeb, 0xa6, 0x4d, 0xb9, 0xdb, 0xd0, 0x63, 0x50, 0xd0, 0x55, 0x43, 0xac, 0xf0, 0x65, 0xc1, 0x53, + 0x60, 0x6b, 0x10, 0x6b, 0xe7, 0xdd, 0xca, 0x9e, 0xc8, 0x64, 0x61, 0xb7, 0xb2, 0x87, 0x59, 0xbb, + 0x7c, 0x05, 0x46, 0xc5, 0x70, 0x44, 0x81, 0x0a, 0x87, 0x03, 0x15, 0x32, 0x80, 0x7e, 0x36, 0x04, + 0xa3, 0x42, 0xfb, 0x01, 0x6c, 0x04, 0xdf, 0x8a, 0x6d, 0x04, 0x2f, 0xf4, 0x37, 0xd2, 0xb9, 0xbb, + 0xc0, 0x76, 0x62, 0x17, 0xf8, 0x72, 0x9f, 0xf8, 0x87, 0x6f, 0x01, 0x3f, 0x91, 0x60, 0x22, 0x1e, + 0x73, 0xe8, 0x3c, 0x94, 0xd9, 0x7a, 0xa4, 0xb6, 0xc8, 0x46, 0x58, 0x50, 0x07, 0x07, 0x3a, 0x8d, + 0xb0, 0x0b, 0x47, 0xe9, 0x50, 0x27, 0x60, 0x63, 0x61, 0x21, 0x9c, 0x92, 0xef, 0x72, 0x97, 0xaa, + 0x5a, 0xd5, 0xbb, 0x7a, 0xaa, 0xae, 0x1a, 0xf4, 0x9a, 0xdd, 0xa0, 0xb6, 0x6a, 0x74, 0x52, 0x82, + 0x78, 0x8c, 0x45, 0x91, 0xe5, 0xdb, 0x12, 0x94, 0x85, 0xca, 0x03, 0xd8, 0xce, 0xbc, 0x19, 0xdf, + 0xce, 0x3c, 0xdf, 0xe7, 0x7c, 0xce, 0xde, 0xcb, 0x7c, 0x16, 0x9a, 0xc2, 0x66, 0x30, 0x4b, 0x30, + 0x3b, 0xa6, 0x43, 0x93, 0x09, 0x86, 0xcd, 0x35, 0xcc, 0x7b, 0xd0, 0xb7, 0x24, 0x38, 0xa5, 0x26, + 0xe6, 0xbc, 0xf0, 0xf5, 0xab, 0xfd, 0xa9, 0x16, 0xc0, 0xd4, 0x2a, 0x42, 0xde, 0xa9, 0x64, 0x0f, + 0x4e, 0x89, 0x94, 0x5d, 0x48, 0x51, 0x21, 0x05, 0x86, 0x77, 0x28, 0xb5, 0xc4, 0x20, 0x2c, 0xf7, + 0x9f, 0x79, 0x42, 0x95, 0x4a, 0xdc, 0xfc, 0x66, 0xb3, 0x8e, 0x39, 0xb4, 0xfc, 0xd3, 0xa1, 0xc0, + 0x61, 0x0d, 0x6f, 0x92, 0x04, 0xf9, 0x56, 0x3a, 0x8e, 0x7c, 0x5b, 0xce, 0xca, 0xb5, 0xe8, 0xbf, + 0xa0, 0x40, 0xb5, 0x7e, 0x37, 0xb4, 0x42, 0x42, 0x73, 0xad, 0x11, 0x26, 0xac, 0xe6, 0x5a, 0x03, + 0x33, 0x48, 0xf4, 0x2e, 0x14, 0xd9, 0x6a, 0xc6, 0xe6, 0x78, 0xa1, 0xff, 0x1c, 0xc2, 0xfc, 0x15, + 0x46, 0x18, 0xfb, 0x72, 0xb0, 0x87, 0x2b, 0x7f, 0x00, 0xe3, 0xb1, 0x44, 0x80, 0x6e, 0xc2, 0x49, + 0xcd, 0x54, 0xda, 0x35, 0x45, 0x53, 0x8c, 0x16, 0xb1, 0x93, 0xa9, 0x31, 0x7b, 0x2f, 0xb4, 0x16, + 0xe1, 0x10, 0x09, 0x25, 0xb8, 0x06, 0x8d, 0xf6, 0xe1, 0x18, 0xb6, 0xac, 0x00, 0x84, 0xd6, 0xa3, + 0x05, 0x28, 0xb2, 0x10, 0xf6, 0x56, 0xa6, 0xb1, 0xda, 0x18, 0xd3, 0x95, 0x45, 0xb6, 0x83, 0xbd, + 0x76, 0x74, 0x0e, 0xc0, 0x21, 0x2d, 0x9b, 0x50, 0x9e, 0x77, 0xbc, 0xd3, 0xa3, 0x20, 0x03, 0x37, + 0x82, 0x1e, 0x1c, 0xa1, 0x92, 0xff, 0x20, 0xc1, 0xf8, 0x06, 0xa1, 0xef, 0x9b, 0xf6, 0xad, 0x3a, + 0xbf, 0x66, 0x1e, 0x40, 0xde, 0xdf, 0x8a, 0xe5, 0xfd, 0xd7, 0x7a, 0x1c, 0xb3, 0x98, 0xb6, 0x79, + 0xd9, 0x5f, 0xfe, 0x8b, 0x04, 0x95, 0x18, 0x65, 0x34, 0x4d, 0x10, 0x28, 0x5a, 0xa6, 0x4d, 0xfd, + 0x35, 0xfe, 0x48, 0x1a, 0xb0, 0x94, 0x1a, 0x59, 0xe5, 0x19, 0x2c, 0xf6, 0xd0, 0x99, 0x9d, 0xdb, + 0xb6, 0xa9, 0x8b, 0xb8, 0x3f, 0x9a, 0x14, 0x42, 0xec, 0xd0, 0xce, 0xcb, 0xb6, 0xa9, 0x63, 0x8e, + 0x2d, 0xff, 0x5e, 0x82, 0xa9, 0x18, 0xe5, 0x00, 0x52, 0xba, 0x12, 0x4f, 0xe9, 0x2f, 0x1f, 0xc5, + 0xb0, 0x9c, 0xc4, 0xfe, 0x4d, 0xd2, 0x2c, 0xe6, 0x00, 0xb4, 0x0d, 0x65, 0xcb, 0x6c, 0x37, 0x8e, + 0xe1, 0x7e, 0x71, 0x92, 0xad, 0x90, 0xf5, 0x10, 0x0b, 0x47, 0x81, 0xd1, 0x1e, 0x4c, 0x19, 0x8a, + 0x4e, 0x1c, 0x4b, 0x69, 0x91, 0xc6, 0x31, 0x9c, 0xa9, 0x3c, 0xc4, 0x6f, 0x29, 0x92, 0x88, 0x38, + 0x2d, 0x44, 0xfe, 0x45, 0xca, 0x6e, 0xd3, 0xa6, 0xe8, 0x75, 0x28, 0xf1, 0xa7, 0x1e, 0x2d, 0x53, + 0x13, 0x4b, 0xdb, 0x79, 0x36, 0x34, 0x75, 0xd1, 0x76, 0x77, 0x7f, 0xe1, 0x9f, 0x0f, 0x3d, 0x12, + 0xf6, 0x09, 0x71, 0x00, 0x83, 0x36, 0x60, 0xd8, 0x3a, 0x4a, 0x99, 0xc1, 0x17, 0x16, 0x5e, 0x5b, + 0x70, 0x1c, 0xf9, 0xaf, 0x49, 0xc5, 0xf9, 0xf2, 0x72, 0xf3, 0xd8, 0x06, 0x2c, 0x28, 0x6b, 0x72, + 0x07, 0xcd, 0x86, 0x51, 0xb1, 0xca, 0x8a, 0xb8, 0xbc, 0x72, 0x94, 0xb8, 0x8c, 0xae, 0x0c, 0xc1, + 0x26, 0xc2, 0x6f, 0xf4, 0x05, 0xc9, 0x7f, 0x92, 0x60, 0x8a, 0x2b, 0xd4, 0x72, 0x6d, 0x95, 0x76, + 0x07, 0x96, 0x41, 0xb7, 0x63, 0x19, 0x74, 0xa5, 0x47, 0x43, 0x53, 0x1a, 0xe7, 0x66, 0xd1, 0x2f, + 0x25, 0x78, 0x28, 0x45, 0x3d, 0x80, 0x0c, 0x43, 0xe2, 0x19, 0xe6, 0xb5, 0xa3, 0x1a, 0x98, 0x93, + 0x65, 0xbe, 0x5d, 0xce, 0x30, 0x8f, 0x07, 0xee, 0x39, 0x00, 0xcb, 0x56, 0x77, 0x55, 0x8d, 0x74, + 0xc4, 0x25, 0x74, 0x29, 0x1c, 0x92, 0x7a, 0xd0, 0x83, 0x23, 0x54, 0xe8, 0x7f, 0x60, 0xb6, 0x4d, + 0xb6, 0x15, 0x57, 0xa3, 0x4b, 0xed, 0xf6, 0xb2, 0x62, 0x29, 0x5b, 0xaa, 0xa6, 0x52, 0x55, 0xec, + 0xb0, 0xc7, 0x6a, 0x97, 0xbc, 0xcb, 0xe1, 0x2c, 0x8a, 0xbb, 0xfb, 0x0b, 0x4f, 0x1e, 0x7e, 0xa9, + 0xe3, 0x13, 0x77, 0x71, 0x8e, 0x10, 0xf4, 0xff, 0x12, 0x54, 0x6c, 0xf2, 0x9e, 0xab, 0xda, 0xa4, + 0xbd, 0x62, 0x9b, 0x56, 0x4c, 0x83, 0x02, 0xd7, 0xe0, 0xca, 0xc1, 0xfe, 0x42, 0x05, 0xe7, 0xd0, + 0xf4, 0xa2, 0x43, 0xae, 0x20, 0x44, 0x61, 0x5a, 0xd1, 0x34, 0xf3, 0x7d, 0x12, 0xf7, 0xc0, 0x30, + 0x97, 0x5f, 0x3b, 0xd8, 0x5f, 0x98, 0x5e, 0x4a, 0x77, 0xf7, 0x22, 0x3a, 0x0b, 0x1e, 0x2d, 0xc2, + 0xe8, 0xae, 0xa9, 0xb9, 0x3a, 0x71, 0x2a, 0x45, 0x2e, 0x89, 0x65, 0xdc, 0xd1, 0xeb, 0x5e, 0xd3, + 0xdd, 0xfd, 0x85, 0x91, 0xcb, 0x0d, 0x7e, 0xf4, 0xe1, 0x53, 0xb1, 0x3d, 0x1a, 0xab, 0x99, 0xc4, + 0x94, 0xe7, 0x67, 0xb6, 0xa5, 0x30, 0xc7, 0x5c, 0x0d, 0xbb, 0x70, 0x94, 0x0e, 0xe9, 0x30, 0xb6, + 0x23, 0xf6, 0xed, 0x4e, 0x65, 0xb4, 0xaf, 0xd5, 0x2f, 0xb6, 0xef, 0xaf, 0x4d, 0x09, 0x91, 0x63, + 0x7e, 0xb3, 0x83, 0x43, 0x09, 0xe8, 0x29, 0x18, 0xe5, 0x1f, 0xab, 0x2b, 0xfc, 0x00, 0xac, 0x14, + 0x66, 0xa2, 0xab, 0x5e, 0x33, 0xf6, 0xfb, 0x7d, 0xd2, 0xd5, 0xfa, 0x32, 0x3f, 0x98, 0x4d, 0x90, + 0xae, 0xd6, 0x97, 0xb1, 0xdf, 0x8f, 0x2c, 0x18, 0x75, 0xc8, 0x9a, 0x6a, 0xb8, 0x7b, 0x15, 0xe8, + 0xeb, 0xaa, 0xb9, 0x71, 0x89, 0x73, 0x27, 0x8e, 0xa2, 0x42, 0x89, 0xa2, 0x1f, 0xfb, 0x62, 0xd0, + 0x1e, 0x8c, 0xd9, 0xae, 0xb1, 0xe4, 0x6c, 0x3a, 0xc4, 0xae, 0x94, 0xb9, 0xcc, 0x5e, 0x93, 0x33, + 0xf6, 0xf9, 0x93, 0x52, 0x03, 0x0f, 0x06, 0x14, 0x38, 0x14, 0x86, 0x7e, 0x28, 0x01, 0x72, 0x5c, + 0xcb, 0xd2, 0x88, 0x4e, 0x0c, 0xaa, 0x68, 0xfc, 0x34, 0xcc, 0xa9, 0x9c, 0xe4, 0x3a, 0xd4, 0x7b, + 0xb5, 0x3b, 0x05, 0x94, 0x54, 0x26, 0x38, 0xa6, 0x4e, 0x93, 0xe2, 0x0c, 0x3d, 0xd8, 0x50, 0x6c, + 0x3b, 0xfc, 0x77, 0x65, 0xbc, 0xaf, 0xa1, 0xc8, 0x3e, 0x15, 0x0c, 0x87, 0x42, 0xf4, 0x63, 0x5f, + 0x0c, 0xba, 0x0e, 0xb3, 0x36, 0x51, 0xda, 0xd7, 0x0c, 0xad, 0x8b, 0x4d, 0x93, 0x5e, 0x56, 0x35, + 0xe2, 0x74, 0x1d, 0x4a, 0xf4, 0xca, 0x04, 0x0f, 0x9b, 0xe0, 0xa9, 0x07, 0xce, 0xa4, 0xc2, 0x39, + 0xdc, 0xe8, 0x35, 0x38, 0x25, 0x26, 0x26, 0x0f, 0x4d, 0x7e, 0x82, 0x36, 0xc9, 0xa7, 0xe2, 0x0c, + 0xdb, 0x11, 0x2f, 0x25, 0xfa, 0x70, 0x8a, 0x9a, 0xbf, 0x43, 0x10, 0x27, 0xc0, 0x83, 0x79, 0x53, + 0x78, 0xb4, 0x77, 0x08, 0xa1, 0xaa, 0x0f, 0xec, 0x1d, 0x42, 0x44, 0xc4, 0xe1, 0x87, 0x50, 0xdf, + 0x0c, 0xc1, 0x74, 0x48, 0x7c, 0xdf, 0xef, 0x10, 0x32, 0x58, 0x06, 0xf0, 0x0e, 0x21, 0xfb, 0x22, + 0xbf, 0xf0, 0xa0, 0x2f, 0xf2, 0x1f, 0xc0, 0xfb, 0x07, 0xfe, 0x36, 0x20, 0x74, 0xe2, 0xdf, 0xff, + 0xdb, 0x80, 0x50, 0xd7, 0x9c, 0x82, 0xe8, 0x57, 0x43, 0x51, 0x83, 0xfe, 0x81, 0x2e, 0xa0, 0x8f, + 0xfe, 0xe2, 0x52, 0xfe, 0xb2, 0x00, 0xa7, 0x92, 0x33, 0x36, 0x76, 0x0f, 0x29, 0xdd, 0xf3, 0x1e, + 0xb2, 0x0e, 0x33, 0xdb, 0xae, 0xa6, 0x75, 0xb9, 0x43, 0x22, 0x97, 0x91, 0xde, 0xb9, 0xff, 0xa3, + 0x82, 0x73, 0xe6, 0x72, 0x06, 0x0d, 0xce, 0xe4, 0xcc, 0xb9, 0x53, 0x2d, 0xf4, 0x75, 0xa7, 0x9a, + 0xba, 0xd2, 0x1b, 0xee, 0xe1, 0x4a, 0x2f, 0xf3, 0x7e, 0xb4, 0xd8, 0xc7, 0xfd, 0xe8, 0x71, 0x5c, + 0x68, 0x66, 0x24, 0xbe, 0x7b, 0x5d, 0x68, 0xca, 0x8f, 0xc2, 0x9c, 0x60, 0xa3, 0xfc, 0xae, 0xd1, + 0xa0, 0xb6, 0xa9, 0x69, 0xc4, 0x5e, 0x71, 0x75, 0xbd, 0x2b, 0x5f, 0x84, 0x89, 0xf8, 0xad, 0xba, + 0x37, 0xf2, 0xde, 0x45, 0xbf, 0xb8, 0x8d, 0x89, 0x8c, 0xbc, 0xd7, 0x8e, 0x03, 0x0a, 0xf9, 0x23, + 0x09, 0x66, 0xb3, 0x5f, 0xf0, 0x21, 0x0d, 0x26, 0x74, 0x65, 0x2f, 0xfa, 0xfc, 0x51, 0xea, 0x73, + 0xcf, 0xce, 0xaf, 0x4f, 0xd7, 0x63, 0x58, 0x38, 0x81, 0x2d, 0x7f, 0x25, 0xc1, 0xe9, 0x9c, 0x8b, + 0xca, 0xc1, 0x6a, 0x82, 0x6e, 0x40, 0x49, 0x57, 0xf6, 0x1a, 0xae, 0xdd, 0x21, 0x7d, 0x9f, 0x52, + 0xf0, 0x5c, 0xb2, 0x2e, 0x50, 0x70, 0x80, 0x27, 0x7f, 0x2a, 0x41, 0x25, 0xaf, 0xa2, 0x44, 0xe7, + 0x63, 0x57, 0xaa, 0x8f, 0x27, 0xae, 0x54, 0xa7, 0x52, 0x7c, 0x03, 0xba, 0x50, 0xfd, 0x4c, 0x82, + 0xd9, 0xec, 0xca, 0x1b, 0x3d, 0x17, 0xd3, 0x78, 0x21, 0xa1, 0xf1, 0x64, 0x82, 0x4b, 0xe8, 0xbb, + 0x03, 0x13, 0xa2, 0x3e, 0x17, 0x30, 0xc2, 0xcb, 0x4f, 0x1f, 0x9e, 0x55, 0x05, 0x98, 0x5f, 0x69, + 0xf2, 0x91, 0x8c, 0xb7, 0xe1, 0x04, 0xae, 0xfc, 0x93, 0x21, 0x28, 0x36, 0x5a, 0x8a, 0x46, 0x06, + 0x50, 0xd4, 0xdd, 0x88, 0x15, 0x75, 0xbd, 0xfe, 0xdf, 0x81, 0x6b, 0x99, 0x5b, 0xcf, 0x6d, 0x25, + 0xea, 0xb9, 0x0b, 0x7d, 0xa1, 0x1f, 0x5e, 0xca, 0xbd, 0x08, 0x63, 0x81, 0x12, 0xbd, 0xad, 0x1e, + 0xf2, 0x27, 0x43, 0x50, 0x8e, 0x88, 0xe8, 0x71, 0xed, 0xd9, 0x8d, 0xad, 0xde, 0xfd, 0xfc, 0xb5, + 0x2a, 0x22, 0xbb, 0xea, 0xaf, 0xdf, 0xde, 0x0b, 0xbe, 0xf0, 0x25, 0x56, 0x7a, 0x59, 0xbf, 0x08, + 0x13, 0x94, 0xff, 0xd3, 0x28, 0x38, 0x25, 0x2c, 0xf0, 0x28, 0x0e, 0xde, 0x85, 0x36, 0x63, 0xbd, + 0x38, 0x41, 0x3d, 0xf7, 0x12, 0x8c, 0xc7, 0x84, 0xf5, 0xf4, 0xe0, 0xee, 0xd7, 0x12, 0x3c, 0x7e, + 0xcf, 0x5d, 0x1d, 0xaa, 0xc5, 0xa6, 0x57, 0x35, 0x31, 0xbd, 0xe6, 0xf3, 0x01, 0x06, 0xf8, 0xdc, + 0xe2, 0xfb, 0x43, 0x80, 0x9a, 0x3b, 0xaa, 0xdd, 0xae, 0x2b, 0x36, 0xed, 0x62, 0xf1, 0x7f, 0xb2, + 0x01, 0x4c, 0xb8, 0xf3, 0x50, 0x6e, 0x13, 0xa7, 0x65, 0xab, 0xdc, 0x59, 0x62, 0xaf, 0x10, 0x9c, + 0xa4, 0xac, 0x84, 0x5d, 0x38, 0x4a, 0x87, 0x3a, 0x50, 0xda, 0xf5, 0xfe, 0xb1, 0xe8, 0xdf, 0xdd, + 0xf5, 0x5a, 0xcc, 0x86, 0xff, 0x79, 0x0c, 0xe3, 0x4b, 0x34, 0x38, 0x38, 0x00, 0x97, 0x3f, 0x96, + 0x60, 0x36, 0xed, 0x98, 0x15, 0xa6, 0xfa, 0x83, 0x77, 0xce, 0xa3, 0x30, 0xcc, 0xd1, 0x99, 0x57, + 0x4e, 0x7a, 0x67, 0xe6, 0x4c, 0x32, 0xe6, 0xad, 0xf2, 0xd7, 0x12, 0xcc, 0x65, 0xab, 0x36, 0x80, + 0xad, 0xc4, 0xcd, 0xf8, 0x56, 0xa2, 0xd7, 0x83, 0x87, 0x6c, 0xbd, 0x73, 0xb6, 0x15, 0xfb, 0x99, + 0x63, 0x30, 0x00, 0x23, 0xb7, 0xe3, 0x46, 0x2e, 0x1d, 0xd9, 0xc8, 0x6c, 0x03, 0x6b, 0x4f, 0xdd, + 0xbe, 0x33, 0x7f, 0xe2, 0xf3, 0x3b, 0xf3, 0x27, 0xbe, 0xb8, 0x33, 0x7f, 0xe2, 0xc3, 0x83, 0x79, + 0xe9, 0xf6, 0xc1, 0xbc, 0xf4, 0xf9, 0xc1, 0xbc, 0xf4, 0xe7, 0x83, 0x79, 0xe9, 0xbb, 0x5f, 0xcd, + 0x9f, 0xb8, 0x31, 0x2a, 0x30, 0xff, 0x16, 0x00, 0x00, 0xff, 0xff, 0x9e, 0x01, 0x30, 0x7b, 0xbe, + 0x3d, 0x00, 0x00, } diff --git a/pkg/apis/extensions/v1beta1/generated.proto b/pkg/apis/extensions/v1beta1/generated.proto index 6611e1b0b6d..2fd829c7db8 100644 --- a/pkg/apis/extensions/v1beta1/generated.proto +++ b/pkg/apis/extensions/v1beta1/generated.proto @@ -123,10 +123,17 @@ message DaemonSetSpec { // +optional optional int32 minReadySeconds = 4; + // DEPRECATED. // A sequence number representing a specific generation of the template. // Populated by the system. It can be set only during the creation. // +optional optional int64 templateGeneration = 5; + + // The number of old history to retain to allow rollback. + // This is a pointer to distinguish between explicit zero and not specified. + // Defaults to 10. + // +optional + optional int32 revisionHistoryLimit = 6; } // DaemonSetStatus represents the current status of a daemon set. @@ -169,6 +176,12 @@ message DaemonSetStatus { // (ready for at least spec.minReadySeconds) // +optional optional int32 numberUnavailable = 8; + + // Count of hash collisions for the DaemonSet. The DaemonSet controller + // uses this field as a collision avoidance mechanism when it needs to + // create the name for the newest ControllerRevision. + // +optional + optional int64 collisionCount = 9; } message DaemonSetUpdateStrategy { @@ -180,7 +193,7 @@ message DaemonSetUpdateStrategy { // Rolling update config params. Present only if type = "RollingUpdate". // --- // TODO: Update this to follow our convention for oneOf, whatever we decide it - // to be. Same as DeploymentStrategy.RollingUpdate. + // to be. Same as Deployment `strategy.rollingUpdate`. // See https://github.com/kubernetes/kubernetes/issues/35345 // +optional optional RollingUpdateDaemonSet rollingUpdate = 2; diff --git a/pkg/apis/extensions/v1beta1/types.generated.go b/pkg/apis/extensions/v1beta1/types.generated.go index 8a542d51cca..5d65fdc480c 100644 --- a/pkg/apis/extensions/v1beta1/types.generated.go +++ b/pkg/apis/extensions/v1beta1/types.generated.go @@ -7552,16 +7552,17 @@ func (x *DaemonSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [5]bool + var yyq2 [6]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[0] = x.Selector != nil yyq2[2] = true yyq2[3] = x.MinReadySeconds != 0 yyq2[4] = x.TemplateGeneration != 0 + yyq2[5] = x.RevisionHistoryLimit != nil var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(5) + r.EncodeArrayStart(6) } else { yynn2 = 1 for _, b := range yyq2 { @@ -7685,6 +7686,41 @@ func (x *DaemonSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { } } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[5] { + if x.RevisionHistoryLimit == nil { + r.EncodeNil() + } else { + yy23 := *x.RevisionHistoryLimit + yym24 := z.EncBinary() + _ = yym24 + if false { + } else { + r.EncodeInt(int64(yy23)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[5] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("revisionHistoryLimit")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.RevisionHistoryLimit == nil { + r.EncodeNil() + } else { + yy25 := *x.RevisionHistoryLimit + yym26 := z.EncBinary() + _ = yym26 + if false { + } else { + r.EncodeInt(int64(yy25)) + } + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -7801,6 +7837,22 @@ func (x *DaemonSetSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { *((*int64)(yyv10)) = int64(r.DecodeInt(64)) } } + case "revisionHistoryLimit": + if r.TryDecodeAsNil() { + if x.RevisionHistoryLimit != nil { + x.RevisionHistoryLimit = nil + } + } else { + if x.RevisionHistoryLimit == nil { + x.RevisionHistoryLimit = new(int32) + } + yym13 := z.DecBinary() + _ = yym13 + if false { + } else { + *((*int32)(x.RevisionHistoryLimit)) = int32(r.DecodeInt(32)) + } + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -7812,16 +7864,16 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj12 int - var yyb12 bool - var yyhl12 bool = l >= 0 - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + var yyj14 int + var yyb14 bool + var yyhl14 bool = l >= 0 + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7834,21 +7886,21 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if x.Selector == nil { x.Selector = new(pkg1_v1.LabelSelector) } - yym14 := z.DecBinary() - _ = yym14 + yym16 := z.DecBinary() + _ = yym16 if false { } else if z.HasExtensions() && z.DecExt(x.Selector) { } else { z.DecFallback(x.Selector, false) } } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7856,16 +7908,16 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.Template = pkg4_v1.PodTemplateSpec{} } else { - yyv15 := &x.Template - yyv15.CodecDecodeSelf(d) + yyv17 := &x.Template + yyv17.CodecDecodeSelf(d) } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7873,16 +7925,16 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.UpdateStrategy = DaemonSetUpdateStrategy{} } else { - yyv16 := &x.UpdateStrategy - yyv16.CodecDecodeSelf(d) + yyv18 := &x.UpdateStrategy + yyv18.CodecDecodeSelf(d) } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7890,21 +7942,21 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.MinReadySeconds = 0 } else { - yyv17 := &x.MinReadySeconds - yym18 := z.DecBinary() - _ = yym18 + yyv19 := &x.MinReadySeconds + yym20 := z.DecBinary() + _ = yym20 if false { } else { - *((*int32)(yyv17)) = int32(r.DecodeInt(32)) + *((*int32)(yyv19)) = int32(r.DecodeInt(32)) } } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7912,26 +7964,52 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.TemplateGeneration = 0 } else { - yyv19 := &x.TemplateGeneration - yym20 := z.DecBinary() - _ = yym20 + yyv21 := &x.TemplateGeneration + yym22 := z.DecBinary() + _ = yym22 if false { } else { - *((*int64)(yyv19)) = int64(r.DecodeInt(64)) + *((*int64)(yyv21)) = int64(r.DecodeInt(64)) + } + } + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l + } else { + yyb14 = r.CheckBreak() + } + if yyb14 { + 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) + } + yym24 := z.DecBinary() + _ = yym24 + if false { + } else { + *((*int32)(x.RevisionHistoryLimit)) = int32(r.DecodeInt(32)) } } for { - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj12-1, "") + z.DecStructFieldNotFound(yyj14-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -7950,16 +8028,17 @@ func (x *DaemonSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [8]bool + var yyq2 [9]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[4] = x.ObservedGeneration != 0 yyq2[5] = x.UpdatedNumberScheduled != 0 yyq2[6] = x.NumberAvailable != 0 yyq2[7] = x.NumberUnavailable != 0 + yyq2[8] = x.CollisionCount != nil var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(8) + r.EncodeArrayStart(9) } else { yynn2 = 4 for _, b := range yyq2 { @@ -8146,6 +8225,41 @@ func (x *DaemonSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { } } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[8] { + if x.CollisionCount == nil { + r.EncodeNil() + } else { + yy28 := *x.CollisionCount + yym29 := z.EncBinary() + _ = yym29 + if false { + } else { + r.EncodeInt(int64(yy28)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[8] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("collisionCount")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.CollisionCount == nil { + r.EncodeNil() + } else { + yy30 := *x.CollisionCount + yym31 := z.EncBinary() + _ = yym31 + if false { + } else { + r.EncodeInt(int64(yy30)) + } + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -8303,6 +8417,22 @@ func (x *DaemonSetStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { *((*int32)(yyv18)) = int32(r.DecodeInt(32)) } } + case "collisionCount": + if r.TryDecodeAsNil() { + if x.CollisionCount != nil { + x.CollisionCount = nil + } + } else { + if x.CollisionCount == nil { + x.CollisionCount = new(int64) + } + yym21 := z.DecBinary() + _ = yym21 + if false { + } else { + *((*int64)(x.CollisionCount)) = int64(r.DecodeInt(64)) + } + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -8314,16 +8444,16 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj20 int - var yyb20 bool - var yyhl20 bool = l >= 0 - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + var yyj22 int + var yyb22 bool + var yyhl22 bool = l >= 0 + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -8331,29 +8461,7 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.CurrentNumberScheduled = 0 } else { - yyv21 := &x.CurrentNumberScheduled - yym22 := z.DecBinary() - _ = yym22 - if false { - } else { - *((*int32)(yyv21)) = int32(r.DecodeInt(32)) - } - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.NumberMisscheduled = 0 - } else { - yyv23 := &x.NumberMisscheduled + yyv23 := &x.CurrentNumberScheduled yym24 := z.DecBinary() _ = yym24 if false { @@ -8361,21 +8469,21 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) *((*int32)(yyv23)) = int32(r.DecodeInt(32)) } } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.DesiredNumberScheduled = 0 + x.NumberMisscheduled = 0 } else { - yyv25 := &x.DesiredNumberScheduled + yyv25 := &x.NumberMisscheduled yym26 := z.DecBinary() _ = yym26 if false { @@ -8383,21 +8491,21 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) *((*int32)(yyv25)) = int32(r.DecodeInt(32)) } } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.NumberReady = 0 + x.DesiredNumberScheduled = 0 } else { - yyv27 := &x.NumberReady + yyv27 := &x.DesiredNumberScheduled yym28 := z.DecBinary() _ = yym28 if false { @@ -8405,13 +8513,35 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) *((*int32)(yyv27)) = int32(r.DecodeInt(32)) } } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.NumberReady = 0 + } else { + yyv29 := &x.NumberReady + yym30 := z.DecBinary() + _ = yym30 + if false { + } else { + *((*int32)(yyv29)) = int32(r.DecodeInt(32)) + } + } + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l + } else { + yyb22 = r.CheckBreak() + } + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -8419,21 +8549,21 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.ObservedGeneration = 0 } else { - yyv29 := &x.ObservedGeneration - yym30 := z.DecBinary() - _ = yym30 + yyv31 := &x.ObservedGeneration + yym32 := z.DecBinary() + _ = yym32 if false { } else { - *((*int64)(yyv29)) = int64(r.DecodeInt(64)) + *((*int64)(yyv31)) = int64(r.DecodeInt(64)) } } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -8441,29 +8571,7 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.UpdatedNumberScheduled = 0 } else { - yyv31 := &x.UpdatedNumberScheduled - yym32 := z.DecBinary() - _ = yym32 - if false { - } else { - *((*int32)(yyv31)) = int32(r.DecodeInt(32)) - } - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.NumberAvailable = 0 - } else { - yyv33 := &x.NumberAvailable + yyv33 := &x.UpdatedNumberScheduled yym34 := z.DecBinary() _ = yym34 if false { @@ -8471,21 +8579,21 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) *((*int32)(yyv33)) = int32(r.DecodeInt(32)) } } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.NumberUnavailable = 0 + x.NumberAvailable = 0 } else { - yyv35 := &x.NumberUnavailable + yyv35 := &x.NumberAvailable yym36 := z.DecBinary() _ = yym36 if false { @@ -8493,18 +8601,66 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) *((*int32)(yyv35)) = int32(r.DecodeInt(32)) } } - for { - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l + } else { + yyb22 = r.CheckBreak() + } + if yyb22 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.NumberUnavailable = 0 + } else { + yyv37 := &x.NumberUnavailable + yym38 := z.DecBinary() + _ = yym38 + if false { } else { - yyb20 = r.CheckBreak() + *((*int32)(yyv37)) = int32(r.DecodeInt(32)) } - if yyb20 { + } + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l + } else { + yyb22 = r.CheckBreak() + } + if yyb22 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.CollisionCount != nil { + x.CollisionCount = nil + } + } else { + if x.CollisionCount == nil { + x.CollisionCount = new(int64) + } + yym40 := z.DecBinary() + _ = yym40 + if false { + } else { + *((*int64)(x.CollisionCount)) = int64(r.DecodeInt(64)) + } + } + for { + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l + } else { + yyb22 = r.CheckBreak() + } + if yyb22 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj20-1, "") + z.DecStructFieldNotFound(yyj22-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -19917,7 +20073,7 @@ func (x codecSelfer1234) decSliceDaemonSet(v *[]DaemonSet, d *codec1978.Decoder) yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 912) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 928) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] diff --git a/pkg/apis/extensions/v1beta1/types.go b/pkg/apis/extensions/v1beta1/types.go index e750f560635..a65af07d64b 100644 --- a/pkg/apis/extensions/v1beta1/types.go +++ b/pkg/apis/extensions/v1beta1/types.go @@ -459,7 +459,7 @@ type DaemonSetSpec struct { // This is a pointer to distinguish between explicit zero and not specified. // Defaults to 10. // +optional - RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"` + RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,6,opt,name=revisionHistoryLimit"` } // DaemonSetStatus represents the current status of a daemon set. diff --git a/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go b/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go index 213f8494ad0..a92590e6b43 100644 --- a/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go +++ b/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go @@ -77,12 +77,13 @@ func (DaemonSetList) SwaggerDoc() map[string]string { } var map_DaemonSetSpec = map[string]string{ - "": "DaemonSetSpec is the specification of a daemon set.", - "selector": "A label query over pods that are managed by the daemon set. Must match in order to be controlled. If empty, defaulted to labels on Pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", - "template": "An object that describes the pod that will be created. The DaemonSet will create exactly one copy of this pod on every node that matches the template's node selector (or on every node if no node selector is specified). More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template", - "updateStrategy": "An update strategy to replace existing DaemonSet pods with new pods.", - "minReadySeconds": "The minimum number of seconds for which a newly created DaemonSet pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready).", - "templateGeneration": "A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.", + "": "DaemonSetSpec is the specification of a daemon set.", + "selector": "A label query over pods that are managed by the daemon set. Must match in order to be controlled. If empty, defaulted to labels on Pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", + "template": "An object that describes the pod that will be created. The DaemonSet will create exactly one copy of this pod on every node that matches the template's node selector (or on every node if no node selector is specified). More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template", + "updateStrategy": "An update strategy to replace existing DaemonSet pods with new pods.", + "minReadySeconds": "The minimum number of seconds for which a newly created DaemonSet pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready).", + "templateGeneration": "DEPRECATED. A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.", + "revisionHistoryLimit": "The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", } func (DaemonSetSpec) SwaggerDoc() map[string]string { @@ -99,6 +100,7 @@ var map_DaemonSetStatus = map[string]string{ "updatedNumberScheduled": "The total number of nodes that are running updated daemon pod", "numberAvailable": "The number of nodes that should be running the daemon pod and have one or more of the daemon pod running and available (ready for at least spec.minReadySeconds)", "numberUnavailable": "The number of nodes that should be running the daemon pod and have none of the daemon pod running and available (ready for at least spec.minReadySeconds)", + "collisionCount": "Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", } func (DaemonSetStatus) SwaggerDoc() map[string]string { diff --git a/pkg/apis/extensions/v1beta1/zz_generated.conversion.go b/pkg/apis/extensions/v1beta1/zz_generated.conversion.go index b904ec7a5f0..403f14f0a48 100644 --- a/pkg/apis/extensions/v1beta1/zz_generated.conversion.go +++ b/pkg/apis/extensions/v1beta1/zz_generated.conversion.go @@ -21,8 +21,6 @@ limitations under the License. package v1beta1 import ( - unsafe "unsafe" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" @@ -30,6 +28,7 @@ import ( api "k8s.io/kubernetes/pkg/api" api_v1 "k8s.io/kubernetes/pkg/api/v1" extensions "k8s.io/kubernetes/pkg/apis/extensions" + unsafe "unsafe" ) func init() { @@ -353,6 +352,7 @@ func autoConvert_v1beta1_DaemonSetSpec_To_extensions_DaemonSetSpec(in *DaemonSet } out.MinReadySeconds = in.MinReadySeconds out.TemplateGeneration = in.TemplateGeneration + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) return nil } @@ -371,6 +371,7 @@ func autoConvert_extensions_DaemonSetSpec_To_v1beta1_DaemonSetSpec(in *extension } out.MinReadySeconds = in.MinReadySeconds out.TemplateGeneration = in.TemplateGeneration + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) return nil } @@ -388,6 +389,7 @@ func autoConvert_v1beta1_DaemonSetStatus_To_extensions_DaemonSetStatus(in *Daemo out.UpdatedNumberScheduled = in.UpdatedNumberScheduled out.NumberAvailable = in.NumberAvailable out.NumberUnavailable = in.NumberUnavailable + out.CollisionCount = (*int64)(unsafe.Pointer(in.CollisionCount)) return nil } @@ -405,6 +407,7 @@ func autoConvert_extensions_DaemonSetStatus_To_v1beta1_DaemonSetStatus(in *exten out.UpdatedNumberScheduled = in.UpdatedNumberScheduled out.NumberAvailable = in.NumberAvailable out.NumberUnavailable = in.NumberUnavailable + out.CollisionCount = (*int64)(unsafe.Pointer(in.CollisionCount)) return nil } diff --git a/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go b/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go index cdbd523b62d..3c845f5c055 100644 --- a/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go @@ -21,13 +21,12 @@ limitations under the License. package v1beta1 import ( - reflect "reflect" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" intstr "k8s.io/apimachinery/pkg/util/intstr" api_v1 "k8s.io/kubernetes/pkg/api/v1" + reflect "reflect" ) func init() { @@ -183,6 +182,9 @@ func DeepCopy_v1beta1_DaemonSet(in interface{}, out interface{}, c *conversion.C if err := DeepCopy_v1beta1_DaemonSetSpec(&in.Spec, &out.Spec, c); err != nil { return err } + if err := DeepCopy_v1beta1_DaemonSetStatus(&in.Status, &out.Status, c); err != nil { + return err + } return nil } } @@ -226,6 +228,11 @@ func DeepCopy_v1beta1_DaemonSetSpec(in interface{}, out interface{}, c *conversi if err := DeepCopy_v1beta1_DaemonSetUpdateStrategy(&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 } } @@ -236,6 +243,11 @@ func DeepCopy_v1beta1_DaemonSetStatus(in interface{}, out interface{}, c *conver in := in.(*DaemonSetStatus) out := out.(*DaemonSetStatus) *out = *in + if in.CollisionCount != nil { + in, out := &in.CollisionCount, &out.CollisionCount + *out = new(int64) + **out = **in + } return nil } } diff --git a/pkg/apis/extensions/zz_generated.deepcopy.go b/pkg/apis/extensions/zz_generated.deepcopy.go index b6aa4d1dee1..97c61d53005 100644 --- a/pkg/apis/extensions/zz_generated.deepcopy.go +++ b/pkg/apis/extensions/zz_generated.deepcopy.go @@ -21,13 +21,12 @@ limitations under the License. package extensions import ( - reflect "reflect" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" conversion "k8s.io/apimachinery/pkg/conversion" runtime "k8s.io/apimachinery/pkg/runtime" intstr "k8s.io/apimachinery/pkg/util/intstr" api "k8s.io/kubernetes/pkg/api" + reflect "reflect" ) func init() { @@ -184,6 +183,9 @@ func DeepCopy_extensions_DaemonSet(in interface{}, out interface{}, c *conversio if err := DeepCopy_extensions_DaemonSetSpec(&in.Spec, &out.Spec, c); err != nil { return err } + if err := DeepCopy_extensions_DaemonSetStatus(&in.Status, &out.Status, c); err != nil { + return err + } return nil } } @@ -227,6 +229,11 @@ func DeepCopy_extensions_DaemonSetSpec(in interface{}, out interface{}, c *conve if err := DeepCopy_extensions_DaemonSetUpdateStrategy(&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 } } @@ -237,6 +244,11 @@ func DeepCopy_extensions_DaemonSetStatus(in interface{}, out interface{}, c *con in := in.(*DaemonSetStatus) out := out.(*DaemonSetStatus) *out = *in + if in.CollisionCount != nil { + in, out := &in.CollisionCount, &out.CollisionCount + *out = new(int64) + **out = **in + } return nil } } diff --git a/pkg/controller/daemon/BUILD b/pkg/controller/daemon/BUILD index b97c2024098..74c714d0c7e 100644 --- a/pkg/controller/daemon/BUILD +++ b/pkg/controller/daemon/BUILD @@ -32,6 +32,7 @@ go_library( "//pkg/controller/daemon/util:go_default_library", "//pkg/features:go_default_library", "//pkg/kubelet/types:go_default_library", + "//pkg/util/labels:go_default_library", "//pkg/util/metrics:go_default_library", "//plugin/pkg/scheduler/algorithm:go_default_library", "//plugin/pkg/scheduler/algorithm/predicates:go_default_library", @@ -72,6 +73,7 @@ go_test( "//pkg/controller:go_default_library", "//pkg/kubelet/types:go_default_library", "//pkg/securitycontext:go_default_library", + "//pkg/util/labels:go_default_library", "//plugin/pkg/scheduler/algorithm:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/pkg/controller/deployment/util/BUILD b/pkg/controller/deployment/util/BUILD index 9f55884588a..e2e00b3ee60 100644 --- a/pkg/controller/deployment/util/BUILD +++ b/pkg/controller/deployment/util/BUILD @@ -28,7 +28,6 @@ go_library( "//pkg/client/listers/extensions/v1beta1:go_default_library", "//pkg/client/retry:go_default_library", "//pkg/controller:go_default_library", - "//pkg/util/hash:go_default_library", "//pkg/util/labels:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", @@ -48,7 +47,6 @@ go_test( srcs = [ "deployment_util_test.go", "hash_test.go", - "pod_util_test.go", ], library = ":go_default_library", tags = ["automanaged"], diff --git a/staging/src/k8s.io/client-go/listers/extensions/v1beta1/BUILD b/staging/src/k8s.io/client-go/listers/extensions/v1beta1/BUILD index 5549d3938d6..b68b5bc6b85 100644 --- a/staging/src/k8s.io/client-go/listers/extensions/v1beta1/BUILD +++ b/staging/src/k8s.io/client-go/listers/extensions/v1beta1/BUILD @@ -28,6 +28,7 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/client-go/pkg/api/v1:go_default_library", + "//vendor/k8s.io/client-go/pkg/apis/apps/v1beta1:go_default_library", "//vendor/k8s.io/client-go/pkg/apis/extensions/v1beta1:go_default_library", "//vendor/k8s.io/client-go/tools/cache:go_default_library", ], diff --git a/staging/src/k8s.io/client-go/listers/extensions/v1beta1/daemonset_expansion.go b/staging/src/k8s.io/client-go/listers/extensions/v1beta1/daemonset_expansion.go index bf9c6381331..d2788907bac 100644 --- a/staging/src/k8s.io/client-go/listers/extensions/v1beta1/daemonset_expansion.go +++ b/staging/src/k8s.io/client-go/listers/extensions/v1beta1/daemonset_expansion.go @@ -22,6 +22,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/pkg/api/v1" + apps "k8s.io/client-go/pkg/apis/apps/v1beta1" "k8s.io/client-go/pkg/apis/extensions/v1beta1" ) @@ -29,6 +30,7 @@ import ( // DaemonSetLister. type DaemonSetListerExpansion interface { GetPodDaemonSets(pod *v1.Pod) ([]*v1beta1.DaemonSet, error) + GetHistoryDaemonSets(history *apps.ControllerRevision) ([]*v1beta1.DaemonSet, error) } // DaemonSetNamespaceListerExpansion allows custom methods to be added to @@ -76,3 +78,37 @@ func (s *daemonSetLister) GetPodDaemonSets(pod *v1.Pod) ([]*v1beta1.DaemonSet, e return daemonSets, nil } + +// GetHistoryDaemonSets returns a list of DaemonSets that potentially +// match a ControllerRevision. Only the one specified in the ControllerRevision's ControllerRef +// will actually manage it. +// Returns an error only if no matching DaemonSets are found. +func (s *daemonSetLister) GetHistoryDaemonSets(history *apps.ControllerRevision) ([]*v1beta1.DaemonSet, error) { + if len(history.Labels) == 0 { + return nil, fmt.Errorf("no DaemonSet found for ControllerRevision %s because it has no labels", history.Name) + } + + list, err := s.DaemonSets(history.Namespace).List(labels.Everything()) + if err != nil { + return nil, err + } + + var daemonSets []*v1beta1.DaemonSet + for _, ds := range list { + selector, err := metav1.LabelSelectorAsSelector(ds.Spec.Selector) + if err != nil { + return nil, fmt.Errorf("invalid label selector: %v", err) + } + // If a DaemonSet with a nil or empty selector creeps in, it should match nothing, not everything. + if selector.Empty() || !selector.Matches(labels.Set(history.Labels)) { + continue + } + daemonSets = append(daemonSets, ds) + } + + if len(daemonSets) == 0 { + return nil, fmt.Errorf("could not find DaemonSets for ControllerRevision %s in namespace %s with labels: %v", history.Name, history.Namespace, history.Labels) + } + + return daemonSets, nil +} diff --git a/staging/src/k8s.io/client-go/pkg/apis/extensions/types.go b/staging/src/k8s.io/client-go/pkg/apis/extensions/types.go index 3b86ce7ccc6..ae7e23a0063 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/extensions/types.go +++ b/staging/src/k8s.io/client-go/pkg/apis/extensions/types.go @@ -386,7 +386,7 @@ type DaemonSetUpdateStrategy struct { // Rolling update config params. Present only if type = "RollingUpdate". //--- // TODO: Update this to follow our convention for oneOf, whatever we decide it - // to be. Same as DeploymentStrategy.RollingUpdate. + // to be. Same as Deployment `strategy.rollingUpdate`. // See https://github.com/kubernetes/kubernetes/issues/35345 // +optional RollingUpdate *RollingUpdateDaemonSet @@ -449,10 +449,17 @@ type DaemonSetSpec struct { // +optional MinReadySeconds int32 + // DEPRECATED. // A sequence number representing a specific generation of the template. // Populated by the system. It can be set only during the creation. // +optional TemplateGeneration int64 + + // The number of old history to retain to allow rollback. + // This is a pointer to distinguish between explicit zero and not specified. + // Defaults to 10. + // +optional + RevisionHistoryLimit *int32 } // DaemonSetStatus represents the current status of a daemon set. @@ -492,6 +499,12 @@ type DaemonSetStatus struct { // (ready for at least spec.minReadySeconds) // +optional NumberUnavailable int32 + + // Count of hash collisions for the DaemonSet. The DaemonSet controller + // uses this field as a collision avoidance mechanism when it needs to + // create the name for the newest ControllerRevision. + // +optional + CollisionCount *int64 } // +genclient=true @@ -519,10 +532,16 @@ type DaemonSet struct { } const ( + // DEPRECATED: DefaultDaemonSetUniqueLabelKey is used instead. // DaemonSetTemplateGenerationKey is the key of the labels that is added // to daemon set pods to distinguish between old and new pod templates // during DaemonSet template update. DaemonSetTemplateGenerationKey string = "pod-template-generation" + + // DefaultDaemonSetUniqueLabelKey is the default label key that is added + // to existing DaemonSet pods to distinguish between old and new + // DaemonSet pods during DaemonSet template updates. + DefaultDaemonSetUniqueLabelKey string = "daemonset-controller-hash" ) // DaemonSetList is a collection of daemon sets. diff --git a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/defaults.go b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/defaults.go index 561917721c0..a644e5ab9d3 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/defaults.go +++ b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/defaults.go @@ -56,6 +56,10 @@ func SetDefaults_DaemonSet(obj *DaemonSet) { updateStrategy.RollingUpdate.MaxUnavailable = &maxUnavailable } } + if obj.Spec.RevisionHistoryLimit == nil { + obj.Spec.RevisionHistoryLimit = new(int32) + *obj.Spec.RevisionHistoryLimit = 10 + } } func SetDefaults_Deployment(obj *Deployment) { diff --git a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/generated.pb.go b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/generated.pb.go index 4152966113a..76898f5d98f 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/generated.pb.go +++ b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/generated.pb.go @@ -689,6 +689,11 @@ func (m *DaemonSetSpec) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x28 i++ i = encodeVarintGenerated(dAtA, i, uint64(m.TemplateGeneration)) + if m.RevisionHistoryLimit != nil { + dAtA[i] = 0x30 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(*m.RevisionHistoryLimit)) + } return i, nil } @@ -731,6 +736,11 @@ func (m *DaemonSetStatus) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x40 i++ i = encodeVarintGenerated(dAtA, i, uint64(m.NumberUnavailable)) + if m.CollisionCount != nil { + dAtA[i] = 0x48 + i++ + i = encodeVarintGenerated(dAtA, i, uint64(*m.CollisionCount)) + } return i, nil } @@ -2795,6 +2805,9 @@ func (m *DaemonSetSpec) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) n += 1 + sovGenerated(uint64(m.MinReadySeconds)) n += 1 + sovGenerated(uint64(m.TemplateGeneration)) + if m.RevisionHistoryLimit != nil { + n += 1 + sovGenerated(uint64(*m.RevisionHistoryLimit)) + } return n } @@ -2809,6 +2822,9 @@ func (m *DaemonSetStatus) Size() (n int) { n += 1 + sovGenerated(uint64(m.UpdatedNumberScheduled)) n += 1 + sovGenerated(uint64(m.NumberAvailable)) n += 1 + sovGenerated(uint64(m.NumberUnavailable)) + if m.CollisionCount != nil { + n += 1 + sovGenerated(uint64(*m.CollisionCount)) + } return n } @@ -3605,6 +3621,7 @@ func (this *DaemonSetSpec) String() string { `UpdateStrategy:` + strings.Replace(strings.Replace(this.UpdateStrategy.String(), "DaemonSetUpdateStrategy", "DaemonSetUpdateStrategy", 1), `&`, ``, 1) + `,`, `MinReadySeconds:` + fmt.Sprintf("%v", this.MinReadySeconds) + `,`, `TemplateGeneration:` + fmt.Sprintf("%v", this.TemplateGeneration) + `,`, + `RevisionHistoryLimit:` + valueToStringGenerated(this.RevisionHistoryLimit) + `,`, `}`, }, "") return s @@ -3622,6 +3639,7 @@ func (this *DaemonSetStatus) String() string { `UpdatedNumberScheduled:` + fmt.Sprintf("%v", this.UpdatedNumberScheduled) + `,`, `NumberAvailable:` + fmt.Sprintf("%v", this.NumberAvailable) + `,`, `NumberUnavailable:` + fmt.Sprintf("%v", this.NumberUnavailable) + `,`, + `CollisionCount:` + valueToStringGenerated(this.CollisionCount) + `,`, `}`, }, "") return s @@ -5102,6 +5120,26 @@ func (m *DaemonSetSpec) Unmarshal(dAtA []byte) error { break } } + case 6: + 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:]) @@ -5304,6 +5342,26 @@ func (m *DaemonSetStatus) Unmarshal(dAtA []byte) error { break } } + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CollisionCount", wireType) + } + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.CollisionCount = &v default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -11873,219 +11931,220 @@ func init() { } var fileDescriptorGenerated = []byte{ - // 3412 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5b, 0xdd, 0x6f, 0x1c, 0x57, - 0x15, 0xcf, 0x78, 0xbd, 0xf1, 0xfa, 0x6c, 0x6c, 0xc7, 0xd7, 0xae, 0xb3, 0x75, 0x5b, 0x3b, 0x1d, - 0x44, 0x9b, 0x42, 0xbb, 0xa6, 0x29, 0x29, 0x6d, 0xda, 0xa6, 0xf5, 0xda, 0xf9, 0x30, 0xb2, 0x9d, - 0xed, 0xdd, 0x75, 0x44, 0xd3, 0x2f, 0xc6, 0xbb, 0xd7, 0xeb, 0x49, 0xe6, 0xab, 0x33, 0x77, 0x5c, - 0xaf, 0x2a, 0xa0, 0x12, 0xa2, 0x0f, 0x3c, 0x20, 0x78, 0x41, 0x45, 0x82, 0x47, 0x1e, 0x78, 0x81, - 0xf6, 0x01, 0x0a, 0x7f, 0x01, 0x79, 0x40, 0xa8, 0x48, 0x20, 0x55, 0xa8, 0x58, 0xc4, 0x15, 0xfd, - 0x07, 0xa0, 0x2f, 0x79, 0x42, 0xf7, 0xce, 0x9d, 0xef, 0x19, 0x27, 0xbb, 0x4e, 0x56, 0x48, 0xbc, - 0xed, 0xdc, 0x7b, 0xce, 0xef, 0x7c, 0xdc, 0x73, 0xcf, 0x3d, 0xf7, 0x63, 0xe1, 0x85, 0xeb, 0xcf, - 0x38, 0x55, 0xd5, 0x5c, 0xb8, 0xee, 0x6e, 0x12, 0xdb, 0x20, 0x94, 0x38, 0x0b, 0xd6, 0xf5, 0xce, - 0x82, 0x62, 0xa9, 0xce, 0x02, 0xd9, 0xa5, 0xc4, 0x70, 0x54, 0xd3, 0x70, 0x16, 0x76, 0x9e, 0xdc, - 0x24, 0x54, 0x79, 0x72, 0xa1, 0x43, 0x0c, 0x62, 0x2b, 0x94, 0xb4, 0xab, 0x96, 0x6d, 0x52, 0x13, - 0x3d, 0xe1, 0xb1, 0x57, 0x43, 0xf6, 0xaa, 0x75, 0xbd, 0x53, 0x65, 0xec, 0xd5, 0x90, 0xbd, 0x2a, - 0xd8, 0x67, 0x9f, 0xe8, 0xa8, 0x74, 0xdb, 0xdd, 0xac, 0xb6, 0x4c, 0x7d, 0xa1, 0x63, 0x76, 0xcc, - 0x05, 0x8e, 0xb2, 0xe9, 0x6e, 0xf1, 0x2f, 0xfe, 0xc1, 0x7f, 0x79, 0xe8, 0xb3, 0x5f, 0x17, 0xca, - 0x29, 0x96, 0xaa, 0x2b, 0xad, 0x6d, 0xd5, 0x20, 0x76, 0xd7, 0x57, 0x6f, 0xc1, 0x26, 0x8e, 0xe9, - 0xda, 0x2d, 0x92, 0xd4, 0xe9, 0x40, 0x2e, 0x67, 0x41, 0x27, 0x54, 0x59, 0xd8, 0x49, 0x59, 0x32, - 0xbb, 0x90, 0xc7, 0x65, 0xbb, 0x06, 0x55, 0xf5, 0xb4, 0x98, 0xa7, 0x6f, 0xc7, 0xe0, 0xb4, 0xb6, - 0x89, 0xae, 0xa4, 0xf8, 0x9e, 0xca, 0xe3, 0x73, 0xa9, 0xaa, 0x2d, 0xa8, 0x06, 0x75, 0xa8, 0x9d, - 0x62, 0x7a, 0x3c, 0x77, 0x98, 0xb2, 0x6c, 0x79, 0xf6, 0x80, 0x41, 0xb5, 0x4c, 0x4d, 0x6d, 0x75, - 0xf3, 0x06, 0x54, 0xae, 0x02, 0x2c, 0xd6, 0x57, 0xae, 0x10, 0x9b, 0x0d, 0x1c, 0x3a, 0x09, 0xc3, - 0x86, 0xa2, 0x93, 0x8a, 0x74, 0x52, 0x3a, 0x35, 0x5a, 0x3b, 0x76, 0x63, 0x6f, 0xfe, 0xc8, 0xfe, - 0xde, 0xfc, 0xf0, 0xba, 0xa2, 0x13, 0xcc, 0x7b, 0xe4, 0x9f, 0x49, 0x70, 0xff, 0x92, 0xeb, 0x50, - 0x53, 0x5f, 0x23, 0xd4, 0x56, 0x5b, 0x4b, 0xae, 0x6d, 0x13, 0x83, 0x36, 0xa8, 0x42, 0x5d, 0xe7, - 0xf6, 0xfc, 0xe8, 0x2a, 0x14, 0x77, 0x14, 0xcd, 0x25, 0x95, 0xa1, 0x93, 0xd2, 0xa9, 0xf2, 0xe9, - 0x6a, 0x55, 0x04, 0x54, 0xd4, 0x3b, 0x7e, 0x48, 0x55, 0xfd, 0x21, 0xaf, 0xbe, 0xec, 0x2a, 0x06, - 0x55, 0x69, 0xb7, 0x36, 0x2d, 0x20, 0x8f, 0x09, 0xb9, 0x57, 0x18, 0x16, 0xf6, 0x20, 0xe5, 0x1f, - 0x49, 0xf0, 0x50, 0xae, 0x6e, 0xab, 0xaa, 0x43, 0x91, 0x0e, 0x45, 0x95, 0x12, 0xdd, 0xa9, 0x48, - 0x27, 0x0b, 0xa7, 0xca, 0xa7, 0x2f, 0x55, 0x7b, 0x0a, 0xe7, 0x6a, 0x2e, 0x78, 0x6d, 0x4c, 0xe8, - 0x55, 0x5c, 0x61, 0xf0, 0xd8, 0x93, 0x22, 0xff, 0x44, 0x02, 0x14, 0xe5, 0x69, 0x2a, 0x76, 0x87, - 0xd0, 0x3b, 0xf0, 0xd2, 0x2b, 0x87, 0xf3, 0xd2, 0x94, 0x80, 0x2c, 0x7b, 0x02, 0x63, 0x4e, 0x7a, - 0x57, 0x82, 0x99, 0xb4, 0x4e, 0xdc, 0x3b, 0x5b, 0x71, 0xef, 0x2c, 0x1e, 0xc2, 0x3b, 0x1e, 0x6a, - 0x8e, 0x5b, 0x7e, 0x3b, 0x04, 0xa3, 0xcb, 0x0a, 0xd1, 0x4d, 0xa3, 0x41, 0x28, 0xfa, 0x36, 0x94, - 0xd8, 0x1c, 0x6d, 0x2b, 0x54, 0xe1, 0x1e, 0x29, 0x9f, 0xfe, 0xda, 0x41, 0xe6, 0x3a, 0x55, 0x46, - 0x5d, 0xdd, 0x79, 0xb2, 0x7a, 0x79, 0xf3, 0x1a, 0x69, 0xd1, 0x35, 0x42, 0x95, 0x1a, 0x12, 0x72, - 0x20, 0x6c, 0xc3, 0x01, 0x2a, 0x7a, 0x03, 0x86, 0x1d, 0x8b, 0xb4, 0x84, 0x33, 0x9f, 0xef, 0xd1, - 0xac, 0x40, 0xd3, 0x86, 0x45, 0x5a, 0xe1, 0x68, 0xb1, 0x2f, 0xcc, 0x71, 0xd1, 0x16, 0x1c, 0x75, - 0x78, 0x18, 0x54, 0x0a, 0x5c, 0xc2, 0xb9, 0xbe, 0x25, 0x78, 0xc1, 0x34, 0x2e, 0x64, 0x1c, 0xf5, - 0xbe, 0xb1, 0x40, 0x97, 0xff, 0x24, 0xc1, 0x58, 0x40, 0xcb, 0x47, 0xec, 0xb5, 0x94, 0xef, 0xaa, - 0x77, 0xe6, 0x3b, 0xc6, 0xcd, 0x3d, 0x77, 0x5c, 0xc8, 0x2a, 0xf9, 0x2d, 0x11, 0xbf, 0xbd, 0xee, - 0xc7, 0xc3, 0x10, 0x8f, 0x87, 0x67, 0xfa, 0x35, 0x2b, 0x27, 0x0c, 0x3e, 0x2b, 0x44, 0xcc, 0x61, - 0xee, 0x44, 0xaf, 0x43, 0xc9, 0x21, 0x1a, 0x69, 0x51, 0xd3, 0x16, 0xe6, 0x3c, 0x75, 0x87, 0xe6, - 0x28, 0x9b, 0x44, 0x6b, 0x08, 0xd6, 0xda, 0x31, 0x66, 0x8f, 0xff, 0x85, 0x03, 0x48, 0xf4, 0x2a, - 0x94, 0x28, 0xd1, 0x2d, 0x4d, 0xa1, 0xfe, 0xc4, 0x7a, 0x22, 0xdf, 0x24, 0x06, 0x5b, 0x37, 0xdb, - 0x4d, 0xc1, 0xc0, 0x07, 0x3f, 0x70, 0x96, 0xdf, 0x8a, 0x03, 0x40, 0xf4, 0x9e, 0x04, 0xe3, 0xae, - 0xd5, 0x66, 0xa4, 0x94, 0x25, 0xd8, 0x4e, 0x57, 0x44, 0xc3, 0x85, 0x7e, 0xdd, 0xb6, 0x11, 0x43, - 0xab, 0xcd, 0x08, 0xe1, 0xe3, 0xf1, 0x76, 0x9c, 0x90, 0x8a, 0x16, 0x61, 0x42, 0x57, 0x0d, 0x4c, - 0x94, 0x76, 0xb7, 0x41, 0x5a, 0xa6, 0xd1, 0x76, 0x2a, 0xc3, 0x27, 0xa5, 0x53, 0xc5, 0xda, 0x09, - 0x01, 0x30, 0xb1, 0x16, 0xef, 0xc6, 0x49, 0x7a, 0xf4, 0x4d, 0x40, 0xbe, 0x5d, 0x17, 0xbd, 0xf5, - 0x42, 0x35, 0x8d, 0x4a, 0xf1, 0xa4, 0x74, 0xaa, 0x50, 0x9b, 0x15, 0x28, 0xa8, 0x99, 0xa2, 0xc0, - 0x19, 0x5c, 0xf2, 0x7f, 0x86, 0x61, 0x22, 0x11, 0xe0, 0xe8, 0x0a, 0xcc, 0xb4, 0xbc, 0xf4, 0xb9, - 0xee, 0xea, 0x9b, 0xc4, 0x6e, 0xb4, 0xb6, 0x49, 0xdb, 0xd5, 0x48, 0x9b, 0x8f, 0x7a, 0xb1, 0x36, - 0x27, 0x64, 0xcc, 0x2c, 0x65, 0x52, 0xe1, 0x1c, 0x6e, 0xa6, 0xb7, 0xc1, 0x9b, 0xd6, 0x54, 0xc7, - 0x09, 0x30, 0x87, 0x38, 0x66, 0xa0, 0xf7, 0x7a, 0x8a, 0x02, 0x67, 0x70, 0x31, 0x1d, 0xdb, 0xc4, - 0x51, 0x6d, 0xd2, 0x4e, 0xea, 0x58, 0x88, 0xeb, 0xb8, 0x9c, 0x49, 0x85, 0x73, 0xb8, 0xd1, 0x19, - 0x28, 0x7b, 0xd2, 0xb8, 0xc7, 0xc5, 0xd0, 0x04, 0x09, 0x7b, 0x3d, 0xec, 0xc2, 0x51, 0x3a, 0x66, - 0x9a, 0xb9, 0xe9, 0x10, 0x7b, 0x87, 0xb4, 0xf3, 0x87, 0xe4, 0x72, 0x8a, 0x02, 0x67, 0x70, 0x31, - 0xd3, 0xbc, 0x98, 0x49, 0x99, 0x76, 0x34, 0x6e, 0xda, 0x46, 0x26, 0x15, 0xce, 0xe1, 0x66, 0x91, - 0xe7, 0xa9, 0xbc, 0xb8, 0xa3, 0xa8, 0x9a, 0xb2, 0xa9, 0x91, 0xca, 0x48, 0x3c, 0xf2, 0xd6, 0xe3, - 0xdd, 0x38, 0x49, 0x8f, 0x2e, 0xc2, 0xa4, 0xd7, 0xb4, 0x61, 0x28, 0x01, 0x48, 0x89, 0x83, 0xdc, - 0x2f, 0x40, 0x26, 0xd7, 0x93, 0x04, 0x38, 0xcd, 0x23, 0xff, 0x5d, 0x82, 0x13, 0x39, 0x33, 0x09, - 0xbd, 0x08, 0xc3, 0xb4, 0x6b, 0xf9, 0xeb, 0xef, 0x57, 0xfd, 0x8c, 0xde, 0xec, 0x5a, 0xe4, 0xd6, - 0xde, 0xfc, 0x03, 0x39, 0x6c, 0xac, 0x1b, 0x73, 0x46, 0xf4, 0x5d, 0x18, 0xb3, 0x4d, 0x4d, 0x53, - 0x8d, 0x8e, 0x47, 0x22, 0xb2, 0xc9, 0xf9, 0x1e, 0x67, 0x3a, 0x8e, 0x62, 0x84, 0xd9, 0x72, 0x72, - 0x7f, 0x6f, 0x7e, 0x2c, 0xd6, 0x87, 0xe3, 0xe2, 0xe4, 0xdf, 0x0f, 0x01, 0x2c, 0x13, 0x4b, 0x33, - 0xbb, 0x3a, 0x31, 0x06, 0xb1, 0x82, 0xbe, 0x19, 0x5b, 0x41, 0x5f, 0xe8, 0x35, 0xa3, 0x05, 0xaa, - 0xe6, 0x2e, 0xa1, 0x9d, 0xc4, 0x12, 0xfa, 0x62, 0xff, 0x22, 0x0e, 0x5e, 0x43, 0x6f, 0x16, 0x60, - 0x2a, 0x24, 0x5e, 0x32, 0x8d, 0xb6, 0xca, 0xe7, 0xc4, 0x73, 0xb1, 0x98, 0x78, 0x34, 0x11, 0x13, - 0x27, 0x32, 0x58, 0x22, 0xf1, 0x70, 0x25, 0xd0, 0x7e, 0x88, 0xb3, 0x9f, 0x8b, 0x0b, 0xbf, 0xb5, - 0x37, 0x7f, 0x60, 0x3d, 0x5f, 0x0d, 0x30, 0xe3, 0xca, 0xa2, 0x47, 0xe0, 0xa8, 0x4d, 0x14, 0xc7, - 0x34, 0x78, 0x9a, 0x18, 0x0d, 0x8d, 0xc2, 0xbc, 0x15, 0x8b, 0x5e, 0xf4, 0x18, 0x8c, 0xe8, 0xc4, - 0x71, 0x94, 0x0e, 0xe1, 0x19, 0x61, 0xb4, 0x36, 0x21, 0x08, 0x47, 0xd6, 0xbc, 0x66, 0xec, 0xf7, - 0xa3, 0x6b, 0x30, 0xae, 0x29, 0x8e, 0x08, 0xed, 0xa6, 0xaa, 0x13, 0x3e, 0xe7, 0xcb, 0xa7, 0xbf, - 0x72, 0x67, 0x11, 0xc3, 0x38, 0xc2, 0x95, 0x68, 0x35, 0x86, 0x84, 0x13, 0xc8, 0x68, 0x07, 0x10, - 0x6b, 0x69, 0xda, 0x8a, 0xe1, 0x78, 0x2e, 0x63, 0xf2, 0x46, 0x7a, 0x96, 0x17, 0xe4, 0xb7, 0xd5, - 0x14, 0x1a, 0xce, 0x90, 0x20, 0xff, 0x59, 0x82, 0xf1, 0x70, 0xc0, 0x06, 0x50, 0x28, 0xbd, 0x11, - 0x2f, 0x94, 0x9e, 0xed, 0x3b, 0x78, 0x73, 0x2a, 0xa5, 0xf7, 0x0b, 0x80, 0x42, 0x22, 0x96, 0x1a, - 0x36, 0x95, 0xd6, 0xf5, 0x3b, 0xd8, 0x47, 0xfc, 0x52, 0x02, 0x24, 0x92, 0xf5, 0xa2, 0x61, 0x98, - 0x94, 0xe7, 0x7f, 0x5f, 0xcd, 0x57, 0xfa, 0x56, 0xd3, 0xd7, 0xa0, 0xba, 0x91, 0xc2, 0x3e, 0x6f, - 0x50, 0xbb, 0x1b, 0x8e, 0x58, 0x9a, 0x00, 0x67, 0x28, 0x84, 0xde, 0x02, 0xb0, 0x05, 0x66, 0xd3, - 0x14, 0x29, 0xe0, 0x85, 0x3e, 0xb2, 0x29, 0x03, 0x58, 0x32, 0x8d, 0x2d, 0xb5, 0x13, 0x26, 0x34, - 0x1c, 0x00, 0xe3, 0x88, 0x90, 0xd9, 0xf3, 0x70, 0x22, 0x47, 0x7b, 0x74, 0x1c, 0x0a, 0xd7, 0x49, - 0xd7, 0x73, 0x2b, 0x66, 0x3f, 0xd1, 0x74, 0x74, 0x3f, 0x36, 0x2a, 0xb6, 0x52, 0x67, 0x87, 0x9e, - 0x91, 0xe4, 0xcf, 0x8b, 0xd1, 0x58, 0xe3, 0x55, 0xec, 0x29, 0x28, 0xd9, 0xc4, 0xd2, 0xd4, 0x96, - 0xe2, 0x88, 0x7a, 0x86, 0x17, 0xa4, 0x58, 0xb4, 0xe1, 0xa0, 0x37, 0x56, 0xef, 0x0e, 0xdd, 0xdb, - 0x7a, 0xb7, 0x70, 0xb7, 0xeb, 0x5d, 0x13, 0x4a, 0x8e, 0x5f, 0xe8, 0x0e, 0x73, 0xf0, 0xc5, 0x43, - 0xe4, 0x6c, 0x51, 0xe3, 0x06, 0x02, 0x83, 0xea, 0x36, 0x10, 0x92, 0x55, 0xd7, 0x16, 0x7b, 0xac, - 0x6b, 0x57, 0x61, 0xda, 0x26, 0x3b, 0x2a, 0x53, 0xe3, 0x92, 0xea, 0x50, 0xd3, 0xee, 0xae, 0xaa, - 0xba, 0x4a, 0x45, 0xd9, 0x53, 0xd9, 0xdf, 0x9b, 0x9f, 0xc6, 0x19, 0xfd, 0x38, 0x93, 0x8b, 0x65, - 0x67, 0x4b, 0x71, 0x1d, 0xd2, 0xe6, 0x29, 0xad, 0x14, 0x66, 0xe7, 0x3a, 0x6f, 0xc5, 0xa2, 0x17, - 0xe9, 0xb1, 0xe0, 0x2e, 0xdd, 0x8d, 0xe0, 0x1e, 0xcf, 0x0f, 0x6c, 0xb4, 0x01, 0x27, 0x2c, 0xdb, - 0xec, 0xd8, 0xc4, 0x71, 0x96, 0x89, 0xd2, 0xd6, 0x54, 0x83, 0xf8, 0xfe, 0x1a, 0xe5, 0x76, 0x3e, - 0xb0, 0xbf, 0x37, 0x7f, 0xa2, 0x9e, 0x4d, 0x82, 0xf3, 0x78, 0xe5, 0x4f, 0x86, 0xe1, 0x78, 0x72, - 0x95, 0xcd, 0xa9, 0x4a, 0xa5, 0xbe, 0xaa, 0xd2, 0xc7, 0x23, 0xd3, 0xc6, 0x2b, 0xd9, 0x83, 0x68, - 0xc8, 0x98, 0x3a, 0x8b, 0x30, 0x21, 0xf2, 0x88, 0xdf, 0x29, 0xea, 0xf2, 0x20, 0x1a, 0x36, 0xe2, - 0xdd, 0x38, 0x49, 0xcf, 0x6a, 0xcd, 0xb0, 0x84, 0xf4, 0x41, 0x86, 0xe3, 0xb5, 0xe6, 0x62, 0x92, - 0x00, 0xa7, 0x79, 0xd0, 0x1a, 0x4c, 0xb9, 0x46, 0x1a, 0xca, 0x8b, 0xce, 0x07, 0x04, 0xd4, 0xd4, - 0x46, 0x9a, 0x04, 0x67, 0xf1, 0xa1, 0x1d, 0x80, 0x96, 0x5f, 0x10, 0x38, 0x95, 0xa3, 0x3c, 0x57, - 0xd7, 0xfa, 0x9e, 0x5b, 0x41, 0x6d, 0x11, 0x66, 0xc4, 0xa0, 0xc9, 0xc1, 0x11, 0x49, 0xe8, 0x39, - 0x18, 0xb3, 0xf9, 0xc6, 0xc3, 0x37, 0xc0, 0x2b, 0xde, 0xef, 0x13, 0x6c, 0x63, 0x38, 0xda, 0x89, - 0xe3, 0xb4, 0xe8, 0x2c, 0x8c, 0xb7, 0x58, 0x8d, 0xca, 0xd4, 0x58, 0x32, 0x5d, 0x83, 0xf2, 0x40, - 0x2f, 0xd4, 0x10, 0xab, 0x13, 0x96, 0x62, 0x3d, 0x38, 0x41, 0x29, 0xff, 0x45, 0x8a, 0x2e, 0x6f, - 0x41, 0x99, 0x7e, 0x36, 0x56, 0x92, 0x3d, 0x92, 0x28, 0xc9, 0x66, 0xd2, 0x1c, 0x91, 0x8a, 0xec, - 0x7b, 0xd9, 0x15, 0xfa, 0x85, 0x43, 0x55, 0xe8, 0xe1, 0x32, 0x7d, 0xfb, 0x12, 0xfd, 0x43, 0x09, - 0x66, 0x2e, 0x34, 0x2e, 0xda, 0xa6, 0x6b, 0xf9, 0xea, 0x5d, 0xb6, 0x3c, 0x3f, 0x7f, 0x03, 0x86, - 0x6d, 0x57, 0xf3, 0xed, 0xfa, 0x92, 0x6f, 0x17, 0x76, 0x35, 0x66, 0xd7, 0x54, 0x82, 0xcb, 0x33, - 0x8a, 0x31, 0xa0, 0x37, 0xe0, 0xa8, 0xad, 0x18, 0x1d, 0xe2, 0x2f, 0xe0, 0x4f, 0xf7, 0x68, 0xcd, - 0xca, 0x32, 0x66, 0xec, 0x91, 0x32, 0x92, 0xa3, 0x61, 0x81, 0x2a, 0xff, 0x42, 0x82, 0x89, 0x4b, - 0xcd, 0x66, 0x7d, 0xc5, 0xe0, 0x19, 0xa0, 0xae, 0xd0, 0x6d, 0x56, 0x63, 0x58, 0x0a, 0xdd, 0x4e, - 0xd6, 0x18, 0xac, 0x0f, 0xf3, 0x1e, 0xb4, 0x0d, 0x23, 0x2c, 0xf3, 0x10, 0xa3, 0xdd, 0xe7, 0xf6, - 0x40, 0x88, 0xab, 0x79, 0x20, 0x61, 0xed, 0x2a, 0x1a, 0xb0, 0x0f, 0x2f, 0xbf, 0x03, 0xd3, 0x11, - 0xf5, 0x98, 0xbf, 0xf8, 0xc9, 0x26, 0x6a, 0x41, 0x91, 0x69, 0xe2, 0x9f, 0x5b, 0xf6, 0x7a, 0xfc, - 0x96, 0x30, 0x39, 0xac, 0xc1, 0xd8, 0x97, 0x83, 0x3d, 0x6c, 0x79, 0x0d, 0xc6, 0x2e, 0x99, 0x0e, - 0xad, 0x9b, 0x36, 0xe5, 0x6e, 0x43, 0x0f, 0x41, 0x41, 0x57, 0x0d, 0xb1, 0xc2, 0x97, 0x05, 0x4f, - 0x81, 0xad, 0x41, 0xac, 0x9d, 0x77, 0x2b, 0xbb, 0x22, 0x93, 0x85, 0xdd, 0xca, 0x2e, 0x66, 0xed, - 0xf2, 0x45, 0x18, 0x11, 0xc3, 0x11, 0x05, 0x2a, 0x1c, 0x0c, 0x54, 0xc8, 0x00, 0xfa, 0xcd, 0x10, - 0x8c, 0x08, 0xed, 0x07, 0xb0, 0x11, 0x7c, 0x2d, 0xb6, 0x11, 0x3c, 0xdb, 0xdf, 0x48, 0xe7, 0xee, - 0x02, 0xdb, 0x89, 0x5d, 0xe0, 0xf3, 0x7d, 0xe2, 0x1f, 0xbc, 0x05, 0xfc, 0x40, 0x82, 0xf1, 0x78, - 0xcc, 0xa1, 0x33, 0x50, 0x66, 0xeb, 0x91, 0xda, 0x22, 0xeb, 0x61, 0x41, 0x1d, 0x1c, 0xca, 0x34, - 0xc2, 0x2e, 0x1c, 0xa5, 0x43, 0x9d, 0x80, 0x8d, 0x85, 0x85, 0x70, 0x4a, 0xbe, 0xcb, 0x5d, 0xaa, - 0x6a, 0x55, 0xef, 0xc2, 0xa7, 0xba, 0x62, 0xd0, 0xcb, 0x76, 0x83, 0xda, 0xaa, 0xd1, 0x49, 0x09, - 0xe2, 0x31, 0x16, 0x45, 0x96, 0x6f, 0x48, 0x50, 0x16, 0x2a, 0x0f, 0x60, 0x3b, 0xf3, 0x6a, 0x7c, - 0x3b, 0xf3, 0x74, 0x9f, 0xf3, 0x39, 0x7b, 0x2f, 0xf3, 0x51, 0x68, 0x0a, 0x9b, 0xc1, 0x2c, 0xc1, - 0x6c, 0x9b, 0x0e, 0x4d, 0x26, 0x18, 0x36, 0xd7, 0x30, 0xef, 0x41, 0x3f, 0x90, 0xe0, 0xb8, 0x9a, - 0x98, 0xf3, 0xc2, 0xd7, 0x2f, 0xf6, 0xa7, 0x5a, 0x00, 0x53, 0xab, 0x08, 0x79, 0xc7, 0x93, 0x3d, - 0x38, 0x25, 0x52, 0x76, 0x21, 0x45, 0x85, 0x14, 0x18, 0xde, 0xa6, 0xd4, 0x12, 0x83, 0xb0, 0xd4, - 0x7f, 0xe6, 0x09, 0x55, 0x2a, 0x71, 0xf3, 0x9b, 0xcd, 0x3a, 0xe6, 0xd0, 0xf2, 0xaf, 0x87, 0x02, - 0x87, 0x35, 0xbc, 0x49, 0x12, 0xe4, 0x5b, 0xe9, 0x6e, 0xe4, 0xdb, 0x72, 0x56, 0xae, 0x45, 0xdf, - 0x82, 0x02, 0xd5, 0xfa, 0xdd, 0xd0, 0x0a, 0x09, 0xcd, 0xd5, 0x46, 0x98, 0xb0, 0x9a, 0xab, 0x0d, - 0xcc, 0x20, 0xd1, 0x9b, 0x50, 0x64, 0xab, 0x19, 0x9b, 0xe3, 0x85, 0xfe, 0x73, 0x08, 0xf3, 0x57, - 0x18, 0x61, 0xec, 0xcb, 0xc1, 0x1e, 0xae, 0xfc, 0x0e, 0x8c, 0xc5, 0x12, 0x01, 0xba, 0x06, 0xc7, - 0x34, 0x53, 0x69, 0xd7, 0x14, 0x4d, 0x31, 0x5a, 0xc4, 0x4e, 0xa6, 0xc6, 0xec, 0xbd, 0xd0, 0x6a, - 0x84, 0x43, 0x24, 0x94, 0xe0, 0xf2, 0x31, 0xda, 0x87, 0x63, 0xd8, 0xb2, 0x02, 0x10, 0x5a, 0x8f, - 0xe6, 0xa1, 0xc8, 0x42, 0xd8, 0x5b, 0x99, 0x46, 0x6b, 0xa3, 0x4c, 0x57, 0x16, 0xd9, 0x0e, 0xf6, - 0xda, 0xd1, 0x69, 0x00, 0x87, 0xb4, 0x6c, 0x42, 0x79, 0xde, 0xf1, 0x4e, 0x8f, 0x82, 0x0c, 0xdc, - 0x08, 0x7a, 0x70, 0x84, 0x4a, 0xfe, 0x9b, 0x04, 0x63, 0xeb, 0x84, 0xbe, 0x6d, 0xda, 0xd7, 0xeb, - 0xfc, 0x72, 0x77, 0x00, 0x79, 0x7f, 0x33, 0x96, 0xf7, 0x5f, 0xea, 0x71, 0xcc, 0x62, 0xda, 0xe6, - 0x65, 0x7f, 0xf9, 0x5f, 0x12, 0x54, 0x62, 0x94, 0xd1, 0x34, 0x41, 0xa0, 0x68, 0x99, 0x36, 0xf5, - 0xd7, 0xf8, 0x43, 0x69, 0xc0, 0x52, 0x6a, 0x64, 0x95, 0x67, 0xb0, 0xd8, 0x43, 0x67, 0x76, 0x6e, - 0xd9, 0xa6, 0x2e, 0xe2, 0xfe, 0x70, 0x52, 0x08, 0xb1, 0x43, 0x3b, 0x2f, 0xd8, 0xa6, 0x8e, 0x39, - 0xb6, 0xfc, 0x57, 0x09, 0x26, 0x63, 0x94, 0x03, 0x48, 0xe9, 0x4a, 0x3c, 0xa5, 0x3f, 0x7f, 0x18, - 0xc3, 0x72, 0x12, 0xfb, 0x17, 0x49, 0xb3, 0x98, 0x03, 0xd0, 0x16, 0x94, 0x2d, 0xb3, 0xdd, 0xb8, - 0x0b, 0xb7, 0x7a, 0x13, 0x6c, 0x85, 0xac, 0x87, 0x58, 0x38, 0x0a, 0x8c, 0x76, 0x61, 0xd2, 0x50, - 0x74, 0xe2, 0x58, 0x4a, 0x8b, 0x34, 0xee, 0xc2, 0x99, 0xca, 0x7d, 0xfc, 0xa6, 0x21, 0x89, 0x88, - 0xd3, 0x42, 0xe4, 0xdf, 0xa5, 0xec, 0x36, 0x6d, 0x8a, 0x5e, 0x86, 0x12, 0x7f, 0x60, 0xd1, 0x32, - 0x35, 0xb1, 0xb4, 0x9d, 0x61, 0x43, 0x53, 0x17, 0x6d, 0xb7, 0xf6, 0xe6, 0xbf, 0x7c, 0xe0, 0x91, - 0xb0, 0x4f, 0x88, 0x03, 0x18, 0xb4, 0x0e, 0xc3, 0xd6, 0x61, 0xca, 0x0c, 0xbe, 0xb0, 0xf0, 0xda, - 0x82, 0xe3, 0xc8, 0xff, 0x4e, 0x2a, 0xce, 0x97, 0x97, 0x6b, 0x77, 0x6d, 0xc0, 0x82, 0xb2, 0x26, - 0x77, 0xd0, 0x6c, 0x18, 0x11, 0xab, 0xac, 0x88, 0xcb, 0x8b, 0x87, 0x89, 0xcb, 0xe8, 0xca, 0x10, - 0x6c, 0x22, 0xfc, 0x46, 0x5f, 0x90, 0xfc, 0x0f, 0x09, 0x26, 0xb9, 0x42, 0x2d, 0xd7, 0x56, 0x69, - 0x77, 0x60, 0x19, 0x74, 0x2b, 0x96, 0x41, 0x97, 0x7b, 0x34, 0x34, 0xa5, 0x71, 0x6e, 0x16, 0xfd, - 0x54, 0x82, 0xfb, 0x52, 0xd4, 0x03, 0xc8, 0x30, 0x24, 0x9e, 0x61, 0x5e, 0x3a, 0xac, 0x81, 0x39, - 0x59, 0xe6, 0x87, 0xe5, 0x0c, 0xf3, 0x78, 0xe0, 0x9e, 0x06, 0xb0, 0x6c, 0x75, 0x47, 0xd5, 0x48, - 0x47, 0x5c, 0x24, 0x97, 0xc2, 0x21, 0xa9, 0x07, 0x3d, 0x38, 0x42, 0x85, 0xbe, 0x03, 0x33, 0x6d, - 0xb2, 0xa5, 0xb8, 0x1a, 0x5d, 0x6c, 0xb7, 0x97, 0x14, 0x4b, 0xd9, 0x54, 0x35, 0x95, 0xaa, 0x62, - 0x87, 0x3d, 0x5a, 0x3b, 0xef, 0x5d, 0xf0, 0x66, 0x51, 0xdc, 0xda, 0x9b, 0x7f, 0xf4, 0xe0, 0x4b, - 0x1d, 0x9f, 0xb8, 0x8b, 0x73, 0x84, 0xa0, 0xef, 0x4b, 0x50, 0xb1, 0xc9, 0x5b, 0xae, 0x6a, 0x93, - 0xf6, 0xb2, 0x6d, 0x5a, 0x31, 0x0d, 0x0a, 0x5c, 0x83, 0x8b, 0xfb, 0x7b, 0xf3, 0x15, 0x9c, 0x43, - 0xd3, 0x8b, 0x0e, 0xb9, 0x82, 0x10, 0x85, 0x29, 0x45, 0xd3, 0xcc, 0xb7, 0x49, 0xdc, 0x03, 0xc3, - 0x5c, 0x7e, 0x6d, 0x7f, 0x6f, 0x7e, 0x6a, 0x31, 0xdd, 0xdd, 0x8b, 0xe8, 0x2c, 0x78, 0xb4, 0x00, - 0x23, 0x3b, 0xa6, 0xe6, 0xea, 0xc4, 0xa9, 0x14, 0xb9, 0x24, 0x96, 0x71, 0x47, 0xae, 0x78, 0x4d, - 0xb7, 0xf6, 0xe6, 0x8f, 0x5e, 0x68, 0xf0, 0xa3, 0x0f, 0x9f, 0x8a, 0xed, 0xd1, 0x58, 0xcd, 0x24, - 0xa6, 0x3c, 0x3f, 0xb3, 0x2d, 0x85, 0x39, 0xe6, 0x52, 0xd8, 0x85, 0xa3, 0x74, 0x48, 0x87, 0xd1, - 0x6d, 0xb1, 0x6f, 0x77, 0x2a, 0x23, 0x7d, 0xad, 0x7e, 0xb1, 0x7d, 0x7f, 0x6d, 0x52, 0x88, 0x1c, - 0xf5, 0x9b, 0x1d, 0x1c, 0x4a, 0x40, 0x8f, 0xc1, 0x08, 0xff, 0x58, 0x59, 0xe6, 0x07, 0x60, 0xa5, - 0x30, 0x13, 0x5d, 0xf2, 0x9a, 0xb1, 0xdf, 0xef, 0x93, 0xae, 0xd4, 0x97, 0xf8, 0xc1, 0x6c, 0x82, - 0x74, 0xa5, 0xbe, 0x84, 0xfd, 0x7e, 0x64, 0xc1, 0x88, 0x43, 0x56, 0x55, 0xc3, 0xdd, 0xad, 0x40, - 0x5f, 0x57, 0xcd, 0x8d, 0xf3, 0x9c, 0x3b, 0x71, 0x14, 0x15, 0x4a, 0x14, 0xfd, 0xd8, 0x17, 0x83, - 0x76, 0x61, 0xd4, 0x76, 0x8d, 0x45, 0x67, 0xc3, 0x21, 0x76, 0xa5, 0xcc, 0x65, 0xf6, 0x9a, 0x9c, - 0xb1, 0xcf, 0x9f, 0x94, 0x1a, 0x78, 0x30, 0xa0, 0xc0, 0xa1, 0x30, 0xf4, 0x73, 0x09, 0x90, 0xe3, - 0x5a, 0x96, 0x46, 0x74, 0x62, 0x50, 0x45, 0xe3, 0xa7, 0x61, 0x4e, 0xe5, 0x18, 0xd7, 0xa1, 0xde, - 0xab, 0xdd, 0x29, 0xa0, 0xa4, 0x32, 0xc1, 0x31, 0x75, 0x9a, 0x14, 0x67, 0xe8, 0xc1, 0x86, 0x62, - 0xcb, 0xe1, 0xbf, 0x2b, 0x63, 0x7d, 0x0d, 0x45, 0xf6, 0xa9, 0x60, 0x38, 0x14, 0xa2, 0x1f, 0xfb, - 0x62, 0xd0, 0x15, 0x98, 0xb1, 0x89, 0xd2, 0xbe, 0x6c, 0x68, 0x5d, 0x6c, 0x9a, 0xf4, 0x82, 0xaa, - 0x11, 0xa7, 0xeb, 0x50, 0xa2, 0x57, 0xc6, 0x79, 0xd8, 0x04, 0xcf, 0x35, 0x70, 0x26, 0x15, 0xce, - 0xe1, 0x46, 0x2f, 0xc1, 0x71, 0x31, 0x31, 0x79, 0x68, 0xf2, 0x13, 0xb4, 0x09, 0x3e, 0x15, 0xa7, - 0xd9, 0x8e, 0x78, 0x31, 0xd1, 0x87, 0x53, 0xd4, 0xfc, 0x1d, 0x82, 0x38, 0x01, 0x1e, 0xcc, 0x4b, - 0xbe, 0xc3, 0xbd, 0x43, 0x08, 0x55, 0xbd, 0x67, 0xef, 0x10, 0x22, 0x22, 0x0e, 0x3e, 0x84, 0xfa, - 0x62, 0x08, 0xa6, 0x42, 0xe2, 0x3b, 0x7e, 0x87, 0x90, 0xc1, 0x32, 0x80, 0x77, 0x08, 0xd9, 0x17, - 0xf9, 0x85, 0x7b, 0x7d, 0x91, 0x7f, 0x0f, 0xde, 0x3f, 0xf0, 0xb7, 0x01, 0xa1, 0x13, 0xff, 0xf7, - 0xdf, 0x06, 0x84, 0xba, 0xe6, 0x14, 0x44, 0x7f, 0x18, 0x8a, 0x1a, 0xf4, 0x7f, 0x74, 0x01, 0x7d, - 0xf8, 0x77, 0x8e, 0xf2, 0xa7, 0x05, 0x38, 0x9e, 0x9c, 0xb1, 0xb1, 0x7b, 0x48, 0xe9, 0xb6, 0xf7, - 0x90, 0x75, 0x98, 0xde, 0x72, 0x35, 0xad, 0xcb, 0x1d, 0x12, 0xb9, 0x8c, 0xf4, 0xce, 0xfd, 0x1f, - 0x14, 0x9c, 0xd3, 0x17, 0x32, 0x68, 0x70, 0x26, 0x67, 0xce, 0x9d, 0x6a, 0xa1, 0xaf, 0x3b, 0xd5, - 0xd4, 0x95, 0xde, 0x70, 0x0f, 0x57, 0x7a, 0x99, 0xf7, 0xa3, 0xc5, 0x3e, 0xee, 0x47, 0xef, 0xc6, - 0x85, 0x66, 0x46, 0xe2, 0xbb, 0xdd, 0x85, 0xa6, 0xfc, 0x20, 0xcc, 0x0a, 0x36, 0xca, 0xef, 0x1a, - 0x0d, 0x6a, 0x9b, 0x9a, 0x46, 0xec, 0x65, 0x57, 0xd7, 0xbb, 0xf2, 0x39, 0x18, 0x8f, 0xdf, 0xaa, - 0x7b, 0x23, 0xef, 0x5d, 0xf4, 0x8b, 0xdb, 0x98, 0xc8, 0xc8, 0x7b, 0xed, 0x38, 0xa0, 0x90, 0xdf, - 0x93, 0x60, 0x26, 0xfb, 0x05, 0x1f, 0xd2, 0x60, 0x5c, 0x57, 0x76, 0xa3, 0x4f, 0x18, 0xa5, 0x3e, - 0xf7, 0xec, 0xfc, 0xfa, 0x74, 0x2d, 0x86, 0x85, 0x13, 0xd8, 0xf2, 0x67, 0x12, 0x9c, 0xc8, 0xb9, - 0xa8, 0x1c, 0xac, 0x26, 0xe8, 0x2a, 0x94, 0x74, 0x65, 0xb7, 0xe1, 0xda, 0x1d, 0xd2, 0xf7, 0x29, - 0x05, 0xcf, 0x25, 0x6b, 0x02, 0x05, 0x07, 0x78, 0xf2, 0x87, 0x12, 0x54, 0xf2, 0x2a, 0x4a, 0x74, - 0x26, 0x76, 0xa5, 0xfa, 0x70, 0xe2, 0x4a, 0x75, 0x32, 0xc5, 0x37, 0xa0, 0x0b, 0xd5, 0x8f, 0x24, - 0x98, 0xc9, 0xae, 0xbc, 0xd1, 0x53, 0x31, 0x8d, 0xe7, 0x13, 0x1a, 0x4f, 0x24, 0xb8, 0x84, 0xbe, - 0xdb, 0x30, 0x2e, 0xea, 0x73, 0x01, 0x23, 0xbc, 0xfc, 0xf8, 0xc1, 0x59, 0x55, 0x80, 0xf9, 0x95, - 0x26, 0x1f, 0xc9, 0x78, 0x1b, 0x4e, 0xe0, 0xca, 0xbf, 0x1a, 0x82, 0x62, 0xa3, 0xa5, 0x68, 0x64, - 0x00, 0x45, 0xdd, 0xd5, 0x58, 0x51, 0xd7, 0xeb, 0xbf, 0x0c, 0xb8, 0x96, 0xb9, 0xf5, 0xdc, 0x66, - 0xa2, 0x9e, 0x3b, 0xdb, 0x17, 0xfa, 0xc1, 0xa5, 0xdc, 0xb3, 0x30, 0x1a, 0x28, 0xd1, 0xdb, 0xea, - 0x21, 0x7f, 0x30, 0x04, 0xe5, 0x88, 0x88, 0x1e, 0xd7, 0x9e, 0x9d, 0xd8, 0xea, 0xdd, 0xcf, 0x1f, - 0x9a, 0x22, 0xb2, 0xab, 0xfe, 0xfa, 0xed, 0xbd, 0xe0, 0x0b, 0x5f, 0x62, 0xa5, 0x97, 0xf5, 0x73, - 0x30, 0x4e, 0xf9, 0xff, 0x7b, 0x82, 0x53, 0xc2, 0x02, 0x8f, 0xe2, 0xe0, 0x5d, 0x68, 0x33, 0xd6, - 0x8b, 0x13, 0xd4, 0xb3, 0xcf, 0xc1, 0x58, 0x4c, 0x58, 0x4f, 0x0f, 0xee, 0xfe, 0x28, 0xc1, 0xc3, - 0xb7, 0xdd, 0xd5, 0xa1, 0x5a, 0x6c, 0x7a, 0x55, 0x13, 0xd3, 0x6b, 0x2e, 0x1f, 0x60, 0x80, 0xcf, - 0x2d, 0x7e, 0x3a, 0x04, 0xa8, 0xb9, 0xad, 0xda, 0xed, 0xba, 0x62, 0xd3, 0x2e, 0x16, 0xff, 0xe2, - 0x1a, 0xc0, 0x84, 0x3b, 0x03, 0xe5, 0x36, 0x71, 0x5a, 0xb6, 0xca, 0x9d, 0x25, 0xf6, 0x0a, 0xc1, - 0x49, 0xca, 0x72, 0xd8, 0x85, 0xa3, 0x74, 0xa8, 0x03, 0xa5, 0x1d, 0xef, 0x7f, 0x82, 0xfe, 0xdd, - 0x5d, 0xaf, 0xc5, 0x6c, 0xf8, 0x4f, 0xc3, 0x30, 0xbe, 0x44, 0x83, 0x83, 0x03, 0x70, 0xf9, 0x7d, - 0x09, 0x66, 0xd2, 0x8e, 0x59, 0x66, 0xaa, 0xdf, 0x7b, 0xe7, 0x3c, 0x08, 0xc3, 0x1c, 0x9d, 0x79, - 0xe5, 0x98, 0x77, 0x66, 0xce, 0x24, 0x63, 0xde, 0x2a, 0x7f, 0x2e, 0xc1, 0x6c, 0xb6, 0x6a, 0x03, - 0xd8, 0x4a, 0x5c, 0x8b, 0x6f, 0x25, 0x7a, 0x3d, 0x78, 0xc8, 0xd6, 0x3b, 0x67, 0x5b, 0xb1, 0x97, - 0x39, 0x06, 0x03, 0x30, 0x72, 0x2b, 0x6e, 0xe4, 0xe2, 0xa1, 0x8d, 0xcc, 0x36, 0xb0, 0xf6, 0xd8, - 0x8d, 0x9b, 0x73, 0x47, 0x3e, 0xbe, 0x39, 0x77, 0xe4, 0x93, 0x9b, 0x73, 0x47, 0xde, 0xdd, 0x9f, - 0x93, 0x6e, 0xec, 0xcf, 0x49, 0x1f, 0xef, 0xcf, 0x49, 0xff, 0xdc, 0x9f, 0x93, 0x7e, 0xfc, 0xd9, - 0xdc, 0x91, 0xab, 0x23, 0x02, 0xf3, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x6d, 0x3f, 0x0c, 0xbe, - 0x34, 0x3d, 0x00, 0x00, + // 3427 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5b, 0xcd, 0x6f, 0x1c, 0xc7, + 0x95, 0x57, 0x73, 0x38, 0xe4, 0xf0, 0x8d, 0x48, 0x8a, 0x45, 0x9a, 0x1a, 0xd3, 0x36, 0x29, 0xf7, + 0x62, 0x6d, 0x79, 0xd7, 0x1e, 0xae, 0xe5, 0x95, 0xd7, 0x96, 0x6d, 0xd9, 0x1c, 0x52, 0x1f, 0x5c, + 0x90, 0xd4, 0xb8, 0x66, 0x28, 0xac, 0xe5, 0xaf, 0x6d, 0xce, 0x14, 0x87, 0x2d, 0xf5, 0x97, 0xbb, + 0xab, 0x69, 0x0e, 0x8c, 0xdd, 0x35, 0xb0, 0x88, 0x0f, 0x39, 0x04, 0xc9, 0x25, 0x70, 0x80, 0xe4, + 0x98, 0x43, 0x2e, 0x89, 0x7d, 0x48, 0x9c, 0xfc, 0x05, 0xd1, 0x21, 0x08, 0x1c, 0x20, 0x01, 0x8c, + 0xc0, 0x21, 0x22, 0x1a, 0xf1, 0x3f, 0x10, 0xf8, 0xa2, 0x53, 0x50, 0xd5, 0xd5, 0xdf, 0xdd, 0x94, + 0x66, 0x48, 0x0d, 0x02, 0xe4, 0x36, 0x5d, 0xf5, 0xde, 0xef, 0x7d, 0xd4, 0xab, 0x57, 0xaf, 0x3e, + 0x06, 0x5e, 0xb9, 0xf5, 0x82, 0x53, 0x55, 0xcd, 0xc5, 0x5b, 0xee, 0x16, 0xb1, 0x0d, 0x42, 0x89, + 0xb3, 0x68, 0xdd, 0xea, 0x2c, 0x2a, 0x96, 0xea, 0x2c, 0x92, 0x3d, 0x4a, 0x0c, 0x47, 0x35, 0x0d, + 0x67, 0x71, 0xf7, 0xd9, 0x2d, 0x42, 0x95, 0x67, 0x17, 0x3b, 0xc4, 0x20, 0xb6, 0x42, 0x49, 0xbb, + 0x6a, 0xd9, 0x26, 0x35, 0xd1, 0x33, 0x1e, 0x7b, 0x35, 0x64, 0xaf, 0x5a, 0xb7, 0x3a, 0x55, 0xc6, + 0x5e, 0x0d, 0xd9, 0xab, 0x82, 0x7d, 0xee, 0x99, 0x8e, 0x4a, 0x77, 0xdc, 0xad, 0x6a, 0xcb, 0xd4, + 0x17, 0x3b, 0x66, 0xc7, 0x5c, 0xe4, 0x28, 0x5b, 0xee, 0x36, 0xff, 0xe2, 0x1f, 0xfc, 0x97, 0x87, + 0x3e, 0xf7, 0xef, 0x42, 0x39, 0xc5, 0x52, 0x75, 0xa5, 0xb5, 0xa3, 0x1a, 0xc4, 0xee, 0xfa, 0xea, + 0x2d, 0xda, 0xc4, 0x31, 0x5d, 0xbb, 0x45, 0x92, 0x3a, 0x1d, 0xca, 0xe5, 0x2c, 0xea, 0x84, 0x2a, + 0x8b, 0xbb, 0x29, 0x4b, 0xe6, 0x16, 0xf3, 0xb8, 0x6c, 0xd7, 0xa0, 0xaa, 0x9e, 0x16, 0xf3, 0xfc, + 0xbd, 0x18, 0x9c, 0xd6, 0x0e, 0xd1, 0x95, 0x14, 0xdf, 0x73, 0x79, 0x7c, 0x2e, 0x55, 0xb5, 0x45, + 0xd5, 0xa0, 0x0e, 0xb5, 0x53, 0x4c, 0x4f, 0xe7, 0x0e, 0x53, 0x96, 0x2d, 0x2f, 0x1e, 0x32, 0xa8, + 0x96, 0xa9, 0xa9, 0xad, 0x6e, 0xde, 0x80, 0xca, 0x55, 0x80, 0xa5, 0xfa, 0xea, 0x75, 0x62, 0xb3, + 0x81, 0x43, 0x67, 0x60, 0xd8, 0x50, 0x74, 0x52, 0x91, 0xce, 0x48, 0x67, 0xc7, 0x6a, 0x27, 0x6f, + 0xef, 0x2f, 0x9c, 0x38, 0xd8, 0x5f, 0x18, 0xde, 0x50, 0x74, 0x82, 0x79, 0x8f, 0xfc, 0x03, 0x09, + 0x1e, 0x5e, 0x76, 0x1d, 0x6a, 0xea, 0xeb, 0x84, 0xda, 0x6a, 0x6b, 0xd9, 0xb5, 0x6d, 0x62, 0xd0, + 0x06, 0x55, 0xa8, 0xeb, 0xdc, 0x9b, 0x1f, 0xdd, 0x80, 0xe2, 0xae, 0xa2, 0xb9, 0xa4, 0x32, 0x74, + 0x46, 0x3a, 0x5b, 0x3e, 0x57, 0xad, 0x8a, 0x80, 0x8a, 0x7a, 0xc7, 0x0f, 0xa9, 0xaa, 0x3f, 0xe4, + 0xd5, 0xd7, 0x5d, 0xc5, 0xa0, 0x2a, 0xed, 0xd6, 0x66, 0x04, 0xe4, 0x49, 0x21, 0xf7, 0x3a, 0xc3, + 0xc2, 0x1e, 0xa4, 0xfc, 0x1d, 0x09, 0x1e, 0xcb, 0xd5, 0x6d, 0x4d, 0x75, 0x28, 0xd2, 0xa1, 0xa8, + 0x52, 0xa2, 0x3b, 0x15, 0xe9, 0x4c, 0xe1, 0x6c, 0xf9, 0xdc, 0xd5, 0x6a, 0x4f, 0xe1, 0x5c, 0xcd, + 0x05, 0xaf, 0x8d, 0x0b, 0xbd, 0x8a, 0xab, 0x0c, 0x1e, 0x7b, 0x52, 0xe4, 0xef, 0x49, 0x80, 0xa2, + 0x3c, 0x4d, 0xc5, 0xee, 0x10, 0x7a, 0x1f, 0x5e, 0x7a, 0xe3, 0x68, 0x5e, 0x9a, 0x16, 0x90, 0x65, + 0x4f, 0x60, 0xcc, 0x49, 0x1f, 0x4a, 0x30, 0x9b, 0xd6, 0x89, 0x7b, 0x67, 0x3b, 0xee, 0x9d, 0xa5, + 0x23, 0x78, 0xc7, 0x43, 0xcd, 0x71, 0xcb, 0xcf, 0x87, 0x60, 0x6c, 0x45, 0x21, 0xba, 0x69, 0x34, + 0x08, 0x45, 0xff, 0x0d, 0x25, 0x36, 0x47, 0xdb, 0x0a, 0x55, 0xb8, 0x47, 0xca, 0xe7, 0xfe, 0xed, + 0x30, 0x73, 0x9d, 0x2a, 0xa3, 0xae, 0xee, 0x3e, 0x5b, 0xbd, 0xb6, 0x75, 0x93, 0xb4, 0xe8, 0x3a, + 0xa1, 0x4a, 0x0d, 0x09, 0x39, 0x10, 0xb6, 0xe1, 0x00, 0x15, 0xbd, 0x03, 0xc3, 0x8e, 0x45, 0x5a, + 0xc2, 0x99, 0x2f, 0xf7, 0x68, 0x56, 0xa0, 0x69, 0xc3, 0x22, 0xad, 0x70, 0xb4, 0xd8, 0x17, 0xe6, + 0xb8, 0x68, 0x1b, 0x46, 0x1c, 0x1e, 0x06, 0x95, 0x02, 0x97, 0x70, 0xb1, 0x6f, 0x09, 0x5e, 0x30, + 0x4d, 0x08, 0x19, 0x23, 0xde, 0x37, 0x16, 0xe8, 0xf2, 0x6f, 0x24, 0x18, 0x0f, 0x68, 0xf9, 0x88, + 0xbd, 0x95, 0xf2, 0x5d, 0xf5, 0xfe, 0x7c, 0xc7, 0xb8, 0xb9, 0xe7, 0x4e, 0x09, 0x59, 0x25, 0xbf, + 0x25, 0xe2, 0xb7, 0xb7, 0xfd, 0x78, 0x18, 0xe2, 0xf1, 0xf0, 0x42, 0xbf, 0x66, 0xe5, 0x84, 0xc1, + 0xa7, 0xc3, 0x11, 0x73, 0x98, 0x3b, 0xd1, 0xdb, 0x50, 0x72, 0x88, 0x46, 0x5a, 0xd4, 0xb4, 0x85, + 0x39, 0xcf, 0xdd, 0xa7, 0x39, 0xca, 0x16, 0xd1, 0x1a, 0x82, 0xb5, 0x76, 0x92, 0xd9, 0xe3, 0x7f, + 0xe1, 0x00, 0x12, 0xbd, 0x09, 0x25, 0x4a, 0x74, 0x4b, 0x53, 0xa8, 0x3f, 0xb1, 0x9e, 0xc9, 0x37, + 0x89, 0xc1, 0xd6, 0xcd, 0x76, 0x53, 0x30, 0xf0, 0xc1, 0x0f, 0x9c, 0xe5, 0xb7, 0xe2, 0x00, 0x10, + 0x7d, 0x24, 0xc1, 0x84, 0x6b, 0xb5, 0x19, 0x29, 0x65, 0x09, 0xb6, 0xd3, 0x15, 0xd1, 0x70, 0xb9, + 0x5f, 0xb7, 0x6d, 0xc6, 0xd0, 0x6a, 0xb3, 0x42, 0xf8, 0x44, 0xbc, 0x1d, 0x27, 0xa4, 0xa2, 0x25, + 0x98, 0xd4, 0x55, 0x03, 0x13, 0xa5, 0xdd, 0x6d, 0x90, 0x96, 0x69, 0xb4, 0x9d, 0xca, 0xf0, 0x19, + 0xe9, 0x6c, 0xb1, 0x76, 0x5a, 0x00, 0x4c, 0xae, 0xc7, 0xbb, 0x71, 0x92, 0x1e, 0xfd, 0x27, 0x20, + 0xdf, 0xae, 0x2b, 0xde, 0x7a, 0xa1, 0x9a, 0x46, 0xa5, 0x78, 0x46, 0x3a, 0x5b, 0xa8, 0xcd, 0x09, + 0x14, 0xd4, 0x4c, 0x51, 0xe0, 0x0c, 0x2e, 0xb4, 0x06, 0x33, 0x36, 0xd9, 0x55, 0x99, 0x8d, 0x57, + 0x55, 0x87, 0x9a, 0x76, 0x77, 0x4d, 0xd5, 0x55, 0x5a, 0x19, 0xe1, 0x3a, 0x55, 0x0e, 0xf6, 0x17, + 0x66, 0x70, 0x46, 0x3f, 0xce, 0xe4, 0x92, 0x3f, 0x29, 0xc2, 0x64, 0x62, 0xba, 0xa0, 0xeb, 0x30, + 0xdb, 0xf2, 0x92, 0xf1, 0x86, 0xab, 0x6f, 0x11, 0xbb, 0xd1, 0xda, 0x21, 0x6d, 0x57, 0x23, 0x6d, + 0x1e, 0x43, 0xc5, 0xda, 0xbc, 0xd0, 0x78, 0x76, 0x39, 0x93, 0x0a, 0xe7, 0x70, 0x33, 0x2f, 0x18, + 0xbc, 0x69, 0x5d, 0x75, 0x9c, 0x00, 0x73, 0x88, 0x63, 0x06, 0x5e, 0xd8, 0x48, 0x51, 0xe0, 0x0c, + 0x2e, 0xa6, 0x63, 0x9b, 0x38, 0xaa, 0x4d, 0xda, 0x49, 0x1d, 0x0b, 0x71, 0x1d, 0x57, 0x32, 0xa9, + 0x70, 0x0e, 0x37, 0x3a, 0x0f, 0x65, 0x4f, 0x1a, 0x1f, 0x3f, 0x31, 0xd0, 0x41, 0xfa, 0xdf, 0x08, + 0xbb, 0x70, 0x94, 0x8e, 0x99, 0x66, 0x6e, 0x39, 0xc4, 0xde, 0x25, 0xed, 0xfc, 0x01, 0xbe, 0x96, + 0xa2, 0xc0, 0x19, 0x5c, 0xcc, 0x34, 0x2f, 0x02, 0x53, 0xa6, 0x8d, 0xc4, 0x4d, 0xdb, 0xcc, 0xa4, + 0xc2, 0x39, 0xdc, 0x2c, 0x8e, 0x3d, 0x95, 0x97, 0x76, 0x15, 0x55, 0x53, 0xb6, 0x34, 0x52, 0x19, + 0x8d, 0xc7, 0xf1, 0x46, 0xbc, 0x1b, 0x27, 0xe9, 0xd1, 0x15, 0x98, 0xf2, 0x9a, 0x36, 0x0d, 0x25, + 0x00, 0x29, 0x71, 0x90, 0x87, 0x05, 0xc8, 0xd4, 0x46, 0x92, 0x00, 0xa7, 0x79, 0xd0, 0x05, 0x98, + 0x68, 0x99, 0x9a, 0xc6, 0xe3, 0x71, 0xd9, 0x74, 0x0d, 0x5a, 0x19, 0xe3, 0xbe, 0x42, 0x6c, 0x3e, + 0x2e, 0xc7, 0x7a, 0x70, 0x82, 0x52, 0xfe, 0xa3, 0x04, 0xa7, 0x73, 0xe6, 0x34, 0x7a, 0x15, 0x86, + 0x69, 0xd7, 0xf2, 0x2b, 0x81, 0x7f, 0xf5, 0xd7, 0x96, 0x66, 0xd7, 0x22, 0x77, 0xf7, 0x17, 0x1e, + 0xc9, 0x61, 0x63, 0xdd, 0x98, 0x33, 0xa2, 0xff, 0x85, 0x71, 0x9b, 0x89, 0x33, 0x3a, 0x1e, 0x89, + 0xc8, 0x6b, 0x97, 0x7a, 0xcc, 0x39, 0x38, 0x8a, 0x11, 0xe6, 0xed, 0xa9, 0x83, 0xfd, 0x85, 0xf1, + 0x58, 0x1f, 0x8e, 0x8b, 0x93, 0x7f, 0x39, 0x04, 0xb0, 0x42, 0x2c, 0xcd, 0xec, 0xea, 0xc4, 0x18, + 0xc4, 0x5a, 0xfe, 0x6e, 0x6c, 0x2d, 0x7f, 0xa5, 0xd7, 0xdc, 0x1a, 0xa8, 0x9a, 0xbb, 0x98, 0x77, + 0x12, 0x8b, 0xf9, 0xab, 0xfd, 0x8b, 0x38, 0x7c, 0x35, 0xbf, 0x53, 0x80, 0xe9, 0x90, 0x78, 0xd9, + 0x34, 0xda, 0x2a, 0x9f, 0x4f, 0x2f, 0xc5, 0x62, 0xe2, 0xc9, 0x44, 0x4c, 0x9c, 0xce, 0x60, 0x89, + 0xc4, 0xc3, 0xf5, 0x40, 0xfb, 0x21, 0xce, 0x7e, 0x31, 0x2e, 0xfc, 0xee, 0xfe, 0xc2, 0xa1, 0x3b, + 0x8b, 0x6a, 0x80, 0x19, 0x57, 0x16, 0x3d, 0x01, 0x23, 0x36, 0x51, 0x1c, 0xd3, 0xe0, 0x29, 0x66, + 0x2c, 0x34, 0x0a, 0xf3, 0x56, 0x2c, 0x7a, 0xd1, 0x53, 0x30, 0xaa, 0x13, 0xc7, 0x51, 0x3a, 0x84, + 0x67, 0x93, 0xb1, 0xda, 0xa4, 0x20, 0x1c, 0x5d, 0xf7, 0x9a, 0xb1, 0xdf, 0x8f, 0x6e, 0xc2, 0x84, + 0xa6, 0x38, 0x22, 0xb4, 0x9b, 0xaa, 0x4e, 0x78, 0xbe, 0x28, 0x9f, 0xfb, 0x97, 0xfb, 0x8b, 0x18, + 0xc6, 0x11, 0xae, 0x89, 0x6b, 0x31, 0x24, 0x9c, 0x40, 0x46, 0xbb, 0x80, 0x58, 0x4b, 0xd3, 0x56, + 0x0c, 0xc7, 0x73, 0x19, 0x93, 0x37, 0xda, 0xb3, 0xbc, 0x20, 0x37, 0xae, 0xa5, 0xd0, 0x70, 0x86, + 0x04, 0xf9, 0xb7, 0x12, 0x4c, 0x84, 0x03, 0x36, 0x80, 0x92, 0xed, 0x9d, 0x78, 0xc9, 0xf6, 0x62, + 0xdf, 0xc1, 0x9b, 0x53, 0xb3, 0x7d, 0x5c, 0x00, 0x14, 0x12, 0xb1, 0xd4, 0xb0, 0xa5, 0xb4, 0x6e, + 0xdd, 0xc7, 0x8e, 0xe6, 0xc7, 0x12, 0x20, 0x91, 0xe8, 0x97, 0x0c, 0xc3, 0xa4, 0x7c, 0xed, 0xf0, + 0xd5, 0x7c, 0xa3, 0x6f, 0x35, 0x7d, 0x0d, 0xaa, 0x9b, 0x29, 0xec, 0x4b, 0x06, 0xb5, 0xbb, 0xe1, + 0x88, 0xa5, 0x09, 0x70, 0x86, 0x42, 0xe8, 0x3d, 0x00, 0x5b, 0x60, 0x36, 0x4d, 0x91, 0x02, 0x5e, + 0xe9, 0x23, 0x9b, 0x32, 0x80, 0x65, 0xd3, 0xd8, 0x56, 0x3b, 0x61, 0x42, 0xc3, 0x01, 0x30, 0x8e, + 0x08, 0x99, 0xbb, 0x04, 0xa7, 0x73, 0xb4, 0x47, 0xa7, 0xa0, 0x70, 0x8b, 0x74, 0x3d, 0xb7, 0x62, + 0xf6, 0x13, 0xcd, 0x44, 0x77, 0x86, 0x63, 0x62, 0x53, 0x77, 0x61, 0xe8, 0x05, 0x49, 0xfe, 0xba, + 0x18, 0x8d, 0x35, 0x5e, 0x4f, 0x9f, 0x85, 0x92, 0x4d, 0x2c, 0x4d, 0x6d, 0x29, 0x8e, 0xa8, 0x85, + 0x78, 0x69, 0x8c, 0x45, 0x1b, 0x0e, 0x7a, 0x63, 0x95, 0xf7, 0xd0, 0x83, 0xad, 0xbc, 0x0b, 0xc7, + 0x5d, 0x79, 0x9b, 0x50, 0x72, 0xfc, 0x92, 0x7b, 0x98, 0x83, 0x2f, 0x1d, 0x21, 0x67, 0x8b, 0x6a, + 0x3b, 0x10, 0x18, 0xd4, 0xd9, 0x81, 0x90, 0xac, 0x0a, 0xbb, 0xd8, 0x63, 0x85, 0x7d, 0xac, 0x55, + 0x31, 0xcb, 0xce, 0x96, 0xe2, 0x3a, 0xa4, 0xcd, 0x53, 0x5a, 0x29, 0xcc, 0xce, 0x75, 0xde, 0x8a, + 0x45, 0x2f, 0xd2, 0x63, 0xc1, 0x5d, 0x3a, 0x8e, 0xe0, 0x9e, 0xc8, 0x0f, 0x6c, 0xb4, 0x09, 0xa7, + 0x2d, 0xdb, 0xec, 0xd8, 0xc4, 0x71, 0x56, 0x88, 0xd2, 0xd6, 0x54, 0x83, 0xf8, 0xfe, 0x1a, 0xe3, + 0x76, 0x3e, 0x72, 0xb0, 0xbf, 0x70, 0xba, 0x9e, 0x4d, 0x82, 0xf3, 0x78, 0xe5, 0x2f, 0x86, 0xe1, + 0x54, 0x72, 0x95, 0xcd, 0xa9, 0x68, 0xa5, 0xbe, 0x2a, 0xda, 0xa7, 0x23, 0xd3, 0xc6, 0x2b, 0xf7, + 0x83, 0x68, 0xc8, 0x98, 0x3a, 0x4b, 0x30, 0x29, 0xf2, 0x88, 0xdf, 0x29, 0x6a, 0xfa, 0x20, 0x1a, + 0x36, 0xe3, 0xdd, 0x38, 0x49, 0xcf, 0xea, 0xd4, 0xb0, 0xfc, 0xf4, 0x41, 0x86, 0xe3, 0x75, 0xea, + 0x52, 0x92, 0x00, 0xa7, 0x79, 0xd0, 0x3a, 0x4c, 0xbb, 0x46, 0x1a, 0xca, 0x8b, 0xce, 0x47, 0x04, + 0xd4, 0xf4, 0x66, 0x9a, 0x04, 0x67, 0xf1, 0xa1, 0x5d, 0x80, 0x96, 0x5f, 0x10, 0x38, 0x95, 0x11, + 0x9e, 0xab, 0x6b, 0x7d, 0xcf, 0xad, 0xa0, 0xb6, 0x08, 0x33, 0x62, 0xd0, 0xe4, 0xe0, 0x88, 0x24, + 0xf4, 0x12, 0x8c, 0xdb, 0x7c, 0xd3, 0xe2, 0x1b, 0xe0, 0x15, 0xfe, 0x0f, 0x09, 0xb6, 0x71, 0x1c, + 0xed, 0xc4, 0x71, 0xda, 0x8c, 0x5a, 0xbd, 0x74, 0xdf, 0xb5, 0xfa, 0xef, 0xa4, 0xe8, 0xf2, 0x16, + 0x94, 0xe9, 0x17, 0x62, 0x25, 0xd9, 0x13, 0x89, 0x92, 0x6c, 0x36, 0xcd, 0x11, 0xa9, 0xc8, 0xfe, + 0x2f, 0xbb, 0x42, 0xbf, 0x7c, 0xa4, 0x0a, 0x3d, 0x5c, 0xa6, 0xef, 0x5d, 0xa2, 0x7f, 0x2a, 0xc1, + 0xec, 0xe5, 0xc6, 0x15, 0xdb, 0x74, 0x2d, 0x5f, 0xbd, 0x6b, 0x96, 0xe7, 0xe7, 0xff, 0x80, 0x61, + 0xdb, 0xd5, 0x7c, 0xbb, 0xfe, 0xc9, 0xb7, 0x0b, 0xbb, 0x1a, 0xb3, 0x6b, 0x3a, 0xc1, 0xe5, 0x19, + 0xc5, 0x18, 0xd0, 0x3b, 0x30, 0x62, 0x2b, 0x46, 0x87, 0xf8, 0x0b, 0xf8, 0xf3, 0x3d, 0x5a, 0xb3, + 0xba, 0x82, 0x19, 0x7b, 0xa4, 0x8c, 0xe4, 0x68, 0x58, 0xa0, 0xca, 0x3f, 0x92, 0x60, 0xf2, 0x6a, + 0xb3, 0x59, 0x5f, 0x35, 0x78, 0x06, 0xa8, 0x2b, 0x74, 0x87, 0xd5, 0x18, 0x96, 0x42, 0x77, 0x92, + 0x35, 0x06, 0xeb, 0xc3, 0xbc, 0x07, 0xed, 0xc0, 0x28, 0xcb, 0x3c, 0xc4, 0x68, 0xf7, 0xb9, 0x3d, + 0x10, 0xe2, 0x6a, 0x1e, 0x48, 0x58, 0xbb, 0x8a, 0x06, 0xec, 0xc3, 0xcb, 0x1f, 0xc0, 0x4c, 0x44, + 0x3d, 0xe6, 0x2f, 0x7e, 0xc6, 0x8a, 0x5a, 0x50, 0x64, 0x9a, 0xf8, 0x27, 0xa8, 0xbd, 0x1e, 0x04, + 0x26, 0x4c, 0x0e, 0x6b, 0x30, 0xf6, 0xe5, 0x60, 0x0f, 0x5b, 0x5e, 0x87, 0xf1, 0xab, 0xa6, 0x43, + 0xeb, 0xa6, 0x4d, 0xb9, 0xdb, 0xd0, 0x63, 0x50, 0xd0, 0x55, 0x43, 0xac, 0xf0, 0x65, 0xc1, 0x53, + 0x60, 0x6b, 0x10, 0x6b, 0xe7, 0xdd, 0xca, 0x9e, 0xc8, 0x64, 0x61, 0xb7, 0xb2, 0x87, 0x59, 0xbb, + 0x7c, 0x05, 0x46, 0xc5, 0x70, 0x44, 0x81, 0x0a, 0x87, 0x03, 0x15, 0x32, 0x80, 0x7e, 0x36, 0x04, + 0xa3, 0x42, 0xfb, 0x01, 0x6c, 0x04, 0xdf, 0x8a, 0x6d, 0x04, 0x2f, 0xf4, 0x37, 0xd2, 0xb9, 0xbb, + 0xc0, 0x76, 0x62, 0x17, 0xf8, 0x72, 0x9f, 0xf8, 0x87, 0x6f, 0x01, 0x3f, 0x91, 0x60, 0x22, 0x1e, + 0x73, 0xe8, 0x3c, 0x94, 0xd9, 0x7a, 0xa4, 0xb6, 0xc8, 0x46, 0x58, 0x50, 0x07, 0x07, 0x3a, 0x8d, + 0xb0, 0x0b, 0x47, 0xe9, 0x50, 0x27, 0x60, 0x63, 0x61, 0x21, 0x9c, 0x92, 0xef, 0x72, 0x97, 0xaa, + 0x5a, 0xd5, 0xbb, 0x7a, 0xaa, 0xae, 0x1a, 0xf4, 0x9a, 0xdd, 0xa0, 0xb6, 0x6a, 0x74, 0x52, 0x82, + 0x78, 0x8c, 0x45, 0x91, 0xe5, 0xdb, 0x12, 0x94, 0x85, 0xca, 0x03, 0xd8, 0xce, 0xbc, 0x19, 0xdf, + 0xce, 0x3c, 0xdf, 0xe7, 0x7c, 0xce, 0xde, 0xcb, 0x7c, 0x16, 0x9a, 0xc2, 0x66, 0x30, 0x4b, 0x30, + 0x3b, 0xa6, 0x43, 0x93, 0x09, 0x86, 0xcd, 0x35, 0xcc, 0x7b, 0xd0, 0xb7, 0x24, 0x38, 0xa5, 0x26, + 0xe6, 0xbc, 0xf0, 0xf5, 0xab, 0xfd, 0xa9, 0x16, 0xc0, 0xd4, 0x2a, 0x42, 0xde, 0xa9, 0x64, 0x0f, + 0x4e, 0x89, 0x94, 0x5d, 0x48, 0x51, 0x21, 0x05, 0x86, 0x77, 0x28, 0xb5, 0xc4, 0x20, 0x2c, 0xf7, + 0x9f, 0x79, 0x42, 0x95, 0x4a, 0xdc, 0xfc, 0x66, 0xb3, 0x8e, 0x39, 0xb4, 0xfc, 0xd3, 0xa1, 0xc0, + 0x61, 0x0d, 0x6f, 0x92, 0x04, 0xf9, 0x56, 0x3a, 0x8e, 0x7c, 0x5b, 0xce, 0xca, 0xb5, 0xe8, 0xbf, + 0xa0, 0x40, 0xb5, 0x7e, 0x37, 0xb4, 0x42, 0x42, 0x73, 0xad, 0x11, 0x26, 0xac, 0xe6, 0x5a, 0x03, + 0x33, 0x48, 0xf4, 0x2e, 0x14, 0xd9, 0x6a, 0xc6, 0xe6, 0x78, 0xa1, 0xff, 0x1c, 0xc2, 0xfc, 0x15, + 0x46, 0x18, 0xfb, 0x72, 0xb0, 0x87, 0x2b, 0x7f, 0x00, 0xe3, 0xb1, 0x44, 0x80, 0x6e, 0xc2, 0x49, + 0xcd, 0x54, 0xda, 0x35, 0x45, 0x53, 0x8c, 0x16, 0xb1, 0x93, 0xa9, 0x31, 0x7b, 0x2f, 0xb4, 0x16, + 0xe1, 0x10, 0x09, 0x25, 0xb8, 0x06, 0x8d, 0xf6, 0xe1, 0x18, 0xb6, 0xac, 0x00, 0x84, 0xd6, 0xa3, + 0x05, 0x28, 0xb2, 0x10, 0xf6, 0x56, 0xa6, 0xb1, 0xda, 0x18, 0xd3, 0x95, 0x45, 0xb6, 0x83, 0xbd, + 0x76, 0x74, 0x0e, 0xc0, 0x21, 0x2d, 0x9b, 0x50, 0x9e, 0x77, 0xbc, 0xd3, 0xa3, 0x20, 0x03, 0x37, + 0x82, 0x1e, 0x1c, 0xa1, 0x92, 0xff, 0x20, 0xc1, 0xf8, 0x06, 0xa1, 0xef, 0x9b, 0xf6, 0xad, 0x3a, + 0xbf, 0x66, 0x1e, 0x40, 0xde, 0xdf, 0x8a, 0xe5, 0xfd, 0xd7, 0x7a, 0x1c, 0xb3, 0x98, 0xb6, 0x79, + 0xd9, 0x5f, 0xfe, 0x8b, 0x04, 0x95, 0x18, 0x65, 0x34, 0x4d, 0x10, 0x28, 0x5a, 0xa6, 0x4d, 0xfd, + 0x35, 0xfe, 0x48, 0x1a, 0xb0, 0x94, 0x1a, 0x59, 0xe5, 0x19, 0x2c, 0xf6, 0xd0, 0x99, 0x9d, 0xdb, + 0xb6, 0xa9, 0x8b, 0xb8, 0x3f, 0x9a, 0x14, 0x42, 0xec, 0xd0, 0xce, 0xcb, 0xb6, 0xa9, 0x63, 0x8e, + 0x2d, 0xff, 0x5e, 0x82, 0xa9, 0x18, 0xe5, 0x00, 0x52, 0xba, 0x12, 0x4f, 0xe9, 0x2f, 0x1f, 0xc5, + 0xb0, 0x9c, 0xc4, 0xfe, 0x4d, 0xd2, 0x2c, 0xe6, 0x00, 0xb4, 0x0d, 0x65, 0xcb, 0x6c, 0x37, 0x8e, + 0xe1, 0x7e, 0x71, 0x92, 0xad, 0x90, 0xf5, 0x10, 0x0b, 0x47, 0x81, 0xd1, 0x1e, 0x4c, 0x19, 0x8a, + 0x4e, 0x1c, 0x4b, 0x69, 0x91, 0xc6, 0x31, 0x9c, 0xa9, 0x3c, 0xc4, 0x6f, 0x29, 0x92, 0x88, 0x38, + 0x2d, 0x44, 0xfe, 0x45, 0xca, 0x6e, 0xd3, 0xa6, 0xe8, 0x75, 0x28, 0xf1, 0xa7, 0x1e, 0x2d, 0x53, + 0x13, 0x4b, 0xdb, 0x79, 0x36, 0x34, 0x75, 0xd1, 0x76, 0x77, 0x7f, 0xe1, 0x9f, 0x0f, 0x3d, 0x12, + 0xf6, 0x09, 0x71, 0x00, 0x83, 0x36, 0x60, 0xd8, 0x3a, 0x4a, 0x99, 0xc1, 0x17, 0x16, 0x5e, 0x5b, + 0x70, 0x1c, 0xf9, 0xaf, 0x49, 0xc5, 0xf9, 0xf2, 0x72, 0xf3, 0xd8, 0x06, 0x2c, 0x28, 0x6b, 0x72, + 0x07, 0xcd, 0x86, 0x51, 0xb1, 0xca, 0x8a, 0xb8, 0xbc, 0x72, 0x94, 0xb8, 0x8c, 0xae, 0x0c, 0xc1, + 0x26, 0xc2, 0x6f, 0xf4, 0x05, 0xc9, 0x7f, 0x92, 0x60, 0x8a, 0x2b, 0xd4, 0x72, 0x6d, 0x95, 0x76, + 0x07, 0x96, 0x41, 0xb7, 0x63, 0x19, 0x74, 0xa5, 0x47, 0x43, 0x53, 0x1a, 0xe7, 0x66, 0xd1, 0x2f, + 0x25, 0x78, 0x28, 0x45, 0x3d, 0x80, 0x0c, 0x43, 0xe2, 0x19, 0xe6, 0xb5, 0xa3, 0x1a, 0x98, 0x93, + 0x65, 0xbe, 0x5d, 0xce, 0x30, 0x8f, 0x07, 0xee, 0x39, 0x00, 0xcb, 0x56, 0x77, 0x55, 0x8d, 0x74, + 0xc4, 0x25, 0x74, 0x29, 0x1c, 0x92, 0x7a, 0xd0, 0x83, 0x23, 0x54, 0xe8, 0x7f, 0x60, 0xb6, 0x4d, + 0xb6, 0x15, 0x57, 0xa3, 0x4b, 0xed, 0xf6, 0xb2, 0x62, 0x29, 0x5b, 0xaa, 0xa6, 0x52, 0x55, 0xec, + 0xb0, 0xc7, 0x6a, 0x97, 0xbc, 0xcb, 0xe1, 0x2c, 0x8a, 0xbb, 0xfb, 0x0b, 0x4f, 0x1e, 0x7e, 0xa9, + 0xe3, 0x13, 0x77, 0x71, 0x8e, 0x10, 0xf4, 0xff, 0x12, 0x54, 0x6c, 0xf2, 0x9e, 0xab, 0xda, 0xa4, + 0xbd, 0x62, 0x9b, 0x56, 0x4c, 0x83, 0x02, 0xd7, 0xe0, 0xca, 0xc1, 0xfe, 0x42, 0x05, 0xe7, 0xd0, + 0xf4, 0xa2, 0x43, 0xae, 0x20, 0x44, 0x61, 0x5a, 0xd1, 0x34, 0xf3, 0x7d, 0x12, 0xf7, 0xc0, 0x30, + 0x97, 0x5f, 0x3b, 0xd8, 0x5f, 0x98, 0x5e, 0x4a, 0x77, 0xf7, 0x22, 0x3a, 0x0b, 0x1e, 0x2d, 0xc2, + 0xe8, 0xae, 0xa9, 0xb9, 0x3a, 0x71, 0x2a, 0x45, 0x2e, 0x89, 0x65, 0xdc, 0xd1, 0xeb, 0x5e, 0xd3, + 0xdd, 0xfd, 0x85, 0x91, 0xcb, 0x0d, 0x7e, 0xf4, 0xe1, 0x53, 0xb1, 0x3d, 0x1a, 0xab, 0x99, 0xc4, + 0x94, 0xe7, 0x67, 0xb6, 0xa5, 0x30, 0xc7, 0x5c, 0x0d, 0xbb, 0x70, 0x94, 0x0e, 0xe9, 0x30, 0xb6, + 0x23, 0xf6, 0xed, 0x4e, 0x65, 0xb4, 0xaf, 0xd5, 0x2f, 0xb6, 0xef, 0xaf, 0x4d, 0x09, 0x91, 0x63, + 0x7e, 0xb3, 0x83, 0x43, 0x09, 0xe8, 0x29, 0x18, 0xe5, 0x1f, 0xab, 0x2b, 0xfc, 0x00, 0xac, 0x14, + 0x66, 0xa2, 0xab, 0x5e, 0x33, 0xf6, 0xfb, 0x7d, 0xd2, 0xd5, 0xfa, 0x32, 0x3f, 0x98, 0x4d, 0x90, + 0xae, 0xd6, 0x97, 0xb1, 0xdf, 0x8f, 0x2c, 0x18, 0x75, 0xc8, 0x9a, 0x6a, 0xb8, 0x7b, 0x15, 0xe8, + 0xeb, 0xaa, 0xb9, 0x71, 0x89, 0x73, 0x27, 0x8e, 0xa2, 0x42, 0x89, 0xa2, 0x1f, 0xfb, 0x62, 0xd0, + 0x1e, 0x8c, 0xd9, 0xae, 0xb1, 0xe4, 0x6c, 0x3a, 0xc4, 0xae, 0x94, 0xb9, 0xcc, 0x5e, 0x93, 0x33, + 0xf6, 0xf9, 0x93, 0x52, 0x03, 0x0f, 0x06, 0x14, 0x38, 0x14, 0x86, 0x7e, 0x28, 0x01, 0x72, 0x5c, + 0xcb, 0xd2, 0x88, 0x4e, 0x0c, 0xaa, 0x68, 0xfc, 0x34, 0xcc, 0xa9, 0x9c, 0xe4, 0x3a, 0xd4, 0x7b, + 0xb5, 0x3b, 0x05, 0x94, 0x54, 0x26, 0x38, 0xa6, 0x4e, 0x93, 0xe2, 0x0c, 0x3d, 0xd8, 0x50, 0x6c, + 0x3b, 0xfc, 0x77, 0x65, 0xbc, 0xaf, 0xa1, 0xc8, 0x3e, 0x15, 0x0c, 0x87, 0x42, 0xf4, 0x63, 0x5f, + 0x0c, 0xba, 0x0e, 0xb3, 0x36, 0x51, 0xda, 0xd7, 0x0c, 0xad, 0x8b, 0x4d, 0x93, 0x5e, 0x56, 0x35, + 0xe2, 0x74, 0x1d, 0x4a, 0xf4, 0xca, 0x04, 0x0f, 0x9b, 0xe0, 0xa9, 0x07, 0xce, 0xa4, 0xc2, 0x39, + 0xdc, 0xe8, 0x35, 0x38, 0x25, 0x26, 0x26, 0x0f, 0x4d, 0x7e, 0x82, 0x36, 0xc9, 0xa7, 0xe2, 0x0c, + 0xdb, 0x11, 0x2f, 0x25, 0xfa, 0x70, 0x8a, 0x9a, 0xbf, 0x43, 0x10, 0x27, 0xc0, 0x83, 0x79, 0x53, + 0x78, 0xb4, 0x77, 0x08, 0xa1, 0xaa, 0x0f, 0xec, 0x1d, 0x42, 0x44, 0xc4, 0xe1, 0x87, 0x50, 0xdf, + 0x0c, 0xc1, 0x74, 0x48, 0x7c, 0xdf, 0xef, 0x10, 0x32, 0x58, 0x06, 0xf0, 0x0e, 0x21, 0xfb, 0x22, + 0xbf, 0xf0, 0xa0, 0x2f, 0xf2, 0x1f, 0xc0, 0xfb, 0x07, 0xfe, 0x36, 0x20, 0x74, 0xe2, 0xdf, 0xff, + 0xdb, 0x80, 0x50, 0xd7, 0x9c, 0x82, 0xe8, 0x57, 0x43, 0x51, 0x83, 0xfe, 0x81, 0x2e, 0xa0, 0x8f, + 0xfe, 0xe2, 0x52, 0xfe, 0xb2, 0x00, 0xa7, 0x92, 0x33, 0x36, 0x76, 0x0f, 0x29, 0xdd, 0xf3, 0x1e, + 0xb2, 0x0e, 0x33, 0xdb, 0xae, 0xa6, 0x75, 0xb9, 0x43, 0x22, 0x97, 0x91, 0xde, 0xb9, 0xff, 0xa3, + 0x82, 0x73, 0xe6, 0x72, 0x06, 0x0d, 0xce, 0xe4, 0xcc, 0xb9, 0x53, 0x2d, 0xf4, 0x75, 0xa7, 0x9a, + 0xba, 0xd2, 0x1b, 0xee, 0xe1, 0x4a, 0x2f, 0xf3, 0x7e, 0xb4, 0xd8, 0xc7, 0xfd, 0xe8, 0x71, 0x5c, + 0x68, 0x66, 0x24, 0xbe, 0x7b, 0x5d, 0x68, 0xca, 0x8f, 0xc2, 0x9c, 0x60, 0xa3, 0xfc, 0xae, 0xd1, + 0xa0, 0xb6, 0xa9, 0x69, 0xc4, 0x5e, 0x71, 0x75, 0xbd, 0x2b, 0x5f, 0x84, 0x89, 0xf8, 0xad, 0xba, + 0x37, 0xf2, 0xde, 0x45, 0xbf, 0xb8, 0x8d, 0x89, 0x8c, 0xbc, 0xd7, 0x8e, 0x03, 0x0a, 0xf9, 0x23, + 0x09, 0x66, 0xb3, 0x5f, 0xf0, 0x21, 0x0d, 0x26, 0x74, 0x65, 0x2f, 0xfa, 0xfc, 0x51, 0xea, 0x73, + 0xcf, 0xce, 0xaf, 0x4f, 0xd7, 0x63, 0x58, 0x38, 0x81, 0x2d, 0x7f, 0x25, 0xc1, 0xe9, 0x9c, 0x8b, + 0xca, 0xc1, 0x6a, 0x82, 0x6e, 0x40, 0x49, 0x57, 0xf6, 0x1a, 0xae, 0xdd, 0x21, 0x7d, 0x9f, 0x52, + 0xf0, 0x5c, 0xb2, 0x2e, 0x50, 0x70, 0x80, 0x27, 0x7f, 0x2a, 0x41, 0x25, 0xaf, 0xa2, 0x44, 0xe7, + 0x63, 0x57, 0xaa, 0x8f, 0x27, 0xae, 0x54, 0xa7, 0x52, 0x7c, 0x03, 0xba, 0x50, 0xfd, 0x4c, 0x82, + 0xd9, 0xec, 0xca, 0x1b, 0x3d, 0x17, 0xd3, 0x78, 0x21, 0xa1, 0xf1, 0x64, 0x82, 0x4b, 0xe8, 0xbb, + 0x03, 0x13, 0xa2, 0x3e, 0x17, 0x30, 0xc2, 0xcb, 0x4f, 0x1f, 0x9e, 0x55, 0x05, 0x98, 0x5f, 0x69, + 0xf2, 0x91, 0x8c, 0xb7, 0xe1, 0x04, 0xae, 0xfc, 0x93, 0x21, 0x28, 0x36, 0x5a, 0x8a, 0x46, 0x06, + 0x50, 0xd4, 0xdd, 0x88, 0x15, 0x75, 0xbd, 0xfe, 0xdf, 0x81, 0x6b, 0x99, 0x5b, 0xcf, 0x6d, 0x25, + 0xea, 0xb9, 0x0b, 0x7d, 0xa1, 0x1f, 0x5e, 0xca, 0xbd, 0x08, 0x63, 0x81, 0x12, 0xbd, 0xad, 0x1e, + 0xf2, 0x27, 0x43, 0x50, 0x8e, 0x88, 0xe8, 0x71, 0xed, 0xd9, 0x8d, 0xad, 0xde, 0xfd, 0xfc, 0xb5, + 0x2a, 0x22, 0xbb, 0xea, 0xaf, 0xdf, 0xde, 0x0b, 0xbe, 0xf0, 0x25, 0x56, 0x7a, 0x59, 0xbf, 0x08, + 0x13, 0x94, 0xff, 0xd3, 0x28, 0x38, 0x25, 0x2c, 0xf0, 0x28, 0x0e, 0xde, 0x85, 0x36, 0x63, 0xbd, + 0x38, 0x41, 0x3d, 0xf7, 0x12, 0x8c, 0xc7, 0x84, 0xf5, 0xf4, 0xe0, 0xee, 0xd7, 0x12, 0x3c, 0x7e, + 0xcf, 0x5d, 0x1d, 0xaa, 0xc5, 0xa6, 0x57, 0x35, 0x31, 0xbd, 0xe6, 0xf3, 0x01, 0x06, 0xf8, 0xdc, + 0xe2, 0xfb, 0x43, 0x80, 0x9a, 0x3b, 0xaa, 0xdd, 0xae, 0x2b, 0x36, 0xed, 0x62, 0xf1, 0x7f, 0xb2, + 0x01, 0x4c, 0xb8, 0xf3, 0x50, 0x6e, 0x13, 0xa7, 0x65, 0xab, 0xdc, 0x59, 0x62, 0xaf, 0x10, 0x9c, + 0xa4, 0xac, 0x84, 0x5d, 0x38, 0x4a, 0x87, 0x3a, 0x50, 0xda, 0xf5, 0xfe, 0xb1, 0xe8, 0xdf, 0xdd, + 0xf5, 0x5a, 0xcc, 0x86, 0xff, 0x79, 0x0c, 0xe3, 0x4b, 0x34, 0x38, 0x38, 0x00, 0x97, 0x3f, 0x96, + 0x60, 0x36, 0xed, 0x98, 0x15, 0xa6, 0xfa, 0x83, 0x77, 0xce, 0xa3, 0x30, 0xcc, 0xd1, 0x99, 0x57, + 0x4e, 0x7a, 0x67, 0xe6, 0x4c, 0x32, 0xe6, 0xad, 0xf2, 0xd7, 0x12, 0xcc, 0x65, 0xab, 0x36, 0x80, + 0xad, 0xc4, 0xcd, 0xf8, 0x56, 0xa2, 0xd7, 0x83, 0x87, 0x6c, 0xbd, 0x73, 0xb6, 0x15, 0xfb, 0x99, + 0x63, 0x30, 0x00, 0x23, 0xb7, 0xe3, 0x46, 0x2e, 0x1d, 0xd9, 0xc8, 0x6c, 0x03, 0x6b, 0x4f, 0xdd, + 0xbe, 0x33, 0x7f, 0xe2, 0xf3, 0x3b, 0xf3, 0x27, 0xbe, 0xb8, 0x33, 0x7f, 0xe2, 0xc3, 0x83, 0x79, + 0xe9, 0xf6, 0xc1, 0xbc, 0xf4, 0xf9, 0xc1, 0xbc, 0xf4, 0xe7, 0x83, 0x79, 0xe9, 0xbb, 0x5f, 0xcd, + 0x9f, 0xb8, 0x31, 0x2a, 0x30, 0xff, 0x16, 0x00, 0x00, 0xff, 0xff, 0x9e, 0x01, 0x30, 0x7b, 0xbe, + 0x3d, 0x00, 0x00, } diff --git a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/generated.proto b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/generated.proto index 7a95c4ae16b..08b8a628963 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/generated.proto +++ b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/generated.proto @@ -123,10 +123,17 @@ message DaemonSetSpec { // +optional optional int32 minReadySeconds = 4; + // DEPRECATED. // A sequence number representing a specific generation of the template. // Populated by the system. It can be set only during the creation. // +optional optional int64 templateGeneration = 5; + + // The number of old history to retain to allow rollback. + // This is a pointer to distinguish between explicit zero and not specified. + // Defaults to 10. + // +optional + optional int32 revisionHistoryLimit = 6; } // DaemonSetStatus represents the current status of a daemon set. @@ -169,6 +176,12 @@ message DaemonSetStatus { // (ready for at least spec.minReadySeconds) // +optional optional int32 numberUnavailable = 8; + + // Count of hash collisions for the DaemonSet. The DaemonSet controller + // uses this field as a collision avoidance mechanism when it needs to + // create the name for the newest ControllerRevision. + // +optional + optional int64 collisionCount = 9; } message DaemonSetUpdateStrategy { @@ -180,7 +193,7 @@ message DaemonSetUpdateStrategy { // Rolling update config params. Present only if type = "RollingUpdate". // --- // TODO: Update this to follow our convention for oneOf, whatever we decide it - // to be. Same as DeploymentStrategy.RollingUpdate. + // to be. Same as Deployment `strategy.rollingUpdate`. // See https://github.com/kubernetes/kubernetes/issues/35345 // +optional optional RollingUpdateDaemonSet rollingUpdate = 2; diff --git a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types.generated.go b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types.generated.go index b7459aa3f7c..92104bbc3d6 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types.generated.go +++ b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types.generated.go @@ -7552,16 +7552,17 @@ func (x *DaemonSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [5]bool + var yyq2 [6]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[0] = x.Selector != nil yyq2[2] = true yyq2[3] = x.MinReadySeconds != 0 yyq2[4] = x.TemplateGeneration != 0 + yyq2[5] = x.RevisionHistoryLimit != nil var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(5) + r.EncodeArrayStart(6) } else { yynn2 = 1 for _, b := range yyq2 { @@ -7685,6 +7686,41 @@ func (x *DaemonSetSpec) CodecEncodeSelf(e *codec1978.Encoder) { } } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[5] { + if x.RevisionHistoryLimit == nil { + r.EncodeNil() + } else { + yy23 := *x.RevisionHistoryLimit + yym24 := z.EncBinary() + _ = yym24 + if false { + } else { + r.EncodeInt(int64(yy23)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[5] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("revisionHistoryLimit")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.RevisionHistoryLimit == nil { + r.EncodeNil() + } else { + yy25 := *x.RevisionHistoryLimit + yym26 := z.EncBinary() + _ = yym26 + if false { + } else { + r.EncodeInt(int64(yy25)) + } + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -7801,6 +7837,22 @@ func (x *DaemonSetSpec) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { *((*int64)(yyv10)) = int64(r.DecodeInt(64)) } } + case "revisionHistoryLimit": + if r.TryDecodeAsNil() { + if x.RevisionHistoryLimit != nil { + x.RevisionHistoryLimit = nil + } + } else { + if x.RevisionHistoryLimit == nil { + x.RevisionHistoryLimit = new(int32) + } + yym13 := z.DecBinary() + _ = yym13 + if false { + } else { + *((*int32)(x.RevisionHistoryLimit)) = int32(r.DecodeInt(32)) + } + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -7812,16 +7864,16 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj12 int - var yyb12 bool - var yyhl12 bool = l >= 0 - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + var yyj14 int + var yyb14 bool + var yyhl14 bool = l >= 0 + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7834,21 +7886,21 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if x.Selector == nil { x.Selector = new(pkg1_v1.LabelSelector) } - yym14 := z.DecBinary() - _ = yym14 + yym16 := z.DecBinary() + _ = yym16 if false { } else if z.HasExtensions() && z.DecExt(x.Selector) { } else { z.DecFallback(x.Selector, false) } } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7856,16 +7908,16 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.Template = pkg4_v1.PodTemplateSpec{} } else { - yyv15 := &x.Template - yyv15.CodecDecodeSelf(d) + yyv17 := &x.Template + yyv17.CodecDecodeSelf(d) } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7873,16 +7925,16 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.UpdateStrategy = DaemonSetUpdateStrategy{} } else { - yyv16 := &x.UpdateStrategy - yyv16.CodecDecodeSelf(d) + yyv18 := &x.UpdateStrategy + yyv18.CodecDecodeSelf(d) } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7890,21 +7942,21 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.MinReadySeconds = 0 } else { - yyv17 := &x.MinReadySeconds - yym18 := z.DecBinary() - _ = yym18 + yyv19 := &x.MinReadySeconds + yym20 := z.DecBinary() + _ = yym20 if false { } else { - *((*int32)(yyv17)) = int32(r.DecodeInt(32)) + *((*int32)(yyv19)) = int32(r.DecodeInt(32)) } } - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -7912,26 +7964,52 @@ func (x *DaemonSetSpec) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { if r.TryDecodeAsNil() { x.TemplateGeneration = 0 } else { - yyv19 := &x.TemplateGeneration - yym20 := z.DecBinary() - _ = yym20 + yyv21 := &x.TemplateGeneration + yym22 := z.DecBinary() + _ = yym22 if false { } else { - *((*int64)(yyv19)) = int64(r.DecodeInt(64)) + *((*int64)(yyv21)) = int64(r.DecodeInt(64)) + } + } + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l + } else { + yyb14 = r.CheckBreak() + } + if yyb14 { + 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) + } + yym24 := z.DecBinary() + _ = yym24 + if false { + } else { + *((*int32)(x.RevisionHistoryLimit)) = int32(r.DecodeInt(32)) } } for { - yyj12++ - if yyhl12 { - yyb12 = yyj12 > l + yyj14++ + if yyhl14 { + yyb14 = yyj14 > l } else { - yyb12 = r.CheckBreak() + yyb14 = r.CheckBreak() } - if yyb12 { + if yyb14 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj12-1, "") + z.DecStructFieldNotFound(yyj14-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -7950,16 +8028,17 @@ func (x *DaemonSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { } else { yysep2 := !z.EncBinary() yy2arr2 := z.EncBasicHandle().StructToArray - var yyq2 [8]bool + var yyq2 [9]bool _, _, _ = yysep2, yyq2, yy2arr2 const yyr2 bool = false yyq2[4] = x.ObservedGeneration != 0 yyq2[5] = x.UpdatedNumberScheduled != 0 yyq2[6] = x.NumberAvailable != 0 yyq2[7] = x.NumberUnavailable != 0 + yyq2[8] = x.CollisionCount != nil var yynn2 int if yyr2 || yy2arr2 { - r.EncodeArrayStart(8) + r.EncodeArrayStart(9) } else { yynn2 = 4 for _, b := range yyq2 { @@ -8146,6 +8225,41 @@ func (x *DaemonSetStatus) CodecEncodeSelf(e *codec1978.Encoder) { } } } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[8] { + if x.CollisionCount == nil { + r.EncodeNil() + } else { + yy28 := *x.CollisionCount + yym29 := z.EncBinary() + _ = yym29 + if false { + } else { + r.EncodeInt(int64(yy28)) + } + } + } else { + r.EncodeNil() + } + } else { + if yyq2[8] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("collisionCount")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.CollisionCount == nil { + r.EncodeNil() + } else { + yy30 := *x.CollisionCount + yym31 := z.EncBinary() + _ = yym31 + if false { + } else { + r.EncodeInt(int64(yy30)) + } + } + } + } if yyr2 || yy2arr2 { z.EncSendContainerState(codecSelfer_containerArrayEnd1234) } else { @@ -8303,6 +8417,22 @@ func (x *DaemonSetStatus) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { *((*int32)(yyv18)) = int32(r.DecodeInt(32)) } } + case "collisionCount": + if r.TryDecodeAsNil() { + if x.CollisionCount != nil { + x.CollisionCount = nil + } + } else { + if x.CollisionCount == nil { + x.CollisionCount = new(int64) + } + yym21 := z.DecBinary() + _ = yym21 + if false { + } else { + *((*int64)(x.CollisionCount)) = int64(r.DecodeInt(64)) + } + } default: z.DecStructFieldNotFound(-1, yys3) } // end switch yys3 @@ -8314,16 +8444,16 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) var h codecSelfer1234 z, r := codec1978.GenHelperDecoder(d) _, _, _ = h, z, r - var yyj20 int - var yyb20 bool - var yyhl20 bool = l >= 0 - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + var yyj22 int + var yyb22 bool + var yyhl22 bool = l >= 0 + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -8331,29 +8461,7 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.CurrentNumberScheduled = 0 } else { - yyv21 := &x.CurrentNumberScheduled - yym22 := z.DecBinary() - _ = yym22 - if false { - } else { - *((*int32)(yyv21)) = int32(r.DecodeInt(32)) - } - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.NumberMisscheduled = 0 - } else { - yyv23 := &x.NumberMisscheduled + yyv23 := &x.CurrentNumberScheduled yym24 := z.DecBinary() _ = yym24 if false { @@ -8361,21 +8469,21 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) *((*int32)(yyv23)) = int32(r.DecodeInt(32)) } } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.DesiredNumberScheduled = 0 + x.NumberMisscheduled = 0 } else { - yyv25 := &x.DesiredNumberScheduled + yyv25 := &x.NumberMisscheduled yym26 := z.DecBinary() _ = yym26 if false { @@ -8383,21 +8491,21 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) *((*int32)(yyv25)) = int32(r.DecodeInt(32)) } } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.NumberReady = 0 + x.DesiredNumberScheduled = 0 } else { - yyv27 := &x.NumberReady + yyv27 := &x.DesiredNumberScheduled yym28 := z.DecBinary() _ = yym28 if false { @@ -8405,13 +8513,35 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) *((*int32)(yyv27)) = int32(r.DecodeInt(32)) } } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.NumberReady = 0 + } else { + yyv29 := &x.NumberReady + yym30 := z.DecBinary() + _ = yym30 + if false { + } else { + *((*int32)(yyv29)) = int32(r.DecodeInt(32)) + } + } + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l + } else { + yyb22 = r.CheckBreak() + } + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -8419,21 +8549,21 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.ObservedGeneration = 0 } else { - yyv29 := &x.ObservedGeneration - yym30 := z.DecBinary() - _ = yym30 + yyv31 := &x.ObservedGeneration + yym32 := z.DecBinary() + _ = yym32 if false { } else { - *((*int64)(yyv29)) = int64(r.DecodeInt(64)) + *((*int64)(yyv31)) = int64(r.DecodeInt(64)) } } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } @@ -8441,29 +8571,7 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) if r.TryDecodeAsNil() { x.UpdatedNumberScheduled = 0 } else { - yyv31 := &x.UpdatedNumberScheduled - yym32 := z.DecBinary() - _ = yym32 - if false { - } else { - *((*int32)(yyv31)) = int32(r.DecodeInt(32)) - } - } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l - } else { - yyb20 = r.CheckBreak() - } - if yyb20 { - z.DecSendContainerState(codecSelfer_containerArrayEnd1234) - return - } - z.DecSendContainerState(codecSelfer_containerArrayElem1234) - if r.TryDecodeAsNil() { - x.NumberAvailable = 0 - } else { - yyv33 := &x.NumberAvailable + yyv33 := &x.UpdatedNumberScheduled yym34 := z.DecBinary() _ = yym34 if false { @@ -8471,21 +8579,21 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) *((*int32)(yyv33)) = int32(r.DecodeInt(32)) } } - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l } else { - yyb20 = r.CheckBreak() + yyb22 = r.CheckBreak() } - if yyb20 { + if yyb22 { z.DecSendContainerState(codecSelfer_containerArrayEnd1234) return } z.DecSendContainerState(codecSelfer_containerArrayElem1234) if r.TryDecodeAsNil() { - x.NumberUnavailable = 0 + x.NumberAvailable = 0 } else { - yyv35 := &x.NumberUnavailable + yyv35 := &x.NumberAvailable yym36 := z.DecBinary() _ = yym36 if false { @@ -8493,18 +8601,66 @@ func (x *DaemonSetStatus) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) *((*int32)(yyv35)) = int32(r.DecodeInt(32)) } } - for { - yyj20++ - if yyhl20 { - yyb20 = yyj20 > l + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l + } else { + yyb22 = r.CheckBreak() + } + if yyb22 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.NumberUnavailable = 0 + } else { + yyv37 := &x.NumberUnavailable + yym38 := z.DecBinary() + _ = yym38 + if false { } else { - yyb20 = r.CheckBreak() + *((*int32)(yyv37)) = int32(r.DecodeInt(32)) } - if yyb20 { + } + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l + } else { + yyb22 = r.CheckBreak() + } + if yyb22 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + if x.CollisionCount != nil { + x.CollisionCount = nil + } + } else { + if x.CollisionCount == nil { + x.CollisionCount = new(int64) + } + yym40 := z.DecBinary() + _ = yym40 + if false { + } else { + *((*int64)(x.CollisionCount)) = int64(r.DecodeInt(64)) + } + } + for { + yyj22++ + if yyhl22 { + yyb22 = yyj22 > l + } else { + yyb22 = r.CheckBreak() + } + if yyb22 { break } z.DecSendContainerState(codecSelfer_containerArrayElem1234) - z.DecStructFieldNotFound(yyj20-1, "") + z.DecStructFieldNotFound(yyj22-1, "") } z.DecSendContainerState(codecSelfer_containerArrayEnd1234) } @@ -19917,7 +20073,7 @@ func (x codecSelfer1234) decSliceDaemonSet(v *[]DaemonSet, d *codec1978.Decoder) yyrg1 := len(yyv1) > 0 yyv21 := yyv1 - yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 912) + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 928) if yyrt1 { if yyrl1 <= cap(yyv1) { yyv1 = yyv1[:yyrl1] diff --git a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types.go b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types.go index 800f4544f95..901a7b4bae1 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types.go +++ b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types.go @@ -386,7 +386,7 @@ type DaemonSetUpdateStrategy struct { // Rolling update config params. Present only if type = "RollingUpdate". //--- // TODO: Update this to follow our convention for oneOf, whatever we decide it - // to be. Same as DeploymentStrategy.RollingUpdate. + // to be. Same as Deployment `strategy.rollingUpdate`. // See https://github.com/kubernetes/kubernetes/issues/35345 // +optional RollingUpdate *RollingUpdateDaemonSet `json:"rollingUpdate,omitempty" protobuf:"bytes,2,opt,name=rollingUpdate"` @@ -449,10 +449,17 @@ type DaemonSetSpec struct { // +optional MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,4,opt,name=minReadySeconds"` + // DEPRECATED. // A sequence number representing a specific generation of the template. // Populated by the system. It can be set only during the creation. // +optional TemplateGeneration int64 `json:"templateGeneration,omitempty" protobuf:"varint,5,opt,name=templateGeneration"` + + // The number of old history to retain to allow rollback. + // This is a pointer to distinguish between explicit zero and not specified. + // Defaults to 10. + // +optional + RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,6,opt,name=revisionHistoryLimit"` } // DaemonSetStatus represents the current status of a daemon set. @@ -495,6 +502,12 @@ type DaemonSetStatus struct { // (ready for at least spec.minReadySeconds) // +optional NumberUnavailable int32 `json:"numberUnavailable,omitempty" protobuf:"varint,8,opt,name=numberUnavailable"` + + // Count of hash collisions for the DaemonSet. The DaemonSet controller + // uses this field as a collision avoidance mechanism when it needs to + // create the name for the newest ControllerRevision. + // +optional + CollisionCount *int64 `json:"collisionCount,omitempty" protobuf:"varint,9,opt,name=collisionCount"` } // +genclient=true @@ -522,10 +535,16 @@ type DaemonSet struct { } const ( + // DEPRECATED: DefaultDaemonSetUniqueLabelKey is used instead. // DaemonSetTemplateGenerationKey is the key of the labels that is added // to daemon set pods to distinguish between old and new pod templates // during DaemonSet template update. DaemonSetTemplateGenerationKey string = "pod-template-generation" + + // DefaultDaemonSetUniqueLabelKey is the default label key that is added + // to existing DaemonSet pods to distinguish between old and new + // DaemonSet pods during DaemonSet template updates. + DefaultDaemonSetUniqueLabelKey string = "daemonset-controller-hash" ) // DaemonSetList is a collection of daemon sets. diff --git a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go index 213f8494ad0..a92590e6b43 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types_swagger_doc_generated.go @@ -77,12 +77,13 @@ func (DaemonSetList) SwaggerDoc() map[string]string { } var map_DaemonSetSpec = map[string]string{ - "": "DaemonSetSpec is the specification of a daemon set.", - "selector": "A label query over pods that are managed by the daemon set. Must match in order to be controlled. If empty, defaulted to labels on Pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", - "template": "An object that describes the pod that will be created. The DaemonSet will create exactly one copy of this pod on every node that matches the template's node selector (or on every node if no node selector is specified). More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template", - "updateStrategy": "An update strategy to replace existing DaemonSet pods with new pods.", - "minReadySeconds": "The minimum number of seconds for which a newly created DaemonSet pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready).", - "templateGeneration": "A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.", + "": "DaemonSetSpec is the specification of a daemon set.", + "selector": "A label query over pods that are managed by the daemon set. Must match in order to be controlled. If empty, defaulted to labels on Pod template. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors", + "template": "An object that describes the pod that will be created. The DaemonSet will create exactly one copy of this pod on every node that matches the template's node selector (or on every node if no node selector is specified). More info: https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller#pod-template", + "updateStrategy": "An update strategy to replace existing DaemonSet pods with new pods.", + "minReadySeconds": "The minimum number of seconds for which a newly created DaemonSet pod should be ready without any of its container crashing, for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready).", + "templateGeneration": "DEPRECATED. A sequence number representing a specific generation of the template. Populated by the system. It can be set only during the creation.", + "revisionHistoryLimit": "The number of old history to retain to allow rollback. This is a pointer to distinguish between explicit zero and not specified. Defaults to 10.", } func (DaemonSetSpec) SwaggerDoc() map[string]string { @@ -99,6 +100,7 @@ var map_DaemonSetStatus = map[string]string{ "updatedNumberScheduled": "The total number of nodes that are running updated daemon pod", "numberAvailable": "The number of nodes that should be running the daemon pod and have one or more of the daemon pod running and available (ready for at least spec.minReadySeconds)", "numberUnavailable": "The number of nodes that should be running the daemon pod and have none of the daemon pod running and available (ready for at least spec.minReadySeconds)", + "collisionCount": "Count of hash collisions for the DaemonSet. The DaemonSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", } func (DaemonSetStatus) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/zz_generated.conversion.go b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/zz_generated.conversion.go index 884661db0d6..a020195bc2c 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/zz_generated.conversion.go +++ b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/zz_generated.conversion.go @@ -352,6 +352,7 @@ func autoConvert_v1beta1_DaemonSetSpec_To_extensions_DaemonSetSpec(in *DaemonSet } out.MinReadySeconds = in.MinReadySeconds out.TemplateGeneration = in.TemplateGeneration + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) return nil } @@ -370,6 +371,7 @@ func autoConvert_extensions_DaemonSetSpec_To_v1beta1_DaemonSetSpec(in *extension } out.MinReadySeconds = in.MinReadySeconds out.TemplateGeneration = in.TemplateGeneration + out.RevisionHistoryLimit = (*int32)(unsafe.Pointer(in.RevisionHistoryLimit)) return nil } @@ -387,6 +389,7 @@ func autoConvert_v1beta1_DaemonSetStatus_To_extensions_DaemonSetStatus(in *Daemo out.UpdatedNumberScheduled = in.UpdatedNumberScheduled out.NumberAvailable = in.NumberAvailable out.NumberUnavailable = in.NumberUnavailable + out.CollisionCount = (*int64)(unsafe.Pointer(in.CollisionCount)) return nil } @@ -404,6 +407,7 @@ func autoConvert_extensions_DaemonSetStatus_To_v1beta1_DaemonSetStatus(in *exten out.UpdatedNumberScheduled = in.UpdatedNumberScheduled out.NumberAvailable = in.NumberAvailable out.NumberUnavailable = in.NumberUnavailable + out.CollisionCount = (*int64)(unsafe.Pointer(in.CollisionCount)) return nil } diff --git a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go index 9bb39222a57..ab8889cb86c 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/zz_generated.deepcopy.go @@ -182,6 +182,9 @@ func DeepCopy_v1beta1_DaemonSet(in interface{}, out interface{}, c *conversion.C if err := DeepCopy_v1beta1_DaemonSetSpec(&in.Spec, &out.Spec, c); err != nil { return err } + if err := DeepCopy_v1beta1_DaemonSetStatus(&in.Status, &out.Status, c); err != nil { + return err + } return nil } } @@ -225,6 +228,11 @@ func DeepCopy_v1beta1_DaemonSetSpec(in interface{}, out interface{}, c *conversi if err := DeepCopy_v1beta1_DaemonSetUpdateStrategy(&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 } } @@ -235,6 +243,11 @@ func DeepCopy_v1beta1_DaemonSetStatus(in interface{}, out interface{}, c *conver in := in.(*DaemonSetStatus) out := out.(*DaemonSetStatus) *out = *in + if in.CollisionCount != nil { + in, out := &in.CollisionCount, &out.CollisionCount + *out = new(int64) + **out = **in + } return nil } } diff --git a/staging/src/k8s.io/client-go/pkg/apis/extensions/zz_generated.deepcopy.go b/staging/src/k8s.io/client-go/pkg/apis/extensions/zz_generated.deepcopy.go index e6d3b1925e3..48243b9d105 100644 --- a/staging/src/k8s.io/client-go/pkg/apis/extensions/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/client-go/pkg/apis/extensions/zz_generated.deepcopy.go @@ -183,6 +183,9 @@ func DeepCopy_extensions_DaemonSet(in interface{}, out interface{}, c *conversio if err := DeepCopy_extensions_DaemonSetSpec(&in.Spec, &out.Spec, c); err != nil { return err } + if err := DeepCopy_extensions_DaemonSetStatus(&in.Status, &out.Status, c); err != nil { + return err + } return nil } } @@ -226,6 +229,11 @@ func DeepCopy_extensions_DaemonSetSpec(in interface{}, out interface{}, c *conve if err := DeepCopy_extensions_DaemonSetUpdateStrategy(&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 } } @@ -236,6 +244,11 @@ func DeepCopy_extensions_DaemonSetStatus(in interface{}, out interface{}, c *con in := in.(*DaemonSetStatus) out := out.(*DaemonSetStatus) *out = *in + if in.CollisionCount != nil { + in, out := &in.CollisionCount, &out.CollisionCount + *out = new(int64) + **out = **in + } return nil } } From d02f40a5e725d70f59e9f9e96b22ddad5c0426f7 Mon Sep 17 00:00:00 2001 From: Janet Kuo Date: Wed, 17 May 2017 16:53:46 -0700 Subject: [PATCH 3/5] Implement DaemonSet history logic in controller 1. Create controllerrevisions (history) and label pods with template hash for both RollingUpdate and OnDelete update strategy 2. Clean up old, non-live history based on revisionHistoryLimit 3. Remove duplicate controllerrevisions (the ones with the same template) and relabel their pods 4. Update RBAC to allow DaemonSet controller to manage controllerrevisions 5. In DaemonSet controller unit tests, create new pods with hash labels --- cmd/kube-controller-manager/app/extensions.go | 1 + pkg/client/listers/extensions/v1beta1/BUILD | 1 + .../extensions/v1beta1/daemonset_expansion.go | 36 ++ pkg/controller/controller_utils.go | 22 +- pkg/controller/controller_utils_test.go | 36 ++ pkg/controller/daemon/BUILD | 5 + pkg/controller/daemon/daemoncontroller.go | 207 ++++++++++- .../daemon/daemoncontroller_test.go | 19 +- pkg/controller/daemon/update.go | 328 +++++++++++++++++- pkg/controller/daemon/util/daemonset_util.go | 22 +- .../daemon/util/daemonset_util_test.go | 102 +++++- pkg/controller/deployment/sync.go | 2 +- pkg/controller/deployment/util/hash_test.go | 5 +- pkg/controller/deployment/util/pod_util.go | 18 - .../deployment/util/pod_util_test.go | 59 ---- .../deployment/util/replicaset_util.go | 3 +- .../rbac/bootstrappolicy/controller_policy.go | 1 + .../testdata/controller-roles.yaml | 11 + 18 files changed, 742 insertions(+), 136 deletions(-) delete mode 100644 pkg/controller/deployment/util/pod_util_test.go diff --git a/cmd/kube-controller-manager/app/extensions.go b/cmd/kube-controller-manager/app/extensions.go index a3218496fa7..f1b65440799 100644 --- a/cmd/kube-controller-manager/app/extensions.go +++ b/cmd/kube-controller-manager/app/extensions.go @@ -33,6 +33,7 @@ func startDaemonSetController(ctx ControllerContext) (bool, error) { } go daemon.NewDaemonSetsController( ctx.InformerFactory.Extensions().V1beta1().DaemonSets(), + ctx.InformerFactory.Apps().V1beta1().ControllerRevisions(), ctx.InformerFactory.Core().V1().Pods(), ctx.InformerFactory.Core().V1().Nodes(), ctx.ClientBuilder.ClientOrDie("daemon-set-controller"), diff --git a/pkg/client/listers/extensions/v1beta1/BUILD b/pkg/client/listers/extensions/v1beta1/BUILD index 2292129b3b8..973ba0bea39 100644 --- a/pkg/client/listers/extensions/v1beta1/BUILD +++ b/pkg/client/listers/extensions/v1beta1/BUILD @@ -26,6 +26,7 @@ go_library( tags = ["automanaged"], deps = [ "//pkg/api/v1:go_default_library", + "//pkg/apis/apps/v1beta1:go_default_library", "//pkg/apis/extensions/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/pkg/client/listers/extensions/v1beta1/daemonset_expansion.go b/pkg/client/listers/extensions/v1beta1/daemonset_expansion.go index 6e913e4770e..09c0ca738c1 100644 --- a/pkg/client/listers/extensions/v1beta1/daemonset_expansion.go +++ b/pkg/client/listers/extensions/v1beta1/daemonset_expansion.go @@ -22,6 +22,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/kubernetes/pkg/api/v1" + apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" ) @@ -29,6 +30,7 @@ import ( // DaemonSetLister. type DaemonSetListerExpansion interface { GetPodDaemonSets(pod *v1.Pod) ([]*v1beta1.DaemonSet, error) + GetHistoryDaemonSets(history *apps.ControllerRevision) ([]*v1beta1.DaemonSet, error) } // DaemonSetNamespaceListerExpansion allows custom methods to be added to @@ -76,3 +78,37 @@ func (s *daemonSetLister) GetPodDaemonSets(pod *v1.Pod) ([]*v1beta1.DaemonSet, e return daemonSets, nil } + +// GetHistoryDaemonSets returns a list of DaemonSets that potentially +// match a ControllerRevision. Only the one specified in the ControllerRevision's ControllerRef +// will actually manage it. +// Returns an error only if no matching DaemonSets are found. +func (s *daemonSetLister) GetHistoryDaemonSets(history *apps.ControllerRevision) ([]*v1beta1.DaemonSet, error) { + if len(history.Labels) == 0 { + return nil, fmt.Errorf("no DaemonSet found for ControllerRevision %s because it has no labels", history.Name) + } + + list, err := s.DaemonSets(history.Namespace).List(labels.Everything()) + if err != nil { + return nil, err + } + + var daemonSets []*v1beta1.DaemonSet + for _, ds := range list { + selector, err := metav1.LabelSelectorAsSelector(ds.Spec.Selector) + if err != nil { + return nil, fmt.Errorf("invalid label selector: %v", err) + } + // If a DaemonSet with a nil or empty selector creeps in, it should match nothing, not everything. + if selector.Empty() || !selector.Matches(labels.Set(history.Labels)) { + continue + } + daemonSets = append(daemonSets, ds) + } + + if len(daemonSets) == 0 { + return nil, fmt.Errorf("could not find DaemonSets for ControllerRevision %s in namespace %s with labels: %v", history.Name, history.Namespace, history.Labels) + } + + return daemonSets, nil +} diff --git a/pkg/controller/controller_utils.go b/pkg/controller/controller_utils.go index f7e29fc171f..f65e697d615 100644 --- a/pkg/controller/controller_utils.go +++ b/pkg/controller/controller_utils.go @@ -17,8 +17,10 @@ limitations under the License. package controller import ( + "encoding/binary" "encoding/json" "fmt" + "hash/fnv" "sync" "sync/atomic" "time" @@ -29,16 +31,14 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/clock" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/wait" - - "k8s.io/apimachinery/pkg/util/clock" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/integer" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" v1helper "k8s.io/kubernetes/pkg/api/v1/helper" @@ -48,6 +48,7 @@ import ( extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" clientretry "k8s.io/kubernetes/pkg/client/retry" + hashutil "k8s.io/kubernetes/pkg/util/hash" "github.com/golang/glog" ) @@ -980,3 +981,18 @@ func WaitForCacheSync(controllerName string, stopCh <-chan struct{}, cacheSyncs glog.Infof("Caches are synced for %s controller", controllerName) return true } + +// ComputeHash returns a hash value calculated from pod template and a collisionCount to avoid hash collision +func ComputeHash(template *v1.PodTemplateSpec, collisionCount *int64) uint32 { + podTemplateSpecHasher := fnv.New32a() + hashutil.DeepHashObject(podTemplateSpecHasher, *template) + + // Add collisionCount in the hash if it exists. + if collisionCount != nil { + collisionCountBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(collisionCountBytes, uint64(*collisionCount)) + podTemplateSpecHasher.Write(collisionCountBytes) + } + + return podTemplateSpecHasher.Sum32() +} diff --git a/pkg/controller/controller_utils_test.go b/pkg/controller/controller_utils_test.go index 8bfe3438782..e4dbbd93e3d 100644 --- a/pkg/controller/controller_utils_test.go +++ b/pkg/controller/controller_utils_test.go @@ -19,6 +19,7 @@ package controller import ( "encoding/json" "fmt" + "math" "math/rand" "net/http/httptest" "reflect" @@ -443,3 +444,38 @@ func TestActiveReplicaSetsFiltering(t *testing.T) { t.Errorf("expected %v, got %v", expectedNames.List(), gotNames.List()) } } + +func int64P(num int64) *int64 { + return &num +} + +func TestComputeHash(t *testing.T) { + tests := []struct { + name string + template *v1.PodTemplateSpec + collisionCount *int64 + otherCollisionCount *int64 + }{ + { + name: "simple", + template: &v1.PodTemplateSpec{}, + collisionCount: int64P(1), + otherCollisionCount: int64P(2), + }, + { + name: "using math.MaxInt64", + template: &v1.PodTemplateSpec{}, + collisionCount: nil, + otherCollisionCount: int64P(int64(math.MaxInt64)), + }, + } + + for _, test := range tests { + hash := ComputeHash(test.template, test.collisionCount) + otherHash := ComputeHash(test.template, test.otherCollisionCount) + + if hash == otherHash { + t.Errorf("expected different hashes but got the same: %d", hash) + } + } +} diff --git a/pkg/controller/daemon/BUILD b/pkg/controller/daemon/BUILD index 74c714d0c7e..9305e1ac274 100644 --- a/pkg/controller/daemon/BUILD +++ b/pkg/controller/daemon/BUILD @@ -21,11 +21,14 @@ go_library( "//pkg/api/v1:go_default_library", "//pkg/api/v1/helper:go_default_library", "//pkg/api/v1/pod:go_default_library", + "//pkg/apis/apps/v1beta1:go_default_library", "//pkg/apis/extensions/v1beta1:go_default_library", "//pkg/client/clientset_generated/clientset:go_default_library", "//pkg/client/clientset_generated/clientset/typed/extensions/v1beta1:go_default_library", + "//pkg/client/informers/informers_generated/externalversions/apps/v1beta1:go_default_library", "//pkg/client/informers/informers_generated/externalversions/core/v1:go_default_library", "//pkg/client/informers/informers_generated/externalversions/extensions/v1beta1:go_default_library", + "//pkg/client/listers/apps/v1beta1:go_default_library", "//pkg/client/listers/core/v1:go_default_library", "//pkg/client/listers/extensions/v1beta1:go_default_library", "//pkg/controller:go_default_library", @@ -38,9 +41,11 @@ go_library( "//plugin/pkg/scheduler/algorithm/predicates:go_default_library", "//plugin/pkg/scheduler/schedulercache:go_default_library", "//vendor/github.com/golang/glog: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/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", diff --git a/pkg/controller/daemon/daemoncontroller.go b/pkg/controller/daemon/daemoncontroller.go index 2a7e83cfc8b..71e35e31bb6 100644 --- a/pkg/controller/daemon/daemoncontroller.go +++ b/pkg/controller/daemon/daemoncontroller.go @@ -39,11 +39,14 @@ import ( "k8s.io/kubernetes/pkg/api/v1" v1helper "k8s.io/kubernetes/pkg/api/v1/helper" podutil "k8s.io/kubernetes/pkg/api/v1/pod" + apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" unversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1" + appsinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/apps/v1beta1" coreinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/core/v1" extensionsinformers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions/extensions/v1beta1" + appslisters "k8s.io/kubernetes/pkg/client/listers/apps/v1beta1" corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1" extensionslisters "k8s.io/kubernetes/pkg/client/listers/extensions/v1beta1" "k8s.io/kubernetes/pkg/controller" @@ -99,6 +102,11 @@ type DaemonSetsController struct { // dsStoreSynced returns true if the daemonset store has been synced at least once. // Added as a member to the struct to allow injection for testing. dsStoreSynced cache.InformerSynced + // historyLister get list/get history from the shared informers's store + historyLister appslisters.ControllerRevisionLister + // historyStoreSynced returns true if the history store has been synced at least once. + // Added as a member to the struct to allow injection for testing. + historyStoreSynced cache.InformerSynced // podLister get list/get pods from the shared informers's store podLister corelisters.PodLister // podStoreSynced returns true if the pod store has been synced at least once. @@ -114,7 +122,7 @@ type DaemonSetsController struct { queue workqueue.RateLimitingInterface } -func NewDaemonSetsController(daemonSetInformer extensionsinformers.DaemonSetInformer, podInformer coreinformers.PodInformer, nodeInformer coreinformers.NodeInformer, kubeClient clientset.Interface) *DaemonSetsController { +func NewDaemonSetsController(daemonSetInformer extensionsinformers.DaemonSetInformer, historyInformer appsinformers.ControllerRevisionInformer, podInformer coreinformers.PodInformer, nodeInformer coreinformers.NodeInformer, kubeClient clientset.Interface) *DaemonSetsController { eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartLogging(glog.Infof) // TODO: remove the wrapper when every clients have moved to use the clientset. @@ -152,6 +160,14 @@ func NewDaemonSetsController(daemonSetInformer extensionsinformers.DaemonSetInfo dsc.dsLister = daemonSetInformer.Lister() dsc.dsStoreSynced = daemonSetInformer.Informer().HasSynced + historyInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: dsc.addHistory, + UpdateFunc: dsc.updateHistory, + DeleteFunc: dsc.deleteHistory, + }) + dsc.historyLister = historyInformer.Lister() + dsc.historyStoreSynced = historyInformer.Informer().HasSynced + // Watch for creation/deletion of pods. The reason we watch is that we don't want a daemon set to create/delete // more pods until all the effects (expectations) of a daemon set's create/delete have been observed. podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ @@ -273,6 +289,138 @@ func (dsc *DaemonSetsController) getPodDaemonSets(pod *v1.Pod) []*extensions.Dae return sets } +// getDaemonSetsForHistory returns a list of DaemonSets that potentially +// match a ControllerRevision. +func (dsc *DaemonSetsController) getDaemonSetsForHistory(history *apps.ControllerRevision) []*extensions.DaemonSet { + daemonSets, err := dsc.dsLister.GetHistoryDaemonSets(history) + if err != nil || len(daemonSets) == 0 { + return nil + } + if len(daemonSets) > 1 { + // ControllerRef will ensure we don't do anything crazy, but more than one + // item in this list nevertheless constitutes user error. + glog.V(4).Infof("User error! more than one DaemonSets is selecting ControllerRevision %s/%s with labels: %#v", + history.Namespace, history.Name, history.Labels) + } + return daemonSets +} + +// addHistory enqueues the DaemonSet that manages a ControllerRevision when the ControllerRevision is created +// or when the controller manager is restarted. +func (dsc *DaemonSetsController) addHistory(obj interface{}) { + history := obj.(*apps.ControllerRevision) + if history.DeletionTimestamp != nil { + // On a restart of the controller manager, it's possible for an object to + // show up in a state that is already pending deletion. + dsc.deleteHistory(history) + return + } + + // If it has a ControllerRef, that's all that matters. + if controllerRef := controller.GetControllerOf(history); controllerRef != nil { + ds := dsc.resolveControllerRef(history.Namespace, controllerRef) + if ds == nil { + return + } + glog.V(4).Infof("ControllerRevision %s added.", history.Name) + return + } + + // Otherwise, it's an orphan. Get a list of all matching DaemonSets and sync + // them to see if anyone wants to adopt it. + daemonSets := dsc.getDaemonSetsForHistory(history) + if len(daemonSets) == 0 { + return + } + glog.V(4).Infof("Orphan ControllerRevision %s added.", history.Name) + for _, ds := range daemonSets { + dsc.enqueueDaemonSet(ds) + } +} + +// updateHistory figures out what DaemonSet(s) manage a ControllerRevision when the ControllerRevision +// is updated and wake them up. If the anything of the ControllerRevision have changed, we need to +// awaken both the old and new DaemonSets. +func (dsc *DaemonSetsController) updateHistory(old, cur interface{}) { + curHistory := cur.(*apps.ControllerRevision) + oldHistory := old.(*apps.ControllerRevision) + if curHistory.ResourceVersion == oldHistory.ResourceVersion { + // Periodic resync will send update events for all known ControllerRevisions. + return + } + + curControllerRef := controller.GetControllerOf(curHistory) + oldControllerRef := controller.GetControllerOf(oldHistory) + controllerRefChanged := !reflect.DeepEqual(curControllerRef, oldControllerRef) + if controllerRefChanged && oldControllerRef != nil { + // The ControllerRef was changed. Sync the old controller, if any. + if ds := dsc.resolveControllerRef(oldHistory.Namespace, oldControllerRef); ds != nil { + dsc.enqueueDaemonSet(ds) + } + } + + // If it has a ControllerRef, that's all that matters. + if curControllerRef != nil { + ds := dsc.resolveControllerRef(curHistory.Namespace, curControllerRef) + if ds == nil { + return + } + glog.V(4).Infof("ControllerRevision %s updated.", curHistory.Name) + dsc.enqueueDaemonSet(ds) + return + } + + // Otherwise, it's an orphan. If anything changed, sync matching controllers + // to see if anyone wants to adopt it now. + labelChanged := !reflect.DeepEqual(curHistory.Labels, oldHistory.Labels) + if labelChanged || controllerRefChanged { + daemonSets := dsc.getDaemonSetsForHistory(curHistory) + if len(daemonSets) == 0 { + return + } + glog.V(4).Infof("Orphan ControllerRevision %s updated.", curHistory.Name) + for _, ds := range daemonSets { + dsc.enqueueDaemonSet(ds) + } + } +} + +// deleteHistory enqueues the DaemonSet that manages a ControllerRevision when +// the ControllerRevision is deleted. obj could be an *app.ControllerRevision, or +// a DeletionFinalStateUnknown marker item. +func (dsc *DaemonSetsController) deleteHistory(obj interface{}) { + history, ok := obj.(*apps.ControllerRevision) + + // When a delete is dropped, the relist will notice a ControllerRevision in the store not + // in the list, leading to the insertion of a tombstone object which contains + // the deleted key/value. Note that this value might be stale. If the ControllerRevision + // changed labels the new DaemonSet will not be woken up till the periodic resync. + if !ok { + tombstone, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + utilruntime.HandleError(fmt.Errorf("Couldn't get object from tombstone %#v", obj)) + return + } + history, ok = tombstone.Obj.(*apps.ControllerRevision) + if !ok { + utilruntime.HandleError(fmt.Errorf("Tombstone contained object that is not a ControllerRevision %#v", obj)) + return + } + } + + controllerRef := controller.GetControllerOf(history) + if controllerRef == nil { + // No controller should care about orphans being deleted. + return + } + ds := dsc.resolveControllerRef(history.Namespace, controllerRef) + if ds == nil { + return + } + glog.V(4).Infof("ControllerRevision %s deleted.", history.Name) + dsc.enqueueDaemonSet(ds) +} + func (dsc *DaemonSetsController) addPod(obj interface{}) { pod := obj.(*v1.Pod) @@ -486,11 +634,11 @@ func (dsc *DaemonSetsController) updateNode(old, cur interface{}) { } } -// getNodesToDaemonSetPods returns a map from nodes to daemon pods (corresponding to ds) running on the nodes. +// getDaemonPods returns daemon pods owned by the given ds. // This also reconciles ControllerRef by adopting/orphaning. // Note that returned Pods are pointers to objects in the cache. // If you want to modify one, you need to deep-copy it first. -func (dsc *DaemonSetsController) getNodesToDaemonPods(ds *extensions.DaemonSet) (map[string][]*v1.Pod, error) { +func (dsc *DaemonSetsController) getDaemonPods(ds *extensions.DaemonSet) ([]*v1.Pod, error) { selector, err := metav1.LabelSelectorAsSelector(ds.Spec.Selector) if err != nil { return nil, err @@ -516,7 +664,15 @@ func (dsc *DaemonSetsController) getNodesToDaemonPods(ds *extensions.DaemonSet) }) // Use ControllerRefManager to adopt/orphan as needed. cm := controller.NewPodControllerRefManager(dsc.podControl, ds, selector, controllerKind, canAdoptFunc) - claimedPods, err := cm.ClaimPods(pods) + return cm.ClaimPods(pods) +} + +// getNodesToDaemonPods returns a map from nodes to daemon pods (corresponding to ds) running on the nodes. +// This also reconciles ControllerRef by adopting/orphaning. +// Note that returned Pods are pointers to objects in the cache. +// If you want to modify one, you need to deep-copy it first. +func (dsc *DaemonSetsController) getNodesToDaemonPods(ds *extensions.DaemonSet) (map[string][]*v1.Pod, error) { + claimedPods, err := dsc.getDaemonPods(ds) if err != nil { return nil, err } @@ -554,18 +710,18 @@ func (dsc *DaemonSetsController) resolveControllerRef(namespace string, controll return ds } -func (dsc *DaemonSetsController) manage(ds *extensions.DaemonSet) error { +func (dsc *DaemonSetsController) manage(ds *extensions.DaemonSet) (string, error) { // Find out which nodes are running the daemon pods controlled by ds. nodeToDaemonPods, err := dsc.getNodesToDaemonPods(ds) if err != nil { - return fmt.Errorf("couldn't get node to daemon pod mapping for daemon set %q: %v", ds.Name, err) + return "", fmt.Errorf("couldn't get node to daemon pod mapping for daemon set %q: %v", ds.Name, err) } // For each node, if the node is running the daemon pod but isn't supposed to, kill the daemon // pod. If the node is supposed to run the daemon pod, but isn't, create the daemon pod on the node. nodeList, err := dsc.nodeLister.List(labels.Everything()) if err != nil { - return fmt.Errorf("couldn't get list of nodes when syncing daemon set %#v: %v", ds, err) + return "", fmt.Errorf("couldn't get list of nodes when syncing daemon set %#v: %v", ds, err) } var nodesNeedingDaemonPods, podsToDelete []string var failedPodsObserved int @@ -612,23 +768,33 @@ func (dsc *DaemonSetsController) manage(ds *extensions.DaemonSet) error { } } } - errors := dsc.syncNodes(ds, podsToDelete, nodesNeedingDaemonPods) + + // Find current history of the DaemonSet, and label new pods using the hash label value of the current history when creating them + cur, _, err := dsc.constructHistory(ds) + if err != nil { + return "", fmt.Errorf("failed to construct revisions of DaemonSet: %v", err) + } + + hash := cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey] + if err = dsc.syncNodes(ds, podsToDelete, nodesNeedingDaemonPods, hash); err != nil { + return "", err + } // Throw an error when the daemon pods fail, to use ratelimiter to prevent kill-recreate hot loop if failedPodsObserved > 0 { - errors = append(errors, fmt.Errorf("deleted %d failed pods of DaemonSet %s/%s", failedPodsObserved, ds.Namespace, ds.Name)) + return "", fmt.Errorf("deleted %d failed pods of DaemonSet %s/%s", failedPodsObserved, ds.Namespace, ds.Name) } - return utilerrors.NewAggregate(errors) + return hash, nil } // syncNodes deletes given pods and creates new daemon set pods on the given nodes // returns slice with erros if any -func (dsc *DaemonSetsController) syncNodes(ds *extensions.DaemonSet, podsToDelete, nodesNeedingDaemonPods []string) []error { +func (dsc *DaemonSetsController) syncNodes(ds *extensions.DaemonSet, podsToDelete, nodesNeedingDaemonPods []string, hash string) error { // We need to set expectations before creating/deleting pods to avoid race conditions. dsKey, err := controller.KeyFunc(ds) if err != nil { - return []error{fmt.Errorf("couldn't get key for object %#v: %v", ds, err)} + return fmt.Errorf("couldn't get key for object %#v: %v", ds, err) } createDiff := len(nodesNeedingDaemonPods) @@ -649,7 +815,7 @@ func (dsc *DaemonSetsController) syncNodes(ds *extensions.DaemonSet, podsToDelet glog.V(4).Infof("Nodes needing daemon pods for daemon set %s: %+v, creating %d", ds.Name, nodesNeedingDaemonPods, createDiff) createWait := sync.WaitGroup{} createWait.Add(createDiff) - template := util.GetPodTemplateWithGeneration(ds.Spec.Template, ds.Spec.TemplateGeneration) + template := util.CreatePodTemplate(ds.Spec.Template, ds.Spec.TemplateGeneration, hash) for i := 0; i < createDiff; i++ { go func(ix int) { defer createWait.Done() @@ -685,7 +851,7 @@ func (dsc *DaemonSetsController) syncNodes(ds *extensions.DaemonSet, podsToDelet for err := range errCh { errors = append(errors, err) } - return errors + return utilerrors.NewAggregate(errors) } func storeDaemonSetStatus(dsClient unversionedextensions.DaemonSetInterface, ds *extensions.DaemonSet, desiredNumberScheduled, currentNumberScheduled, numberMisscheduled, numberReady, updatedNumberScheduled, numberAvailable, numberUnavailable int) error { @@ -767,7 +933,7 @@ func (dsc *DaemonSetsController) updateDaemonSetStatus(ds *extensions.DaemonSet) numberAvailable++ } } - if util.IsPodUpdated(ds.Spec.TemplateGeneration, pod) { + if util.IsPodUpdated(ds.Spec.TemplateGeneration, pod, ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey]) { updatedNumberScheduled++ } } @@ -825,21 +991,28 @@ func (dsc *DaemonSetsController) syncDaemonSet(key string) error { return dsc.updateDaemonSetStatus(ds) } - if err := dsc.manage(ds); err != nil { + hash, err := dsc.manage(ds) + if err != nil { return err } // Process rolling updates if we're ready. if dsc.expectations.SatisfiedExpectations(dsKey) { switch ds.Spec.UpdateStrategy.Type { + case extensions.OnDeleteDaemonSetStrategyType: case extensions.RollingUpdateDaemonSetStrategyType: - err = dsc.rollingUpdate(ds) + err = dsc.rollingUpdate(ds, hash) } if err != nil { return err } } + err = dsc.cleanupHistory(ds) + if err != nil { + return fmt.Errorf("failed to clean up revisions of DaemonSet: %v", err) + } + return dsc.updateDaemonSetStatus(ds) } diff --git a/pkg/controller/daemon/daemoncontroller_test.go b/pkg/controller/daemon/daemoncontroller_test.go index cf30584e25e..51cdab11944 100644 --- a/pkg/controller/daemon/daemoncontroller_test.go +++ b/pkg/controller/daemon/daemoncontroller_test.go @@ -44,6 +44,7 @@ import ( "k8s.io/kubernetes/pkg/controller" kubelettypes "k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/securitycontext" + labelsutil "k8s.io/kubernetes/pkg/util/labels" "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm" ) @@ -84,6 +85,7 @@ func getKey(ds *extensions.DaemonSet, t *testing.T) string { } func newDaemonSet(name string) *extensions.DaemonSet { + two := int32(2) return &extensions.DaemonSet{ TypeMeta: metav1.TypeMeta{APIVersion: testapi.Extensions.GroupVersion().String()}, ObjectMeta: metav1.ObjectMeta{ @@ -92,6 +94,10 @@ func newDaemonSet(name string) *extensions.DaemonSet { Namespace: metav1.NamespaceDefault, }, Spec: extensions.DaemonSetSpec{ + RevisionHistoryLimit: &two, + UpdateStrategy: extensions.DaemonSetUpdateStrategy{ + Type: extensions.OnDeleteDaemonSetStrategyType, + }, Selector: &metav1.LabelSelector{MatchLabels: simpleDaemonSetLabel}, Template: v1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ @@ -139,11 +145,18 @@ func addNodes(nodeStore cache.Store, startIndex, numNodes int, label map[string] } func newPod(podName string, nodeName string, label map[string]string, ds *extensions.DaemonSet) *v1.Pod { + // Add hash unique label to the pod + newLabels := label + if ds != nil { + hash := fmt.Sprint(controller.ComputeHash(&ds.Spec.Template, ds.Status.CollisionCount)) + newLabels = labelsutil.CloneAndAddLabel(label, extensions.DefaultDaemonSetUniqueLabelKey, hash) + } + pod := &v1.Pod{ TypeMeta: metav1.TypeMeta{APIVersion: api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String()}, ObjectMeta: metav1.ObjectMeta{ GenerateName: podName, - Labels: label, + Labels: newLabels, Namespace: metav1.NamespaceDefault, }, Spec: v1.PodSpec{ @@ -168,7 +181,8 @@ func newPod(podName string, nodeName string, label map[string]string, ds *extens func addPods(podStore cache.Store, nodeName string, label map[string]string, ds *extensions.DaemonSet, number int) { for i := 0; i < number; i++ { - podStore.Add(newPod(fmt.Sprintf("%s-", nodeName), nodeName, label, ds)) + pod := newPod(fmt.Sprintf("%s-", nodeName), nodeName, label, ds) + podStore.Add(pod) } } @@ -251,6 +265,7 @@ func newTestController(initialObjects ...runtime.Object) (*daemonSetsController, manager := NewDaemonSetsController( informerFactory.Extensions().V1beta1().DaemonSets(), + informerFactory.Apps().V1beta1().ControllerRevisions(), informerFactory.Core().V1().Pods(), informerFactory.Core().V1().Nodes(), clientset, diff --git a/pkg/controller/daemon/update.go b/pkg/controller/daemon/update.go index d474fb2d31e..9be6960e430 100644 --- a/pkg/controller/daemon/update.go +++ b/pkg/controller/daemon/update.go @@ -17,28 +17,37 @@ limitations under the License. package daemon import ( + "bytes" + "encoding/json" "fmt" + "sort" "github.com/golang/glog" + 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" - utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/runtime" intstrutil "k8s.io/apimachinery/pkg/util/intstr" + "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" extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" + "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller/daemon/util" + labelsutil "k8s.io/kubernetes/pkg/util/labels" ) // rollingUpdate deletes old daemon set pods making sure that no more than // ds.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable pods are unavailable -func (dsc *DaemonSetsController) rollingUpdate(ds *extensions.DaemonSet) error { +func (dsc *DaemonSetsController) rollingUpdate(ds *extensions.DaemonSet, hash string) error { nodeToDaemonPods, err := dsc.getNodesToDaemonPods(ds) if err != nil { return fmt.Errorf("couldn't get node to daemon pod mapping for daemon set %q: %v", ds.Name, err) } - _, oldPods := dsc.getAllDaemonSetPods(ds, nodeToDaemonPods) + _, oldPods := dsc.getAllDaemonSetPods(ds, nodeToDaemonPods, hash) maxUnavailable, numUnavailable, err := dsc.getUnavailableNumbers(ds, nodeToDaemonPods) if err != nil { return fmt.Errorf("Couldn't get unavailable numbers: %v", err) @@ -46,7 +55,7 @@ func (dsc *DaemonSetsController) rollingUpdate(ds *extensions.DaemonSet) error { oldAvailablePods, oldUnavailablePods := util.SplitByAvailablePods(ds.Spec.MinReadySeconds, oldPods) // for oldPods delete all not running pods - var podsToDelete []string + var oldPodsToDelete []string glog.V(4).Infof("Marking all unavailable old pods for deletion") for _, pod := range oldUnavailablePods { // Skip terminating pods. We won't delete them again @@ -54,7 +63,7 @@ func (dsc *DaemonSetsController) rollingUpdate(ds *extensions.DaemonSet) error { continue } glog.V(4).Infof("Marking pod %s/%s for deletion", ds.Name, pod.Name) - podsToDelete = append(podsToDelete, pod.Name) + oldPodsToDelete = append(oldPodsToDelete, pod.Name) } glog.V(4).Infof("Marking old pods for deletion") @@ -64,20 +73,311 @@ func (dsc *DaemonSetsController) rollingUpdate(ds *extensions.DaemonSet) error { break } glog.V(4).Infof("Marking pod %s/%s for deletion", ds.Name, pod.Name) - podsToDelete = append(podsToDelete, pod.Name) + oldPodsToDelete = append(oldPodsToDelete, pod.Name) numUnavailable++ } - errors := dsc.syncNodes(ds, podsToDelete, []string{}) - return utilerrors.NewAggregate(errors) + return dsc.syncNodes(ds, oldPodsToDelete, []string{}, hash) } -func (dsc *DaemonSetsController) getAllDaemonSetPods(ds *extensions.DaemonSet, nodeToDaemonPods map[string][]*v1.Pod) ([]*v1.Pod, []*v1.Pod) { +func (dsc *DaemonSetsController) constructHistory(ds *extensions.DaemonSet) (cur *apps.ControllerRevision, old []*apps.ControllerRevision, err error) { + var histories []*apps.ControllerRevision + var currentHistories []*apps.ControllerRevision + histories, err = dsc.controlledHistories(ds) + if err != nil { + return nil, nil, err + } + for _, history := range histories { + // Add the unique label if it's not already added to the history + // We use history name instead of computing hash, so that we don't need to worry about hash collision + if _, ok := history.Labels[extensions.DefaultDaemonSetUniqueLabelKey]; !ok { + var clone interface{} + clone, err = api.Scheme.DeepCopy(history) + if err != nil { + return nil, nil, err + } + toUpdate := clone.(*apps.ControllerRevision) + toUpdate.Labels[extensions.DefaultDaemonSetUniqueLabelKey] = toUpdate.Name + history, err = dsc.kubeClient.AppsV1beta1().ControllerRevisions(ds.Namespace).Update(toUpdate) + if err != nil { + return nil, nil, err + } + } + // Compare histories with ds to separate cur and old history + found := false + found, err = match(ds, history) + if err != nil { + return nil, nil, err + } + if found { + currentHistories = append(currentHistories, history) + } else { + old = append(old, history) + } + } + + currRevision := maxRevision(old) + 1 + switch len(currentHistories) { + case 0: + // Create a new history if the current one isn't found + cur, err = dsc.snapshot(ds, currRevision) + if err != nil { + return nil, nil, err + } + default: + cur, err = dsc.dedupCurHistories(ds, currentHistories) + if err != nil { + return nil, nil, err + } + // Update revision number if necessary + if cur.Revision < currRevision { + var clone interface{} + clone, err = api.Scheme.DeepCopy(cur) + if err != nil { + return nil, nil, err + } + toUpdate := clone.(*apps.ControllerRevision) + toUpdate.Revision = currRevision + _, err = dsc.kubeClient.AppsV1beta1().ControllerRevisions(ds.Namespace).Update(toUpdate) + if err != nil { + return nil, nil, err + } + } + } + // Label ds with current history's unique label as well + if ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey] != cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey] { + var clone interface{} + clone, err = api.Scheme.DeepCopy(ds) + if err != nil { + return nil, nil, err + } + toUpdate := clone.(*extensions.DaemonSet) + if toUpdate.Labels == nil { + toUpdate.Labels = make(map[string]string) + } + toUpdate.Labels[extensions.DefaultDaemonSetUniqueLabelKey] = cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey] + _, err = dsc.kubeClient.ExtensionsV1beta1().DaemonSets(ds.Namespace).UpdateStatus(toUpdate) + } + return cur, old, err +} + +func (dsc *DaemonSetsController) cleanupHistory(ds *extensions.DaemonSet) error { + nodesToDaemonPods, err := dsc.getNodesToDaemonPods(ds) + if err != nil { + return fmt.Errorf("couldn't get node to daemon pod mapping for daemon set %q: %v", ds.Name, err) + } + _, old, err := dsc.constructHistory(ds) + if err != nil { + return fmt.Errorf("failed to construct revisions of DaemonSet: %v", err) + } + + toKeep := int(*ds.Spec.RevisionHistoryLimit) + toKill := len(old) - toKeep + if toKill <= 0 { + return nil + } + + // Find all hashes of live pods + liveHashes := make(map[string]bool) + for _, pods := range nodesToDaemonPods { + for _, pod := range pods { + if hash := pod.Labels[extensions.DefaultDaemonSetUniqueLabelKey]; len(hash) > 0 { + liveHashes[hash] = true + } + } + } + + // Find all live history with the above hashes + liveHistory := make(map[string]bool) + for _, history := range old { + if hash := history.Labels[extensions.DefaultDaemonSetUniqueLabelKey]; liveHashes[hash] { + liveHistory[history.Name] = true + } + } + + // Clean up old history from smallest to highest revision (from oldest to newest) + sort.Sort(historiesByRevision(old)) + for _, history := range old { + if toKill <= 0 { + break + } + if liveHistory[history.Name] { + continue + } + // Clean up + err := dsc.kubeClient.AppsV1beta1().ControllerRevisions(ds.Namespace).Delete(history.Name, nil) + if err != nil { + return err + } + toKill-- + } + return nil +} + +// maxRevision returns the max revision number of the given list of histories +func maxRevision(histories []*apps.ControllerRevision) int64 { + max := int64(0) + for _, history := range histories { + if history.Revision > max { + max = history.Revision + } + } + return max +} + +func (dsc *DaemonSetsController) dedupCurHistories(ds *extensions.DaemonSet, curHistories []*apps.ControllerRevision) (*apps.ControllerRevision, error) { + if len(curHistories) == 1 { + return curHistories[0], nil + } + var maxRevision int64 + var keepCur *apps.ControllerRevision + for _, cur := range curHistories { + if cur.Revision >= maxRevision { + keepCur = cur + maxRevision = cur.Revision + } + } + // Clean up duplicates and relabel pods + for _, cur := range curHistories { + if cur.Name == keepCur.Name { + continue + } + // Relabel pods before dedup + pods, err := dsc.getDaemonPods(ds) + if err != nil { + return nil, err + } + for _, pod := range pods { + if pod.Labels[extensions.DefaultDaemonSetUniqueLabelKey] != keepCur.Labels[extensions.DefaultDaemonSetUniqueLabelKey] { + clone, err := api.Scheme.DeepCopy(pod) + if err != nil { + return nil, err + } + toUpdate := clone.(*v1.Pod) + if toUpdate.Labels == nil { + toUpdate.Labels = make(map[string]string) + } + toUpdate.Labels[extensions.DefaultDaemonSetUniqueLabelKey] = keepCur.Labels[extensions.DefaultDaemonSetUniqueLabelKey] + _, err = dsc.kubeClient.Core().Pods(ds.Namespace).Update(toUpdate) + if err != nil { + return nil, err + } + } + } + // Remove duplicates + err = dsc.kubeClient.AppsV1beta1().ControllerRevisions(ds.Namespace).Delete(cur.Name, nil) + if err != nil { + return nil, err + } + } + return keepCur, nil +} + +// controlledHistories returns all ControllerRevisions controlled by the given DaemonSet +// Note that returned histories are pointers to objects in the cache. +// If you want to modify one, you need to deep-copy it first. +func (dsc *DaemonSetsController) controlledHistories(ds *extensions.DaemonSet) ([]*apps.ControllerRevision, error) { + var result []*apps.ControllerRevision + selector, err := metav1.LabelSelectorAsSelector(ds.Spec.Selector) + if err != nil { + return nil, err + } + histories, err := dsc.historyLister.List(selector) + if err != nil { + return nil, err + } + for _, history := range histories { + // Skip history that doesn't belong to the DaemonSet + if controllerRef := controller.GetControllerOf(history); controllerRef == nil || controllerRef.UID != ds.UID { + continue + } + result = append(result, history) + } + return result, nil +} + +// match check if ds template is semantically equal to the template stored in history +func match(ds *extensions.DaemonSet, history *apps.ControllerRevision) (bool, error) { + template, err := decodeHistory(history) + return apiequality.Semantic.DeepEqual(&ds.Spec.Template, template), err +} + +func decodeHistory(history *apps.ControllerRevision) (*v1.PodTemplateSpec, error) { + raw := history.Data.Raw + decoder := json.NewDecoder(bytes.NewBuffer(raw)) + template := v1.PodTemplateSpec{} + err := decoder.Decode(&template) + return &template, err +} + +func encodeTemplate(template *v1.PodTemplateSpec) ([]byte, error) { + buffer := new(bytes.Buffer) + encoder := json.NewEncoder(buffer) + err := encoder.Encode(template) + return buffer.Bytes(), err +} + +func (dsc *DaemonSetsController) snapshot(ds *extensions.DaemonSet, revision int64) (*apps.ControllerRevision, error) { + encodedTemplate, err := encodeTemplate(&ds.Spec.Template) + if err != nil { + return nil, err + } + hash := fmt.Sprint(controller.ComputeHash(&ds.Spec.Template, ds.Status.CollisionCount)) + name := ds.Name + "-" + hash + history := &apps.ControllerRevision{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ds.Namespace, + Labels: labelsutil.CloneAndAddLabel(ds.Spec.Template.Labels, extensions.DefaultDaemonSetUniqueLabelKey, hash), + Annotations: ds.Annotations, + OwnerReferences: []metav1.OwnerReference{*newControllerRef(ds)}, + }, + Data: runtime.RawExtension{Raw: encodedTemplate}, + Revision: revision, + } + + history, err = dsc.kubeClient.AppsV1beta1().ControllerRevisions(ds.Namespace).Create(history) + if errors.IsAlreadyExists(err) { + // TODO: Is it okay to get from historyLister? + existedHistory, getErr := dsc.kubeClient.AppsV1beta1().ControllerRevisions(ds.Namespace).Get(name, metav1.GetOptions{}) + if getErr != nil { + return nil, getErr + } + // Check if we already created it + done, err := match(ds, existedHistory) + if err != nil { + return nil, err + } + if done { + return existedHistory, nil + } + + // Handle name collisions between different history + // TODO: Is it okay to get from dsLister? + currDS, getErr := dsc.kubeClient.ExtensionsV1beta1().DaemonSets(ds.Namespace).Get(ds.Name, metav1.GetOptions{}) + if getErr != nil { + return nil, getErr + } + if currDS.Status.CollisionCount == nil { + currDS.Status.CollisionCount = new(int64) + } + *currDS.Status.CollisionCount++ + _, updateErr := dsc.kubeClient.ExtensionsV1beta1().DaemonSets(ds.Namespace).UpdateStatus(currDS) + if updateErr != nil { + return nil, updateErr + } + glog.V(2).Infof("Found a hash collision for DaemonSet %q - bumping collisionCount to %d to resolve it", ds.Name, *currDS.Status.CollisionCount) + return nil, err + } + return history, err +} + +func (dsc *DaemonSetsController) getAllDaemonSetPods(ds *extensions.DaemonSet, nodeToDaemonPods map[string][]*v1.Pod, hash string) ([]*v1.Pod, []*v1.Pod) { var newPods []*v1.Pod var oldPods []*v1.Pod for _, pods := range nodeToDaemonPods { for _, pod := range pods { - if util.IsPodUpdated(ds.Spec.TemplateGeneration, pod) { + if util.IsPodUpdated(ds.Spec.TemplateGeneration, pod, hash) { newPods = append(newPods, pod) } else { oldPods = append(oldPods, pod) @@ -129,3 +429,11 @@ func (dsc *DaemonSetsController) getUnavailableNumbers(ds *extensions.DaemonSet, glog.V(4).Infof(" DaemonSet %s/%s, maxUnavailable: %d, numUnavailable: %d", ds.Namespace, ds.Name, maxUnavailable, numUnavailable) return maxUnavailable, numUnavailable, nil } + +type historiesByRevision []*apps.ControllerRevision + +func (h historiesByRevision) Len() int { return len(h) } +func (h historiesByRevision) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h historiesByRevision) Less(i, j int) bool { + return h[i].Revision < h[j].Revision +} diff --git a/pkg/controller/daemon/util/daemonset_util.go b/pkg/controller/daemon/util/daemonset_util.go index 0b101e769c6..a6acf970864 100644 --- a/pkg/controller/daemon/util/daemonset_util.go +++ b/pkg/controller/daemon/util/daemonset_util.go @@ -29,9 +29,10 @@ import ( "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm" ) -// GetPodTemplateWithHash returns copy of provided template with additional -// label which contains hash of provided template and sets default daemon tolerations. -func GetPodTemplateWithGeneration(template v1.PodTemplateSpec, generation int64) v1.PodTemplateSpec { +// CreatePodTemplate returns copy of provided template with additional +// label which contains templateGeneration (for backward compatibility), +// hash of provided template and sets default daemon tolerations. +func CreatePodTemplate(template v1.PodTemplateSpec, generation int64, hash string) v1.PodTemplateSpec { obj, _ := api.Scheme.DeepCopy(template) newTemplate := obj.(v1.PodTemplateSpec) // DaemonSet pods shouldn't be deleted by NodeController in case of node problems. @@ -60,14 +61,19 @@ func GetPodTemplateWithGeneration(template v1.PodTemplateSpec, generation int64) extensions.DaemonSetTemplateGenerationKey, templateGenerationStr, ) + // TODO: do we need to validate if the DaemonSet is RollingUpdate or not? + if len(hash) > 0 { + newTemplate.ObjectMeta.Labels[extensions.DefaultDaemonSetUniqueLabelKey] = hash + } return newTemplate } -// IsPodUpdate checks if pod contains label with provided hash -func IsPodUpdated(dsTemplateGeneration int64, pod *v1.Pod) bool { - podTemplateGeneration, generationExists := pod.ObjectMeta.Labels[extensions.DaemonSetTemplateGenerationKey] - dsTemplateGenerationStr := fmt.Sprint(dsTemplateGeneration) - return generationExists && podTemplateGeneration == dsTemplateGenerationStr +// IsPodUpdate checks if pod contains label value that either matches templateGeneration or hash +func IsPodUpdated(dsTemplateGeneration int64, pod *v1.Pod, hash string) bool { + // Compare with hash to see if the pod is updated, need to maintain backward compatibility of templateGeneration + templateMatches := pod.Labels[extensions.DaemonSetTemplateGenerationKey] == fmt.Sprint(dsTemplateGeneration) + hashMatches := len(hash) > 0 && pod.Labels[extensions.DefaultDaemonSetUniqueLabelKey] == hash + return hashMatches || templateMatches } // SplitByAvailablePods splits provided daemon set pods by availabilty diff --git a/pkg/controller/daemon/util/daemonset_util_test.go b/pkg/controller/daemon/util/daemonset_util_test.go index 630b60ebdd9..e4fa22c4949 100644 --- a/pkg/controller/daemon/util/daemonset_util_test.go +++ b/pkg/controller/daemon/util/daemonset_util_test.go @@ -47,46 +47,118 @@ func newPod(podName string, nodeName string, label map[string]string) *v1.Pod { } func TestIsPodUpdated(t *testing.T) { + templateGeneration := int64(12345) + hash := "55555" + labels := map[string]string{extensions.DaemonSetTemplateGenerationKey: fmt.Sprint(templateGeneration), extensions.DefaultDaemonSetUniqueLabelKey: hash} + labelsNoHash := map[string]string{extensions.DaemonSetTemplateGenerationKey: fmt.Sprint(templateGeneration)} tests := []struct { + test string templateGeneration int64 pod *v1.Pod + hash string isUpdated bool }{ { - int64(12345), - newPod("pod1", "node1", map[string]string{extensions.DaemonSetTemplateGenerationKey: "12345"}), + "templateGeneration and hash both match", + templateGeneration, + newPod("pod1", "node1", labels), + hash, true, }, { - int64(12355), - newPod("pod1", "node1", map[string]string{extensions.DaemonSetTemplateGenerationKey: "12345"}), + "templateGeneration matches, hash doesn't", + templateGeneration, + newPod("pod1", "node1", labels), + hash + "123", + true, + }, + { + "templateGeneration matches, no hash label, has hash", + templateGeneration, + newPod("pod1", "node1", labelsNoHash), + hash, + true, + }, + { + "templateGeneration matches, no hash label, no hash", + templateGeneration, + newPod("pod1", "node1", labelsNoHash), + "", + true, + }, + { + "templateGeneration matches, has hash label, no hash", + templateGeneration, + newPod("pod1", "node1", labels), + "", + true, + }, + { + "templateGeneration doesn't match, hash does", + templateGeneration + 1, + newPod("pod1", "node1", labels), + hash, + true, + }, + { + "templateGeneration and hash don't match", + templateGeneration + 1, + newPod("pod1", "node1", labels), + hash + "123", false, }, { - int64(12355), + "empty labels, no hash", + templateGeneration, newPod("pod1", "node1", map[string]string{}), + "", false, }, { - int64(12355), + "empty labels", + templateGeneration, + newPod("pod1", "node1", map[string]string{}), + hash, + false, + }, + { + "no labels", + templateGeneration, newPod("pod1", "node1", nil), + hash, false, }, } for _, test := range tests { - updated := IsPodUpdated(test.templateGeneration, test.pod) + updated := IsPodUpdated(test.templateGeneration, test.pod, test.hash) if updated != test.isUpdated { - t.Errorf("IsPodUpdated returned wrong value. Expected %t, got %t. TemplateGeneration: %d", test.isUpdated, updated, test.templateGeneration) + t.Errorf("%s: IsPodUpdated returned wrong value. Expected %t, got %t", test.test, test.isUpdated, updated) } } } -func TestGetPodTemplateWithGeneration(t *testing.T) { - generation := int64(1) - podTemplateSpec := v1.PodTemplateSpec{} - newPodTemplate := GetPodTemplateWithGeneration(podTemplateSpec, generation) - label, exists := newPodTemplate.ObjectMeta.Labels[extensions.DaemonSetTemplateGenerationKey] - if !exists || label != fmt.Sprint(generation) { - t.Errorf("Error in getting podTemplateSpec with label generation. Exists: %t, label: %s", exists, label) +func TestCreatePodTemplate(t *testing.T) { + tests := []struct { + templateGeneration int64 + hash string + expectUniqueLabel bool + }{ + {int64(1), "", false}, + {int64(2), "3242341807", true}, + } + for _, test := range tests { + podTemplateSpec := v1.PodTemplateSpec{} + newPodTemplate := CreatePodTemplate(podTemplateSpec, test.templateGeneration, test.hash) + val, exists := newPodTemplate.ObjectMeta.Labels[extensions.DaemonSetTemplateGenerationKey] + if !exists || val != fmt.Sprint(test.templateGeneration) { + t.Errorf("Expected podTemplateSpec to have generation label value: %d, got: %s", test.templateGeneration, val) + } + val, exists = newPodTemplate.ObjectMeta.Labels[extensions.DefaultDaemonSetUniqueLabelKey] + if test.expectUniqueLabel && (!exists || val != test.hash) { + t.Errorf("Expected podTemplateSpec to have hash label value: %s, got: %s", test.hash, val) + } + if !test.expectUniqueLabel && exists { + t.Errorf("Expected podTemplateSpec to have no hash label, got: %s", val) + } } } diff --git a/pkg/controller/deployment/sync.go b/pkg/controller/deployment/sync.go index 0268572fcd8..7e43d636ca1 100644 --- a/pkg/controller/deployment/sync.go +++ b/pkg/controller/deployment/sync.go @@ -288,7 +288,7 @@ func (dc *DeploymentController) getNewReplicaSet(d *extensions.Deployment, rsLis return nil, err } newRSTemplate := templateCopy.(v1.PodTemplateSpec) - podTemplateSpecHash := fmt.Sprintf("%d", deploymentutil.GetPodTemplateSpecHash(&newRSTemplate, d.Status.CollisionCount)) + podTemplateSpecHash := fmt.Sprintf("%d", controller.ComputeHash(&newRSTemplate, d.Status.CollisionCount)) newRSTemplate.Labels = labelsutil.CloneAndAddLabel(d.Spec.Template.Labels, extensions.DefaultDeploymentUniqueLabelKey, podTemplateSpecHash) // Add podTemplateHash label to selector. newRSSelector := labelsutil.CloneSelectorAndAddLabel(d.Spec.Selector, extensions.DefaultDeploymentUniqueLabelKey, podTemplateSpecHash) diff --git a/pkg/controller/deployment/util/hash_test.go b/pkg/controller/deployment/util/hash_test.go index d1d944df6d1..9020f92218b 100644 --- a/pkg/controller/deployment/util/hash_test.go +++ b/pkg/controller/deployment/util/hash_test.go @@ -24,6 +24,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/controller" hashutil "k8s.io/kubernetes/pkg/util/hash" ) @@ -110,7 +111,7 @@ func TestPodTemplateSpecHash(t *testing.T) { specJson := strings.Replace(podSpec, "@@VERSION@@", strconv.Itoa(i), 1) spec := v1.PodTemplateSpec{} json.Unmarshal([]byte(specJson), &spec) - hash := GetPodTemplateSpecHash(&spec, nil) + hash := controller.ComputeHash(&spec, nil) if v, ok := seenHashes[hash]; ok { t.Errorf("Hash collision, old: %d new: %d", v, i) break @@ -139,6 +140,6 @@ func BenchmarkFnv(b *testing.B) { json.Unmarshal([]byte(podSpec), &spec) for i := 0; i < b.N; i++ { - GetPodTemplateSpecHash(&spec, nil) + controller.ComputeHash(&spec, nil) } } diff --git a/pkg/controller/deployment/util/pod_util.go b/pkg/controller/deployment/util/pod_util.go index 1954ef7076d..9b54953d12e 100644 --- a/pkg/controller/deployment/util/pod_util.go +++ b/pkg/controller/deployment/util/pod_util.go @@ -17,9 +17,6 @@ limitations under the License. package util import ( - "encoding/binary" - "hash/fnv" - "github.com/golang/glog" errorsutil "k8s.io/apimachinery/pkg/util/errors" @@ -28,23 +25,8 @@ import ( v1core "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1" corelisters "k8s.io/kubernetes/pkg/client/listers/core/v1" "k8s.io/kubernetes/pkg/client/retry" - hashutil "k8s.io/kubernetes/pkg/util/hash" ) -func GetPodTemplateSpecHash(template *v1.PodTemplateSpec, uniquifier *int64) uint32 { - podTemplateSpecHasher := fnv.New32a() - hashutil.DeepHashObject(podTemplateSpecHasher, *template) - - // Add uniquifier in the hash if it exists. - if uniquifier != nil { - uniquifierBytes := make([]byte, 8) - binary.LittleEndian.PutUint64(uniquifierBytes, uint64(*uniquifier)) - podTemplateSpecHasher.Write(uniquifierBytes) - } - - return podTemplateSpecHasher.Sum32() -} - // TODO: use client library instead when it starts to support update retries // see https://github.com/kubernetes/kubernetes/issues/21479 type updatePodFunc func(pod *v1.Pod) error diff --git a/pkg/controller/deployment/util/pod_util_test.go b/pkg/controller/deployment/util/pod_util_test.go deleted file mode 100644 index c312a372fb1..00000000000 --- a/pkg/controller/deployment/util/pod_util_test.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -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 util - -import ( - "math" - "testing" - - "k8s.io/kubernetes/pkg/api/v1" -) - -func int64P(num int64) *int64 { - return &num -} - -func TestGetPodTemplateSpecHash(t *testing.T) { - tests := []struct { - name string - template *v1.PodTemplateSpec - collisionCount *int64 - otherCollisionCount *int64 - }{ - { - name: "simple", - template: &v1.PodTemplateSpec{}, - collisionCount: int64P(1), - otherCollisionCount: int64P(2), - }, - { - name: "using math.MaxInt64", - template: &v1.PodTemplateSpec{}, - collisionCount: nil, - otherCollisionCount: int64P(int64(math.MaxInt64)), - }, - } - - for _, test := range tests { - hash := GetPodTemplateSpecHash(test.template, test.collisionCount) - otherHash := GetPodTemplateSpecHash(test.template, test.otherCollisionCount) - - if hash == otherHash { - t.Errorf("expected different hashes but got the same: %d", hash) - } - } -} diff --git a/pkg/controller/deployment/util/replicaset_util.go b/pkg/controller/deployment/util/replicaset_util.go index a816e2522c3..5a58cd2bc99 100644 --- a/pkg/controller/deployment/util/replicaset_util.go +++ b/pkg/controller/deployment/util/replicaset_util.go @@ -28,6 +28,7 @@ import ( unversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1" extensionslisters "k8s.io/kubernetes/pkg/client/listers/extensions/v1beta1" "k8s.io/kubernetes/pkg/client/retry" + "k8s.io/kubernetes/pkg/controller" labelsutil "k8s.io/kubernetes/pkg/util/labels" ) @@ -76,5 +77,5 @@ func GetReplicaSetHash(rs *extensions.ReplicaSet, uniquifier *int64) (string, er } rsTemplate := template.(v1.PodTemplateSpec) rsTemplate.Labels = labelsutil.CloneAndRemoveLabel(rsTemplate.Labels, extensions.DefaultDeploymentUniqueLabelKey) - return fmt.Sprintf("%d", GetPodTemplateSpecHash(&rsTemplate, uniquifier)), nil + return fmt.Sprintf("%d", controller.ComputeHash(&rsTemplate, uniquifier)), nil } diff --git a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go index ddf83429177..d14e8a46f33 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go @@ -86,6 +86,7 @@ func init() { rbac.NewRule("list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(), rbac.NewRule("list", "watch", "create", "delete", "patch").Groups(legacyGroup).Resources("pods").RuleOrDie(), rbac.NewRule("create").Groups(legacyGroup).Resources("pods/binding").RuleOrDie(), + rbac.NewRule("list", "watch", "create", "delete", "update", "patch").Groups(appsGroup).Resources("controllerrevisions").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 9a9e031bdd0..894734ffd3a 100644 --- a/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml +++ b/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml @@ -180,6 +180,17 @@ items: - pods/binding verbs: - create + - apiGroups: + - apps + resources: + - controllerrevisions + verbs: + - create + - delete + - list + - patch + - update + - watch - apiGroups: - "" resources: From d2cf00fcd6109ee367001c23df775d8e0cad5f15 Mon Sep 17 00:00:00 2001 From: Janet Kuo Date: Fri, 19 May 2017 17:34:04 -0700 Subject: [PATCH 4/5] Test both strategies in all daemonSet controller unit tests --- .../daemon/daemoncontroller_test.go | 1618 +++++++++-------- 1 file changed, 901 insertions(+), 717 deletions(-) diff --git a/pkg/controller/daemon/daemoncontroller_test.go b/pkg/controller/daemon/daemoncontroller_test.go index 51cdab11944..a0ead557e1f 100644 --- a/pkg/controller/daemon/daemoncontroller_test.go +++ b/pkg/controller/daemon/daemoncontroller_test.go @@ -27,6 +27,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apiserver/pkg/storage/names" utilfeature "k8s.io/apiserver/pkg/util/feature" @@ -119,6 +120,24 @@ func newDaemonSet(name string) *extensions.DaemonSet { } } +func newRollbackStrategy() *extensions.DaemonSetUpdateStrategy { + one := intstr.FromInt(1) + return &extensions.DaemonSetUpdateStrategy{ + Type: extensions.RollingUpdateDaemonSetStrategyType, + RollingUpdate: &extensions.RollingUpdateDaemonSet{MaxUnavailable: &one}, + } +} + +func newOnDeleteStrategy() *extensions.DaemonSetUpdateStrategy { + return &extensions.DaemonSetUpdateStrategy{ + Type: extensions.OnDeleteDaemonSetStrategyType, + } +} + +func updateStrategies() []*extensions.DaemonSetUpdateStrategy { + return []*extensions.DaemonSetUpdateStrategy{newOnDeleteStrategy(), newRollbackStrategy()} +} + func newNode(name string, label map[string]string) *v1.Node { return &v1.Node{ TypeMeta: metav1.TypeMeta{APIVersion: api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String()}, @@ -334,14 +353,17 @@ func clearExpectations(t *testing.T, manager *daemonSetsController, ds *extensio } func TestDeleteFinalStateUnknown(t *testing.T) { - manager, _, _ := newTestController() - addNodes(manager.nodeStore, 0, 1, nil) - ds := newDaemonSet("foo") - // DeletedFinalStateUnknown should queue the embedded DS if found. - manager.deleteDaemonset(cache.DeletedFinalStateUnknown{Key: "foo", Obj: ds}) - enqueuedKey, _ := manager.queue.Get() - if enqueuedKey.(string) != "default/foo" { - t.Errorf("expected delete of DeletedFinalStateUnknown to enqueue the daemonset but found: %#v", enqueuedKey) + for _, strategy := range updateStrategies() { + manager, _, _ := newTestController() + addNodes(manager.nodeStore, 0, 1, nil) + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + // DeletedFinalStateUnknown should queue the embedded DS if found. + manager.deleteDaemonset(cache.DeletedFinalStateUnknown{Key: "foo", Obj: ds}) + enqueuedKey, _ := manager.queue.Get() + if enqueuedKey.(string) != "default/foo" { + t.Errorf("expected delete of DeletedFinalStateUnknown to enqueue the daemonset but found: %#v", enqueuedKey) + } } } @@ -360,79 +382,97 @@ func markPodReady(pod *v1.Pod) { // DaemonSets without node selectors should launch pods on every node. func TestSimpleDaemonSetLaunchesPods(t *testing.T) { - ds := newDaemonSet("foo") - manager, podControl, _ := newTestController(ds) - addNodes(manager.nodeStore, 0, 5, nil) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 0) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, _ := newTestController(ds) + addNodes(manager.nodeStore, 0, 5, nil) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 0) + } } func TestSimpleDaemonSetUpdatesStatusAfterLaunchingPods(t *testing.T) { - ds := newDaemonSet("foo") - manager, podControl, clientset := newTestController(ds) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, clientset := newTestController(ds) - var updated *extensions.DaemonSet - clientset.PrependReactor("update", "daemonsets", func(action core.Action) (handled bool, ret runtime.Object, err error) { - if action.GetSubresource() != "status" { + var updated *extensions.DaemonSet + clientset.PrependReactor("update", "daemonsets", func(action core.Action) (handled bool, ret runtime.Object, err error) { + if action.GetSubresource() != "status" { + return false, nil, nil + } + if u, ok := action.(core.UpdateAction); ok { + updated = u.GetObject().(*extensions.DaemonSet) + } return false, nil, nil - } - if u, ok := action.(core.UpdateAction); ok { - updated = u.GetObject().(*extensions.DaemonSet) - } - return false, nil, nil - }) + }) - manager.dsStore.Add(ds) - addNodes(manager.nodeStore, 0, 5, nil) - syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 0) + manager.dsStore.Add(ds) + addNodes(manager.nodeStore, 0, 5, nil) + syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 0) - // Make sure the single sync() updated Status already for the change made - // during the manage() phase. - if got, want := updated.Status.CurrentNumberScheduled, int32(5); got != want { - t.Errorf("Status.CurrentNumberScheduled = %v, want %v", got, want) + // Make sure the single sync() updated Status already for the change made + // during the manage() phase. + if got, want := updated.Status.CurrentNumberScheduled, int32(5); got != want { + t.Errorf("Status.CurrentNumberScheduled = %v, want %v", got, want) + } } } // DaemonSets should do nothing if there aren't any nodes func TestNoNodesDoesNothing(t *testing.T) { - manager, podControl, _ := newTestController() - ds := newDaemonSet("foo") - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + for _, strategy := range updateStrategies() { + manager, podControl, _ := newTestController() + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } // DaemonSets without node selectors should launch on a single node in a // single node cluster. func TestOneNodeDaemonLaunchesPod(t *testing.T) { - ds := newDaemonSet("foo") - manager, podControl, _ := newTestController(ds) - manager.nodeStore.Add(newNode("only-node", nil)) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, _ := newTestController(ds) + manager.nodeStore.Add(newNode("only-node", nil)) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + } } // DaemonSets should place onto NotReady nodes func TestNotReadNodeDaemonDoesNotLaunchPod(t *testing.T) { - ds := newDaemonSet("foo") - manager, podControl, _ := newTestController(ds) - node := newNode("not-ready", nil) - node.Status.Conditions = []v1.NodeCondition{ - {Type: v1.NodeReady, Status: v1.ConditionFalse}, + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, _ := newTestController(ds) + node := newNode("not-ready", nil) + node.Status.Conditions = []v1.NodeCondition{ + {Type: v1.NodeReady, Status: v1.ConditionFalse}, + } + manager.nodeStore.Add(node) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) } - manager.nodeStore.Add(node) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) } // DaemonSets should not place onto OutOfDisk nodes func TestOutOfDiskNodeDaemonDoesNotLaunchPod(t *testing.T) { - ds := newDaemonSet("foo") - manager, podControl, _ := newTestController(ds) - node := newNode("not-enough-disk", nil) - node.Status.Conditions = []v1.NodeCondition{{Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}} - manager.nodeStore.Add(node) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, _ := newTestController(ds) + node := newNode("not-enough-disk", nil) + node.Status.Conditions = []v1.NodeCondition{{Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}} + manager.nodeStore.Add(node) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } func resourcePodSpec(nodeName, memory, cpu string) v1.PodSpec { @@ -456,143 +496,167 @@ func allocatableResources(memory, cpu string) v1.ResourceList { // DaemonSets should not place onto nodes with insufficient free resource func TestInsufficientCapacityNodeDaemonDoesNotLaunchPod(t *testing.T) { - podSpec := resourcePodSpec("too-much-mem", "75M", "75m") - ds := newDaemonSet("foo") - ds.Spec.Template.Spec = podSpec - manager, podControl, _ := newTestController(ds) - node := newNode("too-much-mem", nil) - node.Status.Allocatable = allocatableResources("100M", "200m") - manager.nodeStore.Add(node) - manager.podStore.Add(&v1.Pod{ - Spec: podSpec, - }) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + for _, strategy := range updateStrategies() { + podSpec := resourcePodSpec("too-much-mem", "75M", "75m") + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec = podSpec + manager, podControl, _ := newTestController(ds) + node := newNode("too-much-mem", nil) + node.Status.Allocatable = allocatableResources("100M", "200m") + manager.nodeStore.Add(node) + manager.podStore.Add(&v1.Pod{ + Spec: podSpec, + }) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } // DaemonSets should not unschedule a daemonset pod from a node with insufficient free resource func TestInsufficientCapacityNodeDaemonDoesNotUnscheduleRunningPod(t *testing.T) { - podSpec := resourcePodSpec("too-much-mem", "75M", "75m") - podSpec.NodeName = "too-much-mem" - ds := newDaemonSet("foo") - ds.Spec.Template.Spec = podSpec - manager, podControl, _ := newTestController(ds) - node := newNode("too-much-mem", nil) - node.Status.Allocatable = allocatableResources("100M", "200m") - manager.nodeStore.Add(node) - manager.podStore.Add(&v1.Pod{ - Spec: podSpec, - }) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + for _, strategy := range updateStrategies() { + podSpec := resourcePodSpec("too-much-mem", "75M", "75m") + podSpec.NodeName = "too-much-mem" + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec = podSpec + manager, podControl, _ := newTestController(ds) + node := newNode("too-much-mem", nil) + node.Status.Allocatable = allocatableResources("100M", "200m") + manager.nodeStore.Add(node) + manager.podStore.Add(&v1.Pod{ + Spec: podSpec, + }) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } func TestSufficientCapacityWithTerminatedPodsDaemonLaunchesPod(t *testing.T) { - podSpec := resourcePodSpec("too-much-mem", "75M", "75m") - ds := newDaemonSet("foo") - ds.Spec.Template.Spec = podSpec - manager, podControl, _ := newTestController(ds) - node := newNode("too-much-mem", nil) - node.Status.Allocatable = allocatableResources("100M", "200m") - manager.nodeStore.Add(node) - manager.podStore.Add(&v1.Pod{ - Spec: podSpec, - Status: v1.PodStatus{Phase: v1.PodSucceeded}, - }) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + for _, strategy := range updateStrategies() { + podSpec := resourcePodSpec("too-much-mem", "75M", "75m") + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec = podSpec + manager, podControl, _ := newTestController(ds) + node := newNode("too-much-mem", nil) + node.Status.Allocatable = allocatableResources("100M", "200m") + manager.nodeStore.Add(node) + manager.podStore.Add(&v1.Pod{ + Spec: podSpec, + Status: v1.PodStatus{Phase: v1.PodSucceeded}, + }) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + } } // DaemonSets should place onto nodes with sufficient free resource func TestSufficientCapacityNodeDaemonLaunchesPod(t *testing.T) { - podSpec := resourcePodSpec("not-too-much-mem", "75M", "75m") - ds := newDaemonSet("foo") - ds.Spec.Template.Spec = podSpec - manager, podControl, _ := newTestController(ds) - node := newNode("not-too-much-mem", nil) - node.Status.Allocatable = allocatableResources("200M", "200m") - manager.nodeStore.Add(node) - manager.podStore.Add(&v1.Pod{ - Spec: podSpec, - }) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + for _, strategy := range updateStrategies() { + podSpec := resourcePodSpec("not-too-much-mem", "75M", "75m") + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec = podSpec + manager, podControl, _ := newTestController(ds) + node := newNode("not-too-much-mem", nil) + node.Status.Allocatable = allocatableResources("200M", "200m") + manager.nodeStore.Add(node) + manager.podStore.Add(&v1.Pod{ + Spec: podSpec, + }) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + } } // DaemonSet should launch a pod on a node with taint NetworkUnavailable condition. func TestNetworkUnavailableNodeDaemonLaunchesPod(t *testing.T) { - ds := newDaemonSet("simple") - manager, podControl, _ := newTestController(ds) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("simple") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, _ := newTestController(ds) - node := newNode("network-unavailable", nil) - node.Status.Conditions = []v1.NodeCondition{ - {Type: v1.NodeNetworkUnavailable, Status: v1.ConditionTrue}, + node := newNode("network-unavailable", nil) + node.Status.Conditions = []v1.NodeCondition{ + {Type: v1.NodeNetworkUnavailable, Status: v1.ConditionTrue}, + } + manager.nodeStore.Add(node) + manager.dsStore.Add(ds) + + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) } - manager.nodeStore.Add(node) - manager.dsStore.Add(ds) - - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) } // DaemonSets not take any actions when being deleted func TestDontDoAnythingIfBeingDeleted(t *testing.T) { - podSpec := resourcePodSpec("not-too-much-mem", "75M", "75m") - ds := newDaemonSet("foo") - ds.Spec.Template.Spec = podSpec - now := metav1.Now() - ds.DeletionTimestamp = &now - manager, podControl, _ := newTestController(ds) - node := newNode("not-too-much-mem", nil) - node.Status.Allocatable = allocatableResources("200M", "200m") - manager.nodeStore.Add(node) - manager.podStore.Add(&v1.Pod{ - Spec: podSpec, - }) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + for _, strategy := range updateStrategies() { + podSpec := resourcePodSpec("not-too-much-mem", "75M", "75m") + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec = podSpec + now := metav1.Now() + ds.DeletionTimestamp = &now + manager, podControl, _ := newTestController(ds) + node := newNode("not-too-much-mem", nil) + node.Status.Allocatable = allocatableResources("200M", "200m") + manager.nodeStore.Add(node) + manager.podStore.Add(&v1.Pod{ + Spec: podSpec, + }) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } func TestDontDoAnythingIfBeingDeletedRace(t *testing.T) { - // Bare client says it IS deleted. - ds := newDaemonSet("foo") - now := metav1.Now() - ds.DeletionTimestamp = &now - manager, podControl, _ := newTestController(ds) - addNodes(manager.nodeStore, 0, 5, nil) + for _, strategy := range updateStrategies() { + // Bare client says it IS deleted. + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + now := metav1.Now() + ds.DeletionTimestamp = &now + manager, podControl, _ := newTestController(ds) + addNodes(manager.nodeStore, 0, 5, nil) - // Lister (cache) says it's NOT deleted. - ds2 := *ds - ds2.DeletionTimestamp = nil - manager.dsStore.Add(&ds2) + // Lister (cache) says it's NOT deleted. + ds2 := *ds + ds2.DeletionTimestamp = nil + manager.dsStore.Add(&ds2) - // The existence of a matching orphan should block all actions in this state. - pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, nil) - manager.podStore.Add(pod) + // The existence of a matching orphan should block all actions in this state. + pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, nil) + manager.podStore.Add(pod) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } // DaemonSets should not place onto nodes that would cause port conflicts func TestPortConflictNodeDaemonDoesNotLaunchPod(t *testing.T) { - podSpec := v1.PodSpec{ - NodeName: "port-conflict", - Containers: []v1.Container{{ - Ports: []v1.ContainerPort{{ - HostPort: 666, + for _, strategy := range updateStrategies() { + podSpec := v1.PodSpec{ + NodeName: "port-conflict", + Containers: []v1.Container{{ + Ports: []v1.ContainerPort{{ + HostPort: 666, + }}, }}, - }}, - } - manager, podControl, _ := newTestController() - node := newNode("port-conflict", nil) - manager.nodeStore.Add(node) - manager.podStore.Add(&v1.Pod{ - Spec: podSpec, - }) + } + manager, podControl, _ := newTestController() + node := newNode("port-conflict", nil) + manager.nodeStore.Add(node) + manager.podStore.Add(&v1.Pod{ + Spec: podSpec, + }) - ds := newDaemonSet("foo") - ds.Spec.Template.Spec = podSpec - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec = podSpec + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } // Test that if the node is already scheduled with a pod using a host port @@ -600,59 +664,65 @@ func TestPortConflictNodeDaemonDoesNotLaunchPod(t *testing.T) { // // Issue: https://github.com/kubernetes/kubernetes/issues/22309 func TestPortConflictWithSameDaemonPodDoesNotDeletePod(t *testing.T) { - podSpec := v1.PodSpec{ - NodeName: "port-conflict", - Containers: []v1.Container{{ - Ports: []v1.ContainerPort{{ - HostPort: 666, + for _, strategy := range updateStrategies() { + podSpec := v1.PodSpec{ + NodeName: "port-conflict", + Containers: []v1.Container{{ + Ports: []v1.ContainerPort{{ + HostPort: 666, + }}, }}, - }}, + } + manager, podControl, _ := newTestController() + node := newNode("port-conflict", nil) + manager.nodeStore.Add(node) + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec = podSpec + manager.dsStore.Add(ds) + manager.podStore.Add(&v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Labels: simpleDaemonSetLabel, + Namespace: metav1.NamespaceDefault, + OwnerReferences: []metav1.OwnerReference{*newControllerRef(ds)}, + }, + Spec: podSpec, + }) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) } - manager, podControl, _ := newTestController() - node := newNode("port-conflict", nil) - manager.nodeStore.Add(node) - ds := newDaemonSet("foo") - ds.Spec.Template.Spec = podSpec - manager.dsStore.Add(ds) - manager.podStore.Add(&v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Labels: simpleDaemonSetLabel, - Namespace: metav1.NamespaceDefault, - OwnerReferences: []metav1.OwnerReference{*newControllerRef(ds)}, - }, - Spec: podSpec, - }) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) } // DaemonSets should place onto nodes that would not cause port conflicts func TestNoPortConflictNodeDaemonLaunchesPod(t *testing.T) { - podSpec1 := v1.PodSpec{ - NodeName: "no-port-conflict", - Containers: []v1.Container{{ - Ports: []v1.ContainerPort{{ - HostPort: 6661, + for _, strategy := range updateStrategies() { + podSpec1 := v1.PodSpec{ + NodeName: "no-port-conflict", + Containers: []v1.Container{{ + Ports: []v1.ContainerPort{{ + HostPort: 6661, + }}, }}, - }}, - } - podSpec2 := v1.PodSpec{ - NodeName: "no-port-conflict", - Containers: []v1.Container{{ - Ports: []v1.ContainerPort{{ - HostPort: 6662, + } + podSpec2 := v1.PodSpec{ + NodeName: "no-port-conflict", + Containers: []v1.Container{{ + Ports: []v1.ContainerPort{{ + HostPort: 6662, + }}, }}, - }}, + } + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec = podSpec2 + manager, podControl, _ := newTestController(ds) + node := newNode("no-port-conflict", nil) + manager.nodeStore.Add(node) + manager.podStore.Add(&v1.Pod{ + Spec: podSpec1, + }) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) } - ds := newDaemonSet("foo") - ds.Spec.Template.Spec = podSpec2 - manager, podControl, _ := newTestController(ds) - node := newNode("no-port-conflict", nil) - manager.nodeStore.Add(node) - manager.podStore.Add(&v1.Pod{ - Spec: podSpec1, - }) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) } // DaemonSetController should not sync DaemonSets with empty pod selectors. @@ -668,139 +738,169 @@ func TestPodIsNotDeletedByDaemonsetWithEmptyLabelSelector(t *testing.T) { // this case even though it's empty pod selector matches all pods. The DaemonSetController // should detect this misconfiguration and choose not to sync the DaemonSet. We should // not observe a deletion of the pod on node1. - ds := newDaemonSet("foo") - ls := metav1.LabelSelector{} - ds.Spec.Selector = &ls - ds.Spec.Template.Spec.NodeSelector = map[string]string{"foo": "bar"} + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ls := metav1.LabelSelector{} + ds.Spec.Selector = &ls + ds.Spec.Template.Spec.NodeSelector = map[string]string{"foo": "bar"} - manager, podControl, _ := newTestController(ds) - manager.nodeStore.Add(newNode("node1", nil)) - // Create pod not controlled by a daemonset. - manager.podStore.Add(&v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"bang": "boom"}, - Namespace: metav1.NamespaceDefault, - }, - Spec: v1.PodSpec{ - NodeName: "node1", - }, - }) - manager.dsStore.Add(ds) + manager, podControl, _ := newTestController(ds) + manager.nodeStore.Add(newNode("node1", nil)) + // Create pod not controlled by a daemonset. + manager.podStore.Add(&v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"bang": "boom"}, + Namespace: metav1.NamespaceDefault, + }, + Spec: v1.PodSpec{ + NodeName: "node1", + }, + }) + manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } // Controller should not create pods on nodes which have daemon pods, and should remove excess pods from nodes that have extra pods. func TestDealsWithExistingPods(t *testing.T) { - ds := newDaemonSet("foo") - manager, podControl, _ := newTestController(ds) - manager.dsStore.Add(ds) - addNodes(manager.nodeStore, 0, 5, nil) - addPods(manager.podStore, "node-1", simpleDaemonSetLabel, ds, 1) - addPods(manager.podStore, "node-2", simpleDaemonSetLabel, ds, 2) - addPods(manager.podStore, "node-3", simpleDaemonSetLabel, ds, 5) - addPods(manager.podStore, "node-4", simpleDaemonSetLabel2, ds, 2) - syncAndValidateDaemonSets(t, manager, ds, podControl, 2, 5) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, _ := newTestController(ds) + manager.dsStore.Add(ds) + addNodes(manager.nodeStore, 0, 5, nil) + addPods(manager.podStore, "node-1", simpleDaemonSetLabel, ds, 1) + addPods(manager.podStore, "node-2", simpleDaemonSetLabel, ds, 2) + addPods(manager.podStore, "node-3", simpleDaemonSetLabel, ds, 5) + addPods(manager.podStore, "node-4", simpleDaemonSetLabel2, ds, 2) + syncAndValidateDaemonSets(t, manager, ds, podControl, 2, 5) + } } // Daemon with node selector should launch pods on nodes matching selector. func TestSelectorDaemonLaunchesPods(t *testing.T) { - daemon := newDaemonSet("foo") - daemon.Spec.Template.Spec.NodeSelector = simpleNodeLabel - manager, podControl, _ := newTestController(daemon) - addNodes(manager.nodeStore, 0, 4, nil) - addNodes(manager.nodeStore, 4, 3, simpleNodeLabel) - manager.dsStore.Add(daemon) - syncAndValidateDaemonSets(t, manager, daemon, podControl, 3, 0) + for _, strategy := range updateStrategies() { + daemon := newDaemonSet("foo") + daemon.Spec.UpdateStrategy = *strategy + daemon.Spec.Template.Spec.NodeSelector = simpleNodeLabel + manager, podControl, _ := newTestController(daemon) + addNodes(manager.nodeStore, 0, 4, nil) + addNodes(manager.nodeStore, 4, 3, simpleNodeLabel) + manager.dsStore.Add(daemon) + syncAndValidateDaemonSets(t, manager, daemon, podControl, 3, 0) + } } // Daemon with node selector should delete pods from nodes that do not satisfy selector. func TestSelectorDaemonDeletesUnselectedPods(t *testing.T) { - ds := newDaemonSet("foo") - ds.Spec.Template.Spec.NodeSelector = simpleNodeLabel - manager, podControl, _ := newTestController(ds) - manager.dsStore.Add(ds) - addNodes(manager.nodeStore, 0, 5, nil) - addNodes(manager.nodeStore, 5, 5, simpleNodeLabel) - addPods(manager.podStore, "node-0", simpleDaemonSetLabel2, ds, 2) - addPods(manager.podStore, "node-1", simpleDaemonSetLabel, ds, 3) - addPods(manager.podStore, "node-1", simpleDaemonSetLabel2, ds, 1) - addPods(manager.podStore, "node-4", simpleDaemonSetLabel, ds, 1) - syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 4) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec.NodeSelector = simpleNodeLabel + manager, podControl, _ := newTestController(ds) + manager.dsStore.Add(ds) + addNodes(manager.nodeStore, 0, 5, nil) + addNodes(manager.nodeStore, 5, 5, simpleNodeLabel) + addPods(manager.podStore, "node-0", simpleDaemonSetLabel2, ds, 2) + addPods(manager.podStore, "node-1", simpleDaemonSetLabel, ds, 3) + addPods(manager.podStore, "node-1", simpleDaemonSetLabel2, ds, 1) + addPods(manager.podStore, "node-4", simpleDaemonSetLabel, ds, 1) + syncAndValidateDaemonSets(t, manager, ds, podControl, 5, 4) + } } // DaemonSet with node selector should launch pods on nodes matching selector, but also deal with existing pods on nodes. func TestSelectorDaemonDealsWithExistingPods(t *testing.T) { - ds := newDaemonSet("foo") - ds.Spec.Template.Spec.NodeSelector = simpleNodeLabel - manager, podControl, _ := newTestController(ds) - manager.dsStore.Add(ds) - addNodes(manager.nodeStore, 0, 5, nil) - addNodes(manager.nodeStore, 5, 5, simpleNodeLabel) - addPods(manager.podStore, "node-0", simpleDaemonSetLabel, ds, 1) - addPods(manager.podStore, "node-1", simpleDaemonSetLabel, ds, 3) - addPods(manager.podStore, "node-1", simpleDaemonSetLabel2, ds, 2) - addPods(manager.podStore, "node-2", simpleDaemonSetLabel, ds, 4) - addPods(manager.podStore, "node-6", simpleDaemonSetLabel, ds, 13) - addPods(manager.podStore, "node-7", simpleDaemonSetLabel2, ds, 4) - addPods(manager.podStore, "node-9", simpleDaemonSetLabel, ds, 1) - addPods(manager.podStore, "node-9", simpleDaemonSetLabel2, ds, 1) - syncAndValidateDaemonSets(t, manager, ds, podControl, 3, 20) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec.NodeSelector = simpleNodeLabel + manager, podControl, _ := newTestController(ds) + manager.dsStore.Add(ds) + addNodes(manager.nodeStore, 0, 5, nil) + addNodes(manager.nodeStore, 5, 5, simpleNodeLabel) + addPods(manager.podStore, "node-0", simpleDaemonSetLabel, ds, 1) + addPods(manager.podStore, "node-1", simpleDaemonSetLabel, ds, 3) + addPods(manager.podStore, "node-1", simpleDaemonSetLabel2, ds, 2) + addPods(manager.podStore, "node-2", simpleDaemonSetLabel, ds, 4) + addPods(manager.podStore, "node-6", simpleDaemonSetLabel, ds, 13) + addPods(manager.podStore, "node-7", simpleDaemonSetLabel2, ds, 4) + addPods(manager.podStore, "node-9", simpleDaemonSetLabel, ds, 1) + addPods(manager.podStore, "node-9", simpleDaemonSetLabel2, ds, 1) + syncAndValidateDaemonSets(t, manager, ds, podControl, 3, 20) + } } // DaemonSet with node selector which does not match any node labels should not launch pods. func TestBadSelectorDaemonDoesNothing(t *testing.T) { - manager, podControl, _ := newTestController() - addNodes(manager.nodeStore, 0, 4, nil) - addNodes(manager.nodeStore, 4, 3, simpleNodeLabel) - ds := newDaemonSet("foo") - ds.Spec.Template.Spec.NodeSelector = simpleNodeLabel2 - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + for _, strategy := range updateStrategies() { + manager, podControl, _ := newTestController() + addNodes(manager.nodeStore, 0, 4, nil) + addNodes(manager.nodeStore, 4, 3, simpleNodeLabel) + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec.NodeSelector = simpleNodeLabel2 + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } // DaemonSet with node name should launch pod on node with corresponding name. func TestNameDaemonSetLaunchesPods(t *testing.T) { - ds := newDaemonSet("foo") - ds.Spec.Template.Spec.NodeName = "node-0" - manager, podControl, _ := newTestController(ds) - addNodes(manager.nodeStore, 0, 5, nil) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec.NodeName = "node-0" + manager, podControl, _ := newTestController(ds) + addNodes(manager.nodeStore, 0, 5, nil) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + } } // DaemonSet with node name that does not exist should not launch pods. func TestBadNameDaemonSetDoesNothing(t *testing.T) { - ds := newDaemonSet("foo") - ds.Spec.Template.Spec.NodeName = "node-10" - manager, podControl, _ := newTestController(ds) - addNodes(manager.nodeStore, 0, 5, nil) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec.NodeName = "node-10" + manager, podControl, _ := newTestController(ds) + addNodes(manager.nodeStore, 0, 5, nil) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } // DaemonSet with node selector, and node name, matching a node, should launch a pod on the node. func TestNameAndSelectorDaemonSetLaunchesPods(t *testing.T) { - ds := newDaemonSet("foo") - ds.Spec.Template.Spec.NodeSelector = simpleNodeLabel - ds.Spec.Template.Spec.NodeName = "node-6" - manager, podControl, _ := newTestController(ds) - addNodes(manager.nodeStore, 0, 4, nil) - addNodes(manager.nodeStore, 4, 3, simpleNodeLabel) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec.NodeSelector = simpleNodeLabel + ds.Spec.Template.Spec.NodeName = "node-6" + manager, podControl, _ := newTestController(ds) + addNodes(manager.nodeStore, 0, 4, nil) + addNodes(manager.nodeStore, 4, 3, simpleNodeLabel) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + } } // DaemonSet with node selector that matches some nodes, and node name that matches a different node, should do nothing. func TestInconsistentNameSelectorDaemonSetDoesNothing(t *testing.T) { - ds := newDaemonSet("foo") - ds.Spec.Template.Spec.NodeSelector = simpleNodeLabel - ds.Spec.Template.Spec.NodeName = "node-0" - manager, podControl, _ := newTestController(ds) - addNodes(manager.nodeStore, 0, 4, nil) - addNodes(manager.nodeStore, 4, 3, simpleNodeLabel) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec.NodeSelector = simpleNodeLabel + ds.Spec.Template.Spec.NodeName = "node-0" + manager, podControl, _ := newTestController(ds) + addNodes(manager.nodeStore, 0, 4, nil) + addNodes(manager.nodeStore, 4, 3, simpleNodeLabel) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } // DaemonSet with node selector, matching some nodes, should launch pods on all the nodes. @@ -816,90 +916,99 @@ func TestSelectorDaemonSetLaunchesPods(t *testing.T) { // Daemon with node affinity should launch pods on nodes matching affinity. func TestNodeAffinityDaemonLaunchesPods(t *testing.T) { - daemon := newDaemonSet("foo") - daemon.Spec.Template.Spec.Affinity = &v1.Affinity{ - NodeAffinity: &v1.NodeAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ - NodeSelectorTerms: []v1.NodeSelectorTerm{ - { - MatchExpressions: []v1.NodeSelectorRequirement{ - { - Key: "color", - Operator: v1.NodeSelectorOpIn, - Values: []string{simpleNodeLabel["color"]}, + for _, strategy := range updateStrategies() { + daemon := newDaemonSet("foo") + daemon.Spec.UpdateStrategy = *strategy + daemon.Spec.Template.Spec.Affinity = &v1.Affinity{ + NodeAffinity: &v1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ + NodeSelectorTerms: []v1.NodeSelectorTerm{ + { + MatchExpressions: []v1.NodeSelectorRequirement{ + { + Key: "color", + Operator: v1.NodeSelectorOpIn, + Values: []string{simpleNodeLabel["color"]}, + }, }, }, }, }, }, - }, - } + } - manager, podControl, _ := newTestController(daemon) - addNodes(manager.nodeStore, 0, 4, nil) - addNodes(manager.nodeStore, 4, 3, simpleNodeLabel) - manager.dsStore.Add(daemon) - syncAndValidateDaemonSets(t, manager, daemon, podControl, 3, 0) + manager, podControl, _ := newTestController(daemon) + addNodes(manager.nodeStore, 0, 4, nil) + addNodes(manager.nodeStore, 4, 3, simpleNodeLabel) + manager.dsStore.Add(daemon) + syncAndValidateDaemonSets(t, manager, daemon, podControl, 3, 0) + } } func TestNumberReadyStatus(t *testing.T) { - ds := newDaemonSet("foo") - manager, podControl, clientset := newTestController(ds) - var updated *extensions.DaemonSet - clientset.PrependReactor("update", "daemonsets", func(action core.Action) (handled bool, ret runtime.Object, err error) { - if action.GetSubresource() != "status" { + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, clientset := newTestController(ds) + var updated *extensions.DaemonSet + clientset.PrependReactor("update", "daemonsets", func(action core.Action) (handled bool, ret runtime.Object, err error) { + if action.GetSubresource() != "status" { + return false, nil, nil + } + if u, ok := action.(core.UpdateAction); ok { + updated = u.GetObject().(*extensions.DaemonSet) + } return false, nil, nil + }) + addNodes(manager.nodeStore, 0, 2, simpleNodeLabel) + addPods(manager.podStore, "node-0", simpleDaemonSetLabel, ds, 1) + addPods(manager.podStore, "node-1", simpleDaemonSetLabel, ds, 1) + manager.dsStore.Add(ds) + + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + if updated.Status.NumberReady != 0 { + t.Errorf("Wrong daemon %s status: %v", updated.Name, updated.Status) } - if u, ok := action.(core.UpdateAction); ok { - updated = u.GetObject().(*extensions.DaemonSet) + + selector, _ := metav1.LabelSelectorAsSelector(ds.Spec.Selector) + daemonPods, _ := manager.podLister.Pods(ds.Namespace).List(selector) + for _, pod := range daemonPods { + condition := v1.PodCondition{Type: v1.PodReady, Status: v1.ConditionTrue} + pod.Status.Conditions = append(pod.Status.Conditions, condition) } - return false, nil, nil - }) - addNodes(manager.nodeStore, 0, 2, simpleNodeLabel) - addPods(manager.podStore, "node-0", simpleDaemonSetLabel, ds, 1) - addPods(manager.podStore, "node-1", simpleDaemonSetLabel, ds, 1) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) - if updated.Status.NumberReady != 0 { - t.Errorf("Wrong daemon %s status: %v", updated.Name, updated.Status) - } - - selector, _ := metav1.LabelSelectorAsSelector(ds.Spec.Selector) - daemonPods, _ := manager.podLister.Pods(ds.Namespace).List(selector) - for _, pod := range daemonPods { - condition := v1.PodCondition{Type: v1.PodReady, Status: v1.ConditionTrue} - pod.Status.Conditions = append(pod.Status.Conditions, condition) - } - - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) - if updated.Status.NumberReady != 2 { - t.Errorf("Wrong daemon %s status: %v", updated.Name, updated.Status) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + if updated.Status.NumberReady != 2 { + t.Errorf("Wrong daemon %s status: %v", updated.Name, updated.Status) + } } } func TestObservedGeneration(t *testing.T) { - ds := newDaemonSet("foo") - ds.Generation = 1 - manager, podControl, clientset := newTestController(ds) - var updated *extensions.DaemonSet - clientset.PrependReactor("update", "daemonsets", func(action core.Action) (handled bool, ret runtime.Object, err error) { - if action.GetSubresource() != "status" { + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds.Generation = 1 + manager, podControl, clientset := newTestController(ds) + var updated *extensions.DaemonSet + clientset.PrependReactor("update", "daemonsets", func(action core.Action) (handled bool, ret runtime.Object, err error) { + if action.GetSubresource() != "status" { + return false, nil, nil + } + if u, ok := action.(core.UpdateAction); ok { + updated = u.GetObject().(*extensions.DaemonSet) + } return false, nil, nil - } - if u, ok := action.(core.UpdateAction); ok { - updated = u.GetObject().(*extensions.DaemonSet) - } - return false, nil, nil - }) + }) - addNodes(manager.nodeStore, 0, 1, simpleNodeLabel) - addPods(manager.podStore, "node-0", simpleDaemonSetLabel, ds, 1) - manager.dsStore.Add(ds) + addNodes(manager.nodeStore, 0, 1, simpleNodeLabel) + addPods(manager.podStore, "node-0", simpleDaemonSetLabel, ds, 1) + manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) - if updated.Status.ObservedGeneration != ds.Generation { - t.Errorf("Wrong ObservedGeneration for daemon %s in status. Expected %d, got %d", updated.Name, ds.Generation, updated.Status.ObservedGeneration) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + if updated.Status.ObservedGeneration != ds.Generation { + t.Errorf("Wrong ObservedGeneration for daemon %s in status. Expected %d, got %d", updated.Name, ds.Generation, updated.Status.ObservedGeneration) + } } } @@ -918,86 +1027,104 @@ func TestDaemonKillFailedPods(t *testing.T) { for _, test := range tests { t.Logf("test case: %s\n", test.test) - ds := newDaemonSet("foo") - manager, podControl, _ := newTestController(ds) - manager.dsStore.Add(ds) - addNodes(manager.nodeStore, 0, 1, nil) - addFailedPods(manager.podStore, "node-0", simpleDaemonSetLabel, ds, test.numFailedPods) - addPods(manager.podStore, "node-0", simpleDaemonSetLabel, ds, test.numNormalPods) - syncAndValidateDaemonSets(t, manager, ds, podControl, test.expectedCreates, test.expectedDeletes) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, _ := newTestController(ds) + manager.dsStore.Add(ds) + addNodes(manager.nodeStore, 0, 1, nil) + addFailedPods(manager.podStore, "node-0", simpleDaemonSetLabel, ds, test.numFailedPods) + addPods(manager.podStore, "node-0", simpleDaemonSetLabel, ds, test.numNormalPods) + syncAndValidateDaemonSets(t, manager, ds, podControl, test.expectedCreates, test.expectedDeletes) + } } } // DaemonSet should not launch a pod on a tainted node when the pod doesn't tolerate that taint. func TestTaintedNodeDaemonDoesNotLaunchUntoleratePod(t *testing.T) { - ds := newDaemonSet("untolerate") - manager, podControl, _ := newTestController(ds) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("untolerate") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, _ := newTestController(ds) - node := newNode("tainted", nil) - setNodeTaint(node, noScheduleTaints) - manager.nodeStore.Add(node) - manager.dsStore.Add(ds) + node := newNode("tainted", nil) + setNodeTaint(node, noScheduleTaints) + manager.nodeStore.Add(node) + manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } // DaemonSet should launch a pod on a tainted node when the pod can tolerate that taint. func TestTaintedNodeDaemonLaunchesToleratePod(t *testing.T) { - ds := newDaemonSet("tolerate") - setDaemonSetToleration(ds, noScheduleTolerations) - manager, podControl, _ := newTestController(ds) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("tolerate") + ds.Spec.UpdateStrategy = *strategy + setDaemonSetToleration(ds, noScheduleTolerations) + manager, podControl, _ := newTestController(ds) - node := newNode("tainted", nil) - setNodeTaint(node, noScheduleTaints) - manager.nodeStore.Add(node) - manager.dsStore.Add(ds) + node := newNode("tainted", nil) + setNodeTaint(node, noScheduleTaints) + manager.nodeStore.Add(node) + manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + } } // DaemonSet should launch a pod on a not ready node with taint notReady:NoExecute. func TestNotReadyNodeDaemonLaunchesPod(t *testing.T) { - ds := newDaemonSet("simple") - manager, podControl, _ := newTestController(ds) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("simple") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, _ := newTestController(ds) - node := newNode("tainted", nil) - setNodeTaint(node, nodeNotReady) - node.Status.Conditions = []v1.NodeCondition{ - {Type: v1.NodeReady, Status: v1.ConditionFalse}, + node := newNode("tainted", nil) + setNodeTaint(node, nodeNotReady) + node.Status.Conditions = []v1.NodeCondition{ + {Type: v1.NodeReady, Status: v1.ConditionFalse}, + } + manager.nodeStore.Add(node) + manager.dsStore.Add(ds) + + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) } - manager.nodeStore.Add(node) - manager.dsStore.Add(ds) - - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) } // DaemonSet should launch a pod on an unreachable node with taint unreachable:NoExecute. func TestUnreachableNodeDaemonLaunchesPod(t *testing.T) { - ds := newDaemonSet("simple") - manager, podControl, _ := newTestController(ds) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("simple") + ds.Spec.UpdateStrategy = *strategy + manager, podControl, _ := newTestController(ds) - node := newNode("tainted", nil) - setNodeTaint(node, nodeUnreachable) - node.Status.Conditions = []v1.NodeCondition{ - {Type: v1.NodeReady, Status: v1.ConditionUnknown}, + node := newNode("tainted", nil) + setNodeTaint(node, nodeUnreachable) + node.Status.Conditions = []v1.NodeCondition{ + {Type: v1.NodeReady, Status: v1.ConditionUnknown}, + } + manager.nodeStore.Add(node) + manager.dsStore.Add(ds) + + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) } - manager.nodeStore.Add(node) - manager.dsStore.Add(ds) - - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) } // DaemonSet should launch a pod on an untainted node when the pod has tolerations. func TestNodeDaemonLaunchesToleratePod(t *testing.T) { - ds := newDaemonSet("tolerate") - setDaemonSetToleration(ds, noScheduleTolerations) - manager, podControl, _ := newTestController(ds) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("tolerate") + ds.Spec.UpdateStrategy = *strategy + setDaemonSetToleration(ds, noScheduleTolerations) + manager, podControl, _ := newTestController(ds) - node := newNode("untainted", nil) - manager.nodeStore.Add(node) - manager.dsStore.Add(ds) + node := newNode("untainted", nil) + manager.nodeStore.Add(node) + manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + } } func setNodeTaint(node *v1.Node, taints []v1.Taint) { @@ -1010,72 +1137,81 @@ func setDaemonSetToleration(ds *extensions.DaemonSet, tolerations []v1.Toleratio // DaemonSet should launch a critical pod even when the node is OutOfDisk. func TestOutOfDiskNodeDaemonLaunchesCriticalPod(t *testing.T) { - ds := newDaemonSet("critical") - setDaemonSetCritical(ds) - manager, podControl, _ := newTestController(ds) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("critical") + ds.Spec.UpdateStrategy = *strategy + setDaemonSetCritical(ds) + manager, podControl, _ := newTestController(ds) - node := newNode("not-enough-disk", nil) - node.Status.Conditions = []v1.NodeCondition{{Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}} - manager.nodeStore.Add(node) + node := newNode("not-enough-disk", nil) + node.Status.Conditions = []v1.NodeCondition{{Type: v1.NodeOutOfDisk, Status: v1.ConditionTrue}} + manager.nodeStore.Add(node) - // Without enabling critical pod annotation feature gate, we shouldn't create critical pod - utilfeature.DefaultFeatureGate.Set("ExperimentalCriticalPodAnnotation=False") - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + // Without enabling critical pod annotation feature gate, we shouldn't create critical pod + utilfeature.DefaultFeatureGate.Set("ExperimentalCriticalPodAnnotation=False") + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) - // Enabling critical pod annotation feature gate should create critical pod - utilfeature.DefaultFeatureGate.Set("ExperimentalCriticalPodAnnotation=True") - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + // Enabling critical pod annotation feature gate should create critical pod + utilfeature.DefaultFeatureGate.Set("ExperimentalCriticalPodAnnotation=True") + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + } } // DaemonSet should launch a critical pod even when the node has insufficient free resource. func TestInsufficientCapacityNodeDaemonLaunchesCriticalPod(t *testing.T) { - podSpec := resourcePodSpec("too-much-mem", "75M", "75m") - ds := newDaemonSet("critical") - ds.Spec.Template.Spec = podSpec - setDaemonSetCritical(ds) + for _, strategy := range updateStrategies() { + podSpec := resourcePodSpec("too-much-mem", "75M", "75m") + ds := newDaemonSet("critical") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec = podSpec + setDaemonSetCritical(ds) - manager, podControl, _ := newTestController(ds) - node := newNode("too-much-mem", nil) - node.Status.Allocatable = allocatableResources("100M", "200m") - manager.nodeStore.Add(node) - manager.podStore.Add(&v1.Pod{ - Spec: podSpec, - }) + manager, podControl, _ := newTestController(ds) + node := newNode("too-much-mem", nil) + node.Status.Allocatable = allocatableResources("100M", "200m") + manager.nodeStore.Add(node) + manager.podStore.Add(&v1.Pod{ + Spec: podSpec, + }) - // Without enabling critical pod annotation feature gate, we shouldn't create critical pod - utilfeature.DefaultFeatureGate.Set("ExperimentalCriticalPodAnnotation=False") - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + // Without enabling critical pod annotation feature gate, we shouldn't create critical pod + utilfeature.DefaultFeatureGate.Set("ExperimentalCriticalPodAnnotation=False") + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) - // Enabling critical pod annotation feature gate should create critical pod - utilfeature.DefaultFeatureGate.Set("ExperimentalCriticalPodAnnotation=True") - syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + // Enabling critical pod annotation feature gate should create critical pod + utilfeature.DefaultFeatureGate.Set("ExperimentalCriticalPodAnnotation=True") + syncAndValidateDaemonSets(t, manager, ds, podControl, 1, 0) + } } // DaemonSets should NOT launch a critical pod when there are port conflicts. func TestPortConflictNodeDaemonDoesNotLaunchCriticalPod(t *testing.T) { - podSpec := v1.PodSpec{ - NodeName: "port-conflict", - Containers: []v1.Container{{ - Ports: []v1.ContainerPort{{ - HostPort: 666, + for _, strategy := range updateStrategies() { + podSpec := v1.PodSpec{ + NodeName: "port-conflict", + Containers: []v1.Container{{ + Ports: []v1.ContainerPort{{ + HostPort: 666, + }}, }}, - }}, - } - manager, podControl, _ := newTestController() - node := newNode("port-conflict", nil) - manager.nodeStore.Add(node) - manager.podStore.Add(&v1.Pod{ - Spec: podSpec, - }) + } + manager, podControl, _ := newTestController() + node := newNode("port-conflict", nil) + manager.nodeStore.Add(node) + manager.podStore.Add(&v1.Pod{ + Spec: podSpec, + }) - utilfeature.DefaultFeatureGate.Set("ExperimentalCriticalPodAnnotation=True") - ds := newDaemonSet("critical") - ds.Spec.Template.Spec = podSpec - setDaemonSetCritical(ds) - manager.dsStore.Add(ds) - syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + utilfeature.DefaultFeatureGate.Set("ExperimentalCriticalPodAnnotation=True") + ds := newDaemonSet("critical") + ds.Spec.UpdateStrategy = *strategy + ds.Spec.Template.Spec = podSpec + setDaemonSetCritical(ds) + manager.dsStore.Add(ds) + syncAndValidateDaemonSets(t, manager, ds, podControl, 0, 0) + } } func setDaemonSetCritical(ds *extensions.DaemonSet) { @@ -1177,27 +1313,30 @@ func TestNodeShouldRunDaemonPod(t *testing.T) { } for i, c := range cases { - node := newNode("test-node", nil) - node.Status.Allocatable = allocatableResources("100M", "1") - manager, _, _ := newTestController() - manager.nodeStore.Add(node) - for _, p := range c.podsOnNode { - manager.podStore.Add(p) - p.Spec.NodeName = "test-node" - } - wantToRun, shouldSchedule, shouldContinueRunning, err := manager.nodeShouldRunDaemonPod(node, c.ds) + for _, strategy := range updateStrategies() { + node := newNode("test-node", nil) + node.Status.Allocatable = allocatableResources("100M", "1") + manager, _, _ := newTestController() + manager.nodeStore.Add(node) + for _, p := range c.podsOnNode { + manager.podStore.Add(p) + p.Spec.NodeName = "test-node" + } + c.ds.Spec.UpdateStrategy = *strategy + wantToRun, shouldSchedule, shouldContinueRunning, err := manager.nodeShouldRunDaemonPod(node, c.ds) - if wantToRun != c.wantToRun { - t.Errorf("[%v] expected wantToRun: %v, got: %v", i, c.wantToRun, wantToRun) - } - if shouldSchedule != c.shouldSchedule { - t.Errorf("[%v] expected shouldSchedule: %v, got: %v", i, c.shouldSchedule, shouldSchedule) - } - if shouldContinueRunning != c.shouldContinueRunning { - t.Errorf("[%v] expected shouldContinueRunning: %v, got: %v", i, c.shouldContinueRunning, shouldContinueRunning) - } - if err != c.err { - t.Errorf("[%v] expected err: %v, got: %v", i, c.err, err) + if wantToRun != c.wantToRun { + t.Errorf("[%v] expected wantToRun: %v, got: %v", i, c.wantToRun, wantToRun) + } + if shouldSchedule != c.shouldSchedule { + t.Errorf("[%v] expected shouldSchedule: %v, got: %v", i, c.shouldSchedule, shouldSchedule) + } + if shouldContinueRunning != c.shouldContinueRunning { + t.Errorf("[%v] expected shouldContinueRunning: %v, got: %v", i, c.shouldContinueRunning, shouldContinueRunning) + } + if err != c.err { + t.Errorf("[%v] expected err: %v, got: %v", i, c.err, err) + } } } } @@ -1285,84 +1424,91 @@ func TestUpdateNode(t *testing.T) { }, } for _, c := range cases { - manager, podControl, _ := newTestController() - manager.nodeStore.Add(c.oldNode) - manager.dsStore.Add(c.ds) - syncAndValidateDaemonSets(t, manager, c.ds, podControl, 0, 0) + for _, strategy := range updateStrategies() { + manager, podControl, _ := newTestController() + manager.nodeStore.Add(c.oldNode) + c.ds.Spec.UpdateStrategy = *strategy + manager.dsStore.Add(c.ds) + syncAndValidateDaemonSets(t, manager, c.ds, podControl, 0, 0) - manager.enqueueDaemonSet = func(ds *extensions.DaemonSet) { - if ds.Name == "ds" { - enqueued = true + manager.enqueueDaemonSet = func(ds *extensions.DaemonSet) { + if ds.Name == "ds" { + enqueued = true + } } - } - enqueued = false - manager.updateNode(c.oldNode, c.newNode) - if enqueued != c.shouldEnqueue { - t.Errorf("Test case: '%s', expected: %t, got: %t", c.test, c.shouldEnqueue, enqueued) + enqueued = false + manager.updateNode(c.oldNode, c.newNode) + if enqueued != c.shouldEnqueue { + t.Errorf("Test case: '%s', expected: %t, got: %t", c.test, c.shouldEnqueue, enqueued) + } } } } func TestGetNodesToDaemonPods(t *testing.T) { - ds := newDaemonSet("foo") - ds2 := newDaemonSet("foo2") - manager, _, _ := newTestController(ds, ds2) - manager.dsStore.Add(ds) - manager.dsStore.Add(ds2) - addNodes(manager.nodeStore, 0, 2, nil) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + ds2 := newDaemonSet("foo2") + ds2.Spec.UpdateStrategy = *strategy + manager, _, _ := newTestController(ds, ds2) + manager.dsStore.Add(ds) + manager.dsStore.Add(ds2) + addNodes(manager.nodeStore, 0, 2, nil) - // These pods should be returned. - wantedPods := []*v1.Pod{ - newPod("matching-owned-0-", "node-0", simpleDaemonSetLabel, ds), - newPod("matching-orphan-0-", "node-0", simpleDaemonSetLabel, nil), - newPod("matching-owned-1-", "node-1", simpleDaemonSetLabel, ds), - newPod("matching-orphan-1-", "node-1", simpleDaemonSetLabel, nil), - } - failedPod := newPod("matching-owned-failed-pod-1-", "node-1", simpleDaemonSetLabel, ds) - failedPod.Status = v1.PodStatus{Phase: v1.PodFailed} - wantedPods = append(wantedPods, failedPod) - for _, pod := range wantedPods { - manager.podStore.Add(pod) - } + // These pods should be returned. + wantedPods := []*v1.Pod{ + newPod("matching-owned-0-", "node-0", simpleDaemonSetLabel, ds), + newPod("matching-orphan-0-", "node-0", simpleDaemonSetLabel, nil), + newPod("matching-owned-1-", "node-1", simpleDaemonSetLabel, ds), + newPod("matching-orphan-1-", "node-1", simpleDaemonSetLabel, nil), + } + failedPod := newPod("matching-owned-failed-pod-1-", "node-1", simpleDaemonSetLabel, ds) + failedPod.Status = v1.PodStatus{Phase: v1.PodFailed} + wantedPods = append(wantedPods, failedPod) + for _, pod := range wantedPods { + manager.podStore.Add(pod) + } - // These pods should be ignored. - ignoredPods := []*v1.Pod{ - newPod("non-matching-owned-0-", "node-0", simpleDaemonSetLabel2, ds), - newPod("non-matching-orphan-1-", "node-1", simpleDaemonSetLabel2, nil), - newPod("matching-owned-by-other-0-", "node-0", simpleDaemonSetLabel, ds2), - func() *v1.Pod { - pod := newPod("matching-owned-2-but-set-for-deletion", "node-2", simpleDaemonSetLabel, ds) - now := metav1.Now() - pod.DeletionTimestamp = &now - return pod - }(), - } - for _, pod := range ignoredPods { - manager.podStore.Add(pod) - } + // These pods should be ignored. + ignoredPods := []*v1.Pod{ + newPod("non-matching-owned-0-", "node-0", simpleDaemonSetLabel2, ds), + newPod("non-matching-orphan-1-", "node-1", simpleDaemonSetLabel2, nil), + newPod("matching-owned-by-other-0-", "node-0", simpleDaemonSetLabel, ds2), + func() *v1.Pod { + pod := newPod("matching-owned-2-but-set-for-deletion", "node-2", simpleDaemonSetLabel, ds) + now := metav1.Now() + pod.DeletionTimestamp = &now + return pod + }(), + } + for _, pod := range ignoredPods { + manager.podStore.Add(pod) + } - nodesToDaemonPods, err := manager.getNodesToDaemonPods(ds) - if err != nil { - t.Fatalf("getNodesToDaemonPods() error: %v", err) - } - gotPods := map[string]bool{} - for node, pods := range nodesToDaemonPods { - for _, pod := range pods { - if pod.Spec.NodeName != node { - t.Errorf("pod %v grouped into %v but belongs in %v", pod.Name, node, pod.Spec.NodeName) + nodesToDaemonPods, err := manager.getNodesToDaemonPods(ds) + if err != nil { + t.Fatalf("getNodesToDaemonPods() error: %v", err) + } + gotPods := map[string]bool{} + for node, pods := range nodesToDaemonPods { + for _, pod := range pods { + if pod.Spec.NodeName != node { + t.Errorf("pod %v grouped into %v but belongs in %v", pod.Name, node, pod.Spec.NodeName) + } + gotPods[pod.Name] = true } - gotPods[pod.Name] = true } - } - for _, pod := range wantedPods { - if !gotPods[pod.Name] { - t.Errorf("expected pod %v but didn't get it", pod.Name) + for _, pod := range wantedPods { + if !gotPods[pod.Name] { + t.Errorf("expected pod %v but didn't get it", pod.Name) + } + delete(gotPods, pod.Name) + } + for podName := range gotPods { + t.Errorf("unexpected pod %v was returned", podName) } - delete(gotPods, pod.Name) - } - for podName := range gotPods { - t.Errorf("unexpected pod %v was returned", podName) } } @@ -1390,222 +1536,260 @@ func TestAddNode(t *testing.T) { } func TestAddPod(t *testing.T) { - manager, _, _ := newTestController() - ds1 := newDaemonSet("foo1") - ds2 := newDaemonSet("foo2") - manager.dsStore.Add(ds1) - manager.dsStore.Add(ds2) + for _, strategy := range updateStrategies() { + manager, _, _ := newTestController() + ds1 := newDaemonSet("foo1") + ds1.Spec.UpdateStrategy = *strategy + ds2 := newDaemonSet("foo2") + ds2.Spec.UpdateStrategy = *strategy + manager.dsStore.Add(ds1) + manager.dsStore.Add(ds2) - pod1 := newPod("pod1-", "node-0", simpleDaemonSetLabel, ds1) - manager.addPod(pod1) - if got, want := manager.queue.Len(), 1; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) - } - key, done := manager.queue.Get() - if key == nil || done { - t.Fatalf("failed to enqueue controller for pod %v", pod1.Name) - } - expectedKey, _ := controller.KeyFunc(ds1) - if got, want := key.(string), expectedKey; got != want { - t.Errorf("queue.Get() = %v, want %v", got, want) - } + pod1 := newPod("pod1-", "node-0", simpleDaemonSetLabel, ds1) + manager.addPod(pod1) + if got, want := manager.queue.Len(), 1; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } + key, done := manager.queue.Get() + if key == nil || done { + t.Fatalf("failed to enqueue controller for pod %v", pod1.Name) + } + expectedKey, _ := controller.KeyFunc(ds1) + if got, want := key.(string), expectedKey; got != want { + t.Errorf("queue.Get() = %v, want %v", got, want) + } - pod2 := newPod("pod2-", "node-0", simpleDaemonSetLabel, ds2) - manager.addPod(pod2) - if got, want := manager.queue.Len(), 1; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) - } - key, done = manager.queue.Get() - if key == nil || done { - t.Fatalf("failed to enqueue controller for pod %v", pod2.Name) - } - expectedKey, _ = controller.KeyFunc(ds2) - if got, want := key.(string), expectedKey; got != want { - t.Errorf("queue.Get() = %v, want %v", got, want) + pod2 := newPod("pod2-", "node-0", simpleDaemonSetLabel, ds2) + manager.addPod(pod2) + if got, want := manager.queue.Len(), 1; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } + key, done = manager.queue.Get() + if key == nil || done { + t.Fatalf("failed to enqueue controller for pod %v", pod2.Name) + } + expectedKey, _ = controller.KeyFunc(ds2) + if got, want := key.(string), expectedKey; got != want { + t.Errorf("queue.Get() = %v, want %v", got, want) + } } } func TestAddPodOrphan(t *testing.T) { - manager, _, _ := newTestController() - ds1 := newDaemonSet("foo1") - ds2 := newDaemonSet("foo2") - ds3 := newDaemonSet("foo3") - ds3.Spec.Selector.MatchLabels = simpleDaemonSetLabel2 - manager.dsStore.Add(ds1) - manager.dsStore.Add(ds2) - manager.dsStore.Add(ds3) + for _, strategy := range updateStrategies() { + manager, _, _ := newTestController() + ds1 := newDaemonSet("foo1") + ds1.Spec.UpdateStrategy = *strategy + ds2 := newDaemonSet("foo2") + ds2.Spec.UpdateStrategy = *strategy + ds3 := newDaemonSet("foo3") + ds3.Spec.UpdateStrategy = *strategy + ds3.Spec.Selector.MatchLabels = simpleDaemonSetLabel2 + manager.dsStore.Add(ds1) + manager.dsStore.Add(ds2) + manager.dsStore.Add(ds3) - // Make pod an orphan. Expect matching sets to be queued. - pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, nil) - manager.addPod(pod) - if got, want := manager.queue.Len(), 2; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) - } - if got, want := getQueuedKeys(manager.queue), []string{"default/foo1", "default/foo2"}; !reflect.DeepEqual(got, want) { - t.Errorf("getQueuedKeys() = %v, want %v", got, want) + // Make pod an orphan. Expect matching sets to be queued. + pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, nil) + manager.addPod(pod) + if got, want := manager.queue.Len(), 2; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } + if got, want := getQueuedKeys(manager.queue), []string{"default/foo1", "default/foo2"}; !reflect.DeepEqual(got, want) { + t.Errorf("getQueuedKeys() = %v, want %v", got, want) + } } } func TestUpdatePod(t *testing.T) { - manager, _, _ := newTestController() - ds1 := newDaemonSet("foo1") - ds2 := newDaemonSet("foo2") - manager.dsStore.Add(ds1) - manager.dsStore.Add(ds2) + for _, strategy := range updateStrategies() { + manager, _, _ := newTestController() + ds1 := newDaemonSet("foo1") + ds1.Spec.UpdateStrategy = *strategy + ds2 := newDaemonSet("foo2") + ds2.Spec.UpdateStrategy = *strategy + manager.dsStore.Add(ds1) + manager.dsStore.Add(ds2) - pod1 := newPod("pod1-", "node-0", simpleDaemonSetLabel, ds1) - prev := *pod1 - bumpResourceVersion(pod1) - manager.updatePod(&prev, pod1) - if got, want := manager.queue.Len(), 1; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) - } - key, done := manager.queue.Get() - if key == nil || done { - t.Fatalf("failed to enqueue controller for pod %v", pod1.Name) - } - expectedKey, _ := controller.KeyFunc(ds1) - if got, want := key.(string), expectedKey; got != want { - t.Errorf("queue.Get() = %v, want %v", got, want) - } + pod1 := newPod("pod1-", "node-0", simpleDaemonSetLabel, ds1) + prev := *pod1 + bumpResourceVersion(pod1) + manager.updatePod(&prev, pod1) + if got, want := manager.queue.Len(), 1; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } + key, done := manager.queue.Get() + if key == nil || done { + t.Fatalf("failed to enqueue controller for pod %v", pod1.Name) + } + expectedKey, _ := controller.KeyFunc(ds1) + if got, want := key.(string), expectedKey; got != want { + t.Errorf("queue.Get() = %v, want %v", got, want) + } - pod2 := newPod("pod2-", "node-0", simpleDaemonSetLabel, ds2) - prev = *pod2 - bumpResourceVersion(pod2) - manager.updatePod(&prev, pod2) - if got, want := manager.queue.Len(), 1; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) - } - key, done = manager.queue.Get() - if key == nil || done { - t.Fatalf("failed to enqueue controller for pod %v", pod2.Name) - } - expectedKey, _ = controller.KeyFunc(ds2) - if got, want := key.(string), expectedKey; got != want { - t.Errorf("queue.Get() = %v, want %v", got, want) + pod2 := newPod("pod2-", "node-0", simpleDaemonSetLabel, ds2) + prev = *pod2 + bumpResourceVersion(pod2) + manager.updatePod(&prev, pod2) + if got, want := manager.queue.Len(), 1; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } + key, done = manager.queue.Get() + if key == nil || done { + t.Fatalf("failed to enqueue controller for pod %v", pod2.Name) + } + expectedKey, _ = controller.KeyFunc(ds2) + if got, want := key.(string), expectedKey; got != want { + t.Errorf("queue.Get() = %v, want %v", got, want) + } } } func TestUpdatePodOrphanSameLabels(t *testing.T) { - manager, _, _ := newTestController() - ds1 := newDaemonSet("foo1") - ds2 := newDaemonSet("foo2") - manager.dsStore.Add(ds1) - manager.dsStore.Add(ds2) + for _, strategy := range updateStrategies() { + manager, _, _ := newTestController() + ds1 := newDaemonSet("foo1") + ds1.Spec.UpdateStrategy = *strategy + ds2 := newDaemonSet("foo2") + ds2.Spec.UpdateStrategy = *strategy + manager.dsStore.Add(ds1) + manager.dsStore.Add(ds2) - pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, nil) - prev := *pod - bumpResourceVersion(pod) - manager.updatePod(&prev, pod) - if got, want := manager.queue.Len(), 0; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) + pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, nil) + prev := *pod + bumpResourceVersion(pod) + manager.updatePod(&prev, pod) + if got, want := manager.queue.Len(), 0; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } } } func TestUpdatePodOrphanWithNewLabels(t *testing.T) { - manager, _, _ := newTestController() - ds1 := newDaemonSet("foo1") - ds2 := newDaemonSet("foo2") - manager.dsStore.Add(ds1) - manager.dsStore.Add(ds2) + for _, strategy := range updateStrategies() { + manager, _, _ := newTestController() + ds1 := newDaemonSet("foo1") + ds1.Spec.UpdateStrategy = *strategy + ds2 := newDaemonSet("foo2") + ds2.Spec.UpdateStrategy = *strategy + manager.dsStore.Add(ds1) + manager.dsStore.Add(ds2) - pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, nil) - prev := *pod - prev.Labels = map[string]string{"foo2": "bar2"} - bumpResourceVersion(pod) - manager.updatePod(&prev, pod) - if got, want := manager.queue.Len(), 2; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) - } - if got, want := getQueuedKeys(manager.queue), []string{"default/foo1", "default/foo2"}; !reflect.DeepEqual(got, want) { - t.Errorf("getQueuedKeys() = %v, want %v", got, want) + pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, nil) + prev := *pod + prev.Labels = map[string]string{"foo2": "bar2"} + bumpResourceVersion(pod) + manager.updatePod(&prev, pod) + if got, want := manager.queue.Len(), 2; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } + if got, want := getQueuedKeys(manager.queue), []string{"default/foo1", "default/foo2"}; !reflect.DeepEqual(got, want) { + t.Errorf("getQueuedKeys() = %v, want %v", got, want) + } } } func TestUpdatePodChangeControllerRef(t *testing.T) { - manager, _, _ := newTestController() - ds1 := newDaemonSet("foo1") - ds2 := newDaemonSet("foo2") - manager.dsStore.Add(ds1) - manager.dsStore.Add(ds2) + for _, strategy := range updateStrategies() { + ds := newDaemonSet("foo") + ds.Spec.UpdateStrategy = *strategy + manager, _, _ := newTestController() + ds1 := newDaemonSet("foo1") + ds2 := newDaemonSet("foo2") + manager.dsStore.Add(ds1) + manager.dsStore.Add(ds2) - pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, ds1) - prev := *pod - prev.OwnerReferences = []metav1.OwnerReference{*newControllerRef(ds2)} - bumpResourceVersion(pod) - manager.updatePod(&prev, pod) - if got, want := manager.queue.Len(), 2; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) + pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, ds1) + prev := *pod + prev.OwnerReferences = []metav1.OwnerReference{*newControllerRef(ds2)} + bumpResourceVersion(pod) + manager.updatePod(&prev, pod) + if got, want := manager.queue.Len(), 2; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } } } func TestUpdatePodControllerRefRemoved(t *testing.T) { - manager, _, _ := newTestController() - ds1 := newDaemonSet("foo1") - ds2 := newDaemonSet("foo2") - manager.dsStore.Add(ds1) - manager.dsStore.Add(ds2) + for _, strategy := range updateStrategies() { + manager, _, _ := newTestController() + ds1 := newDaemonSet("foo1") + ds1.Spec.UpdateStrategy = *strategy + ds2 := newDaemonSet("foo2") + ds2.Spec.UpdateStrategy = *strategy + manager.dsStore.Add(ds1) + manager.dsStore.Add(ds2) - pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, ds1) - prev := *pod - pod.OwnerReferences = nil - bumpResourceVersion(pod) - manager.updatePod(&prev, pod) - if got, want := manager.queue.Len(), 2; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) + pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, ds1) + prev := *pod + pod.OwnerReferences = nil + bumpResourceVersion(pod) + manager.updatePod(&prev, pod) + if got, want := manager.queue.Len(), 2; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } } } func TestDeletePod(t *testing.T) { - manager, _, _ := newTestController() - ds1 := newDaemonSet("foo1") - ds2 := newDaemonSet("foo2") - manager.dsStore.Add(ds1) - manager.dsStore.Add(ds2) + for _, strategy := range updateStrategies() { + manager, _, _ := newTestController() + ds1 := newDaemonSet("foo1") + ds1.Spec.UpdateStrategy = *strategy + ds2 := newDaemonSet("foo2") + ds2.Spec.UpdateStrategy = *strategy + manager.dsStore.Add(ds1) + manager.dsStore.Add(ds2) - pod1 := newPod("pod1-", "node-0", simpleDaemonSetLabel, ds1) - manager.deletePod(pod1) - if got, want := manager.queue.Len(), 1; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) - } - key, done := manager.queue.Get() - if key == nil || done { - t.Fatalf("failed to enqueue controller for pod %v", pod1.Name) - } - expectedKey, _ := controller.KeyFunc(ds1) - if got, want := key.(string), expectedKey; got != want { - t.Errorf("queue.Get() = %v, want %v", got, want) - } + pod1 := newPod("pod1-", "node-0", simpleDaemonSetLabel, ds1) + manager.deletePod(pod1) + if got, want := manager.queue.Len(), 1; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } + key, done := manager.queue.Get() + if key == nil || done { + t.Fatalf("failed to enqueue controller for pod %v", pod1.Name) + } + expectedKey, _ := controller.KeyFunc(ds1) + if got, want := key.(string), expectedKey; got != want { + t.Errorf("queue.Get() = %v, want %v", got, want) + } - pod2 := newPod("pod2-", "node-0", simpleDaemonSetLabel, ds2) - manager.deletePod(pod2) - if got, want := manager.queue.Len(), 1; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) - } - key, done = manager.queue.Get() - if key == nil || done { - t.Fatalf("failed to enqueue controller for pod %v", pod2.Name) - } - expectedKey, _ = controller.KeyFunc(ds2) - if got, want := key.(string), expectedKey; got != want { - t.Errorf("queue.Get() = %v, want %v", got, want) + pod2 := newPod("pod2-", "node-0", simpleDaemonSetLabel, ds2) + manager.deletePod(pod2) + if got, want := manager.queue.Len(), 1; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } + key, done = manager.queue.Get() + if key == nil || done { + t.Fatalf("failed to enqueue controller for pod %v", pod2.Name) + } + expectedKey, _ = controller.KeyFunc(ds2) + if got, want := key.(string), expectedKey; got != want { + t.Errorf("queue.Get() = %v, want %v", got, want) + } } } func TestDeletePodOrphan(t *testing.T) { - manager, _, _ := newTestController() - ds1 := newDaemonSet("foo1") - ds2 := newDaemonSet("foo2") - ds3 := newDaemonSet("foo3") - ds3.Spec.Selector.MatchLabels = simpleDaemonSetLabel2 - manager.dsStore.Add(ds1) - manager.dsStore.Add(ds2) - manager.dsStore.Add(ds3) + for _, strategy := range updateStrategies() { + manager, _, _ := newTestController() + ds1 := newDaemonSet("foo1") + ds1.Spec.UpdateStrategy = *strategy + ds2 := newDaemonSet("foo2") + ds2.Spec.UpdateStrategy = *strategy + ds3 := newDaemonSet("foo3") + ds3.Spec.UpdateStrategy = *strategy + ds3.Spec.Selector.MatchLabels = simpleDaemonSetLabel2 + manager.dsStore.Add(ds1) + manager.dsStore.Add(ds2) + manager.dsStore.Add(ds3) - pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, nil) - manager.deletePod(pod) - if got, want := manager.queue.Len(), 0; got != want { - t.Fatalf("queue.Len() = %v, want %v", got, want) + pod := newPod("pod1-", "node-0", simpleDaemonSetLabel, nil) + manager.deletePod(pod) + if got, want := manager.queue.Len(), 0; got != want { + t.Fatalf("queue.Len() = %v, want %v", got, want) + } } } From 85ec49c9bb655cb2f2a7d33598780db4f2500b0c Mon Sep 17 00:00:00 2001 From: Janet Kuo Date: Wed, 24 May 2017 15:18:36 -0700 Subject: [PATCH 5/5] Verify histories and pods in DaemonSet e2e test --- pkg/controller/daemon/update.go | 12 ++--- test/e2e/daemon_set.go | 88 +++++++++++++++++++++++++++++++-- 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/pkg/controller/daemon/update.go b/pkg/controller/daemon/update.go index 9be6960e430..4f9eb91ee26 100644 --- a/pkg/controller/daemon/update.go +++ b/pkg/controller/daemon/update.go @@ -104,7 +104,7 @@ func (dsc *DaemonSetsController) constructHistory(ds *extensions.DaemonSet) (cur } // Compare histories with ds to separate cur and old history found := false - found, err = match(ds, history) + found, err = Match(&ds.Spec.Template, history) if err != nil { return nil, nil, err } @@ -295,10 +295,10 @@ func (dsc *DaemonSetsController) controlledHistories(ds *extensions.DaemonSet) ( return result, nil } -// match check if ds template is semantically equal to the template stored in history -func match(ds *extensions.DaemonSet, history *apps.ControllerRevision) (bool, error) { - template, err := decodeHistory(history) - return apiequality.Semantic.DeepEqual(&ds.Spec.Template, template), err +// Match check if ds template is semantically equal to the template stored in history +func Match(template *v1.PodTemplateSpec, history *apps.ControllerRevision) (bool, error) { + t, err := decodeHistory(history) + return apiequality.Semantic.DeepEqual(template, t), err } func decodeHistory(history *apps.ControllerRevision) (*v1.PodTemplateSpec, error) { @@ -343,7 +343,7 @@ func (dsc *DaemonSetsController) snapshot(ds *extensions.DaemonSet, revision int return nil, getErr } // Check if we already created it - done, err := match(ds, existedHistory) + done, err := Match(&ds.Spec.Template, existedHistory) if err != nil { return nil, err } diff --git a/test/e2e/daemon_set.go b/test/e2e/daemon_set.go index f0d269fe720..391287acb17 100644 --- a/test/e2e/daemon_set.go +++ b/test/e2e/daemon_set.go @@ -31,6 +31,7 @@ import ( "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" extensionsinternal "k8s.io/kubernetes/pkg/apis/extensions" extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" @@ -260,9 +261,19 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() { Expect(err).NotTo(HaveOccurred(), "error waiting for daemon pod to start") By("Make sure all daemon pods have correct template generation 1") + templateGeneration := "1" err = checkDaemonPodsTemplateGeneration(c, ns, label, "1") Expect(err).NotTo(HaveOccurred()) + // Check history and labels + ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + first := curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template) + firstHash := ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey] + Expect(first.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(firstHash)) + Expect(first.Revision).To(Equal(int64(1))) + checkDaemonSetPodsLabels(listDaemonPods(c, ns, label), firstHash, templateGeneration) + By("Update daemon pods image.") patch := getDaemonSetImagePatch(ds.Spec.Template.Spec.Containers[0].Name, redisImage) ds, err = c.Extensions().DaemonSets(ns).Patch(dsName, types.StrategicMergePatchType, []byte(patch)) @@ -274,12 +285,21 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() { Expect(err).NotTo(HaveOccurred()) By("Make sure all daemon pods have correct template generation 1") - err = checkDaemonPodsTemplateGeneration(c, ns, label, "1") + err = checkDaemonPodsTemplateGeneration(c, ns, label, templateGeneration) Expect(err).NotTo(HaveOccurred()) By("Check that daemon pods are still running on every node of the cluster.") err = wait.PollImmediate(dsRetryPeriod, dsRetryTimeout, checkRunningOnAllNodes(f, ds)) Expect(err).NotTo(HaveOccurred(), "error waiting for daemon pod to start") + + // Check history and labels + ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + cur := curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template) + curHash := ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey] + Expect(cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(curHash)) + Expect(cur.Revision).To(Equal(int64(2))) + checkDaemonSetPodsLabels(listDaemonPods(c, ns, label), firstHash, templateGeneration) }) It("Should update pod when spec was updated and update strategy is RollingUpdate", func() { @@ -302,23 +322,42 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() { err = checkDaemonPodsTemplateGeneration(c, ns, label, fmt.Sprint(templateGeneration)) Expect(err).NotTo(HaveOccurred()) + // Check history and labels + ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + cur := curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template) + hash := ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey] + Expect(cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(hash)) + Expect(cur.Revision).To(Equal(int64(1))) + checkDaemonSetPodsLabels(listDaemonPods(c, ns, label), hash, fmt.Sprint(templateGeneration)) + By("Update daemon pods image.") patch := getDaemonSetImagePatch(ds.Spec.Template.Spec.Containers[0].Name, redisImage) ds, err = c.Extensions().DaemonSets(ns).Patch(dsName, types.StrategicMergePatchType, []byte(patch)) Expect(err).NotTo(HaveOccurred()) - Expect(ds.Spec.TemplateGeneration).To(Equal(templateGeneration + 1)) + templateGeneration++ + Expect(ds.Spec.TemplateGeneration).To(Equal(templateGeneration)) By("Check that daemon pods images are updated.") err = wait.PollImmediate(dsRetryPeriod, dsRetryTimeout, checkDaemonPodsImageAndAvailability(c, ds, redisImage, 1)) Expect(err).NotTo(HaveOccurred()) - By(fmt.Sprintf("Make sure all daemon pods have correct template generation %d", templateGeneration+1)) - err = checkDaemonPodsTemplateGeneration(c, ns, label, fmt.Sprint(templateGeneration+1)) + By(fmt.Sprintf("Make sure all daemon pods have correct template generation %d", templateGeneration)) + err = checkDaemonPodsTemplateGeneration(c, ns, label, fmt.Sprint(templateGeneration)) Expect(err).NotTo(HaveOccurred()) By("Check that daemon pods are still running on every node of the cluster.") err = wait.PollImmediate(dsRetryPeriod, dsRetryTimeout, checkRunningOnAllNodes(f, ds)) Expect(err).NotTo(HaveOccurred(), "error waiting for daemon pod to start") + + // Check history and labels + ds, err = c.Extensions().DaemonSets(ns).Get(ds.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + cur = curHistory(listDaemonHistories(c, ns, label), &ds.Spec.Template) + hash = ds.Labels[extensions.DefaultDaemonSetUniqueLabelKey] + Expect(cur.Labels[extensions.DefaultDaemonSetUniqueLabelKey]).To(Equal(hash)) + Expect(cur.Revision).To(Equal(int64(2))) + checkDaemonSetPodsLabels(listDaemonPods(c, ns, label), hash, fmt.Sprint(templateGeneration)) }) It("Should adopt or recreate existing pods when creating a RollingUpdate DaemonSet with matching or mismatching templateGeneration", func() { @@ -698,3 +737,44 @@ func checkDaemonSetPodsName(c clientset.Interface, ns, prefix string, label map[ } return nil } + +func checkDaemonSetPodsLabels(podList *v1.PodList, hash, templateGeneration string) { + for _, pod := range podList.Items { + podHash := pod.Labels[extensions.DefaultDaemonSetUniqueLabelKey] + podTemplate := pod.Labels[extensions.DaemonSetTemplateGenerationKey] + Expect(len(podHash)).To(BeNumerically(">", 0)) + if len(hash) > 0 { + Expect(podHash).To(Equal(hash)) + } + Expect(len(podTemplate)).To(BeNumerically(">", 0)) + Expect(podTemplate).To(Equal(templateGeneration)) + } +} + +func listDaemonHistories(c clientset.Interface, ns string, label map[string]string) *apps.ControllerRevisionList { + selector := labels.Set(label).AsSelector() + options := metav1.ListOptions{LabelSelector: selector.String()} + historyList, err := c.Apps().ControllerRevisions(ns).List(options) + Expect(err).NotTo(HaveOccurred()) + Expect(len(historyList.Items)).To(BeNumerically(">", 0)) + return historyList +} + +func curHistory(historyList *apps.ControllerRevisionList, template *v1.PodTemplateSpec) *apps.ControllerRevision { + var curHistory *apps.ControllerRevision + foundCurHistories := 0 + for i := range historyList.Items { + history := &historyList.Items[i] + // Every history should have the hash label + Expect(len(history.Labels[extensions.DefaultDaemonSetUniqueLabelKey])).To(BeNumerically(">", 0)) + match, err := daemon.Match(template, history) + Expect(err).NotTo(HaveOccurred()) + if match { + curHistory = history + foundCurHistories++ + } + } + Expect(foundCurHistories).To(Equal(1)) + Expect(curHistory).NotTo(BeNil()) + return curHistory +}