diff --git a/CHANGELOG.md b/CHANGELOG.md index 567fc510d4b0aa6470c5eb3afae03ab01ca17cf2..0f7ad873fbce9fa9049316e9d1ba7be1dd4f48e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.7.1 / 2017-06-12 + +* [BUGFIX] Fix double prefix redirect. + ## 1.7.0 / 2017-06-06 * [CHANGE] Compress remote storage requests and responses with unframed/raw snappy. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dde3851b35eb8c4c2fbf95663332776c2b47df8e..8d5bd3de475812f1f06475a977c33296de0d7748 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,8 @@ Prometheus uses GitHub to manage reviews of pull requests. +* If you are a new contributor see: [Steps to Contribute](#steps-to-contribute) + * If you have a trivial fix or improvement, go ahead and create a pull request, addressing (with `@...`) a suitable maintainer of this repository (see [MAINTAINERS.md](MAINTAINERS.md)) in the description of the pull request. @@ -16,3 +18,34 @@ Prometheus uses GitHub to manage reviews of pull requests. and the _Formatting and style_ section of Peter Bourgon's [Go: Best Practices for Production Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). + + +## Steps to Contribute + +Should you wish to work on an issue, please claim it first by commenting on the GitHub issue that you want to work on it. This is to prevent duplicated efforts from contributors on the same issue. + +Please check the [`low-hanging-fruit`](https://github.com/prometheus/prometheus/issues?q=is%3Aissue+is%3Aopen+label%3A%22low+hanging+fruit%22) label to find issues that are good for getting started. If you have questions about one of the issues, with or without the tag, please comment on them and one of the maintainers will clarify it. For a quicker response, contact us over [IRC](https://prometheus.io/community). + +For complete instructions on how to compile see: [Building From Source](https://github.com/prometheus/prometheus#building-from-source) + +For quickly compiling and testing your changes do: +``` +# For building. +go build ./cmd/prometheus/ +./prometheus + +# For testing. +make test # Make sure all the tests pass before you commit and push :) +``` + +All our issues are regularly tagged so that you can also filter down the issues involving the components you want to work on. For our labelling policy refer [the wiki page](https://github.com/prometheus/prometheus/wiki/Label-Names-and-Descriptions). + +## Pull Request Checklist + +* Branch from the master branch and, if needed, rebase to the current master branch before submitting your pull request. If it doesn't merge cleanly with master you may be asked to rebase your changes. + +* Commits should be as small as possible, while ensuring that each commit is correct independently (i.e., each commit should compile and pass tests). + +* If your patch is not getting reviewed or you need a specific person to review it, you can @-reply a reviewer asking for a review in the pull request or a comment, or you can ask for a review on IRC channel [#prometheus](https://webchat.freenode.net/?channels=#prometheus) on irc.freenode.net (for the easiest start, [join via Riot](https://riot.im/app/#/room/#prometheus:matrix.org)). + +* Add tests relevant to the fixed bug or new feature. diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index b87c1164514c1347a3bb9b963db5eaf1491b3f73..20cfaa85329e5c4bb3af362ed6bff6ab7c2d6533 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -329,6 +329,10 @@ func main() { // Wait for reload or termination signals. close(hupReady) // Unblock SIGHUP handler. + // Set web server to ready. + webHandler.Ready() + log.Info("Server is Ready to receive requests.") + term := make(chan os.Signal) signal.Notify(term, os.Interrupt, syscall.SIGTERM) select { diff --git a/config/config.go b/config/config.go index ecf9230997f06ebf14b7be2c625e94940b882197..a4df23d2520cdaed31b32991fac3693d3d4051cc 100644 --- a/config/config.go +++ b/config/config.go @@ -173,6 +173,25 @@ var ( // DefaultRemoteWriteConfig is the default remote write configuration. DefaultRemoteWriteConfig = RemoteWriteConfig{ RemoteTimeout: model.Duration(30 * time.Second), + QueueConfig: DefaultQueueConfig, + } + + // DefaultQueueConfig is the default remote queue configuration. + DefaultQueueConfig = QueueConfig{ + // With a maximum of 1000 shards, assuming an average of 100ms remote write + // time and 100 samples per batch, we will be able to push 1M samples/s. + MaxShards: 1000, + MaxSamplesPerSend: 100, + + // By default, buffer 1000 batches, which at 100ms per batch is 1:40mins. At + // 1000 shards, this will buffer 100M samples total. + Capacity: 100 * 1000, + BatchSendDeadline: 5 * time.Second, + + // Max number of times to retry a batch on recoverable errors. + MaxRetries: 10, + MinBackoff: 30 * time.Millisecond, + MaxBackoff: 100 * time.Millisecond, } // DefaultRemoteReadConfig is the default remote read configuration. @@ -1390,13 +1409,14 @@ func (re Regexp) MarshalYAML() (interface{}, error) { // RemoteWriteConfig is the configuration for writing to remote storage. type RemoteWriteConfig struct { - URL *URL `yaml:"url,omitempty"` + URL *URL `yaml:"url"` RemoteTimeout model.Duration `yaml:"remote_timeout,omitempty"` WriteRelabelConfigs []*RelabelConfig `yaml:"write_relabel_configs,omitempty"` // We cannot do proper Go type embedding below as the parser will then parse // values arbitrarily into the overflow maps of further-down types. HTTPClientConfig HTTPClientConfig `yaml:",inline"` + QueueConfig QueueConfig `yaml:"queue_config,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` @@ -1409,15 +1429,49 @@ func (c *RemoteWriteConfig) UnmarshalYAML(unmarshal func(interface{}) error) err if err := unmarshal((*plain)(c)); err != nil { return err } + if c.URL == nil { + return fmt.Errorf("url for remote_write is empty") + } + + // The UnmarshalYAML method of HTTPClientConfig is not being called because it's not a pointer. + // We cannot make it a pointer as the parser panics for inlined pointer structs. + // Thus we just do its validation here. + if err := c.HTTPClientConfig.validate(); err != nil { + return err + } + if err := checkOverflow(c.XXX, "remote_write"); err != nil { return err } return nil } +// QueueConfig is the configuration for the queue used to write to remote +// storage. +type QueueConfig struct { + // Number of samples to buffer per shard before we start dropping them. + Capacity int `yaml:"capacity,omitempty"` + + // Max number of shards, i.e. amount of concurrency. + MaxShards int `yaml:"max_shards,omitempty"` + + // Maximum number of samples per send. + MaxSamplesPerSend int `yaml:"max_samples_per_send,omitempty"` + + // Maximum time sample will wait in buffer. + BatchSendDeadline time.Duration `yaml:"batch_send_deadline,omitempty"` + + // Max number of times to retry a batch on recoverable errors. + MaxRetries int `yaml:"max_retries,omitempty"` + + // On recoverable errors, backoff exponentially. + MinBackoff time.Duration `yaml:"min_backoff,omitempty"` + MaxBackoff time.Duration `yaml:"max_backoff,omitempty"` +} + // RemoteReadConfig is the configuration for reading from remote storage. type RemoteReadConfig struct { - URL *URL `yaml:"url,omitempty"` + URL *URL `yaml:"url"` RemoteTimeout model.Duration `yaml:"remote_timeout,omitempty"` // We cannot do proper Go type embedding below as the parser will then parse @@ -1435,6 +1489,17 @@ func (c *RemoteReadConfig) UnmarshalYAML(unmarshal func(interface{}) error) erro if err := unmarshal((*plain)(c)); err != nil { return err } + if c.URL == nil { + return fmt.Errorf("url for remote_read is empty") + } + + // The UnmarshalYAML method of HTTPClientConfig is not being called because it's not a pointer. + // We cannot make it a pointer as the parser panics for inlined pointer structs. + // Thus we just do its validation here. + if err := c.HTTPClientConfig.validate(); err != nil { + return err + } + if err := checkOverflow(c.XXX, "remote_read"); err != nil { return err } diff --git a/config/config_test.go b/config/config_test.go index 2ffac74bb0eb056f63c1c15f0907e34460baaf61..075eb325af6e242511d8d203aeaa281e7337818a 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -66,10 +66,12 @@ var expectedConf = &Config{ Action: RelabelDrop, }, }, + QueueConfig: DefaultQueueConfig, }, { URL: mustParseURL("http://remote2/push"), RemoteTimeout: model.Duration(30 * time.Second), + QueueConfig: DefaultQueueConfig, }, }, @@ -666,6 +668,12 @@ var expectedErrors = []struct { }, { filename: "unknown_global_attr.bad.yml", errMsg: "unknown fields in global config: nonexistent_field", + }, { + filename: "remote_read_url_missing.bad.yml", + errMsg: `url for remote_read is empty`, + }, { + filename: "remote_write_url_missing.bad.yml", + errMsg: `url for remote_write is empty`, }, } diff --git a/config/testdata/remote_read_url_missing.bad.yml b/config/testdata/remote_read_url_missing.bad.yml new file mode 100644 index 0000000000000000000000000000000000000000..524c1c68ecc32b26d65a59119ab3678810695db3 --- /dev/null +++ b/config/testdata/remote_read_url_missing.bad.yml @@ -0,0 +1,2 @@ +remote_read: + - url: diff --git a/config/testdata/remote_write_url_missing.bad.yml b/config/testdata/remote_write_url_missing.bad.yml new file mode 100644 index 0000000000000000000000000000000000000000..50780199c02ab4dc0652cb6437e7f8899c7f500d --- /dev/null +++ b/config/testdata/remote_write_url_missing.bad.yml @@ -0,0 +1,2 @@ +remote_write: + - url: diff --git a/discovery/README.md b/discovery/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c129e499552040ca1a5f93840378f22699027734 --- /dev/null +++ b/discovery/README.md @@ -0,0 +1,215 @@ +### Service Discovery + +This directory contains the service discovery (SD) component of Prometheus. + + + +## Design of a Prometheus SD + +There are many requests to add new SDs to Prometheus, this section looks at +what makes a good SD and covers some of the common implementation issues. + +### Does this make sense as an SD? + +The first question to be asked is does it make sense to add this particular +SD? An SD mechanism should be reasonably well established, and at a minimum in +use across multiple organisations. It should allow discovering of machines +and/or services running somewhere. When exactly an SD is popular enough to +justify being added to Prometheus natively is an open question. + +It should not be a brand new SD mechanism, or a variant of an established +mechanism. We want to integrate Prometheus with the SD that's already there in +your infrastructure, not invent yet more ways to do service discovery. We also +do not add mechanisms to work around users lacking service discovery and/or +configuration management infrastructure. + +SDs that merely discover other applications running the same software (e.g. +talk to one Kafka or Cassandra server to find the others) are not service +discovery. In that case the SD you should be looking at is whatever decides +that a machine is going to be a Kafka server, likely a machine database or +configuration management system. + +If something is particularly custom or unusual, `file_sd` is the generic +mechanism provided for users to hook in. Generally with Prometheus we offer a +single generic mechanism for things with infinite variations, rather than +trying to support everything natively (see also, alertmanager webhook, remote +read, remote write, node exporter textfile collector). For example anything +that would involve talking to a relational database should use `file_sd` +instead. + +For configuration management systems like Chef, while they do have a +database/API that'd in principle make sense to talk to for service discovery, +the idiomatic approach is to use Chef's templating facilities to write out a +file for use with `file_sd`. + + +### Mapping from SD to Prometheus + +The general principle with SD is to extract all the potentially useful +information we can out of the SD, and let the user choose what they need of it +using +[relabelling](https://prometheus.io/docs/operating/configuration/#). +This information is generally termed metadata. + +Metadata is exposed as a set of key/value pairs (labels) per target. The keys +are prefixed with `__meta__`, and there should also be an `__address__` +label with the host:port of the target (preferably an IP address to avoid DNS +lookups). No other labelnames should be exposed. + +It is very common for initial pull requests for new SDs to include hardcoded +assumptions that make sense for the the author's setup. SD should be generic, +any customisation should be handled via relabelling. There should be basically +no business logic, filtering, or transformations of the data from the SD beyond +that which is needed to fit it into the metadata data model. + +Arrays (e.g. a list of tags) should be converted to a single label with the +array values joined with a comma. Also prefix and suffix the value with a +comma. So for example the array `[a, b, c]` would become `,a,b,c,`. As +relabelling regexes are fully anchored, this makes it easier to write correct +regexes against (`.*,a,.*` works no matter where `a` appears in the list). The +canonical example of this is `__meta_consul_tags`. + +Maps, hashes and other forms of key/value pairs should be all prefixed and +exposed as labels. For example for EC2 tags, there would be +`__meta_ec2_tag_Description=mydescription` for the Description tag. Labelnames +may only contain `[_a-zA-Z0-9]`, sanitize by replacing with underscores as needed. + +For targets with multiple potential ports, you can a) expose them as a list, b) +if they're named expose them as a map or c) expose them each as their own +target. Kubernetes SD takes the target per port approach. a) and b) can be +combined. + +For machine-like SDs (OpenStack, EC2, Kubernetes to some extent) there may +be multiple network interfaces for a target. Thus far reporting the details +of only the first/primary network interface has sufficed. + + +### Other implementation considerations + +SDs are intended to dump all possible targets. For example the optional use of +EC2 service discovery would be to take the entire region's worth of EC2 +instances it provides and do everything needed in one `scrape_config`. For +large deployments where you are only interested in a small proportion of the +returned targets, this may cause performance issues. If this occurs it is +acceptable to also offer filtering via whatever mechanisms the SD exposes. For +EC2 that would be the `Filter` option on `DescribeInstances`. Keep in mind that +this is a performance optimisation, it should be possible to do the same +filtering using relabelling alone. As with SD generally, we do not invent new +ways to filter targets (that is what relabelling is for), merely offer up +whatever functionality the SD itself offers. + +It is a general rule with Prometheus that all configuration comes from the +configuration file. While the libraries you use to talk to the SD may also +offer other mechanisms for providing configuration/authentication under the +covers (EC2's use of environment variables being a prime example), using your SD +mechanism should not require this. Put another way, your SD implementation +should not read environment variables or files to obtain configuration. + +Some SD mechanisms have rate limits that make them challenging to use. As an +example we have unfortunately had to reject Amazon ECS service discovery due to +the rate limits being so low that it would not be usable for anything beyond +small setups. + +If a system offers multiple distinct types of SD, select which is in use with a +configuration option rather than returning them all from one mega SD that +requires relabelling to select just the one you want. So far we have only seen +this with Kubernetes. When a single SD with a selector vs. multiple distinct +SDs makes sense is an open question. + +If there is a failure while processing talking to the SD, abort rather than +returning partial data. It is better to work from stale targets than partial +or incorrect metadata. + +The information obtained from service discovery is not considered sensitive +security wise. Do not return secrets in metadata, anyone with access to +the Prometheus server will be able to see them. + + +## Writing an SD mechanism + +### The SD interface + +A Service Discovery (SD) mechanism has to discover targets and provide them to Prometheus. We expect similar targets to be grouped together, in the form of a [`TargetGroup`](https://godoc.org/github.com/prometheus/prometheus/config#TargetGroup). The SD mechanism sends the targets down to prometheus as list of `TargetGroups`. + +An SD mechanism has to implement the `TargetProvider` Interface: +```go +type TargetProvider interface { + Run(ctx context.Context, up chan<- []*config.TargetGroup) +} +``` + +Prometheus will call the `Run()` method on a provider to initialise the discovery mechanism. The mechanism will then send *all* the `TargetGroup`s into the channel. Now the mechanism will watch for changes and then send only changed and new `TargetGroup`s down the channel. + +For example if we had a discovery mechanism and it retrieves the following groups: + +``` +[]config.TargetGroup{ + { + Targets: []model.LabelSet{ + { + "__instance__": "10.11.150.1:7870", + "hostname": "demo-target-1", + "test": "simple-test", + }, + { + "__instance__": "10.11.150.4:7870", + "hostname": "demo-target-2", + "test": "simple-test", + }, + }, + Labels: map[LabelName][LabelValue] { + "job": "mysql", + }, + "Source": "file1", + }, + { + Targets: []model.LabelSet{ + { + "__instance__": "10.11.122.11:6001", + "hostname": "demo-postgres-1", + "test": "simple-test", + }, + { + "__instance__": "10.11.122.15:6001", + "hostname": "demo-postgres-2", + "test": "simple-test", + }, + }, + Labels: map[LabelName][LabelValue] { + "job": "postgres", + }, + "Source": "file2", + }, +} +``` + +Here there are two `TargetGroups` one group with source `file1` and another with `file2`. The grouping is implementation specific and could even be one target per group. But, one has to make sure every target group sent by an SD instance should have a `Source` which is unique across all the `TargetGroup`s of that SD instance. + +In this case, both the `TargetGroup`s are sent down the channel the first time `Run()` is called. Now, for an update, we need to send the whole _changed_ `TargetGroup` down the channel. i.e, if the target with `hostname: demo-postgres-2` goes away, we send: +``` +&config.TargetGroup{ + Targets: []model.LabelSet{ + { + "__instance__": "10.11.122.11:6001", + "hostname": "demo-postgres-1", + "test": "simple-test", + }, + }, + Labels: map[LabelName][LabelValue] { + "job": "postgres", + }, + "Source": "file2", +} +``` +down the channel. + +If all the targets in a group go away, we need to send the target groups with empty `Targets` down the channel. i.e, if all targets with `job: postgres` go away, we send: +``` +&config.TargetGroup{ + Targets: nil, + "Source": "file2", +} +``` +down the channel. + + diff --git a/documentation/examples/prometheus-kubernetes.yml b/documentation/examples/prometheus-kubernetes.yml index 8ddcdb12e4a892a6b6d17903900ed99b6aafe46a..a6b9bb0fbcd66b77b01a4ca7138f19502af4dba1 100644 --- a/documentation/examples/prometheus-kubernetes.yml +++ b/documentation/examples/prometheus-kubernetes.yml @@ -4,6 +4,9 @@ # # Kubernetes labels will be added as Prometheus labels on metrics via the # `labelmap` relabeling action. +# +# If you are using Kubernetes 1.7.2 or earlier, please take note of the comments +# for the kubernetes-cadvisor job; you will need to edit or remove this job. # Scrape config for API servers. # @@ -47,6 +50,12 @@ scrape_configs: action: keep regex: default;kubernetes;https +# Scrape config for nodes (kubelet). +# +# Rather than connecting directly to the node, the scrape is proxied though the +# Kubernetes apiserver. This means it will work if Prometheus is running out of +# cluster, or can't connect to nodes for some other reason (e.g. because of +# firewalling). - job_name: 'kubernetes-nodes' # Default to scraping over https. If required, just disable this or change to @@ -61,13 +70,6 @@ scrape_configs: # . tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - # If your node certificates are self-signed or use a different CA to the - # master CA, then disable certificate verification below. Note that - # certificate verification is an integral part of a secure infrastructure - # so this should only be disabled in a controlled environment. You can - # disable certificate verification by uncommenting the line below. - # - # insecure_skip_verify: true bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token kubernetes_sd_configs: @@ -83,6 +85,49 @@ scrape_configs: target_label: __metrics_path__ replacement: /api/v1/nodes/${1}/proxy/metrics +# Scrape config for Kubelet cAdvisor. +# +# This is required for Kubernetes 1.7.3 and later, where cAdvisor metrics +# (those whose names begin with 'container_') have been removed from the +# Kubelet metrics endpoint. This job scrapes the cAdvisor endpoint to +# retrieve those metrics. +# +# In Kubernetes 1.7.0-1.7.2, these metrics are only exposed on the cAdvisor +# HTTP endpoint; use "replacement: /api/v1/nodes/${1}:4194/proxy/metrics" +# in that case (and ensure cAdvisor's HTTP server hasn't been disabled with +# the --cadvisor-port=0 Kubelet flag). +# +# This job is not necessary and should be removed in Kubernetes 1.6 and +# earlier versions, or it will cause the metrics to be scraped twice. +- job_name: 'kubernetes-cadvisor' + + # Default to scraping over https. If required, just disable this or change to + # `http`. + scheme: https + + # This TLS & bearer token file config is used to connect to the actual scrape + # endpoints for cluster components. This is separate to discovery auth + # configuration because discovery & scraping are two separate concerns in + # Prometheus. The discovery auth config is automatic if Prometheus runs inside + # the cluster. Otherwise, more config options have to be provided within the + # . + tls_config: + ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token + + kubernetes_sd_configs: + - role: node + + relabel_configs: + - action: labelmap + regex: __meta_kubernetes_node_label_(.+) + - target_label: __address__ + replacement: kubernetes.default.svc:443 + - source_labels: [__meta_kubernetes_node_name] + regex: (.+) + target_label: __metrics_path__ + replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor + # Scrape config for service endpoints. # # The relabeling allows the actual service scrape endpoint to be configured diff --git a/promql/engine.go b/promql/engine.go index eac4a4982097299f58e97c64e49397b29a4c84e4..2facb90be48eb774b36b5007b24e09c304f5ceee 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -145,7 +145,7 @@ type query struct { stmt Statement // Timer stats for the query execution. stats *stats.TimerGroup - // Cancelation function for the query. + // Cancellation function for the query. cancel func() // The engine against which the query is executed. @@ -669,7 +669,7 @@ func (ev *evaluator) Eval(expr Expr) (v Value, err error) { // eval evaluates the given expression as the given AST expression node requires. func (ev *evaluator) eval(expr Expr) Value { // This is the top-level evaluation method. - // Thus, we check for timeout/cancelation here. + // Thus, we check for timeout/cancellation here. if err := contextDone(ev.ctx, "expression evaluation"); err != nil { ev.error(err) } diff --git a/promql/engine_test.go b/promql/engine_test.go index 157ecbaa55b2c04bc077eea4ba300c4c8231d4f5..0ddc113013906bc70a2913e32ea5c93cb31d9799 100644 --- a/promql/engine_test.go +++ b/promql/engine_test.go @@ -194,7 +194,7 @@ func TestEngineShutdown(t *testing.T) { t.Fatalf("expected error on querying with canceled context but got none") } if _, ok := res2.Err.(ErrQueryCanceled); !ok { - t.Fatalf("expected cancelation error, got %q", res2.Err) + t.Fatalf("expected cancellation error, got %q", res2.Err) } } diff --git a/promql/functions.go b/promql/functions.go index f0a8dc0356f26cf6922dd2d65874c6442c67649a..98a667e6f9e37d0b44907fa3176bc23f6bf3ba4a 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -699,7 +699,11 @@ func funcDeriv(ev *evaluator, args Expressions) Value { if len(samples.Points) < 2 { continue } - slope, _ := linearRegression(samples.Points, 0) + + // We pass in an arbitrary timestamp that is near the values in use + // to avoid floating point accuracy issues, see + // https://github.com/prometheus/prometheus/issues/2674 + slope, _ := linearRegression(samples.Points, samples.Points[0].T) resultSample := Sample{ Metric: dropMetricName(samples.Metric), Point: Point{V: slope, T: ev.Timestamp}, diff --git a/promql/functions_test.go b/promql/functions_test.go index 41a5d32167db1003af2953ef97bedaf99a079bb8..e2f3f9c018b4109db6b4411d6631e636f76000cc 100644 --- a/promql/functions_test.go +++ b/promql/functions_test.go @@ -13,7 +13,14 @@ package promql -import "testing" +import ( + "context" + "testing" + + "github.com/prometheus/prometheus/pkg/labels" + "github.com/prometheus/prometheus/pkg/timestamp" + "github.com/prometheus/prometheus/util/testutil" +) func BenchmarkHoltWinters4Week5Min(b *testing.B) { input := ` @@ -71,3 +78,41 @@ eval instant at 1d changes(http_requests[1d]) bench := NewBenchmark(b, input) bench.Run() } + +func TestDeriv(t *testing.T) { + // https://github.com/prometheus/prometheus/issues/2674#issuecomment-315439393 + // This requires more precision than the usual test system offers, + // so we test it by hand. + storage := testutil.NewStorage(t) + defer storage.Close() + engine := NewEngine(storage, nil) + + a, err := storage.Appender() + if err != nil { + t.Fatal(err) + } + + metric := labels.FromStrings("__name__", "foo") + a.Add(metric, 1493712816939, 1.0) + a.Add(metric, 1493712846939, 1.0) + + if err := a.Commit(); err != nil { + t.Fatal(err) + } + + query, err := engine.NewInstantQuery("deriv(foo[30m])", timestamp.Time(1493712846939)) + if err != nil { + t.Fatalf("Error parsing query: %s", err) + } + result := query.Exec(context.Background()) + if result.Err != nil { + t.Fatalf("Error running query: %s", result.Err) + } + vec, _ := result.Vector() + if len(vec) != 1 { + t.Fatalf("Expected 1 result, got %d", len(vec)) + } + if vec[0].V != 0.0 { + t.Fatalf("Expected 0.0 as value, got %f", vec[0].V) + } +} diff --git a/util/httputil/client.go b/util/httputil/client.go index 2c01fd0fd7cc70ef40dd031fb84e9a0640ee2ce9..e48773e314bd59cbd7064d7ce3ffda804a87fa73 100644 --- a/util/httputil/client.go +++ b/util/httputil/client.go @@ -18,11 +18,8 @@ import ( "crypto/x509" "fmt" "io/ioutil" - "net" "net/http" - "net/url" "strings" - "time" "github.com/prometheus/prometheus/config" ) @@ -72,33 +69,6 @@ func NewClientFromConfig(cfg config.HTTPClientConfig) (*http.Client, error) { return NewClient(rt), nil } -// NewDeadlineRoundTripper returns a new http.RoundTripper which will time out -// long running requests. -func NewDeadlineRoundTripper(timeout time.Duration, proxyURL *url.URL) http.RoundTripper { - return &http.Transport{ - // Set proxy (if null, then becomes a direct connection) - Proxy: http.ProxyURL(proxyURL), - // We need to disable keepalive, because we set a deadline on the - // underlying connection. - DisableKeepAlives: true, - Dial: func(netw, addr string) (c net.Conn, err error) { - start := time.Now() - - c, err = net.DialTimeout(netw, addr, timeout) - if err != nil { - return nil, err - } - - if err = c.SetDeadline(start.Add(timeout)); err != nil { - c.Close() - return nil, err - } - - return c, nil - }, - } -} - type bearerAuthRoundTripper struct { bearerToken string rt http.RoundTripper diff --git a/util/httputil/client_test.go b/util/httputil/client_test.go new file mode 100644 index 0000000000000000000000000000000000000000..10e65e863d44fa416de2d61e24875a9be61d7b00 --- /dev/null +++ b/util/httputil/client_test.go @@ -0,0 +1,412 @@ +// Copyright 2017 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package httputil + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "reflect" + "strings" + "testing" + + "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/util/testutil" +) + +const ( + TLSCAChainPath = "testdata/tls-ca-chain.pem" + ServerCertificatePath = "testdata/server.crt" + ServerKeyPath = "testdata/server.key" + BarneyCertificatePath = "testdata/barney.crt" + BarneyKeyNoPassPath = "testdata/barney-no-pass.key" + MissingCA = "missing/ca.crt" + MissingCert = "missing/cert.crt" + MissingKey = "missing/secret.key" + + ExpectedMessage = "I'm here to serve you!!!" + BearerToken = "theanswertothegreatquestionoflifetheuniverseandeverythingisfortytwo" + BearerTokenFile = "testdata/bearer.token" + MissingBearerTokenFile = "missing/bearer.token" + ExpectedBearer = "Bearer " + BearerToken + ExpectedUsername = "arthurdent" + ExpectedPassword = "42" +) + +func newTestServer(handler func(w http.ResponseWriter, r *http.Request)) (*httptest.Server, error) { + testServer := httptest.NewUnstartedServer(http.HandlerFunc(handler)) + + tlsCAChain, err := ioutil.ReadFile(TLSCAChainPath) + if err != nil { + return nil, fmt.Errorf("Can't read %s", TLSCAChainPath) + } + serverCertificate, err := tls.LoadX509KeyPair(ServerCertificatePath, ServerKeyPath) + if err != nil { + return nil, fmt.Errorf("Can't load X509 key pair %s - %s", ServerCertificatePath, ServerKeyPath) + } + + rootCAs := x509.NewCertPool() + rootCAs.AppendCertsFromPEM(tlsCAChain) + + testServer.TLS = &tls.Config{ + Certificates: make([]tls.Certificate, 1), + RootCAs: rootCAs, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: rootCAs} + testServer.TLS.Certificates[0] = serverCertificate + testServer.TLS.BuildNameToCertificate() + + testServer.StartTLS() + + return testServer, nil +} + +func TestNewClientFromConfig(t *testing.T) { + var newClientValidConfig = []struct { + clientConfig config.HTTPClientConfig + handler func(w http.ResponseWriter, r *http.Request) + }{ + { + clientConfig: config.HTTPClientConfig{ + TLSConfig: config.TLSConfig{ + CAFile: "", + CertFile: BarneyCertificatePath, + KeyFile: BarneyKeyNoPassPath, + ServerName: "", + InsecureSkipVerify: true}, + }, + handler: func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, ExpectedMessage) + }, + }, { + clientConfig: config.HTTPClientConfig{ + TLSConfig: config.TLSConfig{ + CAFile: TLSCAChainPath, + CertFile: BarneyCertificatePath, + KeyFile: BarneyKeyNoPassPath, + ServerName: "", + InsecureSkipVerify: false}, + }, + handler: func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, ExpectedMessage) + }, + }, { + clientConfig: config.HTTPClientConfig{ + BearerToken: BearerToken, + TLSConfig: config.TLSConfig{ + CAFile: TLSCAChainPath, + CertFile: BarneyCertificatePath, + KeyFile: BarneyKeyNoPassPath, + ServerName: "", + InsecureSkipVerify: false}, + }, + handler: func(w http.ResponseWriter, r *http.Request) { + bearer := r.Header.Get("Authorization") + if bearer != ExpectedBearer { + fmt.Fprintf(w, "The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", + ExpectedBearer, bearer) + } else { + fmt.Fprint(w, ExpectedMessage) + } + }, + }, { + clientConfig: config.HTTPClientConfig{ + BearerTokenFile: BearerTokenFile, + TLSConfig: config.TLSConfig{ + CAFile: TLSCAChainPath, + CertFile: BarneyCertificatePath, + KeyFile: BarneyKeyNoPassPath, + ServerName: "", + InsecureSkipVerify: false}, + }, + handler: func(w http.ResponseWriter, r *http.Request) { + bearer := r.Header.Get("Authorization") + if bearer != ExpectedBearer { + fmt.Fprintf(w, "The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", + ExpectedBearer, bearer) + } else { + fmt.Fprint(w, ExpectedMessage) + } + }, + }, { + clientConfig: config.HTTPClientConfig{ + BasicAuth: &config.BasicAuth{ + Username: ExpectedUsername, + Password: ExpectedPassword, + }, + TLSConfig: config.TLSConfig{ + CAFile: TLSCAChainPath, + CertFile: BarneyCertificatePath, + KeyFile: BarneyKeyNoPassPath, + ServerName: "", + InsecureSkipVerify: false}, + }, + handler: func(w http.ResponseWriter, r *http.Request) { + username, password, ok := r.BasicAuth() + if ok == false { + fmt.Fprintf(w, "The Authorization header wasn't set") + } else if ExpectedUsername != username { + fmt.Fprintf(w, "The expected username (%s) differs from the obtained username (%s).", ExpectedUsername, username) + } else if ExpectedPassword != password { + fmt.Fprintf(w, "The expected password (%s) differs from the obtained password (%s).", ExpectedPassword, password) + } else { + fmt.Fprint(w, ExpectedMessage) + } + }, + }, + } + + for _, validConfig := range newClientValidConfig { + testServer, err := newTestServer(validConfig.handler) + if err != nil { + t.Fatal(err.Error()) + } + defer testServer.Close() + + client, err := NewClientFromConfig(validConfig.clientConfig) + if err != nil { + t.Errorf("Can't create a client from this config: %+v", validConfig.clientConfig) + continue + } + response, err := client.Get(testServer.URL) + if err != nil { + t.Errorf("Can't connect to the test server using this config: %+v", validConfig.clientConfig) + continue + } + + message, err := ioutil.ReadAll(response.Body) + response.Body.Close() + if err != nil { + t.Errorf("Can't read the server response body using this config: %+v", validConfig.clientConfig) + continue + } + + trimMessage := strings.TrimSpace(string(message)) + if ExpectedMessage != trimMessage { + t.Errorf("The expected message (%s) differs from the obtained message (%s) using this config: %+v", + ExpectedMessage, trimMessage, validConfig.clientConfig) + } + } +} + +func TestNewClientFromInvalidConfig(t *testing.T) { + var newClientInvalidConfig = []struct { + clientConfig config.HTTPClientConfig + errorMsg string + }{ + { + clientConfig: config.HTTPClientConfig{ + TLSConfig: config.TLSConfig{ + CAFile: MissingCA, + CertFile: "", + KeyFile: "", + ServerName: "", + InsecureSkipVerify: true}, + }, + errorMsg: fmt.Sprintf("unable to use specified CA cert %s:", MissingCA), + }, { + clientConfig: config.HTTPClientConfig{ + BearerTokenFile: MissingBearerTokenFile, + TLSConfig: config.TLSConfig{ + CAFile: TLSCAChainPath, + CertFile: BarneyCertificatePath, + KeyFile: BarneyKeyNoPassPath, + ServerName: "", + InsecureSkipVerify: false}, + }, + errorMsg: fmt.Sprintf("unable to read bearer token file %s:", MissingBearerTokenFile), + }, + } + + for _, invalidConfig := range newClientInvalidConfig { + client, err := NewClientFromConfig(invalidConfig.clientConfig) + if client != nil { + t.Errorf("A client instance was returned instead of nil using this config: %+v", invalidConfig.clientConfig) + } + if err == nil { + t.Errorf("No error was returned using this config: %+v", invalidConfig.clientConfig) + } + if !strings.Contains(err.Error(), invalidConfig.errorMsg) { + t.Errorf("Expected error %s does not contain %s", err.Error(), invalidConfig.errorMsg) + } + } +} + +func TestBearerAuthRoundTripper(t *testing.T) { + const ( + newBearerToken = "goodbyeandthankyouforthefish" + ) + + fakeRoundTripper := testutil.NewRoundTripCheckRequest(func(req *http.Request) { + bearer := req.Header.Get("Authorization") + if bearer != ExpectedBearer { + t.Errorf("The expected Bearer Authorization (%s) differs from the obtained Bearer Authorization (%s)", + ExpectedBearer, bearer) + } + }, nil, nil) + + //Normal flow + bearerAuthRoundTripper := NewBearerAuthRoundTripper(BearerToken, fakeRoundTripper) + request, _ := http.NewRequest("GET", "/hitchhiker", nil) + request.Header.Set("User-Agent", "Douglas Adams mind") + bearerAuthRoundTripper.RoundTrip(request) + + //Should honor already Authorization header set + bearerAuthRoundTripperShouldNotModifyExistingAuthorization := NewBearerAuthRoundTripper(newBearerToken, fakeRoundTripper) + request, _ = http.NewRequest("GET", "/hitchhiker", nil) + request.Header.Set("Authorization", ExpectedBearer) + bearerAuthRoundTripperShouldNotModifyExistingAuthorization.RoundTrip(request) +} + +func TestBasicAuthRoundTripper(t *testing.T) { + const ( + newUsername = "fordprefect" + newPassword = "towel" + ) + + fakeRoundTripper := testutil.NewRoundTripCheckRequest(func(req *http.Request) { + username, password, ok := req.BasicAuth() + if ok == false { + t.Errorf("The Authorization header wasn't set") + } + if ExpectedUsername != username { + t.Errorf("The expected username (%s) differs from the obtained username (%s).", ExpectedUsername, username) + } + if ExpectedPassword != password { + t.Errorf("The expected password (%s) differs from the obtained password (%s).", ExpectedPassword, password) + } + }, nil, nil) + + //Normal flow + basicAuthRoundTripper := NewBasicAuthRoundTripper(ExpectedUsername, + ExpectedPassword, fakeRoundTripper) + request, _ := http.NewRequest("GET", "/hitchhiker", nil) + request.Header.Set("User-Agent", "Douglas Adams mind") + basicAuthRoundTripper.RoundTrip(request) + + //Should honor already Authorization header set + basicAuthRoundTripperShouldNotModifyExistingAuthorization := NewBasicAuthRoundTripper(newUsername, + newPassword, fakeRoundTripper) + request, _ = http.NewRequest("GET", "/hitchhiker", nil) + request.SetBasicAuth(ExpectedUsername, ExpectedPassword) + basicAuthRoundTripperShouldNotModifyExistingAuthorization.RoundTrip(request) +} + +func TestTLSConfig(t *testing.T) { + configTLSConfig := config.TLSConfig{ + CAFile: TLSCAChainPath, + CertFile: BarneyCertificatePath, + KeyFile: BarneyKeyNoPassPath, + ServerName: "localhost", + InsecureSkipVerify: false} + + tlsCAChain, err := ioutil.ReadFile(TLSCAChainPath) + if err != nil { + t.Fatalf("Can't read the CA certificate chain (%s)", + TLSCAChainPath) + } + rootCAs := x509.NewCertPool() + rootCAs.AppendCertsFromPEM(tlsCAChain) + + barneyCertificate, err := tls.LoadX509KeyPair(BarneyCertificatePath, BarneyKeyNoPassPath) + if err != nil { + t.Fatalf("Can't load the client key pair ('%s' and '%s'). Reason: %s", + BarneyCertificatePath, BarneyKeyNoPassPath, err) + } + + expectedTLSConfig := &tls.Config{ + RootCAs: rootCAs, + Certificates: []tls.Certificate{barneyCertificate}, + ServerName: configTLSConfig.ServerName, + InsecureSkipVerify: configTLSConfig.InsecureSkipVerify} + expectedTLSConfig.BuildNameToCertificate() + + tlsConfig, err := NewTLSConfig(configTLSConfig) + if err != nil { + t.Fatalf("Can't create a new TLS Config from a configuration (%s).", err) + } + + if !reflect.DeepEqual(tlsConfig, expectedTLSConfig) { + t.Fatalf("Unexpected TLS Config result: \n\n%+v\n expected\n\n%+v", tlsConfig, expectedTLSConfig) + } +} + +func TestTLSConfigEmpty(t *testing.T) { + configTLSConfig := config.TLSConfig{ + CAFile: "", + CertFile: "", + KeyFile: "", + ServerName: "", + InsecureSkipVerify: true} + + expectedTLSConfig := &tls.Config{ + InsecureSkipVerify: configTLSConfig.InsecureSkipVerify} + expectedTLSConfig.BuildNameToCertificate() + + tlsConfig, err := NewTLSConfig(configTLSConfig) + if err != nil { + t.Fatalf("Can't create a new TLS Config from a configuration (%s).", err) + } + + if !reflect.DeepEqual(tlsConfig, expectedTLSConfig) { + t.Fatalf("Unexpected TLS Config result: \n\n%+v\n expected\n\n%+v", tlsConfig, expectedTLSConfig) + } +} + +func TestTLSConfigInvalidCA(t *testing.T) { + var invalidTLSConfig = []struct { + configTLSConfig config.TLSConfig + errorMessage string + }{ + { + configTLSConfig: config.TLSConfig{ + CAFile: MissingCA, + CertFile: "", + KeyFile: "", + ServerName: "", + InsecureSkipVerify: false}, + errorMessage: fmt.Sprintf("unable to use specified CA cert %s:", MissingCA), + }, { + configTLSConfig: config.TLSConfig{ + CAFile: "", + CertFile: MissingCert, + KeyFile: BarneyKeyNoPassPath, + ServerName: "", + InsecureSkipVerify: false}, + errorMessage: fmt.Sprintf("unable to use specified client cert (%s) & key (%s):", MissingCert, BarneyKeyNoPassPath), + }, { + configTLSConfig: config.TLSConfig{ + CAFile: "", + CertFile: BarneyCertificatePath, + KeyFile: MissingKey, + ServerName: "", + InsecureSkipVerify: false}, + errorMessage: fmt.Sprintf("unable to use specified client cert (%s) & key (%s):", BarneyCertificatePath, MissingKey), + }, + } + + for _, anInvalididTLSConfig := range invalidTLSConfig { + tlsConfig, err := NewTLSConfig(anInvalididTLSConfig.configTLSConfig) + if tlsConfig != nil && err == nil { + t.Errorf("The TLS Config could be created even with this %+v", anInvalididTLSConfig.configTLSConfig) + continue + } + if !strings.Contains(err.Error(), anInvalididTLSConfig.errorMessage) { + t.Errorf("The expected error should contain %s, but got %s", anInvalididTLSConfig.errorMessage, err) + } + } +} diff --git a/util/httputil/testdata/barney-no-pass.key b/util/httputil/testdata/barney-no-pass.key new file mode 100644 index 0000000000000000000000000000000000000000..b8e44f5529c3b53301fa3bae8ac84a851e24da31 --- /dev/null +++ b/util/httputil/testdata/barney-no-pass.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAxmYjfBZhZbAup9uSULehoqPCv/U+77ETxUNyS2nviWEHDAb/ +pFS8Btx4oCQ1ECVSyxcUmXSlrvDjMY4sisOHvndNRlGi274M5a8Q5yD1BUqvxq3u +XB/+SYNVShBzaswrSjpzMe89AlOPxPjnE14OXh00j2hHunOG4jhlWgJnY0YyvUQQ +YWO6KrmKMiZ4MgmY0SWh/ZhlkDJPtkp3aUVM2sheCru/70E9viLGfdlhc2pIMshy +wNp4/5IkHBZwbqXFFGX4sRtSXI/auZNvcHOBse+3e3BonWvBWS2lIYbzpX3vLB7B +E9BGIxWn1fgNQr14yFPaccSszBvgtmEUONolnwIDAQABAoIBAQC7nBhQHgXKGBl2 +Z97rb0pstrjRtsLl/Cg68LWi9LEr0tHMIM4bgnkvb8qtfK+k7fZl0BSNrE2EqYvd +75jVO2MgzEYJieccLpKZm7u7JGIut9qSYSU2fpaCw6uiVv4dbqY9EhqejKG/km8w +j0JMATRK8Qkj1zOE7/wL7dKBlCZaK3u+OT17spuA/21PG/cLiPaSGSA3CU/eqbkU +BD6JeBxp33XNTytwWoOvarsigpL0dGqQ7+qhGq6t69qFfWoe9rimV7Ya+tB9zF/U +HzOIEspOYvzxe+C7VJjlVFr4haMYmsrO9qRUJ2ofp49OLVdfEANsdVISSvS63BEp +gBZN8Ko5AoGBAO1z8y8YCsI+2vBG6nxZ1eMba0KHi3bS8db1TaenJBV22w6WQATh +hEaU6VLMFcMvrOUjXN/7HJfnEMyvFT6gb9obPDVEMZw88s9lVN6njgGLZR/jodyN +7N7utLopN043Ra0WfEILAXPSz8esT1yn05OZV6AFHxJEWMrX3/4+spCLAoGBANXl +RomieVY4u3FF/uzhbzKNNb9ETxrQuexfbangKp5eLniwnr2SQWIbyPzeurwp15J8 +HvxB2vpNvs1khSwNx9dQfMdiUVPGLWj7MimAHTHsnQ9LVV9W28ghuSWbjQDGTUt1 +WCCu1MkKIOzupbi+zgsNlI33yilRQKAb9SRxdy29AoGBAOKpvyZiPcrkMxwPpb/k +BU7QGpgcSR25CQ+Xg3QZEVHH7h1DgYLnPtwdQ4g8tj1mohTsp7hKvSWndRrdulrY +zUyWmOeD3BN2/pTI9rW/nceNp49EPHsLo2O+2xelRlzMWB98ikqEtPM59gt1SSB6 +N3X6d3GR0fIe+d9PKEtK0Cs3AoGAZ9r8ReXSvm+ra5ON9Nx8znHMEAON2TpRnBi1 +uY7zgpO+QrGXUfqKrqVJEKbgym4SkribnuYm+fP32eid1McYKk6VV4ZAcMm/0MJv +F8Fx64S0ufFdEX6uFl1xdXYyn5apfyMJ2EyrWrYFSKWTZ8GVb753S/tteGRQWa1Z +eQly0Y0CgYEAnI6G9KFvXI+MLu5y2LPYAwsesDFzaWwyDl96ioQTA9hNSrjR33Vw +xwpiEe0T/WKF8NQ0QWnrQDbTvuCvZUK37TVxscYWuItL6vnBrYqr4Ck0j1BcGwV5 +jT581A/Vw8JJiR/vfcxgmrFYqoUmkMKDmCN1oImfz09GtQ4jQ1rlxz8= +-----END RSA PRIVATE KEY----- diff --git a/util/httputil/testdata/barney.crt b/util/httputil/testdata/barney.crt new file mode 100644 index 0000000000000000000000000000000000000000..e2f95048362286f31c0637a83abd2a07ab10cc0c --- /dev/null +++ b/util/httputil/testdata/barney.crt @@ -0,0 +1,96 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=NO, O=Green AS, OU=Green Certificate Authority, CN=Green TLS CA + Validity + Not Before: Jul 13 04:02:47 2017 GMT + Not After : Jul 13 04:02:47 2019 GMT + Subject: C=NO, O=Telenor AS, OU=Support, CN=Barney Rubble + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c6:66:23:7c:16:61:65:b0:2e:a7:db:92:50:b7: + a1:a2:a3:c2:bf:f5:3e:ef:b1:13:c5:43:72:4b:69: + ef:89:61:07:0c:06:ff:a4:54:bc:06:dc:78:a0:24: + 35:10:25:52:cb:17:14:99:74:a5:ae:f0:e3:31:8e: + 2c:8a:c3:87:be:77:4d:46:51:a2:db:be:0c:e5:af: + 10:e7:20:f5:05:4a:af:c6:ad:ee:5c:1f:fe:49:83: + 55:4a:10:73:6a:cc:2b:4a:3a:73:31:ef:3d:02:53: + 8f:c4:f8:e7:13:5e:0e:5e:1d:34:8f:68:47:ba:73: + 86:e2:38:65:5a:02:67:63:46:32:bd:44:10:61:63: + ba:2a:b9:8a:32:26:78:32:09:98:d1:25:a1:fd:98: + 65:90:32:4f:b6:4a:77:69:45:4c:da:c8:5e:0a:bb: + bf:ef:41:3d:be:22:c6:7d:d9:61:73:6a:48:32:c8: + 72:c0:da:78:ff:92:24:1c:16:70:6e:a5:c5:14:65: + f8:b1:1b:52:5c:8f:da:b9:93:6f:70:73:81:b1:ef: + b7:7b:70:68:9d:6b:c1:59:2d:a5:21:86:f3:a5:7d: + ef:2c:1e:c1:13:d0:46:23:15:a7:d5:f8:0d:42:bd: + 78:c8:53:da:71:c4:ac:cc:1b:e0:b6:61:14:38:da: + 25:9f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature + X509v3 Basic Constraints: + CA:FALSE + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Subject Key Identifier: + F4:17:02:DD:1B:01:AB:C5:BC:17:A4:5C:4B:75:8E:EC:B1:E0:C8:F1 + X509v3 Authority Key Identifier: + keyid:AE:42:88:75:DD:05:A6:8E:48:7F:50:69:F9:B7:34:23:49:B8:B4:71 + + Authority Information Access: + CA Issuers - URI:http://green.no/ca/tls-ca.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://green.no/ca/tls-ca.crl + + X509v3 Subject Alternative Name: + email:barney@telenor.no + Signature Algorithm: sha1WithRSAEncryption + 96:9a:c5:41:8a:2f:4a:c4:80:d9:2b:1a:cf:07:85:e9:b6:18: + 01:20:41:b9:c3:d4:ca:d3:2d:66:c3:1d:52:7f:25:d7:92:0c: + e9:a9:ae:e6:2e:fa:9d:0a:cf:84:b9:03:f2:63:e3:d3:c9:70: + 6a:ac:04:5e:a9:2d:a2:43:7a:34:60:f7:a9:32:e1:48:ec:c6: + 03:ac:b3:06:2e:48:6e:d0:35:11:31:3d:0c:04:66:41:e6:b2: + ec:8c:68:f8:e4:bc:47:85:39:60:69:a9:8a:ee:2f:56:88:8a: + 19:45:d0:84:8e:c2:27:2c:82:9c:07:6c:34:ae:41:61:63:f9: + 32:cb:8b:33:ea:2c:15:5f:f9:35:b0:3c:51:4d:5f:30:de:0b: + 88:28:94:79:f3:bd:69:37:ad:12:20:e1:6b:1d:b6:77:d9:83: + db:81:a4:53:6c:0f:6a:17:5e:2b:c1:94:c6:42:e3:73:cd:9e: + 79:1b:8c:89:cd:da:ce:b0:f4:21:c5:32:25:04:6e:68:9f:a7: + ca:f4:c5:86:e5:4e:d9:fd:69:73:e6:15:50:6e:76:0f:73:5e: + 7a:a3:f4:dc:15:4a:ab:bb:3c:9a:fa:9f:01:7a:5c:47:a9:a3: + 68:1c:49:e0:37:37:77:af:87:07:16:e4:e1:d7:98:39:15:a6: + 51:5d:4c:db +-----BEGIN CERTIFICATE----- +MIIEITCCAwmgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBdMQswCQYDVQQGEwJOTzER +MA8GA1UECgwIR3JlZW4gQVMxJDAiBgNVBAsMG0dyZWVuIENlcnRpZmljYXRlIEF1 +dGhvcml0eTEVMBMGA1UEAwwMR3JlZW4gVExTIENBMB4XDTE3MDcxMzA0MDI0N1oX +DTE5MDcxMzA0MDI0N1owTDELMAkGA1UEBhMCTk8xEzARBgNVBAoMClRlbGVub3Ig +QVMxEDAOBgNVBAsMB1N1cHBvcnQxFjAUBgNVBAMMDUJhcm5leSBSdWJibGUwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGZiN8FmFlsC6n25JQt6Gio8K/ +9T7vsRPFQ3JLae+JYQcMBv+kVLwG3HigJDUQJVLLFxSZdKWu8OMxjiyKw4e+d01G +UaLbvgzlrxDnIPUFSq/Gre5cH/5Jg1VKEHNqzCtKOnMx7z0CU4/E+OcTXg5eHTSP +aEe6c4biOGVaAmdjRjK9RBBhY7oquYoyJngyCZjRJaH9mGWQMk+2SndpRUzayF4K +u7/vQT2+IsZ92WFzakgyyHLA2nj/kiQcFnBupcUUZfixG1Jcj9q5k29wc4Gx77d7 +cGida8FZLaUhhvOlfe8sHsET0EYjFafV+A1CvXjIU9pxxKzMG+C2YRQ42iWfAgMB +AAGjgfwwgfkwDgYDVR0PAQH/BAQDAgeAMAkGA1UdEwQCMAAwEwYDVR0lBAwwCgYI +KwYBBQUHAwIwHQYDVR0OBBYEFPQXAt0bAavFvBekXEt1juyx4MjxMB8GA1UdIwQY +MBaAFK5CiHXdBaaOSH9Qafm3NCNJuLRxMDkGCCsGAQUFBwEBBC0wKzApBggrBgEF +BQcwAoYdaHR0cDovL2dyZWVuLm5vL2NhL3Rscy1jYS5jZXIwLgYDVR0fBCcwJTAj +oCGgH4YdaHR0cDovL2dyZWVuLm5vL2NhL3Rscy1jYS5jcmwwHAYDVR0RBBUwE4ER +YmFybmV5QHRlbGVub3Iubm8wDQYJKoZIhvcNAQEFBQADggEBAJaaxUGKL0rEgNkr +Gs8Hhem2GAEgQbnD1MrTLWbDHVJ/JdeSDOmpruYu+p0Kz4S5A/Jj49PJcGqsBF6p +LaJDejRg96ky4UjsxgOsswYuSG7QNRExPQwEZkHmsuyMaPjkvEeFOWBpqYruL1aI +ihlF0ISOwicsgpwHbDSuQWFj+TLLizPqLBVf+TWwPFFNXzDeC4golHnzvWk3rRIg +4WsdtnfZg9uBpFNsD2oXXivBlMZC43PNnnkbjInN2s6w9CHFMiUEbmifp8r0xYbl +Ttn9aXPmFVBudg9zXnqj9NwVSqu7PJr6nwF6XEepo2gcSeA3N3evhwcW5OHXmDkV +plFdTNs= +-----END CERTIFICATE----- diff --git a/util/httputil/testdata/bearer.token b/util/httputil/testdata/bearer.token new file mode 100644 index 0000000000000000000000000000000000000000..3367abffd5311812d5d2cf77fd29ebfd96664798 --- /dev/null +++ b/util/httputil/testdata/bearer.token @@ -0,0 +1 @@ +theanswertothegreatquestionoflifetheuniverseandeverythingisfortytwo diff --git a/util/httputil/testdata/server.crt b/util/httputil/testdata/server.crt new file mode 100644 index 0000000000000000000000000000000000000000..87ad202fe3732fe524f1ae38aefa2c6d99c29ea6 --- /dev/null +++ b/util/httputil/testdata/server.crt @@ -0,0 +1,96 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4 (0x4) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=NO, O=Green AS, OU=Green Certificate Authority, CN=Green TLS CA + Validity + Not Before: Jul 26 12:47:08 2017 GMT + Not After : Jul 26 12:47:08 2019 GMT + Subject: C=NO, O=Green AS, OU=Green Certificate Authority, CN=Green TLS CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:97:43:c5:f6:24:b8:ce:30:12:70:ea:17:9c:c0: + ce:f2:ef:58:8b:12:7d:46:5e:01:f1:1a:93:b2:3e: + d8:cf:99:bc:10:32:f1:12:b0:ef:00:6c:d6:c4:45: + 85:a8:33:7b:cd:ec:8f:4a:92:d0:5a:4a:41:69:7f: + e3:dd:7e:71:d2:21:9c:df:43:b5:6c:60:bb:2a:12: + a8:08:cf:c5:ee:08:7d:48:ea:4b:54:e4:82:d9:88: + b0:b8:5e:02:12:cb:0e:09:99:b7:5f:42:b6:d7:26: + 34:0f:4a:e7:fc:ac:9c:59:cd:a1:50:4c:88:5f:f1: + d2:7e:5b:21:41:f0:37:50:80:48:71:50:26:61:26: + 79:64:4b:7e:91:8d:0e:f4:27:fe:19:80:bf:39:55: + b7:f3:d0:cd:61:6c:d8:c1:c7:d3:26:77:92:1a:14: + 42:56:cb:bc:fd:1a:4a:eb:17:d8:8d:af:d1:c0:46: + 9f:f0:40:5e:0e:34:2f:e7:db:be:66:fd:89:0b:6b: + 8c:71:c1:0b:0a:c5:c4:c4:eb:7f:44:c1:75:36:23: + fd:ed:b6:ee:87:d9:88:47:e1:4b:7c:60:53:e7:85: + 1c:2f:82:4b:2b:5e:63:1a:49:17:36:2c:fc:39:23: + 49:22:4d:43:b5:51:22:12:24:9e:31:44:d8:16:4e: + a8:eb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Basic Constraints: + CA:FALSE + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + X509v3 Subject Key Identifier: + 70:A9:FB:44:66:3C:63:96:E6:05:B2:74:47:C8:18:7E:43:6D:EE:8B + X509v3 Authority Key Identifier: + keyid:AE:42:88:75:DD:05:A6:8E:48:7F:50:69:F9:B7:34:23:49:B8:B4:71 + + Authority Information Access: + CA Issuers - URI:http://green.no/ca/tls-ca.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://green.no/ca/tls-ca.crl + + X509v3 Subject Alternative Name: + IP Address:127.0.0.1, IP Address:127.0.0.0, DNS:localhost + Signature Algorithm: sha1WithRSAEncryption + 56:1e:b8:52:ba:f5:72:42:ad:15:71:c1:5e:00:63:c9:4d:56: + f2:8d:a3:a9:91:db:d0:b5:1b:88:80:93:80:28:48:b2:d0:a9: + d0:ea:de:40:78:cc:57:8c:00:b8:65:99:68:95:98:9b:fb:a2: + 43:21:ea:00:37:01:77:c7:3b:1a:ec:58:2d:25:9c:ad:23:41: + 5e:ae:fd:ac:2f:26:81:b8:a7:49:9b:5a:10:fe:ad:c3:86:ab: + 59:67:b0:c7:81:72:95:60:b5:cb:fc:9f:ad:27:16:50:85:76: + 33:16:20:2c:1f:c6:14:09:0c:48:9f:c0:19:16:c9:fa:b0:d8: + bf:b7:8d:a7:aa:eb:fe:f8:6f:dd:2b:83:ee:c7:8a:df:c8:59: + e6:2e:13:1f:57:cc:6f:31:db:f7:b7:5c:3f:78:ad:22:2c:48: + bb:6d:c4:ab:dc:c1:76:34:29:d9:1e:67:e0:ac:37:2b:90:f9: + 71:bd:cf:a1:01:b9:eb:0b:0b:79:2e:8b:52:3d:8e:13:97:c8: + 05:a3:ef:68:82:49:12:2a:25:1a:48:49:b8:7c:3c:66:0d:74: + f9:00:8c:5b:57:d7:76:b1:26:95:86:b2:2e:a3:b2:9c:e0:eb: + 2d:fc:77:03:8f:cd:56:46:3a:c9:6a:fa:72:e3:19:d8:ef:de: + 4b:36:95:79 +-----BEGIN CERTIFICATE----- +MIIEQjCCAyqgAwIBAgIBBDANBgkqhkiG9w0BAQUFADBdMQswCQYDVQQGEwJOTzER +MA8GA1UECgwIR3JlZW4gQVMxJDAiBgNVBAsMG0dyZWVuIENlcnRpZmljYXRlIEF1 +dGhvcml0eTEVMBMGA1UEAwwMR3JlZW4gVExTIENBMB4XDTE3MDcyNjEyNDcwOFoX +DTE5MDcyNjEyNDcwOFowXTELMAkGA1UEBhMCTk8xETAPBgNVBAoMCEdyZWVuIEFT +MSQwIgYDVQQLDBtHcmVlbiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFTATBgNVBAMM +DEdyZWVuIFRMUyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJdD +xfYkuM4wEnDqF5zAzvLvWIsSfUZeAfEak7I+2M+ZvBAy8RKw7wBs1sRFhagze83s +j0qS0FpKQWl/491+cdIhnN9DtWxguyoSqAjPxe4IfUjqS1TkgtmIsLheAhLLDgmZ +t19CttcmNA9K5/ysnFnNoVBMiF/x0n5bIUHwN1CASHFQJmEmeWRLfpGNDvQn/hmA +vzlVt/PQzWFs2MHH0yZ3khoUQlbLvP0aSusX2I2v0cBGn/BAXg40L+fbvmb9iQtr +jHHBCwrFxMTrf0TBdTYj/e227ofZiEfhS3xgU+eFHC+CSyteYxpJFzYs/DkjSSJN +Q7VRIhIknjFE2BZOqOsCAwEAAaOCAQswggEHMA4GA1UdDwEB/wQEAwIFoDAJBgNV +HRMEAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQU +cKn7RGY8Y5bmBbJ0R8gYfkNt7oswHwYDVR0jBBgwFoAUrkKIdd0Fpo5If1Bp+bc0 +I0m4tHEwOQYIKwYBBQUHAQEELTArMCkGCCsGAQUFBzAChh1odHRwOi8vZ3JlZW4u +bm8vY2EvdGxzLWNhLmNlcjAuBgNVHR8EJzAlMCOgIaAfhh1odHRwOi8vZ3JlZW4u +bm8vY2EvdGxzLWNhLmNybDAgBgNVHREEGTAXhwR/AAABhwR/AAAAgglsb2NhbGhv +c3QwDQYJKoZIhvcNAQEFBQADggEBAFYeuFK69XJCrRVxwV4AY8lNVvKNo6mR29C1 +G4iAk4AoSLLQqdDq3kB4zFeMALhlmWiVmJv7okMh6gA3AXfHOxrsWC0lnK0jQV6u +/awvJoG4p0mbWhD+rcOGq1lnsMeBcpVgtcv8n60nFlCFdjMWICwfxhQJDEifwBkW +yfqw2L+3jaeq6/74b90rg+7Hit/IWeYuEx9XzG8x2/e3XD94rSIsSLttxKvcwXY0 +KdkeZ+CsNyuQ+XG9z6EBuesLC3kui1I9jhOXyAWj72iCSRIqJRpISbh8PGYNdPkA +jFtX13axJpWGsi6jspzg6y38dwOPzVZGOslq+nLjGdjv3ks2lXk= +-----END CERTIFICATE----- diff --git a/util/httputil/testdata/server.key b/util/httputil/testdata/server.key new file mode 100644 index 0000000000000000000000000000000000000000..126c1b5d032ec10a040ba3b4aaf81f10c3c9e702 --- /dev/null +++ b/util/httputil/testdata/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXQ8X2JLjOMBJw +6hecwM7y71iLEn1GXgHxGpOyPtjPmbwQMvESsO8AbNbERYWoM3vN7I9KktBaSkFp +f+PdfnHSIZzfQ7VsYLsqEqgIz8XuCH1I6ktU5ILZiLC4XgISyw4JmbdfQrbXJjQP +Suf8rJxZzaFQTIhf8dJ+WyFB8DdQgEhxUCZhJnlkS36RjQ70J/4ZgL85Vbfz0M1h +bNjBx9Mmd5IaFEJWy7z9GkrrF9iNr9HARp/wQF4ONC/n275m/YkLa4xxwQsKxcTE +639EwXU2I/3ttu6H2YhH4Ut8YFPnhRwvgksrXmMaSRc2LPw5I0kiTUO1USISJJ4x +RNgWTqjrAgMBAAECggEAVurwo4FyV7gzwIIi00XPJLT3ceJL7dUy1HHrEG8gchnq +gHxlHdJhYyMnPVydcosyxp75r2YxJtCoSZDdRHbVvGLoGzpy0zW6FnDl8TpCh4aF +RxKp+rvbnFf5A9ew5U+cX1PelHRnT7V6EJeAOiaNKOUJnnR7oHX59/UxZQw9HJnX +3H4xUdRDmSS3BGKXEswbd7beQjqJtEIkbConfaw32yEod0w2MC0LI4miZ87/6Hsk +pyvfpeYxXp4z3BTvFBbf/GEBFuozu63VWHayB9PDmEN/TlphoQpJQihdR2r1lz/H +I5QwVlFTDvUSFitNLu+FoaHOfgLprQndbojBXb+tcQKBgQDHCPyM4V7k97RvJgmB +ELgZiDYufDrjRLXvFzrrZ7ySU3N+nx3Gz/EhtgbHicDjnRVagHBIwi/QAfBJksCd +xcioY5k2OW+8PSTsfFZTAA6XwJp/LGfJik/JjvAVv5CnxBu9lYG4WiSBJFp59ojC +zTmfEuB4GPwrjQvzjlqaSpij9QKBgQDCjriwAB2UJIdlgK+DkryLqgim5I4cteB3 ++juVKz+S8ufFmVvmIXkyDcpyy/26VLC6esy8dV0JoWc4EeitoJvQD1JVZ5+CBTY+ +r9umx18oe2A/ZgcEf/A3Zd94jM1MwriF6YC+eIOhwhpi7T1xTLf3hc9B0OJ5B1mA +vob9rGDtXwKBgD4rkW+UCictNIAvenKFPWxEPuBgT6ij0sx/DhlwCtgOFxprK0rp +syFbkVyMq+KtM3lUez5O4c5wfJUOsPnXSOlISxhD8qHy23C/GdvNPcGrGNc2kKjE +ek20R0wTzWSJ/jxG0gE6rwJjz5sfJfLrVd9ZbyI0c7hK03vdcHGXcXxtAoGAeGHl +BwnbQ3niyTx53VijD2wTVGjhQgSLstEDowYSnTNtk8eTpG6b1gvQc32jLnMOsyQe +oJGiEr5q5re2GBDjuDZyxGOMv9/Hs7wOlkCQsbS9Vh0kRHWBRlXjk2zT7yYhFMLp +pXFeSW2X9BRFS2CkCCUkm93K9AZHLDE3x6ishNMCgYEAsDsUCzGhI49Aqe+CMP2l +WPZl7SEMYS5AtdC5sLtbLYBl8+rMXVGL2opKXqVFYBYkqMJiHGdX3Ub6XSVKLYkN +vm4PWmlQS24ZT+jlUl4jk6JU6SAlM/o6ixZl5KNR7yQm6zN2O/RHDeYm0urUQ9tF +9dux7LbIFeOoJmoDTWG2+fI= +-----END PRIVATE KEY----- diff --git a/util/httputil/testdata/tls-ca-chain.pem b/util/httputil/testdata/tls-ca-chain.pem new file mode 100644 index 0000000000000000000000000000000000000000..03e4189e641181b1ab9b0ad1bf5e5837dd19fdde --- /dev/null +++ b/util/httputil/testdata/tls-ca-chain.pem @@ -0,0 +1,172 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=NO, O=Green AS, OU=Green Certificate Authority, CN=Green Root CA + Validity + Not Before: Jul 13 03:47:20 2017 GMT + Not After : Jul 13 03:47:20 2027 GMT + Subject: C=NO, O=Green AS, OU=Green Certificate Authority, CN=Green TLS CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:b5:5a:b3:7a:7f:6a:5b:e9:ee:62:ee:4f:61:42: + 79:93:06:bf:81:fc:9a:1f:b5:80:83:7c:b3:a6:94: + 54:58:8a:b1:74:cb:c3:b8:3c:23:a8:69:1f:ca:2b: + af:be:97:ba:31:73:b5:b8:ce:d9:bf:bf:9a:7a:cf: + 3a:64:51:83:c9:36:d2:f7:3b:3a:0e:4c:c7:66:2e: + bf:1a:df:ce:10:aa:3d:0f:19:74:03:7e:b5:10:bb: + e8:37:bd:62:f0:42:2d:df:3d:ca:70:50:10:17:ce: + a9:ec:55:8e:87:6f:ce:9a:04:36:14:96:cb:d1:a5: + 48:d5:d2:87:02:62:93:4e:21:4a:ff:be:44:f1:d2: + 7e:ed:74:da:c2:51:26:8e:03:a0:c2:bd:bd:5f:b0: + 50:11:78:fd:ab:1d:04:86:6c:c1:8d:20:bd:05:5f: + 51:67:c6:d3:07:95:92:2d:92:90:00:c6:9f:2d:dd: + 36:5c:dc:78:10:7c:f6:68:39:1d:2c:e0:e1:26:64: + 4f:36:34:66:a7:84:6a:90:15:3a:94:b7:79:b1:47: + f5:d2:51:95:54:bf:92:76:9a:b9:88:ee:63:f9:6c: + 0d:38:c6:b6:1c:06:43:ed:24:1d:bb:6c:72:48:cc: + 8c:f4:35:bc:43:fe:a6:96:4c:31:5f:82:0d:0d:20: + 2a:3d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Subject Key Identifier: + AE:42:88:75:DD:05:A6:8E:48:7F:50:69:F9:B7:34:23:49:B8:B4:71 + X509v3 Authority Key Identifier: + keyid:60:93:53:2F:C7:CF:2A:D7:F3:09:28:F6:3C:AE:9C:50:EC:93:63:E5 + + Authority Information Access: + CA Issuers - URI:http://green.no/ca/root-ca.cer + + X509v3 CRL Distribution Points: + + Full Name: + URI:http://green.no/ca/root-ca.crl + + Signature Algorithm: sha1WithRSAEncryption + 15:a7:ac:d7:25:9e:2a:d4:d1:14:b4:99:38:3d:2f:73:61:2a: + d9:b6:8b:13:ea:fe:db:78:d9:0a:6c:df:26:6e:c1:d5:4a:97: + 42:19:dd:97:05:03:e4:2b:fc:1e:1f:38:3c:4e:b0:3b:8c:38: + ad:2b:65:fa:35:2d:81:8e:e0:f6:0a:89:4c:38:97:01:4b:9c: + ac:4e:e1:55:17:ef:0a:ad:a7:eb:1e:4b:86:23:12:f1:52:69: + cb:a3:8a:ce:fb:14:8b:86:d7:bb:81:5e:bd:2a:c7:a7:79:58: + 00:10:c0:db:ff:d4:a5:b9:19:74:b3:23:19:4a:1f:78:4b:a8: + b6:f6:20:26:c1:69:f9:89:7f:b8:1c:3b:a2:f9:37:31:80:2c: + b0:b6:2b:d2:84:44:d7:42:e4:e6:44:51:04:35:d9:1c:a4:48: + c6:b7:35:de:f2:ae:da:4b:ba:c8:09:42:8d:ed:7a:81:dc:ed: + 9d:f0:de:6e:21:b9:01:1c:ad:64:3d:25:4c:91:94:f1:13:18: + bb:89:e9:48:ac:05:73:07:c8:db:bd:69:8e:6f:02:9d:b0:18: + c0:b9:e1:a8:b1:17:50:3d:ac:05:6e:6f:63:4f:b1:73:33:60: + 9a:77:d2:81:8a:01:38:43:e9:4c:3c:90:63:a4:99:4b:d2:1b: + f9:1b:ec:ee +-----BEGIN CERTIFICATE----- +MIIECzCCAvOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJOTzER +MA8GA1UECgwIR3JlZW4gQVMxJDAiBgNVBAsMG0dyZWVuIENlcnRpZmljYXRlIEF1 +dGhvcml0eTEWMBQGA1UEAwwNR3JlZW4gUm9vdCBDQTAeFw0xNzA3MTMwMzQ3MjBa +Fw0yNzA3MTMwMzQ3MjBaMF0xCzAJBgNVBAYTAk5PMREwDwYDVQQKDAhHcmVlbiBB +UzEkMCIGA1UECwwbR3JlZW4gQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRUwEwYDVQQD +DAxHcmVlbiBUTFMgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1 +WrN6f2pb6e5i7k9hQnmTBr+B/JoftYCDfLOmlFRYirF0y8O4PCOoaR/KK6++l7ox +c7W4ztm/v5p6zzpkUYPJNtL3OzoOTMdmLr8a384Qqj0PGXQDfrUQu+g3vWLwQi3f +PcpwUBAXzqnsVY6Hb86aBDYUlsvRpUjV0ocCYpNOIUr/vkTx0n7tdNrCUSaOA6DC +vb1fsFAReP2rHQSGbMGNIL0FX1FnxtMHlZItkpAAxp8t3TZc3HgQfPZoOR0s4OEm +ZE82NGanhGqQFTqUt3mxR/XSUZVUv5J2mrmI7mP5bA04xrYcBkPtJB27bHJIzIz0 +NbxD/qaWTDFfgg0NICo9AgMBAAGjgdQwgdEwDgYDVR0PAQH/BAQDAgEGMBIGA1Ud +EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFK5CiHXdBaaOSH9Qafm3NCNJuLRxMB8G +A1UdIwQYMBaAFGCTUy/HzyrX8wko9jyunFDsk2PlMDoGCCsGAQUFBwEBBC4wLDAq +BggrBgEFBQcwAoYeaHR0cDovL2dyZWVuLm5vL2NhL3Jvb3QtY2EuY2VyMC8GA1Ud +HwQoMCYwJKAioCCGHmh0dHA6Ly9ncmVlbi5uby9jYS9yb290LWNhLmNybDANBgkq +hkiG9w0BAQUFAAOCAQEAFaes1yWeKtTRFLSZOD0vc2Eq2baLE+r+23jZCmzfJm7B +1UqXQhndlwUD5Cv8Hh84PE6wO4w4rStl+jUtgY7g9gqJTDiXAUucrE7hVRfvCq2n +6x5LhiMS8VJpy6OKzvsUi4bXu4FevSrHp3lYABDA2//UpbkZdLMjGUofeEuotvYg +JsFp+Yl/uBw7ovk3MYAssLYr0oRE10Lk5kRRBDXZHKRIxrc13vKu2ku6yAlCje16 +gdztnfDebiG5ARytZD0lTJGU8RMYu4npSKwFcwfI271pjm8CnbAYwLnhqLEXUD2s +BW5vY0+xczNgmnfSgYoBOEPpTDyQY6SZS9Ib+Rvs7g== +-----END CERTIFICATE----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=NO, O=Green AS, OU=Green Certificate Authority, CN=Green Root CA + Validity + Not Before: Jul 13 03:44:39 2017 GMT + Not After : Dec 31 23:59:59 2030 GMT + Subject: C=NO, O=Green AS, OU=Green Certificate Authority, CN=Green Root CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a7:e8:ed:de:d4:54:08:41:07:40:d5:c0:43:d6: + ab:d3:9e:21:87:c6:13:bf:a7:cf:3d:08:4f:c1:fe: + 8f:e5:6c:c5:89:97:e5:27:75:26:c3:2a:73:2d:34: + 7c:6f:35:8d:40:66:61:05:c0:eb:e9:b3:38:47:f8: + 8b:26:35:2c:df:dc:24:31:fe:72:e3:87:10:d1:f7: + a0:57:b7:f3:b1:1a:fe:c7:4b:f8:7b:14:6d:73:08: + 54:eb:63:3c:0c:ce:22:95:5f:3f:f2:6f:89:ae:63: + da:80:74:36:21:13:e8:91:01:58:77:cc:c2:f2:42: + bf:eb:b3:60:a7:21:ed:88:24:7f:eb:ff:07:41:9b: + 93:c8:5f:6a:8e:a6:1a:15:3c:bc:e7:0d:fd:05:fd: + 3c:c1:1c:1d:1f:57:2b:40:27:62:a1:7c:48:63:c1: + 45:e7:2f:20:ed:92:1c:42:94:e4:58:70:7a:b6:d2: + 85:c5:61:d8:cd:c6:37:6b:72:3b:7f:af:55:81:d6: + 9d:dc:10:c9:d8:0e:81:e4:5e:40:13:2f:20:e8:6b: + 46:81:ce:88:47:dd:38:71:3d:ef:21:cc:c0:67:cf: + 0a:f4:e9:3f:a8:9d:26:25:2e:23:1e:a3:11:18:cb: + d1:70:1c:9e:7d:09:b1:a4:20:dc:95:15:1d:49:cf: + 1b:ad + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Subject Key Identifier: + 60:93:53:2F:C7:CF:2A:D7:F3:09:28:F6:3C:AE:9C:50:EC:93:63:E5 + X509v3 Authority Key Identifier: + keyid:60:93:53:2F:C7:CF:2A:D7:F3:09:28:F6:3C:AE:9C:50:EC:93:63:E5 + + Signature Algorithm: sha1WithRSAEncryption + a7:77:71:8b:1a:e5:5a:5b:87:54:08:bf:07:3e:cb:99:2f:dc: + 0e:8d:63:94:95:83:19:c9:92:82:d5:cb:5b:8f:1f:86:55:bc: + 70:01:1d:33:46:ec:99:de:6b:1f:c3:c2:7a:dd:ef:69:ab:96: + 58:ec:6c:6f:6c:70:82:71:8a:7f:f0:3b:80:90:d5:64:fa:80: + 27:b8:7b:50:69:98:4b:37:99:ad:bf:a2:5b:93:22:5e:96:44: + 3c:5a:cf:0c:f4:62:63:4a:6f:72:a7:f6:89:1d:09:26:3d:8f: + a8:86:d4:b4:bc:dd:b3:38:ca:c0:59:16:8c:20:1f:89:35:12: + b4:2d:c0:e9:de:93:e0:39:76:32:fc:80:db:da:44:26:fd:01: + 32:74:97:f8:44:ae:fe:05:b1:34:96:13:34:56:73:b4:93:a5: + 55:56:d1:01:51:9d:9c:55:e7:38:53:28:12:4e:38:72:0c:8f: + bd:91:4c:45:48:3b:e1:0d:03:5f:58:40:c9:d3:a0:ac:b3:89: + ce:af:27:8a:0f:ab:ec:72:4d:40:77:30:6b:36:fd:32:46:9f: + ee:f9:c4:f5:17:06:0f:4b:d3:88:f5:a4:2f:3d:87:9e:f5:26: + 74:f0:c9:dc:cb:ad:d9:a7:8a:d3:71:15:00:d3:5d:9f:4c:59: + 3e:24:63:f5 +-----BEGIN CERTIFICATE----- +MIIDnDCCAoSgAwIBAgIBATANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQGEwJOTzER +MA8GA1UECgwIR3JlZW4gQVMxJDAiBgNVBAsMG0dyZWVuIENlcnRpZmljYXRlIEF1 +dGhvcml0eTEWMBQGA1UEAwwNR3JlZW4gUm9vdCBDQTAgFw0xNzA3MTMwMzQ0Mzla +GA8yMDMwMTIzMTIzNTk1OVowXjELMAkGA1UEBhMCTk8xETAPBgNVBAoMCEdyZWVu +IEFTMSQwIgYDVQQLDBtHcmVlbiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFjAUBgNV +BAMMDUdyZWVuIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCn6O3e1FQIQQdA1cBD1qvTniGHxhO/p889CE/B/o/lbMWJl+UndSbDKnMtNHxv +NY1AZmEFwOvpszhH+IsmNSzf3CQx/nLjhxDR96BXt/OxGv7HS/h7FG1zCFTrYzwM +ziKVXz/yb4muY9qAdDYhE+iRAVh3zMLyQr/rs2CnIe2IJH/r/wdBm5PIX2qOphoV +PLznDf0F/TzBHB0fVytAJ2KhfEhjwUXnLyDtkhxClORYcHq20oXFYdjNxjdrcjt/ +r1WB1p3cEMnYDoHkXkATLyDoa0aBzohH3ThxPe8hzMBnzwr06T+onSYlLiMeoxEY +y9FwHJ59CbGkINyVFR1JzxutAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRgk1Mvx88q1/MJKPY8rpxQ7JNj5TAfBgNV +HSMEGDAWgBRgk1Mvx88q1/MJKPY8rpxQ7JNj5TANBgkqhkiG9w0BAQUFAAOCAQEA +p3dxixrlWluHVAi/Bz7LmS/cDo1jlJWDGcmSgtXLW48fhlW8cAEdM0bsmd5rH8PC +et3vaauWWOxsb2xwgnGKf/A7gJDVZPqAJ7h7UGmYSzeZrb+iW5MiXpZEPFrPDPRi +Y0pvcqf2iR0JJj2PqIbUtLzdszjKwFkWjCAfiTUStC3A6d6T4Dl2MvyA29pEJv0B +MnSX+ESu/gWxNJYTNFZztJOlVVbRAVGdnFXnOFMoEk44cgyPvZFMRUg74Q0DX1hA +ydOgrLOJzq8nig+r7HJNQHcwazb9Mkaf7vnE9RcGD0vTiPWkLz2HnvUmdPDJ3Mut +2aeK03EVANNdn0xZPiRj9Q== +-----END CERTIFICATE----- diff --git a/util/testutil/roundtrip.go b/util/testutil/roundtrip.go new file mode 100644 index 0000000000000000000000000000000000000000..42c1095278e4618d6e40b268747ad2fcb4974b7d --- /dev/null +++ b/util/testutil/roundtrip.go @@ -0,0 +1,47 @@ +// Copyright 2017 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testutil + +import ( + "net/http" +) + +type roundTrip struct { + theResponse *http.Response + theError error +} + +func (rt *roundTrip) RoundTrip(r *http.Request) (*http.Response, error) { + return rt.theResponse, rt.theError +} + +type roundTripCheckRequest struct { + checkRequest func(*http.Request) + roundTrip +} + +func (rt *roundTripCheckRequest) RoundTrip(r *http.Request) (*http.Response, error) { + rt.checkRequest(r) + return rt.theResponse, rt.theError +} + +// NewRoundTripCheckRequest creates a new instance of a type that implements http.RoundTripper, +// wich before returning theResponse and theError, executes checkRequest against a http.Request. +func NewRoundTripCheckRequest(checkRequest func(*http.Request), theResponse *http.Response, theError error) http.RoundTripper { + return &roundTripCheckRequest{ + checkRequest: checkRequest, + roundTrip: roundTrip{ + theResponse: theResponse, + theError: theError}} +} diff --git a/web/ui/bindata.go b/web/ui/bindata.go index 30e3f995d94f90e24ea156d68eedc3e1b4866107..e60f9c3c9ab19183679187449fc51509298acba5 100644 --- a/web/ui/bindata.go +++ b/web/ui/bindata.go @@ -12,12 +12,14 @@ // web/ui/static/css/graph.css // web/ui/static/css/prom_console.css // web/ui/static/css/prometheus.css +// web/ui/static/css/targets.css // web/ui/static/img/ajax-loader.gif // web/ui/static/img/favicon.ico // web/ui/static/js/alerts.js // web/ui/static/js/graph.js // web/ui/static/js/graph_template.handlebar // web/ui/static/js/prom_console.js +// web/ui/static/js/targets.js // web/ui/static/vendor/bootstrap-3.3.1/css/bootstrap-theme.min.css // web/ui/static/vendor/bootstrap-3.3.1/css/bootstrap.min.css // web/ui/static/vendor/bootstrap-3.3.1/fonts/glyphicons-halflings-regular.eot @@ -141,7 +143,7 @@ func webUiTemplatesAlertsHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "web/ui/templates/alerts.html", size: 1836, mode: os.FileMode(420), modTime: time.Unix(1499786499, 0)} + info := bindataFileInfo{name: "web/ui/templates/alerts.html", size: 1836, mode: os.FileMode(420), modTime: time.Unix(1502369604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -221,7 +223,7 @@ func webUiTemplatesRulesHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "web/ui/templates/rules.html", size: 283, mode: os.FileMode(420), modTime: time.Unix(1499786499, 0)} + info := bindataFileInfo{name: "web/ui/templates/rules.html", size: 283, mode: os.FileMode(420), modTime: time.Unix(1502369604, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -246,7 +248,7 @@ func webUiTemplatesStatusHtml() (*asset, error) { return a, nil } -var _webUiTemplatesTargetsHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xbc\x56\x4f\x6f\xdb\x36\x14\xbf\xfb\x53\xbc\x71\xc1\x4e\x95\x05\x14\xd8\x25\xa3\x74\xd8\x56\xa0\x03\xb2\xa1\x6b\xda\xcb\x2e\x05\x25\x3e\x4b\x4c\x19\x52\x23\x9f\x8c\x06\x2c\xbf\xfb\x40\x4a\x72\x6c\xd9\x1e\xda\x15\x58\x0e\x0c\xdf\xff\x7f\xbf\x47\x39\x04\x89\x3b\x65\x10\x58\x8f\x42\xb2\x18\xf9\x77\x45\x01\x46\x7d\x82\xa2\xa8\x43\x40\x23\x63\xdc\x6c\x9e\xb5\x5a\x6b\x08\x0d\xb1\x18\x37\x00\x5c\xaa\x3d\xb4\x5a\x78\x5f\x65\x81\x50\x06\x5d\xb1\xd3\xa3\x92\xac\xde\x00\x00\xf0\xfe\x25\x28\x59\x31\x12\xae\x43\xf2\xac\x7e\x37\x5d\x78\xd9\xbf\x9c\x34\x00\x38\x89\x46\xe3\xe2\x67\x22\xf2\x59\xb4\xd6\x48\x34\x1e\xe5\x4c\x37\xd6\x49\x74\x07\xd2\x93\x53\xc3\x81\xea\xed\x1e\x1d\x5b\x9c\x02\x84\xe0\x84\xe9\x10\x6e\x1e\x6c\xf3\x02\x6e\x06\x6b\x35\xdc\x56\xb0\x9d\x32\x78\x63\xad\xf6\x90\xab\x58\xfe\x38\xa5\x16\xd4\x47\x9c\xc4\x73\x35\xa7\x1e\x5a\xab\xfd\x20\x4c\xc5\x7e\x64\x4b\xa2\x0f\xb6\xf9\x90\x0c\x52\x50\x2e\x72\x95\x0f\xb6\x29\x42\x48\x01\x63\x64\xd0\x3b\xdc\x55\xec\xfb\x13\x66\xbd\xdc\x78\x29\x6a\x5e\x52\x9f\x0e\x77\x1e\xf3\x84\x91\x53\xab\x5f\x19\x39\x58\x65\x28\x5b\x5d\x90\xdf\x93\x20\xbc\x26\xbc\x13\x0d\x6a\x7f\x5d\xea\x09\xee\x5b\x27\x86\xab\x0e\x5e\x39\x67\xdd\xb9\x70\x9d\x7d\xd2\x58\x35\x91\x53\x63\xe5\xd3\x31\xe7\x30\x99\x34\x93\x93\x11\x5c\x29\x5e\x9e\xb1\xc4\xdc\xdd\x10\xb6\xef\xdf\xde\xc1\x67\xe8\xb4\x6d\x84\x7e\xff\xf6\x6e\x6a\x72\xe2\x6e\xef\xdb\x1e\x1f\x31\xc6\xdb\xb2\x9c\x39\xaf\xad\xa7\x18\x67\xe2\x8d\xa0\x7e\x1e\x44\x73\x16\xf4\x28\x4b\x9d\x7a\xf7\x02\x6e\xf6\x42\x8f\xe8\x33\x86\x92\xf9\x9f\x23\xba\x27\x58\xa5\xbf\x32\x55\x8b\x59\xb2\x9a\x1d\x5c\xb4\x00\xe0\x09\x5f\x0b\xb6\x72\x48\xc8\x67\x31\x38\xf5\x28\xdc\x53\x86\x4e\xe6\xc4\x98\xea\x9e\xbc\xc5\xc8\x78\x99\x2c\xcf\xf3\x4f\x69\x4c\xeb\xfb\x65\x7c\x5e\x5e\xe8\xf3\x39\x6b\x95\xa9\xd0\xe8\x08\xf2\x59\x84\x00\xdb\xd7\x28\x34\xf5\xf0\x19\xfa\x7c\x79\x67\x7f\x49\x7a\x10\x23\xf8\x84\xcf\x0f\xca\x48\xd5\x0a\xb2\x0e\x08\x3f\x51\x31\x0e\x03\xba\x56\x78\x64\x97\x0b\x98\xfd\x5d\x28\xe2\x72\xd9\xff\xad\x88\x76\x74\xde\xba\x22\xaf\x17\x3a\x06\x52\x90\x28\xc8\x76\x9d\xc6\x8a\x91\xb5\x9a\xd4\xc0\x80\x14\x25\x7a\x16\xf7\xf4\xa8\x2b\x72\x23\x4e\xa4\x75\xaa\x53\x46\xe8\x62\xd6\xe2\x4d\xfd\x33\xee\xac\x43\x70\x98\xa7\xa6\x4c\x77\xcb\xcb\xa6\x3e\x60\xe3\x63\xc2\x46\x46\xd3\xaf\xca\xb7\xe9\xf1\x42\x39\xad\xe9\xf6\x77\x31\xc4\x98\x40\x19\xc2\xcd\xc7\xd4\x4b\x7a\xd4\xf3\xbf\x18\xab\x1f\xfe\x1e\x2d\xfd\x94\x10\xb0\x16\x2d\x92\x3c\xde\x2b\x1d\x9d\x30\x94\x61\x9c\xdf\xcf\x29\x24\x6c\x9f\x43\x43\x7a\xc7\xd8\xbf\x03\xfb\x64\x27\x32\xb8\x27\xb7\xff\x2b\xb8\xb5\xc7\xaf\x8d\x27\x71\x27\x46\x4d\xac\x36\xd6\xe0\xd7\x6f\xce\x37\x82\x2e\x04\xb5\x4b\x9d\xf6\x34\x3d\xb6\xdb\xdf\xfc\x5f\xe8\x6c\x8c\x7f\xe0\x1e\xdd\x52\x51\x08\x5e\x99\x16\x8f\x15\x63\x04\xd1\xd9\x6f\xdc\xdb\xe7\xe8\xf9\x31\xbf\x54\xde\xb5\xcd\x96\x69\xea\x6e\xbd\xc2\xf9\xa1\x3d\xf2\x77\xad\x9f\x5f\x9a\xf7\xfa\x63\x72\x6e\xc7\xcb\xd5\xc7\xe4\x54\x85\x97\xf9\xa7\x40\x12\xf3\x52\xaa\x7d\xbd\x59\xe4\xff\x04\x00\x00\xff\xff\xdf\x77\xb1\xd0\xe7\x08\x00\x00") +var _webUiTemplatesTargetsHtml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x56\x4d\x6f\xe3\x36\x13\xbe\xfb\x57\x0c\xf4\x06\x2f\x5a\x60\x65\x01\x0b\xf4\x92\x52\x2a\xd0\x76\x81\x2d\x90\x16\xe9\x66\xb7\x87\x5e\x16\x94\x38\xb6\x98\x30\xa4\x4a\x8e\x8c\x35\xb8\xfc\xef\x05\x29\xc9\x76\x12\xc9\x69\xd3\x2e\x7a\x91\x4d\xce\xcc\x43\x3e\xc3\xf9\xf2\x5e\xe0\x46\x6a\x84\xac\x45\x2e\xb2\x10\x56\x4c\x49\x7d\x07\xb4\xef\xb0\xcc\x08\x3f\x51\xd1\x38\x97\x81\x45\x55\x66\x8e\xf6\x0a\x5d\x8b\x48\x19\xb4\x16\x37\x65\xe6\x3d\x74\x9c\xda\x6b\x8b\x1b\xf9\x09\x42\x28\x1c\x71\x92\x4d\xb4\x29\x88\xdb\x2d\x92\x5b\x37\xce\x7d\xb7\x2b\xbd\x87\xba\x97\x4a\xfc\x86\xd6\x49\xa3\x21\x84\xac\x5a\x31\xd7\x58\xd9\x11\x38\xdb\x2c\x63\xdd\x1e\xa1\x6e\x97\x90\x58\x31\x20\x55\x2b\xef\x51\x8b\x10\x56\xab\x23\xb3\xc6\x68\x42\x4d\x91\x1c\x00\x13\x72\x07\x8d\xe2\xce\x95\x49\xc0\xa5\x46\x9b\x6f\x54\x2f\x45\x56\xad\x00\x00\x58\xfb\x1a\xa4\x28\xb3\xf1\xd0\xac\x7a\x3f\xfc\x61\x45\xfb\x7a\xd0\x00\x60\xc4\x6b\x85\x13\xce\xb0\x48\xdf\xbc\x31\x5a\xa0\x76\x28\xc6\x75\x6d\xac\x40\x7b\x58\xb6\x66\x87\x36\x9b\x60\x00\xbc\xb7\x5c\x6f\x11\x2e\x6e\x4d\xfd\x0a\x2e\x3a\x63\x14\x5c\x96\xb0\x1e\xce\xbc\x36\x46\x39\x48\xf7\x3e\x1a\x5c\xb4\xc8\x15\xb5\xfb\xa8\xa7\xfb\xfb\xb7\xe3\x2a\xd9\x3e\x56\x25\x43\x3c\x01\x2a\xd4\x33\x1a\x91\x88\x9d\x58\xdc\x9a\xfa\x63\x0c\x02\xb4\xde\xcb\x0d\x28\x82\xc3\x49\x03\x4e\x08\x20\xe2\x65\xed\xe8\xe3\x13\x1a\x13\x98\x80\xc6\x28\xd7\x71\x5d\x66\xdf\x3c\x11\x03\x30\x39\x1d\x26\x1b\xa3\xf3\xa6\xc5\x9d\x35\x3a\xef\xbb\xf8\x82\xb2\x62\x3c\x39\xfe\xd6\xd4\xb9\xf7\xd1\x23\x21\x4c\x81\xf6\xbf\x07\x9b\xd5\xf4\x0f\xbe\x3a\xfa\x23\x84\x62\x62\x1c\x02\xf4\xdd\xd7\xac\xe0\x4f\x6e\x58\x90\x78\xb8\xc7\x0a\xb2\xd5\xbc\x4b\xa0\x84\xe4\x14\x81\xc4\xa5\x72\x73\x74\x67\x28\xbe\x34\x32\x1c\x59\xd9\x2d\xc6\xc9\xe9\x01\xf1\x91\xe6\x24\xe9\xea\xf3\x82\x64\x56\xbd\xd1\xa2\x33\x52\x13\x2b\xa8\x3d\xa7\x77\x43\x9c\xf0\x39\xa5\x2b\x5e\xa3\x72\xcf\x6b\x39\x82\x9b\xc6\xf2\xee\x59\xc0\x37\xd6\x1a\xbb\xac\xf4\xf4\xa1\x0e\xfb\x4b\x0e\x61\x54\x1b\xb1\x9f\x93\x1c\xd2\x6e\x26\x25\x8e\xd6\x67\x9c\xb9\xf0\x00\x49\xc8\x0f\xc5\x71\xfd\xe1\xdd\x15\x7c\x86\xad\x32\x35\x57\x1f\xde\x5d\x0d\xa1\x1b\x77\xd7\x37\x4d\x8b\xf7\x18\xc2\x65\x51\x8c\x3b\x6f\x8d\xa3\x10\xc6\xc5\x35\xa7\x36\x84\x18\xc1\xac\x5e\xbc\xc6\x09\x0f\x15\x5f\xe3\x15\x5c\xec\xb8\xea\xd1\xa5\x12\x12\x61\x7e\xed\xd1\xee\x61\x81\xe0\x23\x08\x39\x99\x47\xeb\x11\xe8\xac\x25\x00\x8b\xa9\x3e\xc5\x7a\xba\x02\xa4\x6f\xde\x59\x79\xcf\xed\x3e\x25\x6a\xda\x09\x21\xfa\x63\x40\x0d\x21\x63\x45\xb4\x5c\xe6\x15\xaf\x35\xd4\xf1\x97\xc9\x9f\xe6\xf9\x89\xec\xec\xe3\x9d\x32\xe2\x0a\x2d\x41\xfa\xe6\xde\xc3\x7a\x28\xb5\xf0\x19\x86\x8a\xf3\xde\xfc\x90\xca\x44\x08\x10\x5b\x15\x7e\x94\x5a\xc8\x86\x93\xb1\x10\x1b\x67\xde\x77\x1d\xda\x86\x3b\x9c\xcd\xe3\x23\x91\x11\xf7\x0c\xd9\xf3\xee\xfa\x77\xc8\x36\xbd\x75\xc6\xe6\xa9\x40\xa0\xcd\x40\x70\xe2\x39\x99\xed\x56\xc5\x41\xc0\x18\x45\xb2\xcb\x80\x24\xc5\xf5\x28\x6e\xe9\x5e\x95\x64\x7b\x1c\x96\xc6\xca\xad\xd4\x5c\xe5\xa3\x16\xab\xab\xef\x71\x63\x2c\xc6\xf1\x21\x46\x81\xd4\xdb\x4b\x56\xd4\xd5\x21\xe6\xee\x62\xcc\xa5\x68\xfd\x51\xba\x26\xd6\x3c\x14\x43\x61\x09\x21\x06\xbe\xf7\x17\x77\xd1\xdf\x74\xaf\xc6\x9f\x10\xca\xff\xff\xd1\x1b\xfa\x36\x46\xd3\x63\xd1\x24\x99\x6f\x4f\x0f\xbd\x3e\xc4\x65\x4a\x95\x54\x7a\x87\x63\x61\x3d\xfc\xae\x7f\xe6\x5d\xaa\xfe\xd9\x5f\x4b\x9e\x07\xf9\x97\x12\x48\x8d\x34\xfe\xc3\x04\x52\x0e\x5f\x7a\xbe\xc0\x0d\xef\x15\x65\x95\x36\x1a\xff\x79\xb6\x7e\xa1\x00\x4e\x83\xca\x3a\xf6\x98\xa1\xc5\xac\x7f\x72\xbf\xa3\x35\x21\xfc\x82\xbb\x34\xa6\x24\x0f\x78\xef\xa4\x6e\xf0\x54\x31\x04\xe0\x5b\xf3\x85\x6a\xc8\xf1\x56\xa9\xa5\x9d\x73\xcb\x52\xb5\x19\xe6\xac\xc7\x65\x25\x35\x8f\x13\xdc\xe7\xde\xe5\xa5\xfc\x96\x5a\xed\x32\x1e\x2b\x16\x5a\x2d\x2b\xd2\x3c\xf3\x77\xe7\xb0\x87\x27\x9d\x80\xb0\x42\xc8\xdd\x61\xca\xff\x33\x00\x00\xff\xff\x65\x7b\xa9\x65\xbe\x0c\x00\x00") func webUiTemplatesTargetsHtmlBytes() ([]byte, error) { return bindataRead( @@ -261,7 +263,7 @@ func webUiTemplatesTargetsHtml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "web/ui/templates/targets.html", size: 2279, mode: os.FileMode(420), modTime: time.Unix(1499786499, 0)} + info := bindataFileInfo{name: "web/ui/templates/targets.html", size: 3262, mode: os.FileMode(420), modTime: time.Unix(1502370142, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -326,7 +328,7 @@ func webUiStaticCssProm_consoleCss() (*asset, error) { return a, nil } -var _webUiStaticCssPrometheusCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\x8f\xcd\x6a\xeb\x40\x0c\x85\xf7\x7e\x8a\xb3\x0e\x38\x3f\x97\xdb\xcd\xf4\x19\xfa\x0c\x41\xf6\xc8\xb1\xca\x58\x1a\x3c\x72\xe2\xb4\xf4\xdd\x4b\x62\x87\x86\x42\x37\x5a\x7c\x7c\xe7\x70\xb4\xdb\xe0\xcd\xce\x8c\x68\x17\x45\x6b\xea\xac\x8e\x86\x5b\x9a\x0a\xe3\xc2\xe8\xe9\xcc\x20\x74\x32\x73\x84\xd2\xb9\xa1\x11\xde\x93\x43\x0a\x5e\xf6\x79\x86\x53\x4a\xd8\xec\xaa\xc6\xe2\x15\x9f\x15\x90\x29\x46\xd1\x53\xed\x96\xc3\x5d\x79\x7d\x82\x8d\xb9\xdb\x10\xf0\xef\xce\xbf\xaa\xca\xfb\xed\xbb\x35\xc7\x9e\x29\xf2\x78\xcf\x77\xa6\x5e\x17\xf9\xe0\x87\xf5\xab\xf2\xf0\x47\xe5\xe1\x51\xb9\x2d\x4e\xce\x47\xd1\x28\x2d\xb9\x8d\xcf\xab\x02\xf6\xf8\x9f\xe7\xe5\x2e\x76\x12\xe7\x91\xd2\xd1\x26\xcf\x93\xc3\xe3\xcf\x8a\x8e\x06\x49\xd7\x80\xc1\xd4\x4a\xa6\x96\x97\x44\x3b\x8d\xc5\xc6\x3a\x9b\xa8\xaf\xa3\x17\x14\xb0\xb2\xc5\x73\xb3\xe4\x92\x6b\x51\x5d\xb5\x81\xe6\xfa\x22\xd1\xfb\x00\x35\xe5\xdb\x1b\xce\xb3\xd7\x94\xe4\xa4\x01\x89\x3b\xbf\x45\xbf\x03\x00\x00\xff\xff\x86\xf0\x99\x9a\x95\x01\x00\x00") +var _webUiStaticCssPrometheusCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x4c\x8f\x4d\x6a\xc3\x40\x0c\x46\xf7\x3e\xc5\xb7\x0e\x4c\x13\x4a\xbb\x99\x9e\xa1\x67\x08\xb2\x47\x8e\x05\x63\x69\xb0\xe5\x9f\x50\x7a\xf7\x10\x4f\x16\xd9\x68\xf1\x78\xdf\x03\x9d\x4f\xf8\xb5\x95\x91\x6c\x53\x74\xa6\xce\xea\x68\xb9\xa3\x65\x66\x6c\x8c\x81\x56\x06\xa1\x97\x9d\x13\x94\xd6\x96\x26\xf8\x40\x0e\x99\xf1\x7d\x29\x3b\x9c\x72\xc6\xe9\xdc\xb4\x96\xee\xf8\x6b\x80\x42\x29\x89\xde\x82\x5b\x89\x87\xf2\xf3\x06\x5b\x73\xb7\x31\xe2\xf3\xe0\xff\x4d\xf3\x31\x3b\x39\x5f\x45\x93\x74\xe4\x36\xbd\x27\x22\x2e\xf8\x2a\x7b\xbd\xd5\xce\xe2\x3c\x51\xbe\xda\xe2\x65\x71\x78\x3a\xfc\xde\xd4\x43\x4f\xa3\xe4\x7b\xc4\x68\x6a\x73\xa1\x8e\xeb\xa2\x5b\xa6\xd9\xa6\x50\x4c\xd4\xb9\xe6\x2b\x8a\x78\xb1\xea\xb9\x59\x76\x29\x41\x54\x5f\xda\x48\x7b\xd8\x24\xf9\x10\xa1\xa6\xfc\x7c\xc3\x79\xf7\x40\x59\x6e\x1a\x91\xb9\xf7\xe7\xf4\x11\x00\x00\xff\xff\xa0\xeb\x4b\x6c\x42\x01\x00\x00") func webUiStaticCssPrometheusCssBytes() ([]byte, error) { return bindataRead( @@ -341,7 +343,27 @@ func webUiStaticCssPrometheusCss() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "web/ui/static/css/prometheus.css", size: 405, mode: os.FileMode(420), modTime: time.Unix(1462274250, 0)} + info := bindataFileInfo{name: "web/ui/static/css/prometheus.css", size: 322, mode: os.FileMode(420), modTime: time.Unix(1502369610, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _webUiStaticCssTargetsCss = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x8e\x51\xaa\xc2\x30\x10\x45\xff\xb3\x8a\xfb\x16\xd0\x47\xf5\x33\x05\xb7\x22\xa9\x33\xb6\x23\x6d\x26\x4c\x46\x14\x4b\xf7\x2e\x14\x91\xfa\x7b\x38\xf7\x72\xdc\xfe\x6f\xda\x9f\x47\x4e\xc4\x86\x25\x00\xc0\x55\xb3\x37\x55\x5e\x1c\x71\x6c\xcb\xb3\xdb\x60\x49\x44\x92\x87\xc6\xb5\x44\x1c\xbe\x78\x73\x1f\x2c\xc3\xe8\x11\xbd\x4e\xf4\x6b\xf7\xea\xae\xf3\x7e\x70\xb9\x5b\x55\x8b\x28\x2a\xd9\xd9\xba\xb0\x86\xf0\x89\x20\xf6\x24\x53\xc5\x09\x4e\xcb\xfe\x26\xa2\xc5\x9f\xcc\x45\xcd\x53\xf6\x2e\xac\xef\x00\x00\x00\xff\xff\x62\x16\x77\x5a\xb6\x00\x00\x00") + +func webUiStaticCssTargetsCssBytes() ([]byte, error) { + return bindataRead( + _webUiStaticCssTargetsCss, + "web/ui/static/css/targets.css", + ) +} + +func webUiStaticCssTargetsCss() (*asset, error) { + bytes, err := webUiStaticCssTargetsCssBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "web/ui/static/css/targets.css", size: 182, mode: os.FileMode(420), modTime: time.Unix(1502369610, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -406,7 +428,7 @@ func webUiStaticJsAlertsJs() (*asset, error) { return a, nil } -var _webUiStaticJsGraphJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xe4\x7d\x6b\x77\xdb\x38\xb2\xe0\x77\xff\x8a\x0a\x3b\x27\xa2\xda\x32\x65\xa7\x6f\xf7\xde\x91\x2d\xf7\xa6\xf3\x98\x64\x26\xaf\x71\xdc\xaf\xe3\x78\x7c\x20\x12\x12\x19\x53\x24\x07\x00\x6d\xab\x13\xfd\xf7\x3d\x28\x3c\x08\x90\x94\xa5\xee\x9e\x9d\xb3\x7b\xae\x3f\xc8\x16\x1e\x85\x42\x55\xa1\x50\xa8\x2a\xc0\x37\x84\xc1\x7b\x56\x2e\xa9\x48\x69\xcd\x61\xea\x7e\xf9\xf2\x05\x3e\xaf\x8f\xf7\x64\x93\x05\x23\x55\x7a\x4e\x97\x55\x4e\x04\x3d\xde\xc3\xb2\x0f\xcf\x9f\xbe\x7b\xfb\x0c\xa6\x70\x74\x78\x78\x78\xbc\xb7\xd7\xf4\x8c\xfe\x2a\x9b\xc3\x14\xe6\x75\x11\x8b\xac\x2c\x42\x9a\xd3\x25\x2d\xc4\x08\xca\x4a\x7e\xe7\x23\x48\x49\x91\xe4\xf4\x69\x4a\x8a\x05\x35\xdf\xce\xe8\xb2\xbc\xa1\x43\xf8\xbc\x07\x20\xd2\x8c\x47\x34\x87\x29\xe8\xbe\xc7\xa6\x10\x71\x79\x79\xfe\xe6\x35\x4c\xa1\xa8\xf3\xdc\x56\x68\xd8\x30\x35\xa3\xd8\x1a\x77\x30\x98\x7a\x63\xb7\xda\x28\x14\x5c\xd4\x15\x3a\xe0\xa1\x18\xca\x1e\x43\xd9\x75\x6d\xfb\xb3\x2c\xbe\xe6\x29\xb9\x35\x73\xf7\x50\x4b\x88\x20\x30\x85\x8b\xcb\xe3\x3d\x53\x94\x15\x99\xc8\x48\x9e\xfd\x46\xc3\xe1\xf1\xde\xba\x87\x80\x91\xc8\x96\xf4\x05\x89\x45\xc9\xe4\xa4\x24\x1a\xc1\x2a\x98\xc0\x77\x87\xf0\xb5\xfa\x78\xfc\x5f\xf0\x35\x7c\xf3\xdd\xb7\x23\x59\x75\xdb\xad\xfa\x5f\x58\x91\xb4\x2a\xb0\x30\x6d\x0a\xf1\xfb\x12\xbf\xe3\x9f\x3c\x98\xc0\x51\x3f\x46\x5c\xd0\xea\x27\x92\xd7\x54\x22\x74\x21\x1b\x1f\xf1\x60\x04\xc1\xd1\xa1\xfa\xb5\x94\x9f\xdf\xe2\xe7\x91\xfa\xf5\xcd\xa1\xfa\x96\xca\xcf\xc7\xf8\xf9\x1d\x7e\x1e\xa9\x2f\x47\x09\x56\x24\x01\x0e\x7d\x74\x8b\xdf\xf0\xf3\xbf\xf0\xf3\xbf\xf1\xf3\x68\x85\xe5\xab\x60\xef\xb2\x0f\xad\xa2\x5e\xe2\x1f\x12\xab\x3e\x51\x8c\x2a\x56\x8a\x52\xac\x2a\xea\x90\xbd\xcb\x64\x29\xd5\x9c\xe6\x73\x98\x22\x8b\x24\xf7\xe4\xd7\x28\x4b\xbc\x85\xd1\x1e\x74\x7f\x1f\xb9\x3a\x1e\xc3\x07\x2a\x20\xa1\x73\x52\xe7\xc2\xc8\x60\x64\x80\x98\xef\x08\x4c\x83\x3d\x6e\x57\x32\x29\x92\x57\x59\x51\xd5\xc2\xb4\xea\xab\xfa\xf2\x05\x29\x2a\xbb\x67\x73\x08\xbd\x76\x82\xcc\x60\x3a\x9d\x42\x5d\x24\x74\x9e\x15\x34\x31\x02\xdc\x6d\x05\x47\x28\xc2\x1a\xf9\x67\x8c\xdc\xaa\x85\x0e\x71\x59\x08\x56\xe6\x1c\x48\x91\xe0\x17\x92\x15\x94\xc1\x9c\x95\x4b\x78\x89\xeb\x60\x46\x18\x07\xa1\x15\x42\xb4\xa7\x89\xd7\xac\x40\x35\xe4\xa0\x22\x22\x7d\xcf\xe8\x3c\xbb\x1b\x4c\xe0\xfd\x93\xf3\x97\x57\xef\xcf\x9e\xbf\x78\xf5\xcb\x48\x55\xcf\xea\x2c\x4f\x7e\xa2\x8c\x67\x65\x31\x98\xc0\x0f\x3f\xbe\x7a\xfd\xec\xea\xa7\xe7\x67\x1f\x5e\xbd\x7b\x6b\x16\xd7\xa7\x7f\xd4\x94\xad\x22\x7a\x27\x68\x91\x84\x56\x7f\xb8\xb3\x19\x5a\x3a\xba\xba\xe1\x61\xf8\xa6\xe6\x82\xc4\x29\x8d\x18\x2d\x12\xca\x42\x4f\x8b\x59\x5d\x34\x6c\xba\xd3\x3c\x22\x55\x25\xc7\xf1\xa1\x0d\x0d\x83\xff\x4a\x05\x30\x3a\xa7\x8c\x16\x31\xe5\x20\x4a\x20\x79\x0e\x22\xa5\x90\x15\x82\x32\xca\x45\x56\x2c\x8c\xc6\xe2\x90\x15\x58\xd7\x10\x55\xd1\x91\x14\x89\x02\x37\xcb\x8a\x04\xe8\x0d\x2d\x84\x56\x2f\x0c\xe5\xc5\x6a\xdc\x9f\x99\x44\x87\x19\x51\xa0\x79\x34\xcf\x8a\x24\x0c\xbe\xc2\xda\xab\x5b\x55\x1d\xc0\xbe\x11\xa8\x66\x2a\xff\x92\x54\x7b\x51\xb2\x25\x4c\x3d\x58\x1a\x82\xaa\xbf\x9a\x97\x6c\x19\xa8\xd9\xa9\x11\xee\x2a\xd6\xdf\x41\xd0\x3b\x41\x18\x25\x17\x05\x59\xd2\xa9\x6c\x77\x19\x38\x84\xbb\xab\x58\x74\x4d\x57\x15\xa3\x9c\x87\x8d\xda\x37\xb2\x37\x1e\xc3\x73\x49\x20\xb8\x25\x1c\xb0\x11\x4d\xe0\x36\x13\x69\x59\x0b\x24\x11\x4f\xb3\xb9\x80\x6b\xba\x8a\xb0\xbd\x94\x6a\x1a\xdd\xa6\x59\x9c\xc2\x74\x0a\x47\xdf\xc0\xa3\x47\xf0\x80\x46\xd8\xec\xef\x74\x65\xe0\xb6\x27\x1b\xf1\x7a\xb6\xcc\x44\x88\x98\xc9\x1f\x1a\x55\x0c\x09\xfc\x4c\x2d\x4b\x53\x83\x42\x8f\x78\x3d\xa9\x45\x79\xc0\x28\x97\x1a\x41\x62\x22\x27\x0a\x72\xa6\x50\x16\x80\xcb\x4d\xa1\x84\xf2\x3d\x9f\x73\x2a\xb4\x7a\x88\xd4\xb7\x97\x34\x5b\xa4\x02\x0e\x54\x59\x9c\x67\xb4\xd0\x65\xc7\xb6\x9f\x02\x7f\xae\x49\xe8\x6f\x8c\xcd\x54\x00\x1e\xca\xef\x51\xcc\x79\x38\x48\x11\xc4\x60\x04\x03\x52\x8b\x72\xd0\x2e\xa5\x79\xc4\x63\x56\xe6\xb9\x1e\x7e\x5f\xe3\x66\xa6\xa7\x7e\x3d\x54\x1b\x55\x54\x16\xe1\xe0\x9a\xae\xea\x4a\x4d\x68\x30\xf2\x34\x5f\x0b\x3d\xbd\xb9\xc1\x5a\x6d\x70\x2d\x26\xc7\xb8\x6b\xaa\xf5\xe1\xee\xa3\x8e\x10\xa1\xa6\x7a\xe5\xea\xb0\x86\x3f\x4a\x98\x10\x0b\x25\x49\x8e\x5a\x73\x05\x4a\x2e\xdc\x6b\x9a\xfc\x20\x8a\x4d\x30\x4c\x93\xab\x99\x28\xba\x1d\x77\x18\x59\xb7\x74\x47\xcd\x0a\x4e\x99\x78\x43\x05\xcb\xe2\x4d\x10\x38\xcd\x69\xac\x41\xa8\xf6\x57\x4b\xec\xe0\x02\x62\x74\xce\x28\x4f\x5f\x49\x99\xbf\x21\xf9\x2e\xb0\x74\x97\x4b\x77\x39\xc6\x65\xc1\xcb\x9c\x9e\xa3\xb2\xee\x5b\xc5\xba\x41\xd0\xd2\x80\xb2\x03\x6c\xe8\xa2\x54\x87\x55\x46\xee\x70\x82\xcc\x78\x7f\x2f\x72\x21\x2d\x98\x03\x51\x2e\x16\x39\x9d\x0e\x04\x99\x0d\xdc\xe9\xca\x8e\x11\xfd\x57\x67\x23\x1a\xca\x8f\x30\xe0\x69\x79\xdb\x6e\x5d\x16\xaa\xbc\x88\x66\xd8\x34\x70\x64\xd2\xaa\x0d\xb9\x76\x04\x61\x0b\x5c\x73\x0f\x43\x1a\xa9\x2f\x5a\xc8\x7b\x36\x34\x55\x1f\x55\x84\xd1\x42\x84\xc3\x28\x2b\x12\x7a\x17\xba\xed\x5d\x99\x35\x15\x52\xdb\x3c\x0c\x83\xaf\xa4\x22\xd5\x10\x88\x10\x2c\x0c\x08\xcb\xc8\x81\xd9\x0c\x83\xe1\x30\x4a\x09\x7f\x9a\x13\xce\xc3\x80\xd1\xbc\x24\x49\x30\x6c\x69\x22\xa5\x7f\x70\xcb\x6a\x54\x8d\x5a\x45\x4a\xe5\x9f\x51\x51\xb3\x02\xa4\x15\xc9\x61\x5e\xc6\x35\x87\x19\x89\xaf\xe5\x56\x82\xca\x37\x2b\xb8\xa0\x24\x81\x72\x0e\x0a\x96\xdc\x51\xa2\x3e\x01\x8d\x66\xc8\x9a\x6b\xba\x4a\xca\xdb\x42\xda\x47\x0c\x61\xf7\x52\xb2\x59\xc0\x38\xa6\x47\x12\x2c\xbe\x21\x79\xe8\x7f\x1b\xea\x36\x0a\xea\x06\x4d\xba\x1e\x36\x7b\x07\x63\xe5\x86\xcd\x43\xd5\x05\xc3\x28\xcd\x12\x4d\xf5\x46\x58\x9f\x28\x95\xb8\x59\x56\xa5\x52\x6a\x4b\xb8\x59\x51\x16\x82\xd7\xc5\x69\xbd\x7a\x72\x97\xf1\x8d\xad\x57\x57\xe4\x2e\xe3\x4e\xf3\x9c\x2e\x68\x91\x6c\x40\x47\x55\xba\xca\xa6\xca\x8a\x82\x6e\x9a\xb4\xae\x75\xb7\xc9\x1b\x92\x7f\x10\x44\x6c\x58\x65\x58\x7f\xc5\x65\x03\x6f\x53\x2e\x92\x67\x44\xd0\xfe\x3e\x8e\x42\xa3\x45\xd2\x55\xa4\xba\xb3\x3c\x81\x50\x79\x9e\xa8\xb2\xf8\x9a\xb2\x50\x49\x45\x5e\xc6\x24\xa7\x13\x18\xd0\x62\xa0\x4c\x32\x69\x10\x10\x31\x81\xc1\xaf\xbf\xfe\xfa\xeb\xc1\x9b\x37\x07\xcf\x9e\xc1\xcb\x97\x93\xe5\x52\xd7\x8b\xb2\xcc\x67\x84\xbd\xcf\x49\x8c\x36\xce\x04\x06\xb3\x52\x88\xd2\xd4\xf3\x2c\xa1\x3f\xac\x3e\x64\x09\x9d\x80\x60\x35\xd5\xa5\x69\x79\x7b\x5e\x26\x64\xf5\x43\x2d\x44\x59\xb4\xab\x9e\xe6\x94\xb0\x6e\x61\xc9\x1d\x20\x6a\x1f\xea\x58\xbb\x76\xce\xbe\xa0\x37\x93\x26\xe1\x40\xfe\x79\x9e\x2d\xe9\x7b\x9c\xfa\x60\x88\xb4\xd8\x04\x46\x59\xc4\x2d\x38\x52\x59\x25\x95\xde\xfb\x82\xd6\xee\xd9\xb3\xee\xdd\x5d\xb3\xb5\x15\x98\x0d\xb4\x0b\xa2\xae\x24\x5e\x67\xaa\xb9\x01\x62\x17\x3e\xff\x60\x37\xb6\xce\xd1\x54\xaf\x50\x77\xff\x53\x2b\x18\x0f\x02\x83\xa3\x81\x3e\xa9\x9a\x23\x8e\x58\xe5\x14\xc1\xa9\xed\xb5\x03\x4f\x36\xca\xe2\xd2\x6e\xbd\xcd\x66\xac\x84\x6e\x10\x2d\xf2\x55\x95\xca\x26\x03\x47\x85\xfa\x88\x86\x1d\xd5\xd8\x40\x21\x49\xa2\xd5\xe8\x4c\x14\x07\x15\xcb\x96\x84\xad\x02\x6b\xb4\x49\xc0\x4e\x1b\x3b\xd8\x41\x9c\xd2\xf8\xba\xd5\x8e\xe1\x89\xbc\xd3\xb4\x2e\xb0\x31\x4d\x4c\xf3\x35\xd0\x9c\xd3\x8d\x28\x79\x60\x7e\x1f\x56\x9d\xa1\xee\xc7\xcc\x9b\xc4\xda\x1c\x73\x3c\xa6\x84\x0e\xe7\x1d\x1c\xe3\x3c\x8b\xaf\xc3\x0e\xbb\xfa\x68\x2f\xed\xe5\x46\xe5\xfd\xed\xc3\xbb\xb7\x0d\x37\xc6\x63\x78\x35\x77\x0e\x26\xd2\x26\xd7\xa3\x8c\xb0\xb8\x64\xd9\x22\x2b\x48\x0e\x9c\xb2\x8c\x72\x40\xef\xc5\xa2\x14\xb0\xac\x05\x11\x34\x69\xe0\x84\x5c\x2a\x90\x64\x88\x07\xc5\x5b\x0a\x05\xa5\x89\xdc\xca\x18\x95\x96\x89\x60\x75\x2c\x20\x13\xea\xe0\xe8\x41\x96\x18\x21\xdc\xc8\xe5\x87\x76\x93\x28\x2b\x81\x91\x82\x4b\x75\xf4\x4c\x2e\xe2\xd6\x5c\x1a\xe2\x41\x57\xec\x3b\xb4\xf8\x1e\x06\x87\x03\x98\xc8\x95\x60\xf6\xbd\x36\xb5\x2d\x20\xb5\x0a\xf1\x60\x1f\x5a\x03\xb8\x73\xa8\x32\xe7\x8c\x0e\x2f\x5a\x66\x9b\x23\x2f\xc6\x60\x70\xc6\x32\xb6\xda\xfd\xad\x7a\x4c\x0a\xbd\xe0\xe7\x24\xe7\xb4\x65\xa4\xeb\x4d\xc7\xee\xb4\x5d\xd4\xd5\xbe\x31\x43\x4d\x6c\xcc\xd8\xf8\x0a\xed\xf0\xcb\x60\xd8\x23\x64\xc6\xf4\x88\x19\x25\x9c\x9e\x69\xcb\xc9\x1d\xf4\x3e\xe0\x09\xdd\x01\x78\x42\x7b\x80\xef\x8a\x3a\x2d\x92\x5d\x10\x7f\x5e\x24\xbf\x13\xed\x2d\x80\x0d\xd2\x0e\xe0\x5e\x3b\xad\x47\xe3\xb7\x8c\x2f\x75\x0e\x90\x75\x01\xa3\x95\xdc\x5b\x83\x11\x7c\x96\x27\xd1\x49\x0f\x3c\x54\xed\x23\x58\x96\x72\x93\x0d\x66\x74\x5e\x32\x1a\xac\x3b\x16\x9d\x31\xf4\xe4\x3a\x65\x14\xbf\x65\xc5\xa2\x91\x68\x75\x30\x95\x2a\x4a\x6d\x03\x3d\xc6\x85\x39\x99\xc8\x46\xda\xa8\xb0\x3d\x36\x69\x23\xbd\xe9\xa1\x9b\xf4\x1e\x71\x35\x94\xaa\xca\xaa\xce\x89\xa0\xaf\x70\x86\x64\x96\x53\x35\x4b\xae\x85\xd7\x2a\x37\xc7\x2e\x75\x47\xea\xac\x8e\x75\xbf\xe7\xb2\xf1\x00\x6e\x1c\x71\x27\x87\xe0\xc3\x88\x7c\x22\x77\xa1\xd1\xa5\x72\x90\x32\x99\x40\xf0\xd7\xe7\xe7\xc1\x48\x17\xd6\x2c\xf7\xbc\x5d\xb0\x0f\xc1\x98\x54\xd9\xf8\xe6\x68\x9c\x93\x19\xcd\xc7\x57\x57\x92\xb2\x57\x57\xe3\x1b\x74\xa6\xda\x9e\x52\x01\x9e\xaf\x2a\xc9\xd7\x4f\xbc\x2c\x6c\x39\xaf\xe3\x98\x72\x3e\x69\x10\x94\xd5\x23\x74\x56\x48\x83\xb2\xe6\xae\x1b\x41\xd2\x4c\xd6\x4b\xad\x28\x6a\x0e\x0f\xa6\x53\x08\x34\x88\xc0\x6d\x68\x68\x98\x96\xb7\xcf\xa5\x85\x1e\x06\xf8\x0b\xa4\x0e\xca\x8a\x05\x90\x1b\x92\xe5\x92\x42\xa0\x8e\xb8\xfc\x41\xb3\xc5\x35\x8c\x6d\x4a\xd6\xf6\x2f\x49\xb9\xa5\x25\x2b\x22\x23\xe7\xd6\x34\x9d\x97\x0c\x42\x34\x34\xd0\x67\x0b\x19\x9c\x98\x0e\x51\x4e\x8b\x85\x48\x8f\x21\xdb\xdf\xef\xc1\xd6\x5d\x0b\x17\x87\x97\xd6\x86\x23\x49\x12\x16\xf4\x16\xde\xe1\xf7\x50\x03\xbb\xc8\x2e\x47\xd0\xfc\x3d\x1c\xba\xd8\xee\x79\x80\xe7\xf5\x6f\xbf\xad\xce\x28\xaf\x73\x61\x3d\x98\xea\x07\x15\xc5\x04\x5d\xfa\x23\x6f\xfa\xb2\x6d\xb7\x7c\x49\xaa\x09\x7c\x5e\x6f\x1c\x08\x45\x59\xca\x22\x49\x29\x49\x42\x6f\x86\x65\xcd\x62\x3a\x31\x18\xbb\x50\x33\x41\x97\x7c\x02\x01\xc9\xf3\xc0\x1f\x4d\xc4\x29\x65\x8e\x6c\xc8\x96\x3e\xe1\xcc\xa6\x7f\x4b\x21\x25\x37\x54\x63\x8e\x4c\x88\x6b\x26\x0f\xcb\x6a\x8e\x23\xe0\xd7\x59\xe5\x75\xb4\x0b\xd0\x21\x8f\xd2\x9c\x28\x57\xe8\xf5\xc2\xaf\xed\x11\xbb\x54\xd5\xdd\xdc\x4e\xc7\xdb\xba\x2c\x49\x25\x99\xb1\xde\xda\x90\x19\xc6\x61\x61\x34\xcf\x72\x41\x59\xd8\x8c\x14\x69\xcd\x1a\x8e\x61\xbc\x18\xc1\x60\x30\xb4\x72\x31\xea\x60\x0e\x50\x31\x79\x2e\x3a\xe1\x82\x95\xc5\xe2\x74\x30\xea\x36\x28\xb9\x3c\xfd\x9c\x8c\x4d\x93\x56\x8b\xf5\x70\x47\x94\xa3\x79\xc9\x9e\x93\x38\x6d\x54\x29\xeb\x92\xb2\x9f\x32\x17\x2c\x32\x16\xd5\x25\x4c\x81\xb5\x47\x6c\xe3\xe0\x08\x22\x34\x7a\x59\x8a\x0b\x64\x45\xef\x08\x6e\xff\xf5\x68\xcf\x93\x54\x26\x3a\x52\xc7\xdb\x98\x63\x61\x24\xdb\x36\xd3\x23\xa3\x59\x77\x82\x46\x15\xf4\x4e\x73\x76\x19\xf1\xb8\x64\x14\x0e\xfa\xeb\x89\xae\x6f\xcf\xdf\x4c\x10\xcf\x41\x87\xf0\x3d\x90\x48\x1d\x79\x9f\x96\xcb\x8a\x30\x1a\xce\x86\x30\x81\xac\x45\xa4\x16\xd1\x1c\x2a\xf1\xcd\xe4\x48\xb3\x45\x9a\x67\x8b\xd4\xa3\x09\xf4\x2e\x45\x0d\xf0\x61\x38\x38\x49\xb2\x9b\xd3\x81\x71\xdf\xb7\x67\x25\xfb\x5e\x46\x5c\x30\xa9\x8a\xf7\xa5\xa8\x61\xf3\xa1\x8f\x43\x1f\xda\xe3\x31\x9c\xa7\x19\x47\x73\x1c\xa3\x14\x29\x86\x35\x80\xcc\x05\x65\x40\x84\x20\x71\x2a\x81\xa2\xbf\xdb\xe8\x21\xa8\xf2\x7a\x91\x15\x23\x20\x1c\x32\xe1\xc2\x2a\x45\x4a\xd9\x6d\xc6\x29\xcc\x18\x25\xd7\xbc\xd5\xcf\xcc\x96\xe4\x99\x58\x45\x3d\xaa\xce\x73\x39\x39\x48\xa3\x57\x68\xd2\x3d\x7f\xc2\x9f\xda\x98\xd6\xc6\x5d\xb0\xc5\x0e\x58\x50\xf1\xce\xc6\xab\xb6\x6f\xfc\xad\xf8\x56\x73\x9c\x56\x85\xe8\xef\x36\x51\x51\x80\xc0\xf1\x6b\x6b\x6d\x1d\x58\x27\x83\x29\xe0\x82\x56\xed\x12\x3c\xb3\x04\x7b\x00\x97\x9b\x0d\x60\xd5\x65\x18\x51\x4f\x6b\xa0\xaf\x73\x64\x82\x4f\xee\x59\x5e\xda\x1a\x4d\x20\x3d\x92\x5f\x1d\xc7\x67\x94\x15\x4f\x18\x23\xab\x50\x96\x8f\xbc\xe9\x0c\xe1\x74\x0a\x87\x0d\x5b\x30\x2c\xa3\xa1\xa0\xe5\xa2\xb7\x6a\x38\x75\x5b\x81\xa1\x13\x9a\x8f\x97\xce\xc8\xd8\xc7\xf2\xc9\xf3\x8e\xda\x4e\x26\x06\xd5\x32\xfa\xdc\x16\xca\xd7\xdb\x76\xff\x2a\xeb\x14\x97\x96\x8d\xff\x6f\x33\x05\x09\xe3\xf4\x59\xcd\x08\x2e\x56\x47\x0a\x90\x7b\xe7\xf4\x4e\x34\xe2\x80\x45\x67\xcf\x61\x0a\xd2\xc8\x38\xa3\x8b\xe7\x77\x55\x18\xfc\x33\xbc\x38\x3c\xf8\xcb\xe5\xfe\x30\xbc\x58\xdd\x26\xe9\x92\x5f\xee\x0f\x1f\x2a\x59\x44\x13\x08\xf7\x66\x29\x16\x16\x62\x84\x65\xa1\x06\x67\xbd\x5a\x0f\x74\x53\x15\x8f\x41\xb3\x0a\x69\x23\xeb\x74\x95\x21\xf6\x83\x29\x7c\xd3\x72\xfd\x7c\x77\x68\xfc\x56\x72\x54\x24\x33\x4c\x01\xa7\xf7\xaa\x10\x06\xc0\xc5\xd1\xa5\xc5\xac\x2e\x32\xb9\x59\x9a\x9a\xc7\x97\x0e\xf9\x54\xff\xaf\xbb\x21\x6f\x27\x21\xe1\x42\x02\xb8\xdc\x4a\x61\xef\xd4\xb8\xf3\x3a\x43\xe2\x7c\xa0\x71\x59\x24\xd6\x77\xeb\xf1\x2a\x6c\x05\x9a\x1c\x87\x75\x9f\x61\x79\x4f\x1e\x43\x9f\xb1\x29\x69\xee\xa1\x70\xd2\x87\xc2\x3d\x40\xd1\xd0\xf4\x5d\x4d\x2d\x5c\xb7\x74\x3e\x76\x16\xdc\x86\xd3\x0f\xdc\xe3\x1f\x68\x2c\x71\xd7\x42\x5f\xef\x72\x3a\xf2\x4e\xe2\xff\x79\x86\x6d\xe7\x14\x1c\xc0\x91\xe4\xea\xa9\xe2\xee\xc1\xc1\x46\xae\x9d\xfe\xcf\xe1\xda\x82\x8a\xe7\x36\x4a\xb0\x9d\x65\xa8\x70\xbc\xd8\xc2\x97\x2f\xe0\x15\xf8\x58\x33\x13\xb4\x5a\x62\x58\xcd\xe8\x1a\xd7\xef\xbc\x8b\xcb\x7d\xb7\x3d\x99\x7d\xf8\x7d\x93\x91\x45\x89\x6a\xac\xbc\x6a\xb6\xbb\x13\x69\xe2\x4d\xa1\x6c\x3b\x74\xb4\x5d\x82\x29\x6d\x5b\x10\xe3\xbd\x38\x21\xa8\x7b\x53\x87\x76\x21\x8b\x46\x68\x47\x4d\xfa\xbc\xe8\x89\x01\x6c\x20\x4b\x41\x6f\x35\xca\x9a\x75\x86\x40\x2e\x91\xf5\x32\xd4\x6d\xf1\x18\xbd\xf3\xfa\x85\x31\x3c\x1e\xc1\x80\xab\x15\x37\xe8\xa5\xb7\x06\xec\xd4\xf9\xa2\xbf\xa3\x42\xfa\xbf\x3d\x6f\x5e\xcf\x04\x23\xb1\xf8\x7f\x6a\xf2\x4e\xeb\xdd\xd3\xd5\xe2\x9c\x12\xa6\xcc\xe6\x61\x6b\xb5\x77\xf4\x51\xa3\x69\xd6\x7b\x6d\x17\xb2\xb4\xbe\xc3\x9e\xe0\x65\x44\x97\x95\x58\x85\x43\x27\xa0\x44\x98\x90\x72\xad\x8d\x23\x45\x5d\x49\x6f\x59\x18\x0e\xff\x1d\xbb\x84\x4e\xa3\x29\xf3\x5a\xdb\x6a\xd6\xb8\xd9\x6c\x22\x9b\x3c\x0f\x63\x65\x5f\x06\x43\x33\xfb\x2f\x5f\xe0\x0d\x11\x69\xb4\x24\x77\x21\xfe\x31\xcf\xcb\x92\xf9\xfb\xc7\x18\x1e\x7f\x7b\x38\x1c\xc1\x91\x45\xa0\x89\xc4\x76\x34\x0d\x8c\x4d\x1e\xac\xa3\xff\x11\xab\x5f\x52\xe6\x79\x2c\x4d\x61\x44\x66\xf2\x58\x3c\x74\x2d\xb7\x9a\xe5\x66\x2c\xed\xaf\x33\x5f\x2b\xc2\xc8\xb2\xc9\xac\x0b\x10\x4a\x30\x69\x9b\xc9\x26\x9c\xb4\x31\x2d\xd0\xda\xe9\x0a\x60\x84\xbc\x93\x26\xba\x9e\xda\x81\xc7\xa5\x63\xb7\xa9\x0a\x8c\xeb\x86\xc7\x3e\x10\x5a\x49\x1b\xd7\xf2\x47\xd5\xd6\x2c\x97\x5b\x7a\xbf\x23\x54\x25\xa0\xe1\x60\x81\x76\x5d\xab\x19\xbb\x82\xde\xe3\xe5\x74\xd3\x38\x70\xb9\x9c\x51\x5e\x95\x05\xa7\xdd\xc6\xc7\x8a\x16\x5e\xe4\x4f\x63\x2c\x94\xb4\x36\x92\x6b\xd8\xb7\x1b\xde\x7f\x18\xe3\xa7\x2a\x34\xb4\x1d\x67\xeb\x1f\x37\x7c\x57\x7f\xb4\x0e\x85\xbf\xa4\xf2\xa8\xb4\xc1\x27\xdd\x5a\x18\x2a\xa5\x45\x55\x06\x43\xcf\x57\x5d\xb3\x7c\x9b\x07\x5a\x96\x4f\x34\x12\xff\x69\xaf\x34\xf6\x42\x67\xc1\x8e\xde\x67\x0d\x35\xb4\x7e\x67\x9f\xc4\xdb\xfc\x10\x77\x29\x1b\x49\x61\xae\xda\xe8\xcb\x32\x79\xfc\x0a\x70\xe9\xb6\x90\x46\x05\xc1\x3c\x1f\x9c\xec\x73\x97\xb2\x88\x69\x76\x63\xd4\xf3\x41\x5f\x72\xae\xf9\xa1\x4c\x32\xb4\xdd\x47\x4d\xde\x73\x3e\xf9\xd1\xec\x76\x67\x45\x62\x79\xdc\xf4\x3a\x6d\x75\xfc\xd3\x3b\x1a\xd7\x98\xc3\xaa\x5d\xde\x01\xec\x4b\xb0\xc3\x2e\x95\x2d\xf5\xe2\x72\x59\xe5\x54\xd0\x9d\x09\x38\xdd\x40\xc0\xfb\xa3\x09\x49\x73\x4c\xef\xdb\x63\xe0\xa0\x59\xcc\xc7\x5e\x47\x51\x0a\x92\xcb\xe2\x0f\x2a\x9a\x8d\x29\xe2\xf7\x71\x48\x85\xa1\xef\x61\xd3\xc6\x4e\xda\xa3\x2b\xd7\x0f\x2a\xdb\x80\xc7\x24\x27\x2c\x68\x73\xb9\x8b\xd2\xd1\x56\xe6\x76\xfb\xdc\x87\x82\x39\xd6\xf6\x72\x7f\xdd\xf2\xd1\xd9\x8d\x3d\x15\xcb\x3c\x0c\x5e\x97\x24\x01\xa9\x20\x15\xfb\x2d\xe1\xf7\x21\x58\x72\x38\x99\x31\x18\x9f\xc2\x99\xd5\xf5\xaa\x95\xb3\x37\xef\x43\x60\x9a\xc9\x9a\xe0\x5c\x62\x8e\x00\x75\x42\x81\xea\xd1\x9a\x90\x23\x62\xbd\x81\xec\x06\xf5\x1d\x7c\x7b\x56\xb0\x5d\xd5\xbc\xe4\x8b\x2d\xc6\xba\xec\x11\x49\x4d\x81\x6d\x5b\xe5\xc6\x1c\xda\x32\x74\x63\x7d\xfd\xd1\xb1\x07\x83\xf6\xd0\x86\x06\x5b\x86\xf6\x32\x88\x76\xb0\x17\x5d\x3b\x41\xb2\xa7\xac\xc5\xab\x67\x46\x56\x6f\xb3\x22\x29\x6f\xd5\x74\xce\x55\x65\xbb\xa5\x35\x1b\xb3\x56\x9e\x6b\x9f\x51\xd7\x4a\x83\x6a\x2c\x3b\x34\x4f\x0d\x04\xdf\xfd\x65\x33\x46\xcd\x90\x30\x35\x78\x71\xb5\xf0\x25\x56\xfd\x21\xe8\x9e\x03\x76\x6f\x9a\x95\x9c\xc3\xa8\x99\xc1\xd7\xfa\x5e\xd3\x76\x6a\xab\x4b\x05\xaf\xc9\x8c\xe6\x9e\x05\x80\x11\x5e\xde\x90\x1c\xbf\x7f\x40\x2f\x3e\xd7\x77\x80\x1c\xa7\x07\xd6\x42\x56\x80\xdb\x4d\x11\x45\x55\xc9\xed\xc6\x84\x8b\x1d\x45\xe2\x42\x8d\xaa\x9a\xa7\x61\x60\x82\x55\x72\x71\xa9\xbe\xfb\x10\xd8\xf8\x94\xd6\xe5\x3c\x26\x15\x7d\x79\xfe\xe6\xb5\xc6\xf3\x02\x7f\xd9\xb8\xe8\xda\x3f\xda\xe7\x66\x76\xc1\x49\x92\xdd\x40\x9c\x13\xce\xa7\x1f\x03\x55\xfc\x31\x68\x86\x32\x98\x7c\x2a\xb3\x22\x0c\x4e\x66\xec\x34\x18\xaa\xe1\x93\xec\xe6\x34\xd8\x4a\x4c\xe5\xc6\x3f\x2f\xcf\xf9\x5b\xe5\xac\xde\x48\x4e\x61\x5a\xe8\x9a\xc8\x10\x47\xda\xf4\x83\x01\x8e\xfa\x39\x38\xbe\x8f\xf8\x5b\xa9\xbf\x9d\xfc\x3d\xf4\xb7\x24\x9f\x7e\x0c\x2c\x5d\x0c\x7d\x65\xf9\xc7\xc0\x06\x29\x50\x03\xcb\x0f\x3d\x9b\xfd\x69\x1f\x19\x47\x8a\x86\xeb\xc0\xf1\x56\xa8\x0e\xbb\x79\xb6\x7f\xd2\x7e\x60\x4b\x4b\x74\xec\x36\xa4\x54\x2b\x16\x9b\xbe\xc8\x4b\x22\x74\xbd\x59\x94\x19\x7f\x4b\xde\xca\xb2\xa1\x73\x8d\x23\xd8\x7f\x55\xcc\x83\x11\x04\x07\xfa\x37\x7e\x87\xdb\x2c\xcf\x61\x46\x15\xb0\x44\x2e\xa7\x12\xde\x92\xb7\x30\x5b\xb9\xf0\x87\x11\x9c\xa7\xd4\x80\x8a\x49\x31\x10\xb2\x13\x66\x9e\xd0\x64\x04\xbc\xc4\xd4\x4f\x10\x29\x5d\x02\xe1\xb0\x20\x15\x87\xb0\xa8\xf3\x7c\x18\xb9\x8e\x28\x73\xb7\x6e\xed\xf9\xac\xb7\x12\xc5\x4b\x29\x6b\x1b\xed\xf7\x3a\x14\x2a\x92\x53\x21\xcc\xf9\xf6\x4c\x5f\xf5\x8b\x9e\x96\x79\xc9\xa2\xf7\xaa\xb2\x39\x6c\xa3\xd9\xe9\x98\x02\x52\x86\x96\x44\xb0\xec\x2e\xf0\x55\x54\x63\x7e\xe9\xb4\x83\x8c\x43\x51\x0a\x28\xe7\xa0\xda\x63\x94\xed\x01\xbc\xcf\x29\xe1\x14\x28\x5e\xa1\x21\x10\x97\x8c\xd1\x58\x60\xc2\x38\xe5\x3c\x2b\x8b\x28\xf0\x53\x6d\x94\x9c\xaf\x1b\xef\x18\x31\x59\x18\xcc\xc6\x17\x1b\xbd\x29\x78\x3b\x5a\x74\x6c\xbf\x29\x29\x6e\xc2\x45\x82\xeb\xb5\x8a\x06\x0e\xb2\xc6\x2e\x0a\x1d\x67\x32\x56\xcf\xb1\xab\xaa\xb8\x13\xc5\x6f\xd9\x37\x26\x3c\xd5\xa8\x26\xa4\x8e\xaf\x12\x9a\x81\x9b\x14\x0e\x0b\xd8\xd6\xb9\x79\x81\x9a\x14\xee\x28\x13\xfc\x1c\x79\xdd\x27\xfa\xb7\x7f\xd0\x11\x5c\x05\xab\xb8\x4f\x29\x67\x01\xa9\x9f\xd6\x20\xf2\xe7\x6e\xa2\x02\x28\x17\x87\x97\x6e\xd6\xc0\x6a\xe2\xec\x8d\xb8\x32\x15\xb4\x8b\xa3\xcb\x26\xa2\x6b\xd3\x1c\xd6\xc3\xc6\xbc\xce\xe5\xe1\x44\x4b\x60\x84\x5f\x43\xd5\x63\xdd\xe4\xfe\xa1\xe9\xd7\x49\x24\xe0\xce\xc2\x55\xe9\x4e\xc8\x31\x8e\x0a\x90\xe4\x39\x2c\x33\xce\xa5\xb5\x2f\x0f\xf0\xbc\xb9\xe7\x54\xd0\x5b\x6b\x65\x6a\x95\xa9\x96\x41\xe9\x98\xcf\x56\x89\x0a\x67\xdb\xb7\x2e\x85\x63\x10\x70\xe2\x97\xd3\x22\x91\xa5\xfb\xed\xd6\xb4\xf2\x72\x53\x9f\xe4\x79\x79\x8b\xd0\xe7\x52\x69\x48\xf4\xaa\x32\x2b\x04\x64\x05\x89\xe3\x9a\x91\xd8\x06\x99\xd1\x7a\x51\x66\xaf\x0d\x44\x4a\x1c\x1f\x3d\x02\x55\x7c\x51\x95\xfc\x32\xba\x83\x13\x39\x6e\x67\x58\x75\xe8\x77\xd9\x69\x27\xae\x54\xba\x03\xc4\x31\x4f\xab\x12\xaf\x7c\x6a\x46\xb5\x6d\xf5\x16\x88\xcf\x77\x13\x10\x23\xd0\xd9\x43\xeb\x61\x37\xfa\x09\x60\xef\x07\xdb\xbe\x0d\x63\x1b\x27\x35\xd9\xd1\xfe\xeb\x5c\xbe\xbe\x37\x0c\x60\xf3\x6e\x0d\x05\x8d\x93\xc8\x37\xc3\xf0\xe6\x0a\x5e\x8d\x26\xc5\x0a\x04\x23\x31\xe5\x52\x4d\x91\x02\xe8\x5d\xa6\xae\x3d\xa2\x1a\x8f\xfc\x9b\x14\x8d\xaf\xd0\x19\xae\xb9\x86\x11\xa7\x59\x9e\x30\x5a\x84\xc3\x9e\x48\x72\xd3\xb6\x95\x4f\x88\x15\x78\xb1\xc3\xab\x58\xb7\x6f\x88\xe8\x0c\x0b\x6d\xb6\x04\xea\x6a\xc8\xa9\x49\xa3\x38\x6e\x5f\x11\x69\x35\xd7\x77\x43\xba\xed\x1b\xf4\x3b\x97\x45\xb7\x35\xc2\xa1\x1a\xc7\x29\x2d\x12\xed\x36\xdd\xe8\x4f\x94\x94\x7f\x5a\x16\x37\x72\xed\x8a\x12\x7e\x7c\xfb\xea\x17\x3c\x4a\x71\x41\x96\x95\xb9\x2c\xea\x9c\x8d\x77\xf7\x5e\x7f\xf9\x02\xdf\x7c\xa7\x47\x38\x4a\xcd\xbd\xe5\xa8\xc7\xa7\x6b\xd0\x3c\xb0\x03\xd9\x69\x6e\xd7\x3b\xef\x49\x82\x29\x1b\x3a\x97\xfc\x36\x13\x29\x64\xc5\x4d\xc6\xb3\x59\x4e\x21\x90\xab\x22\x50\x0a\x93\x03\x51\x97\x41\xe3\xb2\x98\x67\x8b\x9a\xd1\x04\xee\x0e\x24\x13\x60\x56\xd6\x45\x42\x10\x00\x2d\x78\xcd\x28\x37\xe0\x45\x4a\x84\x92\x3c\x0e\x84\x51\x48\x32\x5e\xe5\x64\xa5\xaf\x97\x02\x81\x79\x76\xd7\xc0\x41\x2a\x78\x77\xac\x0a\x52\x55\x98\x0a\x53\xe2\xd0\x36\xb1\xc4\xc2\x97\x13\x37\xdd\xb0\x49\x93\xb5\xde\xa8\x9f\x8b\x43\xa9\x65\x4e\x1b\xaa\x39\x71\x44\x45\xa3\xba\xc0\xbb\xab\xa8\x0f\x6c\xab\x8e\x5e\x58\xb7\xe1\xfa\xda\xed\x00\x8e\x94\x36\xd3\x1c\xe9\x8c\x62\x55\x8e\x6e\xd0\x3b\x40\x73\x19\xed\x6d\x79\x0b\x31\xa3\x44\xa8\xab\xaf\xd2\xb6\xf1\x17\x71\xe7\x51\x03\xd7\xfa\x51\x49\xf2\x0a\x03\x9d\xe1\x31\x71\x84\xdf\xee\x7f\xea\xd2\xea\xa4\x71\xb8\x3b\x0b\x1b\xcf\xf8\xea\x0e\x6b\x38\x1c\xa1\x3a\x1e\xe9\xe3\x67\x22\xd2\x7b\xfa\xfc\x2c\xeb\xd1\xed\xf3\xdf\x87\x23\x78\x6c\xfb\xa9\x53\x19\x65\x93\x9e\x3b\x11\xdf\xeb\x04\x9b\x00\x26\x10\xe4\x59\x41\x8d\x1b\x14\x4f\x7f\x55\x99\x13\xed\xcf\x90\x75\x84\x69\xdf\xa7\xf1\x59\x58\x79\x57\xc5\xcb\x4c\xb6\x24\xb5\x28\x83\x91\x47\xd4\x17\x59\x91\xe0\x7d\x08\x4e\xb5\x64\x0e\x38\x2c\xc9\xdd\x78\x99\x15\x7b\x1b\x6e\x6b\x48\xa5\x2b\x58\xed\xde\x97\xfe\x39\xa5\x85\xb9\x96\x21\xed\x42\x75\xf7\x32\xb1\x7b\xf1\x92\xdc\x35\x7b\xf1\x3d\x6b\x51\x34\x1e\x16\x2b\x2d\xb2\x7f\x5c\x33\xa6\xca\xdf\xb8\x90\x00\x9a\x0e\x1b\x20\xca\xd2\xf7\x72\x47\x6e\x7b\xf7\x6c\x45\xb4\x82\xd3\xd6\x00\x8f\x1e\x81\x5b\xfd\xa0\x6d\x3b\xa2\xa9\xd3\x42\xc9\xe9\xd0\xe3\x7f\xb4\x5b\xa9\xa4\xc4\xfe\xd4\xef\xad\xa5\xdd\xdd\x30\x3c\x59\x8e\x14\xf9\x96\xe4\xee\xeb\xa3\xe8\xf0\xdb\xcd\xcd\xb2\xc2\xd0\xc6\xdb\xe9\x91\x03\x58\xf7\xaa\x98\x67\x45\x26\x56\xc7\x2d\xce\x1c\xf8\x15\xbf\x93\x43\xff\x1e\x26\x9c\x20\x8e\xbb\x90\x5e\xcd\xe5\x5e\x82\xf7\xf1\x78\xb9\x23\x67\x97\xbb\xf3\x73\xed\xdc\x28\x43\xac\xa6\xc8\xa6\x76\x62\x46\x3f\x33\x61\xbf\xf1\xa4\x6e\xe4\xa6\xfc\x3c\x30\xed\xfa\xae\x85\x6d\x06\x1e\x1e\x46\x47\x5f\xab\x80\x21\x99\xf1\x50\x16\x1e\x48\x78\xc3\xe6\x50\xb2\x65\xd8\xad\x10\xd6\xc6\xa9\x26\x45\xe9\x4e\x9b\x26\x5d\xbd\x1b\xa1\xf9\x83\xbe\xef\xcf\x4a\xcb\x4c\xfa\x54\xb6\x73\xd7\x63\xb5\x05\xd6\xaf\x5a\x95\x6f\x04\xa6\xf4\x5e\xc9\x32\x5a\x08\xab\x29\xe9\xdc\x24\x2f\x8a\x2c\xbe\x7e\xa1\xaf\x8f\x5a\xf8\x2f\xb2\x3b\x21\xb7\xeb\xe8\x6d\xbd\x9c\x51\x16\xa9\xfb\xa5\x7f\x7f\xf3\xc3\xf9\xa8\x67\xdf\x40\x14\xf5\xbe\xe1\x5e\x12\xf1\xc9\xa9\x5f\xf3\x68\x66\x96\x96\x37\x94\x3d\xa3\x82\x64\x79\xff\xfc\x5e\x36\x0d\x76\x9b\xa4\x42\xd3\xcf\x6f\x56\xfb\xc0\x08\xee\x46\xb0\xf2\x55\xa9\xce\x3e\x19\x9c\xf0\x8a\x14\xc6\x7c\x94\x85\x01\x26\xf7\xda\x70\xc5\x1d\x7c\x8d\x46\xdd\x30\x12\xe5\x8f\xe7\x4f\x95\xb3\x27\x1c\xaa\xdc\x5e\xd9\xf7\x74\x70\xec\x80\xe5\xb7\x44\xc4\x69\x17\x30\xce\xe3\x4a\xd5\x06\xea\x2a\xdb\x34\x98\x91\xf8\x7a\xc1\xa4\x99\x74\xa0\x4f\x8c\x2a\xaf\x18\x55\x08\x96\xc8\x61\xa4\x35\xdb\x1d\x28\x2e\x0b\x41\x0b\x3c\xc6\xa9\x21\xf7\x41\xcf\x36\xea\xf3\xb1\xa1\xb1\xa6\x1c\x6d\x13\x70\x9d\x8e\x2b\x3d\x13\x9d\x10\x6f\x86\x70\xf2\x6c\xb0\xc1\x8c\x21\x59\xcc\xa8\x4e\x91\xf6\x14\x37\x7e\x55\x1f\x8d\xae\x0d\x83\x1e\x0a\x73\x65\xbb\x87\xf1\xaf\xb1\xae\xd7\x46\x51\xdd\xac\x91\x72\xaf\x40\x38\xa3\x39\x79\xde\xfd\x43\xfe\x40\x53\x72\x93\x95\x2c\xd2\xea\xfb\xa5\xe9\x10\xc2\x4e\xa2\xa7\xf0\x9a\xe8\xdf\xfe\xe0\x3c\xa5\xf9\x8d\xb4\x56\x77\x1a\xf9\x1c\x2d\x86\xdd\x04\x7e\xd3\xa8\x6e\xe8\xda\xbe\x99\xb0\xd5\x31\xce\xb3\xdf\xfe\xc8\x31\xd4\x57\x5d\x0f\x5a\xfe\xa5\x1e\x4d\x60\x0f\x0a\x36\xf6\xfd\x47\xcd\xc6\x7b\x2c\x85\x46\xdd\xec\x90\x88\xd7\x93\x97\xb0\x25\x3b\xa0\x9f\x26\xf2\xbc\xad\xb1\xd0\xb7\x6e\x39\x54\x04\x9f\xcd\x71\x2f\xe5\xce\x4b\x66\x6d\x44\x75\x08\x42\x27\xaa\x73\x13\x97\x93\x1b\xba\xa7\x4f\x4a\xce\xfd\xdb\x27\x7f\x7b\xf2\x0b\x98\xe0\xa1\x3c\xd9\x94\x2c\xa1\x4c\x5d\xdd\x3d\xb0\x7e\x52\xc8\x84\x72\xe5\x3a\x63\x2a\x60\xb7\xd2\x3a\x95\x10\x6b\x4e\x99\x3c\x74\xc9\x33\x93\xba\x18\x80\xf8\xb8\x8f\x56\xd8\x6b\xbb\xda\x07\xe9\x1d\x1e\xfb\xaf\xfb\xa2\x43\x76\xab\x8b\xa2\xd7\x93\xfa\xb6\x44\x34\xd1\x65\xc4\x61\x2e\x35\x62\xcb\x3b\xda\xf5\x15\x9c\x93\x99\x7f\x5b\xdb\xbd\x86\xeb\x44\x8d\xec\xb5\xe0\x9d\xa4\xa0\x95\xeb\xd1\x4a\x1c\x24\x3b\xc9\x81\x4a\xe8\x6a\xee\x13\xdf\x8f\xa5\x4b\x69\xe5\x23\x37\x41\x93\x1f\xca\x64\x65\x48\xed\x80\xf3\x9f\x91\xb9\xc2\xdb\x90\x20\x66\x65\xa2\xef\xbd\x63\x3f\x2f\xdf\x8b\xdf\x66\x22\x4e\xc3\x56\xb4\x5b\xe1\x1f\x13\x4e\x21\xb8\xa1\xb1\x28\x59\x30\xd9\x73\x4d\x46\x3f\x2c\xed\x73\xd0\x0c\xa3\x1d\x25\xc1\x89\x60\xa7\x27\x22\x81\xb8\xcc\xe5\x5e\x35\x1d\x3c\x1e\x9c\x9e\x64\xa7\x85\x62\xec\xc9\x38\x3b\x3d\x19\x8b\x44\x7e\xb0\xd3\xe6\xda\x47\x3b\x67\xb6\x3f\x13\xbc\x27\x44\xee\x5f\x33\x44\x1e\x68\x5b\x55\x37\xbc\xc8\x2e\xdd\xdd\xd2\x06\xa0\xfa\xbc\xd4\xd6\x49\x7d\x7c\xdf\xd4\x4e\x5b\xa1\x38\x05\x52\x07\xcc\xe4\xd4\x74\x13\xed\x84\xbe\x38\xba\x6c\xaa\xdc\x59\xab\x79\xe2\xa5\x9c\x63\x4b\x7f\x1d\x69\xf8\xff\x98\xfe\x37\x7f\x9c\xfe\x37\x6d\xfa\xdb\xfb\x10\xe7\xf4\x4e\x5a\x38\x81\x0d\x4b\x58\xf4\x3e\x29\xf4\x3e\xc1\x09\xdc\x18\xaf\xbf\xc1\xed\x93\x7f\x05\xb5\x81\xb4\x3f\xb5\x8d\x2f\x3e\x5d\x6a\x0e\xc1\xff\x96\x5c\x73\xcb\x0f\x15\xe7\x66\x6c\x7c\x1a\xf8\xae\xdf\x3f\x29\x1a\x0e\x26\x3b\x4b\x86\x8e\xcb\x28\xc9\xe8\x1f\x5d\x35\xf1\x46\x72\x39\xb1\x49\x10\xdb\x03\xa1\x65\x7b\xff\x40\xd8\xc4\x1b\xc8\x99\xb5\x3f\xe6\x70\xcb\xa0\xda\x75\x39\xe9\xdd\x0f\x7e\x2c\x78\x5d\x55\x25\x13\x34\xd1\x17\x5b\x30\xa6\xd6\x01\xb2\x75\x6b\x67\x1b\x9e\x06\xed\xbb\x24\xde\x7e\x3f\xd0\xf3\x53\x3b\x36\xd5\x59\x7f\xb1\x6f\x6a\xd9\xdb\x84\x6e\x84\x0c\xc9\xd7\x20\x40\x0b\x91\x89\xd5\x1b\x75\x59\x16\x27\x16\x3c\x0a\x26\x10\x3c\x22\xcb\xea\xd8\xdc\x2e\x3b\xc1\x92\x5c\xd8\x82\x53\x2c\x58\xd8\x82\x41\x30\x98\xc0\xe0\xd1\xbf\xea\x52\x1c\xeb\x2b\xaf\xc1\x20\x90\x45\x5f\x7d\xf3\x17\x5b\x32\x56\x25\x77\x8f\x5f\x1c\x0f\xec\xc3\x32\xda\xc8\xd7\x67\x1a\x8d\x5e\x73\xe7\xf6\xe2\xd1\xc9\x69\x30\xf8\x38\xbe\x1c\x2f\x46\xce\xf5\x48\xde\xba\x61\x60\xa7\x71\xc1\x2f\x4d\x0c\x64\xed\x71\xe5\x3d\xe9\xbb\x96\xd2\x3c\x0c\x6b\x42\x56\x2d\x66\xca\x6e\xad\x57\x40\xfb\x39\x89\x40\x9a\x7b\x81\x08\x18\xdd\xe9\x3f\x9e\xbd\x6e\xc2\x18\x6e\xab\x5e\x9d\xea\x35\x50\x5e\xd9\x75\x93\x2f\xe3\xd5\x1a\xd7\x0e\x0e\x45\x92\x44\x59\xe5\xa0\x9f\x98\x45\x69\x0a\xbe\x22\x49\x72\xa5\x9f\xb6\xd2\x0f\x2f\x78\xcd\xd5\x5b\x60\xb2\x68\x04\x9f\xd7\xc3\xae\x85\xd2\x9a\xbf\x99\x51\x97\x06\x72\x76\x3a\xc5\x26\x2f\x63\x3c\xe6\x47\x9c\x12\xa6\x1e\x62\x0c\x82\x16\xc3\x4c\xa0\x59\x53\x0f\xb3\x06\xdf\x9b\x94\xd5\x7e\x38\x11\xaf\x67\x4a\x3e\xc2\xa3\x61\xc4\xab\x3c\x13\xe1\xe0\xd1\xc0\x26\x59\x37\x30\x5e\xd2\xbc\xb2\xc7\xac\xf6\x64\xfe\xd1\x6a\x16\xba\xe1\xb2\x36\x0c\x35\xe1\xa6\x0b\x0f\x1d\x4c\xb7\x52\xcb\x50\xd9\xa5\x96\x79\x3c\xd4\x17\x9c\x2e\xae\xca\x64\x44\x92\x3d\xb4\x0f\x77\x3a\xaf\xef\x69\xa7\x8a\x7e\xd6\x54\x29\x4c\xc9\x59\x65\x70\xfe\x78\xf6\xba\x61\xed\xd0\xa9\x56\xfa\xa4\xc5\xfb\xe1\x1e\xc0\xb0\x79\x61\x58\xad\x07\x25\x7d\x4d\x74\xea\xa1\x66\xef\x50\x9f\xd3\xba\xe9\x53\x26\xe4\x66\x4f\x71\xcd\x43\x38\x92\x4e\xe3\x31\xbc\x7d\x77\xfe\x7c\xd2\xba\x62\x3c\xa3\x70\x4d\x2b\x81\x17\xc9\x57\x45\xac\xc2\x2f\xe3\x5a\x64\xf9\x98\x0b\x66\x7e\xc7\x65\x71\x13\x2d\xca\x09\xc2\x7d\x9d\x15\xd7\x2f\x4a\xf6\xdc\xa6\x31\xdc\xc3\x03\x4b\x8f\xfe\x65\x8b\xec\x54\xca\xc7\xac\x5a\x3d\x7d\x2f\x7e\xbf\x50\x6b\x0b\xaf\xca\xba\x39\x0f\xad\x55\xaf\x28\xd0\x5c\x10\x36\x81\xc7\x3f\x2d\x9e\x0e\x88\x77\xb3\x4f\x34\x96\x4a\xa8\x23\xab\x0b\x5a\x50\x46\x84\x12\x57\xd5\xcc\x53\x38\x06\x7f\x2f\xe3\xe3\xa1\x0a\x6c\x87\x0e\x6c\x93\xdb\xa6\xde\x00\x55\x29\x45\x8f\xf4\xc3\x72\x69\xc6\x45\xc9\x56\x28\x1c\xf2\x08\x42\xc3\xcf\xeb\x11\x04\xc1\x08\x54\x98\xf4\x7b\xb9\x21\x3b\x44\xdd\xba\x46\x1c\x81\x74\x39\xa4\xe4\xae\x47\x47\xbb\x2c\xd2\x6f\x35\x34\x9d\x86\xf0\x59\x4f\x6b\x81\x6e\x00\x6c\xd7\x93\xf6\xd9\x4b\xe9\x96\x80\xec\xd2\xa5\xad\x19\xff\xe1\xa9\x31\x0b\xcd\xd5\x19\x56\xf2\xf0\xe0\x4c\x13\xbf\x0b\xce\x4e\x4d\xeb\x55\x71\x43\xf2\x2c\xe9\x51\x3b\xea\x59\x04\x57\x6d\xa9\x6e\x54\xc4\x86\xd5\x2f\x58\xb9\x7c\xa7\x06\xd0\x00\xba\xc3\x8d\xe0\x70\x47\xca\x44\xcd\xe8\xca\x51\x0b\x53\x18\xff\x73\xf1\x31\xd9\xff\x18\x45\xfb\xd3\x68\xff\xe1\xf8\xf7\x11\xab\x67\x86\x2e\xbd\x50\x22\xcf\xeb\x2a\x37\x91\x0d\x3d\x4d\xa7\xbc\xc3\xfb\xa6\xae\xb5\xd3\xfc\xee\xc9\x45\x82\x72\xe1\xc2\x3b\xee\xcf\x1d\xde\x3a\xc9\xfb\xf8\xb1\x41\x3c\x46\x4a\x64\x5f\x35\x7a\x46\xee\xab\x4e\x83\xc6\x68\x68\x6c\x86\xfe\x2d\xb5\xc2\xe7\xb3\xdf\xcd\xa5\xb6\x45\x78\xde\xfb\x29\x08\x4d\xbd\xb0\x1d\x3a\x43\x9a\xbd\xb4\x40\xaf\xfb\xbb\xb9\x1a\xf4\x45\xc9\x24\x14\xb3\x48\x5d\x74\x76\x66\x43\x53\xa1\xf2\x7c\xf8\xcf\x99\x48\xc3\x0e\x92\x9a\xd8\x36\x0d\x5d\x53\xe0\x3e\x7c\xb6\x53\x62\xdb\x24\xa4\x2d\x11\xd3\xf0\x70\x74\xcf\xbc\x95\xfa\xeb\x05\xd5\x2d\xf4\x37\x8f\x9d\x68\x62\x6d\x9b\x0e\x49\x34\x2d\xdc\x47\xe5\xfc\x37\x25\x1a\x5b\xd3\x59\xdd\xef\xe6\xef\x0a\xbd\x0b\x77\xf1\xb3\x7c\x56\x40\x9e\xc4\x71\xbd\xac\x73\x22\x30\xf7\x7c\x07\x65\xb2\x41\x62\x61\x5f\xdf\x79\xeb\x80\xb5\x69\x0c\xcd\xcb\xeb\xed\x57\x17\x9c\xd6\xbf\x7b\xa9\x6d\x9e\xfc\x76\x35\xec\x3d\xcd\x01\xbe\x70\x77\x22\xae\x2e\x13\x9b\xde\xf2\xa4\xfd\xa4\x48\x4c\xda\xac\x50\x1c\x55\x06\xea\x74\xe0\x6c\xe0\x4d\x73\xfb\xcf\x26\xdc\xbe\x17\x87\xea\xf1\x0e\xb7\xb1\x01\x9a\xd0\xb8\x4c\xe8\x8f\x67\xaf\x9e\x96\xcb\xaa\x2c\x68\x61\x68\xe9\x01\x38\xba\x6c\x8e\x4e\x1f\xf7\xe5\x99\x29\x80\x60\x38\xd4\x50\xe5\x4a\x72\x51\x98\x42\x20\xc8\xcc\xc9\x4e\xf6\x87\xb4\x37\x25\x9d\x62\xf5\x2c\x9c\x20\x33\xc8\x38\xa6\x3f\x2c\x28\xd3\x8e\x03\xd7\x20\xbd\x68\x86\xb9\xb4\x53\xfd\xc9\xbc\xe2\xb1\xee\x61\x7f\xf7\xd1\x8d\x6d\x4c\x6f\xeb\x31\x97\xd5\x8e\xa1\xa6\x47\x09\x16\xd2\x32\xc9\xb4\x98\x06\x51\x37\xb5\x7c\xdb\x78\x3d\xe6\x55\xc7\x62\x69\x59\x5a\x56\xca\x2a\x83\x61\xbf\x06\xce\x3c\xe5\xeb\x9b\x79\x4a\x2c\xd5\xd7\xe8\x9a\xae\xb8\x37\xd2\xb0\x2b\xa4\xd7\xcd\x33\xf7\x0e\xa4\x0b\x8d\xc2\x3e\x5c\xd3\xd5\xa5\xb1\x55\x35\x94\x0b\x59\xd6\xc9\x1d\x74\x7a\xb7\x1c\x0a\xf2\x18\xac\x8d\x68\x75\x87\xf0\x03\x15\x75\xa5\x83\x29\x31\x89\x53\x3a\x51\xaf\xf6\x35\xcc\xf6\xee\x1a\xf6\x3e\x74\xc7\x05\x11\x59\x3c\xfe\xc4\xc7\xea\xb0\x63\xff\x4b\x44\x6a\xfe\x73\xc4\xf7\x37\x53\xc9\x44\xef\xdf\x3d\xe8\x5c\x9b\xce\x8d\xc2\x84\x08\x22\x31\xd4\x92\xed\xfd\x0b\x07\xed\x26\x34\x7e\x35\xfb\xef\x1e\x50\xe0\x55\x4f\x53\xa7\xf2\xd9\x9f\xd1\x8a\xd1\x98\x08\xaa\xce\x73\x78\xa4\xf7\xb3\x79\x93\x8c\xd1\x58\x9c\x97\x6f\xb2\x85\x94\x91\xc4\x9e\xfa\xa1\x2f\xd7\x13\xff\x7b\x8e\x72\x48\xf4\x9c\x01\x42\x27\x67\x14\x85\x52\x91\xbb\x9b\x01\xaa\xbd\x1c\x78\xb4\x3a\x4f\x29\xa7\x20\x6e\x4b\x7d\x8d\x93\xf7\xe3\x8d\x09\x46\xbd\xe8\x0e\x25\x14\xc2\x28\x90\x24\xa1\x09\x94\x45\xbe\x42\x57\xe7\x8c\xc4\xd7\xb7\x84\x25\x78\x5f\x8f\x88\x6c\x96\xe5\x99\x58\xc9\x93\x5b\x99\x27\x4a\x46\x74\xd8\x3b\x72\x04\xa4\x97\x64\x1b\x1d\x05\x29\xe1\xe9\x3d\x96\x4d\xf3\x00\xa4\xd9\xfc\x94\x36\x4c\x5e\x30\xb2\x58\xaa\x08\x74\x8f\x7e\xec\x1b\x45\x45\x27\xd8\xca\x32\x03\x2f\xc0\x69\xc6\xfb\x40\xf5\x9e\x1c\x1e\x0d\x95\xd2\x4b\x58\x59\x61\xa0\x4a\xc2\x81\xaf\x30\xb3\x27\xc6\xb0\x77\xe8\x24\xd4\x75\x51\x6e\xac\x74\x26\xd5\xdf\xda\x59\x47\x1b\xe4\xc6\xaa\x8d\x3f\x37\xcd\x9e\x03\xea\x9f\x99\x6d\xbf\x6a\x6a\x7b\xa5\x3c\xcb\xa7\xf4\xd5\x61\xb3\x6f\x5a\x7d\xd8\xa3\x96\x65\x1b\x57\xdd\x95\xbb\x68\xba\xfb\x75\x5d\xd9\x52\x73\xe0\xfd\x93\x0a\x3b\x31\xbc\x12\xdd\x7f\x1c\x6e\x11\x59\x62\x3e\x6e\x1d\x78\x91\xb5\x0f\x43\xb9\x58\x87\xc7\x7b\xff\x27\x00\x00\xff\xff\x57\xd6\x43\xf5\x21\x6b\x00\x00") +var _webUiStaticJsGraphJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xe4\x7d\x6b\x77\xdb\x38\xb2\xe0\x77\xff\x8a\x0a\x3b\x27\xa2\xda\x32\x65\xa7\x6f\xf7\xde\x91\x2d\xf7\xa6\xf3\x98\x64\x26\xaf\x71\xdc\xaf\xe3\x78\x7c\x20\x12\x12\x19\x53\x24\x07\x00\x6d\xab\x13\xfd\xf7\x3d\x28\x3c\x08\x90\x94\xa5\xee\x9e\x9d\xb3\x7b\xae\x3f\xc8\x16\x1e\x85\x42\x55\xa1\x50\xa8\x2a\xc0\x37\x84\xc1\x7b\x56\x2e\xa9\x48\x69\xcd\x61\xea\x7e\xf9\xf2\x05\x3e\xaf\x8f\xf7\x64\x93\x05\x23\x55\x7a\x4e\x97\x55\x4e\x04\x3d\xde\xc3\xb2\x0f\xcf\x9f\xbe\x7b\xfb\x0c\xa6\x70\x74\x78\x78\x78\xbc\xb7\xd7\xf4\x8c\xfe\x2a\x9b\xc3\x14\xe6\x75\x11\x8b\xac\x2c\x42\x9a\xd3\x25\x2d\xc4\x08\xca\x4a\x7e\xe7\x23\x48\x49\x91\xe4\xf4\x69\x4a\x8a\x05\x35\xdf\xce\xe8\xb2\xbc\xa1\x43\xf8\xbc\x07\x20\xd2\x8c\x47\x34\x87\x29\xe8\xbe\xc7\xa6\x10\x71\x79\x79\xfe\xe6\x35\x4c\xa1\xa8\xf3\xdc\x56\x68\xd8\x30\x35\xa3\xd8\x1a\x77\x30\x98\x7a\x63\xb7\xda\x28\x14\x5c\xd4\x15\x3a\xe0\xa1\x18\xca\x1e\x43\xd9\x75\x6d\xfb\xb3\x2c\xbe\xe6\x29\xb9\x35\x73\xf7\x50\x4b\x88\x20\x30\x85\x8b\xcb\xe3\x3d\x53\x94\x15\x99\xc8\x48\x9e\xfd\x46\xc3\xe1\xf1\xde\xba\x87\x80\x91\xc8\x96\xf4\x05\x89\x45\xc9\xe4\xa4\x24\x1a\xc1\x2a\x98\xc0\x77\x87\xf0\xb5\xfa\x78\xfc\x5f\xf0\x35\x7c\xf3\xdd\xb7\x23\x59\x75\xdb\xad\xfa\x5f\x58\x91\xb4\x2a\xb0\x30\x6d\x0a\xf1\xfb\x12\xbf\xe3\x9f\x3c\x98\xc0\x51\x3f\x46\x5c\xd0\xea\x27\x92\xd7\x54\x22\x74\x21\x1b\x1f\xf1\x60\x04\xc1\xd1\xa1\xfa\xb5\x94\x9f\xdf\xe2\xe7\x91\xfa\xf5\xcd\xa1\xfa\x96\xca\xcf\xc7\xf8\xf9\x1d\x7e\x1e\xa9\x2f\x47\x09\x56\x24\x01\x0e\x7d\x74\x8b\xdf\xf0\xf3\xbf\xf0\xf3\xbf\xf1\xf3\x68\x85\xe5\xab\x60\xef\xb2\x0f\xad\xa2\x5e\xe2\x1f\x12\xab\x3e\x51\x8c\x2a\x56\x8a\x52\xac\x2a\xea\x90\xbd\xcb\x64\x29\xd5\x9c\xe6\x73\x98\x22\x8b\x24\xf7\xe4\xd7\x28\x4b\xbc\x85\xd1\x1e\x74\x7f\x1f\xb9\x3a\x1e\xc3\x07\x2a\x20\xa1\x73\x52\xe7\xc2\xc8\x60\x64\x80\x98\xef\x08\x4c\x83\x3d\x6e\x57\x32\x29\x92\x57\x59\x51\xd5\xc2\xb4\xea\xab\xfa\xf2\x05\x29\x2a\xbb\x67\x73\x08\xbd\x76\x82\xcc\x60\x3a\x9d\x42\x5d\x24\x74\x9e\x15\x34\x31\x02\xdc\x6d\x05\x47\x28\xc2\x1a\xf9\x67\x8c\xdc\xaa\x85\x0e\x71\x59\x08\x56\xe6\x1c\x48\x91\xe0\x17\x92\x15\x94\xc1\x9c\x95\x4b\x78\x89\xeb\x60\x46\x18\x07\xa1\x15\x42\xb4\xa7\x89\xd7\xac\x40\x35\xe4\xa0\x22\x22\x7d\xcf\xe8\x3c\xbb\x1b\x4c\xe0\xfd\x93\xf3\x97\x57\xef\xcf\x9e\xbf\x78\xf5\xcb\x48\x55\xcf\xea\x2c\x4f\x7e\xa2\x8c\x67\x65\x31\x98\xc0\x0f\x3f\xbe\x7a\xfd\xec\xea\xa7\xe7\x67\x1f\x5e\xbd\x7b\x6b\x16\xd7\xa7\x7f\xd4\x94\xad\x22\x7a\x27\x68\x91\x84\x56\x7f\xb8\xb3\x19\x5a\x3a\xba\xba\xe1\x61\xf8\xa6\xe6\x82\xc4\x29\x8d\x18\x2d\x12\xca\x42\x4f\x8b\x59\x5d\x34\x6c\xba\xd3\x3c\x22\x55\x25\xc7\xf1\xa1\x0d\x0d\x83\xff\x4a\x05\x30\x3a\xa7\x8c\x16\x31\xe5\x20\x4a\x20\x79\x0e\x22\xa5\x90\x15\x82\x32\xca\x45\x56\x2c\x8c\xc6\xe2\x90\x15\x58\xd7\x10\x55\xd1\x91\x14\x89\x02\x37\xcb\x8a\x04\xe8\x0d\x2d\x84\x56\x2f\x0c\xe5\xc5\x6a\xdc\x9f\x99\x44\x87\x19\x51\xa0\x79\x34\xcf\x8a\x24\x0c\xbe\xc2\xda\xab\x5b\x55\x1d\xc0\xbe\x11\xa8\x66\x2a\xff\x92\x54\x7b\x51\xb2\x25\x4c\x3d\x58\x1a\x82\xaa\xbf\x9a\x97\x6c\x19\xa8\xd9\xa9\x11\xee\x2a\xd6\xdf\x41\xd0\x3b\x41\x18\x25\x17\x05\x59\xd2\xa9\x6c\x77\x19\x38\x84\xbb\xab\x58\x74\x4d\x57\x15\xa3\x9c\x87\x8d\xda\x37\xb2\x37\x1e\xc3\x73\x49\x20\xb8\x25\x1c\xb0\x11\x4d\xe0\x36\x13\x69\x59\x0b\x24\x11\x4f\xb3\xb9\x80\x6b\xba\x8a\xb0\xbd\x94\x6a\x1a\xdd\xa6\x59\x9c\xc2\x74\x0a\x47\xdf\xc0\xa3\x47\xf0\x80\x46\xd8\xec\xef\x74\x65\xe0\xb6\x27\x1b\xf1\x7a\xb6\xcc\x44\x88\x98\xc9\x1f\x1a\x55\x0c\x09\xfc\x4c\x2d\x4b\x53\x83\x42\x8f\x78\x3d\xa9\x45\x79\xc0\x28\x97\x1a\x41\x62\x22\x27\x0a\x72\xa6\x50\x16\x80\xcb\x4d\xa1\x84\xf2\x3d\x9f\x73\x2a\xb4\x7a\x88\xd4\xb7\x97\x34\x5b\xa4\x02\x0e\x54\x59\x9c\x67\xb4\xd0\x65\xc7\xb6\x9f\x02\x7f\xae\x49\xe8\x6f\x8c\xcd\x54\x00\x1e\xca\xef\x51\xcc\x79\x38\x48\x11\xc4\x60\x04\x03\x52\x8b\x72\xd0\x2e\xa5\x79\xc4\x63\x56\xe6\xb9\x1e\x7e\x5f\xe3\x66\xa6\xa7\x7e\x3d\x54\x1b\x55\x54\x16\xe1\xe0\x9a\xae\xea\x4a\x4d\x68\x30\xf2\x34\x5f\x0b\x3d\xbd\xb9\xc1\x5a\x6d\x70\x2d\x26\xc7\xb8\x6b\xaa\xf5\xe1\xee\xa3\x8e\x10\xa1\xa6\x7a\xe5\xea\xb0\x86\x3f\x4a\x98\x10\x0b\x25\x49\x8e\x5a\x73\x05\x4a\x2e\xdc\x6b\x9a\xfc\x20\x8a\x4d\x30\x4c\x93\xab\x99\x28\xba\x1d\x77\x18\x59\xb7\x74\x47\xcd\x0a\x4e\x99\x78\x43\x05\xcb\xe2\x4d\x10\x38\xcd\x69\xac\x41\xa8\xf6\x57\x4b\xec\xe0\x02\x62\x74\xce\x28\x4f\x5f\x49\x99\xbf\x21\xf9\x2e\xb0\x74\x97\x4b\x77\x39\xc6\x65\xc1\xcb\x9c\x9e\xa3\xb2\xee\x5b\xc5\xba\x41\xd0\xd2\x80\xb2\x03\x6c\xe8\xa2\x54\x87\x55\x46\xee\x70\x82\xcc\x78\x7f\x2f\x72\x21\x2d\x98\x03\x51\x2e\x16\x39\x9d\x0e\x04\x99\x0d\xdc\xe9\xca\x8e\x11\xfd\x57\x67\x23\x1a\xca\x8f\x30\xe0\x69\x79\xdb\x6e\x5d\x16\xaa\xbc\x88\x66\xd8\x34\x70\x64\xd2\xaa\x0d\xb9\x76\x04\x61\x0b\x5c\x73\x0f\x43\x1a\xa9\x2f\x5a\xc8\x7b\x36\x34\x55\x1f\x55\x84\xd1\x42\x84\xc3\x28\x2b\x12\x7a\x17\xba\xed\x5d\x99\x35\x15\x52\xdb\x3c\x0c\x83\xaf\xa4\x22\xd5\x10\x88\x10\x2c\x0c\x08\xcb\xc8\x81\xd9\x0c\x83\xe1\x30\x4a\x09\x7f\x9a\x13\xce\xc3\x80\xd1\xbc\x24\x49\x30\x6c\x69\x22\xa5\x7f\x70\xcb\x6a\x54\x8d\x5a\x45\x4a\xe5\x9f\x51\x51\xb3\x02\xa4\x15\xc9\x61\x5e\xc6\x35\x87\x19\x89\xaf\xe5\x56\x82\xca\x37\x2b\xb8\xa0\x24\x81\x72\x0e\x0a\x96\xdc\x51\xa2\x3e\x01\x8d\x66\xc8\x9a\x6b\xba\x4a\xca\xdb\x42\xda\x47\x0c\x61\xf7\x52\xb2\x59\xc0\x38\xa6\x47\x12\x2c\xbe\x21\x79\xe8\x7f\x1b\xea\x36\x0a\xea\x06\x4d\xba\x1e\x36\x7b\x07\x63\xe5\x86\xcd\x43\xd5\x05\xc3\x28\xcd\x12\x4d\xf5\x46\x58\x9f\x28\x95\xb8\x59\x56\xa5\x52\x6a\x4b\xb8\x59\x51\x16\x82\xd7\xc5\x69\xbd\x7a\x72\x97\xf1\x8d\xad\x57\x57\xe4\x2e\xe3\x4e\xf3\x9c\x2e\x68\x91\x6c\x40\x47\x55\xba\xca\xa6\xca\x8a\x82\x6e\x9a\xb4\xae\x75\xb7\xc9\x1b\x92\x7f\x10\x44\x6c\x58\x65\x58\x7f\xc5\x65\x03\x6f\x53\x2e\x92\x67\x44\xd0\xfe\x3e\x8e\x42\xa3\x45\xd2\x55\xa4\xba\xb3\x3c\x81\x50\x79\x9e\xa8\xb2\xf8\x9a\xb2\x50\x49\x45\x5e\xc6\x24\xa7\x13\x18\xd0\x62\xa0\x4c\x32\x69\x10\x10\x31\x81\xc1\xaf\xbf\xfe\xfa\xeb\xc1\x9b\x37\x07\xcf\x9e\xc1\xcb\x97\x93\xe5\x52\xd7\x8b\xb2\xcc\x67\x84\xbd\xcf\x49\x8c\x36\xce\x04\x06\xb3\x52\x88\xd2\xd4\xf3\x2c\xa1\x3f\xac\x3e\x64\x09\x9d\x80\x60\x35\xd5\xa5\x69\x79\x7b\x5e\x26\x64\xf5\x43\x2d\x44\x59\xb4\xab\x9e\xe6\x94\xb0\x6e\x61\xc9\x1d\x20\x6a\x1f\xea\x58\xbb\x76\xce\xbe\xa0\x37\x93\x26\xe1\x40\xfe\x79\x9e\x2d\xe9\x7b\x9c\xfa\x60\x88\xb4\xd8\x04\x46\x59\xc4\x2d\x38\x52\x59\x25\x95\xde\xfb\x82\xd6\xee\xd9\xb3\xee\xdd\x5d\xb3\xb5\x15\x98\x0d\xb4\x0b\xa2\xae\x24\x5e\x67\xaa\xb9\x01\x62\x17\x3e\xff\x60\x37\xb6\xce\xd1\x54\xaf\x50\x77\xff\x53\x2b\x18\x0f\x02\x83\xa3\x81\x3e\xa9\x9a\x23\x8e\x58\xe5\x14\xc1\xa9\xed\xb5\x03\x4f\x36\xca\xe2\xd2\x6e\xbd\xcd\x66\xac\x84\x6e\x10\x2d\xf2\x55\x95\xca\x26\x03\x47\x85\xfa\x88\x86\x1d\xd5\xd8\x40\x21\x49\xa2\xd5\xe8\x4c\x14\x07\x15\xcb\x96\x84\xad\x02\x6b\xb4\x49\xc0\x4e\x1b\x3b\xd8\x41\x9c\xd2\xf8\xba\xd5\x8e\xe1\x89\xbc\xd3\xb4\x2e\xb0\x31\x4d\x4c\xf3\x35\xd0\x9c\xd3\x8d\x28\x79\x60\x7e\x1f\x56\x9d\xa1\xee\xc7\xcc\x9b\xc4\xda\x1c\x73\x3c\xa6\x84\x0e\xe7\x1d\x1c\xe3\x3c\x8b\xaf\xc3\x0e\xbb\xfa\x68\x2f\xed\xe5\x46\xe5\xfd\xed\xc3\xbb\xb7\x0d\x37\xc6\x63\x78\x35\x77\x0e\x26\xd2\x26\xd7\xa3\x8c\xb0\xb8\x64\xd9\x22\x2b\x48\x0e\x9c\xb2\x8c\x72\x40\xef\xc5\xa2\x14\xb0\xac\x05\x11\x34\x69\xe0\x84\x5c\x2a\x90\x64\x88\x07\xc5\x5b\x0a\x05\xa5\x89\xdc\xca\x18\x95\x96\x89\x60\x75\x2c\x20\x13\xea\xe0\xe8\x41\x96\x18\x21\xdc\xc8\xe5\x87\x76\x93\x28\x2b\x81\x91\x82\x4b\x75\xf4\x4c\x2e\xe2\xd6\x5c\x1a\xe2\x41\x57\xec\x3b\xb4\xf8\x1e\x06\x87\x03\x98\xc8\x95\x60\xf6\xbd\x36\xb5\x2d\x20\xb5\x0a\xf1\x60\x1f\x5a\x03\xb8\x73\xa8\x32\xe7\x8c\x0e\x2f\x5a\x66\x9b\x23\x2f\xc6\x60\x70\xc6\x32\xb6\xda\xfd\xad\x7a\x4c\x0a\xbd\xe0\xe7\x24\xe7\xb4\x65\xa4\xeb\x4d\xc7\xee\xb4\x5d\xd4\xd5\xbe\x31\x43\x4d\x6c\xcc\xd8\xf8\x0a\xed\xf0\xcb\x60\xd8\x23\x64\xc6\xf4\x88\x19\x25\x9c\x9e\x69\xcb\xc9\x1d\xf4\x3e\xe0\x09\xdd\x01\x78\x42\x7b\x80\xef\x8a\x3a\x2d\x92\x5d\x10\x7f\x5e\x24\xbf\x13\xed\x2d\x80\x0d\xd2\x0e\xe0\x5e\x3b\xad\x47\xe3\xb7\x8c\x2f\x75\x0e\x90\x75\x01\xa3\x95\xdc\x5b\x83\x11\x7c\x96\x27\xd1\x49\x0f\x3c\x54\xed\x23\x58\x96\x72\x93\x0d\x66\x74\x5e\x32\x1a\xac\x3b\x16\x9d\x31\xf4\xe4\x3a\x65\x14\xbf\x65\xc5\xa2\x91\x68\x75\x30\x95\x2a\x4a\x6d\x03\x3d\xc6\x85\x39\x99\xc8\x46\xda\xa8\xb0\x3d\x36\x69\x23\xbd\xe9\xa1\x9b\xf4\x1e\x71\x35\x94\xaa\xca\xaa\xce\x89\xa0\xaf\x70\x86\x64\x96\x53\x35\x4b\xae\x85\xd7\x2a\x37\xc7\x2e\x75\x47\xea\xac\x8e\x75\xbf\xe7\xb2\xf1\x00\x6e\x1c\x71\x27\x87\xe0\xc3\x88\x7c\x22\x77\xa1\xd1\xa5\x72\x90\x32\x99\x40\xf0\xd7\xe7\xe7\xc1\x48\x17\xd6\x2c\xf7\xbc\x5d\xb0\x0f\xc1\x98\x54\xd9\xf8\xe6\x68\x9c\x93\x19\xcd\xc7\x57\x57\x92\xb2\x57\x57\xe3\x1b\x74\xa6\xda\x9e\x52\x01\x9e\xaf\x2a\xc9\xd7\x4f\xbc\x2c\x6c\x39\xaf\xe3\x98\x72\x3e\x69\x10\x94\xd5\x23\x74\x56\x48\x83\xb2\xe6\xae\x1b\x41\xd2\x4c\xd6\x4b\xad\x28\x6a\x0e\x0f\xa6\x53\x08\x34\x88\xc0\x6d\x68\x68\x98\x96\xb7\xcf\xa5\x85\x1e\x06\xf8\x0b\xa4\x0e\xca\x8a\x05\x90\x1b\x92\xe5\x92\x42\xa0\x8e\xb8\xfc\x41\xb3\xc5\x35\x8c\x6d\x4a\xd6\xf6\x2f\x49\xb9\xa5\x25\x2b\x22\x23\xe7\xd6\x34\x9d\x97\x0c\x42\x34\x34\xd0\x67\x0b\x19\x9c\x98\x0e\x51\x4e\x8b\x85\x48\x8f\x21\xdb\xdf\xef\xc1\xd6\x5d\x0b\x17\x87\x97\xd6\x86\x23\x49\x12\x16\xf4\x16\xde\xe1\xf7\x50\x03\xbb\xc8\x2e\x47\xd0\xfc\x3d\x1c\xba\xd8\xee\x79\x80\xe7\xf5\x6f\xbf\xad\xce\x28\xaf\x73\x61\x3d\x98\xea\x07\x15\xc5\x04\x5d\xfa\x23\x6f\xfa\xb2\x6d\xb7\x7c\x49\xaa\x09\x7c\x5e\x6f\x1c\x08\x45\x59\xca\x22\x49\x29\x49\x42\x6f\x86\x65\xcd\x62\x3a\x31\x18\xbb\x50\x33\x41\x97\x7c\x02\x01\xc9\xf3\xc0\x1f\x4d\xc4\x29\x65\x8e\x6c\xc8\x96\x3e\xe1\xcc\xa6\x7f\x4b\x21\x25\x37\x54\x63\x8e\x4c\x88\x6b\x26\x0f\xcb\x6a\x8e\x23\xe0\xd7\x59\xe5\x75\xb4\x0b\xd0\x21\x8f\xd2\x9c\x28\x57\xe8\xf5\xc2\xaf\xed\x11\xbb\x54\xd5\xdd\xdc\x4e\xc7\xdb\xba\x2c\x49\x25\x99\xb1\xde\xda\x90\x19\xc6\x61\x61\x34\xcf\x72\x41\x59\xd8\x8c\x14\x69\xcd\x1a\x8e\x61\xbc\x18\xc1\x60\x30\xb4\x72\x31\xea\x60\x0e\x50\x31\x79\x2e\x3a\xe1\x82\x95\xc5\xe2\x74\x30\xea\x36\x28\xb9\x3c\xfd\x9c\x8c\x4d\x93\x56\x8b\xf5\x70\x47\x94\xa3\x79\xc9\x9e\x93\x38\x6d\x54\x29\xeb\x92\xb2\x9f\x32\x17\x2c\x32\x16\xd5\x25\x4c\x81\xb5\x47\x6c\xe3\xe0\x08\x22\x34\x7a\x59\x8a\x0b\x64\x45\xef\x08\x6e\xff\xf5\x68\xcf\x93\x54\x26\x3a\x52\xc7\xdb\x98\x63\x61\x24\xdb\x36\xd3\x23\xa3\x59\x77\x82\x46\x15\xf4\x4e\x73\x76\x19\xf1\xb8\x64\x14\x0e\xfa\xeb\x89\xae\x6f\xcf\xdf\x4c\x10\xcf\x41\x87\xf0\x3d\x90\x48\x1d\x79\x9f\x96\xcb\x8a\x30\x1a\xce\x86\x30\x81\xac\x45\xa4\x16\xd1\x1c\x2a\xf1\xcd\xe4\x48\xb3\x45\x9a\x67\x8b\xd4\xa3\x09\xf4\x2e\x45\x0d\xf0\x61\x38\x38\x49\xb2\x9b\xd3\x81\x71\xdf\xb7\x67\x25\xfb\x5e\x46\x5c\x30\xa9\x8a\xf7\xa5\xa8\x61\xf3\xa1\x8f\x43\x1f\xda\xe3\x31\x9c\xa7\x19\x47\x73\x1c\xa3\x14\x29\x86\x35\x80\xcc\x05\x65\x40\x84\x20\x71\x2a\x81\xa2\xbf\xdb\xe8\x21\xa8\xf2\x7a\x91\x15\x23\x20\x1c\x32\xe1\xc2\x2a\x45\x4a\xd9\x6d\xc6\x29\xcc\x18\x25\xd7\xbc\xd5\xcf\xcc\x96\xe4\x99\x58\x45\x3d\xaa\xce\x73\x39\x39\x48\xa3\x57\x68\xd2\x3d\x7f\xc2\x9f\xda\x98\xd6\xc6\x5d\xb0\xc5\x0e\x58\x50\xf1\xce\xc6\xab\xb6\x6f\xfc\xad\xf8\x56\x73\x9c\x56\x85\xe8\xef\x36\x51\x51\x80\xc0\xf1\x6b\x6b\x6d\x1d\x58\x27\x83\x29\xe0\x82\x56\xed\x12\x3c\xb3\x04\x7b\x00\x97\x9b\x0d\x60\xd5\x65\x18\x51\x4f\x6b\xa0\xaf\x73\x64\x82\x4f\xee\x59\x5e\xda\x1a\x4d\x20\x3d\x92\x5f\x1d\xc7\x67\x94\x15\x4f\x18\x23\xab\x50\x96\x8f\xbc\xe9\x0c\xe1\x74\x0a\x87\x0d\x5b\x30\x2c\xa3\xa1\xa0\xe5\xa2\xb7\x6a\x38\x75\x5b\x81\xa1\x13\x9a\x8f\x97\xce\xc8\xd8\xc7\xf2\xc9\xf3\x8e\xda\x4e\x26\x06\xd5\x32\xfa\xdc\x16\xca\xd7\xdb\x76\xff\x2a\xeb\x14\x97\x96\x8d\xff\x6f\x33\x05\x09\xe3\xf4\x59\xcd\x08\x2e\x56\x47\x0a\x90\x7b\xe7\xf4\x4e\x34\xe2\x80\x45\x67\xcf\x61\x0a\xd2\xc8\x38\xa3\x8b\xe7\x77\x55\x18\xfc\x33\xbc\x38\x3c\xf8\xcb\xe5\xfe\x30\xbc\x58\xdd\x26\xe9\x92\x5f\xee\x0f\x1f\x2a\x59\x44\x13\x08\xf7\x66\x29\x16\x16\x62\x84\x65\xa1\x06\x67\xbd\x5a\x0f\x74\x53\x15\x8f\x41\xb3\x0a\x69\x23\xeb\x74\x95\x21\xf6\x83\x29\x7c\xd3\x72\xfd\x7c\x77\x68\xfc\x56\x72\x54\x24\x33\x4c\x01\xa7\xf7\xaa\x10\x06\xc0\xc5\xd1\xa5\xc5\xac\x2e\x32\xb9\x59\x9a\x9a\xc7\x97\x0e\xf9\x54\xff\xaf\xbb\x21\x6f\x27\x21\xe1\x42\x02\xb8\xdc\x4a\x61\xef\xd4\xb8\xf3\x3a\x43\xe2\x7c\xa0\x71\x59\x24\xd6\x77\xeb\xf1\x2a\x6c\x05\x9a\x1c\x87\x75\x9f\x61\x79\x4f\x1e\x43\x9f\xb1\x29\x69\xee\xa1\x70\xd2\x87\xc2\x3d\x40\xd1\xd0\xf4\x5d\x4d\x2d\x5c\xb7\x74\x3e\x76\x16\xdc\x86\xd3\x0f\xdc\xe3\x1f\x68\x2c\x71\xd7\x42\x5f\xef\x72\x3a\xf2\x4e\xe2\xff\x79\x86\x6d\xe7\x14\x1c\xc0\x91\xe4\xea\xa9\xe2\xee\xc1\xc1\x46\xae\x9d\xfe\xcf\xe1\xda\x82\x8a\xe7\x36\x4a\xb0\x9d\x65\xa8\x70\xbc\xd8\xc2\x97\x2f\xe0\x15\xf8\x58\x33\x13\xb4\x5a\x62\x58\xcd\xe8\x1a\xd7\xef\xbc\x8b\xcb\x7d\xb7\x3d\x99\x7d\xf8\x7d\x93\x91\x45\x89\x6a\xac\xbc\x6a\xb6\xbb\x13\x69\xe2\x4d\xa1\x6c\x3b\x74\xb4\x5d\x82\x29\x6d\x5b\x10\xe3\xbd\x38\x21\xa8\x7b\x53\x87\x76\x21\x8b\x46\x68\x47\x4d\xfa\xbc\xe8\x89\x01\x6c\x20\x4b\x41\x6f\x35\xca\x9a\x75\x86\x40\x2e\x91\xf5\x32\xd4\x6d\xf1\x18\xbd\xf3\xfa\x85\x31\x3c\x1e\xc1\x80\xab\x15\x37\xe8\xa5\xb7\x06\xec\xd4\xf9\xa2\xbf\xa3\x42\xfa\xbf\x3d\x6f\x5e\xcf\x04\x23\xb1\xf8\x7f\x6a\xf2\x4e\xeb\xdd\xd3\xd5\xe2\x9c\x12\xa6\xcc\xe6\x61\x6b\xb5\x77\xf4\x51\xa3\x69\xd6\x7b\x6d\x17\xb2\xb4\xbe\xc3\x9e\xe0\x65\x44\x97\x95\x58\x85\x43\x27\xa0\x44\x98\x90\x72\xad\x8d\x23\x45\x5d\x49\x6f\x59\x18\x0e\xff\x1d\xbb\x84\x4e\xa3\x29\xf3\x5a\xdb\x6a\xd6\xb8\xd9\x6c\x22\x9b\x3c\x0f\x63\x65\x5f\x06\x43\x33\xfb\x2f\x5f\xe0\x0d\x11\x69\xb4\x24\x77\x21\xfe\x31\xcf\xcb\x92\xf9\xfb\xc7\x18\x1e\x7f\x7b\x38\x1c\xc1\x91\x45\xa0\x89\xc4\x76\x34\x0d\x8c\x4d\x1e\xac\xa3\xff\x11\xab\x5f\x52\xe6\x79\x2c\x4d\x61\x44\x66\xf2\x58\x3c\x74\x2d\xb7\x9a\xe5\x66\x2c\xed\xaf\x33\x5f\x2b\xc2\xc8\xb2\xc9\xac\x0b\x10\x4a\x30\x69\x9b\xc9\x26\x9c\xb4\x31\x2d\xd0\xda\xe9\x0a\x60\x84\xbc\x93\x26\xba\x9e\xda\x81\xc7\xa5\x63\xb7\xa9\x0a\x8c\xeb\x86\xc7\x3e\x10\x5a\x49\x1b\xd7\xf2\x47\xd5\xd6\x2c\x97\x5b\x7a\xbf\x23\x54\x25\xa0\xe1\x60\x81\x76\x5d\xab\x19\xbb\x82\xde\xe3\xe5\x74\xd3\x38\x70\xb9\x9c\x51\x5e\x95\x05\xa7\xdd\xc6\xc7\x8a\x16\x5e\xe4\x4f\x63\x2c\x94\xb4\x36\x92\x6b\xd8\xb7\x1b\xde\x7f\x18\xe3\xa7\x2a\x34\xb4\x1d\x67\xeb\x1f\x37\x7c\x57\x7f\xb4\x0e\x85\xbf\xa4\xf2\xa8\xb4\xc1\x27\xdd\x5a\x18\x2a\xa5\x45\x55\x06\x43\xcf\x57\x5d\xb3\x7c\x9b\x07\x5a\x96\x4f\x34\x12\xff\x69\xaf\x34\xf6\x42\x67\xc1\x8e\xde\x67\x0d\x35\xb4\x7e\x67\x9f\xc4\xdb\xfc\x10\x77\x29\x1b\x49\x61\xae\xda\xe8\xcb\x32\x79\xfc\x0a\x70\xe9\xb6\x90\x46\x05\xc1\x3c\x1f\x9c\xec\x73\x97\xb2\x88\x69\x76\x63\xd4\xf3\x41\x5f\x72\xae\xf9\xa1\x4c\x32\xb4\xdd\x47\x4d\xde\x73\x3e\xf9\xd1\xec\x76\x67\x45\x62\x79\xdc\xf4\x3a\x6d\x75\xfc\xd3\x3b\x1a\xd7\x98\xc3\xaa\x5d\xde\x01\xec\x4b\xb0\xc3\x2e\x95\x2d\xf5\xe2\x72\x59\xe5\x54\xd0\x9d\x09\x38\xdd\x40\xc0\xfb\xa3\x09\x49\x73\x4c\xef\xdb\x63\xe0\xa0\x59\xcc\xc7\x5e\x47\x51\x0a\x92\xcb\xe2\x0f\x2a\x9a\x8d\x29\xe2\xf7\x71\x48\x85\xa1\xef\x61\xd3\xc6\x4e\xda\xa3\x2b\xd7\x0f\x2a\xdb\x80\xc7\x24\x27\x2c\x68\x73\xb9\x8b\xd2\xd1\x56\xe6\x76\xfb\xdc\x87\x82\x39\xd6\xf6\x72\x7f\xdd\xf2\xd1\xd9\x8d\x3d\x15\xcb\x3c\x0c\x5e\x97\x24\x01\xa9\x20\x15\xfb\x2d\xe1\xf7\x21\x58\x72\x38\x99\x31\x18\x9f\xc2\x99\xd5\xf5\xaa\x95\xb3\x37\xef\x43\x60\x9a\xc9\x9a\xe0\x5c\x62\x8e\x00\x75\x42\x81\xea\xd1\x9a\x90\x23\x62\xbd\x81\xec\x06\xf5\x1d\x7c\x7b\x56\xb0\x5d\xd5\xbc\xe4\x8b\x2d\xc6\xba\xec\x11\x49\x4d\x81\x6d\x5b\xe5\xc6\x1c\xda\x32\x74\x63\x7d\xfd\xd1\xb1\x07\x83\xf6\xd0\x86\x06\x5b\x86\xf6\x32\x88\x76\xb0\x17\x5d\x3b\x41\xb2\xa7\xac\xc5\xab\x67\x46\x56\x6f\xb3\x22\x29\x6f\xd5\x74\xce\x55\x65\xbb\xa5\x35\x1b\xb3\x56\x9e\x6b\x9f\x51\xd7\x4a\x83\x6a\x2c\x3b\x34\x4f\x0d\x04\xdf\xfd\x65\x33\x46\xcd\x90\x30\x35\x78\x71\xb5\xf0\x25\x56\xfd\x21\xe8\x9e\x03\x76\x6f\x9a\x95\x9c\xc3\xa8\x99\xc1\xd7\xfa\x5e\xd3\x76\x6a\xab\x4b\x05\xaf\xc9\x8c\xe6\x9e\x05\x80\x11\x5e\xde\x90\x1c\xbf\x7f\x40\x2f\x3e\xd7\x77\x80\x1c\xa7\x07\xd6\x42\x56\x80\xdb\x4d\x11\x45\x55\xc9\xed\xc6\x84\x8b\x1d\x45\xe2\x42\x8d\xaa\x9a\xa7\x61\x60\x82\x55\x72\x71\xa9\xbe\xfb\x10\xd8\xf8\x94\xd6\xe5\x3c\x26\x15\x7d\x79\xfe\xe6\xb5\xc6\xf3\x02\x7f\xd9\xb8\xe8\xda\x3f\xda\xe7\x66\x76\xc1\x49\x92\xdd\x40\x9c\x13\xce\xa7\x1f\x03\x55\xfc\x31\x68\x86\x32\x98\x7c\x2a\xb3\x22\x0c\x4e\x66\xec\x34\x18\xaa\xe1\x93\xec\xe6\x34\xd8\x4a\x4c\xe5\xc6\x3f\x2f\xcf\xf9\x5b\xe5\xac\xde\x48\x4e\x61\x5a\xe8\x9a\xc8\x10\x47\xda\xf4\x83\x01\x8e\xfa\x39\x38\xbe\x8f\xf8\x5b\xa9\xbf\x9d\xfc\x3d\xf4\xb7\x24\x9f\x7e\x0c\x2c\x5d\x0c\x7d\x65\xf9\xc7\xc0\x06\x29\x50\x03\xcb\x0f\x3d\x9b\xfd\x69\x1f\x19\x47\x8a\x86\xeb\xc0\xf1\x56\xa8\x0e\xbb\x79\xb6\x7f\xd2\x7e\x60\x4b\x4b\x74\xec\x36\xa4\x54\x2b\x16\x9b\xbe\xc8\x4b\x22\x74\xbd\x59\x94\x19\x7f\x4b\xde\xca\xb2\xa1\x73\x8d\x23\xd8\x7f\x55\xcc\x83\x11\x04\x07\xfa\x37\x7e\x87\xdb\x2c\xcf\x61\x46\x15\xb0\x44\x2e\xa7\x12\xde\x92\xb7\x30\x5b\xb9\xf0\x87\x11\x9c\xa7\xd4\x80\x8a\x49\x31\x10\xb2\x13\x66\x9e\xd0\x64\x04\xbc\xc4\xd4\x4f\x10\x29\x5d\x02\xe1\xb0\x20\x15\x87\xb0\xa8\xf3\x7c\x18\xb9\x8e\x28\x73\xb7\x6e\xed\xf9\xac\xb7\x12\xc5\x4b\x29\x6b\x1b\xed\xf7\x3a\x14\x2a\x92\x53\x21\xcc\xf9\xf6\x4c\x5f\xf5\x8b\x9e\x96\x79\xc9\xa2\xf7\xaa\xb2\x39\x6c\xa3\xd9\xe9\x98\x02\x52\x86\x96\x44\xb0\xec\x2e\xf0\x55\x54\x63\x7e\xe9\xb4\x83\x8c\x43\x51\x0a\x28\xe7\xa0\xda\x63\x94\xed\x01\xbc\xcf\x29\xe1\x14\x28\x5e\xa1\x21\x10\x97\x8c\xd1\x58\x60\xc2\x38\xe5\x3c\x2b\x8b\x28\xf0\x53\x6d\x94\x9c\xaf\x1b\xef\x18\x31\x59\x18\xcc\xc6\x17\x1b\xbd\x29\x78\x3b\x5a\x74\x6c\xbf\x29\x29\x6e\xc2\x45\x82\xeb\xb5\x8a\x06\x0e\xb2\xc6\x2e\x0a\x1d\x67\x32\x56\xcf\xb1\xab\xaa\xb8\x13\xc5\x6f\xd9\x37\x26\x3c\xd5\xa8\x26\xa4\x8e\xaf\x12\x9a\x81\x9b\x14\x0e\x0b\xd8\xd6\xb9\x79\x81\x9a\x14\xee\x28\x13\xfc\x1c\x79\xdd\x27\xfa\xb7\x7f\xd0\x11\x5c\x05\xab\xb8\x4f\x29\x67\x01\xa9\x9f\xd6\x20\xf2\xe7\x6e\xa2\x02\x28\x17\x87\x97\x6e\xd6\xc0\x6a\xe2\xec\x8d\xb8\x32\x15\xb4\x8b\xa3\xcb\x26\xa2\x6b\xd3\x1c\xd6\xc3\xc6\xbc\xce\xe5\xe1\x44\x4b\x60\x84\x5f\x43\xd5\x63\xdd\xe4\xfe\xa1\xe9\xd7\x49\x24\xe0\xce\xc2\x55\xe9\x4e\xc8\x31\x8e\x0a\x90\xe4\x39\x2c\x33\xce\xa5\xb5\x2f\x0f\xf0\xbc\xb9\xe7\x54\xd0\x5b\x6b\x65\x6a\x95\xa9\x96\x41\xe9\x98\xcf\x56\x89\x0a\x67\xdb\xb7\x2e\x85\x63\x10\x70\xe2\x97\xd3\x22\x91\xa5\xfb\xed\xd6\xb4\xf2\x72\x53\x9f\xe4\x79\x79\x8b\xd0\xe7\x52\x69\x48\xf4\xaa\x32\x2b\x04\x64\x05\x89\xe3\x9a\x91\xd8\x06\x99\xd1\x7a\x51\x66\xaf\x0d\x44\x4a\x1c\x1f\x3d\x02\x55\x7c\x51\x95\xfc\x32\xba\x83\x13\x39\x6e\x67\x58\x75\xe8\x77\xd9\x69\x27\xae\x54\xba\x03\xc4\x31\x4f\xab\x12\xaf\x7c\x6a\x46\xb5\x6d\xf5\x16\x88\xcf\x77\x13\x10\x23\xd0\xd9\x43\xeb\x61\x37\xfa\x09\x60\xef\x07\xdb\xbe\x0d\x63\x1b\x27\x35\xd9\xd1\xfe\xeb\x5c\xbe\xbe\x37\x0c\x60\xf3\x6e\x0d\x05\x8d\x93\xc8\x37\xc3\xf0\xe6\x0a\x5e\x8d\x26\xc5\x0a\x04\x23\x31\xe5\x52\x4d\x91\x02\xe8\x5d\xa6\xae\x3d\xa2\x1a\x8f\xfc\x9b\x14\x8d\xaf\xd0\x19\xae\xb9\x86\x11\xa7\x59\x9e\x30\x5a\x84\xc3\x9e\x48\x72\xd3\xb6\x95\x4f\x88\x15\x78\xb1\xc3\xab\x58\xb7\x6f\x88\xe8\x0c\x0b\x6d\xb6\x04\xea\x6a\xc8\xa9\x49\xa3\x38\x6e\x5f\x11\x69\x35\xd7\x77\x43\xba\xed\x1b\xf4\x3b\x97\x45\xb7\x35\xc2\xa1\x1a\xc7\x29\x2d\x12\xed\x36\xdd\xe8\x4f\x94\x94\x7f\x5a\x16\x37\x72\xed\x8a\x12\x7e\x7c\xfb\xea\x17\x3c\x4a\x71\x41\x96\x95\xb9\x2c\xea\x9c\x8d\x77\xf7\x5e\x7f\xf9\x02\xdf\x7c\xa7\x47\x38\x4a\xcd\xbd\xe5\xa8\xc7\xa7\x6b\xd0\x3c\xb0\x03\xd9\x69\x6e\xd7\x3b\xef\x49\x82\x29\x1b\x3a\x97\xfc\x36\x13\x29\x64\xc5\x4d\xc6\xb3\x59\x4e\x21\x90\xab\x22\x50\x0a\x93\x03\x51\x97\x41\xe3\xb2\x98\x67\x8b\x9a\xd1\x04\xee\x0e\x24\x13\x60\x56\xd6\x45\x42\x10\x00\x2d\x78\xcd\x28\x37\xe0\x45\x4a\x84\x92\x3c\x0e\x84\x51\x48\x32\x5e\xe5\x64\xa5\xaf\x97\x02\x81\x79\x76\xd7\xc0\x41\x2a\x78\x77\xac\x0a\x52\x55\x98\x0a\x53\xe2\xd0\x36\xb1\xc4\xc2\x97\x13\x37\xdd\xb0\x49\x93\xb5\xde\xa8\x9f\x8b\x43\xa9\x65\x4e\x1b\xaa\x39\x71\x44\x45\xa3\xba\xc0\xbb\xab\xa8\x0f\x6c\xab\x8e\x5e\x58\xb7\xe1\xfa\xda\xed\x00\x8e\x94\x36\xd3\x1c\xe9\x8c\x62\x55\x8e\x6e\xd0\x3b\x40\x73\x19\xed\x6d\x79\x0b\x31\xa3\x44\xa8\xab\xaf\xd2\xb6\xf1\x17\x71\xe7\x51\x03\xd7\xfa\x51\x49\xf2\x0a\x03\x9d\xe1\x31\x71\x84\xdf\xee\x7f\xea\xd2\xea\xa4\x71\xb8\x3b\x0b\x1b\xcf\xf8\xea\x0e\x6b\x38\x1c\xa1\x3a\x1e\xe9\xe3\x67\x22\xd2\x7b\xfa\xfc\x2c\xeb\xd1\xed\xf3\xdf\x87\x23\x78\x6c\xfb\xa9\x53\x19\x65\x93\x9e\x3b\x11\xdf\xeb\x04\x9b\x00\x26\x10\xe4\x59\x41\x8d\x1b\x14\x4f\x7f\x55\x99\x13\xed\xcf\x90\x75\x84\x69\xdf\xa7\xf1\x59\x58\x79\x57\xc5\xcb\x4c\xb6\x24\xb5\x28\x83\x91\x47\xd4\x17\x59\x91\xe0\x7d\x08\x4e\xb5\x64\x0e\x38\x2c\xc9\xdd\x78\x99\x15\x7b\x1b\x6e\x6b\x48\xa5\x2b\x58\xed\xde\x97\xfe\x39\xa5\x85\xb9\x96\x21\xed\x42\x75\xf7\x32\xb1\x7b\xf1\x92\xdc\x35\x7b\xf1\x3d\x6b\x51\x34\x1e\x16\x2b\x2d\xb2\x7f\x5c\x33\xa6\xca\xdf\xb8\x90\x00\x9a\x0e\x1b\x20\xca\xd2\xf7\x72\x47\x6e\x7b\xf7\x6c\x45\xb4\x82\xd3\xd6\x00\x8f\x1e\x81\x5b\xfd\xa0\x6d\x3b\xa2\xa9\xd3\x42\xc9\xe9\xd0\xe3\x7f\xb4\x5b\xa9\xa4\xc4\xfe\xd4\xef\xad\xa5\xdd\xdd\x30\x3c\x59\x8e\x14\xf9\x96\xe4\xee\xeb\xa3\xe8\xf0\xdb\xcd\xcd\xb2\xc2\xd0\xc6\xdb\xe9\x91\x03\x58\xf7\xaa\x98\x67\x45\x26\x56\xc7\x2d\xce\x1c\xf8\x15\xbf\x93\x43\xff\x1e\x26\x9c\x20\x8e\xbb\x90\x5e\xcd\xe5\x5e\x82\xf7\xf1\x78\xb9\x23\x67\x97\xbb\xf3\x73\xed\xdc\x28\x43\xac\xa6\xc8\xa6\x76\x62\x46\x3f\x33\x61\xbf\xf1\xa4\x6e\xe4\xa6\xfc\x3c\x30\xed\xfa\xae\x85\x6d\x06\x1e\x1e\x46\x47\x5f\xab\x80\x21\x99\xf1\x50\x16\x1e\x48\x78\xc3\xe6\x50\xb2\x65\xd8\xad\x10\xd6\xc6\xa9\x26\x45\xe9\x4e\x9b\x26\x5d\xbd\x1b\xa1\xf9\x83\xbe\xef\xcf\x4a\xcb\x4c\xfa\x54\xb6\x73\xd7\x63\xb5\x05\xd6\xaf\x5a\x95\x6f\x04\xa6\xf4\x5e\xc9\x32\x5a\x08\xab\x29\xe9\xdc\x24\x2f\x8a\x2c\xbe\x7e\xa1\xaf\x8f\x5a\xf8\x2f\xb2\x3b\x21\xb7\xeb\xe8\x6d\xbd\x9c\x51\x16\xa9\xfb\xa5\x7f\x7f\xf3\xc3\xf9\xa8\x67\xdf\x40\x14\xf5\xbe\xe1\x5e\x12\xf1\xc9\xa9\x5f\xf3\x68\x66\x96\x96\x37\x94\x3d\xa3\x82\x64\x79\xff\xfc\x5e\x36\x0d\x76\x9b\xa4\x42\xd3\xcf\x6f\x56\xfb\xc0\x08\xee\x46\xb0\xf2\x55\xa9\xce\x3e\x19\x9c\xf0\x8a\x14\xc6\x7c\x94\x85\x01\x26\xf7\xda\x70\xc5\x1d\x7c\x8d\x46\xdd\x30\x12\xe5\x8f\xe7\x4f\x95\xb3\x27\x1c\xaa\xdc\x5e\xd9\xf7\x74\x70\xec\x80\xe5\xb7\x44\xc4\x69\x17\x30\xce\xe3\x4a\xd5\x06\xea\x2a\xdb\x34\x98\x91\xf8\x7a\xc1\xa4\x99\x74\xa0\x4f\x8c\x2a\xaf\x18\x55\x08\x96\xc8\x61\xa4\x35\xdb\x1d\x28\x2e\x0b\x41\x0b\x3c\xc6\xa9\x21\xf7\x41\xcf\x36\xea\xf3\xb1\xa1\xb1\xa6\x1c\x6d\x13\x70\x9d\x8e\x2b\x3d\x13\x9d\x10\x6f\x86\x70\xf2\x6c\xb0\xc1\x8c\x21\x59\xcc\xa8\x4e\x91\xf6\x14\x37\x7e\x55\x1f\x8d\xae\x0d\x83\x1e\x0a\x73\x65\xbb\x87\xf1\xaf\xb1\xae\xd7\x46\x51\xdd\xac\x91\x72\xaf\x40\x38\xa3\x39\x79\xde\xfd\x43\xfe\x40\x53\x72\x93\x95\x2c\xd2\xea\xfb\xa5\xe9\x10\xc2\x4e\xa2\xa7\xf0\x9a\xe8\xdf\xfe\xe0\x3c\xa5\xf9\x8d\xb4\x56\x77\x1a\xf9\x1c\x2d\x86\xdd\x04\x7e\xd3\xa8\x6e\xe8\xda\xbe\x99\xb0\xd5\x31\xce\xb3\xdf\xfe\xc8\x31\xd4\x57\x5d\x0f\x5a\xfe\xa5\x1e\x4d\x60\x0f\x0a\x36\xf6\xfd\x47\xcd\xc6\x7b\x2c\x85\x46\xdd\xec\x90\x88\xd7\x93\x97\xb0\x25\x3b\xa0\x9f\x26\xf2\xbc\xad\xb1\xd0\xb7\x6e\x39\x54\x04\x9f\xcd\x71\x2f\xe5\xce\x4b\x66\x6d\x44\x75\x08\x42\x27\xaa\x73\x13\x97\x93\x1b\xba\xa7\x4f\x4a\xce\xfd\xdb\x27\x7f\x7b\xf2\x0b\x98\xe0\xa1\x3c\xd9\x94\x2c\xa1\x4c\x5d\xdd\x3d\xb0\x7e\x52\xc8\x84\x72\xe5\x3a\x63\x2a\x60\xb7\xd2\x3a\x95\x10\x6b\x4e\x99\x3c\x74\xc9\x33\x93\xba\x18\x80\xf8\xb8\x8f\x56\xd8\x6b\xbb\xda\x07\xe9\x1d\x1e\xfb\xaf\xfb\xa2\x43\x76\xab\x8b\xa2\xd7\x93\xfa\xb6\x44\x34\xd1\x65\xc4\x61\x2e\x35\x62\xcb\x3b\xda\xf5\x15\x9c\x93\x99\x7f\x5b\xdb\xbd\x86\xeb\x44\x8d\xec\xb5\xe0\x9d\xa4\xa0\x95\xeb\xd1\x4a\x1c\x24\x3b\xc9\x81\x4a\xe8\x6a\xee\x13\xdf\x8f\xa5\x4b\x69\xe5\x23\x37\x41\x93\x1f\xca\x64\x65\x48\xed\x80\xf3\x9f\x91\xb9\xc2\xdb\x90\x20\x66\x65\xa2\xef\xbd\x63\x3f\x2f\xdf\x8b\xdf\x66\x22\x4e\xc3\x56\xb4\x5b\xe1\x1f\x13\x4e\x21\xb8\xa1\xb1\x28\x59\x30\xd9\x73\x4d\x46\x3f\x2c\xed\x73\xd0\x0c\xa3\x1d\x25\xc1\x89\x60\xa7\x27\x22\x81\xb8\xcc\xe5\x5e\x35\x1d\x3c\x1e\x9c\x9e\x64\xa7\x85\x62\xec\xc9\x38\x3b\x3d\x19\x8b\x44\x7e\xb0\xd3\xe6\xda\x47\x3b\x67\xb6\x3f\x13\xbc\x27\x44\xee\x5f\x33\x44\x1e\x68\x5b\x55\x37\xbc\xc8\x2e\xdd\xdd\xd2\x06\xa0\xfa\xbc\xd4\xd6\x49\x7d\x7c\xdf\xd4\x4e\x5b\xa1\x38\x05\x52\x07\xcc\xe4\xd4\x74\x13\xed\x84\xbe\x38\xba\x6c\xaa\xdc\x59\xab\x79\xe2\xa5\x9c\x63\x4b\x7f\x1d\x69\xf8\xff\x98\xfe\x37\x7f\x9c\xfe\x37\x6d\xfa\xdb\xfb\x10\xe7\xf4\x4e\x5a\x38\x81\x0d\x4b\x58\xf4\x3e\x29\xf4\x3e\xc1\x09\xdc\x18\xaf\xbf\xc1\xed\x93\x7f\x05\xb5\x81\xb4\x3f\xb5\x8d\x2f\x3e\x5d\x6a\x0e\xc1\xff\x96\x5c\x73\xcb\x0f\x15\xe7\x66\x6c\x7c\x1a\xf8\xae\xdf\x3f\x29\x1a\x0e\x26\x3b\x4b\x86\x8e\xcb\x28\xc9\xe8\x1f\x5d\x35\xf1\x46\x72\x39\xb1\x49\x10\xdb\x03\xa1\x65\x7b\xff\x40\xd8\xc4\x1b\xc8\x99\xb5\x3f\xe6\x70\xcb\xa0\xda\x75\x39\xe9\xdd\x0f\x7e\x2c\x78\x5d\x55\x25\x13\x34\xd1\x17\x5b\x30\xa6\xd6\x01\xb2\x75\x6b\x67\x1b\x9e\x06\xed\xbb\x24\xde\x7e\x3f\xd0\xf3\x53\x3b\x36\xd5\x59\x7f\xb1\x6f\x6a\xd9\xdb\x84\x6e\x84\x0c\xc9\xd7\x20\x40\x0b\x91\x89\xd5\x1b\x75\x59\x16\x27\x16\x3c\x0a\x26\x10\x3c\x22\xcb\xea\xd8\xdc\x2e\x3b\xc1\x92\x5c\xd8\x82\x53\x2c\x58\xd8\x82\x41\x30\x98\xc0\xe0\xd1\xbf\xea\x52\x1c\xeb\x2b\xaf\xc1\x20\x90\x45\x5f\x7d\xf3\x17\x5b\x32\x56\x25\x77\x8f\x5f\x1c\x0f\xec\xc3\x32\xda\xc8\xd7\x67\x1a\x8d\x5e\x73\xe7\xf6\xe2\xd1\xc9\x69\x30\xf8\x38\xbe\x1c\x2f\x46\xce\xf5\x48\xde\xba\x61\x60\xa7\x71\xc1\x2f\x4d\x0c\x64\xed\x71\xe5\x3d\xe9\xbb\x96\xd2\x3c\x0c\x6b\x42\x56\x2d\x66\xca\x6e\xad\x57\x40\xfb\x39\x89\x40\x9a\x7b\x81\x08\x18\xdd\xe9\x3f\x9e\xbd\x6e\xc2\x18\x6e\xab\x5e\x9d\xea\x35\x50\x5e\xd9\x75\x93\x2f\xe3\xd5\x1a\xd7\x0e\x0e\x45\x92\x44\x59\xe5\xa0\x9f\x98\x45\x69\x0a\xbe\x22\x49\x72\xa5\x9f\xb6\xd2\x0f\x2f\x78\xcd\xd5\x5b\x60\xb2\x68\x04\x9f\xd7\xc3\xae\x85\xd2\x9a\xbf\x99\x51\x97\x06\x72\x76\x3a\xc5\x26\x2f\x63\x3c\xe6\x47\x9c\x12\xa6\x1e\x62\x0c\x82\x16\xc3\x4c\xa0\x59\x53\x0f\xb3\x06\xdf\x9b\x94\xd5\x7e\x38\x11\xaf\x67\x4a\x3e\xc2\xa3\x61\xc4\xab\x3c\x13\xe1\xe0\xd1\xc0\x26\x59\x37\x30\x5e\xd2\xbc\xb2\xc7\xac\xf6\x64\xfe\xd1\x6a\x16\xba\xe1\xb2\x36\x0c\x35\xe1\xa6\x0b\x0f\x1d\x4c\xb7\x52\xcb\x50\xd9\xa5\x96\x79\x3c\xd4\x17\x9c\x2e\xae\xca\x64\x44\x92\x3d\xb4\x0f\x77\x3a\xaf\xef\x69\xa7\x8a\x7e\xd6\x54\x29\x4c\xc9\x59\x65\x70\xfe\x78\xf6\xba\x61\xed\xd0\xa9\x56\xfa\xa4\xc5\xfb\xe1\x1e\xc0\xb0\x79\x61\x58\xad\x07\x25\x7d\x4d\x74\xea\xa1\x66\xef\x50\x9f\xd3\xba\xe9\x53\x26\xe4\x66\x4f\x71\xcd\x43\x38\x92\x4e\xe3\x31\xbc\x7d\x77\xfe\x7c\xd2\xba\x62\x3c\xa3\x70\x4d\x2b\x81\x17\xc9\x57\x45\xac\xc2\x2f\xe3\x5a\x64\xf9\x98\x0b\x66\x7e\xc7\x65\x71\x13\x2d\xca\x09\xc2\x7d\x9d\x15\xd7\x2f\x4a\xf6\xdc\xa6\x31\xdc\xc3\x03\x4b\x8f\xfe\x65\x8b\xec\x54\xca\xc7\xac\x5a\x3d\x7d\x2f\x7e\xbf\x50\x6b\x0b\xaf\xca\xba\x39\x0f\xad\x55\xaf\x28\xd0\x5c\x10\x36\x81\xc7\x3f\x2d\x9e\x0e\x88\x77\xb3\x4f\x34\x96\x4a\xa8\x23\xab\x0b\x5a\x50\x46\x84\x12\x57\xd5\xcc\x53\x38\x06\x7f\x2f\xe3\xe3\xa1\x0a\x6c\x87\x0e\x6c\x93\xdb\xa6\xde\x00\x55\x29\x45\x8f\xf4\xc3\x72\x69\xc6\x45\xc9\x56\x28\x1c\xf2\x08\x42\xc3\xcf\xeb\x11\x04\xc1\x08\x54\x98\xf4\x7b\xb9\x21\x3b\x44\xdd\xba\x46\x1c\x81\x74\x39\xa4\xe4\xae\x47\x47\xbb\x2c\xd2\x6f\x35\x34\x9d\x86\xf0\x59\x4f\x6b\x81\x6e\x00\x6c\xd7\x93\xf6\xd9\x4b\xe9\x96\x80\xec\xd2\xa5\xad\x19\xff\xe1\xa9\x31\x0b\xcd\xd5\x19\x56\xf2\xf0\xe0\x4c\x13\xbf\x0b\xce\x4e\x4d\xeb\x55\x71\x43\xf2\x2c\xe9\x51\x3b\xea\x59\x04\x57\x6d\xa9\x6e\x54\xc4\x86\xd5\x2f\x58\xb9\x7c\xa7\x06\xd0\x00\xba\xc3\x8d\xe0\x70\x47\xca\x44\xcd\xe8\xca\x51\x0b\x53\x18\xff\x73\xf1\x31\xd9\xff\x18\x45\xfb\xd3\x68\xff\xe1\xf8\xf7\x11\xab\x67\x86\x2e\xbd\x50\x22\xcf\xeb\x2a\x37\x91\x0d\x3d\x4d\xa7\xbc\xc3\xfb\xa6\xae\xb5\xd3\xfc\xee\xc9\x45\x82\x72\xe1\xc2\x3b\xee\xcf\x1d\xde\x3a\xc9\xfb\xf8\xb1\x41\x3c\x46\x4a\x64\x5f\x35\x7a\x46\xee\xab\x4e\x83\xc6\x68\x68\x6c\x86\xfe\x2d\xb5\xc2\xe7\xb3\xdf\xcd\xa5\xb6\x45\x78\xde\xfb\x29\x08\x4d\xbd\xb0\x1d\x3a\x43\x9a\xbd\xb4\x40\xaf\xfb\xbb\xb9\x1a\xf4\x45\xc9\x24\x14\xb3\x48\x5d\x74\x76\x66\x43\x53\xa1\xf2\x7c\xf8\xcf\x99\x48\xc3\x0e\x92\x9a\xd8\x36\x0d\x5d\x53\xe0\x3e\x7c\xb6\x53\x62\xdb\x24\xa4\x2d\x11\xd3\xf0\x70\x74\xcf\xbc\x95\xfa\xeb\x05\xd5\x2d\xf4\x37\x8f\x9d\x68\x62\x6d\x9b\x0e\x49\x34\x2d\xdc\x47\xe5\xfc\x37\x25\x1a\x5b\xd3\x59\xdd\xef\xe6\xef\x0a\xbd\x0b\x77\xf1\xb3\x7c\x56\x40\x9e\xc4\x71\xbd\xac\x73\x22\x30\xf7\x7c\x07\x65\xb2\x41\x62\x61\x5f\xdf\x79\xeb\x80\xb5\x69\x0c\xcd\xcb\xeb\xed\x57\x17\x9c\xd6\xbf\x7b\xa9\x6d\x9e\xfc\x76\x35\xec\x3d\xcd\x01\xbe\x70\x77\x22\xae\x2e\x13\x9b\xde\xf2\xa4\xfd\xa4\x48\x4c\xda\xac\x50\x1c\x55\x06\xea\x74\xe0\x6c\xe0\x4d\x73\xfb\xcf\x26\xdc\xbe\x17\x87\xea\xf1\x0e\xb7\xb1\x01\x9a\xd0\xb8\x4c\xe8\x8f\x67\xaf\x9e\x96\xcb\xaa\x2c\x68\x61\x68\xe9\x01\x38\xba\x6c\x8e\x4e\x1f\xf7\xe5\x99\x29\x80\x60\x38\xd4\x50\xe5\x4a\x72\x51\x98\x42\x20\xc8\xcc\xc9\x4e\xf6\x87\xb4\x37\x25\x9d\x62\xf5\x2c\x9c\x20\x33\xc8\x38\xa6\x3f\x2c\x28\xd3\x8e\x03\xd7\x20\xbd\x68\x86\xb9\xb4\x53\xfd\xc9\xbc\xe2\xb1\xee\x61\x7f\xf7\xd1\x8d\x6d\x4c\x6f\xeb\x31\x97\xd5\x8e\xa1\xa6\x47\x09\x16\xd2\x32\xc9\xb4\x98\x06\x51\x37\xb5\x7c\xdb\x78\x3d\xe6\x55\xc7\x62\x69\x59\x5a\x56\xca\x2a\x83\x61\xbf\x06\xce\x3c\xe5\xeb\x9b\x79\x4a\x2c\xd5\xd7\xe8\x9a\xae\xb8\x37\xd2\xb0\x2b\xa4\xd7\xcd\x33\xf7\x0e\xa4\x0b\x8d\xc2\x3e\x5c\xd3\xd5\xa5\xb1\x55\x35\x94\x0b\x59\xd6\xc9\x1d\x74\x7a\xb7\x1c\x0a\xf2\x18\xac\x8d\x68\x75\x87\xf0\x03\x15\x75\xa5\x83\x29\x31\x89\x53\x3a\x51\xaf\xf6\x35\xcc\xf6\xee\x1a\xf6\x3e\x74\xc7\x05\x11\x59\x3c\xfe\xc4\xc7\xea\xb0\x63\xff\x4b\x44\x6a\xfe\x73\xc4\xf7\x37\x53\xc9\x44\xef\xdf\x3d\xe8\x5c\x9b\xce\x8d\xc2\x84\x08\x22\x31\xd4\x92\xed\xfd\x0b\x07\xed\x26\x34\x7e\x35\xfb\xef\x1e\x50\xe0\x55\x4f\x53\xa7\xf2\xd9\x9f\xd1\x8a\xd1\x98\x08\xaa\xce\x73\x78\xa4\xf7\xb3\x79\x93\x8c\xd1\x58\x9c\x97\x6f\xb2\x85\x94\x91\xc4\x9e\xfa\xa1\x2f\xd7\x13\xff\x7b\x8e\x72\x48\xf4\x9c\x01\x42\x27\x67\x14\x85\x52\x91\xbb\x9b\x01\xaa\xbd\x1c\x78\xb4\x3a\x4f\x29\xa7\x20\x6e\x4b\x7d\x8d\x93\xf7\xe3\x8d\x09\x46\xbd\xe8\x0e\x25\x14\xc2\x28\x90\x24\xa1\x09\x94\x45\xbe\x42\x57\xe7\x8c\xc4\xd7\xb7\x84\x25\x78\x5f\x8f\x88\x6c\x96\xe5\x99\x58\xc9\x93\x5b\x99\x27\x4a\x46\x74\xd8\x3b\x72\x04\xa4\x97\x64\x1b\x1d\x05\x29\xe1\xe9\x3d\x96\x4d\xf3\x00\xa4\xd9\xfc\x94\x36\x4c\x5e\x30\xb2\x58\xaa\x08\x74\x8f\x7e\xec\x1b\x45\x45\x27\xd8\xca\x32\x03\x2f\xc0\x69\xc6\xfb\x40\xf5\x9e\x1c\x1e\x0d\x95\xd2\x4b\x58\x59\x61\xa0\x4a\xc2\x81\xaf\x30\xb3\x27\xc6\xb0\x77\xe8\x24\xd4\x75\x51\x6e\xac\x74\x26\xd5\xdf\xda\x59\x47\x1b\xe4\xc6\xaa\x8d\x3f\x37\xcd\x9e\x03\xea\x9f\x99\x6d\xbf\x6a\x6a\x7b\xa5\x3c\xcb\xa7\xf4\xd5\x61\xb3\x6f\x5a\x7d\xd8\xa3\x96\x65\x1b\x57\xdd\x95\xbb\x68\xba\xfb\x75\x5d\xd9\x52\x73\xe0\xfd\x93\x0a\x3b\x31\xbc\x12\xdd\x7f\x1c\x6e\x11\xb9\xe7\x9a\x77\xeb\xf8\x8b\x8c\x7e\x18\xca\xa5\x3b\x3c\xde\xfb\x3f\x01\x00\x00\xff\xff\x8b\x69\x4a\x7d\x2f\x6b\x00\x00") func webUiStaticJsGraphJsBytes() ([]byte, error) { return bindataRead( @@ -421,7 +443,7 @@ func webUiStaticJsGraphJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "web/ui/static/js/graph.js", size: 27425, mode: os.FileMode(420), modTime: time.Unix(1499264549, 0)} + info := bindataFileInfo{name: "web/ui/static/js/graph.js", size: 27439, mode: os.FileMode(420), modTime: time.Unix(1502369610, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -461,7 +483,27 @@ func webUiStaticJsProm_consoleJs() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "web/ui/static/js/prom_console.js", size: 22477, mode: os.FileMode(420), modTime: time.Unix(1499786501, 0)} + info := bindataFileInfo{name: "web/ui/static/js/prom_console.js", size: 22477, mode: os.FileMode(420), modTime: time.Unix(1499860266, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _webUiStaticJsTargetsJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x93\x4d\x8e\x9c\x30\x10\x85\xf7\x9c\xa2\x62\xf5\xc2\x96\x18\x2b\xfb\x16\xab\xac\x66\x9d\x03\x44\xc6\x14\x60\xe2\xb1\x5b\x76\xd1\x19\x29\xe2\xee\x91\x01\x37\x3f\x33\x69\x4d\x2d\x71\xbd\x72\xbd\xef\x99\x76\x74\x9a\x8c\x77\x40\xbe\xeb\x2c\x72\x5f\x0f\x25\x44\x52\x84\xe2\x6f\x01\x00\x70\x57\x01\x8c\xf6\x0e\x2a\xb8\xa4\x53\x21\x5b\xe3\x1a\xce\x0c\x13\xd7\xb9\xc1\xb4\xc0\x53\x83\xb4\xe8\x3a\xea\xa1\xaa\x2a\xf8\x0e\x02\x16\x79\xaa\x80\x34\x06\xb7\x74\x4f\xc5\x43\x34\xdf\x32\xb7\x53\x18\x71\x2f\x98\xc7\x05\x7c\xf3\x77\xfc\x61\x55\x8c\x9c\xa5\x2f\x2f\xba\xc7\x7b\xf0\xee\xa5\xf1\x7f\x1c\x13\x52\x35\xcd\x67\xa7\xe3\x2d\x6f\x36\x01\xda\x88\x5f\x9f\x9b\x94\xff\x9b\xba\xdc\x79\xf0\xb0\xe2\x70\xf8\x4e\x5c\xc8\x95\xdf\x82\xee\x5a\x4c\x45\xf1\x40\x6b\x9c\x21\x9e\xfd\x5d\x38\x93\x83\xaf\x7f\xf5\xa8\x1a\x0c\x4c\x48\x6d\x8d\xfe\xcd\x73\x33\xdf\x73\x48\xec\x07\x5f\xcf\xe8\xa9\x37\x31\xb3\x57\x69\x4f\xa2\xc0\x99\x69\x98\x28\x1f\xfd\xa9\xf0\xfd\xa6\x5c\x83\xe1\x35\x67\xb6\x17\x1a\xf9\xa9\xa9\x0d\x50\x0b\x7c\x3f\x20\x67\xfa\x2d\x65\xba\xdf\x2c\x95\xf5\x5a\xd9\x9f\xe4\x83\xea\x50\x46\xa4\x57\xc2\x37\x3e\xf8\xba\x84\x56\xd9\x88\x2b\xac\x5c\x2b\x9f\xb4\x4d\xb9\x04\xbe\x9d\x7f\x08\xea\xf9\xf8\x93\xfa\xc3\xf4\xf3\xf5\xd3\x92\x5a\x36\x7a\x4c\x00\x12\x4c\x54\xba\xdf\x22\x30\x25\xa4\x64\x4f\x41\x44\xb4\xa8\xc9\x87\xed\x47\xd8\x22\xb8\x1e\x08\x1e\x36\xef\xd6\xcd\xb3\x5c\xcc\x2f\x9e\x25\x0f\xec\x48\x74\xf5\x90\x13\xbb\xa9\x80\x8e\xe2\xe9\xb9\x3c\x73\x37\x15\xc5\x85\xa7\xc7\x26\xae\xff\x02\x00\x00\xff\xff\xc9\x97\xfe\x65\xd7\x03\x00\x00") + +func webUiStaticJsTargetsJsBytes() ([]byte, error) { + return bindataRead( + _webUiStaticJsTargetsJs, + "web/ui/static/js/targets.js", + ) +} + +func webUiStaticJsTargetsJs() (*asset, error) { + bytes, err := webUiStaticJsTargetsJsBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "web/ui/static/js/targets.js", size: 983, mode: os.FileMode(420), modTime: time.Unix(1502369610, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -950,12 +992,14 @@ var _bindata = map[string]func() (*asset, error){ "web/ui/static/css/graph.css": webUiStaticCssGraphCss, "web/ui/static/css/prom_console.css": webUiStaticCssProm_consoleCss, "web/ui/static/css/prometheus.css": webUiStaticCssPrometheusCss, + "web/ui/static/css/targets.css": webUiStaticCssTargetsCss, "web/ui/static/img/ajax-loader.gif": webUiStaticImgAjaxLoaderGif, "web/ui/static/img/favicon.ico": webUiStaticImgFaviconIco, "web/ui/static/js/alerts.js": webUiStaticJsAlertsJs, "web/ui/static/js/graph.js": webUiStaticJsGraphJs, "web/ui/static/js/graph_template.handlebar": webUiStaticJsGraph_templateHandlebar, "web/ui/static/js/prom_console.js": webUiStaticJsProm_consoleJs, + "web/ui/static/js/targets.js": webUiStaticJsTargetsJs, "web/ui/static/vendor/bootstrap-3.3.1/css/bootstrap-theme.min.css": webUiStaticVendorBootstrap331CssBootstrapThemeMinCss, "web/ui/static/vendor/bootstrap-3.3.1/css/bootstrap.min.css": webUiStaticVendorBootstrap331CssBootstrapMinCss, "web/ui/static/vendor/bootstrap-3.3.1/fonts/glyphicons-halflings-regular.eot": webUiStaticVendorBootstrap331FontsGlyphiconsHalflingsRegularEot, @@ -1028,6 +1072,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "graph.css": &bintree{webUiStaticCssGraphCss, map[string]*bintree{}}, "prom_console.css": &bintree{webUiStaticCssProm_consoleCss, map[string]*bintree{}}, "prometheus.css": &bintree{webUiStaticCssPrometheusCss, map[string]*bintree{}}, + "targets.css": &bintree{webUiStaticCssTargetsCss, map[string]*bintree{}}, }}, "img": &bintree{nil, map[string]*bintree{ "ajax-loader.gif": &bintree{webUiStaticImgAjaxLoaderGif, map[string]*bintree{}}, @@ -1038,6 +1083,7 @@ var _bintree = &bintree{nil, map[string]*bintree{ "graph.js": &bintree{webUiStaticJsGraphJs, map[string]*bintree{}}, "graph_template.handlebar": &bintree{webUiStaticJsGraph_templateHandlebar, map[string]*bintree{}}, "prom_console.js": &bintree{webUiStaticJsProm_consoleJs, map[string]*bintree{}}, + "targets.js": &bintree{webUiStaticJsTargetsJs, map[string]*bintree{}}, }}, "vendor": &bintree{nil, map[string]*bintree{ "bootstrap-3.3.1": &bintree{nil, map[string]*bintree{ diff --git a/web/ui/static/css/prometheus.css b/web/ui/static/css/prometheus.css index 34f2dc7e149e97e5c466f114750e9906a6a2528d..6444d5966f3613981be7838d9b799ed3d1f4aca4 100644 --- a/web/ui/static/css/prometheus.css +++ b/web/ui/static/css/prometheus.css @@ -4,12 +4,6 @@ body { padding-bottom: 20px; } -th.job_header { - font-size: 20px; - padding-top: 10px; - padding-bottom: 10px; -} - .state_indicator { padding: 0 4px 0 4px; } diff --git a/web/ui/static/css/targets.css b/web/ui/static/css/targets.css new file mode 100644 index 0000000000000000000000000000000000000000..2d562c656945c3082177919ab5dfbff558271171 --- /dev/null +++ b/web/ui/static/css/targets.css @@ -0,0 +1,11 @@ +tr.job_header { + font-size: 20px; + padding-top: 10px; + font-weight: bold; + padding-bottom: 10px; + cursor: pointer; +} + +tr.job_details > td{ + padding: 0 !important; +} \ No newline at end of file diff --git a/web/ui/static/js/graph.js b/web/ui/static/js/graph.js index 0e794058b42fa8c5da2719cb6299fe81509cde17..fdbcd373a71deb94e76453c061f2369143147f05 100644 --- a/web/ui/static/js/graph.js +++ b/web/ui/static/js/graph.js @@ -927,7 +927,7 @@ function redirectToMigratedURL() { }); }); var query = $.param(queryObject); - window.location = "/graph?" + query; + window.location = PATH_PREFIX + "/graph?" + query; } $(init); diff --git a/web/ui/static/js/targets.js b/web/ui/static/js/targets.js new file mode 100644 index 0000000000000000000000000000000000000000..98a08fd6f4c48e47d6a058a11946cfabb4998afc --- /dev/null +++ b/web/ui/static/js/targets.js @@ -0,0 +1,38 @@ +function toggle(obj, state){ + var icon = $(obj).find("i"); + if (icon.length === 0 ) { + return; + } + + if (state === true) { + icon.removeClass("icon-chevron-down").addClass("icon-chevron-up"); + } else { + icon.removeClass("icon-chevron-up").addClass("icon-chevron-down"); + } + + $(obj).next().toggle(state); +} + +function init() { + $(".job_header").click(function() { + var job = $(this).find("a").attr("id"), + expanderIcon = $(this).find("i.icon-chevron-down"); + + if (expanderIcon.length !== 0) { + localStorage.setItem(job, false); + toggle(this, true); + } else { + localStorage.setItem(job, true); + toggle(this, false); + } + }); + + $(".job_header a").each(function(i, obj) { + var selector = $(obj).attr("id"); + if (localStorage.getItem(selector) === "true") { + toggle($(this).parents(".job_header"), false); + } + }); +} + +$(init); \ No newline at end of file diff --git a/web/ui/templates/targets.html b/web/ui/templates/targets.html index 0ae96d91daa57c485275802bb1d4c3c415910c69..5b227bd0fb79ce7a7be895e1eaa294ad0d8600b2 100644 --- a/web/ui/templates/targets.html +++ b/web/ui/templates/targets.html @@ -1,58 +1,73 @@ -{{define "head"}}{{end}} +{{define "head"}} + + +{{end}} {{define "content"}}

Targets

- +
{{range $job, $pool := .TargetPools }} - - - - - - - - - - - - {{range $pool}} - - - + + + - {{end}} - {{end}}
{{$job}}
EndpointStateLabelsLast ScrapeError
- {{.URL.Scheme}}://{{.URL.Host}}{{.URL.Path}}
- {{range $label, $values := .URL.Query }} - {{range $i, $value := $values}} - {{$label}}="{{$value}}" - {{end}} - {{end}} -
- - {{.Health}} - + {{$healthy := numHealthy $pool}} + {{$total := len $pool}} +
+ {{$job}} ({{$healthy}}/{{$total}} up)
- - {{$labels := stripLabels .Labels.Map "job"}} - {{range $label, $value := $labels}} - {{$label}}="{{$value}}" - {{else}} - none + + + + + + + + + + + + {{range $pool}} + + + + + + + {{end}} - - - - +
EndpointStateLabelsLast ScrapeError
+ {{.URL.Scheme}}://{{.URL.Host}}{{.URL.Path}}
+ {{range $label, $values := .URL.Query }} + {{range $i, $value := $values}} + {{$label}}="{{$value}}" + {{end}} + {{end}} +
+ + {{.Health}} + + + + {{$labels := stripLabels .Labels.Map "job"}} + {{range $label, $value := $labels}} + {{$label}}="{{$value}}" + {{else}} + none + {{end}} + + + {{if .LastScrape.IsZero}}Never{{else}}{{since .LastScrape}} ago{{end}} + + {{if .LastError}} + {{.LastError}} + {{end}} +
- {{if .LastScrape.IsZero}}Never{{else}}{{since .LastScrape}} ago{{end}} - - {{if .LastError}} - {{.LastError}} - {{end}} +
-{{end}} +{{end}} \ No newline at end of file diff --git a/web/web.go b/web/web.go index 74a733d2de546027ce0fe974118d24533cee2fa8..466783e5a968e01419c6a72c6ae304268d05460e 100644 --- a/web/web.go +++ b/web/web.go @@ -28,6 +28,7 @@ import ( "sort" "strings" "sync" + "sync/atomic" "time" "google.golang.org/grpc" @@ -88,6 +89,8 @@ type Handler struct { externalLabels model.LabelSet mtx sync.RWMutex now func() model.Time + + ready uint32 // ready is uint32 rather than boolean to be able to use atomic functions. } // ApplyConfig updates the status state as the new config requires. @@ -162,7 +165,9 @@ func New(o *Options) *Handler { tsdb: o.Storage, storage: ptsdb.Adapter(o.Storage), notifier: o.Notifier, - now: model.Now, + + now: model.Now, + ready: 0, } h.apiV1 = api_v1.NewAPI(h.queryEngine, h.storage, h.targetManager, h.notifier) @@ -177,48 +182,49 @@ func New(o *Options) *Handler { instrh := prometheus.InstrumentHandler instrf := prometheus.InstrumentHandlerFunc + readyf := h.testReady router.Get("/", func(w http.ResponseWriter, r *http.Request) { router.Redirect(w, r, path.Join(o.ExternalURL.Path, "/graph"), http.StatusFound) }) - router.Get("/alerts", instrf("alerts", h.alerts)) - router.Get("/graph", instrf("graph", h.graph)) - router.Get("/status", instrf("status", h.status)) - router.Get("/flags", instrf("flags", h.flags)) - router.Get("/config", instrf("config", h.config)) - router.Get("/rules", instrf("rules", h.rules)) - router.Get("/targets", instrf("targets", h.targets)) - router.Get("/version", instrf("version", h.version)) + router.Get("/alerts", readyf(instrf("alerts", h.alerts))) + router.Get("/graph", readyf(instrf("graph", h.graph))) + router.Get("/status", readyf(instrf("status", h.status))) + router.Get("/flags", readyf(instrf("flags", h.flags))) + router.Get("/config", readyf(instrf("config", h.config))) + router.Get("/rules", readyf(instrf("rules", h.rules))) + router.Get("/targets", readyf(instrf("targets", h.targets))) + router.Get("/version", readyf(instrf("version", h.version))) - router.Get("/heap", instrf("heap", dumpHeap)) + router.Get("/heap", readyf(instrf("heap", dumpHeap))) router.Get("/metrics", prometheus.Handler().ServeHTTP) - router.Get("/federate", instrh("federate", httputil.CompressionHandler{ + router.Get("/federate", readyf(instrh("federate", httputil.CompressionHandler{ Handler: http.HandlerFunc(h.federation), - })) + }))) - router.Get("/consoles/*filepath", instrf("consoles", h.consoles)) + router.Get("/consoles/*filepath", readyf(instrf("consoles", h.consoles))) - router.Get("/static/*filepath", instrf("static", serveStaticAsset)) + router.Get("/static/*filepath", readyf(instrf("static", serveStaticAsset))) if o.UserAssetsPath != "" { - router.Get("/user/*filepath", instrf("user", route.FileServe(o.UserAssetsPath))) + router.Get("/user/*filepath", readyf(instrf("user", route.FileServe(o.UserAssetsPath)))) } if o.EnableLifecycle { router.Post("/-/quit", h.quit) router.Post("/-/reload", h.reload) } else { - router.Post("/-/quit", func(w http.ResponseWriter, _ *http.Request) { + router.Post("/-/quit", readyf(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusForbidden) w.Write([]byte("Lifecycle APIs are not enabled")) - }) - router.Post("/-/reload", func(w http.ResponseWriter, _ *http.Request) { + })) + router.Post("/-/reload", readyf(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusForbidden) w.Write([]byte("Lifecycle APIs are not enabled")) - }) + })) } router.Get("/-/quit", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusMethodNotAllowed) @@ -229,8 +235,17 @@ func New(o *Options) *Handler { w.Write([]byte("Only POST requests allowed")) }) - router.Get("/debug/*subpath", http.DefaultServeMux.ServeHTTP) - router.Post("/debug/*subpath", http.DefaultServeMux.ServeHTTP) + router.Get("/debug/*subpath", readyf(http.DefaultServeMux.ServeHTTP)) + router.Post("/debug/*subpath", readyf(http.DefaultServeMux.ServeHTTP)) + + router.Get("/-/healthy", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "Prometheus is Healthy.\n") + }) + router.Get("/-/ready", readyf(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + fmt.Fprintf(w, "Prometheus is Ready.\n") + })) return h } @@ -271,6 +286,32 @@ func serveStaticAsset(w http.ResponseWriter, req *http.Request) { http.ServeContent(w, req, info.Name(), info.ModTime(), bytes.NewReader(file)) } +// Ready sets Handler to be ready. +func (h *Handler) Ready() { + atomic.StoreUint32(&h.ready, 1) +} + +// Verifies whether the server is ready or not. +func (h *Handler) isReady() bool { + ready := atomic.LoadUint32(&h.ready) + if ready == 0 { + return false + } + return true +} + +// Checks if server is ready, calls f if it is, returns 503 if it is not. +func (h *Handler) testReady(f http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if h.isReady() { + f(w, r) + } else { + w.WriteHeader(http.StatusServiceUnavailable) + fmt.Fprintf(w, "Service Unavailable") + } + } +} + // Quit returns the receive-only quit channel. func (h *Handler) Quit() <-chan struct{} { return h.quitCh @@ -556,6 +597,16 @@ func tmplFuncs(consolesPath string, opts *Options) template_text.FuncMap { } return u }, + "numHealthy": func(pool []*retrieval.Target) int { + alive := len(pool) + for _, p := range pool { + if p.Health() != retrieval.HealthGood { + alive-- + } + } + + return alive + }, "healthToClass": func(th retrieval.TargetHealth) string { switch th { case retrieval.HealthUnknown: diff --git a/web/web_test.go b/web/web_test.go index 2971097f85e540f27d00096e45e513a18b65b329..b4424d8e22b35849d3e6c19e54590b2cee64be72 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -14,8 +14,11 @@ package web import ( + "context" + "net/http" "net/url" "testing" + "time" ) func TestGlobalURL(t *testing.T) { @@ -67,3 +70,80 @@ func TestGlobalURL(t *testing.T) { } } } + +func TestReadyAndHealthy(t *testing.T) { + opts := &Options{ + ListenAddress: ":9090", + ReadTimeout: 30 * time.Second, + MaxConnections: 512, + Context: nil, + Storage: nil, + QueryEngine: nil, + TargetManager: nil, + RuleManager: nil, + Notifier: nil, + RoutePrefix: "/", + MetricsPath: "/metrics/", + } + + opts.Flags = map[string]string{} + + webHandler := New(opts) + go webHandler.Run(context.Background()) + + // Give some time for the web goroutine to run since we need the server + // to be up before starting tests. + time.Sleep(5 * time.Second) + + resp, err := http.Get("http://localhost:9090/-/healthy") + if err != nil { + t.Fatalf("Unexpected HTTP error %s", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("Path /-/healthy with server unready test, Expected status 200 got: %s", resp.Status) + } + + resp, err = http.Get("http://localhost:9090/-/ready") + if err != nil { + t.Fatalf("Unexpected HTTP error %s", err) + } + if resp.StatusCode != http.StatusServiceUnavailable { + t.Fatalf("Path /-/ready with server unready test, Expected status 503 got: %s", resp.Status) + } + + resp, err = http.Get("http://localhost:9090/version") + if err != nil { + t.Fatalf("Unexpected HTTP error %s", err) + } + if resp.StatusCode != http.StatusServiceUnavailable { + t.Fatalf("Path /version with server unready test, Expected status 503 got: %s", resp.Status) + } + + // Set to ready. + webHandler.Ready() + + resp, err = http.Get("http://localhost:9090/-/healthy") + if err != nil { + t.Fatalf("Unexpected HTTP error %s", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("Path /-/healthy with server ready test, Expected status 200 got: %s", resp.Status) + } + + resp, err = http.Get("http://localhost:9090/-/ready") + if err != nil { + t.Fatalf("Unexpected HTTP error %s", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("Path /-/ready with server ready test, Expected status 200 got: %s", resp.Status) + } + + resp, err = http.Get("http://localhost:9090/version") + if err != nil { + t.Fatalf("Unexpected HTTP error %s", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("Path /version with server ready test, Expected status 200 got: %s", resp.Status) + } + +}