From 965fcf9d6b7e748298476ffd5b4793ac63f7968d Mon Sep 17 00:00:00 2001 From: Evan Gilman Date: Tue, 10 May 2016 00:09:15 -0700 Subject: [PATCH 1/3] Enable Stale mode for watchers Solves https://github.com/hashicorp/consul/issues/917 by giving consul watch a `-stale` flag --- command/watch.go | 13 ++++++++++++- watch/funcs.go | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/command/watch.go b/command/watch.go index d5caf325a4..69939f72cf 100644 --- a/command/watch.go +++ b/command/watch.go @@ -47,6 +47,8 @@ Watch Specification: -prefix=val Specifies the key prefix to watch. Only for 'keyprefix' type. -service=val Specifies the service to watch. Required for 'service' type, optional for 'checks' type. + -stale=[true|false] Specefies if watch data is permitted to be stale. Defaults + false. -state=val Specifies the states to watch. Optional for 'checks' type. -tag=val Specifies the service tag to filter on. Optional for 'service' type. @@ -57,7 +59,7 @@ Watch Specification: } func (c *WatchCommand) Run(args []string) int { - var watchType, datacenter, token, key, prefix, service, tag, passingOnly, state, name string + var watchType, datacenter, token, key, prefix, service, tag, passingOnly, stale, state, name string cmdFlags := flag.NewFlagSet("watch", flag.ContinueOnError) cmdFlags.Usage = func() { c.Ui.Output(c.Help()) } cmdFlags.StringVar(&watchType, "type", "", "") @@ -68,6 +70,7 @@ func (c *WatchCommand) Run(args []string) int { cmdFlags.StringVar(&service, "service", "", "") cmdFlags.StringVar(&tag, "tag", "", "") cmdFlags.StringVar(&passingOnly, "passingonly", "", "") + cmdFlags.StringVar(&stale, "stale", "", "") cmdFlags.StringVar(&state, "state", "", "") cmdFlags.StringVar(&name, "name", "", "") httpAddr := HTTPAddrFlag(cmdFlags) @@ -109,6 +112,14 @@ func (c *WatchCommand) Run(args []string) int { if tag != "" { params["tag"] = tag } + if stale != "" { + b, err := strconv.ParseBool(stale) + if err != nil { + c.Ui.Error(fmt.Sprintf("Failed to parse stale flag: %s", err)) + return 1 + } + params["stale"] = b + } if state != "" { params["state"] = state } diff --git a/watch/funcs.go b/watch/funcs.go index 9308e7c633..874aba5558 100644 --- a/watch/funcs.go +++ b/watch/funcs.go @@ -35,9 +35,14 @@ func keyWatch(params map[string]interface{}) (WatchFunc, error) { return nil, fmt.Errorf("Must specify a single key to watch") } + stale := false + if err := assignValueBool(params, "stale", &stale); err != nil { + return nil, err + } + fn := func(p *WatchPlan) (uint64, interface{}, error) { kv := p.client.KV() - opts := consulapi.QueryOptions{WaitIndex: p.lastIndex} + opts := consulapi.QueryOptions{AllowStale: stale, WaitIndex: p.lastIndex} pair, meta, err := kv.Get(key, &opts) if err != nil { return 0, nil, err @@ -60,9 +65,14 @@ func keyPrefixWatch(params map[string]interface{}) (WatchFunc, error) { return nil, fmt.Errorf("Must specify a single prefix to watch") } + stale := false + if err := assignValueBool(params, "stale", &stale); err != nil { + return nil, err + } + fn := func(p *WatchPlan) (uint64, interface{}, error) { kv := p.client.KV() - opts := consulapi.QueryOptions{WaitIndex: p.lastIndex} + opts := consulapi.QueryOptions{AllowStale: stale, WaitIndex: p.lastIndex} pairs, meta, err := kv.List(prefix, &opts) if err != nil { return 0, nil, err @@ -74,9 +84,14 @@ func keyPrefixWatch(params map[string]interface{}) (WatchFunc, error) { // servicesWatch is used to watch the list of available services func servicesWatch(params map[string]interface{}) (WatchFunc, error) { + stale := false + if err := assignValueBool(params, "stale", &stale); err != nil { + return nil, err + } + fn := func(p *WatchPlan) (uint64, interface{}, error) { catalog := p.client.Catalog() - opts := consulapi.QueryOptions{WaitIndex: p.lastIndex} + opts := consulapi.QueryOptions{AllowStale: stale, WaitIndex: p.lastIndex} services, meta, err := catalog.Services(&opts) if err != nil { return 0, nil, err @@ -88,9 +103,14 @@ func servicesWatch(params map[string]interface{}) (WatchFunc, error) { // nodesWatch is used to watch the list of available nodes func nodesWatch(params map[string]interface{}) (WatchFunc, error) { + stale := false + if err := assignValueBool(params, "stale", &stale); err != nil { + return nil, err + } + fn := func(p *WatchPlan) (uint64, interface{}, error) { catalog := p.client.Catalog() - opts := consulapi.QueryOptions{WaitIndex: p.lastIndex} + opts := consulapi.QueryOptions{AllowStale: stale, WaitIndex: p.lastIndex} nodes, meta, err := catalog.Nodes(&opts) if err != nil { return 0, nil, err @@ -119,9 +139,14 @@ func serviceWatch(params map[string]interface{}) (WatchFunc, error) { return nil, err } + stale := false + if err := assignValueBool(params, "stale", &stale); err != nil { + return nil, err + } + fn := func(p *WatchPlan) (uint64, interface{}, error) { health := p.client.Health() - opts := consulapi.QueryOptions{WaitIndex: p.lastIndex} + opts := consulapi.QueryOptions{AllowStale: stale, WaitIndex: p.lastIndex} nodes, meta, err := health.Service(service, tag, passingOnly, &opts) if err != nil { return 0, nil, err From 2b65e7da057d07050b27547732783656b090d58f Mon Sep 17 00:00:00 2001 From: James Phillips Date: Wed, 10 Aug 2016 15:46:28 -0700 Subject: [PATCH 2/3] Fixes a typo and adds stale documentation to website. --- command/watch.go | 4 ++-- website/source/docs/commands/watch.html.markdown | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/command/watch.go b/command/watch.go index 69939f72cf..a6ec677a19 100644 --- a/command/watch.go +++ b/command/watch.go @@ -37,6 +37,8 @@ Options: -http-addr=127.0.0.1:8500 HTTP address of the Consul agent. -datacenter="" Datacenter to query. Defaults to that of agent. -token="" ACL token to use. Defaults to that of agent. + -stale=[true|false] Specifies if watch data is permitted to be stale. + Defaults to false. Watch Specification: @@ -47,8 +49,6 @@ Watch Specification: -prefix=val Specifies the key prefix to watch. Only for 'keyprefix' type. -service=val Specifies the service to watch. Required for 'service' type, optional for 'checks' type. - -stale=[true|false] Specefies if watch data is permitted to be stale. Defaults - false. -state=val Specifies the states to watch. Optional for 'checks' type. -tag=val Specifies the service tag to filter on. Optional for 'service' type. diff --git a/website/source/docs/commands/watch.html.markdown b/website/source/docs/commands/watch.html.markdown index 2406b5253a..54c86508da 100644 --- a/website/source/docs/commands/watch.html.markdown +++ b/website/source/docs/commands/watch.html.markdown @@ -37,12 +37,15 @@ The list of available flags are: * `-token` - ACL token to use. Defaults to that of agent. +* `-stale=[true|false]` - Specifies if watch data is permitted to be stale. Defaults + to false. + * `-key` - Key to watch. Only for `key` type. * `-name`- Event name to watch. Only for `event` type. -* `-passingonly=[true|false]` - Should only passing entries be returned. Default false. - only for `service` type. +* `-passingonly=[true|false]` - Should only passing entries be returned. Defaults to + false and only applies for `service` type. * `-prefix` - Key prefix to watch. Only for `keyprefix` type. From 888f27de2bbd345b5e905bf3c9c3e9a73c4c0431 Mon Sep 17 00:00:00 2001 From: James Phillips Date: Wed, 10 Aug 2016 15:49:51 -0700 Subject: [PATCH 3/3] Moves stale code to same spot and adds it everywhere that supports it. --- watch/funcs.go | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/watch/funcs.go b/watch/funcs.go index 874aba5558..2d267c1635 100644 --- a/watch/funcs.go +++ b/watch/funcs.go @@ -27,6 +27,11 @@ func init() { // keyWatch is used to return a key watching function func keyWatch(params map[string]interface{}) (WatchFunc, error) { + stale := false + if err := assignValueBool(params, "stale", &stale); err != nil { + return nil, err + } + var key string if err := assignValue(params, "key", &key); err != nil { return nil, err @@ -34,12 +39,6 @@ func keyWatch(params map[string]interface{}) (WatchFunc, error) { if key == "" { return nil, fmt.Errorf("Must specify a single key to watch") } - - stale := false - if err := assignValueBool(params, "stale", &stale); err != nil { - return nil, err - } - fn := func(p *WatchPlan) (uint64, interface{}, error) { kv := p.client.KV() opts := consulapi.QueryOptions{AllowStale: stale, WaitIndex: p.lastIndex} @@ -57,6 +56,11 @@ func keyWatch(params map[string]interface{}) (WatchFunc, error) { // keyPrefixWatch is used to return a key prefix watching function func keyPrefixWatch(params map[string]interface{}) (WatchFunc, error) { + stale := false + if err := assignValueBool(params, "stale", &stale); err != nil { + return nil, err + } + var prefix string if err := assignValue(params, "prefix", &prefix); err != nil { return nil, err @@ -64,12 +68,6 @@ func keyPrefixWatch(params map[string]interface{}) (WatchFunc, error) { if prefix == "" { return nil, fmt.Errorf("Must specify a single prefix to watch") } - - stale := false - if err := assignValueBool(params, "stale", &stale); err != nil { - return nil, err - } - fn := func(p *WatchPlan) (uint64, interface{}, error) { kv := p.client.KV() opts := consulapi.QueryOptions{AllowStale: stale, WaitIndex: p.lastIndex} @@ -122,6 +120,11 @@ func nodesWatch(params map[string]interface{}) (WatchFunc, error) { // serviceWatch is used to watch a specific service for changes func serviceWatch(params map[string]interface{}) (WatchFunc, error) { + stale := false + if err := assignValueBool(params, "stale", &stale); err != nil { + return nil, err + } + var service, tag string if err := assignValue(params, "service", &service); err != nil { return nil, err @@ -139,11 +142,6 @@ func serviceWatch(params map[string]interface{}) (WatchFunc, error) { return nil, err } - stale := false - if err := assignValueBool(params, "stale", &stale); err != nil { - return nil, err - } - fn := func(p *WatchPlan) (uint64, interface{}, error) { health := p.client.Health() opts := consulapi.QueryOptions{AllowStale: stale, WaitIndex: p.lastIndex} @@ -158,6 +156,11 @@ func serviceWatch(params map[string]interface{}) (WatchFunc, error) { // checksWatch is used to watch a specific checks in a given state func checksWatch(params map[string]interface{}) (WatchFunc, error) { + stale := false + if err := assignValueBool(params, "stale", &stale); err != nil { + return nil, err + } + var service, state string if err := assignValue(params, "service", &service); err != nil { return nil, err @@ -174,7 +177,7 @@ func checksWatch(params map[string]interface{}) (WatchFunc, error) { fn := func(p *WatchPlan) (uint64, interface{}, error) { health := p.client.Health() - opts := consulapi.QueryOptions{WaitIndex: p.lastIndex} + opts := consulapi.QueryOptions{AllowStale: stale, WaitIndex: p.lastIndex} var checks []*consulapi.HealthCheck var meta *consulapi.QueryMeta var err error @@ -193,6 +196,8 @@ func checksWatch(params map[string]interface{}) (WatchFunc, error) { // eventWatch is used to watch for events, optionally filtering on name func eventWatch(params map[string]interface{}) (WatchFunc, error) { + // The stale setting doesn't apply to events. + var name string if err := assignValue(params, "name", &name); err != nil { return nil, err