From 9ea97c6e5f3232e5e3aaf4d01be538ddd6ed6bb9 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Sat, 24 Jul 2021 07:39:00 -0400 Subject: [PATCH 01/20] feat: add support for Graphite metrics provider Address issue #1403 by adding support for a Graphite metrics provider. Signed-off-by: Mike Ball --- metricproviders/graphite/graphite.go | 222 +++++++++++++++++++ metricproviders/graphite/graphite_test.go | 25 +++ metricproviders/metricproviders.go | 3 + pkg/apis/rollouts/v1alpha1/analysis_types.go | 10 + 4 files changed, 260 insertions(+) create mode 100644 metricproviders/graphite/graphite.go create mode 100644 metricproviders/graphite/graphite_test.go diff --git a/metricproviders/graphite/graphite.go b/metricproviders/graphite/graphite.go new file mode 100644 index 0000000000..8a11af5868 --- /dev/null +++ b/metricproviders/graphite/graphite.go @@ -0,0 +1,222 @@ +package graphite + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "math" + "net/http" + "net/url" + "path" + "regexp" + "strconv" + "time" + + log "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "github.com/argoproj/argo-rollouts/utils/evaluate" + metricutil "github.com/argoproj/argo-rollouts/utils/metric" +) + +const ( + // ProviderType indicates the provider is Graphite. + ProviderType = "Graphite" +) + +type graphiteDataPoint struct { + Value *float64 + TimeStamp time.Time +} + +func (gdp *graphiteDataPoint) UnmarshalJSON(data []byte) error { + var v []interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + if len(v) != 2 { + return fmt.Errorf("error unmarshaling data point: %v", v) + } + + switch v[0].(type) { + case nil: + // no value + case float64: + f, _ := v[0].(float64) + gdp.Value = &f + case string: + f, err := strconv.ParseFloat(v[0].(string), 64) + if err != nil { + return err + } + gdp.Value = &f + default: + f, ok := v[0].(float64) + if !ok { + return fmt.Errorf("error unmarshaling value: %v", v[0]) + } + gdp.Value = &f + } + + switch v[1].(type) { + case nil: + // no value + case float64: + ts := int64(math.Round(v[1].(float64))) + gdp.TimeStamp = time.Unix(ts, 0) + case string: + ts, err := strconv.ParseInt(v[1].(string), 10, 64) + if err != nil { + return err + } + gdp.TimeStamp = time.Unix(ts, 0) + default: + ts, ok := v[1].(int64) + if !ok { + return fmt.Errorf("error unmarshaling timestamp: %v", v[0]) + } + gdp.TimeStamp = time.Unix(ts, 0) + } + + return nil +} + +type graphiteTargetResp struct { + Target string `json:"target"` + DataPoints []graphiteDataPoint `json:"datapoints"` +} + +type graphiteResponse []graphiteTargetResp + +// Provider contains the required components to run a Graphite query. +// TODO: add support for username/password authentication. +type Provider struct { + url url.URL + client *http.Client + timeout time.Duration + logCtx log.Entry +} + +// Type indicates provider is a Graphite provider. +func (p *Provider) Type() string { + return ProviderType +} + +// Run queries Graphite for the metric. +func (p *Provider) Run(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alpha1.Measurement { + startTime := metav1.Now() + newMeasurement := v1alpha1.Measurement{ + StartedAt: &startTime, + } + + query := p.trimQuery(metric.Provider.Graphite.Query) + u, err := url.Parse(fmt.Sprintf("./render?%s", query)) + if err != nil { + return metricutil.MarkMeasurementError(newMeasurement, err) + } + + q := u.Query() + q.Set("format", "json") + u.RawQuery = q.Encode() + + u.Path = path.Join(p.url.Path, u.Path) + u = p.url.ResolveReference(u) + + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return metricutil.MarkMeasurementError(newMeasurement, err) + } + + // TODO: make timeout configurable + ctx, cancel := context.WithTimeout(req.Context(), p.timeout) + defer cancel() + + r, err := p.client.Do(req.WithContext(ctx)) + if err != nil { + return metricutil.MarkMeasurementError(newMeasurement, err) + } + defer r.Body.Close() + + b, err := ioutil.ReadAll(r.Body) + if err != nil { + return metricutil.MarkMeasurementError(newMeasurement, err) + } + + if 400 <= r.StatusCode { + return metricutil.MarkMeasurementError(newMeasurement, err) + } + + var result graphiteResponse + err = json.Unmarshal(b, &result) + if err != nil { + return metricutil.MarkMeasurementError(newMeasurement, err) + } + + var value *float64 + for _, tr := range result { + for _, dp := range tr.DataPoints { + if dp.Value != nil { + value = dp.Value + } + } + } + + if value == nil { + return metricutil.MarkMeasurementError(newMeasurement, errors.New("no values found")) + } + + newMeasurement.Value = fmt.Sprintf("%f", *value) + + newStatus, err := evaluate.EvaluateResult(value, metric, p.logCtx) + if err != nil { + return metricutil.MarkMeasurementError(newMeasurement, err) + } + + newMeasurement.Phase = newStatus + finishedTime := metav1.Now() + newMeasurement.FinishedAt = &finishedTime + + return newMeasurement +} + +// Resume should not be used with the Graphite provider since all the work should occur in the Run method +func (p *Provider) Resume(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { + p.logCtx.Warn("Graphite provider should not execute the Resume method") + return measurement +} + +// Terminate should not be used with the Graphite provider since all the work should occur in the Run method +func (p *Provider) Terminate(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { + p.logCtx.Warn("Graphite provider should not execute the Terminate method") + return measurement +} + +// GarbageCollect is a no-op for the prometheus provider +func (p *Provider) GarbageCollect(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, limit int) error { + return nil +} + +func (p *Provider) trimQuery(q string) string { + space := regexp.MustCompile(`\s+`) + return space.ReplaceAllString(q, " ") +} + +// NewGraphiteProvider returns a new Graphite provider +func NewGraphiteProvider(metric v1alpha1.Metric, logCtx log.Entry) (*Provider, error) { + addr := metric.Provider.Graphite.Address + graphiteURL, err := url.Parse(addr) + if addr == "" || err != nil { + return nil, fmt.Errorf("%s address %s is not a valid URL", ProviderType, addr) + } + + return &Provider{ + logCtx: logCtx, + client: http.DefaultClient, + url: *graphiteURL, + timeout: 5 * time.Second, + }, nil +} diff --git a/metricproviders/graphite/graphite_test.go b/metricproviders/graphite/graphite_test.go new file mode 100644 index 0000000000..daefe8093d --- /dev/null +++ b/metricproviders/graphite/graphite_test.go @@ -0,0 +1,25 @@ +package graphite + +import ( + "testing" + + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" +) + +func TestType_withValidURL(t *testing.T) { + e := log.Entry{} + m := v1alpha1.Metric{ + Provider: v1alpha1.MetricProvider{ + Graphite: &v1alpha1.GraphiteMetric{ + Address: "http://some-graphite.foo", + }, + }, + } + g, err := NewGraphiteProvider(m, e) + + assert.Nil(t, err) + assert.Equal(t, ProviderType, g.Type()) +} diff --git a/metricproviders/metricproviders.go b/metricproviders/metricproviders.go index 11df714b23..c8e6fa7ec1 100644 --- a/metricproviders/metricproviders.go +++ b/metricproviders/metricproviders.go @@ -3,6 +3,7 @@ package metricproviders import ( "fmt" + "github.com/argoproj/argo-rollouts/metricproviders/graphite" "github.com/argoproj/argo-rollouts/metricproviders/newrelic" "github.com/argoproj/argo-rollouts/metricproviders/wavefront" @@ -77,6 +78,8 @@ func (f *ProviderFactory) NewProvider(logCtx log.Entry, metric v1alpha1.Metric) return nil, err } return newrelic.NewNewRelicProvider(client, logCtx), nil + case graphite.ProviderType: + return graphite.NewGraphiteProvider(metric, logCtx) default: return nil, fmt.Errorf("no valid provider in metric '%s'", metric.Name) } diff --git a/pkg/apis/rollouts/v1alpha1/analysis_types.go b/pkg/apis/rollouts/v1alpha1/analysis_types.go index 7144824155..aa3d893030 100644 --- a/pkg/apis/rollouts/v1alpha1/analysis_types.go +++ b/pkg/apis/rollouts/v1alpha1/analysis_types.go @@ -139,6 +139,8 @@ type MetricProvider struct { NewRelic *NewRelicMetric `json:"newRelic,omitempty" protobuf:"bytes,6,opt,name=newRelic"` // Job specifies the job metric run Job *JobMetric `json:"job,omitempty" protobuf:"bytes,7,opt,name=job"` + // Graphite specifies the Graphite metric to query + Graphite *GraphiteMetric `json:"graphite,omitempty" protobuf:"bytes,1,opt,name=graphite"` } // AnalysisPhase is the overall phase of an AnalysisRun, MetricResult, or Measurement @@ -193,6 +195,14 @@ type JobMetric struct { Spec batchv1.JobSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"` } +// GraphiteMetric defines the Graphite query to perform canary analysis +type GraphiteMetric struct { + // Address is the HTTP address and port of the Graphite server + Address string `json:"address,omitempty" protobuf:"bytes,1,opt,name=address"` + // Query is a raw Graphite query to perform + Query string `json:"query,omitempty" protobuf:"bytes,2,opt,name=query"` +} + // AnalysisRun is an instantiation of an AnalysisTemplate // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object From d4b4ba69758c61d60e48f1c0288ee7bb336fa451 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Mon, 26 Jul 2021 08:34:04 -0400 Subject: [PATCH 02/20] improve testability via API interface and mock Signed-off-by: Mike Ball --- metricproviders/graphite/api.go | 156 ++++++++++++++++++++ metricproviders/graphite/api_test.go | 36 +++++ metricproviders/graphite/graphite.go | 164 +++------------------- metricproviders/graphite/graphite_test.go | 30 +++- metricproviders/graphite/mock_test.go | 13 ++ metricproviders/metricproviders.go | 6 +- 6 files changed, 256 insertions(+), 149 deletions(-) create mode 100644 metricproviders/graphite/api.go create mode 100644 metricproviders/graphite/api_test.go create mode 100644 metricproviders/graphite/mock_test.go diff --git a/metricproviders/graphite/api.go b/metricproviders/graphite/api.go new file mode 100644 index 0000000000..7c35d416e1 --- /dev/null +++ b/metricproviders/graphite/api.go @@ -0,0 +1,156 @@ +package graphite + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "math" + "net/http" + "net/url" + "path" + "regexp" + "strconv" + "time" + + log "github.com/sirupsen/logrus" +) + +// API represents a Graphite API client +type API interface { + Query(query string) (*float64, error) +} + +// GraphiteAPI is a Graphite API client +type APIClient struct { + url url.URL + client *http.Client + timeout time.Duration + logCTX log.Entry +} + +// Query performs a Graphite API query with the query it's passed +func (api APIClient) Query(quer string) (*float64, error) { + query := api.trimQuery(quer) + u, err := url.Parse(fmt.Sprintf("./render?%s", query)) + if err != nil { + return nil, err + } + + q := u.Query() + q.Set("format", "json") + u.RawQuery = q.Encode() + + u.Path = path.Join(api.url.Path, u.Path) + u = api.url.ResolveReference(u) + + req, err := http.NewRequest("GET", u.String(), nil) + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(req.Context(), api.timeout) + defer cancel() + + r, err := api.client.Do(req.WithContext(ctx)) + if err != nil { + return nil, err + } + defer r.Body.Close() + + b, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + + if 400 <= r.StatusCode { + return nil, fmt.Errorf("error response: %s", string(b)) + } + + var result graphiteResponse + err = json.Unmarshal(b, &result) + if err != nil { + return nil, err + } + + var value *float64 + for _, tr := range result { + for _, dp := range tr.DataPoints { + if dp.Value != nil { + value = dp.Value + } + } + } + + return value, nil +} + +func (api APIClient) trimQuery(q string) string { + space := regexp.MustCompile(`\s+`) + return space.ReplaceAllString(q, " ") +} + +type graphiteDataPoint struct { + Value *float64 + TimeStamp time.Time +} + +func (gdp *graphiteDataPoint) UnmarshalJSON(data []byte) error { + var v []interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + + if len(v) != 2 { + return fmt.Errorf("error unmarshaling data point: %v", v) + } + + switch v[0].(type) { + case nil: + // no value + case float64: + f, _ := v[0].(float64) + gdp.Value = &f + case string: + f, err := strconv.ParseFloat(v[0].(string), 64) + if err != nil { + return err + } + gdp.Value = &f + default: + f, ok := v[0].(float64) + if !ok { + return fmt.Errorf("error unmarshaling value: %v", v[0]) + } + gdp.Value = &f + } + + switch v[1].(type) { + case nil: + // no value + case float64: + ts := int64(math.Round(v[1].(float64))) + gdp.TimeStamp = time.Unix(ts, 0) + case string: + ts, err := strconv.ParseInt(v[1].(string), 10, 64) + if err != nil { + return err + } + gdp.TimeStamp = time.Unix(ts, 0) + default: + ts, ok := v[1].(int64) + if !ok { + return fmt.Errorf("error unmarshaling timestamp: %v", v[0]) + } + gdp.TimeStamp = time.Unix(ts, 0) + } + + return nil +} + +type graphiteTargetResp struct { + Target string `json:"target"` + DataPoints []graphiteDataPoint `json:"datapoints"` +} + +type graphiteResponse []graphiteTargetResp diff --git a/metricproviders/graphite/api_test.go b/metricproviders/graphite/api_test.go new file mode 100644 index 0000000000..66dbb3c7cf --- /dev/null +++ b/metricproviders/graphite/api_test.go @@ -0,0 +1,36 @@ +package graphite + +import ( + "fmt" + "testing" + + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func testGraphiteMetric(addr string) v1alpha1.Metric { + return v1alpha1.Metric{ + Provider: v1alpha1.MetricProvider{ + Graphite: &v1alpha1.GraphiteMetric{ + Address: addr, + }, + }, + } +} + +func TestNewAPIClientWithValidURL(t *testing.T) { + e := log.Entry{} + _, err := NewAPIClient(testGraphiteMetric("http://some-graphite.foo"), e) + + assert.NoError(t, err) +} + +func TestNewAPIWithInvalidURL(t *testing.T) { + addr := ":::" + e := log.Entry{} + g, err := NewAPIClient(testGraphiteMetric(addr), e) + + assert.Equal(t, err.Error(), fmt.Sprintf("Graphite address %s is not a valid URL", addr)) + assert.Nil(t, g) +} diff --git a/metricproviders/graphite/graphite.go b/metricproviders/graphite/graphite.go index 8a11af5868..5b809472dd 100644 --- a/metricproviders/graphite/graphite.go +++ b/metricproviders/graphite/graphite.go @@ -1,17 +1,10 @@ package graphite import ( - "context" - "encoding/json" "errors" "fmt" - "io/ioutil" - "math" "net/http" "net/url" - "path" - "regexp" - "strconv" "time" log "github.com/sirupsen/logrus" @@ -27,78 +20,27 @@ const ( ProviderType = "Graphite" ) -type graphiteDataPoint struct { - Value *float64 - TimeStamp time.Time -} - -func (gdp *graphiteDataPoint) UnmarshalJSON(data []byte) error { - var v []interface{} - if err := json.Unmarshal(data, &v); err != nil { - return err - } - - if len(v) != 2 { - return fmt.Errorf("error unmarshaling data point: %v", v) - } - - switch v[0].(type) { - case nil: - // no value - case float64: - f, _ := v[0].(float64) - gdp.Value = &f - case string: - f, err := strconv.ParseFloat(v[0].(string), 64) - if err != nil { - return err - } - gdp.Value = &f - default: - f, ok := v[0].(float64) - if !ok { - return fmt.Errorf("error unmarshaling value: %v", v[0]) - } - gdp.Value = &f - } - - switch v[1].(type) { - case nil: - // no value - case float64: - ts := int64(math.Round(v[1].(float64))) - gdp.TimeStamp = time.Unix(ts, 0) - case string: - ts, err := strconv.ParseInt(v[1].(string), 10, 64) - if err != nil { - return err - } - gdp.TimeStamp = time.Unix(ts, 0) - default: - ts, ok := v[1].(int64) - if !ok { - return fmt.Errorf("error unmarshaling timestamp: %v", v[0]) - } - gdp.TimeStamp = time.Unix(ts, 0) +// NewAPIClient generates a APIClient from the metric configuration +func NewAPIClient(metric v1alpha1.Metric, logCTX log.Entry) (*APIClient, error) { + addr := metric.Provider.Graphite.Address + graphiteURL, err := url.Parse(addr) + if addr == "" || err != nil { + return nil, fmt.Errorf("%s address %s is not a valid URL", ProviderType, addr) } - return nil -} - -type graphiteTargetResp struct { - Target string `json:"target"` - DataPoints []graphiteDataPoint `json:"datapoints"` + return &APIClient{ + logCTX: logCTX, + client: http.DefaultClient, + url: *graphiteURL, + timeout: 5 * time.Second, + }, nil } -type graphiteResponse []graphiteTargetResp - // Provider contains the required components to run a Graphite query. // TODO: add support for username/password authentication. type Provider struct { - url url.URL - client *http.Client - timeout time.Duration - logCtx log.Entry + api API + logCtx log.Entry } // Type indicates provider is a Graphite provider. @@ -113,65 +55,14 @@ func (p *Provider) Run(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alph StartedAt: &startTime, } - query := p.trimQuery(metric.Provider.Graphite.Query) - u, err := url.Parse(fmt.Sprintf("./render?%s", query)) - if err != nil { - return metricutil.MarkMeasurementError(newMeasurement, err) - } - - q := u.Query() - q.Set("format", "json") - u.RawQuery = q.Encode() - - u.Path = path.Join(p.url.Path, u.Path) - u = p.url.ResolveReference(u) - - req, err := http.NewRequest("GET", u.String(), nil) - if err != nil { - return metricutil.MarkMeasurementError(newMeasurement, err) - } - - // TODO: make timeout configurable - ctx, cancel := context.WithTimeout(req.Context(), p.timeout) - defer cancel() - - r, err := p.client.Do(req.WithContext(ctx)) - if err != nil { - return metricutil.MarkMeasurementError(newMeasurement, err) - } - defer r.Body.Close() - - b, err := ioutil.ReadAll(r.Body) - if err != nil { - return metricutil.MarkMeasurementError(newMeasurement, err) - } - - if 400 <= r.StatusCode { - return metricutil.MarkMeasurementError(newMeasurement, err) - } - - var result graphiteResponse - err = json.Unmarshal(b, &result) - if err != nil { - return metricutil.MarkMeasurementError(newMeasurement, err) - } - - var value *float64 - for _, tr := range result { - for _, dp := range tr.DataPoints { - if dp.Value != nil { - value = dp.Value - } - } - } - + value, err := p.api.Query(metric.Provider.Graphite.Query) if value == nil { return metricutil.MarkMeasurementError(newMeasurement, errors.New("no values found")) } - newMeasurement.Value = fmt.Sprintf("%f", *value) + newMeasurement.Value = fmt.Sprintf("%.3f", *value) - newStatus, err := evaluate.EvaluateResult(value, metric, p.logCtx) + newStatus, err := evaluate.EvaluateResult(*value, metric, p.logCtx) if err != nil { return metricutil.MarkMeasurementError(newMeasurement, err) } @@ -200,23 +91,10 @@ func (p *Provider) GarbageCollect(run *v1alpha1.AnalysisRun, metric v1alpha1.Met return nil } -func (p *Provider) trimQuery(q string) string { - space := regexp.MustCompile(`\s+`) - return space.ReplaceAllString(q, " ") -} - // NewGraphiteProvider returns a new Graphite provider -func NewGraphiteProvider(metric v1alpha1.Metric, logCtx log.Entry) (*Provider, error) { - addr := metric.Provider.Graphite.Address - graphiteURL, err := url.Parse(addr) - if addr == "" || err != nil { - return nil, fmt.Errorf("%s address %s is not a valid URL", ProviderType, addr) - } - +func NewGraphiteProvider(api API, logCtx log.Entry) *Provider { return &Provider{ - logCtx: logCtx, - client: http.DefaultClient, - url: *graphiteURL, - timeout: 5 * time.Second, - }, nil + logCtx: logCtx, + api: api, + } } diff --git a/metricproviders/graphite/graphite_test.go b/metricproviders/graphite/graphite_test.go index daefe8093d..126860432b 100644 --- a/metricproviders/graphite/graphite_test.go +++ b/metricproviders/graphite/graphite_test.go @@ -9,17 +9,37 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" ) -func TestType_withValidURL(t *testing.T) { +func newMockAPI(response float64) mockAPI { + return mockAPI{ + response: &response, + } +} + +func TestRunSuccessfully(t *testing.T) { e := log.Entry{} - m := v1alpha1.Metric{ + metric := v1alpha1.Metric{ + Name: "foo", + SuccessCondition: "result == 10.000", + FailureCondition: "result != 10.000", Provider: v1alpha1.MetricProvider{ Graphite: &v1alpha1.GraphiteMetric{ Address: "http://some-graphite.foo", + Query: "foo=1", }, }, } - g, err := NewGraphiteProvider(m, e) + g := NewGraphiteProvider(newMockAPI(10.000), e) + measurement := g.Run(&v1alpha1.AnalysisRun{}, metric) + assert.NotNil(t, measurement.StartedAt) + assert.Equal(t, "10.000", measurement.Value) + assert.NotNil(t, measurement.FinishedAt) + assert.Equal(t, v1alpha1.AnalysisPhaseSuccessful, measurement.Phase) +} + +func TestGarbageCollect(t *testing.T) { + e := log.NewEntry(log.New()) + g := NewGraphiteProvider(newMockAPI(1), *e) - assert.Nil(t, err) - assert.Equal(t, ProviderType, g.Type()) + err := g.GarbageCollect(nil, v1alpha1.Metric{}, 0) + assert.NoError(t, err) } diff --git a/metricproviders/graphite/mock_test.go b/metricproviders/graphite/mock_test.go new file mode 100644 index 0000000000..b0b1953144 --- /dev/null +++ b/metricproviders/graphite/mock_test.go @@ -0,0 +1,13 @@ +package graphite + +type mockAPI struct { + response *float64 + err error +} + +func (m mockAPI) Query(query string) (*float64, error) { + if m.err != nil { + return nil, m.err + } + return m.response, nil +} diff --git a/metricproviders/metricproviders.go b/metricproviders/metricproviders.go index c8e6fa7ec1..279dd7e839 100644 --- a/metricproviders/metricproviders.go +++ b/metricproviders/metricproviders.go @@ -79,7 +79,11 @@ func (f *ProviderFactory) NewProvider(logCtx log.Entry, metric v1alpha1.Metric) } return newrelic.NewNewRelicProvider(client, logCtx), nil case graphite.ProviderType: - return graphite.NewGraphiteProvider(metric, logCtx) + client, err := graphite.NewAPIClient(metric, logCtx) + if err != nil { + return nil, err + } + return graphite.NewGraphiteProvider(client, logCtx), nil default: return nil, fmt.Errorf("no valid provider in metric '%s'", metric.Name) } From 1a0124b908d50c694b03f356abf14a572047ad5a Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Sun, 1 Aug 2021 11:44:08 -0400 Subject: [PATCH 03/20] add test for AnalysisPhaseFailed scenario Signed-off-by: Mike Ball --- metricproviders/graphite/graphite_test.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/metricproviders/graphite/graphite_test.go b/metricproviders/graphite/graphite_test.go index 126860432b..5df0e16d25 100644 --- a/metricproviders/graphite/graphite_test.go +++ b/metricproviders/graphite/graphite_test.go @@ -15,7 +15,7 @@ func newMockAPI(response float64) mockAPI { } } -func TestRunSuccessfully(t *testing.T) { +func TestRunSuccessfulEvaluation(t *testing.T) { e := log.Entry{} metric := v1alpha1.Metric{ Name: "foo", @@ -36,6 +36,27 @@ func TestRunSuccessfully(t *testing.T) { assert.Equal(t, v1alpha1.AnalysisPhaseSuccessful, measurement.Phase) } +func TestRunFailedEvaluation(t *testing.T) { + e := log.Entry{} + metric := v1alpha1.Metric{ + Name: "foo", + SuccessCondition: "result == 10.000", + FailureCondition: "result != 10.000", + Provider: v1alpha1.MetricProvider{ + Graphite: &v1alpha1.GraphiteMetric{ + Address: "http://some-graphite.foo", + Query: "foo=1", + }, + }, + } + g := NewGraphiteProvider(newMockAPI(5.000), e) + measurement := g.Run(&v1alpha1.AnalysisRun{}, metric) + assert.NotNil(t, measurement.StartedAt) + assert.Equal(t, "5.000", measurement.Value) + assert.NotNil(t, measurement.FinishedAt) + assert.Equal(t, v1alpha1.AnalysisPhaseFailed, measurement.Phase) +} + func TestGarbageCollect(t *testing.T) { e := log.NewEntry(log.New()) g := NewGraphiteProvider(newMockAPI(1), *e) From 32e96f9f43b2eb5c9bcc1a048af12864d356a646 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Sun, 1 Aug 2021 11:50:31 -0400 Subject: [PATCH 04/20] add graphite.Provider#Type test Signed-off-by: Mike Ball --- metricproviders/graphite/graphite_test.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/metricproviders/graphite/graphite_test.go b/metricproviders/graphite/graphite_test.go index 5df0e16d25..3e722b4eb1 100644 --- a/metricproviders/graphite/graphite_test.go +++ b/metricproviders/graphite/graphite_test.go @@ -15,8 +15,12 @@ func newMockAPI(response float64) mockAPI { } } +func TestType(t *testing.T) { + g := NewGraphiteProvider(newMockAPI(10.000), log.Entry{}) + assert.Equal(t, ProviderType, g.Type()) +} + func TestRunSuccessfulEvaluation(t *testing.T) { - e := log.Entry{} metric := v1alpha1.Metric{ Name: "foo", SuccessCondition: "result == 10.000", @@ -28,7 +32,7 @@ func TestRunSuccessfulEvaluation(t *testing.T) { }, }, } - g := NewGraphiteProvider(newMockAPI(10.000), e) + g := NewGraphiteProvider(newMockAPI(10.000), log.Entry{}) measurement := g.Run(&v1alpha1.AnalysisRun{}, metric) assert.NotNil(t, measurement.StartedAt) assert.Equal(t, "10.000", measurement.Value) @@ -37,7 +41,6 @@ func TestRunSuccessfulEvaluation(t *testing.T) { } func TestRunFailedEvaluation(t *testing.T) { - e := log.Entry{} metric := v1alpha1.Metric{ Name: "foo", SuccessCondition: "result == 10.000", @@ -49,7 +52,7 @@ func TestRunFailedEvaluation(t *testing.T) { }, }, } - g := NewGraphiteProvider(newMockAPI(5.000), e) + g := NewGraphiteProvider(newMockAPI(5.000), log.Entry{}) measurement := g.Run(&v1alpha1.AnalysisRun{}, metric) assert.NotNil(t, measurement.StartedAt) assert.Equal(t, "5.000", measurement.Value) @@ -58,9 +61,7 @@ func TestRunFailedEvaluation(t *testing.T) { } func TestGarbageCollect(t *testing.T) { - e := log.NewEntry(log.New()) - g := NewGraphiteProvider(newMockAPI(1), *e) - + g := NewGraphiteProvider(newMockAPI(1), log.Entry{}) err := g.GarbageCollect(nil, v1alpha1.Metric{}, 0) assert.NoError(t, err) } From f2d75b0be2b6a4d1a0ae937fd4436b60015c8d49 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Mon, 2 Aug 2021 08:40:03 -0400 Subject: [PATCH 05/20] add graphite.APIClient#Query test Signed-off-by: Mike Ball --- metricproviders/graphite/api_test.go | 67 ++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/metricproviders/graphite/api_test.go b/metricproviders/graphite/api_test.go index 66dbb3c7cf..8414ccde68 100644 --- a/metricproviders/graphite/api_test.go +++ b/metricproviders/graphite/api_test.go @@ -2,6 +2,8 @@ package graphite import ( "fmt" + "net/http" + "net/http/httptest" "testing" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" @@ -34,3 +36,68 @@ func TestNewAPIWithInvalidURL(t *testing.T) { assert.Equal(t, err.Error(), fmt.Sprintf("Graphite address %s is not a valid URL", addr)) assert.Nil(t, g) } + +func TestQuery(t *testing.T) { + tests := []struct { + name string + query string + expectedTarget string + expectedFrom string + expectedResult float64 + body string + }{{ + "ok", + "target=sumSeries(app.http.*.*.count)&from=-2min", + "sumSeries(app.http.*.*.count)", + "-2min", + float64(100), + `[ + { + "datapoints": [ + [ + 10, + 1621348400 + ], + [ + 75, + 1621348410 + ], + [ + 25, + 1621348420 + ], + [ + 100, + 1621348430 + ] + ], + "target": "sumSeries(app.http.*.*.count)", + "tags": { + "aggregatedBy": "sum", + "name": "sumSeries(app.http.*.*.count)" + } + } + ]`, + }} + + for _, test := range tests { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + target := r.URL.Query().Get("target") + assert.Equal(t, test.expectedTarget, target) + + from := r.URL.Query().Get("from") + assert.Equal(t, test.expectedFrom, from) + + json := test.body + w.Write([]byte(json)) + })) + defer ts.Close() + + g, err := NewAPIClient(testGraphiteMetric(ts.URL), log.Entry{}) + assert.Nil(t, err) + + val, err := g.Query(test.query) + assert.Nil(t, err) + assert.Equal(t, &test.expectedResult, val) + } +} From a6c2261da74439ab7bfa0fd3f16bbcb991df0934 Mon Sep 17 00:00:00 2001 From: Jesse Suen Date: Mon, 2 Aug 2021 16:37:36 -0700 Subject: [PATCH 06/20] chore: github release action was using incorect docker cache (#1387) Signed-off-by: Jesse Suen --- .github/workflows/release.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d80a2b406b..f146ba475c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -17,6 +17,10 @@ jobs: with: ref: ${{ github.event.inputs.tag }} + - name: Get SHA + id: get-sha + run: echo "::set-output name=sha::$(git log -1 --format='%H')" + - name: Set up QEMU uses: docker/setup-qemu-action@v1 @@ -27,9 +31,7 @@ jobs: uses: actions/cache@v2 with: path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx- + key: ${{ runner.os }}-buildx-${{ steps.get-sha.outputs.sha }} - name: Print Disk Usage run: | From 7486254d1830f86be0093e2935c1c99dc5c68b2d Mon Sep 17 00:00:00 2001 From: Jesse Suen Date: Mon, 2 Aug 2021 19:38:09 -0700 Subject: [PATCH 07/20] chore: release workflow docker build context should use local path and not git context (#1388) Signed-off-by: Jesse Suen --- .github/workflows/release.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f146ba475c..0583bf14eb 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -47,6 +47,8 @@ jobs: ghcr.io/argoproj/argo-rollouts tags: | type=semver,pattern={{version}},prefix=v,value=${{ github.event.inputs.tag }} + flavor: | + latest=false - name: Docker meta (plugin) id: plugin-meta @@ -57,6 +59,8 @@ jobs: ghcr.io/argoproj/kubectl-argo-rollouts tags: | type=semver,pattern={{version}},prefix=v,value=${{ github.event.inputs.tag }} + flavor: | + latest=false - name: Login to GitHub Container Registry if: github.event_name != 'pull_request' @@ -77,6 +81,7 @@ jobs: - name: Build and push (controller-image) uses: docker/build-push-action@v2 with: + context: . platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.controller-meta.outputs.tags }} @@ -86,6 +91,7 @@ jobs: - name: Build and push (plugin-image) uses: docker/build-push-action@v2 with: + context: . target: kubectl-argo-rollouts platforms: linux/amd64,linux/arm64 push: true From 6980947703a689fa6b3c82bf3c6478f0eb649ce6 Mon Sep 17 00:00:00 2001 From: cskh Date: Tue, 3 Aug 2021 03:42:03 -0400 Subject: [PATCH 08/20] chore: Raname variables, import pkg for clarification (#1313) Signed-off-by: Hui Kang --- cmd/rollouts-controller/main.go | 4 ++-- controller/controller.go | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/rollouts-controller/main.go b/cmd/rollouts-controller/main.go index eb3d2e179f..67f08c2baf 100644 --- a/cmd/rollouts-controller/main.go +++ b/cmd/rollouts-controller/main.go @@ -99,7 +99,7 @@ func newCommand() *cobra.Command { kubeClient, err := kubernetes.NewForConfig(config) checkError(err) - rolloutClient, err := clientset.NewForConfig(config) + argoprojClient, err := clientset.NewForConfig(config) checkError(err) dynamicClient, err := dynamic.NewForConfig(config) checkError(err) @@ -150,7 +150,7 @@ func newCommand() *cobra.Command { cm := controller.NewManager( namespace, kubeClient, - rolloutClient, + argoprojClient, dynamicClient, smiClient, discoveryClient, diff --git a/controller/controller.go b/controller/controller.go index dadfb68ec0..47ca955851 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "github.com/argoproj/notifications-engine/pkg/api" - "github.com/argoproj/notifications-engine/pkg/controller" + notificationapi "github.com/argoproj/notifications-engine/pkg/api" + notificationcontroller "github.com/argoproj/notifications-engine/pkg/controller" "github.com/pkg/errors" smiclientset "github.com/servicemeshinterface/smi-sdk-go/pkg/gen/client/split/clientset/versioned" log "github.com/sirupsen/logrus" @@ -72,7 +72,7 @@ type Manager struct { analysisController *analysis.Controller serviceController *service.Controller ingressController *ingress.Controller - notificationsController controller.NotificationController + notificationsController notificationcontroller.NotificationController rolloutSynced cache.InformerSynced experimentSynced cache.InformerSynced @@ -148,10 +148,10 @@ func NewManager( ingressWorkqueue := workqueue.NewNamedRateLimitingQueue(queue.DefaultArgoRolloutsRateLimiter(), "Ingresses") refResolver := rollout.NewInformerBasedWorkloadRefResolver(namespace, dynamicclientset, discoveryClient, argoprojclientset, rolloutsInformer.Informer()) - apiFactory := api.NewFactory(record.NewAPIFactorySettings(), defaults.Namespace(), secretInformer.Informer(), configMapInformer.Informer()) + apiFactory := notificationapi.NewFactory(record.NewAPIFactorySettings(), defaults.Namespace(), secretInformer.Informer(), configMapInformer.Informer()) recorder := record.NewEventRecorder(kubeclientset, metrics.MetricRolloutEventsTotal, apiFactory) - notificationsController := controller.NewController(dynamicclientset.Resource(v1alpha1.RolloutGVR), rolloutsInformer.Informer(), apiFactory, - controller.WithToUnstructured(func(obj metav1.Object) (*unstructured.Unstructured, error) { + notificationsController := notificationcontroller.NewController(dynamicclientset.Resource(v1alpha1.RolloutGVR), rolloutsInformer.Informer(), apiFactory, + notificationcontroller.WithToUnstructured(func(obj metav1.Object) (*unstructured.Unstructured, error) { data, err := json.Marshal(obj) if err != nil { return nil, err From 6b66e9d01d31e799e5984737f18f5565660c39e6 Mon Sep 17 00:00:00 2001 From: Andrii Perenesenko Date: Wed, 4 Aug 2021 21:16:58 -0700 Subject: [PATCH 09/20] feat: configurable and more aggressive cleanup of old AnalysisRuns and Experiments (#1342) Signed-off-by: Andrii Perenesenko --- docs/features/specification.md | 8 + examples/analysis-templates.yaml | 3 +- manifests/crds/rollout-crd.yaml | 9 + manifests/install.yaml | 9 + manifests/namespace-install.yaml | 9 + pkg/apiclient/rollout/rollout.swagger.json | 20 + pkg/apis/rollouts/v1alpha1/generated.pb.go | 1089 ++++++++++------- pkg/apis/rollouts/v1alpha1/generated.proto | 13 + .../rollouts/v1alpha1/openapi_generated.go | 36 +- pkg/apis/rollouts/v1alpha1/types.go | 11 + .../v1alpha1/zz_generated.deepcopy.go | 31 + rollout/analysis.go | 5 +- rollout/experiment.go | 5 +- utils/analysis/filter.go | 37 +- utils/analysis/filter_test.go | 55 +- utils/defaults/defaults.go | 20 + utils/defaults/defaults_test.go | 28 + utils/experiment/filter.go | 24 +- utils/experiment/filter_test.go | 54 +- 19 files changed, 1029 insertions(+), 437 deletions(-) diff --git a/docs/features/specification.md b/docs/features/specification.md index 60acd6cb21..d719d74ca6 100644 --- a/docs/features/specification.md +++ b/docs/features/specification.md @@ -11,6 +11,14 @@ spec: # Number of desired pods. # Defaults to 1. replicas: 5 + analysis: + # limits the number of successful analysis runs and experiments to be stored in a history + # Defaults to 5. + successfulRunHistoryLimit: 10 + # limits the number of unsuccessful analysis runs and experiments to be stored in a history. + # Stages for unsuccessful: "Error", "Failed", "Inconclusive" + # Defaults to 5. + unsuccessfulRunHistoryLimit: 10 # Label selector for pods. Existing ReplicaSets whose pods are selected by # this will be the ones affected by this rollout. It must match the pod diff --git a/examples/analysis-templates.yaml b/examples/analysis-templates.yaml index 71ea53b659..84dde3b1c1 100644 --- a/examples/analysis-templates.yaml +++ b/examples/analysis-templates.yaml @@ -24,7 +24,7 @@ spec: args: [exit 0] restartPolicy: Never backoffLimit: 0 - + count: 1 --- # This AnalysisTemplate will run a Kubernetes Job every 5 seconds, with a 50% chance of failure. # When the number of accumulated failures exceeds failureLimit, it will cause the analysis run to @@ -36,6 +36,7 @@ metadata: spec: metrics: - name: random-fail + count: 2 interval: 5s failureLimit: 1 provider: diff --git a/manifests/crds/rollout-crd.yaml b/manifests/crds/rollout-crd.yaml index a54aaa4891..6c5cb0a944 100644 --- a/manifests/crds/rollout-crd.yaml +++ b/manifests/crds/rollout-crd.yaml @@ -47,6 +47,15 @@ spec: type: object spec: properties: + analysis: + properties: + successfulRunHistoryLimit: + format: int32 + type: integer + unsuccessfulRunHistoryLimit: + format: int32 + type: integer + type: object minReadySeconds: format: int32 type: integer diff --git a/manifests/install.yaml b/manifests/install.yaml index 84c413ad56..8c6b321ab2 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -9745,6 +9745,15 @@ spec: type: object spec: properties: + analysis: + properties: + successfulRunHistoryLimit: + format: int32 + type: integer + unsuccessfulRunHistoryLimit: + format: int32 + type: integer + type: object minReadySeconds: format: int32 type: integer diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 48f30b6761..2fc1caf4b0 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -9745,6 +9745,15 @@ spec: type: object spec: properties: + analysis: + properties: + successfulRunHistoryLimit: + format: int32 + type: integer + unsuccessfulRunHistoryLimit: + format: int32 + type: integer + type: object minReadySeconds: format: int32 type: integer diff --git a/pkg/apiclient/rollout/rollout.swagger.json b/pkg/apiclient/rollout/rollout.swagger.json index 9e20548068..7657b2867c 100644 --- a/pkg/apiclient/rollout/rollout.swagger.json +++ b/pkg/apiclient/rollout/rollout.swagger.json @@ -550,6 +550,22 @@ }, "title": "AnalysisRunArgument argument to add to analysisRun" }, + "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisRunStrategy": { + "type": "object", + "properties": { + "successfulRunHistoryLimit": { + "type": "integer", + "format": "int32", + "title": "SuccessfulRunHistoryLimit limits the number of old successful analysis runs and experiments to be retained in a history" + }, + "unsuccessfulRunHistoryLimit": { + "type": "integer", + "format": "int32", + "title": "UnsuccessfulRunHistoryLimit limits the number of old unsuccessful analysis runs and experiments to be retained in a history.\nStages for unsuccessful: \"Error\", \"Failed\", \"Inconclusive\"" + } + }, + "title": "AnalysisRunStrategy configuration for the analysis runs and experiments to retain" + }, "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AntiAffinity": { "type": "object", "properties": { @@ -1165,6 +1181,10 @@ "restartAt": { "$ref": "#/definitions/k8s.io.apimachinery.pkg.apis.meta.v1.Time", "title": "RestartAt indicates when all the pods of a Rollout should be restarted" + }, + "analysis": { + "$ref": "#/definitions/github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisRunStrategy", + "title": "Analysis configuration for the analysis runs to retain" } }, "title": "RolloutSpec is the spec for a Rollout resource" diff --git a/pkg/apis/rollouts/v1alpha1/generated.pb.go b/pkg/apis/rollouts/v1alpha1/generated.pb.go index 9004935887..471c0d18ed 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.pb.go +++ b/pkg/apis/rollouts/v1alpha1/generated.pb.go @@ -244,10 +244,38 @@ func (m *AnalysisRunStatus) XXX_DiscardUnknown() { var xxx_messageInfo_AnalysisRunStatus proto.InternalMessageInfo +func (m *AnalysisRunStrategy) Reset() { *m = AnalysisRunStrategy{} } +func (*AnalysisRunStrategy) ProtoMessage() {} +func (*AnalysisRunStrategy) Descriptor() ([]byte, []int) { + return fileDescriptor_e0e705f843545fab, []int{7} +} +func (m *AnalysisRunStrategy) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AnalysisRunStrategy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *AnalysisRunStrategy) XXX_Merge(src proto.Message) { + xxx_messageInfo_AnalysisRunStrategy.Merge(m, src) +} +func (m *AnalysisRunStrategy) XXX_Size() int { + return m.Size() +} +func (m *AnalysisRunStrategy) XXX_DiscardUnknown() { + xxx_messageInfo_AnalysisRunStrategy.DiscardUnknown(m) +} + +var xxx_messageInfo_AnalysisRunStrategy proto.InternalMessageInfo + func (m *AnalysisTemplate) Reset() { *m = AnalysisTemplate{} } func (*AnalysisTemplate) ProtoMessage() {} func (*AnalysisTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{7} + return fileDescriptor_e0e705f843545fab, []int{8} } func (m *AnalysisTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -275,7 +303,7 @@ var xxx_messageInfo_AnalysisTemplate proto.InternalMessageInfo func (m *AnalysisTemplateList) Reset() { *m = AnalysisTemplateList{} } func (*AnalysisTemplateList) ProtoMessage() {} func (*AnalysisTemplateList) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{8} + return fileDescriptor_e0e705f843545fab, []int{9} } func (m *AnalysisTemplateList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -303,7 +331,7 @@ var xxx_messageInfo_AnalysisTemplateList proto.InternalMessageInfo func (m *AnalysisTemplateSpec) Reset() { *m = AnalysisTemplateSpec{} } func (*AnalysisTemplateSpec) ProtoMessage() {} func (*AnalysisTemplateSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{9} + return fileDescriptor_e0e705f843545fab, []int{10} } func (m *AnalysisTemplateSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -331,7 +359,7 @@ var xxx_messageInfo_AnalysisTemplateSpec proto.InternalMessageInfo func (m *AntiAffinity) Reset() { *m = AntiAffinity{} } func (*AntiAffinity) ProtoMessage() {} func (*AntiAffinity) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{10} + return fileDescriptor_e0e705f843545fab, []int{11} } func (m *AntiAffinity) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -359,7 +387,7 @@ var xxx_messageInfo_AntiAffinity proto.InternalMessageInfo func (m *Argument) Reset() { *m = Argument{} } func (*Argument) ProtoMessage() {} func (*Argument) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{11} + return fileDescriptor_e0e705f843545fab, []int{12} } func (m *Argument) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -387,7 +415,7 @@ var xxx_messageInfo_Argument proto.InternalMessageInfo func (m *ArgumentValueFrom) Reset() { *m = ArgumentValueFrom{} } func (*ArgumentValueFrom) ProtoMessage() {} func (*ArgumentValueFrom) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{12} + return fileDescriptor_e0e705f843545fab, []int{13} } func (m *ArgumentValueFrom) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -415,7 +443,7 @@ var xxx_messageInfo_ArgumentValueFrom proto.InternalMessageInfo func (m *BlueGreenStatus) Reset() { *m = BlueGreenStatus{} } func (*BlueGreenStatus) ProtoMessage() {} func (*BlueGreenStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{13} + return fileDescriptor_e0e705f843545fab, []int{14} } func (m *BlueGreenStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -443,7 +471,7 @@ var xxx_messageInfo_BlueGreenStatus proto.InternalMessageInfo func (m *BlueGreenStrategy) Reset() { *m = BlueGreenStrategy{} } func (*BlueGreenStrategy) ProtoMessage() {} func (*BlueGreenStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{14} + return fileDescriptor_e0e705f843545fab, []int{15} } func (m *BlueGreenStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -471,7 +499,7 @@ var xxx_messageInfo_BlueGreenStrategy proto.InternalMessageInfo func (m *CanaryStatus) Reset() { *m = CanaryStatus{} } func (*CanaryStatus) ProtoMessage() {} func (*CanaryStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{15} + return fileDescriptor_e0e705f843545fab, []int{16} } func (m *CanaryStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -499,7 +527,7 @@ var xxx_messageInfo_CanaryStatus proto.InternalMessageInfo func (m *CanaryStep) Reset() { *m = CanaryStep{} } func (*CanaryStep) ProtoMessage() {} func (*CanaryStep) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{16} + return fileDescriptor_e0e705f843545fab, []int{17} } func (m *CanaryStep) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -527,7 +555,7 @@ var xxx_messageInfo_CanaryStep proto.InternalMessageInfo func (m *CanaryStrategy) Reset() { *m = CanaryStrategy{} } func (*CanaryStrategy) ProtoMessage() {} func (*CanaryStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{17} + return fileDescriptor_e0e705f843545fab, []int{18} } func (m *CanaryStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -555,7 +583,7 @@ var xxx_messageInfo_CanaryStrategy proto.InternalMessageInfo func (m *ClusterAnalysisTemplate) Reset() { *m = ClusterAnalysisTemplate{} } func (*ClusterAnalysisTemplate) ProtoMessage() {} func (*ClusterAnalysisTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{18} + return fileDescriptor_e0e705f843545fab, []int{19} } func (m *ClusterAnalysisTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -583,7 +611,7 @@ var xxx_messageInfo_ClusterAnalysisTemplate proto.InternalMessageInfo func (m *ClusterAnalysisTemplateList) Reset() { *m = ClusterAnalysisTemplateList{} } func (*ClusterAnalysisTemplateList) ProtoMessage() {} func (*ClusterAnalysisTemplateList) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{19} + return fileDescriptor_e0e705f843545fab, []int{20} } func (m *ClusterAnalysisTemplateList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -611,7 +639,7 @@ var xxx_messageInfo_ClusterAnalysisTemplateList proto.InternalMessageInfo func (m *DatadogMetric) Reset() { *m = DatadogMetric{} } func (*DatadogMetric) ProtoMessage() {} func (*DatadogMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{20} + return fileDescriptor_e0e705f843545fab, []int{21} } func (m *DatadogMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -639,7 +667,7 @@ var xxx_messageInfo_DatadogMetric proto.InternalMessageInfo func (m *Experiment) Reset() { *m = Experiment{} } func (*Experiment) ProtoMessage() {} func (*Experiment) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{21} + return fileDescriptor_e0e705f843545fab, []int{22} } func (m *Experiment) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -667,7 +695,7 @@ var xxx_messageInfo_Experiment proto.InternalMessageInfo func (m *ExperimentAnalysisRunStatus) Reset() { *m = ExperimentAnalysisRunStatus{} } func (*ExperimentAnalysisRunStatus) ProtoMessage() {} func (*ExperimentAnalysisRunStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{22} + return fileDescriptor_e0e705f843545fab, []int{23} } func (m *ExperimentAnalysisRunStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -695,7 +723,7 @@ var xxx_messageInfo_ExperimentAnalysisRunStatus proto.InternalMessageInfo func (m *ExperimentAnalysisTemplateRef) Reset() { *m = ExperimentAnalysisTemplateRef{} } func (*ExperimentAnalysisTemplateRef) ProtoMessage() {} func (*ExperimentAnalysisTemplateRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{23} + return fileDescriptor_e0e705f843545fab, []int{24} } func (m *ExperimentAnalysisTemplateRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -723,7 +751,7 @@ var xxx_messageInfo_ExperimentAnalysisTemplateRef proto.InternalMessageInfo func (m *ExperimentCondition) Reset() { *m = ExperimentCondition{} } func (*ExperimentCondition) ProtoMessage() {} func (*ExperimentCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{24} + return fileDescriptor_e0e705f843545fab, []int{25} } func (m *ExperimentCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -751,7 +779,7 @@ var xxx_messageInfo_ExperimentCondition proto.InternalMessageInfo func (m *ExperimentList) Reset() { *m = ExperimentList{} } func (*ExperimentList) ProtoMessage() {} func (*ExperimentList) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{25} + return fileDescriptor_e0e705f843545fab, []int{26} } func (m *ExperimentList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -779,7 +807,7 @@ var xxx_messageInfo_ExperimentList proto.InternalMessageInfo func (m *ExperimentSpec) Reset() { *m = ExperimentSpec{} } func (*ExperimentSpec) ProtoMessage() {} func (*ExperimentSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{26} + return fileDescriptor_e0e705f843545fab, []int{27} } func (m *ExperimentSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -807,7 +835,7 @@ var xxx_messageInfo_ExperimentSpec proto.InternalMessageInfo func (m *ExperimentStatus) Reset() { *m = ExperimentStatus{} } func (*ExperimentStatus) ProtoMessage() {} func (*ExperimentStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{27} + return fileDescriptor_e0e705f843545fab, []int{28} } func (m *ExperimentStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -835,7 +863,7 @@ var xxx_messageInfo_ExperimentStatus proto.InternalMessageInfo func (m *FieldRef) Reset() { *m = FieldRef{} } func (*FieldRef) ProtoMessage() {} func (*FieldRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{28} + return fileDescriptor_e0e705f843545fab, []int{29} } func (m *FieldRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -863,7 +891,7 @@ var xxx_messageInfo_FieldRef proto.InternalMessageInfo func (m *IstioDestinationRule) Reset() { *m = IstioDestinationRule{} } func (*IstioDestinationRule) ProtoMessage() {} func (*IstioDestinationRule) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{29} + return fileDescriptor_e0e705f843545fab, []int{30} } func (m *IstioDestinationRule) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -891,7 +919,7 @@ var xxx_messageInfo_IstioDestinationRule proto.InternalMessageInfo func (m *IstioTrafficRouting) Reset() { *m = IstioTrafficRouting{} } func (*IstioTrafficRouting) ProtoMessage() {} func (*IstioTrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{30} + return fileDescriptor_e0e705f843545fab, []int{31} } func (m *IstioTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -919,7 +947,7 @@ var xxx_messageInfo_IstioTrafficRouting proto.InternalMessageInfo func (m *IstioVirtualService) Reset() { *m = IstioVirtualService{} } func (*IstioVirtualService) ProtoMessage() {} func (*IstioVirtualService) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{31} + return fileDescriptor_e0e705f843545fab, []int{32} } func (m *IstioVirtualService) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -947,7 +975,7 @@ var xxx_messageInfo_IstioVirtualService proto.InternalMessageInfo func (m *JobMetric) Reset() { *m = JobMetric{} } func (*JobMetric) ProtoMessage() {} func (*JobMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{32} + return fileDescriptor_e0e705f843545fab, []int{33} } func (m *JobMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -975,7 +1003,7 @@ var xxx_messageInfo_JobMetric proto.InternalMessageInfo func (m *KayentaMetric) Reset() { *m = KayentaMetric{} } func (*KayentaMetric) ProtoMessage() {} func (*KayentaMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{33} + return fileDescriptor_e0e705f843545fab, []int{34} } func (m *KayentaMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1003,7 +1031,7 @@ var xxx_messageInfo_KayentaMetric proto.InternalMessageInfo func (m *KayentaScope) Reset() { *m = KayentaScope{} } func (*KayentaScope) ProtoMessage() {} func (*KayentaScope) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{34} + return fileDescriptor_e0e705f843545fab, []int{35} } func (m *KayentaScope) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1031,7 +1059,7 @@ var xxx_messageInfo_KayentaScope proto.InternalMessageInfo func (m *KayentaThreshold) Reset() { *m = KayentaThreshold{} } func (*KayentaThreshold) ProtoMessage() {} func (*KayentaThreshold) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{35} + return fileDescriptor_e0e705f843545fab, []int{36} } func (m *KayentaThreshold) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1059,7 +1087,7 @@ var xxx_messageInfo_KayentaThreshold proto.InternalMessageInfo func (m *Measurement) Reset() { *m = Measurement{} } func (*Measurement) ProtoMessage() {} func (*Measurement) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{36} + return fileDescriptor_e0e705f843545fab, []int{37} } func (m *Measurement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1087,7 +1115,7 @@ var xxx_messageInfo_Measurement proto.InternalMessageInfo func (m *Metric) Reset() { *m = Metric{} } func (*Metric) ProtoMessage() {} func (*Metric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{37} + return fileDescriptor_e0e705f843545fab, []int{38} } func (m *Metric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1115,7 +1143,7 @@ var xxx_messageInfo_Metric proto.InternalMessageInfo func (m *MetricProvider) Reset() { *m = MetricProvider{} } func (*MetricProvider) ProtoMessage() {} func (*MetricProvider) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{38} + return fileDescriptor_e0e705f843545fab, []int{39} } func (m *MetricProvider) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1143,7 +1171,7 @@ var xxx_messageInfo_MetricProvider proto.InternalMessageInfo func (m *MetricResult) Reset() { *m = MetricResult{} } func (*MetricResult) ProtoMessage() {} func (*MetricResult) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{39} + return fileDescriptor_e0e705f843545fab, []int{40} } func (m *MetricResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1171,7 +1199,7 @@ var xxx_messageInfo_MetricResult proto.InternalMessageInfo func (m *NewRelicMetric) Reset() { *m = NewRelicMetric{} } func (*NewRelicMetric) ProtoMessage() {} func (*NewRelicMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{40} + return fileDescriptor_e0e705f843545fab, []int{41} } func (m *NewRelicMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1199,7 +1227,7 @@ var xxx_messageInfo_NewRelicMetric proto.InternalMessageInfo func (m *NginxTrafficRouting) Reset() { *m = NginxTrafficRouting{} } func (*NginxTrafficRouting) ProtoMessage() {} func (*NginxTrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{41} + return fileDescriptor_e0e705f843545fab, []int{42} } func (m *NginxTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1227,7 +1255,7 @@ var xxx_messageInfo_NginxTrafficRouting proto.InternalMessageInfo func (m *ObjectRef) Reset() { *m = ObjectRef{} } func (*ObjectRef) ProtoMessage() {} func (*ObjectRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{42} + return fileDescriptor_e0e705f843545fab, []int{43} } func (m *ObjectRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1255,7 +1283,7 @@ var xxx_messageInfo_ObjectRef proto.InternalMessageInfo func (m *PauseCondition) Reset() { *m = PauseCondition{} } func (*PauseCondition) ProtoMessage() {} func (*PauseCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{43} + return fileDescriptor_e0e705f843545fab, []int{44} } func (m *PauseCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1283,7 +1311,7 @@ var xxx_messageInfo_PauseCondition proto.InternalMessageInfo func (m *PodTemplateMetadata) Reset() { *m = PodTemplateMetadata{} } func (*PodTemplateMetadata) ProtoMessage() {} func (*PodTemplateMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{44} + return fileDescriptor_e0e705f843545fab, []int{45} } func (m *PodTemplateMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1313,7 +1341,7 @@ func (m *PreferredDuringSchedulingIgnoredDuringExecution) Reset() { } func (*PreferredDuringSchedulingIgnoredDuringExecution) ProtoMessage() {} func (*PreferredDuringSchedulingIgnoredDuringExecution) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{45} + return fileDescriptor_e0e705f843545fab, []int{46} } func (m *PreferredDuringSchedulingIgnoredDuringExecution) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1341,7 +1369,7 @@ var xxx_messageInfo_PreferredDuringSchedulingIgnoredDuringExecution proto.Intern func (m *PrometheusMetric) Reset() { *m = PrometheusMetric{} } func (*PrometheusMetric) ProtoMessage() {} func (*PrometheusMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{46} + return fileDescriptor_e0e705f843545fab, []int{47} } func (m *PrometheusMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1371,7 +1399,7 @@ func (m *RequiredDuringSchedulingIgnoredDuringExecution) Reset() { } func (*RequiredDuringSchedulingIgnoredDuringExecution) ProtoMessage() {} func (*RequiredDuringSchedulingIgnoredDuringExecution) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{47} + return fileDescriptor_e0e705f843545fab, []int{48} } func (m *RequiredDuringSchedulingIgnoredDuringExecution) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1399,7 +1427,7 @@ var xxx_messageInfo_RequiredDuringSchedulingIgnoredDuringExecution proto.Interna func (m *Rollout) Reset() { *m = Rollout{} } func (*Rollout) ProtoMessage() {} func (*Rollout) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{48} + return fileDescriptor_e0e705f843545fab, []int{49} } func (m *Rollout) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1427,7 +1455,7 @@ var xxx_messageInfo_Rollout proto.InternalMessageInfo func (m *RolloutAnalysis) Reset() { *m = RolloutAnalysis{} } func (*RolloutAnalysis) ProtoMessage() {} func (*RolloutAnalysis) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{49} + return fileDescriptor_e0e705f843545fab, []int{50} } func (m *RolloutAnalysis) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1455,7 +1483,7 @@ var xxx_messageInfo_RolloutAnalysis proto.InternalMessageInfo func (m *RolloutAnalysisBackground) Reset() { *m = RolloutAnalysisBackground{} } func (*RolloutAnalysisBackground) ProtoMessage() {} func (*RolloutAnalysisBackground) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{50} + return fileDescriptor_e0e705f843545fab, []int{51} } func (m *RolloutAnalysisBackground) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1483,7 +1511,7 @@ var xxx_messageInfo_RolloutAnalysisBackground proto.InternalMessageInfo func (m *RolloutAnalysisRunStatus) Reset() { *m = RolloutAnalysisRunStatus{} } func (*RolloutAnalysisRunStatus) ProtoMessage() {} func (*RolloutAnalysisRunStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{51} + return fileDescriptor_e0e705f843545fab, []int{52} } func (m *RolloutAnalysisRunStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1511,7 +1539,7 @@ var xxx_messageInfo_RolloutAnalysisRunStatus proto.InternalMessageInfo func (m *RolloutAnalysisTemplate) Reset() { *m = RolloutAnalysisTemplate{} } func (*RolloutAnalysisTemplate) ProtoMessage() {} func (*RolloutAnalysisTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{52} + return fileDescriptor_e0e705f843545fab, []int{53} } func (m *RolloutAnalysisTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1539,7 +1567,7 @@ var xxx_messageInfo_RolloutAnalysisTemplate proto.InternalMessageInfo func (m *RolloutCondition) Reset() { *m = RolloutCondition{} } func (*RolloutCondition) ProtoMessage() {} func (*RolloutCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{53} + return fileDescriptor_e0e705f843545fab, []int{54} } func (m *RolloutCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1567,7 +1595,7 @@ var xxx_messageInfo_RolloutCondition proto.InternalMessageInfo func (m *RolloutExperimentStep) Reset() { *m = RolloutExperimentStep{} } func (*RolloutExperimentStep) ProtoMessage() {} func (*RolloutExperimentStep) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{54} + return fileDescriptor_e0e705f843545fab, []int{55} } func (m *RolloutExperimentStep) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1597,7 +1625,7 @@ func (m *RolloutExperimentStepAnalysisTemplateRef) Reset() { } func (*RolloutExperimentStepAnalysisTemplateRef) ProtoMessage() {} func (*RolloutExperimentStepAnalysisTemplateRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{55} + return fileDescriptor_e0e705f843545fab, []int{56} } func (m *RolloutExperimentStepAnalysisTemplateRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1625,7 +1653,7 @@ var xxx_messageInfo_RolloutExperimentStepAnalysisTemplateRef proto.InternalMessa func (m *RolloutExperimentTemplate) Reset() { *m = RolloutExperimentTemplate{} } func (*RolloutExperimentTemplate) ProtoMessage() {} func (*RolloutExperimentTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{56} + return fileDescriptor_e0e705f843545fab, []int{57} } func (m *RolloutExperimentTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1653,7 +1681,7 @@ var xxx_messageInfo_RolloutExperimentTemplate proto.InternalMessageInfo func (m *RolloutList) Reset() { *m = RolloutList{} } func (*RolloutList) ProtoMessage() {} func (*RolloutList) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{57} + return fileDescriptor_e0e705f843545fab, []int{58} } func (m *RolloutList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1681,7 +1709,7 @@ var xxx_messageInfo_RolloutList proto.InternalMessageInfo func (m *RolloutPause) Reset() { *m = RolloutPause{} } func (*RolloutPause) ProtoMessage() {} func (*RolloutPause) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{58} + return fileDescriptor_e0e705f843545fab, []int{59} } func (m *RolloutPause) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1709,7 +1737,7 @@ var xxx_messageInfo_RolloutPause proto.InternalMessageInfo func (m *RolloutSpec) Reset() { *m = RolloutSpec{} } func (*RolloutSpec) ProtoMessage() {} func (*RolloutSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{59} + return fileDescriptor_e0e705f843545fab, []int{60} } func (m *RolloutSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1737,7 +1765,7 @@ var xxx_messageInfo_RolloutSpec proto.InternalMessageInfo func (m *RolloutStatus) Reset() { *m = RolloutStatus{} } func (*RolloutStatus) ProtoMessage() {} func (*RolloutStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{60} + return fileDescriptor_e0e705f843545fab, []int{61} } func (m *RolloutStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1765,7 +1793,7 @@ var xxx_messageInfo_RolloutStatus proto.InternalMessageInfo func (m *RolloutStrategy) Reset() { *m = RolloutStrategy{} } func (*RolloutStrategy) ProtoMessage() {} func (*RolloutStrategy) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{61} + return fileDescriptor_e0e705f843545fab, []int{62} } func (m *RolloutStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1793,7 +1821,7 @@ var xxx_messageInfo_RolloutStrategy proto.InternalMessageInfo func (m *RolloutTrafficRouting) Reset() { *m = RolloutTrafficRouting{} } func (*RolloutTrafficRouting) ProtoMessage() {} func (*RolloutTrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{62} + return fileDescriptor_e0e705f843545fab, []int{63} } func (m *RolloutTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1821,7 +1849,7 @@ var xxx_messageInfo_RolloutTrafficRouting proto.InternalMessageInfo func (m *SMITrafficRouting) Reset() { *m = SMITrafficRouting{} } func (*SMITrafficRouting) ProtoMessage() {} func (*SMITrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{63} + return fileDescriptor_e0e705f843545fab, []int{64} } func (m *SMITrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1849,7 +1877,7 @@ var xxx_messageInfo_SMITrafficRouting proto.InternalMessageInfo func (m *ScopeDetail) Reset() { *m = ScopeDetail{} } func (*ScopeDetail) ProtoMessage() {} func (*ScopeDetail) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{64} + return fileDescriptor_e0e705f843545fab, []int{65} } func (m *ScopeDetail) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1877,7 +1905,7 @@ var xxx_messageInfo_ScopeDetail proto.InternalMessageInfo func (m *SecretKeyRef) Reset() { *m = SecretKeyRef{} } func (*SecretKeyRef) ProtoMessage() {} func (*SecretKeyRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{65} + return fileDescriptor_e0e705f843545fab, []int{66} } func (m *SecretKeyRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1905,7 +1933,7 @@ var xxx_messageInfo_SecretKeyRef proto.InternalMessageInfo func (m *SetCanaryScale) Reset() { *m = SetCanaryScale{} } func (*SetCanaryScale) ProtoMessage() {} func (*SetCanaryScale) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{66} + return fileDescriptor_e0e705f843545fab, []int{67} } func (m *SetCanaryScale) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1933,7 +1961,7 @@ var xxx_messageInfo_SetCanaryScale proto.InternalMessageInfo func (m *TemplateService) Reset() { *m = TemplateService{} } func (*TemplateService) ProtoMessage() {} func (*TemplateService) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{67} + return fileDescriptor_e0e705f843545fab, []int{68} } func (m *TemplateService) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1961,7 +1989,7 @@ var xxx_messageInfo_TemplateService proto.InternalMessageInfo func (m *TemplateSpec) Reset() { *m = TemplateSpec{} } func (*TemplateSpec) ProtoMessage() {} func (*TemplateSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{68} + return fileDescriptor_e0e705f843545fab, []int{69} } func (m *TemplateSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1989,7 +2017,7 @@ var xxx_messageInfo_TemplateSpec proto.InternalMessageInfo func (m *TemplateStatus) Reset() { *m = TemplateStatus{} } func (*TemplateStatus) ProtoMessage() {} func (*TemplateStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{69} + return fileDescriptor_e0e705f843545fab, []int{70} } func (m *TemplateStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2017,7 +2045,7 @@ var xxx_messageInfo_TemplateStatus proto.InternalMessageInfo func (m *ValueFrom) Reset() { *m = ValueFrom{} } func (*ValueFrom) ProtoMessage() {} func (*ValueFrom) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{70} + return fileDescriptor_e0e705f843545fab, []int{71} } func (m *ValueFrom) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2045,7 +2073,7 @@ var xxx_messageInfo_ValueFrom proto.InternalMessageInfo func (m *WavefrontMetric) Reset() { *m = WavefrontMetric{} } func (*WavefrontMetric) ProtoMessage() {} func (*WavefrontMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{71} + return fileDescriptor_e0e705f843545fab, []int{72} } func (m *WavefrontMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2073,7 +2101,7 @@ var xxx_messageInfo_WavefrontMetric proto.InternalMessageInfo func (m *WebMetric) Reset() { *m = WebMetric{} } func (*WebMetric) ProtoMessage() {} func (*WebMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{72} + return fileDescriptor_e0e705f843545fab, []int{73} } func (m *WebMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2101,7 +2129,7 @@ var xxx_messageInfo_WebMetric proto.InternalMessageInfo func (m *WebMetricHeader) Reset() { *m = WebMetricHeader{} } func (*WebMetricHeader) ProtoMessage() {} func (*WebMetricHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{73} + return fileDescriptor_e0e705f843545fab, []int{74} } func (m *WebMetricHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2134,6 +2162,7 @@ func init() { proto.RegisterType((*AnalysisRunList)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisRunList") proto.RegisterType((*AnalysisRunSpec)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisRunSpec") proto.RegisterType((*AnalysisRunStatus)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisRunStatus") + proto.RegisterType((*AnalysisRunStrategy)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisRunStrategy") proto.RegisterType((*AnalysisTemplate)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisTemplate") proto.RegisterType((*AnalysisTemplateList)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisTemplateList") proto.RegisterType((*AnalysisTemplateSpec)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.AnalysisTemplateSpec") @@ -2212,367 +2241,371 @@ func init() { } var fileDescriptor_e0e705f843545fab = []byte{ - // 5748 bytes of a gzipped FileDescriptorProto + // 5816 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x24, 0xd9, - 0x55, 0xf0, 0x56, 0xff, 0xd8, 0xed, 0xd3, 0xfe, 0xbd, 0xe3, 0xc9, 0x74, 0x66, 0x77, 0xdc, 0x93, - 0xda, 0x68, 0xbf, 0xc9, 0x47, 0x62, 0x27, 0xb3, 0xbb, 0xb0, 0x64, 0xa3, 0x15, 0xdd, 0xf6, 0xcc, - 0x8e, 0xbd, 0xf6, 0x8c, 0xe7, 0xb6, 0x67, 0x47, 0xd9, 0x64, 0x43, 0xca, 0xdd, 0xd7, 0xed, 0x9a, - 0xa9, 0xae, 0xea, 0x54, 0x55, 0x7b, 0xc6, 0x9b, 0x28, 0x3f, 0x44, 0xf9, 0x01, 0x25, 0xca, 0xf2, - 0xf3, 0x82, 0x10, 0x08, 0x21, 0x1e, 0x10, 0xbc, 0x20, 0x94, 0xc7, 0x44, 0x44, 0x40, 0xa4, 0x20, - 0x04, 0x0a, 0x2f, 0x6c, 0x40, 0x4a, 0x93, 0xed, 0x20, 0x21, 0x78, 0x41, 0x41, 0x91, 0x50, 0x46, - 0x42, 0x42, 0xf7, 0xa7, 0x6e, 0xd5, 0xad, 0xae, 0x6e, 0x77, 0xbb, 0xcb, 0x43, 0x04, 0xbc, 0xb9, - 0xef, 0x39, 0xf7, 0x9c, 0x7b, 0xeb, 0x9e, 0x7b, 0x7e, 0xee, 0x39, 0xf7, 0x1a, 0xb6, 0x9b, 0xa6, - 0x7f, 0xd8, 0xd9, 0x5f, 0xad, 0x3b, 0xad, 0x35, 0xc3, 0x6d, 0x3a, 0x6d, 0xd7, 0xb9, 0xc7, 0xfe, - 0x78, 0x9f, 0xeb, 0x58, 0x96, 0xd3, 0xf1, 0xbd, 0xb5, 0xf6, 0xfd, 0xe6, 0x9a, 0xd1, 0x36, 0xbd, - 0x35, 0xd9, 0x72, 0xf4, 0x01, 0xc3, 0x6a, 0x1f, 0x1a, 0x1f, 0x58, 0x6b, 0x12, 0x9b, 0xb8, 0x86, - 0x4f, 0x1a, 0xab, 0x6d, 0xd7, 0xf1, 0x1d, 0xf4, 0xa1, 0x90, 0xda, 0x6a, 0x40, 0x8d, 0xfd, 0xf1, - 0x8b, 0x41, 0xdf, 0xd5, 0xf6, 0xfd, 0xe6, 0x2a, 0xa5, 0xb6, 0x2a, 0x5b, 0x02, 0x6a, 0x17, 0xdf, - 0x17, 0x19, 0x4b, 0xd3, 0x69, 0x3a, 0x6b, 0x8c, 0xe8, 0x7e, 0xe7, 0x80, 0xfd, 0x62, 0x3f, 0xd8, - 0x5f, 0x9c, 0xd9, 0xc5, 0xa7, 0xef, 0xbf, 0xe0, 0xad, 0x9a, 0x0e, 0x1d, 0xdb, 0xda, 0xbe, 0xe1, - 0xd7, 0x0f, 0xd7, 0x8e, 0xfa, 0x46, 0x74, 0x51, 0x8f, 0x20, 0xd5, 0x1d, 0x97, 0x24, 0xe1, 0x3c, - 0x17, 0xe2, 0xb4, 0x8c, 0xfa, 0xa1, 0x69, 0x13, 0xf7, 0x38, 0x9c, 0x75, 0x8b, 0xf8, 0x46, 0x52, - 0xaf, 0xb5, 0x41, 0xbd, 0xdc, 0x8e, 0xed, 0x9b, 0x2d, 0xd2, 0xd7, 0xe1, 0x67, 0x4f, 0xea, 0xe0, - 0xd5, 0x0f, 0x49, 0xcb, 0xe8, 0xeb, 0xf7, 0xec, 0xa0, 0x7e, 0x1d, 0xdf, 0xb4, 0xd6, 0x4c, 0xdb, - 0xf7, 0x7c, 0x37, 0xde, 0x49, 0xff, 0x77, 0x0d, 0x96, 0x2a, 0xdb, 0xd5, 0x3d, 0xd7, 0x38, 0x38, - 0x30, 0xeb, 0xd8, 0xe9, 0xf8, 0xa6, 0xdd, 0x44, 0xef, 0x81, 0x69, 0xd3, 0x6e, 0xba, 0xc4, 0xf3, - 0x4a, 0xda, 0x65, 0xed, 0xca, 0x4c, 0x75, 0xe1, 0x3b, 0xdd, 0xf2, 0x13, 0xbd, 0x6e, 0x79, 0x7a, - 0x93, 0x37, 0xe3, 0x00, 0x8e, 0x9e, 0x87, 0xa2, 0x47, 0xdc, 0x23, 0xb3, 0x4e, 0x76, 0x1d, 0xd7, - 0x2f, 0x65, 0x2e, 0x6b, 0x57, 0xf2, 0xd5, 0x73, 0x02, 0xbd, 0x58, 0x0b, 0x41, 0x38, 0x8a, 0x47, - 0xbb, 0xb9, 0x8e, 0xe3, 0x0b, 0x78, 0x29, 0xcb, 0xb8, 0xc8, 0x6e, 0x38, 0x04, 0xe1, 0x28, 0x1e, - 0xda, 0x80, 0x45, 0xc3, 0xb6, 0x1d, 0xdf, 0xf0, 0x4d, 0xc7, 0xde, 0x75, 0xc9, 0x81, 0xf9, 0xb0, - 0x94, 0x63, 0x7d, 0x4b, 0xa2, 0xef, 0x62, 0x25, 0x06, 0xc7, 0x7d, 0x3d, 0xf4, 0x0d, 0x28, 0x55, - 0x5a, 0xfb, 0x86, 0xe7, 0x19, 0x0d, 0xc7, 0x8d, 0x4d, 0xfd, 0x0a, 0x14, 0x5a, 0x46, 0xbb, 0x6d, - 0xda, 0x4d, 0x3a, 0xf7, 0xec, 0x95, 0x99, 0xea, 0x6c, 0xaf, 0x5b, 0x2e, 0xec, 0x88, 0x36, 0x2c, - 0xa1, 0xfa, 0xdf, 0x67, 0xa0, 0x58, 0xb1, 0x0d, 0xeb, 0xd8, 0x33, 0x3d, 0xdc, 0xb1, 0xd1, 0xc7, - 0xa1, 0x40, 0x65, 0xa0, 0x61, 0xf8, 0x06, 0xfb, 0x6a, 0xc5, 0xab, 0xef, 0x5f, 0xe5, 0x4b, 0xb2, - 0x1a, 0x5d, 0x92, 0x50, 0xb2, 0x29, 0xf6, 0xea, 0xd1, 0x07, 0x56, 0x6f, 0xed, 0xdf, 0x23, 0x75, - 0x7f, 0x87, 0xf8, 0x46, 0x15, 0x89, 0x59, 0x40, 0xd8, 0x86, 0x25, 0x55, 0xe4, 0x40, 0xce, 0x6b, - 0x93, 0x3a, 0xfb, 0xc8, 0xc5, 0xab, 0x3b, 0xab, 0x93, 0xec, 0xa2, 0xd5, 0xc8, 0xd0, 0x6b, 0x6d, - 0x52, 0xaf, 0xce, 0x0a, 0xd6, 0x39, 0xfa, 0x0b, 0x33, 0x46, 0xe8, 0x01, 0x4c, 0x79, 0xbe, 0xe1, - 0x77, 0x3c, 0xb6, 0x40, 0xc5, 0xab, 0xb7, 0xd2, 0x63, 0xc9, 0xc8, 0x56, 0xe7, 0x05, 0xd3, 0x29, - 0xfe, 0x1b, 0x0b, 0x76, 0xfa, 0x3f, 0x68, 0x70, 0x2e, 0x82, 0x5d, 0x71, 0x9b, 0x9d, 0x16, 0xb1, - 0x7d, 0x74, 0x19, 0x72, 0xb6, 0xd1, 0x22, 0x42, 0x2a, 0xe5, 0x90, 0x6f, 0x1a, 0x2d, 0x82, 0x19, - 0x04, 0x3d, 0x0d, 0xf9, 0x23, 0xc3, 0xea, 0x10, 0xf6, 0x91, 0x66, 0xaa, 0x73, 0x02, 0x25, 0xff, - 0x2a, 0x6d, 0xc4, 0x1c, 0x86, 0x3e, 0x05, 0x33, 0xec, 0x8f, 0xeb, 0xae, 0xd3, 0x4a, 0x69, 0x6a, - 0x62, 0x84, 0xaf, 0x06, 0x64, 0xab, 0x73, 0xbd, 0x6e, 0x79, 0x46, 0xfe, 0xc4, 0x21, 0x43, 0xfd, - 0x1f, 0x35, 0x58, 0x88, 0x4c, 0x6e, 0xdb, 0xf4, 0x7c, 0xf4, 0xd1, 0x3e, 0xe1, 0x59, 0x1d, 0x4d, - 0x78, 0x68, 0x6f, 0x26, 0x3a, 0x8b, 0x62, 0xa6, 0x85, 0xa0, 0x25, 0x22, 0x38, 0x36, 0xe4, 0x4d, - 0x9f, 0xb4, 0xbc, 0x52, 0xe6, 0x72, 0xf6, 0x4a, 0xf1, 0xea, 0x66, 0x6a, 0xcb, 0x18, 0x7e, 0xdf, - 0x4d, 0x4a, 0x1f, 0x73, 0x36, 0xfa, 0x6f, 0x67, 0x94, 0x19, 0x52, 0x89, 0x42, 0x0e, 0x4c, 0xb7, - 0x88, 0xef, 0x9a, 0x75, 0xbe, 0xaf, 0x8a, 0x57, 0x37, 0x26, 0x1b, 0xc5, 0x0e, 0x23, 0x16, 0x6a, - 0x26, 0xfe, 0xdb, 0xc3, 0x01, 0x17, 0x74, 0x08, 0x39, 0xc3, 0x6d, 0x06, 0x73, 0xbe, 0x9e, 0xce, - 0xfa, 0x86, 0x32, 0x57, 0x71, 0x9b, 0x1e, 0x66, 0x1c, 0xd0, 0x1a, 0xcc, 0xf8, 0xc4, 0x6d, 0x99, - 0xb6, 0xe1, 0x73, 0x55, 0x56, 0xa8, 0x2e, 0x09, 0xb4, 0x99, 0xbd, 0x00, 0x80, 0x43, 0x1c, 0xfd, - 0xad, 0x0c, 0x2c, 0xf5, 0x6d, 0x06, 0xf4, 0x1c, 0xe4, 0xdb, 0x87, 0x86, 0x17, 0x48, 0xf7, 0x4a, - 0xf0, 0x69, 0x77, 0x69, 0xe3, 0xa3, 0x6e, 0x79, 0x2e, 0xe8, 0xc2, 0x1a, 0x30, 0x47, 0xa6, 0xba, - 0xba, 0x45, 0x3c, 0xcf, 0x68, 0x06, 0x22, 0x1f, 0xf9, 0x22, 0xac, 0x19, 0x07, 0x70, 0xf4, 0x25, - 0x0d, 0xe6, 0xf8, 0xd7, 0xc1, 0xc4, 0xeb, 0x58, 0x3e, 0xdd, 0xd6, 0xf4, 0xdb, 0x6c, 0xa5, 0xb1, - 0x12, 0x9c, 0x64, 0xf5, 0xbc, 0xe0, 0x3e, 0x17, 0x6d, 0xf5, 0xb0, 0xca, 0x17, 0xdd, 0x85, 0x19, - 0xcf, 0x37, 0x5c, 0x9f, 0x34, 0x2a, 0x3e, 0x53, 0xe0, 0xc5, 0xab, 0xff, 0x7f, 0x34, 0x79, 0xdf, - 0x33, 0x5b, 0x84, 0xef, 0xad, 0x5a, 0x40, 0x00, 0x87, 0xb4, 0xf4, 0x7f, 0xd5, 0x60, 0x31, 0xf8, - 0x4c, 0x7b, 0xa4, 0xd5, 0xb6, 0x0c, 0x9f, 0x3c, 0x06, 0xcd, 0xec, 0x2b, 0x9a, 0x19, 0xa7, 0xb3, - 0xbf, 0x82, 0xf1, 0x0f, 0x52, 0xcf, 0xfa, 0xbf, 0x68, 0xb0, 0x1c, 0x47, 0x7e, 0x0c, 0xda, 0xc4, - 0x53, 0xb5, 0xc9, 0xcd, 0x74, 0x67, 0x3b, 0x40, 0xa5, 0xfc, 0x28, 0x61, 0xae, 0xff, 0xc3, 0xf5, - 0x8a, 0xfe, 0x07, 0x39, 0x98, 0xad, 0xd8, 0xbe, 0x59, 0x39, 0x38, 0x30, 0x6d, 0xd3, 0x3f, 0x46, - 0x5f, 0xc9, 0xc0, 0x5a, 0xdb, 0x25, 0x07, 0xc4, 0x75, 0x49, 0x63, 0xa3, 0xe3, 0x9a, 0x76, 0xb3, - 0x56, 0x3f, 0x24, 0x8d, 0x8e, 0x65, 0xda, 0xcd, 0xcd, 0xa6, 0xed, 0xc8, 0xe6, 0x6b, 0x0f, 0x49, - 0xbd, 0x43, 0x5d, 0x1e, 0xb1, 0xfe, 0xad, 0xc9, 0x86, 0xb9, 0x3b, 0x1e, 0xd3, 0xea, 0xb3, 0xbd, - 0x6e, 0x79, 0x6d, 0xcc, 0x4e, 0x78, 0xdc, 0xa9, 0xa1, 0x2f, 0x67, 0x60, 0xd5, 0x25, 0x9f, 0xe8, - 0x98, 0xa3, 0x7f, 0x0d, 0xbe, 0x41, 0xad, 0xc9, 0xbe, 0x06, 0x1e, 0x8b, 0x67, 0xf5, 0x6a, 0xaf, - 0x5b, 0x1e, 0xb3, 0x0f, 0x1e, 0x73, 0x5e, 0xfa, 0x9f, 0x6b, 0x50, 0x18, 0xc3, 0x4b, 0x2a, 0xab, - 0x5e, 0xd2, 0x4c, 0x9f, 0x87, 0xe4, 0xf7, 0x7b, 0x48, 0x2f, 0x4f, 0xf6, 0xd1, 0x46, 0xf1, 0x8c, - 0xfe, 0x8d, 0x46, 0x23, 0x71, 0x4f, 0x0a, 0x1d, 0xc2, 0x72, 0xdb, 0x69, 0x04, 0x9b, 0xfe, 0x86, - 0xe1, 0x1d, 0x32, 0x98, 0x98, 0xde, 0x73, 0xbd, 0x6e, 0x79, 0x79, 0x37, 0x01, 0xfe, 0xa8, 0x5b, - 0x2e, 0x49, 0x22, 0x31, 0x04, 0x9c, 0x48, 0x11, 0xb5, 0xa1, 0x70, 0x60, 0x12, 0xab, 0x81, 0xc9, - 0x81, 0x90, 0x94, 0x09, 0xb7, 0xf7, 0x75, 0x41, 0x8d, 0x07, 0x11, 0xc1, 0x2f, 0x2c, 0xb9, 0xe8, - 0x3f, 0xc9, 0xc1, 0x42, 0xd5, 0xea, 0x90, 0x97, 0x5d, 0x42, 0x02, 0x3f, 0xa0, 0x02, 0x0b, 0x6d, - 0x97, 0x1c, 0x99, 0xe4, 0x41, 0x8d, 0x58, 0xa4, 0xee, 0x3b, 0xae, 0x98, 0xea, 0x05, 0xb1, 0x92, - 0x0b, 0xbb, 0x2a, 0x18, 0xc7, 0xf1, 0xd1, 0x4b, 0x30, 0x6f, 0xd4, 0x7d, 0xf3, 0x88, 0x48, 0x0a, - 0x7c, 0xa1, 0xdf, 0x21, 0x28, 0xcc, 0x57, 0x14, 0x28, 0x8e, 0x61, 0xa3, 0x8f, 0x42, 0xc9, 0xab, - 0x1b, 0x16, 0xb9, 0xd3, 0x16, 0xac, 0xd6, 0x0f, 0x49, 0xfd, 0xfe, 0xae, 0x63, 0xda, 0xbe, 0x70, - 0x70, 0x2e, 0x0b, 0x4a, 0xa5, 0xda, 0x00, 0x3c, 0x3c, 0x90, 0x02, 0xfa, 0x53, 0x0d, 0x2e, 0xb5, - 0x5d, 0xb2, 0xeb, 0x3a, 0x2d, 0x87, 0x4a, 0x6f, 0x9f, 0x2b, 0x24, 0x5c, 0x82, 0x57, 0x27, 0xdc, - 0xa6, 0xbc, 0xa5, 0x3f, 0xea, 0x78, 0x57, 0xaf, 0x5b, 0xbe, 0xb4, 0x3b, 0x6c, 0x00, 0x78, 0xf8, - 0xf8, 0xd0, 0x9f, 0x69, 0xb0, 0xd2, 0x76, 0x3c, 0x7f, 0xc8, 0x14, 0xf2, 0x67, 0x3a, 0x05, 0xbd, - 0xd7, 0x2d, 0xaf, 0xec, 0x0e, 0x1d, 0x01, 0x3e, 0x61, 0x84, 0x7a, 0xaf, 0x08, 0x4b, 0x11, 0xd9, - 0x73, 0x0d, 0x9f, 0x34, 0x8f, 0xd1, 0x8b, 0x30, 0x17, 0x08, 0x03, 0x8f, 0xcd, 0xb9, 0xec, 0x49, - 0xbf, 0xae, 0x12, 0x05, 0x62, 0x15, 0x97, 0xca, 0x9d, 0x14, 0x45, 0xde, 0x3b, 0x26, 0x77, 0xbb, - 0x0a, 0x14, 0xc7, 0xb0, 0xd1, 0x26, 0x9c, 0x13, 0x2d, 0x98, 0xb4, 0x2d, 0xb3, 0x6e, 0xac, 0x3b, - 0x1d, 0x21, 0x72, 0xf9, 0xea, 0x85, 0x5e, 0xb7, 0x7c, 0x6e, 0xb7, 0x1f, 0x8c, 0x93, 0xfa, 0xa0, - 0x6d, 0x58, 0x36, 0x3a, 0xbe, 0x23, 0xe7, 0x7f, 0xcd, 0x36, 0xf6, 0x2d, 0xd2, 0x60, 0xa2, 0x55, - 0xa8, 0x96, 0xa8, 0xd6, 0xa8, 0x24, 0xc0, 0x71, 0x62, 0x2f, 0xb4, 0x1b, 0xa3, 0x56, 0x23, 0x75, - 0xc7, 0x6e, 0xf0, 0x55, 0xce, 0x57, 0x9f, 0x12, 0xd3, 0x53, 0x29, 0x0a, 0x1c, 0x9c, 0xd8, 0x13, - 0x59, 0x30, 0xdf, 0x32, 0x1e, 0xde, 0xb1, 0x8d, 0x23, 0xc3, 0xb4, 0x28, 0x93, 0xd2, 0xd4, 0x09, - 0xae, 0x69, 0xc7, 0x37, 0xad, 0x55, 0x7e, 0x8e, 0xb3, 0xba, 0x69, 0xfb, 0xb7, 0xdc, 0x9a, 0x4f, - 0x8d, 0x40, 0x15, 0xd1, 0x0f, 0xbb, 0xa3, 0xd0, 0xc2, 0x31, 0xda, 0xe8, 0x16, 0x9c, 0x67, 0xdb, - 0x71, 0xc3, 0x79, 0x60, 0x6f, 0x10, 0xcb, 0x38, 0x0e, 0x26, 0x30, 0xcd, 0x26, 0xf0, 0xce, 0x5e, - 0xb7, 0x7c, 0xbe, 0x96, 0x84, 0x80, 0x93, 0xfb, 0x21, 0x03, 0x9e, 0x54, 0x01, 0x98, 0x1c, 0x99, - 0x9e, 0xe9, 0xd8, 0xdb, 0x66, 0xcb, 0xf4, 0x4b, 0x05, 0x46, 0xb6, 0xdc, 0xeb, 0x96, 0x9f, 0xac, - 0x0d, 0x46, 0xc3, 0xc3, 0x68, 0xa0, 0xdf, 0xd2, 0x60, 0x39, 0x69, 0x1b, 0x96, 0x66, 0xd2, 0x38, - 0xff, 0x88, 0x6d, 0x2d, 0x2e, 0x11, 0x89, 0x4a, 0x21, 0x71, 0x10, 0xe8, 0xb3, 0x1a, 0xcc, 0x1a, - 0x11, 0xe7, 0xac, 0x04, 0x6c, 0x54, 0x5b, 0x93, 0x7a, 0xc3, 0x21, 0xc5, 0xea, 0x62, 0xaf, 0x5b, - 0x56, 0x1c, 0x40, 0xac, 0x70, 0x44, 0xbf, 0xa3, 0xc1, 0xf9, 0xc4, 0x3d, 0x5e, 0x2a, 0x9e, 0xc5, - 0x17, 0x62, 0x42, 0x92, 0xac, 0x73, 0x92, 0x87, 0x81, 0xde, 0xd4, 0xa4, 0x29, 0xdb, 0x09, 0xe2, - 0x91, 0x59, 0x36, 0xb4, 0xdb, 0x13, 0xfa, 0xa3, 0xa1, 0xf5, 0x0e, 0x08, 0x57, 0xcf, 0x45, 0x2c, - 0x63, 0xd0, 0x88, 0xe3, 0xec, 0xd1, 0x57, 0xb5, 0xc0, 0x34, 0xca, 0x11, 0xcd, 0x9d, 0xd5, 0x88, - 0x50, 0x68, 0x69, 0xe5, 0x80, 0x62, 0xcc, 0xd1, 0xc7, 0xe0, 0xa2, 0xb1, 0xef, 0xb8, 0x7e, 0xe2, - 0xe6, 0x2b, 0xcd, 0xb3, 0x6d, 0xb4, 0xd2, 0xeb, 0x96, 0x2f, 0x56, 0x06, 0x62, 0xe1, 0x21, 0x14, - 0xf4, 0x7f, 0xce, 0xc2, 0xec, 0xba, 0x61, 0x1b, 0xee, 0xb1, 0x30, 0x5d, 0xdf, 0xd0, 0xe0, 0xa9, - 0x7a, 0xc7, 0x75, 0x89, 0xed, 0xd7, 0x7c, 0xd2, 0xee, 0x37, 0x5c, 0xda, 0x99, 0x1a, 0xae, 0xcb, - 0xbd, 0x6e, 0xf9, 0xa9, 0xf5, 0x21, 0xfc, 0xf1, 0xd0, 0xd1, 0xa1, 0xbf, 0xd1, 0x40, 0x17, 0x08, - 0x55, 0xa3, 0x7e, 0xbf, 0xe9, 0x3a, 0x1d, 0xbb, 0xd1, 0x3f, 0x89, 0xcc, 0x99, 0x4e, 0xe2, 0x99, - 0x5e, 0xb7, 0xac, 0xaf, 0x9f, 0x38, 0x0a, 0x3c, 0xc2, 0x48, 0xd1, 0xcb, 0xb0, 0x24, 0xb0, 0xae, - 0x3d, 0x6c, 0x13, 0xd7, 0xa4, 0xbe, 0xaf, 0x38, 0x0f, 0x7f, 0xa7, 0x30, 0x2b, 0x4b, 0xeb, 0x71, - 0x04, 0xdc, 0xdf, 0x47, 0xff, 0xe3, 0x1c, 0x40, 0xb0, 0xd2, 0xa4, 0x8d, 0x7e, 0x06, 0x66, 0x3c, - 0xe2, 0xdf, 0x25, 0x66, 0xf3, 0xd0, 0x67, 0x6b, 0x9a, 0x17, 0xc7, 0x26, 0x41, 0x23, 0x0e, 0xe1, - 0xe8, 0x3e, 0xe4, 0xdb, 0x46, 0xc7, 0x23, 0xe2, 0xbb, 0x6d, 0xa5, 0xf2, 0xdd, 0x76, 0x29, 0x45, - 0x1e, 0x5b, 0xb0, 0x3f, 0x31, 0xe7, 0x81, 0x3e, 0xaf, 0x01, 0x10, 0x75, 0xae, 0xc5, 0xab, 0xb5, - 0x54, 0x58, 0x86, 0x9f, 0x83, 0x7e, 0x83, 0xea, 0x7c, 0xaf, 0x5b, 0x86, 0xc8, 0x57, 0x8b, 0xb0, - 0x45, 0x0f, 0xa0, 0x60, 0x04, 0xea, 0x32, 0x77, 0x16, 0xea, 0x92, 0xb9, 0xfc, 0x72, 0xbd, 0x25, - 0x33, 0xf4, 0x65, 0x0d, 0xe6, 0x3d, 0xe2, 0x8b, 0xa5, 0xa2, 0x9b, 0x56, 0xf8, 0x8a, 0xdb, 0x93, - 0xf1, 0xaf, 0x29, 0x34, 0xb9, 0xf2, 0x51, 0xdb, 0x70, 0x8c, 0xaf, 0xfe, 0x66, 0x11, 0xe6, 0x03, - 0x91, 0x09, 0xdd, 0xbf, 0x3a, 0x6f, 0x49, 0x76, 0xff, 0xd6, 0xa3, 0x40, 0xac, 0xe2, 0xd2, 0xce, - 0x9e, 0x4f, 0xfd, 0x0d, 0xd5, 0xfb, 0x93, 0x9d, 0x6b, 0x51, 0x20, 0x56, 0x71, 0x51, 0x0b, 0xf2, - 0x9e, 0x4f, 0xda, 0xc1, 0xa1, 0xe4, 0x8d, 0xc9, 0xbe, 0x46, 0xb8, 0x13, 0xc2, 0x03, 0x25, 0xfa, - 0xcb, 0xc3, 0x9c, 0x0b, 0xfa, 0x9a, 0x06, 0xf3, 0xbe, 0x92, 0xfb, 0x11, 0x62, 0x90, 0x8e, 0x24, - 0xaa, 0x69, 0x25, 0xbe, 0x1a, 0x6a, 0x1b, 0x8e, 0xb1, 0x4f, 0xf0, 0x08, 0xf3, 0x67, 0xe8, 0x11, - 0xbe, 0x06, 0x85, 0x96, 0xf1, 0xb0, 0xd6, 0x71, 0x9b, 0xa7, 0xf7, 0x3c, 0x45, 0x6a, 0x8c, 0x53, - 0xc1, 0x92, 0x1e, 0xfa, 0x9c, 0x16, 0xd9, 0x5c, 0xd3, 0x8c, 0xf8, 0xdd, 0x74, 0x37, 0x97, 0x54, - 0xa8, 0x03, 0xb7, 0x59, 0x9f, 0x7f, 0x56, 0x78, 0xec, 0xfe, 0x19, 0xf5, 0x35, 0xf8, 0x06, 0x91, - 0xbe, 0xc6, 0xcc, 0x99, 0xfa, 0x1a, 0xeb, 0x0a, 0x33, 0x1c, 0x63, 0xce, 0xc6, 0xc3, 0xf7, 0x9c, - 0x1c, 0x0f, 0x9c, 0xe9, 0x78, 0x6a, 0x0a, 0x33, 0x1c, 0x63, 0x3e, 0x38, 0x28, 0x29, 0x9e, 0x4d, - 0x50, 0x32, 0x9b, 0x42, 0x50, 0x32, 0xdc, 0x5f, 0x9b, 0x9b, 0xd8, 0x5f, 0xfb, 0x91, 0x06, 0x17, - 0xd6, 0xad, 0x8e, 0xe7, 0x13, 0xf7, 0x7f, 0x4d, 0x1e, 0xe3, 0x3f, 0x34, 0x78, 0x72, 0xc0, 0x9c, - 0x1f, 0x43, 0x3a, 0xe3, 0x0d, 0x35, 0x9d, 0x71, 0x67, 0x42, 0xbb, 0x93, 0x3c, 0x8f, 0x01, 0x59, - 0x0d, 0x1f, 0xe6, 0x36, 0x0c, 0xdf, 0x68, 0x38, 0x4d, 0x9e, 0x66, 0x40, 0x2f, 0x41, 0xc1, 0xb4, - 0x7d, 0xe2, 0x1e, 0x19, 0x96, 0xb0, 0xbc, 0x7a, 0x30, 0xf4, 0x4d, 0xd1, 0xfe, 0xa8, 0x5b, 0x9e, - 0xdf, 0xe8, 0xb8, 0xac, 0xa0, 0x81, 0xeb, 0x61, 0x2c, 0xfb, 0xa0, 0xa7, 0x21, 0xff, 0x89, 0x0e, - 0x71, 0x8f, 0xe3, 0xe9, 0xef, 0xdb, 0xb4, 0x11, 0x73, 0x98, 0xfe, 0x77, 0x19, 0x88, 0x78, 0x45, - 0x8f, 0x41, 0xac, 0x6c, 0x45, 0xac, 0x26, 0xf4, 0x73, 0x22, 0x3e, 0xde, 0xa0, 0xba, 0x85, 0xa3, - 0x58, 0xdd, 0xc2, 0xcd, 0xd4, 0x38, 0x0e, 0x2f, 0x5b, 0x78, 0x4b, 0x83, 0x27, 0x43, 0xe4, 0x7e, - 0x5f, 0xff, 0xe4, 0x83, 0xf9, 0xe7, 0xa1, 0x68, 0x84, 0xdd, 0xc4, 0x2a, 0xca, 0xba, 0x98, 0x08, - 0x45, 0x1c, 0xc5, 0x0b, 0x53, 0xc7, 0xd9, 0x53, 0xa6, 0x8e, 0x73, 0xc3, 0x53, 0xc7, 0xfa, 0x8f, - 0x33, 0x70, 0xa9, 0x7f, 0x66, 0x81, 0x74, 0x63, 0x72, 0x30, 0xc2, 0xdc, 0x5e, 0x80, 0x59, 0x5f, - 0x74, 0xa0, 0xad, 0x62, 0x72, 0xcb, 0x02, 0x73, 0x76, 0x2f, 0x02, 0xc3, 0x0a, 0x26, 0xed, 0x59, - 0xe7, 0xfb, 0xaa, 0x56, 0x77, 0xda, 0x41, 0x8e, 0x5d, 0xf6, 0x5c, 0x8f, 0xc0, 0xb0, 0x82, 0x29, - 0x93, 0x75, 0xb9, 0x33, 0x2f, 0x02, 0xa8, 0xc1, 0xf9, 0x20, 0x67, 0x73, 0xdd, 0x71, 0xd7, 0x9d, - 0x56, 0xdb, 0x22, 0x2c, 0xe5, 0x94, 0x67, 0x83, 0xbd, 0x24, 0xba, 0x9c, 0xc7, 0x49, 0x48, 0x38, - 0xb9, 0xaf, 0xfe, 0x56, 0x16, 0xce, 0x85, 0x9f, 0x7d, 0xdd, 0xb1, 0x1b, 0x26, 0xcb, 0x7c, 0xbd, - 0x08, 0x39, 0xff, 0xb8, 0x1d, 0x7c, 0xec, 0xff, 0x17, 0x0c, 0x67, 0xef, 0xb8, 0x4d, 0x57, 0xfb, - 0x42, 0x42, 0x17, 0x0a, 0xc2, 0xac, 0x13, 0xda, 0x96, 0xbb, 0x83, 0xaf, 0xc0, 0x73, 0xaa, 0x34, - 0x3f, 0xea, 0x96, 0x13, 0xaa, 0xe1, 0x56, 0x25, 0x25, 0x55, 0xe6, 0xd1, 0x3d, 0x98, 0xb7, 0x0c, - 0xcf, 0xbf, 0xd3, 0x6e, 0x18, 0x3e, 0xd9, 0x33, 0x5b, 0x44, 0xec, 0xb9, 0x71, 0xf2, 0xf9, 0xf2, - 0x78, 0x78, 0x5b, 0xa1, 0x84, 0x63, 0x94, 0xd1, 0x11, 0x20, 0xda, 0xb2, 0xe7, 0x1a, 0xb6, 0xc7, - 0x67, 0x45, 0xf9, 0x8d, 0x5f, 0x3f, 0x70, 0x51, 0xf0, 0x43, 0xdb, 0x7d, 0xd4, 0x70, 0x02, 0x07, - 0xf4, 0x0c, 0x4c, 0xb9, 0xc4, 0xf0, 0xc4, 0x62, 0xce, 0x84, 0xfb, 0x1f, 0xb3, 0x56, 0x2c, 0xa0, - 0xd1, 0x0d, 0x35, 0x75, 0xc2, 0x86, 0xfa, 0xbe, 0x06, 0xf3, 0xe1, 0x32, 0x3d, 0x06, 0x33, 0xd7, - 0x52, 0xcd, 0xdc, 0x8d, 0xb4, 0x54, 0xe2, 0x00, 0xcb, 0xf6, 0x27, 0xb9, 0xe8, 0xfc, 0x58, 0xa6, - 0xfe, 0x93, 0x30, 0x13, 0xec, 0xea, 0x20, 0x57, 0x3f, 0xa1, 0x37, 0xae, 0x78, 0x16, 0x91, 0x92, - 0x1b, 0xc1, 0x04, 0x87, 0xfc, 0xa8, 0x61, 0x6d, 0x08, 0xa3, 0x29, 0xc4, 0x5e, 0x1a, 0xd6, 0xc0, - 0x98, 0x26, 0x19, 0xd6, 0xa0, 0x0f, 0xba, 0x03, 0x17, 0xda, 0xae, 0xc3, 0x6a, 0x1e, 0x37, 0x88, - 0xd1, 0xb0, 0x4c, 0x9b, 0x04, 0x4e, 0x1f, 0xcf, 0x4e, 0x3c, 0xd9, 0xeb, 0x96, 0x2f, 0xec, 0x26, - 0xa3, 0xe0, 0x41, 0x7d, 0xd5, 0xd2, 0xa1, 0xdc, 0xc9, 0xa5, 0x43, 0xe8, 0x97, 0x65, 0x68, 0x45, - 0xbc, 0x52, 0x9e, 0x7d, 0xc4, 0x8f, 0xa4, 0xb5, 0x94, 0x09, 0x6a, 0x3d, 0x14, 0xa9, 0x8a, 0x60, - 0x8a, 0x25, 0xfb, 0xc1, 0xfe, 0xfb, 0xd4, 0xe9, 0xfc, 0x77, 0xfd, 0x0b, 0x79, 0x58, 0x8c, 0x1b, - 0xdb, 0xb3, 0x2f, 0x8b, 0xfa, 0x35, 0x0d, 0x16, 0x03, 0x41, 0xe1, 0x3c, 0x49, 0x70, 0x08, 0xb1, - 0x9d, 0x92, 0x7c, 0x72, 0xb7, 0x41, 0xd6, 0xa8, 0xee, 0xc5, 0xb8, 0xe1, 0x3e, 0xfe, 0xe8, 0x75, - 0x28, 0xca, 0x58, 0xfd, 0x54, 0x35, 0x52, 0x0b, 0xcc, 0x61, 0x08, 0x49, 0xe0, 0x28, 0x3d, 0xf4, - 0x05, 0x0d, 0xa0, 0x1e, 0x68, 0xf4, 0x40, 0x90, 0x6e, 0xa7, 0x25, 0x48, 0xd2, 0x56, 0x84, 0x7e, - 0xa1, 0x6c, 0xf2, 0x70, 0x84, 0x31, 0xfa, 0x75, 0x16, 0xa5, 0x4b, 0x47, 0x86, 0x8a, 0x0e, 0x1d, - 0xc9, 0x87, 0xd3, 0x16, 0xe9, 0xf0, 0xec, 0x56, 0x7a, 0x0d, 0x11, 0x90, 0x87, 0x95, 0x41, 0xe8, - 0x2f, 0x82, 0xcc, 0xd5, 0xd3, 0x1d, 0xca, 0xb2, 0xf5, 0xbb, 0x86, 0x7f, 0x28, 0x44, 0x50, 0xee, - 0xd0, 0xeb, 0x01, 0x00, 0x87, 0x38, 0xfa, 0x5f, 0x68, 0xb0, 0xbc, 0xe9, 0xf9, 0xa6, 0xb3, 0x41, - 0x3c, 0x9f, 0x6e, 0x5a, 0x6a, 0xdf, 0x3b, 0x16, 0x19, 0xc1, 0x43, 0xda, 0x80, 0x45, 0x71, 0xa0, - 0xd6, 0xd9, 0xf7, 0x88, 0x1f, 0xf1, 0x92, 0xa4, 0xe8, 0xac, 0xc7, 0xe0, 0xb8, 0xaf, 0x07, 0xa5, - 0x22, 0x4e, 0xd6, 0x42, 0x2a, 0x59, 0x95, 0x4a, 0x2d, 0x06, 0xc7, 0x7d, 0x3d, 0xf4, 0x6f, 0x66, - 0xe0, 0x1c, 0x9b, 0x46, 0xac, 0x40, 0xfa, 0x57, 0x35, 0x98, 0x3f, 0x32, 0x5d, 0xbf, 0x63, 0x58, - 0xd1, 0x23, 0xc2, 0x89, 0xa5, 0x87, 0xf1, 0x7a, 0x55, 0x21, 0x1c, 0xfa, 0x05, 0x6a, 0x3b, 0x8e, - 0x0d, 0x80, 0x8e, 0x69, 0xa1, 0xa1, 0x7e, 0xed, 0x74, 0x42, 0xd8, 0xa4, 0x75, 0xe4, 0x89, 0xa6, - 0x58, 0x23, 0x8e, 0xf3, 0xd7, 0x3f, 0x22, 0x3e, 0x9f, 0x3a, 0xf4, 0x11, 0x84, 0x40, 0x87, 0x29, - 0xd7, 0xe9, 0x50, 0x1b, 0x99, 0x61, 0xf5, 0xe7, 0xc0, 0x1c, 0x0d, 0xd6, 0x82, 0x05, 0x44, 0xff, - 0x23, 0x0d, 0x66, 0xb6, 0x9c, 0x7d, 0x11, 0x34, 0x7e, 0x2c, 0x85, 0x00, 0x4e, 0xea, 0x79, 0x79, - 0x5a, 0x13, 0xba, 0x0e, 0x2f, 0x29, 0xe1, 0xdb, 0x53, 0x11, 0xda, 0xab, 0xec, 0x42, 0x05, 0x25, - 0xb5, 0xe5, 0xec, 0x0f, 0x8c, 0xef, 0x7f, 0x2f, 0x0f, 0x73, 0xaf, 0x18, 0xc7, 0xc4, 0xf6, 0x0d, - 0x31, 0xe2, 0xf7, 0xc0, 0xb4, 0xd1, 0x68, 0x24, 0x5d, 0x30, 0xa8, 0xf0, 0x66, 0x1c, 0xc0, 0x59, - 0x44, 0xd4, 0x66, 0x79, 0xfd, 0x88, 0xed, 0x0e, 0x23, 0xa2, 0x10, 0x84, 0xa3, 0x78, 0xe1, 0x56, - 0x5a, 0x77, 0xec, 0x03, 0xb3, 0x99, 0xb4, 0x09, 0xd6, 0x63, 0x70, 0xdc, 0xd7, 0x03, 0x6d, 0x01, - 0x12, 0x65, 0x7f, 0x95, 0x7a, 0xdd, 0xe9, 0xd8, 0x7c, 0x33, 0xf1, 0x60, 0x49, 0x3a, 0x91, 0x3b, - 0x7d, 0x18, 0x38, 0xa1, 0x17, 0xfa, 0x28, 0x94, 0xea, 0x8c, 0xb2, 0x70, 0x29, 0xa2, 0x14, 0xb9, - 0x5b, 0x29, 0x6b, 0x6a, 0xd6, 0x07, 0xe0, 0xe1, 0x81, 0x14, 0xe8, 0x48, 0x3d, 0xdf, 0x71, 0x8d, - 0x26, 0x89, 0xd2, 0x9d, 0x52, 0x47, 0x5a, 0xeb, 0xc3, 0xc0, 0x09, 0xbd, 0xd0, 0x67, 0x60, 0xc6, - 0x3f, 0x74, 0x89, 0x77, 0xe8, 0x58, 0x0d, 0x71, 0x7c, 0x3b, 0x61, 0x04, 0x2d, 0x56, 0x7f, 0x2f, - 0xa0, 0x1a, 0x71, 0x72, 0x82, 0x26, 0x1c, 0xf2, 0x44, 0x2e, 0x4c, 0x79, 0x34, 0x7c, 0xf3, 0x4a, - 0x85, 0x34, 0xdc, 0x44, 0xc1, 0x9d, 0x45, 0x84, 0x91, 0xd8, 0x9d, 0x71, 0xc0, 0x82, 0x93, 0xfe, - 0xed, 0x0c, 0xcc, 0x46, 0x11, 0x47, 0xd8, 0xa9, 0x9f, 0xd7, 0x60, 0xb6, 0xee, 0xd8, 0xbe, 0xeb, - 0x58, 0x3c, 0x2e, 0xe5, 0x1b, 0x64, 0xc2, 0xf2, 0x7a, 0x46, 0x6a, 0x83, 0xf8, 0x86, 0x69, 0x45, - 0x42, 0xdc, 0x08, 0x1b, 0xac, 0x30, 0x45, 0x5f, 0xd1, 0x60, 0x21, 0xcc, 0x6b, 0x85, 0x01, 0x72, - 0xaa, 0x03, 0x91, 0xa5, 0x67, 0xd7, 0x54, 0x4e, 0x38, 0xce, 0x5a, 0xdf, 0x87, 0xc5, 0xf8, 0x6a, - 0xd3, 0x4f, 0xd9, 0x36, 0xc4, 0x5e, 0xcf, 0x86, 0x9f, 0x72, 0xd7, 0xf0, 0x3c, 0xcc, 0x20, 0xe8, - 0xbd, 0x50, 0x68, 0x19, 0x6e, 0xd3, 0xb4, 0x0d, 0x8b, 0x7d, 0xc5, 0x6c, 0x44, 0x21, 0x89, 0x76, - 0x2c, 0x31, 0xf4, 0x1f, 0xe6, 0xa0, 0xb8, 0x43, 0x0c, 0xaf, 0xe3, 0x12, 0x76, 0x82, 0x75, 0xe6, - 0x2e, 0xa2, 0x52, 0xaf, 0x9e, 0x4d, 0xaf, 0x5e, 0x1d, 0xbd, 0x06, 0x70, 0x60, 0xda, 0xa6, 0x77, - 0x78, 0xca, 0x4a, 0x78, 0x96, 0xe1, 0xbc, 0x2e, 0x29, 0xe0, 0x08, 0xb5, 0xf0, 0x2a, 0x4c, 0x7e, - 0xc8, 0x55, 0x98, 0x2f, 0x68, 0x11, 0xe3, 0xc1, 0x9d, 0xaf, 0xbb, 0x93, 0x16, 0x50, 0xcb, 0x85, - 0x59, 0x0d, 0x8c, 0xc9, 0x35, 0xdb, 0x77, 0x8f, 0x87, 0xda, 0x98, 0x3d, 0x28, 0xb8, 0xc4, 0xeb, - 0xb4, 0xa8, 0xb3, 0x3b, 0x3d, 0xf6, 0x67, 0x60, 0x49, 0x20, 0x2c, 0xfa, 0x63, 0x49, 0xe9, 0xe2, - 0x8b, 0x30, 0xa7, 0x0c, 0x01, 0x2d, 0x42, 0xf6, 0x3e, 0x39, 0xe6, 0x72, 0x82, 0xe9, 0x9f, 0x68, - 0x59, 0x29, 0x85, 0x15, 0x9f, 0xe5, 0x83, 0x99, 0x17, 0x34, 0xfd, 0xc7, 0x53, 0x30, 0x25, 0xec, - 0xd5, 0xc9, 0xba, 0x20, 0x7a, 0x70, 0x9b, 0x39, 0xc5, 0xc1, 0xed, 0x16, 0xcc, 0x9a, 0xb6, 0xe9, - 0x9b, 0x86, 0xc5, 0x22, 0x22, 0x61, 0xab, 0x9e, 0x09, 0xf6, 0xff, 0x66, 0x04, 0x96, 0x40, 0x47, - 0xe9, 0x8b, 0x6e, 0x43, 0x9e, 0x29, 0x73, 0x21, 0x4f, 0xe3, 0xe7, 0xf5, 0x58, 0xce, 0x9e, 0xd7, - 0xd6, 0x71, 0x4a, 0xcc, 0xa7, 0xec, 0xd4, 0xeb, 0xc4, 0xf3, 0xa4, 0x23, 0x2f, 0xc4, 0x2a, 0xf4, - 0x29, 0x63, 0x70, 0xdc, 0xd7, 0x83, 0x52, 0x39, 0x30, 0x4c, 0xab, 0xe3, 0x92, 0x90, 0xca, 0x94, - 0x4a, 0xe5, 0x7a, 0x0c, 0x8e, 0xfb, 0x7a, 0xa0, 0x03, 0x98, 0x15, 0x6d, 0x3c, 0xad, 0x33, 0x7d, - 0xca, 0x59, 0xb2, 0xf4, 0xdd, 0xf5, 0x08, 0x25, 0xac, 0xd0, 0x45, 0x1d, 0x58, 0x32, 0xed, 0xba, - 0x63, 0xd7, 0xad, 0x8e, 0x67, 0x1e, 0x91, 0xb0, 0xb0, 0xed, 0x34, 0xcc, 0xce, 0xf7, 0xba, 0xe5, - 0xa5, 0xcd, 0x38, 0x39, 0xdc, 0xcf, 0x01, 0x7d, 0x4e, 0x83, 0xf3, 0x75, 0xc7, 0xf6, 0x58, 0x69, - 0xf7, 0x11, 0xb9, 0xe6, 0xba, 0x8e, 0xcb, 0x79, 0xcf, 0x9c, 0x92, 0x37, 0x0b, 0xc4, 0xd7, 0x93, - 0x48, 0xe2, 0x64, 0x4e, 0xe8, 0x0d, 0x28, 0xb4, 0x5d, 0xe7, 0xc8, 0x6c, 0x10, 0x57, 0xa4, 0x08, - 0xb7, 0xd3, 0xb8, 0x55, 0xb1, 0x2b, 0x68, 0x86, 0x9a, 0x20, 0x68, 0xc1, 0x92, 0x9f, 0xfe, 0xf5, - 0x29, 0x98, 0x57, 0xd1, 0xd1, 0xa7, 0x01, 0xda, 0xae, 0xd3, 0x22, 0xfe, 0x21, 0x91, 0x05, 0x4a, - 0x37, 0x27, 0xbd, 0xd1, 0x10, 0xd0, 0x13, 0x17, 0x3e, 0x98, 0x26, 0x0d, 0x5b, 0x71, 0x84, 0x23, - 0x72, 0x61, 0xfa, 0x3e, 0xb7, 0x69, 0xc2, 0xc4, 0xbf, 0x92, 0x8a, 0x43, 0x22, 0x38, 0x17, 0xa9, - 0xc9, 0x11, 0x4d, 0x38, 0x60, 0x84, 0xf6, 0x21, 0xfb, 0x80, 0xec, 0xa7, 0x53, 0x7b, 0x7f, 0x97, - 0x88, 0x50, 0xa1, 0x3a, 0xdd, 0xeb, 0x96, 0xb3, 0x77, 0xc9, 0x3e, 0xa6, 0xc4, 0xe9, 0xbc, 0x1a, - 0x3c, 0xfd, 0x24, 0x54, 0xc5, 0x84, 0xf3, 0x52, 0x72, 0x59, 0x7c, 0x5e, 0xa2, 0x09, 0x07, 0x8c, - 0xd0, 0x1b, 0x30, 0xf3, 0xc0, 0x38, 0x22, 0x07, 0xae, 0x63, 0xfb, 0xa2, 0xc0, 0x61, 0xc2, 0xc2, - 0x9b, 0xbb, 0x01, 0x39, 0xc1, 0x97, 0x59, 0x5b, 0xd9, 0x88, 0x43, 0x76, 0xe8, 0x08, 0x0a, 0x36, - 0x79, 0x80, 0x89, 0x65, 0xd6, 0x45, 0xcd, 0xc3, 0x84, 0x62, 0x7d, 0x53, 0x50, 0x13, 0x9c, 0x99, - 0x19, 0x0a, 0xda, 0xb0, 0xe4, 0x45, 0xd7, 0xf2, 0x9e, 0xb3, 0x2f, 0x14, 0xd5, 0x84, 0x6b, 0x29, - 0xc3, 0x3e, 0xbe, 0x96, 0x5b, 0xce, 0x3e, 0xa6, 0xc4, 0xf5, 0x6f, 0xe6, 0x60, 0x36, 0x7a, 0xe7, - 0x6e, 0x04, 0x9b, 0x25, 0xdd, 0xa6, 0xcc, 0x38, 0x6e, 0x13, 0xf5, 0x7a, 0x5b, 0xa1, 0x8d, 0x0f, - 0x8e, 0xca, 0x36, 0x53, 0xf3, 0x1a, 0x42, 0xaf, 0x37, 0xd2, 0xe8, 0x61, 0x85, 0xe9, 0x18, 0xb9, - 0x2b, 0xea, 0x07, 0x71, 0x73, 0xc8, 0x8b, 0xb5, 0xa5, 0x1f, 0xa4, 0x18, 0xb8, 0xab, 0x00, 0xc2, - 0x5c, 0x1d, 0x74, 0x2c, 0x71, 0x80, 0x29, 0x0f, 0xaf, 0x6a, 0x12, 0x82, 0x23, 0x58, 0xe8, 0x19, - 0x98, 0xa2, 0x06, 0x83, 0x34, 0x44, 0x15, 0xb5, 0x0c, 0x2d, 0xae, 0xb3, 0x56, 0x2c, 0xa0, 0xe8, - 0x05, 0x6a, 0xdb, 0x43, 0x35, 0x2f, 0x8a, 0xa3, 0x97, 0x43, 0xdb, 0x1e, 0xc2, 0xb0, 0x82, 0x49, - 0x87, 0x4e, 0xa8, 0x56, 0x66, 0xaa, 0x3f, 0x32, 0x74, 0xa6, 0xaa, 0x31, 0x87, 0xb1, 0x50, 0x37, - 0xa6, 0xc5, 0x99, 0xd2, 0xce, 0x47, 0x42, 0xdd, 0x18, 0x1c, 0xf7, 0xf5, 0xd0, 0x3f, 0x0e, 0xf3, - 0xaa, 0x34, 0xd3, 0x4f, 0xdc, 0x76, 0x9d, 0x03, 0xd3, 0x22, 0xf1, 0x20, 0x7d, 0x97, 0x37, 0xe3, - 0x00, 0x3e, 0x5a, 0xda, 0xf9, 0x2f, 0xb3, 0x70, 0xee, 0x66, 0xd3, 0xb4, 0x1f, 0xc6, 0x4e, 0x94, - 0x92, 0x2e, 0xf5, 0x6b, 0xe3, 0x5e, 0xea, 0x0f, 0x6b, 0xcf, 0xc4, 0x13, 0x05, 0xc9, 0xb5, 0x67, - 0xc1, 0xfb, 0x05, 0x2a, 0x2e, 0xfa, 0xbe, 0x06, 0x4f, 0x19, 0x0d, 0xee, 0x5f, 0x18, 0x96, 0x68, - 0x0d, 0x99, 0x06, 0x32, 0xee, 0x4d, 0xa8, 0x2d, 0xfa, 0x27, 0xbf, 0x5a, 0x19, 0xc2, 0x95, 0x7b, - 0xcd, 0xef, 0x16, 0x33, 0x78, 0x6a, 0x18, 0x2a, 0x1e, 0x3a, 0xfc, 0x8b, 0xb7, 0xe0, 0x5d, 0x27, - 0x32, 0x1a, 0xcb, 0x37, 0xfe, 0xbc, 0x06, 0x33, 0xfc, 0xf4, 0x08, 0x93, 0x03, 0xba, 0x79, 0x8c, - 0xb6, 0xf9, 0x2a, 0x71, 0xbd, 0xe0, 0xc6, 0xe1, 0x4c, 0xb8, 0x79, 0x2a, 0xbb, 0x9b, 0x02, 0x82, - 0x23, 0x58, 0x54, 0x3d, 0xdd, 0x37, 0xed, 0x86, 0x58, 0x26, 0xa9, 0x9e, 0x5e, 0x31, 0xed, 0x06, - 0x66, 0x10, 0xa9, 0xc0, 0xb2, 0x83, 0x14, 0x98, 0xfe, 0xfb, 0x1a, 0xcc, 0xb3, 0xd2, 0xd2, 0xd0, - 0x39, 0x7c, 0x5e, 0xa6, 0xea, 0xf8, 0x30, 0x2e, 0xa9, 0xa9, 0xba, 0x47, 0xdd, 0x72, 0x91, 0x17, - 0xa3, 0xaa, 0x99, 0xbb, 0x8f, 0x88, 0x00, 0x8f, 0x25, 0x14, 0x33, 0x63, 0xc7, 0x1f, 0xf2, 0x38, - 0xa3, 0x16, 0x10, 0xc1, 0x21, 0x3d, 0xfd, 0xeb, 0x59, 0x38, 0x97, 0x50, 0x23, 0x45, 0x63, 0xaf, - 0x29, 0xcb, 0xd8, 0x27, 0x56, 0x90, 0x0e, 0x7b, 0x3d, 0xf5, 0x3a, 0xac, 0xd5, 0x6d, 0x46, 0x9f, - 0x4b, 0x92, 0xd4, 0x4f, 0xbc, 0x11, 0x0b, 0xe6, 0xe8, 0x37, 0x35, 0x28, 0x1a, 0x11, 0x61, 0xe7, - 0x19, 0xc2, 0xfd, 0xf4, 0x07, 0xd3, 0x27, 0xdb, 0x91, 0xca, 0x86, 0x50, 0x94, 0xa3, 0x63, 0xb9, - 0xf8, 0xf3, 0x50, 0x8c, 0x4c, 0x61, 0x1c, 0x19, 0xbd, 0xf8, 0x12, 0x2c, 0x4e, 0x24, 0xe3, 0x1f, - 0x86, 0x71, 0xaf, 0xb0, 0x52, 0x8b, 0xf0, 0x20, 0x5a, 0x71, 0x2d, 0xbf, 0xb8, 0x28, 0xb9, 0x16, - 0x50, 0x7d, 0x1f, 0x16, 0xe3, 0x0e, 0xe8, 0x38, 0x67, 0xa2, 0x23, 0xa9, 0xdb, 0xf7, 0xc3, 0x98, - 0x97, 0x4e, 0xf5, 0xbf, 0xce, 0xc0, 0xb4, 0x28, 0xb4, 0x7c, 0x0c, 0x45, 0x41, 0xf7, 0x95, 0x53, - 0xe5, 0xcd, 0x54, 0xea, 0x43, 0x07, 0x56, 0x04, 0x79, 0xb1, 0x8a, 0xa0, 0x57, 0xd2, 0x61, 0x37, - 0xbc, 0x1c, 0xe8, 0x6b, 0x19, 0x58, 0x88, 0x15, 0xae, 0xa2, 0x2f, 0x6a, 0xfd, 0x59, 0xf0, 0x3b, - 0xa9, 0xd6, 0xc6, 0xca, 0x92, 0xb3, 0xe1, 0x09, 0x71, 0x4f, 0xb9, 0xc6, 0x7e, 0x3b, 0xb5, 0x27, - 0x41, 0x86, 0xde, 0x68, 0xff, 0x27, 0x0d, 0xde, 0x39, 0xb0, 0x94, 0x97, 0x5d, 0x17, 0x72, 0x55, - 0xa8, 0x90, 0xbd, 0x94, 0x4b, 0xf3, 0xe5, 0x69, 0x66, 0xfc, 0x86, 0x47, 0x9c, 0x3d, 0x7a, 0x0e, - 0x66, 0x99, 0x1e, 0xa7, 0xdb, 0xc7, 0x27, 0x6d, 0xf1, 0xbe, 0x11, 0x3b, 0x39, 0xa8, 0x45, 0xda, - 0xb1, 0x82, 0xa5, 0xff, 0xae, 0x06, 0xa5, 0x41, 0x97, 0x47, 0x46, 0xf0, 0xcb, 0x7f, 0x2e, 0x56, - 0xa0, 0x53, 0xee, 0x2b, 0xd0, 0x89, 0x79, 0xe6, 0x41, 0x2d, 0x4e, 0xc4, 0x29, 0xce, 0x9e, 0x50, - 0x7f, 0xf2, 0x55, 0x0d, 0x2e, 0x0c, 0x10, 0x9c, 0xbe, 0x42, 0x2d, 0xed, 0xd4, 0x85, 0x5a, 0x99, - 0x51, 0x0b, 0xb5, 0xf4, 0xbf, 0xcd, 0xc2, 0xa2, 0x18, 0x4f, 0x68, 0xcc, 0x5f, 0x50, 0xca, 0x9c, - 0xde, 0x1d, 0x2b, 0x73, 0x5a, 0x8e, 0xe3, 0xff, 0x5f, 0x8d, 0xd3, 0x4f, 0x57, 0x8d, 0xd3, 0x4f, - 0x32, 0x70, 0x3e, 0xf1, 0x62, 0x0e, 0xfa, 0x72, 0x82, 0x16, 0xbc, 0x9b, 0xf2, 0x0d, 0xa0, 0x11, - 0xf5, 0xe0, 0xa4, 0x85, 0x41, 0xbf, 0x11, 0x2d, 0xc8, 0xe1, 0x61, 0xc2, 0xc1, 0x19, 0xdc, 0x65, - 0x1a, 0xb3, 0x36, 0x47, 0xff, 0x95, 0x2c, 0x5c, 0x19, 0x95, 0xd0, 0x4f, 0x69, 0xed, 0xa6, 0xa7, - 0xd4, 0x6e, 0x3e, 0x1e, 0x0b, 0x75, 0x36, 0x65, 0x9c, 0x5f, 0xca, 0x4a, 0xb3, 0xd7, 0x2f, 0x9f, - 0x23, 0x25, 0x17, 0xa6, 0xa9, 0x17, 0x13, 0x3c, 0x4b, 0x11, 0xaa, 0xc2, 0xe9, 0x1a, 0x6f, 0x7e, - 0xd4, 0x2d, 0x2f, 0x89, 0xdb, 0xef, 0x35, 0xe2, 0x8b, 0x46, 0x1c, 0x74, 0x42, 0x57, 0xa0, 0xe0, - 0x72, 0x68, 0x50, 0xad, 0x26, 0x12, 0x26, 0xbc, 0x0d, 0x4b, 0x28, 0xfa, 0x4c, 0xc4, 0xed, 0xcb, - 0x9d, 0xd5, 0xdd, 0x90, 0x61, 0x79, 0xa0, 0xd7, 0xa1, 0xe0, 0x05, 0x6f, 0x56, 0xf0, 0xd3, 0xc1, - 0x67, 0x47, 0x2c, 0x82, 0xa4, 0x51, 0x42, 0xf0, 0x80, 0x05, 0x9f, 0x9f, 0x7c, 0xde, 0x42, 0x92, - 0xd4, 0xdf, 0xd2, 0xa0, 0x28, 0x56, 0xe2, 0x31, 0xd4, 0x5c, 0xde, 0x53, 0x6b, 0x2e, 0xaf, 0xa5, - 0xa2, 0x17, 0x06, 0x14, 0x5c, 0xde, 0x83, 0xd9, 0xe8, 0xbd, 0x4b, 0xf4, 0x5a, 0x44, 0xaf, 0x69, - 0x93, 0xdc, 0xef, 0x0a, 0x34, 0x5f, 0xa8, 0xf3, 0xf4, 0xbf, 0x9a, 0x92, 0x5f, 0x91, 0x55, 0x76, - 0x46, 0xe5, 0x4b, 0x1b, 0x2a, 0x5f, 0xd1, 0xe5, 0xcd, 0xa4, 0xbe, 0xbc, 0xe8, 0x36, 0x14, 0x02, - 0xe5, 0x23, 0x4c, 0xf4, 0xd3, 0xd1, 0x6a, 0x15, 0x6a, 0xe7, 0x29, 0xb1, 0x88, 0x50, 0xb2, 0x88, - 0x41, 0xae, 0xa1, 0x54, 0x8a, 0x92, 0x0c, 0x7a, 0x03, 0x8a, 0x0f, 0x1c, 0xf7, 0xbe, 0xe5, 0x18, - 0xec, 0x59, 0x18, 0x48, 0xe3, 0x0c, 0x57, 0x9e, 0x9c, 0xf0, 0x2a, 0xbd, 0xbb, 0x21, 0x7d, 0x1c, - 0x65, 0x86, 0x2a, 0xb0, 0xd0, 0x32, 0x6d, 0x4c, 0x8c, 0x86, 0x2c, 0xad, 0xcc, 0xf1, 0xa7, 0x30, - 0x02, 0x07, 0x76, 0x47, 0x05, 0xe3, 0x38, 0x3e, 0xfa, 0x24, 0x14, 0x3c, 0x71, 0xb7, 0x33, 0x9d, - 0xd3, 0x76, 0x19, 0xfa, 0x70, 0xa2, 0xe1, 0xb7, 0x0b, 0x5a, 0xb0, 0x64, 0x88, 0xb6, 0x61, 0xd9, - 0x15, 0xb7, 0xa7, 0x6e, 0x98, 0x9e, 0xef, 0xb8, 0xc7, 0x3c, 0x91, 0xc5, 0x8f, 0x57, 0xd9, 0x8b, - 0x0b, 0x38, 0x01, 0x8e, 0x13, 0x7b, 0x51, 0x0f, 0x85, 0x5d, 0x20, 0xe6, 0xc7, 0xad, 0x85, 0xd0, - 0x43, 0x61, 0x02, 0xdf, 0xc0, 0x02, 0x3a, 0xac, 0x54, 0xb7, 0x30, 0x41, 0xa9, 0xee, 0x5d, 0x98, - 0x71, 0x09, 0x73, 0xf3, 0x2b, 0x41, 0x2a, 0x6e, 0xec, 0x1a, 0x00, 0x1c, 0x10, 0xc0, 0x21, 0x2d, - 0xfd, 0x3f, 0xe7, 0x60, 0x4e, 0x09, 0x28, 0x69, 0x7c, 0xcf, 0xae, 0x88, 0xb1, 0xcd, 0x54, 0x08, - 0x37, 0x3c, 0xbb, 0x53, 0x86, 0x39, 0x0c, 0x7d, 0x4d, 0x83, 0x85, 0xb6, 0x72, 0xf8, 0x15, 0xe8, - 0x99, 0x09, 0x93, 0x1a, 0xea, 0x89, 0x5a, 0xe4, 0xd5, 0x21, 0x95, 0x19, 0x8e, 0x73, 0xa7, 0xe2, - 0x2a, 0x2a, 0x53, 0x2c, 0xe2, 0x32, 0x6c, 0x61, 0xed, 0x25, 0x89, 0x75, 0x15, 0x8c, 0xe3, 0xf8, - 0xf4, 0x23, 0xb3, 0xd9, 0x4d, 0xf2, 0x30, 0x60, 0x25, 0x20, 0x80, 0x43, 0x5a, 0xe8, 0x25, 0x98, - 0x17, 0x57, 0xe6, 0x77, 0x9d, 0xc6, 0x0d, 0xc3, 0x3b, 0x14, 0x6e, 0xae, 0x74, 0xcb, 0xd7, 0x15, - 0x28, 0x8e, 0x61, 0xb3, 0xb9, 0x85, 0xef, 0x12, 0x30, 0x02, 0x53, 0xea, 0xa3, 0x4c, 0xeb, 0x2a, - 0x18, 0xc7, 0xf1, 0xd1, 0x7b, 0x23, 0x5a, 0x92, 0x27, 0x0c, 0xe4, 0xde, 0x49, 0xd0, 0x94, 0x15, - 0x58, 0xe8, 0xb0, 0xa8, 0xa0, 0x11, 0x00, 0x85, 0xf4, 0x4a, 0x86, 0x77, 0x54, 0x30, 0x8e, 0xe3, - 0xa3, 0x17, 0x61, 0xce, 0xa5, 0xba, 0x40, 0x12, 0xe0, 0x59, 0x04, 0x79, 0x24, 0x8e, 0xa3, 0x40, - 0xac, 0xe2, 0xa2, 0x97, 0x61, 0x29, 0xbc, 0x3c, 0x1c, 0x10, 0xe0, 0x69, 0x05, 0xf9, 0x2e, 0x41, - 0x25, 0x8e, 0x80, 0xfb, 0xfb, 0xa0, 0x5f, 0x80, 0xc5, 0xc8, 0x97, 0xd8, 0xb4, 0x1b, 0xe4, 0xa1, - 0xb8, 0xe0, 0xb9, 0xcc, 0x52, 0x13, 0x31, 0x18, 0xee, 0xc3, 0x46, 0x1f, 0x84, 0xf9, 0xba, 0x63, - 0x59, 0x4c, 0x23, 0xf0, 0x07, 0x81, 0xf8, 0x4d, 0x4e, 0x7e, 0xe7, 0x55, 0x81, 0xe0, 0x18, 0x26, - 0xda, 0x02, 0xe4, 0xec, 0x7b, 0xc4, 0x3d, 0x22, 0x8d, 0x97, 0xf9, 0xdb, 0xc7, 0xd4, 0x20, 0xce, - 0xa9, 0x75, 0x71, 0xb7, 0xfa, 0x30, 0x70, 0x42, 0x2f, 0xb4, 0x0f, 0x17, 0x03, 0xed, 0xdc, 0xdf, - 0xa3, 0x54, 0x52, 0x82, 0x87, 0x8b, 0x77, 0x07, 0x62, 0xe2, 0x21, 0x54, 0xd0, 0x2f, 0xa9, 0x85, - 0xd9, 0xf3, 0x69, 0x3c, 0xb1, 0x18, 0x8f, 0x93, 0x4f, 0xac, 0xca, 0x76, 0x61, 0x8a, 0x97, 0x42, - 0x96, 0x16, 0xd2, 0xb8, 0x34, 0x1d, 0x7d, 0x7f, 0x24, 0xd4, 0xda, 0xbc, 0x15, 0x0b, 0x4e, 0xe8, - 0xd3, 0x30, 0xb3, 0x1f, 0x3c, 0x46, 0x55, 0x5a, 0x4c, 0xc3, 0x52, 0xc5, 0xde, 0x55, 0x0b, 0xe3, - 0x40, 0x09, 0xc0, 0x21, 0x4b, 0xf4, 0x0c, 0x14, 0x6f, 0xec, 0x56, 0xa4, 0xa4, 0x2f, 0x31, 0x09, - 0xcb, 0xd1, 0x2e, 0x38, 0x0a, 0xa0, 0xbb, 0x58, 0x7a, 0x30, 0x88, 0x2d, 0x79, 0x68, 0x01, 0xfb, - 0x1d, 0x12, 0x8a, 0xcd, 0x32, 0x4d, 0xb8, 0x56, 0x3a, 0x17, 0xc3, 0x16, 0xed, 0x58, 0x62, 0xa0, - 0xd7, 0xa1, 0x28, 0xcc, 0x02, 0xd3, 0x7f, 0xcb, 0xa7, 0x2b, 0xfa, 0xc7, 0x21, 0x09, 0x1c, 0xa5, - 0x87, 0x9e, 0x87, 0x62, 0x9b, 0xbd, 0xd1, 0x43, 0xae, 0x77, 0x2c, 0xab, 0x74, 0x9e, 0xe9, 0x66, - 0x79, 0x04, 0xbf, 0x1b, 0x82, 0x70, 0x14, 0x0f, 0x3d, 0x1b, 0xa4, 0x89, 0xdf, 0xa1, 0x64, 0x54, - 0x64, 0x9a, 0x58, 0xfa, 0x9d, 0x03, 0x8a, 0xeb, 0x2e, 0x9c, 0x70, 0x4c, 0xf0, 0xb9, 0xf0, 0x98, - 0x54, 0x3e, 0x43, 0xf1, 0xa9, 0xa8, 0x34, 0x68, 0x69, 0xbc, 0xd0, 0xdc, 0xf7, 0xd2, 0x19, 0x37, - 0x16, 0x89, 0xb2, 0xd0, 0x96, 0xf2, 0x9f, 0xca, 0x8d, 0x55, 0xf5, 0x89, 0x0d, 0x5e, 0xd0, 0xad, - 0x4a, 0xbf, 0xfe, 0x83, 0x9c, 0x3c, 0x2a, 0x89, 0x65, 0x47, 0x5d, 0xc8, 0x9b, 0x9e, 0x6f, 0x3a, - 0x29, 0x56, 0xd9, 0xc7, 0xde, 0xa6, 0x60, 0xd5, 0x5e, 0x0c, 0x80, 0x39, 0x2b, 0xca, 0xd3, 0x6e, - 0x9a, 0xf6, 0x43, 0x31, 0xfd, 0xdb, 0xa9, 0xa7, 0x3d, 0x39, 0x4f, 0x06, 0xc0, 0x9c, 0x15, 0xba, - 0x07, 0x59, 0xc3, 0xda, 0x4f, 0xe9, 0x35, 0xee, 0xf8, 0x8b, 0xf6, 0xbc, 0x56, 0xa2, 0xb2, 0x5d, - 0xc5, 0x94, 0x09, 0xe5, 0xe5, 0xb5, 0x4c, 0xe1, 0x5f, 0x4c, 0xc8, 0xab, 0xb6, 0xb3, 0x99, 0xc4, - 0xab, 0xb6, 0xb3, 0x89, 0x29, 0x13, 0xf4, 0x45, 0x0d, 0xc0, 0x90, 0xaf, 0xcd, 0xa7, 0xf3, 0x2c, - 0xe0, 0xa0, 0xd7, 0xeb, 0x79, 0x11, 0x53, 0x08, 0xc5, 0x11, 0xce, 0xfa, 0x9b, 0x1a, 0x2c, 0xf5, - 0x0d, 0x36, 0xfe, 0x10, 0xbf, 0x36, 0xfa, 0x43, 0xfc, 0xe2, 0xf5, 0x92, 0x5a, 0xdb, 0x32, 0x13, - 0x6f, 0xaa, 0xec, 0xc5, 0xe0, 0xb8, 0xaf, 0x87, 0xfe, 0x2d, 0x0d, 0x8a, 0x91, 0x2a, 0x63, 0xea, - 0xf7, 0xb2, 0x6a, 0x6c, 0x31, 0x8c, 0xf0, 0xe1, 0x16, 0x76, 0x3a, 0xc4, 0x61, 0xfc, 0xa0, 0xb2, - 0x19, 0x1e, 0xd7, 0x45, 0x0e, 0x2a, 0x69, 0x2b, 0x16, 0x50, 0x74, 0x19, 0x72, 0x9e, 0x4f, 0xda, - 0x4c, 0xa2, 0x22, 0x45, 0xc7, 0xec, 0xb8, 0x9e, 0x41, 0x18, 0x3b, 0xaa, 0x1c, 0x45, 0x05, 0x49, - 0xe4, 0x9d, 0x18, 0x83, 0xba, 0xd9, 0x0c, 0x86, 0x2e, 0x41, 0x96, 0xd8, 0x0d, 0xe1, 0x2d, 0x16, - 0x05, 0x4a, 0xf6, 0x9a, 0xdd, 0xc0, 0xb4, 0x5d, 0xbf, 0x05, 0xb3, 0x35, 0x52, 0x77, 0x89, 0xff, - 0x0a, 0x39, 0x1e, 0xed, 0x28, 0xed, 0x12, 0x4f, 0x41, 0x66, 0x54, 0x82, 0xb4, 0x3b, 0x6d, 0xd7, - 0xff, 0x50, 0x83, 0xd8, 0xb3, 0x3d, 0x48, 0x8f, 0x65, 0x15, 0xa1, 0x3f, 0xa3, 0xa8, 0x84, 0xe0, - 0x99, 0xa1, 0x21, 0xf8, 0x16, 0xa0, 0x96, 0xe1, 0xd7, 0x0f, 0xc5, 0xfa, 0x88, 0x17, 0xa2, 0xb8, - 0xa3, 0x1e, 0xde, 0x69, 0xe8, 0xc3, 0xc0, 0x09, 0xbd, 0xf4, 0x25, 0x58, 0x90, 0x81, 0x34, 0x97, - 0x0c, 0xfd, 0xdb, 0x59, 0x98, 0x55, 0x1e, 0x68, 0x3e, 0xf9, 0x8b, 0x8c, 0x3e, 0xf6, 0x84, 0x80, - 0x38, 0x3b, 0x66, 0x40, 0x1c, 0x3d, 0x81, 0xc8, 0x9d, 0xed, 0x09, 0x44, 0x3e, 0x9d, 0x13, 0x08, - 0x1f, 0xa6, 0xc5, 0xbf, 0xce, 0x10, 0x45, 0x6b, 0x3b, 0x29, 0xdd, 0x4a, 0x14, 0xb7, 0xac, 0x58, - 0x9d, 0x5e, 0xb0, 0xcb, 0x03, 0x56, 0xfa, 0x37, 0xf2, 0x30, 0xaf, 0xde, 0x53, 0x1c, 0x61, 0x25, - 0xdf, 0xdb, 0xb7, 0x92, 0x63, 0x86, 0x38, 0xd9, 0x49, 0x43, 0x9c, 0xdc, 0xa4, 0x21, 0x4e, 0xfe, - 0x14, 0x21, 0x4e, 0x7f, 0x80, 0x32, 0x35, 0x72, 0x80, 0xf2, 0x21, 0x99, 0xa9, 0x9a, 0x56, 0x8e, - 0x76, 0xc3, 0x4c, 0x15, 0x52, 0x97, 0x61, 0xdd, 0x69, 0x24, 0x66, 0xfc, 0x0a, 0x27, 0x94, 0xc1, - 0xb9, 0x89, 0x89, 0xa5, 0xf1, 0x0f, 0x32, 0xde, 0x31, 0x46, 0x52, 0x29, 0xfc, 0xef, 0x30, 0xcc, - 0x42, 0x80, 0x6a, 0x5d, 0x6a, 0x21, 0x08, 0x47, 0xf1, 0xd8, 0x0b, 0xc8, 0xea, 0xfb, 0xcc, 0x2c, - 0x62, 0x8c, 0xbe, 0x80, 0x1c, 0x7b, 0xcf, 0x39, 0x8e, 0xaf, 0x7f, 0x36, 0x03, 0xe1, 0x1b, 0xd3, - 0xec, 0x31, 0x28, 0x2f, 0xa2, 0xa6, 0x85, 0x33, 0xb5, 0x35, 0xe9, 0x8b, 0x6b, 0x21, 0x45, 0x91, - 0x13, 0x8e, 0xb4, 0x60, 0x85, 0xe3, 0x7f, 0xc3, 0xdb, 0xd2, 0x06, 0x2c, 0xc4, 0x4a, 0x63, 0x53, - 0xaf, 0x31, 0xf9, 0x56, 0x06, 0x66, 0x64, 0x71, 0x31, 0xb5, 0x6c, 0x1d, 0x37, 0x78, 0xb7, 0x46, - 0x5a, 0xb6, 0x3b, 0x78, 0x1b, 0xd3, 0x76, 0xf4, 0x10, 0xa6, 0x0f, 0x89, 0xd1, 0x20, 0x6e, 0x70, - 0x4e, 0xb5, 0x93, 0x52, 0x55, 0xf3, 0x0d, 0x46, 0x35, 0x9c, 0x0b, 0xff, 0xed, 0xe1, 0x80, 0x1d, - 0x7a, 0x09, 0xe6, 0x7d, 0xb3, 0x45, 0x68, 0x80, 0x11, 0xb1, 0x1a, 0xd9, 0xf0, 0xf0, 0x67, 0x4f, - 0x81, 0xe2, 0x18, 0x36, 0x55, 0x6b, 0xf7, 0x3c, 0xc7, 0x66, 0x57, 0x80, 0x73, 0x6a, 0x14, 0xb7, - 0x55, 0xbb, 0x75, 0x93, 0xdd, 0x00, 0x96, 0x18, 0x14, 0xdb, 0x64, 0xc5, 0x95, 0x2e, 0x11, 0x59, - 0xa3, 0xc5, 0xf0, 0x2a, 0x08, 0x6f, 0xc7, 0x12, 0x43, 0xbf, 0x03, 0x0b, 0xb1, 0x89, 0x04, 0x1e, - 0x82, 0x96, 0xec, 0x21, 0x8c, 0xf4, 0x2f, 0x6e, 0xaa, 0xab, 0xdf, 0x79, 0x7b, 0xe5, 0x89, 0xef, - 0xbe, 0xbd, 0xf2, 0xc4, 0xf7, 0xde, 0x5e, 0x79, 0xe2, 0xb3, 0xbd, 0x15, 0xed, 0x3b, 0xbd, 0x15, - 0xed, 0xbb, 0xbd, 0x15, 0xed, 0x7b, 0xbd, 0x15, 0xed, 0x07, 0xbd, 0x15, 0xed, 0xcd, 0x1f, 0xae, - 0x3c, 0xf1, 0x5a, 0x21, 0xf8, 0x98, 0xff, 0x15, 0x00, 0x00, 0xff, 0xff, 0x29, 0xaa, 0x1d, 0xb9, - 0xe1, 0x6b, 0x00, 0x00, + 0x55, 0xf0, 0x56, 0xff, 0xd8, 0xed, 0xd3, 0xfe, 0xbd, 0xe3, 0xc9, 0xf4, 0xce, 0xee, 0xb8, 0x27, + 0xb5, 0xd1, 0x7e, 0x93, 0x8f, 0xc4, 0x4e, 0x66, 0x77, 0x61, 0xc9, 0x46, 0x2b, 0xba, 0xed, 0x99, + 0x1d, 0x7b, 0xed, 0x19, 0xcf, 0x6d, 0xcf, 0x8e, 0xb2, 0x9b, 0x0d, 0x29, 0x77, 0x5f, 0xb7, 0x6b, + 0xa6, 0xba, 0xaa, 0x53, 0x55, 0xed, 0x19, 0x6f, 0xa2, 0xfc, 0x10, 0xe5, 0x07, 0x94, 0x28, 0xcb, + 0xcf, 0x0b, 0x42, 0x20, 0x84, 0x78, 0x40, 0xf0, 0x82, 0x50, 0x1e, 0x13, 0x11, 0x01, 0x91, 0x96, + 0x07, 0x50, 0x78, 0x80, 0x0d, 0x48, 0x69, 0x92, 0x0e, 0x12, 0x82, 0x17, 0x14, 0x14, 0x09, 0x65, + 0x25, 0x24, 0x74, 0x7f, 0xea, 0x56, 0xdd, 0xea, 0xea, 0x76, 0xb7, 0xbb, 0x3c, 0x44, 0xc0, 0x9b, + 0xfb, 0x9e, 0x73, 0xcf, 0xb9, 0xb7, 0xee, 0xb9, 0xe7, 0xe7, 0x9e, 0x73, 0xaf, 0x61, 0xbb, 0x69, + 0xfa, 0x87, 0x9d, 0xfd, 0xd5, 0xba, 0xd3, 0x5a, 0x33, 0xdc, 0xa6, 0xd3, 0x76, 0x9d, 0x7b, 0xec, + 0x8f, 0xf7, 0xbb, 0x8e, 0x65, 0x39, 0x1d, 0xdf, 0x5b, 0x6b, 0xdf, 0x6f, 0xae, 0x19, 0x6d, 0xd3, + 0x5b, 0x93, 0x2d, 0x47, 0x1f, 0x34, 0xac, 0xf6, 0xa1, 0xf1, 0xc1, 0xb5, 0x26, 0xb1, 0x89, 0x6b, + 0xf8, 0xa4, 0xb1, 0xda, 0x76, 0x1d, 0xdf, 0x41, 0x1f, 0x0e, 0xa9, 0xad, 0x06, 0xd4, 0xd8, 0x1f, + 0xbf, 0x18, 0xf4, 0x5d, 0x6d, 0xdf, 0x6f, 0xae, 0x52, 0x6a, 0xab, 0xb2, 0x25, 0xa0, 0x76, 0xf1, + 0xfd, 0x91, 0xb1, 0x34, 0x9d, 0xa6, 0xb3, 0xc6, 0x88, 0xee, 0x77, 0x0e, 0xd8, 0x2f, 0xf6, 0x83, + 0xfd, 0xc5, 0x99, 0x5d, 0x7c, 0xea, 0xfe, 0xf3, 0xde, 0xaa, 0xe9, 0xd0, 0xb1, 0xad, 0xed, 0x1b, + 0x7e, 0xfd, 0x70, 0xed, 0xa8, 0x6f, 0x44, 0x17, 0xf5, 0x08, 0x52, 0xdd, 0x71, 0x49, 0x12, 0xce, + 0xb3, 0x21, 0x4e, 0xcb, 0xa8, 0x1f, 0x9a, 0x36, 0x71, 0x8f, 0xc3, 0x59, 0xb7, 0x88, 0x6f, 0x24, + 0xf5, 0x5a, 0x1b, 0xd4, 0xcb, 0xed, 0xd8, 0xbe, 0xd9, 0x22, 0x7d, 0x1d, 0x7e, 0xf6, 0xa4, 0x0e, + 0x5e, 0xfd, 0x90, 0xb4, 0x8c, 0xbe, 0x7e, 0xcf, 0x0c, 0xea, 0xd7, 0xf1, 0x4d, 0x6b, 0xcd, 0xb4, + 0x7d, 0xcf, 0x77, 0xe3, 0x9d, 0xf4, 0x7f, 0xd7, 0x60, 0xa9, 0xb2, 0x5d, 0xdd, 0x73, 0x8d, 0x83, + 0x03, 0xb3, 0x8e, 0x9d, 0x8e, 0x6f, 0xda, 0x4d, 0xf4, 0x5e, 0x98, 0x36, 0xed, 0xa6, 0x4b, 0x3c, + 0xaf, 0xa4, 0x5d, 0xd6, 0xae, 0xcc, 0x54, 0x17, 0xde, 0xea, 0x96, 0x1f, 0xeb, 0x75, 0xcb, 0xd3, + 0x9b, 0xbc, 0x19, 0x07, 0x70, 0xf4, 0x1c, 0x14, 0x3d, 0xe2, 0x1e, 0x99, 0x75, 0xb2, 0xeb, 0xb8, + 0x7e, 0x29, 0x73, 0x59, 0xbb, 0x92, 0xaf, 0x9e, 0x13, 0xe8, 0xc5, 0x5a, 0x08, 0xc2, 0x51, 0x3c, + 0xda, 0xcd, 0x75, 0x1c, 0x5f, 0xc0, 0x4b, 0x59, 0xc6, 0x45, 0x76, 0xc3, 0x21, 0x08, 0x47, 0xf1, + 0xd0, 0x06, 0x2c, 0x1a, 0xb6, 0xed, 0xf8, 0x86, 0x6f, 0x3a, 0xf6, 0xae, 0x4b, 0x0e, 0xcc, 0x87, + 0xa5, 0x1c, 0xeb, 0x5b, 0x12, 0x7d, 0x17, 0x2b, 0x31, 0x38, 0xee, 0xeb, 0xa1, 0x6f, 0x40, 0xa9, + 0xd2, 0xda, 0x37, 0x3c, 0xcf, 0x68, 0x38, 0x6e, 0x6c, 0xea, 0x57, 0xa0, 0xd0, 0x32, 0xda, 0x6d, + 0xd3, 0x6e, 0xd2, 0xb9, 0x67, 0xaf, 0xcc, 0x54, 0x67, 0x7b, 0xdd, 0x72, 0x61, 0x47, 0xb4, 0x61, + 0x09, 0xd5, 0xff, 0x3e, 0x03, 0xc5, 0x8a, 0x6d, 0x58, 0xc7, 0x9e, 0xe9, 0xe1, 0x8e, 0x8d, 0x3e, + 0x0e, 0x05, 0x2a, 0x03, 0x0d, 0xc3, 0x37, 0xd8, 0x57, 0x2b, 0x5e, 0xfd, 0xc0, 0x2a, 0x5f, 0x92, + 0xd5, 0xe8, 0x92, 0x84, 0x92, 0x4d, 0xb1, 0x57, 0x8f, 0x3e, 0xb8, 0x7a, 0x6b, 0xff, 0x1e, 0xa9, + 0xfb, 0x3b, 0xc4, 0x37, 0xaa, 0x48, 0xcc, 0x02, 0xc2, 0x36, 0x2c, 0xa9, 0x22, 0x07, 0x72, 0x5e, + 0x9b, 0xd4, 0xd9, 0x47, 0x2e, 0x5e, 0xdd, 0x59, 0x9d, 0x64, 0x17, 0xad, 0x46, 0x86, 0x5e, 0x6b, + 0x93, 0x7a, 0x75, 0x56, 0xb0, 0xce, 0xd1, 0x5f, 0x98, 0x31, 0x42, 0x0f, 0x60, 0xca, 0xf3, 0x0d, + 0xbf, 0xe3, 0xb1, 0x05, 0x2a, 0x5e, 0xbd, 0x95, 0x1e, 0x4b, 0x46, 0xb6, 0x3a, 0x2f, 0x98, 0x4e, + 0xf1, 0xdf, 0x58, 0xb0, 0xd3, 0xff, 0x41, 0x83, 0x73, 0x11, 0xec, 0x8a, 0xdb, 0xec, 0xb4, 0x88, + 0xed, 0xa3, 0xcb, 0x90, 0xb3, 0x8d, 0x16, 0x11, 0x52, 0x29, 0x87, 0x7c, 0xd3, 0x68, 0x11, 0xcc, + 0x20, 0xe8, 0x29, 0xc8, 0x1f, 0x19, 0x56, 0x87, 0xb0, 0x8f, 0x34, 0x53, 0x9d, 0x13, 0x28, 0xf9, + 0x57, 0x68, 0x23, 0xe6, 0x30, 0xf4, 0x29, 0x98, 0x61, 0x7f, 0x5c, 0x77, 0x9d, 0x56, 0x4a, 0x53, + 0x13, 0x23, 0x7c, 0x25, 0x20, 0x5b, 0x9d, 0xeb, 0x75, 0xcb, 0x33, 0xf2, 0x27, 0x0e, 0x19, 0xea, + 0xff, 0xa8, 0xc1, 0x42, 0x64, 0x72, 0xdb, 0xa6, 0xe7, 0xa3, 0x8f, 0xf6, 0x09, 0xcf, 0xea, 0x68, + 0xc2, 0x43, 0x7b, 0x33, 0xd1, 0x59, 0x14, 0x33, 0x2d, 0x04, 0x2d, 0x11, 0xc1, 0xb1, 0x21, 0x6f, + 0xfa, 0xa4, 0xe5, 0x95, 0x32, 0x97, 0xb3, 0x57, 0x8a, 0x57, 0x37, 0x53, 0x5b, 0xc6, 0xf0, 0xfb, + 0x6e, 0x52, 0xfa, 0x98, 0xb3, 0xd1, 0x7f, 0x3b, 0xa3, 0xcc, 0x90, 0x4a, 0x14, 0x72, 0x60, 0xba, + 0x45, 0x7c, 0xd7, 0xac, 0xf3, 0x7d, 0x55, 0xbc, 0xba, 0x31, 0xd9, 0x28, 0x76, 0x18, 0xb1, 0x50, + 0x33, 0xf1, 0xdf, 0x1e, 0x0e, 0xb8, 0xa0, 0x43, 0xc8, 0x19, 0x6e, 0x33, 0x98, 0xf3, 0xf5, 0x74, + 0xd6, 0x37, 0x94, 0xb9, 0x8a, 0xdb, 0xf4, 0x30, 0xe3, 0x80, 0xd6, 0x60, 0xc6, 0x27, 0x6e, 0xcb, + 0xb4, 0x0d, 0x9f, 0xab, 0xb2, 0x42, 0x75, 0x49, 0xa0, 0xcd, 0xec, 0x05, 0x00, 0x1c, 0xe2, 0xe8, + 0x6f, 0x67, 0x60, 0xa9, 0x6f, 0x33, 0xa0, 0x67, 0x21, 0xdf, 0x3e, 0x34, 0xbc, 0x40, 0xba, 0x57, + 0x82, 0x4f, 0xbb, 0x4b, 0x1b, 0xdf, 0xe9, 0x96, 0xe7, 0x82, 0x2e, 0xac, 0x01, 0x73, 0x64, 0xaa, + 0xab, 0x5b, 0xc4, 0xf3, 0x8c, 0x66, 0x20, 0xf2, 0x91, 0x2f, 0xc2, 0x9a, 0x71, 0x00, 0x47, 0x5f, + 0xd2, 0x60, 0x8e, 0x7f, 0x1d, 0x4c, 0xbc, 0x8e, 0xe5, 0xd3, 0x6d, 0x4d, 0xbf, 0xcd, 0x56, 0x1a, + 0x2b, 0xc1, 0x49, 0x56, 0xcf, 0x0b, 0xee, 0x73, 0xd1, 0x56, 0x0f, 0xab, 0x7c, 0xd1, 0x5d, 0x98, + 0xf1, 0x7c, 0xc3, 0xf5, 0x49, 0xa3, 0xe2, 0x33, 0x05, 0x5e, 0xbc, 0xfa, 0xff, 0x47, 0x93, 0xf7, + 0x3d, 0xb3, 0x45, 0xf8, 0xde, 0xaa, 0x05, 0x04, 0x70, 0x48, 0x4b, 0xff, 0x5b, 0x55, 0x71, 0xd4, + 0x7c, 0x6a, 0xec, 0x9a, 0xc7, 0xe8, 0x35, 0x78, 0xdc, 0xeb, 0xd4, 0xeb, 0xc4, 0xf3, 0x0e, 0x3a, + 0x16, 0xee, 0xd8, 0x37, 0x4c, 0xcf, 0x77, 0xdc, 0xe3, 0x6d, 0xb3, 0x65, 0xfa, 0xec, 0x7b, 0xe7, + 0xab, 0x97, 0x7a, 0xdd, 0xf2, 0xe3, 0xb5, 0x41, 0x48, 0x78, 0x70, 0x7f, 0x64, 0xc0, 0x13, 0x1d, + 0x7b, 0x30, 0x79, 0x6e, 0x13, 0xcb, 0xbd, 0x6e, 0xf9, 0x89, 0x3b, 0x83, 0xd1, 0xf0, 0x30, 0x1a, + 0xfa, 0xbf, 0x6a, 0xb0, 0x18, 0xcc, 0x6b, 0x8f, 0xb4, 0xda, 0x96, 0xe1, 0x93, 0x47, 0x60, 0x71, + 0x7c, 0xc5, 0xe2, 0xe0, 0x74, 0xf4, 0x46, 0x30, 0xfe, 0x41, 0x66, 0x47, 0xff, 0x17, 0x0d, 0x96, + 0xe3, 0xc8, 0x8f, 0x40, 0x4b, 0x7a, 0xaa, 0x96, 0xbc, 0x99, 0xee, 0x6c, 0x07, 0xa8, 0xca, 0x1f, + 0x25, 0xcc, 0xf5, 0x7f, 0xb8, 0xbe, 0xd4, 0xff, 0x20, 0x07, 0xb3, 0x15, 0xdb, 0x37, 0x2b, 0x07, + 0x07, 0xa6, 0x6d, 0xfa, 0xc7, 0xe8, 0x2b, 0x19, 0x58, 0x6b, 0xbb, 0xe4, 0x80, 0xb8, 0x2e, 0x69, + 0x6c, 0x74, 0x5c, 0xd3, 0x6e, 0xd6, 0xea, 0x87, 0xa4, 0xd1, 0xb1, 0x4c, 0xbb, 0xb9, 0xd9, 0xb4, + 0x1d, 0xd9, 0x7c, 0xed, 0x21, 0xa9, 0x77, 0xa8, 0x2b, 0x27, 0xd6, 0xbf, 0x35, 0xd9, 0x30, 0x77, + 0xc7, 0x63, 0x5a, 0x7d, 0xa6, 0xd7, 0x2d, 0xaf, 0x8d, 0xd9, 0x09, 0x8f, 0x3b, 0x35, 0xf4, 0xe5, + 0x0c, 0xac, 0xba, 0xe4, 0x13, 0x1d, 0x73, 0xf4, 0xaf, 0xc1, 0x37, 0xa8, 0x35, 0xd9, 0xd7, 0xc0, + 0x63, 0xf1, 0xac, 0x5e, 0xed, 0x75, 0xcb, 0x63, 0xf6, 0xc1, 0x63, 0xce, 0x4b, 0xff, 0x73, 0x0d, + 0x0a, 0x63, 0x78, 0x7f, 0x65, 0xd5, 0xfb, 0x9b, 0xe9, 0xf3, 0xfc, 0xfc, 0x7e, 0xcf, 0xef, 0xa5, + 0xc9, 0x3e, 0xda, 0x28, 0x1e, 0xdf, 0xbf, 0xd1, 0x28, 0x2b, 0xee, 0x21, 0xa2, 0x43, 0x58, 0x6e, + 0x3b, 0x8d, 0x60, 0xd3, 0xdf, 0x30, 0xbc, 0x43, 0x06, 0x13, 0xd3, 0x7b, 0xb6, 0xd7, 0x2d, 0x2f, + 0xef, 0x26, 0xc0, 0xdf, 0xe9, 0x96, 0x4b, 0x92, 0x48, 0x0c, 0x01, 0x27, 0x52, 0x44, 0x6d, 0x28, + 0x1c, 0x98, 0xc4, 0x6a, 0x60, 0x72, 0x20, 0x24, 0x65, 0xc2, 0xed, 0x7d, 0x5d, 0x50, 0xe3, 0xc1, + 0x51, 0xf0, 0x0b, 0x4b, 0x2e, 0xfa, 0x4f, 0x72, 0xb0, 0x50, 0xb5, 0x3a, 0xe4, 0x25, 0x97, 0x90, + 0xc0, 0xbf, 0xa9, 0xc0, 0x42, 0xdb, 0x25, 0x47, 0x26, 0x79, 0x50, 0x23, 0x16, 0xa9, 0xfb, 0x8e, + 0x2b, 0xa6, 0x7a, 0x41, 0xac, 0xe4, 0xc2, 0xae, 0x0a, 0xc6, 0x71, 0x7c, 0xf4, 0x22, 0xcc, 0x1b, + 0x75, 0xdf, 0x3c, 0x22, 0x92, 0x02, 0x5f, 0xe8, 0x77, 0x09, 0x0a, 0xf3, 0x15, 0x05, 0x8a, 0x63, + 0xd8, 0xe8, 0xa3, 0x50, 0xf2, 0xea, 0x86, 0x45, 0xee, 0xb4, 0x05, 0xab, 0xf5, 0x43, 0x52, 0xbf, + 0xbf, 0xeb, 0x98, 0xb6, 0x2f, 0x1c, 0xb7, 0xcb, 0x82, 0x52, 0xa9, 0x36, 0x00, 0x0f, 0x0f, 0xa4, + 0x80, 0xfe, 0x54, 0x83, 0x4b, 0x6d, 0x97, 0xec, 0xba, 0x4e, 0xcb, 0xa1, 0xd2, 0xdb, 0xe7, 0xe2, + 0x09, 0x57, 0xe7, 0x95, 0x09, 0xb7, 0x29, 0x6f, 0xe9, 0x8f, 0xa6, 0xde, 0xdd, 0xeb, 0x96, 0x2f, + 0xed, 0x0e, 0x1b, 0x00, 0x1e, 0x3e, 0x3e, 0xf4, 0x67, 0x1a, 0xac, 0xb4, 0x1d, 0xcf, 0x1f, 0x32, + 0x85, 0xfc, 0x99, 0x4e, 0x41, 0xef, 0x75, 0xcb, 0x2b, 0xbb, 0x43, 0x47, 0x80, 0x4f, 0x18, 0xa1, + 0xde, 0x2b, 0xc2, 0x52, 0x44, 0xf6, 0x84, 0x07, 0xf8, 0x02, 0xcc, 0x05, 0xc2, 0xc0, 0xcf, 0x1c, + 0xb8, 0xec, 0x49, 0x7f, 0xb5, 0x12, 0x05, 0x62, 0x15, 0x97, 0xca, 0x9d, 0x14, 0x45, 0xde, 0x3b, + 0x26, 0x77, 0xbb, 0x0a, 0x14, 0xc7, 0xb0, 0xd1, 0x26, 0x9c, 0x13, 0x2d, 0x98, 0xb4, 0x2d, 0xb3, + 0x6e, 0xac, 0x3b, 0x1d, 0x21, 0x72, 0xf9, 0xea, 0x85, 0x5e, 0xb7, 0x7c, 0x6e, 0xb7, 0x1f, 0x8c, + 0x93, 0xfa, 0xa0, 0x6d, 0x58, 0x36, 0x3a, 0xbe, 0x23, 0xe7, 0x7f, 0xcd, 0x36, 0xf6, 0x2d, 0xd2, + 0x60, 0xa2, 0x55, 0xa8, 0x96, 0xa8, 0xd6, 0xa8, 0x24, 0xc0, 0x71, 0x62, 0x2f, 0xb4, 0x1b, 0xa3, + 0x56, 0x23, 0x75, 0xc7, 0x6e, 0xf0, 0x55, 0xce, 0x57, 0x9f, 0x14, 0xd3, 0x53, 0x29, 0x0a, 0x1c, + 0x9c, 0xd8, 0x13, 0x59, 0x30, 0xdf, 0x32, 0x1e, 0xde, 0xb1, 0x8d, 0x23, 0xc3, 0xb4, 0x28, 0x93, + 0xd2, 0xd4, 0x09, 0xae, 0x69, 0xc7, 0x37, 0xad, 0x55, 0x7e, 0x3e, 0xb5, 0xba, 0x69, 0xfb, 0xb7, + 0xdc, 0x9a, 0x4f, 0x8d, 0x40, 0x15, 0xd1, 0x0f, 0xbb, 0xa3, 0xd0, 0xc2, 0x31, 0xda, 0xe8, 0x16, + 0x9c, 0x67, 0xdb, 0x71, 0xc3, 0x79, 0x60, 0x6f, 0x10, 0xcb, 0x38, 0x0e, 0x26, 0x30, 0xcd, 0x26, + 0xf0, 0x78, 0xaf, 0x5b, 0x3e, 0x5f, 0x4b, 0x42, 0xc0, 0xc9, 0xfd, 0xa8, 0x2f, 0xaf, 0x02, 0x30, + 0x39, 0x32, 0x3d, 0xd3, 0xb1, 0xb9, 0x2f, 0x5f, 0x08, 0x7d, 0xf9, 0xda, 0x60, 0x34, 0x3c, 0x8c, + 0x06, 0xfa, 0x2d, 0x0d, 0x96, 0x93, 0xb6, 0x61, 0x69, 0x26, 0x8d, 0x73, 0x9d, 0xd8, 0xd6, 0xe2, + 0x12, 0x91, 0xa8, 0x14, 0x12, 0x07, 0x81, 0x3e, 0xab, 0xc1, 0xac, 0x11, 0x71, 0xce, 0x4a, 0xc0, + 0x46, 0xb5, 0x35, 0xa9, 0x37, 0x1c, 0x52, 0xac, 0x2e, 0xf6, 0xba, 0x65, 0xc5, 0x01, 0xc4, 0x0a, + 0x47, 0xf4, 0x3b, 0x1a, 0x9c, 0x4f, 0xdc, 0xe3, 0xa5, 0xe2, 0x59, 0x7c, 0x21, 0x26, 0x24, 0xc9, + 0x3a, 0x27, 0x79, 0x18, 0xe8, 0x4d, 0x4d, 0x9a, 0xb2, 0x9d, 0x20, 0x1e, 0x99, 0x65, 0x43, 0xbb, + 0x3d, 0xa1, 0x3f, 0x1a, 0x5a, 0xef, 0x80, 0x70, 0xf5, 0x5c, 0xc4, 0x32, 0x06, 0x8d, 0x38, 0xce, + 0x1e, 0x7d, 0x55, 0x0b, 0x4c, 0xa3, 0x1c, 0xd1, 0xdc, 0x59, 0x8d, 0x08, 0x85, 0x96, 0x56, 0x0e, + 0x28, 0xc6, 0x1c, 0x7d, 0x0c, 0x2e, 0x1a, 0xfb, 0x8e, 0xeb, 0x27, 0x6e, 0xbe, 0xd2, 0x3c, 0xdb, + 0x46, 0x2b, 0xbd, 0x6e, 0xf9, 0x62, 0x65, 0x20, 0x16, 0x1e, 0x42, 0x41, 0xff, 0xe7, 0x2c, 0xcc, + 0xae, 0x1b, 0xb6, 0xe1, 0x1e, 0x0b, 0xd3, 0xf5, 0x0d, 0x0d, 0x9e, 0xac, 0x77, 0x5c, 0x97, 0xd8, + 0x7e, 0xcd, 0x27, 0xed, 0x7e, 0xc3, 0xa5, 0x9d, 0xa9, 0xe1, 0xba, 0xdc, 0xeb, 0x96, 0x9f, 0x5c, + 0x1f, 0xc2, 0x1f, 0x0f, 0x1d, 0x1d, 0xfa, 0x6b, 0x0d, 0x74, 0x81, 0x50, 0x35, 0xea, 0xf7, 0x9b, + 0xae, 0xd3, 0xb1, 0x1b, 0xfd, 0x93, 0xc8, 0x9c, 0xe9, 0x24, 0x9e, 0xee, 0x75, 0xcb, 0xfa, 0xfa, + 0x89, 0xa3, 0xc0, 0x23, 0x8c, 0x14, 0xbd, 0x04, 0x4b, 0x02, 0xeb, 0xda, 0xc3, 0x36, 0x71, 0x4d, + 0xea, 0xfb, 0x8a, 0x73, 0xfe, 0xc7, 0x85, 0x59, 0x59, 0x5a, 0x8f, 0x23, 0xe0, 0xfe, 0x3e, 0xfa, + 0x1f, 0xe7, 0x00, 0x82, 0x95, 0x26, 0x6d, 0xf4, 0x33, 0x30, 0xe3, 0x11, 0xff, 0x2e, 0x31, 0x9b, + 0x87, 0xc1, 0xc9, 0x0d, 0x3f, 0x0e, 0x0a, 0x1a, 0x71, 0x08, 0x47, 0xf7, 0x21, 0xdf, 0x36, 0x3a, + 0x1e, 0x11, 0xdf, 0x6d, 0x2b, 0x95, 0xef, 0xb6, 0x4b, 0x29, 0xf2, 0xd8, 0x82, 0xfd, 0x89, 0x39, + 0x0f, 0xf4, 0x79, 0x0d, 0x80, 0xa8, 0x73, 0x2d, 0x5e, 0xad, 0xa5, 0xc2, 0x32, 0xfc, 0x1c, 0xf4, + 0x1b, 0x54, 0xe7, 0x7b, 0xdd, 0x32, 0x44, 0xbe, 0x5a, 0x84, 0x2d, 0x7a, 0x00, 0x05, 0x23, 0x50, + 0x97, 0xb9, 0xb3, 0x50, 0x97, 0xcc, 0xe5, 0x97, 0xeb, 0x2d, 0x99, 0xa1, 0x2f, 0x6b, 0x30, 0xef, + 0x11, 0x5f, 0x2c, 0x15, 0xdd, 0xb4, 0xc2, 0x57, 0xdc, 0x9e, 0x8c, 0x7f, 0x4d, 0xa1, 0xc9, 0x95, + 0x8f, 0xda, 0x86, 0x63, 0x7c, 0xf5, 0x37, 0x8b, 0x30, 0x1f, 0x88, 0x4c, 0xe8, 0xfe, 0xd5, 0x79, + 0x4b, 0xb2, 0xfb, 0xb7, 0x1e, 0x05, 0x62, 0x15, 0x97, 0x76, 0xf6, 0x7c, 0xea, 0x6f, 0xa8, 0xde, + 0x9f, 0xec, 0x5c, 0x8b, 0x02, 0xb1, 0x8a, 0x8b, 0x5a, 0x90, 0xf7, 0x7c, 0xd2, 0x0e, 0x0e, 0x5b, + 0x6f, 0x4c, 0xf6, 0x35, 0xc2, 0x9d, 0x10, 0x1e, 0x28, 0xd1, 0x5f, 0x1e, 0xe6, 0x5c, 0xd0, 0xd7, + 0x34, 0x98, 0xf7, 0x95, 0x9c, 0x96, 0x10, 0x83, 0x74, 0x24, 0x51, 0x4d, 0x97, 0xf1, 0xd5, 0x50, + 0xdb, 0x70, 0x8c, 0x7d, 0x82, 0x47, 0x98, 0x3f, 0x43, 0x8f, 0xf0, 0x55, 0x28, 0xb4, 0x8c, 0x87, + 0xb5, 0x8e, 0xdb, 0x3c, 0xbd, 0xe7, 0x29, 0x52, 0x7e, 0x9c, 0x0a, 0x96, 0xf4, 0xd0, 0xe7, 0xb4, + 0xc8, 0xe6, 0x9a, 0x66, 0xc4, 0xef, 0xa6, 0xbb, 0xb9, 0xa4, 0x42, 0x1d, 0xb8, 0xcd, 0xfa, 0xfc, + 0xb3, 0xc2, 0x23, 0xf7, 0xcf, 0xa8, 0xaf, 0xc1, 0x37, 0x88, 0xf4, 0x35, 0x66, 0xce, 0xd4, 0xd7, + 0x58, 0x57, 0x98, 0xe1, 0x18, 0x73, 0x36, 0x1e, 0xbe, 0xe7, 0xe4, 0x78, 0xe0, 0x4c, 0xc7, 0x53, + 0x53, 0x98, 0xe1, 0x18, 0xf3, 0xc1, 0x41, 0x49, 0xf1, 0x6c, 0x82, 0x92, 0xd9, 0x14, 0x82, 0x92, + 0xe1, 0xfe, 0xda, 0xdc, 0xc4, 0xfe, 0xda, 0x8f, 0x34, 0xb8, 0xb0, 0x6e, 0x75, 0x3c, 0x9f, 0xb8, + 0xff, 0x6b, 0xf2, 0x18, 0xff, 0xa1, 0xc1, 0x13, 0x03, 0xe6, 0xfc, 0x08, 0xd2, 0x19, 0x6f, 0xa8, + 0xe9, 0x8c, 0x3b, 0x13, 0xda, 0x9d, 0xe4, 0x79, 0x0c, 0xc8, 0x6a, 0xf8, 0x30, 0xb7, 0x61, 0xf8, + 0x46, 0xc3, 0x69, 0xf2, 0x34, 0x03, 0x7a, 0x11, 0x0a, 0xa6, 0xed, 0x13, 0xf7, 0xc8, 0xb0, 0x84, + 0xe5, 0xd5, 0x83, 0xa1, 0x6f, 0x8a, 0xf6, 0x77, 0xba, 0xe5, 0xf9, 0x8d, 0x8e, 0xcb, 0x0a, 0x35, + 0xb8, 0x1e, 0xc6, 0xb2, 0x0f, 0x7a, 0x0a, 0xf2, 0x9f, 0xe8, 0x10, 0xf7, 0x38, 0x9e, 0xd6, 0xbf, + 0x4d, 0x1b, 0x31, 0x87, 0xe9, 0x7f, 0x97, 0x81, 0x88, 0x57, 0xf4, 0x08, 0xc4, 0xca, 0x56, 0xc4, + 0x6a, 0x42, 0x3f, 0x27, 0xe2, 0xe3, 0x0d, 0xaa, 0xc7, 0x38, 0x8a, 0xd5, 0x63, 0xdc, 0x4c, 0x8d, + 0xe3, 0xf0, 0x72, 0x8c, 0xb7, 0x35, 0x78, 0x22, 0x44, 0xee, 0xf7, 0xf5, 0x4f, 0x3e, 0x98, 0x7f, + 0x0e, 0x8a, 0x46, 0xd8, 0x4d, 0xac, 0xa2, 0xac, 0xf7, 0x89, 0x50, 0xc4, 0x51, 0xbc, 0x30, 0x25, + 0x9e, 0x3d, 0x65, 0x4a, 0x3c, 0x37, 0x3c, 0x25, 0xae, 0xff, 0x38, 0x03, 0x97, 0xfa, 0x67, 0x16, + 0x48, 0x37, 0x26, 0x07, 0x23, 0xcc, 0xed, 0x79, 0x98, 0xf5, 0x45, 0x07, 0xda, 0x2a, 0x26, 0xb7, + 0x2c, 0x30, 0x67, 0xf7, 0x22, 0x30, 0xac, 0x60, 0xd2, 0x9e, 0x75, 0xbe, 0xaf, 0x6a, 0x75, 0xa7, + 0x1d, 0xd4, 0x0e, 0xc8, 0x9e, 0xeb, 0x11, 0x18, 0x56, 0x30, 0x65, 0xb2, 0x2e, 0x77, 0xe6, 0xc5, + 0x0d, 0x35, 0x38, 0x1f, 0xe4, 0x6c, 0xae, 0x3b, 0xee, 0xba, 0xd3, 0x6a, 0x5b, 0x84, 0xa5, 0x9c, + 0xf2, 0x6c, 0xb0, 0x97, 0x44, 0x97, 0xf3, 0x38, 0x09, 0x09, 0x27, 0xf7, 0xd5, 0xdf, 0xce, 0xc2, + 0xb9, 0xf0, 0xb3, 0xaf, 0x3b, 0x76, 0xc3, 0x64, 0x99, 0xaf, 0x17, 0x20, 0xe7, 0x1f, 0xb7, 0x83, + 0x8f, 0xfd, 0xff, 0x82, 0xe1, 0xec, 0x1d, 0xb7, 0xe9, 0x6a, 0x5f, 0x48, 0xe8, 0x42, 0x41, 0x98, + 0x75, 0x42, 0xdb, 0x72, 0x77, 0xf0, 0x15, 0x78, 0x56, 0x95, 0xe6, 0x77, 0xba, 0xe5, 0x84, 0x2a, + 0xbf, 0x55, 0x49, 0x49, 0x95, 0x79, 0x74, 0x0f, 0xe6, 0x2d, 0xc3, 0xf3, 0xef, 0xb4, 0x1b, 0x86, + 0x4f, 0xf6, 0xcc, 0x16, 0x11, 0x7b, 0x6e, 0x9c, 0x3a, 0x05, 0x79, 0x3c, 0xbc, 0xad, 0x50, 0xc2, + 0x31, 0xca, 0xe8, 0x08, 0x10, 0x6d, 0xd9, 0x73, 0x0d, 0xdb, 0xe3, 0xb3, 0xa2, 0xfc, 0xc6, 0xaf, + 0x8b, 0xb8, 0x28, 0xf8, 0xa1, 0xed, 0x3e, 0x6a, 0x38, 0x81, 0x03, 0x7a, 0x1a, 0xa6, 0x5c, 0x62, + 0x78, 0x62, 0x31, 0x67, 0xc2, 0xfd, 0x8f, 0x59, 0x2b, 0x16, 0xd0, 0xe8, 0x86, 0x9a, 0x3a, 0x61, + 0x43, 0x7d, 0x4f, 0x83, 0xf9, 0x70, 0x99, 0x1e, 0x81, 0x99, 0x6b, 0xa9, 0x66, 0xee, 0x46, 0x5a, + 0x2a, 0x71, 0x80, 0x65, 0xfb, 0x93, 0x5c, 0x74, 0x7e, 0x2c, 0x53, 0xff, 0x49, 0x98, 0x09, 0x76, + 0x75, 0x90, 0xab, 0x9f, 0xd0, 0x1b, 0x57, 0x3c, 0x8b, 0x48, 0x29, 0x91, 0x60, 0x82, 0x43, 0x7e, + 0xd4, 0xb0, 0x36, 0x84, 0xd1, 0x14, 0x62, 0x2f, 0x0d, 0x6b, 0x60, 0x4c, 0x93, 0x0c, 0x6b, 0xd0, + 0x07, 0xdd, 0x81, 0x0b, 0x6d, 0xd7, 0x61, 0xb5, 0x9c, 0x1b, 0xc4, 0x68, 0x58, 0xa6, 0x4d, 0x02, + 0xa7, 0x8f, 0x67, 0x27, 0x9e, 0xe8, 0x75, 0xcb, 0x17, 0x76, 0x93, 0x51, 0xf0, 0xa0, 0xbe, 0x6a, + 0x49, 0x54, 0xee, 0xe4, 0x92, 0x28, 0xf4, 0xcb, 0x32, 0xb4, 0x22, 0x5e, 0x29, 0xcf, 0x3e, 0xe2, + 0x6b, 0x69, 0x2d, 0x65, 0x82, 0x5a, 0x0f, 0x45, 0xaa, 0x22, 0x98, 0x62, 0xc9, 0x7e, 0xb0, 0xff, + 0x3e, 0x75, 0x3a, 0xff, 0x5d, 0xff, 0x42, 0x1e, 0x16, 0xe3, 0xc6, 0xf6, 0xec, 0xcb, 0xbd, 0x7e, + 0x4d, 0x83, 0xc5, 0x40, 0x50, 0x38, 0x4f, 0x12, 0x1c, 0x42, 0x6c, 0xa7, 0x24, 0x9f, 0xdc, 0x6d, + 0x90, 0xb5, 0xb7, 0x7b, 0x31, 0x6e, 0xb8, 0x8f, 0x3f, 0x7a, 0x1d, 0x8a, 0x32, 0x56, 0x3f, 0x55, + 0xed, 0xd7, 0x02, 0x73, 0x18, 0x42, 0x12, 0x38, 0x4a, 0x0f, 0x7d, 0x41, 0x03, 0xa8, 0x07, 0x1a, + 0x3d, 0x10, 0xa4, 0xdb, 0x69, 0x09, 0x92, 0xb4, 0x15, 0xa1, 0x5f, 0x28, 0x9b, 0x3c, 0x1c, 0x61, + 0x8c, 0x7e, 0x9d, 0x45, 0xe9, 0xd2, 0x91, 0xa1, 0xa2, 0x43, 0x47, 0xf2, 0x91, 0xb4, 0x45, 0x3a, + 0x3c, 0xbb, 0x95, 0x5e, 0x43, 0x04, 0xe4, 0x61, 0x65, 0x10, 0xfa, 0x0b, 0x20, 0x73, 0xf5, 0x74, + 0x87, 0xb2, 0x6c, 0xfd, 0xae, 0xe1, 0x1f, 0x0a, 0x11, 0x94, 0x3b, 0xf4, 0x7a, 0x00, 0xc0, 0x21, + 0x8e, 0xfe, 0x17, 0x1a, 0x2c, 0x6f, 0x7a, 0xbe, 0xe9, 0x6c, 0x10, 0xcf, 0xa7, 0x9b, 0x96, 0xda, + 0xf7, 0x8e, 0x45, 0x46, 0xf0, 0x90, 0x36, 0x60, 0x51, 0x1c, 0xa8, 0x75, 0xf6, 0x3d, 0xe2, 0x47, + 0xbc, 0x24, 0x29, 0x3a, 0xeb, 0x31, 0x38, 0xee, 0xeb, 0x41, 0xa9, 0x88, 0x93, 0xb5, 0x90, 0x4a, + 0x56, 0xa5, 0x52, 0x8b, 0xc1, 0x71, 0x5f, 0x0f, 0xfd, 0x9b, 0x19, 0x38, 0xc7, 0xa6, 0x11, 0x2b, + 0xfc, 0xfe, 0x55, 0x0d, 0xe6, 0x8f, 0x4c, 0xd7, 0xef, 0x18, 0x56, 0xf4, 0x88, 0x70, 0x62, 0xe9, + 0x61, 0xbc, 0x5e, 0x51, 0x08, 0x87, 0x7e, 0x81, 0xda, 0x8e, 0x63, 0x03, 0xa0, 0x63, 0x5a, 0x68, + 0xa8, 0x5f, 0x3b, 0x9d, 0x10, 0x36, 0x69, 0x1d, 0x79, 0xa2, 0x29, 0xd6, 0x88, 0xe3, 0xfc, 0xf5, + 0xd7, 0xc4, 0xe7, 0x53, 0x87, 0x3e, 0x82, 0x10, 0xe8, 0x30, 0xe5, 0x3a, 0x1d, 0x6a, 0x23, 0x33, + 0xac, 0xae, 0x1e, 0x98, 0xa3, 0xc1, 0x5a, 0xb0, 0x80, 0xe8, 0x7f, 0xa4, 0xc1, 0xcc, 0x96, 0xb3, + 0x2f, 0x82, 0xc6, 0x8f, 0xa5, 0x10, 0xc0, 0x49, 0x3d, 0x2f, 0x4f, 0x6b, 0x42, 0xd7, 0xe1, 0x45, + 0x25, 0x7c, 0x7b, 0x32, 0x42, 0x7b, 0x95, 0x5d, 0x14, 0xa1, 0xa4, 0xb6, 0x9c, 0xfd, 0x81, 0xf1, + 0xfd, 0xef, 0xe5, 0x61, 0xee, 0x65, 0xe3, 0x98, 0xd8, 0xbe, 0x21, 0x46, 0xfc, 0x5e, 0x98, 0x36, + 0x1a, 0x8d, 0xa4, 0x8b, 0x13, 0x15, 0xde, 0x8c, 0x03, 0x38, 0x8b, 0x88, 0xda, 0x2c, 0xaf, 0x1f, + 0xb1, 0xdd, 0x61, 0x44, 0x14, 0x82, 0x70, 0x14, 0x2f, 0xdc, 0x4a, 0xeb, 0x8e, 0x7d, 0x60, 0x36, + 0x93, 0x36, 0xc1, 0x7a, 0x0c, 0x8e, 0xfb, 0x7a, 0xa0, 0x2d, 0x40, 0xa2, 0xec, 0xaf, 0x52, 0xaf, + 0x3b, 0x1d, 0x9b, 0x6f, 0x26, 0x1e, 0x2c, 0x49, 0x27, 0x72, 0xa7, 0x0f, 0x03, 0x27, 0xf4, 0x42, + 0x1f, 0x85, 0x52, 0x9d, 0x51, 0x16, 0x2e, 0x45, 0x94, 0x22, 0x77, 0x2b, 0x65, 0x4d, 0xcd, 0xfa, + 0x00, 0x3c, 0x3c, 0x90, 0x02, 0x1d, 0xa9, 0xe7, 0x3b, 0xae, 0xd1, 0x24, 0x51, 0xba, 0x53, 0xea, + 0x48, 0x6b, 0x7d, 0x18, 0x38, 0xa1, 0x17, 0xfa, 0x0c, 0xcc, 0xf8, 0x87, 0x2e, 0xf1, 0x0e, 0x1d, + 0xab, 0x21, 0x8e, 0x6f, 0x27, 0x8c, 0xa0, 0xc5, 0xea, 0xef, 0x05, 0x54, 0x23, 0x4e, 0x4e, 0xd0, + 0x84, 0x43, 0x9e, 0xc8, 0x85, 0x29, 0x8f, 0x86, 0x6f, 0x5e, 0xa9, 0x90, 0x86, 0x9b, 0x28, 0xb8, + 0xb3, 0x88, 0x30, 0x12, 0xbb, 0x33, 0x0e, 0x58, 0x70, 0xd2, 0xbf, 0x9d, 0x81, 0xd9, 0x28, 0xe2, + 0x08, 0x3b, 0xf5, 0xf3, 0x1a, 0xcc, 0xd6, 0x1d, 0xdb, 0x77, 0x1d, 0x8b, 0xc7, 0xa5, 0x7c, 0x83, + 0x4c, 0x78, 0x6d, 0x80, 0x91, 0xda, 0x20, 0xbe, 0x61, 0x5a, 0x91, 0x10, 0x37, 0xc2, 0x06, 0x2b, + 0x4c, 0xd1, 0x57, 0x34, 0x58, 0x08, 0xf3, 0x5a, 0x61, 0x80, 0x9c, 0xea, 0x40, 0x64, 0xe9, 0xd9, + 0x35, 0x95, 0x13, 0x8e, 0xb3, 0xd6, 0xf7, 0x61, 0x31, 0xbe, 0xda, 0xf4, 0x53, 0xb6, 0x0d, 0xb1, + 0xd7, 0xb3, 0xe1, 0xa7, 0xdc, 0x35, 0x3c, 0x0f, 0x33, 0x08, 0x7a, 0x1f, 0x14, 0x5a, 0x86, 0xdb, + 0x34, 0x6d, 0xc3, 0x62, 0x5f, 0x31, 0x1b, 0x51, 0x48, 0xa2, 0x1d, 0x4b, 0x0c, 0xfd, 0x87, 0x39, + 0x28, 0xee, 0x10, 0xc3, 0xeb, 0xb8, 0x84, 0x9d, 0x60, 0x9d, 0xb9, 0x8b, 0xa8, 0xd4, 0xe1, 0x67, + 0xd3, 0xab, 0xc3, 0x47, 0xaf, 0x02, 0x1c, 0x98, 0xb6, 0xe9, 0x1d, 0x9e, 0xb2, 0xc2, 0x9f, 0x65, + 0x38, 0xaf, 0x4b, 0x0a, 0x38, 0x42, 0x2d, 0xbc, 0xe2, 0x93, 0x1f, 0x72, 0xc5, 0xe7, 0x0b, 0x5a, + 0xc4, 0x78, 0x70, 0xe7, 0xeb, 0xee, 0xa4, 0x05, 0xd4, 0x72, 0x61, 0x56, 0x03, 0x63, 0x72, 0xcd, + 0xf6, 0xdd, 0xe3, 0xa1, 0x36, 0x66, 0x0f, 0x0a, 0x2e, 0xf1, 0x3a, 0x2d, 0xea, 0xec, 0x4e, 0x8f, + 0xfd, 0x19, 0x58, 0x12, 0x08, 0x8b, 0xfe, 0x58, 0x52, 0xba, 0xf8, 0x02, 0xcc, 0x29, 0x43, 0x40, + 0x8b, 0x90, 0xbd, 0x4f, 0x8e, 0xb9, 0x9c, 0x60, 0xfa, 0x27, 0x5a, 0x56, 0x4a, 0x61, 0xc5, 0x67, + 0xf9, 0x50, 0xe6, 0x79, 0x4d, 0xff, 0xf1, 0x14, 0x4c, 0x09, 0x7b, 0x75, 0xb2, 0x2e, 0x88, 0x1e, + 0xdc, 0x66, 0x4e, 0x71, 0x70, 0xbb, 0x05, 0xb3, 0xa6, 0x6d, 0xfa, 0xa6, 0x61, 0xb1, 0x88, 0x48, + 0xd8, 0xaa, 0xa7, 0x83, 0xfd, 0xbf, 0x19, 0x81, 0x25, 0xd0, 0x51, 0xfa, 0xa2, 0xdb, 0x90, 0x67, + 0xca, 0x5c, 0xc8, 0xd3, 0xf8, 0x79, 0x3d, 0x96, 0xb3, 0xe7, 0xb5, 0x75, 0x9c, 0x12, 0xf3, 0x29, + 0xf9, 0xa5, 0x0b, 0xe9, 0xc8, 0x0b, 0xb1, 0x0a, 0x7d, 0xca, 0x18, 0x1c, 0xf7, 0xf5, 0xa0, 0x54, + 0x0e, 0x0c, 0xd3, 0xea, 0xb8, 0x24, 0xa4, 0x32, 0xa5, 0x52, 0xb9, 0x1e, 0x83, 0xe3, 0xbe, 0x1e, + 0xe8, 0x00, 0x66, 0x45, 0x1b, 0x4f, 0xeb, 0x4c, 0x9f, 0x72, 0x96, 0x2c, 0x7d, 0x77, 0x3d, 0x42, + 0x09, 0x2b, 0x74, 0x51, 0x07, 0x96, 0x4c, 0xbb, 0xee, 0xd8, 0x75, 0xab, 0xe3, 0x99, 0x47, 0x24, + 0x2c, 0x6c, 0x3b, 0x0d, 0xb3, 0xf3, 0xbd, 0x6e, 0x79, 0x69, 0x33, 0x4e, 0x0e, 0xf7, 0x73, 0x40, + 0x9f, 0xd3, 0xe0, 0x7c, 0xdd, 0xb1, 0x3d, 0x56, 0xda, 0x7d, 0x44, 0xae, 0xb9, 0xae, 0xe3, 0x72, + 0xde, 0x33, 0xa7, 0xe4, 0xcd, 0x02, 0xf1, 0xf5, 0x24, 0x92, 0x38, 0x99, 0x13, 0x7a, 0x03, 0x0a, + 0x6d, 0xd7, 0x39, 0x32, 0x1b, 0xc4, 0x15, 0x29, 0xc2, 0xed, 0x34, 0x6e, 0x55, 0xec, 0x0a, 0x9a, + 0xa1, 0x26, 0x08, 0x5a, 0xb0, 0xe4, 0xa7, 0x7f, 0x7d, 0x0a, 0xe6, 0x55, 0x74, 0xf4, 0x69, 0x80, + 0xb6, 0xeb, 0xb4, 0x88, 0x7f, 0x48, 0x64, 0x81, 0xd2, 0xcd, 0x49, 0x6f, 0x34, 0x04, 0xf4, 0xc4, + 0x85, 0x0f, 0xa6, 0x49, 0xc3, 0x56, 0x1c, 0xe1, 0x88, 0x5c, 0x98, 0xbe, 0xcf, 0x6d, 0x9a, 0x30, + 0xf1, 0x2f, 0xa7, 0xe2, 0x90, 0x08, 0xce, 0x45, 0x6a, 0x72, 0x44, 0x13, 0x0e, 0x18, 0xa1, 0x7d, + 0xc8, 0x3e, 0x20, 0xfb, 0xe9, 0xd4, 0xde, 0xdf, 0x25, 0x22, 0x54, 0xa8, 0x4e, 0xf7, 0xba, 0xe5, + 0xec, 0x5d, 0xb2, 0x8f, 0x29, 0x71, 0x3a, 0xaf, 0x06, 0x4f, 0x3f, 0x09, 0x55, 0x31, 0xe1, 0xbc, + 0x94, 0x5c, 0x16, 0x9f, 0x97, 0x68, 0xc2, 0x01, 0x23, 0xf4, 0x06, 0xcc, 0x3c, 0x30, 0x8e, 0xc8, + 0x81, 0xeb, 0xd8, 0xbe, 0x28, 0x70, 0x98, 0xb0, 0xf0, 0xe6, 0x6e, 0x40, 0x4e, 0xf0, 0x65, 0xd6, + 0x56, 0x36, 0xe2, 0x90, 0x1d, 0x3a, 0x82, 0x82, 0x4d, 0x1e, 0x60, 0x62, 0x99, 0x75, 0x51, 0xf3, + 0x30, 0xa1, 0x58, 0xdf, 0x14, 0xd4, 0x04, 0x67, 0x66, 0x86, 0x82, 0x36, 0x2c, 0x79, 0xd1, 0xb5, + 0xbc, 0xe7, 0xec, 0x0b, 0x45, 0x35, 0xe1, 0x5a, 0xca, 0xb0, 0x8f, 0xaf, 0xe5, 0x96, 0xb3, 0x8f, + 0x29, 0x71, 0xfd, 0x9b, 0x39, 0x98, 0x8d, 0xde, 0x25, 0x1c, 0xc1, 0x66, 0x49, 0xb7, 0x29, 0x33, + 0x8e, 0xdb, 0x44, 0xbd, 0xde, 0x56, 0x68, 0xe3, 0x83, 0xa3, 0xb2, 0xcd, 0xd4, 0xbc, 0x86, 0xd0, + 0xeb, 0x8d, 0x34, 0x7a, 0x58, 0x61, 0x3a, 0x46, 0xee, 0x8a, 0xfa, 0x41, 0xdc, 0x1c, 0xf2, 0x62, + 0x6d, 0xe9, 0x07, 0x29, 0x06, 0xee, 0x2a, 0x40, 0x78, 0xab, 0x50, 0x1c, 0x60, 0xca, 0xc3, 0xab, + 0xc8, 0x6d, 0xc7, 0x08, 0x16, 0x7a, 0x1a, 0xa6, 0xa8, 0xc1, 0x20, 0x0d, 0x51, 0x45, 0x2d, 0x43, + 0x8b, 0xeb, 0xac, 0x15, 0x0b, 0x28, 0x7a, 0x9e, 0xda, 0xf6, 0x50, 0xcd, 0x8b, 0xe2, 0xe8, 0xe5, + 0xd0, 0xb6, 0x87, 0x30, 0xac, 0x60, 0xd2, 0xa1, 0x13, 0xaa, 0x95, 0x99, 0xea, 0x8f, 0x0c, 0x9d, + 0xa9, 0x6a, 0xcc, 0x61, 0x2c, 0xd4, 0x8d, 0x69, 0x71, 0xa6, 0xb4, 0xf3, 0x91, 0x50, 0x37, 0x06, + 0xc7, 0x7d, 0x3d, 0xf4, 0x8f, 0xc3, 0xbc, 0x2a, 0xcd, 0xf4, 0x13, 0xb7, 0x5d, 0xe7, 0xc0, 0xb4, + 0x48, 0x3c, 0x48, 0xdf, 0xe5, 0xcd, 0x38, 0x80, 0x8f, 0x96, 0x76, 0xfe, 0xcb, 0x2c, 0x9c, 0xbb, + 0xd9, 0x34, 0xed, 0x87, 0xb1, 0x13, 0xa5, 0xa4, 0xc7, 0x0a, 0xb4, 0x71, 0x1f, 0x2b, 0x08, 0x6b, + 0xcf, 0xc4, 0xd3, 0x0b, 0xc9, 0xb5, 0x67, 0xc1, 0xbb, 0x0c, 0x2a, 0x2e, 0xfa, 0x9e, 0x06, 0x4f, + 0x1a, 0x0d, 0xee, 0x5f, 0x18, 0x96, 0x68, 0x0d, 0x99, 0x06, 0x32, 0xee, 0x4d, 0xa8, 0x2d, 0xfa, + 0x27, 0xbf, 0x5a, 0x19, 0xc2, 0x95, 0x7b, 0xcd, 0xef, 0x11, 0x33, 0x78, 0x72, 0x18, 0x2a, 0x1e, + 0x3a, 0xfc, 0x8b, 0xb7, 0xe0, 0xdd, 0x27, 0x32, 0x1a, 0xcb, 0x37, 0xfe, 0xbc, 0x06, 0x33, 0xfc, + 0xf4, 0x08, 0x93, 0x03, 0xba, 0x79, 0x8c, 0xb6, 0xf9, 0x0a, 0x71, 0xbd, 0xe0, 0xc6, 0xe1, 0x4c, + 0xb8, 0x79, 0x2a, 0xbb, 0x9b, 0x02, 0x82, 0x23, 0x58, 0x54, 0x3d, 0xdd, 0x37, 0xed, 0x86, 0x58, + 0x26, 0xa9, 0x9e, 0x5e, 0x36, 0xed, 0x06, 0x66, 0x10, 0xa9, 0xc0, 0xb2, 0x83, 0x14, 0x98, 0xfe, + 0xfb, 0x1a, 0xcc, 0xb3, 0xd2, 0xd2, 0xd0, 0x39, 0x7c, 0x4e, 0xa6, 0xea, 0xf8, 0x30, 0x2e, 0xa9, + 0xa9, 0xba, 0x77, 0xba, 0xe5, 0x22, 0x2f, 0x46, 0x55, 0x33, 0x77, 0xaf, 0x89, 0x00, 0x8f, 0x25, + 0x14, 0x33, 0x63, 0xc7, 0x1f, 0xf2, 0x38, 0xa3, 0x16, 0x10, 0xc1, 0x21, 0x3d, 0xfd, 0xeb, 0x59, + 0x38, 0x97, 0x50, 0x23, 0x45, 0x63, 0xaf, 0x29, 0xcb, 0xd8, 0x27, 0x56, 0x90, 0x0e, 0x7b, 0x3d, + 0xf5, 0x3a, 0xac, 0xd5, 0x6d, 0x46, 0x9f, 0x4b, 0x92, 0xd4, 0x4f, 0xbc, 0x11, 0x0b, 0xe6, 0xe8, + 0x37, 0x35, 0x28, 0x1a, 0x11, 0x61, 0xe7, 0x19, 0xc2, 0xfd, 0xf4, 0x07, 0xd3, 0x27, 0xdb, 0x91, + 0xca, 0x86, 0x50, 0x94, 0xa3, 0x63, 0xb9, 0xf8, 0xf3, 0x50, 0x8c, 0x4c, 0x61, 0x1c, 0x19, 0xbd, + 0xf8, 0x22, 0x2c, 0x4e, 0x24, 0xe3, 0x1f, 0x81, 0x71, 0xaf, 0xb0, 0x52, 0x8b, 0xf0, 0x20, 0x5a, + 0x71, 0x2d, 0xbf, 0xb8, 0x28, 0xb9, 0x16, 0x50, 0x7d, 0x1f, 0x16, 0xe3, 0x0e, 0xe8, 0x38, 0x67, + 0xa2, 0x23, 0xa9, 0xdb, 0x0f, 0xc0, 0x98, 0x97, 0x4e, 0xf5, 0xbf, 0xca, 0xc0, 0xb4, 0x28, 0xb4, + 0x7c, 0x04, 0x45, 0x41, 0xf7, 0x95, 0x53, 0xe5, 0xcd, 0x54, 0xea, 0x43, 0x07, 0x56, 0x04, 0x79, + 0xb1, 0x8a, 0xa0, 0x97, 0xd3, 0x61, 0x37, 0xbc, 0x1c, 0xe8, 0x6b, 0x19, 0x58, 0x88, 0x15, 0xae, + 0xa2, 0x2f, 0x6a, 0xfd, 0x59, 0xf0, 0x3b, 0xa9, 0xd6, 0xc6, 0xca, 0x92, 0xb3, 0xe1, 0x09, 0x71, + 0x4f, 0xb9, 0xc6, 0x7e, 0x3b, 0xb5, 0xa7, 0x4e, 0x86, 0xde, 0x68, 0xff, 0x27, 0x0d, 0x1e, 0x1f, + 0x58, 0xca, 0xcb, 0xae, 0x0b, 0xb9, 0x2a, 0x54, 0xc8, 0x5e, 0xca, 0xa5, 0xf9, 0xf2, 0x34, 0x33, + 0x7e, 0xc3, 0x23, 0xce, 0x1e, 0x3d, 0x0b, 0xb3, 0x4c, 0x8f, 0xd3, 0xed, 0xe3, 0x93, 0xb6, 0x78, + 0xa3, 0x82, 0x9d, 0x1c, 0xd4, 0x22, 0xed, 0x58, 0xc1, 0xd2, 0x7f, 0x57, 0x83, 0xd2, 0xa0, 0xcb, + 0x23, 0x23, 0xf8, 0xe5, 0x3f, 0x17, 0x2b, 0xd0, 0x29, 0xf7, 0x15, 0xe8, 0xc4, 0x3c, 0xf3, 0xa0, + 0x16, 0x27, 0xe2, 0x14, 0x67, 0x4f, 0xa8, 0x3f, 0xf9, 0xaa, 0x06, 0x17, 0x06, 0x08, 0x4e, 0x5f, + 0xa1, 0x96, 0x76, 0xea, 0x42, 0xad, 0xcc, 0xa8, 0x85, 0x5a, 0xfa, 0xdf, 0x64, 0x61, 0x51, 0x8c, + 0x27, 0x34, 0xe6, 0xcf, 0x2b, 0x65, 0x4e, 0xef, 0x89, 0x95, 0x39, 0x2d, 0xc7, 0xf1, 0xff, 0xaf, + 0xc6, 0xe9, 0xa7, 0xab, 0xc6, 0xe9, 0x27, 0x19, 0x38, 0x9f, 0x78, 0x31, 0x07, 0x7d, 0x39, 0x41, + 0x0b, 0xde, 0x4d, 0xf9, 0x06, 0xd0, 0x88, 0x7a, 0x70, 0xd2, 0xc2, 0xa0, 0xdf, 0x88, 0x16, 0xe4, + 0xf0, 0x30, 0xe1, 0xe0, 0x0c, 0xee, 0x32, 0x8d, 0x59, 0x9b, 0xa3, 0xff, 0x4a, 0x16, 0xae, 0x8c, + 0x4a, 0xe8, 0xa7, 0xb4, 0x76, 0xd3, 0x53, 0x6a, 0x37, 0x1f, 0x8d, 0x85, 0x3a, 0x9b, 0x32, 0xce, + 0x2f, 0x65, 0xa5, 0xd9, 0xeb, 0x97, 0xcf, 0x91, 0x92, 0x0b, 0xd3, 0xd4, 0x8b, 0x09, 0x9e, 0xa5, + 0x08, 0x55, 0xe1, 0x74, 0x8d, 0x37, 0xbf, 0xd3, 0x2d, 0x2f, 0x89, 0xdb, 0xef, 0x35, 0xe2, 0x8b, + 0x46, 0x1c, 0x74, 0x42, 0x57, 0xa0, 0xe0, 0x72, 0x68, 0x50, 0xad, 0x26, 0x12, 0x26, 0xbc, 0x0d, + 0x4b, 0x28, 0xfa, 0x4c, 0xc4, 0xed, 0xcb, 0x9d, 0xd5, 0xdd, 0x90, 0x61, 0x79, 0xa0, 0xd7, 0xa1, + 0xe0, 0x05, 0x6f, 0x56, 0xf0, 0xd3, 0xc1, 0x67, 0x46, 0x2c, 0x82, 0xa4, 0x51, 0x42, 0xf0, 0x80, + 0x05, 0x9f, 0x9f, 0x7c, 0xde, 0x42, 0x92, 0xd4, 0xdf, 0xd6, 0xa0, 0x28, 0x56, 0xe2, 0x11, 0xd4, + 0x5c, 0xde, 0x53, 0x6b, 0x2e, 0xaf, 0xa5, 0xa2, 0x17, 0x06, 0x14, 0x5c, 0xde, 0x83, 0xd9, 0xe8, + 0xbd, 0x4b, 0xf4, 0x6a, 0x44, 0xaf, 0x69, 0x93, 0xdc, 0xef, 0x0a, 0x34, 0x5f, 0xa8, 0xf3, 0xf4, + 0xb7, 0xa6, 0xe5, 0x57, 0x64, 0x95, 0x9d, 0x51, 0xf9, 0xd2, 0x86, 0xca, 0x57, 0x74, 0x79, 0x33, + 0xa9, 0x2f, 0x2f, 0xba, 0x0d, 0x85, 0x40, 0xf9, 0x08, 0x13, 0xfd, 0x54, 0xb4, 0x5a, 0x85, 0xda, + 0x79, 0x4a, 0x2c, 0x22, 0x94, 0x2c, 0x62, 0x90, 0x6b, 0x28, 0x95, 0xa2, 0x24, 0x83, 0xde, 0x80, + 0xe2, 0x03, 0xc7, 0xbd, 0x6f, 0x39, 0x06, 0x7b, 0x16, 0x06, 0xd2, 0x38, 0xc3, 0x95, 0x27, 0x27, + 0xbc, 0x4a, 0xef, 0x6e, 0x48, 0x1f, 0x47, 0x99, 0xa1, 0x0a, 0x2c, 0xb4, 0x4c, 0x1b, 0x13, 0xa3, + 0x21, 0x4b, 0x2b, 0x73, 0xfc, 0x29, 0x8c, 0xc0, 0x81, 0xdd, 0x51, 0xc1, 0x38, 0x8e, 0x8f, 0x3e, + 0x09, 0x05, 0x4f, 0xdc, 0xed, 0x4c, 0xe7, 0xb4, 0x5d, 0x86, 0x3e, 0x9c, 0x68, 0xf8, 0xed, 0x82, + 0x16, 0x2c, 0x19, 0xa2, 0x6d, 0x58, 0x76, 0xc5, 0xed, 0x29, 0xe5, 0xa5, 0x37, 0x7e, 0xbc, 0xca, + 0x5e, 0x5c, 0xc0, 0x09, 0x70, 0x9c, 0xd8, 0x8b, 0x7a, 0x28, 0xec, 0x02, 0x31, 0x3f, 0x6e, 0x2d, + 0x84, 0x1e, 0x0a, 0x13, 0xf8, 0x06, 0x16, 0xd0, 0x61, 0xa5, 0xba, 0x85, 0x09, 0x4a, 0x75, 0xef, + 0xc2, 0x8c, 0x4b, 0x98, 0x9b, 0x5f, 0x09, 0x52, 0x71, 0x63, 0xd7, 0x00, 0xe0, 0x80, 0x00, 0x0e, + 0x69, 0xd1, 0x25, 0x32, 0xd4, 0x87, 0x1b, 0x6e, 0xa7, 0xf8, 0x7e, 0xa8, 0x58, 0xa6, 0x01, 0xd7, + 0x24, 0xf5, 0xff, 0x9c, 0x83, 0x39, 0x25, 0x9a, 0x45, 0x4f, 0x41, 0x9e, 0xdd, 0x4f, 0x63, 0x3b, + 0xb9, 0x10, 0x6a, 0x1b, 0x76, 0xa1, 0x0d, 0x73, 0x18, 0xfa, 0x9a, 0x06, 0x0b, 0x6d, 0xe5, 0xe4, + 0x2d, 0x50, 0x72, 0x13, 0x66, 0x54, 0xd4, 0xe3, 0xbc, 0xc8, 0x93, 0x47, 0x2a, 0x33, 0x1c, 0xe7, + 0x4e, 0xf7, 0x8a, 0x28, 0x8b, 0xb1, 0x88, 0xcb, 0xb0, 0x85, 0xab, 0x21, 0x49, 0xac, 0xab, 0x60, + 0x1c, 0xc7, 0xa7, 0x2b, 0xcc, 0x66, 0x37, 0xc9, 0x6b, 0x8b, 0x95, 0x80, 0x00, 0x0e, 0x69, 0xa1, + 0x17, 0x61, 0x5e, 0xdc, 0xd7, 0xdf, 0x75, 0x1a, 0x37, 0x0c, 0xef, 0x50, 0xf8, 0xd8, 0x32, 0x26, + 0x58, 0x57, 0xa0, 0x38, 0x86, 0xcd, 0xe6, 0x16, 0x3e, 0x8a, 0xc0, 0x08, 0x4c, 0xa9, 0x2f, 0x42, + 0xad, 0xab, 0x60, 0x1c, 0xc7, 0x47, 0xef, 0x8b, 0xa8, 0x68, 0x9e, 0xad, 0x90, 0x1b, 0x37, 0x41, + 0x4d, 0x57, 0x60, 0xa1, 0xc3, 0x42, 0x92, 0x46, 0x00, 0x14, 0x5b, 0x47, 0x32, 0xbc, 0xa3, 0x82, + 0x71, 0x1c, 0x1f, 0xbd, 0x00, 0x73, 0x2e, 0x55, 0x44, 0x92, 0x00, 0x4f, 0x61, 0xc8, 0xf3, 0x78, + 0x1c, 0x05, 0x62, 0x15, 0x17, 0xbd, 0x04, 0x4b, 0xe1, 0xcd, 0xe5, 0x80, 0x00, 0xcf, 0x69, 0xc8, + 0x47, 0x11, 0x2a, 0x71, 0x04, 0xdc, 0xdf, 0x07, 0xfd, 0x02, 0x2c, 0x46, 0xbe, 0xc4, 0xa6, 0xdd, + 0x20, 0x0f, 0xc5, 0xed, 0xd2, 0x65, 0x96, 0x17, 0x89, 0xc1, 0x70, 0x1f, 0x36, 0xfa, 0x10, 0xcc, + 0xd7, 0x1d, 0xcb, 0x62, 0xea, 0x88, 0xbf, 0x46, 0xc4, 0xaf, 0x91, 0xf2, 0x0b, 0xb7, 0x0a, 0x04, + 0xc7, 0x30, 0xd1, 0x16, 0x20, 0x67, 0xdf, 0x23, 0xee, 0x11, 0x69, 0xbc, 0xc4, 0x1f, 0x94, 0xa6, + 0xd6, 0x78, 0x4e, 0x2d, 0xca, 0xbb, 0xd5, 0x87, 0x81, 0x13, 0x7a, 0xa1, 0x7d, 0xb8, 0x18, 0x98, + 0x86, 0xfe, 0x1e, 0xa5, 0x92, 0x12, 0xb9, 0x5c, 0xbc, 0x3b, 0x10, 0x13, 0x0f, 0xa1, 0x82, 0x7e, + 0x49, 0xad, 0x0a, 0x9f, 0x4f, 0xe3, 0x7d, 0xc7, 0x78, 0x90, 0x7e, 0x62, 0x49, 0xb8, 0x0b, 0x53, + 0xbc, 0x0e, 0xb3, 0xb4, 0x90, 0xc6, 0x8d, 0xed, 0xe8, 0xe3, 0x27, 0xa1, 0xc9, 0xe0, 0xad, 0x58, + 0x70, 0x42, 0x9f, 0x86, 0x99, 0xfd, 0xe0, 0x25, 0xac, 0xd2, 0x62, 0x1a, 0x66, 0x32, 0xf6, 0xa8, + 0x5b, 0x18, 0x84, 0x4a, 0x00, 0x0e, 0x59, 0xa2, 0xa7, 0xa1, 0x78, 0x63, 0xb7, 0x22, 0x25, 0x7d, + 0x89, 0x49, 0x58, 0x8e, 0x76, 0xc1, 0x51, 0x00, 0xdd, 0xc5, 0xd2, 0x7d, 0x42, 0x6c, 0xc9, 0x43, + 0xf3, 0xdb, 0xef, 0x0d, 0x51, 0x6c, 0x96, 0xe6, 0xc2, 0xb5, 0xd2, 0xb9, 0x18, 0xb6, 0x68, 0xc7, + 0x12, 0x03, 0xbd, 0x0e, 0x45, 0x61, 0x93, 0x98, 0xfe, 0x5b, 0x3e, 0xdd, 0x8d, 0x03, 0x1c, 0x92, + 0xc0, 0x51, 0x7a, 0xe8, 0x39, 0x28, 0xb6, 0xd9, 0x03, 0x41, 0xe4, 0x7a, 0xc7, 0xb2, 0x4a, 0xe7, + 0x99, 0x6e, 0x96, 0xe7, 0xff, 0xbb, 0x21, 0x08, 0x47, 0xf1, 0xd0, 0x33, 0x41, 0x8e, 0xfa, 0x5d, + 0x4a, 0x3a, 0x47, 0xe6, 0xa8, 0xa5, 0xd3, 0x3b, 0xa0, 0xb2, 0xef, 0xc2, 0x09, 0x67, 0x14, 0x9f, + 0x0b, 0xcf, 0x68, 0xe5, 0x1b, 0x18, 0x9f, 0x8a, 0x4a, 0x83, 0x96, 0xc6, 0xb3, 0xd7, 0x7d, 0xcf, + 0xac, 0x71, 0x63, 0x91, 0x28, 0x0b, 0x6d, 0x29, 0xff, 0xa9, 0x5c, 0x97, 0x55, 0xdf, 0xf7, 0xe0, + 0xd5, 0xe4, 0xaa, 0xf4, 0xeb, 0xdf, 0xcf, 0xc9, 0x73, 0x9a, 0x58, 0x6a, 0xd6, 0x85, 0xbc, 0xe9, + 0xf9, 0xa6, 0x93, 0x62, 0x89, 0x7f, 0xec, 0x61, 0x0c, 0x56, 0x6a, 0xc6, 0x00, 0x98, 0xb3, 0xa2, + 0x3c, 0xed, 0xa6, 0x69, 0x3f, 0x14, 0xd3, 0xbf, 0x9d, 0x7a, 0xce, 0x95, 0xf3, 0x64, 0x00, 0xcc, + 0x59, 0xa1, 0x7b, 0x90, 0x35, 0xac, 0xfd, 0x94, 0x9e, 0x38, 0x8f, 0xff, 0x9b, 0x00, 0x5e, 0xa8, + 0x51, 0xd9, 0xae, 0x62, 0xca, 0x84, 0xf2, 0xf2, 0x5a, 0xa6, 0xf0, 0x2f, 0x26, 0xe4, 0x55, 0xdb, + 0xd9, 0x4c, 0xe2, 0x55, 0xdb, 0xd9, 0xc4, 0x94, 0x09, 0xfa, 0xa2, 0x06, 0x60, 0xc8, 0x27, 0xfc, + 0xd3, 0x79, 0x93, 0x70, 0xd0, 0xbf, 0x04, 0xe0, 0x15, 0x54, 0x21, 0x14, 0x47, 0x38, 0xeb, 0x6f, + 0x6a, 0xb0, 0xd4, 0x37, 0xd8, 0xf8, 0x7f, 0x37, 0xd0, 0x46, 0xff, 0xef, 0x06, 0xe2, 0xe9, 0x94, + 0x5a, 0xdb, 0x32, 0x13, 0xaf, 0xc9, 0xec, 0xc5, 0xe0, 0xb8, 0xaf, 0x87, 0xfe, 0x2d, 0x0d, 0x8a, + 0x91, 0x12, 0x67, 0xea, 0xf7, 0xb2, 0x52, 0x70, 0x31, 0x8c, 0xf0, 0xd5, 0x18, 0x76, 0x34, 0xc5, + 0x61, 0xfc, 0x94, 0xb4, 0x19, 0x9e, 0x15, 0x46, 0x4e, 0x49, 0x69, 0x2b, 0x16, 0x50, 0x74, 0x19, + 0x72, 0x9e, 0x4f, 0xda, 0x4c, 0xa2, 0x22, 0x15, 0xcf, 0x2c, 0x57, 0xc0, 0x20, 0x8c, 0x1d, 0x55, + 0x8e, 0xa2, 0x7c, 0x25, 0xf2, 0x48, 0x8d, 0x41, 0xdd, 0x6c, 0x06, 0x43, 0x97, 0x20, 0x4b, 0xec, + 0x86, 0xf0, 0x16, 0x8b, 0x02, 0x25, 0x7b, 0xcd, 0x6e, 0x60, 0xda, 0xae, 0xdf, 0x82, 0xd9, 0x1a, + 0xa9, 0xbb, 0xc4, 0x7f, 0x99, 0x1c, 0x8f, 0x76, 0x8e, 0x77, 0x89, 0xe7, 0x3f, 0x33, 0x2a, 0x41, + 0xda, 0x9d, 0xb6, 0xeb, 0x7f, 0xa8, 0x41, 0xec, 0xcd, 0x20, 0xa4, 0xc7, 0x52, 0x9a, 0xd0, 0x9f, + 0xce, 0x54, 0xe2, 0xff, 0xcc, 0xd0, 0xf8, 0x7f, 0x0b, 0x50, 0xcb, 0xf0, 0xeb, 0x87, 0x62, 0x7d, + 0xc4, 0xf3, 0x54, 0xdc, 0x51, 0x0f, 0x2f, 0x54, 0xf4, 0x61, 0xe0, 0x84, 0x5e, 0xfa, 0x12, 0x2c, + 0xc8, 0x28, 0x9e, 0x4b, 0x86, 0xfe, 0xed, 0x2c, 0xcc, 0x2a, 0xaf, 0x43, 0x9f, 0xfc, 0x45, 0x46, + 0x1f, 0x7b, 0x42, 0x34, 0x9e, 0x1d, 0x33, 0x1a, 0x8f, 0x1e, 0x7f, 0xe4, 0xce, 0xf6, 0xf8, 0x23, + 0x9f, 0xce, 0xf1, 0x87, 0x0f, 0xd3, 0xe2, 0xff, 0x91, 0x88, 0x8a, 0xb9, 0x9d, 0x94, 0xae, 0x44, + 0x8a, 0x2b, 0x5e, 0xac, 0x48, 0x30, 0xd8, 0xe5, 0x01, 0x2b, 0xfd, 0x1b, 0x79, 0x98, 0x57, 0x2f, + 0x49, 0x8e, 0xb0, 0x92, 0xef, 0xeb, 0x5b, 0xc9, 0x31, 0x43, 0x9c, 0xec, 0xa4, 0x21, 0x4e, 0x6e, + 0xd2, 0x10, 0x27, 0x7f, 0x8a, 0x10, 0xa7, 0x3f, 0x40, 0x99, 0x1a, 0x39, 0x40, 0xf9, 0xb0, 0x4c, + 0x93, 0x4d, 0x2b, 0xe7, 0xca, 0x61, 0x9a, 0x0c, 0xa9, 0xcb, 0xb0, 0xee, 0x34, 0x12, 0xd3, 0x8d, + 0x85, 0x13, 0x6a, 0xf0, 0xdc, 0xc4, 0xac, 0xd6, 0xf8, 0xa7, 0x28, 0xef, 0x1a, 0x23, 0xa3, 0x15, + 0xfe, 0xcb, 0x1d, 0x66, 0x21, 0x40, 0xb5, 0x2e, 0xb5, 0x10, 0x84, 0xa3, 0x78, 0xec, 0xf9, 0x65, + 0xf5, 0x71, 0x68, 0x16, 0x31, 0x46, 0x9f, 0x5f, 0x8e, 0x3d, 0x26, 0x1d, 0xc7, 0xd7, 0x3f, 0x9b, + 0x81, 0xf0, 0x81, 0x6b, 0xf6, 0x12, 0x95, 0x17, 0x51, 0xd3, 0xc2, 0x99, 0xda, 0x9a, 0xf4, 0xb9, + 0xb7, 0x90, 0xa2, 0x48, 0x48, 0x47, 0x5a, 0xb0, 0xc2, 0xf1, 0xbf, 0xe1, 0x61, 0x6b, 0x03, 0x16, + 0x62, 0x75, 0xb9, 0xa9, 0x17, 0xb8, 0x7c, 0x2b, 0x03, 0x33, 0xb2, 0xb2, 0x99, 0x5a, 0xb6, 0x8e, + 0x1b, 0x3c, 0x9a, 0x23, 0x2d, 0xdb, 0x1d, 0xbc, 0x8d, 0x69, 0x3b, 0x7a, 0x08, 0xd3, 0x87, 0xc4, + 0x68, 0x10, 0x37, 0x38, 0xa7, 0xda, 0x49, 0xa9, 0xa4, 0xfa, 0x06, 0xa3, 0x1a, 0xce, 0x85, 0xff, + 0xf6, 0x70, 0xc0, 0x0e, 0xbd, 0x08, 0xf3, 0xbe, 0xd9, 0x22, 0x34, 0xc0, 0x88, 0x58, 0x8d, 0x6c, + 0x78, 0xf8, 0xb3, 0xa7, 0x40, 0x71, 0x0c, 0x9b, 0xaa, 0xb5, 0x7b, 0x9e, 0x63, 0xb3, 0xfb, 0xc7, + 0x39, 0x35, 0x8a, 0xdb, 0xaa, 0xdd, 0xba, 0xc9, 0xae, 0x1f, 0x4b, 0x0c, 0x8a, 0x6d, 0xb2, 0xca, + 0x4e, 0x97, 0x88, 0x94, 0xd5, 0x62, 0x78, 0x0f, 0x85, 0xb7, 0x63, 0x89, 0xa1, 0xdf, 0x81, 0x85, + 0xd8, 0x44, 0x02, 0x0f, 0x41, 0x4b, 0xf6, 0x10, 0x46, 0xfa, 0xbf, 0x41, 0xd5, 0xd5, 0xb7, 0x7e, + 0xb0, 0xf2, 0xd8, 0x77, 0x7e, 0xb0, 0xf2, 0xd8, 0x77, 0x7f, 0xb0, 0xf2, 0xd8, 0x67, 0x7b, 0x2b, + 0xda, 0x5b, 0xbd, 0x15, 0xed, 0x3b, 0xbd, 0x15, 0xed, 0xbb, 0xbd, 0x15, 0xed, 0xfb, 0xbd, 0x15, + 0xed, 0xcd, 0x1f, 0xae, 0x3c, 0xf6, 0x6a, 0x21, 0xf8, 0x98, 0xff, 0x15, 0x00, 0x00, 0xff, 0xff, + 0xfb, 0x73, 0xce, 0x74, 0x36, 0x6d, 0x00, 0x00, } func (m *ALBTrafficRouting) Marshal() (dAtA []byte, err error) { @@ -2911,6 +2944,39 @@ func (m *AnalysisRunStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *AnalysisRunStrategy) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AnalysisRunStrategy) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AnalysisRunStrategy) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.UnsuccessfulRunHistoryLimit != nil { + i = encodeVarintGenerated(dAtA, i, uint64(*m.UnsuccessfulRunHistoryLimit)) + i-- + dAtA[i] = 0x10 + } + if m.SuccessfulRunHistoryLimit != nil { + i = encodeVarintGenerated(dAtA, i, uint64(*m.SuccessfulRunHistoryLimit)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *AnalysisTemplate) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5823,6 +5889,18 @@ func (m *RolloutSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Analysis != nil { + { + size, err := m.Analysis.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } if m.WorkloadRef != nil { { size, err := m.WorkloadRef.MarshalToSizedBuffer(dAtA[:i]) @@ -6870,6 +6948,21 @@ func (m *AnalysisRunStatus) Size() (n int) { return n } +func (m *AnalysisRunStrategy) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SuccessfulRunHistoryLimit != nil { + n += 1 + sovGenerated(uint64(*m.SuccessfulRunHistoryLimit)) + } + if m.UnsuccessfulRunHistoryLimit != nil { + n += 1 + sovGenerated(uint64(*m.UnsuccessfulRunHistoryLimit)) + } + return n +} + func (m *AnalysisTemplate) Size() (n int) { if m == nil { return 0 @@ -7959,6 +8052,10 @@ func (m *RolloutSpec) Size() (n int) { l = m.WorkloadRef.Size() n += 1 + l + sovGenerated(uint64(l)) } + if m.Analysis != nil { + l = m.Analysis.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -8368,6 +8465,17 @@ func (this *AnalysisRunStatus) String() string { }, "") return s } +func (this *AnalysisRunStrategy) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&AnalysisRunStrategy{`, + `SuccessfulRunHistoryLimit:` + valueToStringGenerated(this.SuccessfulRunHistoryLimit) + `,`, + `UnsuccessfulRunHistoryLimit:` + valueToStringGenerated(this.UnsuccessfulRunHistoryLimit) + `,`, + `}`, + }, "") + return s +} func (this *AnalysisTemplate) String() string { if this == nil { return "nil" @@ -9188,6 +9296,7 @@ func (this *RolloutSpec) String() string { `ProgressDeadlineSeconds:` + valueToStringGenerated(this.ProgressDeadlineSeconds) + `,`, `RestartAt:` + strings.Replace(fmt.Sprintf("%v", this.RestartAt), "Time", "v1.Time", 1) + `,`, `WorkloadRef:` + strings.Replace(this.WorkloadRef.String(), "ObjectRef", "ObjectRef", 1) + `,`, + `Analysis:` + strings.Replace(this.Analysis.String(), "AnalysisRunStrategy", "AnalysisRunStrategy", 1) + `,`, `}`, }, "") return s @@ -10397,6 +10506,96 @@ func (m *AnalysisRunStatus) Unmarshal(dAtA []byte) error { } return nil } +func (m *AnalysisRunStrategy) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AnalysisRunStrategy: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AnalysisRunStrategy: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SuccessfulRunHistoryLimit", 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.SuccessfulRunHistoryLimit = &v + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UnsuccessfulRunHistoryLimit", 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.UnsuccessfulRunHistoryLimit = &v + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *AnalysisTemplate) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -19856,6 +20055,42 @@ func (m *RolloutSpec) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Analysis", 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 + } + if m.Analysis == nil { + m.Analysis = &AnalysisRunStrategy{} + } + if err := m.Analysis.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/pkg/apis/rollouts/v1alpha1/generated.proto b/pkg/apis/rollouts/v1alpha1/generated.proto index fc7c59bf94..368d315dc5 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.proto +++ b/pkg/apis/rollouts/v1alpha1/generated.proto @@ -120,6 +120,16 @@ message AnalysisRunStatus { optional k8s.io.apimachinery.pkg.apis.meta.v1.Time startedAt = 4; } +// AnalysisRunStrategy configuration for the analysis runs and experiments to retain +message AnalysisRunStrategy { + // SuccessfulRunHistoryLimit limits the number of old successful analysis runs and experiments to be retained in a history + optional int32 successfulRunHistoryLimit = 1; + + // UnsuccessfulRunHistoryLimit limits the number of old unsuccessful analysis runs and experiments to be retained in a history. + // Stages for unsuccessful: "Error", "Failed", "Inconclusive" + optional int32 unsuccessfulRunHistoryLimit = 2; +} + // AnalysisTemplate holds the template for performing canary analysis // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -1014,6 +1024,9 @@ message RolloutSpec { // RestartAt indicates when all the pods of a Rollout should be restarted optional k8s.io.apimachinery.pkg.apis.meta.v1.Time restartAt = 9; + + // Analysis configuration for the analysis runs to retain + optional AnalysisRunStrategy analysis = 11; } // RolloutStatus is the status for a Rollout resource diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index 29a7aa4dd5..1c990ed785 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -36,6 +36,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisRunList": schema_pkg_apis_rollouts_v1alpha1_AnalysisRunList(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisRunSpec": schema_pkg_apis_rollouts_v1alpha1_AnalysisRunSpec(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisRunStatus": schema_pkg_apis_rollouts_v1alpha1_AnalysisRunStatus(ref), + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisRunStrategy": schema_pkg_apis_rollouts_v1alpha1_AnalysisRunStrategy(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisTemplate": schema_pkg_apis_rollouts_v1alpha1_AnalysisTemplate(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisTemplateList": schema_pkg_apis_rollouts_v1alpha1_AnalysisTemplateList(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisTemplateSpec": schema_pkg_apis_rollouts_v1alpha1_AnalysisTemplateSpec(ref), @@ -427,6 +428,33 @@ func schema_pkg_apis_rollouts_v1alpha1_AnalysisRunStatus(ref common.ReferenceCal } } +func schema_pkg_apis_rollouts_v1alpha1_AnalysisRunStrategy(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "AnalysisRunStrategy configuration for the analysis runs and experiments to retain", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "successfulRunHistoryLimit": { + SchemaProps: spec.SchemaProps{ + Description: "SuccessfulRunHistoryLimit limits the number of old successful analysis runs and experiments to be retained in a history", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "unsuccessfulRunHistoryLimit": { + SchemaProps: spec.SchemaProps{ + Description: "UnsuccessfulRunHistoryLimit limits the number of old unsuccessful analysis runs and experiments to be retained in a history. Stages for unsuccessful: \"Error\", \"Failed\", \"Inconclusive\"", + Type: []string{"integer"}, + Format: "int32", + }, + }, + }, + }, + }, + } +} + func schema_pkg_apis_rollouts_v1alpha1_AnalysisTemplate(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2990,11 +3018,17 @@ func schema_pkg_apis_rollouts_v1alpha1_RolloutSpec(ref common.ReferenceCallback) Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Time"), }, }, + "analysis": { + SchemaProps: spec.SchemaProps{ + Description: "Analysis configuration for the analysis runs to retain", + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisRunStrategy"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ObjectRef", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutStrategy", "k8s.io/api/core/v1.PodTemplateSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.AnalysisRunStrategy", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ObjectRef", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.RolloutStrategy", "k8s.io/api/core/v1.PodTemplateSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector", "k8s.io/apimachinery/pkg/apis/meta/v1.Time"}, } } diff --git a/pkg/apis/rollouts/v1alpha1/types.go b/pkg/apis/rollouts/v1alpha1/types.go index 589fdc821d..bfd2475538 100644 --- a/pkg/apis/rollouts/v1alpha1/types.go +++ b/pkg/apis/rollouts/v1alpha1/types.go @@ -71,6 +71,8 @@ type RolloutSpec struct { ProgressDeadlineSeconds *int32 `json:"progressDeadlineSeconds,omitempty" protobuf:"varint,8,opt,name=progressDeadlineSeconds"` // RestartAt indicates when all the pods of a Rollout should be restarted RestartAt *metav1.Time `json:"restartAt,omitempty" protobuf:"bytes,9,opt,name=restartAt"` + // Analysis configuration for the analysis runs to retain + Analysis *AnalysisRunStrategy `json:"analysis,omitempty" protobuf:"bytes,11,opt,name=analysis"` } func (s *RolloutSpec) SetResolvedSelector(selector *metav1.LabelSelector) { @@ -295,6 +297,15 @@ type CanaryStrategy struct { AbortScaleDownDelaySeconds *int32 `json:"abortScaleDownDelaySeconds,omitempty" protobuf:"varint,13,opt,name=abortScaleDownDelaySeconds"` } +// AnalysisRunStrategy configuration for the analysis runs and experiments to retain +type AnalysisRunStrategy struct { + // SuccessfulRunHistoryLimit limits the number of old successful analysis runs and experiments to be retained in a history + SuccessfulRunHistoryLimit *int32 `json:"successfulRunHistoryLimit,omitempty" protobuf:"varint,1,opt,name=successfulRunHistoryLimit"` + // UnsuccessfulRunHistoryLimit limits the number of old unsuccessful analysis runs and experiments to be retained in a history. + // Stages for unsuccessful: "Error", "Failed", "Inconclusive" + UnsuccessfulRunHistoryLimit *int32 `json:"unsuccessfulRunHistoryLimit,omitempty" protobuf:"varint,2,opt,name=unsuccessfulRunHistoryLimit"` +} + // ALBTrafficRouting configuration for ALB ingress controller to control traffic routing type ALBTrafficRouting struct { // Ingress refers to the name of an `Ingress` resource in the same namespace as the `Rollout` diff --git a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go index ab24ca447e..aa31789c56 100644 --- a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go @@ -202,6 +202,32 @@ func (in *AnalysisRunStatus) DeepCopy() *AnalysisRunStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AnalysisRunStrategy) DeepCopyInto(out *AnalysisRunStrategy) { + *out = *in + if in.SuccessfulRunHistoryLimit != nil { + in, out := &in.SuccessfulRunHistoryLimit, &out.SuccessfulRunHistoryLimit + *out = new(int32) + **out = **in + } + if in.UnsuccessfulRunHistoryLimit != nil { + in, out := &in.UnsuccessfulRunHistoryLimit, &out.UnsuccessfulRunHistoryLimit + *out = new(int32) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnalysisRunStrategy. +func (in *AnalysisRunStrategy) DeepCopy() *AnalysisRunStrategy { + if in == nil { + return nil + } + out := new(AnalysisRunStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AnalysisTemplate) DeepCopyInto(out *AnalysisTemplate) { *out = *in @@ -1621,6 +1647,11 @@ func (in *RolloutSpec) DeepCopyInto(out *RolloutSpec) { in, out := &in.RestartAt, &out.RestartAt *out = (*in).DeepCopy() } + if in.Analysis != nil { + in, out := &in.Analysis, &out.Analysis + *out = new(AnalysisRunStrategy) + (*in).DeepCopyInto(*out) + } return } diff --git a/rollout/analysis.go b/rollout/analysis.go index 74424f45b1..02a944c874 100644 --- a/rollout/analysis.go +++ b/rollout/analysis.go @@ -17,6 +17,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" analysisutil "github.com/argoproj/argo-rollouts/utils/analysis" "github.com/argoproj/argo-rollouts/utils/annotations" + "github.com/argoproj/argo-rollouts/utils/defaults" logutil "github.com/argoproj/argo-rollouts/utils/log" "github.com/argoproj/argo-rollouts/utils/record" replicasetutil "github.com/argoproj/argo-rollouts/utils/replicaset" @@ -131,7 +132,9 @@ func (c *rolloutContext) reconcileAnalysisRuns() error { return err } - arsToDelete := analysisutil.FilterAnalysisRunsToDelete(otherArs, c.allRSs) + limitSucceedArs := defaults.GetAnalysisRunSuccessfulHistoryLimitOrDefault(c.rollout) + limitFailedArs := defaults.GetAnalysisRunUnsuccessfulHistoryLimitOrDefault(c.rollout) + arsToDelete := analysisutil.FilterAnalysisRunsToDelete(otherArs, c.allRSs, limitSucceedArs, limitFailedArs) err = c.deleteAnalysisRuns(arsToDelete) if err != nil { return err diff --git a/rollout/experiment.go b/rollout/experiment.go index 7f21d788e2..a8c8f06f5d 100644 --- a/rollout/experiment.go +++ b/rollout/experiment.go @@ -7,6 +7,7 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" analysisutil "github.com/argoproj/argo-rollouts/utils/analysis" "github.com/argoproj/argo-rollouts/utils/annotations" + "github.com/argoproj/argo-rollouts/utils/defaults" experimentutil "github.com/argoproj/argo-rollouts/utils/experiment" "github.com/argoproj/argo-rollouts/utils/record" replicasetutil "github.com/argoproj/argo-rollouts/utils/replicaset" @@ -188,7 +189,9 @@ func (c *rolloutContext) reconcileExperiments() error { return err } - exsToDelete := experimentutil.FilterExperimentsToDelete(otherExs, c.allRSs) + limitSuccessful := defaults.GetAnalysisRunSuccessfulHistoryLimitOrDefault(c.rollout) + limitUnsuccessful := defaults.GetAnalysisRunUnsuccessfulHistoryLimitOrDefault(c.rollout) + exsToDelete := experimentutil.FilterExperimentsToDelete(otherExs, c.allRSs, limitSuccessful, limitUnsuccessful) err = c.deleteExperiments(exsToDelete) if err != nil { return err diff --git a/utils/analysis/filter.go b/utils/analysis/filter.go index 7806fe15ab..ce34f0486f 100644 --- a/utils/analysis/filter.go +++ b/utils/analysis/filter.go @@ -1,6 +1,8 @@ package analysis import ( + "sort" + appsv1 "k8s.io/api/apps/v1" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" @@ -108,6 +110,15 @@ func SortAnalysisRunByPodHash(ars []*v1alpha1.AnalysisRun) map[string][]*v1alpha return podHashToAr } +// AnalysisRunByCreationTimestamp sorts a list of AnalysisRun by creation timestamp +type AnalysisRunByCreationTimestamp []*v1alpha1.AnalysisRun + +func (o AnalysisRunByCreationTimestamp) Len() int { return len(o) } +func (o AnalysisRunByCreationTimestamp) Swap(i, j int) { o[i], o[j] = o[j], o[i] } +func (o AnalysisRunByCreationTimestamp) Less(i, j int) bool { + return o[i].CreationTimestamp.Before(&o[j].CreationTimestamp) +} + // FilterAnalysisRunsToDelete returns a list of analysis runs that should be deleted in the cases where: // 1. The analysis run has no pod hash label, // 2. There is no ReplicaSet with the same pod hash as the analysis run @@ -115,10 +126,10 @@ func SortAnalysisRunByPodHash(ars []*v1alpha1.AnalysisRun) map[string][]*v1alpha // Note: It is okay to use pod hash for filtering since the analysis run's pod hash is originally derived from the new RS. // Even if there is a library change during the lifetime of the analysis run, the ReplicaSet's pod hash that the analysis // run references does not change. -func FilterAnalysisRunsToDelete(ars []*v1alpha1.AnalysisRun, olderRSs []*appsv1.ReplicaSet) []*v1alpha1.AnalysisRun { +func FilterAnalysisRunsToDelete(ars []*v1alpha1.AnalysisRun, allRSs []*appsv1.ReplicaSet, limitSuccessful int32, limitUnsuccessful int32) []*v1alpha1.AnalysisRun { olderRsPodHashes := map[string]bool{} - for i := range olderRSs { - rs := olderRSs[i] + for i := range allRSs { + rs := allRSs[i] if rs == nil { continue } @@ -126,6 +137,10 @@ func FilterAnalysisRunsToDelete(ars []*v1alpha1.AnalysisRun, olderRSs []*appsv1. olderRsPodHashes[podHash] = rs.DeletionTimestamp != nil } } + sort.Sort(sort.Reverse(AnalysisRunByCreationTimestamp(ars))) + + var retainedSuccessful int32 = 0 + var retainedUnsuccessful int32 = 0 arsToDelete := []*v1alpha1.AnalysisRun{} for i := range ars { ar := ars[i] @@ -148,6 +163,22 @@ func FilterAnalysisRunsToDelete(ars []*v1alpha1.AnalysisRun, olderRSs []*appsv1. arsToDelete = append(arsToDelete, ar) continue } + + if ar.Status.Phase == v1alpha1.AnalysisPhaseSuccessful { + if retainedSuccessful < limitSuccessful { + retainedSuccessful++ + } else { + arsToDelete = append(arsToDelete, ar) + } + } else if ar.Status.Phase == v1alpha1.AnalysisPhaseFailed || + ar.Status.Phase == v1alpha1.AnalysisPhaseError || + ar.Status.Phase == v1alpha1.AnalysisPhaseInconclusive { + if retainedUnsuccessful < limitUnsuccessful { + retainedUnsuccessful++ + } else { + arsToDelete = append(arsToDelete, ar) + } + } } return arsToDelete } diff --git a/utils/analysis/filter_test.go b/utils/analysis/filter_test.go index e4240b961e..c7ca00f04b 100644 --- a/utils/analysis/filter_test.go +++ b/utils/analysis/filter_test.go @@ -196,7 +196,7 @@ func TestFilterAnalysisRunsToDelete(t *testing.T) { deletedRS, nil, } - filteredArs := FilterAnalysisRunsToDelete(ars, olderRSs) + filteredArs := FilterAnalysisRunsToDelete(ars, olderRSs, 4, 4) assert.Len(t, filteredArs, 3) assert.NotContains(t, filteredArs, arNoDeletion) assert.Contains(t, filteredArs, arWithNoPodHash) @@ -204,6 +204,59 @@ func TestFilterAnalysisRunsToDelete(t *testing.T) { assert.Contains(t, filteredArs, arWithNoMatchingRS) } +func TestFilterAnalysisRunsToDeleteByLimit(t *testing.T) { + rs := func(podHash string) *appsv1.ReplicaSet { + return &appsv1.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: podHash}, + }, + } + } + ar := func(podHash string, phase v1alpha1.AnalysisPhase) *v1alpha1.AnalysisRun { + return &v1alpha1.AnalysisRun{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: podHash}, + CreationTimestamp: metav1.Now(), + }, + Status: v1alpha1.AnalysisRunStatus{ + Phase: phase, + }, + } + } + + arS0 := ar("a", v1alpha1.AnalysisPhaseSuccessful) + arS1 := ar("a", v1alpha1.AnalysisPhaseSuccessful) + arS2 := ar("a", v1alpha1.AnalysisPhaseSuccessful) + arS3 := ar("a", v1alpha1.AnalysisPhaseSuccessful) + arS4 := ar("a", v1alpha1.AnalysisPhaseSuccessful) + arF0 := ar("a", v1alpha1.AnalysisPhaseFailed) + arF1 := ar("a", v1alpha1.AnalysisPhaseFailed) + arF2 := ar("a", v1alpha1.AnalysisPhaseFailed) + arF3 := ar("a", v1alpha1.AnalysisPhaseFailed) + arF4 := ar("a", v1alpha1.AnalysisPhaseFailed) + + validRS := rs("a") + ars := []*v1alpha1.AnalysisRun{ + arS0, arF0, arS1, arF1, arS2, arF2, arS3, arF3, arS4, arF4, + } + olderRSs := []*appsv1.ReplicaSet{ + validRS, + nil, + } + + assert.Len(t, FilterAnalysisRunsToDelete(ars, olderRSs, 1, 0), 9) + assert.Len(t, FilterAnalysisRunsToDelete(ars, olderRSs, 5, 0), 5) + assert.Len(t, FilterAnalysisRunsToDelete(ars, olderRSs, 10, 0), 5) + + assert.Len(t, FilterAnalysisRunsToDelete(ars, olderRSs, 0, 1), 9) + assert.Len(t, FilterAnalysisRunsToDelete(ars, olderRSs, 0, 5), 5) + assert.Len(t, FilterAnalysisRunsToDelete(ars, olderRSs, 0, 10), 5) + + assert.Len(t, FilterAnalysisRunsToDelete(ars, olderRSs, 0, 0), 10) + assert.Len(t, FilterAnalysisRunsToDelete(ars, olderRSs, 4, 4), 2) + assert.Len(t, FilterAnalysisRunsToDelete(ars, olderRSs, 10, 10), 0) +} + func TestSortAnalysisRunByPodHash(t *testing.T) { emptyMap := SortAnalysisRunByPodHash(nil) assert.NotNil(t, 0) diff --git a/utils/defaults/defaults.go b/utils/defaults/defaults.go index 99e6520400..cd6e864a11 100644 --- a/utils/defaults/defaults.go +++ b/utils/defaults/defaults.go @@ -16,6 +16,10 @@ const ( DefaultReplicas = int32(1) // DefaultRevisionHistoryLimit default number of revisions to keep if .Spec.RevisionHistoryLimit is nil DefaultRevisionHistoryLimit = int32(10) + // DefaultAnalysisRunSuccessfulHistoryLimit default number of successful AnalysisRuns to keep if .Spec.Analysis.SuccessfulRunHistoryLimit is nil + DefaultAnalysisRunSuccessfulHistoryLimit = int32(5) + // DefaultAnalysisRunUnsuccessfulHistoryLimit default number of unsuccessful AnalysisRuns to keep if .Spec.Analysis.UnsuccessfulRunHistoryLimit is nil + DefaultAnalysisRunUnsuccessfulHistoryLimit = int32(5) // DefaultMaxSurge default number for the max number of additional pods that can be brought up during a rollout DefaultMaxSurge = "25" // DefaultMaxUnavailable default number for the max number of unavailable pods during a rollout @@ -56,6 +60,22 @@ func GetRevisionHistoryLimitOrDefault(rollout *v1alpha1.Rollout) int32 { return *rollout.Spec.RevisionHistoryLimit } +// GetAnalysisRunSuccessfulHistoryLimitOrDefault returns the specified number of succeed AnalysisRuns to keep or the default number +func GetAnalysisRunSuccessfulHistoryLimitOrDefault(rollout *v1alpha1.Rollout) int32 { + if rollout.Spec.Analysis == nil || rollout.Spec.Analysis.SuccessfulRunHistoryLimit == nil { + return DefaultAnalysisRunSuccessfulHistoryLimit + } + return *rollout.Spec.Analysis.SuccessfulRunHistoryLimit +} + +// GetAnalysisRunUnsuccessfulHistoryLimitOrDefault returns the specified number of failed AnalysisRuns to keep or the default number +func GetAnalysisRunUnsuccessfulHistoryLimitOrDefault(rollout *v1alpha1.Rollout) int32 { + if rollout.Spec.Analysis == nil || rollout.Spec.Analysis.UnsuccessfulRunHistoryLimit == nil { + return DefaultAnalysisRunUnsuccessfulHistoryLimit + } + return *rollout.Spec.Analysis.UnsuccessfulRunHistoryLimit +} + func GetMaxSurgeOrDefault(rollout *v1alpha1.Rollout) *intstr.IntOrString { if rollout.Spec.Strategy.Canary != nil && rollout.Spec.Strategy.Canary.MaxSurge != nil { return rollout.Spec.Strategy.Canary.MaxSurge diff --git a/utils/defaults/defaults_test.go b/utils/defaults/defaults_test.go index f76c3b097b..a3e5322c3e 100644 --- a/utils/defaults/defaults_test.go +++ b/utils/defaults/defaults_test.go @@ -45,6 +45,34 @@ func TestGetRevisionHistoryOrDefault(t *testing.T) { assert.Equal(t, DefaultRevisionHistoryLimit, GetRevisionHistoryLimitOrDefault(rolloutDefaultValue)) } +func TestGetAnalysisRunSuccessfulHistoryLimitOrDefault(t *testing.T) { + succeedHistoryLimit := int32(2) + rolloutNonDefaultValue := &v1alpha1.Rollout{ + Spec: v1alpha1.RolloutSpec{ + Analysis: &v1alpha1.AnalysisRunStrategy{SuccessfulRunHistoryLimit: &succeedHistoryLimit}, + }, + } + + assert.Equal(t, succeedHistoryLimit, GetAnalysisRunSuccessfulHistoryLimitOrDefault(rolloutNonDefaultValue)) + assert.Equal(t, DefaultAnalysisRunSuccessfulHistoryLimit, GetAnalysisRunSuccessfulHistoryLimitOrDefault(&v1alpha1.Rollout{})) + assert.Equal(t, DefaultAnalysisRunSuccessfulHistoryLimit, GetAnalysisRunSuccessfulHistoryLimitOrDefault(&v1alpha1.Rollout{Spec: v1alpha1.RolloutSpec{}})) + assert.Equal(t, DefaultAnalysisRunSuccessfulHistoryLimit, GetAnalysisRunSuccessfulHistoryLimitOrDefault(&v1alpha1.Rollout{Spec: v1alpha1.RolloutSpec{Analysis: &v1alpha1.AnalysisRunStrategy{}}})) +} + +func TestGetAnalysisRunUnsuccessfulHistoryLimitOrDefault(t *testing.T) { + failedHistoryLimit := int32(3) + rolloutNonDefaultValue := &v1alpha1.Rollout{ + Spec: v1alpha1.RolloutSpec{ + Analysis: &v1alpha1.AnalysisRunStrategy{UnsuccessfulRunHistoryLimit: &failedHistoryLimit}, + }, + } + + assert.Equal(t, failedHistoryLimit, GetAnalysisRunUnsuccessfulHistoryLimitOrDefault(rolloutNonDefaultValue)) + assert.Equal(t, DefaultAnalysisRunUnsuccessfulHistoryLimit, GetAnalysisRunUnsuccessfulHistoryLimitOrDefault(&v1alpha1.Rollout{})) + assert.Equal(t, DefaultAnalysisRunUnsuccessfulHistoryLimit, GetAnalysisRunUnsuccessfulHistoryLimitOrDefault(&v1alpha1.Rollout{Spec: v1alpha1.RolloutSpec{}})) + assert.Equal(t, DefaultAnalysisRunUnsuccessfulHistoryLimit, GetAnalysisRunUnsuccessfulHistoryLimitOrDefault(&v1alpha1.Rollout{Spec: v1alpha1.RolloutSpec{Analysis: &v1alpha1.AnalysisRunStrategy{}}})) +} + func TestGetMaxSurgeOrDefault(t *testing.T) { maxSurge := intstr.FromInt(2) rolloutNonDefaultValue := &v1alpha1.Rollout{ diff --git a/utils/experiment/filter.go b/utils/experiment/filter.go index e75dcc1ee4..a639a13d7b 100644 --- a/utils/experiment/filter.go +++ b/utils/experiment/filter.go @@ -1,6 +1,8 @@ package experiment import ( + "sort" + appsv1 "k8s.io/api/apps/v1" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" @@ -70,7 +72,7 @@ func SortExperimentsByPodHash(exs []*v1alpha1.Experiment) map[string][]*v1alpha1 // Note: It is okay to use pod hash for filtering since the experiments's pod hash is originally derived from the new RS. // Even if there is a library change during the lifetime of the experiments, the ReplicaSet's pod hash that the // experiments references does not change. -func FilterExperimentsToDelete(exs []*v1alpha1.Experiment, olderRSs []*appsv1.ReplicaSet) []*v1alpha1.Experiment { +func FilterExperimentsToDelete(exs []*v1alpha1.Experiment, olderRSs []*appsv1.ReplicaSet, limitSuccessful int32, limitUnsuccessful int32) []*v1alpha1.Experiment { olderRsPodHashes := map[string]bool{} for i := range olderRSs { rs := olderRSs[i] @@ -78,6 +80,10 @@ func FilterExperimentsToDelete(exs []*v1alpha1.Experiment, olderRSs []*appsv1.Re olderRsPodHashes[podHash] = rs.DeletionTimestamp != nil } } + sort.Sort(sort.Reverse(ExperimentByCreationTimestamp(exs))) + + var retainedSuccessful int32 = 0 + var retainedUnsuccessful int32 = 0 exsToDelete := []*v1alpha1.Experiment{} for i := range exs { ex := exs[i] @@ -100,6 +106,22 @@ func FilterExperimentsToDelete(exs []*v1alpha1.Experiment, olderRSs []*appsv1.Re exsToDelete = append(exsToDelete, ex) continue } + + if ex.Status.Phase == v1alpha1.AnalysisPhaseSuccessful { + if retainedSuccessful < limitSuccessful { + retainedSuccessful++ + } else { + exsToDelete = append(exsToDelete, ex) + } + } else if ex.Status.Phase == v1alpha1.AnalysisPhaseFailed || + ex.Status.Phase == v1alpha1.AnalysisPhaseError || + ex.Status.Phase == v1alpha1.AnalysisPhaseInconclusive { + if retainedUnsuccessful < limitUnsuccessful { + retainedUnsuccessful++ + } else { + exsToDelete = append(exsToDelete, ex) + } + } } return exsToDelete } diff --git a/utils/experiment/filter_test.go b/utils/experiment/filter_test.go index 734bb7c841..ae3e28fb72 100644 --- a/utils/experiment/filter_test.go +++ b/utils/experiment/filter_test.go @@ -110,10 +110,62 @@ func TestFilterExperimentsToDelete(t *testing.T) { validRS, deletedRS, } - filteredArs := FilterExperimentsToDelete(exs, olderRSs) + filteredArs := FilterExperimentsToDelete(exs, olderRSs, 5, 5) assert.Len(t, filteredArs, 3) assert.NotContains(t, filteredArs, exNoDeletion) assert.Contains(t, filteredArs, exWithNoPodHash) assert.Contains(t, filteredArs, exWithDeletedRS) assert.Contains(t, filteredArs, exWithNoMatchingRS) } + +func TestFilterExperimentsToDeleteByLimit(t *testing.T) { + rs := func(podHash string) *appsv1.ReplicaSet { + return &appsv1.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: podHash}, + }, + } + } + ex := func(podHash string, phase v1alpha1.AnalysisPhase) *v1alpha1.Experiment { + return &v1alpha1.Experiment{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{v1alpha1.DefaultRolloutUniqueLabelKey: podHash}, + CreationTimestamp: metav1.Now(), + }, + Status: v1alpha1.ExperimentStatus{ + Phase: phase, + }, + } + } + + arS0 := ex("a", v1alpha1.AnalysisPhaseSuccessful) + arS1 := ex("a", v1alpha1.AnalysisPhaseSuccessful) + arS2 := ex("a", v1alpha1.AnalysisPhaseSuccessful) + arS3 := ex("a", v1alpha1.AnalysisPhaseSuccessful) + arS4 := ex("a", v1alpha1.AnalysisPhaseSuccessful) + arF0 := ex("a", v1alpha1.AnalysisPhaseFailed) + arF1 := ex("a", v1alpha1.AnalysisPhaseError) + arF2 := ex("a", v1alpha1.AnalysisPhaseInconclusive) + arF3 := ex("a", v1alpha1.AnalysisPhaseFailed) + arF4 := ex("a", v1alpha1.AnalysisPhaseFailed) + + validRS := rs("a") + ars := []*v1alpha1.Experiment{ + arS0, arF0, arS1, arF1, arS2, arF2, arS3, arF3, arS4, arF4, + } + olderRSs := []*appsv1.ReplicaSet{ + validRS, + } + + assert.Len(t, FilterExperimentsToDelete(ars, olderRSs, 1, 0), 9) + assert.Len(t, FilterExperimentsToDelete(ars, olderRSs, 5, 0), 5) + assert.Len(t, FilterExperimentsToDelete(ars, olderRSs, 10, 0), 5) + + assert.Len(t, FilterExperimentsToDelete(ars, olderRSs, 0, 1), 9) + assert.Len(t, FilterExperimentsToDelete(ars, olderRSs, 0, 5), 5) + assert.Len(t, FilterExperimentsToDelete(ars, olderRSs, 0, 10), 5) + + assert.Len(t, FilterExperimentsToDelete(ars, olderRSs, 0, 0), 10) + assert.Len(t, FilterExperimentsToDelete(ars, olderRSs, 4, 4), 2) + assert.Len(t, FilterExperimentsToDelete(ars, olderRSs, 10, 10), 0) +} From 91be1609aeb54917b3f24799b24d77049d1ec0fc Mon Sep 17 00:00:00 2001 From: Jesse Suen Date: Fri, 6 Aug 2021 03:15:18 -0700 Subject: [PATCH 10/20] fix: retarget blue-green previewService before scaling up preview ReplicaSet (#1368) Signed-off-by: Jesse Suen --- docs/features/bluegreen.md | 29 ++++++++++++++++++++--------- rollout/analysis_test.go | 3 +++ rollout/bluegreen.go | 11 ++++++----- rollout/bluegreen_test.go | 1 + rollout/canary_test.go | 13 +++++++++++-- rollout/ephemeralmetadata_test.go | 5 ++++- rollout/experiment_test.go | 1 + rollout/sync.go | 14 +++----------- rollout/sync_test.go | 7 +++++-- test/e2e/functional_test.go | 12 ++++++++---- utils/conditions/conditions.go | 2 +- 11 files changed, 63 insertions(+), 35 deletions(-) diff --git a/docs/features/bluegreen.md b/docs/features/bluegreen.md index 95e2ce4241..afb2d2f158 100644 --- a/docs/features/bluegreen.md +++ b/docs/features/bluegreen.md @@ -68,6 +68,25 @@ spec: scaleDownDelayRevisionLimit: *int32 ``` +## Sequence of Events + +The following describes the sequence of events that happen during a blue-green update. + +1. Beginning at a fully promoted, steady-state, a revision 1 ReplicaSet is pointed to by both the `activeService` and `previewService`. +1. A user initiates an update by modifying the pod template (`spec.template.spec`). +1. The revision 2 ReplicaSet is created with size 0. +1. The preview service is modified to point to the revision 2 ReplicaSet. The `activeService` remains pointing to revision 1. +1. The revision 2 ReplicaSet is scaled to either `spec.replicas` or `previewReplicaCount` if set. +1. Once revision 2 ReplicaSet Pods are fully available, `prePromotionAnalysis` begins. +1. Upon success of `prePromotionAnalysis`, the blue/green pauses if `autoPromotionEnabled` is false, or `autoPromotionSeconds` is non-zero. +1. The rollout is resumed either manually by a user, or automatically by surpassing `autoPromotionSeconds`. +1. The revision 2 ReplicaSet is scaled to the `spec.replicas`, if the `previewReplicaCount` feature was used. +1. The rollout "promotes" the revision 2 ReplicaSet by updating the `activeService` to point to it. At this point, there are no services pointing to revision 1 +1. `postPromotionAnalysis` analysis begins +1. Once `postPromotionAnalysis` completes successfully, the update is successful and the revision 2 ReplicaSet is marked as stable. The rollout is considered fully-promoted. +1. After waiting `scaleDownDelaySeconds` (default 30 seconds), the revision 1 ReplicaSet is scaled down + + ### autoPromotionEnabled The AutoPromotionEnabled will make the rollout automatically promote the new ReplicaSet to the active service once the new ReplicaSet is healthy. This field is defaulted to true if it is not specified. @@ -111,15 +130,6 @@ This feature is used to provide an endpoint that can be used to test a new versi Defaults to an empty string -Here is a timeline of how the active and preview services work (if you use a preview service): - -1. During the Initial deployment there is only one ReplicaSet. Both active and preview services point to it. This is the **old** version of the application. -1. A change happens in the Rollout resource. A new ReplicaSet is created. This is the **new** version of the application. The preview service is modified to point to the new ReplicaSet. The active service still points to the old version. -1. The blue/green deployment is "promoted". Both active and preview services are pointing to the new version. The old version is still there but no service is pointing at it. -1. Once the the blue/green deployment is scaled down (see the `scaleDownDelaySeconds` field) the old ReplicaSet is has 0 replicas and we are back to the initial state. Both active and preview services point to the new version (which is the only one present anyway) - - - ### previewReplicaCount The PreviewReplicaCount field will indicate the number of replicas that the new version of an application should run. Once the application is ready to promote to the active service, the controller will scale the new ReplicaSet to the value of the `spec.replicas`. The rollout will not switch over the active service to the new ReplicaSet until it matches the `spec.replicas` count. @@ -136,3 +146,4 @@ Defaults to 30 The ScaleDownDelayRevisionLimit limits the number of old active ReplicaSets to keep scaled up while they wait for the scaleDownDelay to pass after being removed from the active service. If omitted, all ReplicaSets will be retained for the specified scaleDownDelay + diff --git a/rollout/analysis_test.go b/rollout/analysis_test.go index 8e665ed2f7..e32aa5e982 100644 --- a/rollout/analysis_test.go +++ b/rollout/analysis_test.go @@ -1517,6 +1517,7 @@ func TestDoNotCreateBackgroundAnalysisRunOnNewCanaryRollout(t *testing.T) { f.expectCreateReplicaSetAction(rs1) f.expectUpdateRolloutStatusAction(r1) // update conditions + f.expectUpdateReplicaSetAction(rs1) // scale replica set f.expectPatchRolloutAction(r1) f.run(getKey(r1, t)) } @@ -1551,6 +1552,7 @@ func TestDoNotCreateBackgroundAnalysisRunOnNewCanaryRolloutStableRSEmpty(t *test f.expectCreateReplicaSetAction(rs1) f.expectUpdateRolloutStatusAction(r1) // update conditions + f.expectUpdateReplicaSetAction(rs1) // scale replica set f.expectPatchRolloutAction(r1) f.run(getKey(r1, t)) } @@ -1686,6 +1688,7 @@ func TestDoNotCreatePrePromotionAnalysisRunOnNewRollout(t *testing.T) { f.expectCreateReplicaSetAction(rs) f.expectUpdateRolloutStatusAction(r) + f.expectUpdateReplicaSetAction(rs) // scale RS f.expectPatchRolloutAction(r) f.run(getKey(r, t)) } diff --git a/rollout/bluegreen.go b/rollout/bluegreen.go index fd8ab46848..8789666b41 100644 --- a/rollout/bluegreen.go +++ b/rollout/bluegreen.go @@ -25,6 +25,12 @@ func (c *rolloutContext) rolloutBlueGreen() error { return err } + // This must happen right after the new replicaset is created + err = c.reconcilePreviewService(previewSvc) + if err != nil { + return err + } + if replicasetutil.CheckPodSpecChange(c.rollout, c.newRS) { return c.syncRolloutStatusBlueGreen(previewSvc, activeSvc) } @@ -39,11 +45,6 @@ func (c *rolloutContext) rolloutBlueGreen() error { return err } - err = c.reconcilePreviewService(previewSvc) - if err != nil { - return err - } - c.reconcileBlueGreenPause(activeSvc, previewSvc) err = c.reconcileActiveService(activeSvc) diff --git a/rollout/bluegreen_test.go b/rollout/bluegreen_test.go index 500343a521..25aa70d46c 100644 --- a/rollout/bluegreen_test.go +++ b/rollout/bluegreen_test.go @@ -56,6 +56,7 @@ func TestBlueGreenCreatesReplicaSet(t *testing.T) { f.expectCreateReplicaSetAction(rs) servicePatchIndex := f.expectPatchServiceAction(previewSvc, rsPodHash) + f.expectUpdateReplicaSetAction(rs) // scale up RS updatedRolloutIndex := f.expectUpdateRolloutStatusAction(r) expectedPatchWithoutSubs := `{ "status":{ diff --git a/rollout/canary_test.go b/rollout/canary_test.go index ace03f0ea9..bb7d4cae23 100644 --- a/rollout/canary_test.go +++ b/rollout/canary_test.go @@ -77,15 +77,19 @@ func TestCanaryRolloutBumpVersion(t *testing.T) { f.replicaSetLister = append(f.replicaSetLister, rs1) createdRSIndex := f.expectCreateReplicaSetAction(rs2) + updatedRSIndex := f.expectUpdateReplicaSetAction(rs2) // scale up RS updatedRolloutRevisionIndex := f.expectUpdateRolloutAction(r2) // update rollout revision updatedRolloutConditionsIndex := f.expectUpdateRolloutStatusAction(r2) // update rollout conditions f.expectPatchRolloutAction(r2) f.run(getKey(r2, t)) createdRS := f.getCreatedReplicaSet(createdRSIndex) - assert.Equal(t, int32(1), *createdRS.Spec.Replicas) + assert.Equal(t, int32(0), *createdRS.Spec.Replicas) assert.Equal(t, "2", createdRS.Annotations[annotations.RevisionAnnotation]) + updatedRS := f.getUpdatedReplicaSet(updatedRSIndex) + assert.Equal(t, int32(1), *updatedRS.Spec.Replicas) + updatedRollout := f.getUpdatedRollout(updatedRolloutRevisionIndex) assert.Equal(t, "2", updatedRollout.Annotations[annotations.RevisionAnnotation]) @@ -475,6 +479,7 @@ func TestCanaryRolloutCreateFirstReplicasetNoSteps(t *testing.T) { rs := newReplicaSet(r, 1) f.expectCreateReplicaSetAction(rs) + f.expectUpdateReplicaSetAction(rs) // scale up rs updatedRolloutIndex := f.expectUpdateRolloutStatusAction(r) patchIndex := f.expectPatchRolloutAction(r) f.run(getKey(r, t)) @@ -514,6 +519,7 @@ func TestCanaryRolloutCreateFirstReplicasetWithSteps(t *testing.T) { rs := newReplicaSet(r, 1) f.expectCreateReplicaSetAction(rs) + f.expectUpdateReplicaSetAction(rs) // scale up rs updatedRolloutIndex := f.expectUpdateRolloutStatusAction(r) patchIndex := f.expectPatchRolloutAction(r) f.run(getKey(r, t)) @@ -559,12 +565,15 @@ func TestCanaryRolloutCreateNewReplicaWithCorrectWeight(t *testing.T) { f.replicaSetLister = append(f.replicaSetLister, rs1) createdRSIndex := f.expectCreateReplicaSetAction(rs2) + updatedRSIndex := f.expectUpdateReplicaSetAction(rs2) updatedRolloutIndex := f.expectUpdateRolloutStatusAction(r2) f.expectPatchRolloutAction(r2) f.run(getKey(r2, t)) createdRS := f.getCreatedReplicaSet(createdRSIndex) - assert.Equal(t, int32(1), *createdRS.Spec.Replicas) + assert.Equal(t, int32(0), *createdRS.Spec.Replicas) + updatedRS := f.getUpdatedReplicaSet(updatedRSIndex) + assert.Equal(t, int32(1), *updatedRS.Spec.Replicas) updatedRollout := f.getUpdatedRollout(updatedRolloutIndex) progressingCondition := conditions.GetRolloutCondition(updatedRollout.Status, v1alpha1.RolloutProgressing) diff --git a/rollout/ephemeralmetadata_test.go b/rollout/ephemeralmetadata_test.go index 7e9d4b850d..a68e4f39d2 100644 --- a/rollout/ephemeralmetadata_test.go +++ b/rollout/ephemeralmetadata_test.go @@ -37,6 +37,7 @@ func TestSyncCanaryEphemeralMetadataInitialRevision(t *testing.T) { f.expectUpdateRolloutStatusAction(r1) idx := f.expectCreateReplicaSetAction(rs1) + f.expectUpdateReplicaSetAction(rs1) _ = f.expectPatchRolloutAction(r1) f.run(getKey(r1, t)) createdRS1 := f.getCreatedReplicaSet(idx) @@ -75,8 +76,9 @@ func TestSyncBlueGreenEphemeralMetadataInitialRevision(t *testing.T) { f.expectUpdateRolloutStatusAction(r1) idx := f.expectCreateReplicaSetAction(rs1) - _ = f.expectPatchRolloutAction(r1) + f.expectPatchRolloutAction(r1) f.expectPatchServiceAction(previewSvc, rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey]) + f.expectUpdateReplicaSetAction(rs1) // scale replicaset f.run(getKey(r1, t)) createdRS1 := f.getCreatedReplicaSet(idx) expectedLabels := map[string]string{ @@ -209,6 +211,7 @@ func TestSyncBlueGreenEphemeralMetadataSecondRevision(t *testing.T) { f.expectUpdateRolloutStatusAction(r2) // Update Rollout conditions rs2idx := f.expectCreateReplicaSetAction(rs2) // Create revision 2 ReplicaSet f.expectPatchServiceAction(previewSvc, rs2PodHash) // Update preview service to point at revision 2 replicaset + f.expectUpdateReplicaSetAction(rs2) // scale revision 2 ReplicaSet up f.expectListPodAction(r1.Namespace) // list pods to patch ephemeral data on revision 1 ReplicaSets pods` podIdx := f.expectUpdatePodAction(&pod) // Update pod with ephemeral data rs1idx := f.expectUpdateReplicaSetAction(rs1) // update stable replicaset with stable metadata diff --git a/rollout/experiment_test.go b/rollout/experiment_test.go index 32159734fb..18277d3640 100644 --- a/rollout/experiment_test.go +++ b/rollout/experiment_test.go @@ -519,6 +519,7 @@ func TestRolloutDoNotCreateExperimentWithoutStableRS(t *testing.T) { f.expectCreateReplicaSetAction(rs2) f.expectUpdateRolloutAction(r2) // update revision f.expectUpdateRolloutStatusAction(r2) // update progressing condition + f.expectUpdateReplicaSetAction(rs2) // scale replicaset f.expectPatchRolloutAction(r1) f.run(getKey(r2, t)) } diff --git a/rollout/sync.go b/rollout/sync.go index d541ece498..27f9872195 100644 --- a/rollout/sync.go +++ b/rollout/sync.go @@ -159,13 +159,7 @@ func (c *rolloutContext) createDesiredReplicaSet() (*appsv1.ReplicaSet, error) { Template: newRSTemplate, }, } - allRSs := append(c.allRSs, newRS) - newReplicasCount, err := replicasetutil.NewRSNewReplicas(c.rollout, allRSs, newRS) - if err != nil { - return nil, err - } - - newRS.Spec.Replicas = pointer.Int32Ptr(newReplicasCount) + newRS.Spec.Replicas = pointer.Int32Ptr(0) // Set new replica set's annotation annotations.SetNewReplicaSetAnnotations(c.rollout, newRS, newRevision, false) @@ -250,12 +244,10 @@ func (c *rolloutContext) createDesiredReplicaSet() (*appsv1.ReplicaSet, error) { return nil, err } - if !alreadyExists && newReplicasCount > 0 { + if !alreadyExists { revision, _ := replicasetutil.Revision(createdRS) - c.recorder.Eventf(c.rollout, record.EventOptions{EventReason: conditions.NewReplicaSetReason}, conditions.NewReplicaSetDetailedMessage, createdRS.Name, revision, newReplicasCount) - } + c.recorder.Eventf(c.rollout, record.EventOptions{EventReason: conditions.NewReplicaSetReason}, conditions.NewReplicaSetDetailedMessage, createdRS.Name, revision) - if !alreadyExists { msg := fmt.Sprintf(conditions.NewReplicaSetMessage, createdRS.Name) condition := conditions.NewRolloutCondition(v1alpha1.RolloutProgressing, corev1.ConditionTrue, conditions.NewReplicaSetReason, msg) conditions.SetRolloutCondition(&c.rollout.Status, *condition) diff --git a/rollout/sync_test.go b/rollout/sync_test.go index 61902f713b..ba3eb6f2c5 100644 --- a/rollout/sync_test.go +++ b/rollout/sync_test.go @@ -304,14 +304,17 @@ func TestCanaryPromoteFull(t *testing.T) { f.kubeobjects = append(f.kubeobjects, rs1) f.replicaSetLister = append(f.replicaSetLister, rs1) - createdRS2Index := f.expectCreateReplicaSetAction(rs2) // create new ReplicaSet (surge to 10) + createdRS2Index := f.expectCreateReplicaSetAction(rs2) // create new ReplicaSet (size 0) f.expectUpdateRolloutAction(r2) // update rollout revision f.expectUpdateRolloutStatusAction(r2) // update rollout conditions + updatedRS2Index := f.expectUpdateReplicaSetAction(rs2) // scale new ReplicaSet to 10 patchedRolloutIndex := f.expectPatchRolloutAction(r2) f.run(getKey(r2, t)) createdRS2 := f.getCreatedReplicaSet(createdRS2Index) - assert.Equal(t, int32(10), *createdRS2.Spec.Replicas) // verify we ignored steps + assert.Equal(t, int32(0), *createdRS2.Spec.Replicas) + updatedRS2 := f.getUpdatedReplicaSet(updatedRS2Index) + assert.Equal(t, int32(10), *updatedRS2.Spec.Replicas) // verify we ignored steps and fully scaled it patchedRollout := f.getPatchedRolloutAsObject(patchedRolloutIndex) assert.Equal(t, int32(2), *patchedRollout.Status.CurrentStepIndex) // verify we updated to last step diff --git a/test/e2e/functional_test.go b/test/e2e/functional_test.go index fc381773f3..1c07a73620 100644 --- a/test/e2e/functional_test.go +++ b/test/e2e/functional_test.go @@ -90,10 +90,12 @@ spec: ExpectRevisionPodCount("2", 1). ExpectRolloutEvents([]string{ "RolloutUpdated", // Rollout updated to revision 1 - "NewReplicaSetCreated", // Created ReplicaSet abort-retry-promote-698fbfb9dc (revision 1) with size 1 + "NewReplicaSetCreated", // Created ReplicaSet abort-retry-promote-698fbfb9dc (revision 1) + "ScalingReplicaSet", // Scaled up ReplicaSet abort-retry-promote-698fbfb9dc (revision 1) from 0 to 1 "RolloutCompleted", // Rollout completed update to revision 1 (698fbfb9dc): Initial deploy "RolloutUpdated", // Rollout updated to revision 2 - "NewReplicaSetCreated", // Created ReplicaSet abort-retry-promote-75dcb5ddd6 (revision 2) with size 1 + "NewReplicaSetCreated", // Created ReplicaSet abort-retry-promote-75dcb5ddd6 (revision 2) + "ScalingReplicaSet", // Scaled up ReplicaSet abort-retry-promote-75dcb5ddd6 (revision 2) from 0 to 1 "RolloutStepCompleted", // Rollout step 1/2 completed (setWeight: 50) "RolloutPaused", // Rollout is paused (CanaryPauseStep) "ScalingReplicaSet", // Scaled down ReplicaSet abort-retry-promote-75dcb5ddd6 (revision 2) from 1 to 0 @@ -696,11 +698,13 @@ func (s *FunctionalSuite) TestBlueGreenUpdate() { ExpectReplicaCounts(3, 6, 3, 3, 3). ExpectRolloutEvents([]string{ "RolloutUpdated", // Rollout updated to revision 1 - "NewReplicaSetCreated", // Created ReplicaSet bluegreen-7dcd8f8869 (revision 1) with size 3 + "NewReplicaSetCreated", // Created ReplicaSet bluegreen-7dcd8f8869 (revision 1) + "ScalingReplicaSet", // Scaled up ReplicaSet bluegreen-7dcd8f8869 (revision 1) from 0 to 3 "RolloutCompleted", // Rollout completed update to revision 1 (7dcd8f8869): Initial deploy "SwitchService", // Switched selector for service 'bluegreen' from '' to '7dcd8f8869' "RolloutUpdated", // Rollout updated to revision 2 - "NewReplicaSetCreated", // Created ReplicaSet bluegreen-5498785cd6 (revision 2) with size 3 + "NewReplicaSetCreated", // Created ReplicaSet bluegreen-5498785cd6 (revision 2) + "ScalingReplicaSet", // Scaled up ReplicaSet bluegreen-5498785cd6 (revision 2) from 0 to 3 "SwitchService", // Switched selector for service 'bluegreen' from '7dcd8f8869' to '6c779b88b6' "RolloutCompleted", // Rollout completed update to revision 2 (6c779b88b6): Completed blue-green update }) diff --git a/utils/conditions/conditions.go b/utils/conditions/conditions.go index c65d8f4734..5826db3f89 100644 --- a/utils/conditions/conditions.go +++ b/utils/conditions/conditions.go @@ -52,7 +52,7 @@ const ( //NewReplicaSetMessage is added in a rollout when it creates a new replicas set. NewReplicaSetMessage = "Created new replica set %q" // NewReplicaSetDetailedMessage is a more detailed format message - NewReplicaSetDetailedMessage = "Created ReplicaSet %s (revision %d) with size %d" + NewReplicaSetDetailedMessage = "Created ReplicaSet %s (revision %d)" // FoundNewRSReason is added in a rollout when it adopts an existing replica set. FoundNewRSReason = "FoundNewReplicaSet" From d07c8a80c4e4b1c8acaccb89523214e72d5e2586 Mon Sep 17 00:00:00 2001 From: dabaooline <201028369@qq.com> Date: Fri, 6 Aug 2021 18:19:37 +0800 Subject: [PATCH 11/20] docs: add custom namespace name tips (#1354) --- docs/installation.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/installation.md b/docs/installation.md index 85074936bd..3456ab6507 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -9,6 +9,9 @@ kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/rele This will create a new namespace, `argo-rollouts`, where Argo Rollouts controller will run. +!!! tip + If you are using another namspace name, please update `install.yall` clusterrolebinding's serviceaccount namespace name. + !!! tip When installing Argo Rollouts on Kubernetes v1.14 or lower, the CRD manifests must be kubectl applied with the --validate=false option. This is caused by use of new CRD fields introduced in v1.15, which are rejected by default in lower API servers. From e33bd174c2a0523deb415acff791f8ce60395144 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Sun, 8 Aug 2021 10:27:56 -0400 Subject: [PATCH 12/20] test invalid Graphite JSON response scenario Signed-off-by: Mike Ball --- metricproviders/graphite/api_test.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/metricproviders/graphite/api_test.go b/metricproviders/graphite/api_test.go index 8414ccde68..8bef77f6f6 100644 --- a/metricproviders/graphite/api_test.go +++ b/metricproviders/graphite/api_test.go @@ -1,6 +1,7 @@ package graphite import ( + "errors" "fmt" "net/http" "net/http/httptest" @@ -38,19 +39,22 @@ func TestNewAPIWithInvalidURL(t *testing.T) { } func TestQuery(t *testing.T) { + goodResult := float64(100) tests := []struct { name string query string expectedTarget string expectedFrom string - expectedResult float64 + expectedResult *float64 + expectedErr error body string }{{ "ok", "target=sumSeries(app.http.*.*.count)&from=-2min", "sumSeries(app.http.*.*.count)", "-2min", - float64(100), + &goodResult, + nil, `[ { "datapoints": [ @@ -78,6 +82,14 @@ func TestQuery(t *testing.T) { } } ]`, + }, { + "graphite response body with invalid JSON", + "target=sumSeries(app.http.*.*.count)&from=-2min", + "sumSeries(app.http.*.*.count)", + "-2min", + nil, + errors.New("invalid character 'i' looking for beginning of value"), + "invalid JSON", }} for _, test := range tests { @@ -97,7 +109,11 @@ func TestQuery(t *testing.T) { assert.Nil(t, err) val, err := g.Query(test.query) - assert.Nil(t, err) - assert.Equal(t, &test.expectedResult, val) + if test.expectedErr != nil { + assert.Equal(t, err.Error(), test.expectedErr.Error()) + } else { + assert.Nil(t, err) + } + assert.Equal(t, test.expectedResult, val) } } From c4aa5430a49ecb6ce70993e8cf7f67b4cee6ad00 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Sun, 8 Aug 2021 10:44:28 -0400 Subject: [PATCH 13/20] test Graphite error response code scenario Signed-off-by: Mike Ball --- metricproviders/graphite/api_test.go | 59 ++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/metricproviders/graphite/api_test.go b/metricproviders/graphite/api_test.go index 8bef77f6f6..d610750d62 100644 --- a/metricproviders/graphite/api_test.go +++ b/metricproviders/graphite/api_test.go @@ -48,6 +48,7 @@ func TestQuery(t *testing.T) { expectedResult *float64 expectedErr error body string + responseCode int }{{ "ok", "target=sumSeries(app.http.*.*.count)&from=-2min", @@ -82,6 +83,7 @@ func TestQuery(t *testing.T) { } } ]`, + 200, }, { "graphite response body with invalid JSON", "target=sumSeries(app.http.*.*.count)&from=-2min", @@ -90,30 +92,53 @@ func TestQuery(t *testing.T) { nil, errors.New("invalid character 'i' looking for beginning of value"), "invalid JSON", + 200, + }, { + "400 graphite response status", + "target=sumSeries(app.http.*.*.count)&from=-2min", + "sumSeries(app.http.*.*.count)", + "-2min", + nil, + errors.New("error response: foo"), + "foo", + 400, + }, { + "500 graphite response status", + "target=sumSeries(app.http.*.*.count)&from=-2min", + "sumSeries(app.http.*.*.count)", + "-2min", + nil, + errors.New("error response: bar"), + "bar", + 500, }} for _, test := range tests { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - target := r.URL.Query().Get("target") - assert.Equal(t, test.expectedTarget, target) + t.Run(test.name, func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + target := r.URL.Query().Get("target") + assert.Equal(t, test.expectedTarget, target) - from := r.URL.Query().Get("from") - assert.Equal(t, test.expectedFrom, from) + from := r.URL.Query().Get("from") + assert.Equal(t, test.expectedFrom, from) - json := test.body - w.Write([]byte(json)) - })) - defer ts.Close() + w.WriteHeader(test.responseCode) - g, err := NewAPIClient(testGraphiteMetric(ts.URL), log.Entry{}) - assert.Nil(t, err) + json := test.body + w.Write([]byte(json)) + })) + defer ts.Close() - val, err := g.Query(test.query) - if test.expectedErr != nil { - assert.Equal(t, err.Error(), test.expectedErr.Error()) - } else { + g, err := NewAPIClient(testGraphiteMetric(ts.URL), log.Entry{}) assert.Nil(t, err) - } - assert.Equal(t, test.expectedResult, val) + + val, err := g.Query(test.query) + if test.expectedErr != nil { + assert.Equal(t, err.Error(), test.expectedErr.Error()) + } else { + assert.Nil(t, err) + } + assert.Equal(t, test.expectedResult, val) + }) } } From 3820303427d57cfd587f297a50fd032b8b632f48 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Mon, 9 Aug 2021 08:07:48 -0400 Subject: [PATCH 14/20] Graphite provider test exercising evaluation error Signed-off-by: Mike Ball --- metricproviders/graphite/graphite_test.go | 56 +++++++++++++---------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/metricproviders/graphite/graphite_test.go b/metricproviders/graphite/graphite_test.go index 3e722b4eb1..9dd983eaa4 100644 --- a/metricproviders/graphite/graphite_test.go +++ b/metricproviders/graphite/graphite_test.go @@ -9,19 +9,14 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" ) -func newMockAPI(response float64) mockAPI { +func newMockAPI(response *float64) mockAPI { return mockAPI{ - response: &response, + response: response, } } -func TestType(t *testing.T) { - g := NewGraphiteProvider(newMockAPI(10.000), log.Entry{}) - assert.Equal(t, ProviderType, g.Type()) -} - -func TestRunSuccessfulEvaluation(t *testing.T) { - metric := v1alpha1.Metric{ +func newTestingMetric() v1alpha1.Metric { + return v1alpha1.Metric{ Name: "foo", SuccessCondition: "result == 10.000", FailureCondition: "result != 10.000", @@ -32,8 +27,18 @@ func TestRunSuccessfulEvaluation(t *testing.T) { }, }, } - g := NewGraphiteProvider(newMockAPI(10.000), log.Entry{}) - measurement := g.Run(&v1alpha1.AnalysisRun{}, metric) +} + +func TestType(t *testing.T) { + response := 10.000 + g := NewGraphiteProvider(newMockAPI(&response), log.Entry{}) + assert.Equal(t, ProviderType, g.Type()) +} + +func TestRunSuccessfulEvaluation(t *testing.T) { + response := 10.000 + g := NewGraphiteProvider(newMockAPI(&response), log.Entry{}) + measurement := g.Run(&v1alpha1.AnalysisRun{}, newTestingMetric()) assert.NotNil(t, measurement.StartedAt) assert.Equal(t, "10.000", measurement.Value) assert.NotNil(t, measurement.FinishedAt) @@ -41,27 +46,28 @@ func TestRunSuccessfulEvaluation(t *testing.T) { } func TestRunFailedEvaluation(t *testing.T) { - metric := v1alpha1.Metric{ - Name: "foo", - SuccessCondition: "result == 10.000", - FailureCondition: "result != 10.000", - Provider: v1alpha1.MetricProvider{ - Graphite: &v1alpha1.GraphiteMetric{ - Address: "http://some-graphite.foo", - Query: "foo=1", - }, - }, - } - g := NewGraphiteProvider(newMockAPI(5.000), log.Entry{}) - measurement := g.Run(&v1alpha1.AnalysisRun{}, metric) + response := 5.000 + g := NewGraphiteProvider(newMockAPI(&response), log.Entry{}) + measurement := g.Run(&v1alpha1.AnalysisRun{}, newTestingMetric()) assert.NotNil(t, measurement.StartedAt) assert.Equal(t, "5.000", measurement.Value) assert.NotNil(t, measurement.FinishedAt) assert.Equal(t, v1alpha1.AnalysisPhaseFailed, measurement.Phase) } +func TestRunErrorEvaluationFromNilQueryResponse(t *testing.T) { + g := NewGraphiteProvider(newMockAPI(nil), log.Entry{}) + measurement := g.Run(&v1alpha1.AnalysisRun{}, newTestingMetric()) + assert.NotNil(t, measurement.StartedAt) + assert.Equal(t, "", measurement.Value) + assert.NotNil(t, measurement.FinishedAt) + assert.Equal(t, v1alpha1.AnalysisPhaseError, measurement.Phase) + assert.Equal(t, "no values found", measurement.Message) +} + func TestGarbageCollect(t *testing.T) { - g := NewGraphiteProvider(newMockAPI(1), log.Entry{}) + response := 1.000 + g := NewGraphiteProvider(newMockAPI(&response), log.Entry{}) err := g.GarbageCollect(nil, v1alpha1.Metric{}, 0) assert.NoError(t, err) } From 1bc173d36269f9d2aa5d4424f579e97978b4c0ab Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Mon, 9 Aug 2021 08:26:45 -0400 Subject: [PATCH 15/20] add provider test exercising Graphite query error Signed-off-by: Mike Ball --- metricproviders/graphite/graphite.go | 4 ++++ metricproviders/graphite/graphite_test.go | 25 +++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/metricproviders/graphite/graphite.go b/metricproviders/graphite/graphite.go index 5b809472dd..9a191e123d 100644 --- a/metricproviders/graphite/graphite.go +++ b/metricproviders/graphite/graphite.go @@ -56,6 +56,10 @@ func (p *Provider) Run(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alph } value, err := p.api.Query(metric.Provider.Graphite.Query) + if err != nil { + return metricutil.MarkMeasurementError(newMeasurement, err) + } + if value == nil { return metricutil.MarkMeasurementError(newMeasurement, errors.New("no values found")) } diff --git a/metricproviders/graphite/graphite_test.go b/metricproviders/graphite/graphite_test.go index 9dd983eaa4..5ea0528f62 100644 --- a/metricproviders/graphite/graphite_test.go +++ b/metricproviders/graphite/graphite_test.go @@ -1,6 +1,7 @@ package graphite import ( + "errors" "testing" log "github.com/sirupsen/logrus" @@ -9,9 +10,10 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" ) -func newMockAPI(response *float64) mockAPI { +func newMockAPI(response *float64, err error) mockAPI { return mockAPI{ response: response, + err: err, } } @@ -31,13 +33,13 @@ func newTestingMetric() v1alpha1.Metric { func TestType(t *testing.T) { response := 10.000 - g := NewGraphiteProvider(newMockAPI(&response), log.Entry{}) + g := NewGraphiteProvider(newMockAPI(&response, nil), log.Entry{}) assert.Equal(t, ProviderType, g.Type()) } func TestRunSuccessfulEvaluation(t *testing.T) { response := 10.000 - g := NewGraphiteProvider(newMockAPI(&response), log.Entry{}) + g := NewGraphiteProvider(newMockAPI(&response, nil), log.Entry{}) measurement := g.Run(&v1alpha1.AnalysisRun{}, newTestingMetric()) assert.NotNil(t, measurement.StartedAt) assert.Equal(t, "10.000", measurement.Value) @@ -47,7 +49,7 @@ func TestRunSuccessfulEvaluation(t *testing.T) { func TestRunFailedEvaluation(t *testing.T) { response := 5.000 - g := NewGraphiteProvider(newMockAPI(&response), log.Entry{}) + g := NewGraphiteProvider(newMockAPI(&response, nil), log.Entry{}) measurement := g.Run(&v1alpha1.AnalysisRun{}, newTestingMetric()) assert.NotNil(t, measurement.StartedAt) assert.Equal(t, "5.000", measurement.Value) @@ -56,7 +58,7 @@ func TestRunFailedEvaluation(t *testing.T) { } func TestRunErrorEvaluationFromNilQueryResponse(t *testing.T) { - g := NewGraphiteProvider(newMockAPI(nil), log.Entry{}) + g := NewGraphiteProvider(newMockAPI(nil, nil), log.Entry{}) measurement := g.Run(&v1alpha1.AnalysisRun{}, newTestingMetric()) assert.NotNil(t, measurement.StartedAt) assert.Equal(t, "", measurement.Value) @@ -65,9 +67,20 @@ func TestRunErrorEvaluationFromNilQueryResponse(t *testing.T) { assert.Equal(t, "no values found", measurement.Message) } +func TestRunErrorEvaluationFromErrorQueryResponse(t *testing.T) { + response := 10.000 + g := NewGraphiteProvider(newMockAPI(&response, errors.New("some err")), log.Entry{}) + measurement := g.Run(&v1alpha1.AnalysisRun{}, newTestingMetric()) + assert.NotNil(t, measurement.StartedAt) + assert.Equal(t, "", measurement.Value) + assert.NotNil(t, measurement.FinishedAt) + assert.Equal(t, v1alpha1.AnalysisPhaseError, measurement.Phase) + assert.Equal(t, "some err", measurement.Message) +} + func TestGarbageCollect(t *testing.T) { response := 1.000 - g := NewGraphiteProvider(newMockAPI(&response), log.Entry{}) + g := NewGraphiteProvider(newMockAPI(&response, nil), log.Entry{}) err := g.GarbageCollect(nil, v1alpha1.Metric{}, 0) assert.NoError(t, err) } From 5efba2402a902b22c2881b79b79d21d03102aae3 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Mon, 9 Aug 2021 08:42:38 -0400 Subject: [PATCH 16/20] add Graphite provider #Resume and #Terminate tests Signed-off-by: Mike Ball --- metricproviders/graphite/graphite_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/metricproviders/graphite/graphite_test.go b/metricproviders/graphite/graphite_test.go index 5ea0528f62..f6c62129b4 100644 --- a/metricproviders/graphite/graphite_test.go +++ b/metricproviders/graphite/graphite_test.go @@ -78,6 +78,28 @@ func TestRunErrorEvaluationFromErrorQueryResponse(t *testing.T) { assert.Equal(t, "some err", measurement.Message) } +func TestResume(t *testing.T) { + response := 1.000 + e := log.NewEntry(log.New()) + g := NewGraphiteProvider(newMockAPI(&response, nil), *e) + metric := newTestingMetric() + analysisRun := &v1alpha1.AnalysisRun{} + measurement := g.Run(analysisRun, metric) + m := g.Resume(nil, metric, measurement) + assert.Equal(t, m, measurement) +} + +func TestTerminate(t *testing.T) { + response := 1.000 + e := log.NewEntry(log.New()) + g := NewGraphiteProvider(newMockAPI(&response, nil), *e) + metric := newTestingMetric() + analysisRun := &v1alpha1.AnalysisRun{} + measurement := g.Run(analysisRun, metric) + m := g.Terminate(nil, metric, measurement) + assert.Equal(t, m, measurement) +} + func TestGarbageCollect(t *testing.T) { response := 1.000 g := NewGraphiteProvider(newMockAPI(&response, nil), log.Entry{}) From e5ff86b0b15eb66efb1b226fbb323b2a3cf8c05b Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Mon, 9 Aug 2021 08:50:31 -0400 Subject: [PATCH 17/20] test Graphite Provider #Run measurement error Signed-off-by: Mike Ball --- metricproviders/graphite/graphite_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/metricproviders/graphite/graphite_test.go b/metricproviders/graphite/graphite_test.go index f6c62129b4..9130c679e6 100644 --- a/metricproviders/graphite/graphite_test.go +++ b/metricproviders/graphite/graphite_test.go @@ -57,6 +57,29 @@ func TestRunFailedEvaluation(t *testing.T) { assert.Equal(t, v1alpha1.AnalysisPhaseFailed, measurement.Phase) } +func TestRunMeasurementError(t *testing.T) { + metric := v1alpha1.Metric{ + Name: "foo", + // Malformed Success and Failure Conditions + SuccessCondition: "result 10.000", + FailureCondition: "result 10.000", + Provider: v1alpha1.MetricProvider{ + Graphite: &v1alpha1.GraphiteMetric{ + Address: "http://some-graphite.foo", + Query: "foo=1", + }, + }, + } + response := 10.000 + g := NewGraphiteProvider(newMockAPI(&response, nil), log.Entry{}) + measurement := g.Run(&v1alpha1.AnalysisRun{}, metric) + assert.NotNil(t, measurement.StartedAt) + assert.Equal(t, "10.000", measurement.Value) + assert.NotNil(t, measurement.FinishedAt) + assert.Equal(t, v1alpha1.AnalysisPhaseError, measurement.Phase) + assert.Equal(t, "unexpected token Number(\"10.000\")", measurement.Message) +} + func TestRunErrorEvaluationFromNilQueryResponse(t *testing.T) { g := NewGraphiteProvider(newMockAPI(nil, nil), log.Entry{}) measurement := g.Run(&v1alpha1.AnalysisRun{}, newTestingMetric()) From 971d12bf1ab9c0b8ad57daa63330fef849f67c02 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Mon, 9 Aug 2021 20:19:16 -0400 Subject: [PATCH 18/20] add test exercising invalid query string Signed-off-by: Mike Ball --- metricproviders/graphite/api_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/metricproviders/graphite/api_test.go b/metricproviders/graphite/api_test.go index d610750d62..333b5b7f61 100644 --- a/metricproviders/graphite/api_test.go +++ b/metricproviders/graphite/api_test.go @@ -111,6 +111,15 @@ func TestQuery(t *testing.T) { errors.New("error response: bar"), "bar", 500, + }, { + "invalid query", + "target=#$%^&*(proper$#$%%^(password&from=-2min", + "#$%^&*(proper$#$%%^(password", + "-2min", + nil, + errors.New("parse \"./render?target=#$%^&*(proper$#$%%^(password&from=-2min\": invalid URL escape \"%^&\""), + "", + 200, }} for _, test := range tests { From 8513292ce1aaf752431398117e424427da5daf9a Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Mon, 9 Aug 2021 20:42:22 -0400 Subject: [PATCH 19/20] test if Graphite returns a data point with 1 item Signed-off-by: Mike Ball --- metricproviders/graphite/api_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/metricproviders/graphite/api_test.go b/metricproviders/graphite/api_test.go index 333b5b7f61..2fbfc16e40 100644 --- a/metricproviders/graphite/api_test.go +++ b/metricproviders/graphite/api_test.go @@ -120,6 +120,28 @@ func TestQuery(t *testing.T) { errors.New("parse \"./render?target=#$%^&*(proper$#$%%^(password&from=-2min\": invalid URL escape \"%^&\""), "", 200, + }, { + "graphite returns data point JSON with only one item", + "target=sumSeries(app.http.*.*.count)&from=-2min", + "sumSeries(app.http.*.*.count)", + "-2min", + nil, + errors.New("error unmarshaling data point: [10]"), + `[ + { + "datapoints": [ + [ + 10 + ] + ], + "target": "sumSeries(app.http.*.*.count)", + "tags": { + "aggregatedBy": "sum", + "name": "sumSeries(app.http.*.*.count)" + } + } + ]`, + 200, }} for _, test := range tests { From f107673a933cd14ffa50377f59f01e82203734c9 Mon Sep 17 00:00:00 2001 From: Mike Ball Date: Mon, 9 Aug 2021 21:13:08 -0400 Subject: [PATCH 20/20] test various Graphite JSON unmarshaling scenarios Signed-off-by: Mike Ball --- metricproviders/graphite/api_test.go | 97 +++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/metricproviders/graphite/api_test.go b/metricproviders/graphite/api_test.go index 2fbfc16e40..ade46d733f 100644 --- a/metricproviders/graphite/api_test.go +++ b/metricproviders/graphite/api_test.go @@ -121,7 +121,7 @@ func TestQuery(t *testing.T) { "", 200, }, { - "graphite returns data point JSON with only one item", + "graphite response data point JSON with only one item", "target=sumSeries(app.http.*.*.count)&from=-2min", "sumSeries(app.http.*.*.count)", "-2min", @@ -142,6 +142,101 @@ func TestQuery(t *testing.T) { } ]`, 200, + }, { + "graphite response data point JSON with an invalid timestamp", + "target=sumSeries(app.http.*.*.count)&from=-2min", + "sumSeries(app.http.*.*.count)", + "-2min", + nil, + errors.New("strconv.ParseInt: parsing \"f\": invalid syntax"), + `[ + { + "datapoints": [ + [ + 100, + "f" + ] + ], + "target": "sumSeries(app.http.*.*.count)" + } + ]`, + 200, + }, { + "graphite response data point JSON with a string value", + "target=sumSeries(app.http.*.*.count)&from=-2min", + "sumSeries(app.http.*.*.count)", + "-2min", + &goodResult, + nil, + `[ + { + "datapoints": [ + [ + "100", + 1621348420 + ] + ], + "target": "sumSeries(app.http.*.*.count)" + } + ]`, + 200, + }, { + "graphite response data point JSON triggers unmarshaling error", + "target=sumSeries(app.http.*.*.count)&from=-2min", + "sumSeries(app.http.*.*.count)", + "-2min", + nil, + errors.New("error unmarshaling value: []"), + `[ + { + "datapoints": [ + [ + [], + 1621348420 + ] + ], + "target": "sumSeries(app.http.*.*.count)" + } + ]`, + 200, + }, { + "graphite response data point JSON with a string timestamp", + "target=sumSeries(app.http.*.*.count)&from=-2min", + "sumSeries(app.http.*.*.count)", + "-2min", + &goodResult, + nil, + `[ + { + "datapoints": [ + [ + 100, + "1621348420" + ] + ], + "target": "sumSeries(app.http.*.*.count)" + } + ]`, + 200, + }, { + "graphite response data point timestamp JSON triggers unmarshaling error", + "target=sumSeries(app.http.*.*.count)&from=-2min", + "sumSeries(app.http.*.*.count)", + "-2min", + nil, + errors.New("error unmarshaling timestamp: 100"), + `[ + { + "datapoints": [ + [ + 100, + [] + ] + ], + "target": "sumSeries(app.http.*.*.count)" + } + ]`, + 200, }} for _, test := range tests {