From 14fc18828c656205771fbdf99faed34a1f50ec62 Mon Sep 17 00:00:00 2001 From: zachaller Date: Sun, 29 Jan 2023 08:55:34 -0600 Subject: [PATCH 01/53] clean up commits Signed-off-by: zachaller --- Makefile | 11 +- analysis/analysis.go | 2 +- cmd/rollouts-controller/main.go | 15 +- .../internal/plugin/plugin.go | 185 +++ .../internal/plugin/plugin_test.go | 108 ++ cmd/sample-metrics-plugin/main.go | 37 + controller/controller.go | 3 + docs/analysis/plugins.md | 62 + docs/best-practices.md | 11 +- .../features/kustomize/rollout_cr_schema.json | 24 + go.mod | 8 +- go.sum | 19 +- manifests/crds/analysis-run-crd.yaml | 5 + manifests/crds/analysis-template-crd.yaml | 5 + .../crds/cluster-analysis-template-crd.yaml | 5 + manifests/install.yaml | 15 + metric/provider.go | 21 + metricproviders/metricproviders.go | 27 +- metricproviders/plugin/plugin.go | 105 ++ metricproviders/plugin/rpc/rpc.go | 232 +++ metricproviders/plugin/rpc/rpc_test.go | 162 +++ .../plugin/rpc/rpc_test_implementation.go | 59 + mkdocs.yml | 1 + pkg/apis/rollouts/v1alpha1/analysis_types.go | 8 + pkg/apis/rollouts/v1alpha1/generated.pb.go | 1281 ++++++++++------- pkg/apis/rollouts/v1alpha1/generated.proto | 9 + .../rollouts/v1alpha1/openapi_generated.go | 27 +- .../v1alpha1/zz_generated.deepcopy.go | 26 + rollout/controller_test.go | 3 + utils/analysis/factory.go | 3 + utils/defaults/defaults.go | 28 + utils/defaults/defaults_test.go | 15 + utils/plugin/plugin.go | 125 ++ utils/plugin/plugin_test.go | 78 + utils/plugin/types/types.go | 28 + 35 files changed, 2196 insertions(+), 557 deletions(-) create mode 100644 cmd/sample-metrics-plugin/internal/plugin/plugin.go create mode 100644 cmd/sample-metrics-plugin/internal/plugin/plugin_test.go create mode 100644 cmd/sample-metrics-plugin/main.go create mode 100644 docs/analysis/plugins.md create mode 100644 metric/provider.go create mode 100644 metricproviders/plugin/plugin.go create mode 100644 metricproviders/plugin/rpc/rpc.go create mode 100644 metricproviders/plugin/rpc/rpc_test.go create mode 100644 metricproviders/plugin/rpc/rpc_test_implementation.go create mode 100644 utils/plugin/plugin.go create mode 100644 utils/plugin/plugin_test.go create mode 100644 utils/plugin/types/types.go diff --git a/Makefile b/Makefile index 6442844ac9..6830d17bdf 100644 --- a/Makefile +++ b/Makefile @@ -209,7 +209,7 @@ lint: go-mod-vendor .PHONY: test test: test-kustomize - go test -covermode=count -coverprofile=coverage.out ${TEST_TARGET} + @make test-unit .PHONY: test-kustomize test-kustomize: @@ -225,7 +225,7 @@ test-e2e: install-devtools-local .PHONY: test-unit test-unit: install-devtools-local - ${DIST_DIR}/gotestsum --junitfile=junit.xml --format=testname --packages="./..." -- -covermode=count -coverprofile=coverage.out ./... + ${DIST_DIR}/gotestsum --junitfile=junit.xml --format=testname -- -covermode=count -coverprofile=coverage.out `go list ./... | grep -v ./cmd/sample-metrics-plugin` .PHONY: coverage @@ -279,3 +279,10 @@ trivy: .PHONY: checksums checksums: shasum -a 256 ./dist/kubectl-argo-rollouts-* | awk -F './dist/' '{print $$1 $$2}' > ./dist/argo-rollouts-checksums.txt + +# Build sample plugin with debug info +# https://www.jetbrains.com/help/go/attach-to-running-go-processes-with-debugger.html +.PHONY: build-sample-metric-plugin-debug +build-sample-metric-plugin-debug: + go build -gcflags="all=-N -l" -o metric-plugin cmd/sample-metrics-plugin/main.go + diff --git a/analysis/analysis.go b/analysis/analysis.go index e5c6fc907d..4e98556794 100644 --- a/analysis/analysis.go +++ b/analysis/analysis.go @@ -327,7 +327,7 @@ func (c *Controller) runMeasurements(run *v1alpha1.AnalysisRun, tasks []metricTa provider, err := c.newProvider(*logger, t.metric) if err != nil { - log.Errorf("Error in getting provider :%v", err) + log.Errorf("Error in getting metric provider :%v", err) return err } if metricResult == nil { diff --git a/cmd/rollouts-controller/main.go b/cmd/rollouts-controller/main.go index 4aadf9ad82..820634c1c0 100644 --- a/cmd/rollouts-controller/main.go +++ b/cmd/rollouts-controller/main.go @@ -6,6 +6,9 @@ import ( "strings" "time" + "github.com/argoproj/argo-rollouts/utils/plugin" + + "github.com/argoproj/pkg/kubeclientmetrics" smiclientset "github.com/servicemeshinterface/smi-sdk-go/pkg/gen/client/split/clientset/versioned" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -20,8 +23,6 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" "k8s.io/client-go/tools/clientcmd" - "github.com/argoproj/pkg/kubeclientmetrics" - "github.com/argoproj/argo-rollouts/controller" "github.com/argoproj/argo-rollouts/controller/metrics" jobprovider "github.com/argoproj/argo-rollouts/metricproviders/job" @@ -70,6 +71,8 @@ func newCommand() *cobra.Command { awsVerifyTargetGroup bool namespaced bool printVersion bool + metricPluginLocation string + metricPluginSha256 string ) electOpts := controller.NewLeaderElectionOptions() var command = cobra.Command{ @@ -199,6 +202,12 @@ func newCommand() *cobra.Command { controllerNamespaceInformerFactory, jobInformerFactory) + defaults.SetMetricPluginLocation(metricPluginLocation) + err = plugin.InitMetricsPlugin(metricPluginLocation, plugin.FileDownloaderImpl{}, metricPluginSha256) + if err != nil { + log.Fatalf("Failed to init metric plugin: %v", err) + } + if err = cm.Run(ctx, rolloutThreads, serviceThreads, ingressThreads, experimentThreads, analysisThreads, electOpts); err != nil { log.Fatalf("Error running controller: %s", err.Error()) } @@ -240,6 +249,8 @@ func newCommand() *cobra.Command { command.Flags().DurationVar(&electOpts.LeaderElectionLeaseDuration, "leader-election-lease-duration", controller.DefaultLeaderElectionLeaseDuration, "The duration that non-leader candidates will wait after observing a leadership renewal until attempting to acquire leadership of a led but unrenewed leader slot. This is effectively the maximum duration that a leader can be stopped before it is replaced by another candidate. This is only applicable if leader election is enabled.") command.Flags().DurationVar(&electOpts.LeaderElectionRenewDeadline, "leader-election-renew-deadline", controller.DefaultLeaderElectionRenewDeadline, "The interval between attempts by the acting master to renew a leadership slot before it stops leading. This must be less than or equal to the lease duration. This is only applicable if leader election is enabled.") command.Flags().DurationVar(&electOpts.LeaderElectionRetryPeriod, "leader-election-retry-period", controller.DefaultLeaderElectionRetryPeriod, "The duration the clients should wait between attempting acquisition and renewal of a leadership. This is only applicable if leader election is enabled.") + command.Flags().StringVar(&metricPluginLocation, "metric-plugin-location", defaults.DefaultMetricsPluginLocation, "The file path to the location of the metric plugin binary") + command.Flags().StringVar(&metricPluginSha256, "metric-plugin-sha256", "", "The expected sha256 of the metric plugin binary") return &command } diff --git a/cmd/sample-metrics-plugin/internal/plugin/plugin.go b/cmd/sample-metrics-plugin/internal/plugin/plugin.go new file mode 100644 index 0000000000..a0750a6bd6 --- /dev/null +++ b/cmd/sample-metrics-plugin/internal/plugin/plugin.go @@ -0,0 +1,185 @@ +package plugin + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "net/url" + "os" + "time" + + "github.com/argoproj/argo-rollouts/utils/plugin/types" + + "github.com/argoproj/argo-rollouts/metricproviders/plugin" + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "github.com/argoproj/argo-rollouts/utils/evaluate" + metricutil "github.com/argoproj/argo-rollouts/utils/metric" + timeutil "github.com/argoproj/argo-rollouts/utils/time" + "github.com/prometheus/client_golang/api" + v1 "github.com/prometheus/client_golang/api/prometheus/v1" + "github.com/prometheus/common/model" + log "github.com/sirupsen/logrus" +) + +const EnvVarArgoRolloutsPrometheusAddress string = "ARGO_ROLLOUTS_PROMETHEUS_ADDRESS" + +// Here is a real implementation of MetricsPlugin +type RpcPlugin struct { + LogCtx log.Entry + api v1.API +} + +type Config struct { + // Address is the HTTP address and port of the prometheus server + Address string `json:"address,omitempty" protobuf:"bytes,1,opt,name=address"` + // Query is a raw prometheus query to perform + Query string `json:"query,omitempty" protobuf:"bytes,2,opt,name=query"` +} + +func (g *RpcPlugin) NewMetricsPlugin(metric v1alpha1.Metric) types.RpcError { + config := Config{} + err := json.Unmarshal(metric.Provider.Plugin.Config, &config) + if err != nil { + return types.RpcError{ErrorString: err.Error()} + } + + api, err := newPrometheusAPI(config.Address) + g.api = api + + return types.RpcError{} +} + +func (g *RpcPlugin) Run(anaysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alpha1.Measurement { + startTime := timeutil.MetaNow() + newMeasurement := v1alpha1.Measurement{ + StartedAt: &startTime, + } + + config := Config{} + json.Unmarshal(metric.Provider.Plugin.Config, &config) + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + response, warnings, err := g.api.Query(ctx, config.Query, time.Now()) + if err != nil { + return metricutil.MarkMeasurementError(newMeasurement, err) + } + + newValue, newStatus, err := g.processResponse(metric, response) + if err != nil { + return metricutil.MarkMeasurementError(newMeasurement, err) + + } + newMeasurement.Value = newValue + if len(warnings) > 0 { + warningMetadata := "" + for _, warning := range warnings { + warningMetadata = fmt.Sprintf(`%s"%s", `, warningMetadata, warning) + } + warningMetadata = warningMetadata[:len(warningMetadata)-2] + if warningMetadata != "" { + newMeasurement.Metadata = map[string]string{"warnings": warningMetadata} + g.LogCtx.Warnf("Prometheus returned the following warnings: %s", warningMetadata) + } + } + + newMeasurement.Phase = newStatus + finishedTime := timeutil.MetaNow() + newMeasurement.FinishedAt = &finishedTime + return newMeasurement +} + +func (g *RpcPlugin) Resume(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { + return measurement +} + +func (g *RpcPlugin) Terminate(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { + return measurement +} + +func (g *RpcPlugin) GarbageCollect(*v1alpha1.AnalysisRun, v1alpha1.Metric, int) types.RpcError { + return types.RpcError{} +} + +func (g *RpcPlugin) Type() string { + return plugin.ProviderType +} + +func (g *RpcPlugin) GetMetadata(metric v1alpha1.Metric) map[string]string { + metricsMetadata := make(map[string]string) + + config := Config{} + json.Unmarshal(metric.Provider.Plugin.Config, &config) + if config.Query != "" { + metricsMetadata["ResolvedPrometheusQuery"] = config.Query + } + return metricsMetadata +} + +func (g *RpcPlugin) processResponse(metric v1alpha1.Metric, response model.Value) (string, v1alpha1.AnalysisPhase, error) { + switch value := response.(type) { + case *model.Scalar: + valueStr := value.Value.String() + result := float64(value.Value) + newStatus, err := evaluate.EvaluateResult(result, metric, g.LogCtx) + return valueStr, newStatus, err + case model.Vector: + results := make([]float64, 0, len(value)) + valueStr := "[" + for _, s := range value { + if s != nil { + valueStr = valueStr + s.Value.String() + "," + results = append(results, float64(s.Value)) + } + } + // if we appended to the string, we should remove the last comma on the string + if len(valueStr) > 1 { + valueStr = valueStr[:len(valueStr)-1] + } + valueStr = valueStr + "]" + newStatus, err := evaluate.EvaluateResult(results, metric, g.LogCtx) + return valueStr, newStatus, err + default: + return "", v1alpha1.AnalysisPhaseError, fmt.Errorf("Prometheus metric type not supported") + } +} + +func newPrometheusAPI(address string) (v1.API, error) { + envValuesByKey := make(map[string]string) + if value, ok := os.LookupEnv(fmt.Sprintf("%s", EnvVarArgoRolloutsPrometheusAddress)); ok { + envValuesByKey[EnvVarArgoRolloutsPrometheusAddress] = value + log.Debugf("ARGO_ROLLOUTS_PROMETHEUS_ADDRESS: %v", envValuesByKey[EnvVarArgoRolloutsPrometheusAddress]) + } + if len(address) != 0 { + if !isUrl(address) { + return nil, errors.New("prometheus address is not is url format") + } + } else if envValuesByKey[EnvVarArgoRolloutsPrometheusAddress] != "" { + if isUrl(envValuesByKey[EnvVarArgoRolloutsPrometheusAddress]) { + address = envValuesByKey[EnvVarArgoRolloutsPrometheusAddress] + } else { + return nil, errors.New("prometheus address is not is url format") + } + } else { + return nil, errors.New("prometheus address is not configured") + } + client, err := api.NewClient(api.Config{ + Address: address, + }) + if err != nil { + log.Errorf("Error in getting prometheus client: %v", err) + return nil, err + } + return v1.NewAPI(client), nil +} + +func isUrl(str string) bool { + u, err := url.Parse(str) + if err != nil { + log.Errorf("Error in parsing url: %v", err) + } + log.Debugf("Parsed url: %v", u) + return err == nil && u.Scheme != "" && u.Host != "" +} diff --git a/cmd/sample-metrics-plugin/internal/plugin/plugin_test.go b/cmd/sample-metrics-plugin/internal/plugin/plugin_test.go new file mode 100644 index 0000000000..da5c0b6845 --- /dev/null +++ b/cmd/sample-metrics-plugin/internal/plugin/plugin_test.go @@ -0,0 +1,108 @@ +package plugin + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" + + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + log "github.com/sirupsen/logrus" + + goPlugin "github.com/hashicorp/go-plugin" +) + +var testHandshake = goPlugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "ARGO_ROLLOUTS_RPC_PLUGIN", + MagicCookieValue: "metrics", +} + +// This is just an example of how to test a plugin. +func TestRunSuccessfully(t *testing.T) { + //Skip test because this is just an example of how to test a plugin. + t.Skip("Skipping test because it requires a running prometheus server") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + logCtx := *log.WithFields(log.Fields{"plugin-test": "prometheus"}) + + rpcPluginImp := &RpcPlugin{ + LogCtx: logCtx, + } + + // pluginMap is the map of plugins we can dispense. + var pluginMap = map[string]goPlugin.Plugin{ + "RpcMetricsPlugin": &rpc.RpcMetricsPlugin{Impl: rpcPluginImp}, + } + + ch := make(chan *goPlugin.ReattachConfig, 1) + closeCh := make(chan struct{}) + go goPlugin.Serve(&goPlugin.ServeConfig{ + HandshakeConfig: testHandshake, + Plugins: pluginMap, + Test: &goPlugin.ServeTestConfig{ + Context: ctx, + ReattachConfigCh: ch, + CloseCh: closeCh, + }, + }) + + // We should get a config + var config *goPlugin.ReattachConfig + select { + case config = <-ch: + case <-time.After(2000 * time.Millisecond): + t.Fatal("should've received reattach") + } + if config == nil { + t.Fatal("config should not be nil") + } + + // Connect! + c := goPlugin.NewClient(&goPlugin.ClientConfig{ + Cmd: nil, + HandshakeConfig: testHandshake, + Plugins: pluginMap, + Reattach: config, + }) + client, err := c.Client() + if err != nil { + t.Fatalf("err: %s", err) + } + + // Pinging should work + if err := client.Ping(); err != nil { + t.Fatalf("should not err: %s", err) + } + + // Kill which should do nothing + c.Kill() + if err := client.Ping(); err != nil { + t.Fatalf("should not err: %s", err) + } + + // Request the plugin + raw, err := client.Dispense("RpcMetricsPlugin") + if err != nil { + t.Fail() + } + + plugin := raw.(rpc.MetricsPlugin) + + err = plugin.NewMetricsPlugin(v1alpha1.Metric{ + Provider: v1alpha1.MetricProvider{ + Plugin: &v1alpha1.PluginMetric{Config: json.RawMessage(`{"address":"http://prometheus.local", "query":"machine_cpu_cores"}`)}, + }, + }) + if err != nil { + t.Fail() + } + + // Canceling should cause an exit + cancel() + <-closeCh +} diff --git a/cmd/sample-metrics-plugin/main.go b/cmd/sample-metrics-plugin/main.go new file mode 100644 index 0000000000..4047f197b5 --- /dev/null +++ b/cmd/sample-metrics-plugin/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "github.com/argoproj/argo-rollouts/cmd/sample-metrics-plugin/internal/plugin" + "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" + goPlugin "github.com/hashicorp/go-plugin" + log "github.com/sirupsen/logrus" +) + +// handshakeConfigs are used to just do a basic handshake between +// a plugin and host. If the handshake fails, a user friendly error is shown. +// This prevents users from executing bad plugins or executing a plugin +// directory. It is a UX feature, not a security feature. +var handshakeConfig = goPlugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "ARGO_ROLLOUTS_RPC_PLUGIN", + MagicCookieValue: "metrics", +} + +func main() { + logCtx := *log.WithFields(log.Fields{"plugin": "prometheus"}) + + rpcPluginImp := &plugin.RpcPlugin{ + LogCtx: logCtx, + } + // pluginMap is the map of plugins we can dispense. + var pluginMap = map[string]goPlugin.Plugin{ + "RpcMetricsPlugin": &rpc.RpcMetricsPlugin{Impl: rpcPluginImp}, + } + + logCtx.Debug("message from plugin", "foo", "bar") + + goPlugin.Serve(&goPlugin.ServeConfig{ + HandshakeConfig: handshakeConfig, + Plugins: pluginMap, + }) +} diff --git a/controller/controller.go b/controller/controller.go index 55ef4a45f8..058cff4906 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -9,6 +9,8 @@ import ( "sync" "time" + goPlugin "github.com/hashicorp/go-plugin" + "k8s.io/apimachinery/pkg/util/wait" istioutil "github.com/argoproj/argo-rollouts/utils/istio" @@ -423,6 +425,7 @@ func (c *Manager) Run(ctx context.Context, rolloutThreadiness, serviceThreadines }) } log.Info("Shutting down workers") + goPlugin.CleanupClients() c.serviceWorkqueue.ShutDownWithDrain() c.ingressWorkqueue.ShutDownWithDrain() diff --git a/docs/analysis/plugins.md b/docs/analysis/plugins.md new file mode 100644 index 0000000000..1d65bc8887 --- /dev/null +++ b/docs/analysis/plugins.md @@ -0,0 +1,62 @@ +# Metric Plugins + +!!! important Available since v1.5 + +Argo Rollouts supports getting analysis metrics via 3rd party plugin system. This allows users to extend the capabilities of Rollouts +to support metrics that are not natively supported by Rollouts. Rollouts uses a plugin library called +[go-plugin](https://github.com/hashicorp/go-plugin) to do this. You can find a sample plugin +here: [sample-rollouts-metric-plugin](https://github.com/argoproj-labs/sample-rollouts-metric-plugin) + +## Using a Metric Plugin + +There are two methods to installing and using an argo rollouts plugin. The first method is to mount up the plugin executable +into the rollouts controller container. The second method is to use a HTTP(S) server to host the plugin executable. + +### Mounting the plugin executable into the rollouts controller container + +To use this method, you will need to build or download the plugin executable and then mount it into the rollouts controller container. +The plugin executable must be mounted into the rollouts controller container at the path specified by the `--metric-plugin-location` flag. + +There are a few ways to mount the plugin executable into the rollouts controller container. Some of these will depend on your +particular infrastructure. Here are a few methods: + +* Using an init container to download the plugin executable +* Using a Kubernetes volume mount with a shared volume such as NFS, EBS, etc. +* Building the plugin into the rollouts controller container + +### Using a HTTP(S) server to host the plugin executable + +Argo Rollouts supports downloading the plugin executable from a HTTP(S) server. To use this method, you will need to specify the +`--metric-plugin-location` flag to point to the HTTP(S) server that hosts the plugin executable. Example: + +```bash +./rollouts-controller --metric-plugin-location=https://example.com/metric-plugin +``` + +You can also specify a sha256 checksum of the plugin executable. This is useful for ensuring the plugin executable has not been tampered with. +To use this feature, you will need to specify the `--metric-plugin-sha256` flag. Example: + +```bash +./rollouts-controller --metric-plugin-location=https://example.com/metric-plugin --metric-plugin-sha256=1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef +``` + +## Some words of caution + +Depending on which method you use to install and the plugin, there are some things to be aware of. +The rollouts controller will not start if it can not download or find the plugin executable. This means that if you are using +a method of installation that requires a download of the plugin and the server hosting the plugin for some reason is not available and the rollouts +controllers pod got deleted while the server was down or is coming up for the first time, it will not be able to start until +the server hosting the plugin is available again. + +Argo Rollouts will download the plugin at startup only once but if the pod is deleted it will need to download the plugin again on next startup. Running +Argo Rollouts in HA mode can help a little with this situation because each pod will download the plugin at startup. So if a single pod gets +deleted during a server outage, the other pods will still be able to take over because there will already be a plugin executable available to it. However, +it is up to you to define your risk for and decide how you want to install the plugin executable. + +## List of Available Plugins (alphabetical order) + +#### Add Your Plugin Here + * If you have created a plugin, please submit a PR to add it to this list. +#### [sample-rollouts-metric-plugin](https://github.com/argoproj-labs/sample-rollouts-metric-plugin) + * This is just a sample plugin that can be used as a starting point for creating your own plugin. +It is not meant to be used in production. It is based on the built-in prometheus provider. diff --git a/docs/best-practices.md b/docs/best-practices.md index d9b5bd3a1a..ffbb4ff411 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -51,4 +51,13 @@ spec: ``` The above technique has the a benefit in that it would not incur additional cost of allocating -additional load balancers. \ No newline at end of file +additional load balancers. + +## Reducing operator memory usage + +On clusters with thousands of rollouts memory usage for the argo-rollouts +operator can be reduced significantly by changing RevisionHistoryLimit from the +default of 10 to a lower number. One user of Argo Rollouts saw a 27% reduction +in memory usage for a cluster with 1290 rollouts by changing +RevisionHistoryLimit from 10 to 0. + diff --git a/docs/features/kustomize/rollout_cr_schema.json b/docs/features/kustomize/rollout_cr_schema.json index fbff809be6..8751fe00a9 100644 --- a/docs/features/kustomize/rollout_cr_schema.json +++ b/docs/features/kustomize/rollout_cr_schema.json @@ -4165,6 +4165,14 @@ ], "type": "object" }, + "plugin": { + "properties": { + "config": { + "x-kubernetes-preserve-unknown-fields": true + } + }, + "type": "object" + }, "prometheus": { "properties": { "address": { @@ -8433,6 +8441,14 @@ ], "type": "object" }, + "plugin": { + "properties": { + "config": { + "x-kubernetes-preserve-unknown-fields": true + } + }, + "type": "object" + }, "prometheus": { "properties": { "address": { @@ -12701,6 +12717,14 @@ ], "type": "object" }, + "plugin": { + "properties": { + "config": { + "x-kubernetes-preserve-unknown-fields": true + } + }, + "type": "object" + }, "prometheus": { "properties": { "address": { diff --git a/go.mod b/go.mod index 83740b5b42..86e436e2c0 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,8 @@ require ( github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.2 github.com/grpc-ecosystem/grpc-gateway v1.16.0 - github.com/influxdata/influxdb-client-go/v2 v2.12.1 + github.com/hashicorp/go-plugin v1.4.8 + github.com/influxdata/influxdb-client-go/v2 v2.12.2 github.com/juju/ansiterm v1.0.0 github.com/mitchellh/mapstructure v1.5.0 github.com/newrelic/newrelic-client-go v1.1.0 @@ -92,6 +93,7 @@ require ( github.com/emicklei/go-restful/v3 v3.8.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect + github.com/fatih/color v1.7.0 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -114,7 +116,9 @@ require ( github.com/gregdel/pushover v1.1.0 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v0.14.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.1 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect @@ -130,6 +134,7 @@ require ( github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/spdystream v0.2.0 // indirect @@ -139,6 +144,7 @@ require ( github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/oklog/run v1.0.0 // indirect github.com/onsi/ginkgo v1.16.4 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.13 // indirect diff --git a/go.sum b/go.sum index ed9ca221cf..818b6a91f2 100644 --- a/go.sum +++ b/go.sum @@ -293,6 +293,7 @@ github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htX github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= @@ -522,11 +523,14 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-plugin v1.4.8 h1:CHGwpxYDOttQOY7HOWgETU9dyVjOXzniXDqJcYJE1zM= +github.com/hashicorp/go-plugin v1.4.8/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.1/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= @@ -543,6 +547,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/heketi/heketi v10.3.0+incompatible/go.mod h1:bB9ly3RchcQqsQ9CpyaQwvva7RS5ytVoSoholZQON6o= github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6/go.mod h1:xGMAM8JLi7UkZt1i4FQeQy0R2T8GLUwQhOP5M1gBhy4= github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= @@ -557,13 +563,14 @@ github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/influxdata/influxdb-client-go/v2 v2.12.1 h1:RrjoDNyBGFYvjKfjmtIyYAn6GY/SrtocSo4RPlt+Lng= -github.com/influxdata/influxdb-client-go/v2 v2.12.1/go.mod h1:YteV91FiQxRdccyJ2cHvj2f/5sq4y4Njqu1fQzsQCOU= +github.com/influxdata/influxdb-client-go/v2 v2.12.2 h1:uYABKdrEKlYm+++qfKdbgaHKBPmoWR5wpbmj6MBB/2g= +github.com/influxdata/influxdb-client-go/v2 v2.12.2/go.mod h1:YteV91FiQxRdccyJ2cHvj2f/5sq4y4Njqu1fQzsQCOU= github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU= github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= github.com/ishidawataru/sctp v0.0.0-20190723014705-7c296d48a2b5/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -650,12 +657,14 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/matryer/moq v0.2.7/go.mod h1:kITsx543GOENm48TUAQyJ9+SAvFSr7iGQXPoth/VUBk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.10/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -677,6 +686,7 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -725,6 +735,8 @@ github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rR github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= @@ -1170,6 +1182,7 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/manifests/crds/analysis-run-crd.yaml b/manifests/crds/analysis-run-crd.yaml index da21888bde..cc2da51d06 100644 --- a/manifests/crds/analysis-run-crd.yaml +++ b/manifests/crds/analysis-run-crd.yaml @@ -2700,6 +2700,11 @@ spec: required: - query type: object + plugin: + properties: + config: + x-kubernetes-preserve-unknown-fields: true + type: object prometheus: properties: address: diff --git a/manifests/crds/analysis-template-crd.yaml b/manifests/crds/analysis-template-crd.yaml index 1e37db2dd0..784e088dad 100644 --- a/manifests/crds/analysis-template-crd.yaml +++ b/manifests/crds/analysis-template-crd.yaml @@ -2696,6 +2696,11 @@ spec: required: - query type: object + plugin: + properties: + config: + x-kubernetes-preserve-unknown-fields: true + type: object prometheus: properties: address: diff --git a/manifests/crds/cluster-analysis-template-crd.yaml b/manifests/crds/cluster-analysis-template-crd.yaml index fb149f2fa3..d309aa0169 100644 --- a/manifests/crds/cluster-analysis-template-crd.yaml +++ b/manifests/crds/cluster-analysis-template-crd.yaml @@ -2696,6 +2696,11 @@ spec: required: - query type: object + plugin: + properties: + config: + x-kubernetes-preserve-unknown-fields: true + type: object prometheus: properties: address: diff --git a/manifests/install.yaml b/manifests/install.yaml index c90546d8b7..11130bd027 100755 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -2701,6 +2701,11 @@ spec: required: - query type: object + plugin: + properties: + config: + x-kubernetes-preserve-unknown-fields: true + type: object prometheus: properties: address: @@ -5578,6 +5583,11 @@ spec: required: - query type: object + plugin: + properties: + config: + x-kubernetes-preserve-unknown-fields: true + type: object prometheus: properties: address: @@ -8341,6 +8351,11 @@ spec: required: - query type: object + plugin: + properties: + config: + x-kubernetes-preserve-unknown-fields: true + type: object prometheus: properties: address: diff --git a/metric/provider.go b/metric/provider.go new file mode 100644 index 0000000000..aba5ddbd0c --- /dev/null +++ b/metric/provider.go @@ -0,0 +1,21 @@ +package metric + +import "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + +// Provider methods to query an external systems and generate a measurement +type Provider interface { + // Run start a new external system call for a measurement + // Should be idempotent and do nothing if a call has already been started + Run(*v1alpha1.AnalysisRun, v1alpha1.Metric) v1alpha1.Measurement + // Checks if the external system call is finished and returns the current measurement + Resume(*v1alpha1.AnalysisRun, v1alpha1.Metric, v1alpha1.Measurement) v1alpha1.Measurement + // Terminate will terminate an in-progress measurement + Terminate(*v1alpha1.AnalysisRun, v1alpha1.Metric, v1alpha1.Measurement) v1alpha1.Measurement + // GarbageCollect is used to garbage collect completed measurements to the specified limit + GarbageCollect(*v1alpha1.AnalysisRun, v1alpha1.Metric, int) error + // Type gets the provider type + Type() string + // GetMetadata returns any additional metadata which providers need to store/display as part + // of the metric result. For example, Prometheus uses is to store the final resolved queries. + GetMetadata(metric v1alpha1.Metric) map[string]string +} diff --git a/metricproviders/metricproviders.go b/metricproviders/metricproviders.go index d8a1637acf..2e0f664a02 100644 --- a/metricproviders/metricproviders.go +++ b/metricproviders/metricproviders.go @@ -3,13 +3,14 @@ package metricproviders import ( "fmt" - "github.com/argoproj/argo-rollouts/metricproviders/influxdb" - + "github.com/argoproj/argo-rollouts/metric" "github.com/argoproj/argo-rollouts/metricproviders/cloudwatch" "github.com/argoproj/argo-rollouts/metricproviders/datadog" "github.com/argoproj/argo-rollouts/metricproviders/graphite" + "github.com/argoproj/argo-rollouts/metricproviders/influxdb" "github.com/argoproj/argo-rollouts/metricproviders/kayenta" "github.com/argoproj/argo-rollouts/metricproviders/newrelic" + "github.com/argoproj/argo-rollouts/metricproviders/plugin" "github.com/argoproj/argo-rollouts/metricproviders/wavefront" "github.com/argoproj/argo-rollouts/metricproviders/webmetric" @@ -23,22 +24,9 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" ) -// Provider methods to query a external systems and generate a measurement +// Provider this is here just for backwards compatibility the interface is now in the metric package type Provider interface { - // Run start a new external system call for a measurement - // Should be idempotent and do nothing if a call has already been started - Run(*v1alpha1.AnalysisRun, v1alpha1.Metric) v1alpha1.Measurement - // Checks if the external system call is finished and returns the current measurement - Resume(*v1alpha1.AnalysisRun, v1alpha1.Metric, v1alpha1.Measurement) v1alpha1.Measurement - // Terminate will terminate an in-progress measurement - Terminate(*v1alpha1.AnalysisRun, v1alpha1.Metric, v1alpha1.Measurement) v1alpha1.Measurement - // GarbageCollect is used to garbage collect completed measurements to the specified limit - GarbageCollect(*v1alpha1.AnalysisRun, v1alpha1.Metric, int) error - // Type gets the provider type - Type() string - // GetMetadata returns any additional metadata which providers need to store/display as part - // of the metric result. For example, Prometheus uses is to store the final resolved queries. - GetMetadata(metric v1alpha1.Metric) map[string]string + metric.Provider } type ProviderFactory struct { @@ -101,6 +89,9 @@ func (f *ProviderFactory) NewProvider(logCtx log.Entry, metric v1alpha1.Metric) return nil, err } return influxdb.NewInfluxdbProvider(client, logCtx), nil + case plugin.ProviderType: + plugin, err := plugin.NewRpcPlugin(metric) + return plugin, err default: return nil, fmt.Errorf("no valid provider in metric '%s'", metric.Name) } @@ -127,6 +118,8 @@ func Type(metric v1alpha1.Metric) string { return graphite.ProviderType } else if metric.Provider.Influxdb != nil { return influxdb.ProviderType + } else if metric.Provider.Plugin != nil { + return plugin.ProviderType } return "Unknown Provider" diff --git a/metricproviders/plugin/plugin.go b/metricproviders/plugin/plugin.go new file mode 100644 index 0000000000..bcc6283fe3 --- /dev/null +++ b/metricproviders/plugin/plugin.go @@ -0,0 +1,105 @@ +package plugin + +import ( + "fmt" + "os/exec" + + "github.com/argoproj/argo-rollouts/metric" + "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "github.com/argoproj/argo-rollouts/utils/defaults" + goPlugin "github.com/hashicorp/go-plugin" +) + +const ProviderType = "RPCPlugin" + +var pluginClient *goPlugin.Client +var plugin rpc.MetricsPlugin + +type MetricPlugin struct { + metric.Provider +} + +func NewRpcPlugin(metric v1alpha1.Metric) (metric.Provider, error) { + err := startPluginSystem(metric) + if err != nil { + return nil, err + } + + return MetricPlugin{}, nil +} + +func startPluginSystem(metric v1alpha1.Metric) error { + if defaults.GetMetricPluginLocation() == "" { + return fmt.Errorf("no plugin location specified") + } + + var handshakeConfig = goPlugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "ARGO_ROLLOUTS_RPC_PLUGIN", + MagicCookieValue: "metrics", + } + + // pluginMap is the map of plugins we can dispense. + var pluginMap = map[string]goPlugin.Plugin{ + "RpcMetricsPlugin": &rpc.RpcMetricsPlugin{}, + } + + if pluginClient == nil || pluginClient.Exited() { + pluginClient = goPlugin.NewClient(&goPlugin.ClientConfig{ + HandshakeConfig: handshakeConfig, + Plugins: pluginMap, + VersionedPlugins: nil, + Cmd: exec.Command(defaults.GetMetricPluginLocation()), + Managed: true, + }) + + rpcClient, err := pluginClient.Client() + if err != nil { + return err + } + + // Request the plugin + raw, err := rpcClient.Dispense("RpcMetricsPlugin") + if err != nil { + return err + } + + plugin = raw.(rpc.MetricsPlugin) + + err = plugin.NewMetricsPlugin(metric) + if err.Error() != "" { + return err + } + } + + return nil +} + +func (m MetricPlugin) Run(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alpha1.Measurement { + return plugin.Run(run, metric) +} + +func (m MetricPlugin) Resume(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { + return plugin.Resume(run, metric, measurement) +} + +func (m MetricPlugin) Terminate(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { + return plugin.Terminate(run, metric, measurement) +} + +func (m MetricPlugin) GarbageCollect(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, limit int) error { + err := plugin.GarbageCollect(run, metric, limit) + if err.Error() != "" { + return err + } + return nil +} + +func (m MetricPlugin) Type() string { + return ProviderType +} + +func (m MetricPlugin) GetMetadata(metric v1alpha1.Metric) map[string]string { + return plugin.GetMetadata(metric) +} diff --git a/metricproviders/plugin/rpc/rpc.go b/metricproviders/plugin/rpc/rpc.go new file mode 100644 index 0000000000..55320f257e --- /dev/null +++ b/metricproviders/plugin/rpc/rpc.go @@ -0,0 +1,232 @@ +package rpc + +import ( + "encoding/gob" + "fmt" + "net/rpc" + + "github.com/argoproj/argo-rollouts/utils/plugin/types" + + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + metricutil "github.com/argoproj/argo-rollouts/utils/metric" + "github.com/hashicorp/go-plugin" + log "github.com/sirupsen/logrus" +) + +type RunArgs struct { + AnalysisRun *v1alpha1.AnalysisRun + Metric v1alpha1.Metric +} + +type TerminateAndResumeArgs struct { + AnalysisRun *v1alpha1.AnalysisRun + Metric v1alpha1.Metric + Measurement v1alpha1.Measurement +} + +type GarbageCollectArgs struct { + AnalysisRun *v1alpha1.AnalysisRun + Metric v1alpha1.Metric + Limit int +} + +type InitMetricsPluginAndGetMetadataArgs struct { + Metric v1alpha1.Metric +} + +func init() { + gob.RegisterName("RunArgs", new(RunArgs)) + gob.RegisterName("TerminateAndResumeArgs", new(TerminateAndResumeArgs)) + gob.RegisterName("GarbageCollectArgs", new(GarbageCollectArgs)) + gob.RegisterName("InitMetricsPluginAndGetMetadataArgs", new(InitMetricsPluginAndGetMetadataArgs)) + gob.RegisterName("RpcError", new(types.RpcError)) +} + +// MetricsPlugin is the interface that we're exposing as a plugin. It needs to match metricproviders.Providers but we can +// not import that package because it would create a circular dependency. +type MetricsPlugin interface { + NewMetricsPlugin(metric v1alpha1.Metric) types.RpcError + types.RpcMetricProvider +} + +// MetricsPluginRPC Here is an implementation that talks over RPC +type MetricsPluginRPC struct{ client *rpc.Client } + +func (g *MetricsPluginRPC) NewMetricsPlugin(metric v1alpha1.Metric) types.RpcError { + var resp types.RpcError + var args interface{} = InitMetricsPluginAndGetMetadataArgs{ + Metric: metric, + } + err := g.client.Call("Plugin.NewMetricsPlugin", &args, &resp) + if err != nil { + return types.RpcError{ErrorString: err.Error()} + } + return resp +} + +func (g *MetricsPluginRPC) Run(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alpha1.Measurement { + var resp v1alpha1.Measurement + var args interface{} = RunArgs{ + AnalysisRun: analysisRun, + Metric: metric, + } + err := g.client.Call("Plugin.Run", &args, &resp) + if err != nil { + return metricutil.MarkMeasurementError(resp, err) + } + return resp +} + +func (g *MetricsPluginRPC) Resume(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { + var resp v1alpha1.Measurement + var args interface{} = TerminateAndResumeArgs{ + AnalysisRun: analysisRun, + Metric: metric, + Measurement: measurement, + } + err := g.client.Call("Plugin.Resume", &args, &resp) + if err != nil { + return metricutil.MarkMeasurementError(resp, err) + } + return resp +} + +func (g *MetricsPluginRPC) Terminate(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { + var resp v1alpha1.Measurement + var args interface{} = TerminateAndResumeArgs{ + AnalysisRun: analysisRun, + Metric: metric, + Measurement: measurement, + } + err := g.client.Call("Plugin.Terminate", &args, &resp) + if err != nil { + return metricutil.MarkMeasurementError(resp, err) + } + return resp +} + +func (g *MetricsPluginRPC) GarbageCollect(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric, limit int) types.RpcError { + var resp types.RpcError + var args interface{} = GarbageCollectArgs{ + AnalysisRun: analysisRun, + Metric: metric, + Limit: limit, + } + err := g.client.Call("Plugin.GarbageCollect", &args, &resp) + if err != nil { + return types.RpcError{ErrorString: err.Error()} + } + return resp +} + +func (g *MetricsPluginRPC) Type() string { + var resp string + err := g.client.Call("Plugin.Type", new(interface{}), &resp) + if err != nil { + return err.Error() + } + + return resp +} + +func (g *MetricsPluginRPC) GetMetadata(metric v1alpha1.Metric) map[string]string { + var resp map[string]string + var args interface{} = InitMetricsPluginAndGetMetadataArgs{ + Metric: metric, + } + err := g.client.Call("Plugin.GetMetadata", &args, &resp) + if err != nil { + log.Errorf("Error calling GetMetadata: %v", err) + return map[string]string{"error": err.Error()} + } + return resp +} + +// MetricsRPCServer Here is the RPC server that MetricsPluginRPC talks to, conforming to +// the requirements of net/rpc +type MetricsRPCServer struct { + // This is the real implementation + Impl MetricsPlugin +} + +func (s *MetricsRPCServer) NewMetricsPlugin(args interface{}, resp *types.RpcError) error { + initArgs, ok := args.(*InitMetricsPluginAndGetMetadataArgs) + if !ok { + return fmt.Errorf("invalid args %s", args) + } + *resp = s.Impl.NewMetricsPlugin(initArgs.Metric) + return nil +} + +func (s *MetricsRPCServer) Run(args interface{}, resp *v1alpha1.Measurement) error { + runArgs, ok := args.(*RunArgs) + if !ok { + return fmt.Errorf("invalid args %s", args) + } + *resp = s.Impl.Run(runArgs.AnalysisRun, runArgs.Metric) + return nil +} + +func (s *MetricsRPCServer) Resume(args interface{}, resp *v1alpha1.Measurement) error { + resumeArgs, ok := args.(*TerminateAndResumeArgs) + if !ok { + return fmt.Errorf("invalid args %s", args) + } + *resp = s.Impl.Resume(resumeArgs.AnalysisRun, resumeArgs.Metric, resumeArgs.Measurement) + return nil +} + +func (s *MetricsRPCServer) Terminate(args interface{}, resp *v1alpha1.Measurement) error { + resumeArgs, ok := args.(*TerminateAndResumeArgs) + if !ok { + return fmt.Errorf("invalid args %s", args) + } + *resp = s.Impl.Terminate(resumeArgs.AnalysisRun, resumeArgs.Metric, resumeArgs.Measurement) + return nil +} + +func (s *MetricsRPCServer) GarbageCollect(args interface{}, resp *types.RpcError) error { + gcArgs, ok := args.(*GarbageCollectArgs) + if !ok { + return fmt.Errorf("invalid args %s", args) + } + *resp = s.Impl.GarbageCollect(gcArgs.AnalysisRun, gcArgs.Metric, gcArgs.Limit) + return nil +} + +func (s *MetricsRPCServer) Type(args interface{}, resp *string) error { + *resp = s.Impl.Type() + return nil +} + +func (s *MetricsRPCServer) GetMetadata(args interface{}, resp *map[string]string) error { + getMetadataArgs, ok := args.(*InitMetricsPluginAndGetMetadataArgs) + if !ok { + return fmt.Errorf("invalid args %s", args) + } + *resp = s.Impl.GetMetadata(getMetadataArgs.Metric) + return nil +} + +// RpcMetricsPlugin This is the implementation of plugin.Plugin so we can serve/consume +// +// This has two methods: Server must return an RPC server for this plugin +// type. We construct a MetricsRPCServer for this. +// +// Client must return an implementation of our interface that communicates +// over an RPC client. We return MetricsPluginRPC for this. +// +// Ignore MuxBroker. That is used to create more multiplexed streams on our +// plugin connection and is a more advanced use case. +type RpcMetricsPlugin struct { + // Impl Injection + Impl MetricsPlugin +} + +func (p *RpcMetricsPlugin) Server(*plugin.MuxBroker) (interface{}, error) { + return &MetricsRPCServer{Impl: p.Impl}, nil +} + +func (RpcMetricsPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { + return &MetricsPluginRPC{client: c}, nil +} diff --git a/metricproviders/plugin/rpc/rpc_test.go b/metricproviders/plugin/rpc/rpc_test.go new file mode 100644 index 0000000000..070dafc1bf --- /dev/null +++ b/metricproviders/plugin/rpc/rpc_test.go @@ -0,0 +1,162 @@ +package rpc + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + goPlugin "github.com/hashicorp/go-plugin" + "github.com/tj/assert" +) + +var testHandshake = goPlugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "ARGO_ROLLOUTS_RPC_PLUGIN", + MagicCookieValue: "metrics", +} + +func pluginClient(t *testing.T) (MetricsPlugin, goPlugin.ClientProtocol, func(), chan struct{}) { + ctx, cancel := context.WithCancel(context.Background()) + + rpcPluginImp := &testRpcPlugin{} + + // pluginMap is the map of plugins we can dispense. + var pluginMap = map[string]goPlugin.Plugin{ + "RpcMetricsPlugin": &RpcMetricsPlugin{Impl: rpcPluginImp}, + } + + ch := make(chan *goPlugin.ReattachConfig, 1) + closeCh := make(chan struct{}) + go goPlugin.Serve(&goPlugin.ServeConfig{ + HandshakeConfig: testHandshake, + Plugins: pluginMap, + Test: &goPlugin.ServeTestConfig{ + Context: ctx, + ReattachConfigCh: ch, + CloseCh: closeCh, + }, + }) + + // We should get a config + var config *goPlugin.ReattachConfig + select { + case config = <-ch: + case <-time.After(2000 * time.Millisecond): + t.Fatal("should've received reattach") + } + if config == nil { + t.Fatal("config should not be nil") + } + + // Connect! + c := goPlugin.NewClient(&goPlugin.ClientConfig{ + Cmd: nil, + HandshakeConfig: testHandshake, + Plugins: pluginMap, + Reattach: config, + }) + client, err := c.Client() + if err != nil { + t.Fatalf("err: %s", err) + } + + // Request the plugin + raw, err := client.Dispense("RpcMetricsPlugin") + if err != nil { + t.Fail() + } + + plugin, ok := raw.(MetricsPlugin) + if !ok { + t.Fail() + } + + return plugin, client, cancel, closeCh +} + +func TestPlugin(t *testing.T) { + plugin, _, cancel, closeCh := pluginClient(t) + defer cancel() + + err := plugin.NewMetricsPlugin(v1alpha1.Metric{ + Provider: v1alpha1.MetricProvider{ + Plugin: &v1alpha1.PluginMetric{Config: json.RawMessage(`{"address":"http://prometheus.local", "query":"machine_cpu_cores"}`)}, + }, + }) + if err.Error() != "" { + t.Fail() + } + + runMeasurement := plugin.Run(&v1alpha1.AnalysisRun{}, v1alpha1.Metric{}) + assert.Equal(t, "TestCompleted", string(runMeasurement.Phase)) + + runMeasurementErr := plugin.Run(nil, v1alpha1.Metric{}) + assert.Equal(t, "Error", string(runMeasurementErr.Phase)) + assert.Equal(t, "analysisRun is nil", runMeasurementErr.Message) + + resumeMeasurement := plugin.Resume(&v1alpha1.AnalysisRun{}, v1alpha1.Metric{}, v1alpha1.Measurement{ + Phase: "TestCompletedResume", + Message: "Check to see if we get same phase back", + }) + assert.Equal(t, "TestCompletedResume", string(resumeMeasurement.Phase)) + + terminateMeasurement := plugin.Terminate(&v1alpha1.AnalysisRun{}, v1alpha1.Metric{}, v1alpha1.Measurement{ + Phase: "TestCompletedTerminate", + Message: "Check to see if we get same phase back", + }) + assert.Equal(t, "TestCompletedTerminate", string(terminateMeasurement.Phase)) + + gcError := plugin.GarbageCollect(&v1alpha1.AnalysisRun{}, v1alpha1.Metric{}, 0) + assert.Equal(t, "", gcError.Error()) + + typeTest := plugin.Type() + assert.Equal(t, "TestRPCPlugin", typeTest) + + metadata := plugin.GetMetadata(v1alpha1.Metric{ + Name: "testMetric", + }) + assert.Equal(t, "testMetric", metadata["metricName"]) + + // Canceling should cause an exit + cancel() + <-closeCh +} + +func TestPluginClosedConnection(t *testing.T) { + plugin, client, cancel, closeCh := pluginClient(t) + defer cancel() + + client.Close() + time.Sleep(100 * time.Millisecond) + + const expectedError = "connection is shut down" + + newMetrics := plugin.NewMetricsPlugin(v1alpha1.Metric{}) + assert.Equal(t, expectedError, newMetrics.Error()) + + measurement := plugin.Terminate(&v1alpha1.AnalysisRun{}, v1alpha1.Metric{}, v1alpha1.Measurement{}) + assert.Equal(t, expectedError, measurement.Message) + + measurement = plugin.Run(&v1alpha1.AnalysisRun{}, v1alpha1.Metric{}) + assert.Equal(t, expectedError, measurement.Message) + + measurement = plugin.Resume(&v1alpha1.AnalysisRun{}, v1alpha1.Metric{}, v1alpha1.Measurement{}) + assert.Equal(t, expectedError, measurement.Message) + + measurement = plugin.Terminate(&v1alpha1.AnalysisRun{}, v1alpha1.Metric{}, v1alpha1.Measurement{}) + assert.Equal(t, expectedError, measurement.Message) + + typeStr := plugin.Type() + assert.Equal(t, expectedError, typeStr) + + metadata := plugin.GetMetadata(v1alpha1.Metric{}) + assert.Equal(t, expectedError, metadata["error"]) + + gcError := plugin.GarbageCollect(&v1alpha1.AnalysisRun{}, v1alpha1.Metric{}, 0) + assert.Equal(t, expectedError, gcError.Error()) + + cancel() + <-closeCh +} diff --git a/metricproviders/plugin/rpc/rpc_test_implementation.go b/metricproviders/plugin/rpc/rpc_test_implementation.go new file mode 100644 index 0000000000..c3a23720c8 --- /dev/null +++ b/metricproviders/plugin/rpc/rpc_test_implementation.go @@ -0,0 +1,59 @@ +package rpc + +import ( + "fmt" + "time" + + "github.com/argoproj/argo-rollouts/utils/plugin/types" + + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + metricutil "github.com/argoproj/argo-rollouts/utils/metric" + timeutil "github.com/argoproj/argo-rollouts/utils/time" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type testRpcPlugin struct{} + +func (g *testRpcPlugin) NewMetricsPlugin(metric v1alpha1.Metric) types.RpcError { + return types.RpcError{} +} + +func (g *testRpcPlugin) Run(anaysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alpha1.Measurement { + startTime := timeutil.MetaNow() + finishTime := v1.Time{Time: startTime.Add(10 * time.Second)} + newMeasurement := v1alpha1.Measurement{ + Phase: "TestCompleted", + Message: "Test run completed", + StartedAt: &startTime, + FinishedAt: &finishTime, + Value: "", + Metadata: nil, + ResumeAt: nil, + } + if anaysisRun == nil { + return metricutil.MarkMeasurementError(newMeasurement, fmt.Errorf("analysisRun is nil")) + } + return newMeasurement +} + +func (g *testRpcPlugin) Resume(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { + return measurement +} + +func (g *testRpcPlugin) Terminate(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { + return measurement +} + +func (g *testRpcPlugin) GarbageCollect(*v1alpha1.AnalysisRun, v1alpha1.Metric, int) types.RpcError { + return types.RpcError{} +} + +func (g *testRpcPlugin) Type() string { + return "TestRPCPlugin" +} + +func (g *testRpcPlugin) GetMetadata(metric v1alpha1.Metric) map[string]string { + metricsMetadata := make(map[string]string) + metricsMetadata["metricName"] = metric.Name + return metricsMetadata +} diff --git a/mkdocs.yml b/mkdocs.yml index 9e63a2f89e..8413ed001e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -53,6 +53,7 @@ nav: - Traefik: features/traffic-management/traefik.md - Analysis: - Overview: features/analysis.md + - Plugins: analysis/plugins.md - Prometheus: analysis/prometheus.md - DataDog: analysis/datadog.md - NewRelic: analysis/newrelic.md diff --git a/pkg/apis/rollouts/v1alpha1/analysis_types.go b/pkg/apis/rollouts/v1alpha1/analysis_types.go index f26fc91585..742c7bec61 100644 --- a/pkg/apis/rollouts/v1alpha1/analysis_types.go +++ b/pkg/apis/rollouts/v1alpha1/analysis_types.go @@ -173,6 +173,14 @@ type MetricProvider struct { Graphite *GraphiteMetric `json:"graphite,omitempty" protobuf:"bytes,9,opt,name=graphite"` // Influxdb specifies the influxdb metric to query Influxdb *InfluxdbMetric `json:"influxdb,omitempty" protobuf:"bytes,10,opt,name=influxdb"` + // Plugin specifies the hashicorp go-plugin metric to query + Plugin *PluginMetric `json:"plugin,omitempty" protobuf:"bytes,11,opt,name=plugin"` +} + +type PluginMetric struct { + // +kubebuilder:pruning:PreserveUnknownFields + // +kubebuilder:validation:Schemaless + Config json.RawMessage `json:"config,omitempty" protobuf:"bytes,1,rep,name=config"` } // AnalysisPhase is the overall phase of an AnalysisRun, MetricResult, or Measurement diff --git a/pkg/apis/rollouts/v1alpha1/generated.pb.go b/pkg/apis/rollouts/v1alpha1/generated.pb.go index 35baf606ba..aaea5c09a5 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.pb.go +++ b/pkg/apis/rollouts/v1alpha1/generated.pb.go @@ -1868,10 +1868,38 @@ func (m *PingPongSpec) XXX_DiscardUnknown() { var xxx_messageInfo_PingPongSpec proto.InternalMessageInfo +func (m *PluginMetric) Reset() { *m = PluginMetric{} } +func (*PluginMetric) ProtoMessage() {} +func (*PluginMetric) Descriptor() ([]byte, []int) { + return fileDescriptor_e0e705f843545fab, []int{65} +} +func (m *PluginMetric) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PluginMetric) 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 *PluginMetric) XXX_Merge(src proto.Message) { + xxx_messageInfo_PluginMetric.Merge(m, src) +} +func (m *PluginMetric) XXX_Size() int { + return m.Size() +} +func (m *PluginMetric) XXX_DiscardUnknown() { + xxx_messageInfo_PluginMetric.DiscardUnknown(m) +} + +var xxx_messageInfo_PluginMetric proto.InternalMessageInfo + func (m *PodTemplateMetadata) Reset() { *m = PodTemplateMetadata{} } func (*PodTemplateMetadata) ProtoMessage() {} func (*PodTemplateMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{65} + return fileDescriptor_e0e705f843545fab, []int{66} } func (m *PodTemplateMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1901,7 +1929,7 @@ func (m *PreferredDuringSchedulingIgnoredDuringExecution) Reset() { } func (*PreferredDuringSchedulingIgnoredDuringExecution) ProtoMessage() {} func (*PreferredDuringSchedulingIgnoredDuringExecution) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{66} + return fileDescriptor_e0e705f843545fab, []int{67} } func (m *PreferredDuringSchedulingIgnoredDuringExecution) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1929,7 +1957,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{67} + return fileDescriptor_e0e705f843545fab, []int{68} } func (m *PrometheusMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1959,7 +1987,7 @@ func (m *RequiredDuringSchedulingIgnoredDuringExecution) Reset() { } func (*RequiredDuringSchedulingIgnoredDuringExecution) ProtoMessage() {} func (*RequiredDuringSchedulingIgnoredDuringExecution) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{68} + return fileDescriptor_e0e705f843545fab, []int{69} } func (m *RequiredDuringSchedulingIgnoredDuringExecution) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1987,7 +2015,7 @@ var xxx_messageInfo_RequiredDuringSchedulingIgnoredDuringExecution proto.Interna func (m *RollbackWindowSpec) Reset() { *m = RollbackWindowSpec{} } func (*RollbackWindowSpec) ProtoMessage() {} func (*RollbackWindowSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{69} + return fileDescriptor_e0e705f843545fab, []int{70} } func (m *RollbackWindowSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2015,7 +2043,7 @@ var xxx_messageInfo_RollbackWindowSpec proto.InternalMessageInfo func (m *Rollout) Reset() { *m = Rollout{} } func (*Rollout) ProtoMessage() {} func (*Rollout) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{70} + return fileDescriptor_e0e705f843545fab, []int{71} } func (m *Rollout) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2043,7 +2071,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{71} + return fileDescriptor_e0e705f843545fab, []int{72} } func (m *RolloutAnalysis) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2071,7 +2099,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{72} + return fileDescriptor_e0e705f843545fab, []int{73} } func (m *RolloutAnalysisBackground) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2099,7 +2127,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{73} + return fileDescriptor_e0e705f843545fab, []int{74} } func (m *RolloutAnalysisRunStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2127,7 +2155,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{74} + return fileDescriptor_e0e705f843545fab, []int{75} } func (m *RolloutAnalysisTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2155,7 +2183,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{75} + return fileDescriptor_e0e705f843545fab, []int{76} } func (m *RolloutCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2183,7 +2211,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{76} + return fileDescriptor_e0e705f843545fab, []int{77} } func (m *RolloutExperimentStep) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2213,7 +2241,7 @@ func (m *RolloutExperimentStepAnalysisTemplateRef) Reset() { } func (*RolloutExperimentStepAnalysisTemplateRef) ProtoMessage() {} func (*RolloutExperimentStepAnalysisTemplateRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{77} + return fileDescriptor_e0e705f843545fab, []int{78} } func (m *RolloutExperimentStepAnalysisTemplateRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2241,7 +2269,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{78} + return fileDescriptor_e0e705f843545fab, []int{79} } func (m *RolloutExperimentTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2269,7 +2297,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{79} + return fileDescriptor_e0e705f843545fab, []int{80} } func (m *RolloutList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2297,7 +2325,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{80} + return fileDescriptor_e0e705f843545fab, []int{81} } func (m *RolloutPause) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2325,7 +2353,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{81} + return fileDescriptor_e0e705f843545fab, []int{82} } func (m *RolloutSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2353,7 +2381,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{82} + return fileDescriptor_e0e705f843545fab, []int{83} } func (m *RolloutStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2381,7 +2409,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{83} + return fileDescriptor_e0e705f843545fab, []int{84} } func (m *RolloutStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2409,7 +2437,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{84} + return fileDescriptor_e0e705f843545fab, []int{85} } func (m *RolloutTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2437,7 +2465,7 @@ var xxx_messageInfo_RolloutTrafficRouting proto.InternalMessageInfo func (m *RouteMatch) Reset() { *m = RouteMatch{} } func (*RouteMatch) ProtoMessage() {} func (*RouteMatch) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{85} + return fileDescriptor_e0e705f843545fab, []int{86} } func (m *RouteMatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2465,7 +2493,7 @@ var xxx_messageInfo_RouteMatch proto.InternalMessageInfo func (m *RunSummary) Reset() { *m = RunSummary{} } func (*RunSummary) ProtoMessage() {} func (*RunSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{86} + return fileDescriptor_e0e705f843545fab, []int{87} } func (m *RunSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2493,7 +2521,7 @@ var xxx_messageInfo_RunSummary proto.InternalMessageInfo func (m *SMITrafficRouting) Reset() { *m = SMITrafficRouting{} } func (*SMITrafficRouting) ProtoMessage() {} func (*SMITrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{87} + return fileDescriptor_e0e705f843545fab, []int{88} } func (m *SMITrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2521,7 +2549,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{88} + return fileDescriptor_e0e705f843545fab, []int{89} } func (m *ScopeDetail) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2549,7 +2577,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{89} + return fileDescriptor_e0e705f843545fab, []int{90} } func (m *SecretKeyRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2577,7 +2605,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{90} + return fileDescriptor_e0e705f843545fab, []int{91} } func (m *SetCanaryScale) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2605,7 +2633,7 @@ var xxx_messageInfo_SetCanaryScale proto.InternalMessageInfo func (m *SetHeaderRoute) Reset() { *m = SetHeaderRoute{} } func (*SetHeaderRoute) ProtoMessage() {} func (*SetHeaderRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{91} + return fileDescriptor_e0e705f843545fab, []int{92} } func (m *SetHeaderRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2633,7 +2661,7 @@ var xxx_messageInfo_SetHeaderRoute proto.InternalMessageInfo func (m *SetMirrorRoute) Reset() { *m = SetMirrorRoute{} } func (*SetMirrorRoute) ProtoMessage() {} func (*SetMirrorRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{92} + return fileDescriptor_e0e705f843545fab, []int{93} } func (m *SetMirrorRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2661,7 +2689,7 @@ var xxx_messageInfo_SetMirrorRoute proto.InternalMessageInfo func (m *StickinessConfig) Reset() { *m = StickinessConfig{} } func (*StickinessConfig) ProtoMessage() {} func (*StickinessConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{93} + return fileDescriptor_e0e705f843545fab, []int{94} } func (m *StickinessConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2689,7 +2717,7 @@ var xxx_messageInfo_StickinessConfig proto.InternalMessageInfo func (m *StringMatch) Reset() { *m = StringMatch{} } func (*StringMatch) ProtoMessage() {} func (*StringMatch) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{94} + return fileDescriptor_e0e705f843545fab, []int{95} } func (m *StringMatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2717,7 +2745,7 @@ var xxx_messageInfo_StringMatch proto.InternalMessageInfo func (m *TCPRoute) Reset() { *m = TCPRoute{} } func (*TCPRoute) ProtoMessage() {} func (*TCPRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{95} + return fileDescriptor_e0e705f843545fab, []int{96} } func (m *TCPRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2745,7 +2773,7 @@ var xxx_messageInfo_TCPRoute proto.InternalMessageInfo func (m *TLSRoute) Reset() { *m = TLSRoute{} } func (*TLSRoute) ProtoMessage() {} func (*TLSRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{96} + return fileDescriptor_e0e705f843545fab, []int{97} } func (m *TLSRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2773,7 +2801,7 @@ var xxx_messageInfo_TLSRoute proto.InternalMessageInfo func (m *TemplateService) Reset() { *m = TemplateService{} } func (*TemplateService) ProtoMessage() {} func (*TemplateService) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{97} + return fileDescriptor_e0e705f843545fab, []int{98} } func (m *TemplateService) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2801,7 +2829,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{98} + return fileDescriptor_e0e705f843545fab, []int{99} } func (m *TemplateSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2829,7 +2857,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{99} + return fileDescriptor_e0e705f843545fab, []int{100} } func (m *TemplateStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2857,7 +2885,7 @@ var xxx_messageInfo_TemplateStatus proto.InternalMessageInfo func (m *TraefikTrafficRouting) Reset() { *m = TraefikTrafficRouting{} } func (*TraefikTrafficRouting) ProtoMessage() {} func (*TraefikTrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{100} + return fileDescriptor_e0e705f843545fab, []int{101} } func (m *TraefikTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2885,7 +2913,7 @@ var xxx_messageInfo_TraefikTrafficRouting proto.InternalMessageInfo func (m *TrafficWeights) Reset() { *m = TrafficWeights{} } func (*TrafficWeights) ProtoMessage() {} func (*TrafficWeights) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{101} + return fileDescriptor_e0e705f843545fab, []int{102} } func (m *TrafficWeights) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2913,7 +2941,7 @@ var xxx_messageInfo_TrafficWeights proto.InternalMessageInfo func (m *ValueFrom) Reset() { *m = ValueFrom{} } func (*ValueFrom) ProtoMessage() {} func (*ValueFrom) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{102} + return fileDescriptor_e0e705f843545fab, []int{103} } func (m *ValueFrom) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2941,7 +2969,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{103} + return fileDescriptor_e0e705f843545fab, []int{104} } func (m *WavefrontMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2969,7 +2997,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{104} + return fileDescriptor_e0e705f843545fab, []int{105} } func (m *WebMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2997,7 +3025,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{105} + return fileDescriptor_e0e705f843545fab, []int{106} } func (m *WebMetricHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3025,7 +3053,7 @@ var xxx_messageInfo_WebMetricHeader proto.InternalMessageInfo func (m *WeightDestination) Reset() { *m = WeightDestination{} } func (*WeightDestination) ProtoMessage() {} func (*WeightDestination) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{106} + return fileDescriptor_e0e705f843545fab, []int{107} } func (m *WeightDestination) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3119,6 +3147,7 @@ func init() { proto.RegisterType((*ObjectRef)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.ObjectRef") proto.RegisterType((*PauseCondition)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PauseCondition") proto.RegisterType((*PingPongSpec)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PingPongSpec") + proto.RegisterType((*PluginMetric)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PluginMetric") proto.RegisterType((*PodTemplateMetadata)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PodTemplateMetadata") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PodTemplateMetadata.AnnotationsEntry") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PodTemplateMetadata.LabelsEntry") @@ -3171,493 +3200,497 @@ func init() { } var fileDescriptor_e0e705f843545fab = []byte{ - // 7773 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5d, 0x6c, 0x23, 0xd7, - 0x75, 0xb0, 0x87, 0x14, 0x25, 0xf2, 0xe8, 0xff, 0xae, 0x36, 0x2b, 0xcb, 0xde, 0xa5, 0x33, 0x0e, - 0xfc, 0x39, 0xdf, 0xe7, 0x48, 0x89, 0x7f, 0xbe, 0xcf, 0x89, 0x0d, 0x7f, 0x25, 0xa5, 0x5d, 0xaf, - 0xd6, 0xd2, 0x2e, 0xf7, 0x52, 0xbb, 0x9b, 0x38, 0x71, 0x92, 0x11, 0x79, 0x45, 0xcd, 0x8a, 0x9c, - 0x61, 0x66, 0x86, 0xd2, 0xca, 0x31, 0x12, 0x3b, 0x81, 0xdd, 0xb4, 0x48, 0x10, 0xb7, 0x49, 0x50, - 0x14, 0x05, 0x8a, 0xa0, 0x30, 0xd0, 0x9f, 0xe4, 0x29, 0x68, 0xd1, 0x97, 0x00, 0x2d, 0x9a, 0x9f, - 0xa6, 0x0f, 0x29, 0x92, 0x87, 0x36, 0x3f, 0x40, 0xd8, 0x5a, 0xe9, 0x4b, 0x8b, 0x16, 0x41, 0x81, - 0x14, 0x45, 0xf6, 0xa9, 0xb8, 0xbf, 0x73, 0x67, 0x38, 0xd4, 0x52, 0xe2, 0x68, 0x63, 0xb4, 0x79, - 0x23, 0xef, 0x39, 0xf7, 0x9c, 0x73, 0x7f, 0xcf, 0xbd, 0xe7, 0x9e, 0x73, 0x06, 0xd6, 0x1a, 0x76, - 0xb0, 0xdd, 0xd9, 0x5c, 0xac, 0xb9, 0xad, 0x25, 0xcb, 0x6b, 0xb8, 0x6d, 0xcf, 0xbd, 0xc9, 0x7e, - 0xbc, 0xcb, 0x73, 0x9b, 0x4d, 0xb7, 0x13, 0xf8, 0x4b, 0xed, 0x9d, 0xc6, 0x92, 0xd5, 0xb6, 0xfd, - 0x25, 0x55, 0xb2, 0xfb, 0x1e, 0xab, 0xd9, 0xde, 0xb6, 0xde, 0xb3, 0xd4, 0x20, 0x0e, 0xf1, 0xac, - 0x80, 0xd4, 0x17, 0xdb, 0x9e, 0x1b, 0xb8, 0xe8, 0xe9, 0x90, 0xda, 0xa2, 0xa4, 0xc6, 0x7e, 0x7c, - 0x44, 0xd6, 0x5d, 0x6c, 0xef, 0x34, 0x16, 0x29, 0xb5, 0x45, 0x55, 0x22, 0xa9, 0x2d, 0xbc, 0x4b, - 0x93, 0xa5, 0xe1, 0x36, 0xdc, 0x25, 0x46, 0x74, 0xb3, 0xb3, 0xc5, 0xfe, 0xb1, 0x3f, 0xec, 0x17, - 0x67, 0xb6, 0xf0, 0xe0, 0xce, 0x93, 0xfe, 0xa2, 0xed, 0x52, 0xd9, 0x96, 0x36, 0xad, 0xa0, 0xb6, - 0xbd, 0xb4, 0xdb, 0x23, 0xd1, 0x82, 0xa9, 0x21, 0xd5, 0x5c, 0x8f, 0x24, 0xe1, 0x3c, 0x1e, 0xe2, - 0xb4, 0xac, 0xda, 0xb6, 0xed, 0x10, 0x6f, 0x3f, 0x6c, 0x75, 0x8b, 0x04, 0x56, 0x52, 0xad, 0xa5, - 0x7e, 0xb5, 0xbc, 0x8e, 0x13, 0xd8, 0x2d, 0xd2, 0x53, 0xe1, 0xff, 0xde, 0xa9, 0x82, 0x5f, 0xdb, - 0x26, 0x2d, 0xab, 0xa7, 0xde, 0x63, 0xfd, 0xea, 0x75, 0x02, 0xbb, 0xb9, 0x64, 0x3b, 0x81, 0x1f, - 0x78, 0xf1, 0x4a, 0xe6, 0x37, 0xb3, 0x50, 0x28, 0xad, 0x95, 0xab, 0x81, 0x15, 0x74, 0x7c, 0xf4, - 0x9a, 0x01, 0x13, 0x4d, 0xd7, 0xaa, 0x97, 0xad, 0xa6, 0xe5, 0xd4, 0x88, 0x37, 0x6f, 0x3c, 0x60, - 0x3c, 0x3c, 0xfe, 0xe8, 0xda, 0xe2, 0x30, 0xe3, 0xb5, 0x58, 0xda, 0xf3, 0x31, 0xf1, 0xdd, 0x8e, - 0x57, 0x23, 0x98, 0x6c, 0x95, 0xe7, 0xbe, 0xd3, 0x2d, 0xde, 0x73, 0xd0, 0x2d, 0x4e, 0xac, 0x69, - 0x9c, 0x70, 0x84, 0x2f, 0xfa, 0x92, 0x01, 0xb3, 0x35, 0xcb, 0xb1, 0xbc, 0xfd, 0x0d, 0xcb, 0x6b, - 0x90, 0xe0, 0x59, 0xcf, 0xed, 0xb4, 0xe7, 0x33, 0x27, 0x20, 0xcd, 0xbd, 0x42, 0x9a, 0xd9, 0xe5, - 0x38, 0x3b, 0xdc, 0x2b, 0x01, 0x93, 0xcb, 0x0f, 0xac, 0xcd, 0x26, 0xd1, 0xe5, 0xca, 0x9e, 0xa4, - 0x5c, 0xd5, 0x38, 0x3b, 0xdc, 0x2b, 0x81, 0xf9, 0x6a, 0x16, 0x66, 0x4b, 0x6b, 0xe5, 0x0d, 0xcf, - 0xda, 0xda, 0xb2, 0x6b, 0xd8, 0xed, 0x04, 0xb6, 0xd3, 0x40, 0xef, 0x84, 0x31, 0xdb, 0x69, 0x78, - 0xc4, 0xf7, 0xd9, 0x40, 0x16, 0xca, 0xd3, 0x82, 0xe8, 0xd8, 0x2a, 0x2f, 0xc6, 0x12, 0x8e, 0x9e, - 0x80, 0x71, 0x9f, 0x78, 0xbb, 0x76, 0x8d, 0x54, 0x5c, 0x2f, 0x60, 0x3d, 0x9d, 0x2b, 0x9f, 0x12, - 0xe8, 0xe3, 0xd5, 0x10, 0x84, 0x75, 0x3c, 0x5a, 0xcd, 0x73, 0xdd, 0x40, 0xc0, 0x59, 0x47, 0x14, - 0xc2, 0x6a, 0x38, 0x04, 0x61, 0x1d, 0x0f, 0xbd, 0x6e, 0xc0, 0x8c, 0x1f, 0xd8, 0xb5, 0x1d, 0xdb, - 0x21, 0xbe, 0xbf, 0xec, 0x3a, 0x5b, 0x76, 0x63, 0x3e, 0xc7, 0x7a, 0xf1, 0xf2, 0x70, 0xbd, 0x58, - 0x8d, 0x51, 0x2d, 0xcf, 0x1d, 0x74, 0x8b, 0x33, 0xf1, 0x52, 0xdc, 0xc3, 0x1d, 0xad, 0xc0, 0x8c, - 0xe5, 0x38, 0x6e, 0x60, 0x05, 0xb6, 0xeb, 0x54, 0x3c, 0xb2, 0x65, 0xdf, 0x9a, 0x1f, 0x61, 0xcd, - 0x99, 0x17, 0xcd, 0x99, 0x29, 0xc5, 0xe0, 0xb8, 0xa7, 0x86, 0xb9, 0x02, 0xf3, 0xa5, 0xd6, 0xa6, - 0xe5, 0xfb, 0x56, 0xdd, 0xf5, 0x62, 0xa3, 0xf1, 0x30, 0xe4, 0x5b, 0x56, 0xbb, 0x6d, 0x3b, 0x0d, - 0x3a, 0x1c, 0xd9, 0x87, 0x0b, 0xe5, 0x89, 0x83, 0x6e, 0x31, 0xbf, 0x2e, 0xca, 0xb0, 0x82, 0x9a, - 0x3f, 0xca, 0xc0, 0x78, 0xc9, 0xb1, 0x9a, 0xfb, 0xbe, 0xed, 0xe3, 0x8e, 0x83, 0x3e, 0x0a, 0x79, - 0xba, 0xbb, 0xd4, 0xad, 0xc0, 0x12, 0x2b, 0xf2, 0xdd, 0x8b, 0x7c, 0xb1, 0x2f, 0xea, 0x8b, 0x3d, - 0xec, 0x17, 0x8a, 0xbd, 0xb8, 0xfb, 0x9e, 0xc5, 0x2b, 0x9b, 0x37, 0x49, 0x2d, 0x58, 0x27, 0x81, - 0x55, 0x46, 0xa2, 0x15, 0x10, 0x96, 0x61, 0x45, 0x15, 0xb9, 0x30, 0xe2, 0xb7, 0x49, 0x4d, 0xac, - 0xb0, 0xf5, 0x21, 0x67, 0x72, 0x28, 0x7a, 0xb5, 0x4d, 0x6a, 0xe5, 0x09, 0xc1, 0x7a, 0x84, 0xfe, - 0xc3, 0x8c, 0x11, 0xda, 0x83, 0x51, 0x9f, 0xed, 0x39, 0x62, 0xf1, 0x5c, 0x49, 0x8f, 0x25, 0x23, - 0x5b, 0x9e, 0x12, 0x4c, 0x47, 0xf9, 0x7f, 0x2c, 0xd8, 0x99, 0x3f, 0x36, 0xe0, 0x94, 0x86, 0x5d, - 0xf2, 0x1a, 0x9d, 0x16, 0x71, 0x02, 0xf4, 0x00, 0x8c, 0x38, 0x56, 0x8b, 0x88, 0x85, 0xa2, 0x44, - 0xbe, 0x6c, 0xb5, 0x08, 0x66, 0x10, 0xf4, 0x20, 0xe4, 0x76, 0xad, 0x66, 0x87, 0xb0, 0x4e, 0x2a, - 0x94, 0x27, 0x05, 0x4a, 0xee, 0x3a, 0x2d, 0xc4, 0x1c, 0x86, 0x5e, 0x82, 0x02, 0xfb, 0x71, 0xc1, - 0x73, 0x5b, 0x29, 0x35, 0x4d, 0x48, 0x78, 0x5d, 0x92, 0x2d, 0x4f, 0x1e, 0x74, 0x8b, 0x05, 0xf5, - 0x17, 0x87, 0x0c, 0xcd, 0x7f, 0x30, 0x60, 0x5a, 0x6b, 0xdc, 0x9a, 0xed, 0x07, 0xe8, 0x43, 0x3d, - 0x93, 0x67, 0x71, 0xb0, 0xc9, 0x43, 0x6b, 0xb3, 0xa9, 0x33, 0x23, 0x5a, 0x9a, 0x97, 0x25, 0xda, - 0xc4, 0x71, 0x20, 0x67, 0x07, 0xa4, 0xe5, 0xcf, 0x67, 0x1e, 0xc8, 0x3e, 0x3c, 0xfe, 0xe8, 0x6a, - 0x6a, 0xc3, 0x18, 0xf6, 0xef, 0x2a, 0xa5, 0x8f, 0x39, 0x1b, 0xf3, 0x6b, 0x23, 0x91, 0x16, 0xd2, - 0x19, 0x85, 0x5c, 0x18, 0x6b, 0x91, 0xc0, 0xb3, 0x6b, 0x7c, 0x5d, 0x8d, 0x3f, 0xba, 0x32, 0x9c, - 0x14, 0xeb, 0x8c, 0x58, 0xb8, 0x59, 0xf2, 0xff, 0x3e, 0x96, 0x5c, 0xd0, 0x36, 0x8c, 0x58, 0x5e, - 0x43, 0xb6, 0xf9, 0x42, 0x3a, 0xe3, 0x1b, 0xce, 0xb9, 0x92, 0xd7, 0xf0, 0x31, 0xe3, 0x80, 0x96, - 0xa0, 0x10, 0x10, 0xaf, 0x65, 0x3b, 0x56, 0xc0, 0x77, 0xd7, 0x7c, 0x79, 0x56, 0xa0, 0x15, 0x36, - 0x24, 0x00, 0x87, 0x38, 0xa8, 0x09, 0xa3, 0x75, 0x6f, 0x1f, 0x77, 0x9c, 0xf9, 0x91, 0x34, 0xba, - 0x62, 0x85, 0xd1, 0x0a, 0x17, 0x13, 0xff, 0x8f, 0x05, 0x0f, 0xf4, 0x86, 0x01, 0x73, 0x2d, 0x62, - 0xf9, 0x1d, 0x8f, 0xd0, 0x26, 0x60, 0x12, 0x10, 0x87, 0xee, 0x86, 0xf3, 0x39, 0xc6, 0x1c, 0x0f, - 0x3b, 0x0e, 0xbd, 0x94, 0xcb, 0xf7, 0x0b, 0x51, 0xe6, 0x92, 0xa0, 0x38, 0x51, 0x1a, 0xf3, 0x47, - 0x23, 0x30, 0xdb, 0xb3, 0x43, 0xa0, 0xc7, 0x21, 0xd7, 0xde, 0xb6, 0x7c, 0xb9, 0xe4, 0xcf, 0xc9, - 0xf9, 0x56, 0xa1, 0x85, 0xb7, 0xbb, 0xc5, 0x49, 0x59, 0x85, 0x15, 0x60, 0x8e, 0x4c, 0x75, 0x6a, - 0x8b, 0xf8, 0xbe, 0xd5, 0x90, 0xfb, 0x80, 0x36, 0x4d, 0x58, 0x31, 0x96, 0x70, 0xf4, 0xeb, 0x06, - 0x4c, 0xf2, 0x29, 0x83, 0x89, 0xdf, 0x69, 0x06, 0x74, 0xaf, 0xa3, 0xdd, 0x72, 0x29, 0x8d, 0xe9, - 0xc9, 0x49, 0x96, 0x4f, 0x0b, 0xee, 0x93, 0x7a, 0xa9, 0x8f, 0xa3, 0x7c, 0xd1, 0x0d, 0x28, 0xf8, - 0x81, 0xe5, 0x05, 0xa4, 0x5e, 0x0a, 0x98, 0x56, 0x1b, 0x7f, 0xf4, 0x7f, 0x0f, 0xb6, 0x09, 0x6c, - 0xd8, 0x2d, 0xc2, 0x37, 0x9c, 0xaa, 0x24, 0x80, 0x43, 0x5a, 0xe8, 0x25, 0x00, 0xaf, 0xe3, 0x54, - 0x3b, 0xad, 0x96, 0xe5, 0xed, 0x0b, 0x0d, 0x7e, 0x71, 0xb8, 0xe6, 0x61, 0x45, 0x2f, 0xd4, 0x59, - 0x61, 0x19, 0xd6, 0xf8, 0xa1, 0x57, 0x0c, 0x98, 0xe4, 0x33, 0x51, 0x4a, 0x30, 0x9a, 0xb2, 0x04, - 0xb3, 0xb4, 0x6b, 0x57, 0x74, 0x16, 0x38, 0xca, 0xd1, 0xfc, 0xbb, 0xa8, 0x3e, 0xa9, 0x06, 0xf4, - 0x74, 0xdd, 0xd8, 0x47, 0x1f, 0x84, 0x7b, 0xfd, 0x4e, 0xad, 0x46, 0x7c, 0x7f, 0xab, 0xd3, 0xc4, - 0x1d, 0xe7, 0xa2, 0xed, 0x07, 0xae, 0xb7, 0xbf, 0x66, 0xb7, 0xec, 0x80, 0xcd, 0xb8, 0x5c, 0xf9, - 0xec, 0x41, 0xb7, 0x78, 0x6f, 0xb5, 0x1f, 0x12, 0xee, 0x5f, 0x1f, 0x59, 0x70, 0x5f, 0xc7, 0xe9, - 0x4f, 0x9e, 0x9f, 0xde, 0x8a, 0x07, 0xdd, 0xe2, 0x7d, 0xd7, 0xfa, 0xa3, 0xe1, 0xc3, 0x68, 0x98, - 0xff, 0x62, 0xc0, 0x8c, 0x6c, 0xd7, 0x06, 0x69, 0xb5, 0x9b, 0x74, 0x77, 0x39, 0xf9, 0x83, 0x48, - 0x10, 0x39, 0x88, 0xe0, 0x74, 0xd4, 0x89, 0x94, 0xbf, 0xdf, 0x69, 0xc4, 0xfc, 0x67, 0x03, 0xe6, - 0xe2, 0xc8, 0x77, 0x41, 0x79, 0xfa, 0x51, 0xe5, 0x79, 0x39, 0xdd, 0xd6, 0xf6, 0xd1, 0xa0, 0xaf, - 0x8d, 0xf4, 0xb6, 0xf5, 0xbf, 0xbb, 0x1a, 0x0d, 0xb5, 0x62, 0xf6, 0x97, 0xa9, 0x15, 0x47, 0xde, - 0x52, 0x5a, 0xf1, 0x8f, 0x46, 0x60, 0xa2, 0xe4, 0x04, 0x76, 0x69, 0x6b, 0xcb, 0x76, 0xec, 0x60, - 0x1f, 0x7d, 0x36, 0x03, 0x4b, 0x6d, 0x8f, 0x6c, 0x11, 0xcf, 0x23, 0xf5, 0x95, 0x8e, 0x67, 0x3b, - 0x8d, 0x6a, 0x6d, 0x9b, 0xd4, 0x3b, 0x4d, 0xdb, 0x69, 0xac, 0x36, 0x1c, 0x57, 0x15, 0x9f, 0xbf, - 0x45, 0x6a, 0x1d, 0xd6, 0x24, 0xbe, 0x28, 0x5a, 0xc3, 0x35, 0xa9, 0x72, 0x34, 0xa6, 0xe5, 0xc7, - 0x0e, 0xba, 0xc5, 0xa5, 0x23, 0x56, 0xc2, 0x47, 0x6d, 0x1a, 0xfa, 0x4c, 0x06, 0x16, 0x3d, 0xf2, - 0xb1, 0x8e, 0x3d, 0x78, 0x6f, 0xf0, 0x5d, 0xab, 0x39, 0xa4, 0xfa, 0x39, 0x12, 0xcf, 0xf2, 0xa3, - 0x07, 0xdd, 0xe2, 0x11, 0xeb, 0xe0, 0x23, 0xb6, 0xcb, 0xac, 0xc0, 0x78, 0xa9, 0x6d, 0xfb, 0xf6, - 0x2d, 0x7a, 0x97, 0x25, 0x03, 0xdc, 0x95, 0x8a, 0x90, 0xf3, 0x3a, 0x4d, 0xc2, 0xd7, 0x76, 0xa1, - 0x5c, 0xa0, 0xbb, 0x10, 0xa6, 0x05, 0x98, 0x97, 0x9b, 0x9f, 0xa2, 0x3b, 0x2e, 0x23, 0x19, 0xbb, - 0x25, 0xdf, 0x84, 0x9c, 0x47, 0x99, 0x88, 0x99, 0x35, 0xec, 0x85, 0x22, 0x94, 0x5a, 0x08, 0x41, - 0x7f, 0x62, 0xce, 0xc2, 0xfc, 0x46, 0x06, 0x4e, 0x97, 0xda, 0xed, 0x75, 0xe2, 0x6f, 0xc7, 0xa4, - 0xf8, 0xbc, 0x01, 0x53, 0xbb, 0xb6, 0x17, 0x74, 0xac, 0xa6, 0xb4, 0x6d, 0x70, 0x79, 0xaa, 0xc3, - 0xca, 0xc3, 0xb8, 0x5d, 0x8f, 0x90, 0x2e, 0xa3, 0x83, 0x6e, 0x71, 0x2a, 0x5a, 0x86, 0x63, 0xec, - 0xd1, 0xef, 0x18, 0x30, 0x23, 0x8a, 0x2e, 0xbb, 0x75, 0xa2, 0x1b, 0xc4, 0xae, 0xa5, 0x29, 0x93, - 0x22, 0xce, 0x2d, 0x27, 0xf1, 0x52, 0xdc, 0x23, 0x84, 0xf9, 0x6f, 0x19, 0x38, 0xd3, 0x87, 0x06, - 0xfa, 0x43, 0x03, 0xe6, 0xb8, 0x15, 0x4d, 0x03, 0x61, 0xb2, 0x25, 0x7a, 0xf3, 0x03, 0x69, 0x4b, - 0x8e, 0xe9, 0x12, 0x27, 0x4e, 0x8d, 0x94, 0xe7, 0xe9, 0x6e, 0xb8, 0x9c, 0xc0, 0x1a, 0x27, 0x0a, - 0xc4, 0x24, 0xe5, 0x76, 0xb5, 0x98, 0xa4, 0x99, 0xbb, 0x22, 0x69, 0x35, 0x81, 0x35, 0x4e, 0x14, - 0xc8, 0xfc, 0xff, 0x70, 0xdf, 0x21, 0xe4, 0xee, 0xbc, 0x38, 0xcd, 0x17, 0xd4, 0xac, 0x8f, 0xce, - 0xb9, 0x01, 0xd6, 0xb5, 0x09, 0xa3, 0x6c, 0xe9, 0xc8, 0x85, 0x0d, 0x54, 0xfd, 0xb1, 0x35, 0xe5, - 0x63, 0x01, 0x31, 0xbf, 0x61, 0x40, 0xfe, 0x08, 0x66, 0x95, 0x62, 0xd4, 0xac, 0x52, 0xe8, 0x31, - 0xa9, 0x04, 0xbd, 0x26, 0x95, 0x67, 0x87, 0x1b, 0x8d, 0x41, 0x4c, 0x29, 0x3f, 0x33, 0x60, 0xb6, - 0xc7, 0xf4, 0x82, 0xb6, 0x61, 0xae, 0xed, 0xd6, 0xe5, 0xb1, 0xe9, 0xa2, 0xe5, 0x6f, 0x33, 0x98, - 0x68, 0xde, 0xe3, 0x74, 0x24, 0x2b, 0x09, 0xf0, 0xdb, 0xdd, 0xe2, 0xbc, 0x22, 0x12, 0x43, 0xc0, - 0x89, 0x14, 0x51, 0x1b, 0xf2, 0x5b, 0x36, 0x69, 0xd6, 0xc3, 0x29, 0x38, 0xe4, 0x01, 0xe9, 0x82, - 0xa0, 0xc6, 0xad, 0x8e, 0xf2, 0x1f, 0x56, 0x5c, 0xcc, 0xab, 0x30, 0x15, 0xb5, 0x41, 0x0f, 0x30, - 0x78, 0x67, 0x21, 0x6b, 0x79, 0x8e, 0x18, 0xba, 0x71, 0x81, 0x90, 0x2d, 0xe1, 0xcb, 0x98, 0x96, - 0x9b, 0xbf, 0x18, 0x81, 0xe9, 0x72, 0xb3, 0x43, 0x9e, 0xf5, 0x08, 0x91, 0xd7, 0xee, 0x12, 0x4c, - 0xb7, 0x3d, 0xb2, 0x6b, 0x93, 0xbd, 0x2a, 0x69, 0x92, 0x5a, 0xe0, 0x7a, 0x82, 0xfe, 0x19, 0x51, - 0x7d, 0xba, 0x12, 0x05, 0xe3, 0x38, 0x3e, 0x7a, 0x06, 0xa6, 0xac, 0x5a, 0x60, 0xef, 0x12, 0x45, - 0x81, 0x0b, 0xf0, 0x36, 0x41, 0x61, 0xaa, 0x14, 0x81, 0xe2, 0x18, 0x36, 0xfa, 0x10, 0xcc, 0xfb, - 0x35, 0xab, 0x49, 0xae, 0xb5, 0x05, 0xab, 0xe5, 0x6d, 0x52, 0xdb, 0xa9, 0xb8, 0xb6, 0x13, 0x08, - 0x23, 0xcb, 0x03, 0x82, 0xd2, 0x7c, 0xb5, 0x0f, 0x1e, 0xee, 0x4b, 0x01, 0xfd, 0x85, 0x01, 0x67, - 0xdb, 0x1e, 0xa9, 0x78, 0x6e, 0xcb, 0xa5, 0xda, 0xb3, 0xc7, 0xf2, 0x20, 0x6e, 0xe0, 0xd7, 0x87, - 0x3c, 0x26, 0xf0, 0x92, 0x5e, 0xcb, 0xe7, 0xdb, 0x0f, 0xba, 0xc5, 0xb3, 0x95, 0xc3, 0x04, 0xc0, - 0x87, 0xcb, 0x87, 0xfe, 0xca, 0x80, 0x73, 0x6d, 0xd7, 0x0f, 0x0e, 0x69, 0x42, 0xee, 0x44, 0x9b, - 0x60, 0x1e, 0x74, 0x8b, 0xe7, 0x2a, 0x87, 0x4a, 0x80, 0xef, 0x20, 0xa1, 0x79, 0x30, 0x0e, 0xb3, - 0xda, 0xdc, 0x13, 0xd7, 0xf2, 0xa7, 0x60, 0x52, 0x4e, 0x86, 0x50, 0xad, 0x17, 0x42, 0x33, 0x4a, - 0x49, 0x07, 0xe2, 0x28, 0x2e, 0x9d, 0x77, 0x6a, 0x2a, 0xf2, 0xda, 0xb1, 0x79, 0x57, 0x89, 0x40, - 0x71, 0x0c, 0x1b, 0xad, 0xc2, 0x29, 0x51, 0x82, 0x49, 0xbb, 0x69, 0xd7, 0xac, 0x65, 0xb7, 0x23, - 0xa6, 0x5c, 0xae, 0x7c, 0xe6, 0xa0, 0x5b, 0x3c, 0x55, 0xe9, 0x05, 0xe3, 0xa4, 0x3a, 0x68, 0x0d, - 0xe6, 0xac, 0x4e, 0xe0, 0xaa, 0xf6, 0x9f, 0x77, 0xa8, 0xa6, 0xa8, 0xb3, 0xa9, 0x95, 0xe7, 0x2a, - 0xa5, 0x94, 0x00, 0xc7, 0x89, 0xb5, 0x50, 0x25, 0x46, 0xad, 0x4a, 0x6a, 0xae, 0x53, 0xe7, 0xa3, - 0x9c, 0x0b, 0x2f, 0x17, 0xa5, 0x04, 0x1c, 0x9c, 0x58, 0x13, 0x35, 0x61, 0xaa, 0x65, 0xdd, 0xba, - 0xe6, 0x58, 0xbb, 0x96, 0xdd, 0xa4, 0x4c, 0x84, 0x69, 0xa6, 0xbf, 0xbd, 0xa0, 0x13, 0xd8, 0xcd, - 0x45, 0xfe, 0x4a, 0xb9, 0xb8, 0xea, 0x04, 0x57, 0xbc, 0x6a, 0x40, 0x0f, 0xa1, 0xfc, 0x70, 0xb4, - 0x1e, 0xa1, 0x85, 0x63, 0xb4, 0xd1, 0x15, 0x38, 0xcd, 0x96, 0xe3, 0x8a, 0xbb, 0xe7, 0xac, 0x90, - 0xa6, 0xb5, 0x2f, 0x1b, 0x30, 0xc6, 0x1a, 0x70, 0xef, 0x41, 0xb7, 0x78, 0xba, 0x9a, 0x84, 0x80, - 0x93, 0xeb, 0x21, 0x0b, 0xee, 0x8b, 0x02, 0x30, 0xd9, 0xb5, 0x7d, 0xdb, 0x75, 0xb8, 0x81, 0x25, - 0x1f, 0x1a, 0x58, 0xaa, 0xfd, 0xd1, 0xf0, 0x61, 0x34, 0xd0, 0xef, 0x19, 0x30, 0x97, 0xb4, 0x0c, - 0xe7, 0x0b, 0x69, 0xbc, 0xc1, 0xc4, 0x96, 0x16, 0x9f, 0x11, 0x89, 0x9b, 0x42, 0xa2, 0x10, 0xe8, - 0x65, 0x03, 0x26, 0x2c, 0xed, 0x72, 0x38, 0x0f, 0x4c, 0xaa, 0x4b, 0xc3, 0x9a, 0x28, 0x42, 0x8a, - 0xe5, 0x99, 0x83, 0x6e, 0x31, 0x72, 0x01, 0xc5, 0x11, 0x8e, 0xe8, 0xf7, 0x0d, 0x38, 0x9d, 0xb8, - 0xc6, 0xe7, 0xc7, 0x4f, 0xa2, 0x87, 0xd8, 0x24, 0x49, 0xde, 0x73, 0x92, 0xc5, 0x40, 0xaf, 0x1b, - 0x4a, 0x95, 0xad, 0x4b, 0x23, 0xd1, 0x04, 0x13, 0xed, 0xea, 0x90, 0xf7, 0xe1, 0xf0, 0x40, 0x20, - 0x09, 0x97, 0x4f, 0x69, 0x9a, 0x51, 0x16, 0xe2, 0x38, 0x7b, 0xf4, 0x39, 0x43, 0xaa, 0x46, 0x25, - 0xd1, 0xe4, 0x49, 0x49, 0x84, 0x42, 0x4d, 0xab, 0x04, 0x8a, 0x31, 0x47, 0x1f, 0x86, 0x05, 0x6b, - 0xd3, 0xf5, 0x82, 0xc4, 0xc5, 0x37, 0x3f, 0xc5, 0x96, 0xd1, 0xb9, 0x83, 0x6e, 0x71, 0xa1, 0xd4, - 0x17, 0x0b, 0x1f, 0x42, 0xc1, 0xfc, 0x6a, 0x0e, 0x26, 0xf8, 0x21, 0x5f, 0xa8, 0xae, 0xaf, 0x1b, - 0x70, 0x7f, 0xad, 0xe3, 0x79, 0xc4, 0x09, 0xaa, 0x01, 0x69, 0xf7, 0x2a, 0x2e, 0xe3, 0x44, 0x15, - 0xd7, 0x03, 0x07, 0xdd, 0xe2, 0xfd, 0xcb, 0x87, 0xf0, 0xc7, 0x87, 0x4a, 0x87, 0xfe, 0xd6, 0x00, - 0x53, 0x20, 0x94, 0xad, 0xda, 0x4e, 0xc3, 0x73, 0x3b, 0x4e, 0xbd, 0xb7, 0x11, 0x99, 0x13, 0x6d, - 0xc4, 0x43, 0x07, 0xdd, 0xa2, 0xb9, 0x7c, 0x47, 0x29, 0xf0, 0x00, 0x92, 0xa2, 0x67, 0x61, 0x56, - 0x60, 0x9d, 0xbf, 0xd5, 0x26, 0x9e, 0x4d, 0x8f, 0xd3, 0xc2, 0x4d, 0x20, 0xf4, 0xbc, 0x88, 0x23, - 0xe0, 0xde, 0x3a, 0xc8, 0x87, 0xb1, 0x3d, 0x62, 0x37, 0xb6, 0x03, 0x79, 0x7c, 0x1a, 0xd2, 0xdd, - 0x42, 0x5c, 0xf8, 0x6f, 0x70, 0x9a, 0xe5, 0xf1, 0x83, 0x6e, 0x71, 0x4c, 0xfc, 0xc1, 0x92, 0x13, - 0xba, 0x0c, 0x53, 0xfc, 0x0a, 0x56, 0xb1, 0x9d, 0x46, 0xc5, 0x75, 0xb8, 0x93, 0x42, 0xa1, 0xfc, - 0x90, 0x54, 0xf8, 0xd5, 0x08, 0xf4, 0x76, 0xb7, 0x38, 0x21, 0x7f, 0x6f, 0xec, 0xb7, 0x09, 0x8e, - 0xd5, 0x36, 0xbf, 0x3d, 0x0a, 0x20, 0xa7, 0x2b, 0x69, 0xa3, 0xff, 0x03, 0x05, 0x9f, 0x04, 0x9c, - 0xab, 0x78, 0x13, 0xe0, 0x4f, 0x2d, 0xb2, 0x10, 0x87, 0x70, 0xb4, 0x03, 0xb9, 0xb6, 0xd5, 0xf1, - 0x89, 0x18, 0xfc, 0x4b, 0xa9, 0x0c, 0x7e, 0x85, 0x52, 0xe4, 0x77, 0x2e, 0xf6, 0x13, 0x73, 0x1e, - 0xe8, 0xd3, 0x06, 0x00, 0x89, 0x0e, 0xd8, 0xd0, 0xb6, 0x0f, 0xc1, 0x32, 0x1c, 0x53, 0xda, 0x07, - 0xe5, 0xa9, 0x83, 0x6e, 0x11, 0xb4, 0xa1, 0xd7, 0xd8, 0xa2, 0x3d, 0xc8, 0x5b, 0x72, 0xcf, 0x1f, - 0x39, 0x89, 0x3d, 0x9f, 0x5d, 0x85, 0xd4, 0xa4, 0x55, 0xcc, 0xd0, 0x67, 0x0c, 0x98, 0xf2, 0x49, - 0x20, 0x86, 0x8a, 0xee, 0x3c, 0xe2, 0xc0, 0x3b, 0xe4, 0xa4, 0xab, 0x46, 0x68, 0xf2, 0x1d, 0x34, - 0x5a, 0x86, 0x63, 0x7c, 0xa5, 0x28, 0x17, 0x89, 0x55, 0x27, 0x1e, 0xbb, 0x69, 0x8b, 0x93, 0xd4, - 0xf0, 0xa2, 0x68, 0x34, 0x95, 0x28, 0x5a, 0x19, 0x8e, 0xf1, 0x95, 0xa2, 0xac, 0xdb, 0x9e, 0xe7, - 0x0a, 0x51, 0xf2, 0x29, 0x89, 0xa2, 0xd1, 0x54, 0xa2, 0x68, 0x65, 0x38, 0xc6, 0xd7, 0xfc, 0xf2, - 0x24, 0x4c, 0xc9, 0x85, 0x14, 0x9e, 0xec, 0xb9, 0x61, 0xa7, 0xcf, 0xc9, 0x7e, 0x59, 0x07, 0xe2, - 0x28, 0x2e, 0xad, 0xcc, 0x97, 0x6a, 0xf4, 0x60, 0xaf, 0x2a, 0x57, 0x75, 0x20, 0x8e, 0xe2, 0xa2, - 0x16, 0xe4, 0xfc, 0x80, 0xb4, 0xe5, 0xf3, 0xee, 0x90, 0xaf, 0x8f, 0xe1, 0xfe, 0x10, 0x3e, 0xe0, - 0xd0, 0x7f, 0x3e, 0xe6, 0x5c, 0x98, 0x6d, 0x32, 0x88, 0x98, 0x2b, 0xc5, 0xe2, 0x48, 0x67, 0x7d, - 0x46, 0x2d, 0xa1, 0x7c, 0x34, 0xa2, 0x65, 0x38, 0xc6, 0x3e, 0xe1, 0xb0, 0x9f, 0x3b, 0xc1, 0xc3, - 0xfe, 0xf3, 0x90, 0x6f, 0x59, 0xb7, 0xaa, 0x1d, 0xaf, 0x71, 0xfc, 0x4b, 0x85, 0xf0, 0xbc, 0xe2, - 0x54, 0xb0, 0xa2, 0x87, 0x5e, 0x31, 0xb4, 0x2d, 0x67, 0x8c, 0x11, 0xbf, 0x91, 0xee, 0x96, 0xa3, - 0x74, 0x65, 0xdf, 0xcd, 0xa7, 0xe7, 0xe8, 0x9d, 0xbf, 0xeb, 0x47, 0x6f, 0x7a, 0x8c, 0xe4, 0x0b, - 0x44, 0x1d, 0x23, 0x0b, 0x27, 0x7a, 0x8c, 0x5c, 0x8e, 0x30, 0xc3, 0x31, 0xe6, 0x4c, 0x1e, 0xbe, - 0xe6, 0x94, 0x3c, 0x70, 0xa2, 0xf2, 0x54, 0x23, 0xcc, 0x70, 0x8c, 0x79, 0xff, 0xfb, 0xe6, 0xf8, - 0xc9, 0xdc, 0x37, 0x27, 0x52, 0xb8, 0x6f, 0x1e, 0x7e, 0x14, 0x9f, 0x1c, 0xf6, 0x28, 0x8e, 0x2e, - 0x01, 0xaa, 0xef, 0x3b, 0x56, 0xcb, 0xae, 0x89, 0xcd, 0x92, 0xa9, 0xcd, 0x29, 0x66, 0x8f, 0x58, - 0x10, 0x1b, 0x19, 0x5a, 0xe9, 0xc1, 0xc0, 0x09, 0xb5, 0x50, 0x00, 0xf9, 0xb6, 0x3c, 0x71, 0x4d, - 0xa7, 0x31, 0xfb, 0xe5, 0x09, 0x8c, 0x7b, 0x00, 0xd0, 0x85, 0x27, 0x4b, 0xb0, 0xe2, 0x84, 0xd6, - 0x60, 0xae, 0x65, 0x3b, 0x15, 0xb7, 0xee, 0x57, 0x88, 0x27, 0xac, 0x2d, 0x55, 0x12, 0xcc, 0xcf, - 0xb0, 0xbe, 0x61, 0x37, 0xe8, 0xf5, 0x04, 0x38, 0x4e, 0xac, 0x65, 0xfe, 0x87, 0x01, 0x33, 0xcb, - 0x4d, 0xb7, 0x53, 0xbf, 0x61, 0x05, 0xb5, 0x6d, 0xfe, 0xf8, 0x8d, 0x9e, 0x81, 0xbc, 0xed, 0x04, - 0xc4, 0xdb, 0xb5, 0x9a, 0x42, 0x3f, 0x99, 0xd2, 0x3f, 0x60, 0x55, 0x94, 0xdf, 0xee, 0x16, 0xa7, - 0x56, 0x3a, 0x1e, 0xf3, 0x2a, 0xe5, 0xbb, 0x15, 0x56, 0x75, 0xd0, 0x97, 0x0d, 0x98, 0xe5, 0xcf, - 0xe7, 0x2b, 0x56, 0x60, 0x5d, 0xed, 0x10, 0xcf, 0x26, 0xf2, 0x01, 0x7d, 0xc8, 0x8d, 0x2a, 0x2e, - 0xab, 0x64, 0xb0, 0x1f, 0x1e, 0xd4, 0xd7, 0xe3, 0x9c, 0x71, 0xaf, 0x30, 0xe6, 0x17, 0xb2, 0x70, - 0x6f, 0x5f, 0x5a, 0x68, 0x01, 0x32, 0x76, 0x5d, 0x34, 0x1d, 0x04, 0xdd, 0xcc, 0x6a, 0x1d, 0x67, - 0xec, 0x3a, 0x5a, 0x64, 0x67, 0x4e, 0x8f, 0xf8, 0xbe, 0x7c, 0x4b, 0x2d, 0xa8, 0xe3, 0xa1, 0x28, - 0xc5, 0x1a, 0x06, 0x2a, 0x42, 0xae, 0x69, 0x6d, 0x92, 0xa6, 0xb8, 0x4f, 0xb0, 0x53, 0xec, 0x1a, - 0x2d, 0xc0, 0xbc, 0x1c, 0x7d, 0xca, 0x00, 0xe0, 0x02, 0xd2, 0xdb, 0x88, 0xd0, 0x92, 0x38, 0xdd, - 0x6e, 0xa2, 0x94, 0xb9, 0x94, 0xe1, 0x7f, 0xac, 0x71, 0x45, 0x1b, 0x30, 0x4a, 0x0f, 0xb4, 0x6e, - 0xfd, 0xd8, 0x4a, 0x91, 0x3d, 0xb2, 0x54, 0x18, 0x0d, 0x2c, 0x68, 0xd1, 0xbe, 0xf2, 0x48, 0xd0, - 0xf1, 0x1c, 0xda, 0xb5, 0x4c, 0x0d, 0xe6, 0xb9, 0x14, 0x58, 0x95, 0x62, 0x0d, 0xc3, 0xfc, 0xf3, - 0x0c, 0xcc, 0x25, 0x89, 0x4e, 0xb5, 0xcd, 0x28, 0x97, 0x56, 0x5c, 0x8d, 0xdf, 0x9f, 0x7e, 0xff, - 0x08, 0x4f, 0x10, 0xe5, 0x2f, 0x21, 0x7c, 0xd5, 0x04, 0x5f, 0xf4, 0x7e, 0xd5, 0x43, 0x99, 0x63, - 0xf6, 0x90, 0xa2, 0x1c, 0xeb, 0xa5, 0x07, 0x60, 0xc4, 0xa7, 0x23, 0x9f, 0x8d, 0x3e, 0x60, 0xb0, - 0x31, 0x62, 0x10, 0x8a, 0xd1, 0x71, 0xec, 0x40, 0xb8, 0x7a, 0x2b, 0x8c, 0x6b, 0x8e, 0x1d, 0x60, - 0x06, 0x31, 0xbf, 0x94, 0x81, 0x85, 0xfe, 0x8d, 0x42, 0x5f, 0x32, 0x00, 0xea, 0xf4, 0xba, 0x42, - 0xa7, 0xa4, 0xf4, 0x9c, 0xb1, 0x4e, 0xaa, 0x0f, 0x57, 0x24, 0xa7, 0xd0, 0x8d, 0x4a, 0x15, 0xf9, - 0x58, 0x13, 0x04, 0x3d, 0x2a, 0xa7, 0xfe, 0x65, 0xab, 0x25, 0x8f, 0xb3, 0xaa, 0xce, 0xba, 0x82, - 0x60, 0x0d, 0x8b, 0xde, 0x47, 0x1d, 0xab, 0x45, 0xfc, 0xb6, 0xa5, 0x7c, 0xf9, 0xd9, 0x7d, 0xf4, - 0xb2, 0x2c, 0xc4, 0x21, 0xdc, 0x6c, 0xc2, 0x83, 0x03, 0xc8, 0x99, 0x92, 0x5f, 0xb5, 0xf9, 0xef, - 0x06, 0x9c, 0x59, 0x6e, 0x76, 0xfc, 0x80, 0x78, 0xff, 0x63, 0xbc, 0xd2, 0xfe, 0xd3, 0x80, 0xfb, - 0xfa, 0xb4, 0xf9, 0x2e, 0x38, 0xa7, 0xbd, 0x18, 0x75, 0x4e, 0xbb, 0x36, 0xec, 0x94, 0x4e, 0x6c, - 0x47, 0x1f, 0x1f, 0xb5, 0x00, 0x26, 0xe9, 0xae, 0x55, 0x77, 0x1b, 0x29, 0xe9, 0xcd, 0x07, 0x21, - 0xf7, 0x31, 0xaa, 0x7f, 0xe2, 0x73, 0x8c, 0x29, 0x25, 0xcc, 0x61, 0xe6, 0xd3, 0x20, 0x3c, 0xb9, - 0x62, 0x8b, 0xc7, 0x18, 0x64, 0xf1, 0x98, 0x7f, 0x9f, 0x01, 0xcd, 0x8e, 0x71, 0x17, 0x26, 0xa5, - 0x13, 0x99, 0x94, 0x43, 0xde, 0xc1, 0x35, 0xab, 0x4c, 0xbf, 0x90, 0x8d, 0xdd, 0x58, 0xc8, 0xc6, - 0xe5, 0xd4, 0x38, 0x1e, 0x1e, 0xb1, 0xf1, 0x03, 0x03, 0xee, 0x0b, 0x91, 0x7b, 0x4d, 0x8c, 0x77, - 0xde, 0x61, 0x9e, 0x80, 0x71, 0x2b, 0xac, 0x26, 0xe6, 0x80, 0x8a, 0x52, 0xd2, 0x28, 0x62, 0x1d, - 0x2f, 0x74, 0x10, 0xcf, 0x1e, 0xd3, 0x41, 0x7c, 0xe4, 0x70, 0x07, 0x71, 0xf3, 0xe7, 0x19, 0x38, - 0xdb, 0xdb, 0x32, 0xb9, 0x36, 0x06, 0x7b, 0x81, 0x7f, 0x12, 0x26, 0x02, 0x51, 0x41, 0xdb, 0xe9, - 0x55, 0x8c, 0xdd, 0x86, 0x06, 0xc3, 0x11, 0x4c, 0x5a, 0xb3, 0xc6, 0x57, 0x65, 0xb5, 0xe6, 0xb6, - 0x65, 0x78, 0x81, 0xaa, 0xb9, 0xac, 0xc1, 0x70, 0x04, 0x53, 0x39, 0x6e, 0x8e, 0x9c, 0xb8, 0xe3, - 0x66, 0x15, 0x4e, 0x4b, 0x57, 0xb5, 0x0b, 0xae, 0xb7, 0xec, 0xb6, 0xda, 0x4d, 0x22, 0x02, 0x0c, - 0xa8, 0xb0, 0x67, 0x45, 0x95, 0xd3, 0x38, 0x09, 0x09, 0x27, 0xd7, 0x35, 0x7f, 0x90, 0x85, 0x53, - 0x61, 0xb7, 0x2f, 0xbb, 0x4e, 0xdd, 0x66, 0x0e, 0x7f, 0x4f, 0xc1, 0x48, 0xb0, 0xdf, 0x96, 0x9d, - 0xfd, 0xbf, 0xa4, 0x38, 0x1b, 0xfb, 0x6d, 0x3a, 0xda, 0x67, 0x12, 0xaa, 0x30, 0x23, 0x2f, 0xab, - 0x84, 0xd6, 0xd4, 0xea, 0xe0, 0x23, 0xf0, 0x78, 0x74, 0x36, 0xdf, 0xee, 0x16, 0x13, 0x42, 0x4c, - 0x17, 0x15, 0xa5, 0xe8, 0x9c, 0x47, 0x37, 0x61, 0xaa, 0x69, 0xf9, 0xc1, 0xb5, 0x76, 0xdd, 0x0a, - 0xc8, 0x86, 0xdd, 0x22, 0x62, 0xcd, 0x1d, 0xc5, 0x6b, 0x5f, 0xbd, 0x4a, 0xaf, 0x45, 0x28, 0xe1, - 0x18, 0x65, 0xb4, 0x0b, 0x88, 0x96, 0x6c, 0x78, 0x96, 0xe3, 0xf3, 0x56, 0x51, 0x7e, 0x47, 0x8f, - 0x12, 0x50, 0x97, 0xbc, 0xb5, 0x1e, 0x6a, 0x38, 0x81, 0x03, 0x7a, 0x08, 0x46, 0x3d, 0x62, 0xf9, - 0x62, 0x30, 0x0b, 0xe1, 0xfa, 0xc7, 0xac, 0x14, 0x0b, 0xa8, 0xbe, 0xa0, 0x46, 0xef, 0xb0, 0xa0, - 0x7e, 0x62, 0xc0, 0x54, 0x38, 0x4c, 0x77, 0x41, 0x49, 0xb6, 0xa2, 0x4a, 0xf2, 0x62, 0x5a, 0x5b, - 0x62, 0x1f, 0xbd, 0xf8, 0xd7, 0xa3, 0x7a, 0xfb, 0x98, 0xd7, 0xf6, 0xc7, 0xa1, 0x20, 0x57, 0xb5, - 0x3c, 0x7d, 0x0e, 0x79, 0x57, 0x8e, 0x9c, 0x4b, 0xb4, 0x68, 0x23, 0xc1, 0x04, 0x87, 0xfc, 0xa8, - 0x5a, 0xae, 0x0b, 0x95, 0x2b, 0xa6, 0xbd, 0x52, 0xcb, 0x52, 0x15, 0x27, 0xa9, 0x65, 0x59, 0x07, - 0x5d, 0x83, 0x33, 0x6d, 0xcf, 0x65, 0x11, 0xa8, 0x2b, 0xc4, 0xaa, 0x37, 0x6d, 0x87, 0x48, 0x83, - 0x04, 0x77, 0x8a, 0xb8, 0xef, 0xa0, 0x5b, 0x3c, 0x53, 0x49, 0x46, 0xc1, 0xfd, 0xea, 0x46, 0xa3, - 0xa6, 0x46, 0x06, 0x88, 0x9a, 0xfa, 0x0d, 0x65, 0xf6, 0x23, 0xbe, 0x88, 0x5d, 0xfa, 0x60, 0x5a, - 0x43, 0x99, 0xb0, 0xad, 0x87, 0x53, 0xaa, 0x24, 0x98, 0x62, 0xc5, 0xbe, 0xbf, 0x6d, 0x69, 0xf4, - 0x98, 0xb6, 0xa5, 0xd0, 0xf9, 0x7d, 0xec, 0x97, 0xe9, 0xfc, 0x9e, 0x7f, 0x4b, 0x39, 0xbf, 0xbf, - 0x9a, 0x83, 0x99, 0xf8, 0x09, 0xe4, 0xe4, 0x23, 0xc2, 0x7e, 0xdb, 0x80, 0x19, 0xb9, 0x7a, 0x38, - 0x4f, 0x22, 0x5f, 0x0d, 0xd6, 0x52, 0x5a, 0xb4, 0xfc, 0x2c, 0xa5, 0x62, 0x96, 0x37, 0x62, 0xdc, - 0x70, 0x0f, 0x7f, 0xf4, 0x02, 0x8c, 0x2b, 0xe3, 0xfa, 0xb1, 0xc2, 0xc3, 0xa6, 0xd9, 0x29, 0x2a, - 0x24, 0x81, 0x75, 0x7a, 0xe8, 0x55, 0x03, 0xa0, 0x26, 0xd5, 0x9c, 0x5c, 0x5d, 0x57, 0xd3, 0x5a, - 0x5d, 0x4a, 0x81, 0x86, 0x87, 0x65, 0x55, 0xe4, 0x63, 0x8d, 0x31, 0xfa, 0x02, 0x33, 0xab, 0xab, - 0xd3, 0x1d, 0x5d, 0x4f, 0xd9, 0xe1, 0x1d, 0x7b, 0x0f, 0x39, 0x98, 0x86, 0x47, 0x29, 0x0d, 0xe4, - 0xe3, 0x88, 0x10, 0xe6, 0x53, 0xa0, 0x5c, 0x31, 0xe9, 0xb6, 0xc5, 0x9c, 0x31, 0x2b, 0x56, 0xb0, - 0x2d, 0xa6, 0xa0, 0xda, 0xb6, 0x2e, 0x48, 0x00, 0x0e, 0x71, 0xcc, 0x8f, 0xc2, 0xd4, 0xb3, 0x9e, - 0xd5, 0xde, 0xb6, 0x99, 0xf9, 0x9a, 0xde, 0x93, 0xde, 0x09, 0x63, 0x56, 0xbd, 0x9e, 0x14, 0xf1, - 0x5f, 0xe2, 0xc5, 0x58, 0xc2, 0x07, 0xbb, 0x12, 0x7d, 0xdb, 0x00, 0x14, 0x3e, 0x01, 0xda, 0x4e, - 0x63, 0x9d, 0xde, 0xf6, 0xe9, 0xfd, 0x68, 0x9b, 0x95, 0x26, 0xdd, 0x8f, 0x2e, 0x2a, 0x08, 0xd6, - 0xb0, 0xd0, 0x4b, 0x30, 0xce, 0xff, 0x5d, 0x57, 0x97, 0xfd, 0xa1, 0xdd, 0xfb, 0xb9, 0x42, 0x61, - 0x32, 0xf1, 0x59, 0x78, 0x31, 0xe4, 0x80, 0x75, 0x76, 0xb4, 0xab, 0x56, 0x9d, 0xad, 0x66, 0xe7, - 0x56, 0x7d, 0x33, 0xec, 0xaa, 0xb6, 0xe7, 0x6e, 0xd9, 0x4d, 0x12, 0xef, 0xaa, 0x0a, 0x2f, 0xc6, - 0x12, 0x3e, 0x58, 0x57, 0x7d, 0xd3, 0x80, 0xb9, 0x55, 0x3f, 0xb0, 0xdd, 0x15, 0xe2, 0x07, 0x54, - 0xad, 0xd0, 0xcd, 0xa7, 0xd3, 0x1c, 0xc4, 0xab, 0x7a, 0x05, 0x66, 0xc4, 0x73, 0x64, 0x67, 0xd3, - 0x27, 0x81, 0x76, 0x8e, 0x57, 0xeb, 0x78, 0x39, 0x06, 0xc7, 0x3d, 0x35, 0x28, 0x15, 0xf1, 0x2e, - 0x19, 0x52, 0xc9, 0x46, 0xa9, 0x54, 0x63, 0x70, 0xdc, 0x53, 0xc3, 0xfc, 0x5e, 0x16, 0x4e, 0xb1, - 0x66, 0xc4, 0x22, 0x22, 0x3e, 0xd7, 0x2f, 0x22, 0x62, 0xc8, 0xa5, 0xcc, 0x78, 0x1d, 0x23, 0x1e, - 0xe2, 0xb7, 0x0c, 0x98, 0xae, 0x47, 0x7b, 0x3a, 0x1d, 0xf3, 0x4c, 0xd2, 0x18, 0x72, 0xef, 0xab, - 0x58, 0x21, 0x8e, 0xf3, 0x47, 0x5f, 0x34, 0x60, 0x3a, 0x2a, 0xa6, 0xdc, 0xdd, 0x4f, 0xa0, 0x93, - 0x94, 0xbb, 0x74, 0xb4, 0xdc, 0xc7, 0x71, 0x11, 0xcc, 0xef, 0x66, 0xc4, 0x90, 0x9e, 0x84, 0xbb, - 0x3f, 0xda, 0x83, 0x42, 0xd0, 0xf4, 0x79, 0xa1, 0x68, 0xed, 0x90, 0x37, 0xc2, 0x8d, 0xb5, 0x2a, - 0xf7, 0x04, 0x08, 0x0f, 0x6d, 0xa2, 0x84, 0x1e, 0x3e, 0x25, 0x2f, 0xc6, 0xb8, 0xd6, 0x16, 0x8c, - 0x53, 0xb9, 0x8a, 0x6e, 0x2c, 0x57, 0xe2, 0x8c, 0x45, 0x09, 0x65, 0x2c, 0x79, 0x99, 0x5f, 0x31, - 0xa0, 0x70, 0xc9, 0x95, 0xfb, 0xc8, 0x87, 0x53, 0x30, 0xf4, 0xa8, 0xf3, 0xa0, 0x7a, 0x71, 0x0c, - 0xaf, 0x18, 0xcf, 0x44, 0xcc, 0x3c, 0xf7, 0x6b, 0xb4, 0x17, 0x59, 0x36, 0x23, 0x4a, 0xea, 0x92, - 0xbb, 0xd9, 0xd7, 0x8a, 0xf8, 0x07, 0x39, 0x98, 0x7c, 0xce, 0xda, 0x27, 0x4e, 0x60, 0x1d, 0x5d, - 0x49, 0x3c, 0x01, 0xe3, 0x56, 0x9b, 0x3d, 0x69, 0x69, 0x67, 0xfc, 0xd0, 0x72, 0x12, 0x82, 0xb0, - 0x8e, 0x17, 0x6e, 0x68, 0x3c, 0xb9, 0x4a, 0xd2, 0x56, 0xb4, 0x1c, 0x83, 0xe3, 0x9e, 0x1a, 0xe8, - 0x12, 0x20, 0x11, 0x2a, 0x5a, 0xaa, 0xd5, 0xdc, 0x8e, 0xc3, 0xb7, 0x34, 0x6e, 0x54, 0x51, 0x97, - 0xcd, 0xf5, 0x1e, 0x0c, 0x9c, 0x50, 0x0b, 0x7d, 0x08, 0xe6, 0x6b, 0x8c, 0xb2, 0xb8, 0x7a, 0xe8, - 0x14, 0xf9, 0xf5, 0x53, 0xb9, 0xfc, 0x2f, 0xf7, 0xc1, 0xc3, 0x7d, 0x29, 0x50, 0x49, 0xfd, 0xc0, - 0xf5, 0xac, 0x06, 0xd1, 0xe9, 0x8e, 0x46, 0x25, 0xad, 0xf6, 0x60, 0xe0, 0x84, 0x5a, 0xe8, 0x93, - 0x50, 0x08, 0xb6, 0x3d, 0xe2, 0x6f, 0xbb, 0xcd, 0xba, 0x70, 0x41, 0x18, 0xd2, 0xd2, 0x26, 0x46, - 0x7f, 0x43, 0x52, 0xd5, 0xa6, 0xb7, 0x2c, 0xc2, 0x21, 0x4f, 0xe4, 0xc1, 0xa8, 0x5f, 0x73, 0xdb, - 0xc4, 0x17, 0x47, 0xf6, 0x4b, 0xa9, 0x70, 0x67, 0x96, 0x23, 0xcd, 0xc6, 0xc7, 0x38, 0x60, 0xc1, - 0xc9, 0xfc, 0x56, 0x06, 0x26, 0x74, 0xc4, 0x01, 0xf6, 0xa6, 0x4f, 0x1b, 0x30, 0x51, 0x73, 0x9d, - 0xc0, 0x73, 0x9b, 0xdc, 0x7e, 0x95, 0xce, 0x89, 0x82, 0x92, 0x5a, 0x21, 0x81, 0x65, 0x37, 0x35, - 0x53, 0x98, 0xc6, 0x06, 0x47, 0x98, 0xa2, 0xcf, 0x1a, 0x30, 0x1d, 0x7a, 0xac, 0x85, 0x86, 0xb4, - 0x54, 0x05, 0x51, 0x5b, 0xfd, 0xf9, 0x28, 0x27, 0x1c, 0x67, 0x6d, 0x6e, 0xc2, 0x4c, 0x7c, 0xb4, - 0x69, 0x57, 0xb6, 0x2d, 0xb1, 0xd6, 0xb3, 0x61, 0x57, 0x56, 0x2c, 0xdf, 0xc7, 0x0c, 0x82, 0x1e, - 0x81, 0x7c, 0xcb, 0xf2, 0x1a, 0xb6, 0x63, 0x35, 0x59, 0x2f, 0x66, 0xb5, 0x0d, 0x49, 0x94, 0x63, - 0x85, 0x61, 0xbe, 0x1b, 0x26, 0xd6, 0x2d, 0xa7, 0x41, 0xea, 0x62, 0x1f, 0xbe, 0x73, 0xc0, 0xd9, - 0x4f, 0x47, 0x60, 0x5c, 0xbb, 0x9b, 0x9d, 0xfc, 0x3d, 0x2b, 0x92, 0xef, 0x22, 0x9b, 0x62, 0xbe, - 0x8b, 0xe7, 0x01, 0xb6, 0x6c, 0xc7, 0xf6, 0xb7, 0x8f, 0x99, 0x49, 0x83, 0x3d, 0xd1, 0x5e, 0x50, - 0x14, 0xb0, 0x46, 0x2d, 0x7c, 0x07, 0xcb, 0x1d, 0x92, 0x5f, 0xe8, 0x55, 0x43, 0x53, 0x37, 0xa3, - 0x69, 0xbc, 0xfb, 0x6b, 0x03, 0xb3, 0x28, 0xd5, 0xcf, 0x79, 0x27, 0xf0, 0xf6, 0x0f, 0xd5, 0x4a, - 0x1b, 0x90, 0xf7, 0x88, 0xdf, 0x69, 0xd1, 0x1b, 0xe3, 0xd8, 0x91, 0xbb, 0x81, 0x79, 0x60, 0x60, - 0x51, 0x1f, 0x2b, 0x4a, 0x0b, 0x4f, 0xc1, 0x64, 0x44, 0x04, 0x34, 0x03, 0xd9, 0x1d, 0xb2, 0xcf, - 0xe7, 0x09, 0xa6, 0x3f, 0xd1, 0x5c, 0xe4, 0xb5, 0x50, 0x74, 0xcb, 0xfb, 0x32, 0x4f, 0x1a, 0xa6, - 0x0b, 0x89, 0x06, 0x80, 0xe3, 0x3c, 0xe6, 0xd0, 0xb1, 0x68, 0x6a, 0xa9, 0x34, 0xd4, 0x58, 0x70, - 0x3f, 0x1b, 0x0e, 0x33, 0x7f, 0x3e, 0x0a, 0xe2, 0x29, 0x7b, 0x80, 0xed, 0x4a, 0x7f, 0xc1, 0xca, - 0x1c, 0xe3, 0x05, 0xeb, 0x12, 0x4c, 0xd8, 0x8e, 0x1d, 0xd8, 0x56, 0x93, 0x19, 0x77, 0x84, 0x3a, - 0x95, 0x8e, 0xc8, 0x13, 0xab, 0x1a, 0x2c, 0x81, 0x4e, 0xa4, 0x2e, 0xba, 0x0a, 0x39, 0xa6, 0x6f, - 0xc4, 0x04, 0x3e, 0xfa, 0x7b, 0x3b, 0x73, 0xb5, 0xe0, 0xd1, 0x49, 0x9c, 0x12, 0xbb, 0x7c, 0xf0, - 0x5c, 0x22, 0xea, 0xfa, 0x2d, 0xe6, 0x71, 0x78, 0xf9, 0x88, 0xc1, 0x71, 0x4f, 0x0d, 0x4a, 0x65, - 0xcb, 0xb2, 0x9b, 0x1d, 0x8f, 0x84, 0x54, 0x46, 0xa3, 0x54, 0x2e, 0xc4, 0xe0, 0xb8, 0xa7, 0x06, - 0xda, 0x82, 0x09, 0x51, 0xc6, 0xbd, 0xa7, 0xc6, 0x8e, 0xd9, 0x4a, 0xe6, 0x25, 0x77, 0x41, 0xa3, - 0x84, 0x23, 0x74, 0x51, 0x07, 0x66, 0x6d, 0xa7, 0xe6, 0x3a, 0xb5, 0x66, 0xc7, 0xb7, 0x77, 0x49, - 0x18, 0x1a, 0x74, 0x1c, 0x66, 0xa7, 0x0f, 0xba, 0xc5, 0xd9, 0xd5, 0x38, 0x39, 0xdc, 0xcb, 0x01, - 0xbd, 0x62, 0xc0, 0xe9, 0x9a, 0xeb, 0xf8, 0x2c, 0x38, 0x7f, 0x97, 0x9c, 0xf7, 0x3c, 0xd7, 0xe3, - 0xbc, 0x0b, 0xc7, 0xe4, 0xcd, 0x6c, 0x8a, 0xcb, 0x49, 0x24, 0x71, 0x32, 0x27, 0xf4, 0x22, 0xe4, - 0xdb, 0x9e, 0xbb, 0x6b, 0xd7, 0x89, 0x27, 0x3c, 0xf1, 0xd6, 0xd2, 0x48, 0x16, 0x52, 0x11, 0x34, - 0xc3, 0xad, 0x47, 0x96, 0x60, 0xc5, 0xcf, 0x7c, 0xa3, 0x00, 0x53, 0x51, 0x74, 0xf4, 0x09, 0x80, - 0xb6, 0xe7, 0xb6, 0x48, 0xb0, 0x4d, 0x54, 0x88, 0xc7, 0xe5, 0x61, 0x73, 0x52, 0x48, 0x7a, 0xd2, - 0x7b, 0x85, 0x6e, 0x17, 0x61, 0x29, 0xd6, 0x38, 0x22, 0x0f, 0xc6, 0x76, 0xb8, 0xda, 0x15, 0xa7, - 0x90, 0xe7, 0x52, 0x39, 0x33, 0x09, 0xce, 0x2c, 0x36, 0x41, 0x14, 0x61, 0xc9, 0x08, 0x6d, 0x42, - 0x76, 0x8f, 0x6c, 0xa6, 0x13, 0x10, 0x7d, 0x83, 0x88, 0xdb, 0x4c, 0x79, 0xec, 0xa0, 0x5b, 0xcc, - 0xde, 0x20, 0x9b, 0x98, 0x12, 0xa7, 0xed, 0xaa, 0xf3, 0x77, 0x78, 0xb1, 0x55, 0x0c, 0xd9, 0xae, - 0xc8, 0xa3, 0x3e, 0x6f, 0x97, 0x28, 0xc2, 0x92, 0x11, 0x7a, 0x11, 0x0a, 0x7b, 0xd6, 0x2e, 0xd9, - 0xf2, 0x5c, 0x27, 0x10, 0x2e, 0x53, 0x43, 0x7a, 0xfd, 0xdf, 0x90, 0xe4, 0x04, 0x5f, 0xa6, 0xde, - 0x55, 0x21, 0x0e, 0xd9, 0xa1, 0x5d, 0xc8, 0x3b, 0x64, 0x0f, 0x93, 0xa6, 0x5d, 0x4b, 0xc7, 0xcb, - 0xfe, 0xb2, 0xa0, 0x26, 0x38, 0x33, 0xbd, 0x27, 0xcb, 0xb0, 0xe2, 0x45, 0xc7, 0xf2, 0xa6, 0xbb, - 0x29, 0x36, 0xaa, 0x21, 0xc7, 0x52, 0xdd, 0x4c, 0xf9, 0x58, 0x5e, 0x72, 0x37, 0x31, 0x25, 0x4e, - 0xd7, 0x48, 0x4d, 0xf9, 0xeb, 0x88, 0x6d, 0xea, 0x72, 0xba, 0x7e, 0x4a, 0x7c, 0x8d, 0x84, 0xa5, - 0x58, 0xe3, 0x48, 0xfb, 0xb6, 0x21, 0x8c, 0x95, 0x62, 0xa3, 0x1a, 0xb2, 0x6f, 0xa3, 0xa6, 0x4f, - 0xde, 0xb7, 0xb2, 0x0c, 0x2b, 0x5e, 0x94, 0xaf, 0x2d, 0x2c, 0x7f, 0xe9, 0x6c, 0x55, 0x51, 0x3b, - 0x22, 0xe7, 0x2b, 0xcb, 0xb0, 0xe2, 0x65, 0x7e, 0x65, 0x14, 0x26, 0xf4, 0xa4, 0x6c, 0x03, 0x9c, - 0x11, 0xd4, 0xb9, 0x38, 0x73, 0x94, 0x73, 0x31, 0xbd, 0x08, 0x69, 0x6f, 0x1c, 0xd2, 0x08, 0xb3, - 0x9a, 0xda, 0xb1, 0x30, 0xbc, 0x08, 0x69, 0x85, 0x3e, 0x8e, 0x30, 0x3d, 0x82, 0xdb, 0x03, 0x3d, - 0x5c, 0xf1, 0xe3, 0x47, 0x2e, 0x7a, 0xb8, 0x8a, 0x1c, 0x28, 0x1e, 0x05, 0x08, 0x93, 0x93, 0x89, - 0xb7, 0x2f, 0x75, 0x6a, 0xd3, 0x92, 0xa6, 0x69, 0x58, 0xe8, 0x21, 0x18, 0xa5, 0x0a, 0x9a, 0xd4, - 0x45, 0xdc, 0xaf, 0xba, 0x6d, 0x5e, 0x60, 0xa5, 0x58, 0x40, 0xd1, 0x93, 0xf4, 0x2c, 0x15, 0xaa, - 0x55, 0x11, 0xce, 0x3b, 0x17, 0x9e, 0xa5, 0x42, 0x18, 0x8e, 0x60, 0x52, 0xd1, 0x09, 0xd5, 0x82, - 0x6c, 0x06, 0x6b, 0xa2, 0x33, 0xd5, 0x88, 0x39, 0x8c, 0x59, 0x3f, 0x62, 0x5a, 0x93, 0xcd, 0xbc, - 0x9c, 0x66, 0xfd, 0x88, 0xc1, 0x71, 0x4f, 0x0d, 0xda, 0x18, 0xf1, 0x6c, 0x37, 0xce, 0xbd, 0x3b, - 0xfb, 0x3c, 0xb8, 0xbd, 0xa6, 0xdf, 0x08, 0x26, 0xd8, 0xd0, 0xbf, 0x3f, 0xbd, 0x04, 0x83, 0x83, - 0x5f, 0x09, 0x86, 0x3b, 0xbc, 0x7f, 0x14, 0xa6, 0xa2, 0x7b, 0x65, 0xea, 0xf6, 0xf9, 0xbf, 0xc9, - 0xc2, 0xa9, 0xcb, 0x0d, 0xdb, 0x89, 0x27, 0x1c, 0x4a, 0x4a, 0xfc, 0x6b, 0x1c, 0x35, 0xf1, 0x6f, - 0x18, 0x40, 0x24, 0x32, 0x2b, 0x27, 0x07, 0x10, 0xc9, 0xb4, 0xcb, 0x51, 0x5c, 0xf4, 0x13, 0x03, - 0xee, 0xb7, 0xea, 0xfc, 0xf4, 0x6a, 0x35, 0x45, 0x69, 0xc8, 0x54, 0xae, 0x68, 0x7f, 0x48, 0x5d, - 0xd4, 0xdb, 0xf8, 0xc5, 0xd2, 0x21, 0x5c, 0xf9, 0x88, 0xbf, 0x43, 0xb4, 0xe0, 0xfe, 0xc3, 0x50, - 0xf1, 0xa1, 0xe2, 0x2f, 0x5c, 0x81, 0xb7, 0xdf, 0x91, 0xd1, 0x91, 0x66, 0xcb, 0xa7, 0x0d, 0x28, - 0x70, 0xf3, 0x29, 0x26, 0x5b, 0x74, 0xab, 0xb0, 0xda, 0xf6, 0x75, 0xe2, 0xf9, 0x32, 0x23, 0x99, - 0x76, 0xc1, 0x2b, 0x55, 0x56, 0x05, 0x04, 0x6b, 0x58, 0x74, 0x33, 0xde, 0xb1, 0x9d, 0xba, 0x18, - 0x26, 0xb5, 0x19, 0x3f, 0x67, 0x3b, 0x75, 0xcc, 0x20, 0x6a, 0xbb, 0xce, 0xf6, 0x35, 0x6b, 0xbc, - 0x61, 0xc0, 0x14, 0x8b, 0x9a, 0x0c, 0xaf, 0x1e, 0x4f, 0x28, 0x9f, 0x16, 0x2e, 0xc6, 0xd9, 0xa8, - 0x4f, 0xcb, 0xed, 0x6e, 0x71, 0x9c, 0xc7, 0x59, 0x46, 0x5d, 0x5c, 0x3e, 0x28, 0xec, 0x15, 0xcc, - 0xf3, 0x26, 0x73, 0xe4, 0xeb, 0xb4, 0xb2, 0xe7, 0x55, 0x25, 0x11, 0x1c, 0xd2, 0x33, 0x5f, 0x82, - 0x09, 0x3d, 0xfc, 0x01, 0x3d, 0x01, 0xe3, 0x6d, 0xdb, 0x69, 0x44, 0xc3, 0xe4, 0x94, 0x4d, 0xb7, - 0x12, 0x82, 0xb0, 0x8e, 0xc7, 0xaa, 0xb9, 0x61, 0xb5, 0x98, 0x29, 0xb8, 0xe2, 0xea, 0xd5, 0xc2, - 0x3f, 0xe6, 0x9f, 0x66, 0xe1, 0x54, 0x42, 0x98, 0x0d, 0x7a, 0xd5, 0x80, 0x51, 0xe6, 0xa5, 0x2f, - 0xbd, 0x56, 0x5e, 0x48, 0x3d, 0x94, 0x67, 0x91, 0x05, 0x03, 0x88, 0x79, 0xac, 0xb6, 0x4f, 0x5e, - 0x88, 0x05, 0x73, 0xf4, 0xbb, 0x06, 0x8c, 0x5b, 0xda, 0x52, 0xe3, 0x8e, 0x3c, 0x9b, 0xe9, 0x0b, - 0xd3, 0xb3, 0xb2, 0x34, 0x07, 0xc4, 0x70, 0x21, 0xe9, 0xb2, 0x2c, 0xbc, 0x17, 0xc6, 0xb5, 0x26, - 0x1c, 0x65, 0x85, 0x2c, 0x3c, 0x03, 0x33, 0x43, 0xad, 0xb0, 0x0f, 0xc0, 0x51, 0x13, 0xec, 0x51, - 0x85, 0xb5, 0xa7, 0x87, 0x32, 0xab, 0x1e, 0x17, 0xb1, 0xcc, 0x02, 0x6a, 0x6e, 0xc2, 0x4c, 0xfc, - 0x72, 0x95, 0xfa, 0xbb, 0xf5, 0xbb, 0xe1, 0x88, 0x29, 0xf1, 0xcc, 0xf3, 0x80, 0xb0, 0xdb, 0x6c, - 0x6e, 0x5a, 0xb5, 0x9d, 0x1b, 0xb6, 0x53, 0x77, 0xf7, 0xd8, 0x5a, 0x59, 0x82, 0x82, 0x27, 0xa2, - 0xa8, 0x7c, 0xd1, 0x2c, 0xb5, 0xd8, 0x64, 0x78, 0x95, 0x8f, 0x43, 0x1c, 0xf3, 0xbb, 0x19, 0x18, - 0x13, 0x21, 0x7f, 0x77, 0xc1, 0x05, 0x78, 0x27, 0xf2, 0x36, 0xb4, 0x9a, 0x4a, 0xa4, 0x62, 0x5f, - 0xff, 0x5f, 0x3f, 0xe6, 0xff, 0xfb, 0x5c, 0x3a, 0xec, 0x0e, 0x77, 0xfe, 0x7d, 0x63, 0x04, 0xa6, - 0x63, 0x21, 0x94, 0xf4, 0xc4, 0xd3, 0xe3, 0xf3, 0x76, 0x2d, 0xd5, 0x28, 0x4d, 0xe5, 0x9e, 0x7e, - 0xb8, 0xfb, 0x9b, 0x1f, 0x49, 0x60, 0x7a, 0x35, 0xb5, 0xdc, 0xe7, 0xbf, 0xca, 0x65, 0x7a, 0x54, - 0x77, 0xae, 0x7f, 0x32, 0xe0, 0xde, 0xbe, 0x91, 0xb6, 0x2c, 0x51, 0x8b, 0x17, 0x85, 0x8a, 0x05, - 0x99, 0x72, 0x3e, 0x01, 0xf5, 0x50, 0x13, 0xcf, 0xad, 0x11, 0x67, 0x8f, 0x1e, 0x87, 0x09, 0xa6, - 0xa1, 0xe9, 0xd6, 0x14, 0x90, 0xb6, 0xb0, 0x33, 0x33, 0x8b, 0x63, 0x55, 0x2b, 0xc7, 0x11, 0x2c, - 0xf3, 0xcb, 0x06, 0xcc, 0xf7, 0x4b, 0xdb, 0x31, 0xc0, 0xfd, 0xf2, 0xff, 0xc5, 0x7c, 0x94, 0x8b, - 0x3d, 0x3e, 0xca, 0xb1, 0x1b, 0xa6, 0x74, 0x47, 0xd6, 0x2e, 0x77, 0xd9, 0x3b, 0xb8, 0xe0, 0x7e, - 0xce, 0x80, 0x33, 0x7d, 0x56, 0x53, 0x8f, 0xaf, 0xba, 0x71, 0x6c, 0x5f, 0xf5, 0xcc, 0xa0, 0xbe, - 0xea, 0xe6, 0xf7, 0xb3, 0x30, 0x23, 0xe4, 0x09, 0x8f, 0x69, 0x4f, 0x46, 0x3c, 0xbd, 0xdf, 0x11, - 0xf3, 0xf4, 0x9e, 0x8b, 0xe3, 0xff, 0xca, 0xcd, 0xfb, 0xad, 0xe5, 0xe6, 0xfd, 0x8b, 0x0c, 0x9c, - 0x4e, 0xcc, 0x26, 0x82, 0x3e, 0x93, 0xa0, 0x1a, 0x6e, 0xa4, 0x9c, 0xb6, 0x64, 0x40, 0xe5, 0x30, - 0xac, 0x6f, 0xf4, 0x17, 0x75, 0x9f, 0x64, 0xbe, 0xd5, 0x6f, 0x9d, 0x40, 0x02, 0x96, 0x23, 0xba, - 0x27, 0x9b, 0xbf, 0x99, 0x85, 0x87, 0x07, 0x25, 0xf4, 0x16, 0x0d, 0x5f, 0xf1, 0x23, 0xe1, 0x2b, - 0x77, 0x49, 0x6d, 0x9f, 0x48, 0x24, 0xcb, 0x57, 0xb2, 0x4a, 0xed, 0xf5, 0xce, 0xcf, 0x81, 0x1e, - 0x25, 0xc7, 0xe8, 0xd1, 0x4e, 0xe6, 0x18, 0x0d, 0xb7, 0xc2, 0xb1, 0x2a, 0x2f, 0xbe, 0xdd, 0x2d, - 0xce, 0x86, 0x31, 0xed, 0xa2, 0x10, 0xcb, 0x4a, 0xe8, 0x61, 0xc8, 0x7b, 0x1c, 0x2a, 0x1d, 0xf6, - 0xc5, 0xcb, 0x2e, 0x2f, 0xc3, 0x0a, 0x8a, 0x3e, 0xa9, 0x9d, 0x85, 0x47, 0x4e, 0x2a, 0x75, 0xc3, - 0x61, 0x0f, 0xd6, 0x2f, 0x40, 0xde, 0x97, 0xd9, 0x42, 0xf9, 0xab, 0xc2, 0x63, 0x03, 0xc6, 0x81, - 0xd0, 0x1b, 0x98, 0x4c, 0x1d, 0xca, 0xdb, 0xa7, 0x12, 0x8b, 0x2a, 0x92, 0xc8, 0x54, 0x97, 0x1f, - 0x6e, 0xaa, 0x84, 0x84, 0x8b, 0xcf, 0x0f, 0x0c, 0x18, 0x17, 0xa3, 0x75, 0x17, 0x42, 0x53, 0x6e, - 0x46, 0x43, 0x53, 0xce, 0xa7, 0xb2, 0x77, 0xf4, 0x89, 0x4b, 0xb9, 0x09, 0x13, 0x7a, 0x42, 0x29, - 0xf4, 0xbc, 0xb6, 0xf7, 0x19, 0xc3, 0xa4, 0x68, 0x91, 0xbb, 0x63, 0xb8, 0x2f, 0x9a, 0x5f, 0x2d, - 0xa8, 0x5e, 0x64, 0x57, 0x34, 0x7d, 0x0e, 0x1a, 0x87, 0xce, 0x41, 0x7d, 0x0a, 0x64, 0xd2, 0x9f, - 0x02, 0x57, 0x21, 0x2f, 0x37, 0x28, 0xa1, 0xc6, 0x1f, 0xd4, 0x9d, 0xf5, 0xe8, 0x59, 0x80, 0x12, - 0xd3, 0x26, 0x2e, 0xbb, 0x6a, 0xa9, 0x31, 0x54, 0x1b, 0xa7, 0x22, 0x83, 0x5e, 0x84, 0xf1, 0x3d, - 0xd7, 0xdb, 0x69, 0xba, 0x16, 0xcb, 0x03, 0x0c, 0x69, 0xbc, 0x0f, 0x29, 0xbb, 0x19, 0xf7, 0x98, - 0xbe, 0x11, 0xd2, 0xc7, 0x3a, 0x33, 0x54, 0x82, 0xe9, 0x96, 0xed, 0x60, 0x62, 0xd5, 0x55, 0x04, - 0xca, 0x08, 0x4f, 0x54, 0x2a, 0x0f, 0xb9, 0xeb, 0x51, 0x30, 0x8e, 0xe3, 0xa3, 0xcf, 0x1a, 0x30, - 0xe5, 0x45, 0x2e, 0xd5, 0x22, 0x1b, 0x61, 0x65, 0xf8, 0xc9, 0x18, 0xbd, 0xa8, 0x73, 0x97, 0xe1, - 0x68, 0x39, 0x8e, 0xf1, 0x46, 0x1f, 0x87, 0xbc, 0x2f, 0xb2, 0x45, 0xa5, 0xf3, 0xb0, 0xa8, 0xae, - 0xb0, 0x9c, 0x68, 0x38, 0x94, 0xb2, 0x04, 0x2b, 0x86, 0x68, 0x0d, 0xe6, 0xa4, 0x95, 0x20, 0xf2, - 0xad, 0x96, 0xd1, 0x30, 0xb9, 0x08, 0x4e, 0x80, 0xe3, 0xc4, 0x5a, 0xf4, 0x50, 0xc5, 0x12, 0xb5, - 0xf1, 0x97, 0x0e, 0xed, 0x71, 0x80, 0xad, 0xbf, 0x3a, 0x16, 0xd0, 0xc3, 0x02, 0xac, 0xf2, 0x43, - 0x04, 0x58, 0x55, 0xe1, 0x74, 0x1c, 0xc4, 0xb2, 0xc6, 0xb0, 0x44, 0x35, 0x9a, 0x32, 0xab, 0x24, - 0x21, 0xe1, 0xe4, 0xba, 0xe8, 0x06, 0x14, 0x3c, 0xc2, 0xae, 0x3b, 0x25, 0xe9, 0xca, 0x70, 0x64, - 0xa7, 0x2d, 0x2c, 0x09, 0xe0, 0x90, 0x16, 0x1d, 0x77, 0x2b, 0x9a, 0x3a, 0xf4, 0x6a, 0x8a, 0x5f, - 0x9b, 0x13, 0x63, 0xdf, 0x27, 0x9b, 0x93, 0xf9, 0xe6, 0x14, 0x4c, 0x46, 0x4c, 0x1d, 0xe8, 0x41, - 0xc8, 0xb1, 0x34, 0x3a, 0x6c, 0xb7, 0xca, 0x87, 0x3b, 0x2a, 0xef, 0x1c, 0x0e, 0x43, 0x9f, 0x37, - 0x60, 0xba, 0x1d, 0xb1, 0x2d, 0xcb, 0x8d, 0x7c, 0xc8, 0xd7, 0xcb, 0xa8, 0xc1, 0x5a, 0x4b, 0xba, - 0x1d, 0x65, 0x86, 0xe3, 0xdc, 0xe9, 0x7e, 0x20, 0x3c, 0x1f, 0x9b, 0xc4, 0x63, 0xd8, 0xe2, 0xc8, - 0xa5, 0x48, 0x2c, 0x47, 0xc1, 0x38, 0x8e, 0x4f, 0x47, 0x98, 0xb5, 0x6e, 0x98, 0xcf, 0x50, 0x95, - 0x24, 0x01, 0x1c, 0xd2, 0x42, 0xcf, 0xc0, 0x94, 0xc8, 0x18, 0x59, 0x71, 0xeb, 0x17, 0x2d, 0x7f, - 0x5b, 0xdc, 0x35, 0xd4, 0xdd, 0x68, 0x39, 0x02, 0xc5, 0x31, 0x6c, 0xd6, 0xb6, 0x30, 0x2d, 0x27, - 0x23, 0x30, 0x1a, 0xcd, 0x49, 0xbe, 0x1c, 0x05, 0xe3, 0x38, 0x3e, 0x7a, 0x44, 0x53, 0x43, 0xfc, - 0xf5, 0x51, 0xed, 0x06, 0x09, 0xaa, 0xa8, 0x04, 0xd3, 0x1d, 0x76, 0x35, 0xab, 0x4b, 0xa0, 0x58, - 0x8f, 0x8a, 0xe1, 0xb5, 0x28, 0x18, 0xc7, 0xf1, 0xd1, 0x53, 0x30, 0xe9, 0xd1, 0xcd, 0x56, 0x11, - 0xe0, 0x4f, 0x92, 0xea, 0xc5, 0x09, 0xeb, 0x40, 0x1c, 0xc5, 0x45, 0xcf, 0xc2, 0x6c, 0x98, 0x60, - 0x4d, 0x12, 0xe0, 0x6f, 0x94, 0x2a, 0xdb, 0x4f, 0x29, 0x8e, 0x80, 0x7b, 0xeb, 0xa0, 0x5f, 0x83, - 0x19, 0xad, 0x27, 0x56, 0x9d, 0x3a, 0xb9, 0x25, 0x92, 0x60, 0xb1, 0xcf, 0x47, 0x2c, 0xc7, 0x60, - 0xb8, 0x07, 0x1b, 0xbd, 0x0f, 0xa6, 0x6a, 0x6e, 0xb3, 0xc9, 0xf6, 0x38, 0x9e, 0x0f, 0x9b, 0x67, - 0xbb, 0xe2, 0x79, 0xc1, 0x22, 0x10, 0x1c, 0xc3, 0x44, 0x97, 0x00, 0xb9, 0x9b, 0x3e, 0xf1, 0x76, - 0x49, 0xfd, 0x59, 0xfe, 0x61, 0x5b, 0x7a, 0xe2, 0x98, 0x8c, 0xfa, 0x5d, 0x5f, 0xe9, 0xc1, 0xc0, - 0x09, 0xb5, 0x58, 0xb2, 0x20, 0x2d, 0x4e, 0x6d, 0x2a, 0x8d, 0x4f, 0x32, 0xc5, 0x0d, 0x09, 0x77, - 0x0c, 0x52, 0xf3, 0x60, 0x94, 0xbb, 0xc1, 0xa7, 0x93, 0xf6, 0x4a, 0x4f, 0x8d, 0x1b, 0xea, 0x08, - 0x5e, 0x8a, 0x05, 0x27, 0xf4, 0x09, 0x28, 0x6c, 0xca, 0x3c, 0xe9, 0x2c, 0xd7, 0xd5, 0xd0, 0x7a, - 0x31, 0x96, 0xf2, 0x3f, 0xbc, 0x28, 0x2b, 0x00, 0x0e, 0x59, 0xa2, 0x87, 0x60, 0xfc, 0x62, 0xa5, - 0xa4, 0x66, 0xe1, 0x2c, 0x1b, 0xfd, 0x11, 0x5a, 0x05, 0xeb, 0x00, 0xba, 0xc2, 0xd4, 0xf1, 0x0d, - 0xb1, 0x21, 0x0e, 0xf5, 0x6d, 0xef, 0x69, 0x8c, 0x62, 0xb3, 0x47, 0x56, 0x5c, 0x9d, 0x3f, 0x15, - 0xc3, 0x16, 0xe5, 0x58, 0x61, 0xa0, 0x17, 0x60, 0x5c, 0xe8, 0x0b, 0xb6, 0x37, 0xcd, 0x1d, 0x2f, - 0x06, 0x12, 0x87, 0x24, 0xb0, 0x4e, 0x8f, 0xbd, 0x9d, 0xb1, 0xf4, 0xd1, 0xe4, 0x42, 0xa7, 0xd9, - 0x9c, 0x3f, 0xcd, 0xf6, 0xcd, 0xf0, 0xed, 0x2c, 0x04, 0x61, 0x1d, 0x0f, 0x3d, 0x26, 0xfd, 0x41, - 0xde, 0x16, 0x79, 0x4c, 0x54, 0xfe, 0x20, 0xea, 0xd0, 0xdd, 0xc7, 0x4d, 0xfa, 0xcc, 0x1d, 0x1c, - 0x31, 0x36, 0x61, 0x41, 0x9e, 0xf8, 0x7a, 0x17, 0xc9, 0xfc, 0x7c, 0xc4, 0x68, 0xb1, 0x70, 0xa3, - 0x2f, 0x26, 0x3e, 0x84, 0x0a, 0xda, 0x84, 0xac, 0xd5, 0xdc, 0x9c, 0xbf, 0x37, 0x8d, 0xa3, 0xab, - 0xfa, 0x50, 0x35, 0x77, 0x6d, 0x2a, 0xad, 0x95, 0x31, 0x25, 0x6e, 0xbe, 0x92, 0x51, 0x8f, 0x04, - 0x2a, 0x1d, 0xe8, 0x4b, 0xfa, 0xac, 0x36, 0xd2, 0xf8, 0x10, 0x6b, 0xcf, 0xc7, 0x04, 0xb8, 0x42, - 0x4a, 0x9c, 0xd3, 0x6d, 0xb5, 0x8e, 0x53, 0xc9, 0xce, 0x12, 0x4d, 0x75, 0xca, 0x2f, 0x97, 0xd1, - 0x55, 0x6c, 0xfe, 0x38, 0xaf, 0x6c, 0x62, 0x31, 0x07, 0x07, 0x0f, 0x72, 0xb6, 0x1f, 0xd8, 0x6e, - 0x8a, 0xf1, 0x7a, 0xb1, 0x1c, 0xa1, 0xcc, 0x1d, 0x98, 0x01, 0x30, 0x67, 0x45, 0x79, 0x3a, 0x0d, - 0xdb, 0xb9, 0x25, 0x9a, 0x7f, 0x35, 0x75, 0xcf, 0x05, 0xce, 0x93, 0x01, 0x30, 0x67, 0x85, 0x6e, - 0xf2, 0x99, 0x96, 0xce, 0x47, 0x77, 0xe3, 0xdf, 0xd2, 0x8e, 0xce, 0x38, 0xca, 0xcb, 0x6f, 0xd9, - 0xe2, 0x0c, 0x33, 0x24, 0xaf, 0xea, 0xfa, 0x6a, 0x12, 0xaf, 0xea, 0xfa, 0x2a, 0xa6, 0x4c, 0xd0, - 0x6b, 0x06, 0x80, 0xa5, 0x3e, 0x2a, 0x9d, 0xce, 0x97, 0x37, 0xfa, 0x7d, 0xa4, 0x9a, 0x7b, 0xf0, - 0x85, 0x50, 0xac, 0x71, 0x46, 0x2f, 0xc2, 0x98, 0xc5, 0xbf, 0x1b, 0x24, 0x9c, 0x23, 0xd3, 0xf9, - 0x18, 0x56, 0x4c, 0x02, 0xe6, 0x15, 0x2a, 0x40, 0x58, 0x32, 0xa4, 0xbc, 0x03, 0xcf, 0x22, 0x5b, - 0xf6, 0x8e, 0xf0, 0x92, 0xac, 0x0e, 0x9d, 0xfe, 0x9b, 0x12, 0x4b, 0xe2, 0x2d, 0x40, 0x58, 0x32, - 0xe4, 0xdf, 0x71, 0xb5, 0x1c, 0x4b, 0x85, 0xbc, 0xa4, 0x13, 0x18, 0xa5, 0x07, 0xd1, 0x68, 0xdf, - 0x71, 0xd5, 0x19, 0xe1, 0x28, 0x5f, 0xb4, 0x0b, 0xa3, 0x16, 0xfb, 0xa2, 0x99, 0xb8, 0x1f, 0xe1, - 0x34, 0xbe, 0x8e, 0x16, 0xeb, 0x03, 0xb6, 0xb9, 0x88, 0xef, 0xa6, 0x09, 0x6e, 0xe6, 0xcf, 0xb2, - 0x00, 0x4c, 0x04, 0x1e, 0xfe, 0xdd, 0x62, 0x29, 0x03, 0xb7, 0xdd, 0x7a, 0x3a, 0x1f, 0x69, 0xd3, - 0xa3, 0xb8, 0x41, 0xe4, 0x07, 0xdc, 0x76, 0xeb, 0x58, 0x30, 0x41, 0x0d, 0x18, 0x69, 0x5b, 0xc1, - 0x76, 0xfa, 0x21, 0xe3, 0x79, 0x1e, 0x07, 0x15, 0x6c, 0x63, 0xc6, 0x00, 0xbd, 0x6c, 0xc0, 0x18, - 0x0f, 0x1a, 0x97, 0x16, 0xf7, 0xa1, 0x9f, 0x95, 0x65, 0x9f, 0x2d, 0xf2, 0xc8, 0x74, 0xe1, 0xfa, - 0xa1, 0x54, 0xb2, 0x28, 0xc5, 0x92, 0xed, 0xc2, 0xab, 0x06, 0x4c, 0xe8, 0xa8, 0x09, 0x4e, 0x1b, - 0x1f, 0xd1, 0x9d, 0x36, 0xd2, 0xec, 0x0f, 0xdd, 0xff, 0xe3, 0x5f, 0x0d, 0xd0, 0xbe, 0xba, 0x1b, - 0xba, 0x6c, 0x1a, 0x03, 0xbb, 0x6c, 0x66, 0x8e, 0xe8, 0xb2, 0x99, 0x3d, 0x92, 0xcb, 0xe6, 0xc8, - 0xd1, 0x5d, 0x36, 0x73, 0xfd, 0x5d, 0x36, 0xcd, 0xd7, 0x0d, 0x98, 0xed, 0xd9, 0x87, 0xe9, 0xb1, - 0xcd, 0x73, 0xdd, 0xa0, 0x8f, 0xa7, 0x14, 0x0e, 0x41, 0x58, 0xc7, 0x43, 0x2b, 0x30, 0x23, 0x12, - 0x64, 0x57, 0xdb, 0x4d, 0x3b, 0x31, 0x9c, 0x7f, 0x23, 0x06, 0xc7, 0x3d, 0x35, 0xcc, 0xbf, 0x34, - 0x60, 0x5c, 0x0b, 0x02, 0xa4, 0xed, 0x60, 0xc1, 0x92, 0x42, 0x8c, 0x30, 0x37, 0x38, 0x7b, 0xe1, - 0xe0, 0x30, 0xfe, 0xd8, 0xd6, 0xd0, 0xd2, 0xa7, 0x86, 0x8f, 0x6d, 0xb4, 0x14, 0x0b, 0x28, 0x4f, - 0x8c, 0x49, 0xda, 0xac, 0xd3, 0xb3, 0x7a, 0x62, 0x4c, 0xd2, 0xc6, 0x0c, 0xc2, 0xd8, 0xd1, 0xf3, - 0xab, 0xf0, 0xe6, 0xd5, 0x52, 0x91, 0x5b, 0x5e, 0x80, 0x39, 0x0c, 0x9d, 0x85, 0x2c, 0x71, 0xea, - 0xe2, 0xb2, 0xad, 0x3e, 0xff, 0x75, 0xde, 0xa9, 0x63, 0x5a, 0x6e, 0x5e, 0x81, 0x89, 0x2a, 0xa9, - 0x79, 0x24, 0x78, 0x8e, 0xec, 0x0f, 0xfc, 0x3d, 0x31, 0x3a, 0xdb, 0x63, 0xdf, 0x13, 0xa3, 0xd5, - 0x69, 0xb9, 0xf9, 0x27, 0x06, 0xc4, 0xf2, 0xe5, 0x6b, 0x86, 0x77, 0xa3, 0x9f, 0xe1, 0x3d, 0x62, - 0x22, 0xce, 0x1c, 0x6a, 0x22, 0xbe, 0x04, 0xa8, 0x45, 0x97, 0x42, 0xe4, 0xeb, 0x10, 0xc2, 0xce, - 0x11, 0x86, 0x1c, 0xf7, 0x60, 0xe0, 0x84, 0x5a, 0xe6, 0x1f, 0x73, 0x61, 0xf5, 0x0c, 0xfa, 0x77, - 0xee, 0x80, 0x0e, 0xe4, 0x18, 0x29, 0x61, 0xec, 0x19, 0xd2, 0x50, 0xda, 0x9b, 0xba, 0x23, 0x1c, - 0x48, 0xb1, 0xe4, 0x19, 0x37, 0xf3, 0xfb, 0x5c, 0x56, 0x2d, 0xc5, 0xfe, 0x00, 0xb2, 0xb6, 0xa2, - 0xb2, 0x5e, 0x4c, 0x6b, 0xaf, 0x4c, 0x96, 0x11, 0x2d, 0x02, 0xb4, 0x89, 0x57, 0x23, 0x4e, 0x20, - 0x9d, 0xcc, 0x73, 0x22, 0x28, 0x47, 0x95, 0x62, 0x0d, 0xc3, 0x7c, 0xd9, 0x80, 0x99, 0x6a, 0x60, - 0xd7, 0x76, 0x6c, 0x87, 0x07, 0x99, 0x6d, 0xd9, 0x0d, 0x7a, 0x3b, 0x22, 0xe2, 0x53, 0x59, 0xdc, - 0xfc, 0xa6, 0xb6, 0x62, 0xf9, 0x85, 0x2c, 0x09, 0x47, 0x25, 0x98, 0x96, 0x8f, 0x0e, 0xd2, 0x66, - 0xca, 0x83, 0x63, 0x95, 0x8d, 0x66, 0x25, 0x0a, 0xc6, 0x71, 0x7c, 0xf3, 0x93, 0x30, 0xae, 0xed, - 0xaf, 0x6c, 0x2b, 0xba, 0x65, 0xd5, 0x82, 0xf8, 0x12, 0x3e, 0x4f, 0x0b, 0x31, 0x87, 0x31, 0xd3, - 0x2e, 0xf7, 0x42, 0x8e, 0x2d, 0x61, 0xe1, 0x7b, 0x2c, 0xa0, 0x94, 0x98, 0x47, 0x1a, 0xe4, 0x96, - 0x4c, 0xd4, 0x2a, 0x89, 0x61, 0x5a, 0x88, 0x39, 0xcc, 0x7c, 0x04, 0xf2, 0x32, 0x85, 0x01, 0x8b, - 0x03, 0x96, 0x66, 0x47, 0x3d, 0x0e, 0xd8, 0xf5, 0x02, 0xcc, 0x20, 0xe6, 0x75, 0xc8, 0xcb, 0x4c, - 0x0b, 0x77, 0xc6, 0xa6, 0xab, 0xca, 0x77, 0xec, 0x8b, 0xae, 0x1f, 0xc8, 0xf4, 0x10, 0xfc, 0x65, - 0xe4, 0xf2, 0x2a, 0x2b, 0xc3, 0x0a, 0x6a, 0xce, 0xc2, 0xb4, 0x7a, 0xf2, 0x10, 0x6e, 0xa1, 0xdf, - 0xca, 0xc2, 0x44, 0xe4, 0xeb, 0xd3, 0x77, 0x9e, 0x6e, 0x83, 0xaf, 0xe2, 0x84, 0xa7, 0x8b, 0xec, - 0x11, 0x9f, 0x2e, 0xf4, 0xb7, 0xa2, 0x91, 0x93, 0x7d, 0x2b, 0xca, 0xa5, 0xf3, 0x56, 0x14, 0xc0, - 0x98, 0x2f, 0x14, 0xd5, 0x68, 0x1a, 0x46, 0x9c, 0xd8, 0x88, 0xf1, 0xb3, 0xb1, 0xd4, 0x77, 0x92, - 0x95, 0xf9, 0xf5, 0x1c, 0x4c, 0x45, 0x73, 0x4c, 0x0d, 0x30, 0x92, 0x8f, 0xf4, 0x8c, 0xe4, 0x11, - 0x6d, 0xa5, 0xd9, 0x61, 0x6d, 0xa5, 0x23, 0xc3, 0xda, 0x4a, 0x73, 0xc7, 0xb0, 0x95, 0xf6, 0x5a, - 0x3a, 0x47, 0x07, 0xb6, 0x74, 0x3e, 0xad, 0xfc, 0x8e, 0xc6, 0x22, 0x0f, 0xf5, 0xa1, 0xdf, 0x11, - 0x8a, 0x0e, 0xc3, 0xb2, 0x5b, 0x4f, 0xf4, 0xdf, 0xca, 0xdf, 0xc1, 0x26, 0xe4, 0x25, 0xba, 0x09, - 0x1d, 0xfd, 0x39, 0xe6, 0x6d, 0x47, 0x70, 0x11, 0x7a, 0x02, 0xc6, 0xc5, 0x7c, 0x62, 0x67, 0x25, - 0x88, 0x9e, 0xb3, 0xaa, 0x21, 0x08, 0xeb, 0x78, 0xec, 0x4b, 0xa2, 0xd1, 0x4f, 0xa7, 0x32, 0xd3, - 0xb3, 0xfe, 0x25, 0xd1, 0xd8, 0xa7, 0x56, 0xe3, 0xf8, 0xe6, 0xc7, 0xe1, 0x74, 0xe2, 0x4d, 0x90, - 0x99, 0xc6, 0x98, 0x1a, 0x27, 0x75, 0x81, 0xa0, 0x89, 0x11, 0x4b, 0x41, 0xbc, 0x70, 0xa3, 0x2f, - 0x26, 0x3e, 0x84, 0x8a, 0xf9, 0xb5, 0x2c, 0x4c, 0x45, 0x3f, 0x43, 0x85, 0xf6, 0x94, 0xdd, 0x28, - 0x15, 0x93, 0x15, 0x27, 0xab, 0xe5, 0x2d, 0xea, 0x6b, 0x04, 0xde, 0x63, 0xf3, 0x6b, 0x53, 0x25, - 0x51, 0x3a, 0x39, 0xc6, 0xc2, 0xfa, 0x2a, 0xd8, 0xb1, 0x2f, 0x4d, 0x85, 0xc1, 0x23, 0xe2, 0xda, - 0x95, 0x3a, 0xf7, 0x30, 0x1c, 0x44, 0xb1, 0xc2, 0x1a, 0x5b, 0xaa, 0x5b, 0x76, 0x89, 0x67, 0x6f, - 0xd9, 0xea, 0x13, 0x9a, 0x6c, 0xe7, 0xbe, 0x2e, 0xca, 0xb0, 0x82, 0x9a, 0x2f, 0x67, 0x20, 0xfc, - 0x60, 0x30, 0xfb, 0x56, 0x8b, 0xaf, 0x1d, 0x71, 0xc5, 0xb0, 0x5d, 0x1a, 0xf6, 0x83, 0x48, 0x21, - 0x45, 0xe1, 0x13, 0xaa, 0x95, 0xe0, 0x08, 0xc7, 0x5f, 0xc2, 0x87, 0x82, 0x2d, 0x98, 0x8e, 0x85, - 0xd4, 0xa6, 0xee, 0xbf, 0xff, 0xd3, 0x2c, 0x14, 0x54, 0x50, 0x32, 0x7a, 0x6f, 0xc4, 0xde, 0x50, - 0x28, 0xbf, 0x5d, 0xfb, 0x90, 0xc0, 0xb6, 0x5b, 0xbf, 0xdd, 0x2d, 0x4e, 0x2b, 0xe4, 0x98, 0xed, - 0xe0, 0x2c, 0x64, 0x3b, 0x5e, 0x33, 0x7e, 0xa1, 0xb8, 0x86, 0xd7, 0x30, 0x2d, 0x47, 0xb7, 0xe2, - 0x17, 0xfe, 0xf5, 0x94, 0x02, 0xa9, 0xf9, 0xc9, 0xbb, 0xff, 0x45, 0x9f, 0x6a, 0xc9, 0x4d, 0xb7, - 0xbe, 0x1f, 0xff, 0xf0, 0x40, 0xd9, 0xad, 0xef, 0x63, 0x06, 0x41, 0xcf, 0xc0, 0x54, 0x60, 0xb7, - 0x88, 0xdb, 0x09, 0xf4, 0xcf, 0xb1, 0x66, 0xc3, 0x47, 0xcd, 0x8d, 0x08, 0x14, 0xc7, 0xb0, 0xa9, - 0x96, 0xbd, 0xe9, 0xbb, 0x0e, 0xcb, 0x26, 0x38, 0x1a, 0x7d, 0x01, 0xb9, 0x54, 0xbd, 0x72, 0x99, - 0xd9, 0x3d, 0x14, 0x06, 0xc5, 0xb6, 0x59, 0x04, 0xa2, 0x47, 0x84, 0x4f, 0xc1, 0x4c, 0x98, 0x9f, - 0x82, 0x97, 0x63, 0x85, 0x81, 0x56, 0x38, 0x6d, 0x2a, 0x2d, 0xd3, 0x28, 0x13, 0xe5, 0x87, 0x25, - 0x5d, 0x5a, 0x76, 0xbb, 0x5b, 0x9c, 0x27, 0x4e, 0xcd, 0xad, 0xdb, 0x4e, 0x63, 0x89, 0x22, 0x2e, - 0x62, 0x6b, 0x4f, 0xaa, 0x1a, 0x55, 0xd3, 0xbc, 0x06, 0xd3, 0xb1, 0x0e, 0x93, 0x17, 0x40, 0x23, - 0xf9, 0x02, 0x38, 0xd8, 0xb7, 0x02, 0xfe, 0xcc, 0x80, 0xd9, 0x9e, 0x2d, 0x60, 0xd0, 0xf0, 0x94, - 0xb8, 0x32, 0xca, 0x1c, 0x5f, 0x19, 0x65, 0x8f, 0xa6, 0x8c, 0xca, 0x9b, 0xdf, 0x79, 0xf3, 0xdc, - 0x3d, 0xdf, 0x7b, 0xf3, 0xdc, 0x3d, 0x3f, 0x7c, 0xf3, 0xdc, 0x3d, 0x2f, 0x1f, 0x9c, 0x33, 0xbe, - 0x73, 0x70, 0xce, 0xf8, 0xde, 0xc1, 0x39, 0xe3, 0x87, 0x07, 0xe7, 0x8c, 0x7f, 0x3c, 0x38, 0x67, - 0xbc, 0xfe, 0xd3, 0x73, 0xf7, 0x3c, 0xff, 0x74, 0x38, 0x41, 0x97, 0xe4, 0x04, 0x65, 0x3f, 0xde, - 0x25, 0xa7, 0xe3, 0x52, 0x7b, 0xa7, 0xb1, 0x44, 0x27, 0xe8, 0x92, 0x2a, 0x91, 0x13, 0xf4, 0xbf, - 0x02, 0x00, 0x00, 0xff, 0xff, 0x3e, 0xb7, 0xa2, 0x97, 0xc8, 0x95, 0x00, 0x00, + // 7827 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5b, 0x6c, 0x24, 0xd7, + 0x75, 0xa0, 0xaa, 0x9b, 0x4d, 0x76, 0x1f, 0xbe, 0xef, 0x70, 0x3c, 0x14, 0xa5, 0x99, 0x96, 0x4b, + 0x86, 0x56, 0xde, 0x95, 0x49, 0x5b, 0x8f, 0x5d, 0xd9, 0x12, 0xb4, 0xdb, 0x4d, 0xce, 0x68, 0x38, + 0x22, 0x39, 0x3d, 0xb7, 0x39, 0x33, 0xb6, 0x6c, 0xd9, 0x2e, 0x76, 0x5f, 0x36, 0x6b, 0xd8, 0x5d, + 0xd5, 0xae, 0xaa, 0x26, 0x87, 0xb2, 0x60, 0x4b, 0x36, 0xa4, 0xf5, 0x2e, 0x6c, 0x58, 0xbb, 0xb6, + 0xb1, 0x58, 0x2c, 0xb0, 0x30, 0x16, 0x06, 0x36, 0x89, 0xfd, 0x65, 0x24, 0xc8, 0x8f, 0x81, 0x04, + 0xf1, 0x23, 0xce, 0x87, 0x03, 0xfb, 0x23, 0xf1, 0x03, 0x70, 0x27, 0xa2, 0xf3, 0x93, 0x20, 0x81, + 0x11, 0xc0, 0x41, 0xe0, 0xf9, 0x0a, 0xee, 0xb3, 0x6e, 0x55, 0x57, 0x73, 0x9a, 0xec, 0xe2, 0x58, + 0x48, 0xfc, 0xd7, 0x7d, 0xcf, 0xb9, 0xe7, 0x9c, 0xfb, 0x3c, 0xf7, 0x9e, 0x7b, 0xce, 0x29, 0x58, + 0x6b, 0xd8, 0xc1, 0x4e, 0x67, 0x6b, 0xb1, 0xe6, 0xb6, 0x96, 0x2c, 0xaf, 0xe1, 0xb6, 0x3d, 0xf7, + 0x16, 0xfb, 0xf1, 0x1e, 0xcf, 0x6d, 0x36, 0xdd, 0x4e, 0xe0, 0x2f, 0xb5, 0x77, 0x1b, 0x4b, 0x56, + 0xdb, 0xf6, 0x97, 0x54, 0xc9, 0xde, 0xfb, 0xac, 0x66, 0x7b, 0xc7, 0x7a, 0xdf, 0x52, 0x83, 0x38, + 0xc4, 0xb3, 0x02, 0x52, 0x5f, 0x6c, 0x7b, 0x6e, 0xe0, 0xa2, 0x67, 0x43, 0x6a, 0x8b, 0x92, 0x1a, + 0xfb, 0xf1, 0x31, 0x59, 0x77, 0xb1, 0xbd, 0xdb, 0x58, 0xa4, 0xd4, 0x16, 0x55, 0x89, 0xa4, 0xb6, + 0xf0, 0x1e, 0x4d, 0x96, 0x86, 0xdb, 0x70, 0x97, 0x18, 0xd1, 0xad, 0xce, 0x36, 0xfb, 0xc7, 0xfe, + 0xb0, 0x5f, 0x9c, 0xd9, 0xc2, 0xc3, 0xbb, 0x4f, 0xfb, 0x8b, 0xb6, 0x4b, 0x65, 0x5b, 0xda, 0xb2, + 0x82, 0xda, 0xce, 0xd2, 0x5e, 0x8f, 0x44, 0x0b, 0xa6, 0x86, 0x54, 0x73, 0x3d, 0x92, 0x84, 0xf3, + 0x64, 0x88, 0xd3, 0xb2, 0x6a, 0x3b, 0xb6, 0x43, 0xbc, 0x83, 0xb0, 0xd5, 0x2d, 0x12, 0x58, 0x49, + 0xb5, 0x96, 0xfa, 0xd5, 0xf2, 0x3a, 0x4e, 0x60, 0xb7, 0x48, 0x4f, 0x85, 0xff, 0x78, 0xb7, 0x0a, + 0x7e, 0x6d, 0x87, 0xb4, 0xac, 0x9e, 0x7a, 0x4f, 0xf4, 0xab, 0xd7, 0x09, 0xec, 0xe6, 0x92, 0xed, + 0x04, 0x7e, 0xe0, 0xc5, 0x2b, 0x99, 0xdf, 0xc9, 0x42, 0xa1, 0xb4, 0x56, 0xae, 0x06, 0x56, 0xd0, + 0xf1, 0xd1, 0x1b, 0x06, 0x4c, 0x34, 0x5d, 0xab, 0x5e, 0xb6, 0x9a, 0x96, 0x53, 0x23, 0xde, 0xbc, + 0xf1, 0x90, 0xf1, 0xe8, 0xf8, 0xe3, 0x6b, 0x8b, 0xc3, 0x8c, 0xd7, 0x62, 0x69, 0xdf, 0xc7, 0xc4, + 0x77, 0x3b, 0x5e, 0x8d, 0x60, 0xb2, 0x5d, 0x9e, 0xfb, 0x7e, 0xb7, 0x78, 0xdf, 0x61, 0xb7, 0x38, + 0xb1, 0xa6, 0x71, 0xc2, 0x11, 0xbe, 0xe8, 0x2b, 0x06, 0xcc, 0xd6, 0x2c, 0xc7, 0xf2, 0x0e, 0x36, + 0x2d, 0xaf, 0x41, 0x82, 0xe7, 0x3d, 0xb7, 0xd3, 0x9e, 0xcf, 0x9c, 0x82, 0x34, 0xf7, 0x0b, 0x69, + 0x66, 0x97, 0xe3, 0xec, 0x70, 0xaf, 0x04, 0x4c, 0x2e, 0x3f, 0xb0, 0xb6, 0x9a, 0x44, 0x97, 0x2b, + 0x7b, 0x9a, 0x72, 0x55, 0xe3, 0xec, 0x70, 0xaf, 0x04, 0xe6, 0xeb, 0x59, 0x98, 0x2d, 0xad, 0x95, + 0x37, 0x3d, 0x6b, 0x7b, 0xdb, 0xae, 0x61, 0xb7, 0x13, 0xd8, 0x4e, 0x03, 0xbd, 0x1b, 0xc6, 0x6c, + 0xa7, 0xe1, 0x11, 0xdf, 0x67, 0x03, 0x59, 0x28, 0x4f, 0x0b, 0xa2, 0x63, 0xab, 0xbc, 0x18, 0x4b, + 0x38, 0x7a, 0x0a, 0xc6, 0x7d, 0xe2, 0xed, 0xd9, 0x35, 0x52, 0x71, 0xbd, 0x80, 0xf5, 0x74, 0xae, + 0x7c, 0x46, 0xa0, 0x8f, 0x57, 0x43, 0x10, 0xd6, 0xf1, 0x68, 0x35, 0xcf, 0x75, 0x03, 0x01, 0x67, + 0x1d, 0x51, 0x08, 0xab, 0xe1, 0x10, 0x84, 0x75, 0x3c, 0xf4, 0xa6, 0x01, 0x33, 0x7e, 0x60, 0xd7, + 0x76, 0x6d, 0x87, 0xf8, 0xfe, 0xb2, 0xeb, 0x6c, 0xdb, 0x8d, 0xf9, 0x1c, 0xeb, 0xc5, 0x8d, 0xe1, + 0x7a, 0xb1, 0x1a, 0xa3, 0x5a, 0x9e, 0x3b, 0xec, 0x16, 0x67, 0xe2, 0xa5, 0xb8, 0x87, 0x3b, 0x5a, + 0x81, 0x19, 0xcb, 0x71, 0xdc, 0xc0, 0x0a, 0x6c, 0xd7, 0xa9, 0x78, 0x64, 0xdb, 0xbe, 0x3d, 0x3f, + 0xc2, 0x9a, 0x33, 0x2f, 0x9a, 0x33, 0x53, 0x8a, 0xc1, 0x71, 0x4f, 0x0d, 0x73, 0x05, 0xe6, 0x4b, + 0xad, 0x2d, 0xcb, 0xf7, 0xad, 0xba, 0xeb, 0xc5, 0x46, 0xe3, 0x51, 0xc8, 0xb7, 0xac, 0x76, 0xdb, + 0x76, 0x1a, 0x74, 0x38, 0xb2, 0x8f, 0x16, 0xca, 0x13, 0x87, 0xdd, 0x62, 0x7e, 0x5d, 0x94, 0x61, + 0x05, 0x35, 0x7f, 0x9a, 0x81, 0xf1, 0x92, 0x63, 0x35, 0x0f, 0x7c, 0xdb, 0xc7, 0x1d, 0x07, 0x7d, + 0x1c, 0xf2, 0x74, 0x77, 0xa9, 0x5b, 0x81, 0x25, 0x56, 0xe4, 0x7b, 0x17, 0xf9, 0x62, 0x5f, 0xd4, + 0x17, 0x7b, 0xd8, 0x2f, 0x14, 0x7b, 0x71, 0xef, 0x7d, 0x8b, 0x57, 0xb7, 0x6e, 0x91, 0x5a, 0xb0, + 0x4e, 0x02, 0xab, 0x8c, 0x44, 0x2b, 0x20, 0x2c, 0xc3, 0x8a, 0x2a, 0x72, 0x61, 0xc4, 0x6f, 0x93, + 0x9a, 0x58, 0x61, 0xeb, 0x43, 0xce, 0xe4, 0x50, 0xf4, 0x6a, 0x9b, 0xd4, 0xca, 0x13, 0x82, 0xf5, + 0x08, 0xfd, 0x87, 0x19, 0x23, 0xb4, 0x0f, 0xa3, 0x3e, 0xdb, 0x73, 0xc4, 0xe2, 0xb9, 0x9a, 0x1e, + 0x4b, 0x46, 0xb6, 0x3c, 0x25, 0x98, 0x8e, 0xf2, 0xff, 0x58, 0xb0, 0x33, 0x7f, 0x66, 0xc0, 0x19, + 0x0d, 0xbb, 0xe4, 0x35, 0x3a, 0x2d, 0xe2, 0x04, 0xe8, 0x21, 0x18, 0x71, 0xac, 0x16, 0x11, 0x0b, + 0x45, 0x89, 0xbc, 0x61, 0xb5, 0x08, 0x66, 0x10, 0xf4, 0x30, 0xe4, 0xf6, 0xac, 0x66, 0x87, 0xb0, + 0x4e, 0x2a, 0x94, 0x27, 0x05, 0x4a, 0xee, 0x06, 0x2d, 0xc4, 0x1c, 0x86, 0x5e, 0x81, 0x02, 0xfb, + 0x71, 0xc9, 0x73, 0x5b, 0x29, 0x35, 0x4d, 0x48, 0x78, 0x43, 0x92, 0x2d, 0x4f, 0x1e, 0x76, 0x8b, + 0x05, 0xf5, 0x17, 0x87, 0x0c, 0xcd, 0xbf, 0x32, 0x60, 0x5a, 0x6b, 0xdc, 0x9a, 0xed, 0x07, 0xe8, + 0x23, 0x3d, 0x93, 0x67, 0x71, 0xb0, 0xc9, 0x43, 0x6b, 0xb3, 0xa9, 0x33, 0x23, 0x5a, 0x9a, 0x97, + 0x25, 0xda, 0xc4, 0x71, 0x20, 0x67, 0x07, 0xa4, 0xe5, 0xcf, 0x67, 0x1e, 0xca, 0x3e, 0x3a, 0xfe, + 0xf8, 0x6a, 0x6a, 0xc3, 0x18, 0xf6, 0xef, 0x2a, 0xa5, 0x8f, 0x39, 0x1b, 0xf3, 0x9b, 0x23, 0x91, + 0x16, 0xd2, 0x19, 0x85, 0x5c, 0x18, 0x6b, 0x91, 0xc0, 0xb3, 0x6b, 0x7c, 0x5d, 0x8d, 0x3f, 0xbe, + 0x32, 0x9c, 0x14, 0xeb, 0x8c, 0x58, 0xb8, 0x59, 0xf2, 0xff, 0x3e, 0x96, 0x5c, 0xd0, 0x0e, 0x8c, + 0x58, 0x5e, 0x43, 0xb6, 0xf9, 0x52, 0x3a, 0xe3, 0x1b, 0xce, 0xb9, 0x92, 0xd7, 0xf0, 0x31, 0xe3, + 0x80, 0x96, 0xa0, 0x10, 0x10, 0xaf, 0x65, 0x3b, 0x56, 0xc0, 0x77, 0xd7, 0x7c, 0x79, 0x56, 0xa0, + 0x15, 0x36, 0x25, 0x00, 0x87, 0x38, 0xa8, 0x09, 0xa3, 0x75, 0xef, 0x00, 0x77, 0x9c, 0xf9, 0x91, + 0x34, 0xba, 0x62, 0x85, 0xd1, 0x0a, 0x17, 0x13, 0xff, 0x8f, 0x05, 0x0f, 0xf4, 0x35, 0x03, 0xe6, + 0x5a, 0xc4, 0xf2, 0x3b, 0x1e, 0xa1, 0x4d, 0xc0, 0x24, 0x20, 0x0e, 0xdd, 0x0d, 0xe7, 0x73, 0x8c, + 0x39, 0x1e, 0x76, 0x1c, 0x7a, 0x29, 0x97, 0x1f, 0x14, 0xa2, 0xcc, 0x25, 0x41, 0x71, 0xa2, 0x34, + 0xe6, 0x4f, 0x47, 0x60, 0xb6, 0x67, 0x87, 0x40, 0x4f, 0x42, 0xae, 0xbd, 0x63, 0xf9, 0x72, 0xc9, + 0x5f, 0x90, 0xf3, 0xad, 0x42, 0x0b, 0xef, 0x74, 0x8b, 0x93, 0xb2, 0x0a, 0x2b, 0xc0, 0x1c, 0x99, + 0xea, 0xd4, 0x16, 0xf1, 0x7d, 0xab, 0x21, 0xf7, 0x01, 0x6d, 0x9a, 0xb0, 0x62, 0x2c, 0xe1, 0xe8, + 0xbf, 0x1a, 0x30, 0xc9, 0xa7, 0x0c, 0x26, 0x7e, 0xa7, 0x19, 0xd0, 0xbd, 0x8e, 0x76, 0xcb, 0x95, + 0x34, 0xa6, 0x27, 0x27, 0x59, 0x3e, 0x2b, 0xb8, 0x4f, 0xea, 0xa5, 0x3e, 0x8e, 0xf2, 0x45, 0x37, + 0xa1, 0xe0, 0x07, 0x96, 0x17, 0x90, 0x7a, 0x29, 0x60, 0x5a, 0x6d, 0xfc, 0xf1, 0x7f, 0x3f, 0xd8, + 0x26, 0xb0, 0x69, 0xb7, 0x08, 0xdf, 0x70, 0xaa, 0x92, 0x00, 0x0e, 0x69, 0xa1, 0x57, 0x00, 0xbc, + 0x8e, 0x53, 0xed, 0xb4, 0x5a, 0x96, 0x77, 0x20, 0x34, 0xf8, 0xe5, 0xe1, 0x9a, 0x87, 0x15, 0xbd, + 0x50, 0x67, 0x85, 0x65, 0x58, 0xe3, 0x87, 0x5e, 0x33, 0x60, 0x92, 0xcf, 0x44, 0x29, 0xc1, 0x68, + 0xca, 0x12, 0xcc, 0xd2, 0xae, 0x5d, 0xd1, 0x59, 0xe0, 0x28, 0x47, 0xf3, 0x2f, 0xa2, 0xfa, 0xa4, + 0x1a, 0xd0, 0xd3, 0x75, 0xe3, 0x00, 0x7d, 0x18, 0xee, 0xf7, 0x3b, 0xb5, 0x1a, 0xf1, 0xfd, 0xed, + 0x4e, 0x13, 0x77, 0x9c, 0xcb, 0xb6, 0x1f, 0xb8, 0xde, 0xc1, 0x9a, 0xdd, 0xb2, 0x03, 0x36, 0xe3, + 0x72, 0xe5, 0xf3, 0x87, 0xdd, 0xe2, 0xfd, 0xd5, 0x7e, 0x48, 0xb8, 0x7f, 0x7d, 0x64, 0xc1, 0x03, + 0x1d, 0xa7, 0x3f, 0x79, 0x7e, 0x7a, 0x2b, 0x1e, 0x76, 0x8b, 0x0f, 0x5c, 0xef, 0x8f, 0x86, 0x8f, + 0xa2, 0x61, 0xfe, 0x9d, 0x01, 0x33, 0xb2, 0x5d, 0x9b, 0xa4, 0xd5, 0x6e, 0xd2, 0xdd, 0xe5, 0xf4, + 0x0f, 0x22, 0x41, 0xe4, 0x20, 0x82, 0xd3, 0x51, 0x27, 0x52, 0xfe, 0x7e, 0xa7, 0x11, 0xf3, 0x6f, + 0x0d, 0x98, 0x8b, 0x23, 0xdf, 0x03, 0xe5, 0xe9, 0x47, 0x95, 0xe7, 0x46, 0xba, 0xad, 0xed, 0xa3, + 0x41, 0xdf, 0x18, 0xe9, 0x6d, 0xeb, 0xbf, 0x76, 0x35, 0x1a, 0x6a, 0xc5, 0xec, 0x6f, 0x52, 0x2b, + 0x8e, 0xbc, 0xad, 0xb4, 0xe2, 0xef, 0x8c, 0xc0, 0x44, 0xc9, 0x09, 0xec, 0xd2, 0xf6, 0xb6, 0xed, + 0xd8, 0xc1, 0x01, 0xfa, 0x7c, 0x06, 0x96, 0xda, 0x1e, 0xd9, 0x26, 0x9e, 0x47, 0xea, 0x2b, 0x1d, + 0xcf, 0x76, 0x1a, 0xd5, 0xda, 0x0e, 0xa9, 0x77, 0x9a, 0xb6, 0xd3, 0x58, 0x6d, 0x38, 0xae, 0x2a, + 0xbe, 0x78, 0x9b, 0xd4, 0x3a, 0xac, 0x49, 0x7c, 0x51, 0xb4, 0x86, 0x6b, 0x52, 0xe5, 0x78, 0x4c, + 0xcb, 0x4f, 0x1c, 0x76, 0x8b, 0x4b, 0xc7, 0xac, 0x84, 0x8f, 0xdb, 0x34, 0xf4, 0xb9, 0x0c, 0x2c, + 0x7a, 0xe4, 0x13, 0x1d, 0x7b, 0xf0, 0xde, 0xe0, 0xbb, 0x56, 0x73, 0x48, 0xf5, 0x73, 0x2c, 0x9e, + 0xe5, 0xc7, 0x0f, 0xbb, 0xc5, 0x63, 0xd6, 0xc1, 0xc7, 0x6c, 0x97, 0x59, 0x81, 0xf1, 0x52, 0xdb, + 0xf6, 0xed, 0xdb, 0xf4, 0x2e, 0x4b, 0x06, 0xb8, 0x2b, 0x15, 0x21, 0xe7, 0x75, 0x9a, 0x84, 0xaf, + 0xed, 0x42, 0xb9, 0x40, 0x77, 0x21, 0x4c, 0x0b, 0x30, 0x2f, 0x37, 0x3f, 0x43, 0x77, 0x5c, 0x46, + 0x32, 0x76, 0x4b, 0xbe, 0x05, 0x39, 0x8f, 0x32, 0x11, 0x33, 0x6b, 0xd8, 0x0b, 0x45, 0x28, 0xb5, + 0x10, 0x82, 0xfe, 0xc4, 0x9c, 0x85, 0xf9, 0xed, 0x0c, 0x9c, 0x2d, 0xb5, 0xdb, 0xeb, 0xc4, 0xdf, + 0x89, 0x49, 0xf1, 0x45, 0x03, 0xa6, 0xf6, 0x6c, 0x2f, 0xe8, 0x58, 0x4d, 0x69, 0xdb, 0xe0, 0xf2, + 0x54, 0x87, 0x95, 0x87, 0x71, 0xbb, 0x11, 0x21, 0x5d, 0x46, 0x87, 0xdd, 0xe2, 0x54, 0xb4, 0x0c, + 0xc7, 0xd8, 0xa3, 0xff, 0x65, 0xc0, 0x8c, 0x28, 0xda, 0x70, 0xeb, 0x44, 0x37, 0x88, 0x5d, 0x4f, + 0x53, 0x26, 0x45, 0x9c, 0x5b, 0x4e, 0xe2, 0xa5, 0xb8, 0x47, 0x08, 0xf3, 0x1f, 0x32, 0x70, 0xae, + 0x0f, 0x0d, 0xf4, 0xff, 0x0d, 0x98, 0xe3, 0x56, 0x34, 0x0d, 0x84, 0xc9, 0xb6, 0xe8, 0xcd, 0x0f, + 0xa5, 0x2d, 0x39, 0xa6, 0x4b, 0x9c, 0x38, 0x35, 0x52, 0x9e, 0xa7, 0xbb, 0xe1, 0x72, 0x02, 0x6b, + 0x9c, 0x28, 0x10, 0x93, 0x94, 0xdb, 0xd5, 0x62, 0x92, 0x66, 0xee, 0x89, 0xa4, 0xd5, 0x04, 0xd6, + 0x38, 0x51, 0x20, 0xf3, 0x3f, 0xc3, 0x03, 0x47, 0x90, 0xbb, 0xfb, 0xe2, 0x34, 0x5f, 0x52, 0xb3, + 0x3e, 0x3a, 0xe7, 0x06, 0x58, 0xd7, 0x26, 0x8c, 0xb2, 0xa5, 0x23, 0x17, 0x36, 0x50, 0xf5, 0xc7, + 0xd6, 0x94, 0x8f, 0x05, 0xc4, 0xfc, 0xb6, 0x01, 0xf9, 0x63, 0x98, 0x55, 0x8a, 0x51, 0xb3, 0x4a, + 0xa1, 0xc7, 0xa4, 0x12, 0xf4, 0x9a, 0x54, 0x9e, 0x1f, 0x6e, 0x34, 0x06, 0x31, 0xa5, 0xfc, 0xd2, + 0x80, 0xd9, 0x1e, 0xd3, 0x0b, 0xda, 0x81, 0xb9, 0xb6, 0x5b, 0x97, 0xc7, 0xa6, 0xcb, 0x96, 0xbf, + 0xc3, 0x60, 0xa2, 0x79, 0x4f, 0xd2, 0x91, 0xac, 0x24, 0xc0, 0xef, 0x74, 0x8b, 0xf3, 0x8a, 0x48, + 0x0c, 0x01, 0x27, 0x52, 0x44, 0x6d, 0xc8, 0x6f, 0xdb, 0xa4, 0x59, 0x0f, 0xa7, 0xe0, 0x90, 0x07, + 0xa4, 0x4b, 0x82, 0x1a, 0xb7, 0x3a, 0xca, 0x7f, 0x58, 0x71, 0x31, 0xaf, 0xc1, 0x54, 0xd4, 0x06, + 0x3d, 0xc0, 0xe0, 0x9d, 0x87, 0xac, 0xe5, 0x39, 0x62, 0xe8, 0xc6, 0x05, 0x42, 0xb6, 0x84, 0x37, + 0x30, 0x2d, 0x37, 0x7f, 0x3d, 0x02, 0xd3, 0xe5, 0x66, 0x87, 0x3c, 0xef, 0x11, 0x22, 0xaf, 0xdd, + 0x25, 0x98, 0x6e, 0x7b, 0x64, 0xcf, 0x26, 0xfb, 0x55, 0xd2, 0x24, 0xb5, 0xc0, 0xf5, 0x04, 0xfd, + 0x73, 0xa2, 0xfa, 0x74, 0x25, 0x0a, 0xc6, 0x71, 0x7c, 0xf4, 0x1c, 0x4c, 0x59, 0xb5, 0xc0, 0xde, + 0x23, 0x8a, 0x02, 0x17, 0xe0, 0x1d, 0x82, 0xc2, 0x54, 0x29, 0x02, 0xc5, 0x31, 0x6c, 0xf4, 0x11, + 0x98, 0xf7, 0x6b, 0x56, 0x93, 0x5c, 0x6f, 0x0b, 0x56, 0xcb, 0x3b, 0xa4, 0xb6, 0x5b, 0x71, 0x6d, + 0x27, 0x10, 0x46, 0x96, 0x87, 0x04, 0xa5, 0xf9, 0x6a, 0x1f, 0x3c, 0xdc, 0x97, 0x02, 0xfa, 0x23, + 0x03, 0xce, 0xb7, 0x3d, 0x52, 0xf1, 0xdc, 0x96, 0x4b, 0xb5, 0x67, 0x8f, 0xe5, 0x41, 0xdc, 0xc0, + 0x6f, 0x0c, 0x79, 0x4c, 0xe0, 0x25, 0xbd, 0x96, 0xcf, 0x77, 0x1e, 0x76, 0x8b, 0xe7, 0x2b, 0x47, + 0x09, 0x80, 0x8f, 0x96, 0x0f, 0xfd, 0x89, 0x01, 0x17, 0xda, 0xae, 0x1f, 0x1c, 0xd1, 0x84, 0xdc, + 0xa9, 0x36, 0xc1, 0x3c, 0xec, 0x16, 0x2f, 0x54, 0x8e, 0x94, 0x00, 0xdf, 0x45, 0x42, 0xf3, 0x70, + 0x1c, 0x66, 0xb5, 0xb9, 0x27, 0xae, 0xe5, 0xcf, 0xc0, 0xa4, 0x9c, 0x0c, 0xa1, 0x5a, 0x2f, 0x84, + 0x66, 0x94, 0x92, 0x0e, 0xc4, 0x51, 0x5c, 0x3a, 0xef, 0xd4, 0x54, 0xe4, 0xb5, 0x63, 0xf3, 0xae, + 0x12, 0x81, 0xe2, 0x18, 0x36, 0x5a, 0x85, 0x33, 0xa2, 0x04, 0x93, 0x76, 0xd3, 0xae, 0x59, 0xcb, + 0x6e, 0x47, 0x4c, 0xb9, 0x5c, 0xf9, 0xdc, 0x61, 0xb7, 0x78, 0xa6, 0xd2, 0x0b, 0xc6, 0x49, 0x75, + 0xd0, 0x1a, 0xcc, 0x59, 0x9d, 0xc0, 0x55, 0xed, 0xbf, 0xe8, 0x50, 0x4d, 0x51, 0x67, 0x53, 0x2b, + 0xcf, 0x55, 0x4a, 0x29, 0x01, 0x8e, 0x13, 0x6b, 0xa1, 0x4a, 0x8c, 0x5a, 0x95, 0xd4, 0x5c, 0xa7, + 0xce, 0x47, 0x39, 0x17, 0x5e, 0x2e, 0x4a, 0x09, 0x38, 0x38, 0xb1, 0x26, 0x6a, 0xc2, 0x54, 0xcb, + 0xba, 0x7d, 0xdd, 0xb1, 0xf6, 0x2c, 0xbb, 0x49, 0x99, 0x08, 0xd3, 0x4c, 0x7f, 0x7b, 0x41, 0x27, + 0xb0, 0x9b, 0x8b, 0xfc, 0x95, 0x72, 0x71, 0xd5, 0x09, 0xae, 0x7a, 0xd5, 0x80, 0x1e, 0x42, 0xf9, + 0xe1, 0x68, 0x3d, 0x42, 0x0b, 0xc7, 0x68, 0xa3, 0xab, 0x70, 0x96, 0x2d, 0xc7, 0x15, 0x77, 0xdf, + 0x59, 0x21, 0x4d, 0xeb, 0x40, 0x36, 0x60, 0x8c, 0x35, 0xe0, 0xfe, 0xc3, 0x6e, 0xf1, 0x6c, 0x35, + 0x09, 0x01, 0x27, 0xd7, 0x43, 0x16, 0x3c, 0x10, 0x05, 0x60, 0xb2, 0x67, 0xfb, 0xb6, 0xeb, 0x70, + 0x03, 0x4b, 0x3e, 0x34, 0xb0, 0x54, 0xfb, 0xa3, 0xe1, 0xa3, 0x68, 0xa0, 0xff, 0x63, 0xc0, 0x5c, + 0xd2, 0x32, 0x9c, 0x2f, 0xa4, 0xf1, 0x06, 0x13, 0x5b, 0x5a, 0x7c, 0x46, 0x24, 0x6e, 0x0a, 0x89, + 0x42, 0xa0, 0x57, 0x0d, 0x98, 0xb0, 0xb4, 0xcb, 0xe1, 0x3c, 0x30, 0xa9, 0xae, 0x0c, 0x6b, 0xa2, + 0x08, 0x29, 0x96, 0x67, 0x0e, 0xbb, 0xc5, 0xc8, 0x05, 0x14, 0x47, 0x38, 0xa2, 0xff, 0x6b, 0xc0, + 0xd9, 0xc4, 0x35, 0x3e, 0x3f, 0x7e, 0x1a, 0x3d, 0xc4, 0x26, 0x49, 0xf2, 0x9e, 0x93, 0x2c, 0x06, + 0x7a, 0xd3, 0x50, 0xaa, 0x6c, 0x5d, 0x1a, 0x89, 0x26, 0x98, 0x68, 0xd7, 0x86, 0xbc, 0x0f, 0x87, + 0x07, 0x02, 0x49, 0xb8, 0x7c, 0x46, 0xd3, 0x8c, 0xb2, 0x10, 0xc7, 0xd9, 0xa3, 0x2f, 0x18, 0x52, + 0x35, 0x2a, 0x89, 0x26, 0x4f, 0x4b, 0x22, 0x14, 0x6a, 0x5a, 0x25, 0x50, 0x8c, 0x39, 0xfa, 0x28, + 0x2c, 0x58, 0x5b, 0xae, 0x17, 0x24, 0x2e, 0xbe, 0xf9, 0x29, 0xb6, 0x8c, 0x2e, 0x1c, 0x76, 0x8b, + 0x0b, 0xa5, 0xbe, 0x58, 0xf8, 0x08, 0x0a, 0xe6, 0x37, 0x72, 0x30, 0xc1, 0x0f, 0xf9, 0x42, 0x75, + 0x7d, 0xcb, 0x80, 0x07, 0x6b, 0x1d, 0xcf, 0x23, 0x4e, 0x50, 0x0d, 0x48, 0xbb, 0x57, 0x71, 0x19, + 0xa7, 0xaa, 0xb8, 0x1e, 0x3a, 0xec, 0x16, 0x1f, 0x5c, 0x3e, 0x82, 0x3f, 0x3e, 0x52, 0x3a, 0xf4, + 0xe7, 0x06, 0x98, 0x02, 0xa1, 0x6c, 0xd5, 0x76, 0x1b, 0x9e, 0xdb, 0x71, 0xea, 0xbd, 0x8d, 0xc8, + 0x9c, 0x6a, 0x23, 0x1e, 0x39, 0xec, 0x16, 0xcd, 0xe5, 0xbb, 0x4a, 0x81, 0x07, 0x90, 0x14, 0x3d, + 0x0f, 0xb3, 0x02, 0xeb, 0xe2, 0xed, 0x36, 0xf1, 0x6c, 0x7a, 0x9c, 0x16, 0x6e, 0x02, 0xa1, 0xe7, + 0x45, 0x1c, 0x01, 0xf7, 0xd6, 0x41, 0x3e, 0x8c, 0xed, 0x13, 0xbb, 0xb1, 0x13, 0xc8, 0xe3, 0xd3, + 0x90, 0xee, 0x16, 0xe2, 0xc2, 0x7f, 0x93, 0xd3, 0x2c, 0x8f, 0x1f, 0x76, 0x8b, 0x63, 0xe2, 0x0f, + 0x96, 0x9c, 0xd0, 0x06, 0x4c, 0xf1, 0x2b, 0x58, 0xc5, 0x76, 0x1a, 0x15, 0xd7, 0xe1, 0x4e, 0x0a, + 0x85, 0xf2, 0x23, 0x52, 0xe1, 0x57, 0x23, 0xd0, 0x3b, 0xdd, 0xe2, 0x84, 0xfc, 0xbd, 0x79, 0xd0, + 0x26, 0x38, 0x56, 0xdb, 0xfc, 0xde, 0x28, 0x80, 0x9c, 0xae, 0xa4, 0x8d, 0xfe, 0x03, 0x14, 0x7c, + 0x12, 0x70, 0xae, 0xe2, 0x4d, 0x80, 0x3f, 0xb5, 0xc8, 0x42, 0x1c, 0xc2, 0xd1, 0x2e, 0xe4, 0xda, + 0x56, 0xc7, 0x27, 0x62, 0xf0, 0xaf, 0xa4, 0x32, 0xf8, 0x15, 0x4a, 0x91, 0xdf, 0xb9, 0xd8, 0x4f, + 0xcc, 0x79, 0xa0, 0xcf, 0x1a, 0x00, 0x24, 0x3a, 0x60, 0x43, 0xdb, 0x3e, 0x04, 0xcb, 0x70, 0x4c, + 0x69, 0x1f, 0x94, 0xa7, 0x0e, 0xbb, 0x45, 0xd0, 0x86, 0x5e, 0x63, 0x8b, 0xf6, 0x21, 0x6f, 0xc9, + 0x3d, 0x7f, 0xe4, 0x34, 0xf6, 0x7c, 0x76, 0x15, 0x52, 0x93, 0x56, 0x31, 0x43, 0x9f, 0x33, 0x60, + 0xca, 0x27, 0x81, 0x18, 0x2a, 0xba, 0xf3, 0x88, 0x03, 0xef, 0x90, 0x93, 0xae, 0x1a, 0xa1, 0xc9, + 0x77, 0xd0, 0x68, 0x19, 0x8e, 0xf1, 0x95, 0xa2, 0x5c, 0x26, 0x56, 0x9d, 0x78, 0xec, 0xa6, 0x2d, + 0x4e, 0x52, 0xc3, 0x8b, 0xa2, 0xd1, 0x54, 0xa2, 0x68, 0x65, 0x38, 0xc6, 0x57, 0x8a, 0xb2, 0x6e, + 0x7b, 0x9e, 0x2b, 0x44, 0xc9, 0xa7, 0x24, 0x8a, 0x46, 0x53, 0x89, 0xa2, 0x95, 0xe1, 0x18, 0x5f, + 0xf3, 0xab, 0x93, 0x30, 0x25, 0x17, 0x52, 0x78, 0xb2, 0xe7, 0x86, 0x9d, 0x3e, 0x27, 0xfb, 0x65, + 0x1d, 0x88, 0xa3, 0xb8, 0xb4, 0x32, 0x5f, 0xaa, 0xd1, 0x83, 0xbd, 0xaa, 0x5c, 0xd5, 0x81, 0x38, + 0x8a, 0x8b, 0x5a, 0x90, 0xf3, 0x03, 0xd2, 0x96, 0xcf, 0xbb, 0x43, 0xbe, 0x3e, 0x86, 0xfb, 0x43, + 0xf8, 0x80, 0x43, 0xff, 0xf9, 0x98, 0x73, 0x61, 0xb6, 0xc9, 0x20, 0x62, 0xae, 0x14, 0x8b, 0x23, + 0x9d, 0xf5, 0x19, 0xb5, 0x84, 0xf2, 0xd1, 0x88, 0x96, 0xe1, 0x18, 0xfb, 0x84, 0xc3, 0x7e, 0xee, + 0x14, 0x0f, 0xfb, 0x2f, 0x42, 0xbe, 0x65, 0xdd, 0xae, 0x76, 0xbc, 0xc6, 0xc9, 0x2f, 0x15, 0xc2, + 0xf3, 0x8a, 0x53, 0xc1, 0x8a, 0x1e, 0x7a, 0xcd, 0xd0, 0xb6, 0x9c, 0x31, 0x46, 0xfc, 0x66, 0xba, + 0x5b, 0x8e, 0xd2, 0x95, 0x7d, 0x37, 0x9f, 0x9e, 0xa3, 0x77, 0xfe, 0x9e, 0x1f, 0xbd, 0xe9, 0x31, + 0x92, 0x2f, 0x10, 0x75, 0x8c, 0x2c, 0x9c, 0xea, 0x31, 0x72, 0x39, 0xc2, 0x0c, 0xc7, 0x98, 0x33, + 0x79, 0xf8, 0x9a, 0x53, 0xf2, 0xc0, 0xa9, 0xca, 0x53, 0x8d, 0x30, 0xc3, 0x31, 0xe6, 0xfd, 0xef, + 0x9b, 0xe3, 0xa7, 0x73, 0xdf, 0x9c, 0x48, 0xe1, 0xbe, 0x79, 0xf4, 0x51, 0x7c, 0x72, 0xd8, 0xa3, + 0x38, 0xba, 0x02, 0xa8, 0x7e, 0xe0, 0x58, 0x2d, 0xbb, 0x26, 0x36, 0x4b, 0xa6, 0x36, 0xa7, 0x98, + 0x3d, 0x62, 0x41, 0x6c, 0x64, 0x68, 0xa5, 0x07, 0x03, 0x27, 0xd4, 0x42, 0x01, 0xe4, 0xdb, 0xf2, + 0xc4, 0x35, 0x9d, 0xc6, 0xec, 0x97, 0x27, 0x30, 0xee, 0x01, 0x40, 0x17, 0x9e, 0x2c, 0xc1, 0x8a, + 0x13, 0x5a, 0x83, 0xb9, 0x96, 0xed, 0x54, 0xdc, 0xba, 0x5f, 0x21, 0x9e, 0xb0, 0xb6, 0x54, 0x49, + 0x30, 0x3f, 0xc3, 0xfa, 0x86, 0xdd, 0xa0, 0xd7, 0x13, 0xe0, 0x38, 0xb1, 0x96, 0xf9, 0x4f, 0x06, + 0xcc, 0x2c, 0x37, 0xdd, 0x4e, 0xfd, 0xa6, 0x15, 0xd4, 0x76, 0xf8, 0xe3, 0x37, 0x7a, 0x0e, 0xf2, + 0xb6, 0x13, 0x10, 0x6f, 0xcf, 0x6a, 0x0a, 0xfd, 0x64, 0x4a, 0xff, 0x80, 0x55, 0x51, 0x7e, 0xa7, + 0x5b, 0x9c, 0x5a, 0xe9, 0x78, 0xcc, 0xab, 0x94, 0xef, 0x56, 0x58, 0xd5, 0x41, 0x5f, 0x35, 0x60, + 0x96, 0x3f, 0x9f, 0xaf, 0x58, 0x81, 0x75, 0xad, 0x43, 0x3c, 0x9b, 0xc8, 0x07, 0xf4, 0x21, 0x37, + 0xaa, 0xb8, 0xac, 0x92, 0xc1, 0x41, 0x78, 0x50, 0x5f, 0x8f, 0x73, 0xc6, 0xbd, 0xc2, 0x98, 0x5f, + 0xca, 0xc2, 0xfd, 0x7d, 0x69, 0xa1, 0x05, 0xc8, 0xd8, 0x75, 0xd1, 0x74, 0x10, 0x74, 0x33, 0xab, + 0x75, 0x9c, 0xb1, 0xeb, 0x68, 0x91, 0x9d, 0x39, 0x3d, 0xe2, 0xfb, 0xf2, 0x2d, 0xb5, 0xa0, 0x8e, + 0x87, 0xa2, 0x14, 0x6b, 0x18, 0xa8, 0x08, 0xb9, 0xa6, 0xb5, 0x45, 0x9a, 0xe2, 0x3e, 0xc1, 0x4e, + 0xb1, 0x6b, 0xb4, 0x00, 0xf3, 0x72, 0xf4, 0x19, 0x03, 0x80, 0x0b, 0x48, 0x6f, 0x23, 0x42, 0x4b, + 0xe2, 0x74, 0xbb, 0x89, 0x52, 0xe6, 0x52, 0x86, 0xff, 0xb1, 0xc6, 0x15, 0x6d, 0xc2, 0x28, 0x3d, + 0xd0, 0xba, 0xf5, 0x13, 0x2b, 0x45, 0xf6, 0xc8, 0x52, 0x61, 0x34, 0xb0, 0xa0, 0x45, 0xfb, 0xca, + 0x23, 0x41, 0xc7, 0x73, 0x68, 0xd7, 0x32, 0x35, 0x98, 0xe7, 0x52, 0x60, 0x55, 0x8a, 0x35, 0x0c, + 0xf3, 0x0f, 0x33, 0x30, 0x97, 0x24, 0x3a, 0xd5, 0x36, 0xa3, 0x5c, 0x5a, 0x71, 0x35, 0xfe, 0x60, + 0xfa, 0xfd, 0x23, 0x3c, 0x41, 0x94, 0xbf, 0x84, 0xf0, 0x55, 0x13, 0x7c, 0xd1, 0x07, 0x55, 0x0f, + 0x65, 0x4e, 0xd8, 0x43, 0x8a, 0x72, 0xac, 0x97, 0x1e, 0x82, 0x11, 0x9f, 0x8e, 0x7c, 0x36, 0xfa, + 0x80, 0xc1, 0xc6, 0x88, 0x41, 0x28, 0x46, 0xc7, 0xb1, 0x03, 0xe1, 0xea, 0xad, 0x30, 0xae, 0x3b, + 0x76, 0x80, 0x19, 0xc4, 0xfc, 0x4a, 0x06, 0x16, 0xfa, 0x37, 0x0a, 0x7d, 0xc5, 0x00, 0xa8, 0xd3, + 0xeb, 0x0a, 0x9d, 0x92, 0xd2, 0x73, 0xc6, 0x3a, 0xad, 0x3e, 0x5c, 0x91, 0x9c, 0x42, 0x37, 0x2a, + 0x55, 0xe4, 0x63, 0x4d, 0x10, 0xf4, 0xb8, 0x9c, 0xfa, 0x1b, 0x56, 0x4b, 0x1e, 0x67, 0x55, 0x9d, + 0x75, 0x05, 0xc1, 0x1a, 0x16, 0xbd, 0x8f, 0x3a, 0x56, 0x8b, 0xf8, 0x6d, 0x4b, 0xf9, 0xf2, 0xb3, + 0xfb, 0xe8, 0x86, 0x2c, 0xc4, 0x21, 0xdc, 0x6c, 0xc2, 0xc3, 0x03, 0xc8, 0x99, 0x92, 0x5f, 0xb5, + 0xf9, 0x8f, 0x06, 0x9c, 0x5b, 0x6e, 0x76, 0xfc, 0x80, 0x78, 0xff, 0x66, 0xbc, 0xd2, 0xfe, 0xd9, + 0x80, 0x07, 0xfa, 0xb4, 0xf9, 0x1e, 0x38, 0xa7, 0xbd, 0x1c, 0x75, 0x4e, 0xbb, 0x3e, 0xec, 0x94, + 0x4e, 0x6c, 0x47, 0x1f, 0x1f, 0xb5, 0x00, 0x26, 0xe9, 0xae, 0x55, 0x77, 0x1b, 0x29, 0xe9, 0xcd, + 0x87, 0x21, 0xf7, 0x09, 0xaa, 0x7f, 0xe2, 0x73, 0x8c, 0x29, 0x25, 0xcc, 0x61, 0xe6, 0xb3, 0x20, + 0x3c, 0xb9, 0x62, 0x8b, 0xc7, 0x18, 0x64, 0xf1, 0x98, 0x7f, 0x99, 0x01, 0xcd, 0x8e, 0x71, 0x0f, + 0x26, 0xa5, 0x13, 0x99, 0x94, 0x43, 0xde, 0xc1, 0x35, 0xab, 0x4c, 0xbf, 0x90, 0x8d, 0xbd, 0x58, + 0xc8, 0xc6, 0x46, 0x6a, 0x1c, 0x8f, 0x8e, 0xd8, 0xf8, 0xb1, 0x01, 0x0f, 0x84, 0xc8, 0xbd, 0x26, + 0xc6, 0xbb, 0xef, 0x30, 0x4f, 0xc1, 0xb8, 0x15, 0x56, 0x13, 0x73, 0x40, 0x45, 0x29, 0x69, 0x14, + 0xb1, 0x8e, 0x17, 0x3a, 0x88, 0x67, 0x4f, 0xe8, 0x20, 0x3e, 0x72, 0xb4, 0x83, 0xb8, 0xf9, 0xab, + 0x0c, 0x9c, 0xef, 0x6d, 0x99, 0x5c, 0x1b, 0x83, 0xbd, 0xc0, 0x3f, 0x0d, 0x13, 0x81, 0xa8, 0xa0, + 0xed, 0xf4, 0x2a, 0xc6, 0x6e, 0x53, 0x83, 0xe1, 0x08, 0x26, 0xad, 0x59, 0xe3, 0xab, 0xb2, 0x5a, + 0x73, 0xdb, 0x32, 0xbc, 0x40, 0xd5, 0x5c, 0xd6, 0x60, 0x38, 0x82, 0xa9, 0x1c, 0x37, 0x47, 0x4e, + 0xdd, 0x71, 0xb3, 0x0a, 0x67, 0xa5, 0xab, 0xda, 0x25, 0xd7, 0x5b, 0x76, 0x5b, 0xed, 0x26, 0x11, + 0x01, 0x06, 0x54, 0xd8, 0xf3, 0xa2, 0xca, 0x59, 0x9c, 0x84, 0x84, 0x93, 0xeb, 0x9a, 0x3f, 0xce, + 0xc2, 0x99, 0xb0, 0xdb, 0x97, 0x5d, 0xa7, 0x6e, 0x33, 0x87, 0xbf, 0x67, 0x60, 0x24, 0x38, 0x68, + 0xcb, 0xce, 0xfe, 0x77, 0x52, 0x9c, 0xcd, 0x83, 0x36, 0x1d, 0xed, 0x73, 0x09, 0x55, 0x98, 0x91, + 0x97, 0x55, 0x42, 0x6b, 0x6a, 0x75, 0xf0, 0x11, 0x78, 0x32, 0x3a, 0x9b, 0xef, 0x74, 0x8b, 0x09, + 0x21, 0xa6, 0x8b, 0x8a, 0x52, 0x74, 0xce, 0xa3, 0x5b, 0x30, 0xd5, 0xb4, 0xfc, 0xe0, 0x7a, 0xbb, + 0x6e, 0x05, 0x64, 0xd3, 0x6e, 0x11, 0xb1, 0xe6, 0x8e, 0xe3, 0xb5, 0xaf, 0x5e, 0xa5, 0xd7, 0x22, + 0x94, 0x70, 0x8c, 0x32, 0xda, 0x03, 0x44, 0x4b, 0x36, 0x3d, 0xcb, 0xf1, 0x79, 0xab, 0x28, 0xbf, + 0xe3, 0x47, 0x09, 0xa8, 0x4b, 0xde, 0x5a, 0x0f, 0x35, 0x9c, 0xc0, 0x01, 0x3d, 0x02, 0xa3, 0x1e, + 0xb1, 0x7c, 0x31, 0x98, 0x85, 0x70, 0xfd, 0x63, 0x56, 0x8a, 0x05, 0x54, 0x5f, 0x50, 0xa3, 0x77, + 0x59, 0x50, 0x3f, 0x37, 0x60, 0x2a, 0x1c, 0xa6, 0x7b, 0xa0, 0x24, 0x5b, 0x51, 0x25, 0x79, 0x39, + 0xad, 0x2d, 0xb1, 0x8f, 0x5e, 0xfc, 0xd3, 0x51, 0xbd, 0x7d, 0xcc, 0x6b, 0xfb, 0x93, 0x50, 0x90, + 0xab, 0x5a, 0x9e, 0x3e, 0x87, 0xbc, 0x2b, 0x47, 0xce, 0x25, 0x5a, 0xb4, 0x91, 0x60, 0x82, 0x43, + 0x7e, 0x54, 0x2d, 0xd7, 0x85, 0xca, 0x15, 0xd3, 0x5e, 0xa9, 0x65, 0xa9, 0x8a, 0x93, 0xd4, 0xb2, + 0xac, 0x83, 0xae, 0xc3, 0xb9, 0xb6, 0xe7, 0xb2, 0x08, 0xd4, 0x15, 0x62, 0xd5, 0x9b, 0xb6, 0x43, + 0xa4, 0x41, 0x82, 0x3b, 0x45, 0x3c, 0x70, 0xd8, 0x2d, 0x9e, 0xab, 0x24, 0xa3, 0xe0, 0x7e, 0x75, + 0xa3, 0x51, 0x53, 0x23, 0x03, 0x44, 0x4d, 0xfd, 0x37, 0x65, 0xf6, 0x23, 0xbe, 0x88, 0x5d, 0xfa, + 0x70, 0x5a, 0x43, 0x99, 0xb0, 0xad, 0x87, 0x53, 0xaa, 0x24, 0x98, 0x62, 0xc5, 0xbe, 0xbf, 0x6d, + 0x69, 0xf4, 0x84, 0xb6, 0xa5, 0xd0, 0xf9, 0x7d, 0xec, 0x37, 0xe9, 0xfc, 0x9e, 0x7f, 0x5b, 0x39, + 0xbf, 0xbf, 0x9e, 0x83, 0x99, 0xf8, 0x09, 0xe4, 0xf4, 0x23, 0xc2, 0xfe, 0xa7, 0x01, 0x33, 0x72, + 0xf5, 0x70, 0x9e, 0x44, 0xbe, 0x1a, 0xac, 0xa5, 0xb4, 0x68, 0xf9, 0x59, 0x4a, 0xc5, 0x2c, 0x6f, + 0xc6, 0xb8, 0xe1, 0x1e, 0xfe, 0xe8, 0x25, 0x18, 0x57, 0xc6, 0xf5, 0x13, 0x85, 0x87, 0x4d, 0xb3, + 0x53, 0x54, 0x48, 0x02, 0xeb, 0xf4, 0xd0, 0xeb, 0x06, 0x40, 0x4d, 0xaa, 0x39, 0xb9, 0xba, 0xae, + 0xa5, 0xb5, 0xba, 0x94, 0x02, 0x0d, 0x0f, 0xcb, 0xaa, 0xc8, 0xc7, 0x1a, 0x63, 0xf4, 0x25, 0x66, + 0x56, 0x57, 0xa7, 0x3b, 0xba, 0x9e, 0xb2, 0xc3, 0x3b, 0xf6, 0x1e, 0x71, 0x30, 0x0d, 0x8f, 0x52, + 0x1a, 0xc8, 0xc7, 0x11, 0x21, 0xcc, 0x67, 0x40, 0xb9, 0x62, 0xd2, 0x6d, 0x8b, 0x39, 0x63, 0x56, + 0xac, 0x60, 0x47, 0x4c, 0x41, 0xb5, 0x6d, 0x5d, 0x92, 0x00, 0x1c, 0xe2, 0x98, 0x1f, 0x87, 0xa9, + 0xe7, 0x3d, 0xab, 0xbd, 0x63, 0x33, 0xf3, 0x35, 0xbd, 0x27, 0xbd, 0x1b, 0xc6, 0xac, 0x7a, 0x3d, + 0x29, 0xe2, 0xbf, 0xc4, 0x8b, 0xb1, 0x84, 0x0f, 0x76, 0x25, 0xfa, 0x9e, 0x01, 0x28, 0x7c, 0x02, + 0xb4, 0x9d, 0xc6, 0x3a, 0xbd, 0xed, 0xd3, 0xfb, 0xd1, 0x0e, 0x2b, 0x4d, 0xba, 0x1f, 0x5d, 0x56, + 0x10, 0xac, 0x61, 0xa1, 0x57, 0x60, 0x9c, 0xff, 0xbb, 0xa1, 0x2e, 0xfb, 0x43, 0xbb, 0xf7, 0x73, + 0x85, 0xc2, 0x64, 0xe2, 0xb3, 0xf0, 0x72, 0xc8, 0x01, 0xeb, 0xec, 0x68, 0x57, 0xad, 0x3a, 0xdb, + 0xcd, 0xce, 0xed, 0xfa, 0x56, 0xd8, 0x55, 0x6d, 0xcf, 0xdd, 0xb6, 0x9b, 0x24, 0xde, 0x55, 0x15, + 0x5e, 0x8c, 0x25, 0x7c, 0xb0, 0xae, 0xfa, 0x8e, 0x01, 0x73, 0xab, 0x7e, 0x60, 0xbb, 0x2b, 0xc4, + 0x0f, 0xa8, 0x5a, 0xa1, 0x9b, 0x4f, 0xa7, 0x39, 0x88, 0x57, 0xf5, 0x0a, 0xcc, 0x88, 0xe7, 0xc8, + 0xce, 0x96, 0x4f, 0x02, 0xed, 0x1c, 0xaf, 0xd6, 0xf1, 0x72, 0x0c, 0x8e, 0x7b, 0x6a, 0x50, 0x2a, + 0xe2, 0x5d, 0x32, 0xa4, 0x92, 0x8d, 0x52, 0xa9, 0xc6, 0xe0, 0xb8, 0xa7, 0x86, 0xf9, 0xc3, 0x2c, + 0x9c, 0x61, 0xcd, 0x88, 0x45, 0x44, 0x7c, 0xa1, 0x5f, 0x44, 0xc4, 0x90, 0x4b, 0x99, 0xf1, 0x3a, + 0x41, 0x3c, 0xc4, 0xff, 0x30, 0x60, 0xba, 0x1e, 0xed, 0xe9, 0x74, 0xcc, 0x33, 0x49, 0x63, 0xc8, + 0xbd, 0xaf, 0x62, 0x85, 0x38, 0xce, 0x1f, 0x7d, 0xd9, 0x80, 0xe9, 0xa8, 0x98, 0x72, 0x77, 0x3f, + 0x85, 0x4e, 0x52, 0xee, 0xd2, 0xd1, 0x72, 0x1f, 0xc7, 0x45, 0x30, 0x7f, 0x90, 0x11, 0x43, 0x7a, + 0x1a, 0xee, 0xfe, 0x68, 0x1f, 0x0a, 0x41, 0xd3, 0xe7, 0x85, 0xa2, 0xb5, 0x43, 0xde, 0x08, 0x37, + 0xd7, 0xaa, 0xdc, 0x13, 0x20, 0x3c, 0xb4, 0x89, 0x12, 0x7a, 0xf8, 0x94, 0xbc, 0x18, 0xe3, 0x5a, + 0x5b, 0x30, 0x4e, 0xe5, 0x2a, 0xba, 0xb9, 0x5c, 0x89, 0x33, 0x16, 0x25, 0x94, 0xb1, 0xe4, 0x65, + 0x7e, 0xdd, 0x80, 0xc2, 0x15, 0x57, 0xee, 0x23, 0x1f, 0x4d, 0xc1, 0xd0, 0xa3, 0xce, 0x83, 0xea, + 0xc5, 0x31, 0xbc, 0x62, 0x3c, 0x17, 0x31, 0xf3, 0x3c, 0xa8, 0xd1, 0x5e, 0x64, 0xd9, 0x8c, 0x28, + 0xa9, 0x2b, 0xee, 0x56, 0x5f, 0x2b, 0xe2, 0xff, 0xcb, 0xc1, 0xe4, 0x0b, 0xd6, 0x01, 0x71, 0x02, + 0xeb, 0xf8, 0x4a, 0xe2, 0x29, 0x18, 0xb7, 0xda, 0xec, 0x49, 0x4b, 0x3b, 0xe3, 0x87, 0x96, 0x93, + 0x10, 0x84, 0x75, 0xbc, 0x70, 0x43, 0xe3, 0xc9, 0x55, 0x92, 0xb6, 0xa2, 0xe5, 0x18, 0x1c, 0xf7, + 0xd4, 0x40, 0x57, 0x00, 0x89, 0x50, 0xd1, 0x52, 0xad, 0xe6, 0x76, 0x1c, 0xbe, 0xa5, 0x71, 0xa3, + 0x8a, 0xba, 0x6c, 0xae, 0xf7, 0x60, 0xe0, 0x84, 0x5a, 0xe8, 0x23, 0x30, 0x5f, 0x63, 0x94, 0xc5, + 0xd5, 0x43, 0xa7, 0xc8, 0xaf, 0x9f, 0xca, 0xe5, 0x7f, 0xb9, 0x0f, 0x1e, 0xee, 0x4b, 0x81, 0x4a, + 0xea, 0x07, 0xae, 0x67, 0x35, 0x88, 0x4e, 0x77, 0x34, 0x2a, 0x69, 0xb5, 0x07, 0x03, 0x27, 0xd4, + 0x42, 0x9f, 0x86, 0x42, 0xb0, 0xe3, 0x11, 0x7f, 0xc7, 0x6d, 0xd6, 0x85, 0x0b, 0xc2, 0x90, 0x96, + 0x36, 0x31, 0xfa, 0x9b, 0x92, 0xaa, 0x36, 0xbd, 0x65, 0x11, 0x0e, 0x79, 0x22, 0x0f, 0x46, 0xfd, + 0x9a, 0xdb, 0x26, 0xbe, 0x38, 0xb2, 0x5f, 0x49, 0x85, 0x3b, 0xb3, 0x1c, 0x69, 0x36, 0x3e, 0xc6, + 0x01, 0x0b, 0x4e, 0xe6, 0x77, 0x33, 0x30, 0xa1, 0x23, 0x0e, 0xb0, 0x37, 0x7d, 0xd6, 0x80, 0x89, + 0x9a, 0xeb, 0x04, 0x9e, 0xdb, 0xe4, 0xf6, 0xab, 0x74, 0x4e, 0x14, 0x94, 0xd4, 0x0a, 0x09, 0x2c, + 0xbb, 0xa9, 0x99, 0xc2, 0x34, 0x36, 0x38, 0xc2, 0x14, 0x7d, 0xde, 0x80, 0xe9, 0xd0, 0x63, 0x2d, + 0x34, 0xa4, 0xa5, 0x2a, 0x88, 0xda, 0xea, 0x2f, 0x46, 0x39, 0xe1, 0x38, 0x6b, 0x73, 0x0b, 0x66, + 0xe2, 0xa3, 0x4d, 0xbb, 0xb2, 0x6d, 0x89, 0xb5, 0x9e, 0x0d, 0xbb, 0xb2, 0x62, 0xf9, 0x3e, 0x66, + 0x10, 0xf4, 0x18, 0xe4, 0x5b, 0x96, 0xd7, 0xb0, 0x1d, 0xab, 0xc9, 0x7a, 0x31, 0xab, 0x6d, 0x48, + 0xa2, 0x1c, 0x2b, 0x0c, 0xf3, 0xbd, 0x30, 0xb1, 0x6e, 0x39, 0x0d, 0x52, 0x17, 0xfb, 0xf0, 0xdd, + 0x03, 0xce, 0x7e, 0x31, 0x02, 0xe3, 0xda, 0xdd, 0xec, 0xf4, 0xef, 0x59, 0x91, 0x7c, 0x17, 0xd9, + 0x14, 0xf3, 0x5d, 0xbc, 0x08, 0xb0, 0x6d, 0x3b, 0xb6, 0xbf, 0x73, 0xc2, 0x4c, 0x1a, 0xec, 0x89, + 0xf6, 0x92, 0xa2, 0x80, 0x35, 0x6a, 0xe1, 0x3b, 0x58, 0xee, 0x88, 0xfc, 0x42, 0xaf, 0x1b, 0x9a, + 0xba, 0x19, 0x4d, 0xe3, 0xdd, 0x5f, 0x1b, 0x98, 0x45, 0xa9, 0x7e, 0x2e, 0x3a, 0x81, 0x77, 0x70, + 0xa4, 0x56, 0xda, 0x84, 0xbc, 0x47, 0xfc, 0x4e, 0x8b, 0xde, 0x18, 0xc7, 0x8e, 0xdd, 0x0d, 0xcc, + 0x03, 0x03, 0x8b, 0xfa, 0x58, 0x51, 0x5a, 0x78, 0x06, 0x26, 0x23, 0x22, 0xa0, 0x19, 0xc8, 0xee, + 0x92, 0x03, 0x3e, 0x4f, 0x30, 0xfd, 0x89, 0xe6, 0x22, 0xaf, 0x85, 0xa2, 0x5b, 0x3e, 0x90, 0x79, + 0xda, 0x30, 0x5d, 0x48, 0x34, 0x00, 0x9c, 0xe4, 0x31, 0x87, 0x8e, 0x45, 0x53, 0x4b, 0xa5, 0xa1, + 0xc6, 0x82, 0xfb, 0xd9, 0x70, 0x98, 0xf9, 0xab, 0x51, 0x10, 0x4f, 0xd9, 0x03, 0x6c, 0x57, 0xfa, + 0x0b, 0x56, 0xe6, 0x04, 0x2f, 0x58, 0x57, 0x60, 0xc2, 0x76, 0xec, 0xc0, 0xb6, 0x9a, 0xcc, 0xb8, + 0x23, 0xd4, 0xa9, 0x74, 0x44, 0x9e, 0x58, 0xd5, 0x60, 0x09, 0x74, 0x22, 0x75, 0xd1, 0x35, 0xc8, + 0x31, 0x7d, 0x23, 0x26, 0xf0, 0xf1, 0xdf, 0xdb, 0x99, 0xab, 0x05, 0x8f, 0x4e, 0xe2, 0x94, 0xd8, + 0xe5, 0x83, 0xe7, 0x12, 0x51, 0xd7, 0x6f, 0x31, 0x8f, 0xc3, 0xcb, 0x47, 0x0c, 0x8e, 0x7b, 0x6a, + 0x50, 0x2a, 0xdb, 0x96, 0xdd, 0xec, 0x78, 0x24, 0xa4, 0x32, 0x1a, 0xa5, 0x72, 0x29, 0x06, 0xc7, + 0x3d, 0x35, 0xd0, 0x36, 0x4c, 0x88, 0x32, 0xee, 0x3d, 0x35, 0x76, 0xc2, 0x56, 0x32, 0x2f, 0xb9, + 0x4b, 0x1a, 0x25, 0x1c, 0xa1, 0x8b, 0x3a, 0x30, 0x6b, 0x3b, 0x35, 0xd7, 0xa9, 0x35, 0x3b, 0xbe, + 0xbd, 0x47, 0xc2, 0xd0, 0xa0, 0x93, 0x30, 0x3b, 0x7b, 0xd8, 0x2d, 0xce, 0xae, 0xc6, 0xc9, 0xe1, + 0x5e, 0x0e, 0xe8, 0x35, 0x03, 0xce, 0xd6, 0x5c, 0xc7, 0x67, 0xc1, 0xf9, 0x7b, 0xe4, 0xa2, 0xe7, + 0xb9, 0x1e, 0xe7, 0x5d, 0x38, 0x21, 0x6f, 0x66, 0x53, 0x5c, 0x4e, 0x22, 0x89, 0x93, 0x39, 0xa1, + 0x97, 0x21, 0xdf, 0xf6, 0xdc, 0x3d, 0xbb, 0x4e, 0x3c, 0xe1, 0x89, 0xb7, 0x96, 0x46, 0xb2, 0x90, + 0x8a, 0xa0, 0x19, 0x6e, 0x3d, 0xb2, 0x04, 0x2b, 0x7e, 0xe6, 0x97, 0x01, 0xa6, 0xa2, 0xe8, 0xe8, + 0x53, 0x00, 0x6d, 0xcf, 0x6d, 0x91, 0x60, 0x87, 0xa8, 0x10, 0x8f, 0x8d, 0x61, 0x73, 0x52, 0x48, + 0x7a, 0xd2, 0x7b, 0x85, 0x6e, 0x17, 0x61, 0x29, 0xd6, 0x38, 0x22, 0x0f, 0xc6, 0x76, 0xb9, 0xda, + 0x15, 0xa7, 0x90, 0x17, 0x52, 0x39, 0x33, 0x09, 0xce, 0x2c, 0x36, 0x41, 0x14, 0x61, 0xc9, 0x08, + 0x6d, 0x41, 0x76, 0x9f, 0x6c, 0xa5, 0x13, 0x10, 0x7d, 0x93, 0x88, 0xdb, 0x4c, 0x79, 0xec, 0xb0, + 0x5b, 0xcc, 0xde, 0x24, 0x5b, 0x98, 0x12, 0xa7, 0xed, 0xaa, 0xf3, 0x77, 0x78, 0xb1, 0x55, 0x0c, + 0xd9, 0xae, 0xc8, 0xa3, 0x3e, 0x6f, 0x97, 0x28, 0xc2, 0x92, 0x11, 0x7a, 0x19, 0x0a, 0xfb, 0xd6, + 0x1e, 0xd9, 0xf6, 0x5c, 0x27, 0x10, 0x2e, 0x53, 0x43, 0x7a, 0xfd, 0xdf, 0x94, 0xe4, 0x04, 0x5f, + 0xa6, 0xde, 0x55, 0x21, 0x0e, 0xd9, 0xa1, 0x3d, 0xc8, 0x3b, 0x64, 0x1f, 0x93, 0xa6, 0x5d, 0x4b, + 0xc7, 0xcb, 0x7e, 0x43, 0x50, 0x13, 0x9c, 0x99, 0xde, 0x93, 0x65, 0x58, 0xf1, 0xa2, 0x63, 0x79, + 0xcb, 0xdd, 0x12, 0x1b, 0xd5, 0x90, 0x63, 0xa9, 0x6e, 0xa6, 0x7c, 0x2c, 0xaf, 0xb8, 0x5b, 0x98, + 0x12, 0xa7, 0x6b, 0xa4, 0xa6, 0xfc, 0x75, 0xc4, 0x36, 0xb5, 0x91, 0xae, 0x9f, 0x12, 0x5f, 0x23, + 0x61, 0x29, 0xd6, 0x38, 0xd2, 0xbe, 0x6d, 0x08, 0x63, 0xa5, 0xd8, 0xa8, 0x86, 0xec, 0xdb, 0xa8, + 0xe9, 0x93, 0xf7, 0xad, 0x2c, 0xc3, 0x8a, 0x17, 0xe5, 0x6b, 0x0b, 0xcb, 0x5f, 0x3a, 0x5b, 0x55, + 0xd4, 0x8e, 0xc8, 0xf9, 0xca, 0x32, 0xac, 0x78, 0x21, 0x07, 0x46, 0xdb, 0xcd, 0x4e, 0xc3, 0x76, + 0x44, 0xb8, 0xe2, 0xb0, 0x1e, 0xac, 0x8c, 0x96, 0xe0, 0xc9, 0x3d, 0x02, 0x59, 0x09, 0x16, 0x5c, + 0xcc, 0xaf, 0x8f, 0xc2, 0x84, 0x9e, 0x04, 0x6e, 0x80, 0x33, 0x89, 0x3a, 0x87, 0x67, 0x8e, 0x73, + 0x0e, 0xa7, 0x17, 0x2f, 0xed, 0x4d, 0x45, 0x1a, 0x7d, 0x56, 0x53, 0x3b, 0x86, 0x86, 0x17, 0x2f, + 0xad, 0xd0, 0xc7, 0x11, 0xa6, 0xc7, 0x70, 0xb3, 0xa0, 0x87, 0x39, 0x7e, 0xdc, 0xc9, 0x45, 0x0f, + 0x73, 0x91, 0x03, 0xcc, 0xe3, 0x00, 0x61, 0x32, 0x34, 0xf1, 0xd6, 0xa6, 0x4e, 0x89, 0x5a, 0x92, + 0x36, 0x0d, 0x0b, 0x3d, 0x02, 0xa3, 0xf4, 0x40, 0x40, 0xea, 0x22, 0xce, 0x58, 0xdd, 0x6e, 0x2f, + 0xb1, 0x52, 0x2c, 0xa0, 0xe8, 0x69, 0x7a, 0x76, 0x0b, 0xd5, 0xb8, 0x08, 0x1f, 0x9e, 0x0b, 0xcf, + 0x6e, 0x21, 0x0c, 0x47, 0x30, 0xa9, 0xe8, 0x84, 0x6a, 0x5d, 0xb6, 0x62, 0x34, 0xd1, 0x99, 0x2a, + 0xc6, 0x1c, 0xc6, 0xac, 0x2d, 0x31, 0x2d, 0xcd, 0x66, 0x7a, 0x4e, 0xb3, 0xb6, 0xc4, 0xe0, 0xb8, + 0xa7, 0x06, 0x6d, 0x8c, 0x78, 0x26, 0x1c, 0xe7, 0xde, 0xa4, 0x7d, 0x1e, 0xf8, 0xde, 0xd0, 0x6f, + 0x20, 0x13, 0x6c, 0xe8, 0x3f, 0x98, 0x5e, 0x42, 0xc3, 0xc1, 0xaf, 0x20, 0xc3, 0x5d, 0x16, 0x3e, + 0x0e, 0x53, 0xd1, 0xbd, 0x39, 0xf5, 0xf7, 0x80, 0x3f, 0xcb, 0xc2, 0x99, 0x8d, 0x86, 0xed, 0xc4, + 0x13, 0x1c, 0x25, 0x25, 0x1a, 0x36, 0x8e, 0x9b, 0x68, 0x38, 0x0c, 0x58, 0x12, 0x99, 0x9c, 0x93, + 0x03, 0x96, 0x64, 0x9a, 0xe7, 0x28, 0x2e, 0xfa, 0xb9, 0x01, 0x0f, 0x5a, 0x75, 0x7e, 0x5a, 0xb6, + 0x9a, 0xa2, 0x34, 0x64, 0x2a, 0x57, 0xb4, 0x3f, 0xa4, 0xee, 0xeb, 0x6d, 0xfc, 0x62, 0xe9, 0x08, + 0xae, 0x7c, 0xc4, 0xdf, 0x25, 0x5a, 0xf0, 0xe0, 0x51, 0xa8, 0xf8, 0x48, 0xf1, 0x17, 0xae, 0xc2, + 0x3b, 0xef, 0xca, 0xe8, 0x58, 0xb3, 0xe5, 0xb3, 0x06, 0x14, 0xb8, 0xb9, 0x16, 0x93, 0x6d, 0xba, + 0x55, 0x58, 0x6d, 0xfb, 0x06, 0xf1, 0x7c, 0x99, 0x01, 0x4d, 0xbb, 0x50, 0x96, 0x2a, 0xab, 0x02, + 0x82, 0x35, 0x2c, 0xba, 0x19, 0xef, 0xda, 0x4e, 0x5d, 0x0c, 0x93, 0xda, 0x8c, 0x5f, 0xb0, 0x9d, + 0x3a, 0x66, 0x10, 0xb5, 0x5d, 0x67, 0xfb, 0x9a, 0x51, 0xbe, 0x66, 0xc0, 0x14, 0x8b, 0xd2, 0x0c, + 0xaf, 0x3a, 0x4f, 0x29, 0x1f, 0x1a, 0x2e, 0xc6, 0xf9, 0xa8, 0x0f, 0xcd, 0x9d, 0x6e, 0x71, 0x9c, + 0xc7, 0x75, 0x46, 0x5d, 0x6a, 0x3e, 0x2c, 0xec, 0x23, 0xcc, 0xd3, 0x27, 0x73, 0xec, 0xeb, 0xbb, + 0xb2, 0x1f, 0x56, 0x25, 0x11, 0x1c, 0xd2, 0x33, 0x5f, 0x81, 0x09, 0x3d, 0xdc, 0x02, 0x3d, 0x05, + 0xe3, 0x6d, 0xdb, 0x69, 0x44, 0xc3, 0xf2, 0x94, 0x0d, 0xb9, 0x12, 0x82, 0xb0, 0x8e, 0xc7, 0xaa, + 0xb9, 0x61, 0xb5, 0x98, 0xe9, 0xb9, 0xe2, 0xea, 0xd5, 0xc2, 0x3f, 0xe6, 0x06, 0x4c, 0xe8, 0xaa, + 0x12, 0x3d, 0x07, 0xa3, 0xdc, 0x6c, 0xcb, 0x18, 0x4f, 0xb0, 0xe0, 0xe6, 0x51, 0x6e, 0xe2, 0xbd, + 0xd3, 0x2d, 0xce, 0x13, 0xa7, 0xe6, 0xd6, 0x6d, 0xa7, 0xb1, 0x74, 0xcb, 0x77, 0x9d, 0x45, 0x6c, + 0xed, 0x4b, 0xed, 0x21, 0x6a, 0x99, 0xbf, 0x9f, 0x85, 0x33, 0x09, 0x61, 0x42, 0xe8, 0x75, 0x03, + 0x46, 0x59, 0x94, 0x81, 0xf4, 0xba, 0x79, 0x29, 0xf5, 0x50, 0xa4, 0x45, 0x16, 0xcc, 0x20, 0xd6, + 0x85, 0xda, 0x8e, 0x79, 0x21, 0x16, 0xcc, 0xd1, 0xff, 0x36, 0x60, 0xdc, 0xd2, 0x96, 0x2e, 0x77, + 0x44, 0xda, 0x4a, 0x5f, 0x98, 0x9e, 0x95, 0xaa, 0x39, 0x50, 0x86, 0x0b, 0x53, 0x97, 0x65, 0xe1, + 0xfd, 0x30, 0xae, 0x35, 0xe1, 0x38, 0x2b, 0x6e, 0xe1, 0x39, 0x98, 0x19, 0x6a, 0xc5, 0x7e, 0x08, + 0x8e, 0x9b, 0x20, 0x90, 0x2a, 0xc0, 0x7d, 0x3d, 0x14, 0x5b, 0xf5, 0xb8, 0x88, 0xc5, 0x16, 0x50, + 0x73, 0x0b, 0x66, 0xe2, 0x97, 0xc3, 0xd4, 0xdf, 0xdd, 0xdf, 0x0b, 0xc7, 0x4c, 0xe9, 0x67, 0x5e, + 0x04, 0x84, 0xdd, 0x66, 0x73, 0xcb, 0xaa, 0xed, 0xde, 0xb4, 0x9d, 0xba, 0xbb, 0xcf, 0xd6, 0xde, + 0x12, 0x14, 0x3c, 0x11, 0x05, 0xe6, 0x8b, 0x66, 0xa9, 0xc5, 0x2b, 0xc3, 0xc3, 0x7c, 0x1c, 0xe2, + 0x98, 0x3f, 0xc8, 0xc0, 0x98, 0x08, 0x59, 0xbc, 0x07, 0x2e, 0xcc, 0xbb, 0x91, 0xb7, 0xad, 0xd5, + 0x54, 0x22, 0x2d, 0xfb, 0xfa, 0x2f, 0xfb, 0x31, 0xff, 0xe5, 0x17, 0xd2, 0x61, 0x77, 0xb4, 0xf3, + 0xf2, 0xd7, 0x46, 0x60, 0x3a, 0x16, 0x02, 0x4a, 0x4f, 0x50, 0x3d, 0x3e, 0x7b, 0xd7, 0x53, 0x8d, + 0x32, 0x55, 0xee, 0xf5, 0x47, 0xbb, 0xef, 0xf9, 0x91, 0x04, 0xac, 0xd7, 0x52, 0xcb, 0xdd, 0xfe, + 0xdb, 0x5c, 0xac, 0xc7, 0x75, 0x47, 0xfb, 0x1b, 0x03, 0xee, 0xef, 0x1b, 0x29, 0xcc, 0x12, 0xcd, + 0x78, 0x51, 0xa8, 0x58, 0x90, 0x29, 0xe7, 0x43, 0x50, 0x0f, 0x4d, 0xf1, 0xdc, 0x20, 0x71, 0xf6, + 0xe8, 0x49, 0x98, 0x60, 0x1a, 0x9f, 0x6e, 0x4d, 0x01, 0x69, 0x0b, 0x3b, 0x39, 0xb3, 0x98, 0x56, + 0xb5, 0x72, 0x1c, 0xc1, 0x32, 0xbf, 0x6a, 0xc0, 0x7c, 0xbf, 0xb4, 0x23, 0x03, 0xdc, 0x57, 0xff, + 0x53, 0xcc, 0xc7, 0xba, 0xd8, 0xe3, 0x63, 0x1d, 0xbb, 0xb1, 0x4a, 0x77, 0x6a, 0xed, 0xb2, 0x98, + 0xbd, 0x8b, 0x0b, 0xf1, 0x17, 0x0c, 0x38, 0xd7, 0x67, 0x35, 0xf5, 0xf8, 0xda, 0x1b, 0x27, 0xf6, + 0xb5, 0xcf, 0x0c, 0xea, 0x6b, 0x6f, 0xfe, 0x28, 0x0b, 0x33, 0x42, 0x9e, 0xf0, 0xd8, 0xf7, 0x74, + 0xc4, 0x53, 0xfd, 0x5d, 0x31, 0x4f, 0xf5, 0xb9, 0x38, 0xfe, 0x6f, 0xdd, 0xd4, 0xdf, 0x5e, 0x6e, + 0xea, 0xbf, 0xce, 0xc0, 0xd9, 0xc4, 0x6c, 0x28, 0xe8, 0x73, 0x09, 0xaa, 0xe1, 0x66, 0xca, 0x69, + 0x57, 0x06, 0x54, 0x0e, 0xc3, 0xfa, 0x76, 0x7f, 0x59, 0xf7, 0xa9, 0xe6, 0x5b, 0xfd, 0xf6, 0x29, + 0x24, 0x90, 0x39, 0xa6, 0x7b, 0xb5, 0xf9, 0xdf, 0xb3, 0xf0, 0xe8, 0xa0, 0x84, 0xde, 0xa6, 0xe1, + 0x37, 0x7e, 0x24, 0xfc, 0xe6, 0x1e, 0xa9, 0xed, 0x53, 0x89, 0xc4, 0xf9, 0x7a, 0x56, 0xa9, 0xbd, + 0xde, 0xf9, 0x39, 0xd0, 0xa3, 0xea, 0x18, 0x3d, 0xda, 0xc9, 0x1c, 0xa9, 0xe1, 0x56, 0x38, 0x56, + 0xe5, 0xc5, 0x77, 0xba, 0xc5, 0xd9, 0x30, 0x26, 0x5f, 0x14, 0x62, 0x59, 0x09, 0x3d, 0x0a, 0x79, + 0x8f, 0x43, 0x65, 0xc0, 0x81, 0x78, 0x99, 0xe6, 0x65, 0x58, 0x41, 0xd1, 0xa7, 0xb5, 0xb3, 0xf0, + 0xc8, 0x69, 0xa5, 0x9e, 0x38, 0xea, 0xc1, 0xfd, 0x25, 0xc8, 0xfb, 0x32, 0xdb, 0x29, 0x7f, 0x15, + 0x79, 0x62, 0xc0, 0x38, 0x16, 0x7a, 0x03, 0x93, 0xa9, 0x4f, 0x79, 0xfb, 0x54, 0x62, 0x54, 0x45, + 0x12, 0x99, 0xea, 0xf2, 0xc3, 0x4d, 0x9f, 0x90, 0x70, 0xf1, 0xf9, 0xb1, 0x01, 0xe3, 0x62, 0xb4, + 0xee, 0x41, 0x68, 0xcd, 0xad, 0x68, 0x68, 0xcd, 0xc5, 0x54, 0xf6, 0x8e, 0x3e, 0x71, 0x35, 0xb7, + 0x60, 0x42, 0x4f, 0x88, 0x85, 0x5e, 0xd4, 0xf6, 0x3e, 0x63, 0x98, 0x14, 0x33, 0x72, 0x77, 0x0c, + 0xf7, 0x45, 0xf3, 0x1b, 0x05, 0xd5, 0x8b, 0xec, 0x8a, 0xa6, 0xcf, 0x41, 0xe3, 0xc8, 0x39, 0xa8, + 0x4f, 0x81, 0x4c, 0xfa, 0x53, 0xe0, 0x1a, 0xe4, 0xe5, 0x06, 0x25, 0xd4, 0xf8, 0xc3, 0xba, 0xb3, + 0x21, 0x3d, 0x0b, 0x50, 0x62, 0xda, 0xc4, 0x65, 0x57, 0x2d, 0x35, 0x86, 0x6a, 0xe3, 0x54, 0x64, + 0xd0, 0xcb, 0x30, 0xbe, 0xef, 0x7a, 0xbb, 0x4d, 0xd7, 0x62, 0x79, 0x8c, 0x21, 0x8d, 0xf7, 0x2d, + 0x65, 0x87, 0xe3, 0x1e, 0xdf, 0x37, 0x43, 0xfa, 0x58, 0x67, 0x86, 0x4a, 0x30, 0xdd, 0xb2, 0x1d, + 0x4c, 0xac, 0xba, 0x8a, 0xa0, 0x19, 0xe1, 0x89, 0x56, 0xe5, 0x21, 0x77, 0x3d, 0x0a, 0xc6, 0x71, + 0x7c, 0xf4, 0x79, 0x03, 0xa6, 0xbc, 0xc8, 0xa5, 0x5a, 0x64, 0x53, 0xac, 0x0c, 0x3f, 0x19, 0xa3, + 0x17, 0x75, 0xee, 0xf2, 0x1c, 0x2d, 0xc7, 0x31, 0xde, 0xe8, 0x93, 0x90, 0xf7, 0x45, 0xb6, 0xab, + 0x74, 0x1e, 0x46, 0xd5, 0x15, 0x96, 0x13, 0x0d, 0x87, 0x52, 0x96, 0x60, 0xc5, 0x10, 0xad, 0xc1, + 0x9c, 0xb4, 0x12, 0x44, 0xbe, 0x35, 0x33, 0x1a, 0x26, 0x47, 0xc1, 0x09, 0x70, 0x9c, 0x58, 0x8b, + 0x1e, 0xaa, 0x58, 0xa2, 0x39, 0xfe, 0x72, 0xa2, 0x3d, 0x36, 0xb0, 0xf5, 0x57, 0xc7, 0x02, 0x7a, + 0x54, 0x80, 0x58, 0x7e, 0x88, 0x00, 0xb1, 0x2a, 0x9c, 0x8d, 0x83, 0x58, 0xd6, 0x1b, 0x96, 0x68, + 0x47, 0x53, 0x66, 0x95, 0x24, 0x24, 0x9c, 0x5c, 0x17, 0xdd, 0x84, 0x82, 0x47, 0xd8, 0x75, 0xa7, + 0x24, 0x5d, 0x31, 0x8e, 0xed, 0x74, 0x86, 0x25, 0x01, 0x1c, 0xd2, 0xa2, 0xe3, 0x6e, 0x45, 0x53, + 0x9f, 0x5e, 0x4b, 0xf1, 0x6b, 0x79, 0x62, 0xec, 0xfb, 0x64, 0xa3, 0x32, 0xdf, 0x9a, 0x82, 0xc9, + 0x88, 0xa9, 0x03, 0x3d, 0x0c, 0x39, 0x96, 0x06, 0x88, 0xed, 0x56, 0xf9, 0x70, 0x47, 0xe5, 0x9d, + 0xc3, 0x61, 0xe8, 0x8b, 0x06, 0x4c, 0xb7, 0x23, 0xb6, 0x6a, 0xb9, 0x91, 0x0f, 0xf9, 0xfa, 0x1a, + 0x35, 0x80, 0x6b, 0x49, 0xc3, 0xa3, 0xcc, 0x70, 0x9c, 0x3b, 0xdd, 0x0f, 0x84, 0xe7, 0x66, 0x93, + 0x78, 0x0c, 0x5b, 0x1c, 0xb9, 0x14, 0x89, 0xe5, 0x28, 0x18, 0xc7, 0xf1, 0xe9, 0x08, 0xb3, 0xd6, + 0x0d, 0xf3, 0x19, 0xad, 0x92, 0x24, 0x80, 0x43, 0x5a, 0xe8, 0x39, 0x98, 0x12, 0x19, 0x2f, 0x2b, + 0x6e, 0xfd, 0xb2, 0xe5, 0xef, 0x88, 0xbb, 0x86, 0xba, 0x1b, 0x2d, 0x47, 0xa0, 0x38, 0x86, 0xcd, + 0xda, 0x16, 0xa6, 0x15, 0x65, 0x04, 0x46, 0xa3, 0x39, 0xd5, 0x97, 0xa3, 0x60, 0x1c, 0xc7, 0x47, + 0x8f, 0x69, 0x6a, 0x88, 0xbf, 0x66, 0xaa, 0xdd, 0x20, 0x41, 0x15, 0x95, 0x60, 0xba, 0xc3, 0xae, + 0x66, 0x75, 0x09, 0x14, 0xeb, 0x51, 0x31, 0xbc, 0x1e, 0x05, 0xe3, 0x38, 0x3e, 0x7a, 0x06, 0x26, + 0x3d, 0xba, 0xd9, 0x2a, 0x02, 0xfc, 0x89, 0x53, 0xbd, 0x60, 0x61, 0x1d, 0x88, 0xa3, 0xb8, 0xe8, + 0x79, 0x98, 0x0d, 0x13, 0xc4, 0x49, 0x02, 0xfc, 0xcd, 0x53, 0x65, 0x2b, 0x2a, 0xc5, 0x11, 0x70, + 0x6f, 0x1d, 0xf4, 0x5f, 0x60, 0x46, 0xeb, 0x89, 0x55, 0xa7, 0x4e, 0x6e, 0x8b, 0x24, 0x5e, 0xec, + 0xf3, 0x17, 0xcb, 0x31, 0x18, 0xee, 0xc1, 0x46, 0x1f, 0x80, 0xa9, 0x9a, 0xdb, 0x6c, 0xb2, 0x3d, + 0x8e, 0xe7, 0xf3, 0xe6, 0xd9, 0xba, 0x78, 0x5e, 0xb3, 0x08, 0x04, 0xc7, 0x30, 0xd1, 0x15, 0x40, + 0xee, 0x96, 0x4f, 0xbc, 0x3d, 0x52, 0x7f, 0x9e, 0x7f, 0x98, 0x97, 0x9e, 0x38, 0x26, 0xa3, 0x7e, + 0xe3, 0x57, 0x7b, 0x30, 0x70, 0x42, 0x2d, 0x96, 0xec, 0x48, 0x8b, 0xb3, 0x9b, 0x4a, 0xe3, 0x93, + 0x52, 0x71, 0x43, 0xc2, 0x5d, 0x83, 0xec, 0x3c, 0x18, 0xe5, 0x6e, 0xfc, 0xe9, 0xa4, 0xed, 0xd2, + 0x53, 0xfb, 0x86, 0x3a, 0x82, 0x97, 0x62, 0xc1, 0x09, 0x7d, 0x0a, 0x0a, 0x5b, 0x32, 0xcf, 0x3b, + 0xcb, 0xd5, 0x35, 0xb4, 0x5e, 0x8c, 0x7d, 0xb2, 0x20, 0xbc, 0x28, 0x2b, 0x00, 0x0e, 0x59, 0xa2, + 0x47, 0x60, 0xfc, 0x72, 0xa5, 0xa4, 0x66, 0xe1, 0x2c, 0x1b, 0xfd, 0x11, 0x5a, 0x05, 0xeb, 0x00, + 0xba, 0xc2, 0xd4, 0xf1, 0x0d, 0xb1, 0x21, 0x0e, 0xf5, 0x6d, 0xef, 0x69, 0x8c, 0x62, 0xb3, 0x47, + 0x5b, 0x5c, 0x9d, 0x3f, 0x13, 0xc3, 0x16, 0xe5, 0x58, 0x61, 0xa0, 0x97, 0x60, 0x5c, 0xe8, 0x0b, + 0xb6, 0x37, 0xcd, 0x9d, 0x2c, 0x86, 0x13, 0x87, 0x24, 0xb0, 0x4e, 0x8f, 0xbd, 0xc5, 0xb1, 0xf4, + 0xd7, 0xe4, 0x52, 0xa7, 0xd9, 0x9c, 0x3f, 0xcb, 0xf6, 0xcd, 0xf0, 0x2d, 0x2e, 0x04, 0x61, 0x1d, + 0x0f, 0x3d, 0x21, 0xfd, 0x4b, 0xde, 0x11, 0x79, 0x9c, 0x54, 0xfe, 0x25, 0xea, 0xd0, 0xdd, 0xc7, + 0xcd, 0xfb, 0xdc, 0x5d, 0x1c, 0x3b, 0xb6, 0x60, 0x41, 0x9e, 0xf8, 0x7a, 0x17, 0xc9, 0xfc, 0x7c, + 0xc4, 0x68, 0xb1, 0x70, 0xb3, 0x2f, 0x26, 0x3e, 0x82, 0x0a, 0xda, 0x82, 0xac, 0xd5, 0xdc, 0x9a, + 0xbf, 0x3f, 0x8d, 0xa3, 0xab, 0xfa, 0xd0, 0x36, 0x77, 0xcd, 0x2a, 0xad, 0x95, 0x31, 0x25, 0x6e, + 0xbe, 0x96, 0x51, 0x8f, 0x04, 0x2a, 0x9d, 0xe9, 0x2b, 0xfa, 0xac, 0x36, 0xd2, 0xf8, 0x90, 0x6c, + 0xcf, 0xc7, 0x10, 0xb8, 0x42, 0x4a, 0x9c, 0xd3, 0x6d, 0xb5, 0x8e, 0x53, 0xc9, 0x2e, 0x13, 0x4d, + 0xd5, 0xca, 0x2f, 0x97, 0xd1, 0x55, 0x6c, 0xfe, 0x2c, 0xaf, 0x6c, 0x62, 0x31, 0x87, 0x09, 0x0f, + 0x72, 0xb6, 0x1f, 0xd8, 0x6e, 0x8a, 0xf1, 0x86, 0xb1, 0x1c, 0xa7, 0xcc, 0x9d, 0x99, 0x01, 0x30, + 0x67, 0x45, 0x79, 0x3a, 0x0d, 0xdb, 0xb9, 0x2d, 0x9a, 0x7f, 0x2d, 0x75, 0x4f, 0x08, 0xce, 0x93, + 0x01, 0x30, 0x67, 0x85, 0x6e, 0xf1, 0x99, 0x96, 0xce, 0x47, 0x83, 0xe3, 0xdf, 0x02, 0x8f, 0xce, + 0x38, 0xca, 0xcb, 0x6f, 0xd9, 0xe2, 0x0c, 0x33, 0x24, 0xaf, 0xea, 0xfa, 0x6a, 0x12, 0xaf, 0xea, + 0xfa, 0x2a, 0xa6, 0x4c, 0xd0, 0x1b, 0x06, 0x80, 0xa5, 0x3e, 0x8a, 0x9d, 0xce, 0x97, 0x43, 0xfa, + 0x7d, 0x64, 0x9b, 0x7b, 0x20, 0x86, 0x50, 0xac, 0x71, 0x46, 0x2f, 0xc3, 0x98, 0xc5, 0xbf, 0x7b, + 0x24, 0x9c, 0x3b, 0xd3, 0xf9, 0x98, 0x57, 0x4c, 0x02, 0xe6, 0xd5, 0x2a, 0x40, 0x58, 0x32, 0xa4, + 0xbc, 0x03, 0xcf, 0x22, 0xdb, 0xf6, 0xae, 0xf0, 0xf2, 0xac, 0x0e, 0x9d, 0xbe, 0x9c, 0x12, 0x4b, + 0xe2, 0x2d, 0x40, 0x58, 0x32, 0xe4, 0xdf, 0xa1, 0xb5, 0x1c, 0x4b, 0x85, 0xec, 0xa4, 0x13, 0xd8, + 0xa5, 0x07, 0x01, 0x69, 0xdf, 0xa1, 0xd5, 0x19, 0xe1, 0x28, 0x5f, 0xb4, 0x07, 0xa3, 0x16, 0xfb, + 0x22, 0x9b, 0xb8, 0x1f, 0xe1, 0x34, 0xbe, 0xee, 0x16, 0xeb, 0x03, 0xb6, 0xb9, 0x88, 0xef, 0xbe, + 0x09, 0x6e, 0xe6, 0x2f, 0xb3, 0x00, 0x4c, 0x04, 0x1e, 0xbe, 0xde, 0x62, 0x29, 0x0f, 0x77, 0xdc, + 0x7a, 0x3a, 0x1f, 0x99, 0xd3, 0xa3, 0xd0, 0x41, 0xe4, 0x37, 0xdc, 0x71, 0xeb, 0x58, 0x30, 0x41, + 0x0d, 0x18, 0x69, 0x5b, 0xc1, 0x4e, 0xfa, 0x21, 0xef, 0x79, 0x1e, 0xc7, 0x15, 0xec, 0x60, 0xc6, + 0x00, 0xbd, 0x6a, 0xc0, 0x18, 0x0f, 0x7a, 0x97, 0x16, 0xf7, 0xa1, 0x9f, 0x95, 0x65, 0x9f, 0x2d, + 0xf2, 0xc8, 0x7a, 0xe1, 0xfa, 0xa1, 0x54, 0xb2, 0x28, 0xc5, 0x92, 0xed, 0xc2, 0xeb, 0x06, 0x4c, + 0xe8, 0xa8, 0x09, 0x4e, 0x1b, 0x1f, 0xd3, 0x9d, 0x36, 0xd2, 0xec, 0x0f, 0xdd, 0xff, 0xe3, 0xef, + 0x0d, 0xd0, 0xbe, 0x1a, 0x1c, 0xba, 0x80, 0x1a, 0x03, 0xbb, 0x80, 0x66, 0x8e, 0xe9, 0x02, 0x9a, + 0x3d, 0x96, 0x0b, 0xe8, 0xc8, 0xf1, 0x5d, 0x40, 0x73, 0xfd, 0x5d, 0x40, 0xcd, 0x37, 0x0d, 0x98, + 0xed, 0xd9, 0x87, 0xe9, 0xb1, 0xcd, 0x73, 0xdd, 0xa0, 0x8f, 0xe7, 0x15, 0x0e, 0x41, 0x58, 0xc7, + 0x43, 0x2b, 0x30, 0x23, 0x12, 0x7c, 0x57, 0xdb, 0x4d, 0x3b, 0x31, 0x1d, 0xc1, 0x66, 0x0c, 0x8e, + 0x7b, 0x6a, 0x98, 0x7f, 0x6c, 0xc0, 0xb8, 0x16, 0xc4, 0x48, 0xdb, 0xc1, 0x82, 0x3d, 0x85, 0x18, + 0x61, 0x6e, 0x73, 0xf6, 0xc2, 0xc1, 0x61, 0xfc, 0xb1, 0xad, 0xa1, 0xa5, 0x7f, 0x0d, 0x1f, 0xdb, + 0x68, 0x29, 0x16, 0x50, 0x9e, 0xd8, 0x93, 0xb4, 0x59, 0xa7, 0x67, 0xf5, 0xc4, 0x9e, 0xa4, 0x8d, + 0x19, 0x84, 0xb1, 0xa3, 0xe7, 0x57, 0xe1, 0x1d, 0xac, 0xa5, 0x52, 0xb7, 0xbc, 0x00, 0x73, 0x18, + 0x3a, 0x0f, 0x59, 0xe2, 0xd4, 0xc5, 0x65, 0x5b, 0x7d, 0xbe, 0xec, 0xa2, 0x53, 0xc7, 0xb4, 0xdc, + 0xbc, 0x0a, 0x13, 0x55, 0x52, 0xf3, 0x48, 0xf0, 0x02, 0x39, 0x18, 0xf8, 0x7b, 0x68, 0x74, 0xb6, + 0xc7, 0xbe, 0x87, 0x46, 0xab, 0xd3, 0x72, 0xf3, 0xf7, 0x0c, 0x88, 0xe5, 0xfb, 0xd7, 0x0c, 0xef, + 0x46, 0x3f, 0xc3, 0x7b, 0xc4, 0x44, 0x9c, 0x39, 0xd2, 0x44, 0x7c, 0x05, 0x50, 0x8b, 0x2e, 0x85, + 0xc8, 0xd7, 0x2d, 0x84, 0x9d, 0x23, 0x0c, 0x99, 0xee, 0xc1, 0xc0, 0x09, 0xb5, 0xcc, 0xdf, 0xe5, + 0xc2, 0xea, 0x5f, 0x00, 0xb8, 0x7b, 0x07, 0x74, 0x20, 0xc7, 0x48, 0x09, 0x63, 0xcf, 0x90, 0x86, + 0xd2, 0xde, 0xd4, 0x23, 0xe1, 0x40, 0x8a, 0x25, 0xcf, 0xb8, 0x99, 0x3f, 0xe2, 0xb2, 0x6a, 0x9f, + 0x08, 0x18, 0x40, 0xd6, 0x56, 0x54, 0xd6, 0xcb, 0x69, 0xed, 0x95, 0xc9, 0x32, 0xa2, 0x45, 0x80, + 0x36, 0xf1, 0x6a, 0xc4, 0x09, 0xa4, 0xd3, 0x7a, 0x4e, 0x04, 0x15, 0xa9, 0x52, 0xac, 0x61, 0x98, + 0xaf, 0x1a, 0x30, 0x53, 0x0d, 0xec, 0xda, 0xae, 0xed, 0xf0, 0x20, 0xb9, 0x6d, 0xbb, 0x41, 0x6f, + 0x47, 0x44, 0x7c, 0xea, 0x8b, 0x9b, 0xdf, 0xd4, 0x56, 0x2c, 0xbf, 0xf0, 0x25, 0xe1, 0xa8, 0x04, + 0xd3, 0xf2, 0xd1, 0x41, 0xda, 0x4c, 0x79, 0x70, 0xaf, 0xb2, 0xd1, 0xac, 0x44, 0xc1, 0x38, 0x8e, + 0x6f, 0x7e, 0x1a, 0xc6, 0xb5, 0xfd, 0x95, 0x6d, 0x45, 0xb7, 0xad, 0x5a, 0x10, 0x5f, 0xc2, 0x17, + 0x69, 0x21, 0xe6, 0x30, 0x66, 0xda, 0xe5, 0x5e, 0xcd, 0xb1, 0x25, 0x2c, 0x7c, 0x99, 0x05, 0x94, + 0x12, 0xf3, 0x48, 0x83, 0xdc, 0x96, 0x89, 0x66, 0x25, 0x31, 0x4c, 0x0b, 0x31, 0x87, 0x99, 0x8f, + 0x41, 0x5e, 0xa6, 0x60, 0x60, 0x71, 0xcc, 0xd2, 0xec, 0xa8, 0xc7, 0x31, 0xbb, 0x5e, 0x80, 0x19, + 0xc4, 0xbc, 0x01, 0x79, 0x99, 0x29, 0xe2, 0xee, 0xd8, 0x74, 0x55, 0xf9, 0x8e, 0x7d, 0xd9, 0xf5, + 0x03, 0x99, 0xde, 0x82, 0xbf, 0x8c, 0x6c, 0xac, 0xb2, 0x32, 0xac, 0xa0, 0xe6, 0x2c, 0x4c, 0xab, + 0x27, 0x0f, 0xe1, 0x66, 0xfa, 0xdd, 0x2c, 0x4c, 0x44, 0xbe, 0x9e, 0x7d, 0xf7, 0xe9, 0x36, 0xf8, + 0x2a, 0x4e, 0x78, 0xba, 0xc8, 0x1e, 0xf3, 0xe9, 0x42, 0x7f, 0x2b, 0x1a, 0x39, 0xdd, 0xb7, 0xa2, + 0x5c, 0x3a, 0x6f, 0x45, 0x01, 0x8c, 0xf9, 0x42, 0x51, 0x8d, 0xa6, 0x61, 0xc4, 0x89, 0x8d, 0x18, + 0x3f, 0x1b, 0x4b, 0x7d, 0x27, 0x59, 0x99, 0xdf, 0xca, 0xc1, 0x54, 0x34, 0x47, 0xd6, 0x00, 0x23, + 0xf9, 0x58, 0xcf, 0x48, 0x1e, 0xd3, 0x56, 0x9a, 0x1d, 0xd6, 0x56, 0x3a, 0x32, 0xac, 0xad, 0x34, + 0x77, 0x02, 0x5b, 0x69, 0xaf, 0xa5, 0x73, 0x74, 0x60, 0x4b, 0xe7, 0xb3, 0xca, 0xef, 0x68, 0x2c, + 0xf2, 0x50, 0x1f, 0xfa, 0x1d, 0xa1, 0xe8, 0x30, 0x2c, 0xbb, 0xf5, 0x44, 0xff, 0xad, 0xfc, 0x5d, + 0x6c, 0x42, 0x5e, 0xa2, 0x9b, 0xd0, 0xf1, 0x9f, 0x63, 0xde, 0x71, 0x0c, 0x17, 0xa1, 0xa7, 0x60, + 0x5c, 0xcc, 0x27, 0x76, 0x56, 0x82, 0xe8, 0x39, 0xab, 0x1a, 0x82, 0xb0, 0x8e, 0xc7, 0xbe, 0x84, + 0x1a, 0xfd, 0xf4, 0x2b, 0x33, 0x3d, 0xeb, 0x5f, 0x42, 0x8d, 0x7d, 0x2a, 0x36, 0x8e, 0x6f, 0x7e, + 0x12, 0xce, 0x26, 0xde, 0x04, 0x99, 0x69, 0x8c, 0xa9, 0x71, 0x52, 0x17, 0x08, 0x9a, 0x18, 0xb1, + 0x14, 0xca, 0x0b, 0x37, 0xfb, 0x62, 0xe2, 0x23, 0xa8, 0x98, 0xdf, 0xcc, 0xc2, 0x54, 0xf4, 0x33, + 0x5a, 0x68, 0x5f, 0xd9, 0x8d, 0x52, 0x31, 0x59, 0x71, 0xb2, 0x5a, 0xde, 0xa5, 0xbe, 0x46, 0xe0, + 0x7d, 0x36, 0xbf, 0xb6, 0x54, 0x12, 0xa8, 0xd3, 0x63, 0x2c, 0xac, 0xaf, 0x82, 0x1d, 0xfb, 0x52, + 0x56, 0x18, 0x8c, 0x22, 0xae, 0x5d, 0xa9, 0x73, 0x0f, 0xc3, 0x4b, 0x14, 0x2b, 0xac, 0xb1, 0xa5, + 0xba, 0x65, 0x8f, 0x78, 0xf6, 0xb6, 0xad, 0x3e, 0x01, 0xca, 0x76, 0xee, 0x1b, 0xa2, 0x0c, 0x2b, + 0xa8, 0xf9, 0x6a, 0x06, 0xc2, 0x0f, 0x1e, 0xb3, 0x6f, 0xcd, 0xf8, 0xda, 0x11, 0x57, 0x0c, 0xdb, + 0x95, 0x61, 0x3f, 0xe8, 0x14, 0x52, 0x14, 0x3e, 0xa1, 0x5a, 0x09, 0x8e, 0x70, 0xfc, 0x0d, 0x7c, + 0xe8, 0xd8, 0x82, 0xe9, 0x58, 0x48, 0x70, 0xea, 0xfe, 0xfb, 0xbf, 0xc8, 0x42, 0x41, 0x05, 0x55, + 0xa3, 0xf7, 0x47, 0xec, 0x0d, 0x85, 0xf2, 0x3b, 0xb5, 0x0f, 0x21, 0xec, 0xb8, 0xf5, 0x3b, 0xdd, + 0xe2, 0xb4, 0x42, 0x8e, 0xd9, 0x0e, 0xce, 0x43, 0xb6, 0xe3, 0x35, 0xe3, 0x17, 0x8a, 0xeb, 0x78, + 0x0d, 0xd3, 0x72, 0x74, 0x3b, 0x7e, 0xe1, 0x5f, 0x4f, 0x29, 0x10, 0x9c, 0x9f, 0xbc, 0xfb, 0x5f, + 0xf4, 0xa9, 0x96, 0xdc, 0x72, 0xeb, 0x07, 0xf1, 0x0f, 0x27, 0x94, 0xdd, 0xfa, 0x01, 0x66, 0x10, + 0xf4, 0x1c, 0x4c, 0x05, 0x76, 0x8b, 0xb8, 0x9d, 0x40, 0xff, 0x9c, 0x6c, 0x36, 0x7c, 0xd4, 0xdc, + 0x8c, 0x40, 0x71, 0x0c, 0x9b, 0x6a, 0xd9, 0x5b, 0xbe, 0xeb, 0xb0, 0x6c, 0x88, 0xa3, 0xd1, 0x17, + 0x90, 0x2b, 0xd5, 0xab, 0x1b, 0xcc, 0xee, 0xa1, 0x30, 0x28, 0xb6, 0xcd, 0x22, 0x1a, 0x3d, 0x22, + 0x7c, 0x0a, 0x66, 0xc2, 0xfc, 0x1a, 0xbc, 0x1c, 0x2b, 0x0c, 0xb4, 0xc2, 0x69, 0x53, 0x69, 0x99, + 0x46, 0x99, 0x28, 0x3f, 0x2a, 0xe9, 0xd2, 0xb2, 0x23, 0x23, 0x83, 0x54, 0x4d, 0xf3, 0x3a, 0x4c, + 0xc7, 0x3a, 0x4c, 0x5e, 0x00, 0x8d, 0xe4, 0x0b, 0xe0, 0x60, 0xdf, 0x3a, 0xf8, 0x03, 0x03, 0x66, + 0x7b, 0xb6, 0x80, 0x41, 0xc3, 0x53, 0xe2, 0xca, 0x28, 0x73, 0x72, 0x65, 0x94, 0x3d, 0x9e, 0x32, + 0x2a, 0x6f, 0x7d, 0xff, 0xad, 0x0b, 0xf7, 0xfd, 0xf0, 0xad, 0x0b, 0xf7, 0xfd, 0xe4, 0xad, 0x0b, + 0xf7, 0xbd, 0x7a, 0x78, 0xc1, 0xf8, 0xfe, 0xe1, 0x05, 0xe3, 0x87, 0x87, 0x17, 0x8c, 0x9f, 0x1c, + 0x5e, 0x30, 0xfe, 0xfa, 0xf0, 0x82, 0xf1, 0xe6, 0x2f, 0x2e, 0xdc, 0xf7, 0xe2, 0xb3, 0xe1, 0x04, + 0x5d, 0x92, 0x13, 0x94, 0xfd, 0x78, 0x8f, 0x9c, 0x8e, 0x4b, 0xed, 0xdd, 0xc6, 0x12, 0x9d, 0xa0, + 0x4b, 0xaa, 0x44, 0x4e, 0xd0, 0x7f, 0x09, 0x00, 0x00, 0xff, 0xff, 0xb4, 0xa1, 0xef, 0x00, 0x88, + 0x96, 0x00, 0x00, } func (m *ALBStatus) Marshal() (dAtA []byte, err error) { @@ -6898,6 +6931,18 @@ func (m *MetricProvider) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Plugin != nil { + { + size, err := m.Plugin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } if m.Influxdb != nil { { size, err := m.Influxdb.MarshalToSizedBuffer(dAtA[:i]) @@ -7322,6 +7367,36 @@ func (m *PingPongSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *PluginMetric) 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 *PluginMetric) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PluginMetric) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Config != nil { + i -= len(m.Config) + copy(dAtA[i:], m.Config) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Config))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *PodTemplateMetadata) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -10842,6 +10917,10 @@ func (m *MetricProvider) Size() (n int) { l = m.Influxdb.Size() n += 1 + l + sovGenerated(uint64(l)) } + if m.Plugin != nil { + l = m.Plugin.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -10956,6 +11035,19 @@ func (m *PingPongSpec) Size() (n int) { return n } +func (m *PluginMetric) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Config != nil { + l = len(m.Config) + n += 1 + l + sovGenerated(uint64(l)) + } + return n +} + func (m *PodTemplateMetadata) Size() (n int) { if m == nil { return 0 @@ -12687,6 +12779,7 @@ func (this *MetricProvider) String() string { `CloudWatch:` + strings.Replace(this.CloudWatch.String(), "CloudWatchMetric", "CloudWatchMetric", 1) + `,`, `Graphite:` + strings.Replace(this.Graphite.String(), "GraphiteMetric", "GraphiteMetric", 1) + `,`, `Influxdb:` + strings.Replace(this.Influxdb.String(), "InfluxdbMetric", "InfluxdbMetric", 1) + `,`, + `Plugin:` + strings.Replace(this.Plugin.String(), "PluginMetric", "PluginMetric", 1) + `,`, `}`, }, "") return s @@ -12794,6 +12887,16 @@ func (this *PingPongSpec) String() string { }, "") return s } +func (this *PluginMetric) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&PluginMetric{`, + `Config:` + valueToStringGenerated(this.Config) + `,`, + `}`, + }, "") + return s +} func (this *PodTemplateMetadata) String() string { if this == nil { return "nil" @@ -23659,6 +23762,42 @@ func (m *MetricProvider) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Plugin", 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.Plugin == nil { + m.Plugin = &PluginMetric{} + } + if err := m.Plugin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -24851,6 +24990,90 @@ func (m *PingPongSpec) Unmarshal(dAtA []byte) error { } return nil } +func (m *PluginMetric) 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: PluginMetric: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PluginMetric: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Config = append(m.Config[:0], dAtA[iNdEx:postIndex]...) + if m.Config == nil { + m.Config = []byte{} + } + iNdEx = postIndex + 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 *PodTemplateMetadata) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/apis/rollouts/v1alpha1/generated.proto b/pkg/apis/rollouts/v1alpha1/generated.proto index df63a7f33c..9d9d027d60 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.proto +++ b/pkg/apis/rollouts/v1alpha1/generated.proto @@ -976,6 +976,9 @@ message MetricProvider { // Influxdb specifies the influxdb metric to query optional InfluxdbMetric influxdb = 10; + + // Plugin specifies the hashicorp go-plugin metric to query + optional PluginMetric plugin = 11; } // MetricResult contain a list of the most recent measurements for a single metric along with @@ -1072,6 +1075,12 @@ message PingPongSpec { optional string pongService = 2; } +message PluginMetric { + // +kubebuilder:pruning:PreserveUnknownFields + // +kubebuilder:validation:Schemaless + optional bytes config = 1; +} + // PodTemplateMetadata extra labels to add to the template message PodTemplateMetadata { // Labels Additional labels to add to the experiment diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index d124a2acf3..be10fbd4cd 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -95,6 +95,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ObjectRef": schema_pkg_apis_rollouts_v1alpha1_ObjectRef(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PauseCondition": schema_pkg_apis_rollouts_v1alpha1_PauseCondition(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PingPongSpec": schema_pkg_apis_rollouts_v1alpha1_PingPongSpec(ref), + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PluginMetric": schema_pkg_apis_rollouts_v1alpha1_PluginMetric(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PodTemplateMetadata": schema_pkg_apis_rollouts_v1alpha1_PodTemplateMetadata(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PreferredDuringSchedulingIgnoredDuringExecution": schema_pkg_apis_rollouts_v1alpha1_PreferredDuringSchedulingIgnoredDuringExecution(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PrometheusMetric": schema_pkg_apis_rollouts_v1alpha1_PrometheusMetric(ref), @@ -2925,11 +2926,17 @@ func schema_pkg_apis_rollouts_v1alpha1_MetricProvider(ref common.ReferenceCallba Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.InfluxdbMetric"), }, }, + "plugin": { + SchemaProps: spec.SchemaProps{ + Description: "Plugin specifies the hashicorp go-plugin metric to query", + Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PluginMetric"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.CloudWatchMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.DatadogMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.GraphiteMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.InfluxdbMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.JobMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.KayentaMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.NewRelicMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PrometheusMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.WavefrontMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.WebMetric"}, + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.CloudWatchMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.DatadogMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.GraphiteMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.InfluxdbMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.JobMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.KayentaMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.NewRelicMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PluginMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PrometheusMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.WavefrontMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.WebMetric"}, } } @@ -3217,6 +3224,24 @@ func schema_pkg_apis_rollouts_v1alpha1_PingPongSpec(ref common.ReferenceCallback } } +func schema_pkg_apis_rollouts_v1alpha1_PluginMetric(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "config": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "byte", + }, + }, + }, + }, + }, + } +} + func schema_pkg_apis_rollouts_v1alpha1_PodTemplateMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go index dad43fb3e0..db989aa202 100644 --- a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go @@ -1663,6 +1663,11 @@ func (in *MetricProvider) DeepCopyInto(out *MetricProvider) { *out = new(InfluxdbMetric) **out = **in } + if in.Plugin != nil { + in, out := &in.Plugin, &out.Plugin + *out = new(PluginMetric) + (*in).DeepCopyInto(*out) + } return } @@ -1794,6 +1799,27 @@ func (in *PingPongSpec) DeepCopy() *PingPongSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PluginMetric) DeepCopyInto(out *PluginMetric) { + *out = *in + if in.Config != nil { + in, out := &in.Config, &out.Config + *out = make(json.RawMessage, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginMetric. +func (in *PluginMetric) DeepCopy() *PluginMetric { + if in == nil { + return nil + } + out := new(PluginMetric) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PodTemplateMetadata) DeepCopyInto(out *PodTemplateMetadata) { *out = *in diff --git a/rollout/controller_test.go b/rollout/controller_test.go index d7683fddec..1d9a738254 100644 --- a/rollout/controller_test.go +++ b/rollout/controller_test.go @@ -1944,6 +1944,9 @@ func TestWriteBackToInformer(t *testing.T) { roKey := getKey(r1, t) f.runController(roKey, true, false, c, i, k8sI) + // Give controller a little time to process the rollout + time.Sleep(250 * time.Millisecond) + // Verify the informer was updated with the new unstructured object after reconciliation obj, exists, err := c.rolloutsIndexer.GetByKey(roKey) assert.NoError(t, err) diff --git a/utils/analysis/factory.go b/utils/analysis/factory.go index 0fb57030f5..56ea1d3798 100644 --- a/utils/analysis/factory.go +++ b/utils/analysis/factory.go @@ -225,6 +225,9 @@ func ValidateMetric(metric v1alpha1.Metric) error { if metric.Provider.Influxdb != nil { numProviders++ } + if metric.Provider.Plugin != nil { + numProviders++ + } if numProviders == 0 { return fmt.Errorf("no provider specified") } diff --git a/utils/defaults/defaults.go b/utils/defaults/defaults.go index db605eb99c..37b90365dc 100644 --- a/utils/defaults/defaults.go +++ b/utils/defaults/defaults.go @@ -2,6 +2,7 @@ package defaults import ( "io/ioutil" + "net/url" "os" "strconv" "strings" @@ -45,6 +46,10 @@ const ( // DefaultMetricCleanupDelay is the default time to delay metrics removal upon object removal, gives time for metrics // to be collected DefaultMetricCleanupDelay = int32(65) + // DefaultMetricsPluginLocation is the default path to the metrics plugin binary + DefaultMetricsPluginLocation = "" + // DefaultPluginHttpFileLocation is the default path to the metrics plugin when downloaded via http(s) + DefaultPluginHttpFileLocation = "/tmp/metric-plugin" ) const ( @@ -68,6 +73,7 @@ var ( targetGroupBindingAPIVersion = DefaultTargetGroupBindingAPIVersion appmeshCRDVersion = DefaultAppMeshCRDVersion defaultMetricCleanupDelay = DefaultMetricCleanupDelay + metricPluginLocation = &url.URL{Path: DefaultMetricsPluginLocation} ) const ( @@ -320,3 +326,25 @@ func GetMetricCleanupDelaySeconds() time.Duration { func SetMetricCleanupDelaySeconds(seconds int32) { defaultMetricCleanupDelay = seconds } + +// GetMetricPluginLocation returns the location of the metric plugin binary +func GetMetricPluginLocation() string { + switch metricPluginLocation.Scheme { + case "http", "https": + return DefaultPluginHttpFileLocation // return relative path to the plugin + case "file": + return metricPluginLocation.Path + default: + return "" + } +} + +// SetMetricPluginLocation sets the location of the metric plugin binary +func SetMetricPluginLocation(pluginLocation string) error { + urlObj, err := url.ParseRequestURI(pluginLocation) + if err != nil { + return err + } + metricPluginLocation = urlObj + return nil +} diff --git a/utils/defaults/defaults_test.go b/utils/defaults/defaults_test.go index 2162f1be82..7ffa9e9157 100644 --- a/utils/defaults/defaults_test.go +++ b/utils/defaults/defaults_test.go @@ -411,4 +411,19 @@ func TestSetDefaults(t *testing.T) { assert.Equal(t, DefaultMetricCleanupDelay, int32(GetMetricCleanupDelaySeconds().Seconds())) SetMetricCleanupDelaySeconds(24) assert.Equal(t, time.Duration(24)*time.Second, GetMetricCleanupDelaySeconds()) + + assert.Equal(t, DefaultMetricsPluginLocation, "") + err := SetMetricPluginLocation("file:///tmp/metric-plugin") + assert.NoError(t, err) + assert.Equal(t, "/tmp/metric-plugin", GetMetricPluginLocation()) + + assert.Equal(t, DefaultMetricsPluginLocation, "") + err = SetMetricPluginLocation("http://localhost:8080/metric-plugin") + assert.NoError(t, err) + assert.Equal(t, DefaultPluginHttpFileLocation, GetMetricPluginLocation()) + + err = SetMetricPluginLocation("foo://localhost:8080/metric-plugin") + assert.NoError(t, err) + urlStr := GetMetricPluginLocation() + assert.Equal(t, "", urlStr) } diff --git a/utils/plugin/plugin.go b/utils/plugin/plugin.go new file mode 100644 index 0000000000..6b7222cd3b --- /dev/null +++ b/utils/plugin/plugin.go @@ -0,0 +1,125 @@ +package plugin + +import ( + "crypto/sha256" + "fmt" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + + "github.com/argoproj/argo-rollouts/utils/defaults" + "github.com/argoproj/argo-rollouts/utils/time" + log "github.com/sirupsen/logrus" +) + +type FileDownloader interface { + Get(url string) (resp *http.Response, err error) +} + +type FileDownloaderImpl struct { + FileDownloader +} + +func (fd FileDownloaderImpl) Get(url string) (resp *http.Response, err error) { + return http.Get(url) +} + +// CheckPluginExists this function checks if the plugin exists in the configured path if not we panic +func checkPluginExists() error { + if defaults.GetMetricPluginLocation() != "" { + //Check for plugin executable existence + _, err := os.Stat(defaults.GetMetricPluginLocation()) + if err != nil { + return err + } + } + return nil +} + +func checkShaOfPlugin(pluginLocation string, expectedSha256 string) (bool, error) { + hasher := sha256.New() + fileBytes, err := os.ReadFile(pluginLocation) + if err != nil { + return false, err + } + fileSha256 := fmt.Sprintf("%x", hasher.Sum(fileBytes)) + log.Printf("exected sha256: %s, actual sha256: %s, of downloaded metric plugin", expectedSha256, fileSha256) + return fileSha256 == expectedSha256, nil +} + +func downloadFile(filepath string, url string, downloader FileDownloader) error { + // Get the data + resp, err := downloader.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + // Create the file + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + // Write the body to file + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + // Set the file permissions, to allow execution + err = os.Chmod(filepath, 0700) + if err != nil { + return err + } + + return err +} + +// InitMetricsPlugin this function downloads and/or checks that a plugin executable exits on the filesystem +func InitMetricsPlugin(pluginPath string, fd FileDownloader, expectedSha256Hash string) error { + if pluginPath == "" { + return nil + } + + urlObj, err := url.ParseRequestURI(pluginPath) + if err != nil { + return err + } + + switch urlObj.Scheme { + case "http", "https": + log.Printf("Downloading plugin from: %s", pluginPath) + startTime := time.Now() + err = downloadFile(defaults.DefaultPluginHttpFileLocation, urlObj.String(), fd) + if err != nil { + return err + } + timeTakenToDownload := time.Now().Sub(startTime) + log.Printf("Download complete, it took %s", timeTakenToDownload) + defaults.SetMetricPluginLocation("file://" + defaults.DefaultPluginHttpFileLocation) + + if expectedSha256Hash != "" { + sha256Matched, err := checkShaOfPlugin(defaults.DefaultPluginHttpFileLocation, expectedSha256Hash) + if err != nil { + return err + } + if !sha256Matched { + return fmt.Errorf("sha256 hash of downloaded plugin does not match expected hash") + } + } + case "file": + pluginPath, err = filepath.Abs(urlObj.Host + urlObj.Path) + if err != nil { + return err + } + defaults.SetMetricPluginLocation(urlObj.Scheme + "://" + pluginPath) + default: + return fmt.Errorf("plugin location must be of http(s) or file scheme") + } + + return checkPluginExists() +} diff --git a/utils/plugin/plugin_test.go b/utils/plugin/plugin_test.go new file mode 100644 index 0000000000..d83e9f9f90 --- /dev/null +++ b/utils/plugin/plugin_test.go @@ -0,0 +1,78 @@ +package plugin + +import ( + "bytes" + "io" + "net/http" + "os" + "path/filepath" + "testing" + + "github.com/argoproj/argo-rollouts/utils/defaults" + "github.com/tj/assert" +) + +type MockFileDownloader struct { + FileDownloader +} + +func (m MockFileDownloader) Get(url string) (*http.Response, error) { + responseBody := io.NopCloser(bytes.NewReader([]byte(`test`))) + return &http.Response{ + Status: "200", + StatusCode: 200, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: nil, + Body: responseBody, + ContentLength: 4, + }, nil +} + +func TestInitPlugin(t *testing.T) { + fd := &MockFileDownloader{} + + err := InitMetricsPlugin("file://./plugin.go", fd, "") + assert.NoError(t, err) + + err = InitMetricsPlugin("file://plugin.go", fd, "") + assert.NoError(t, err) + + err = InitMetricsPlugin("file://./file-not-found.go", fd, "") + assert.Error(t, err) + + err = InitMetricsPlugin("file://file-not-found.go", fd, "") + assert.Error(t, err) + + err = InitMetricsPlugin("file://./../../Dockerfile", fd, "") + assert.NoError(t, err) + + f, err := filepath.Abs("plugin.go") + if err != nil { + t.Fatal(err) + } + err = InitMetricsPlugin("file://"+f, fd, "") + assert.NoError(t, err) + + f, err = filepath.Abs("file-not-found.go") + if err != nil { + t.Fatal(err) + } + err = InitMetricsPlugin("file://"+f, fd, "") + assert.Error(t, err) + + err = InitMetricsPlugin("http://mock-host:8080/plugin", fd, "74657374e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") + assert.NoError(t, err) + + err = InitMetricsPlugin("http://mock-host:8080/plugin", fd, "bad-sha") + assert.Error(t, err) + + err = InitMetricsPlugin("", fd, "bad-sha") + assert.NoError(t, err) + + err = InitMetricsPlugin("*(&^%$#@!", fd, "bad-sha") + assert.Error(t, err) + + os.Remove(defaults.DefaultPluginHttpFileLocation) +} diff --git a/utils/plugin/types/types.go b/utils/plugin/types/types.go new file mode 100644 index 0000000000..c1b8370479 --- /dev/null +++ b/utils/plugin/types/types.go @@ -0,0 +1,28 @@ +package types + +import "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + +type RpcError struct { + ErrorString string +} + +func (e RpcError) Error() string { + return e.ErrorString +} + +type RpcMetricProvider interface { + // Run start a new external system call for a measurement + // Should be idempotent and do nothing if a call has already been started + Run(*v1alpha1.AnalysisRun, v1alpha1.Metric) v1alpha1.Measurement + // Checks if the external system call is finished and returns the current measurement + Resume(*v1alpha1.AnalysisRun, v1alpha1.Metric, v1alpha1.Measurement) v1alpha1.Measurement + // Terminate will terminate an in-progress measurement + Terminate(*v1alpha1.AnalysisRun, v1alpha1.Metric, v1alpha1.Measurement) v1alpha1.Measurement + // GarbageCollect is used to garbage collect completed measurements to the specified limit + GarbageCollect(*v1alpha1.AnalysisRun, v1alpha1.Metric, int) RpcError + // Type gets the provider type + Type() string + // GetMetadata returns any additional metadata which providers need to store/display as part + // of the metric result. For example, Prometheus uses is to store the final resolved queries. + GetMetadata(metric v1alpha1.Metric) map[string]string +} From acc21f1bc7d3ff39c5499f82708ef26f8566f4f3 Mon Sep 17 00:00:00 2001 From: zachaller Date: Mon, 30 Jan 2023 10:02:28 -0600 Subject: [PATCH 02/53] add test for error Signed-off-by: zachaller --- metricproviders/plugin/rpc/rpc_test.go | 2 +- metricproviders/plugin/rpc/rpc_test_implementation.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metricproviders/plugin/rpc/rpc_test.go b/metricproviders/plugin/rpc/rpc_test.go index 070dafc1bf..fce15b2c58 100644 --- a/metricproviders/plugin/rpc/rpc_test.go +++ b/metricproviders/plugin/rpc/rpc_test.go @@ -109,7 +109,7 @@ func TestPlugin(t *testing.T) { assert.Equal(t, "TestCompletedTerminate", string(terminateMeasurement.Phase)) gcError := plugin.GarbageCollect(&v1alpha1.AnalysisRun{}, v1alpha1.Metric{}, 0) - assert.Equal(t, "", gcError.Error()) + assert.Equal(t, "not-implemented", gcError.Error()) typeTest := plugin.Type() assert.Equal(t, "TestRPCPlugin", typeTest) diff --git a/metricproviders/plugin/rpc/rpc_test_implementation.go b/metricproviders/plugin/rpc/rpc_test_implementation.go index c3a23720c8..8ed09262a5 100644 --- a/metricproviders/plugin/rpc/rpc_test_implementation.go +++ b/metricproviders/plugin/rpc/rpc_test_implementation.go @@ -45,7 +45,7 @@ func (g *testRpcPlugin) Terminate(analysisRun *v1alpha1.AnalysisRun, metric v1al } func (g *testRpcPlugin) GarbageCollect(*v1alpha1.AnalysisRun, v1alpha1.Metric, int) types.RpcError { - return types.RpcError{} + return types.RpcError{ErrorString: "not-implemented"} } func (g *testRpcPlugin) Type() string { From 6ceb973311e2d8ac9dab9c7536afc9072f63ea6d Mon Sep 17 00:00:00 2001 From: zachaller Date: Mon, 30 Jan 2023 10:32:22 -0600 Subject: [PATCH 03/53] remove test changes Signed-off-by: zachaller --- rollout/controller_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/rollout/controller_test.go b/rollout/controller_test.go index 1d9a738254..d7683fddec 100644 --- a/rollout/controller_test.go +++ b/rollout/controller_test.go @@ -1944,9 +1944,6 @@ func TestWriteBackToInformer(t *testing.T) { roKey := getKey(r1, t) f.runController(roKey, true, false, c, i, k8sI) - // Give controller a little time to process the rollout - time.Sleep(250 * time.Millisecond) - // Verify the informer was updated with the new unstructured object after reconciliation obj, exists, err := c.rolloutsIndexer.GetByKey(roKey) assert.NoError(t, err) From 4a66c5b81ee1fc87792072f4b512fb5afd43055b Mon Sep 17 00:00:00 2001 From: zachaller Date: Mon, 30 Jan 2023 14:26:51 -0600 Subject: [PATCH 04/53] small refactor Signed-off-by: zachaller --- Makefile | 2 +- metricproviders/metricproviders.go | 11 +++-------- metricproviders/plugin/plugin.go | 9 ++++----- pkg/apis/rollouts/v1alpha1/analysis_types.go | 5 ++++- pkg/apis/rollouts/v1alpha1/generated.pb.go | 8 ++++---- pkg/apis/rollouts/v1alpha1/generated.proto | 3 ++- pkg/apis/rollouts/v1alpha1/openapi_generated.go | 6 ++++-- .../sample-metrics-plugin/internal/plugin/plugin.go | 0 .../internal/plugin/plugin_test.go | 0 {cmd => test/cmd}/sample-metrics-plugin/main.go | 2 +- 10 files changed, 23 insertions(+), 23 deletions(-) rename {cmd => test/cmd}/sample-metrics-plugin/internal/plugin/plugin.go (100%) rename {cmd => test/cmd}/sample-metrics-plugin/internal/plugin/plugin_test.go (100%) rename {cmd => test/cmd}/sample-metrics-plugin/main.go (92%) diff --git a/Makefile b/Makefile index 6830d17bdf..ac3ba0b1bf 100644 --- a/Makefile +++ b/Makefile @@ -284,5 +284,5 @@ checksums: # https://www.jetbrains.com/help/go/attach-to-running-go-processes-with-debugger.html .PHONY: build-sample-metric-plugin-debug build-sample-metric-plugin-debug: - go build -gcflags="all=-N -l" -o metric-plugin cmd/sample-metrics-plugin/main.go + go build -gcflags="all=-N -l" -o metric-plugin test/cmd/sample-metrics-plugin/main.go diff --git a/metricproviders/metricproviders.go b/metricproviders/metricproviders.go index 2e0f664a02..0459842129 100644 --- a/metricproviders/metricproviders.go +++ b/metricproviders/metricproviders.go @@ -2,8 +2,8 @@ package metricproviders import ( "fmt" - "github.com/argoproj/argo-rollouts/metric" + "github.com/argoproj/argo-rollouts/metricproviders/cloudwatch" "github.com/argoproj/argo-rollouts/metricproviders/datadog" "github.com/argoproj/argo-rollouts/metricproviders/graphite" @@ -24,20 +24,15 @@ import ( "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" ) -// Provider this is here just for backwards compatibility the interface is now in the metric package -type Provider interface { - metric.Provider -} - type ProviderFactory struct { KubeClient kubernetes.Interface JobLister batchlisters.JobLister } -type ProviderFactoryFunc func(logCtx log.Entry, metric v1alpha1.Metric) (Provider, error) +type ProviderFactoryFunc func(logCtx log.Entry, metric v1alpha1.Metric) (metric.Provider, error) // NewProvider creates the correct provider based on the provider type of the Metric -func (f *ProviderFactory) NewProvider(logCtx log.Entry, metric v1alpha1.Metric) (Provider, error) { +func (f *ProviderFactory) NewProvider(logCtx log.Entry, metric v1alpha1.Metric) (metric.Provider, error) { switch provider := Type(metric); provider { case prometheus.ProviderType: api, err := prometheus.NewPrometheusAPI(metric) diff --git a/metricproviders/plugin/plugin.go b/metricproviders/plugin/plugin.go index bcc6283fe3..60b423e92c 100644 --- a/metricproviders/plugin/plugin.go +++ b/metricproviders/plugin/plugin.go @@ -47,11 +47,10 @@ func startPluginSystem(metric v1alpha1.Metric) error { if pluginClient == nil || pluginClient.Exited() { pluginClient = goPlugin.NewClient(&goPlugin.ClientConfig{ - HandshakeConfig: handshakeConfig, - Plugins: pluginMap, - VersionedPlugins: nil, - Cmd: exec.Command(defaults.GetMetricPluginLocation()), - Managed: true, + HandshakeConfig: handshakeConfig, + Plugins: pluginMap, + Cmd: exec.Command(defaults.GetMetricPluginLocation()), + Managed: true, }) rpcClient, err := pluginClient.Client() diff --git a/pkg/apis/rollouts/v1alpha1/analysis_types.go b/pkg/apis/rollouts/v1alpha1/analysis_types.go index 742c7bec61..262ff9cfe6 100644 --- a/pkg/apis/rollouts/v1alpha1/analysis_types.go +++ b/pkg/apis/rollouts/v1alpha1/analysis_types.go @@ -175,12 +175,15 @@ type MetricProvider struct { Influxdb *InfluxdbMetric `json:"influxdb,omitempty" protobuf:"bytes,10,opt,name=influxdb"` // Plugin specifies the hashicorp go-plugin metric to query Plugin *PluginMetric `json:"plugin,omitempty" protobuf:"bytes,11,opt,name=plugin"` + // Plugins specifies the hashicorp go-plugin metric to query + //Plugins json.RawMessage `json:"plugins,omitempty" protobuf:"bytes,11,opt,name=plugins"` } type PluginMetric struct { // +kubebuilder:pruning:PreserveUnknownFields // +kubebuilder:validation:Schemaless - Config json.RawMessage `json:"config,omitempty" protobuf:"bytes,1,rep,name=config"` + //PluginName string `json:"name" protobuf:"bytes,1,opt,name=pluginName"` + Config json.RawMessage `json:"config,omitempty" protobuf:"bytes,2,rep,name=config"` } // AnalysisPhase is the overall phase of an AnalysisRun, MetricResult, or Measurement diff --git a/pkg/apis/rollouts/v1alpha1/generated.pb.go b/pkg/apis/rollouts/v1alpha1/generated.pb.go index aaea5c09a5..0f4e61a710 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.pb.go +++ b/pkg/apis/rollouts/v1alpha1/generated.pb.go @@ -3531,7 +3531,7 @@ var fileDescriptor_e0e705f843545fab = []byte{ 0xb2, 0x1f, 0x56, 0x25, 0x11, 0x1c, 0xd2, 0x33, 0x5f, 0x81, 0x09, 0x3d, 0xdc, 0x02, 0x3d, 0x05, 0xe3, 0x6d, 0xdb, 0x69, 0x44, 0xc3, 0xf2, 0x94, 0x0d, 0xb9, 0x12, 0x82, 0xb0, 0x8e, 0xc7, 0xaa, 0xb9, 0x61, 0xb5, 0x98, 0xe9, 0xb9, 0xe2, 0xea, 0xd5, 0xc2, 0x3f, 0xe6, 0x06, 0x4c, 0xe8, 0xaa, - 0x12, 0x3d, 0x07, 0xa3, 0xdc, 0x6c, 0xcb, 0x18, 0x4f, 0xb0, 0xe0, 0xe6, 0x51, 0x6e, 0xe2, 0xbd, + 0x12, 0x3d, 0x07, 0xa3, 0xdc, 0x6c, 0xcb, 0x28, 0x4c, 0xb0, 0xe0, 0xe6, 0x51, 0x6e, 0xe2, 0xbd, 0xd3, 0x2d, 0xce, 0x13, 0xa7, 0xe6, 0xd6, 0x6d, 0xa7, 0xb1, 0x74, 0xcb, 0x77, 0x9d, 0x45, 0x6c, 0xed, 0x4b, 0xed, 0x21, 0x6a, 0x99, 0xbf, 0x9f, 0x85, 0x33, 0x09, 0x61, 0x42, 0xe8, 0x75, 0x03, 0x46, 0x59, 0x94, 0x81, 0xf4, 0xba, 0x79, 0x29, 0xf5, 0x50, 0xa4, 0x45, 0x16, 0xcc, 0x20, 0xd6, @@ -3689,7 +3689,7 @@ var fileDescriptor_e0e705f843545fab = []byte{ 0xf7, 0xbd, 0x7a, 0x78, 0xc1, 0xf8, 0xfe, 0xe1, 0x05, 0xe3, 0x87, 0x87, 0x17, 0x8c, 0x9f, 0x1c, 0x5e, 0x30, 0xfe, 0xfa, 0xf0, 0x82, 0xf1, 0xe6, 0x2f, 0x2e, 0xdc, 0xf7, 0xe2, 0xb3, 0xe1, 0x04, 0x5d, 0x92, 0x13, 0x94, 0xfd, 0x78, 0x8f, 0x9c, 0x8e, 0x4b, 0xed, 0xdd, 0xc6, 0x12, 0x9d, 0xa0, - 0x4b, 0xaa, 0x44, 0x4e, 0xd0, 0x7f, 0x09, 0x00, 0x00, 0xff, 0xff, 0xb4, 0xa1, 0xef, 0x00, 0x88, + 0x4b, 0xaa, 0x44, 0x4e, 0xd0, 0x7f, 0x09, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x70, 0x0c, 0x96, 0x88, 0x96, 0x00, 0x00, } @@ -7392,7 +7392,7 @@ func (m *PluginMetric) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.Config) i = encodeVarintGenerated(dAtA, i, uint64(len(m.Config))) i-- - dAtA[i] = 0xa + dAtA[i] = 0x12 } return len(dAtA) - i, nil } @@ -25019,7 +25019,7 @@ func (m *PluginMetric) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: PluginMetric: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 1: + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) } diff --git a/pkg/apis/rollouts/v1alpha1/generated.proto b/pkg/apis/rollouts/v1alpha1/generated.proto index 9d9d027d60..9fff22ea0d 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.proto +++ b/pkg/apis/rollouts/v1alpha1/generated.proto @@ -1078,7 +1078,8 @@ message PingPongSpec { message PluginMetric { // +kubebuilder:pruning:PreserveUnknownFields // +kubebuilder:validation:Schemaless - optional bytes config = 1; + // PluginName string `json:"name" protobuf:"bytes,1,opt,name=pluginName"` + optional bytes config = 2; } // PodTemplateMetadata extra labels to add to the template diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index be10fbd4cd..489b541180 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -2929,6 +2929,7 @@ func schema_pkg_apis_rollouts_v1alpha1_MetricProvider(ref common.ReferenceCallba "plugin": { SchemaProps: spec.SchemaProps{ Description: "Plugin specifies the hashicorp go-plugin metric to query", + Default: map[string]interface{}{}, Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PluginMetric"), }, }, @@ -3232,8 +3233,9 @@ func schema_pkg_apis_rollouts_v1alpha1_PluginMetric(ref common.ReferenceCallback Properties: map[string]spec.Schema{ "config": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "byte", + Description: "PluginName string `json:\"name\" protobuf:\"bytes,1,opt,name=pluginName\"`", + Type: []string{"string"}, + Format: "byte", }, }, }, diff --git a/cmd/sample-metrics-plugin/internal/plugin/plugin.go b/test/cmd/sample-metrics-plugin/internal/plugin/plugin.go similarity index 100% rename from cmd/sample-metrics-plugin/internal/plugin/plugin.go rename to test/cmd/sample-metrics-plugin/internal/plugin/plugin.go diff --git a/cmd/sample-metrics-plugin/internal/plugin/plugin_test.go b/test/cmd/sample-metrics-plugin/internal/plugin/plugin_test.go similarity index 100% rename from cmd/sample-metrics-plugin/internal/plugin/plugin_test.go rename to test/cmd/sample-metrics-plugin/internal/plugin/plugin_test.go diff --git a/cmd/sample-metrics-plugin/main.go b/test/cmd/sample-metrics-plugin/main.go similarity index 92% rename from cmd/sample-metrics-plugin/main.go rename to test/cmd/sample-metrics-plugin/main.go index 4047f197b5..ed4a9ec7d5 100644 --- a/cmd/sample-metrics-plugin/main.go +++ b/test/cmd/sample-metrics-plugin/main.go @@ -1,8 +1,8 @@ package main import ( - "github.com/argoproj/argo-rollouts/cmd/sample-metrics-plugin/internal/plugin" "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" + "github.com/argoproj/argo-rollouts/test/cmd/sample-metrics-plugin/internal/plugin" goPlugin "github.com/hashicorp/go-plugin" log "github.com/sirupsen/logrus" ) From 00edfbba49664de6551d3b039bc341e09aba4efa Mon Sep 17 00:00:00 2001 From: zachaller Date: Mon, 30 Jan 2023 14:35:12 -0600 Subject: [PATCH 05/53] refactor Signed-off-by: zachaller --- analysis/controller.go | 3 ++- analysis/controller_test.go | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/analysis/controller.go b/analysis/controller.go index f05c955bb1..77d335b7c5 100644 --- a/analysis/controller.go +++ b/analysis/controller.go @@ -2,6 +2,7 @@ package analysis import ( "context" + "github.com/argoproj/argo-rollouts/metric" "sync" "time" @@ -44,7 +45,7 @@ type Controller struct { metricsServer *metrics.MetricsServer - newProvider func(logCtx log.Entry, metric v1alpha1.Metric) (metricproviders.Provider, error) + newProvider func(logCtx log.Entry, metric v1alpha1.Metric) (metric.Provider, error) // used for unit testing enqueueAnalysis func(obj interface{}) diff --git a/analysis/controller_test.go b/analysis/controller_test.go index a0af638c9e..71f8722e04 100644 --- a/analysis/controller_test.go +++ b/analysis/controller_test.go @@ -3,6 +3,7 @@ package analysis import ( "context" "encoding/json" + "github.com/argoproj/argo-rollouts/metric" "reflect" "testing" "time" @@ -25,7 +26,6 @@ import ( "k8s.io/client-go/util/workqueue" "github.com/argoproj/argo-rollouts/controller/metrics" - "github.com/argoproj/argo-rollouts/metricproviders" "github.com/argoproj/argo-rollouts/metricproviders/mocks" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/pkg/client/clientset/versioned/fake" @@ -129,7 +129,7 @@ func (f *fixture) newController(resync resyncFunc) (*Controller, informers.Share c.enqueueAnalysis(obj) } f.provider = &mocks.Provider{} - c.newProvider = func(logCtx log.Entry, metric v1alpha1.Metric) (metricproviders.Provider, error) { + c.newProvider = func(logCtx log.Entry, metric v1alpha1.Metric) (metric.Provider, error) { return f.provider, nil } From 42a5b03ce8dd8842df4fcafe7c6b81449e988f0d Mon Sep 17 00:00:00 2001 From: zachaller Date: Mon, 30 Jan 2023 14:46:27 -0600 Subject: [PATCH 06/53] refactor Signed-off-by: zachaller --- hack/update-mocks.sh | 2 +- metricproviders/mocks/Provider.go | 8 ++--- .../rollouts/v1alpha1/openapi_generated.go | 34 ++++++------------- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/hack/update-mocks.sh b/hack/update-mocks.sh index 453d98d7f9..6f1bcfad4a 100755 --- a/hack/update-mocks.sh +++ b/hack/update-mocks.sh @@ -8,7 +8,7 @@ set -o pipefail PROJECT_ROOT=$(cd $(dirname "$0")/.. ; pwd) mockery \ - --dir "${PROJECT_ROOT}"/metricproviders \ + --dir "${PROJECT_ROOT}"/metric \ --name Provider \ --output "${PROJECT_ROOT}"/metricproviders/mocks diff --git a/metricproviders/mocks/Provider.go b/metricproviders/mocks/Provider.go index 1b54e4281c..6944f7b05c 100644 --- a/metricproviders/mocks/Provider.go +++ b/metricproviders/mocks/Provider.go @@ -26,13 +26,13 @@ func (_m *Provider) GarbageCollect(_a0 *v1alpha1.AnalysisRun, _a1 v1alpha1.Metri return r0 } -// GetMetadata provides a mock function with given fields: metric -func (_m *Provider) GetMetadata(metric v1alpha1.Metric) map[string]string { - ret := _m.Called(metric) +// GetMetadata provides a mock function with given fields: _a0 +func (_m *Provider) GetMetadata(_a0 v1alpha1.Metric) map[string]string { + ret := _m.Called(_a0) var r0 map[string]string if rf, ok := ret.Get(0).(func(v1alpha1.Metric) map[string]string); ok { - r0 = rf(metric) + r0 = rf(_a0) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(map[string]string) diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index 489b541180..f3634a21b4 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -95,7 +95,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.ObjectRef": schema_pkg_apis_rollouts_v1alpha1_ObjectRef(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PauseCondition": schema_pkg_apis_rollouts_v1alpha1_PauseCondition(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PingPongSpec": schema_pkg_apis_rollouts_v1alpha1_PingPongSpec(ref), - "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PluginMetric": schema_pkg_apis_rollouts_v1alpha1_PluginMetric(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PodTemplateMetadata": schema_pkg_apis_rollouts_v1alpha1_PodTemplateMetadata(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PreferredDuringSchedulingIgnoredDuringExecution": schema_pkg_apis_rollouts_v1alpha1_PreferredDuringSchedulingIgnoredDuringExecution(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PrometheusMetric": schema_pkg_apis_rollouts_v1alpha1_PrometheusMetric(ref), @@ -2929,15 +2928,23 @@ func schema_pkg_apis_rollouts_v1alpha1_MetricProvider(ref common.ReferenceCallba "plugin": { SchemaProps: spec.SchemaProps{ Description: "Plugin specifies the hashicorp go-plugin metric to query", - Default: map[string]interface{}{}, - Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PluginMetric"), + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "byte", + }, + }, + }, }, }, }, }, }, Dependencies: []string{ - "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.CloudWatchMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.DatadogMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.GraphiteMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.InfluxdbMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.JobMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.KayentaMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.NewRelicMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PluginMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PrometheusMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.WavefrontMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.WebMetric"}, + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.CloudWatchMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.DatadogMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.GraphiteMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.InfluxdbMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.JobMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.KayentaMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.NewRelicMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PrometheusMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.WavefrontMetric", "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.WebMetric"}, } } @@ -3225,25 +3232,6 @@ func schema_pkg_apis_rollouts_v1alpha1_PingPongSpec(ref common.ReferenceCallback } } -func schema_pkg_apis_rollouts_v1alpha1_PluginMetric(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "config": { - SchemaProps: spec.SchemaProps{ - Description: "PluginName string `json:\"name\" protobuf:\"bytes,1,opt,name=pluginName\"`", - Type: []string{"string"}, - Format: "byte", - }, - }, - }, - }, - }, - } -} - func schema_pkg_apis_rollouts_v1alpha1_PodTemplateMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ From 9b7ca413477742124b16a5fa0f355c8a35d4110d Mon Sep 17 00:00:00 2001 From: zachaller Date: Mon, 30 Jan 2023 15:28:03 -0600 Subject: [PATCH 07/53] change type Signed-off-by: zachaller --- .../features/kustomize/rollout_cr_schema.json | 24 +- manifests/crds/analysis-run-crd.yaml | 4 +- manifests/crds/analysis-template-crd.yaml | 4 +- .../crds/cluster-analysis-template-crd.yaml | 4 +- manifests/install.yaml | 12 +- metricproviders/plugin/rpc/rpc_test.go | 2 +- .../plugin/rpc/rpc_test_implementation.go | 11 + pkg/apis/rollouts/v1alpha1/analysis_types.go | 14 +- pkg/apis/rollouts/v1alpha1/generated.pb.go | 1390 ++++++++--------- pkg/apis/rollouts/v1alpha1/generated.proto | 12 +- .../rollouts/v1alpha1/openapi_generated.go | 19 + .../v1alpha1/zz_generated.deepcopy.go | 35 +- 12 files changed, 738 insertions(+), 793 deletions(-) diff --git a/docs/features/kustomize/rollout_cr_schema.json b/docs/features/kustomize/rollout_cr_schema.json index 8751fe00a9..cf31606c7b 100644 --- a/docs/features/kustomize/rollout_cr_schema.json +++ b/docs/features/kustomize/rollout_cr_schema.json @@ -4166,12 +4166,8 @@ "type": "object" }, "plugin": { - "properties": { - "config": { - "x-kubernetes-preserve-unknown-fields": true - } - }, - "type": "object" + "type": "object", + "x-kubernetes-preserve-unknown-fields": true }, "prometheus": { "properties": { @@ -8442,12 +8438,8 @@ "type": "object" }, "plugin": { - "properties": { - "config": { - "x-kubernetes-preserve-unknown-fields": true - } - }, - "type": "object" + "type": "object", + "x-kubernetes-preserve-unknown-fields": true }, "prometheus": { "properties": { @@ -12718,12 +12710,8 @@ "type": "object" }, "plugin": { - "properties": { - "config": { - "x-kubernetes-preserve-unknown-fields": true - } - }, - "type": "object" + "type": "object", + "x-kubernetes-preserve-unknown-fields": true }, "prometheus": { "properties": { diff --git a/manifests/crds/analysis-run-crd.yaml b/manifests/crds/analysis-run-crd.yaml index cc2da51d06..a58eaaf3ab 100644 --- a/manifests/crds/analysis-run-crd.yaml +++ b/manifests/crds/analysis-run-crd.yaml @@ -2701,10 +2701,8 @@ spec: - query type: object plugin: - properties: - config: - x-kubernetes-preserve-unknown-fields: true type: object + x-kubernetes-preserve-unknown-fields: true prometheus: properties: address: diff --git a/manifests/crds/analysis-template-crd.yaml b/manifests/crds/analysis-template-crd.yaml index 784e088dad..d9c91722b8 100644 --- a/manifests/crds/analysis-template-crd.yaml +++ b/manifests/crds/analysis-template-crd.yaml @@ -2697,10 +2697,8 @@ spec: - query type: object plugin: - properties: - config: - x-kubernetes-preserve-unknown-fields: true type: object + x-kubernetes-preserve-unknown-fields: true prometheus: properties: address: diff --git a/manifests/crds/cluster-analysis-template-crd.yaml b/manifests/crds/cluster-analysis-template-crd.yaml index d309aa0169..76bbf2d68b 100644 --- a/manifests/crds/cluster-analysis-template-crd.yaml +++ b/manifests/crds/cluster-analysis-template-crd.yaml @@ -2697,10 +2697,8 @@ spec: - query type: object plugin: - properties: - config: - x-kubernetes-preserve-unknown-fields: true type: object + x-kubernetes-preserve-unknown-fields: true prometheus: properties: address: diff --git a/manifests/install.yaml b/manifests/install.yaml index 11130bd027..29c7693202 100755 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -2702,10 +2702,8 @@ spec: - query type: object plugin: - properties: - config: - x-kubernetes-preserve-unknown-fields: true type: object + x-kubernetes-preserve-unknown-fields: true prometheus: properties: address: @@ -5584,10 +5582,8 @@ spec: - query type: object plugin: - properties: - config: - x-kubernetes-preserve-unknown-fields: true type: object + x-kubernetes-preserve-unknown-fields: true prometheus: properties: address: @@ -8352,10 +8348,8 @@ spec: - query type: object plugin: - properties: - config: - x-kubernetes-preserve-unknown-fields: true type: object + x-kubernetes-preserve-unknown-fields: true prometheus: properties: address: diff --git a/metricproviders/plugin/rpc/rpc_test.go b/metricproviders/plugin/rpc/rpc_test.go index fce15b2c58..d981a009bc 100644 --- a/metricproviders/plugin/rpc/rpc_test.go +++ b/metricproviders/plugin/rpc/rpc_test.go @@ -82,7 +82,7 @@ func TestPlugin(t *testing.T) { err := plugin.NewMetricsPlugin(v1alpha1.Metric{ Provider: v1alpha1.MetricProvider{ - Plugin: &v1alpha1.PluginMetric{Config: json.RawMessage(`{"address":"http://prometheus.local", "query":"machine_cpu_cores"}`)}, + Plugin: map[string]json.RawMessage{"prometheus": json.RawMessage(`{"address":"http://prometheus.local", "query":"machine_cpu_cores"}`)}, }, }) if err.Error() != "" { diff --git a/metricproviders/plugin/rpc/rpc_test_implementation.go b/metricproviders/plugin/rpc/rpc_test_implementation.go index 8ed09262a5..bb90d2339f 100644 --- a/metricproviders/plugin/rpc/rpc_test_implementation.go +++ b/metricproviders/plugin/rpc/rpc_test_implementation.go @@ -2,6 +2,7 @@ package rpc import ( "fmt" + "k8s.io/apimachinery/pkg/util/json" "time" "github.com/argoproj/argo-rollouts/utils/plugin/types" @@ -14,7 +15,17 @@ import ( type testRpcPlugin struct{} +type config struct { + Address string `json:"address"` + Query string `json:"query"` +} + func (g *testRpcPlugin) NewMetricsPlugin(metric v1alpha1.Metric) types.RpcError { + var c config + err := json.Unmarshal(metric.Provider.Plugin["prometheus"], &c) + if err != nil { + return types.RpcError{ErrorString: err.Error()} + } return types.RpcError{} } diff --git a/pkg/apis/rollouts/v1alpha1/analysis_types.go b/pkg/apis/rollouts/v1alpha1/analysis_types.go index 262ff9cfe6..db72d42bf1 100644 --- a/pkg/apis/rollouts/v1alpha1/analysis_types.go +++ b/pkg/apis/rollouts/v1alpha1/analysis_types.go @@ -173,17 +173,11 @@ type MetricProvider struct { Graphite *GraphiteMetric `json:"graphite,omitempty" protobuf:"bytes,9,opt,name=graphite"` // Influxdb specifies the influxdb metric to query Influxdb *InfluxdbMetric `json:"influxdb,omitempty" protobuf:"bytes,10,opt,name=influxdb"` - // Plugin specifies the hashicorp go-plugin metric to query - Plugin *PluginMetric `json:"plugin,omitempty" protobuf:"bytes,11,opt,name=plugin"` - // Plugins specifies the hashicorp go-plugin metric to query - //Plugins json.RawMessage `json:"plugins,omitempty" protobuf:"bytes,11,opt,name=plugins"` -} - -type PluginMetric struct { - // +kubebuilder:pruning:PreserveUnknownFields // +kubebuilder:validation:Schemaless - //PluginName string `json:"name" protobuf:"bytes,1,opt,name=pluginName"` - Config json.RawMessage `json:"config,omitempty" protobuf:"bytes,2,rep,name=config"` + // +kubebuilder:pruning:PreserveUnknownFields + // +kubebuilder:validation:Type=object + // Plugin specifies the hashicorp go-plugin metric to query + Plugin map[string]json.RawMessage `json:"plugin,omitempty" protobuf:"bytes,11,opt,name=plugin"` } // AnalysisPhase is the overall phase of an AnalysisRun, MetricResult, or Measurement diff --git a/pkg/apis/rollouts/v1alpha1/generated.pb.go b/pkg/apis/rollouts/v1alpha1/generated.pb.go index 0f4e61a710..b2837b8974 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.pb.go +++ b/pkg/apis/rollouts/v1alpha1/generated.pb.go @@ -20,6 +20,7 @@ limitations under the License. package v1alpha1 import ( + encoding_json "encoding/json" fmt "fmt" io "io" @@ -1868,38 +1869,10 @@ func (m *PingPongSpec) XXX_DiscardUnknown() { var xxx_messageInfo_PingPongSpec proto.InternalMessageInfo -func (m *PluginMetric) Reset() { *m = PluginMetric{} } -func (*PluginMetric) ProtoMessage() {} -func (*PluginMetric) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{65} -} -func (m *PluginMetric) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *PluginMetric) 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 *PluginMetric) XXX_Merge(src proto.Message) { - xxx_messageInfo_PluginMetric.Merge(m, src) -} -func (m *PluginMetric) XXX_Size() int { - return m.Size() -} -func (m *PluginMetric) XXX_DiscardUnknown() { - xxx_messageInfo_PluginMetric.DiscardUnknown(m) -} - -var xxx_messageInfo_PluginMetric proto.InternalMessageInfo - func (m *PodTemplateMetadata) Reset() { *m = PodTemplateMetadata{} } func (*PodTemplateMetadata) ProtoMessage() {} func (*PodTemplateMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{66} + return fileDescriptor_e0e705f843545fab, []int{65} } func (m *PodTemplateMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1929,7 +1902,7 @@ func (m *PreferredDuringSchedulingIgnoredDuringExecution) Reset() { } func (*PreferredDuringSchedulingIgnoredDuringExecution) ProtoMessage() {} func (*PreferredDuringSchedulingIgnoredDuringExecution) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{67} + return fileDescriptor_e0e705f843545fab, []int{66} } func (m *PreferredDuringSchedulingIgnoredDuringExecution) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1957,7 +1930,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{68} + return fileDescriptor_e0e705f843545fab, []int{67} } func (m *PrometheusMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1987,7 +1960,7 @@ func (m *RequiredDuringSchedulingIgnoredDuringExecution) Reset() { } func (*RequiredDuringSchedulingIgnoredDuringExecution) ProtoMessage() {} func (*RequiredDuringSchedulingIgnoredDuringExecution) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{69} + return fileDescriptor_e0e705f843545fab, []int{68} } func (m *RequiredDuringSchedulingIgnoredDuringExecution) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2015,7 +1988,7 @@ var xxx_messageInfo_RequiredDuringSchedulingIgnoredDuringExecution proto.Interna func (m *RollbackWindowSpec) Reset() { *m = RollbackWindowSpec{} } func (*RollbackWindowSpec) ProtoMessage() {} func (*RollbackWindowSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{70} + return fileDescriptor_e0e705f843545fab, []int{69} } func (m *RollbackWindowSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2043,7 +2016,7 @@ var xxx_messageInfo_RollbackWindowSpec proto.InternalMessageInfo func (m *Rollout) Reset() { *m = Rollout{} } func (*Rollout) ProtoMessage() {} func (*Rollout) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{71} + return fileDescriptor_e0e705f843545fab, []int{70} } func (m *Rollout) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2071,7 +2044,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{72} + return fileDescriptor_e0e705f843545fab, []int{71} } func (m *RolloutAnalysis) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2099,7 +2072,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{73} + return fileDescriptor_e0e705f843545fab, []int{72} } func (m *RolloutAnalysisBackground) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2127,7 +2100,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{74} + return fileDescriptor_e0e705f843545fab, []int{73} } func (m *RolloutAnalysisRunStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2155,7 +2128,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{75} + return fileDescriptor_e0e705f843545fab, []int{74} } func (m *RolloutAnalysisTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2183,7 +2156,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{76} + return fileDescriptor_e0e705f843545fab, []int{75} } func (m *RolloutCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2211,7 +2184,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{77} + return fileDescriptor_e0e705f843545fab, []int{76} } func (m *RolloutExperimentStep) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2241,7 +2214,7 @@ func (m *RolloutExperimentStepAnalysisTemplateRef) Reset() { } func (*RolloutExperimentStepAnalysisTemplateRef) ProtoMessage() {} func (*RolloutExperimentStepAnalysisTemplateRef) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{78} + return fileDescriptor_e0e705f843545fab, []int{77} } func (m *RolloutExperimentStepAnalysisTemplateRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2269,7 +2242,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{79} + return fileDescriptor_e0e705f843545fab, []int{78} } func (m *RolloutExperimentTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2297,7 +2270,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{80} + return fileDescriptor_e0e705f843545fab, []int{79} } func (m *RolloutList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2325,7 +2298,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{81} + return fileDescriptor_e0e705f843545fab, []int{80} } func (m *RolloutPause) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2353,7 +2326,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{82} + return fileDescriptor_e0e705f843545fab, []int{81} } func (m *RolloutSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2381,7 +2354,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{83} + return fileDescriptor_e0e705f843545fab, []int{82} } func (m *RolloutStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2409,7 +2382,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{84} + return fileDescriptor_e0e705f843545fab, []int{83} } func (m *RolloutStrategy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2437,7 +2410,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{85} + return fileDescriptor_e0e705f843545fab, []int{84} } func (m *RolloutTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2465,7 +2438,7 @@ var xxx_messageInfo_RolloutTrafficRouting proto.InternalMessageInfo func (m *RouteMatch) Reset() { *m = RouteMatch{} } func (*RouteMatch) ProtoMessage() {} func (*RouteMatch) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{86} + return fileDescriptor_e0e705f843545fab, []int{85} } func (m *RouteMatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2493,7 +2466,7 @@ var xxx_messageInfo_RouteMatch proto.InternalMessageInfo func (m *RunSummary) Reset() { *m = RunSummary{} } func (*RunSummary) ProtoMessage() {} func (*RunSummary) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{87} + return fileDescriptor_e0e705f843545fab, []int{86} } func (m *RunSummary) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2521,7 +2494,7 @@ var xxx_messageInfo_RunSummary proto.InternalMessageInfo func (m *SMITrafficRouting) Reset() { *m = SMITrafficRouting{} } func (*SMITrafficRouting) ProtoMessage() {} func (*SMITrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{88} + return fileDescriptor_e0e705f843545fab, []int{87} } func (m *SMITrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2549,7 +2522,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{89} + return fileDescriptor_e0e705f843545fab, []int{88} } func (m *ScopeDetail) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2577,7 +2550,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{90} + return fileDescriptor_e0e705f843545fab, []int{89} } func (m *SecretKeyRef) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2605,7 +2578,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{91} + return fileDescriptor_e0e705f843545fab, []int{90} } func (m *SetCanaryScale) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2633,7 +2606,7 @@ var xxx_messageInfo_SetCanaryScale proto.InternalMessageInfo func (m *SetHeaderRoute) Reset() { *m = SetHeaderRoute{} } func (*SetHeaderRoute) ProtoMessage() {} func (*SetHeaderRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{92} + return fileDescriptor_e0e705f843545fab, []int{91} } func (m *SetHeaderRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2661,7 +2634,7 @@ var xxx_messageInfo_SetHeaderRoute proto.InternalMessageInfo func (m *SetMirrorRoute) Reset() { *m = SetMirrorRoute{} } func (*SetMirrorRoute) ProtoMessage() {} func (*SetMirrorRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{93} + return fileDescriptor_e0e705f843545fab, []int{92} } func (m *SetMirrorRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2689,7 +2662,7 @@ var xxx_messageInfo_SetMirrorRoute proto.InternalMessageInfo func (m *StickinessConfig) Reset() { *m = StickinessConfig{} } func (*StickinessConfig) ProtoMessage() {} func (*StickinessConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{94} + return fileDescriptor_e0e705f843545fab, []int{93} } func (m *StickinessConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2717,7 +2690,7 @@ var xxx_messageInfo_StickinessConfig proto.InternalMessageInfo func (m *StringMatch) Reset() { *m = StringMatch{} } func (*StringMatch) ProtoMessage() {} func (*StringMatch) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{95} + return fileDescriptor_e0e705f843545fab, []int{94} } func (m *StringMatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2745,7 +2718,7 @@ var xxx_messageInfo_StringMatch proto.InternalMessageInfo func (m *TCPRoute) Reset() { *m = TCPRoute{} } func (*TCPRoute) ProtoMessage() {} func (*TCPRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{96} + return fileDescriptor_e0e705f843545fab, []int{95} } func (m *TCPRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2773,7 +2746,7 @@ var xxx_messageInfo_TCPRoute proto.InternalMessageInfo func (m *TLSRoute) Reset() { *m = TLSRoute{} } func (*TLSRoute) ProtoMessage() {} func (*TLSRoute) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{97} + return fileDescriptor_e0e705f843545fab, []int{96} } func (m *TLSRoute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2801,7 +2774,7 @@ var xxx_messageInfo_TLSRoute proto.InternalMessageInfo func (m *TemplateService) Reset() { *m = TemplateService{} } func (*TemplateService) ProtoMessage() {} func (*TemplateService) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{98} + return fileDescriptor_e0e705f843545fab, []int{97} } func (m *TemplateService) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2829,7 +2802,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{99} + return fileDescriptor_e0e705f843545fab, []int{98} } func (m *TemplateSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2857,7 +2830,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{100} + return fileDescriptor_e0e705f843545fab, []int{99} } func (m *TemplateStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2885,7 +2858,7 @@ var xxx_messageInfo_TemplateStatus proto.InternalMessageInfo func (m *TraefikTrafficRouting) Reset() { *m = TraefikTrafficRouting{} } func (*TraefikTrafficRouting) ProtoMessage() {} func (*TraefikTrafficRouting) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{101} + return fileDescriptor_e0e705f843545fab, []int{100} } func (m *TraefikTrafficRouting) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2913,7 +2886,7 @@ var xxx_messageInfo_TraefikTrafficRouting proto.InternalMessageInfo func (m *TrafficWeights) Reset() { *m = TrafficWeights{} } func (*TrafficWeights) ProtoMessage() {} func (*TrafficWeights) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{102} + return fileDescriptor_e0e705f843545fab, []int{101} } func (m *TrafficWeights) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2941,7 +2914,7 @@ var xxx_messageInfo_TrafficWeights proto.InternalMessageInfo func (m *ValueFrom) Reset() { *m = ValueFrom{} } func (*ValueFrom) ProtoMessage() {} func (*ValueFrom) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{103} + return fileDescriptor_e0e705f843545fab, []int{102} } func (m *ValueFrom) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2969,7 +2942,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{104} + return fileDescriptor_e0e705f843545fab, []int{103} } func (m *WavefrontMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2997,7 +2970,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{105} + return fileDescriptor_e0e705f843545fab, []int{104} } func (m *WebMetric) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3025,7 +2998,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{106} + return fileDescriptor_e0e705f843545fab, []int{105} } func (m *WebMetricHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3053,7 +3026,7 @@ var xxx_messageInfo_WebMetricHeader proto.InternalMessageInfo func (m *WeightDestination) Reset() { *m = WeightDestination{} } func (*WeightDestination) ProtoMessage() {} func (*WeightDestination) Descriptor() ([]byte, []int) { - return fileDescriptor_e0e705f843545fab, []int{107} + return fileDescriptor_e0e705f843545fab, []int{106} } func (m *WeightDestination) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3139,6 +3112,7 @@ func init() { proto.RegisterType((*MeasurementRetention)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.MeasurementRetention") proto.RegisterType((*Metric)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.Metric") proto.RegisterType((*MetricProvider)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.MetricProvider") + proto.RegisterMapType((map[string]encoding_json.RawMessage)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.MetricProvider.PluginEntry") proto.RegisterType((*MetricResult)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.MetricResult") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.MetricResult.MetadataEntry") proto.RegisterType((*NewRelicMetric)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.NewRelicMetric") @@ -3147,7 +3121,6 @@ func init() { proto.RegisterType((*ObjectRef)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.ObjectRef") proto.RegisterType((*PauseCondition)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PauseCondition") proto.RegisterType((*PingPongSpec)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PingPongSpec") - proto.RegisterType((*PluginMetric)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PluginMetric") proto.RegisterType((*PodTemplateMetadata)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PodTemplateMetadata") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PodTemplateMetadata.AnnotationsEntry") proto.RegisterMapType((map[string]string)(nil), "github.com.argoproj.argo_rollouts.pkg.apis.rollouts.v1alpha1.PodTemplateMetadata.LabelsEntry") @@ -3200,497 +3173,497 @@ func init() { } var fileDescriptor_e0e705f843545fab = []byte{ - // 7827 bytes of a gzipped FileDescriptorProto + // 7825 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0x5b, 0x6c, 0x24, 0xd7, - 0x75, 0xa0, 0xaa, 0x9b, 0x4d, 0x76, 0x1f, 0xbe, 0xef, 0x70, 0x3c, 0x14, 0xa5, 0x99, 0x96, 0x4b, - 0x86, 0x56, 0xde, 0x95, 0x49, 0x5b, 0x8f, 0x5d, 0xd9, 0x12, 0xb4, 0xdb, 0x4d, 0xce, 0x68, 0x38, - 0x22, 0x39, 0x3d, 0xb7, 0x39, 0x33, 0xb6, 0x6c, 0xd9, 0x2e, 0x76, 0x5f, 0x36, 0x6b, 0xd8, 0x5d, - 0xd5, 0xae, 0xaa, 0x26, 0x87, 0xb2, 0x60, 0x4b, 0x36, 0xa4, 0xf5, 0x2e, 0x6c, 0x58, 0xbb, 0xb6, - 0xb1, 0x58, 0x2c, 0xb0, 0x30, 0x16, 0x06, 0x36, 0x89, 0xfd, 0x65, 0x24, 0xc8, 0x8f, 0x81, 0x04, - 0xf1, 0x23, 0xce, 0x87, 0x03, 0xfb, 0x23, 0xf1, 0x03, 0x70, 0x27, 0xa2, 0xf3, 0x93, 0x20, 0x81, - 0x11, 0xc0, 0x41, 0xe0, 0xf9, 0x0a, 0xee, 0xb3, 0x6e, 0x55, 0x57, 0x73, 0x9a, 0xec, 0xe2, 0x58, - 0x48, 0xfc, 0xd7, 0x7d, 0xcf, 0xb9, 0xe7, 0x9c, 0xfb, 0x3c, 0xf7, 0x9e, 0x7b, 0xce, 0x29, 0x58, - 0x6b, 0xd8, 0xc1, 0x4e, 0x67, 0x6b, 0xb1, 0xe6, 0xb6, 0x96, 0x2c, 0xaf, 0xe1, 0xb6, 0x3d, 0xf7, - 0x16, 0xfb, 0xf1, 0x1e, 0xcf, 0x6d, 0x36, 0xdd, 0x4e, 0xe0, 0x2f, 0xb5, 0x77, 0x1b, 0x4b, 0x56, - 0xdb, 0xf6, 0x97, 0x54, 0xc9, 0xde, 0xfb, 0xac, 0x66, 0x7b, 0xc7, 0x7a, 0xdf, 0x52, 0x83, 0x38, - 0xc4, 0xb3, 0x02, 0x52, 0x5f, 0x6c, 0x7b, 0x6e, 0xe0, 0xa2, 0x67, 0x43, 0x6a, 0x8b, 0x92, 0x1a, - 0xfb, 0xf1, 0x31, 0x59, 0x77, 0xb1, 0xbd, 0xdb, 0x58, 0xa4, 0xd4, 0x16, 0x55, 0x89, 0xa4, 0xb6, - 0xf0, 0x1e, 0x4d, 0x96, 0x86, 0xdb, 0x70, 0x97, 0x18, 0xd1, 0xad, 0xce, 0x36, 0xfb, 0xc7, 0xfe, - 0xb0, 0x5f, 0x9c, 0xd9, 0xc2, 0xc3, 0xbb, 0x4f, 0xfb, 0x8b, 0xb6, 0x4b, 0x65, 0x5b, 0xda, 0xb2, - 0x82, 0xda, 0xce, 0xd2, 0x5e, 0x8f, 0x44, 0x0b, 0xa6, 0x86, 0x54, 0x73, 0x3d, 0x92, 0x84, 0xf3, - 0x64, 0x88, 0xd3, 0xb2, 0x6a, 0x3b, 0xb6, 0x43, 0xbc, 0x83, 0xb0, 0xd5, 0x2d, 0x12, 0x58, 0x49, - 0xb5, 0x96, 0xfa, 0xd5, 0xf2, 0x3a, 0x4e, 0x60, 0xb7, 0x48, 0x4f, 0x85, 0xff, 0x78, 0xb7, 0x0a, - 0x7e, 0x6d, 0x87, 0xb4, 0xac, 0x9e, 0x7a, 0x4f, 0xf4, 0xab, 0xd7, 0x09, 0xec, 0xe6, 0x92, 0xed, - 0x04, 0x7e, 0xe0, 0xc5, 0x2b, 0x99, 0xdf, 0xc9, 0x42, 0xa1, 0xb4, 0x56, 0xae, 0x06, 0x56, 0xd0, - 0xf1, 0xd1, 0x1b, 0x06, 0x4c, 0x34, 0x5d, 0xab, 0x5e, 0xb6, 0x9a, 0x96, 0x53, 0x23, 0xde, 0xbc, - 0xf1, 0x90, 0xf1, 0xe8, 0xf8, 0xe3, 0x6b, 0x8b, 0xc3, 0x8c, 0xd7, 0x62, 0x69, 0xdf, 0xc7, 0xc4, - 0x77, 0x3b, 0x5e, 0x8d, 0x60, 0xb2, 0x5d, 0x9e, 0xfb, 0x7e, 0xb7, 0x78, 0xdf, 0x61, 0xb7, 0x38, - 0xb1, 0xa6, 0x71, 0xc2, 0x11, 0xbe, 0xe8, 0x2b, 0x06, 0xcc, 0xd6, 0x2c, 0xc7, 0xf2, 0x0e, 0x36, - 0x2d, 0xaf, 0x41, 0x82, 0xe7, 0x3d, 0xb7, 0xd3, 0x9e, 0xcf, 0x9c, 0x82, 0x34, 0xf7, 0x0b, 0x69, - 0x66, 0x97, 0xe3, 0xec, 0x70, 0xaf, 0x04, 0x4c, 0x2e, 0x3f, 0xb0, 0xb6, 0x9a, 0x44, 0x97, 0x2b, - 0x7b, 0x9a, 0x72, 0x55, 0xe3, 0xec, 0x70, 0xaf, 0x04, 0xe6, 0xeb, 0x59, 0x98, 0x2d, 0xad, 0x95, - 0x37, 0x3d, 0x6b, 0x7b, 0xdb, 0xae, 0x61, 0xb7, 0x13, 0xd8, 0x4e, 0x03, 0xbd, 0x1b, 0xc6, 0x6c, - 0xa7, 0xe1, 0x11, 0xdf, 0x67, 0x03, 0x59, 0x28, 0x4f, 0x0b, 0xa2, 0x63, 0xab, 0xbc, 0x18, 0x4b, - 0x38, 0x7a, 0x0a, 0xc6, 0x7d, 0xe2, 0xed, 0xd9, 0x35, 0x52, 0x71, 0xbd, 0x80, 0xf5, 0x74, 0xae, - 0x7c, 0x46, 0xa0, 0x8f, 0x57, 0x43, 0x10, 0xd6, 0xf1, 0x68, 0x35, 0xcf, 0x75, 0x03, 0x01, 0x67, - 0x1d, 0x51, 0x08, 0xab, 0xe1, 0x10, 0x84, 0x75, 0x3c, 0xf4, 0xa6, 0x01, 0x33, 0x7e, 0x60, 0xd7, - 0x76, 0x6d, 0x87, 0xf8, 0xfe, 0xb2, 0xeb, 0x6c, 0xdb, 0x8d, 0xf9, 0x1c, 0xeb, 0xc5, 0x8d, 0xe1, - 0x7a, 0xb1, 0x1a, 0xa3, 0x5a, 0x9e, 0x3b, 0xec, 0x16, 0x67, 0xe2, 0xa5, 0xb8, 0x87, 0x3b, 0x5a, - 0x81, 0x19, 0xcb, 0x71, 0xdc, 0xc0, 0x0a, 0x6c, 0xd7, 0xa9, 0x78, 0x64, 0xdb, 0xbe, 0x3d, 0x3f, - 0xc2, 0x9a, 0x33, 0x2f, 0x9a, 0x33, 0x53, 0x8a, 0xc1, 0x71, 0x4f, 0x0d, 0x73, 0x05, 0xe6, 0x4b, - 0xad, 0x2d, 0xcb, 0xf7, 0xad, 0xba, 0xeb, 0xc5, 0x46, 0xe3, 0x51, 0xc8, 0xb7, 0xac, 0x76, 0xdb, - 0x76, 0x1a, 0x74, 0x38, 0xb2, 0x8f, 0x16, 0xca, 0x13, 0x87, 0xdd, 0x62, 0x7e, 0x5d, 0x94, 0x61, - 0x05, 0x35, 0x7f, 0x9a, 0x81, 0xf1, 0x92, 0x63, 0x35, 0x0f, 0x7c, 0xdb, 0xc7, 0x1d, 0x07, 0x7d, - 0x1c, 0xf2, 0x74, 0x77, 0xa9, 0x5b, 0x81, 0x25, 0x56, 0xe4, 0x7b, 0x17, 0xf9, 0x62, 0x5f, 0xd4, - 0x17, 0x7b, 0xd8, 0x2f, 0x14, 0x7b, 0x71, 0xef, 0x7d, 0x8b, 0x57, 0xb7, 0x6e, 0x91, 0x5a, 0xb0, - 0x4e, 0x02, 0xab, 0x8c, 0x44, 0x2b, 0x20, 0x2c, 0xc3, 0x8a, 0x2a, 0x72, 0x61, 0xc4, 0x6f, 0x93, - 0x9a, 0x58, 0x61, 0xeb, 0x43, 0xce, 0xe4, 0x50, 0xf4, 0x6a, 0x9b, 0xd4, 0xca, 0x13, 0x82, 0xf5, - 0x08, 0xfd, 0x87, 0x19, 0x23, 0xb4, 0x0f, 0xa3, 0x3e, 0xdb, 0x73, 0xc4, 0xe2, 0xb9, 0x9a, 0x1e, - 0x4b, 0x46, 0xb6, 0x3c, 0x25, 0x98, 0x8e, 0xf2, 0xff, 0x58, 0xb0, 0x33, 0x7f, 0x66, 0xc0, 0x19, - 0x0d, 0xbb, 0xe4, 0x35, 0x3a, 0x2d, 0xe2, 0x04, 0xe8, 0x21, 0x18, 0x71, 0xac, 0x16, 0x11, 0x0b, - 0x45, 0x89, 0xbc, 0x61, 0xb5, 0x08, 0x66, 0x10, 0xf4, 0x30, 0xe4, 0xf6, 0xac, 0x66, 0x87, 0xb0, - 0x4e, 0x2a, 0x94, 0x27, 0x05, 0x4a, 0xee, 0x06, 0x2d, 0xc4, 0x1c, 0x86, 0x5e, 0x81, 0x02, 0xfb, - 0x71, 0xc9, 0x73, 0x5b, 0x29, 0x35, 0x4d, 0x48, 0x78, 0x43, 0x92, 0x2d, 0x4f, 0x1e, 0x76, 0x8b, - 0x05, 0xf5, 0x17, 0x87, 0x0c, 0xcd, 0xbf, 0x32, 0x60, 0x5a, 0x6b, 0xdc, 0x9a, 0xed, 0x07, 0xe8, - 0x23, 0x3d, 0x93, 0x67, 0x71, 0xb0, 0xc9, 0x43, 0x6b, 0xb3, 0xa9, 0x33, 0x23, 0x5a, 0x9a, 0x97, - 0x25, 0xda, 0xc4, 0x71, 0x20, 0x67, 0x07, 0xa4, 0xe5, 0xcf, 0x67, 0x1e, 0xca, 0x3e, 0x3a, 0xfe, - 0xf8, 0x6a, 0x6a, 0xc3, 0x18, 0xf6, 0xef, 0x2a, 0xa5, 0x8f, 0x39, 0x1b, 0xf3, 0x9b, 0x23, 0x91, - 0x16, 0xd2, 0x19, 0x85, 0x5c, 0x18, 0x6b, 0x91, 0xc0, 0xb3, 0x6b, 0x7c, 0x5d, 0x8d, 0x3f, 0xbe, - 0x32, 0x9c, 0x14, 0xeb, 0x8c, 0x58, 0xb8, 0x59, 0xf2, 0xff, 0x3e, 0x96, 0x5c, 0xd0, 0x0e, 0x8c, - 0x58, 0x5e, 0x43, 0xb6, 0xf9, 0x52, 0x3a, 0xe3, 0x1b, 0xce, 0xb9, 0x92, 0xd7, 0xf0, 0x31, 0xe3, - 0x80, 0x96, 0xa0, 0x10, 0x10, 0xaf, 0x65, 0x3b, 0x56, 0xc0, 0x77, 0xd7, 0x7c, 0x79, 0x56, 0xa0, - 0x15, 0x36, 0x25, 0x00, 0x87, 0x38, 0xa8, 0x09, 0xa3, 0x75, 0xef, 0x00, 0x77, 0x9c, 0xf9, 0x91, - 0x34, 0xba, 0x62, 0x85, 0xd1, 0x0a, 0x17, 0x13, 0xff, 0x8f, 0x05, 0x0f, 0xf4, 0x35, 0x03, 0xe6, - 0x5a, 0xc4, 0xf2, 0x3b, 0x1e, 0xa1, 0x4d, 0xc0, 0x24, 0x20, 0x0e, 0xdd, 0x0d, 0xe7, 0x73, 0x8c, - 0x39, 0x1e, 0x76, 0x1c, 0x7a, 0x29, 0x97, 0x1f, 0x14, 0xa2, 0xcc, 0x25, 0x41, 0x71, 0xa2, 0x34, - 0xe6, 0x4f, 0x47, 0x60, 0xb6, 0x67, 0x87, 0x40, 0x4f, 0x42, 0xae, 0xbd, 0x63, 0xf9, 0x72, 0xc9, - 0x5f, 0x90, 0xf3, 0xad, 0x42, 0x0b, 0xef, 0x74, 0x8b, 0x93, 0xb2, 0x0a, 0x2b, 0xc0, 0x1c, 0x99, - 0xea, 0xd4, 0x16, 0xf1, 0x7d, 0xab, 0x21, 0xf7, 0x01, 0x6d, 0x9a, 0xb0, 0x62, 0x2c, 0xe1, 0xe8, - 0xbf, 0x1a, 0x30, 0xc9, 0xa7, 0x0c, 0x26, 0x7e, 0xa7, 0x19, 0xd0, 0xbd, 0x8e, 0x76, 0xcb, 0x95, - 0x34, 0xa6, 0x27, 0x27, 0x59, 0x3e, 0x2b, 0xb8, 0x4f, 0xea, 0xa5, 0x3e, 0x8e, 0xf2, 0x45, 0x37, - 0xa1, 0xe0, 0x07, 0x96, 0x17, 0x90, 0x7a, 0x29, 0x60, 0x5a, 0x6d, 0xfc, 0xf1, 0x7f, 0x3f, 0xd8, - 0x26, 0xb0, 0x69, 0xb7, 0x08, 0xdf, 0x70, 0xaa, 0x92, 0x00, 0x0e, 0x69, 0xa1, 0x57, 0x00, 0xbc, - 0x8e, 0x53, 0xed, 0xb4, 0x5a, 0x96, 0x77, 0x20, 0x34, 0xf8, 0xe5, 0xe1, 0x9a, 0x87, 0x15, 0xbd, - 0x50, 0x67, 0x85, 0x65, 0x58, 0xe3, 0x87, 0x5e, 0x33, 0x60, 0x92, 0xcf, 0x44, 0x29, 0xc1, 0x68, - 0xca, 0x12, 0xcc, 0xd2, 0xae, 0x5d, 0xd1, 0x59, 0xe0, 0x28, 0x47, 0xf3, 0x2f, 0xa2, 0xfa, 0xa4, - 0x1a, 0xd0, 0xd3, 0x75, 0xe3, 0x00, 0x7d, 0x18, 0xee, 0xf7, 0x3b, 0xb5, 0x1a, 0xf1, 0xfd, 0xed, - 0x4e, 0x13, 0x77, 0x9c, 0xcb, 0xb6, 0x1f, 0xb8, 0xde, 0xc1, 0x9a, 0xdd, 0xb2, 0x03, 0x36, 0xe3, - 0x72, 0xe5, 0xf3, 0x87, 0xdd, 0xe2, 0xfd, 0xd5, 0x7e, 0x48, 0xb8, 0x7f, 0x7d, 0x64, 0xc1, 0x03, - 0x1d, 0xa7, 0x3f, 0x79, 0x7e, 0x7a, 0x2b, 0x1e, 0x76, 0x8b, 0x0f, 0x5c, 0xef, 0x8f, 0x86, 0x8f, - 0xa2, 0x61, 0xfe, 0x9d, 0x01, 0x33, 0xb2, 0x5d, 0x9b, 0xa4, 0xd5, 0x6e, 0xd2, 0xdd, 0xe5, 0xf4, - 0x0f, 0x22, 0x41, 0xe4, 0x20, 0x82, 0xd3, 0x51, 0x27, 0x52, 0xfe, 0x7e, 0xa7, 0x11, 0xf3, 0x6f, - 0x0d, 0x98, 0x8b, 0x23, 0xdf, 0x03, 0xe5, 0xe9, 0x47, 0x95, 0xe7, 0x46, 0xba, 0xad, 0xed, 0xa3, - 0x41, 0xdf, 0x18, 0xe9, 0x6d, 0xeb, 0xbf, 0x76, 0x35, 0x1a, 0x6a, 0xc5, 0xec, 0x6f, 0x52, 0x2b, - 0x8e, 0xbc, 0xad, 0xb4, 0xe2, 0xef, 0x8c, 0xc0, 0x44, 0xc9, 0x09, 0xec, 0xd2, 0xf6, 0xb6, 0xed, - 0xd8, 0xc1, 0x01, 0xfa, 0x7c, 0x06, 0x96, 0xda, 0x1e, 0xd9, 0x26, 0x9e, 0x47, 0xea, 0x2b, 0x1d, - 0xcf, 0x76, 0x1a, 0xd5, 0xda, 0x0e, 0xa9, 0x77, 0x9a, 0xb6, 0xd3, 0x58, 0x6d, 0x38, 0xae, 0x2a, - 0xbe, 0x78, 0x9b, 0xd4, 0x3a, 0xac, 0x49, 0x7c, 0x51, 0xb4, 0x86, 0x6b, 0x52, 0xe5, 0x78, 0x4c, - 0xcb, 0x4f, 0x1c, 0x76, 0x8b, 0x4b, 0xc7, 0xac, 0x84, 0x8f, 0xdb, 0x34, 0xf4, 0xb9, 0x0c, 0x2c, - 0x7a, 0xe4, 0x13, 0x1d, 0x7b, 0xf0, 0xde, 0xe0, 0xbb, 0x56, 0x73, 0x48, 0xf5, 0x73, 0x2c, 0x9e, - 0xe5, 0xc7, 0x0f, 0xbb, 0xc5, 0x63, 0xd6, 0xc1, 0xc7, 0x6c, 0x97, 0x59, 0x81, 0xf1, 0x52, 0xdb, - 0xf6, 0xed, 0xdb, 0xf4, 0x2e, 0x4b, 0x06, 0xb8, 0x2b, 0x15, 0x21, 0xe7, 0x75, 0x9a, 0x84, 0xaf, - 0xed, 0x42, 0xb9, 0x40, 0x77, 0x21, 0x4c, 0x0b, 0x30, 0x2f, 0x37, 0x3f, 0x43, 0x77, 0x5c, 0x46, - 0x32, 0x76, 0x4b, 0xbe, 0x05, 0x39, 0x8f, 0x32, 0x11, 0x33, 0x6b, 0xd8, 0x0b, 0x45, 0x28, 0xb5, - 0x10, 0x82, 0xfe, 0xc4, 0x9c, 0x85, 0xf9, 0xed, 0x0c, 0x9c, 0x2d, 0xb5, 0xdb, 0xeb, 0xc4, 0xdf, - 0x89, 0x49, 0xf1, 0x45, 0x03, 0xa6, 0xf6, 0x6c, 0x2f, 0xe8, 0x58, 0x4d, 0x69, 0xdb, 0xe0, 0xf2, - 0x54, 0x87, 0x95, 0x87, 0x71, 0xbb, 0x11, 0x21, 0x5d, 0x46, 0x87, 0xdd, 0xe2, 0x54, 0xb4, 0x0c, - 0xc7, 0xd8, 0xa3, 0xff, 0x65, 0xc0, 0x8c, 0x28, 0xda, 0x70, 0xeb, 0x44, 0x37, 0x88, 0x5d, 0x4f, - 0x53, 0x26, 0x45, 0x9c, 0x5b, 0x4e, 0xe2, 0xa5, 0xb8, 0x47, 0x08, 0xf3, 0x1f, 0x32, 0x70, 0xae, - 0x0f, 0x0d, 0xf4, 0xff, 0x0d, 0x98, 0xe3, 0x56, 0x34, 0x0d, 0x84, 0xc9, 0xb6, 0xe8, 0xcd, 0x0f, - 0xa5, 0x2d, 0x39, 0xa6, 0x4b, 0x9c, 0x38, 0x35, 0x52, 0x9e, 0xa7, 0xbb, 0xe1, 0x72, 0x02, 0x6b, - 0x9c, 0x28, 0x10, 0x93, 0x94, 0xdb, 0xd5, 0x62, 0x92, 0x66, 0xee, 0x89, 0xa4, 0xd5, 0x04, 0xd6, - 0x38, 0x51, 0x20, 0xf3, 0x3f, 0xc3, 0x03, 0x47, 0x90, 0xbb, 0xfb, 0xe2, 0x34, 0x5f, 0x52, 0xb3, - 0x3e, 0x3a, 0xe7, 0x06, 0x58, 0xd7, 0x26, 0x8c, 0xb2, 0xa5, 0x23, 0x17, 0x36, 0x50, 0xf5, 0xc7, - 0xd6, 0x94, 0x8f, 0x05, 0xc4, 0xfc, 0xb6, 0x01, 0xf9, 0x63, 0x98, 0x55, 0x8a, 0x51, 0xb3, 0x4a, - 0xa1, 0xc7, 0xa4, 0x12, 0xf4, 0x9a, 0x54, 0x9e, 0x1f, 0x6e, 0x34, 0x06, 0x31, 0xa5, 0xfc, 0xd2, - 0x80, 0xd9, 0x1e, 0xd3, 0x0b, 0xda, 0x81, 0xb9, 0xb6, 0x5b, 0x97, 0xc7, 0xa6, 0xcb, 0x96, 0xbf, - 0xc3, 0x60, 0xa2, 0x79, 0x4f, 0xd2, 0x91, 0xac, 0x24, 0xc0, 0xef, 0x74, 0x8b, 0xf3, 0x8a, 0x48, - 0x0c, 0x01, 0x27, 0x52, 0x44, 0x6d, 0xc8, 0x6f, 0xdb, 0xa4, 0x59, 0x0f, 0xa7, 0xe0, 0x90, 0x07, - 0xa4, 0x4b, 0x82, 0x1a, 0xb7, 0x3a, 0xca, 0x7f, 0x58, 0x71, 0x31, 0xaf, 0xc1, 0x54, 0xd4, 0x06, - 0x3d, 0xc0, 0xe0, 0x9d, 0x87, 0xac, 0xe5, 0x39, 0x62, 0xe8, 0xc6, 0x05, 0x42, 0xb6, 0x84, 0x37, - 0x30, 0x2d, 0x37, 0x7f, 0x3d, 0x02, 0xd3, 0xe5, 0x66, 0x87, 0x3c, 0xef, 0x11, 0x22, 0xaf, 0xdd, - 0x25, 0x98, 0x6e, 0x7b, 0x64, 0xcf, 0x26, 0xfb, 0x55, 0xd2, 0x24, 0xb5, 0xc0, 0xf5, 0x04, 0xfd, - 0x73, 0xa2, 0xfa, 0x74, 0x25, 0x0a, 0xc6, 0x71, 0x7c, 0xf4, 0x1c, 0x4c, 0x59, 0xb5, 0xc0, 0xde, - 0x23, 0x8a, 0x02, 0x17, 0xe0, 0x1d, 0x82, 0xc2, 0x54, 0x29, 0x02, 0xc5, 0x31, 0x6c, 0xf4, 0x11, - 0x98, 0xf7, 0x6b, 0x56, 0x93, 0x5c, 0x6f, 0x0b, 0x56, 0xcb, 0x3b, 0xa4, 0xb6, 0x5b, 0x71, 0x6d, - 0x27, 0x10, 0x46, 0x96, 0x87, 0x04, 0xa5, 0xf9, 0x6a, 0x1f, 0x3c, 0xdc, 0x97, 0x02, 0xfa, 0x23, - 0x03, 0xce, 0xb7, 0x3d, 0x52, 0xf1, 0xdc, 0x96, 0x4b, 0xb5, 0x67, 0x8f, 0xe5, 0x41, 0xdc, 0xc0, - 0x6f, 0x0c, 0x79, 0x4c, 0xe0, 0x25, 0xbd, 0x96, 0xcf, 0x77, 0x1e, 0x76, 0x8b, 0xe7, 0x2b, 0x47, - 0x09, 0x80, 0x8f, 0x96, 0x0f, 0xfd, 0x89, 0x01, 0x17, 0xda, 0xae, 0x1f, 0x1c, 0xd1, 0x84, 0xdc, - 0xa9, 0x36, 0xc1, 0x3c, 0xec, 0x16, 0x2f, 0x54, 0x8e, 0x94, 0x00, 0xdf, 0x45, 0x42, 0xf3, 0x70, - 0x1c, 0x66, 0xb5, 0xb9, 0x27, 0xae, 0xe5, 0xcf, 0xc0, 0xa4, 0x9c, 0x0c, 0xa1, 0x5a, 0x2f, 0x84, - 0x66, 0x94, 0x92, 0x0e, 0xc4, 0x51, 0x5c, 0x3a, 0xef, 0xd4, 0x54, 0xe4, 0xb5, 0x63, 0xf3, 0xae, - 0x12, 0x81, 0xe2, 0x18, 0x36, 0x5a, 0x85, 0x33, 0xa2, 0x04, 0x93, 0x76, 0xd3, 0xae, 0x59, 0xcb, - 0x6e, 0x47, 0x4c, 0xb9, 0x5c, 0xf9, 0xdc, 0x61, 0xb7, 0x78, 0xa6, 0xd2, 0x0b, 0xc6, 0x49, 0x75, - 0xd0, 0x1a, 0xcc, 0x59, 0x9d, 0xc0, 0x55, 0xed, 0xbf, 0xe8, 0x50, 0x4d, 0x51, 0x67, 0x53, 0x2b, - 0xcf, 0x55, 0x4a, 0x29, 0x01, 0x8e, 0x13, 0x6b, 0xa1, 0x4a, 0x8c, 0x5a, 0x95, 0xd4, 0x5c, 0xa7, - 0xce, 0x47, 0x39, 0x17, 0x5e, 0x2e, 0x4a, 0x09, 0x38, 0x38, 0xb1, 0x26, 0x6a, 0xc2, 0x54, 0xcb, - 0xba, 0x7d, 0xdd, 0xb1, 0xf6, 0x2c, 0xbb, 0x49, 0x99, 0x08, 0xd3, 0x4c, 0x7f, 0x7b, 0x41, 0x27, - 0xb0, 0x9b, 0x8b, 0xfc, 0x95, 0x72, 0x71, 0xd5, 0x09, 0xae, 0x7a, 0xd5, 0x80, 0x1e, 0x42, 0xf9, - 0xe1, 0x68, 0x3d, 0x42, 0x0b, 0xc7, 0x68, 0xa3, 0xab, 0x70, 0x96, 0x2d, 0xc7, 0x15, 0x77, 0xdf, - 0x59, 0x21, 0x4d, 0xeb, 0x40, 0x36, 0x60, 0x8c, 0x35, 0xe0, 0xfe, 0xc3, 0x6e, 0xf1, 0x6c, 0x35, - 0x09, 0x01, 0x27, 0xd7, 0x43, 0x16, 0x3c, 0x10, 0x05, 0x60, 0xb2, 0x67, 0xfb, 0xb6, 0xeb, 0x70, - 0x03, 0x4b, 0x3e, 0x34, 0xb0, 0x54, 0xfb, 0xa3, 0xe1, 0xa3, 0x68, 0xa0, 0xff, 0x63, 0xc0, 0x5c, - 0xd2, 0x32, 0x9c, 0x2f, 0xa4, 0xf1, 0x06, 0x13, 0x5b, 0x5a, 0x7c, 0x46, 0x24, 0x6e, 0x0a, 0x89, - 0x42, 0xa0, 0x57, 0x0d, 0x98, 0xb0, 0xb4, 0xcb, 0xe1, 0x3c, 0x30, 0xa9, 0xae, 0x0c, 0x6b, 0xa2, - 0x08, 0x29, 0x96, 0x67, 0x0e, 0xbb, 0xc5, 0xc8, 0x05, 0x14, 0x47, 0x38, 0xa2, 0xff, 0x6b, 0xc0, - 0xd9, 0xc4, 0x35, 0x3e, 0x3f, 0x7e, 0x1a, 0x3d, 0xc4, 0x26, 0x49, 0xf2, 0x9e, 0x93, 0x2c, 0x06, - 0x7a, 0xd3, 0x50, 0xaa, 0x6c, 0x5d, 0x1a, 0x89, 0x26, 0x98, 0x68, 0xd7, 0x86, 0xbc, 0x0f, 0x87, - 0x07, 0x02, 0x49, 0xb8, 0x7c, 0x46, 0xd3, 0x8c, 0xb2, 0x10, 0xc7, 0xd9, 0xa3, 0x2f, 0x18, 0x52, - 0x35, 0x2a, 0x89, 0x26, 0x4f, 0x4b, 0x22, 0x14, 0x6a, 0x5a, 0x25, 0x50, 0x8c, 0x39, 0xfa, 0x28, - 0x2c, 0x58, 0x5b, 0xae, 0x17, 0x24, 0x2e, 0xbe, 0xf9, 0x29, 0xb6, 0x8c, 0x2e, 0x1c, 0x76, 0x8b, - 0x0b, 0xa5, 0xbe, 0x58, 0xf8, 0x08, 0x0a, 0xe6, 0x37, 0x72, 0x30, 0xc1, 0x0f, 0xf9, 0x42, 0x75, - 0x7d, 0xcb, 0x80, 0x07, 0x6b, 0x1d, 0xcf, 0x23, 0x4e, 0x50, 0x0d, 0x48, 0xbb, 0x57, 0x71, 0x19, - 0xa7, 0xaa, 0xb8, 0x1e, 0x3a, 0xec, 0x16, 0x1f, 0x5c, 0x3e, 0x82, 0x3f, 0x3e, 0x52, 0x3a, 0xf4, - 0xe7, 0x06, 0x98, 0x02, 0xa1, 0x6c, 0xd5, 0x76, 0x1b, 0x9e, 0xdb, 0x71, 0xea, 0xbd, 0x8d, 0xc8, - 0x9c, 0x6a, 0x23, 0x1e, 0x39, 0xec, 0x16, 0xcd, 0xe5, 0xbb, 0x4a, 0x81, 0x07, 0x90, 0x14, 0x3d, - 0x0f, 0xb3, 0x02, 0xeb, 0xe2, 0xed, 0x36, 0xf1, 0x6c, 0x7a, 0x9c, 0x16, 0x6e, 0x02, 0xa1, 0xe7, - 0x45, 0x1c, 0x01, 0xf7, 0xd6, 0x41, 0x3e, 0x8c, 0xed, 0x13, 0xbb, 0xb1, 0x13, 0xc8, 0xe3, 0xd3, - 0x90, 0xee, 0x16, 0xe2, 0xc2, 0x7f, 0x93, 0xd3, 0x2c, 0x8f, 0x1f, 0x76, 0x8b, 0x63, 0xe2, 0x0f, - 0x96, 0x9c, 0xd0, 0x06, 0x4c, 0xf1, 0x2b, 0x58, 0xc5, 0x76, 0x1a, 0x15, 0xd7, 0xe1, 0x4e, 0x0a, - 0x85, 0xf2, 0x23, 0x52, 0xe1, 0x57, 0x23, 0xd0, 0x3b, 0xdd, 0xe2, 0x84, 0xfc, 0xbd, 0x79, 0xd0, - 0x26, 0x38, 0x56, 0xdb, 0xfc, 0xde, 0x28, 0x80, 0x9c, 0xae, 0xa4, 0x8d, 0xfe, 0x03, 0x14, 0x7c, - 0x12, 0x70, 0xae, 0xe2, 0x4d, 0x80, 0x3f, 0xb5, 0xc8, 0x42, 0x1c, 0xc2, 0xd1, 0x2e, 0xe4, 0xda, - 0x56, 0xc7, 0x27, 0x62, 0xf0, 0xaf, 0xa4, 0x32, 0xf8, 0x15, 0x4a, 0x91, 0xdf, 0xb9, 0xd8, 0x4f, - 0xcc, 0x79, 0xa0, 0xcf, 0x1a, 0x00, 0x24, 0x3a, 0x60, 0x43, 0xdb, 0x3e, 0x04, 0xcb, 0x70, 0x4c, - 0x69, 0x1f, 0x94, 0xa7, 0x0e, 0xbb, 0x45, 0xd0, 0x86, 0x5e, 0x63, 0x8b, 0xf6, 0x21, 0x6f, 0xc9, - 0x3d, 0x7f, 0xe4, 0x34, 0xf6, 0x7c, 0x76, 0x15, 0x52, 0x93, 0x56, 0x31, 0x43, 0x9f, 0x33, 0x60, - 0xca, 0x27, 0x81, 0x18, 0x2a, 0xba, 0xf3, 0x88, 0x03, 0xef, 0x90, 0x93, 0xae, 0x1a, 0xa1, 0xc9, - 0x77, 0xd0, 0x68, 0x19, 0x8e, 0xf1, 0x95, 0xa2, 0x5c, 0x26, 0x56, 0x9d, 0x78, 0xec, 0xa6, 0x2d, - 0x4e, 0x52, 0xc3, 0x8b, 0xa2, 0xd1, 0x54, 0xa2, 0x68, 0x65, 0x38, 0xc6, 0x57, 0x8a, 0xb2, 0x6e, - 0x7b, 0x9e, 0x2b, 0x44, 0xc9, 0xa7, 0x24, 0x8a, 0x46, 0x53, 0x89, 0xa2, 0x95, 0xe1, 0x18, 0x5f, - 0xf3, 0xab, 0x93, 0x30, 0x25, 0x17, 0x52, 0x78, 0xb2, 0xe7, 0x86, 0x9d, 0x3e, 0x27, 0xfb, 0x65, - 0x1d, 0x88, 0xa3, 0xb8, 0xb4, 0x32, 0x5f, 0xaa, 0xd1, 0x83, 0xbd, 0xaa, 0x5c, 0xd5, 0x81, 0x38, - 0x8a, 0x8b, 0x5a, 0x90, 0xf3, 0x03, 0xd2, 0x96, 0xcf, 0xbb, 0x43, 0xbe, 0x3e, 0x86, 0xfb, 0x43, - 0xf8, 0x80, 0x43, 0xff, 0xf9, 0x98, 0x73, 0x61, 0xb6, 0xc9, 0x20, 0x62, 0xae, 0x14, 0x8b, 0x23, - 0x9d, 0xf5, 0x19, 0xb5, 0x84, 0xf2, 0xd1, 0x88, 0x96, 0xe1, 0x18, 0xfb, 0x84, 0xc3, 0x7e, 0xee, - 0x14, 0x0f, 0xfb, 0x2f, 0x42, 0xbe, 0x65, 0xdd, 0xae, 0x76, 0xbc, 0xc6, 0xc9, 0x2f, 0x15, 0xc2, - 0xf3, 0x8a, 0x53, 0xc1, 0x8a, 0x1e, 0x7a, 0xcd, 0xd0, 0xb6, 0x9c, 0x31, 0x46, 0xfc, 0x66, 0xba, - 0x5b, 0x8e, 0xd2, 0x95, 0x7d, 0x37, 0x9f, 0x9e, 0xa3, 0x77, 0xfe, 0x9e, 0x1f, 0xbd, 0xe9, 0x31, - 0x92, 0x2f, 0x10, 0x75, 0x8c, 0x2c, 0x9c, 0xea, 0x31, 0x72, 0x39, 0xc2, 0x0c, 0xc7, 0x98, 0x33, - 0x79, 0xf8, 0x9a, 0x53, 0xf2, 0xc0, 0xa9, 0xca, 0x53, 0x8d, 0x30, 0xc3, 0x31, 0xe6, 0xfd, 0xef, - 0x9b, 0xe3, 0xa7, 0x73, 0xdf, 0x9c, 0x48, 0xe1, 0xbe, 0x79, 0xf4, 0x51, 0x7c, 0x72, 0xd8, 0xa3, - 0x38, 0xba, 0x02, 0xa8, 0x7e, 0xe0, 0x58, 0x2d, 0xbb, 0x26, 0x36, 0x4b, 0xa6, 0x36, 0xa7, 0x98, - 0x3d, 0x62, 0x41, 0x6c, 0x64, 0x68, 0xa5, 0x07, 0x03, 0x27, 0xd4, 0x42, 0x01, 0xe4, 0xdb, 0xf2, - 0xc4, 0x35, 0x9d, 0xc6, 0xec, 0x97, 0x27, 0x30, 0xee, 0x01, 0x40, 0x17, 0x9e, 0x2c, 0xc1, 0x8a, - 0x13, 0x5a, 0x83, 0xb9, 0x96, 0xed, 0x54, 0xdc, 0xba, 0x5f, 0x21, 0x9e, 0xb0, 0xb6, 0x54, 0x49, - 0x30, 0x3f, 0xc3, 0xfa, 0x86, 0xdd, 0xa0, 0xd7, 0x13, 0xe0, 0x38, 0xb1, 0x96, 0xf9, 0x4f, 0x06, - 0xcc, 0x2c, 0x37, 0xdd, 0x4e, 0xfd, 0xa6, 0x15, 0xd4, 0x76, 0xf8, 0xe3, 0x37, 0x7a, 0x0e, 0xf2, - 0xb6, 0x13, 0x10, 0x6f, 0xcf, 0x6a, 0x0a, 0xfd, 0x64, 0x4a, 0xff, 0x80, 0x55, 0x51, 0x7e, 0xa7, - 0x5b, 0x9c, 0x5a, 0xe9, 0x78, 0xcc, 0xab, 0x94, 0xef, 0x56, 0x58, 0xd5, 0x41, 0x5f, 0x35, 0x60, - 0x96, 0x3f, 0x9f, 0xaf, 0x58, 0x81, 0x75, 0xad, 0x43, 0x3c, 0x9b, 0xc8, 0x07, 0xf4, 0x21, 0x37, - 0xaa, 0xb8, 0xac, 0x92, 0xc1, 0x41, 0x78, 0x50, 0x5f, 0x8f, 0x73, 0xc6, 0xbd, 0xc2, 0x98, 0x5f, - 0xca, 0xc2, 0xfd, 0x7d, 0x69, 0xa1, 0x05, 0xc8, 0xd8, 0x75, 0xd1, 0x74, 0x10, 0x74, 0x33, 0xab, - 0x75, 0x9c, 0xb1, 0xeb, 0x68, 0x91, 0x9d, 0x39, 0x3d, 0xe2, 0xfb, 0xf2, 0x2d, 0xb5, 0xa0, 0x8e, - 0x87, 0xa2, 0x14, 0x6b, 0x18, 0xa8, 0x08, 0xb9, 0xa6, 0xb5, 0x45, 0x9a, 0xe2, 0x3e, 0xc1, 0x4e, - 0xb1, 0x6b, 0xb4, 0x00, 0xf3, 0x72, 0xf4, 0x19, 0x03, 0x80, 0x0b, 0x48, 0x6f, 0x23, 0x42, 0x4b, - 0xe2, 0x74, 0xbb, 0x89, 0x52, 0xe6, 0x52, 0x86, 0xff, 0xb1, 0xc6, 0x15, 0x6d, 0xc2, 0x28, 0x3d, - 0xd0, 0xba, 0xf5, 0x13, 0x2b, 0x45, 0xf6, 0xc8, 0x52, 0x61, 0x34, 0xb0, 0xa0, 0x45, 0xfb, 0xca, - 0x23, 0x41, 0xc7, 0x73, 0x68, 0xd7, 0x32, 0x35, 0x98, 0xe7, 0x52, 0x60, 0x55, 0x8a, 0x35, 0x0c, - 0xf3, 0x0f, 0x33, 0x30, 0x97, 0x24, 0x3a, 0xd5, 0x36, 0xa3, 0x5c, 0x5a, 0x71, 0x35, 0xfe, 0x60, - 0xfa, 0xfd, 0x23, 0x3c, 0x41, 0x94, 0xbf, 0x84, 0xf0, 0x55, 0x13, 0x7c, 0xd1, 0x07, 0x55, 0x0f, - 0x65, 0x4e, 0xd8, 0x43, 0x8a, 0x72, 0xac, 0x97, 0x1e, 0x82, 0x11, 0x9f, 0x8e, 0x7c, 0x36, 0xfa, - 0x80, 0xc1, 0xc6, 0x88, 0x41, 0x28, 0x46, 0xc7, 0xb1, 0x03, 0xe1, 0xea, 0xad, 0x30, 0xae, 0x3b, - 0x76, 0x80, 0x19, 0xc4, 0xfc, 0x4a, 0x06, 0x16, 0xfa, 0x37, 0x0a, 0x7d, 0xc5, 0x00, 0xa8, 0xd3, - 0xeb, 0x0a, 0x9d, 0x92, 0xd2, 0x73, 0xc6, 0x3a, 0xad, 0x3e, 0x5c, 0x91, 0x9c, 0x42, 0x37, 0x2a, - 0x55, 0xe4, 0x63, 0x4d, 0x10, 0xf4, 0xb8, 0x9c, 0xfa, 0x1b, 0x56, 0x4b, 0x1e, 0x67, 0x55, 0x9d, - 0x75, 0x05, 0xc1, 0x1a, 0x16, 0xbd, 0x8f, 0x3a, 0x56, 0x8b, 0xf8, 0x6d, 0x4b, 0xf9, 0xf2, 0xb3, - 0xfb, 0xe8, 0x86, 0x2c, 0xc4, 0x21, 0xdc, 0x6c, 0xc2, 0xc3, 0x03, 0xc8, 0x99, 0x92, 0x5f, 0xb5, - 0xf9, 0x8f, 0x06, 0x9c, 0x5b, 0x6e, 0x76, 0xfc, 0x80, 0x78, 0xff, 0x66, 0xbc, 0xd2, 0xfe, 0xd9, - 0x80, 0x07, 0xfa, 0xb4, 0xf9, 0x1e, 0x38, 0xa7, 0xbd, 0x1c, 0x75, 0x4e, 0xbb, 0x3e, 0xec, 0x94, - 0x4e, 0x6c, 0x47, 0x1f, 0x1f, 0xb5, 0x00, 0x26, 0xe9, 0xae, 0x55, 0x77, 0x1b, 0x29, 0xe9, 0xcd, - 0x87, 0x21, 0xf7, 0x09, 0xaa, 0x7f, 0xe2, 0x73, 0x8c, 0x29, 0x25, 0xcc, 0x61, 0xe6, 0xb3, 0x20, - 0x3c, 0xb9, 0x62, 0x8b, 0xc7, 0x18, 0x64, 0xf1, 0x98, 0x7f, 0x99, 0x01, 0xcd, 0x8e, 0x71, 0x0f, - 0x26, 0xa5, 0x13, 0x99, 0x94, 0x43, 0xde, 0xc1, 0x35, 0xab, 0x4c, 0xbf, 0x90, 0x8d, 0xbd, 0x58, - 0xc8, 0xc6, 0x46, 0x6a, 0x1c, 0x8f, 0x8e, 0xd8, 0xf8, 0xb1, 0x01, 0x0f, 0x84, 0xc8, 0xbd, 0x26, - 0xc6, 0xbb, 0xef, 0x30, 0x4f, 0xc1, 0xb8, 0x15, 0x56, 0x13, 0x73, 0x40, 0x45, 0x29, 0x69, 0x14, - 0xb1, 0x8e, 0x17, 0x3a, 0x88, 0x67, 0x4f, 0xe8, 0x20, 0x3e, 0x72, 0xb4, 0x83, 0xb8, 0xf9, 0xab, - 0x0c, 0x9c, 0xef, 0x6d, 0x99, 0x5c, 0x1b, 0x83, 0xbd, 0xc0, 0x3f, 0x0d, 0x13, 0x81, 0xa8, 0xa0, - 0xed, 0xf4, 0x2a, 0xc6, 0x6e, 0x53, 0x83, 0xe1, 0x08, 0x26, 0xad, 0x59, 0xe3, 0xab, 0xb2, 0x5a, - 0x73, 0xdb, 0x32, 0xbc, 0x40, 0xd5, 0x5c, 0xd6, 0x60, 0x38, 0x82, 0xa9, 0x1c, 0x37, 0x47, 0x4e, - 0xdd, 0x71, 0xb3, 0x0a, 0x67, 0xa5, 0xab, 0xda, 0x25, 0xd7, 0x5b, 0x76, 0x5b, 0xed, 0x26, 0x11, - 0x01, 0x06, 0x54, 0xd8, 0xf3, 0xa2, 0xca, 0x59, 0x9c, 0x84, 0x84, 0x93, 0xeb, 0x9a, 0x3f, 0xce, - 0xc2, 0x99, 0xb0, 0xdb, 0x97, 0x5d, 0xa7, 0x6e, 0x33, 0x87, 0xbf, 0x67, 0x60, 0x24, 0x38, 0x68, - 0xcb, 0xce, 0xfe, 0x77, 0x52, 0x9c, 0xcd, 0x83, 0x36, 0x1d, 0xed, 0x73, 0x09, 0x55, 0x98, 0x91, - 0x97, 0x55, 0x42, 0x6b, 0x6a, 0x75, 0xf0, 0x11, 0x78, 0x32, 0x3a, 0x9b, 0xef, 0x74, 0x8b, 0x09, - 0x21, 0xa6, 0x8b, 0x8a, 0x52, 0x74, 0xce, 0xa3, 0x5b, 0x30, 0xd5, 0xb4, 0xfc, 0xe0, 0x7a, 0xbb, - 0x6e, 0x05, 0x64, 0xd3, 0x6e, 0x11, 0xb1, 0xe6, 0x8e, 0xe3, 0xb5, 0xaf, 0x5e, 0xa5, 0xd7, 0x22, - 0x94, 0x70, 0x8c, 0x32, 0xda, 0x03, 0x44, 0x4b, 0x36, 0x3d, 0xcb, 0xf1, 0x79, 0xab, 0x28, 0xbf, - 0xe3, 0x47, 0x09, 0xa8, 0x4b, 0xde, 0x5a, 0x0f, 0x35, 0x9c, 0xc0, 0x01, 0x3d, 0x02, 0xa3, 0x1e, - 0xb1, 0x7c, 0x31, 0x98, 0x85, 0x70, 0xfd, 0x63, 0x56, 0x8a, 0x05, 0x54, 0x5f, 0x50, 0xa3, 0x77, - 0x59, 0x50, 0x3f, 0x37, 0x60, 0x2a, 0x1c, 0xa6, 0x7b, 0xa0, 0x24, 0x5b, 0x51, 0x25, 0x79, 0x39, - 0xad, 0x2d, 0xb1, 0x8f, 0x5e, 0xfc, 0xd3, 0x51, 0xbd, 0x7d, 0xcc, 0x6b, 0xfb, 0x93, 0x50, 0x90, - 0xab, 0x5a, 0x9e, 0x3e, 0x87, 0xbc, 0x2b, 0x47, 0xce, 0x25, 0x5a, 0xb4, 0x91, 0x60, 0x82, 0x43, - 0x7e, 0x54, 0x2d, 0xd7, 0x85, 0xca, 0x15, 0xd3, 0x5e, 0xa9, 0x65, 0xa9, 0x8a, 0x93, 0xd4, 0xb2, - 0xac, 0x83, 0xae, 0xc3, 0xb9, 0xb6, 0xe7, 0xb2, 0x08, 0xd4, 0x15, 0x62, 0xd5, 0x9b, 0xb6, 0x43, - 0xa4, 0x41, 0x82, 0x3b, 0x45, 0x3c, 0x70, 0xd8, 0x2d, 0x9e, 0xab, 0x24, 0xa3, 0xe0, 0x7e, 0x75, - 0xa3, 0x51, 0x53, 0x23, 0x03, 0x44, 0x4d, 0xfd, 0x37, 0x65, 0xf6, 0x23, 0xbe, 0x88, 0x5d, 0xfa, - 0x70, 0x5a, 0x43, 0x99, 0xb0, 0xad, 0x87, 0x53, 0xaa, 0x24, 0x98, 0x62, 0xc5, 0xbe, 0xbf, 0x6d, - 0x69, 0xf4, 0x84, 0xb6, 0xa5, 0xd0, 0xf9, 0x7d, 0xec, 0x37, 0xe9, 0xfc, 0x9e, 0x7f, 0x5b, 0x39, - 0xbf, 0xbf, 0x9e, 0x83, 0x99, 0xf8, 0x09, 0xe4, 0xf4, 0x23, 0xc2, 0xfe, 0xa7, 0x01, 0x33, 0x72, - 0xf5, 0x70, 0x9e, 0x44, 0xbe, 0x1a, 0xac, 0xa5, 0xb4, 0x68, 0xf9, 0x59, 0x4a, 0xc5, 0x2c, 0x6f, - 0xc6, 0xb8, 0xe1, 0x1e, 0xfe, 0xe8, 0x25, 0x18, 0x57, 0xc6, 0xf5, 0x13, 0x85, 0x87, 0x4d, 0xb3, - 0x53, 0x54, 0x48, 0x02, 0xeb, 0xf4, 0xd0, 0xeb, 0x06, 0x40, 0x4d, 0xaa, 0x39, 0xb9, 0xba, 0xae, - 0xa5, 0xb5, 0xba, 0x94, 0x02, 0x0d, 0x0f, 0xcb, 0xaa, 0xc8, 0xc7, 0x1a, 0x63, 0xf4, 0x25, 0x66, - 0x56, 0x57, 0xa7, 0x3b, 0xba, 0x9e, 0xb2, 0xc3, 0x3b, 0xf6, 0x1e, 0x71, 0x30, 0x0d, 0x8f, 0x52, - 0x1a, 0xc8, 0xc7, 0x11, 0x21, 0xcc, 0x67, 0x40, 0xb9, 0x62, 0xd2, 0x6d, 0x8b, 0x39, 0x63, 0x56, - 0xac, 0x60, 0x47, 0x4c, 0x41, 0xb5, 0x6d, 0x5d, 0x92, 0x00, 0x1c, 0xe2, 0x98, 0x1f, 0x87, 0xa9, - 0xe7, 0x3d, 0xab, 0xbd, 0x63, 0x33, 0xf3, 0x35, 0xbd, 0x27, 0xbd, 0x1b, 0xc6, 0xac, 0x7a, 0x3d, - 0x29, 0xe2, 0xbf, 0xc4, 0x8b, 0xb1, 0x84, 0x0f, 0x76, 0x25, 0xfa, 0x9e, 0x01, 0x28, 0x7c, 0x02, - 0xb4, 0x9d, 0xc6, 0x3a, 0xbd, 0xed, 0xd3, 0xfb, 0xd1, 0x0e, 0x2b, 0x4d, 0xba, 0x1f, 0x5d, 0x56, - 0x10, 0xac, 0x61, 0xa1, 0x57, 0x60, 0x9c, 0xff, 0xbb, 0xa1, 0x2e, 0xfb, 0x43, 0xbb, 0xf7, 0x73, - 0x85, 0xc2, 0x64, 0xe2, 0xb3, 0xf0, 0x72, 0xc8, 0x01, 0xeb, 0xec, 0x68, 0x57, 0xad, 0x3a, 0xdb, - 0xcd, 0xce, 0xed, 0xfa, 0x56, 0xd8, 0x55, 0x6d, 0xcf, 0xdd, 0xb6, 0x9b, 0x24, 0xde, 0x55, 0x15, - 0x5e, 0x8c, 0x25, 0x7c, 0xb0, 0xae, 0xfa, 0x8e, 0x01, 0x73, 0xab, 0x7e, 0x60, 0xbb, 0x2b, 0xc4, - 0x0f, 0xa8, 0x5a, 0xa1, 0x9b, 0x4f, 0xa7, 0x39, 0x88, 0x57, 0xf5, 0x0a, 0xcc, 0x88, 0xe7, 0xc8, - 0xce, 0x96, 0x4f, 0x02, 0xed, 0x1c, 0xaf, 0xd6, 0xf1, 0x72, 0x0c, 0x8e, 0x7b, 0x6a, 0x50, 0x2a, - 0xe2, 0x5d, 0x32, 0xa4, 0x92, 0x8d, 0x52, 0xa9, 0xc6, 0xe0, 0xb8, 0xa7, 0x86, 0xf9, 0xc3, 0x2c, - 0x9c, 0x61, 0xcd, 0x88, 0x45, 0x44, 0x7c, 0xa1, 0x5f, 0x44, 0xc4, 0x90, 0x4b, 0x99, 0xf1, 0x3a, - 0x41, 0x3c, 0xc4, 0xff, 0x30, 0x60, 0xba, 0x1e, 0xed, 0xe9, 0x74, 0xcc, 0x33, 0x49, 0x63, 0xc8, - 0xbd, 0xaf, 0x62, 0x85, 0x38, 0xce, 0x1f, 0x7d, 0xd9, 0x80, 0xe9, 0xa8, 0x98, 0x72, 0x77, 0x3f, - 0x85, 0x4e, 0x52, 0xee, 0xd2, 0xd1, 0x72, 0x1f, 0xc7, 0x45, 0x30, 0x7f, 0x90, 0x11, 0x43, 0x7a, - 0x1a, 0xee, 0xfe, 0x68, 0x1f, 0x0a, 0x41, 0xd3, 0xe7, 0x85, 0xa2, 0xb5, 0x43, 0xde, 0x08, 0x37, - 0xd7, 0xaa, 0xdc, 0x13, 0x20, 0x3c, 0xb4, 0x89, 0x12, 0x7a, 0xf8, 0x94, 0xbc, 0x18, 0xe3, 0x5a, - 0x5b, 0x30, 0x4e, 0xe5, 0x2a, 0xba, 0xb9, 0x5c, 0x89, 0x33, 0x16, 0x25, 0x94, 0xb1, 0xe4, 0x65, - 0x7e, 0xdd, 0x80, 0xc2, 0x15, 0x57, 0xee, 0x23, 0x1f, 0x4d, 0xc1, 0xd0, 0xa3, 0xce, 0x83, 0xea, - 0xc5, 0x31, 0xbc, 0x62, 0x3c, 0x17, 0x31, 0xf3, 0x3c, 0xa8, 0xd1, 0x5e, 0x64, 0xd9, 0x8c, 0x28, - 0xa9, 0x2b, 0xee, 0x56, 0x5f, 0x2b, 0xe2, 0xff, 0xcb, 0xc1, 0xe4, 0x0b, 0xd6, 0x01, 0x71, 0x02, - 0xeb, 0xf8, 0x4a, 0xe2, 0x29, 0x18, 0xb7, 0xda, 0xec, 0x49, 0x4b, 0x3b, 0xe3, 0x87, 0x96, 0x93, - 0x10, 0x84, 0x75, 0xbc, 0x70, 0x43, 0xe3, 0xc9, 0x55, 0x92, 0xb6, 0xa2, 0xe5, 0x18, 0x1c, 0xf7, - 0xd4, 0x40, 0x57, 0x00, 0x89, 0x50, 0xd1, 0x52, 0xad, 0xe6, 0x76, 0x1c, 0xbe, 0xa5, 0x71, 0xa3, - 0x8a, 0xba, 0x6c, 0xae, 0xf7, 0x60, 0xe0, 0x84, 0x5a, 0xe8, 0x23, 0x30, 0x5f, 0x63, 0x94, 0xc5, - 0xd5, 0x43, 0xa7, 0xc8, 0xaf, 0x9f, 0xca, 0xe5, 0x7f, 0xb9, 0x0f, 0x1e, 0xee, 0x4b, 0x81, 0x4a, - 0xea, 0x07, 0xae, 0x67, 0x35, 0x88, 0x4e, 0x77, 0x34, 0x2a, 0x69, 0xb5, 0x07, 0x03, 0x27, 0xd4, - 0x42, 0x9f, 0x86, 0x42, 0xb0, 0xe3, 0x11, 0x7f, 0xc7, 0x6d, 0xd6, 0x85, 0x0b, 0xc2, 0x90, 0x96, - 0x36, 0x31, 0xfa, 0x9b, 0x92, 0xaa, 0x36, 0xbd, 0x65, 0x11, 0x0e, 0x79, 0x22, 0x0f, 0x46, 0xfd, - 0x9a, 0xdb, 0x26, 0xbe, 0x38, 0xb2, 0x5f, 0x49, 0x85, 0x3b, 0xb3, 0x1c, 0x69, 0x36, 0x3e, 0xc6, - 0x01, 0x0b, 0x4e, 0xe6, 0x77, 0x33, 0x30, 0xa1, 0x23, 0x0e, 0xb0, 0x37, 0x7d, 0xd6, 0x80, 0x89, - 0x9a, 0xeb, 0x04, 0x9e, 0xdb, 0xe4, 0xf6, 0xab, 0x74, 0x4e, 0x14, 0x94, 0xd4, 0x0a, 0x09, 0x2c, - 0xbb, 0xa9, 0x99, 0xc2, 0x34, 0x36, 0x38, 0xc2, 0x14, 0x7d, 0xde, 0x80, 0xe9, 0xd0, 0x63, 0x2d, - 0x34, 0xa4, 0xa5, 0x2a, 0x88, 0xda, 0xea, 0x2f, 0x46, 0x39, 0xe1, 0x38, 0x6b, 0x73, 0x0b, 0x66, - 0xe2, 0xa3, 0x4d, 0xbb, 0xb2, 0x6d, 0x89, 0xb5, 0x9e, 0x0d, 0xbb, 0xb2, 0x62, 0xf9, 0x3e, 0x66, - 0x10, 0xf4, 0x18, 0xe4, 0x5b, 0x96, 0xd7, 0xb0, 0x1d, 0xab, 0xc9, 0x7a, 0x31, 0xab, 0x6d, 0x48, - 0xa2, 0x1c, 0x2b, 0x0c, 0xf3, 0xbd, 0x30, 0xb1, 0x6e, 0x39, 0x0d, 0x52, 0x17, 0xfb, 0xf0, 0xdd, - 0x03, 0xce, 0x7e, 0x31, 0x02, 0xe3, 0xda, 0xdd, 0xec, 0xf4, 0xef, 0x59, 0x91, 0x7c, 0x17, 0xd9, - 0x14, 0xf3, 0x5d, 0xbc, 0x08, 0xb0, 0x6d, 0x3b, 0xb6, 0xbf, 0x73, 0xc2, 0x4c, 0x1a, 0xec, 0x89, - 0xf6, 0x92, 0xa2, 0x80, 0x35, 0x6a, 0xe1, 0x3b, 0x58, 0xee, 0x88, 0xfc, 0x42, 0xaf, 0x1b, 0x9a, - 0xba, 0x19, 0x4d, 0xe3, 0xdd, 0x5f, 0x1b, 0x98, 0x45, 0xa9, 0x7e, 0x2e, 0x3a, 0x81, 0x77, 0x70, - 0xa4, 0x56, 0xda, 0x84, 0xbc, 0x47, 0xfc, 0x4e, 0x8b, 0xde, 0x18, 0xc7, 0x8e, 0xdd, 0x0d, 0xcc, - 0x03, 0x03, 0x8b, 0xfa, 0x58, 0x51, 0x5a, 0x78, 0x06, 0x26, 0x23, 0x22, 0xa0, 0x19, 0xc8, 0xee, - 0x92, 0x03, 0x3e, 0x4f, 0x30, 0xfd, 0x89, 0xe6, 0x22, 0xaf, 0x85, 0xa2, 0x5b, 0x3e, 0x90, 0x79, - 0xda, 0x30, 0x5d, 0x48, 0x34, 0x00, 0x9c, 0xe4, 0x31, 0x87, 0x8e, 0x45, 0x53, 0x4b, 0xa5, 0xa1, - 0xc6, 0x82, 0xfb, 0xd9, 0x70, 0x98, 0xf9, 0xab, 0x51, 0x10, 0x4f, 0xd9, 0x03, 0x6c, 0x57, 0xfa, - 0x0b, 0x56, 0xe6, 0x04, 0x2f, 0x58, 0x57, 0x60, 0xc2, 0x76, 0xec, 0xc0, 0xb6, 0x9a, 0xcc, 0xb8, - 0x23, 0xd4, 0xa9, 0x74, 0x44, 0x9e, 0x58, 0xd5, 0x60, 0x09, 0x74, 0x22, 0x75, 0xd1, 0x35, 0xc8, - 0x31, 0x7d, 0x23, 0x26, 0xf0, 0xf1, 0xdf, 0xdb, 0x99, 0xab, 0x05, 0x8f, 0x4e, 0xe2, 0x94, 0xd8, - 0xe5, 0x83, 0xe7, 0x12, 0x51, 0xd7, 0x6f, 0x31, 0x8f, 0xc3, 0xcb, 0x47, 0x0c, 0x8e, 0x7b, 0x6a, - 0x50, 0x2a, 0xdb, 0x96, 0xdd, 0xec, 0x78, 0x24, 0xa4, 0x32, 0x1a, 0xa5, 0x72, 0x29, 0x06, 0xc7, - 0x3d, 0x35, 0xd0, 0x36, 0x4c, 0x88, 0x32, 0xee, 0x3d, 0x35, 0x76, 0xc2, 0x56, 0x32, 0x2f, 0xb9, - 0x4b, 0x1a, 0x25, 0x1c, 0xa1, 0x8b, 0x3a, 0x30, 0x6b, 0x3b, 0x35, 0xd7, 0xa9, 0x35, 0x3b, 0xbe, - 0xbd, 0x47, 0xc2, 0xd0, 0xa0, 0x93, 0x30, 0x3b, 0x7b, 0xd8, 0x2d, 0xce, 0xae, 0xc6, 0xc9, 0xe1, - 0x5e, 0x0e, 0xe8, 0x35, 0x03, 0xce, 0xd6, 0x5c, 0xc7, 0x67, 0xc1, 0xf9, 0x7b, 0xe4, 0xa2, 0xe7, - 0xb9, 0x1e, 0xe7, 0x5d, 0x38, 0x21, 0x6f, 0x66, 0x53, 0x5c, 0x4e, 0x22, 0x89, 0x93, 0x39, 0xa1, - 0x97, 0x21, 0xdf, 0xf6, 0xdc, 0x3d, 0xbb, 0x4e, 0x3c, 0xe1, 0x89, 0xb7, 0x96, 0x46, 0xb2, 0x90, - 0x8a, 0xa0, 0x19, 0x6e, 0x3d, 0xb2, 0x04, 0x2b, 0x7e, 0xe6, 0x97, 0x01, 0xa6, 0xa2, 0xe8, 0xe8, - 0x53, 0x00, 0x6d, 0xcf, 0x6d, 0x91, 0x60, 0x87, 0xa8, 0x10, 0x8f, 0x8d, 0x61, 0x73, 0x52, 0x48, - 0x7a, 0xd2, 0x7b, 0x85, 0x6e, 0x17, 0x61, 0x29, 0xd6, 0x38, 0x22, 0x0f, 0xc6, 0x76, 0xb9, 0xda, - 0x15, 0xa7, 0x90, 0x17, 0x52, 0x39, 0x33, 0x09, 0xce, 0x2c, 0x36, 0x41, 0x14, 0x61, 0xc9, 0x08, - 0x6d, 0x41, 0x76, 0x9f, 0x6c, 0xa5, 0x13, 0x10, 0x7d, 0x93, 0x88, 0xdb, 0x4c, 0x79, 0xec, 0xb0, - 0x5b, 0xcc, 0xde, 0x24, 0x5b, 0x98, 0x12, 0xa7, 0xed, 0xaa, 0xf3, 0x77, 0x78, 0xb1, 0x55, 0x0c, - 0xd9, 0xae, 0xc8, 0xa3, 0x3e, 0x6f, 0x97, 0x28, 0xc2, 0x92, 0x11, 0x7a, 0x19, 0x0a, 0xfb, 0xd6, - 0x1e, 0xd9, 0xf6, 0x5c, 0x27, 0x10, 0x2e, 0x53, 0x43, 0x7a, 0xfd, 0xdf, 0x94, 0xe4, 0x04, 0x5f, - 0xa6, 0xde, 0x55, 0x21, 0x0e, 0xd9, 0xa1, 0x3d, 0xc8, 0x3b, 0x64, 0x1f, 0x93, 0xa6, 0x5d, 0x4b, - 0xc7, 0xcb, 0x7e, 0x43, 0x50, 0x13, 0x9c, 0x99, 0xde, 0x93, 0x65, 0x58, 0xf1, 0xa2, 0x63, 0x79, - 0xcb, 0xdd, 0x12, 0x1b, 0xd5, 0x90, 0x63, 0xa9, 0x6e, 0xa6, 0x7c, 0x2c, 0xaf, 0xb8, 0x5b, 0x98, - 0x12, 0xa7, 0x6b, 0xa4, 0xa6, 0xfc, 0x75, 0xc4, 0x36, 0xb5, 0x91, 0xae, 0x9f, 0x12, 0x5f, 0x23, - 0x61, 0x29, 0xd6, 0x38, 0xd2, 0xbe, 0x6d, 0x08, 0x63, 0xa5, 0xd8, 0xa8, 0x86, 0xec, 0xdb, 0xa8, - 0xe9, 0x93, 0xf7, 0xad, 0x2c, 0xc3, 0x8a, 0x17, 0xe5, 0x6b, 0x0b, 0xcb, 0x5f, 0x3a, 0x5b, 0x55, - 0xd4, 0x8e, 0xc8, 0xf9, 0xca, 0x32, 0xac, 0x78, 0x21, 0x07, 0x46, 0xdb, 0xcd, 0x4e, 0xc3, 0x76, - 0x44, 0xb8, 0xe2, 0xb0, 0x1e, 0xac, 0x8c, 0x96, 0xe0, 0xc9, 0x3d, 0x02, 0x59, 0x09, 0x16, 0x5c, - 0xcc, 0xaf, 0x8f, 0xc2, 0x84, 0x9e, 0x04, 0x6e, 0x80, 0x33, 0x89, 0x3a, 0x87, 0x67, 0x8e, 0x73, - 0x0e, 0xa7, 0x17, 0x2f, 0xed, 0x4d, 0x45, 0x1a, 0x7d, 0x56, 0x53, 0x3b, 0x86, 0x86, 0x17, 0x2f, - 0xad, 0xd0, 0xc7, 0x11, 0xa6, 0xc7, 0x70, 0xb3, 0xa0, 0x87, 0x39, 0x7e, 0xdc, 0xc9, 0x45, 0x0f, - 0x73, 0x91, 0x03, 0xcc, 0xe3, 0x00, 0x61, 0x32, 0x34, 0xf1, 0xd6, 0xa6, 0x4e, 0x89, 0x5a, 0x92, - 0x36, 0x0d, 0x0b, 0x3d, 0x02, 0xa3, 0xf4, 0x40, 0x40, 0xea, 0x22, 0xce, 0x58, 0xdd, 0x6e, 0x2f, - 0xb1, 0x52, 0x2c, 0xa0, 0xe8, 0x69, 0x7a, 0x76, 0x0b, 0xd5, 0xb8, 0x08, 0x1f, 0x9e, 0x0b, 0xcf, - 0x6e, 0x21, 0x0c, 0x47, 0x30, 0xa9, 0xe8, 0x84, 0x6a, 0x5d, 0xb6, 0x62, 0x34, 0xd1, 0x99, 0x2a, - 0xc6, 0x1c, 0xc6, 0xac, 0x2d, 0x31, 0x2d, 0xcd, 0x66, 0x7a, 0x4e, 0xb3, 0xb6, 0xc4, 0xe0, 0xb8, - 0xa7, 0x06, 0x6d, 0x8c, 0x78, 0x26, 0x1c, 0xe7, 0xde, 0xa4, 0x7d, 0x1e, 0xf8, 0xde, 0xd0, 0x6f, - 0x20, 0x13, 0x6c, 0xe8, 0x3f, 0x98, 0x5e, 0x42, 0xc3, 0xc1, 0xaf, 0x20, 0xc3, 0x5d, 0x16, 0x3e, - 0x0e, 0x53, 0xd1, 0xbd, 0x39, 0xf5, 0xf7, 0x80, 0x3f, 0xcb, 0xc2, 0x99, 0x8d, 0x86, 0xed, 0xc4, - 0x13, 0x1c, 0x25, 0x25, 0x1a, 0x36, 0x8e, 0x9b, 0x68, 0x38, 0x0c, 0x58, 0x12, 0x99, 0x9c, 0x93, - 0x03, 0x96, 0x64, 0x9a, 0xe7, 0x28, 0x2e, 0xfa, 0xb9, 0x01, 0x0f, 0x5a, 0x75, 0x7e, 0x5a, 0xb6, - 0x9a, 0xa2, 0x34, 0x64, 0x2a, 0x57, 0xb4, 0x3f, 0xa4, 0xee, 0xeb, 0x6d, 0xfc, 0x62, 0xe9, 0x08, - 0xae, 0x7c, 0xc4, 0xdf, 0x25, 0x5a, 0xf0, 0xe0, 0x51, 0xa8, 0xf8, 0x48, 0xf1, 0x17, 0xae, 0xc2, - 0x3b, 0xef, 0xca, 0xe8, 0x58, 0xb3, 0xe5, 0xb3, 0x06, 0x14, 0xb8, 0xb9, 0x16, 0x93, 0x6d, 0xba, - 0x55, 0x58, 0x6d, 0xfb, 0x06, 0xf1, 0x7c, 0x99, 0x01, 0x4d, 0xbb, 0x50, 0x96, 0x2a, 0xab, 0x02, - 0x82, 0x35, 0x2c, 0xba, 0x19, 0xef, 0xda, 0x4e, 0x5d, 0x0c, 0x93, 0xda, 0x8c, 0x5f, 0xb0, 0x9d, - 0x3a, 0x66, 0x10, 0xb5, 0x5d, 0x67, 0xfb, 0x9a, 0x51, 0xbe, 0x66, 0xc0, 0x14, 0x8b, 0xd2, 0x0c, - 0xaf, 0x3a, 0x4f, 0x29, 0x1f, 0x1a, 0x2e, 0xc6, 0xf9, 0xa8, 0x0f, 0xcd, 0x9d, 0x6e, 0x71, 0x9c, - 0xc7, 0x75, 0x46, 0x5d, 0x6a, 0x3e, 0x2c, 0xec, 0x23, 0xcc, 0xd3, 0x27, 0x73, 0xec, 0xeb, 0xbb, - 0xb2, 0x1f, 0x56, 0x25, 0x11, 0x1c, 0xd2, 0x33, 0x5f, 0x81, 0x09, 0x3d, 0xdc, 0x02, 0x3d, 0x05, - 0xe3, 0x6d, 0xdb, 0x69, 0x44, 0xc3, 0xf2, 0x94, 0x0d, 0xb9, 0x12, 0x82, 0xb0, 0x8e, 0xc7, 0xaa, - 0xb9, 0x61, 0xb5, 0x98, 0xe9, 0xb9, 0xe2, 0xea, 0xd5, 0xc2, 0x3f, 0xe6, 0x06, 0x4c, 0xe8, 0xaa, - 0x12, 0x3d, 0x07, 0xa3, 0xdc, 0x6c, 0xcb, 0x28, 0x4c, 0xb0, 0xe0, 0xe6, 0x51, 0x6e, 0xe2, 0xbd, - 0xd3, 0x2d, 0xce, 0x13, 0xa7, 0xe6, 0xd6, 0x6d, 0xa7, 0xb1, 0x74, 0xcb, 0x77, 0x9d, 0x45, 0x6c, - 0xed, 0x4b, 0xed, 0x21, 0x6a, 0x99, 0xbf, 0x9f, 0x85, 0x33, 0x09, 0x61, 0x42, 0xe8, 0x75, 0x03, - 0x46, 0x59, 0x94, 0x81, 0xf4, 0xba, 0x79, 0x29, 0xf5, 0x50, 0xa4, 0x45, 0x16, 0xcc, 0x20, 0xd6, - 0x85, 0xda, 0x8e, 0x79, 0x21, 0x16, 0xcc, 0xd1, 0xff, 0x36, 0x60, 0xdc, 0xd2, 0x96, 0x2e, 0x77, - 0x44, 0xda, 0x4a, 0x5f, 0x98, 0x9e, 0x95, 0xaa, 0x39, 0x50, 0x86, 0x0b, 0x53, 0x97, 0x65, 0xe1, - 0xfd, 0x30, 0xae, 0x35, 0xe1, 0x38, 0x2b, 0x6e, 0xe1, 0x39, 0x98, 0x19, 0x6a, 0xc5, 0x7e, 0x08, - 0x8e, 0x9b, 0x20, 0x90, 0x2a, 0xc0, 0x7d, 0x3d, 0x14, 0x5b, 0xf5, 0xb8, 0x88, 0xc5, 0x16, 0x50, - 0x73, 0x0b, 0x66, 0xe2, 0x97, 0xc3, 0xd4, 0xdf, 0xdd, 0xdf, 0x0b, 0xc7, 0x4c, 0xe9, 0x67, 0x5e, - 0x04, 0x84, 0xdd, 0x66, 0x73, 0xcb, 0xaa, 0xed, 0xde, 0xb4, 0x9d, 0xba, 0xbb, 0xcf, 0xd6, 0xde, - 0x12, 0x14, 0x3c, 0x11, 0x05, 0xe6, 0x8b, 0x66, 0xa9, 0xc5, 0x2b, 0xc3, 0xc3, 0x7c, 0x1c, 0xe2, - 0x98, 0x3f, 0xc8, 0xc0, 0x98, 0x08, 0x59, 0xbc, 0x07, 0x2e, 0xcc, 0xbb, 0x91, 0xb7, 0xad, 0xd5, - 0x54, 0x22, 0x2d, 0xfb, 0xfa, 0x2f, 0xfb, 0x31, 0xff, 0xe5, 0x17, 0xd2, 0x61, 0x77, 0xb4, 0xf3, - 0xf2, 0xd7, 0x46, 0x60, 0x3a, 0x16, 0x02, 0x4a, 0x4f, 0x50, 0x3d, 0x3e, 0x7b, 0xd7, 0x53, 0x8d, - 0x32, 0x55, 0xee, 0xf5, 0x47, 0xbb, 0xef, 0xf9, 0x91, 0x04, 0xac, 0xd7, 0x52, 0xcb, 0xdd, 0xfe, - 0xdb, 0x5c, 0xac, 0xc7, 0x75, 0x47, 0xfb, 0x1b, 0x03, 0xee, 0xef, 0x1b, 0x29, 0xcc, 0x12, 0xcd, - 0x78, 0x51, 0xa8, 0x58, 0x90, 0x29, 0xe7, 0x43, 0x50, 0x0f, 0x4d, 0xf1, 0xdc, 0x20, 0x71, 0xf6, - 0xe8, 0x49, 0x98, 0x60, 0x1a, 0x9f, 0x6e, 0x4d, 0x01, 0x69, 0x0b, 0x3b, 0x39, 0xb3, 0x98, 0x56, - 0xb5, 0x72, 0x1c, 0xc1, 0x32, 0xbf, 0x6a, 0xc0, 0x7c, 0xbf, 0xb4, 0x23, 0x03, 0xdc, 0x57, 0xff, - 0x53, 0xcc, 0xc7, 0xba, 0xd8, 0xe3, 0x63, 0x1d, 0xbb, 0xb1, 0x4a, 0x77, 0x6a, 0xed, 0xb2, 0x98, - 0xbd, 0x8b, 0x0b, 0xf1, 0x17, 0x0c, 0x38, 0xd7, 0x67, 0x35, 0xf5, 0xf8, 0xda, 0x1b, 0x27, 0xf6, - 0xb5, 0xcf, 0x0c, 0xea, 0x6b, 0x6f, 0xfe, 0x28, 0x0b, 0x33, 0x42, 0x9e, 0xf0, 0xd8, 0xf7, 0x74, - 0xc4, 0x53, 0xfd, 0x5d, 0x31, 0x4f, 0xf5, 0xb9, 0x38, 0xfe, 0x6f, 0xdd, 0xd4, 0xdf, 0x5e, 0x6e, - 0xea, 0xbf, 0xce, 0xc0, 0xd9, 0xc4, 0x6c, 0x28, 0xe8, 0x73, 0x09, 0xaa, 0xe1, 0x66, 0xca, 0x69, - 0x57, 0x06, 0x54, 0x0e, 0xc3, 0xfa, 0x76, 0x7f, 0x59, 0xf7, 0xa9, 0xe6, 0x5b, 0xfd, 0xf6, 0x29, - 0x24, 0x90, 0x39, 0xa6, 0x7b, 0xb5, 0xf9, 0xdf, 0xb3, 0xf0, 0xe8, 0xa0, 0x84, 0xde, 0xa6, 0xe1, - 0x37, 0x7e, 0x24, 0xfc, 0xe6, 0x1e, 0xa9, 0xed, 0x53, 0x89, 0xc4, 0xf9, 0x7a, 0x56, 0xa9, 0xbd, - 0xde, 0xf9, 0x39, 0xd0, 0xa3, 0xea, 0x18, 0x3d, 0xda, 0xc9, 0x1c, 0xa9, 0xe1, 0x56, 0x38, 0x56, - 0xe5, 0xc5, 0x77, 0xba, 0xc5, 0xd9, 0x30, 0x26, 0x5f, 0x14, 0x62, 0x59, 0x09, 0x3d, 0x0a, 0x79, - 0x8f, 0x43, 0x65, 0xc0, 0x81, 0x78, 0x99, 0xe6, 0x65, 0x58, 0x41, 0xd1, 0xa7, 0xb5, 0xb3, 0xf0, - 0xc8, 0x69, 0xa5, 0x9e, 0x38, 0xea, 0xc1, 0xfd, 0x25, 0xc8, 0xfb, 0x32, 0xdb, 0x29, 0x7f, 0x15, - 0x79, 0x62, 0xc0, 0x38, 0x16, 0x7a, 0x03, 0x93, 0xa9, 0x4f, 0x79, 0xfb, 0x54, 0x62, 0x54, 0x45, - 0x12, 0x99, 0xea, 0xf2, 0xc3, 0x4d, 0x9f, 0x90, 0x70, 0xf1, 0xf9, 0xb1, 0x01, 0xe3, 0x62, 0xb4, - 0xee, 0x41, 0x68, 0xcd, 0xad, 0x68, 0x68, 0xcd, 0xc5, 0x54, 0xf6, 0x8e, 0x3e, 0x71, 0x35, 0xb7, - 0x60, 0x42, 0x4f, 0x88, 0x85, 0x5e, 0xd4, 0xf6, 0x3e, 0x63, 0x98, 0x14, 0x33, 0x72, 0x77, 0x0c, - 0xf7, 0x45, 0xf3, 0x1b, 0x05, 0xd5, 0x8b, 0xec, 0x8a, 0xa6, 0xcf, 0x41, 0xe3, 0xc8, 0x39, 0xa8, - 0x4f, 0x81, 0x4c, 0xfa, 0x53, 0xe0, 0x1a, 0xe4, 0xe5, 0x06, 0x25, 0xd4, 0xf8, 0xc3, 0xba, 0xb3, - 0x21, 0x3d, 0x0b, 0x50, 0x62, 0xda, 0xc4, 0x65, 0x57, 0x2d, 0x35, 0x86, 0x6a, 0xe3, 0x54, 0x64, - 0xd0, 0xcb, 0x30, 0xbe, 0xef, 0x7a, 0xbb, 0x4d, 0xd7, 0x62, 0x79, 0x8c, 0x21, 0x8d, 0xf7, 0x2d, - 0x65, 0x87, 0xe3, 0x1e, 0xdf, 0x37, 0x43, 0xfa, 0x58, 0x67, 0x86, 0x4a, 0x30, 0xdd, 0xb2, 0x1d, - 0x4c, 0xac, 0xba, 0x8a, 0xa0, 0x19, 0xe1, 0x89, 0x56, 0xe5, 0x21, 0x77, 0x3d, 0x0a, 0xc6, 0x71, - 0x7c, 0xf4, 0x79, 0x03, 0xa6, 0xbc, 0xc8, 0xa5, 0x5a, 0x64, 0x53, 0xac, 0x0c, 0x3f, 0x19, 0xa3, - 0x17, 0x75, 0xee, 0xf2, 0x1c, 0x2d, 0xc7, 0x31, 0xde, 0xe8, 0x93, 0x90, 0xf7, 0x45, 0xb6, 0xab, - 0x74, 0x1e, 0x46, 0xd5, 0x15, 0x96, 0x13, 0x0d, 0x87, 0x52, 0x96, 0x60, 0xc5, 0x10, 0xad, 0xc1, - 0x9c, 0xb4, 0x12, 0x44, 0xbe, 0x35, 0x33, 0x1a, 0x26, 0x47, 0xc1, 0x09, 0x70, 0x9c, 0x58, 0x8b, - 0x1e, 0xaa, 0x58, 0xa2, 0x39, 0xfe, 0x72, 0xa2, 0x3d, 0x36, 0xb0, 0xf5, 0x57, 0xc7, 0x02, 0x7a, - 0x54, 0x80, 0x58, 0x7e, 0x88, 0x00, 0xb1, 0x2a, 0x9c, 0x8d, 0x83, 0x58, 0xd6, 0x1b, 0x96, 0x68, - 0x47, 0x53, 0x66, 0x95, 0x24, 0x24, 0x9c, 0x5c, 0x17, 0xdd, 0x84, 0x82, 0x47, 0xd8, 0x75, 0xa7, - 0x24, 0x5d, 0x31, 0x8e, 0xed, 0x74, 0x86, 0x25, 0x01, 0x1c, 0xd2, 0xa2, 0xe3, 0x6e, 0x45, 0x53, - 0x9f, 0x5e, 0x4b, 0xf1, 0x6b, 0x79, 0x62, 0xec, 0xfb, 0x64, 0xa3, 0x32, 0xdf, 0x9a, 0x82, 0xc9, - 0x88, 0xa9, 0x03, 0x3d, 0x0c, 0x39, 0x96, 0x06, 0x88, 0xed, 0x56, 0xf9, 0x70, 0x47, 0xe5, 0x9d, - 0xc3, 0x61, 0xe8, 0x8b, 0x06, 0x4c, 0xb7, 0x23, 0xb6, 0x6a, 0xb9, 0x91, 0x0f, 0xf9, 0xfa, 0x1a, - 0x35, 0x80, 0x6b, 0x49, 0xc3, 0xa3, 0xcc, 0x70, 0x9c, 0x3b, 0xdd, 0x0f, 0x84, 0xe7, 0x66, 0x93, - 0x78, 0x0c, 0x5b, 0x1c, 0xb9, 0x14, 0x89, 0xe5, 0x28, 0x18, 0xc7, 0xf1, 0xe9, 0x08, 0xb3, 0xd6, - 0x0d, 0xf3, 0x19, 0xad, 0x92, 0x24, 0x80, 0x43, 0x5a, 0xe8, 0x39, 0x98, 0x12, 0x19, 0x2f, 0x2b, - 0x6e, 0xfd, 0xb2, 0xe5, 0xef, 0x88, 0xbb, 0x86, 0xba, 0x1b, 0x2d, 0x47, 0xa0, 0x38, 0x86, 0xcd, - 0xda, 0x16, 0xa6, 0x15, 0x65, 0x04, 0x46, 0xa3, 0x39, 0xd5, 0x97, 0xa3, 0x60, 0x1c, 0xc7, 0x47, - 0x8f, 0x69, 0x6a, 0x88, 0xbf, 0x66, 0xaa, 0xdd, 0x20, 0x41, 0x15, 0x95, 0x60, 0xba, 0xc3, 0xae, - 0x66, 0x75, 0x09, 0x14, 0xeb, 0x51, 0x31, 0xbc, 0x1e, 0x05, 0xe3, 0x38, 0x3e, 0x7a, 0x06, 0x26, - 0x3d, 0xba, 0xd9, 0x2a, 0x02, 0xfc, 0x89, 0x53, 0xbd, 0x60, 0x61, 0x1d, 0x88, 0xa3, 0xb8, 0xe8, - 0x79, 0x98, 0x0d, 0x13, 0xc4, 0x49, 0x02, 0xfc, 0xcd, 0x53, 0x65, 0x2b, 0x2a, 0xc5, 0x11, 0x70, - 0x6f, 0x1d, 0xf4, 0x5f, 0x60, 0x46, 0xeb, 0x89, 0x55, 0xa7, 0x4e, 0x6e, 0x8b, 0x24, 0x5e, 0xec, - 0xf3, 0x17, 0xcb, 0x31, 0x18, 0xee, 0xc1, 0x46, 0x1f, 0x80, 0xa9, 0x9a, 0xdb, 0x6c, 0xb2, 0x3d, - 0x8e, 0xe7, 0xf3, 0xe6, 0xd9, 0xba, 0x78, 0x5e, 0xb3, 0x08, 0x04, 0xc7, 0x30, 0xd1, 0x15, 0x40, - 0xee, 0x96, 0x4f, 0xbc, 0x3d, 0x52, 0x7f, 0x9e, 0x7f, 0x98, 0x97, 0x9e, 0x38, 0x26, 0xa3, 0x7e, - 0xe3, 0x57, 0x7b, 0x30, 0x70, 0x42, 0x2d, 0x96, 0xec, 0x48, 0x8b, 0xb3, 0x9b, 0x4a, 0xe3, 0x93, - 0x52, 0x71, 0x43, 0xc2, 0x5d, 0x83, 0xec, 0x3c, 0x18, 0xe5, 0x6e, 0xfc, 0xe9, 0xa4, 0xed, 0xd2, - 0x53, 0xfb, 0x86, 0x3a, 0x82, 0x97, 0x62, 0xc1, 0x09, 0x7d, 0x0a, 0x0a, 0x5b, 0x32, 0xcf, 0x3b, - 0xcb, 0xd5, 0x35, 0xb4, 0x5e, 0x8c, 0x7d, 0xb2, 0x20, 0xbc, 0x28, 0x2b, 0x00, 0x0e, 0x59, 0xa2, - 0x47, 0x60, 0xfc, 0x72, 0xa5, 0xa4, 0x66, 0xe1, 0x2c, 0x1b, 0xfd, 0x11, 0x5a, 0x05, 0xeb, 0x00, - 0xba, 0xc2, 0xd4, 0xf1, 0x0d, 0xb1, 0x21, 0x0e, 0xf5, 0x6d, 0xef, 0x69, 0x8c, 0x62, 0xb3, 0x47, - 0x5b, 0x5c, 0x9d, 0x3f, 0x13, 0xc3, 0x16, 0xe5, 0x58, 0x61, 0xa0, 0x97, 0x60, 0x5c, 0xe8, 0x0b, - 0xb6, 0x37, 0xcd, 0x9d, 0x2c, 0x86, 0x13, 0x87, 0x24, 0xb0, 0x4e, 0x8f, 0xbd, 0xc5, 0xb1, 0xf4, - 0xd7, 0xe4, 0x52, 0xa7, 0xd9, 0x9c, 0x3f, 0xcb, 0xf6, 0xcd, 0xf0, 0x2d, 0x2e, 0x04, 0x61, 0x1d, - 0x0f, 0x3d, 0x21, 0xfd, 0x4b, 0xde, 0x11, 0x79, 0x9c, 0x54, 0xfe, 0x25, 0xea, 0xd0, 0xdd, 0xc7, - 0xcd, 0xfb, 0xdc, 0x5d, 0x1c, 0x3b, 0xb6, 0x60, 0x41, 0x9e, 0xf8, 0x7a, 0x17, 0xc9, 0xfc, 0x7c, - 0xc4, 0x68, 0xb1, 0x70, 0xb3, 0x2f, 0x26, 0x3e, 0x82, 0x0a, 0xda, 0x82, 0xac, 0xd5, 0xdc, 0x9a, - 0xbf, 0x3f, 0x8d, 0xa3, 0xab, 0xfa, 0xd0, 0x36, 0x77, 0xcd, 0x2a, 0xad, 0x95, 0x31, 0x25, 0x6e, - 0xbe, 0x96, 0x51, 0x8f, 0x04, 0x2a, 0x9d, 0xe9, 0x2b, 0xfa, 0xac, 0x36, 0xd2, 0xf8, 0x90, 0x6c, - 0xcf, 0xc7, 0x10, 0xb8, 0x42, 0x4a, 0x9c, 0xd3, 0x6d, 0xb5, 0x8e, 0x53, 0xc9, 0x2e, 0x13, 0x4d, - 0xd5, 0xca, 0x2f, 0x97, 0xd1, 0x55, 0x6c, 0xfe, 0x2c, 0xaf, 0x6c, 0x62, 0x31, 0x87, 0x09, 0x0f, - 0x72, 0xb6, 0x1f, 0xd8, 0x6e, 0x8a, 0xf1, 0x86, 0xb1, 0x1c, 0xa7, 0xcc, 0x9d, 0x99, 0x01, 0x30, - 0x67, 0x45, 0x79, 0x3a, 0x0d, 0xdb, 0xb9, 0x2d, 0x9a, 0x7f, 0x2d, 0x75, 0x4f, 0x08, 0xce, 0x93, - 0x01, 0x30, 0x67, 0x85, 0x6e, 0xf1, 0x99, 0x96, 0xce, 0x47, 0x83, 0xe3, 0xdf, 0x02, 0x8f, 0xce, - 0x38, 0xca, 0xcb, 0x6f, 0xd9, 0xe2, 0x0c, 0x33, 0x24, 0xaf, 0xea, 0xfa, 0x6a, 0x12, 0xaf, 0xea, - 0xfa, 0x2a, 0xa6, 0x4c, 0xd0, 0x1b, 0x06, 0x80, 0xa5, 0x3e, 0x8a, 0x9d, 0xce, 0x97, 0x43, 0xfa, - 0x7d, 0x64, 0x9b, 0x7b, 0x20, 0x86, 0x50, 0xac, 0x71, 0x46, 0x2f, 0xc3, 0x98, 0xc5, 0xbf, 0x7b, - 0x24, 0x9c, 0x3b, 0xd3, 0xf9, 0x98, 0x57, 0x4c, 0x02, 0xe6, 0xd5, 0x2a, 0x40, 0x58, 0x32, 0xa4, - 0xbc, 0x03, 0xcf, 0x22, 0xdb, 0xf6, 0xae, 0xf0, 0xf2, 0xac, 0x0e, 0x9d, 0xbe, 0x9c, 0x12, 0x4b, - 0xe2, 0x2d, 0x40, 0x58, 0x32, 0xe4, 0xdf, 0xa1, 0xb5, 0x1c, 0x4b, 0x85, 0xec, 0xa4, 0x13, 0xd8, - 0xa5, 0x07, 0x01, 0x69, 0xdf, 0xa1, 0xd5, 0x19, 0xe1, 0x28, 0x5f, 0xb4, 0x07, 0xa3, 0x16, 0xfb, - 0x22, 0x9b, 0xb8, 0x1f, 0xe1, 0x34, 0xbe, 0xee, 0x16, 0xeb, 0x03, 0xb6, 0xb9, 0x88, 0xef, 0xbe, - 0x09, 0x6e, 0xe6, 0x2f, 0xb3, 0x00, 0x4c, 0x04, 0x1e, 0xbe, 0xde, 0x62, 0x29, 0x0f, 0x77, 0xdc, - 0x7a, 0x3a, 0x1f, 0x99, 0xd3, 0xa3, 0xd0, 0x41, 0xe4, 0x37, 0xdc, 0x71, 0xeb, 0x58, 0x30, 0x41, - 0x0d, 0x18, 0x69, 0x5b, 0xc1, 0x4e, 0xfa, 0x21, 0xef, 0x79, 0x1e, 0xc7, 0x15, 0xec, 0x60, 0xc6, - 0x00, 0xbd, 0x6a, 0xc0, 0x18, 0x0f, 0x7a, 0x97, 0x16, 0xf7, 0xa1, 0x9f, 0x95, 0x65, 0x9f, 0x2d, - 0xf2, 0xc8, 0x7a, 0xe1, 0xfa, 0xa1, 0x54, 0xb2, 0x28, 0xc5, 0x92, 0xed, 0xc2, 0xeb, 0x06, 0x4c, - 0xe8, 0xa8, 0x09, 0x4e, 0x1b, 0x1f, 0xd3, 0x9d, 0x36, 0xd2, 0xec, 0x0f, 0xdd, 0xff, 0xe3, 0xef, - 0x0d, 0xd0, 0xbe, 0x1a, 0x1c, 0xba, 0x80, 0x1a, 0x03, 0xbb, 0x80, 0x66, 0x8e, 0xe9, 0x02, 0x9a, - 0x3d, 0x96, 0x0b, 0xe8, 0xc8, 0xf1, 0x5d, 0x40, 0x73, 0xfd, 0x5d, 0x40, 0xcd, 0x37, 0x0d, 0x98, - 0xed, 0xd9, 0x87, 0xe9, 0xb1, 0xcd, 0x73, 0xdd, 0xa0, 0x8f, 0xe7, 0x15, 0x0e, 0x41, 0x58, 0xc7, - 0x43, 0x2b, 0x30, 0x23, 0x12, 0x7c, 0x57, 0xdb, 0x4d, 0x3b, 0x31, 0x1d, 0xc1, 0x66, 0x0c, 0x8e, - 0x7b, 0x6a, 0x98, 0x7f, 0x6c, 0xc0, 0xb8, 0x16, 0xc4, 0x48, 0xdb, 0xc1, 0x82, 0x3d, 0x85, 0x18, - 0x61, 0x6e, 0x73, 0xf6, 0xc2, 0xc1, 0x61, 0xfc, 0xb1, 0xad, 0xa1, 0xa5, 0x7f, 0x0d, 0x1f, 0xdb, - 0x68, 0x29, 0x16, 0x50, 0x9e, 0xd8, 0x93, 0xb4, 0x59, 0xa7, 0x67, 0xf5, 0xc4, 0x9e, 0xa4, 0x8d, - 0x19, 0x84, 0xb1, 0xa3, 0xe7, 0x57, 0xe1, 0x1d, 0xac, 0xa5, 0x52, 0xb7, 0xbc, 0x00, 0x73, 0x18, - 0x3a, 0x0f, 0x59, 0xe2, 0xd4, 0xc5, 0x65, 0x5b, 0x7d, 0xbe, 0xec, 0xa2, 0x53, 0xc7, 0xb4, 0xdc, - 0xbc, 0x0a, 0x13, 0x55, 0x52, 0xf3, 0x48, 0xf0, 0x02, 0x39, 0x18, 0xf8, 0x7b, 0x68, 0x74, 0xb6, - 0xc7, 0xbe, 0x87, 0x46, 0xab, 0xd3, 0x72, 0xf3, 0xf7, 0x0c, 0x88, 0xe5, 0xfb, 0xd7, 0x0c, 0xef, - 0x46, 0x3f, 0xc3, 0x7b, 0xc4, 0x44, 0x9c, 0x39, 0xd2, 0x44, 0x7c, 0x05, 0x50, 0x8b, 0x2e, 0x85, - 0xc8, 0xd7, 0x2d, 0x84, 0x9d, 0x23, 0x0c, 0x99, 0xee, 0xc1, 0xc0, 0x09, 0xb5, 0xcc, 0xdf, 0xe5, - 0xc2, 0xea, 0x5f, 0x00, 0xb8, 0x7b, 0x07, 0x74, 0x20, 0xc7, 0x48, 0x09, 0x63, 0xcf, 0x90, 0x86, - 0xd2, 0xde, 0xd4, 0x23, 0xe1, 0x40, 0x8a, 0x25, 0xcf, 0xb8, 0x99, 0x3f, 0xe2, 0xb2, 0x6a, 0x9f, - 0x08, 0x18, 0x40, 0xd6, 0x56, 0x54, 0xd6, 0xcb, 0x69, 0xed, 0x95, 0xc9, 0x32, 0xa2, 0x45, 0x80, - 0x36, 0xf1, 0x6a, 0xc4, 0x09, 0xa4, 0xd3, 0x7a, 0x4e, 0x04, 0x15, 0xa9, 0x52, 0xac, 0x61, 0x98, - 0xaf, 0x1a, 0x30, 0x53, 0x0d, 0xec, 0xda, 0xae, 0xed, 0xf0, 0x20, 0xb9, 0x6d, 0xbb, 0x41, 0x6f, - 0x47, 0x44, 0x7c, 0xea, 0x8b, 0x9b, 0xdf, 0xd4, 0x56, 0x2c, 0xbf, 0xf0, 0x25, 0xe1, 0xa8, 0x04, - 0xd3, 0xf2, 0xd1, 0x41, 0xda, 0x4c, 0x79, 0x70, 0xaf, 0xb2, 0xd1, 0xac, 0x44, 0xc1, 0x38, 0x8e, - 0x6f, 0x7e, 0x1a, 0xc6, 0xb5, 0xfd, 0x95, 0x6d, 0x45, 0xb7, 0xad, 0x5a, 0x10, 0x5f, 0xc2, 0x17, - 0x69, 0x21, 0xe6, 0x30, 0x66, 0xda, 0xe5, 0x5e, 0xcd, 0xb1, 0x25, 0x2c, 0x7c, 0x99, 0x05, 0x94, - 0x12, 0xf3, 0x48, 0x83, 0xdc, 0x96, 0x89, 0x66, 0x25, 0x31, 0x4c, 0x0b, 0x31, 0x87, 0x99, 0x8f, - 0x41, 0x5e, 0xa6, 0x60, 0x60, 0x71, 0xcc, 0xd2, 0xec, 0xa8, 0xc7, 0x31, 0xbb, 0x5e, 0x80, 0x19, - 0xc4, 0xbc, 0x01, 0x79, 0x99, 0x29, 0xe2, 0xee, 0xd8, 0x74, 0x55, 0xf9, 0x8e, 0x7d, 0xd9, 0xf5, - 0x03, 0x99, 0xde, 0x82, 0xbf, 0x8c, 0x6c, 0xac, 0xb2, 0x32, 0xac, 0xa0, 0xe6, 0x2c, 0x4c, 0xab, - 0x27, 0x0f, 0xe1, 0x66, 0xfa, 0xdd, 0x2c, 0x4c, 0x44, 0xbe, 0x9e, 0x7d, 0xf7, 0xe9, 0x36, 0xf8, - 0x2a, 0x4e, 0x78, 0xba, 0xc8, 0x1e, 0xf3, 0xe9, 0x42, 0x7f, 0x2b, 0x1a, 0x39, 0xdd, 0xb7, 0xa2, - 0x5c, 0x3a, 0x6f, 0x45, 0x01, 0x8c, 0xf9, 0x42, 0x51, 0x8d, 0xa6, 0x61, 0xc4, 0x89, 0x8d, 0x18, - 0x3f, 0x1b, 0x4b, 0x7d, 0x27, 0x59, 0x99, 0xdf, 0xca, 0xc1, 0x54, 0x34, 0x47, 0xd6, 0x00, 0x23, - 0xf9, 0x58, 0xcf, 0x48, 0x1e, 0xd3, 0x56, 0x9a, 0x1d, 0xd6, 0x56, 0x3a, 0x32, 0xac, 0xad, 0x34, - 0x77, 0x02, 0x5b, 0x69, 0xaf, 0xa5, 0x73, 0x74, 0x60, 0x4b, 0xe7, 0xb3, 0xca, 0xef, 0x68, 0x2c, - 0xf2, 0x50, 0x1f, 0xfa, 0x1d, 0xa1, 0xe8, 0x30, 0x2c, 0xbb, 0xf5, 0x44, 0xff, 0xad, 0xfc, 0x5d, - 0x6c, 0x42, 0x5e, 0xa2, 0x9b, 0xd0, 0xf1, 0x9f, 0x63, 0xde, 0x71, 0x0c, 0x17, 0xa1, 0xa7, 0x60, - 0x5c, 0xcc, 0x27, 0x76, 0x56, 0x82, 0xe8, 0x39, 0xab, 0x1a, 0x82, 0xb0, 0x8e, 0xc7, 0xbe, 0x84, - 0x1a, 0xfd, 0xf4, 0x2b, 0x33, 0x3d, 0xeb, 0x5f, 0x42, 0x8d, 0x7d, 0x2a, 0x36, 0x8e, 0x6f, 0x7e, - 0x12, 0xce, 0x26, 0xde, 0x04, 0x99, 0x69, 0x8c, 0xa9, 0x71, 0x52, 0x17, 0x08, 0x9a, 0x18, 0xb1, - 0x14, 0xca, 0x0b, 0x37, 0xfb, 0x62, 0xe2, 0x23, 0xa8, 0x98, 0xdf, 0xcc, 0xc2, 0x54, 0xf4, 0x33, - 0x5a, 0x68, 0x5f, 0xd9, 0x8d, 0x52, 0x31, 0x59, 0x71, 0xb2, 0x5a, 0xde, 0xa5, 0xbe, 0x46, 0xe0, - 0x7d, 0x36, 0xbf, 0xb6, 0x54, 0x12, 0xa8, 0xd3, 0x63, 0x2c, 0xac, 0xaf, 0x82, 0x1d, 0xfb, 0x52, - 0x56, 0x18, 0x8c, 0x22, 0xae, 0x5d, 0xa9, 0x73, 0x0f, 0xc3, 0x4b, 0x14, 0x2b, 0xac, 0xb1, 0xa5, - 0xba, 0x65, 0x8f, 0x78, 0xf6, 0xb6, 0xad, 0x3e, 0x01, 0xca, 0x76, 0xee, 0x1b, 0xa2, 0x0c, 0x2b, - 0xa8, 0xf9, 0x6a, 0x06, 0xc2, 0x0f, 0x1e, 0xb3, 0x6f, 0xcd, 0xf8, 0xda, 0x11, 0x57, 0x0c, 0xdb, - 0x95, 0x61, 0x3f, 0xe8, 0x14, 0x52, 0x14, 0x3e, 0xa1, 0x5a, 0x09, 0x8e, 0x70, 0xfc, 0x0d, 0x7c, - 0xe8, 0xd8, 0x82, 0xe9, 0x58, 0x48, 0x70, 0xea, 0xfe, 0xfb, 0xbf, 0xc8, 0x42, 0x41, 0x05, 0x55, - 0xa3, 0xf7, 0x47, 0xec, 0x0d, 0x85, 0xf2, 0x3b, 0xb5, 0x0f, 0x21, 0xec, 0xb8, 0xf5, 0x3b, 0xdd, - 0xe2, 0xb4, 0x42, 0x8e, 0xd9, 0x0e, 0xce, 0x43, 0xb6, 0xe3, 0x35, 0xe3, 0x17, 0x8a, 0xeb, 0x78, - 0x0d, 0xd3, 0x72, 0x74, 0x3b, 0x7e, 0xe1, 0x5f, 0x4f, 0x29, 0x10, 0x9c, 0x9f, 0xbc, 0xfb, 0x5f, - 0xf4, 0xa9, 0x96, 0xdc, 0x72, 0xeb, 0x07, 0xf1, 0x0f, 0x27, 0x94, 0xdd, 0xfa, 0x01, 0x66, 0x10, - 0xf4, 0x1c, 0x4c, 0x05, 0x76, 0x8b, 0xb8, 0x9d, 0x40, 0xff, 0x9c, 0x6c, 0x36, 0x7c, 0xd4, 0xdc, - 0x8c, 0x40, 0x71, 0x0c, 0x9b, 0x6a, 0xd9, 0x5b, 0xbe, 0xeb, 0xb0, 0x6c, 0x88, 0xa3, 0xd1, 0x17, - 0x90, 0x2b, 0xd5, 0xab, 0x1b, 0xcc, 0xee, 0xa1, 0x30, 0x28, 0xb6, 0xcd, 0x22, 0x1a, 0x3d, 0x22, - 0x7c, 0x0a, 0x66, 0xc2, 0xfc, 0x1a, 0xbc, 0x1c, 0x2b, 0x0c, 0xb4, 0xc2, 0x69, 0x53, 0x69, 0x99, - 0x46, 0x99, 0x28, 0x3f, 0x2a, 0xe9, 0xd2, 0xb2, 0x23, 0x23, 0x83, 0x54, 0x4d, 0xf3, 0x3a, 0x4c, - 0xc7, 0x3a, 0x4c, 0x5e, 0x00, 0x8d, 0xe4, 0x0b, 0xe0, 0x60, 0xdf, 0x3a, 0xf8, 0x03, 0x03, 0x66, - 0x7b, 0xb6, 0x80, 0x41, 0xc3, 0x53, 0xe2, 0xca, 0x28, 0x73, 0x72, 0x65, 0x94, 0x3d, 0x9e, 0x32, - 0x2a, 0x6f, 0x7d, 0xff, 0xad, 0x0b, 0xf7, 0xfd, 0xf0, 0xad, 0x0b, 0xf7, 0xfd, 0xe4, 0xad, 0x0b, - 0xf7, 0xbd, 0x7a, 0x78, 0xc1, 0xf8, 0xfe, 0xe1, 0x05, 0xe3, 0x87, 0x87, 0x17, 0x8c, 0x9f, 0x1c, - 0x5e, 0x30, 0xfe, 0xfa, 0xf0, 0x82, 0xf1, 0xe6, 0x2f, 0x2e, 0xdc, 0xf7, 0xe2, 0xb3, 0xe1, 0x04, - 0x5d, 0x92, 0x13, 0x94, 0xfd, 0x78, 0x8f, 0x9c, 0x8e, 0x4b, 0xed, 0xdd, 0xc6, 0x12, 0x9d, 0xa0, - 0x4b, 0xaa, 0x44, 0x4e, 0xd0, 0x7f, 0x09, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x70, 0x0c, 0x96, 0x88, - 0x96, 0x00, 0x00, + 0x95, 0x98, 0xaa, 0x9b, 0x4d, 0x76, 0x1f, 0xbe, 0xef, 0x70, 0x3c, 0x14, 0xa5, 0x99, 0x96, 0x4b, + 0x86, 0x22, 0x3b, 0x32, 0x69, 0xeb, 0x91, 0xc8, 0x96, 0xa0, 0xa4, 0x9b, 0x9c, 0xd1, 0x70, 0x44, + 0xce, 0xf4, 0xdc, 0xe6, 0xcc, 0xd8, 0xb2, 0x65, 0xbb, 0xd8, 0x7d, 0xd9, 0xac, 0x61, 0x77, 0x55, + 0xbb, 0xaa, 0x9a, 0x1c, 0xca, 0x82, 0x2d, 0xd9, 0x90, 0xe2, 0x18, 0x36, 0xac, 0xc4, 0x36, 0x82, + 0x20, 0x41, 0x60, 0x04, 0x06, 0xf2, 0xb0, 0xbf, 0x8c, 0x04, 0xf9, 0x31, 0x90, 0x20, 0x7e, 0xc4, + 0xf9, 0xf0, 0xc2, 0xfe, 0xd8, 0xf5, 0x03, 0x70, 0x7b, 0x45, 0xef, 0xcf, 0x2e, 0x76, 0x61, 0x2c, + 0xe0, 0xc5, 0xc2, 0xf3, 0xb1, 0x58, 0xdc, 0x67, 0xdd, 0xaa, 0xae, 0x26, 0x9b, 0xec, 0xe2, 0x58, + 0xd8, 0xf5, 0x5f, 0xf7, 0x3d, 0xe7, 0x9e, 0x73, 0xea, 0x3e, 0xcf, 0x3d, 0xf7, 0x9c, 0x73, 0x61, + 0xad, 0x61, 0x07, 0xdb, 0x9d, 0xcd, 0xc5, 0x9a, 0xdb, 0x5a, 0xb2, 0xbc, 0x86, 0xdb, 0xf6, 0xdc, + 0xdb, 0xec, 0xc7, 0x7b, 0x3d, 0xb7, 0xd9, 0x74, 0x3b, 0x81, 0xbf, 0xd4, 0xde, 0x69, 0x2c, 0x59, + 0x6d, 0xdb, 0x5f, 0x52, 0x25, 0xbb, 0xef, 0xb7, 0x9a, 0xed, 0x6d, 0xeb, 0xfd, 0x4b, 0x0d, 0xe2, + 0x10, 0xcf, 0x0a, 0x48, 0x7d, 0xb1, 0xed, 0xb9, 0x81, 0x8b, 0x9e, 0x0d, 0xa9, 0x2d, 0x4a, 0x6a, + 0xec, 0xc7, 0xc7, 0x65, 0xdd, 0xc5, 0xf6, 0x4e, 0x63, 0x91, 0x52, 0x5b, 0x54, 0x25, 0x92, 0xda, + 0xc2, 0x7b, 0x35, 0x59, 0x1a, 0x6e, 0xc3, 0x5d, 0x62, 0x44, 0x37, 0x3b, 0x5b, 0xec, 0x1f, 0xfb, + 0xc3, 0x7e, 0x71, 0x66, 0x0b, 0x0f, 0xef, 0x3c, 0xed, 0x2f, 0xda, 0x2e, 0x95, 0x6d, 0x69, 0xd3, + 0x0a, 0x6a, 0xdb, 0x4b, 0xbb, 0x3d, 0x12, 0x2d, 0x98, 0x1a, 0x52, 0xcd, 0xf5, 0x48, 0x12, 0xce, + 0x93, 0x21, 0x4e, 0xcb, 0xaa, 0x6d, 0xdb, 0x0e, 0xf1, 0xf6, 0xc3, 0xaf, 0x6e, 0x91, 0xc0, 0x4a, + 0xaa, 0xb5, 0xd4, 0xaf, 0x96, 0xd7, 0x71, 0x02, 0xbb, 0x45, 0x7a, 0x2a, 0xfc, 0xb3, 0xa3, 0x2a, + 0xf8, 0xb5, 0x6d, 0xd2, 0xb2, 0x7a, 0xea, 0x3d, 0xd1, 0xaf, 0x5e, 0x27, 0xb0, 0x9b, 0x4b, 0xb6, + 0x13, 0xf8, 0x81, 0x17, 0xaf, 0x64, 0x7e, 0x2f, 0x0b, 0x85, 0xd2, 0x5a, 0xb9, 0x1a, 0x58, 0x41, + 0xc7, 0x47, 0x6f, 0x18, 0x30, 0xd1, 0x74, 0xad, 0x7a, 0xd9, 0x6a, 0x5a, 0x4e, 0x8d, 0x78, 0xf3, + 0xc6, 0x43, 0xc6, 0xa3, 0xe3, 0x8f, 0xaf, 0x2d, 0x0e, 0xd3, 0x5f, 0x8b, 0xa5, 0x3d, 0x1f, 0x13, + 0xdf, 0xed, 0x78, 0x35, 0x82, 0xc9, 0x56, 0x79, 0xee, 0x87, 0xdd, 0xe2, 0x7d, 0x07, 0xdd, 0xe2, + 0xc4, 0x9a, 0xc6, 0x09, 0x47, 0xf8, 0xa2, 0xaf, 0x19, 0x30, 0x5b, 0xb3, 0x1c, 0xcb, 0xdb, 0xdf, + 0xb0, 0xbc, 0x06, 0x09, 0x9e, 0xf7, 0xdc, 0x4e, 0x7b, 0x3e, 0x73, 0x0a, 0xd2, 0xdc, 0x2f, 0xa4, + 0x99, 0x5d, 0x8e, 0xb3, 0xc3, 0xbd, 0x12, 0x30, 0xb9, 0xfc, 0xc0, 0xda, 0x6c, 0x12, 0x5d, 0xae, + 0xec, 0x69, 0xca, 0x55, 0x8d, 0xb3, 0xc3, 0xbd, 0x12, 0x98, 0xaf, 0x67, 0x61, 0xb6, 0xb4, 0x56, + 0xde, 0xf0, 0xac, 0xad, 0x2d, 0xbb, 0x86, 0xdd, 0x4e, 0x60, 0x3b, 0x0d, 0xf4, 0x6e, 0x18, 0xb3, + 0x9d, 0x86, 0x47, 0x7c, 0x9f, 0x75, 0x64, 0xa1, 0x3c, 0x2d, 0x88, 0x8e, 0xad, 0xf2, 0x62, 0x2c, + 0xe1, 0xe8, 0x29, 0x18, 0xf7, 0x89, 0xb7, 0x6b, 0xd7, 0x48, 0xc5, 0xf5, 0x02, 0xd6, 0xd2, 0xb9, + 0xf2, 0x19, 0x81, 0x3e, 0x5e, 0x0d, 0x41, 0x58, 0xc7, 0xa3, 0xd5, 0x3c, 0xd7, 0x0d, 0x04, 0x9c, + 0x35, 0x44, 0x21, 0xac, 0x86, 0x43, 0x10, 0xd6, 0xf1, 0xd0, 0x9b, 0x06, 0xcc, 0xf8, 0x81, 0x5d, + 0xdb, 0xb1, 0x1d, 0xe2, 0xfb, 0xcb, 0xae, 0xb3, 0x65, 0x37, 0xe6, 0x73, 0xac, 0x15, 0xaf, 0x0e, + 0xd7, 0x8a, 0xd5, 0x18, 0xd5, 0xf2, 0xdc, 0x41, 0xb7, 0x38, 0x13, 0x2f, 0xc5, 0x3d, 0xdc, 0xd1, + 0x0a, 0xcc, 0x58, 0x8e, 0xe3, 0x06, 0x56, 0x60, 0xbb, 0x4e, 0xc5, 0x23, 0x5b, 0xf6, 0x9d, 0xf9, + 0x11, 0xf6, 0x39, 0xf3, 0xe2, 0x73, 0x66, 0x4a, 0x31, 0x38, 0xee, 0xa9, 0x61, 0xae, 0xc0, 0x7c, + 0xa9, 0xb5, 0x69, 0xf9, 0xbe, 0x55, 0x77, 0xbd, 0x58, 0x6f, 0x3c, 0x0a, 0xf9, 0x96, 0xd5, 0x6e, + 0xdb, 0x4e, 0x83, 0x76, 0x47, 0xf6, 0xd1, 0x42, 0x79, 0xe2, 0xa0, 0x5b, 0xcc, 0xaf, 0x8b, 0x32, + 0xac, 0xa0, 0xe6, 0xcf, 0x33, 0x30, 0x5e, 0x72, 0xac, 0xe6, 0xbe, 0x6f, 0xfb, 0xb8, 0xe3, 0xa0, + 0x4f, 0x40, 0x9e, 0xae, 0x2e, 0x75, 0x2b, 0xb0, 0xc4, 0x8c, 0x7c, 0xdf, 0x22, 0x9f, 0xec, 0x8b, + 0xfa, 0x64, 0x0f, 0xdb, 0x85, 0x62, 0x2f, 0xee, 0xbe, 0x7f, 0xf1, 0xda, 0xe6, 0x6d, 0x52, 0x0b, + 0xd6, 0x49, 0x60, 0x95, 0x91, 0xf8, 0x0a, 0x08, 0xcb, 0xb0, 0xa2, 0x8a, 0x5c, 0x18, 0xf1, 0xdb, + 0xa4, 0x26, 0x66, 0xd8, 0xfa, 0x90, 0x23, 0x39, 0x14, 0xbd, 0xda, 0x26, 0xb5, 0xf2, 0x84, 0x60, + 0x3d, 0x42, 0xff, 0x61, 0xc6, 0x08, 0xed, 0xc1, 0xa8, 0xcf, 0xd6, 0x1c, 0x31, 0x79, 0xae, 0xa5, + 0xc7, 0x92, 0x91, 0x2d, 0x4f, 0x09, 0xa6, 0xa3, 0xfc, 0x3f, 0x16, 0xec, 0xcc, 0x5f, 0x18, 0x70, + 0x46, 0xc3, 0x2e, 0x79, 0x8d, 0x4e, 0x8b, 0x38, 0x01, 0x7a, 0x08, 0x46, 0x1c, 0xab, 0x45, 0xc4, + 0x44, 0x51, 0x22, 0x5f, 0xb5, 0x5a, 0x04, 0x33, 0x08, 0x7a, 0x18, 0x72, 0xbb, 0x56, 0xb3, 0x43, + 0x58, 0x23, 0x15, 0xca, 0x93, 0x02, 0x25, 0x77, 0x93, 0x16, 0x62, 0x0e, 0x43, 0xaf, 0x40, 0x81, + 0xfd, 0xb8, 0xe4, 0xb9, 0xad, 0x94, 0x3e, 0x4d, 0x48, 0x78, 0x53, 0x92, 0x2d, 0x4f, 0x1e, 0x74, + 0x8b, 0x05, 0xf5, 0x17, 0x87, 0x0c, 0xcd, 0x5f, 0x19, 0x30, 0xad, 0x7d, 0xdc, 0x9a, 0xed, 0x07, + 0xe8, 0xa3, 0x3d, 0x83, 0x67, 0x71, 0xb0, 0xc1, 0x43, 0x6b, 0xb3, 0xa1, 0x33, 0x23, 0xbe, 0x34, + 0x2f, 0x4b, 0xb4, 0x81, 0xe3, 0x40, 0xce, 0x0e, 0x48, 0xcb, 0x9f, 0xcf, 0x3c, 0x94, 0x7d, 0x74, + 0xfc, 0xf1, 0xd5, 0xd4, 0xba, 0x31, 0x6c, 0xdf, 0x55, 0x4a, 0x1f, 0x73, 0x36, 0xe6, 0xb7, 0x47, + 0x22, 0x5f, 0x48, 0x47, 0x14, 0x72, 0x61, 0xac, 0x45, 0x02, 0xcf, 0xae, 0xf1, 0x79, 0x35, 0xfe, + 0xf8, 0xca, 0x70, 0x52, 0xac, 0x33, 0x62, 0xe1, 0x62, 0xc9, 0xff, 0xfb, 0x58, 0x72, 0x41, 0xdb, + 0x30, 0x62, 0x79, 0x0d, 0xf9, 0xcd, 0x97, 0xd2, 0xe9, 0xdf, 0x70, 0xcc, 0x95, 0xbc, 0x86, 0x8f, + 0x19, 0x07, 0xb4, 0x04, 0x85, 0x80, 0x78, 0x2d, 0xdb, 0xb1, 0x02, 0xbe, 0xba, 0xe6, 0xcb, 0xb3, + 0x02, 0xad, 0xb0, 0x21, 0x01, 0x38, 0xc4, 0x41, 0x4d, 0x18, 0xad, 0x7b, 0xfb, 0xb8, 0xe3, 0xcc, + 0x8f, 0xa4, 0xd1, 0x14, 0x2b, 0x8c, 0x56, 0x38, 0x99, 0xf8, 0x7f, 0x2c, 0x78, 0xa0, 0x6f, 0x18, + 0x30, 0xd7, 0x22, 0x96, 0xdf, 0xf1, 0x08, 0xfd, 0x04, 0x4c, 0x02, 0xe2, 0xd0, 0xd5, 0x70, 0x3e, + 0xc7, 0x98, 0xe3, 0x61, 0xfb, 0xa1, 0x97, 0x72, 0xf9, 0x41, 0x21, 0xca, 0x5c, 0x12, 0x14, 0x27, + 0x4a, 0x63, 0xfe, 0x7c, 0x04, 0x66, 0x7b, 0x56, 0x08, 0xf4, 0x24, 0xe4, 0xda, 0xdb, 0x96, 0x2f, + 0xa7, 0xfc, 0x05, 0x39, 0xde, 0x2a, 0xb4, 0xf0, 0x6e, 0xb7, 0x38, 0x29, 0xab, 0xb0, 0x02, 0xcc, + 0x91, 0xe9, 0x9e, 0xda, 0x22, 0xbe, 0x6f, 0x35, 0xe4, 0x3a, 0xa0, 0x0d, 0x13, 0x56, 0x8c, 0x25, + 0x1c, 0xfd, 0x2b, 0x03, 0x26, 0xf9, 0x90, 0xc1, 0xc4, 0xef, 0x34, 0x03, 0xba, 0xd6, 0xd1, 0x66, + 0xb9, 0x92, 0xc6, 0xf0, 0xe4, 0x24, 0xcb, 0x67, 0x05, 0xf7, 0x49, 0xbd, 0xd4, 0xc7, 0x51, 0xbe, + 0xe8, 0x16, 0x14, 0xfc, 0xc0, 0xf2, 0x02, 0x52, 0x2f, 0x05, 0x6c, 0x57, 0x1b, 0x7f, 0xfc, 0x3d, + 0x83, 0x2d, 0x02, 0x1b, 0x76, 0x8b, 0xf0, 0x05, 0xa7, 0x2a, 0x09, 0xe0, 0x90, 0x16, 0x7a, 0x05, + 0xc0, 0xeb, 0x38, 0xd5, 0x4e, 0xab, 0x65, 0x79, 0xfb, 0x62, 0x07, 0xbf, 0x3c, 0xdc, 0xe7, 0x61, + 0x45, 0x2f, 0xdc, 0xb3, 0xc2, 0x32, 0xac, 0xf1, 0x43, 0xaf, 0x19, 0x30, 0xc9, 0x47, 0xa2, 0x94, + 0x60, 0x34, 0x65, 0x09, 0x66, 0x69, 0xd3, 0xae, 0xe8, 0x2c, 0x70, 0x94, 0xa3, 0xf9, 0xc7, 0xd1, + 0xfd, 0xa4, 0x1a, 0x50, 0xed, 0xba, 0xb1, 0x8f, 0x3e, 0x02, 0xf7, 0xfb, 0x9d, 0x5a, 0x8d, 0xf8, + 0xfe, 0x56, 0xa7, 0x89, 0x3b, 0xce, 0x65, 0xdb, 0x0f, 0x5c, 0x6f, 0x7f, 0xcd, 0x6e, 0xd9, 0x01, + 0x1b, 0x71, 0xb9, 0xf2, 0xf9, 0x83, 0x6e, 0xf1, 0xfe, 0x6a, 0x3f, 0x24, 0xdc, 0xbf, 0x3e, 0xb2, + 0xe0, 0x81, 0x8e, 0xd3, 0x9f, 0x3c, 0xd7, 0xde, 0x8a, 0x07, 0xdd, 0xe2, 0x03, 0x37, 0xfa, 0xa3, + 0xe1, 0xc3, 0x68, 0x98, 0x7f, 0x61, 0xc0, 0x8c, 0xfc, 0xae, 0x0d, 0xd2, 0x6a, 0x37, 0xe9, 0xea, + 0x72, 0xfa, 0x8a, 0x48, 0x10, 0x51, 0x44, 0x70, 0x3a, 0xdb, 0x89, 0x94, 0xbf, 0x9f, 0x36, 0x62, + 0xfe, 0xb9, 0x01, 0x73, 0x71, 0xe4, 0x7b, 0xb0, 0x79, 0xfa, 0xd1, 0xcd, 0xf3, 0x6a, 0xba, 0x5f, + 0xdb, 0x67, 0x07, 0x7d, 0x63, 0xa4, 0xf7, 0x5b, 0xff, 0xa1, 0x6f, 0xa3, 0xe1, 0xae, 0x98, 0xfd, + 0x7d, 0xee, 0x8a, 0x23, 0x6f, 0xab, 0x5d, 0xf1, 0xbf, 0x8e, 0xc0, 0x44, 0xc9, 0x09, 0xec, 0xd2, + 0xd6, 0x96, 0xed, 0xd8, 0xc1, 0x3e, 0xfa, 0x62, 0x06, 0x96, 0xda, 0x1e, 0xd9, 0x22, 0x9e, 0x47, + 0xea, 0x2b, 0x1d, 0xcf, 0x76, 0x1a, 0xd5, 0xda, 0x36, 0xa9, 0x77, 0x9a, 0xb6, 0xd3, 0x58, 0x6d, + 0x38, 0xae, 0x2a, 0xbe, 0x78, 0x87, 0xd4, 0x3a, 0xec, 0x93, 0xf8, 0xa4, 0x68, 0x0d, 0xf7, 0x49, + 0x95, 0xe3, 0x31, 0x2d, 0x3f, 0x71, 0xd0, 0x2d, 0x2e, 0x1d, 0xb3, 0x12, 0x3e, 0xee, 0xa7, 0xa1, + 0xcf, 0x67, 0x60, 0xd1, 0x23, 0x9f, 0xec, 0xd8, 0x83, 0xb7, 0x06, 0x5f, 0xb5, 0x9a, 0x43, 0x6e, + 0x3f, 0xc7, 0xe2, 0x59, 0x7e, 0xfc, 0xa0, 0x5b, 0x3c, 0x66, 0x1d, 0x7c, 0xcc, 0xef, 0x32, 0x2b, + 0x30, 0x5e, 0x6a, 0xdb, 0xbe, 0x7d, 0x87, 0x9e, 0x65, 0xc9, 0x00, 0x67, 0xa5, 0x22, 0xe4, 0xbc, + 0x4e, 0x93, 0xf0, 0xb9, 0x5d, 0x28, 0x17, 0xe8, 0x2a, 0x84, 0x69, 0x01, 0xe6, 0xe5, 0xe6, 0x67, + 0xe9, 0x8a, 0xcb, 0x48, 0xc6, 0x4e, 0xc9, 0xb7, 0x21, 0xe7, 0x51, 0x26, 0x62, 0x64, 0x0d, 0x7b, + 0xa0, 0x08, 0xa5, 0x16, 0x42, 0xd0, 0x9f, 0x98, 0xb3, 0x30, 0xbf, 0x9b, 0x81, 0xb3, 0xa5, 0x76, + 0x7b, 0x9d, 0xf8, 0xdb, 0x31, 0x29, 0xbe, 0x6c, 0xc0, 0xd4, 0xae, 0xed, 0x05, 0x1d, 0xab, 0x29, + 0x6d, 0x1b, 0x5c, 0x9e, 0xea, 0xb0, 0xf2, 0x30, 0x6e, 0x37, 0x23, 0xa4, 0xcb, 0xe8, 0xa0, 0x5b, + 0x9c, 0x8a, 0x96, 0xe1, 0x18, 0x7b, 0xf4, 0xef, 0x0c, 0x98, 0x11, 0x45, 0x57, 0xdd, 0x3a, 0xd1, + 0x0d, 0x62, 0x37, 0xd2, 0x94, 0x49, 0x11, 0xe7, 0x96, 0x93, 0x78, 0x29, 0xee, 0x11, 0xc2, 0xfc, + 0xab, 0x0c, 0x9c, 0xeb, 0x43, 0x03, 0xfd, 0x17, 0x03, 0xe6, 0xb8, 0x15, 0x4d, 0x03, 0x61, 0xb2, + 0x25, 0x5a, 0xf3, 0xc3, 0x69, 0x4b, 0x8e, 0xe9, 0x14, 0x27, 0x4e, 0x8d, 0x94, 0xe7, 0xe9, 0x6a, + 0xb8, 0x9c, 0xc0, 0x1a, 0x27, 0x0a, 0xc4, 0x24, 0xe5, 0x76, 0xb5, 0x98, 0xa4, 0x99, 0x7b, 0x22, + 0x69, 0x35, 0x81, 0x35, 0x4e, 0x14, 0xc8, 0xfc, 0x17, 0xf0, 0xc0, 0x21, 0xe4, 0x8e, 0x9e, 0x9c, + 0xe6, 0x4b, 0x6a, 0xd4, 0x47, 0xc7, 0xdc, 0x00, 0xf3, 0xda, 0x84, 0x51, 0x36, 0x75, 0xe4, 0xc4, + 0x06, 0xba, 0xfd, 0xb1, 0x39, 0xe5, 0x63, 0x01, 0x31, 0xbf, 0x6b, 0x40, 0xfe, 0x18, 0x66, 0x95, + 0x62, 0xd4, 0xac, 0x52, 0xe8, 0x31, 0xa9, 0x04, 0xbd, 0x26, 0x95, 0xe7, 0x87, 0xeb, 0x8d, 0x41, + 0x4c, 0x29, 0xbf, 0x31, 0x60, 0xb6, 0xc7, 0xf4, 0x82, 0xb6, 0x61, 0xae, 0xed, 0xd6, 0xa5, 0xda, + 0x74, 0xd9, 0xf2, 0xb7, 0x19, 0x4c, 0x7c, 0xde, 0x93, 0xb4, 0x27, 0x2b, 0x09, 0xf0, 0xbb, 0xdd, + 0xe2, 0xbc, 0x22, 0x12, 0x43, 0xc0, 0x89, 0x14, 0x51, 0x1b, 0xf2, 0x5b, 0x36, 0x69, 0xd6, 0xc3, + 0x21, 0x38, 0xa4, 0x82, 0x74, 0x49, 0x50, 0xe3, 0x56, 0x47, 0xf9, 0x0f, 0x2b, 0x2e, 0xe6, 0x75, + 0x98, 0x8a, 0xda, 0xa0, 0x07, 0xe8, 0xbc, 0xf3, 0x90, 0xb5, 0x3c, 0x47, 0x74, 0xdd, 0xb8, 0x40, + 0xc8, 0x96, 0xf0, 0x55, 0x4c, 0xcb, 0xcd, 0xdf, 0x8d, 0xc0, 0x74, 0xb9, 0xd9, 0x21, 0xcf, 0x7b, + 0x84, 0xc8, 0x63, 0x77, 0x09, 0xa6, 0xdb, 0x1e, 0xd9, 0xb5, 0xc9, 0x5e, 0x95, 0x34, 0x49, 0x2d, + 0x70, 0x3d, 0x41, 0xff, 0x9c, 0xa8, 0x3e, 0x5d, 0x89, 0x82, 0x71, 0x1c, 0x1f, 0x3d, 0x07, 0x53, + 0x56, 0x2d, 0xb0, 0x77, 0x89, 0xa2, 0xc0, 0x05, 0x78, 0x87, 0xa0, 0x30, 0x55, 0x8a, 0x40, 0x71, + 0x0c, 0x1b, 0x7d, 0x14, 0xe6, 0xfd, 0x9a, 0xd5, 0x24, 0x37, 0xda, 0x82, 0xd5, 0xf2, 0x36, 0xa9, + 0xed, 0x54, 0x5c, 0xdb, 0x09, 0x84, 0x91, 0xe5, 0x21, 0x41, 0x69, 0xbe, 0xda, 0x07, 0x0f, 0xf7, + 0xa5, 0x80, 0xfe, 0xb7, 0x01, 0xe7, 0xdb, 0x1e, 0xa9, 0x78, 0x6e, 0xcb, 0xa5, 0xbb, 0x67, 0x8f, + 0xe5, 0x41, 0x9c, 0xc0, 0x6f, 0x0e, 0xa9, 0x26, 0xf0, 0x92, 0x5e, 0xcb, 0xe7, 0x3b, 0x0f, 0xba, + 0xc5, 0xf3, 0x95, 0xc3, 0x04, 0xc0, 0x87, 0xcb, 0x87, 0xfe, 0xaf, 0x01, 0x17, 0xda, 0xae, 0x1f, + 0x1c, 0xf2, 0x09, 0xb9, 0x53, 0xfd, 0x04, 0xf3, 0xa0, 0x5b, 0xbc, 0x50, 0x39, 0x54, 0x02, 0x7c, + 0x84, 0x84, 0xe6, 0xc1, 0x38, 0xcc, 0x6a, 0x63, 0x4f, 0x1c, 0xcb, 0x9f, 0x81, 0x49, 0x39, 0x18, + 0xc2, 0x6d, 0xbd, 0x10, 0x9a, 0x51, 0x4a, 0x3a, 0x10, 0x47, 0x71, 0xe9, 0xb8, 0x53, 0x43, 0x91, + 0xd7, 0x8e, 0x8d, 0xbb, 0x4a, 0x04, 0x8a, 0x63, 0xd8, 0x68, 0x15, 0xce, 0x88, 0x12, 0x4c, 0xda, + 0x4d, 0xbb, 0x66, 0x2d, 0xbb, 0x1d, 0x31, 0xe4, 0x72, 0xe5, 0x73, 0x07, 0xdd, 0xe2, 0x99, 0x4a, + 0x2f, 0x18, 0x27, 0xd5, 0x41, 0x6b, 0x30, 0x67, 0x75, 0x02, 0x57, 0x7d, 0xff, 0x45, 0x87, 0xee, + 0x14, 0x75, 0x36, 0xb4, 0xf2, 0x7c, 0x4b, 0x29, 0x25, 0xc0, 0x71, 0x62, 0x2d, 0x54, 0x89, 0x51, + 0xab, 0x92, 0x9a, 0xeb, 0xd4, 0x79, 0x2f, 0xe7, 0xc2, 0xc3, 0x45, 0x29, 0x01, 0x07, 0x27, 0xd6, + 0x44, 0x4d, 0x98, 0x6a, 0x59, 0x77, 0x6e, 0x38, 0xd6, 0xae, 0x65, 0x37, 0x29, 0x13, 0x61, 0x9a, + 0xe9, 0x6f, 0x2f, 0xe8, 0x04, 0x76, 0x73, 0x91, 0xdf, 0x52, 0x2e, 0xae, 0x3a, 0xc1, 0x35, 0xaf, + 0x1a, 0x50, 0x25, 0x94, 0x2b, 0x47, 0xeb, 0x11, 0x5a, 0x38, 0x46, 0x1b, 0x5d, 0x83, 0xb3, 0x6c, + 0x3a, 0xae, 0xb8, 0x7b, 0xce, 0x0a, 0x69, 0x5a, 0xfb, 0xf2, 0x03, 0xc6, 0xd8, 0x07, 0xdc, 0x7f, + 0xd0, 0x2d, 0x9e, 0xad, 0x26, 0x21, 0xe0, 0xe4, 0x7a, 0xc8, 0x82, 0x07, 0xa2, 0x00, 0x4c, 0x76, + 0x6d, 0xdf, 0x76, 0x1d, 0x6e, 0x60, 0xc9, 0x87, 0x06, 0x96, 0x6a, 0x7f, 0x34, 0x7c, 0x18, 0x0d, + 0xf4, 0x1f, 0x0c, 0x98, 0x4b, 0x9a, 0x86, 0xf3, 0x85, 0x34, 0xee, 0x60, 0x62, 0x53, 0x8b, 0x8f, + 0x88, 0xc4, 0x45, 0x21, 0x51, 0x08, 0xf4, 0xaa, 0x01, 0x13, 0x96, 0x76, 0x38, 0x9c, 0x07, 0x26, + 0xd5, 0x95, 0x61, 0x4d, 0x14, 0x21, 0xc5, 0xf2, 0xcc, 0x41, 0xb7, 0x18, 0x39, 0x80, 0xe2, 0x08, + 0x47, 0xf4, 0x9f, 0x0c, 0x38, 0x9b, 0x38, 0xc7, 0xe7, 0xc7, 0x4f, 0xa3, 0x85, 0xd8, 0x20, 0x49, + 0x5e, 0x73, 0x92, 0xc5, 0x40, 0x6f, 0x1a, 0x6a, 0x2b, 0x5b, 0x97, 0x46, 0xa2, 0x09, 0x26, 0xda, + 0xf5, 0x21, 0xcf, 0xc3, 0xa1, 0x42, 0x20, 0x09, 0x97, 0xcf, 0x68, 0x3b, 0xa3, 0x2c, 0xc4, 0x71, + 0xf6, 0xe8, 0x4b, 0x86, 0xdc, 0x1a, 0x95, 0x44, 0x93, 0xa7, 0x25, 0x11, 0x0a, 0x77, 0x5a, 0x25, + 0x50, 0x8c, 0x39, 0xfa, 0x18, 0x2c, 0x58, 0x9b, 0xae, 0x17, 0x24, 0x4e, 0xbe, 0xf9, 0x29, 0x36, + 0x8d, 0x2e, 0x1c, 0x74, 0x8b, 0x0b, 0xa5, 0xbe, 0x58, 0xf8, 0x10, 0x0a, 0xe6, 0xb7, 0x72, 0x30, + 0xc1, 0x95, 0x7c, 0xb1, 0x75, 0x7d, 0xc7, 0x80, 0x07, 0x6b, 0x1d, 0xcf, 0x23, 0x4e, 0x50, 0x0d, + 0x48, 0xbb, 0x77, 0xe3, 0x32, 0x4e, 0x75, 0xe3, 0x7a, 0xe8, 0xa0, 0x5b, 0x7c, 0x70, 0xf9, 0x10, + 0xfe, 0xf8, 0x50, 0xe9, 0xd0, 0x1f, 0x19, 0x60, 0x0a, 0x84, 0xb2, 0x55, 0xdb, 0x69, 0x78, 0x6e, + 0xc7, 0xa9, 0xf7, 0x7e, 0x44, 0xe6, 0x54, 0x3f, 0xe2, 0x91, 0x83, 0x6e, 0xd1, 0x5c, 0x3e, 0x52, + 0x0a, 0x3c, 0x80, 0xa4, 0xe8, 0x79, 0x98, 0x15, 0x58, 0x17, 0xef, 0xb4, 0x89, 0x67, 0x53, 0x75, + 0x5a, 0xb8, 0x09, 0x84, 0x9e, 0x17, 0x71, 0x04, 0xdc, 0x5b, 0x07, 0xf9, 0x30, 0xb6, 0x47, 0xec, + 0xc6, 0x76, 0x20, 0xd5, 0xa7, 0x21, 0xdd, 0x2d, 0xc4, 0x81, 0xff, 0x16, 0xa7, 0x59, 0x1e, 0x3f, + 0xe8, 0x16, 0xc7, 0xc4, 0x1f, 0x2c, 0x39, 0xa1, 0xab, 0x30, 0xc5, 0x8f, 0x60, 0x15, 0xdb, 0x69, + 0x54, 0x5c, 0x87, 0x3b, 0x29, 0x14, 0xca, 0x8f, 0xc8, 0x0d, 0xbf, 0x1a, 0x81, 0xde, 0xed, 0x16, + 0x27, 0xe4, 0xef, 0x8d, 0xfd, 0x36, 0xc1, 0xb1, 0xda, 0xe6, 0x0f, 0x46, 0x01, 0xe4, 0x70, 0x25, + 0x6d, 0xf4, 0x4f, 0xa1, 0xe0, 0x93, 0x80, 0x73, 0x15, 0x77, 0x02, 0xfc, 0xaa, 0x45, 0x16, 0xe2, + 0x10, 0x8e, 0x76, 0x20, 0xd7, 0xb6, 0x3a, 0x3e, 0x11, 0x9d, 0x7f, 0x25, 0x95, 0xce, 0xaf, 0x50, + 0x8a, 0xfc, 0xcc, 0xc5, 0x7e, 0x62, 0xce, 0x03, 0x7d, 0xce, 0x00, 0x20, 0xd1, 0x0e, 0x1b, 0xda, + 0xf6, 0x21, 0x58, 0x86, 0x7d, 0x4a, 0xdb, 0xa0, 0x3c, 0x75, 0xd0, 0x2d, 0x82, 0xd6, 0xf5, 0x1a, + 0x5b, 0xb4, 0x07, 0x79, 0x4b, 0xae, 0xf9, 0x23, 0xa7, 0xb1, 0xe6, 0xb3, 0xa3, 0x90, 0x1a, 0xb4, + 0x8a, 0x19, 0xfa, 0xbc, 0x01, 0x53, 0x3e, 0x09, 0x44, 0x57, 0xd1, 0x95, 0x47, 0x28, 0xbc, 0x43, + 0x0e, 0xba, 0x6a, 0x84, 0x26, 0x5f, 0x41, 0xa3, 0x65, 0x38, 0xc6, 0x57, 0x8a, 0x72, 0x99, 0x58, + 0x75, 0xe2, 0xb1, 0x93, 0xb6, 0xd0, 0xa4, 0x86, 0x17, 0x45, 0xa3, 0xa9, 0x44, 0xd1, 0xca, 0x70, + 0x8c, 0xaf, 0x14, 0x65, 0xdd, 0xf6, 0x3c, 0x57, 0x88, 0x92, 0x4f, 0x49, 0x14, 0x8d, 0xa6, 0x12, + 0x45, 0x2b, 0xc3, 0x31, 0xbe, 0xe6, 0xd7, 0x27, 0x61, 0x4a, 0x4e, 0xa4, 0x50, 0xb3, 0xe7, 0x86, + 0x9d, 0x3e, 0x9a, 0xfd, 0xb2, 0x0e, 0xc4, 0x51, 0x5c, 0x5a, 0x99, 0x4f, 0xd5, 0xa8, 0x62, 0xaf, + 0x2a, 0x57, 0x75, 0x20, 0x8e, 0xe2, 0xa2, 0x16, 0xe4, 0xfc, 0x80, 0xb4, 0xe5, 0xf5, 0xee, 0x90, + 0xb7, 0x8f, 0xe1, 0xfa, 0x10, 0x5e, 0xe0, 0xd0, 0x7f, 0x3e, 0xe6, 0x5c, 0x98, 0x6d, 0x32, 0x88, + 0x98, 0x2b, 0xc5, 0xe4, 0x48, 0x67, 0x7e, 0x46, 0x2d, 0xa1, 0xbc, 0x37, 0xa2, 0x65, 0x38, 0xc6, + 0x3e, 0x41, 0xd9, 0xcf, 0x9d, 0xa2, 0xb2, 0xff, 0x22, 0xe4, 0x5b, 0xd6, 0x9d, 0x6a, 0xc7, 0x6b, + 0x9c, 0xfc, 0x50, 0x21, 0x3c, 0xaf, 0x38, 0x15, 0xac, 0xe8, 0xa1, 0xd7, 0x0c, 0x6d, 0xc9, 0x19, + 0x63, 0xc4, 0x6f, 0xa5, 0xbb, 0xe4, 0xa8, 0xbd, 0xb2, 0xef, 0xe2, 0xd3, 0xa3, 0x7a, 0xe7, 0xef, + 0xb9, 0xea, 0x4d, 0xd5, 0x48, 0x3e, 0x41, 0x94, 0x1a, 0x59, 0x38, 0x55, 0x35, 0x72, 0x39, 0xc2, + 0x0c, 0xc7, 0x98, 0x33, 0x79, 0xf8, 0x9c, 0x53, 0xf2, 0xc0, 0xa9, 0xca, 0x53, 0x8d, 0x30, 0xc3, + 0x31, 0xe6, 0xfd, 0xcf, 0x9b, 0xe3, 0xa7, 0x73, 0xde, 0x9c, 0x48, 0xe1, 0xbc, 0x79, 0xb8, 0x2a, + 0x3e, 0x39, 0xac, 0x2a, 0x8e, 0xae, 0x00, 0xaa, 0xef, 0x3b, 0x56, 0xcb, 0xae, 0x89, 0xc5, 0x92, + 0x6d, 0x9b, 0x53, 0xcc, 0x1e, 0xb1, 0x20, 0x16, 0x32, 0xb4, 0xd2, 0x83, 0x81, 0x13, 0x6a, 0xa1, + 0x00, 0xf2, 0x6d, 0xa9, 0x71, 0x4d, 0xa7, 0x31, 0xfa, 0xa5, 0x06, 0xc6, 0x3d, 0x00, 0xe8, 0xc4, + 0x93, 0x25, 0x58, 0x71, 0x42, 0x6b, 0x30, 0xd7, 0xb2, 0x9d, 0x8a, 0x5b, 0xf7, 0x2b, 0xc4, 0x13, + 0xd6, 0x96, 0x2a, 0x09, 0xe6, 0x67, 0x58, 0xdb, 0xb0, 0x13, 0xf4, 0x7a, 0x02, 0x1c, 0x27, 0xd6, + 0x32, 0xff, 0xc6, 0x80, 0x99, 0xe5, 0xa6, 0xdb, 0xa9, 0xdf, 0xb2, 0x82, 0xda, 0x36, 0xbf, 0xfc, + 0x46, 0xcf, 0x41, 0xde, 0x76, 0x02, 0xe2, 0xed, 0x5a, 0x4d, 0xb1, 0x3f, 0x99, 0xd2, 0x3f, 0x60, + 0x55, 0x94, 0xdf, 0xed, 0x16, 0xa7, 0x56, 0x3a, 0x1e, 0xf3, 0x2a, 0xe5, 0xab, 0x15, 0x56, 0x75, + 0xd0, 0xd7, 0x0d, 0x98, 0xe5, 0xd7, 0xe7, 0x2b, 0x56, 0x60, 0x5d, 0xef, 0x10, 0xcf, 0x26, 0xf2, + 0x02, 0x7d, 0xc8, 0x85, 0x2a, 0x2e, 0xab, 0x64, 0xb0, 0x1f, 0x2a, 0xea, 0xeb, 0x71, 0xce, 0xb8, + 0x57, 0x18, 0xf3, 0x2b, 0x59, 0xb8, 0xbf, 0x2f, 0x2d, 0xb4, 0x00, 0x19, 0xbb, 0x2e, 0x3e, 0x1d, + 0x04, 0xdd, 0xcc, 0x6a, 0x1d, 0x67, 0xec, 0x3a, 0x5a, 0x64, 0x3a, 0xa7, 0x47, 0x7c, 0x5f, 0xde, + 0xa5, 0x16, 0x94, 0x7a, 0x28, 0x4a, 0xb1, 0x86, 0x81, 0x8a, 0x90, 0x6b, 0x5a, 0x9b, 0xa4, 0x29, + 0xce, 0x13, 0x4c, 0x8b, 0x5d, 0xa3, 0x05, 0x98, 0x97, 0xa3, 0xcf, 0x1a, 0x00, 0x5c, 0x40, 0x7a, + 0x1a, 0x11, 0xbb, 0x24, 0x4e, 0xb7, 0x99, 0x28, 0x65, 0x2e, 0x65, 0xf8, 0x1f, 0x6b, 0x5c, 0xd1, + 0x06, 0x8c, 0x52, 0x85, 0xd6, 0xad, 0x9f, 0x78, 0x53, 0x64, 0x97, 0x2c, 0x15, 0x46, 0x03, 0x0b, + 0x5a, 0xb4, 0xad, 0x3c, 0x12, 0x74, 0x3c, 0x87, 0x36, 0x2d, 0xdb, 0x06, 0xf3, 0x5c, 0x0a, 0xac, + 0x4a, 0xb1, 0x86, 0x61, 0xfe, 0xaf, 0x0c, 0xcc, 0x25, 0x89, 0x4e, 0x77, 0x9b, 0x51, 0x2e, 0xad, + 0x38, 0x1a, 0x7f, 0x28, 0xfd, 0xf6, 0x11, 0x9e, 0x20, 0xca, 0x5f, 0x42, 0xf8, 0xaa, 0x09, 0xbe, + 0xe8, 0x43, 0xaa, 0x85, 0x32, 0x27, 0x6c, 0x21, 0x45, 0x39, 0xd6, 0x4a, 0x0f, 0xc1, 0x88, 0x4f, + 0x7b, 0x3e, 0x1b, 0xbd, 0xc0, 0x60, 0x7d, 0xc4, 0x20, 0x14, 0xa3, 0xe3, 0xd8, 0x81, 0x70, 0xf5, + 0x56, 0x18, 0x37, 0x1c, 0x3b, 0xc0, 0x0c, 0x62, 0x7e, 0x2d, 0x03, 0x0b, 0xfd, 0x3f, 0x0a, 0x7d, + 0xcd, 0x00, 0xa8, 0xd3, 0xe3, 0x0a, 0x1d, 0x92, 0xd2, 0x73, 0xc6, 0x3a, 0xad, 0x36, 0x5c, 0x91, + 0x9c, 0x42, 0x37, 0x2a, 0x55, 0xe4, 0x63, 0x4d, 0x10, 0xf4, 0xb8, 0x1c, 0xfa, 0x57, 0xad, 0x96, + 0x54, 0x67, 0x55, 0x9d, 0x75, 0x05, 0xc1, 0x1a, 0x16, 0x3d, 0x8f, 0x3a, 0x56, 0x8b, 0xf8, 0x6d, + 0x4b, 0xf9, 0xf2, 0xb3, 0xf3, 0xe8, 0x55, 0x59, 0x88, 0x43, 0xb8, 0xd9, 0x84, 0x87, 0x07, 0x90, + 0x33, 0x25, 0xbf, 0x6a, 0xf3, 0xaf, 0x0d, 0x38, 0xb7, 0xdc, 0xec, 0xf8, 0x01, 0xf1, 0xfe, 0xd1, + 0x78, 0xa5, 0xfd, 0xad, 0x01, 0x0f, 0xf4, 0xf9, 0xe6, 0x7b, 0xe0, 0x9c, 0xf6, 0x72, 0xd4, 0x39, + 0xed, 0xc6, 0xb0, 0x43, 0x3a, 0xf1, 0x3b, 0xfa, 0xf8, 0xa8, 0x05, 0x30, 0x49, 0x57, 0xad, 0xba, + 0xdb, 0x48, 0x69, 0xdf, 0x7c, 0x18, 0x72, 0x9f, 0xa4, 0xfb, 0x4f, 0x7c, 0x8c, 0xb1, 0x4d, 0x09, + 0x73, 0x98, 0xf9, 0x2c, 0x08, 0x4f, 0xae, 0xd8, 0xe4, 0x31, 0x06, 0x99, 0x3c, 0xe6, 0x9f, 0x64, + 0x40, 0xb3, 0x63, 0xdc, 0x83, 0x41, 0xe9, 0x44, 0x06, 0xe5, 0x90, 0x67, 0x70, 0xcd, 0x2a, 0xd3, + 0x2f, 0x64, 0x63, 0x37, 0x16, 0xb2, 0x71, 0x35, 0x35, 0x8e, 0x87, 0x47, 0x6c, 0xfc, 0xd4, 0x80, + 0x07, 0x42, 0xe4, 0x5e, 0x13, 0xe3, 0xd1, 0x2b, 0xcc, 0x53, 0x30, 0x6e, 0x85, 0xd5, 0xc4, 0x18, + 0x50, 0x51, 0x4a, 0x1a, 0x45, 0xac, 0xe3, 0x85, 0x0e, 0xe2, 0xd9, 0x13, 0x3a, 0x88, 0x8f, 0x1c, + 0xee, 0x20, 0x6e, 0xfe, 0x36, 0x03, 0xe7, 0x7b, 0xbf, 0x4c, 0xce, 0x8d, 0xc1, 0x6e, 0xe0, 0x9f, + 0x86, 0x89, 0x40, 0x54, 0xd0, 0x56, 0x7a, 0x15, 0x63, 0xb7, 0xa1, 0xc1, 0x70, 0x04, 0x93, 0xd6, + 0xac, 0xf1, 0x59, 0x59, 0xad, 0xb9, 0x6d, 0x19, 0x5e, 0xa0, 0x6a, 0x2e, 0x6b, 0x30, 0x1c, 0xc1, + 0x54, 0x8e, 0x9b, 0x23, 0xa7, 0xee, 0xb8, 0x59, 0x85, 0xb3, 0xd2, 0x55, 0xed, 0x92, 0xeb, 0x2d, + 0xbb, 0xad, 0x76, 0x93, 0x88, 0x00, 0x03, 0x2a, 0xec, 0x79, 0x51, 0xe5, 0x2c, 0x4e, 0x42, 0xc2, + 0xc9, 0x75, 0xcd, 0x9f, 0x66, 0xe1, 0x4c, 0xd8, 0xec, 0xcb, 0xae, 0x53, 0xb7, 0x99, 0xc3, 0xdf, + 0x33, 0x30, 0x12, 0xec, 0xb7, 0x65, 0x63, 0xff, 0x13, 0x29, 0xce, 0xc6, 0x7e, 0x9b, 0xf6, 0xf6, + 0xb9, 0x84, 0x2a, 0xcc, 0xc8, 0xcb, 0x2a, 0xa1, 0x35, 0x35, 0x3b, 0x78, 0x0f, 0x3c, 0x19, 0x1d, + 0xcd, 0x77, 0xbb, 0xc5, 0x84, 0x10, 0xd3, 0x45, 0x45, 0x29, 0x3a, 0xe6, 0xd1, 0x6d, 0x98, 0x6a, + 0x5a, 0x7e, 0x70, 0xa3, 0x5d, 0xb7, 0x02, 0xb2, 0x61, 0xb7, 0x88, 0x98, 0x73, 0xc7, 0xf1, 0xda, + 0x57, 0xb7, 0xd2, 0x6b, 0x11, 0x4a, 0x38, 0x46, 0x19, 0xed, 0x02, 0xa2, 0x25, 0x1b, 0x9e, 0xe5, + 0xf8, 0xfc, 0xab, 0x28, 0xbf, 0xe3, 0x47, 0x09, 0xa8, 0x43, 0xde, 0x5a, 0x0f, 0x35, 0x9c, 0xc0, + 0x01, 0x3d, 0x02, 0xa3, 0x1e, 0xb1, 0x7c, 0xd1, 0x99, 0x85, 0x70, 0xfe, 0x63, 0x56, 0x8a, 0x05, + 0x54, 0x9f, 0x50, 0xa3, 0x47, 0x4c, 0xa8, 0x5f, 0x1a, 0x30, 0x15, 0x76, 0xd3, 0x3d, 0xd8, 0x24, + 0x5b, 0xd1, 0x4d, 0xf2, 0x72, 0x5a, 0x4b, 0x62, 0x9f, 0x7d, 0xf1, 0xff, 0x8d, 0xea, 0xdf, 0xc7, + 0xbc, 0xb6, 0x3f, 0x05, 0x05, 0x39, 0xab, 0xa5, 0xf6, 0x39, 0xe4, 0x59, 0x39, 0xa2, 0x97, 0x68, + 0xd1, 0x46, 0x82, 0x09, 0x0e, 0xf9, 0xd1, 0x6d, 0xb9, 0x2e, 0xb6, 0x5c, 0x31, 0xec, 0xd5, 0xb6, + 0x2c, 0xb7, 0xe2, 0xa4, 0x6d, 0x59, 0xd6, 0x41, 0x37, 0xe0, 0x5c, 0xdb, 0x73, 0x59, 0x04, 0xea, + 0x0a, 0xb1, 0xea, 0x4d, 0xdb, 0x21, 0xd2, 0x20, 0xc1, 0x9d, 0x22, 0x1e, 0x38, 0xe8, 0x16, 0xcf, + 0x55, 0x92, 0x51, 0x70, 0xbf, 0xba, 0xd1, 0xa8, 0xa9, 0x91, 0x01, 0xa2, 0xa6, 0xfe, 0xb5, 0x32, + 0xfb, 0x11, 0x5f, 0xc4, 0x2e, 0x7d, 0x24, 0xad, 0xae, 0x4c, 0x58, 0xd6, 0xc3, 0x21, 0x55, 0x12, + 0x4c, 0xb1, 0x62, 0xdf, 0xdf, 0xb6, 0x34, 0x7a, 0x42, 0xdb, 0x52, 0xe8, 0xfc, 0x3e, 0xf6, 0xfb, + 0x74, 0x7e, 0xcf, 0xbf, 0xad, 0x9c, 0xdf, 0x5f, 0xcf, 0xc1, 0x4c, 0x5c, 0x03, 0x39, 0xfd, 0x88, + 0xb0, 0x7f, 0x6b, 0xc0, 0x8c, 0x9c, 0x3d, 0x9c, 0x27, 0x91, 0xb7, 0x06, 0x6b, 0x29, 0x4d, 0x5a, + 0xae, 0x4b, 0xa9, 0x98, 0xe5, 0x8d, 0x18, 0x37, 0xdc, 0xc3, 0x1f, 0xbd, 0x04, 0xe3, 0xca, 0xb8, + 0x7e, 0xa2, 0xf0, 0xb0, 0x69, 0xa6, 0x45, 0x85, 0x24, 0xb0, 0x4e, 0x0f, 0xbd, 0x6e, 0x00, 0xd4, + 0xe4, 0x36, 0x27, 0x67, 0xd7, 0xf5, 0xb4, 0x66, 0x97, 0xda, 0x40, 0x43, 0x65, 0x59, 0x15, 0xf9, + 0x58, 0x63, 0x8c, 0xbe, 0xc2, 0xcc, 0xea, 0x4a, 0xbb, 0xa3, 0xf3, 0x29, 0x3b, 0xbc, 0x63, 0xef, + 0x21, 0x8a, 0x69, 0xa8, 0x4a, 0x69, 0x20, 0x1f, 0x47, 0x84, 0x30, 0x9f, 0x01, 0xe5, 0x8a, 0x49, + 0x97, 0x2d, 0xe6, 0x8c, 0x59, 0xb1, 0x82, 0x6d, 0x31, 0x04, 0xd5, 0xb2, 0x75, 0x49, 0x02, 0x70, + 0x88, 0x63, 0x7e, 0x02, 0xa6, 0x9e, 0xf7, 0xac, 0xf6, 0xb6, 0xcd, 0xcc, 0xd7, 0xf4, 0x9c, 0xf4, + 0x6e, 0x18, 0xb3, 0xea, 0xf5, 0xa4, 0x88, 0xff, 0x12, 0x2f, 0xc6, 0x12, 0x3e, 0xd8, 0x91, 0xe8, + 0x07, 0x06, 0xa0, 0xf0, 0x0a, 0xd0, 0x76, 0x1a, 0xeb, 0xf4, 0xb4, 0x4f, 0xcf, 0x47, 0xdb, 0xac, + 0x34, 0xe9, 0x7c, 0x74, 0x59, 0x41, 0xb0, 0x86, 0x85, 0x5e, 0x81, 0x71, 0xfe, 0xef, 0xa6, 0x3a, + 0xec, 0x0f, 0xed, 0xde, 0xcf, 0x37, 0x14, 0x26, 0x13, 0x1f, 0x85, 0x97, 0x43, 0x0e, 0x58, 0x67, + 0x47, 0x9b, 0x6a, 0xd5, 0xd9, 0x6a, 0x76, 0xee, 0xd4, 0x37, 0xc3, 0xa6, 0x6a, 0x7b, 0xee, 0x96, + 0xdd, 0x24, 0xf1, 0xa6, 0xaa, 0xf0, 0x62, 0x2c, 0xe1, 0x83, 0x35, 0xd5, 0xf7, 0x0c, 0x98, 0x5b, + 0xf5, 0x03, 0xdb, 0x5d, 0x21, 0x7e, 0x40, 0xb7, 0x15, 0xba, 0xf8, 0x74, 0x9a, 0x83, 0x78, 0x55, + 0xaf, 0xc0, 0x8c, 0xb8, 0x8e, 0xec, 0x6c, 0xfa, 0x24, 0xd0, 0xf4, 0x78, 0x35, 0x8f, 0x97, 0x63, + 0x70, 0xdc, 0x53, 0x83, 0x52, 0x11, 0xf7, 0x92, 0x21, 0x95, 0x6c, 0x94, 0x4a, 0x35, 0x06, 0xc7, + 0x3d, 0x35, 0xcc, 0x1f, 0x67, 0xe1, 0x0c, 0xfb, 0x8c, 0x58, 0x44, 0xc4, 0x97, 0xfa, 0x45, 0x44, + 0x0c, 0x39, 0x95, 0x19, 0xaf, 0x13, 0xc4, 0x43, 0xfc, 0x1b, 0x03, 0xa6, 0xeb, 0xd1, 0x96, 0x4e, + 0xc7, 0x3c, 0x93, 0xd4, 0x87, 0xdc, 0xfb, 0x2a, 0x56, 0x88, 0xe3, 0xfc, 0xd1, 0x57, 0x0d, 0x98, + 0x8e, 0x8a, 0x29, 0x57, 0xf7, 0x53, 0x68, 0x24, 0xe5, 0x2e, 0x1d, 0x2d, 0xf7, 0x71, 0x5c, 0x04, + 0xf3, 0x47, 0x19, 0xd1, 0xa5, 0xa7, 0xe1, 0xee, 0x8f, 0xf6, 0xa0, 0x10, 0x34, 0x7d, 0x5e, 0x28, + 0xbe, 0x76, 0xc8, 0x13, 0xe1, 0xc6, 0x5a, 0x95, 0x7b, 0x02, 0x84, 0x4a, 0x9b, 0x28, 0xa1, 0xca, + 0xa7, 0xe4, 0xc5, 0x18, 0xd7, 0xda, 0x82, 0x71, 0x2a, 0x47, 0xd1, 0x8d, 0xe5, 0x4a, 0x9c, 0xb1, + 0x28, 0xa1, 0x8c, 0x25, 0x2f, 0xf3, 0x9b, 0x06, 0x14, 0xae, 0xb8, 0x72, 0x1d, 0xf9, 0x58, 0x0a, + 0x86, 0x1e, 0xa5, 0x0f, 0xaa, 0x1b, 0xc7, 0xf0, 0x88, 0xf1, 0x5c, 0xc4, 0xcc, 0xf3, 0xa0, 0x46, + 0x7b, 0x91, 0x65, 0x33, 0xa2, 0xa4, 0xae, 0xb8, 0x9b, 0x7d, 0xad, 0x88, 0xff, 0x39, 0x07, 0x93, + 0x2f, 0x58, 0xfb, 0xc4, 0x09, 0xac, 0xe3, 0x6f, 0x12, 0x4f, 0xc1, 0xb8, 0xd5, 0x66, 0x57, 0x5a, + 0x9a, 0x8e, 0x1f, 0x5a, 0x4e, 0x42, 0x10, 0xd6, 0xf1, 0xc2, 0x05, 0x8d, 0x27, 0x57, 0x49, 0x5a, + 0x8a, 0x96, 0x63, 0x70, 0xdc, 0x53, 0x03, 0x5d, 0x01, 0x24, 0x42, 0x45, 0x4b, 0xb5, 0x9a, 0xdb, + 0x71, 0xf8, 0x92, 0xc6, 0x8d, 0x2a, 0xea, 0xb0, 0xb9, 0xde, 0x83, 0x81, 0x13, 0x6a, 0xa1, 0x8f, + 0xc2, 0x7c, 0x8d, 0x51, 0x16, 0x47, 0x0f, 0x9d, 0x22, 0x3f, 0x7e, 0x2a, 0x97, 0xff, 0xe5, 0x3e, + 0x78, 0xb8, 0x2f, 0x05, 0x2a, 0xa9, 0x1f, 0xb8, 0x9e, 0xd5, 0x20, 0x3a, 0xdd, 0xd1, 0xa8, 0xa4, + 0xd5, 0x1e, 0x0c, 0x9c, 0x50, 0x0b, 0x7d, 0x06, 0x0a, 0xc1, 0xb6, 0x47, 0xfc, 0x6d, 0xb7, 0x59, + 0x17, 0x2e, 0x08, 0x43, 0x5a, 0xda, 0x44, 0xef, 0x6f, 0x48, 0xaa, 0xda, 0xf0, 0x96, 0x45, 0x38, + 0xe4, 0x89, 0x3c, 0x18, 0xf5, 0x6b, 0x6e, 0x9b, 0xf8, 0x42, 0x65, 0xbf, 0x92, 0x0a, 0x77, 0x66, + 0x39, 0xd2, 0x6c, 0x7c, 0x8c, 0x03, 0x16, 0x9c, 0xcc, 0xef, 0x67, 0x60, 0x42, 0x47, 0x1c, 0x60, + 0x6d, 0xfa, 0x9c, 0x01, 0x13, 0x35, 0xd7, 0x09, 0x3c, 0xb7, 0xc9, 0xed, 0x57, 0xe9, 0x68, 0x14, + 0x94, 0xd4, 0x0a, 0x09, 0x2c, 0xbb, 0xa9, 0x99, 0xc2, 0x34, 0x36, 0x38, 0xc2, 0x14, 0x7d, 0xd1, + 0x80, 0xe9, 0xd0, 0x63, 0x2d, 0x34, 0xa4, 0xa5, 0x2a, 0x88, 0x5a, 0xea, 0x2f, 0x46, 0x39, 0xe1, + 0x38, 0x6b, 0x73, 0x13, 0x66, 0xe2, 0xbd, 0x4d, 0x9b, 0xb2, 0x6d, 0x89, 0xb9, 0x9e, 0x0d, 0x9b, + 0xb2, 0x62, 0xf9, 0x3e, 0x66, 0x10, 0xf4, 0x18, 0xe4, 0x5b, 0x96, 0xd7, 0xb0, 0x1d, 0xab, 0xc9, + 0x5a, 0x31, 0xab, 0x2d, 0x48, 0xa2, 0x1c, 0x2b, 0x0c, 0xf3, 0x7d, 0x30, 0xb1, 0x6e, 0x39, 0x0d, + 0x52, 0x17, 0xeb, 0xf0, 0xd1, 0x01, 0x67, 0xbf, 0x1e, 0x81, 0x71, 0xed, 0x6c, 0x76, 0xfa, 0xe7, + 0xac, 0x48, 0xbe, 0x8b, 0x6c, 0x8a, 0xf9, 0x2e, 0x5e, 0x04, 0xd8, 0xb2, 0x1d, 0xdb, 0xdf, 0x3e, + 0x61, 0x26, 0x0d, 0x76, 0x45, 0x7b, 0x49, 0x51, 0xc0, 0x1a, 0xb5, 0xf0, 0x1e, 0x2c, 0x77, 0x48, + 0x7e, 0xa1, 0xd7, 0x0d, 0x6d, 0xbb, 0x19, 0x4d, 0xe3, 0xde, 0x5f, 0xeb, 0x98, 0x45, 0xb9, 0xfd, + 0x5c, 0x74, 0x02, 0x6f, 0xff, 0xd0, 0x5d, 0x69, 0x03, 0xf2, 0x1e, 0xf1, 0x3b, 0x2d, 0x7a, 0x62, + 0x1c, 0x3b, 0x76, 0x33, 0x30, 0x0f, 0x0c, 0x2c, 0xea, 0x63, 0x45, 0x69, 0xe1, 0x19, 0x98, 0x8c, + 0x88, 0x80, 0x66, 0x20, 0xbb, 0x43, 0xf6, 0xf9, 0x38, 0xc1, 0xf4, 0x27, 0x9a, 0x8b, 0xdc, 0x16, + 0x8a, 0x66, 0xf9, 0x60, 0xe6, 0x69, 0xc3, 0x74, 0x21, 0xd1, 0x00, 0x70, 0x92, 0xcb, 0x1c, 0xda, + 0x17, 0x4d, 0x2d, 0x95, 0x86, 0xea, 0x0b, 0xee, 0x67, 0xc3, 0x61, 0xe6, 0x6f, 0x47, 0x41, 0x5c, + 0x65, 0x0f, 0xb0, 0x5c, 0xe9, 0x37, 0x58, 0x99, 0x13, 0xdc, 0x60, 0x5d, 0x81, 0x09, 0xdb, 0xb1, + 0x03, 0xdb, 0x6a, 0x32, 0xe3, 0x8e, 0xd8, 0x4e, 0xa5, 0x23, 0xf2, 0xc4, 0xaa, 0x06, 0x4b, 0xa0, + 0x13, 0xa9, 0x8b, 0xae, 0x43, 0x8e, 0xed, 0x37, 0x62, 0x00, 0x1f, 0xff, 0xbe, 0x9d, 0xb9, 0x5a, + 0xf0, 0xe8, 0x24, 0x4e, 0x89, 0x1d, 0x3e, 0x78, 0x2e, 0x11, 0x75, 0xfc, 0x16, 0xe3, 0x38, 0x3c, + 0x7c, 0xc4, 0xe0, 0xb8, 0xa7, 0x06, 0xa5, 0xb2, 0x65, 0xd9, 0xcd, 0x8e, 0x47, 0x42, 0x2a, 0xa3, + 0x51, 0x2a, 0x97, 0x62, 0x70, 0xdc, 0x53, 0x03, 0x6d, 0xc1, 0x84, 0x28, 0xe3, 0xde, 0x53, 0x63, + 0x27, 0xfc, 0x4a, 0xe6, 0x25, 0x77, 0x49, 0xa3, 0x84, 0x23, 0x74, 0x51, 0x07, 0x66, 0x6d, 0xa7, + 0xe6, 0x3a, 0xb5, 0x66, 0xc7, 0xb7, 0x77, 0x49, 0x18, 0x1a, 0x74, 0x12, 0x66, 0x67, 0x0f, 0xba, + 0xc5, 0xd9, 0xd5, 0x38, 0x39, 0xdc, 0xcb, 0x01, 0xbd, 0x66, 0xc0, 0xd9, 0x9a, 0xeb, 0xf8, 0x2c, + 0x38, 0x7f, 0x97, 0x5c, 0xf4, 0x3c, 0xd7, 0xe3, 0xbc, 0x0b, 0x27, 0xe4, 0xcd, 0x6c, 0x8a, 0xcb, + 0x49, 0x24, 0x71, 0x32, 0x27, 0xf4, 0x32, 0xe4, 0xdb, 0x9e, 0xbb, 0x6b, 0xd7, 0x89, 0x27, 0x3c, + 0xf1, 0xd6, 0xd2, 0x48, 0x16, 0x52, 0x11, 0x34, 0xc3, 0xa5, 0x47, 0x96, 0x60, 0xc5, 0xcf, 0xfc, + 0x3b, 0x80, 0xa9, 0x28, 0x3a, 0xfa, 0x34, 0x40, 0xdb, 0x73, 0x5b, 0x24, 0xd8, 0x26, 0x2a, 0xc4, + 0xe3, 0xea, 0xb0, 0x39, 0x29, 0x24, 0x3d, 0xe9, 0xbd, 0x42, 0x97, 0x8b, 0xb0, 0x14, 0x6b, 0x1c, + 0x91, 0x07, 0x63, 0x3b, 0x7c, 0xdb, 0x15, 0x5a, 0xc8, 0x0b, 0xa9, 0xe8, 0x4c, 0x82, 0x33, 0x8b, + 0x4d, 0x10, 0x45, 0x58, 0x32, 0x42, 0x9b, 0x90, 0xdd, 0x23, 0x9b, 0xe9, 0x04, 0x44, 0xdf, 0x22, + 0xe2, 0x34, 0x53, 0x1e, 0x3b, 0xe8, 0x16, 0xb3, 0xb7, 0xc8, 0x26, 0xa6, 0xc4, 0xe9, 0x77, 0xd5, + 0xf9, 0x3d, 0xbc, 0x58, 0x2a, 0x86, 0xfc, 0xae, 0xc8, 0xa5, 0x3e, 0xff, 0x2e, 0x51, 0x84, 0x25, + 0x23, 0xf4, 0x32, 0x14, 0xf6, 0xac, 0x5d, 0xb2, 0xe5, 0xb9, 0x4e, 0x20, 0x5c, 0xa6, 0x86, 0xf4, + 0xfa, 0xbf, 0x25, 0xc9, 0x09, 0xbe, 0x6c, 0x7b, 0x57, 0x85, 0x38, 0x64, 0x87, 0x76, 0x21, 0xef, + 0x90, 0x3d, 0x4c, 0x9a, 0x76, 0x2d, 0x1d, 0x2f, 0xfb, 0xab, 0x82, 0x9a, 0xe0, 0xcc, 0xf6, 0x3d, + 0x59, 0x86, 0x15, 0x2f, 0xda, 0x97, 0xb7, 0xdd, 0x4d, 0xb1, 0x50, 0x0d, 0xd9, 0x97, 0xea, 0x64, + 0xca, 0xfb, 0xf2, 0x8a, 0xbb, 0x89, 0x29, 0x71, 0x3a, 0x47, 0x6a, 0xca, 0x5f, 0x47, 0x2c, 0x53, + 0x57, 0xd3, 0xf5, 0x53, 0xe2, 0x73, 0x24, 0x2c, 0xc5, 0x1a, 0x47, 0xda, 0xb6, 0x0d, 0x61, 0xac, + 0x14, 0x0b, 0xd5, 0x90, 0x6d, 0x1b, 0x35, 0x7d, 0xf2, 0xb6, 0x95, 0x65, 0x58, 0xf1, 0xa2, 0x7c, + 0x6d, 0x61, 0xf9, 0x4b, 0x67, 0xa9, 0x8a, 0xda, 0x11, 0x39, 0x5f, 0x59, 0x86, 0x15, 0x2f, 0xf4, + 0x1f, 0x0d, 0x18, 0x6d, 0x37, 0x3b, 0x0d, 0xdb, 0x99, 0x1f, 0x67, 0x7a, 0xda, 0x87, 0xd2, 0x5c, + 0x21, 0x17, 0x2b, 0x8c, 0x34, 0x57, 0xd4, 0xde, 0xa3, 0xdc, 0xdf, 0x58, 0xe1, 0x17, 0x7e, 0x55, + 0x9c, 0x27, 0x4e, 0xcd, 0xad, 0xdb, 0x4e, 0x63, 0xe9, 0xb6, 0xef, 0x3a, 0x8b, 0xd8, 0xda, 0x93, + 0x3a, 0xb2, 0x90, 0x69, 0xe1, 0x03, 0x30, 0xae, 0x91, 0x38, 0x4a, 0xd1, 0x9a, 0xd0, 0x15, 0xad, + 0x6f, 0x8e, 0xc2, 0x84, 0x9e, 0x6e, 0x6e, 0x00, 0xed, 0x47, 0x69, 0xfc, 0x99, 0xe3, 0x68, 0xfc, + 0xf4, 0x88, 0xa7, 0xdd, 0xde, 0x48, 0xf3, 0xd2, 0x6a, 0x6a, 0x0a, 0x6f, 0x78, 0xc4, 0xd3, 0x0a, + 0x7d, 0x1c, 0x61, 0x7a, 0x0c, 0x87, 0x0e, 0xaa, 0x36, 0x72, 0xc5, 0x2a, 0x17, 0x55, 0x1b, 0x23, + 0xaa, 0xd2, 0xe3, 0x00, 0x61, 0xda, 0x35, 0x71, 0xab, 0xa7, 0xf4, 0x51, 0x2d, 0x1d, 0x9c, 0x86, + 0x85, 0x1e, 0x81, 0x51, 0xaa, 0x7a, 0x90, 0xba, 0x88, 0x68, 0x56, 0xe7, 0xe8, 0x4b, 0xac, 0x14, + 0x0b, 0x28, 0x7a, 0x9a, 0x6a, 0x89, 0xa1, 0xc2, 0x20, 0x02, 0x95, 0xe7, 0x42, 0x2d, 0x31, 0x84, + 0xe1, 0x08, 0x26, 0x15, 0x9d, 0xd0, 0xfd, 0x9d, 0xcd, 0x4d, 0x4d, 0x74, 0xb6, 0xe9, 0x63, 0x0e, + 0x63, 0x76, 0x9d, 0x98, 0x3e, 0xc0, 0xe6, 0x54, 0x4e, 0xb3, 0xeb, 0xc4, 0xe0, 0xb8, 0xa7, 0x06, + 0xfd, 0x18, 0x71, 0x21, 0x39, 0xce, 0xfd, 0x56, 0xfb, 0x5c, 0x25, 0xbe, 0xa1, 0x9f, 0x75, 0x26, + 0xd2, 0x9b, 0x43, 0x7c, 0xd4, 0x0e, 0x7e, 0xd8, 0x19, 0xee, 0x58, 0xf2, 0x09, 0x98, 0x8a, 0xee, + 0x02, 0xa9, 0xdf, 0x3c, 0xfc, 0xff, 0x2c, 0x9c, 0xb9, 0xda, 0xb0, 0x9d, 0x78, 0x2a, 0xa5, 0xa4, + 0x94, 0xc6, 0xc6, 0x71, 0x53, 0x1a, 0x87, 0xa1, 0x51, 0x22, 0x67, 0x74, 0x72, 0x68, 0x94, 0x4c, + 0x28, 0x1d, 0xc5, 0x45, 0xbf, 0x34, 0xe0, 0x41, 0xab, 0xce, 0xf5, 0x72, 0xab, 0x29, 0x4a, 0x43, + 0xa6, 0x72, 0x46, 0xfb, 0x43, 0xee, 0xb2, 0xbd, 0x1f, 0xbf, 0x58, 0x3a, 0x84, 0x2b, 0xef, 0xf1, + 0x77, 0x89, 0x2f, 0x78, 0xf0, 0x30, 0x54, 0x7c, 0xa8, 0xf8, 0x0b, 0xd7, 0xe0, 0x9d, 0x47, 0x32, + 0x3a, 0xd6, 0x68, 0xf9, 0x9c, 0x01, 0x05, 0x6e, 0x18, 0xc6, 0x64, 0x8b, 0x2e, 0x15, 0x56, 0xdb, + 0xbe, 0x49, 0x3c, 0x5f, 0xe6, 0x5a, 0xd3, 0x8e, 0xae, 0xa5, 0xca, 0xaa, 0x80, 0x60, 0x0d, 0x8b, + 0x2e, 0xc6, 0x3b, 0xb6, 0x53, 0x17, 0xdd, 0xa4, 0x16, 0xe3, 0x17, 0x6c, 0xa7, 0x8e, 0x19, 0x44, + 0x2d, 0xd7, 0xd9, 0xbe, 0x06, 0x9b, 0x6f, 0x18, 0x30, 0xc5, 0xe2, 0x41, 0xc3, 0x43, 0xd5, 0x53, + 0xca, 0x5b, 0x87, 0x8b, 0x71, 0x3e, 0xea, 0xad, 0x73, 0xb7, 0x5b, 0x1c, 0xe7, 0x11, 0xa4, 0x51, + 0xe7, 0x9d, 0x8f, 0x08, 0x4b, 0x0c, 0xf3, 0x29, 0xca, 0x1c, 0xdb, 0x50, 0xa0, 0x2c, 0x95, 0x55, + 0x49, 0x04, 0x87, 0xf4, 0xcc, 0x57, 0x60, 0x42, 0x0f, 0xec, 0x40, 0x4f, 0xc1, 0x78, 0xdb, 0x76, + 0x1a, 0xd1, 0x00, 0x40, 0x65, 0xad, 0xae, 0x84, 0x20, 0xac, 0xe3, 0xb1, 0x6a, 0x6e, 0x58, 0x2d, + 0x66, 0xe4, 0xae, 0xb8, 0x7a, 0xb5, 0xf0, 0x8f, 0xf9, 0x3f, 0xb2, 0x70, 0x26, 0x21, 0x80, 0x08, + 0xbd, 0x6e, 0xc0, 0x28, 0x8b, 0x3f, 0x90, 0xfe, 0x38, 0x2f, 0xa5, 0x1e, 0xa4, 0xb4, 0xc8, 0xc2, + 0x1c, 0xc4, 0x38, 0x56, 0xcb, 0x27, 0x2f, 0xc4, 0x82, 0x39, 0xfa, 0xf7, 0x06, 0x8c, 0x5b, 0xda, + 0x54, 0xe3, 0x2e, 0x4a, 0x9b, 0xe9, 0x0b, 0xd3, 0x33, 0xb3, 0x34, 0xd7, 0xca, 0x70, 0x22, 0xe9, + 0xb2, 0x50, 0xed, 0x43, 0xfb, 0x84, 0xe3, 0xcc, 0x90, 0x85, 0xe7, 0x60, 0x66, 0xa8, 0x19, 0xf6, + 0x61, 0x38, 0x6e, 0xea, 0x40, 0xba, 0x61, 0xed, 0xe9, 0x41, 0xda, 0xaa, 0xc5, 0x45, 0x94, 0xb6, + 0x80, 0x9a, 0x9b, 0x30, 0x13, 0x3f, 0x36, 0xa6, 0x7e, 0x23, 0xff, 0x3e, 0x38, 0x66, 0xb2, 0x3f, + 0xf3, 0x22, 0x20, 0xec, 0x36, 0x9b, 0x9b, 0x56, 0x6d, 0xe7, 0x96, 0xed, 0xd4, 0xdd, 0x3d, 0x36, + 0x57, 0x96, 0xa0, 0xe0, 0x89, 0xf8, 0x30, 0x5f, 0x7c, 0x96, 0x9a, 0x6c, 0x32, 0x70, 0xcc, 0xc7, + 0x21, 0x8e, 0xf9, 0xa3, 0x0c, 0x8c, 0x89, 0x60, 0xc6, 0x7b, 0xe0, 0xdc, 0xbc, 0x13, 0xb9, 0xf5, + 0x5a, 0x4d, 0x25, 0x06, 0xb3, 0xaf, 0x67, 0xb3, 0x1f, 0xf3, 0x6c, 0x7e, 0x21, 0x1d, 0x76, 0x87, + 0xbb, 0x35, 0x7f, 0x63, 0x04, 0xa6, 0x63, 0xc1, 0xa1, 0x54, 0xe3, 0xe9, 0xf1, 0xe6, 0xbb, 0x91, + 0x6a, 0xfc, 0xa9, 0x72, 0xbc, 0x3f, 0xdc, 0xb1, 0xcf, 0x8f, 0xa4, 0x66, 0xbd, 0x9e, 0x5a, 0x56, + 0xf7, 0x3f, 0x64, 0x69, 0x3d, 0xae, 0xa3, 0xda, 0x9f, 0x19, 0x70, 0x7f, 0xdf, 0x18, 0x62, 0x96, + 0x82, 0xc6, 0x8b, 0x42, 0xc5, 0x84, 0x4c, 0x39, 0x53, 0x82, 0xba, 0x82, 0x8a, 0x67, 0x0d, 0x89, + 0xb3, 0x47, 0x4f, 0xc2, 0x04, 0xdb, 0xa1, 0xe9, 0xd2, 0x14, 0x90, 0xb6, 0xb0, 0xa0, 0x33, 0x5b, + 0x6a, 0x55, 0x2b, 0xc7, 0x11, 0x2c, 0xf3, 0xeb, 0x06, 0xcc, 0xf7, 0x4b, 0x48, 0x32, 0xc0, 0xf9, + 0xf2, 0x9f, 0xc7, 0xbc, 0xaf, 0x8b, 0x3d, 0xde, 0xd7, 0xb1, 0x13, 0xa6, 0x74, 0xb4, 0xd6, 0x0e, + 0x77, 0xd9, 0x23, 0x9c, 0x8b, 0xbf, 0x64, 0xc0, 0xb9, 0x3e, 0xb3, 0xa9, 0xc7, 0x0b, 0xdf, 0x38, + 0xb1, 0x17, 0x7e, 0x66, 0x50, 0x2f, 0x7c, 0xf3, 0x27, 0x59, 0x98, 0x11, 0xf2, 0x84, 0x6a, 0xda, + 0xd3, 0x11, 0x1f, 0xf6, 0x77, 0xc5, 0x7c, 0xd8, 0xe7, 0xe2, 0xf8, 0x7f, 0x70, 0x60, 0x7f, 0x7b, + 0x39, 0xb0, 0xff, 0x2e, 0x03, 0x67, 0x13, 0xf3, 0xa4, 0xa0, 0xcf, 0x27, 0x6c, 0x0d, 0xb7, 0x52, + 0x4e, 0xc8, 0x32, 0xe0, 0xe6, 0x30, 0xac, 0xd7, 0xf7, 0x57, 0x75, 0x6f, 0x6b, 0xbe, 0xd4, 0x6f, + 0x9d, 0x42, 0x6a, 0x99, 0x63, 0x3a, 0x5e, 0x9b, 0x5f, 0xc8, 0xc2, 0xa3, 0x83, 0x12, 0x7a, 0x9b, + 0x06, 0xe6, 0xf8, 0x91, 0xc0, 0x9c, 0x7b, 0xb4, 0x6d, 0x9f, 0x4a, 0x8c, 0xce, 0x37, 0xb3, 0x6a, + 0xdb, 0xeb, 0x1d, 0x9f, 0x03, 0x5d, 0xb7, 0x8e, 0x51, 0xd5, 0x4e, 0x66, 0x4f, 0x0d, 0x97, 0xc2, + 0xb1, 0x2a, 0x2f, 0xbe, 0xdb, 0x2d, 0xce, 0x86, 0xd1, 0xfa, 0xa2, 0x10, 0xcb, 0x4a, 0xe8, 0x51, + 0xc8, 0x7b, 0x1c, 0x2a, 0x43, 0x11, 0xc4, 0x9d, 0x35, 0x2f, 0xc3, 0x0a, 0x8a, 0x3e, 0xa3, 0xe9, + 0xc2, 0x23, 0xa7, 0x95, 0x94, 0xe2, 0xb0, 0xab, 0xf8, 0x97, 0x20, 0xef, 0xcb, 0x3c, 0xa8, 0xfc, + 0xbe, 0xe4, 0x89, 0x01, 0x23, 0x5c, 0xe8, 0x09, 0x4c, 0x26, 0x45, 0xe5, 0xdf, 0xa7, 0x52, 0xa6, + 0x2a, 0x92, 0xc8, 0x54, 0x87, 0x1f, 0x6e, 0xaa, 0x84, 0x84, 0x83, 0xcf, 0x4f, 0x0d, 0x18, 0x17, + 0xbd, 0x75, 0x0f, 0x82, 0x6e, 0x6e, 0x47, 0x83, 0x6e, 0x2e, 0xa6, 0xb2, 0x76, 0xf4, 0x89, 0xb8, + 0xb9, 0x0d, 0x13, 0x7a, 0xaa, 0x2c, 0xf4, 0xa2, 0xb6, 0xf6, 0x19, 0xc3, 0x24, 0x9f, 0x91, 0xab, + 0x63, 0xb8, 0x2e, 0x9a, 0xdf, 0x2a, 0xa8, 0x56, 0x64, 0x47, 0x34, 0x7d, 0x0c, 0x1a, 0x87, 0x8e, + 0x41, 0x7d, 0x08, 0x64, 0xd2, 0x1f, 0x02, 0xd7, 0x21, 0x2f, 0x17, 0x28, 0xb1, 0x8d, 0x3f, 0xac, + 0xbb, 0x21, 0x52, 0x5d, 0x80, 0x12, 0xd3, 0x06, 0x2e, 0x3b, 0x6a, 0xa9, 0x3e, 0x54, 0x0b, 0xa7, + 0x22, 0x83, 0x5e, 0x86, 0xf1, 0x3d, 0xd7, 0xdb, 0x69, 0xba, 0x16, 0xcb, 0x70, 0x0c, 0x69, 0xdc, + 0x7c, 0x29, 0xbb, 0x19, 0xf7, 0x05, 0xbf, 0x15, 0xd2, 0xc7, 0x3a, 0x33, 0x54, 0x82, 0xe9, 0x96, + 0xed, 0x60, 0x62, 0xd5, 0x55, 0x6c, 0xcd, 0x08, 0x4f, 0xc1, 0x2a, 0x95, 0xdc, 0xf5, 0x28, 0x18, + 0xc7, 0xf1, 0xd1, 0x17, 0x0d, 0x98, 0xf2, 0x22, 0x87, 0x6a, 0x91, 0x67, 0xb1, 0x32, 0xfc, 0x60, + 0x8c, 0x1e, 0xd4, 0xb9, 0x33, 0x74, 0xb4, 0x1c, 0xc7, 0x78, 0xa3, 0x4f, 0x41, 0xde, 0x17, 0x79, + 0xb0, 0xd2, 0xb9, 0x32, 0x55, 0x47, 0x58, 0x4e, 0x34, 0xec, 0x4a, 0x59, 0x82, 0x15, 0x43, 0xb4, + 0x06, 0x73, 0xd2, 0x4a, 0x10, 0x79, 0x85, 0x66, 0x34, 0x4c, 0x9b, 0x82, 0x13, 0xe0, 0x38, 0xb1, + 0x16, 0x55, 0xaa, 0x58, 0x0a, 0x3a, 0x7e, 0xd3, 0xa1, 0x5d, 0x0e, 0xb0, 0xf9, 0x57, 0xc7, 0x02, + 0x7a, 0x58, 0xe8, 0x58, 0x7e, 0x88, 0xd0, 0xb1, 0x2a, 0x9c, 0x8d, 0x83, 0x58, 0x3e, 0x1c, 0x96, + 0x82, 0x47, 0xdb, 0xcc, 0x2a, 0x49, 0x48, 0x38, 0xb9, 0x2e, 0xba, 0x05, 0x05, 0x8f, 0xb0, 0xe3, + 0x4e, 0x49, 0x3a, 0x69, 0x1c, 0xdb, 0x1d, 0x0d, 0x4b, 0x02, 0x38, 0xa4, 0x45, 0xfb, 0xdd, 0x8a, + 0x26, 0x45, 0xbd, 0x9e, 0xe2, 0x3b, 0x7a, 0xa2, 0xef, 0xfb, 0xe4, 0xa9, 0x32, 0xdf, 0x9a, 0x82, + 0xc9, 0x88, 0xa9, 0x03, 0x3d, 0x0c, 0x39, 0x96, 0x20, 0x88, 0xad, 0x56, 0xf9, 0x70, 0x45, 0xe5, + 0x8d, 0xc3, 0x61, 0xe8, 0xcb, 0x06, 0x4c, 0xb7, 0x23, 0xb6, 0x65, 0xb9, 0x90, 0x0f, 0x79, 0x2f, + 0x1b, 0x35, 0x58, 0x6b, 0xe9, 0xc4, 0xa3, 0xcc, 0x70, 0x9c, 0x3b, 0x5d, 0x0f, 0x84, 0x4f, 0x67, + 0x93, 0x78, 0x0c, 0x5b, 0xa8, 0x5c, 0x8a, 0xc4, 0x72, 0x14, 0x8c, 0xe3, 0xf8, 0xb4, 0x87, 0xd9, + 0xd7, 0x0d, 0xf3, 0xc0, 0x56, 0x49, 0x12, 0xc0, 0x21, 0x2d, 0xf4, 0x1c, 0x4c, 0x89, 0x5c, 0x98, + 0x15, 0xb7, 0x7e, 0xd9, 0xf2, 0xb7, 0xc5, 0x59, 0x43, 0x9d, 0x8d, 0x96, 0x23, 0x50, 0x1c, 0xc3, + 0x66, 0xdf, 0x16, 0x26, 0x1c, 0x65, 0x04, 0x46, 0xa3, 0xd9, 0xd6, 0x97, 0xa3, 0x60, 0x1c, 0xc7, + 0x47, 0x8f, 0x69, 0xdb, 0x10, 0xbf, 0x7d, 0x54, 0xab, 0x41, 0xc2, 0x56, 0x54, 0x82, 0xe9, 0x0e, + 0x3b, 0x9a, 0xd5, 0x25, 0x50, 0xcc, 0x47, 0xc5, 0xf0, 0x46, 0x14, 0x8c, 0xe3, 0xf8, 0xe8, 0x19, + 0x98, 0xf4, 0xe8, 0x62, 0xab, 0x08, 0xf0, 0x2b, 0x49, 0x75, 0xe3, 0x84, 0x75, 0x20, 0x8e, 0xe2, + 0xa2, 0xe7, 0x61, 0x36, 0x4c, 0x1d, 0x27, 0x09, 0xf0, 0x3b, 0x4a, 0x95, 0xc7, 0xa8, 0x14, 0x47, + 0xc0, 0xbd, 0x75, 0xd0, 0xbf, 0x84, 0x19, 0xad, 0x25, 0x56, 0x9d, 0x3a, 0xb9, 0x23, 0xd2, 0x7b, + 0xb1, 0x87, 0x31, 0x96, 0x63, 0x30, 0xdc, 0x83, 0x8d, 0x3e, 0x08, 0x53, 0x35, 0xb7, 0xd9, 0x64, + 0x6b, 0x1c, 0xcf, 0xf4, 0xcd, 0xf3, 0x78, 0xf1, 0x8c, 0x67, 0x11, 0x08, 0x8e, 0x61, 0xa2, 0x2b, + 0x80, 0xdc, 0x4d, 0x9f, 0x78, 0xbb, 0xa4, 0xfe, 0x3c, 0x7f, 0xb2, 0x97, 0x6a, 0x1c, 0x93, 0x51, + 0x8f, 0xf2, 0x6b, 0x3d, 0x18, 0x38, 0xa1, 0x16, 0x4b, 0x83, 0xa4, 0x45, 0xe0, 0x4d, 0xa5, 0xf1, + 0xd8, 0x54, 0xdc, 0x90, 0x70, 0x64, 0xf8, 0x9d, 0x07, 0xa3, 0xdc, 0xc1, 0x3f, 0x9d, 0x84, 0x5e, + 0x7a, 0xd2, 0xdf, 0x70, 0x8f, 0xe0, 0xa5, 0x58, 0x70, 0x42, 0x9f, 0x86, 0xc2, 0xa6, 0xcc, 0x00, + 0xcf, 0xb2, 0x78, 0x0d, 0xbd, 0x2f, 0xc6, 0x1e, 0x33, 0x08, 0x0f, 0xca, 0x0a, 0x80, 0x43, 0x96, + 0xe8, 0x11, 0x18, 0xbf, 0x5c, 0x29, 0xa9, 0x51, 0x38, 0xcb, 0x7a, 0x7f, 0x84, 0x56, 0xc1, 0x3a, + 0x80, 0xce, 0x30, 0xa5, 0xbe, 0x21, 0xd6, 0xc5, 0xe1, 0x7e, 0xdb, 0xab, 0x8d, 0x51, 0x6c, 0x76, + 0xc9, 0x8a, 0xab, 0xf3, 0x67, 0x62, 0xd8, 0xa2, 0x1c, 0x2b, 0x0c, 0xf4, 0x12, 0x8c, 0x8b, 0xfd, + 0x82, 0xad, 0x4d, 0x73, 0x27, 0x8b, 0xee, 0xc4, 0x21, 0x09, 0xac, 0xd3, 0x63, 0x77, 0x67, 0x2c, + 0x31, 0x36, 0xb9, 0xd4, 0x69, 0x36, 0xe7, 0xcf, 0xb2, 0x75, 0x33, 0xbc, 0x3b, 0x0b, 0x41, 0x58, + 0xc7, 0x43, 0x4f, 0x48, 0x7f, 0x90, 0x77, 0x44, 0x2e, 0x13, 0x95, 0x3f, 0x88, 0x52, 0xba, 0xfb, + 0x38, 0x80, 0x9f, 0x3b, 0xc2, 0x11, 0x63, 0x13, 0x16, 0xa4, 0xc6, 0xd7, 0x3b, 0x49, 0xe6, 0xe7, + 0x23, 0x46, 0x8b, 0x85, 0x5b, 0x7d, 0x31, 0xf1, 0x21, 0x54, 0xd0, 0x26, 0x64, 0xad, 0xe6, 0xe6, + 0xfc, 0xfd, 0x69, 0xa8, 0xae, 0xea, 0x09, 0x6e, 0xee, 0xb4, 0x55, 0x5a, 0x2b, 0x63, 0x4a, 0xdc, + 0x7c, 0x2d, 0xa3, 0x2e, 0x09, 0x54, 0xa2, 0xd3, 0x57, 0xf4, 0x51, 0x6d, 0xa4, 0xf1, 0xc4, 0x6c, + 0xcf, 0x33, 0x09, 0x7c, 0x43, 0x4a, 0x1c, 0xd3, 0x6d, 0x35, 0x8f, 0x53, 0xc9, 0x3b, 0x13, 0x4d, + 0xe2, 0xca, 0x0f, 0x97, 0xd1, 0x59, 0x6c, 0xfe, 0x22, 0xaf, 0x6c, 0x62, 0x31, 0x07, 0x07, 0x0f, + 0x72, 0xb6, 0x1f, 0xd8, 0x6e, 0x8a, 0x91, 0x88, 0xb1, 0xec, 0xa7, 0xcc, 0xd1, 0x99, 0x01, 0x30, + 0x67, 0x45, 0x79, 0x3a, 0x0d, 0xdb, 0xb9, 0x23, 0x3e, 0xff, 0x7a, 0xea, 0x9e, 0x0b, 0x9c, 0x27, + 0x03, 0x60, 0xce, 0x0a, 0xdd, 0xe6, 0x23, 0x2d, 0x9d, 0xe7, 0x84, 0xe3, 0xaf, 0x84, 0x47, 0x47, + 0x1c, 0xe5, 0xe5, 0xb7, 0x6c, 0xa1, 0xc3, 0x0c, 0xc9, 0xab, 0xba, 0xbe, 0x9a, 0xc4, 0xab, 0xba, + 0xbe, 0x8a, 0x29, 0x13, 0xf4, 0x86, 0x01, 0x60, 0xa9, 0xe7, 0xb2, 0xd3, 0x79, 0x53, 0xa4, 0xdf, + 0xf3, 0xdb, 0xdc, 0x37, 0x31, 0x84, 0x62, 0x8d, 0x33, 0x7a, 0x19, 0xc6, 0x2c, 0xfe, 0x22, 0x92, + 0x70, 0xfb, 0x4c, 0xe7, 0x99, 0xaf, 0x98, 0x04, 0xcc, 0xdf, 0x55, 0x80, 0xb0, 0x64, 0x48, 0x79, + 0x07, 0x9e, 0x45, 0xb6, 0xec, 0x1d, 0xe1, 0xff, 0x59, 0x1d, 0x3a, 0xb1, 0x39, 0x25, 0x96, 0xc4, + 0x5b, 0x80, 0xb0, 0x64, 0xc8, 0x5f, 0xa8, 0xb5, 0x1c, 0x4b, 0x05, 0xf3, 0xa4, 0x13, 0xf2, 0xa5, + 0x87, 0x07, 0x69, 0x2f, 0xd4, 0xea, 0x8c, 0x70, 0x94, 0x2f, 0xda, 0x85, 0x51, 0x8b, 0xbd, 0xd5, + 0x26, 0xce, 0x47, 0x38, 0x8d, 0x77, 0xdf, 0x62, 0x6d, 0xc0, 0x16, 0x17, 0xf1, 0x22, 0x9c, 0xe0, + 0x66, 0xfe, 0x26, 0x0b, 0xc0, 0x44, 0xe0, 0x81, 0xed, 0x2d, 0x96, 0x0c, 0x71, 0xdb, 0xad, 0xa7, + 0xf3, 0xfc, 0x9c, 0x1e, 0x9f, 0x0e, 0x22, 0xf3, 0xe1, 0xb6, 0x5b, 0xc7, 0x82, 0x09, 0x6a, 0xc0, + 0x48, 0xdb, 0x0a, 0xb6, 0xd3, 0x0f, 0x86, 0xcf, 0xf3, 0x08, 0xaf, 0x60, 0x1b, 0x33, 0x06, 0xe8, + 0x55, 0x03, 0xc6, 0x78, 0x38, 0xbc, 0xb4, 0xb8, 0x0f, 0x7d, 0xad, 0x2c, 0xdb, 0x6c, 0x91, 0xc7, + 0xdc, 0x0b, 0xd7, 0x0f, 0xb5, 0x25, 0x8b, 0x52, 0x2c, 0xd9, 0x2e, 0xbc, 0x6e, 0xc0, 0x84, 0x8e, + 0x9a, 0xe0, 0xb4, 0xf1, 0x71, 0xdd, 0x69, 0x23, 0xcd, 0xf6, 0xd0, 0xfd, 0x3f, 0xfe, 0xd2, 0x00, + 0xed, 0x3d, 0xe1, 0xd0, 0x65, 0xd3, 0x18, 0xd8, 0x65, 0x33, 0x73, 0x4c, 0x97, 0xcd, 0xec, 0xb1, + 0x5c, 0x36, 0x47, 0x8e, 0xef, 0xb2, 0x99, 0xeb, 0xef, 0xb2, 0x69, 0xbe, 0x69, 0xc0, 0x6c, 0xcf, + 0x3a, 0x4c, 0xd5, 0x36, 0xcf, 0x75, 0x83, 0x3e, 0x9e, 0x52, 0x38, 0x04, 0x61, 0x1d, 0x0f, 0xad, + 0xc0, 0x8c, 0x48, 0xfd, 0x5d, 0x6d, 0x37, 0xed, 0xc4, 0x44, 0x05, 0x1b, 0x31, 0x38, 0xee, 0xa9, + 0x61, 0xfe, 0x1f, 0x03, 0xc6, 0xb5, 0xf0, 0x46, 0xfa, 0x1d, 0x2c, 0x0c, 0x54, 0x88, 0x11, 0x66, + 0x3d, 0x67, 0x37, 0x1c, 0x1c, 0xc6, 0x2f, 0xdb, 0x1a, 0x5a, 0x62, 0xd8, 0xf0, 0xb2, 0x8d, 0x96, + 0x62, 0x01, 0xe5, 0x29, 0x3f, 0x49, 0x9b, 0x35, 0x7a, 0x56, 0x4f, 0xf9, 0x49, 0xda, 0x98, 0x41, + 0x18, 0x3b, 0xaa, 0xbf, 0x0a, 0x6f, 0x5e, 0x2d, 0xc9, 0xba, 0xe5, 0x05, 0x98, 0xc3, 0xd0, 0x79, + 0xc8, 0x12, 0xa7, 0x2e, 0x0e, 0xdb, 0xea, 0x61, 0xb3, 0x8b, 0x4e, 0x1d, 0xd3, 0x72, 0xf3, 0x1a, + 0x4c, 0x54, 0x49, 0xcd, 0x23, 0xc1, 0x0b, 0x64, 0x7f, 0xe0, 0x97, 0xd2, 0xe8, 0x68, 0x8f, 0xbd, + 0x94, 0x46, 0xab, 0xd3, 0x72, 0xf3, 0xbf, 0x1b, 0x10, 0x7b, 0x09, 0x40, 0x33, 0xbc, 0x1b, 0xfd, + 0x0c, 0xef, 0x11, 0x13, 0x71, 0xe6, 0x50, 0x13, 0xf1, 0x15, 0x40, 0x2d, 0x3a, 0x15, 0x22, 0xef, + 0x5e, 0x08, 0x3b, 0x47, 0x18, 0x4c, 0xdd, 0x83, 0x81, 0x13, 0x6a, 0x99, 0xff, 0x8d, 0x0b, 0xab, + 0xbf, 0x0d, 0x70, 0x74, 0x03, 0x74, 0x20, 0xc7, 0x48, 0x09, 0x63, 0xcf, 0x90, 0x86, 0xd2, 0xde, + 0xa4, 0x24, 0x61, 0x47, 0x8a, 0x29, 0xcf, 0xb8, 0x99, 0x3f, 0xe1, 0xb2, 0x6a, 0x8f, 0x07, 0x0c, + 0x20, 0x6b, 0x2b, 0x2a, 0xeb, 0xe5, 0xb4, 0xd6, 0xca, 0x64, 0x19, 0xd1, 0x22, 0x40, 0x9b, 0x78, + 0x35, 0xe2, 0x04, 0xd2, 0xc9, 0x3c, 0x27, 0xc2, 0x8d, 0x54, 0x29, 0xd6, 0x30, 0xcc, 0x57, 0x0d, + 0x98, 0xa9, 0x06, 0x76, 0x6d, 0xc7, 0x76, 0x78, 0xf8, 0xdc, 0x96, 0xdd, 0xa0, 0xa7, 0x23, 0x22, + 0x1e, 0x01, 0xe3, 0xe6, 0x37, 0xb5, 0x14, 0xcb, 0xb7, 0xbf, 0x24, 0x1c, 0x95, 0x60, 0x5a, 0x5e, + 0x3a, 0x48, 0x9b, 0x29, 0x0f, 0xfb, 0x55, 0x36, 0x9a, 0x95, 0x28, 0x18, 0xc7, 0xf1, 0xcd, 0xcf, + 0xc0, 0xb8, 0xb6, 0xbe, 0xb2, 0xa5, 0xe8, 0x8e, 0x55, 0x0b, 0xe2, 0x53, 0xf8, 0x22, 0x2d, 0xc4, + 0x1c, 0xc6, 0x4c, 0xbb, 0xdc, 0x0b, 0x39, 0x36, 0x85, 0x85, 0xef, 0xb1, 0x80, 0x52, 0x62, 0x1e, + 0x69, 0x90, 0x3b, 0x32, 0x05, 0xad, 0x24, 0x86, 0x69, 0x21, 0xe6, 0x30, 0xf3, 0x31, 0xc8, 0xcb, + 0xe4, 0x0c, 0x2c, 0xc2, 0x59, 0x9a, 0x1d, 0xf5, 0x08, 0x67, 0xd7, 0x0b, 0x30, 0x83, 0x98, 0x37, + 0x21, 0x2f, 0x73, 0x48, 0x1c, 0x8d, 0x4d, 0x67, 0x95, 0xef, 0xd8, 0x97, 0x5d, 0x3f, 0x90, 0x89, + 0x2f, 0xf8, 0xcd, 0xc8, 0xd5, 0x55, 0x56, 0x86, 0x15, 0xd4, 0x9c, 0x85, 0x69, 0x75, 0xe5, 0x21, + 0xdc, 0x42, 0xbf, 0x9f, 0x85, 0x89, 0xc8, 0xbb, 0xda, 0x47, 0x0f, 0xb7, 0xc1, 0x67, 0x71, 0xc2, + 0xd5, 0x45, 0xf6, 0x98, 0x57, 0x17, 0xfa, 0x5d, 0xd1, 0xc8, 0xe9, 0xde, 0x15, 0xe5, 0xd2, 0xb9, + 0x2b, 0x0a, 0x60, 0xcc, 0x17, 0x1b, 0xd5, 0x68, 0x1a, 0x46, 0x9c, 0x58, 0x8f, 0x71, 0xdd, 0x58, + 0xee, 0x77, 0x92, 0x95, 0xf9, 0x9d, 0x1c, 0x4c, 0x45, 0xb3, 0x67, 0x0d, 0xd0, 0x93, 0x8f, 0xf5, + 0xf4, 0xe4, 0x31, 0x6d, 0xa5, 0xd9, 0x61, 0x6d, 0xa5, 0x23, 0xc3, 0xda, 0x4a, 0x73, 0x27, 0xb0, + 0x95, 0xf6, 0x5a, 0x3a, 0x47, 0x07, 0xb6, 0x74, 0x3e, 0xab, 0xfc, 0x8e, 0xc6, 0x22, 0x17, 0xf5, + 0xa1, 0xdf, 0x11, 0x8a, 0x76, 0xc3, 0xb2, 0x5b, 0x4f, 0xf4, 0xdf, 0xca, 0x1f, 0x61, 0x13, 0xf2, + 0x12, 0xdd, 0x84, 0x8e, 0x7f, 0x1d, 0xf3, 0x8e, 0x63, 0xb8, 0x08, 0x3d, 0x05, 0xe3, 0x62, 0x3c, + 0x31, 0x5d, 0x09, 0xa2, 0x7a, 0x56, 0x35, 0x04, 0x61, 0x1d, 0x8f, 0xbd, 0x91, 0x1a, 0x7d, 0x14, + 0x96, 0x99, 0x9e, 0xf5, 0x37, 0x52, 0x63, 0x8f, 0xc8, 0xc6, 0xf1, 0xcd, 0x4f, 0xc1, 0xd9, 0xc4, + 0x93, 0x20, 0x33, 0x8d, 0xb1, 0x6d, 0x9c, 0xd4, 0x05, 0x82, 0x26, 0x46, 0x2c, 0xb9, 0xf2, 0xc2, + 0xad, 0xbe, 0x98, 0xf8, 0x10, 0x2a, 0xe6, 0xb7, 0xb3, 0x30, 0x15, 0x7d, 0x60, 0x0b, 0xed, 0x29, + 0xbb, 0x51, 0x2a, 0x26, 0x2b, 0x4e, 0x56, 0xcb, 0xc8, 0xd4, 0xd7, 0x08, 0xbc, 0xc7, 0xc6, 0xd7, + 0xa6, 0x4a, 0x0f, 0x75, 0x7a, 0x8c, 0x85, 0xf5, 0x55, 0xb0, 0x63, 0x6f, 0x68, 0x85, 0xc1, 0x23, + 0xe2, 0xd8, 0x95, 0x3a, 0xf7, 0x30, 0x1c, 0x44, 0xb1, 0xc2, 0x1a, 0x5b, 0xba, 0xb7, 0xec, 0x12, + 0xcf, 0xde, 0xb2, 0xd5, 0xe3, 0xa0, 0x6c, 0xe5, 0xbe, 0x29, 0xca, 0xb0, 0x82, 0x9a, 0xaf, 0x66, + 0x20, 0x7c, 0x0a, 0x99, 0xbd, 0x42, 0xe3, 0x6b, 0x2a, 0xae, 0xe8, 0xb6, 0x2b, 0xc3, 0x3e, 0xf5, + 0x14, 0x52, 0x14, 0x3e, 0xa1, 0x5a, 0x09, 0x8e, 0x70, 0xfc, 0x3d, 0x3c, 0x81, 0x6c, 0xc1, 0x74, + 0x2c, 0x58, 0x38, 0x75, 0xff, 0xfd, 0x5f, 0x67, 0xa1, 0xa0, 0xc2, 0xad, 0xd1, 0x07, 0x22, 0xf6, + 0x86, 0x42, 0xf9, 0x9d, 0xda, 0x13, 0x09, 0xdb, 0x6e, 0xfd, 0x6e, 0xb7, 0x38, 0xad, 0x90, 0x63, + 0xb6, 0x83, 0xf3, 0x90, 0xed, 0x78, 0xcd, 0xf8, 0x81, 0xe2, 0x06, 0x5e, 0xc3, 0xb4, 0x1c, 0xdd, + 0x89, 0x1f, 0xf8, 0xd7, 0x53, 0x0a, 0x11, 0xe7, 0x9a, 0x77, 0xff, 0x83, 0x3e, 0xdd, 0x25, 0x37, + 0xdd, 0xfa, 0x7e, 0xfc, 0x49, 0x85, 0xb2, 0x5b, 0xdf, 0xc7, 0x0c, 0x82, 0x9e, 0x83, 0xa9, 0xc0, + 0x6e, 0x11, 0xb7, 0x13, 0xe8, 0x0f, 0xcd, 0x66, 0xc3, 0x4b, 0xcd, 0x8d, 0x08, 0x14, 0xc7, 0xb0, + 0xe9, 0x2e, 0x7b, 0xdb, 0x77, 0x1d, 0x96, 0x27, 0x71, 0x34, 0x7a, 0x03, 0x72, 0xa5, 0x7a, 0xed, + 0x2a, 0xb3, 0x7b, 0x28, 0x0c, 0x8a, 0x6d, 0xb3, 0x08, 0x44, 0x8f, 0x08, 0x9f, 0x82, 0x99, 0x30, + 0xf3, 0x06, 0x2f, 0xc7, 0x0a, 0x03, 0xad, 0x70, 0xda, 0x54, 0x5a, 0xb6, 0xa3, 0x4c, 0x94, 0x1f, + 0x95, 0x74, 0x69, 0xd9, 0xdd, 0xee, 0x21, 0xb1, 0xb5, 0xaa, 0xa6, 0x79, 0x03, 0xa6, 0x63, 0x0d, + 0x26, 0x0f, 0x80, 0x46, 0xf2, 0x01, 0x70, 0xb0, 0x57, 0x10, 0xfe, 0xa7, 0x01, 0xb3, 0x3d, 0x4b, + 0xc0, 0xa0, 0xe1, 0x29, 0xf1, 0xcd, 0x28, 0x73, 0xf2, 0xcd, 0x28, 0x7b, 0xbc, 0xcd, 0xa8, 0xbc, + 0xf9, 0xc3, 0xb7, 0x2e, 0xdc, 0xf7, 0xe3, 0xb7, 0x2e, 0xdc, 0xf7, 0xb3, 0xb7, 0x2e, 0xdc, 0xf7, + 0xea, 0xc1, 0x05, 0xe3, 0x87, 0x07, 0x17, 0x8c, 0x1f, 0x1f, 0x5c, 0x30, 0x7e, 0x76, 0x70, 0xc1, + 0xf8, 0xd3, 0x83, 0x0b, 0xc6, 0x9b, 0xbf, 0xbe, 0x70, 0xdf, 0x8b, 0xcf, 0x86, 0x03, 0x74, 0x49, + 0x0e, 0x50, 0xf6, 0xe3, 0xbd, 0x72, 0x38, 0x2e, 0xb5, 0x77, 0x1a, 0x4b, 0x74, 0x80, 0x2e, 0xa9, + 0x12, 0x39, 0x40, 0xff, 0x3e, 0x00, 0x00, 0xff, 0xff, 0xeb, 0x5e, 0x0d, 0x3c, 0xa2, 0x96, 0x00, + 0x00, } func (m *ALBStatus) Marshal() (dAtA []byte, err error) { @@ -6931,17 +6904,31 @@ func (m *MetricProvider) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Plugin != nil { - { - size, err := m.Plugin.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenerated(dAtA, i, uint64(size)) + if len(m.Plugin) > 0 { + keysForPlugin := make([]string, 0, len(m.Plugin)) + for k := range m.Plugin { + keysForPlugin = append(keysForPlugin, string(k)) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForPlugin) + for iNdEx := len(keysForPlugin) - 1; iNdEx >= 0; iNdEx-- { + v := m.Plugin[string(keysForPlugin[iNdEx])] + baseI := i + if v != nil { + i -= len(v) + copy(dAtA[i:], v) + i = encodeVarintGenerated(dAtA, i, uint64(len(v))) + i-- + dAtA[i] = 0x12 + } + i -= len(keysForPlugin[iNdEx]) + copy(dAtA[i:], keysForPlugin[iNdEx]) + i = encodeVarintGenerated(dAtA, i, uint64(len(keysForPlugin[iNdEx]))) + i-- + dAtA[i] = 0xa + i = encodeVarintGenerated(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x5a } - i-- - dAtA[i] = 0x5a } if m.Influxdb != nil { { @@ -7367,36 +7354,6 @@ func (m *PingPongSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *PluginMetric) 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 *PluginMetric) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *PluginMetric) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Config != nil { - i -= len(m.Config) - copy(dAtA[i:], m.Config) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.Config))) - i-- - dAtA[i] = 0x12 - } - return len(dAtA) - i, nil -} - func (m *PodTemplateMetadata) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -10917,9 +10874,17 @@ func (m *MetricProvider) Size() (n int) { l = m.Influxdb.Size() n += 1 + l + sovGenerated(uint64(l)) } - if m.Plugin != nil { - l = m.Plugin.Size() - n += 1 + l + sovGenerated(uint64(l)) + if len(m.Plugin) > 0 { + for k, v := range m.Plugin { + _ = k + _ = v + l = 0 + if v != nil { + l = 1 + len(v) + sovGenerated(uint64(len(v))) + } + mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + l + n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize)) + } } return n } @@ -11035,19 +11000,6 @@ func (m *PingPongSpec) Size() (n int) { return n } -func (m *PluginMetric) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Config != nil { - l = len(m.Config) - n += 1 + l + sovGenerated(uint64(l)) - } - return n -} - func (m *PodTemplateMetadata) Size() (n int) { if m == nil { return 0 @@ -12768,6 +12720,16 @@ func (this *MetricProvider) String() string { if this == nil { return "nil" } + keysForPlugin := make([]string, 0, len(this.Plugin)) + for k := range this.Plugin { + keysForPlugin = append(keysForPlugin, k) + } + github_com_gogo_protobuf_sortkeys.Strings(keysForPlugin) + mapStringForPlugin := "map[string]encoding_json.RawMessage{" + for _, k := range keysForPlugin { + mapStringForPlugin += fmt.Sprintf("%v: %v,", k, this.Plugin[k]) + } + mapStringForPlugin += "}" s := strings.Join([]string{`&MetricProvider{`, `Prometheus:` + strings.Replace(this.Prometheus.String(), "PrometheusMetric", "PrometheusMetric", 1) + `,`, `Kayenta:` + strings.Replace(this.Kayenta.String(), "KayentaMetric", "KayentaMetric", 1) + `,`, @@ -12779,7 +12741,7 @@ func (this *MetricProvider) String() string { `CloudWatch:` + strings.Replace(this.CloudWatch.String(), "CloudWatchMetric", "CloudWatchMetric", 1) + `,`, `Graphite:` + strings.Replace(this.Graphite.String(), "GraphiteMetric", "GraphiteMetric", 1) + `,`, `Influxdb:` + strings.Replace(this.Influxdb.String(), "InfluxdbMetric", "InfluxdbMetric", 1) + `,`, - `Plugin:` + strings.Replace(this.Plugin.String(), "PluginMetric", "PluginMetric", 1) + `,`, + `Plugin:` + mapStringForPlugin + `,`, `}`, }, "") return s @@ -12887,16 +12849,6 @@ func (this *PingPongSpec) String() string { }, "") return s } -func (this *PluginMetric) String() string { - if this == nil { - return "nil" - } - s := strings.Join([]string{`&PluginMetric{`, - `Config:` + valueToStringGenerated(this.Config) + `,`, - `}`, - }, "") - return s -} func (this *PodTemplateMetadata) String() string { if this == nil { return "nil" @@ -23792,11 +23744,103 @@ func (m *MetricProvider) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.Plugin == nil { - m.Plugin = &PluginMetric{} + m.Plugin = make(map[string]encoding_json.RawMessage) } - if err := m.Plugin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + var mapkey string + mapvalue := []byte{} + for iNdEx < postIndex { + entryPreIndex := 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) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthGenerated + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthGenerated + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapbyteLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapbyteLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intMapbyteLen := int(mapbyteLen) + if intMapbyteLen < 0 { + return ErrInvalidLengthGenerated + } + postbytesIndex := iNdEx + intMapbyteLen + if postbytesIndex < 0 { + return ErrInvalidLengthGenerated + } + if postbytesIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = make([]byte, mapbyteLen) + copy(mapvalue, dAtA[iNdEx:postbytesIndex]) + iNdEx = postbytesIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } } + m.Plugin[mapkey] = ((encoding_json.RawMessage)(mapvalue)) iNdEx = postIndex default: iNdEx = preIndex @@ -24990,90 +25034,6 @@ func (m *PingPongSpec) Unmarshal(dAtA []byte) error { } return nil } -func (m *PluginMetric) 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: PluginMetric: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: PluginMetric: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Config = append(m.Config[:0], dAtA[iNdEx:postIndex]...) - if m.Config == nil { - m.Config = []byte{} - } - iNdEx = postIndex - 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 *PodTemplateMetadata) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/pkg/apis/rollouts/v1alpha1/generated.proto b/pkg/apis/rollouts/v1alpha1/generated.proto index 9fff22ea0d..0666a820cb 100644 --- a/pkg/apis/rollouts/v1alpha1/generated.proto +++ b/pkg/apis/rollouts/v1alpha1/generated.proto @@ -977,8 +977,11 @@ message MetricProvider { // Influxdb specifies the influxdb metric to query optional InfluxdbMetric influxdb = 10; + // +kubebuilder:validation:Schemaless + // +kubebuilder:pruning:PreserveUnknownFields + // +kubebuilder:validation:Type=object // Plugin specifies the hashicorp go-plugin metric to query - optional PluginMetric plugin = 11; + map plugin = 11; } // MetricResult contain a list of the most recent measurements for a single metric along with @@ -1075,13 +1078,6 @@ message PingPongSpec { optional string pongService = 2; } -message PluginMetric { - // +kubebuilder:pruning:PreserveUnknownFields - // +kubebuilder:validation:Schemaless - // PluginName string `json:"name" protobuf:"bytes,1,opt,name=pluginName"` - optional bytes config = 2; -} - // PodTemplateMetadata extra labels to add to the template message PodTemplateMetadata { // Labels Additional labels to add to the experiment diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index f3634a21b4..083fe4f116 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -88,6 +88,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.Measurement": schema_pkg_apis_rollouts_v1alpha1_Measurement(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MeasurementRetention": schema_pkg_apis_rollouts_v1alpha1_MeasurementRetention(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.Metric": schema_pkg_apis_rollouts_v1alpha1_Metric(ref), + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MetricPlugin": schema_pkg_apis_rollouts_v1alpha1_MetricPlugin(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MetricProvider": schema_pkg_apis_rollouts_v1alpha1_MetricProvider(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MetricResult": schema_pkg_apis_rollouts_v1alpha1_MetricResult(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.NewRelicMetric": schema_pkg_apis_rollouts_v1alpha1_NewRelicMetric(ref), @@ -2858,6 +2859,24 @@ func schema_pkg_apis_rollouts_v1alpha1_Metric(ref common.ReferenceCallback) comm } } +func schema_pkg_apis_rollouts_v1alpha1_MetricPlugin(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "config": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "byte", + }, + }, + }, + }, + }, + } +} + func schema_pkg_apis_rollouts_v1alpha1_MetricProvider(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go index db989aa202..e5ca988b0e 100644 --- a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go @@ -1665,8 +1665,18 @@ func (in *MetricProvider) DeepCopyInto(out *MetricProvider) { } if in.Plugin != nil { in, out := &in.Plugin, &out.Plugin - *out = new(PluginMetric) - (*in).DeepCopyInto(*out) + *out = make(map[string]json.RawMessage, len(*in)) + for key, val := range *in { + var outVal []byte + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = make(json.RawMessage, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } } return } @@ -1799,27 +1809,6 @@ func (in *PingPongSpec) DeepCopy() *PingPongSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PluginMetric) DeepCopyInto(out *PluginMetric) { - *out = *in - if in.Config != nil { - in, out := &in.Config, &out.Config - *out = make(json.RawMessage, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PluginMetric. -func (in *PluginMetric) DeepCopy() *PluginMetric { - if in == nil { - return nil - } - out := new(PluginMetric) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PodTemplateMetadata) DeepCopyInto(out *PodTemplateMetadata) { *out = *in From 14907af624d4cd3abcdeb064b7a717548dbd295d Mon Sep 17 00:00:00 2001 From: zachaller Date: Mon, 30 Jan 2023 15:36:52 -0600 Subject: [PATCH 08/53] allow multiple plugins Signed-off-by: zachaller --- test/cmd/sample-metrics-plugin/internal/plugin/plugin.go | 6 +++--- utils/analysis/factory.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/cmd/sample-metrics-plugin/internal/plugin/plugin.go b/test/cmd/sample-metrics-plugin/internal/plugin/plugin.go index a0750a6bd6..bbe3f7ce28 100644 --- a/test/cmd/sample-metrics-plugin/internal/plugin/plugin.go +++ b/test/cmd/sample-metrics-plugin/internal/plugin/plugin.go @@ -39,7 +39,7 @@ type Config struct { func (g *RpcPlugin) NewMetricsPlugin(metric v1alpha1.Metric) types.RpcError { config := Config{} - err := json.Unmarshal(metric.Provider.Plugin.Config, &config) + err := json.Unmarshal(metric.Provider.Plugin["prometheus"], &config) if err != nil { return types.RpcError{ErrorString: err.Error()} } @@ -57,7 +57,7 @@ func (g *RpcPlugin) Run(anaysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric } config := Config{} - json.Unmarshal(metric.Provider.Plugin.Config, &config) + json.Unmarshal(metric.Provider.Plugin["prometheus"], &config) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() @@ -111,7 +111,7 @@ func (g *RpcPlugin) GetMetadata(metric v1alpha1.Metric) map[string]string { metricsMetadata := make(map[string]string) config := Config{} - json.Unmarshal(metric.Provider.Plugin.Config, &config) + json.Unmarshal(metric.Provider.Plugin["prometheus"], &config) if config.Query != "" { metricsMetadata["ResolvedPrometheusQuery"] = config.Query } diff --git a/utils/analysis/factory.go b/utils/analysis/factory.go index 56ea1d3798..d5c4b488aa 100644 --- a/utils/analysis/factory.go +++ b/utils/analysis/factory.go @@ -225,7 +225,7 @@ func ValidateMetric(metric v1alpha1.Metric) error { if metric.Provider.Influxdb != nil { numProviders++ } - if metric.Provider.Plugin != nil { + if metric.Provider.Plugin != nil && len(metric.Provider.Plugin) > 0 { numProviders++ } if numProviders == 0 { From 58b30b1ac2aa2d16646d90152096d99edfb1d7d5 Mon Sep 17 00:00:00 2001 From: zachaller Date: Mon, 30 Jan 2023 15:38:42 -0600 Subject: [PATCH 09/53] allow multiple plugins Signed-off-by: zachaller --- .../rollouts/v1alpha1/openapi_generated.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index 083fe4f116..f3634a21b4 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -88,7 +88,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.Measurement": schema_pkg_apis_rollouts_v1alpha1_Measurement(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MeasurementRetention": schema_pkg_apis_rollouts_v1alpha1_MeasurementRetention(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.Metric": schema_pkg_apis_rollouts_v1alpha1_Metric(ref), - "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MetricPlugin": schema_pkg_apis_rollouts_v1alpha1_MetricPlugin(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MetricProvider": schema_pkg_apis_rollouts_v1alpha1_MetricProvider(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.MetricResult": schema_pkg_apis_rollouts_v1alpha1_MetricResult(ref), "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.NewRelicMetric": schema_pkg_apis_rollouts_v1alpha1_NewRelicMetric(ref), @@ -2859,24 +2858,6 @@ func schema_pkg_apis_rollouts_v1alpha1_Metric(ref common.ReferenceCallback) comm } } -func schema_pkg_apis_rollouts_v1alpha1_MetricPlugin(ref common.ReferenceCallback) common.OpenAPIDefinition { - return common.OpenAPIDefinition{ - Schema: spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: map[string]spec.Schema{ - "config": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "byte", - }, - }, - }, - }, - }, - } -} - func schema_pkg_apis_rollouts_v1alpha1_MetricProvider(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ From a718b47b0cd236ffd66789cbde082808530e5340 Mon Sep 17 00:00:00 2001 From: zachaller Date: Mon, 30 Jan 2023 16:54:05 -0600 Subject: [PATCH 10/53] move client into own package Signed-off-by: zachaller --- metricproviders/plugin/client/client.go | 87 +++++++++++++++++++++++++ metricproviders/plugin/plugin.go | 70 +++----------------- utils/analysis/factory.go | 3 +- 3 files changed, 98 insertions(+), 62 deletions(-) create mode 100644 metricproviders/plugin/client/client.go diff --git a/metricproviders/plugin/client/client.go b/metricproviders/plugin/client/client.go new file mode 100644 index 0000000000..8784cb8de2 --- /dev/null +++ b/metricproviders/plugin/client/client.go @@ -0,0 +1,87 @@ +package client + +import ( + "fmt" + "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "github.com/argoproj/argo-rollouts/utils/defaults" + goPlugin "github.com/hashicorp/go-plugin" + "os/exec" +) + +type singletonMetricPlugin struct { + pluginClient map[string]*goPlugin.Client + plugin map[string]rpc.MetricsPlugin +} + +var singletonPluginClient *singletonMetricPlugin + +func GetMetricPlugin(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { + if singletonPluginClient == nil { + singletonPluginClient = &singletonMetricPlugin{ + pluginClient: make(map[string]*goPlugin.Client), + plugin: make(map[string]rpc.MetricsPlugin), + } + + } + plugin, err := singletonPluginClient.startPluginSystem(metric) + if err != nil { + return nil, err + } + return plugin, nil +} + +func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { + if defaults.GetMetricPluginLocation() == "" { + return nil, fmt.Errorf("no plugin location specified") + } + + var handshakeConfig = goPlugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "ARGO_ROLLOUTS_RPC_PLUGIN", + MagicCookieValue: "metrics", + } + + // pluginMap is the map of plugins we can dispense. + var pluginMap = map[string]goPlugin.Plugin{ + "RpcMetricsPlugin": &rpc.RpcMetricsPlugin{}, + } + + //There should only ever be one plugin defined in metric.Provider.Plugin + for pluginName, _ := range metric.Provider.Plugin { + if m.pluginClient[pluginName] == nil || m.pluginClient[pluginName].Exited() { + m.pluginClient[pluginName] = goPlugin.NewClient(&goPlugin.ClientConfig{ + HandshakeConfig: handshakeConfig, + Plugins: pluginMap, + Cmd: exec.Command(defaults.GetMetricPluginLocation()), + Managed: true, + }) + + rpcClient, err := m.pluginClient[pluginName].Client() + if err != nil { + return nil, err + } + + // Request the plugin + raw, err := rpcClient.Dispense("RpcMetricsPlugin") + if err != nil { + return nil, err + } + + pluginType, ok := raw.(rpc.MetricsPlugin) + if !ok { + return nil, fmt.Errorf("unexpected type from plugin") + } + + m.plugin[pluginName] = pluginType + + err = m.plugin[pluginName].NewMetricsPlugin(metric) + if err.Error() != "" { + return nil, err + } + } + + return m.plugin[pluginName], nil + } + return nil, fmt.Errorf("no plugin found") +} diff --git a/metricproviders/plugin/plugin.go b/metricproviders/plugin/plugin.go index 60b423e92c..2b6da693e6 100644 --- a/metricproviders/plugin/plugin.go +++ b/metricproviders/plugin/plugin.go @@ -1,94 +1,42 @@ package plugin import ( - "fmt" - "os/exec" - "github.com/argoproj/argo-rollouts/metric" + "github.com/argoproj/argo-rollouts/metricproviders/plugin/client" "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" - "github.com/argoproj/argo-rollouts/utils/defaults" - goPlugin "github.com/hashicorp/go-plugin" ) const ProviderType = "RPCPlugin" -var pluginClient *goPlugin.Client -var plugin rpc.MetricsPlugin - type MetricPlugin struct { + plugin rpc.MetricsPlugin metric.Provider } func NewRpcPlugin(metric v1alpha1.Metric) (metric.Provider, error) { - err := startPluginSystem(metric) + pluginClient, err := client.GetMetricPlugin(metric) if err != nil { return nil, err } - return MetricPlugin{}, nil -} - -func startPluginSystem(metric v1alpha1.Metric) error { - if defaults.GetMetricPluginLocation() == "" { - return fmt.Errorf("no plugin location specified") - } - - var handshakeConfig = goPlugin.HandshakeConfig{ - ProtocolVersion: 1, - MagicCookieKey: "ARGO_ROLLOUTS_RPC_PLUGIN", - MagicCookieValue: "metrics", - } - - // pluginMap is the map of plugins we can dispense. - var pluginMap = map[string]goPlugin.Plugin{ - "RpcMetricsPlugin": &rpc.RpcMetricsPlugin{}, - } - - if pluginClient == nil || pluginClient.Exited() { - pluginClient = goPlugin.NewClient(&goPlugin.ClientConfig{ - HandshakeConfig: handshakeConfig, - Plugins: pluginMap, - Cmd: exec.Command(defaults.GetMetricPluginLocation()), - Managed: true, - }) - - rpcClient, err := pluginClient.Client() - if err != nil { - return err - } - - // Request the plugin - raw, err := rpcClient.Dispense("RpcMetricsPlugin") - if err != nil { - return err - } - - plugin = raw.(rpc.MetricsPlugin) - - err = plugin.NewMetricsPlugin(metric) - if err.Error() != "" { - return err - } - } - - return nil + return MetricPlugin{plugin: pluginClient}, nil } func (m MetricPlugin) Run(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alpha1.Measurement { - return plugin.Run(run, metric) + return m.plugin.Run(run, metric) } func (m MetricPlugin) Resume(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { - return plugin.Resume(run, metric, measurement) + return m.plugin.Resume(run, metric, measurement) } func (m MetricPlugin) Terminate(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { - return plugin.Terminate(run, metric, measurement) + return m.plugin.Terminate(run, metric, measurement) } func (m MetricPlugin) GarbageCollect(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, limit int) error { - err := plugin.GarbageCollect(run, metric, limit) + err := m.plugin.GarbageCollect(run, metric, limit) if err.Error() != "" { return err } @@ -100,5 +48,5 @@ func (m MetricPlugin) Type() string { } func (m MetricPlugin) GetMetadata(metric v1alpha1.Metric) map[string]string { - return plugin.GetMetadata(metric) + return m.plugin.GetMetadata(metric) } diff --git a/utils/analysis/factory.go b/utils/analysis/factory.go index d5c4b488aa..6366c833dd 100644 --- a/utils/analysis/factory.go +++ b/utils/analysis/factory.go @@ -226,7 +226,8 @@ func ValidateMetric(metric v1alpha1.Metric) error { numProviders++ } if metric.Provider.Plugin != nil && len(metric.Provider.Plugin) > 0 { - numProviders++ + // We allow exactly one plugin to be specified per analysis run template + numProviders = numProviders + len(metric.Provider.Plugin) } if numProviders == 0 { return fmt.Errorf("no provider specified") From 39488c821e55acd2a709ed50954cf97a04d9be12 Mon Sep 17 00:00:00 2001 From: zachaller Date: Mon, 30 Jan 2023 18:40:46 -0600 Subject: [PATCH 11/53] lint Signed-off-by: zachaller --- analysis/controller.go | 3 ++- analysis/controller_test.go | 3 ++- metricproviders/metricproviders.go | 1 + metricproviders/plugin/client/client.go | 6 +++--- metricproviders/plugin/rpc/rpc_test_implementation.go | 3 ++- .../sample-metrics-plugin/internal/plugin/plugin_test.go | 2 +- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/analysis/controller.go b/analysis/controller.go index 77d335b7c5..505e6f005c 100644 --- a/analysis/controller.go +++ b/analysis/controller.go @@ -2,10 +2,11 @@ package analysis import ( "context" - "github.com/argoproj/argo-rollouts/metric" "sync" "time" + "github.com/argoproj/argo-rollouts/metric" + unstructuredutil "github.com/argoproj/argo-rollouts/utils/unstructured" log "github.com/sirupsen/logrus" diff --git a/analysis/controller_test.go b/analysis/controller_test.go index 71f8722e04..8d5efe11f9 100644 --- a/analysis/controller_test.go +++ b/analysis/controller_test.go @@ -3,11 +3,12 @@ package analysis import ( "context" "encoding/json" - "github.com/argoproj/argo-rollouts/metric" "reflect" "testing" "time" + "github.com/argoproj/argo-rollouts/metric" + timeutil "github.com/argoproj/argo-rollouts/utils/time" "github.com/argoproj/argo-rollouts/utils/queue" diff --git a/metricproviders/metricproviders.go b/metricproviders/metricproviders.go index 0459842129..7c6be31cb6 100644 --- a/metricproviders/metricproviders.go +++ b/metricproviders/metricproviders.go @@ -2,6 +2,7 @@ package metricproviders import ( "fmt" + "github.com/argoproj/argo-rollouts/metric" "github.com/argoproj/argo-rollouts/metricproviders/cloudwatch" diff --git a/metricproviders/plugin/client/client.go b/metricproviders/plugin/client/client.go index 8784cb8de2..d714258e43 100644 --- a/metricproviders/plugin/client/client.go +++ b/metricproviders/plugin/client/client.go @@ -2,11 +2,12 @@ package client import ( "fmt" + "os/exec" + "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" "github.com/argoproj/argo-rollouts/utils/defaults" goPlugin "github.com/hashicorp/go-plugin" - "os/exec" ) type singletonMetricPlugin struct { @@ -48,7 +49,7 @@ func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.M } //There should only ever be one plugin defined in metric.Provider.Plugin - for pluginName, _ := range metric.Provider.Plugin { + for pluginName := range metric.Provider.Plugin { if m.pluginClient[pluginName] == nil || m.pluginClient[pluginName].Exited() { m.pluginClient[pluginName] = goPlugin.NewClient(&goPlugin.ClientConfig{ HandshakeConfig: handshakeConfig, @@ -72,7 +73,6 @@ func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.M if !ok { return nil, fmt.Errorf("unexpected type from plugin") } - m.plugin[pluginName] = pluginType err = m.plugin[pluginName].NewMetricsPlugin(metric) diff --git a/metricproviders/plugin/rpc/rpc_test_implementation.go b/metricproviders/plugin/rpc/rpc_test_implementation.go index bb90d2339f..4962f16eb4 100644 --- a/metricproviders/plugin/rpc/rpc_test_implementation.go +++ b/metricproviders/plugin/rpc/rpc_test_implementation.go @@ -2,9 +2,10 @@ package rpc import ( "fmt" - "k8s.io/apimachinery/pkg/util/json" "time" + "k8s.io/apimachinery/pkg/util/json" + "github.com/argoproj/argo-rollouts/utils/plugin/types" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" diff --git a/test/cmd/sample-metrics-plugin/internal/plugin/plugin_test.go b/test/cmd/sample-metrics-plugin/internal/plugin/plugin_test.go index da5c0b6845..fc66bc0398 100644 --- a/test/cmd/sample-metrics-plugin/internal/plugin/plugin_test.go +++ b/test/cmd/sample-metrics-plugin/internal/plugin/plugin_test.go @@ -95,7 +95,7 @@ func TestRunSuccessfully(t *testing.T) { err = plugin.NewMetricsPlugin(v1alpha1.Metric{ Provider: v1alpha1.MetricProvider{ - Plugin: &v1alpha1.PluginMetric{Config: json.RawMessage(`{"address":"http://prometheus.local", "query":"machine_cpu_cores"}`)}, + Plugin: map[string]json.RawMessage{"prometheus": json.RawMessage(`{"address":"http://prometheus.local", "query":"machine_cpu_cores"}`)}, }, }) if err != nil { From 106aa7c2a8073dcf197f14868f167ab9ed26d219 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 14:27:28 -0600 Subject: [PATCH 12/53] wip: configmap Signed-off-by: zachaller --- cmd/rollouts-controller/main.go | 20 ++- controller/controller.go | 18 ++- metricproviders/plugin/client/client.go | 13 +- utils/config/config.go | 58 ++++++++ utils/config/plugin_downloader.go | 165 ++++++++++++++++++++++ utils/config/plugin_downloader_test.go | 177 ++++++++++++++++++++++++ utils/defaults/defaults.go | 30 +--- utils/plugin/plugin.go | 120 ++-------------- utils/plugin/plugin_test.go | 78 ----------- utils/plugin/types/types.go | 10 ++ 10 files changed, 449 insertions(+), 240 deletions(-) create mode 100755 utils/config/config.go create mode 100644 utils/config/plugin_downloader.go create mode 100644 utils/config/plugin_downloader_test.go delete mode 100644 utils/plugin/plugin_test.go diff --git a/cmd/rollouts-controller/main.go b/cmd/rollouts-controller/main.go index 820634c1c0..997a6d54c1 100644 --- a/cmd/rollouts-controller/main.go +++ b/cmd/rollouts-controller/main.go @@ -6,8 +6,6 @@ import ( "strings" "time" - "github.com/argoproj/argo-rollouts/utils/plugin" - "github.com/argoproj/pkg/kubeclientmetrics" smiclientset "github.com/servicemeshinterface/smi-sdk-go/pkg/gen/client/split/clientset/versioned" log "github.com/sirupsen/logrus" @@ -71,8 +69,6 @@ func newCommand() *cobra.Command { awsVerifyTargetGroup bool namespaced bool printVersion bool - metricPluginLocation string - metricPluginSha256 string ) electOpts := controller.NewLeaderElectionOptions() var command = cobra.Command{ @@ -202,11 +198,15 @@ func newCommand() *cobra.Command { controllerNamespaceInformerFactory, jobInformerFactory) - defaults.SetMetricPluginLocation(metricPluginLocation) - err = plugin.InitMetricsPlugin(metricPluginLocation, plugin.FileDownloaderImpl{}, metricPluginSha256) - if err != nil { - log.Fatalf("Failed to init metric plugin: %v", err) - } + //err = rolloutsConfig.InitConfig(configMapInformer, "argo-rollouts-config") + //if err != nil { + // log.Fatalf("Failed to init config: %v", err) + //} + // + //err = plugin.InitMetricsPlugins(ctx, kubeClient, "argo-rollouts-config", plugin.FileDownloaderImpl{}) + //if err != nil { + // log.Fatalf("Failed to init metric plugin: %v", err) + //} if err = cm.Run(ctx, rolloutThreads, serviceThreads, ingressThreads, experimentThreads, analysisThreads, electOpts); err != nil { log.Fatalf("Error running controller: %s", err.Error()) @@ -249,8 +249,6 @@ func newCommand() *cobra.Command { command.Flags().DurationVar(&electOpts.LeaderElectionLeaseDuration, "leader-election-lease-duration", controller.DefaultLeaderElectionLeaseDuration, "The duration that non-leader candidates will wait after observing a leadership renewal until attempting to acquire leadership of a led but unrenewed leader slot. This is effectively the maximum duration that a leader can be stopped before it is replaced by another candidate. This is only applicable if leader election is enabled.") command.Flags().DurationVar(&electOpts.LeaderElectionRenewDeadline, "leader-election-renew-deadline", controller.DefaultLeaderElectionRenewDeadline, "The interval between attempts by the acting master to renew a leadership slot before it stops leading. This must be less than or equal to the lease duration. This is only applicable if leader election is enabled.") command.Flags().DurationVar(&electOpts.LeaderElectionRetryPeriod, "leader-election-retry-period", controller.DefaultLeaderElectionRetryPeriod, "The duration the clients should wait between attempting acquisition and renewal of a leadership. This is only applicable if leader election is enabled.") - command.Flags().StringVar(&metricPluginLocation, "metric-plugin-location", defaults.DefaultMetricsPluginLocation, "The file path to the location of the metric plugin binary") - command.Flags().StringVar(&metricPluginSha256, "metric-plugin-sha256", "", "The expected sha256 of the metric plugin binary") return &command } diff --git a/controller/controller.go b/controller/controller.go index 058cff4906..e075590a4c 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -4,6 +4,8 @@ import ( "context" "encoding/json" "fmt" + rolloutsConfig "github.com/argoproj/argo-rollouts/utils/config" + istioutil "github.com/argoproj/argo-rollouts/utils/istio" "net/http" "os" "sync" @@ -13,7 +15,6 @@ import ( "k8s.io/apimachinery/pkg/util/wait" - istioutil "github.com/argoproj/argo-rollouts/utils/istio" "k8s.io/client-go/dynamic/dynamicinformer" kubeinformers "k8s.io/client-go/informers" @@ -459,11 +460,6 @@ func (c *Manager) startLeading(ctx context.Context, rolloutThreadiness, serviceT c.controllerNamespaceInformerFactory.Start(ctx.Done()) c.jobInformerFactory.Start(ctx.Done()) - // Check if Istio installed on cluster before starting dynamicInformerFactory - if istioutil.DoesIstioExist(c.istioPrimaryDynamicClient, c.namespace) { - c.istioDynamicInformerFactory.Start(ctx.Done()) - } - // Wait for the caches to be synced before starting workers log.Info("Waiting for controller's informer caches to sync") if ok := cache.WaitForCacheSync(ctx.Done(), c.serviceSynced, c.ingressSynced, c.jobSynced, c.rolloutSynced, c.experimentSynced, c.analysisRunSynced, c.analysisTemplateSynced, c.replicasSetSynced, c.configMapSynced, c.secretSynced); !ok { @@ -476,6 +472,16 @@ func (c *Manager) startLeading(ctx context.Context, rolloutThreadiness, serviceT } } + _, err := rolloutsConfig.InitializeConfig(c.controllerNamespaceInformerFactory.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, rolloutsConfig.FileDownloaderImpl{}) + if err != nil { + log.Fatalf("Failed to init config: %v", err) + } + + // Check if Istio installed on cluster before starting dynamicInformerFactory + if istioutil.DoesIstioExist(c.istioPrimaryDynamicClient, c.namespace) { + c.istioDynamicInformerFactory.Start(ctx.Done()) + } + go wait.Until(func() { c.wg.Add(1); c.rolloutController.Run(ctx, rolloutThreadiness); c.wg.Done() }, time.Second, ctx.Done()) go wait.Until(func() { c.wg.Add(1); c.serviceController.Run(ctx, serviceThreadiness); c.wg.Done() }, time.Second, ctx.Done()) go wait.Until(func() { c.wg.Add(1); c.ingressController.Run(ctx, ingressThreadiness); c.wg.Done() }, time.Second, ctx.Done()) diff --git a/metricproviders/plugin/client/client.go b/metricproviders/plugin/client/client.go index d714258e43..5cff22ccac 100644 --- a/metricproviders/plugin/client/client.go +++ b/metricproviders/plugin/client/client.go @@ -2,11 +2,12 @@ package client import ( "fmt" + "github.com/argoproj/argo-rollouts/utils/plugin" "os/exec" + "path/filepath" "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" - "github.com/argoproj/argo-rollouts/utils/defaults" goPlugin "github.com/hashicorp/go-plugin" ) @@ -33,9 +34,9 @@ func GetMetricPlugin(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { } func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { - if defaults.GetMetricPluginLocation() == "" { - return nil, fmt.Errorf("no plugin location specified") - } + //if defaults.GetMetricPluginLocation() == "" { + // return nil, fmt.Errorf("no plugin location specified") + //} var handshakeConfig = goPlugin.HandshakeConfig{ ProtocolVersion: 1, @@ -50,11 +51,13 @@ func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.M //There should only ever be one plugin defined in metric.Provider.Plugin for pluginName := range metric.Provider.Plugin { + plugin.GetPluginLocation(pluginName) + if m.pluginClient[pluginName] == nil || m.pluginClient[pluginName].Exited() { m.pluginClient[pluginName] = goPlugin.NewClient(&goPlugin.ClientConfig{ HandshakeConfig: handshakeConfig, Plugins: pluginMap, - Cmd: exec.Command(defaults.GetMetricPluginLocation()), + Cmd: exec.Command(filepath.Join("/tmp", pluginName)), Managed: true, }) diff --git a/utils/config/config.go b/utils/config/config.go new file mode 100755 index 0000000000..8f3f74f2bf --- /dev/null +++ b/utils/config/config.go @@ -0,0 +1,58 @@ +package config + +import ( + "fmt" + "github.com/argoproj/argo-rollouts/utils/defaults" + "github.com/argoproj/argo-rollouts/utils/plugin/types" + "github.com/ghodss/yaml" + v1 "k8s.io/api/core/v1" + k8errors "k8s.io/apimachinery/pkg/api/errors" + informers "k8s.io/client-go/informers/core/v1" +) + +type Config struct { + configMap *v1.ConfigMap + plugins types.Plugin +} + +var configMemoryCache *Config + +// InitializeConfig initializes the in memory config and downloads the plugins to the filesystem. Subsequent calls to this function will return +// the same config object. +func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapName string, downloader FileDownloader) (*Config, error) { + configMapCluster, err := configMapInformer.Lister().ConfigMaps(defaults.Namespace()).Get(configMapName) + if err != nil { + if k8errors.IsNotFound(err) { + // If the configmap is not found, we return + return nil, nil + } + return nil, err + } + + plugins := types.Plugin{} + if err = yaml.Unmarshal([]byte(configMapCluster.Data["plugins"]), &plugins); err != nil { + return nil, err + } + + configMemoryCache = &Config{ + configMap: configMapCluster, + plugins: plugins, + } + + if err := initMetricsPlugins(downloader); err != nil { + return nil, err + } + return configMemoryCache, nil +} + +// GetConfig returns the initialized in memory config object if it exists otherwise errors if InitializeConfig has not been called. +func GetConfig() (*Config, error) { + if configMemoryCache == nil { + return nil, fmt.Errorf("config not initialized") + } + return configMemoryCache, nil +} + +func (c *Config) GetMetricPluginsConfig() []types.PluginItem { + return configMemoryCache.plugins.Metrics +} diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go new file mode 100644 index 0000000000..a7d5f8012c --- /dev/null +++ b/utils/config/plugin_downloader.go @@ -0,0 +1,165 @@ +package config + +import ( + "crypto/sha256" + "fmt" + log "github.com/sirupsen/logrus" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "time" +) + +type FileDownloader interface { + Get(url string) (resp *http.Response, err error) +} + +type FileDownloaderImpl struct { + FileDownloader +} + +func (fd FileDownloaderImpl) Get(url string) (resp *http.Response, err error) { + return http.Get(url) +} + +// CheckPluginExists this function checks if the plugin exists in the configured path if not we panic +func checkPluginExists(pluginLocation string) error { + if pluginLocation != "" { + //Check for plugin executable existence + _, err := os.Stat(pluginLocation) + if err != nil { + return err + } + } + return nil +} + +func checkShaOfPlugin(pluginLocation string, expectedSha256 string) (bool, error) { + hasher := sha256.New() + fileBytes, err := os.ReadFile(pluginLocation) + if err != nil { + return false, err + } + fileSha256 := fmt.Sprintf("%x", hasher.Sum(fileBytes)) + match := fileSha256 == expectedSha256 + if !match { + log.Printf("expected sha256: %s, actual sha256: %s, of downloaded metric plugin (%s)", expectedSha256, fileSha256, pluginLocation) + } + return match, nil +} + +func downloadFile(filepath string, url string, downloader FileDownloader) error { + // Get the data + resp, err := downloader.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + + // Create the file + out, err := os.Create(filepath) + if err != nil { + return err + } + defer out.Close() + + // Write the body to file + _, err = io.Copy(out, resp.Body) + if err != nil { + return err + } + + // Set the file permissions, to allow execution + err = os.Chmod(filepath, 0700) + if err != nil { + return err + } + + return err +} + +// initMetricsPlugins this function downloads and/or checks that a plugin executable exits on the filesystem +func initMetricsPlugins(fd FileDownloader) error { + config, err := GetConfig() + if err != nil { + return err + } + for _, plugin := range config.GetMetricPluginsConfig() { + urlObj, err := url.ParseRequestURI(plugin.PluginLocation) + finalFileLocation := filepath.Join("/tmp", plugin.Name) + if err != nil { + return err + } + + switch urlObj.Scheme { + case "http", "https": + log.Printf("Downloading plugin from: %s", plugin.PluginLocation) + startTime := time.Now() + err = downloadFile(finalFileLocation, urlObj.String(), fd) + if err != nil { + return err + } + timeTakenToDownload := time.Now().Sub(startTime) + log.Printf("Download complete, it took %s", timeTakenToDownload) + + if plugin.PluginSha256 != "" { + sha256Matched, err := checkShaOfPlugin(finalFileLocation, plugin.PluginSha256) + if err != nil { + return err + } + if !sha256Matched { + return fmt.Errorf("sha256 hash of downloaded plugin (%s) does not match expected hash", plugin.PluginLocation) + } + } + if checkPluginExists(finalFileLocation) != nil { + return fmt.Errorf("failed to find plugin at location: %s", plugin.PluginLocation) + } + + case "file": + pluginPath, err := filepath.Abs(urlObj.Host + urlObj.Path) + if err != nil { + return err + } + + if err = copyFile(pluginPath, finalFileLocation); err != nil { + return err + } + if checkPluginExists(finalFileLocation) != nil { + return fmt.Errorf("failed to find plugin at location: %s", plugin.PluginLocation) + } + default: + return fmt.Errorf("plugin location must be of http(s) or file scheme") + } + } + + return nil +} + +// CopyFile copies a file from src to dst. If src and dst files exist, and are +// the same, then return success. Otherise, attempt to create a hard link +// between the two files. If that fail, copy the file contents from src to dst. +func copyFile(src, dst string) (err error) { + sfi, err := os.Stat(src) + if err != nil { + return + } + dfi, err := os.Stat(dst) + if err != nil { + if !os.IsNotExist(err) { + return + } + } else { + if !(dfi.Mode().IsRegular()) { + return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String()) + } + if os.SameFile(sfi, dfi) { + return + } + } + if err = os.Link(src, dst); err == nil { + return + } + return +} diff --git a/utils/config/plugin_downloader_test.go b/utils/config/plugin_downloader_test.go new file mode 100644 index 0000000000..785cc6da63 --- /dev/null +++ b/utils/config/plugin_downloader_test.go @@ -0,0 +1,177 @@ +package config + +import ( + "bytes" + "github.com/argoproj/argo-rollouts/utils/defaults" + "github.com/tj/assert" + "io" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes/fake" + "net/http" + "os" + "testing" +) + +type MockFileDownloader struct { + FileDownloader +} + +func (m MockFileDownloader) Get(url string) (*http.Response, error) { + responseBody := io.NopCloser(bytes.NewReader([]byte(`test`))) + return &http.Response{ + Status: "200", + StatusCode: 200, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: nil, + Body: responseBody, + ContentLength: 4, + }, nil +} + +func TestInitPlugin(t *testing.T) { + fd := &MockFileDownloader{} + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argo-rollouts-config", + Namespace: "argo-rollouts", + }, + Data: map[string]string{"plugins": "metrics:\n - name: http\n pluginLocation: https://test/plugin\n - name: http-sha\n pluginLocation: https://test/plugin\n pluginSha256: 74657374e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, + } + client := fake.NewSimpleClientset(cm) + i := informers.NewSharedInformerFactory(client, 0) + i.Start(make(chan struct{})) + cmi := i.Core().V1().ConfigMaps() + go cmi.Informer().Run(make(chan struct{})) + + err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) + assert.NoError(t, err) + + _, err = InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config", fd) + assert.NoError(t, err) + + os.Remove("/tmp/http") +} + +func TestInitPluginBadSha(t *testing.T) { + fd := &MockFileDownloader{} + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: defaults.DefaultRolloutsConfigMapName, + Namespace: defaults.Namespace(), + }, + Data: map[string]string{"plugins": "metrics:\n - name: http-badsha\n pluginLocation: https://test/plugin\n pluginSha256: badsha352"}, + } + client := fake.NewSimpleClientset(cm) + i := informers.NewSharedInformerFactory(client, 0) + i.Start(make(chan struct{})) + cmi := i.Core().V1().ConfigMaps() + go cmi.Informer().Run(make(chan struct{})) + + err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) + assert.NoError(t, err) + + _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) + assert.Error(t, err) + + os.Remove("/tmp/http-badsha") +} + +func TestInitPluginConfigNotFound(t *testing.T) { + fd := &MockFileDownloader{} + client := fake.NewSimpleClientset() + i := informers.NewSharedInformerFactory(client, 0) + i.Start(make(chan struct{})) + cmi := i.Core().V1().ConfigMaps() + go cmi.Informer().Run(make(chan struct{})) + + cm, err := InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) + assert.NoError(t, err) + assert.Nil(t, cm) +} + +func TestFileMove(t *testing.T) { + fd := &MockFileDownloader{} + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: defaults.DefaultRolloutsConfigMapName, + Namespace: defaults.Namespace(), + }, + Data: map[string]string{"plugins": "metrics:\n - name: file-plugin\n pluginLocation: file://./config.go"}, + } + client := fake.NewSimpleClientset(cm) + i := informers.NewSharedInformerFactory(client, 0) + i.Start(make(chan struct{})) + cmi := i.Core().V1().ConfigMaps() + go cmi.Informer().Run(make(chan struct{})) + + err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) + assert.NoError(t, err) + + _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) + assert.NoError(t, err) + + err = os.Remove("/tmp/file-plugin") + assert.NoError(t, err) +} + +func TestDoubleInit(t *testing.T) { + fd := &MockFileDownloader{} + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: defaults.DefaultRolloutsConfigMapName, + Namespace: defaults.Namespace(), + }, + Data: map[string]string{"plugins": "metrics:\n - name: file-plugin\n pluginLocation: file://./config.go"}, + } + client := fake.NewSimpleClientset(cm) + i := informers.NewSharedInformerFactory(client, 0) + i.Start(make(chan struct{})) + cmi := i.Core().V1().ConfigMaps() + go cmi.Informer().Run(make(chan struct{})) + + err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) + assert.NoError(t, err) + + _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) + assert.NoError(t, err) + + _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) + assert.NoError(t, err) + + err = os.Remove("/tmp/file-plugin") + assert.NoError(t, err) +} + +func TestNotInitialized(t *testing.T) { + configMemoryCache = nil + _, err := GetConfig() + assert.Error(t, err) +} + +func TestBadConfigMap(t *testing.T) { + fd := &MockFileDownloader{} + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argo-rollouts-config", + Namespace: "argo-rollouts", + }, + Data: map[string]string{"plugins": "badconfigmap"}, + } + client := fake.NewSimpleClientset(cm) + i := informers.NewSharedInformerFactory(client, 0) + i.Start(make(chan struct{})) + cmi := i.Core().V1().ConfigMaps() + go cmi.Informer().Run(make(chan struct{})) + + err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) + assert.NoError(t, err) + + _, err = InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config", fd) + assert.Error(t, err) + + os.Remove("/tmp/http") +} diff --git a/utils/defaults/defaults.go b/utils/defaults/defaults.go index 37b90365dc..92a6947275 100644 --- a/utils/defaults/defaults.go +++ b/utils/defaults/defaults.go @@ -2,7 +2,6 @@ package defaults import ( "io/ioutil" - "net/url" "os" "strconv" "strings" @@ -46,10 +45,8 @@ const ( // DefaultMetricCleanupDelay is the default time to delay metrics removal upon object removal, gives time for metrics // to be collected DefaultMetricCleanupDelay = int32(65) - // DefaultMetricsPluginLocation is the default path to the metrics plugin binary - DefaultMetricsPluginLocation = "" - // DefaultPluginHttpFileLocation is the default path to the metrics plugin when downloaded via http(s) - DefaultPluginHttpFileLocation = "/tmp/metric-plugin" + // DefaultRolloutsConfigMapName is the default name of the ConfigMap that contains the Rollouts controller configuration + DefaultRolloutsConfigMapName = "argo-rollouts-config" ) const ( @@ -73,7 +70,6 @@ var ( targetGroupBindingAPIVersion = DefaultTargetGroupBindingAPIVersion appmeshCRDVersion = DefaultAppMeshCRDVersion defaultMetricCleanupDelay = DefaultMetricCleanupDelay - metricPluginLocation = &url.URL{Path: DefaultMetricsPluginLocation} ) const ( @@ -326,25 +322,3 @@ func GetMetricCleanupDelaySeconds() time.Duration { func SetMetricCleanupDelaySeconds(seconds int32) { defaultMetricCleanupDelay = seconds } - -// GetMetricPluginLocation returns the location of the metric plugin binary -func GetMetricPluginLocation() string { - switch metricPluginLocation.Scheme { - case "http", "https": - return DefaultPluginHttpFileLocation // return relative path to the plugin - case "file": - return metricPluginLocation.Path - default: - return "" - } -} - -// SetMetricPluginLocation sets the location of the metric plugin binary -func SetMetricPluginLocation(pluginLocation string) error { - urlObj, err := url.ParseRequestURI(pluginLocation) - if err != nil { - return err - } - metricPluginLocation = urlObj - return nil -} diff --git a/utils/plugin/plugin.go b/utils/plugin/plugin.go index 6b7222cd3b..95daa6a637 100644 --- a/utils/plugin/plugin.go +++ b/utils/plugin/plugin.go @@ -1,125 +1,21 @@ package plugin import ( - "crypto/sha256" "fmt" - "io" - "net/http" - "net/url" - "os" + "github.com/argoproj/argo-rollouts/utils/config" "path/filepath" - - "github.com/argoproj/argo-rollouts/utils/defaults" - "github.com/argoproj/argo-rollouts/utils/time" - log "github.com/sirupsen/logrus" ) -type FileDownloader interface { - Get(url string) (resp *http.Response, err error) -} - -type FileDownloaderImpl struct { - FileDownloader -} - -func (fd FileDownloaderImpl) Get(url string) (resp *http.Response, err error) { - return http.Get(url) -} - -// CheckPluginExists this function checks if the plugin exists in the configured path if not we panic -func checkPluginExists() error { - if defaults.GetMetricPluginLocation() != "" { - //Check for plugin executable existence - _, err := os.Stat(defaults.GetMetricPluginLocation()) - if err != nil { - return err - } - } - return nil -} - -func checkShaOfPlugin(pluginLocation string, expectedSha256 string) (bool, error) { - hasher := sha256.New() - fileBytes, err := os.ReadFile(pluginLocation) - if err != nil { - return false, err - } - fileSha256 := fmt.Sprintf("%x", hasher.Sum(fileBytes)) - log.Printf("exected sha256: %s, actual sha256: %s, of downloaded metric plugin", expectedSha256, fileSha256) - return fileSha256 == expectedSha256, nil -} - -func downloadFile(filepath string, url string, downloader FileDownloader) error { - // Get the data - resp, err := downloader.Get(url) - if err != nil { - return err - } - defer resp.Body.Close() - - // Create the file - out, err := os.Create(filepath) +func GetPluginLocation(pluginName string) (string, error) { + configMap, err := config.GetConfig() if err != nil { - return err + return "", err } - defer out.Close() - // Write the body to file - _, err = io.Copy(out, resp.Body) - if err != nil { - return err - } - - // Set the file permissions, to allow execution - err = os.Chmod(filepath, 0700) - if err != nil { - return err - } - - return err -} - -// InitMetricsPlugin this function downloads and/or checks that a plugin executable exits on the filesystem -func InitMetricsPlugin(pluginPath string, fd FileDownloader, expectedSha256Hash string) error { - if pluginPath == "" { - return nil - } - - urlObj, err := url.ParseRequestURI(pluginPath) - if err != nil { - return err - } - - switch urlObj.Scheme { - case "http", "https": - log.Printf("Downloading plugin from: %s", pluginPath) - startTime := time.Now() - err = downloadFile(defaults.DefaultPluginHttpFileLocation, urlObj.String(), fd) - if err != nil { - return err + for _, item := range configMap.GetMetricPluginsConfig() { + if pluginName == item.Name { + return filepath.Join("/tmp", item.Name), nil } - timeTakenToDownload := time.Now().Sub(startTime) - log.Printf("Download complete, it took %s", timeTakenToDownload) - defaults.SetMetricPluginLocation("file://" + defaults.DefaultPluginHttpFileLocation) - - if expectedSha256Hash != "" { - sha256Matched, err := checkShaOfPlugin(defaults.DefaultPluginHttpFileLocation, expectedSha256Hash) - if err != nil { - return err - } - if !sha256Matched { - return fmt.Errorf("sha256 hash of downloaded plugin does not match expected hash") - } - } - case "file": - pluginPath, err = filepath.Abs(urlObj.Host + urlObj.Path) - if err != nil { - return err - } - defaults.SetMetricPluginLocation(urlObj.Scheme + "://" + pluginPath) - default: - return fmt.Errorf("plugin location must be of http(s) or file scheme") } - - return checkPluginExists() + return "", fmt.Errorf("plugin %s not configured", pluginName) } diff --git a/utils/plugin/plugin_test.go b/utils/plugin/plugin_test.go deleted file mode 100644 index d83e9f9f90..0000000000 --- a/utils/plugin/plugin_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package plugin - -import ( - "bytes" - "io" - "net/http" - "os" - "path/filepath" - "testing" - - "github.com/argoproj/argo-rollouts/utils/defaults" - "github.com/tj/assert" -) - -type MockFileDownloader struct { - FileDownloader -} - -func (m MockFileDownloader) Get(url string) (*http.Response, error) { - responseBody := io.NopCloser(bytes.NewReader([]byte(`test`))) - return &http.Response{ - Status: "200", - StatusCode: 200, - Proto: "HTTP/1.1", - ProtoMajor: 1, - ProtoMinor: 1, - Header: nil, - Body: responseBody, - ContentLength: 4, - }, nil -} - -func TestInitPlugin(t *testing.T) { - fd := &MockFileDownloader{} - - err := InitMetricsPlugin("file://./plugin.go", fd, "") - assert.NoError(t, err) - - err = InitMetricsPlugin("file://plugin.go", fd, "") - assert.NoError(t, err) - - err = InitMetricsPlugin("file://./file-not-found.go", fd, "") - assert.Error(t, err) - - err = InitMetricsPlugin("file://file-not-found.go", fd, "") - assert.Error(t, err) - - err = InitMetricsPlugin("file://./../../Dockerfile", fd, "") - assert.NoError(t, err) - - f, err := filepath.Abs("plugin.go") - if err != nil { - t.Fatal(err) - } - err = InitMetricsPlugin("file://"+f, fd, "") - assert.NoError(t, err) - - f, err = filepath.Abs("file-not-found.go") - if err != nil { - t.Fatal(err) - } - err = InitMetricsPlugin("file://"+f, fd, "") - assert.Error(t, err) - - err = InitMetricsPlugin("http://mock-host:8080/plugin", fd, "74657374e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - assert.NoError(t, err) - - err = InitMetricsPlugin("http://mock-host:8080/plugin", fd, "bad-sha") - assert.Error(t, err) - - err = InitMetricsPlugin("", fd, "bad-sha") - assert.NoError(t, err) - - err = InitMetricsPlugin("*(&^%$#@!", fd, "bad-sha") - assert.Error(t, err) - - os.Remove(defaults.DefaultPluginHttpFileLocation) -} diff --git a/utils/plugin/types/types.go b/utils/plugin/types/types.go index c1b8370479..ea77d66183 100644 --- a/utils/plugin/types/types.go +++ b/utils/plugin/types/types.go @@ -26,3 +26,13 @@ type RpcMetricProvider interface { // of the metric result. For example, Prometheus uses is to store the final resolved queries. GetMetadata(metric v1alpha1.Metric) map[string]string } + +type Plugin struct { + Metrics []PluginItem `json:"metrics" yaml:"metrics"` +} + +type PluginItem struct { + Name string `json:"name" yaml:"name"` + PluginLocation string `json:"pluginLocation" yaml:"pluginLocation"` + PluginSha256 string `json:"pluginSha256" yaml:"pluginSha256"` +} From 5904c053a8b2ad7301332ac84ddd6933221704ed Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 14:33:30 -0600 Subject: [PATCH 13/53] lint Signed-off-by: zachaller --- controller/controller.go | 5 +++-- metricproviders/plugin/client/client.go | 15 ++++++--------- utils/config/config.go | 1 + utils/config/plugin_downloader.go | 3 ++- utils/config/plugin_downloader_test.go | 9 +++++---- utils/defaults/defaults_test.go | 15 --------------- utils/plugin/plugin.go | 3 ++- 7 files changed, 19 insertions(+), 32 deletions(-) mode change 100755 => 100644 utils/config/config.go diff --git a/controller/controller.go b/controller/controller.go index e075590a4c..419b0f5057 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -4,13 +4,14 @@ import ( "context" "encoding/json" "fmt" - rolloutsConfig "github.com/argoproj/argo-rollouts/utils/config" - istioutil "github.com/argoproj/argo-rollouts/utils/istio" "net/http" "os" "sync" "time" + rolloutsConfig "github.com/argoproj/argo-rollouts/utils/config" + istioutil "github.com/argoproj/argo-rollouts/utils/istio" + goPlugin "github.com/hashicorp/go-plugin" "k8s.io/apimachinery/pkg/util/wait" diff --git a/metricproviders/plugin/client/client.go b/metricproviders/plugin/client/client.go index 5cff22ccac..e9c5ec6fc0 100644 --- a/metricproviders/plugin/client/client.go +++ b/metricproviders/plugin/client/client.go @@ -2,12 +2,11 @@ package client import ( "fmt" - "github.com/argoproj/argo-rollouts/utils/plugin" "os/exec" - "path/filepath" "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" + "github.com/argoproj/argo-rollouts/utils/plugin" goPlugin "github.com/hashicorp/go-plugin" ) @@ -24,7 +23,6 @@ func GetMetricPlugin(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { pluginClient: make(map[string]*goPlugin.Client), plugin: make(map[string]rpc.MetricsPlugin), } - } plugin, err := singletonPluginClient.startPluginSystem(metric) if err != nil { @@ -34,10 +32,6 @@ func GetMetricPlugin(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { } func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { - //if defaults.GetMetricPluginLocation() == "" { - // return nil, fmt.Errorf("no plugin location specified") - //} - var handshakeConfig = goPlugin.HandshakeConfig{ ProtocolVersion: 1, MagicCookieKey: "ARGO_ROLLOUTS_RPC_PLUGIN", @@ -51,13 +45,16 @@ func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.M //There should only ever be one plugin defined in metric.Provider.Plugin for pluginName := range metric.Provider.Plugin { - plugin.GetPluginLocation(pluginName) + pluginPath, err := plugin.GetPluginLocation(pluginName) + if err != nil { + return nil, err + } if m.pluginClient[pluginName] == nil || m.pluginClient[pluginName].Exited() { m.pluginClient[pluginName] = goPlugin.NewClient(&goPlugin.ClientConfig{ HandshakeConfig: handshakeConfig, Plugins: pluginMap, - Cmd: exec.Command(filepath.Join("/tmp", pluginName)), + Cmd: exec.Command(pluginPath), Managed: true, }) diff --git a/utils/config/config.go b/utils/config/config.go old mode 100755 new mode 100644 index 8f3f74f2bf..730c013874 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -2,6 +2,7 @@ package config import ( "fmt" + "github.com/argoproj/argo-rollouts/utils/defaults" "github.com/argoproj/argo-rollouts/utils/plugin/types" "github.com/ghodss/yaml" diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index a7d5f8012c..7a3201241f 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -3,13 +3,14 @@ package config import ( "crypto/sha256" "fmt" - log "github.com/sirupsen/logrus" "io" "net/http" "net/url" "os" "path/filepath" "time" + + log "github.com/sirupsen/logrus" ) type FileDownloader interface { diff --git a/utils/config/plugin_downloader_test.go b/utils/config/plugin_downloader_test.go index 785cc6da63..e96af1d40b 100644 --- a/utils/config/plugin_downloader_test.go +++ b/utils/config/plugin_downloader_test.go @@ -2,16 +2,17 @@ package config import ( "bytes" + "io" + "net/http" + "os" + "testing" + "github.com/argoproj/argo-rollouts/utils/defaults" "github.com/tj/assert" - "io" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" - "net/http" - "os" - "testing" ) type MockFileDownloader struct { diff --git a/utils/defaults/defaults_test.go b/utils/defaults/defaults_test.go index 7ffa9e9157..2162f1be82 100644 --- a/utils/defaults/defaults_test.go +++ b/utils/defaults/defaults_test.go @@ -411,19 +411,4 @@ func TestSetDefaults(t *testing.T) { assert.Equal(t, DefaultMetricCleanupDelay, int32(GetMetricCleanupDelaySeconds().Seconds())) SetMetricCleanupDelaySeconds(24) assert.Equal(t, time.Duration(24)*time.Second, GetMetricCleanupDelaySeconds()) - - assert.Equal(t, DefaultMetricsPluginLocation, "") - err := SetMetricPluginLocation("file:///tmp/metric-plugin") - assert.NoError(t, err) - assert.Equal(t, "/tmp/metric-plugin", GetMetricPluginLocation()) - - assert.Equal(t, DefaultMetricsPluginLocation, "") - err = SetMetricPluginLocation("http://localhost:8080/metric-plugin") - assert.NoError(t, err) - assert.Equal(t, DefaultPluginHttpFileLocation, GetMetricPluginLocation()) - - err = SetMetricPluginLocation("foo://localhost:8080/metric-plugin") - assert.NoError(t, err) - urlStr := GetMetricPluginLocation() - assert.Equal(t, "", urlStr) } diff --git a/utils/plugin/plugin.go b/utils/plugin/plugin.go index 95daa6a637..e385d2bbaf 100644 --- a/utils/plugin/plugin.go +++ b/utils/plugin/plugin.go @@ -2,8 +2,9 @@ package plugin import ( "fmt" - "github.com/argoproj/argo-rollouts/utils/config" "path/filepath" + + "github.com/argoproj/argo-rollouts/utils/config" ) func GetPluginLocation(pluginName string) (string, error) { From eb38b7572f2b7313754c44c5415d1f8d3234897b Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 15:32:47 -0600 Subject: [PATCH 14/53] downloading tests Signed-off-by: zachaller --- utils/config/config.go | 0 utils/config/plugin_downloader.go | 45 +++++++++++++++----------- utils/config/plugin_downloader_test.go | 24 ++++++++++---- utils/defaults/defaults.go | 2 ++ 4 files changed, 47 insertions(+), 24 deletions(-) mode change 100644 => 100755 utils/config/config.go diff --git a/utils/config/config.go b/utils/config/config.go old mode 100644 new mode 100755 diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index 7a3201241f..6ee20af6c6 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -10,6 +10,8 @@ import ( "path/filepath" "time" + "github.com/argoproj/argo-rollouts/utils/defaults" + log "github.com/sirupsen/logrus" ) @@ -87,9 +89,15 @@ func initMetricsPlugins(fd FileDownloader) error { if err != nil { return err } + + err = os.MkdirAll(defaults.DefaultRolloutPluginFolder, 0700) + if err != nil { + return err + } + for _, plugin := range config.GetMetricPluginsConfig() { urlObj, err := url.ParseRequestURI(plugin.PluginLocation) - finalFileLocation := filepath.Join("/tmp", plugin.Name) + finalFileLocation := filepath.Join(defaults.DefaultRolloutPluginFolder, plugin.Name) if err != nil { return err } @@ -141,26 +149,27 @@ func initMetricsPlugins(fd FileDownloader) error { // CopyFile copies a file from src to dst. If src and dst files exist, and are // the same, then return success. Otherise, attempt to create a hard link // between the two files. If that fail, copy the file contents from src to dst. -func copyFile(src, dst string) (err error) { - sfi, err := os.Stat(src) +func copyFile(src, dst string) error { + sourceFileStat, err := os.Stat(src) if err != nil { - return + return err + } + + if !sourceFileStat.Mode().IsRegular() { + return fmt.Errorf("%s is not a regular file", src) } - dfi, err := os.Stat(dst) + + source, err := os.Open(src) if err != nil { - if !os.IsNotExist(err) { - return - } - } else { - if !(dfi.Mode().IsRegular()) { - return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String()) - } - if os.SameFile(sfi, dfi) { - return - } + return err } - if err = os.Link(src, dst); err == nil { - return + defer source.Close() + + destination, err := os.Create(dst) + if err != nil { + return err } - return + defer destination.Close() + _, err = io.Copy(destination, source) + return err } diff --git a/utils/config/plugin_downloader_test.go b/utils/config/plugin_downloader_test.go index e96af1d40b..0dad8642e3 100644 --- a/utils/config/plugin_downloader_test.go +++ b/utils/config/plugin_downloader_test.go @@ -5,6 +5,7 @@ import ( "io" "net/http" "os" + "path/filepath" "testing" "github.com/argoproj/argo-rollouts/utils/defaults" @@ -54,7 +55,13 @@ func TestInitPlugin(t *testing.T) { _, err = InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config", fd) assert.NoError(t, err) - os.Remove("/tmp/http") + filepath.Join(defaults.DefaultRolloutPluginFolder, "http") + err = os.Remove(filepath.Join(defaults.DefaultRolloutPluginFolder, "http")) + assert.NoError(t, err) + err = os.Remove(filepath.Join(defaults.DefaultRolloutPluginFolder, "http-sha")) + assert.NoError(t, err) + err = os.RemoveAll(defaults.DefaultRolloutPluginFolder) + assert.NoError(t, err) } func TestInitPluginBadSha(t *testing.T) { @@ -78,7 +85,10 @@ func TestInitPluginBadSha(t *testing.T) { _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) assert.Error(t, err) - os.Remove("/tmp/http-badsha") + err = os.Remove(filepath.Join(defaults.DefaultRolloutPluginFolder, "http-badsha")) + assert.NoError(t, err) + err = os.RemoveAll(defaults.DefaultRolloutPluginFolder) + assert.NoError(t, err) } func TestInitPluginConfigNotFound(t *testing.T) { @@ -115,7 +125,9 @@ func TestFileMove(t *testing.T) { _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) assert.NoError(t, err) - err = os.Remove("/tmp/file-plugin") + err = os.Remove(filepath.Join(defaults.DefaultRolloutPluginFolder, "file-plugin")) + assert.NoError(t, err) + err = os.RemoveAll(defaults.DefaultRolloutPluginFolder) assert.NoError(t, err) } @@ -143,7 +155,9 @@ func TestDoubleInit(t *testing.T) { _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) assert.NoError(t, err) - err = os.Remove("/tmp/file-plugin") + err = os.Remove(filepath.Join(defaults.DefaultRolloutPluginFolder, "file-plugin")) + assert.NoError(t, err) + err = os.RemoveAll(defaults.DefaultRolloutPluginFolder) assert.NoError(t, err) } @@ -173,6 +187,4 @@ func TestBadConfigMap(t *testing.T) { _, err = InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config", fd) assert.Error(t, err) - - os.Remove("/tmp/http") } diff --git a/utils/defaults/defaults.go b/utils/defaults/defaults.go index 92a6947275..b5ce8d03a8 100644 --- a/utils/defaults/defaults.go +++ b/utils/defaults/defaults.go @@ -47,6 +47,8 @@ const ( DefaultMetricCleanupDelay = int32(65) // DefaultRolloutsConfigMapName is the default name of the ConfigMap that contains the Rollouts controller configuration DefaultRolloutsConfigMapName = "argo-rollouts-config" + // DefaultRolloutPluginFolder is the default location where plugins will be downloaded and/or moved to. + DefaultRolloutPluginFolder = "plugin-bin" ) const ( From cbbc238958eb0055760e9e23a4c77125aa9e0388 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 16:12:41 -0600 Subject: [PATCH 15/53] add one more test Signed-off-by: zachaller --- Makefile | 2 +- utils/config/plugin_downloader.go | 9 ++++----- utils/config/plugin_downloader_test.go | 22 ++++++++++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index ac3ba0b1bf..fda72dde50 100644 --- a/Makefile +++ b/Makefile @@ -225,7 +225,7 @@ test-e2e: install-devtools-local .PHONY: test-unit test-unit: install-devtools-local - ${DIST_DIR}/gotestsum --junitfile=junit.xml --format=testname -- -covermode=count -coverprofile=coverage.out `go list ./... | grep -v ./cmd/sample-metrics-plugin` + ${DIST_DIR}/gotestsum --junitfile=junit.xml --format=testname -- -covermode=count -coverprofile=coverage.out `go list ./... | grep -v ./test/cmd/sample-metrics-plugin` .PHONY: coverage diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index 6ee20af6c6..d3ef2c1788 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -27,7 +27,7 @@ func (fd FileDownloaderImpl) Get(url string) (resp *http.Response, err error) { return http.Get(url) } -// CheckPluginExists this function checks if the plugin exists in the configured path if not we panic +// checkPluginExists this function checks if the plugin exists in the configured path if not we panic func checkPluginExists(pluginLocation string) error { if pluginLocation != "" { //Check for plugin executable existence @@ -97,11 +97,12 @@ func initMetricsPlugins(fd FileDownloader) error { for _, plugin := range config.GetMetricPluginsConfig() { urlObj, err := url.ParseRequestURI(plugin.PluginLocation) - finalFileLocation := filepath.Join(defaults.DefaultRolloutPluginFolder, plugin.Name) if err != nil { return err } + finalFileLocation := filepath.Join(defaults.DefaultRolloutPluginFolder, plugin.Name) + switch urlObj.Scheme { case "http", "https": log.Printf("Downloading plugin from: %s", plugin.PluginLocation) @@ -146,9 +147,7 @@ func initMetricsPlugins(fd FileDownloader) error { return nil } -// CopyFile copies a file from src to dst. If src and dst files exist, and are -// the same, then return success. Otherise, attempt to create a hard link -// between the two files. If that fail, copy the file contents from src to dst. +// CopyFile copies a file from src to dst. func copyFile(src, dst string) error { sourceFileStat, err := os.Stat(src) if err != nil { diff --git a/utils/config/plugin_downloader_test.go b/utils/config/plugin_downloader_test.go index 0dad8642e3..3d1e3aa5e0 100644 --- a/utils/config/plugin_downloader_test.go +++ b/utils/config/plugin_downloader_test.go @@ -188,3 +188,25 @@ func TestBadConfigMap(t *testing.T) { _, err = InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config", fd) assert.Error(t, err) } + +func TestBadLocation(t *testing.T) { + fd := &MockFileDownloader{} + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argo-rollouts-config", + Namespace: "argo-rollouts", + }, + Data: map[string]string{"plugins": "metrics:\n - name: http\n pluginLocation: agwegasdlkjf2324"}, + } + client := fake.NewSimpleClientset(cm) + i := informers.NewSharedInformerFactory(client, 0) + i.Start(make(chan struct{})) + cmi := i.Core().V1().ConfigMaps() + go cmi.Informer().Run(make(chan struct{})) + + err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) + assert.NoError(t, err) + + _, err = InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config", fd) + assert.Error(t, err) +} From 037f49c68ac2c3e9eea73769b1de326362f1b993 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 16:25:57 -0600 Subject: [PATCH 16/53] better error msg's Signed-off-by: zachaller --- metricproviders/plugin/client/client.go | 10 +++++----- utils/config/config.go | 6 +++--- utils/config/plugin_downloader.go | 18 +++++++++--------- utils/plugin/plugin.go | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/metricproviders/plugin/client/client.go b/metricproviders/plugin/client/client.go index e9c5ec6fc0..faa0713c42 100644 --- a/metricproviders/plugin/client/client.go +++ b/metricproviders/plugin/client/client.go @@ -26,7 +26,7 @@ func GetMetricPlugin(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { } plugin, err := singletonPluginClient.startPluginSystem(metric) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to start plugin system: %w", err) } return plugin, nil } @@ -47,7 +47,7 @@ func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.M for pluginName := range metric.Provider.Plugin { pluginPath, err := plugin.GetPluginLocation(pluginName) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to find plugin %s: %w", pluginName, err) } if m.pluginClient[pluginName] == nil || m.pluginClient[pluginName].Exited() { @@ -60,13 +60,13 @@ func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.M rpcClient, err := m.pluginClient[pluginName].Client() if err != nil { - return nil, err + return nil, fmt.Errorf("unable to start plugin %s: %w", pluginName, err) } // Request the plugin raw, err := rpcClient.Dispense("RpcMetricsPlugin") if err != nil { - return nil, err + return nil, fmt.Errorf("unable to dispense plugin %s: %w", pluginName, err) } pluginType, ok := raw.(rpc.MetricsPlugin) @@ -77,7 +77,7 @@ func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.M err = m.plugin[pluginName].NewMetricsPlugin(metric) if err.Error() != "" { - return nil, err + return nil, fmt.Errorf("unable to initialize plugin via rpc %s: %w", pluginName, err) } } diff --git a/utils/config/config.go b/utils/config/config.go index 730c013874..84b0a4ceb9 100755 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -27,12 +27,12 @@ func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapNa // If the configmap is not found, we return return nil, nil } - return nil, err + return nil, fmt.Errorf("failed to get configmap %s/%s: %w", defaults.Namespace(), configMapName, err) } plugins := types.Plugin{} if err = yaml.Unmarshal([]byte(configMapCluster.Data["plugins"]), &plugins); err != nil { - return nil, err + return nil, fmt.Errorf("failed to unmarshal plugins while initalizing: %w", err) } configMemoryCache = &Config{ @@ -49,7 +49,7 @@ func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapNa // GetConfig returns the initialized in memory config object if it exists otherwise errors if InitializeConfig has not been called. func GetConfig() (*Config, error) { if configMemoryCache == nil { - return nil, fmt.Errorf("config not initialized") + return nil, fmt.Errorf("config not initialized, please initialize before use") } return configMemoryCache, nil } diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index d3ef2c1788..1fa2db9772 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -87,37 +87,37 @@ func downloadFile(filepath string, url string, downloader FileDownloader) error func initMetricsPlugins(fd FileDownloader) error { config, err := GetConfig() if err != nil { - return err + return fmt.Errorf("failed to get config: %w", err } err = os.MkdirAll(defaults.DefaultRolloutPluginFolder, 0700) if err != nil { - return err + return fmt.Errorf("failed to create plugin folder: %w", err) } for _, plugin := range config.GetMetricPluginsConfig() { urlObj, err := url.ParseRequestURI(plugin.PluginLocation) if err != nil { - return err + return fmt.Errorf("failed to parse plugin location: %w", err) } finalFileLocation := filepath.Join(defaults.DefaultRolloutPluginFolder, plugin.Name) switch urlObj.Scheme { case "http", "https": - log.Printf("Downloading plugin from: %s", plugin.PluginLocation) + log.Infof("Downloading plugin from: %s", plugin.PluginLocation) startTime := time.Now() err = downloadFile(finalFileLocation, urlObj.String(), fd) if err != nil { - return err + return fmt.Errorf("failed to download plugin from %s: %w", plugin.PluginLocation, err) } timeTakenToDownload := time.Now().Sub(startTime) - log.Printf("Download complete, it took %s", timeTakenToDownload) + log.Infof("Download complete, it took %s", timeTakenToDownload) if plugin.PluginSha256 != "" { sha256Matched, err := checkShaOfPlugin(finalFileLocation, plugin.PluginSha256) if err != nil { - return err + return fmt.Errorf("failed to check sha256 of downloaded plugin: %w", err) } if !sha256Matched { return fmt.Errorf("sha256 hash of downloaded plugin (%s) does not match expected hash", plugin.PluginLocation) @@ -130,11 +130,11 @@ func initMetricsPlugins(fd FileDownloader) error { case "file": pluginPath, err := filepath.Abs(urlObj.Host + urlObj.Path) if err != nil { - return err + return fmt.Errorf("failed to get absolute path of plugin: %w", err) } if err = copyFile(pluginPath, finalFileLocation); err != nil { - return err + return fmt.Errorf("failed to copy plugin from %s to %s: %w", pluginPath, finalFileLocation, err) } if checkPluginExists(finalFileLocation) != nil { return fmt.Errorf("failed to find plugin at location: %s", plugin.PluginLocation) diff --git a/utils/plugin/plugin.go b/utils/plugin/plugin.go index e385d2bbaf..c99a717b97 100644 --- a/utils/plugin/plugin.go +++ b/utils/plugin/plugin.go @@ -10,7 +10,7 @@ import ( func GetPluginLocation(pluginName string) (string, error) { configMap, err := config.GetConfig() if err != nil { - return "", err + return "", fmt.Errorf("failed to get config: %w", err) } for _, item := range configMap.GetMetricPluginsConfig() { From 9b93bce3d20a087635afbf6e548bc44ccdadaf42 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 16:26:56 -0600 Subject: [PATCH 17/53] cleanup Signed-off-by: zachaller --- cmd/rollouts-controller/main.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cmd/rollouts-controller/main.go b/cmd/rollouts-controller/main.go index 997a6d54c1..671cc8681d 100644 --- a/cmd/rollouts-controller/main.go +++ b/cmd/rollouts-controller/main.go @@ -198,16 +198,6 @@ func newCommand() *cobra.Command { controllerNamespaceInformerFactory, jobInformerFactory) - //err = rolloutsConfig.InitConfig(configMapInformer, "argo-rollouts-config") - //if err != nil { - // log.Fatalf("Failed to init config: %v", err) - //} - // - //err = plugin.InitMetricsPlugins(ctx, kubeClient, "argo-rollouts-config", plugin.FileDownloaderImpl{}) - //if err != nil { - // log.Fatalf("Failed to init metric plugin: %v", err) - //} - if err = cm.Run(ctx, rolloutThreads, serviceThreads, ingressThreads, experimentThreads, analysisThreads, electOpts); err != nil { log.Fatalf("Error running controller: %s", err.Error()) } From 893e2153c1dfb8aaa67cfb156c3325ab16623032 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 16:30:39 -0600 Subject: [PATCH 18/53] more errors and lint Signed-off-by: zachaller --- utils/config/config.go | 2 +- utils/config/plugin_downloader.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) mode change 100755 => 100644 utils/config/config.go diff --git a/utils/config/config.go b/utils/config/config.go old mode 100755 new mode 100644 index 84b0a4ceb9..fb9468c704 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -32,7 +32,7 @@ func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapNa plugins := types.Plugin{} if err = yaml.Unmarshal([]byte(configMapCluster.Data["plugins"]), &plugins); err != nil { - return nil, fmt.Errorf("failed to unmarshal plugins while initalizing: %w", err) + return nil, fmt.Errorf("failed to unmarshal plugins while initializing: %w", err) } configMemoryCache = &Config{ diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index 1fa2db9772..22c436c2d2 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -87,7 +87,7 @@ func downloadFile(filepath string, url string, downloader FileDownloader) error func initMetricsPlugins(fd FileDownloader) error { config, err := GetConfig() if err != nil { - return fmt.Errorf("failed to get config: %w", err + return fmt.Errorf("failed to get config: %w", err) } err = os.MkdirAll(defaults.DefaultRolloutPluginFolder, 0700) @@ -151,7 +151,7 @@ func initMetricsPlugins(fd FileDownloader) error { func copyFile(src, dst string) error { sourceFileStat, err := os.Stat(src) if err != nil { - return err + return fmt.Errorf("failed to get file stat of %s: %w", src, err) } if !sourceFileStat.Mode().IsRegular() { @@ -160,15 +160,15 @@ func copyFile(src, dst string) error { source, err := os.Open(src) if err != nil { - return err + return fmt.Errorf("failed to open source file %s: %w", src, err) } defer source.Close() destination, err := os.Create(dst) if err != nil { - return err + return fmt.Errorf("failed to create destination file %s: %w", dst, err) } defer destination.Close() _, err = io.Copy(destination, source) - return err + return fmt.Errorf("failed to copy file from %s to %s: %w", src, dst, err) } From 6130621e3f556699ae8865ed75078c53c511ed54 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 16:33:27 -0600 Subject: [PATCH 19/53] add in configmap Signed-off-by: zachaller --- manifests/base/argo-rollouts-config.yaml | 9 +++++++++ manifests/base/kustomization.yaml | 1 + utils/config/plugin_downloader.go | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 manifests/base/argo-rollouts-config.yaml diff --git a/manifests/base/argo-rollouts-config.yaml b/manifests/base/argo-rollouts-config.yaml new file mode 100644 index 0000000000..c7455b2982 --- /dev/null +++ b/manifests/base/argo-rollouts-config.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: argo-rollouts-config + labels: + app.kubernetes.io/component: rollouts-controller + app.kubernetes.io/name: argo-rollouts + app.kubernetes.io/part-of: argo-rollouts +data: diff --git a/manifests/base/kustomization.yaml b/manifests/base/kustomization.yaml index 663d7af691..fd6bb3e311 100644 --- a/manifests/base/kustomization.yaml +++ b/manifests/base/kustomization.yaml @@ -7,6 +7,7 @@ resources: - argo-rollouts-aggregate-roles.yaml - argo-rollouts-metrics-service.yaml - argo-rollouts-notification-secret.yaml +- argo-rollouts-config.yaml images: - name: quay.io/argoproj/argo-rollouts newTag: latest diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index 22c436c2d2..dc89c5b1b7 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -33,7 +33,7 @@ func checkPluginExists(pluginLocation string) error { //Check for plugin executable existence _, err := os.Stat(pluginLocation) if err != nil { - return err + return fmt.Errorf("plugin stat file at %s", pluginLocation) } } return nil From af0f9fa24f8e7b5a731c4bf5fcd6a40bb3424012 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 16:36:30 -0600 Subject: [PATCH 20/53] codegen Signed-off-by: zachaller --- manifests/base/argo-rollouts-config.yaml | 6 ++++++ manifests/install.yaml | 16 ++++++++++++++++ manifests/namespace-install.yaml | 16 ++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/manifests/base/argo-rollouts-config.yaml b/manifests/base/argo-rollouts-config.yaml index c7455b2982..c9349d0fed 100644 --- a/manifests/base/argo-rollouts-config.yaml +++ b/manifests/base/argo-rollouts-config.yaml @@ -7,3 +7,9 @@ metadata: app.kubernetes.io/name: argo-rollouts app.kubernetes.io/part-of: argo-rollouts data: + plugins: |- + metrics: + - name: "prometheus" + pluginLocation: "file:///Users/zaller/Development/argo-rollouts/metric-plugin" + - name: "prometheus-remote" + pluginLocation: "https://github.com/argoproj-labs/sample-rollouts-metric-plugin/releases/download/v0.0.2/metric-plugin-darwin-arm64" diff --git a/manifests/install.yaml b/manifests/install.yaml index 29c7693202..bc3a0f200e 100755 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -14830,6 +14830,22 @@ subjects: namespace: argo-rollouts --- apiVersion: v1 +data: + plugins: |- + metrics: + - name: "prometheus" + pluginLocation: "file:///Users/zaller/Development/argo-rollouts/metric-plugin" + - name: "prometheus-remote" + pluginLocation: "https://github.com/argoproj-labs/sample-rollouts-metric-plugin/releases/download/v0.0.2/metric-plugin-darwin-arm64" +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: rollouts-controller + app.kubernetes.io/name: argo-rollouts + app.kubernetes.io/part-of: argo-rollouts + name: argo-rollouts-config +--- +apiVersion: v1 kind: Secret metadata: name: argo-rollouts-notification-secret diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 7e883a0d85..f1e9557501 100755 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -332,6 +332,22 @@ subjects: name: argo-rollouts --- apiVersion: v1 +data: + plugins: |- + metrics: + - name: "prometheus" + pluginLocation: "file:///Users/zaller/Development/argo-rollouts/metric-plugin" + - name: "prometheus-remote" + pluginLocation: "https://github.com/argoproj-labs/sample-rollouts-metric-plugin/releases/download/v0.0.2/metric-plugin-darwin-arm64" +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: rollouts-controller + app.kubernetes.io/name: argo-rollouts + app.kubernetes.io/part-of: argo-rollouts + name: argo-rollouts-config +--- +apiVersion: v1 kind: Secret metadata: name: argo-rollouts-notification-secret From 29ace7cd9f26dbe50f2550390a5c31c1c72e896f Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 16:38:44 -0600 Subject: [PATCH 21/53] cleanup configmap Signed-off-by: zachaller --- manifests/base/argo-rollouts-config.yaml | 7 ------- manifests/install.yaml | 7 ------- manifests/namespace-install.yaml | 7 ------- 3 files changed, 21 deletions(-) diff --git a/manifests/base/argo-rollouts-config.yaml b/manifests/base/argo-rollouts-config.yaml index c9349d0fed..4f679092b9 100644 --- a/manifests/base/argo-rollouts-config.yaml +++ b/manifests/base/argo-rollouts-config.yaml @@ -6,10 +6,3 @@ metadata: app.kubernetes.io/component: rollouts-controller app.kubernetes.io/name: argo-rollouts app.kubernetes.io/part-of: argo-rollouts -data: - plugins: |- - metrics: - - name: "prometheus" - pluginLocation: "file:///Users/zaller/Development/argo-rollouts/metric-plugin" - - name: "prometheus-remote" - pluginLocation: "https://github.com/argoproj-labs/sample-rollouts-metric-plugin/releases/download/v0.0.2/metric-plugin-darwin-arm64" diff --git a/manifests/install.yaml b/manifests/install.yaml index bc3a0f200e..af6c126c67 100755 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -14830,13 +14830,6 @@ subjects: namespace: argo-rollouts --- apiVersion: v1 -data: - plugins: |- - metrics: - - name: "prometheus" - pluginLocation: "file:///Users/zaller/Development/argo-rollouts/metric-plugin" - - name: "prometheus-remote" - pluginLocation: "https://github.com/argoproj-labs/sample-rollouts-metric-plugin/releases/download/v0.0.2/metric-plugin-darwin-arm64" kind: ConfigMap metadata: labels: diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index f1e9557501..1972b411b8 100755 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -332,13 +332,6 @@ subjects: name: argo-rollouts --- apiVersion: v1 -data: - plugins: |- - metrics: - - name: "prometheus" - pluginLocation: "file:///Users/zaller/Development/argo-rollouts/metric-plugin" - - name: "prometheus-remote" - pluginLocation: "https://github.com/argoproj-labs/sample-rollouts-metric-plugin/releases/download/v0.0.2/metric-plugin-darwin-arm64" kind: ConfigMap metadata: labels: From 37295e9ab30ed8c70dfab14712402a140c40731a Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 16:56:00 -0600 Subject: [PATCH 22/53] fix file copy error Signed-off-by: zachaller --- .gitignore | 1 + utils/config/plugin_downloader.go | 20 ++++++++++++++------ utils/plugin/plugin.go | 9 +++++++-- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index db184381d6..1a22b8eef6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ junit.xml rerunreport.txt site/ vendor/ +plugin-bin/ # static server/static/* !server/static/.gitkeep diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index dc89c5b1b7..625b0deb7b 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -90,7 +90,12 @@ func initMetricsPlugins(fd FileDownloader) error { return fmt.Errorf("failed to get config: %w", err) } - err = os.MkdirAll(defaults.DefaultRolloutPluginFolder, 0700) + absoluteFilepath, err := filepath.Abs(defaults.DefaultRolloutPluginFolder) + if err != nil { + return fmt.Errorf("failed to get absolute path of plugin folder: %w", err) + } + + err = os.MkdirAll(absoluteFilepath, 0700) if err != nil { return fmt.Errorf("failed to create plugin folder: %w", err) } @@ -101,7 +106,7 @@ func initMetricsPlugins(fd FileDownloader) error { return fmt.Errorf("failed to parse plugin location: %w", err) } - finalFileLocation := filepath.Join(defaults.DefaultRolloutPluginFolder, plugin.Name) + finalFileLocation := filepath.Join(absoluteFilepath, plugin.Name) switch urlObj.Scheme { case "http", "https": @@ -124,7 +129,7 @@ func initMetricsPlugins(fd FileDownloader) error { } } if checkPluginExists(finalFileLocation) != nil { - return fmt.Errorf("failed to find plugin at location: %s", plugin.PluginLocation) + return fmt.Errorf("failed to find downloaded plugin at location: %s", plugin.PluginLocation) } case "file": @@ -133,11 +138,11 @@ func initMetricsPlugins(fd FileDownloader) error { return fmt.Errorf("failed to get absolute path of plugin: %w", err) } - if err = copyFile(pluginPath, finalFileLocation); err != nil { + if err := copyFile(pluginPath, finalFileLocation); err != nil { return fmt.Errorf("failed to copy plugin from %s to %s: %w", pluginPath, finalFileLocation, err) } if checkPluginExists(finalFileLocation) != nil { - return fmt.Errorf("failed to find plugin at location: %s", plugin.PluginLocation) + return fmt.Errorf("failed to find filebased plugin at location: %s", plugin.PluginLocation) } default: return fmt.Errorf("plugin location must be of http(s) or file scheme") @@ -170,5 +175,8 @@ func copyFile(src, dst string) error { } defer destination.Close() _, err = io.Copy(destination, source) - return fmt.Errorf("failed to copy file from %s to %s: %w", src, dst, err) + if err != nil { + return fmt.Errorf("failed to copy file from %s to %s: %w", src, dst, err) + } + return nil } diff --git a/utils/plugin/plugin.go b/utils/plugin/plugin.go index c99a717b97..0ce19586c3 100644 --- a/utils/plugin/plugin.go +++ b/utils/plugin/plugin.go @@ -2,6 +2,7 @@ package plugin import ( "fmt" + "github.com/argoproj/argo-rollouts/utils/defaults" "path/filepath" "github.com/argoproj/argo-rollouts/utils/config" @@ -15,8 +16,12 @@ func GetPluginLocation(pluginName string) (string, error) { for _, item := range configMap.GetMetricPluginsConfig() { if pluginName == item.Name { - return filepath.Join("/tmp", item.Name), nil + asbFilePath, err := filepath.Abs(filepath.Join(defaults.DefaultRolloutPluginFolder, item.Name)) + if err != nil { + return "", fmt.Errorf("failed to get absolute path of plugin folder: %w", err) + } + return asbFilePath, nil } } - return "", fmt.Errorf("plugin %s not configured", pluginName) + return "", fmt.Errorf("plugin %s not configured in configmap", pluginName) } From d6445d58521eeda76998c94cad4c0c4debb08843 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 16:59:52 -0600 Subject: [PATCH 23/53] set file perms Signed-off-by: zachaller --- utils/config/plugin_downloader.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index 625b0deb7b..d695e15d2f 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -144,6 +144,11 @@ func initMetricsPlugins(fd FileDownloader) error { if checkPluginExists(finalFileLocation) != nil { return fmt.Errorf("failed to find filebased plugin at location: %s", plugin.PluginLocation) } + // Set the file permissions, to allow execution + err = os.Chmod(finalFileLocation, 0700) + if err != nil { + return fmt.Errorf("failed to set file permissions of plugin (%s): %w", finalFileLocation, err) + } default: return fmt.Errorf("plugin location must be of http(s) or file scheme") } From de7d59d86a66213fc84a90d6d5503b54037d7944 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 18:25:25 -0600 Subject: [PATCH 24/53] switch from scratch to distroless Signed-off-by: zachaller --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c2809aba6b..0fff9cf749 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,7 +84,7 @@ CMD ["dashboard"] #################################################################################################### # Final image #################################################################################################### -FROM scratch +FROM gcr.io/distroless/static-debian11 COPY --from=argo-rollouts-build /go/src/github.com/argoproj/argo-rollouts/dist/rollouts-controller /bin/ COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ From 2fab44bdaebeccb899c7fd1c3c0670d4f6ea33b2 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 20:22:34 -0600 Subject: [PATCH 25/53] update docs Signed-off-by: zachaller --- docs/analysis/plugins.md | 42 +++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/docs/analysis/plugins.md b/docs/analysis/plugins.md index 1d65bc8887..bca5e653e2 100644 --- a/docs/analysis/plugins.md +++ b/docs/analysis/plugins.md @@ -3,13 +3,13 @@ !!! important Available since v1.5 Argo Rollouts supports getting analysis metrics via 3rd party plugin system. This allows users to extend the capabilities of Rollouts -to support metrics that are not natively supported by Rollouts. Rollouts uses a plugin library called +to support metric providers that are not natively supported. Rollouts uses a plugin library called [go-plugin](https://github.com/hashicorp/go-plugin) to do this. You can find a sample plugin here: [sample-rollouts-metric-plugin](https://github.com/argoproj-labs/sample-rollouts-metric-plugin) ## Using a Metric Plugin -There are two methods to installing and using an argo rollouts plugin. The first method is to mount up the plugin executable +There are two methods of installing and using an argo rollouts plugin. The first method is to mount up the plugin executable into the rollouts controller container. The second method is to use a HTTP(S) server to host the plugin executable. ### Mounting the plugin executable into the rollouts controller container @@ -24,20 +24,36 @@ particular infrastructure. Here are a few methods: * Using a Kubernetes volume mount with a shared volume such as NFS, EBS, etc. * Building the plugin into the rollouts controller container -### Using a HTTP(S) server to host the plugin executable - -Argo Rollouts supports downloading the plugin executable from a HTTP(S) server. To use this method, you will need to specify the -`--metric-plugin-location` flag to point to the HTTP(S) server that hosts the plugin executable. Example: - -```bash -./rollouts-controller --metric-plugin-location=https://example.com/metric-plugin +Then you can use setup the configmap to point to the plugin executable. Example: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argo-rollouts-config +data: + plugins: |- + metrics: + - name: "prometheus" # name of the plugin uses the name to find this configuration, it must match the name required by the plugin + pluginLocation: "file://./my-custom-plugin" # supports http(s):// urls and file:// ``` -You can also specify a sha256 checksum of the plugin executable. This is useful for ensuring the plugin executable has not been tampered with. -To use this feature, you will need to specify the `--metric-plugin-sha256` flag. Example: +### Using a HTTP(S) server to host the plugin executable -```bash -./rollouts-controller --metric-plugin-location=https://example.com/metric-plugin --metric-plugin-sha256=1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef +Argo Rollouts supports downloading the plugin executable from a HTTP(S) server. To use this method, you will need to +configure the controller via the `argo-rollouts-config` configmaps `pluginLocation` to an http(s) url. Example: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: argo-rollouts-config +data: + plugins: |- + metrics: + - name: "prometheus" # name of the plugin uses the name to find this configuration, it must match the name required by the plugin + pluginLocation: "https://github.com/argoproj-labs/sample-rollouts-metric-plugin/releases/download/v0.0.3/metric-plugin-linux-amd64" # supports http(s):// urls and file:// + pluginSha256: "08f588b1c799a37bbe8d0fc74cc1b1492dd70b2c" #optional sha256 checksum of the plugin executable ``` ## Some words of caution From 1a2cbff33380b445c8cb11f20b34522ed96877a0 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 21:20:13 -0600 Subject: [PATCH 26/53] lint Signed-off-by: zachaller --- utils/plugin/plugin.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/plugin/plugin.go b/utils/plugin/plugin.go index 0ce19586c3..4527b6f015 100644 --- a/utils/plugin/plugin.go +++ b/utils/plugin/plugin.go @@ -2,9 +2,10 @@ package plugin import ( "fmt" - "github.com/argoproj/argo-rollouts/utils/defaults" "path/filepath" + "github.com/argoproj/argo-rollouts/utils/defaults" + "github.com/argoproj/argo-rollouts/utils/config" ) From 44f1057812beb2e327f14b58cf0b4eec8427a41f Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 21:42:42 -0600 Subject: [PATCH 27/53] add godocs Signed-off-by: zachaller --- metric/provider.go | 2 +- metricproviders/plugin/client/client.go | 2 ++ metricproviders/plugin/plugin.go | 7 +++++++ metricproviders/plugin/rpc/rpc.go | 22 ++++++++++++++++++++++ utils/config/config.go | 2 ++ utils/config/plugin_downloader.go | 2 ++ utils/plugin/plugin.go | 2 ++ 7 files changed, 38 insertions(+), 1 deletion(-) diff --git a/metric/provider.go b/metric/provider.go index aba5ddbd0c..98da1acd30 100644 --- a/metric/provider.go +++ b/metric/provider.go @@ -7,7 +7,7 @@ type Provider interface { // Run start a new external system call for a measurement // Should be idempotent and do nothing if a call has already been started Run(*v1alpha1.AnalysisRun, v1alpha1.Metric) v1alpha1.Measurement - // Checks if the external system call is finished and returns the current measurement + // Resume Checks if the external system call is finished and returns the current measurement Resume(*v1alpha1.AnalysisRun, v1alpha1.Metric, v1alpha1.Measurement) v1alpha1.Measurement // Terminate will terminate an in-progress measurement Terminate(*v1alpha1.AnalysisRun, v1alpha1.Metric, v1alpha1.Measurement) v1alpha1.Measurement diff --git a/metricproviders/plugin/client/client.go b/metricproviders/plugin/client/client.go index faa0713c42..00d5719c57 100644 --- a/metricproviders/plugin/client/client.go +++ b/metricproviders/plugin/client/client.go @@ -17,6 +17,8 @@ type singletonMetricPlugin struct { var singletonPluginClient *singletonMetricPlugin +// GetMetricPlugin returns a singleton plugin client for the given metric plugin. Calling this multi +// returns the same plugin client for the same plugin name. func GetMetricPlugin(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { if singletonPluginClient == nil { singletonPluginClient = &singletonMetricPlugin{ diff --git a/metricproviders/plugin/plugin.go b/metricproviders/plugin/plugin.go index 2b6da693e6..e65c87625f 100644 --- a/metricproviders/plugin/plugin.go +++ b/metricproviders/plugin/plugin.go @@ -14,6 +14,7 @@ type MetricPlugin struct { metric.Provider } +// NewRpcPlugin returns a new RPC plugin with a singleton client func NewRpcPlugin(metric v1alpha1.Metric) (metric.Provider, error) { pluginClient, err := client.GetMetricPlugin(metric) if err != nil { @@ -23,18 +24,22 @@ func NewRpcPlugin(metric v1alpha1.Metric) (metric.Provider, error) { return MetricPlugin{plugin: pluginClient}, nil } +// Run calls the plugins run method and returns the current measurement func (m MetricPlugin) Run(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alpha1.Measurement { return m.plugin.Run(run, metric) } +// Resume calls the plugins resume method and returns the current measurement func (m MetricPlugin) Resume(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { return m.plugin.Resume(run, metric, measurement) } +// Terminate calls the plugins terminate method and returns the current measurement func (m MetricPlugin) Terminate(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { return m.plugin.Terminate(run, metric, measurement) } +// GarbageCollect calls the plugins garbage collect method func (m MetricPlugin) GarbageCollect(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, limit int) error { err := m.plugin.GarbageCollect(run, metric, limit) if err.Error() != "" { @@ -43,10 +48,12 @@ func (m MetricPlugin) GarbageCollect(run *v1alpha1.AnalysisRun, metric v1alpha1. return nil } +// Type returns the provider type func (m MetricPlugin) Type() string { return ProviderType } +// GetMetadata calls the plugins get metadata method func (m MetricPlugin) GetMetadata(metric v1alpha1.Metric) map[string]string { return m.plugin.GetMetadata(metric) } diff --git a/metricproviders/plugin/rpc/rpc.go b/metricproviders/plugin/rpc/rpc.go index 55320f257e..3d085002da 100644 --- a/metricproviders/plugin/rpc/rpc.go +++ b/metricproviders/plugin/rpc/rpc.go @@ -52,6 +52,8 @@ type MetricsPlugin interface { // MetricsPluginRPC Here is an implementation that talks over RPC type MetricsPluginRPC struct{ client *rpc.Client } +// NewMetricsPlugin is the client side function that is wrapped by a local provider this makes an rpc call to the +// server side function. func (g *MetricsPluginRPC) NewMetricsPlugin(metric v1alpha1.Metric) types.RpcError { var resp types.RpcError var args interface{} = InitMetricsPluginAndGetMetadataArgs{ @@ -64,6 +66,7 @@ func (g *MetricsPluginRPC) NewMetricsPlugin(metric v1alpha1.Metric) types.RpcErr return resp } +// Run is the client side function that is wrapped by a local provider this makes an rpc call to the server side function. func (g *MetricsPluginRPC) Run(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alpha1.Measurement { var resp v1alpha1.Measurement var args interface{} = RunArgs{ @@ -77,6 +80,7 @@ func (g *MetricsPluginRPC) Run(analysisRun *v1alpha1.AnalysisRun, metric v1alpha return resp } +// Resume is the client side function that is wrapped by a local provider this makes an rpc call to the server side function. func (g *MetricsPluginRPC) Resume(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { var resp v1alpha1.Measurement var args interface{} = TerminateAndResumeArgs{ @@ -91,6 +95,7 @@ func (g *MetricsPluginRPC) Resume(analysisRun *v1alpha1.AnalysisRun, metric v1al return resp } +// Terminate is the client side function that is wrapped by a local provider this makes an rpc call to the server side function. func (g *MetricsPluginRPC) Terminate(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { var resp v1alpha1.Measurement var args interface{} = TerminateAndResumeArgs{ @@ -105,6 +110,7 @@ func (g *MetricsPluginRPC) Terminate(analysisRun *v1alpha1.AnalysisRun, metric v return resp } +// GarbageCollect is the client side function that is wrapped by a local provider this makes an rpc call to the server side function. func (g *MetricsPluginRPC) GarbageCollect(analysisRun *v1alpha1.AnalysisRun, metric v1alpha1.Metric, limit int) types.RpcError { var resp types.RpcError var args interface{} = GarbageCollectArgs{ @@ -119,6 +125,7 @@ func (g *MetricsPluginRPC) GarbageCollect(analysisRun *v1alpha1.AnalysisRun, met return resp } +// Type is the client side function that is wrapped by a local provider this makes an rpc call to the server side function. func (g *MetricsPluginRPC) Type() string { var resp string err := g.client.Call("Plugin.Type", new(interface{}), &resp) @@ -129,6 +136,7 @@ func (g *MetricsPluginRPC) Type() string { return resp } +// GetMetadata is the client side function that is wrapped by a local provider this makes an rpc call to the server side function. func (g *MetricsPluginRPC) GetMetadata(metric v1alpha1.Metric) map[string]string { var resp map[string]string var args interface{} = InitMetricsPluginAndGetMetadataArgs{ @@ -149,6 +157,8 @@ type MetricsRPCServer struct { Impl MetricsPlugin } +// NewMetricsPlugin is the receiving end of the RPC call running in the plugin executable process (the server), and it calls the +// implementation of the plugin. func (s *MetricsRPCServer) NewMetricsPlugin(args interface{}, resp *types.RpcError) error { initArgs, ok := args.(*InitMetricsPluginAndGetMetadataArgs) if !ok { @@ -158,6 +168,8 @@ func (s *MetricsRPCServer) NewMetricsPlugin(args interface{}, resp *types.RpcErr return nil } +// Run is the receiving end of the RPC call running in the plugin executable process (the server), and it calls the +// implementation of the plugin. func (s *MetricsRPCServer) Run(args interface{}, resp *v1alpha1.Measurement) error { runArgs, ok := args.(*RunArgs) if !ok { @@ -167,6 +179,8 @@ func (s *MetricsRPCServer) Run(args interface{}, resp *v1alpha1.Measurement) err return nil } +// Resume is the receiving end of the RPC call running in the plugin executable process (the server), and it calls the +// implementation of the plugin. func (s *MetricsRPCServer) Resume(args interface{}, resp *v1alpha1.Measurement) error { resumeArgs, ok := args.(*TerminateAndResumeArgs) if !ok { @@ -176,6 +190,8 @@ func (s *MetricsRPCServer) Resume(args interface{}, resp *v1alpha1.Measurement) return nil } +// Terminate is the receiving end of the RPC call running in the plugin executable process (the server), and it calls the +// implementation of the plugin. func (s *MetricsRPCServer) Terminate(args interface{}, resp *v1alpha1.Measurement) error { resumeArgs, ok := args.(*TerminateAndResumeArgs) if !ok { @@ -185,6 +201,8 @@ func (s *MetricsRPCServer) Terminate(args interface{}, resp *v1alpha1.Measuremen return nil } +// GarbageCollect is the receiving end of the RPC call running in the plugin executable process (the server), and it calls the +// implementation of the plugin. func (s *MetricsRPCServer) GarbageCollect(args interface{}, resp *types.RpcError) error { gcArgs, ok := args.(*GarbageCollectArgs) if !ok { @@ -194,11 +212,15 @@ func (s *MetricsRPCServer) GarbageCollect(args interface{}, resp *types.RpcError return nil } +// Type is the receiving end of the RPC call running in the plugin executable process (the server), and it calls the +// implementation of the plugin. func (s *MetricsRPCServer) Type(args interface{}, resp *string) error { *resp = s.Impl.Type() return nil } +// GetMetadata is the receiving end of the RPC call running in the plugin executable process (the server), and it calls the +// implementation of the plugin. func (s *MetricsRPCServer) GetMetadata(args interface{}, resp *map[string]string) error { getMetadataArgs, ok := args.(*InitMetricsPluginAndGetMetadataArgs) if !ok { diff --git a/utils/config/config.go b/utils/config/config.go index fb9468c704..8f83a0d954 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -11,6 +11,7 @@ import ( informers "k8s.io/client-go/informers/core/v1" ) +// Config is the in memory representation of the configmap with some additional fields/functions for ease of use. type Config struct { configMap *v1.ConfigMap plugins types.Plugin @@ -54,6 +55,7 @@ func GetConfig() (*Config, error) { return configMemoryCache, nil } +// GetMetricPluginsConfig returns the metric plugins configured in the configmap func (c *Config) GetMetricPluginsConfig() []types.PluginItem { return configMemoryCache.plugins.Metrics } diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index d695e15d2f..0c02ea9f5f 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -15,10 +15,12 @@ import ( log "github.com/sirupsen/logrus" ) +// FileDownloader is an interface that allows us to mock the http.Get function type FileDownloader interface { Get(url string) (resp *http.Response, err error) } +// FileDownloaderImpl is the default/real implementation of the FileDownloader interface type FileDownloaderImpl struct { FileDownloader } diff --git a/utils/plugin/plugin.go b/utils/plugin/plugin.go index 4527b6f015..5555d4db0e 100644 --- a/utils/plugin/plugin.go +++ b/utils/plugin/plugin.go @@ -9,6 +9,8 @@ import ( "github.com/argoproj/argo-rollouts/utils/config" ) +// GetPluginLocation returns the location of the plugin on the filesystem via plugin name. If the plugin is not +// configured in the configmap, an error is returned. func GetPluginLocation(pluginName string) (string, error) { configMap, err := config.GetConfig() if err != nil { From 0d73d03961833038241cb13eb2835e7b00c3e4a3 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 22:28:16 -0600 Subject: [PATCH 28/53] put code back where it was Signed-off-by: zachaller --- controller/controller.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/controller/controller.go b/controller/controller.go index 419b0f5057..ad916a1930 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -461,6 +461,11 @@ func (c *Manager) startLeading(ctx context.Context, rolloutThreadiness, serviceT c.controllerNamespaceInformerFactory.Start(ctx.Done()) c.jobInformerFactory.Start(ctx.Done()) + // Check if Istio installed on cluster before starting dynamicInformerFactory + if istioutil.DoesIstioExist(c.istioPrimaryDynamicClient, c.namespace) { + c.istioDynamicInformerFactory.Start(ctx.Done()) + } + // Wait for the caches to be synced before starting workers log.Info("Waiting for controller's informer caches to sync") if ok := cache.WaitForCacheSync(ctx.Done(), c.serviceSynced, c.ingressSynced, c.jobSynced, c.rolloutSynced, c.experimentSynced, c.analysisRunSynced, c.analysisTemplateSynced, c.replicasSetSynced, c.configMapSynced, c.secretSynced); !ok { From 41e151a392e71711eb1d9093bb16d82e6cba4985 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 22:29:02 -0600 Subject: [PATCH 29/53] lint Signed-off-by: zachaller --- controller/controller.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index ad916a1930..d3c35743bb 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -9,9 +9,9 @@ import ( "sync" "time" - rolloutsConfig "github.com/argoproj/argo-rollouts/utils/config" istioutil "github.com/argoproj/argo-rollouts/utils/istio" + rolloutsConfig "github.com/argoproj/argo-rollouts/utils/config" goPlugin "github.com/hashicorp/go-plugin" "k8s.io/apimachinery/pkg/util/wait" @@ -483,11 +483,6 @@ func (c *Manager) startLeading(ctx context.Context, rolloutThreadiness, serviceT log.Fatalf("Failed to init config: %v", err) } - // Check if Istio installed on cluster before starting dynamicInformerFactory - if istioutil.DoesIstioExist(c.istioPrimaryDynamicClient, c.namespace) { - c.istioDynamicInformerFactory.Start(ctx.Done()) - } - go wait.Until(func() { c.wg.Add(1); c.rolloutController.Run(ctx, rolloutThreadiness); c.wg.Done() }, time.Second, ctx.Done()) go wait.Until(func() { c.wg.Add(1); c.serviceController.Run(ctx, serviceThreadiness); c.wg.Done() }, time.Second, ctx.Done()) go wait.Until(func() { c.wg.Add(1); c.ingressController.Run(ctx, ingressThreadiness); c.wg.Done() }, time.Second, ctx.Done()) From 0eac40a252ccf5e8940d72023f02e41cdc6e90f9 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 31 Jan 2023 23:25:26 -0600 Subject: [PATCH 30/53] remove embedded interface Signed-off-by: zachaller --- utils/config/plugin_downloader.go | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index 0c02ea9f5f..bea1a519a8 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -22,7 +22,6 @@ type FileDownloader interface { // FileDownloaderImpl is the default/real implementation of the FileDownloader interface type FileDownloaderImpl struct { - FileDownloader } func (fd FileDownloaderImpl) Get(url string) (resp *http.Response, err error) { From 7fea5b3fb86b3e88ffef9fa81f46e682c49efd88 Mon Sep 17 00:00:00 2001 From: zachaller Date: Wed, 1 Feb 2023 09:47:47 -0600 Subject: [PATCH 31/53] setup singleton config for when no configmap can be found Signed-off-by: zachaller --- utils/config/config.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/config/config.go b/utils/config/config.go index 8f83a0d954..70609e78db 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -25,8 +25,9 @@ func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapNa configMapCluster, err := configMapInformer.Lister().ConfigMaps(defaults.Namespace()).Get(configMapName) if err != nil { if k8errors.IsNotFound(err) { + configMemoryCache = &Config{} // We create an empty config so that we don't try to initialize again // If the configmap is not found, we return - return nil, nil + return configMemoryCache, nil } return nil, fmt.Errorf("failed to get configmap %s/%s: %w", defaults.Namespace(), configMapName, err) } From 7d13003f7438e94681602b41140937c6defa7881 Mon Sep 17 00:00:00 2001 From: zachaller Date: Wed, 1 Feb 2023 10:05:28 -0600 Subject: [PATCH 32/53] add some extra logs Signed-off-by: zachaller --- utils/config/plugin_downloader.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/utils/config/plugin_downloader.go b/utils/config/plugin_downloader.go index bea1a519a8..19b96f394f 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/config/plugin_downloader.go @@ -44,7 +44,7 @@ func checkShaOfPlugin(pluginLocation string, expectedSha256 string) (bool, error hasher := sha256.New() fileBytes, err := os.ReadFile(pluginLocation) if err != nil { - return false, err + return false, fmt.Errorf("failed to read file %s: %w", pluginLocation, err) } fileSha256 := fmt.Sprintf("%x", hasher.Sum(fileBytes)) match := fileSha256 == expectedSha256 @@ -58,27 +58,27 @@ func downloadFile(filepath string, url string, downloader FileDownloader) error // Get the data resp, err := downloader.Get(url) if err != nil { - return err + return fmt.Errorf("failed to download file from %s: %w", url, err) } defer resp.Body.Close() // Create the file out, err := os.Create(filepath) if err != nil { - return err + return fmt.Errorf("failed to create file %s: %w", filepath, err) } defer out.Close() // Write the body to file _, err = io.Copy(out, resp.Body) if err != nil { - return err + return fmt.Errorf("failed to write to file %s: %w", filepath, err) } // Set the file permissions, to allow execution err = os.Chmod(filepath, 0700) if err != nil { - return err + return fmt.Errorf("failed to set file permissions on %s: %w", filepath, err) } return err @@ -111,7 +111,7 @@ func initMetricsPlugins(fd FileDownloader) error { switch urlObj.Scheme { case "http", "https": - log.Infof("Downloading plugin from: %s", plugin.PluginLocation) + log.Infof("Downloading plugin %s from: %s", plugin.Name, plugin.PluginLocation) startTime := time.Now() err = downloadFile(finalFileLocation, urlObj.String(), fd) if err != nil { From e77809aed540438e8bfca9f9f94d453517584e84 Mon Sep 17 00:00:00 2001 From: zachaller Date: Wed, 1 Feb 2023 11:10:09 -0600 Subject: [PATCH 33/53] some docs changes Signed-off-by: zachaller --- docs/analysis/plugins.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/analysis/plugins.md b/docs/analysis/plugins.md index bca5e653e2..74fb25452d 100644 --- a/docs/analysis/plugins.md +++ b/docs/analysis/plugins.md @@ -3,7 +3,7 @@ !!! important Available since v1.5 Argo Rollouts supports getting analysis metrics via 3rd party plugin system. This allows users to extend the capabilities of Rollouts -to support metric providers that are not natively supported. Rollouts uses a plugin library called +to support metric providers that are not natively supported. Rollout's uses a plugin library called [go-plugin](https://github.com/hashicorp/go-plugin) to do this. You can find a sample plugin here: [sample-rollouts-metric-plugin](https://github.com/argoproj-labs/sample-rollouts-metric-plugin) @@ -24,7 +24,7 @@ particular infrastructure. Here are a few methods: * Using a Kubernetes volume mount with a shared volume such as NFS, EBS, etc. * Building the plugin into the rollouts controller container -Then you can use setup the configmap to point to the plugin executable. Example: +Then you can use the configmap to point to the plugin executable. Example: ```yaml apiVersion: v1 @@ -34,14 +34,14 @@ metadata: data: plugins: |- metrics: - - name: "prometheus" # name of the plugin uses the name to find this configuration, it must match the name required by the plugin + - name: "prometheus" # name the plugin uses to find this configuration, it must match the name required by the plugin pluginLocation: "file://./my-custom-plugin" # supports http(s):// urls and file:// ``` ### Using a HTTP(S) server to host the plugin executable Argo Rollouts supports downloading the plugin executable from a HTTP(S) server. To use this method, you will need to -configure the controller via the `argo-rollouts-config` configmaps `pluginLocation` to an http(s) url. Example: +configure the controller via the `argo-rollouts-config` configmap and set `pluginLocation` to a http(s) url. Example: ```yaml apiVersion: v1 @@ -51,7 +51,7 @@ metadata: data: plugins: |- metrics: - - name: "prometheus" # name of the plugin uses the name to find this configuration, it must match the name required by the plugin + - name: "prometheus" # name the plugin uses to find this configuration, it must match the name required by the plugin pluginLocation: "https://github.com/argoproj-labs/sample-rollouts-metric-plugin/releases/download/v0.0.3/metric-plugin-linux-amd64" # supports http(s):// urls and file:// pluginSha256: "08f588b1c799a37bbe8d0fc74cc1b1492dd70b2c" #optional sha256 checksum of the plugin executable ``` From 520b68d0f7aed8820f5717ce859ba4949543e4b5 Mon Sep 17 00:00:00 2001 From: zachaller Date: Wed, 1 Feb 2023 14:57:42 -0600 Subject: [PATCH 34/53] create /tmp dir for plugins Signed-off-by: zachaller --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 0fff9cf749..93c22d2369 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,10 +84,12 @@ CMD ["dashboard"] #################################################################################################### # Final image #################################################################################################### -FROM gcr.io/distroless/static-debian11 +FROM scratch COPY --from=argo-rollouts-build /go/src/github.com/argoproj/argo-rollouts/dist/rollouts-controller /bin/ COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +# Used for plugin sockets creation +COPY --from=builder --chmod=777 /tmp /tmp # Use numeric user, allows kubernetes to identify this user as being # non-root when we use a security context with runAsNonRoot: true From be708e19d0257e4fb03bfa8da62016c7fbabfbe4 Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 11:10:28 -0600 Subject: [PATCH 35/53] review changes Signed-off-by: zachaller --- metricproviders/plugin/client/client.go | 32 +++++++++++++------------ metricproviders/plugin/plugin.go | 17 ++++++------- utils/config/plugin_downloader_test.go | 2 +- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/metricproviders/plugin/client/client.go b/metricproviders/plugin/client/client.go index 00d5719c57..3131a53578 100644 --- a/metricproviders/plugin/client/client.go +++ b/metricproviders/plugin/client/client.go @@ -3,6 +3,7 @@ package client import ( "fmt" "os/exec" + "sync" "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" @@ -10,30 +11,31 @@ import ( goPlugin "github.com/hashicorp/go-plugin" ) -type singletonMetricPlugin struct { +type metricPlugin struct { pluginClient map[string]*goPlugin.Client plugin map[string]rpc.MetricsPlugin } -var singletonPluginClient *singletonMetricPlugin +var pluginClients *metricPlugin +var once sync.Once -// GetMetricPlugin returns a singleton plugin client for the given metric plugin. Calling this multi -// returns the same plugin client for the same plugin name. +// GetMetricPlugin returns a singleton plugin client for the given metric plugin. Calling this multiple times +// returns the same plugin client instance for the plugin name defined in the metric. func GetMetricPlugin(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { - if singletonPluginClient == nil { - singletonPluginClient = &singletonMetricPlugin{ + once.Do(func() { + pluginClients = &metricPlugin{ pluginClient: make(map[string]*goPlugin.Client), plugin: make(map[string]rpc.MetricsPlugin), } - } - plugin, err := singletonPluginClient.startPluginSystem(metric) + }) + plugin, err := pluginClients.startPluginSystem(metric) if err != nil { return nil, fmt.Errorf("unable to start plugin system: %w", err) } return plugin, nil } -func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { +func (m *metricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.MetricsPlugin, error) { var handshakeConfig = goPlugin.HandshakeConfig{ ProtocolVersion: 1, MagicCookieKey: "ARGO_ROLLOUTS_RPC_PLUGIN", @@ -49,7 +51,7 @@ func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.M for pluginName := range metric.Provider.Plugin { pluginPath, err := plugin.GetPluginLocation(pluginName) if err != nil { - return nil, fmt.Errorf("unable to find plugin %s: %w", pluginName, err) + return nil, fmt.Errorf("unable to find plugin (%s): %w", pluginName, err) } if m.pluginClient[pluginName] == nil || m.pluginClient[pluginName].Exited() { @@ -62,16 +64,16 @@ func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.M rpcClient, err := m.pluginClient[pluginName].Client() if err != nil { - return nil, fmt.Errorf("unable to start plugin %s: %w", pluginName, err) + return nil, fmt.Errorf("unable to start plugin (%s): %w", pluginName, err) } // Request the plugin - raw, err := rpcClient.Dispense("RpcMetricsPlugin") + plugin, err := rpcClient.Dispense("RpcMetricsPlugin") if err != nil { - return nil, fmt.Errorf("unable to dispense plugin %s: %w", pluginName, err) + return nil, fmt.Errorf("unable to dispense plugin (%s): %w", pluginName, err) } - pluginType, ok := raw.(rpc.MetricsPlugin) + pluginType, ok := plugin.(rpc.MetricsPlugin) if !ok { return nil, fmt.Errorf("unexpected type from plugin") } @@ -79,7 +81,7 @@ func (m *singletonMetricPlugin) startPluginSystem(metric v1alpha1.Metric) (rpc.M err = m.plugin[pluginName].NewMetricsPlugin(metric) if err.Error() != "" { - return nil, fmt.Errorf("unable to initialize plugin via rpc %s: %w", pluginName, err) + return nil, fmt.Errorf("unable to initialize plugin via rpc (%s): %w", pluginName, err) } } diff --git a/metricproviders/plugin/plugin.go b/metricproviders/plugin/plugin.go index e65c87625f..04e53bfbda 100644 --- a/metricproviders/plugin/plugin.go +++ b/metricproviders/plugin/plugin.go @@ -10,8 +10,7 @@ import ( const ProviderType = "RPCPlugin" type MetricPlugin struct { - plugin rpc.MetricsPlugin - metric.Provider + rpc.MetricsPlugin } // NewRpcPlugin returns a new RPC plugin with a singleton client @@ -21,27 +20,29 @@ func NewRpcPlugin(metric v1alpha1.Metric) (metric.Provider, error) { return nil, err } - return MetricPlugin{plugin: pluginClient}, nil + return MetricPlugin{ + MetricsPlugin: pluginClient, + }, nil } // Run calls the plugins run method and returns the current measurement func (m MetricPlugin) Run(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alpha1.Measurement { - return m.plugin.Run(run, metric) + return m.Run(run, metric) } // Resume calls the plugins resume method and returns the current measurement func (m MetricPlugin) Resume(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { - return m.plugin.Resume(run, metric, measurement) + return m.Resume(run, metric, measurement) } // Terminate calls the plugins terminate method and returns the current measurement func (m MetricPlugin) Terminate(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { - return m.plugin.Terminate(run, metric, measurement) + return m.Terminate(run, metric, measurement) } // GarbageCollect calls the plugins garbage collect method func (m MetricPlugin) GarbageCollect(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, limit int) error { - err := m.plugin.GarbageCollect(run, metric, limit) + err := m.GarbageCollect(run, metric, limit) if err.Error() != "" { return err } @@ -55,5 +56,5 @@ func (m MetricPlugin) Type() string { // GetMetadata calls the plugins get metadata method func (m MetricPlugin) GetMetadata(metric v1alpha1.Metric) map[string]string { - return m.plugin.GetMetadata(metric) + return m.GetMetadata(metric) } diff --git a/utils/config/plugin_downloader_test.go b/utils/config/plugin_downloader_test.go index 3d1e3aa5e0..ca2bed505e 100644 --- a/utils/config/plugin_downloader_test.go +++ b/utils/config/plugin_downloader_test.go @@ -101,7 +101,7 @@ func TestInitPluginConfigNotFound(t *testing.T) { cm, err := InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) assert.NoError(t, err) - assert.Nil(t, cm) + assert.Equal(t, cm, &Config{}) } func TestFileMove(t *testing.T) { From 924451ce5f20905312b03aa582f9838e02b775bc Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 11:19:33 -0600 Subject: [PATCH 36/53] depend on forwarded functions when we can Signed-off-by: zachaller --- metricproviders/plugin/plugin.go | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/metricproviders/plugin/plugin.go b/metricproviders/plugin/plugin.go index 04e53bfbda..ed8a1aee97 100644 --- a/metricproviders/plugin/plugin.go +++ b/metricproviders/plugin/plugin.go @@ -25,22 +25,7 @@ func NewRpcPlugin(metric v1alpha1.Metric) (metric.Provider, error) { }, nil } -// Run calls the plugins run method and returns the current measurement -func (m MetricPlugin) Run(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric) v1alpha1.Measurement { - return m.Run(run, metric) -} - -// Resume calls the plugins resume method and returns the current measurement -func (m MetricPlugin) Resume(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { - return m.Resume(run, metric, measurement) -} - -// Terminate calls the plugins terminate method and returns the current measurement -func (m MetricPlugin) Terminate(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, measurement v1alpha1.Measurement) v1alpha1.Measurement { - return m.Terminate(run, metric, measurement) -} - -// GarbageCollect calls the plugins garbage collect method +// GarbageCollect calls the plugins garbage collect method but cast the error back to an "error" type for the internal interface func (m MetricPlugin) GarbageCollect(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, limit int) error { err := m.GarbageCollect(run, metric, limit) if err.Error() != "" { @@ -48,13 +33,3 @@ func (m MetricPlugin) GarbageCollect(run *v1alpha1.AnalysisRun, metric v1alpha1. } return nil } - -// Type returns the provider type -func (m MetricPlugin) Type() string { - return ProviderType -} - -// GetMetadata calls the plugins get metadata method -func (m MetricPlugin) GetMetadata(metric v1alpha1.Metric) map[string]string { - return m.GetMetadata(metric) -} From 49eab756b06e9223d36c0e9d1c7a021b017c8cb7 Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 12:37:19 -0600 Subject: [PATCH 37/53] refactor plugin download Signed-off-by: zachaller --- controller/controller.go | 8 ++- utils/config/config.go | 6 +- utils/{config => plugin}/plugin_downloader.go | 7 +- .../plugin_downloader_test.go | 65 ++++++++++++------- 4 files changed, 52 insertions(+), 34 deletions(-) rename utils/{config => plugin}/plugin_downloader.go (97%) rename utils/{config => plugin}/plugin_downloader_test.go (78%) diff --git a/controller/controller.go b/controller/controller.go index d3c35743bb..ea09503ca9 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/argoproj/argo-rollouts/utils/plugin" "net/http" "os" "sync" @@ -478,11 +479,16 @@ func (c *Manager) startLeading(ctx context.Context, rolloutThreadiness, serviceT } } - _, err := rolloutsConfig.InitializeConfig(c.controllerNamespaceInformerFactory.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, rolloutsConfig.FileDownloaderImpl{}) + _, err := rolloutsConfig.InitializeConfig(c.controllerNamespaceInformerFactory.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) if err != nil { log.Fatalf("Failed to init config: %v", err) } + err = plugin.DownloadPlugins(plugin.FileDownloaderImpl{}) + if err != nil { + log.Fatalf("Failed to download plugins: %v", err) + } + go wait.Until(func() { c.wg.Add(1); c.rolloutController.Run(ctx, rolloutThreadiness); c.wg.Done() }, time.Second, ctx.Done()) go wait.Until(func() { c.wg.Add(1); c.serviceController.Run(ctx, serviceThreadiness); c.wg.Done() }, time.Second, ctx.Done()) go wait.Until(func() { c.wg.Add(1); c.ingressController.Run(ctx, ingressThreadiness); c.wg.Done() }, time.Second, ctx.Done()) diff --git a/utils/config/config.go b/utils/config/config.go index 70609e78db..93eda1dd47 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -2,7 +2,6 @@ package config import ( "fmt" - "github.com/argoproj/argo-rollouts/utils/defaults" "github.com/argoproj/argo-rollouts/utils/plugin/types" "github.com/ghodss/yaml" @@ -21,7 +20,7 @@ var configMemoryCache *Config // InitializeConfig initializes the in memory config and downloads the plugins to the filesystem. Subsequent calls to this function will return // the same config object. -func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapName string, downloader FileDownloader) (*Config, error) { +func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapName string) (*Config, error) { configMapCluster, err := configMapInformer.Lister().ConfigMaps(defaults.Namespace()).Get(configMapName) if err != nil { if k8errors.IsNotFound(err) { @@ -42,9 +41,6 @@ func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapNa plugins: plugins, } - if err := initMetricsPlugins(downloader); err != nil { - return nil, err - } return configMemoryCache, nil } diff --git a/utils/config/plugin_downloader.go b/utils/plugin/plugin_downloader.go similarity index 97% rename from utils/config/plugin_downloader.go rename to utils/plugin/plugin_downloader.go index 19b96f394f..c640628f08 100644 --- a/utils/config/plugin_downloader.go +++ b/utils/plugin/plugin_downloader.go @@ -1,8 +1,9 @@ -package config +package plugin import ( "crypto/sha256" "fmt" + "github.com/argoproj/argo-rollouts/utils/config" "io" "net/http" "net/url" @@ -85,8 +86,8 @@ func downloadFile(filepath string, url string, downloader FileDownloader) error } // initMetricsPlugins this function downloads and/or checks that a plugin executable exits on the filesystem -func initMetricsPlugins(fd FileDownloader) error { - config, err := GetConfig() +func DownloadPlugins(fd FileDownloader) error { + config, err := config.GetConfig() if err != nil { return fmt.Errorf("failed to get config: %w", err) } diff --git a/utils/config/plugin_downloader_test.go b/utils/plugin/plugin_downloader_test.go similarity index 78% rename from utils/config/plugin_downloader_test.go rename to utils/plugin/plugin_downloader_test.go index ca2bed505e..b30201944f 100644 --- a/utils/config/plugin_downloader_test.go +++ b/utils/plugin/plugin_downloader_test.go @@ -1,7 +1,8 @@ -package config +package plugin import ( "bytes" + "github.com/argoproj/argo-rollouts/utils/config" "io" "net/http" "os" @@ -34,8 +35,13 @@ func (m MockFileDownloader) Get(url string) (*http.Response, error) { }, nil } +func TestNotInitialized(t *testing.T) { + //configMemoryCache = nil + _, err := config.GetConfig() + assert.Error(t, err) +} + func TestInitPlugin(t *testing.T) { - fd := &MockFileDownloader{} cm := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "argo-rollouts-config", @@ -52,7 +58,10 @@ func TestInitPlugin(t *testing.T) { err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) assert.NoError(t, err) - _, err = InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config", fd) + _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config") + assert.NoError(t, err) + + err = DownloadPlugins(MockFileDownloader{}) assert.NoError(t, err) filepath.Join(defaults.DefaultRolloutPluginFolder, "http") @@ -65,7 +74,6 @@ func TestInitPlugin(t *testing.T) { } func TestInitPluginBadSha(t *testing.T) { - fd := &MockFileDownloader{} cm := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: defaults.DefaultRolloutsConfigMapName, @@ -82,7 +90,10 @@ func TestInitPluginBadSha(t *testing.T) { err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) assert.NoError(t, err) - _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) + _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) + assert.NoError(t, err) + + err = DownloadPlugins(MockFileDownloader{}) assert.Error(t, err) err = os.Remove(filepath.Join(defaults.DefaultRolloutPluginFolder, "http-badsha")) @@ -92,26 +103,27 @@ func TestInitPluginBadSha(t *testing.T) { } func TestInitPluginConfigNotFound(t *testing.T) { - fd := &MockFileDownloader{} client := fake.NewSimpleClientset() i := informers.NewSharedInformerFactory(client, 0) i.Start(make(chan struct{})) cmi := i.Core().V1().ConfigMaps() go cmi.Informer().Run(make(chan struct{})) - cm, err := InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) + cm, err := config.InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) + assert.NoError(t, err) + assert.Equal(t, cm, &config.Config{}) + + err = DownloadPlugins(MockFileDownloader{}) assert.NoError(t, err) - assert.Equal(t, cm, &Config{}) } func TestFileMove(t *testing.T) { - fd := &MockFileDownloader{} cm := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: defaults.DefaultRolloutsConfigMapName, Namespace: defaults.Namespace(), }, - Data: map[string]string{"plugins": "metrics:\n - name: file-plugin\n pluginLocation: file://./config.go"}, + Data: map[string]string{"plugins": "metrics:\n - name: file-plugin\n pluginLocation: file://./plugin.go"}, } client := fake.NewSimpleClientset(cm) i := informers.NewSharedInformerFactory(client, 0) @@ -122,7 +134,10 @@ func TestFileMove(t *testing.T) { err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) assert.NoError(t, err) - _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) + _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) + assert.NoError(t, err) + + err = DownloadPlugins(MockFileDownloader{}) assert.NoError(t, err) err = os.Remove(filepath.Join(defaults.DefaultRolloutPluginFolder, "file-plugin")) @@ -132,13 +147,12 @@ func TestFileMove(t *testing.T) { } func TestDoubleInit(t *testing.T) { - fd := &MockFileDownloader{} cm := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: defaults.DefaultRolloutsConfigMapName, Namespace: defaults.Namespace(), }, - Data: map[string]string{"plugins": "metrics:\n - name: file-plugin\n pluginLocation: file://./config.go"}, + Data: map[string]string{"plugins": "metrics:\n - name: file-plugin\n pluginLocation: file://./plugin.go"}, } client := fake.NewSimpleClientset(cm) i := informers.NewSharedInformerFactory(client, 0) @@ -149,10 +163,13 @@ func TestDoubleInit(t *testing.T) { err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) assert.NoError(t, err) - _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) + _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) + assert.NoError(t, err) + + _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) assert.NoError(t, err) - _, err = InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName, fd) + err = DownloadPlugins(MockFileDownloader{}) assert.NoError(t, err) err = os.Remove(filepath.Join(defaults.DefaultRolloutPluginFolder, "file-plugin")) @@ -161,14 +178,7 @@ func TestDoubleInit(t *testing.T) { assert.NoError(t, err) } -func TestNotInitialized(t *testing.T) { - configMemoryCache = nil - _, err := GetConfig() - assert.Error(t, err) -} - func TestBadConfigMap(t *testing.T) { - fd := &MockFileDownloader{} cm := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "argo-rollouts-config", @@ -185,12 +195,14 @@ func TestBadConfigMap(t *testing.T) { err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) assert.NoError(t, err) - _, err = InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config", fd) + _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config") assert.Error(t, err) + + err = DownloadPlugins(MockFileDownloader{}) + assert.NoError(t, err) } func TestBadLocation(t *testing.T) { - fd := &MockFileDownloader{} cm := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Name: "argo-rollouts-config", @@ -207,6 +219,9 @@ func TestBadLocation(t *testing.T) { err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) assert.NoError(t, err) - _, err = InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config", fd) + _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config") + assert.NoError(t, err) + + err = DownloadPlugins(MockFileDownloader{}) assert.Error(t, err) } From be311795fa248076f7d64c146c716b488e1bdccf Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 13:11:18 -0600 Subject: [PATCH 38/53] make config thread safe Signed-off-by: zachaller --- utils/config/config.go | 14 +++++++++++++- utils/plugin/plugin_downloader.go | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/utils/config/config.go b/utils/config/config.go index 93eda1dd47..a8e3b7c689 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -8,6 +8,7 @@ import ( v1 "k8s.io/api/core/v1" k8errors "k8s.io/apimachinery/pkg/api/errors" informers "k8s.io/client-go/informers/core/v1" + "sync" ) // Config is the in memory representation of the configmap with some additional fields/functions for ease of use. @@ -17,6 +18,7 @@ type Config struct { } var configMemoryCache *Config +var mutex sync.RWMutex // InitializeConfig initializes the in memory config and downloads the plugins to the filesystem. Subsequent calls to this function will return // the same config object. @@ -36,16 +38,20 @@ func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapNa return nil, fmt.Errorf("failed to unmarshal plugins while initializing: %w", err) } + mutex.Lock() configMemoryCache = &Config{ configMap: configMapCluster, plugins: plugins, } + mutex.Unlock() return configMemoryCache, nil } // GetConfig returns the initialized in memory config object if it exists otherwise errors if InitializeConfig has not been called. func GetConfig() (*Config, error) { + mutex.RLock() + defer mutex.RUnlock() if configMemoryCache == nil { return nil, fmt.Errorf("config not initialized, please initialize before use") } @@ -54,5 +60,11 @@ func GetConfig() (*Config, error) { // GetMetricPluginsConfig returns the metric plugins configured in the configmap func (c *Config) GetMetricPluginsConfig() []types.PluginItem { - return configMemoryCache.plugins.Metrics + mutex.RLock() + defer mutex.RUnlock() + var copiedPlugins []types.PluginItem + for _, p := range configMemoryCache.plugins.Metrics { + copiedPlugins = append(copiedPlugins, p) + } + return copiedPlugins } diff --git a/utils/plugin/plugin_downloader.go b/utils/plugin/plugin_downloader.go index c640628f08..97f5edb486 100644 --- a/utils/plugin/plugin_downloader.go +++ b/utils/plugin/plugin_downloader.go @@ -85,7 +85,7 @@ func downloadFile(filepath string, url string, downloader FileDownloader) error return err } -// initMetricsPlugins this function downloads and/or checks that a plugin executable exits on the filesystem +// DownloadPlugins this function downloads and/or checks that a plugin executable exits on the filesystem func DownloadPlugins(fd FileDownloader) error { config, err := config.GetConfig() if err != nil { From ec93e927eef3798ce79636383eb9fb8e1db4c73e Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 13:12:42 -0600 Subject: [PATCH 39/53] update comments Signed-off-by: zachaller --- utils/config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/config/config.go b/utils/config/config.go index a8e3b7c689..3d48df8d1b 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -20,8 +20,8 @@ type Config struct { var configMemoryCache *Config var mutex sync.RWMutex -// InitializeConfig initializes the in memory config and downloads the plugins to the filesystem. Subsequent calls to this function will return -// the same config object. +// InitializeConfig initializes the in memory config and downloads the plugins to the filesystem. Subsequent calls to this +//function will update the configmap in memory. func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapName string) (*Config, error) { configMapCluster, err := configMapInformer.Lister().ConfigMaps(defaults.Namespace()).Get(configMapName) if err != nil { From 623ef077453b87344427a608c01df02f0e50c9f5 Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 13:22:42 -0600 Subject: [PATCH 40/53] lint Signed-off-by: zachaller --- controller/controller.go | 3 ++- utils/config/config.go | 5 +++-- utils/plugin/plugin_downloader.go | 3 ++- utils/plugin/plugin_downloader_test.go | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index ea09503ca9..b2aea6082d 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -4,12 +4,13 @@ import ( "context" "encoding/json" "fmt" - "github.com/argoproj/argo-rollouts/utils/plugin" "net/http" "os" "sync" "time" + "github.com/argoproj/argo-rollouts/utils/plugin" + istioutil "github.com/argoproj/argo-rollouts/utils/istio" rolloutsConfig "github.com/argoproj/argo-rollouts/utils/config" diff --git a/utils/config/config.go b/utils/config/config.go index 3d48df8d1b..16192e4d4c 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -2,13 +2,14 @@ package config import ( "fmt" + "sync" + "github.com/argoproj/argo-rollouts/utils/defaults" "github.com/argoproj/argo-rollouts/utils/plugin/types" "github.com/ghodss/yaml" v1 "k8s.io/api/core/v1" k8errors "k8s.io/apimachinery/pkg/api/errors" informers "k8s.io/client-go/informers/core/v1" - "sync" ) // Config is the in memory representation of the configmap with some additional fields/functions for ease of use. @@ -21,7 +22,7 @@ var configMemoryCache *Config var mutex sync.RWMutex // InitializeConfig initializes the in memory config and downloads the plugins to the filesystem. Subsequent calls to this -//function will update the configmap in memory. +// function will update the configmap in memory. func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapName string) (*Config, error) { configMapCluster, err := configMapInformer.Lister().ConfigMaps(defaults.Namespace()).Get(configMapName) if err != nil { diff --git a/utils/plugin/plugin_downloader.go b/utils/plugin/plugin_downloader.go index 97f5edb486..55e1575de6 100644 --- a/utils/plugin/plugin_downloader.go +++ b/utils/plugin/plugin_downloader.go @@ -3,7 +3,6 @@ package plugin import ( "crypto/sha256" "fmt" - "github.com/argoproj/argo-rollouts/utils/config" "io" "net/http" "net/url" @@ -11,6 +10,8 @@ import ( "path/filepath" "time" + "github.com/argoproj/argo-rollouts/utils/config" + "github.com/argoproj/argo-rollouts/utils/defaults" log "github.com/sirupsen/logrus" diff --git a/utils/plugin/plugin_downloader_test.go b/utils/plugin/plugin_downloader_test.go index b30201944f..773c49cf33 100644 --- a/utils/plugin/plugin_downloader_test.go +++ b/utils/plugin/plugin_downloader_test.go @@ -2,13 +2,14 @@ package plugin import ( "bytes" - "github.com/argoproj/argo-rollouts/utils/config" "io" "net/http" "os" "path/filepath" "testing" + "github.com/argoproj/argo-rollouts/utils/config" + "github.com/argoproj/argo-rollouts/utils/defaults" "github.com/tj/assert" v1 "k8s.io/api/core/v1" From cb321d85c61b20d8e7c12fc34cd08c875c3777a0 Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 13:33:48 -0600 Subject: [PATCH 41/53] download plugins on non leading controllers as well Signed-off-by: zachaller --- controller/controller.go | 20 ++++++++++---------- utils/config/config.go | 8 +++++--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/controller/controller.go b/controller/controller.go index b2aea6082d..5e5fb3fb2e 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -358,6 +358,16 @@ func NewManager( istioPrimaryDynamicClient: istioPrimaryDynamicClient, } + _, err := rolloutsConfig.InitializeConfig(kubeclientset, defaults.DefaultRolloutsConfigMapName) + if err != nil { + log.Fatalf("Failed to init config: %v", err) + } + + err = plugin.DownloadPlugins(plugin.FileDownloaderImpl{}) + if err != nil { + log.Fatalf("Failed to download plugins: %v", err) + } + return cm } @@ -480,16 +490,6 @@ func (c *Manager) startLeading(ctx context.Context, rolloutThreadiness, serviceT } } - _, err := rolloutsConfig.InitializeConfig(c.controllerNamespaceInformerFactory.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) - if err != nil { - log.Fatalf("Failed to init config: %v", err) - } - - err = plugin.DownloadPlugins(plugin.FileDownloaderImpl{}) - if err != nil { - log.Fatalf("Failed to download plugins: %v", err) - } - go wait.Until(func() { c.wg.Add(1); c.rolloutController.Run(ctx, rolloutThreadiness); c.wg.Done() }, time.Second, ctx.Done()) go wait.Until(func() { c.wg.Add(1); c.serviceController.Run(ctx, serviceThreadiness); c.wg.Done() }, time.Second, ctx.Done()) go wait.Until(func() { c.wg.Add(1); c.ingressController.Run(ctx, ingressThreadiness); c.wg.Done() }, time.Second, ctx.Done()) diff --git a/utils/config/config.go b/utils/config/config.go index 16192e4d4c..506bd2c252 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -1,7 +1,10 @@ package config import ( + "context" "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" "sync" "github.com/argoproj/argo-rollouts/utils/defaults" @@ -9,7 +12,6 @@ import ( "github.com/ghodss/yaml" v1 "k8s.io/api/core/v1" k8errors "k8s.io/apimachinery/pkg/api/errors" - informers "k8s.io/client-go/informers/core/v1" ) // Config is the in memory representation of the configmap with some additional fields/functions for ease of use. @@ -23,8 +25,8 @@ var mutex sync.RWMutex // InitializeConfig initializes the in memory config and downloads the plugins to the filesystem. Subsequent calls to this // function will update the configmap in memory. -func InitializeConfig(configMapInformer informers.ConfigMapInformer, configMapName string) (*Config, error) { - configMapCluster, err := configMapInformer.Lister().ConfigMaps(defaults.Namespace()).Get(configMapName) +func InitializeConfig(k8sClientset kubernetes.Interface, configMapName string) (*Config, error) { + configMapCluster, err := k8sClientset.CoreV1().ConfigMaps(defaults.Namespace()).Get(context.Background(), configMapName, metav1.GetOptions{}) if err != nil { if k8errors.IsNotFound(err) { configMemoryCache = &Config{} // We create an empty config so that we don't try to initialize again From 6e59272edd758bb54dd033949baad4f6d2d6f21b Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 13:44:35 -0600 Subject: [PATCH 42/53] fix tests Signed-off-by: zachaller --- utils/plugin/plugin_downloader_test.go | 63 ++++---------------------- 1 file changed, 8 insertions(+), 55 deletions(-) diff --git a/utils/plugin/plugin_downloader_test.go b/utils/plugin/plugin_downloader_test.go index 773c49cf33..7ef17ddf10 100644 --- a/utils/plugin/plugin_downloader_test.go +++ b/utils/plugin/plugin_downloader_test.go @@ -14,7 +14,6 @@ import ( "github.com/tj/assert" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" ) @@ -51,15 +50,8 @@ func TestInitPlugin(t *testing.T) { Data: map[string]string{"plugins": "metrics:\n - name: http\n pluginLocation: https://test/plugin\n - name: http-sha\n pluginLocation: https://test/plugin\n pluginSha256: 74657374e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, } client := fake.NewSimpleClientset(cm) - i := informers.NewSharedInformerFactory(client, 0) - i.Start(make(chan struct{})) - cmi := i.Core().V1().ConfigMaps() - go cmi.Informer().Run(make(chan struct{})) - err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) - assert.NoError(t, err) - - _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config") + _, err := config.InitializeConfig(client, "argo-rollouts-config") assert.NoError(t, err) err = DownloadPlugins(MockFileDownloader{}) @@ -83,15 +75,8 @@ func TestInitPluginBadSha(t *testing.T) { Data: map[string]string{"plugins": "metrics:\n - name: http-badsha\n pluginLocation: https://test/plugin\n pluginSha256: badsha352"}, } client := fake.NewSimpleClientset(cm) - i := informers.NewSharedInformerFactory(client, 0) - i.Start(make(chan struct{})) - cmi := i.Core().V1().ConfigMaps() - go cmi.Informer().Run(make(chan struct{})) - - err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) - assert.NoError(t, err) - _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) + _, err := config.InitializeConfig(client, defaults.DefaultRolloutsConfigMapName) assert.NoError(t, err) err = DownloadPlugins(MockFileDownloader{}) @@ -105,12 +90,8 @@ func TestInitPluginBadSha(t *testing.T) { func TestInitPluginConfigNotFound(t *testing.T) { client := fake.NewSimpleClientset() - i := informers.NewSharedInformerFactory(client, 0) - i.Start(make(chan struct{})) - cmi := i.Core().V1().ConfigMaps() - go cmi.Informer().Run(make(chan struct{})) - cm, err := config.InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) + cm, err := config.InitializeConfig(client, defaults.DefaultRolloutsConfigMapName) assert.NoError(t, err) assert.Equal(t, cm, &config.Config{}) @@ -127,15 +108,8 @@ func TestFileMove(t *testing.T) { Data: map[string]string{"plugins": "metrics:\n - name: file-plugin\n pluginLocation: file://./plugin.go"}, } client := fake.NewSimpleClientset(cm) - i := informers.NewSharedInformerFactory(client, 0) - i.Start(make(chan struct{})) - cmi := i.Core().V1().ConfigMaps() - go cmi.Informer().Run(make(chan struct{})) - - err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) - assert.NoError(t, err) - _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) + _, err := config.InitializeConfig(client, defaults.DefaultRolloutsConfigMapName) assert.NoError(t, err) err = DownloadPlugins(MockFileDownloader{}) @@ -156,18 +130,11 @@ func TestDoubleInit(t *testing.T) { Data: map[string]string{"plugins": "metrics:\n - name: file-plugin\n pluginLocation: file://./plugin.go"}, } client := fake.NewSimpleClientset(cm) - i := informers.NewSharedInformerFactory(client, 0) - i.Start(make(chan struct{})) - cmi := i.Core().V1().ConfigMaps() - go cmi.Informer().Run(make(chan struct{})) - err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) + _, err := config.InitializeConfig(client, defaults.DefaultRolloutsConfigMapName) assert.NoError(t, err) - _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) - assert.NoError(t, err) - - _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), defaults.DefaultRolloutsConfigMapName) + _, err = config.InitializeConfig(client, defaults.DefaultRolloutsConfigMapName) assert.NoError(t, err) err = DownloadPlugins(MockFileDownloader{}) @@ -188,15 +155,8 @@ func TestBadConfigMap(t *testing.T) { Data: map[string]string{"plugins": "badconfigmap"}, } client := fake.NewSimpleClientset(cm) - i := informers.NewSharedInformerFactory(client, 0) - i.Start(make(chan struct{})) - cmi := i.Core().V1().ConfigMaps() - go cmi.Informer().Run(make(chan struct{})) - - err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) - assert.NoError(t, err) - _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config") + _, err := config.InitializeConfig(client, "argo-rollouts-config") assert.Error(t, err) err = DownloadPlugins(MockFileDownloader{}) @@ -212,15 +172,8 @@ func TestBadLocation(t *testing.T) { Data: map[string]string{"plugins": "metrics:\n - name: http\n pluginLocation: agwegasdlkjf2324"}, } client := fake.NewSimpleClientset(cm) - i := informers.NewSharedInformerFactory(client, 0) - i.Start(make(chan struct{})) - cmi := i.Core().V1().ConfigMaps() - go cmi.Informer().Run(make(chan struct{})) - - err := i.Core().V1().ConfigMaps().Informer().GetIndexer().Add(cm) - assert.NoError(t, err) - _, err = config.InitializeConfig(i.Core().V1().ConfigMaps(), "argo-rollouts-config") + _, err := config.InitializeConfig(client, "argo-rollouts-config") assert.NoError(t, err) err = DownloadPlugins(MockFileDownloader{}) From fc5957f26a2efead801732af7507db24298faced Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 14:10:12 -0600 Subject: [PATCH 43/53] lint Signed-off-by: zachaller --- utils/config/config.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/config/config.go b/utils/config/config.go index 506bd2c252..6a1f59fc4e 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -3,9 +3,10 @@ package config import ( "context" "fmt" + "sync" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" - "sync" "github.com/argoproj/argo-rollouts/utils/defaults" "github.com/argoproj/argo-rollouts/utils/plugin/types" From fce8c6c979612e7d7da5f6b746cf176749325203 Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 14:24:37 -0600 Subject: [PATCH 44/53] cleanup tests Signed-off-by: zachaller --- utils/plugin/plugin_downloader_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/utils/plugin/plugin_downloader_test.go b/utils/plugin/plugin_downloader_test.go index 7ef17ddf10..687335ae72 100644 --- a/utils/plugin/plugin_downloader_test.go +++ b/utils/plugin/plugin_downloader_test.go @@ -36,7 +36,6 @@ func (m MockFileDownloader) Get(url string) (*http.Response, error) { } func TestNotInitialized(t *testing.T) { - //configMemoryCache = nil _, err := config.GetConfig() assert.Error(t, err) } @@ -97,6 +96,8 @@ func TestInitPluginConfigNotFound(t *testing.T) { err = DownloadPlugins(MockFileDownloader{}) assert.NoError(t, err) + err = os.RemoveAll(defaults.DefaultRolloutPluginFolder) + assert.NoError(t, err) } func TestFileMove(t *testing.T) { @@ -158,9 +159,6 @@ func TestBadConfigMap(t *testing.T) { _, err := config.InitializeConfig(client, "argo-rollouts-config") assert.Error(t, err) - - err = DownloadPlugins(MockFileDownloader{}) - assert.NoError(t, err) } func TestBadLocation(t *testing.T) { @@ -178,4 +176,7 @@ func TestBadLocation(t *testing.T) { err = DownloadPlugins(MockFileDownloader{}) assert.Error(t, err) + + err = os.RemoveAll(defaults.DefaultRolloutPluginFolder) + assert.NoError(t, err) } From 5d62e952fe824b78a301c630ac6c8ed2216d87e4 Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 15:10:16 -0600 Subject: [PATCH 45/53] add new test Signed-off-by: zachaller --- utils/plugin/plugin_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 utils/plugin/plugin_test.go diff --git a/utils/plugin/plugin_test.go b/utils/plugin/plugin_test.go new file mode 100644 index 0000000000..1673ceaba6 --- /dev/null +++ b/utils/plugin/plugin_test.go @@ -0,0 +1,33 @@ +package plugin + +import ( + "github.com/argoproj/argo-rollouts/utils/config" + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + "testing" +) + +func TestGetPluginLocationNotInit(t *testing.T) { + _, err := GetPluginLocation("http") + assert.Error(t, err) +} + +func TestGetPluginLocation(t *testing.T) { + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argo-rollouts-config", + Namespace: "argo-rollouts", + }, + Data: map[string]string{"plugins": "metrics:\n - name: http\n pluginLocation: https://test/plugin\n - name: http-sha\n pluginLocation: https://test/plugin\n pluginSha256: 74657374e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, + } + client := fake.NewSimpleClientset(cm) + + _, err := config.InitializeConfig(client, "argo-rollouts-config") + assert.NoError(t, err) + + location, err := GetPluginLocation("http") + assert.NoError(t, err) + assert.Equal(t, "/Users/zaller/Development/argo-rollouts/utils/plugin/plugin-bin/http", location) +} From 825bfb2727aa7d145aac943a02d57aaf124b6464 Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 15:26:49 -0600 Subject: [PATCH 46/53] lint Signed-off-by: zachaller --- utils/plugin/plugin_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/plugin/plugin_test.go b/utils/plugin/plugin_test.go index 1673ceaba6..bd72e2fecf 100644 --- a/utils/plugin/plugin_test.go +++ b/utils/plugin/plugin_test.go @@ -1,12 +1,13 @@ package plugin import ( + "testing" + "github.com/argoproj/argo-rollouts/utils/config" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" - "testing" ) func TestGetPluginLocationNotInit(t *testing.T) { From b9400db2195eb8898876dcab444937fcd84ace76 Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 15:47:47 -0600 Subject: [PATCH 47/53] fix test Signed-off-by: zachaller --- utils/plugin/plugin_test.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/utils/plugin/plugin_test.go b/utils/plugin/plugin_test.go index bd72e2fecf..d36311c804 100644 --- a/utils/plugin/plugin_test.go +++ b/utils/plugin/plugin_test.go @@ -1,8 +1,11 @@ package plugin import ( + "path/filepath" "testing" + "github.com/argoproj/argo-rollouts/utils/defaults" + "github.com/argoproj/argo-rollouts/utils/config" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" @@ -30,5 +33,25 @@ func TestGetPluginLocation(t *testing.T) { location, err := GetPluginLocation("http") assert.NoError(t, err) - assert.Equal(t, "/Users/zaller/Development/argo-rollouts/utils/plugin/plugin-bin/http", location) + fp, err := filepath.Abs(filepath.Join(defaults.DefaultRolloutPluginFolder, "http")) + assert.NoError(t, err) + assert.Equal(t, fp, location) +} + +func TestGetPluginLocationNoNamedPlugin(t *testing.T) { + cm := &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "argo-rollouts-config", + Namespace: "argo-rollouts", + }, + Data: map[string]string{"plugins": "metrics:\n - name: http\n pluginLocation: https://test/plugin\n - name: http-sha\n pluginLocation: https://test/plugin\n pluginSha256: 74657374e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, + } + client := fake.NewSimpleClientset(cm) + + _, err := config.InitializeConfig(client, "argo-rollouts-config") + assert.NoError(t, err) + + location, err := GetPluginLocation("dose-not-exist") + assert.Error(t, err) + assert.Equal(t, "", location) } From 1cff9a281e6c5e90fd79f2588bf183d7cda510c9 Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 18:17:32 -0600 Subject: [PATCH 48/53] fix test Signed-off-by: zachaller --- utils/plugin/plugin_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/utils/plugin/plugin_test.go b/utils/plugin/plugin_test.go index d36311c804..c4d656943f 100644 --- a/utils/plugin/plugin_test.go +++ b/utils/plugin/plugin_test.go @@ -13,11 +13,6 @@ import ( "k8s.io/client-go/kubernetes/fake" ) -func TestGetPluginLocationNotInit(t *testing.T) { - _, err := GetPluginLocation("http") - assert.Error(t, err) -} - func TestGetPluginLocation(t *testing.T) { cm := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ From ccf4abaa5441559900bd59f227c506630a6541a8 Mon Sep 17 00:00:00 2001 From: zachaller Date: Thu, 2 Feb 2023 21:54:48 -0600 Subject: [PATCH 49/53] more tests Signed-off-by: zachaller --- utils/config/config.go | 6 ++++ utils/plugin/error | 1 + utils/plugin/plugin_downloader_test.go | 43 ++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100755 utils/plugin/error diff --git a/utils/config/config.go b/utils/config/config.go index 6a1f59fc4e..82a03f73c8 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -62,6 +62,12 @@ func GetConfig() (*Config, error) { return configMemoryCache, nil } +func UnInitializeConfig() { + mutex.Lock() + defer mutex.Unlock() + configMemoryCache = nil +} + // GetMetricPluginsConfig returns the metric plugins configured in the configmap func (c *Config) GetMetricPluginsConfig() []types.PluginItem { mutex.RLock() diff --git a/utils/plugin/error b/utils/plugin/error new file mode 100755 index 0000000000..30d74d2584 --- /dev/null +++ b/utils/plugin/error @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/utils/plugin/plugin_downloader_test.go b/utils/plugin/plugin_downloader_test.go index 687335ae72..0a0296b796 100644 --- a/utils/plugin/plugin_downloader_test.go +++ b/utils/plugin/plugin_downloader_test.go @@ -50,6 +50,8 @@ func TestInitPlugin(t *testing.T) { } client := fake.NewSimpleClientset(cm) + config.UnInitializeConfig() + _, err := config.InitializeConfig(client, "argo-rollouts-config") assert.NoError(t, err) @@ -75,6 +77,8 @@ func TestInitPluginBadSha(t *testing.T) { } client := fake.NewSimpleClientset(cm) + config.UnInitializeConfig() + _, err := config.InitializeConfig(client, defaults.DefaultRolloutsConfigMapName) assert.NoError(t, err) @@ -90,6 +94,8 @@ func TestInitPluginBadSha(t *testing.T) { func TestInitPluginConfigNotFound(t *testing.T) { client := fake.NewSimpleClientset() + config.UnInitializeConfig() + cm, err := config.InitializeConfig(client, defaults.DefaultRolloutsConfigMapName) assert.NoError(t, err) assert.Equal(t, cm, &config.Config{}) @@ -110,6 +116,8 @@ func TestFileMove(t *testing.T) { } client := fake.NewSimpleClientset(cm) + config.UnInitializeConfig() + _, err := config.InitializeConfig(client, defaults.DefaultRolloutsConfigMapName) assert.NoError(t, err) @@ -132,6 +140,8 @@ func TestDoubleInit(t *testing.T) { } client := fake.NewSimpleClientset(cm) + config.UnInitializeConfig() + _, err := config.InitializeConfig(client, defaults.DefaultRolloutsConfigMapName) assert.NoError(t, err) @@ -157,8 +167,13 @@ func TestBadConfigMap(t *testing.T) { } client := fake.NewSimpleClientset(cm) + config.UnInitializeConfig() + _, err := config.InitializeConfig(client, "argo-rollouts-config") assert.Error(t, err) + + err = DownloadPlugins(MockFileDownloader{}) + assert.Error(t, err) } func TestBadLocation(t *testing.T) { @@ -171,6 +186,8 @@ func TestBadLocation(t *testing.T) { } client := fake.NewSimpleClientset(cm) + config.UnInitializeConfig() + _, err := config.InitializeConfig(client, "argo-rollouts-config") assert.NoError(t, err) @@ -180,3 +197,29 @@ func TestBadLocation(t *testing.T) { err = os.RemoveAll(defaults.DefaultRolloutPluginFolder) assert.NoError(t, err) } + +func TestCheckPluginExits(t *testing.T) { + err := checkPluginExists("nonexistentplugin") + assert.Error(t, err) + + realfile, err := filepath.Abs("plugin.go") + assert.NoError(t, err) + err = checkPluginExists(realfile) + assert.NoError(t, err) +} + +func TestCheckShaOfPlugin(t *testing.T) { + _, err := checkShaOfPlugin("nonexistentplugin", "") + assert.Error(t, err) + + realfile, err := filepath.Abs("plugin.go") + assert.NoError(t, err) + _, err = checkShaOfPlugin(realfile, "") + assert.NoError(t, err) +} + +func TestDownloadFile(t *testing.T) { + err := downloadFile("error", "", FileDownloaderImpl{}) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to download file from") +} From d02efd3213651afda406eee8d4c75fff29c63146 Mon Sep 17 00:00:00 2001 From: zachaller Date: Fri, 3 Feb 2023 01:09:26 -0600 Subject: [PATCH 50/53] more tests Signed-off-by: zachaller --- metricproviders/plugin/rpc/rpc_test.go | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/metricproviders/plugin/rpc/rpc_test.go b/metricproviders/plugin/rpc/rpc_test.go index d981a009bc..2313b7bb0a 100644 --- a/metricproviders/plugin/rpc/rpc_test.go +++ b/metricproviders/plugin/rpc/rpc_test.go @@ -6,6 +6,8 @@ import ( "testing" "time" + "github.com/argoproj/argo-rollouts/utils/plugin/types" + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" goPlugin "github.com/hashicorp/go-plugin" "github.com/tj/assert" @@ -160,3 +162,28 @@ func TestPluginClosedConnection(t *testing.T) { cancel() <-closeCh } + +func TestInvalidArgs(t *testing.T) { + server := MetricsRPCServer{} + badtype := struct { + Args string + }{} + err := server.Run(badtype, &v1alpha1.Measurement{}) + assert.Error(t, err) + + err = server.Resume(badtype, &v1alpha1.Measurement{}) + assert.Error(t, err) + + err = server.Terminate(badtype, &v1alpha1.Measurement{}) + assert.Error(t, err) + + err = server.GarbageCollect(badtype, &types.RpcError{}) + assert.Error(t, err) + + err = server.NewMetricsPlugin(badtype, &types.RpcError{}) + assert.Error(t, err) + + resp := make(map[string]string) + err = server.GetMetadata(badtype, &resp) + assert.Error(t, err) +} From 4ecca704769774c33c03498780599788b118e145 Mon Sep 17 00:00:00 2001 From: zachaller Date: Fri, 3 Feb 2023 08:15:19 -0600 Subject: [PATCH 51/53] add godoc Signed-off-by: zachaller --- utils/config/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/config/config.go b/utils/config/config.go index 82a03f73c8..575f44a1a1 100644 --- a/utils/config/config.go +++ b/utils/config/config.go @@ -62,6 +62,7 @@ func GetConfig() (*Config, error) { return configMemoryCache, nil } +// UnInitializeConfig resets the in memory config to nil. This is useful for testing. func UnInitializeConfig() { mutex.Lock() defer mutex.Unlock() From b4d5993abe2a6eff9221ecc27ce9af98c4d4fdec Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 7 Feb 2023 13:50:39 -0600 Subject: [PATCH 52/53] use distroless for plugins chmod cp did not work Signed-off-by: zachaller --- Dockerfile | 4 +--- Dockerfile.dev | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 93c22d2369..0fff9cf749 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,12 +84,10 @@ CMD ["dashboard"] #################################################################################################### # Final image #################################################################################################### -FROM scratch +FROM gcr.io/distroless/static-debian11 COPY --from=argo-rollouts-build /go/src/github.com/argoproj/argo-rollouts/dist/rollouts-controller /bin/ COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -# Used for plugin sockets creation -COPY --from=builder --chmod=777 /tmp /tmp # Use numeric user, allows kubernetes to identify this user as being # non-root when we use a security context with runAsNonRoot: true diff --git a/Dockerfile.dev b/Dockerfile.dev index 2da9591a0e..93d3320ece 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -8,7 +8,7 @@ RUN apt-get update && apt-get install -y \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -FROM scratch +FROM gcr.io/distroless/static-debian11 COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY rollouts-controller-linux-amd64 /bin/rollouts-controller From 3f94bcc93ebea11e808953d213d84417eeb083d8 Mon Sep 17 00:00:00 2001 From: zachaller Date: Tue, 7 Feb 2023 14:36:45 -0600 Subject: [PATCH 53/53] rename file Signed-off-by: zachaller --- utils/plugin/{plugin_downloader.go => downloader.go} | 0 utils/plugin/{plugin_downloader_test.go => downloader_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename utils/plugin/{plugin_downloader.go => downloader.go} (100%) rename utils/plugin/{plugin_downloader_test.go => downloader_test.go} (100%) diff --git a/utils/plugin/plugin_downloader.go b/utils/plugin/downloader.go similarity index 100% rename from utils/plugin/plugin_downloader.go rename to utils/plugin/downloader.go diff --git a/utils/plugin/plugin_downloader_test.go b/utils/plugin/downloader_test.go similarity index 100% rename from utils/plugin/plugin_downloader_test.go rename to utils/plugin/downloader_test.go