Enabling "service" watch handler to accept a slice of tags
Originally from PR #5347
This commit is contained in:
parent
26ef7943ed
commit
32e821eda2
|
@ -134,15 +134,17 @@ func serviceWatch(params map[string]interface{}) (WatcherFunc, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var service, tag string
|
var (
|
||||||
|
service string
|
||||||
|
tags []string
|
||||||
|
)
|
||||||
if err := assignValue(params, "service", &service); err != nil {
|
if err := assignValue(params, "service", &service); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if service == "" {
|
if service == "" {
|
||||||
return nil, fmt.Errorf("Must specify a single service to watch")
|
return nil, fmt.Errorf("Must specify a single service to watch")
|
||||||
}
|
}
|
||||||
|
if err := assignValueStringSlice(params, "tag", &tags); err != nil {
|
||||||
if err := assignValue(params, "tag", &tag); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +157,7 @@ func serviceWatch(params map[string]interface{}) (WatcherFunc, error) {
|
||||||
health := p.client.Health()
|
health := p.client.Health()
|
||||||
opts := makeQueryOptionsWithContext(p, stale)
|
opts := makeQueryOptionsWithContext(p, stale)
|
||||||
defer p.cancelFunc()
|
defer p.cancelFunc()
|
||||||
nodes, meta, err := health.Service(service, tag, passingOnly, &opts)
|
nodes, meta, err := health.ServiceMultipleTags(service, tags, passingOnly, &opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -428,6 +428,101 @@ func TestServiceWatch(t *testing.T) {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServiceMultipleTagsWatch(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
c, s := makeClient(t)
|
||||||
|
defer s.Stop()
|
||||||
|
|
||||||
|
invoke := makeInvokeCh()
|
||||||
|
plan := mustParse(t, `{"type":"service", "service":"foo", "tag":["bar","buzz"], "passingonly":true}`)
|
||||||
|
plan.Handler = func(idx uint64, raw interface{}) {
|
||||||
|
if raw == nil {
|
||||||
|
return // ignore
|
||||||
|
}
|
||||||
|
v, ok := raw.([]*api.ServiceEntry)
|
||||||
|
if !ok || len(v) == 0 {
|
||||||
|
return // ignore
|
||||||
|
}
|
||||||
|
if v[0].Service.ID != "foobarbuzzbiff" {
|
||||||
|
invoke <- errBadContent
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(v[0].Service.Tags) == 0 {
|
||||||
|
invoke <- errBadContent
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// test for our tags
|
||||||
|
barFound := false
|
||||||
|
buzzFound := false
|
||||||
|
for _, t := range v[0].Service.Tags {
|
||||||
|
if t == "bar" {
|
||||||
|
barFound = true
|
||||||
|
} else if t == "buzz" {
|
||||||
|
buzzFound = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !barFound || !buzzFound {
|
||||||
|
invoke <- errBadContent
|
||||||
|
return
|
||||||
|
}
|
||||||
|
invoke <- nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
agent := c.Agent()
|
||||||
|
|
||||||
|
// we do not want to find this one.
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
reg := &api.AgentServiceRegistration{
|
||||||
|
ID: "foobarbiff",
|
||||||
|
Name: "foo",
|
||||||
|
Tags: []string{"bar", "biff"},
|
||||||
|
}
|
||||||
|
if err := agent.ServiceRegister(reg); err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do not want to find this one.
|
||||||
|
reg = &api.AgentServiceRegistration{
|
||||||
|
ID: "foobuzzbiff",
|
||||||
|
Name: "foo",
|
||||||
|
Tags: []string{"buzz", "biff"},
|
||||||
|
}
|
||||||
|
if err := agent.ServiceRegister(reg); err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we want to find this one
|
||||||
|
reg = &api.AgentServiceRegistration{
|
||||||
|
ID: "foobarbuzzbiff",
|
||||||
|
Name: "foo",
|
||||||
|
Tags: []string{"bar", "buzz", "biff"},
|
||||||
|
}
|
||||||
|
if err := agent.ServiceRegister(reg); err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if err := plan.Run(s.HTTPAddr); err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := <-invoke; err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
plan.Stop()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
func TestChecksWatch_State(t *testing.T) {
|
func TestChecksWatch_State(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
|
|
|
@ -233,6 +233,37 @@ func assignValueBool(params map[string]interface{}, name string, out *bool) erro
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assignValueStringSlice is used to extract a value ensuring it is either a string or a slice of strings
|
||||||
|
func assignValueStringSlice(params map[string]interface{}, name string, out *[]string) error {
|
||||||
|
if raw, ok := params[name]; ok {
|
||||||
|
var tmp []string
|
||||||
|
switch raw.(type) {
|
||||||
|
case string:
|
||||||
|
tmp = make([]string, 1, 1)
|
||||||
|
tmp[0] = raw.(string)
|
||||||
|
case []string:
|
||||||
|
l := len(raw.([]string))
|
||||||
|
tmp = make([]string, l, l)
|
||||||
|
copy(tmp, raw.([]string))
|
||||||
|
case []interface{}:
|
||||||
|
l := len(raw.([]interface{}))
|
||||||
|
tmp = make([]string, l, l)
|
||||||
|
for i, v := range raw.([]interface{}) {
|
||||||
|
if s, ok := v.(string); ok {
|
||||||
|
tmp[i] = s
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Index %d of %s expected to be string", i, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Expecting %s to be a string or []string", name)
|
||||||
|
}
|
||||||
|
*out = tmp
|
||||||
|
delete(params, name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the 'http_handler_config' parameters
|
// Parse the 'http_handler_config' parameters
|
||||||
func parseHttpHandlerConfig(configParams interface{}) (*HttpHandlerConfig, error) {
|
func parseHttpHandlerConfig(configParams interface{}) (*HttpHandlerConfig, error) {
|
||||||
var config HttpHandlerConfig
|
var config HttpHandlerConfig
|
||||||
|
|
|
@ -36,7 +36,7 @@ type cmd struct {
|
||||||
key string
|
key string
|
||||||
prefix string
|
prefix string
|
||||||
service string
|
service string
|
||||||
tag string
|
tag []string
|
||||||
passingOnly string
|
passingOnly string
|
||||||
state string
|
state string
|
||||||
name string
|
name string
|
||||||
|
@ -55,8 +55,8 @@ func (c *cmd) init() {
|
||||||
c.flags.StringVar(&c.service, "service", "",
|
c.flags.StringVar(&c.service, "service", "",
|
||||||
"Specifies the service to watch. Required for 'service' type, "+
|
"Specifies the service to watch. Required for 'service' type, "+
|
||||||
"optional for 'checks' type.")
|
"optional for 'checks' type.")
|
||||||
c.flags.StringVar(&c.tag, "tag", "",
|
c.flags.Var((*flags.AppendSliceValue)(&c.tag), "tag", "Specifies the service tag(s) to filter on. "+
|
||||||
"Specifies the service tag to filter on. Optional for 'service' type.")
|
"Optional for 'service' type. May be specified multiple times")
|
||||||
c.flags.StringVar(&c.passingOnly, "passingonly", "",
|
c.flags.StringVar(&c.passingOnly, "passingonly", "",
|
||||||
"Specifies if only hosts passing all checks are displayed. "+
|
"Specifies if only hosts passing all checks are displayed. "+
|
||||||
"Optional for 'service' type, must be one of `[true|false]`. Defaults false.")
|
"Optional for 'service' type, must be one of `[true|false]`. Defaults false.")
|
||||||
|
@ -115,7 +115,7 @@ func (c *cmd) Run(args []string) int {
|
||||||
if c.service != "" {
|
if c.service != "" {
|
||||||
params["service"] = c.service
|
params["service"] = c.service
|
||||||
}
|
}
|
||||||
if c.tag != "" {
|
if len(c.tag) > 0 {
|
||||||
params["tag"] = c.tag
|
params["tag"] = c.tag
|
||||||
}
|
}
|
||||||
if c.http.Stale() {
|
if c.http.Stale() {
|
||||||
|
|
|
@ -266,26 +266,45 @@ An example of the output of this command:
|
||||||
|
|
||||||
The "service" watch type is used to monitor the providers
|
The "service" watch type is used to monitor the providers
|
||||||
of a single service. It requires the "service" parameter
|
of a single service. It requires the "service" parameter
|
||||||
and optionally takes the parameters "tag" and "passingonly".
|
and optionally takes the parameters "tag" and
|
||||||
The "tag" parameter will filter by tag, and "passingonly" is
|
"passingonly". The "tag" parameter will filter by one or more tags.
|
||||||
a boolean that will filter to only the instances passing all
|
It may be either a single string value or a slice of strings.
|
||||||
health checks.
|
The "passingonly" is a boolean that will filter to only the
|
||||||
|
instances passing all health checks.
|
||||||
|
|
||||||
This maps to the `/v1/health/service` API internally.
|
This maps to the `/v1/health/service` API internally.
|
||||||
|
|
||||||
Here is an example configuration:
|
Here is an example configuration with a single tag:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
"type": "service",
|
"type": "service",
|
||||||
"service": "redis",
|
"service": "redis",
|
||||||
"args": ["/usr/bin/my-service-handler.sh", "-redis"]
|
"args": ["/usr/bin/my-service-handler.sh", "-redis"],
|
||||||
|
"tag": "bar"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is an example configuration with multiple tags:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
"type": "service",
|
||||||
|
"service": "redis",
|
||||||
|
"args": ["/usr/bin/my-service-handler.sh", "-redis"],
|
||||||
|
"tag": ["bar", "foo"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, using the watch command:
|
Or, using the watch command:
|
||||||
|
|
||||||
$ consul watch -type=service -service=redis /usr/bin/my-service-handler.sh
|
Single tag:
|
||||||
|
|
||||||
|
$ consul watch -type=service -service=redis -tag=bar /usr/bin/my-service-handler.sh
|
||||||
|
|
||||||
|
Multiple tag:
|
||||||
|
|
||||||
|
$ consul watch -type=service -service=redis -tag=bar -tag=foo /usr/bin/my-service-handler.sh
|
||||||
|
|
||||||
An example of the output of this command:
|
An example of the output of this command:
|
||||||
|
|
||||||
|
@ -299,7 +318,10 @@ An example of the output of this command:
|
||||||
"Service": {
|
"Service": {
|
||||||
"ID": "redis",
|
"ID": "redis",
|
||||||
"Service": "redis",
|
"Service": "redis",
|
||||||
"Tags": null,
|
"Tags": [
|
||||||
|
"bar",
|
||||||
|
"foo"
|
||||||
|
],
|
||||||
"Port": 8000
|
"Port": 8000
|
||||||
},
|
},
|
||||||
"Checks": [
|
"Checks": [
|
||||||
|
|
Loading…
Reference in New Issue