diff --git a/consul/prepared_query/template.go b/consul/prepared_query/template.go index d175db5c75..2da712b9cb 100644 --- a/consul/prepared_query/template.go +++ b/consul/prepared_query/template.go @@ -31,6 +31,57 @@ type CompiledTemplate struct { re *regexp.Regexp } +// Compile validates a prepared query template and returns an opaque compiled +// object that can be used later to render the template. +func Compile(query *structs.PreparedQuery) (*CompiledTemplate, error) { + // Make sure it's a type we understand. + if query.Template.Type != structs.QueryTemplateTypeNamePrefixMatch { + return nil, fmt.Errorf("Bad Template.Type '%s'", query.Template.Type) + } + + // Start compile. + ct := &CompiledTemplate{ + trees: make(map[string]ast.Node), + } + + // Make a copy of the query to use as the basis for rendering later. + dup, err := copystructure.Copy(query) + if err != nil { + return nil, err + } + var ok bool + ct.query, ok = dup.(*structs.PreparedQuery) + if !ok { + return nil, fmt.Errorf("Failed to copy query") + } + + // Walk over all the string fields in the Service sub-structure and + // parse them as HIL. + parse := func(path string, v reflect.Value) error { + tree, err := hil.Parse(v.String()) + if err != nil { + return fmt.Errorf("Bad Service%s field with contents '%s': %s", path, v.String(), err) + } + + ct.trees[path] = tree + return nil + } + if err := walk(&ct.query.Service, parse); err != nil { + return nil, err + } + + // If they supplied a regexp then compile it. + if ct.query.Template.Regexp != "" { + var err error + ct.re, err = regexp.Compile(ct.query.Template.Regexp) + if err != nil { + return nil, fmt.Errorf("Bad Regexp: %s", err) + } + } + + return ct, nil +} + // Render takes a compiled template and renders it for the given name. For // example, if the user looks up foobar.query.consul via DNS then we will call // this function with "foobar" on the compiled template. @@ -118,97 +169,3 @@ func (ct *CompiledTemplate) Render(name string) (*structs.PreparedQuery, error) return query, nil } - -// Compile validates a prepared query template and returns an opaque compiled -// object that can be used later to render the template. -func Compile(query *structs.PreparedQuery) (*CompiledTemplate, error) { - // Make sure it's a type we understand. - if query.Template.Type != structs.QueryTemplateTypeNamePrefixMatch { - return nil, fmt.Errorf("Bad Template.Type '%s'", query.Template.Type) - } - - // Start compile. - ct := &CompiledTemplate{ - trees: make(map[string]ast.Node), - } - - // Make a copy of the query to use as the basis for rendering later. - dup, err := copystructure.Copy(query) - if err != nil { - return nil, err - } - var ok bool - ct.query, ok = dup.(*structs.PreparedQuery) - if !ok { - return nil, fmt.Errorf("Failed to copy query") - } - - // Walk over all the string fields in the Service sub-structure and - // parse them as HIL. - parse := func(path string, v reflect.Value) error { - tree, err := hil.Parse(v.String()) - if err != nil { - return fmt.Errorf("Bad Service%s field with contents '%s': %s", path, v.String(), err) - } - - ct.trees[path] = tree - return nil - } - if err := walk(&ct.query.Service, parse); err != nil { - return nil, err - } - - // If they supplied a regexp then compile it. - if ct.query.Template.Regexp != "" { - var err error - ct.re, err = regexp.Compile(ct.query.Template.Regexp) - if err != nil { - return nil, fmt.Errorf("Bad Regexp: %s", err) - } - } - - return ct, nil -} - -// visitor is a function that will get called for each string element of a -// structure. -type visitor func(path string, v reflect.Value) error - -// visit calls the visitor function for each string it finds, and will descend -// recursively into structures and slices. If any visitor returns an error then -// the search will stop and that error will be returned. -func visit(path string, v reflect.Value, t reflect.Type, fn visitor) error { - switch v.Kind() { - case reflect.String: - return fn(path, v) - case reflect.Struct: - for i := 0; i < v.NumField(); i++ { - vf := v.Field(i) - tf := t.Field(i) - newPath := fmt.Sprintf("%s.%s", path, tf.Name) - if err := visit(newPath, vf, tf.Type, fn); err != nil { - return err - } - } - case reflect.Slice: - for i := 0; i < v.Len(); i++ { - vi := v.Index(i) - ti := vi.Type() - newPath := fmt.Sprintf("%s[%d]", path, i) - if err := visit(newPath, vi, ti, fn); err != nil { - return err - } - } - } - return nil -} - -// walk finds all the string elements of a given structure (and its sub- -// structures) and calls the visitor function. Each string found will get -// a unique path computed. If any visitor returns an error then the search -// will stop and that error will be returned. -func walk(obj interface{}, fn visitor) error { - v := reflect.ValueOf(obj).Elem() - t := v.Type() - return visit("", v, t, fn) -} diff --git a/consul/prepared_query/walk.go b/consul/prepared_query/walk.go new file mode 100644 index 0000000000..11f6c14dc8 --- /dev/null +++ b/consul/prepared_query/walk.go @@ -0,0 +1,49 @@ +package prepared_query + +import ( + "fmt" + "reflect" +) + +// visitor is a function that will get called for each string element of a +// structure. +type visitor func(path string, v reflect.Value) error + +// visit calls the visitor function for each string it finds, and will descend +// recursively into structures and slices. If any visitor returns an error then +// the search will stop and that error will be returned. +func visit(path string, v reflect.Value, t reflect.Type, fn visitor) error { + switch v.Kind() { + case reflect.String: + return fn(path, v) + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + vf := v.Field(i) + tf := t.Field(i) + newPath := fmt.Sprintf("%s.%s", path, tf.Name) + if err := visit(newPath, vf, tf.Type, fn); err != nil { + return err + } + } + case reflect.Slice: + for i := 0; i < v.Len(); i++ { + vi := v.Index(i) + ti := vi.Type() + newPath := fmt.Sprintf("%s[%d]", path, i) + if err := visit(newPath, vi, ti, fn); err != nil { + return err + } + } + } + return nil +} + +// walk finds all the string elements of a given structure (and its sub- +// structures) and calls the visitor function. Each string found will get +// a unique path computed. If any visitor returns an error then the search +// will stop and that error will be returned. +func walk(obj interface{}, fn visitor) error { + v := reflect.ValueOf(obj).Elem() + t := v.Type() + return visit("", v, t, fn) +}