Adds option to prepared queries to remove empty tags. (#3330)

This commit is contained in:
James Phillips 2017-07-26 22:46:43 -07:00 committed by GitHub
parent f4daf8a84c
commit 6b51744ddf
4 changed files with 110 additions and 4 deletions

View File

@ -29,6 +29,10 @@ type CompiledTemplate struct {
// re is the compiled regexp, if they supplied one (this can be nil).
re *regexp.Regexp
// removeEmptyTags will cause the service tags to be stripped of any
// empty strings after interpolation.
removeEmptyTags bool
}
// Compile validates a prepared query template and returns an opaque compiled
@ -41,7 +45,8 @@ func Compile(query *structs.PreparedQuery) (*CompiledTemplate, error) {
// Start compile.
ct := &CompiledTemplate{
trees: make(map[string]ast.Node),
trees: make(map[string]ast.Node),
removeEmptyTags: query.Template.RemoveEmptyTags,
}
// Make a copy of the query to use as the basis for rendering later.
@ -181,5 +186,15 @@ func (ct *CompiledTemplate) Render(name string) (*structs.PreparedQuery, error)
return nil, err
}
if ct.removeEmptyTags {
tags := make([]string, 0, len(query.Service.Tags))
for _, tag := range query.Service.Tags {
if tag != "" {
tags = append(tags, tag)
}
}
query.Service.Tags = tags
}
return query, nil
}

View File

@ -15,8 +15,9 @@ var (
bigBench = &structs.PreparedQuery{
Name: "hello",
Template: structs.QueryTemplateOptions{
Type: structs.QueryTemplateTypeNamePrefixMatch,
Regexp: "^hello-(.*)-(.*)$",
Type: structs.QueryTemplateTypeNamePrefixMatch,
Regexp: "^hello-(.*)-(.*)$",
RemoveEmptyTags: true,
},
Service: structs.ServiceQuery{
Service: "${name.full}",
@ -296,4 +297,84 @@ func TestTemplate_Render(t *testing.T) {
t.Fatalf("bad: %#v", actual)
}
}
// Try all the variables and functions, removing empty tags.
query = &structs.PreparedQuery{
Name: "hello-",
Template: structs.QueryTemplateOptions{
Type: structs.QueryTemplateTypeNamePrefixMatch,
Regexp: "^(.*?)-(.*?)-(.*)$",
RemoveEmptyTags: true,
},
Service: structs.ServiceQuery{
Service: "${name.prefix} xxx ${name.full} xxx ${name.suffix}",
Tags: []string{
"${match(-1)}",
"${match(0)}",
"${match(1)}",
"${match(2)}",
"${match(3)}",
"${match(4)}",
"${40 + 2}",
},
},
}
ct, err = Compile(query)
if err != nil {
t.Fatalf("err: %v", err)
}
// Run a case that matches the regexp, removing empty tags.
{
actual, err := ct.Render("hello-foo-bar-none")
if err != nil {
t.Fatalf("err: %v", err)
}
expected := &structs.PreparedQuery{
Name: "hello-",
Template: structs.QueryTemplateOptions{
Type: structs.QueryTemplateTypeNamePrefixMatch,
Regexp: "^(.*?)-(.*?)-(.*)$",
RemoveEmptyTags: true,
},
Service: structs.ServiceQuery{
Service: "hello- xxx hello-foo-bar-none xxx foo-bar-none",
Tags: []string{
"hello-foo-bar-none",
"hello",
"foo",
"bar-none",
"42",
},
},
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad:\n%#v\nexpected:\n%#v\n", actual, expected)
}
}
// Run a case that doesn't match the regexp, removing empty tags.
{
actual, err := ct.Render("hello-nope")
if err != nil {
t.Fatalf("err: %v", err)
}
expected := &structs.PreparedQuery{
Name: "hello-",
Template: structs.QueryTemplateOptions{
Type: structs.QueryTemplateTypeNamePrefixMatch,
Regexp: "^(.*?)-(.*?)-(.*)$",
RemoveEmptyTags: true,
},
Service: structs.ServiceQuery{
Service: "hello- xxx hello-nope xxx nope",
Tags: []string{
"42",
},
},
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}
}

View File

@ -68,6 +68,9 @@ type QueryTemplateOptions struct {
// used to extract parts of the name and choose a service name, set
// tags, etc.
Regexp string
// RemoveEmptyTags, if true, removes empty tags from matched tag list
RemoveEmptyTags bool
}
// PreparedQuery defines a complete prepared query, and is the structure we

View File

@ -32,7 +32,8 @@ Here is an example prepared query template:
{
"Template": {
"Type": "name_prefix_match",
"Regexp": "^geo-db-(.*?)-([^\\-]+?)$"
"Regexp": "^geo-db-(.*?)-([^\\-]+?)$",
"RemoveEmptyTags": false
}
}
```
@ -55,6 +56,12 @@ static query. It has two fields:
[RE2](https://github.com/google/re2/wiki/Syntax) reference for syntax of this
regular expression.
- `RemoveEmptyTags` is optional, and if set to true, will cause the `Tags` list
inside the `Service` structure to be stripped of any empty strings. This defaults
to false, meaning that empty strings will remain in the list. This is useful
when interpolating into tags in a way where the tag is optional, and where
searching for an empty tag would yield no results from the query.
All other fields of the query have the same meanings as for a static query,
except that several interpolation variables are available to dynamically
populate the query before it is executed. All of the string fields inside the