Merge pull request #98127 from mortent/AddConditionsToPDBs

Add conditions to PDB status
This commit is contained in:
Kubernetes Prow Robot 2021-03-04 22:30:21 -08:00 committed by GitHub
commit 7a49140145
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 694 additions and 155 deletions

View File

@ -13330,6 +13330,19 @@
"io.k8s.api.policy.v1beta1.PodDisruptionBudgetStatus": {
"description": "PodDisruptionBudgetStatus represents information about the status of a PodDisruptionBudget. Status may trail the actual state of a system.",
"properties": {
"conditions": {
"description": "Conditions contain conditions for PDB. The disruption controller sets the DisruptionAllowed condition. The following are known values for the reason field (additional reasons could be added in the future): - SyncFailed: The controller encountered an error and wasn't able to compute\n the number of allowed disruptions. Therefore no disruptions are\n allowed and the status of the condition will be False.\n- InsufficientPods: The number of pods are either at or below the number\n required by the PodDisruptionBudget. No disruptions are\n allowed and the status of the condition will be False.\n- SufficientPods: There are more pods than required by the PodDisruptionBudget.\n The condition will be True, and the number of allowed\n disruptions are provided by the disruptionsAllowed property.",
"items": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map",
"x-kubernetes-patch-merge-key": "type",
"x-kubernetes-patch-strategy": "merge"
},
"currentHealthy": {
"description": "current number of healthy pods",
"format": "int32",

View File

@ -77,6 +77,10 @@ type PodDisruptionBudgetStatus struct {
// total number of pods counted by this disruption budget
ExpectedPods int32
// Conditions contain conditions for PDB
// +optional
Conditions []metav1.Condition
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

View File

@ -468,6 +468,7 @@ func autoConvert_v1beta1_PodDisruptionBudgetStatus_To_policy_PodDisruptionBudget
out.CurrentHealthy = in.CurrentHealthy
out.DesiredHealthy = in.DesiredHealthy
out.ExpectedPods = in.ExpectedPods
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
return nil
}
@ -483,6 +484,7 @@ func autoConvert_policy_PodDisruptionBudgetStatus_To_v1beta1_PodDisruptionBudget
out.CurrentHealthy = in.CurrentHealthy
out.DesiredHealthy = in.DesiredHealthy
out.ExpectedPods = in.ExpectedPods
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
return nil
}

View File

@ -23,8 +23,10 @@ import (
"strings"
"k8s.io/api/core/v1"
policyapiv1beta1 "k8s.io/api/policy/v1beta1"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
appsvalidation "k8s.io/kubernetes/pkg/apis/apps/validation"
@ -40,7 +42,6 @@ import (
// with any errors.
func ValidatePodDisruptionBudget(pdb *policy.PodDisruptionBudget) field.ErrorList {
allErrs := ValidatePodDisruptionBudgetSpec(pdb.Spec, field.NewPath("spec"))
allErrs = append(allErrs, ValidatePodDisruptionBudgetStatus(pdb.Status, field.NewPath("status"))...)
return allErrs
}
@ -68,10 +69,16 @@ func ValidatePodDisruptionBudgetSpec(spec policy.PodDisruptionBudgetSpec, fldPat
return allErrs
}
// ValidatePodDisruptionBudgetStatus validates a PodDisruptionBudgetStatus and returns an ErrorList
// ValidatePodDisruptionBudgetStatusUpdate validates a PodDisruptionBudgetStatus and returns an ErrorList
// with any errors.
func ValidatePodDisruptionBudgetStatus(status policy.PodDisruptionBudgetStatus, fldPath *field.Path) field.ErrorList {
func ValidatePodDisruptionBudgetStatusUpdate(status, oldStatus policy.PodDisruptionBudgetStatus, fldPath *field.Path, apiVersion schema.GroupVersion) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, unversionedvalidation.ValidateConditions(status.Conditions, fldPath.Child("conditions"))...)
// Don't run other validations for v1beta1 since we don't want to introduce
// new validations retroactively.
if apiVersion == policyapiv1beta1.SchemeGroupVersion {
return allErrs
}
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.DisruptionsAllowed), fldPath.Child("disruptionsAllowed"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.CurrentHealthy), fldPath.Child("currentHealthy"))...)
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(status.DesiredHealthy), fldPath.Child("desiredHealthy"))...)

View File

@ -19,10 +19,13 @@ package validation
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation/field"
api "k8s.io/kubernetes/pkg/apis/core"
@ -98,26 +101,150 @@ func TestValidateMinAvailablePodAndMaxUnavailableDisruptionBudgetSpec(t *testing
}
func TestValidatePodDisruptionBudgetStatus(t *testing.T) {
successCases := []policy.PodDisruptionBudgetStatus{
{DisruptionsAllowed: 10},
{CurrentHealthy: 5},
{DesiredHealthy: 3},
{ExpectedPods: 2}}
for _, c := range successCases {
errors := ValidatePodDisruptionBudgetStatus(c, field.NewPath("status"))
if len(errors) > 0 {
t.Errorf("unexpected failure %v for %v", errors, c)
}
const expectNoErrors = false
const expectErrors = true
testCases := []struct {
name string
pdbStatus policy.PodDisruptionBudgetStatus
expectErrForVersion map[schema.GroupVersion]bool
}{
{
name: "DisruptionsAllowed: 10",
pdbStatus: policy.PodDisruptionBudgetStatus{
DisruptionsAllowed: 10,
},
expectErrForVersion: map[schema.GroupVersion]bool{
policy.SchemeGroupVersion: expectNoErrors,
policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
},
{
name: "CurrentHealthy: 5",
pdbStatus: policy.PodDisruptionBudgetStatus{
CurrentHealthy: 5,
},
expectErrForVersion: map[schema.GroupVersion]bool{
policy.SchemeGroupVersion: expectNoErrors,
policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
},
{
name: "DesiredHealthy: 3",
pdbStatus: policy.PodDisruptionBudgetStatus{
DesiredHealthy: 3,
},
expectErrForVersion: map[schema.GroupVersion]bool{
policy.SchemeGroupVersion: expectNoErrors,
policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
},
{
name: "ExpectedPods: 2",
pdbStatus: policy.PodDisruptionBudgetStatus{
ExpectedPods: 2,
},
expectErrForVersion: map[schema.GroupVersion]bool{
policy.SchemeGroupVersion: expectNoErrors,
policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
},
{
name: "DisruptionsAllowed: -10",
pdbStatus: policy.PodDisruptionBudgetStatus{
DisruptionsAllowed: -10,
},
expectErrForVersion: map[schema.GroupVersion]bool{
policy.SchemeGroupVersion: expectErrors,
policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
},
{
name: "CurrentHealthy: -5",
pdbStatus: policy.PodDisruptionBudgetStatus{
CurrentHealthy: -5,
},
expectErrForVersion: map[schema.GroupVersion]bool{
policy.SchemeGroupVersion: expectErrors,
policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
},
{
name: "DesiredHealthy: -3",
pdbStatus: policy.PodDisruptionBudgetStatus{
DesiredHealthy: -3,
},
expectErrForVersion: map[schema.GroupVersion]bool{
policy.SchemeGroupVersion: expectErrors,
policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
},
{
name: "ExpectedPods: -2",
pdbStatus: policy.PodDisruptionBudgetStatus{
ExpectedPods: -2,
},
expectErrForVersion: map[schema.GroupVersion]bool{
policy.SchemeGroupVersion: expectErrors,
policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
},
{
name: "Conditions valid",
pdbStatus: policy.PodDisruptionBudgetStatus{
Conditions: []metav1.Condition{
{
Type: policyv1beta1.DisruptionAllowedCondition,
Status: metav1.ConditionTrue,
LastTransitionTime: metav1.Time{
Time: time.Now().Add(-5 * time.Minute),
},
Reason: policyv1beta1.SufficientPodsReason,
Message: "message",
ObservedGeneration: 3,
},
},
},
expectErrForVersion: map[schema.GroupVersion]bool{
policy.SchemeGroupVersion: expectNoErrors,
policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
},
{
name: "Conditions not valid",
pdbStatus: policy.PodDisruptionBudgetStatus{
Conditions: []metav1.Condition{
{
Type: policyv1beta1.DisruptionAllowedCondition,
Status: metav1.ConditionTrue,
},
{
Type: policyv1beta1.DisruptionAllowedCondition,
Status: metav1.ConditionFalse,
},
},
},
expectErrForVersion: map[schema.GroupVersion]bool{
policy.SchemeGroupVersion: expectErrors,
policyv1beta1.SchemeGroupVersion: expectErrors,
},
},
}
failureCases := []policy.PodDisruptionBudgetStatus{
{DisruptionsAllowed: -10},
{CurrentHealthy: -5},
{DesiredHealthy: -3},
{ExpectedPods: -2}}
for _, c := range failureCases {
errors := ValidatePodDisruptionBudgetStatus(c, field.NewPath("status"))
if len(errors) == 0 {
t.Errorf("unexpected success for %v", c)
for _, tc := range testCases {
for apiVersion, expectErrors := range tc.expectErrForVersion {
t.Run(fmt.Sprintf("apiVersion: %s, %s", apiVersion.String(), tc.name), func(t *testing.T) {
errors := ValidatePodDisruptionBudgetStatusUpdate(tc.pdbStatus, policy.PodDisruptionBudgetStatus{},
field.NewPath("status"), apiVersion)
errCount := len(errors)
if errCount > 0 && !expectErrors {
t.Errorf("unexpected failure %v for %v", errors, tc.pdbStatus)
}
if errCount == 0 && expectErrors {
t.Errorf("expected errors but didn't one for %v", tc.pdbStatus)
}
})
}
}
}

View File

@ -261,6 +261,13 @@ func (in *PodDisruptionBudgetStatus) DeepCopyInto(out *PodDisruptionBudgetStatus
(*out)[key] = *val.DeepCopy()
}
}
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}

View File

@ -49,6 +49,7 @@ import (
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
pdbhelper "k8s.io/component-helpers/apps/poddisruptionbudget"
"k8s.io/klog/v2"
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/pkg/controller"
@ -63,7 +64,9 @@ import (
// If the controller is running on a different node it is important that the two nodes have synced
// clock (via ntp for example). Otherwise PodDisruptionBudget controller may not provide enough
// protection against unwanted pod disruptions.
const DeletionTimeout = 2 * 60 * time.Second
const (
DeletionTimeout = 2 * 60 * time.Second
)
type updater func(*policy.PodDisruptionBudget) error
@ -579,7 +582,7 @@ func (dc *DisruptionController) sync(key string) error {
}
if err != nil {
klog.Errorf("Failed to sync pdb %s/%s: %v", pdb.Namespace, pdb.Name, err)
return dc.failSafe(pdb)
return dc.failSafe(pdb, err)
}
return nil
@ -774,9 +777,21 @@ func (dc *DisruptionController) buildDisruptedPodMap(pods []*v1.Pod, pdb *policy
// implement the "fail open" part of the design since if we manage to update
// this field correctly, we will prevent the /evict handler from approving an
// eviction when it may be unsafe to do so.
func (dc *DisruptionController) failSafe(pdb *policy.PodDisruptionBudget) error {
func (dc *DisruptionController) failSafe(pdb *policy.PodDisruptionBudget, err error) error {
newPdb := pdb.DeepCopy()
newPdb.Status.DisruptionsAllowed = 0
if newPdb.Status.Conditions == nil {
newPdb.Status.Conditions = make([]metav1.Condition, 0)
}
apimeta.SetStatusCondition(&newPdb.Status.Conditions, metav1.Condition{
Type: policy.DisruptionAllowedCondition,
Status: metav1.ConditionFalse,
Reason: policy.SyncFailedReason,
Message: err.Error(),
ObservedGeneration: newPdb.Status.ObservedGeneration,
})
return dc.getUpdater()(newPdb)
}
@ -797,7 +812,8 @@ func (dc *DisruptionController) updatePdbStatus(pdb *policy.PodDisruptionBudget,
pdb.Status.ExpectedPods == expectedCount &&
pdb.Status.DisruptionsAllowed == disruptionsAllowed &&
apiequality.Semantic.DeepEqual(pdb.Status.DisruptedPods, disruptedPods) &&
pdb.Status.ObservedGeneration == pdb.Generation {
pdb.Status.ObservedGeneration == pdb.Generation &&
pdbhelper.ConditionsAreUpToDate(pdb) {
return nil
}
@ -811,6 +827,8 @@ func (dc *DisruptionController) updatePdbStatus(pdb *policy.PodDisruptionBudget,
ObservedGeneration: pdb.Generation,
}
pdbhelper.UpdateDisruptionAllowedCondition(newPdb)
return dc.getUpdater()(newPdb)
}

View File

@ -32,6 +32,7 @@ import (
policy "k8s.io/api/policy/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/meta/testrestmapper"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@ -73,6 +74,8 @@ func (ps *pdbStates) Get(key string) policy.PodDisruptionBudget {
func (ps *pdbStates) VerifyPdbStatus(t *testing.T, key string, disruptionsAllowed, currentHealthy, desiredHealthy, expectedPods int32,
disruptedPodMap map[string]metav1.Time) {
actualPDB := ps.Get(key)
actualConditions := actualPDB.Status.Conditions
actualPDB.Status.Conditions = nil
expectedStatus := policy.PodDisruptionBudgetStatus{
DisruptionsAllowed: disruptionsAllowed,
CurrentHealthy: currentHealthy,
@ -86,6 +89,22 @@ func (ps *pdbStates) VerifyPdbStatus(t *testing.T, key string, disruptionsAllowe
debug.PrintStack()
t.Fatalf("PDB %q status mismatch. Expected %+v but got %+v.", key, expectedStatus, actualStatus)
}
cond := apimeta.FindStatusCondition(actualConditions, policy.DisruptionAllowedCondition)
if cond == nil {
t.Fatalf("Expected condition %q, but didn't find it", policy.DisruptionAllowedCondition)
}
if disruptionsAllowed > 0 {
if cond.Status != metav1.ConditionTrue {
t.Fatalf("Expected condition %q to have status %q, but was %q",
policy.DisruptionAllowedCondition, metav1.ConditionTrue, cond.Status)
}
} else {
if cond.Status != metav1.ConditionFalse {
t.Fatalf("Expected condition %q to have status %q, but was %q",
policy.DisruptionAllowedCondition, metav1.ConditionFalse, cond.Status)
}
}
}
func (ps *pdbStates) VerifyDisruptionAllowed(t *testing.T, key string, disruptionsAllowed int32) {

View File

@ -33,6 +33,7 @@ import (
"k8s.io/apiserver/pkg/util/dryrun"
policyclient "k8s.io/client-go/kubernetes/typed/policy/v1beta1"
"k8s.io/client-go/util/retry"
pdbhelper "k8s.io/component-helpers/apps/poddisruptionbudget"
podutil "k8s.io/kubernetes/pkg/api/pod"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/policy"
@ -331,6 +332,10 @@ func (r *EvictionREST) checkAndDecrement(namespace string, podName string, pdb p
}
pdb.Status.DisruptionsAllowed--
if pdb.Status.DisruptionsAllowed == 0 {
pdbhelper.UpdateDisruptionAllowedCondition(&pdb)
}
// If this is a dry-run, we don't need to go any further than that.
if dryRun == true {
return nil

View File

@ -23,6 +23,7 @@ import (
policyv1beta1 "k8s.io/api/policy/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@ -505,6 +506,103 @@ func TestEvictionDryRun(t *testing.T) {
}
}
func TestEvictionPDBStatus(t *testing.T) {
testcases := []struct {
name string
pdb *policyv1beta1.PodDisruptionBudget
expectedDisruptionsAllowed int32
expectedReason string
}{
{
name: "pdb status is updated after eviction",
pdb: &policyv1beta1.PodDisruptionBudget{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
Status: policyv1beta1.PodDisruptionBudgetStatus{
DisruptionsAllowed: 1,
Conditions: []metav1.Condition{
{
Type: policyv1beta1.DisruptionAllowedCondition,
Reason: policyv1beta1.SufficientPodsReason,
Status: metav1.ConditionTrue,
},
},
},
},
expectedDisruptionsAllowed: 0,
expectedReason: policyv1beta1.InsufficientPodsReason,
},
{
name: "condition reason is only updated if AllowedDisruptions becomes 0",
pdb: &policyv1beta1.PodDisruptionBudget{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
Spec: policyv1beta1.PodDisruptionBudgetSpec{Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "true"}}},
Status: policyv1beta1.PodDisruptionBudgetStatus{
DisruptionsAllowed: 3,
Conditions: []metav1.Condition{
{
Type: policyv1beta1.DisruptionAllowedCondition,
Reason: policyv1beta1.SufficientPodsReason,
Status: metav1.ConditionTrue,
},
},
},
},
expectedDisruptionsAllowed: 2,
expectedReason: policyv1beta1.SufficientPodsReason,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
testContext := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
storage, _, statusStorage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
client := fake.NewSimpleClientset(tc.pdb)
for _, podName := range []string{"foo-1", "foo-2"} {
pod := validNewPod()
pod.Labels = map[string]string{"a": "true"}
pod.ObjectMeta.Name = podName
pod.Spec.NodeName = "foo"
newPod, err := storage.Create(testContext, pod, nil, &metav1.CreateOptions{})
if err != nil {
t.Error(err)
}
(newPod.(*api.Pod)).Status.Phase = api.PodRunning
_, _, err = statusStorage.Update(testContext, pod.Name, rest.DefaultUpdatedObjectInfo(newPod),
nil, nil, false, &metav1.UpdateOptions{})
if err != nil {
t.Error(err)
}
}
evictionRest := newEvictionStorage(storage.Store, client.PolicyV1beta1())
eviction := &policy.Eviction{ObjectMeta: metav1.ObjectMeta{Name: "foo-1", Namespace: "default"}, DeleteOptions: &metav1.DeleteOptions{}}
_, err := evictionRest.Create(testContext, "foo-1", eviction, nil, &metav1.CreateOptions{})
if err != nil {
t.Fatalf("Failed to run eviction: %v", err)
}
existingPDB, err := client.PolicyV1beta1().PodDisruptionBudgets(metav1.NamespaceDefault).Get(context.TODO(), tc.pdb.Name, metav1.GetOptions{})
if err != nil {
t.Errorf("%#v", err)
return
}
if want, got := tc.expectedDisruptionsAllowed, existingPDB.Status.DisruptionsAllowed; got != want {
t.Errorf("expected DisruptionsAllowed to be %d, but got %d", want, got)
}
cond := apimeta.FindStatusCondition(existingPDB.Status.Conditions, policyv1beta1.DisruptionAllowedCondition)
if want, got := tc.expectedReason, cond.Reason; want != got {
t.Errorf("expected Reason to be %q, but got %q", want, got)
}
})
}
}
func resource(resource string) schema.GroupResource {
return schema.GroupResource{Group: "", Resource: resource}
}

View File

@ -21,7 +21,9 @@ import (
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/policy"
@ -109,7 +111,13 @@ func (podDisruptionBudgetStatusStrategy) PrepareForUpdate(ctx context.Context, o
// ValidateUpdate is the default update validation for an end user updating status
func (podDisruptionBudgetStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
// TODO: Validate status updates.
return field.ErrorList{}
// return validation.ValidatePodDisruptionBudgetStatusUpdate(obj.(*policy.PodDisruptionBudget), old.(*policy.PodDisruptionBudget))
var apiVersion schema.GroupVersion
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
apiVersion = schema.GroupVersion{
Group: requestInfo.APIVersion,
Version: requestInfo.APIGroup,
}
}
return validation.ValidatePodDisruptionBudgetStatusUpdate(obj.(*policy.PodDisruptionBudget).Status,
old.(*policy.PodDisruptionBudget).Status, field.NewPath("status"), apiVersion)
}

View File

@ -609,125 +609,126 @@ func init() {
}
var fileDescriptor_014060e454a820dc = []byte{
// 1878 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xdd, 0x6e, 0x1b, 0xc7,
0x15, 0xd6, 0x9a, 0xfa, 0xa1, 0x46, 0x3f, 0x16, 0x47, 0x3f, 0x5e, 0x2b, 0x0d, 0xd7, 0xd9, 0x00,
0x85, 0x9b, 0x26, 0xcb, 0x58, 0x76, 0x5c, 0xa3, 0x69, 0x8b, 0x68, 0x45, 0xc9, 0x56, 0x60, 0x59,
0xec, 0xd0, 0x0e, 0xda, 0xc2, 0x2d, 0x3a, 0xe4, 0x8e, 0xa8, 0x8d, 0x96, 0xbb, 0xdb, 0x99, 0x59,
0x46, 0xbc, 0xeb, 0x45, 0x2f, 0x7a, 0xd9, 0x17, 0x08, 0xfa, 0x00, 0x45, 0xaf, 0xfa, 0x12, 0x0e,
0x50, 0x04, 0xb9, 0x0c, 0x7a, 0x41, 0xd4, 0xec, 0x5b, 0xf8, 0xaa, 0xd8, 0xe1, 0xec, 0x92, 0xfb,
0x47, 0x5a, 0x01, 0xec, 0x3b, 0xee, 0x9c, 0xef, 0xfb, 0xce, 0xcc, 0x99, 0x33, 0x67, 0x0e, 0x07,
0x98, 0x17, 0x0f, 0x98, 0x61, 0x7b, 0xb5, 0x8b, 0xa0, 0x45, 0xa8, 0x4b, 0x38, 0x61, 0xb5, 0x1e,
0x71, 0x2d, 0x8f, 0xd6, 0xa4, 0x01, 0xfb, 0x76, 0xcd, 0xf7, 0x1c, 0xbb, 0xdd, 0xaf, 0xf5, 0xee,
0xb4, 0x08, 0xc7, 0x77, 0x6a, 0x1d, 0xe2, 0x12, 0x8a, 0x39, 0xb1, 0x0c, 0x9f, 0x7a, 0xdc, 0x83,
0x37, 0x47, 0x50, 0x03, 0xfb, 0xb6, 0x31, 0x82, 0x1a, 0x12, 0xba, 0xfb, 0x51, 0xc7, 0xe6, 0xe7,
0x41, 0xcb, 0x68, 0x7b, 0xdd, 0x5a, 0xc7, 0xeb, 0x78, 0x35, 0xc1, 0x68, 0x05, 0x67, 0xe2, 0x4b,
0x7c, 0x88, 0x5f, 0x23, 0xa5, 0x5d, 0x7d, 0xc2, 0x69, 0xdb, 0xa3, 0xa4, 0xd6, 0xcb, 0x78, 0xdb,
0xbd, 0x37, 0xc6, 0x74, 0x71, 0xfb, 0xdc, 0x76, 0x09, 0xed, 0xd7, 0xfc, 0x8b, 0x4e, 0x38, 0xc0,
0x6a, 0x5d, 0xc2, 0x71, 0x1e, 0xab, 0x56, 0xc4, 0xa2, 0x81, 0xcb, 0xed, 0x2e, 0xc9, 0x10, 0xee,
0xcf, 0x22, 0xb0, 0xf6, 0x39, 0xe9, 0xe2, 0x0c, 0xef, 0x6e, 0x11, 0x2f, 0xe0, 0xb6, 0x53, 0xb3,
0x5d, 0xce, 0x38, 0x4d, 0x93, 0xf4, 0x7b, 0x60, 0x63, 0xdf, 0x71, 0xbc, 0xaf, 0x88, 0x75, 0xd0,
0x3c, 0xae, 0x53, 0xbb, 0x47, 0x28, 0xbc, 0x05, 0xe6, 0x5d, 0xdc, 0x25, 0xaa, 0x72, 0x4b, 0xb9,
0xbd, 0x6c, 0xae, 0xbe, 0x18, 0x68, 0x73, 0xc3, 0x81, 0x36, 0xff, 0x04, 0x77, 0x09, 0x12, 0x16,
0xfd, 0x53, 0x50, 0x91, 0xac, 0x23, 0x87, 0x5c, 0x7e, 0xe1, 0x39, 0x41, 0x97, 0xc0, 0x1f, 0x83,
0x45, 0x4b, 0x08, 0x48, 0xe2, 0xba, 0x24, 0x2e, 0x8e, 0x64, 0x91, 0xb4, 0xea, 0x0c, 0x5c, 0x97,
0xe4, 0x47, 0x1e, 0xe3, 0x0d, 0xcc, 0xcf, 0xe1, 0x1e, 0x00, 0x3e, 0xe6, 0xe7, 0x0d, 0x4a, 0xce,
0xec, 0x4b, 0x49, 0x87, 0x92, 0x0e, 0x1a, 0xb1, 0x05, 0x4d, 0xa0, 0xe0, 0x87, 0xa0, 0x4c, 0x09,
0xb6, 0x4e, 0x5d, 0xa7, 0xaf, 0x5e, 0xbb, 0xa5, 0xdc, 0x2e, 0x9b, 0x1b, 0x92, 0x51, 0x46, 0x72,
0x1c, 0xc5, 0x08, 0xfd, 0x3f, 0x0a, 0x28, 0x1f, 0xf6, 0xec, 0x36, 0xb7, 0x3d, 0x17, 0xfe, 0x11,
0x94, 0xc3, 0xdd, 0xb2, 0x30, 0xc7, 0xc2, 0xd9, 0xca, 0xde, 0xc7, 0xc6, 0x38, 0x93, 0xe2, 0xe0,
0x19, 0xfe, 0x45, 0x27, 0x1c, 0x60, 0x46, 0x88, 0x36, 0x7a, 0x77, 0x8c, 0xd3, 0xd6, 0x97, 0xa4,
0xcd, 0x4f, 0x08, 0xc7, 0xe3, 0xe9, 0x8d, 0xc7, 0x50, 0xac, 0x0a, 0x1d, 0xb0, 0x66, 0x11, 0x87,
0x70, 0x72, 0xea, 0x87, 0x1e, 0x99, 0x98, 0xe1, 0xca, 0xde, 0xdd, 0xd7, 0x73, 0x53, 0x9f, 0xa4,
0x9a, 0x95, 0xe1, 0x40, 0x5b, 0x4b, 0x0c, 0xa1, 0xa4, 0xb8, 0xfe, 0xb5, 0x02, 0x76, 0x8e, 0x9a,
0x0f, 0xa9, 0x17, 0xf8, 0x4d, 0x1e, 0xee, 0x6e, 0xa7, 0x2f, 0x4d, 0xf0, 0x67, 0x60, 0x9e, 0x06,
0x4e, 0xb4, 0x97, 0xef, 0x47, 0x7b, 0x89, 0x02, 0x87, 0xbc, 0x1a, 0x68, 0x9b, 0x29, 0xd6, 0xd3,
0xbe, 0x4f, 0x90, 0x20, 0xc0, 0xcf, 0xc1, 0x22, 0xc5, 0x6e, 0x87, 0x84, 0x53, 0x2f, 0xdd, 0x5e,
0xd9, 0xd3, 0x8d, 0xc2, 0xb3, 0x66, 0x1c, 0xd7, 0x51, 0x08, 0x1d, 0xef, 0xb8, 0xf8, 0x64, 0x48,
0x2a, 0xe8, 0x27, 0x60, 0x4d, 0x6c, 0xb5, 0x47, 0xb9, 0xb0, 0xc0, 0x77, 0x41, 0xa9, 0x6b, 0xbb,
0x62, 0x52, 0x0b, 0xe6, 0x8a, 0x64, 0x95, 0x4e, 0x6c, 0x17, 0x85, 0xe3, 0xc2, 0x8c, 0x2f, 0x45,
0xcc, 0x26, 0xcd, 0xf8, 0x12, 0x85, 0xe3, 0xfa, 0x43, 0xb0, 0x24, 0x3d, 0x4e, 0x0a, 0x95, 0xa6,
0x0b, 0x95, 0x72, 0x84, 0xfe, 0x71, 0x0d, 0x6c, 0x36, 0x3c, 0xab, 0x6e, 0x33, 0x1a, 0x88, 0x78,
0x99, 0x81, 0xd5, 0x21, 0xfc, 0x2d, 0xe4, 0xc7, 0x53, 0x30, 0xcf, 0x7c, 0xd2, 0x96, 0x69, 0xb1,
0x37, 0x25, 0xb6, 0x39, 0xf3, 0x6b, 0xfa, 0xa4, 0x3d, 0x3e, 0x96, 0xe1, 0x17, 0x12, 0x6a, 0xf0,
0x39, 0x58, 0x64, 0x1c, 0xf3, 0x80, 0xa9, 0x25, 0xa1, 0x7b, 0xef, 0x8a, 0xba, 0x82, 0x3b, 0xde,
0xc5, 0xd1, 0x37, 0x92, 0x9a, 0xfa, 0xbf, 0x15, 0x70, 0x23, 0x87, 0xf5, 0xd8, 0x66, 0x1c, 0x3e,
0xcf, 0x44, 0xcc, 0x78, 0xbd, 0x88, 0x85, 0x6c, 0x11, 0xaf, 0xf8, 0xf0, 0x46, 0x23, 0x13, 0xd1,
0x6a, 0x82, 0x05, 0x9b, 0x93, 0x6e, 0x94, 0x8a, 0xc6, 0xd5, 0x96, 0x65, 0xae, 0x49, 0xe9, 0x85,
0xe3, 0x50, 0x04, 0x8d, 0xb4, 0xf4, 0x6f, 0xaf, 0xe5, 0x2e, 0x27, 0x0c, 0x27, 0x3c, 0x03, 0xab,
0x5d, 0xdb, 0xdd, 0xef, 0x61, 0xdb, 0xc1, 0x2d, 0x79, 0x7a, 0xa6, 0x25, 0x41, 0x58, 0x61, 0x8d,
0x51, 0x85, 0x35, 0x8e, 0x5d, 0x7e, 0x4a, 0x9b, 0x9c, 0xda, 0x6e, 0xc7, 0xdc, 0x18, 0x0e, 0xb4,
0xd5, 0x93, 0x09, 0x25, 0x94, 0xd0, 0x85, 0xbf, 0x07, 0x65, 0x46, 0x1c, 0xd2, 0xe6, 0x1e, 0xbd,
0x5a, 0x85, 0x78, 0x8c, 0x5b, 0xc4, 0x69, 0x4a, 0xaa, 0xb9, 0x1a, 0xc6, 0x2d, 0xfa, 0x42, 0xb1,
0x24, 0x74, 0xc0, 0x7a, 0x17, 0x5f, 0x3e, 0x73, 0x71, 0xbc, 0x90, 0xd2, 0x0f, 0x5c, 0x08, 0x1c,
0x0e, 0xb4, 0xf5, 0x93, 0x84, 0x16, 0x4a, 0x69, 0xeb, 0xc3, 0x79, 0x70, 0xb3, 0x30, 0xab, 0xe0,
0xe7, 0x00, 0x7a, 0x2d, 0x46, 0x68, 0x8f, 0x58, 0x0f, 0x47, 0x77, 0x90, 0xed, 0x45, 0x07, 0x77,
0x57, 0x6e, 0x10, 0x3c, 0xcd, 0x20, 0x50, 0x0e, 0x0b, 0xfe, 0x45, 0x01, 0x6b, 0xd6, 0xc8, 0x0d,
0xb1, 0x1a, 0x9e, 0x15, 0x25, 0xc6, 0xc3, 0x1f, 0x92, 0xef, 0x46, 0x7d, 0x52, 0xe9, 0xd0, 0xe5,
0xb4, 0x6f, 0x6e, 0xcb, 0x09, 0xad, 0x25, 0x6c, 0x28, 0xe9, 0x34, 0x5c, 0x92, 0x15, 0x4b, 0x32,
0x79, 0xa7, 0x89, 0x10, 0x2f, 0x8c, 0x97, 0x54, 0xcf, 0x20, 0x50, 0x0e, 0x0b, 0xfe, 0x0a, 0xac,
0xb7, 0x03, 0x4a, 0x89, 0xcb, 0x1f, 0x11, 0xec, 0xf0, 0xf3, 0xbe, 0x3a, 0x2f, 0x74, 0x76, 0xa4,
0xce, 0xfa, 0x41, 0xc2, 0x8a, 0x52, 0xe8, 0x90, 0x6f, 0x11, 0x66, 0x53, 0x62, 0x45, 0xfc, 0x85,
0x24, 0xbf, 0x9e, 0xb0, 0xa2, 0x14, 0x1a, 0x3e, 0x00, 0xab, 0xe4, 0xd2, 0x27, 0xed, 0x28, 0xa0,
0x8b, 0x82, 0xbd, 0x25, 0xd9, 0xab, 0x87, 0x13, 0x36, 0x94, 0x40, 0xee, 0x3a, 0x00, 0x66, 0x23,
0x08, 0x37, 0x40, 0xe9, 0x82, 0xf4, 0x47, 0xd7, 0x0e, 0x0a, 0x7f, 0xc2, 0xcf, 0xc0, 0x42, 0x0f,
0x3b, 0x01, 0x91, 0x89, 0xfe, 0xc1, 0xeb, 0x25, 0xfa, 0x53, 0xbb, 0x4b, 0xd0, 0x88, 0xf8, 0xf3,
0x6b, 0x0f, 0x14, 0xfd, 0x1b, 0x05, 0x54, 0x1a, 0x9e, 0xd5, 0x24, 0xed, 0x80, 0xda, 0xbc, 0xdf,
0x10, 0x9b, 0xfc, 0x16, 0x0a, 0x36, 0x4a, 0x14, 0xec, 0x8f, 0xa7, 0x27, 0x5a, 0x72, 0x76, 0x45,
0xe5, 0x5a, 0x7f, 0xa1, 0x80, 0xed, 0x0c, 0xfa, 0x2d, 0x94, 0xd3, 0x5f, 0x27, 0xcb, 0xe9, 0x87,
0x57, 0x59, 0x4c, 0x41, 0x31, 0xfd, 0xa6, 0x92, 0xb3, 0x14, 0x51, 0x4a, 0xc3, 0xd6, 0x8e, 0xda,
0x3d, 0xdb, 0x21, 0x1d, 0x62, 0x89, 0xc5, 0x94, 0x27, 0x5a, 0xbb, 0xd8, 0x82, 0x26, 0x50, 0x90,
0x81, 0x1d, 0x8b, 0x9c, 0xe1, 0xc0, 0xe1, 0xfb, 0x96, 0x75, 0x80, 0x7d, 0xdc, 0xb2, 0x1d, 0x9b,
0xdb, 0xb2, 0x17, 0x59, 0x36, 0x3f, 0x1d, 0x0e, 0xb4, 0x9d, 0x7a, 0x2e, 0xe2, 0xd5, 0x40, 0x7b,
0x37, 0xdb, 0xca, 0x1b, 0x31, 0xa4, 0x8f, 0x0a, 0xa4, 0x61, 0x1f, 0xa8, 0x94, 0xfc, 0x29, 0x08,
0x0f, 0x45, 0x9d, 0x7a, 0x7e, 0xc2, 0x6d, 0x49, 0xb8, 0xfd, 0xe5, 0x70, 0xa0, 0xa9, 0xa8, 0x00,
0x33, 0xdb, 0x71, 0xa1, 0x3c, 0xfc, 0x12, 0x6c, 0x62, 0xd9, 0x84, 0x4f, 0x7a, 0x9d, 0x17, 0x5e,
0x1f, 0x0c, 0x07, 0xda, 0xe6, 0x7e, 0xd6, 0x3c, 0xdb, 0x61, 0x9e, 0x28, 0xac, 0x81, 0xa5, 0x9e,
0xe8, 0xd7, 0x99, 0xba, 0x20, 0xf4, 0xb7, 0x87, 0x03, 0x6d, 0x69, 0xd4, 0xc2, 0x87, 0x9a, 0x8b,
0x47, 0x4d, 0xd1, 0x05, 0x46, 0x28, 0xf8, 0x09, 0x58, 0x39, 0xf7, 0x18, 0x7f, 0x42, 0xf8, 0x57,
0x1e, 0xbd, 0x10, 0x85, 0xa1, 0x6c, 0x6e, 0xca, 0x1d, 0x5c, 0x79, 0x34, 0x36, 0xa1, 0x49, 0x1c,
0xfc, 0x2d, 0x58, 0x3e, 0x97, 0x3d, 0x1f, 0x53, 0x97, 0x44, 0xa2, 0xdd, 0x9e, 0x92, 0x68, 0x89,
0xfe, 0xd0, 0xac, 0x48, 0xf9, 0xe5, 0x68, 0x98, 0xa1, 0xb1, 0x1a, 0xfc, 0x09, 0x58, 0x12, 0x1f,
0xc7, 0x75, 0xb5, 0x2c, 0x66, 0x73, 0x5d, 0xc2, 0x97, 0x1e, 0x8d, 0x86, 0x51, 0x64, 0x8f, 0xa0,
0xc7, 0x8d, 0x03, 0x75, 0x39, 0x0b, 0x3d, 0x6e, 0x1c, 0xa0, 0xc8, 0x0e, 0x9f, 0x83, 0x25, 0x46,
0x1e, 0xdb, 0x6e, 0x70, 0xa9, 0x02, 0x71, 0xe4, 0xee, 0x4c, 0x99, 0x6e, 0xf3, 0x50, 0x20, 0x53,
0xdd, 0xf6, 0x58, 0x5d, 0xda, 0x51, 0x24, 0x09, 0x2d, 0xb0, 0x4c, 0x03, 0x77, 0x9f, 0x3d, 0x63,
0x84, 0xaa, 0x2b, 0x99, 0xab, 0x3e, 0xad, 0x8f, 0x22, 0x6c, 0xda, 0x43, 0x1c, 0x99, 0x18, 0x81,
0xc6, 0xc2, 0xd0, 0x02, 0x40, 0x7c, 0x88, 0xa6, 0x5e, 0xdd, 0x99, 0xd9, 0x04, 0xa2, 0x18, 0x9c,
0xf6, 0xb3, 0x1e, 0x1e, 0xcf, 0xb1, 0x19, 0x4d, 0xe8, 0xc2, 0xbf, 0x2a, 0x00, 0xb2, 0xc0, 0xf7,
0x1d, 0xd2, 0x25, 0x2e, 0xc7, 0x8e, 0x18, 0x65, 0xea, 0xaa, 0x70, 0xf7, 0x8b, 0x69, 0x51, 0xcb,
0x90, 0xd2, 0x6e, 0xe3, 0x6b, 0x33, 0x0b, 0x45, 0x39, 0x3e, 0xc3, 0x4d, 0x3b, 0x93, 0xab, 0x5d,
0x9b, 0xb9, 0x69, 0xf9, 0x7f, 0x91, 0xc6, 0x9b, 0x26, 0xed, 0x28, 0x92, 0x84, 0x5f, 0x80, 0x9d,
0xe8, 0x0f, 0x24, 0xf2, 0x3c, 0x7e, 0x64, 0x3b, 0x84, 0xf5, 0x19, 0x27, 0x5d, 0x75, 0x5d, 0x24,
0x53, 0x55, 0x32, 0x77, 0x50, 0x2e, 0x0a, 0x15, 0xb0, 0x61, 0x17, 0x68, 0x51, 0x11, 0x0a, 0x4f,
0x68, 0x5c, 0x05, 0x0f, 0x59, 0x1b, 0x3b, 0xa3, 0xc6, 0xe8, 0xba, 0x70, 0xf0, 0xfe, 0x70, 0xa0,
0x69, 0xf5, 0xe9, 0x50, 0x34, 0x4b, 0x0b, 0xfe, 0x06, 0xa8, 0xb8, 0xc8, 0xcf, 0x86, 0xf0, 0xf3,
0xa3, 0xb0, 0xb2, 0x15, 0x3a, 0x28, 0x64, 0x43, 0x1f, 0x6c, 0xe0, 0xe4, 0x5f, 0x79, 0xa6, 0x56,
0xc4, 0x59, 0xff, 0x60, 0xca, 0x3e, 0xa4, 0xfe, 0xfd, 0x9b, 0xaa, 0x0c, 0xe3, 0x46, 0xca, 0xc0,
0x50, 0x46, 0x1d, 0x5e, 0x02, 0x88, 0xd3, 0x2f, 0x0f, 0x4c, 0x85, 0x33, 0x2f, 0xb2, 0xcc, 0x73,
0xc5, 0x38, 0xd5, 0x32, 0x26, 0x86, 0x72, 0x7c, 0x40, 0x0e, 0x2a, 0x38, 0xf5, 0x52, 0xc2, 0xd4,
0x1b, 0xc2, 0xf1, 0x4f, 0x67, 0x3b, 0x8e, 0x39, 0xe6, 0x4d, 0xe9, 0xb7, 0x92, 0xb6, 0x30, 0x94,
0x75, 0x00, 0x1f, 0x83, 0x2d, 0x39, 0xf8, 0xcc, 0x65, 0xf8, 0x8c, 0x34, 0xfb, 0xac, 0xcd, 0x1d,
0xa6, 0x6e, 0x8a, 0xda, 0xad, 0x0e, 0x07, 0xda, 0xd6, 0x7e, 0x8e, 0x1d, 0xe5, 0xb2, 0xe0, 0x67,
0x60, 0xe3, 0xcc, 0xa3, 0x2d, 0xdb, 0xb2, 0x88, 0x1b, 0x29, 0x6d, 0x09, 0xa5, 0xad, 0x30, 0xfe,
0x47, 0x29, 0x1b, 0xca, 0xa0, 0x21, 0x03, 0xdb, 0x52, 0xb9, 0x41, 0xbd, 0xf6, 0x89, 0x17, 0xb8,
0x3c, 0xbc, 0x2e, 0x98, 0xba, 0x1d, 0x5f, 0x91, 0xdb, 0xfb, 0x79, 0x80, 0x57, 0x03, 0xed, 0x56,
0xce, 0x75, 0x95, 0x00, 0xa1, 0x7c, 0x6d, 0xe8, 0x80, 0x55, 0xf9, 0xf6, 0x75, 0xe0, 0x60, 0xc6,
0x54, 0x55, 0x1c, 0xf5, 0xfb, 0xd3, 0x0b, 0x5b, 0x0c, 0x4f, 0x9f, 0x77, 0xf1, 0xa7, 0x6c, 0x12,
0x80, 0x12, 0xea, 0xfa, 0xdf, 0x15, 0x70, 0xb3, 0xb0, 0x30, 0xc2, 0xfb, 0x89, 0x07, 0x15, 0x3d,
0xf5, 0xa0, 0x02, 0xb3, 0xc4, 0x37, 0xf0, 0x9e, 0xf2, 0xb5, 0x02, 0xd4, 0xa2, 0x1b, 0x02, 0x7e,
0x92, 0x98, 0xe0, 0x7b, 0xa9, 0x09, 0x56, 0x32, 0xbc, 0x37, 0x30, 0xbf, 0x6f, 0x15, 0xf0, 0xce,
0x94, 0x1d, 0x88, 0x0b, 0x12, 0xb1, 0x26, 0x51, 0x4f, 0x70, 0x78, 0x94, 0x15, 0x91, 0x47, 0xe3,
0x82, 0x94, 0x83, 0x41, 0x85, 0x6c, 0xf8, 0x0c, 0xdc, 0x90, 0xd5, 0x30, 0x6d, 0x13, 0x9d, 0xfb,
0xb2, 0xf9, 0xce, 0x70, 0xa0, 0xdd, 0xa8, 0xe7, 0x43, 0x50, 0x11, 0x57, 0xff, 0xa7, 0x02, 0x76,
0xf2, 0xaf, 0x7c, 0x78, 0x37, 0x11, 0x6e, 0x2d, 0x15, 0xee, 0xeb, 0x29, 0x96, 0x0c, 0xf6, 0x1f,
0xc0, 0xba, 0x6c, 0x0c, 0x92, 0xef, 0x83, 0x89, 0xa0, 0x87, 0x47, 0x24, 0xec, 0xe9, 0xa5, 0x44,
0x94, 0xbe, 0xe2, 0xaf, 0x78, 0x72, 0x0c, 0xa5, 0xd4, 0xf4, 0x7f, 0x29, 0xe0, 0xbd, 0x99, 0x97,
0x2d, 0x34, 0x13, 0x53, 0x37, 0x52, 0x53, 0xaf, 0x16, 0x0b, 0xbc, 0x99, 0x67, 0x42, 0xf3, 0xa3,
0x17, 0x2f, 0xab, 0x73, 0xdf, 0xbd, 0xac, 0xce, 0x7d, 0xff, 0xb2, 0x3a, 0xf7, 0xe7, 0x61, 0x55,
0x79, 0x31, 0xac, 0x2a, 0xdf, 0x0d, 0xab, 0xca, 0xf7, 0xc3, 0xaa, 0xf2, 0xdf, 0x61, 0x55, 0xf9,
0xdb, 0xff, 0xaa, 0x73, 0xbf, 0x5b, 0x92, 0x72, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x5d, 0xe0,
0x55, 0x1c, 0x41, 0x18, 0x00, 0x00,
// 1904 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x5b, 0x6f, 0xdc, 0xc6,
0x15, 0x16, 0xbd, 0xba, 0xac, 0x46, 0x17, 0x6b, 0x47, 0x17, 0xd3, 0x4a, 0xb3, 0x74, 0x18, 0xa0,
0x70, 0xd3, 0x84, 0x1b, 0xcb, 0x8e, 0x6b, 0x34, 0x6d, 0x11, 0x51, 0x2b, 0xd9, 0x0a, 0x2c, 0x6b,
0x3b, 0x6b, 0x07, 0x6d, 0xe1, 0x16, 0x9d, 0x25, 0x47, 0x2b, 0x46, 0x5c, 0x92, 0xe5, 0x0c, 0x37,
0xda, 0xb7, 0x3e, 0xf4, 0xa1, 0x8f, 0xfd, 0x03, 0x41, 0x7f, 0x40, 0xd1, 0xa7, 0xfe, 0x88, 0x3a,
0x40, 0x11, 0xe4, 0x31, 0xe8, 0xc3, 0xa2, 0xde, 0xfe, 0x0b, 0x3f, 0x05, 0x9c, 0x1d, 0x72, 0x79,
0xdd, 0xb5, 0x0d, 0xd8, 0x6f, 0xe4, 0x9c, 0xef, 0xfb, 0xce, 0xcc, 0x99, 0x33, 0x67, 0x2e, 0x40,
0xbf, 0xb8, 0x47, 0x35, 0xcb, 0x6d, 0x5c, 0x04, 0x1d, 0xe2, 0x3b, 0x84, 0x11, 0xda, 0xe8, 0x13,
0xc7, 0x74, 0xfd, 0x86, 0x30, 0x60, 0xcf, 0x6a, 0x78, 0xae, 0x6d, 0x19, 0x83, 0x46, 0xff, 0x56,
0x87, 0x30, 0x7c, 0xab, 0xd1, 0x25, 0x0e, 0xf1, 0x31, 0x23, 0xa6, 0xe6, 0xf9, 0x2e, 0x73, 0xe1,
0xf5, 0x31, 0x54, 0xc3, 0x9e, 0xa5, 0x8d, 0xa1, 0x9a, 0x80, 0xee, 0x7e, 0xd4, 0xb5, 0xd8, 0x79,
0xd0, 0xd1, 0x0c, 0xb7, 0xd7, 0xe8, 0xba, 0x5d, 0xb7, 0xc1, 0x19, 0x9d, 0xe0, 0x8c, 0xff, 0xf1,
0x1f, 0xfe, 0x35, 0x56, 0xda, 0x55, 0x13, 0x4e, 0x0d, 0xd7, 0x27, 0x8d, 0x7e, 0xce, 0xdb, 0xee,
0x9d, 0x09, 0xa6, 0x87, 0x8d, 0x73, 0xcb, 0x21, 0xfe, 0xa0, 0xe1, 0x5d, 0x74, 0xc3, 0x06, 0xda,
0xe8, 0x11, 0x86, 0x8b, 0x58, 0x8d, 0x32, 0x96, 0x1f, 0x38, 0xcc, 0xea, 0x91, 0x1c, 0xe1, 0xee,
0x2c, 0x02, 0x35, 0xce, 0x49, 0x0f, 0xe7, 0x78, 0xb7, 0xcb, 0x78, 0x01, 0xb3, 0xec, 0x86, 0xe5,
0x30, 0xca, 0xfc, 0x2c, 0x49, 0xbd, 0x03, 0x36, 0xf6, 0x6d, 0xdb, 0xfd, 0x8a, 0x98, 0x07, 0xed,
0xe3, 0xa6, 0x6f, 0xf5, 0x89, 0x0f, 0x6f, 0x80, 0x79, 0x07, 0xf7, 0x88, 0x2c, 0xdd, 0x90, 0x6e,
0x2e, 0xeb, 0xab, 0xcf, 0x86, 0xca, 0xdc, 0x68, 0xa8, 0xcc, 0x3f, 0xc2, 0x3d, 0x82, 0xb8, 0x45,
0xfd, 0x14, 0xd4, 0x04, 0xeb, 0xc8, 0x26, 0x97, 0x5f, 0xb8, 0x76, 0xd0, 0x23, 0xf0, 0xc7, 0x60,
0xd1, 0xe4, 0x02, 0x82, 0xb8, 0x2e, 0x88, 0x8b, 0x63, 0x59, 0x24, 0xac, 0x2a, 0x05, 0x57, 0x05,
0xf9, 0x81, 0x4b, 0x59, 0x0b, 0xb3, 0x73, 0xb8, 0x07, 0x80, 0x87, 0xd9, 0x79, 0xcb, 0x27, 0x67,
0xd6, 0xa5, 0xa0, 0x43, 0x41, 0x07, 0xad, 0xd8, 0x82, 0x12, 0x28, 0xf8, 0x21, 0xa8, 0xfa, 0x04,
0x9b, 0xa7, 0x8e, 0x3d, 0x90, 0xaf, 0xdc, 0x90, 0x6e, 0x56, 0xf5, 0x0d, 0xc1, 0xa8, 0x22, 0xd1,
0x8e, 0x62, 0x84, 0xfa, 0x5f, 0x09, 0x54, 0x0f, 0xfb, 0x96, 0xc1, 0x2c, 0xd7, 0x81, 0x7f, 0x04,
0xd5, 0x70, 0xb6, 0x4c, 0xcc, 0x30, 0x77, 0xb6, 0xb2, 0xf7, 0xb1, 0x36, 0xc9, 0xa4, 0x38, 0x78,
0x9a, 0x77, 0xd1, 0x0d, 0x1b, 0xa8, 0x16, 0xa2, 0xb5, 0xfe, 0x2d, 0xed, 0xb4, 0xf3, 0x25, 0x31,
0xd8, 0x09, 0x61, 0x78, 0xd2, 0xbd, 0x49, 0x1b, 0x8a, 0x55, 0xa1, 0x0d, 0xd6, 0x4c, 0x62, 0x13,
0x46, 0x4e, 0xbd, 0xd0, 0x23, 0xe5, 0x3d, 0x5c, 0xd9, 0xbb, 0xfd, 0x72, 0x6e, 0x9a, 0x49, 0xaa,
0x5e, 0x1b, 0x0d, 0x95, 0xb5, 0x54, 0x13, 0x4a, 0x8b, 0xab, 0x5f, 0x4b, 0x60, 0xe7, 0xa8, 0x7d,
0xdf, 0x77, 0x03, 0xaf, 0xcd, 0xc2, 0xd9, 0xed, 0x0e, 0x84, 0x09, 0xfe, 0x0c, 0xcc, 0xfb, 0x81,
0x1d, 0xcd, 0xe5, 0xfb, 0xd1, 0x5c, 0xa2, 0xc0, 0x26, 0x2f, 0x86, 0xca, 0x66, 0x86, 0xf5, 0x78,
0xe0, 0x11, 0xc4, 0x09, 0xf0, 0x73, 0xb0, 0xe8, 0x63, 0xa7, 0x4b, 0xc2, 0xae, 0x57, 0x6e, 0xae,
0xec, 0xa9, 0x5a, 0xe9, 0x5a, 0xd3, 0x8e, 0x9b, 0x28, 0x84, 0x4e, 0x66, 0x9c, 0xff, 0x52, 0x24,
0x14, 0xd4, 0x13, 0xb0, 0xc6, 0xa7, 0xda, 0xf5, 0x19, 0xb7, 0xc0, 0x77, 0x41, 0xa5, 0x67, 0x39,
0xbc, 0x53, 0x0b, 0xfa, 0x8a, 0x60, 0x55, 0x4e, 0x2c, 0x07, 0x85, 0xed, 0xdc, 0x8c, 0x2f, 0x79,
0xcc, 0x92, 0x66, 0x7c, 0x89, 0xc2, 0x76, 0xf5, 0x3e, 0x58, 0x12, 0x1e, 0x93, 0x42, 0x95, 0xe9,
0x42, 0x95, 0x02, 0xa1, 0x7f, 0x5c, 0x01, 0x9b, 0x2d, 0xd7, 0x6c, 0x5a, 0xd4, 0x0f, 0x78, 0xbc,
0xf4, 0xc0, 0xec, 0x12, 0xf6, 0x16, 0xf2, 0xe3, 0x31, 0x98, 0xa7, 0x1e, 0x31, 0x44, 0x5a, 0xec,
0x4d, 0x89, 0x6d, 0x41, 0xff, 0xda, 0x1e, 0x31, 0x26, 0xcb, 0x32, 0xfc, 0x43, 0x5c, 0x0d, 0x3e,
0x05, 0x8b, 0x94, 0x61, 0x16, 0x50, 0xb9, 0xc2, 0x75, 0xef, 0xbc, 0xa2, 0x2e, 0xe7, 0x4e, 0x66,
0x71, 0xfc, 0x8f, 0x84, 0xa6, 0xfa, 0x1f, 0x09, 0x5c, 0x2b, 0x60, 0x3d, 0xb4, 0x28, 0x83, 0x4f,
0x73, 0x11, 0xd3, 0x5e, 0x2e, 0x62, 0x21, 0x9b, 0xc7, 0x2b, 0x5e, 0xbc, 0x51, 0x4b, 0x22, 0x5a,
0x6d, 0xb0, 0x60, 0x31, 0xd2, 0x8b, 0x52, 0x51, 0x7b, 0xb5, 0x61, 0xe9, 0x6b, 0x42, 0x7a, 0xe1,
0x38, 0x14, 0x41, 0x63, 0x2d, 0xf5, 0xdb, 0x2b, 0x85, 0xc3, 0x09, 0xc3, 0x09, 0xcf, 0xc0, 0x6a,
0xcf, 0x72, 0xf6, 0xfb, 0xd8, 0xb2, 0x71, 0x47, 0xac, 0x9e, 0x69, 0x49, 0x10, 0x56, 0x58, 0x6d,
0x5c, 0x61, 0xb5, 0x63, 0x87, 0x9d, 0xfa, 0x6d, 0xe6, 0x5b, 0x4e, 0x57, 0xdf, 0x18, 0x0d, 0x95,
0xd5, 0x93, 0x84, 0x12, 0x4a, 0xe9, 0xc2, 0xdf, 0x83, 0x2a, 0x25, 0x36, 0x31, 0x98, 0xeb, 0xbf,
0x5a, 0x85, 0x78, 0x88, 0x3b, 0xc4, 0x6e, 0x0b, 0xaa, 0xbe, 0x1a, 0xc6, 0x2d, 0xfa, 0x43, 0xb1,
0x24, 0xb4, 0xc1, 0x7a, 0x0f, 0x5f, 0x3e, 0x71, 0x70, 0x3c, 0x90, 0xca, 0x6b, 0x0e, 0x04, 0x8e,
0x86, 0xca, 0xfa, 0x49, 0x4a, 0x0b, 0x65, 0xb4, 0xd5, 0x7f, 0x2f, 0x80, 0xeb, 0xa5, 0x59, 0x05,
0x3f, 0x07, 0xd0, 0xed, 0x50, 0xe2, 0xf7, 0x89, 0x79, 0x7f, 0xbc, 0x07, 0x59, 0x6e, 0xb4, 0x70,
0x77, 0xc5, 0x04, 0xc1, 0xd3, 0x1c, 0x02, 0x15, 0xb0, 0xe0, 0x5f, 0x24, 0xb0, 0x66, 0x8e, 0xdd,
0x10, 0xb3, 0xe5, 0x9a, 0x51, 0x62, 0xdc, 0x7f, 0x9d, 0x7c, 0xd7, 0x9a, 0x49, 0xa5, 0x43, 0x87,
0xf9, 0x03, 0x7d, 0x5b, 0x74, 0x68, 0x2d, 0x65, 0x43, 0x69, 0xa7, 0xe1, 0x90, 0xcc, 0x58, 0x92,
0x8a, 0x3d, 0x8d, 0x87, 0x78, 0x61, 0x32, 0xa4, 0x66, 0x0e, 0x81, 0x0a, 0x58, 0xf0, 0x57, 0x60,
0xdd, 0x08, 0x7c, 0x9f, 0x38, 0xec, 0x01, 0xc1, 0x36, 0x3b, 0x1f, 0xc8, 0xf3, 0x5c, 0x67, 0x47,
0xe8, 0xac, 0x1f, 0xa4, 0xac, 0x28, 0x83, 0x0e, 0xf9, 0x26, 0xa1, 0x96, 0x4f, 0xcc, 0x88, 0xbf,
0x90, 0xe6, 0x37, 0x53, 0x56, 0x94, 0x41, 0xc3, 0x7b, 0x60, 0x95, 0x5c, 0x7a, 0xc4, 0x88, 0x02,
0xba, 0xc8, 0xd9, 0x5b, 0x82, 0xbd, 0x7a, 0x98, 0xb0, 0xa1, 0x14, 0x12, 0x1a, 0x00, 0x18, 0xae,
0x63, 0x5a, 0xe3, 0x7d, 0x6e, 0x89, 0x4f, 0x44, 0xe3, 0xe5, 0xb2, 0xf8, 0x20, 0xe2, 0x4d, 0xaa,
0x65, 0xdc, 0x44, 0x51, 0x42, 0x76, 0xd7, 0x06, 0x30, 0x3f, 0x4d, 0x70, 0x03, 0x54, 0x2e, 0xc8,
0x60, 0xbc, 0xb7, 0xa1, 0xf0, 0x13, 0x7e, 0x06, 0x16, 0xfa, 0xd8, 0x0e, 0x88, 0x58, 0x4d, 0x1f,
0xbc, 0x5c, 0x3f, 0x1e, 0x5b, 0x3d, 0x82, 0xc6, 0xc4, 0x9f, 0x5f, 0xb9, 0x27, 0xa9, 0xdf, 0x48,
0xa0, 0xd6, 0x72, 0xcd, 0x36, 0x31, 0x02, 0xdf, 0x62, 0x83, 0x16, 0xcf, 0xa4, 0xb7, 0xb0, 0x2b,
0xa0, 0xd4, 0xae, 0xf0, 0xf1, 0xf4, 0x6c, 0x4e, 0xf7, 0xae, 0x6c, 0x4f, 0x50, 0x9f, 0x49, 0x60,
0x3b, 0x87, 0x7e, 0x0b, 0x35, 0xfb, 0xd7, 0xe9, 0x9a, 0xfd, 0xe1, 0xab, 0x0c, 0xa6, 0xa4, 0x62,
0x7f, 0x53, 0x2b, 0x18, 0x0a, 0xaf, 0xd7, 0xe1, 0xf9, 0xd1, 0xb7, 0xfa, 0x96, 0x4d, 0xba, 0xc4,
0xe4, 0x83, 0xa9, 0x26, 0xce, 0x8f, 0xb1, 0x05, 0x25, 0x50, 0x90, 0x82, 0x1d, 0x93, 0x9c, 0xe1,
0xc0, 0x66, 0xfb, 0xa6, 0x79, 0x80, 0x3d, 0xdc, 0xb1, 0x6c, 0x8b, 0x59, 0xe2, 0xc0, 0xb3, 0xac,
0x7f, 0x3a, 0x1a, 0x2a, 0x3b, 0xcd, 0x42, 0xc4, 0x8b, 0xa1, 0xf2, 0x6e, 0xfe, 0xbe, 0xa0, 0xc5,
0x90, 0x01, 0x2a, 0x91, 0x86, 0x03, 0x20, 0xfb, 0xe4, 0x4f, 0x41, 0xb8, 0xf2, 0x9a, 0xbe, 0xeb,
0xa5, 0xdc, 0x56, 0xb8, 0xdb, 0x5f, 0x8e, 0x86, 0x8a, 0x8c, 0x4a, 0x30, 0xb3, 0x1d, 0x97, 0xca,
0xc3, 0x2f, 0xc1, 0x26, 0x16, 0x27, 0xfd, 0xa4, 0xd7, 0x79, 0xee, 0xf5, 0xde, 0x68, 0xa8, 0x6c,
0xee, 0xe7, 0xcd, 0xb3, 0x1d, 0x16, 0x89, 0xc2, 0x06, 0x58, 0xea, 0xf3, 0x4b, 0x01, 0x95, 0x17,
0xb8, 0xfe, 0xf6, 0x68, 0xa8, 0x2c, 0x8d, 0xef, 0x09, 0xa1, 0xe6, 0xe2, 0x51, 0x9b, 0x1f, 0x35,
0x23, 0x14, 0xfc, 0x04, 0xac, 0x9c, 0xbb, 0x94, 0x3d, 0x22, 0xec, 0x2b, 0xd7, 0xbf, 0xe0, 0xd5,
0xa7, 0xaa, 0x6f, 0x8a, 0x19, 0x5c, 0x79, 0x30, 0x31, 0xa1, 0x24, 0x0e, 0xfe, 0x16, 0x2c, 0x9f,
0x8b, 0x83, 0x65, 0x54, 0x7a, 0x6e, 0x4e, 0x49, 0xb4, 0xd4, 0x21, 0x54, 0xaf, 0x09, 0xf9, 0xe5,
0xa8, 0x99, 0xa2, 0x89, 0x1a, 0xfc, 0x09, 0x58, 0xe2, 0x3f, 0xc7, 0x4d, 0xb9, 0xca, 0x7b, 0x73,
0x55, 0xc0, 0x97, 0x1e, 0x8c, 0x9b, 0x51, 0x64, 0x8f, 0xa0, 0xc7, 0xad, 0x03, 0x79, 0x39, 0x0f,
0x3d, 0x6e, 0x1d, 0xa0, 0xc8, 0x0e, 0x9f, 0x82, 0x25, 0x4a, 0x1e, 0x5a, 0x4e, 0x70, 0x29, 0x03,
0xbe, 0xe4, 0x6e, 0x4d, 0xe9, 0x6e, 0xfb, 0x90, 0x23, 0x33, 0x47, 0xfa, 0x89, 0xba, 0xb0, 0xa3,
0x48, 0x12, 0x9a, 0x60, 0xd9, 0x0f, 0x9c, 0x7d, 0xfa, 0x84, 0x12, 0x5f, 0x5e, 0xc9, 0x9d, 0x27,
0xb2, 0xfa, 0x28, 0xc2, 0x66, 0x3d, 0xc4, 0x91, 0x89, 0x11, 0x68, 0x22, 0x0c, 0x4d, 0x00, 0xf8,
0x0f, 0xbf, 0x39, 0xc8, 0x3b, 0x33, 0x4f, 0x9a, 0x28, 0x06, 0x67, 0xfd, 0xac, 0x87, 0xcb, 0x73,
0x62, 0x46, 0x09, 0x5d, 0xf8, 0x57, 0x09, 0x40, 0x1a, 0x78, 0x9e, 0x4d, 0x7a, 0xc4, 0x61, 0xd8,
0xe6, 0xad, 0x54, 0x5e, 0xe5, 0xee, 0x7e, 0x31, 0x2d, 0x6a, 0x39, 0x52, 0xd6, 0x6d, 0xbc, 0x37,
0xe7, 0xa1, 0xa8, 0xc0, 0x67, 0x38, 0x69, 0x67, 0x62, 0xb4, 0x6b, 0x33, 0x27, 0xad, 0xf8, 0x1e,
0x36, 0x99, 0x34, 0x61, 0x47, 0x91, 0x24, 0xfc, 0x02, 0xec, 0x44, 0xb7, 0x54, 0xe4, 0xba, 0xec,
0xc8, 0xb2, 0x09, 0x1d, 0x50, 0x46, 0x7a, 0xf2, 0x3a, 0x4f, 0xa6, 0xba, 0x60, 0xee, 0xa0, 0x42,
0x14, 0x2a, 0x61, 0xc3, 0x1e, 0x50, 0xa2, 0x22, 0x14, 0xae, 0xd0, 0xb8, 0x0a, 0x1e, 0x52, 0x03,
0xdb, 0xe3, 0xd3, 0xd7, 0x55, 0xee, 0xe0, 0xfd, 0xd1, 0x50, 0x51, 0x9a, 0xd3, 0xa1, 0x68, 0x96,
0x16, 0xfc, 0x0d, 0x90, 0x71, 0x99, 0x9f, 0x0d, 0xee, 0xe7, 0x47, 0x61, 0x65, 0x2b, 0x75, 0x50,
0xca, 0x86, 0x1e, 0xd8, 0xc0, 0xe9, 0xf7, 0x02, 0x2a, 0xd7, 0xf8, 0x5a, 0xff, 0x60, 0xca, 0x3c,
0x64, 0x9e, 0x18, 0x74, 0x59, 0x84, 0x71, 0x23, 0x63, 0xa0, 0x28, 0xa7, 0x0e, 0x2f, 0x01, 0xc4,
0xd9, 0xe7, 0x0d, 0x2a, 0xc3, 0x99, 0x1b, 0x59, 0xee, 0x4d, 0x64, 0x92, 0x6a, 0x39, 0x13, 0x45,
0x05, 0x3e, 0x20, 0x03, 0x35, 0x9c, 0x79, 0x8e, 0xa1, 0xf2, 0x35, 0xee, 0xf8, 0xa7, 0xb3, 0x1d,
0xc7, 0x1c, 0xfd, 0xba, 0xf0, 0x5b, 0xcb, 0x5a, 0x28, 0xca, 0x3b, 0x80, 0x0f, 0xc1, 0x96, 0x68,
0x7c, 0xe2, 0x50, 0x7c, 0x46, 0xda, 0x03, 0x6a, 0x30, 0x9b, 0xca, 0x9b, 0xbc, 0x76, 0xcb, 0xa3,
0xa1, 0xb2, 0xb5, 0x5f, 0x60, 0x47, 0x85, 0x2c, 0xf8, 0x19, 0xd8, 0x38, 0x73, 0xfd, 0x8e, 0x65,
0x9a, 0xc4, 0x89, 0x94, 0xb6, 0xb8, 0xd2, 0x56, 0x18, 0xff, 0xa3, 0x8c, 0x0d, 0xe5, 0xd0, 0x90,
0x82, 0x6d, 0xa1, 0xdc, 0xf2, 0x5d, 0xe3, 0xc4, 0x0d, 0x1c, 0x16, 0x6e, 0x17, 0x54, 0xde, 0x8e,
0xb7, 0xc8, 0xed, 0xfd, 0x22, 0xc0, 0x8b, 0xa1, 0x72, 0xa3, 0x60, 0xbb, 0x4a, 0x81, 0x50, 0xb1,
0x36, 0xb4, 0xc1, 0xaa, 0x78, 0x60, 0x3b, 0xb0, 0x31, 0xa5, 0xb2, 0xcc, 0x97, 0xfa, 0xdd, 0xe9,
0x85, 0x2d, 0x86, 0x67, 0xd7, 0x3b, 0xbf, 0xf9, 0x25, 0x01, 0x28, 0xa5, 0xae, 0xfe, 0x5d, 0x02,
0xd7, 0x4b, 0x0b, 0x23, 0xbc, 0x9b, 0x7a, 0xb5, 0x51, 0x33, 0xaf, 0x36, 0x30, 0x4f, 0x7c, 0x03,
0x8f, 0x36, 0x5f, 0x4b, 0x40, 0x2e, 0xdb, 0x21, 0xe0, 0x27, 0xa9, 0x0e, 0xbe, 0x97, 0xe9, 0x60,
0x2d, 0xc7, 0x7b, 0x03, 0xfd, 0xfb, 0x56, 0x02, 0xef, 0x4c, 0x99, 0x81, 0xb8, 0x20, 0x11, 0x33,
0x89, 0x7a, 0x84, 0xc3, 0xa5, 0x2c, 0xf1, 0x3c, 0x9a, 0x14, 0xa4, 0x02, 0x0c, 0x2a, 0x65, 0xc3,
0x27, 0xe0, 0x9a, 0xa8, 0x86, 0x59, 0x1b, 0x3f, 0xb9, 0x2f, 0xeb, 0xef, 0x8c, 0x86, 0xca, 0xb5,
0x66, 0x31, 0x04, 0x95, 0x71, 0xd5, 0x7f, 0x4a, 0x60, 0xa7, 0x78, 0xcb, 0x87, 0xb7, 0x53, 0xe1,
0x56, 0x32, 0xe1, 0xbe, 0x9a, 0x61, 0x89, 0x60, 0xff, 0x01, 0xac, 0x8b, 0x83, 0x41, 0xfa, 0x11,
0x32, 0x15, 0xf4, 0x70, 0x89, 0x84, 0x67, 0x7a, 0x21, 0x11, 0xa5, 0x2f, 0xbf, 0xef, 0xa7, 0xdb,
0x50, 0x46, 0x4d, 0xfd, 0x97, 0x04, 0xde, 0x9b, 0xb9, 0xd9, 0x42, 0x3d, 0xd5, 0x75, 0x2d, 0xd3,
0xf5, 0x7a, 0xb9, 0xc0, 0x9b, 0x79, 0x8b, 0xd4, 0x3f, 0x7a, 0xf6, 0xbc, 0x3e, 0xf7, 0xdd, 0xf3,
0xfa, 0xdc, 0xf7, 0xcf, 0xeb, 0x73, 0x7f, 0x1e, 0xd5, 0xa5, 0x67, 0xa3, 0xba, 0xf4, 0xdd, 0xa8,
0x2e, 0x7d, 0x3f, 0xaa, 0x4b, 0xff, 0x1b, 0xd5, 0xa5, 0xbf, 0xfd, 0xbf, 0x3e, 0xf7, 0xbb, 0x25,
0x21, 0xf7, 0x43, 0x00, 0x00, 0x00, 0xff, 0xff, 0xde, 0x4e, 0x7c, 0x8c, 0xa6, 0x18, 0x00, 0x00,
}
func (m *AllowedCSIDriver) Marshal() (dAtA []byte, err error) {
@ -1146,6 +1147,20 @@ func (m *PodDisruptionBudgetStatus) MarshalToSizedBuffer(dAtA []byte) (int, erro
_ = i
var l int
_ = l
if len(m.Conditions) > 0 {
for iNdEx := len(m.Conditions) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Conditions[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x3a
}
}
i = encodeVarintGenerated(dAtA, i, uint64(m.ExpectedPods))
i--
dAtA[i] = 0x30
@ -1944,6 +1959,12 @@ func (m *PodDisruptionBudgetStatus) Size() (n int) {
n += 1 + sovGenerated(uint64(m.CurrentHealthy))
n += 1 + sovGenerated(uint64(m.DesiredHealthy))
n += 1 + sovGenerated(uint64(m.ExpectedPods))
if len(m.Conditions) > 0 {
for _, e := range m.Conditions {
l = e.Size()
n += 1 + l + sovGenerated(uint64(l))
}
}
return n
}
@ -2294,6 +2315,11 @@ func (this *PodDisruptionBudgetStatus) String() string {
if this == nil {
return "nil"
}
repeatedStringForConditions := "[]Condition{"
for _, f := range this.Conditions {
repeatedStringForConditions += fmt.Sprintf("%v", f) + ","
}
repeatedStringForConditions += "}"
keysForDisruptedPods := make([]string, 0, len(this.DisruptedPods))
for k := range this.DisruptedPods {
keysForDisruptedPods = append(keysForDisruptedPods, k)
@ -2311,6 +2337,7 @@ func (this *PodDisruptionBudgetStatus) String() string {
`CurrentHealthy:` + fmt.Sprintf("%v", this.CurrentHealthy) + `,`,
`DesiredHealthy:` + fmt.Sprintf("%v", this.DesiredHealthy) + `,`,
`ExpectedPods:` + fmt.Sprintf("%v", this.ExpectedPods) + `,`,
`Conditions:` + repeatedStringForConditions + `,`,
`}`,
}, "")
return s
@ -3827,6 +3854,40 @@ func (m *PodDisruptionBudgetStatus) Unmarshal(dAtA []byte) error {
break
}
}
case 7:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Conditions", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Conditions = append(m.Conditions, v1.Condition{})
if err := m.Conditions[len(m.Conditions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])

View File

@ -180,6 +180,26 @@ message PodDisruptionBudgetStatus {
// total number of pods counted by this disruption budget
optional int32 expectedPods = 6;
// Conditions contain conditions for PDB. The disruption controller sets the
// DisruptionAllowed condition. The following are known values for the reason field
// (additional reasons could be added in the future):
// - SyncFailed: The controller encountered an error and wasn't able to compute
// the number of allowed disruptions. Therefore no disruptions are
// allowed and the status of the condition will be False.
// - InsufficientPods: The number of pods are either at or below the number
// required by the PodDisruptionBudget. No disruptions are
// allowed and the status of the condition will be False.
// - SufficientPods: There are more pods than required by the PodDisruptionBudget.
// The condition will be True, and the number of allowed
// disruptions are provided by the disruptionsAllowed property.
//
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
repeated k8s.io.apimachinery.pkg.apis.meta.v1.Condition conditions = 7;
}
// PodSecurityPolicy governs the ability to make requests that affect the Security Context

View File

@ -77,8 +77,44 @@ type PodDisruptionBudgetStatus struct {
// total number of pods counted by this disruption budget
ExpectedPods int32 `json:"expectedPods" protobuf:"varint,6,opt,name=expectedPods"`
// Conditions contain conditions for PDB. The disruption controller sets the
// DisruptionAllowed condition. The following are known values for the reason field
// (additional reasons could be added in the future):
// - SyncFailed: The controller encountered an error and wasn't able to compute
// the number of allowed disruptions. Therefore no disruptions are
// allowed and the status of the condition will be False.
// - InsufficientPods: The number of pods are either at or below the number
// required by the PodDisruptionBudget. No disruptions are
// allowed and the status of the condition will be False.
// - SufficientPods: There are more pods than required by the PodDisruptionBudget.
// The condition will be True, and the number of allowed
// disruptions are provided by the disruptionsAllowed property.
//
// +optional
// +patchMergeKey=type
// +patchStrategy=merge
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,7,rep,name=conditions"`
}
const (
// DisruptionAllowedCondition is a condition set by the disruption controller
// that signal whether any of the pods covered by the PDB can be disrupted.
DisruptionAllowedCondition = "DisruptionAllowed"
// SyncFailedReason is set on the DisruptionAllowed condition if reconcile
// of the PDB failed and therefore disruption of pods are not allowed.
SyncFailedReason = "SyncFailed"
// SufficientPodsReason is set on the DisruptionAllowed condition if there are
// more pods covered by the PDB than required and at least one can be disrupted.
SufficientPodsReason = "SufficientPods"
// InsufficientPodsReason is set on the DisruptionAllowed condition if the number
// of pods are equal to or fewer than required by the PDB.
InsufficientPodsReason = "InsufficientPods"
)
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:prerelease-lifecycle-gen:introduced=1.5

View File

@ -132,6 +132,7 @@ var map_PodDisruptionBudgetStatus = map[string]string{
"currentHealthy": "current number of healthy pods",
"desiredHealthy": "minimum desired number of healthy pods",
"expectedPods": "total number of pods counted by this disruption budget",
"conditions": "Conditions contain conditions for PDB. The disruption controller sets the DisruptionAllowed condition. The following are known values for the reason field (additional reasons could be added in the future): - SyncFailed: The controller encountered an error and wasn't able to compute\n the number of allowed disruptions. Therefore no disruptions are\n allowed and the status of the condition will be False.\n- InsufficientPods: The number of pods are either at or below the number\n required by the PodDisruptionBudget. No disruptions are\n allowed and the status of the condition will be False.\n- SufficientPods: There are more pods than required by the PodDisruptionBudget.\n The condition will be True, and the number of allowed\n disruptions are provided by the disruptionsAllowed property.",
}
func (PodDisruptionBudgetStatus) SwaggerDoc() map[string]string {

View File

@ -261,6 +261,13 @@ func (in *PodDisruptionBudgetStatus) DeepCopyInto(out *PodDisruptionBudgetStatus
(*out)[key] = *val.DeepCopy()
}
}
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}

View File

@ -66,6 +66,16 @@
"disruptionsAllowed": -1942073618,
"currentHealthy": -2037845840,
"desiredHealthy": -1965578645,
"expectedPods": -347579237
"expectedPods": -347579237,
"conditions": [
{
"type": "26",
"status": "蓿彭聡A3fƻfʣ繡楙¯ĦE勗E濞",
"observedGeneration": -8886126363972008903,
"lastTransitionTime": "2544-09-25T10:59:09Z",
"reason": "27",
"message": "28"
}
]
}
}

View File

@ -41,6 +41,13 @@ spec:
matchLabels:
8---jop9641lg.p-g8c2-k-912e5-c-e63-n-3n/E9.8ThjT9s-j41-0-6p-JFHn7y-74.-0MUORQQ.N2.3: 68._bQw.-dG6c-.6--_x.--0wmZk1_8._3s_-_Bq.m_4
status:
conditions:
- lastTransitionTime: "2544-09-25T10:59:09Z"
message: "28"
observedGeneration: -8886126363972008903
reason: "27"
status: 蓿彭聡A3fƻfʣ繡楙¯ĦE勗E濞
type: "26"
currentHealthy: -2037845840
desiredHealthy: -1965578645
disruptedPods:

View File

@ -20,17 +20,19 @@ package v1beta1
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1 "k8s.io/client-go/applyconfigurations/meta/v1"
)
// PodDisruptionBudgetStatusApplyConfiguration represents an declarative configuration of the PodDisruptionBudgetStatus type for use
// with apply.
type PodDisruptionBudgetStatusApplyConfiguration struct {
ObservedGeneration *int64 `json:"observedGeneration,omitempty"`
DisruptedPods map[string]v1.Time `json:"disruptedPods,omitempty"`
DisruptionsAllowed *int32 `json:"disruptionsAllowed,omitempty"`
CurrentHealthy *int32 `json:"currentHealthy,omitempty"`
DesiredHealthy *int32 `json:"desiredHealthy,omitempty"`
ExpectedPods *int32 `json:"expectedPods,omitempty"`
ObservedGeneration *int64 `json:"observedGeneration,omitempty"`
DisruptedPods map[string]v1.Time `json:"disruptedPods,omitempty"`
DisruptionsAllowed *int32 `json:"disruptionsAllowed,omitempty"`
CurrentHealthy *int32 `json:"currentHealthy,omitempty"`
DesiredHealthy *int32 `json:"desiredHealthy,omitempty"`
ExpectedPods *int32 `json:"expectedPods,omitempty"`
Conditions []metav1.ConditionApplyConfiguration `json:"conditions,omitempty"`
}
// PodDisruptionBudgetStatusApplyConfiguration constructs an declarative configuration of the PodDisruptionBudgetStatus type for use with
@ -92,3 +94,16 @@ func (b *PodDisruptionBudgetStatusApplyConfiguration) WithExpectedPods(value int
b.ExpectedPods = &value
return b
}
// WithConditions adds the given value to the Conditions field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Conditions field.
func (b *PodDisruptionBudgetStatusApplyConfiguration) WithConditions(values ...*metav1.ConditionApplyConfiguration) *PodDisruptionBudgetStatusApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithConditions")
}
b.Conditions = append(b.Conditions, *values[i])
}
return b
}

View File

@ -0,0 +1,8 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- sig-apps-api-approvers
reviewers:
- sig-apps-api-reviewers
labels:
- sig/apps

View File

@ -0,0 +1,65 @@
/*
Copyright 2021 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 poddisruptionbudget
import (
policy "k8s.io/api/policy/v1beta1"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// UpdateDisruptionAllowedCondition updates the DisruptionAllowed condition
// on a PodDisruptionBudget based on the value of the DisruptionsAllowed field.
func UpdateDisruptionAllowedCondition(pdb *policy.PodDisruptionBudget) {
if pdb.Status.Conditions == nil {
pdb.Status.Conditions = make([]metav1.Condition, 0)
}
if pdb.Status.DisruptionsAllowed > 0 {
apimeta.SetStatusCondition(&pdb.Status.Conditions, metav1.Condition{
Type: policy.DisruptionAllowedCondition,
Reason: policy.SufficientPodsReason,
Status: metav1.ConditionTrue,
ObservedGeneration: pdb.Status.ObservedGeneration,
})
} else {
apimeta.SetStatusCondition(&pdb.Status.Conditions, metav1.Condition{
Type: policy.DisruptionAllowedCondition,
Reason: policy.InsufficientPodsReason,
Status: metav1.ConditionFalse,
ObservedGeneration: pdb.Status.ObservedGeneration,
})
}
}
// ConditionsAreUpToDate checks whether the status and reason for the
// DisruptionAllowed condition are set to the correct values based on the
// DisruptionsAllowed field.
func ConditionsAreUpToDate(pdb *policy.PodDisruptionBudget) bool {
cond := apimeta.FindStatusCondition(pdb.Status.Conditions, policy.DisruptionAllowedCondition)
if cond == nil {
return false
}
if pdb.Status.ObservedGeneration != pdb.Generation {
return false
}
if pdb.Status.DisruptionsAllowed > 0 {
return cond.Status == metav1.ConditionTrue && cond.Reason == policy.SufficientPodsReason
}
return cond.Status == metav1.ConditionFalse && cond.Reason == policy.InsufficientPodsReason
}

1
vendor/modules.txt vendored
View File

@ -2241,6 +2241,7 @@ k8s.io/component-base/version/verflag
## explicit
# k8s.io/component-helpers => ./staging/src/k8s.io/component-helpers
k8s.io/component-helpers/apimachinery/lease
k8s.io/component-helpers/apps/poddisruptionbudget
k8s.io/component-helpers/auth/rbac/reconciliation
k8s.io/component-helpers/auth/rbac/validation
k8s.io/component-helpers/scheduling/corev1