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
|
||||
}
|
||||
|
||||
var service, tag string
|
||||
var (
|
||||
service string
|
||||
tags []string
|
||||
)
|
||||
if err := assignValue(params, "service", &service); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if service == "" {
|
||||
return nil, fmt.Errorf("Must specify a single service to watch")
|
||||
}
|
||||
|
||||
if err := assignValue(params, "tag", &tag); err != nil {
|
||||
if err := assignValueStringSlice(params, "tag", &tags); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -155,7 +157,7 @@ func serviceWatch(params map[string]interface{}) (WatcherFunc, error) {
|
|||
health := p.client.Health()
|
||||
opts := makeQueryOptionsWithContext(p, stale)
|
||||
defer p.cancelFunc()
|
||||
nodes, meta, err := health.Service(service, tag, passingOnly, &opts)
|
||||
nodes, meta, err := health.ServiceMultipleTags(service, tags, passingOnly, &opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -428,6 +428,101 @@ func TestServiceWatch(t *testing.T) {
|
|||
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) {
|
||||
t.Parallel()
|
||||
c, s := makeClient(t)
|
||||
|
|
|
@ -233,6 +233,37 @@ func assignValueBool(params map[string]interface{}, name string, out *bool) erro
|
|||
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
|
||||
func parseHttpHandlerConfig(configParams interface{}) (*HttpHandlerConfig, error) {
|
||||
var config HttpHandlerConfig
|
||||
|
|
|
@ -36,7 +36,7 @@ type cmd struct {
|
|||
key string
|
||||
prefix string
|
||||
service string
|
||||
tag string
|
||||
tag []string
|
||||
passingOnly string
|
||||
state string
|
||||
name string
|
||||
|
@ -55,8 +55,8 @@ func (c *cmd) init() {
|
|||
c.flags.StringVar(&c.service, "service", "",
|
||||
"Specifies the service to watch. Required for 'service' type, "+
|
||||
"optional for 'checks' type.")
|
||||
c.flags.StringVar(&c.tag, "tag", "",
|
||||
"Specifies the service tag to filter on. Optional for 'service' type.")
|
||||
c.flags.Var((*flags.AppendSliceValue)(&c.tag), "tag", "Specifies the service tag(s) to filter on. "+
|
||||
"Optional for 'service' type. May be specified multiple times")
|
||||
c.flags.StringVar(&c.passingOnly, "passingonly", "",
|
||||
"Specifies if only hosts passing all checks are displayed. "+
|
||||
"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 != "" {
|
||||
params["service"] = c.service
|
||||
}
|
||||
if c.tag != "" {
|
||||
if len(c.tag) > 0 {
|
||||
params["tag"] = c.tag
|
||||
}
|
||||
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
|
||||
of a single service. It requires the "service" parameter
|
||||
and optionally takes the parameters "tag" and "passingonly".
|
||||
The "tag" parameter will filter by tag, and "passingonly" is
|
||||
a boolean that will filter to only the instances passing all
|
||||
health checks.
|
||||
and optionally takes the parameters "tag" and
|
||||
"passingonly". The "tag" parameter will filter by one or more tags.
|
||||
It may be either a single string value or a slice of strings.
|
||||
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.
|
||||
|
||||
Here is an example configuration:
|
||||
Here is an example configuration with a single tag:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "service",
|
||||
"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:
|
||||
|
||||
$ 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:
|
||||
|
||||
|
@ -299,7 +318,10 @@ An example of the output of this command:
|
|||
"Service": {
|
||||
"ID": "redis",
|
||||
"Service": "redis",
|
||||
"Tags": null,
|
||||
"Tags": [
|
||||
"bar",
|
||||
"foo"
|
||||
],
|
||||
"Port": 8000
|
||||
},
|
||||
"Checks": [
|
||||
|
|
Loading…
Reference in New Issue