Factors rendering down into the resolve function.

This commit is contained in:
James Phillips 2016-02-26 12:07:43 -08:00
parent ee43212da4
commit 2a9a5f823e
5 changed files with 46 additions and 37 deletions

View File

@ -86,6 +86,11 @@ func Compile(query *structs.PreparedQuery) (*CompiledTemplate, error) {
// example, if the user looks up foobar.query.consul via DNS then we will call
// this function with "foobar" on the compiled template.
func (ct *CompiledTemplate) Render(name string) (*structs.PreparedQuery, error) {
// Make it "safe" to render a default structure.
if ct == nil {
return nil, fmt.Errorf("Cannot render an uncompiled template")
}
// Start with a fresh, detached copy of the original so we don't disturb
// the prototype.
dup, err := copystructure.Copy(ct.query)

View File

@ -8,7 +8,6 @@ import (
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/consul/consul/prepared_query"
"github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/go-uuid"
)
@ -290,7 +289,7 @@ func (p *PreparedQuery) Execute(args *structs.PreparedQueryExecuteRequest,
// Try to locate the query.
state := p.srv.fsm.State()
_, query, ct, err := state.PreparedQueryLookup(args.QueryIDOrName)
_, query, err := state.PreparedQueryResolve(args.QueryIDOrName)
if err != nil {
return err
}
@ -298,18 +297,6 @@ func (p *PreparedQuery) Execute(args *structs.PreparedQueryExecuteRequest,
return ErrQueryNotFound
}
// If this is a template then render the query first.
if prepared_query.IsTemplate(query) {
if ct == nil {
return fmt.Errorf("Missing compiled template")
}
render, err := ct.Render(args.QueryIDOrName)
if err != nil {
return err
}
query = render
}
// Execute the query for the local DC.
if err := p.execute(query, reply); err != nil {
return err

View File

@ -261,21 +261,22 @@ func (s *StateStore) PreparedQueryGet(queryID string) (uint64, *structs.Prepared
return idx, toPreparedQuery(wrapped), nil
}
// PreparedQueryLookup returns the given prepared query by looking up an ID or
// Name.
func (s *StateStore) PreparedQueryLookup(queryIDOrName string) (uint64, *structs.PreparedQuery, *prepared_query.CompiledTemplate, error) {
// PreparedQueryResolve returns the given prepared query by looking up an ID or
// Name. If the query was looked up by name and it's a template, then the
// template will be rendered before it is returned.
func (s *StateStore) PreparedQueryResolve(queryIDOrName string) (uint64, *structs.PreparedQuery, error) {
tx := s.db.Txn(false)
defer tx.Abort()
// Get the table index.
idx := maxIndexTxn(tx, s.getWatchTables("PreparedQueryLookup")...)
idx := maxIndexTxn(tx, s.getWatchTables("PreparedQueryResolve")...)
// Explicitly ban an empty query. This will never match an ID and the
// schema is set up so it will never match a query with an empty name,
// but we check it here to be explicit about it (we'd never want to
// return the results from the first query w/o a name).
if queryIDOrName == "" {
return 0, nil, nil, ErrMissingQueryID
return 0, nil, ErrMissingQueryID
}
// Try first by ID if it looks like they gave us an ID. We check the
@ -284,11 +285,29 @@ func (s *StateStore) PreparedQueryLookup(queryIDOrName string) (uint64, *structs
if isUUID(queryIDOrName) {
wrapped, err := tx.First("prepared-queries", "id", queryIDOrName)
if err != nil {
return 0, nil, nil, fmt.Errorf("failed prepared query lookup: %s", err)
return 0, nil, fmt.Errorf("failed prepared query lookup: %s", err)
}
if wrapped != nil {
wrapper := wrapped.(*queryWrapper)
return idx, wrapper.PreparedQuery, wrapper.ct, nil
query := toPreparedQuery(wrapped)
if prepared_query.IsTemplate(query) {
return idx, nil, fmt.Errorf("prepared query templates can only be resolved up by name, not by ID")
}
return idx, query, nil
}
}
// prep will check to see if the query is a template and render it
// first, otherwise it will just return a regular query.
prep := func(wrapped interface{}) (uint64, *structs.PreparedQuery, error) {
wrapper := wrapped.(*queryWrapper)
if prepared_query.IsTemplate(wrapper.PreparedQuery) {
render, err := wrapper.ct.Render(queryIDOrName)
if err != nil {
return idx, nil, err
}
return idx, render, nil
} else {
return idx, wrapper.PreparedQuery, nil
}
}
@ -300,13 +319,12 @@ func (s *StateStore) PreparedQueryLookup(queryIDOrName string) (uint64, *structs
{
wrapped, err := tx.First("prepared-queries", "name_prefix", queryIDOrName)
if err != nil {
return 0, nil, nil, fmt.Errorf("failed prepared query lookup: %s", err)
return 0, nil, fmt.Errorf("failed prepared query lookup: %s", err)
}
if wrapped != nil {
wrapper := wrapped.(*queryWrapper)
query, ct := wrapper.PreparedQuery, wrapper.ct
query := toPreparedQuery(wrapped)
if query.Name == queryIDOrName || prepared_query.IsTemplate(query) {
return idx, query, ct, nil
return prep(wrapped)
}
}
}
@ -315,15 +333,14 @@ func (s *StateStore) PreparedQueryLookup(queryIDOrName string) (uint64, *structs
{
wrapped, err := tx.First("prepared-queries", "wild", true)
if err != nil {
return 0, nil, nil, fmt.Errorf("failed prepared query lookup: %s", err)
return 0, nil, fmt.Errorf("failed prepared query lookup: %s", err)
}
if wrapped != nil {
wrapper := wrapped.(*queryWrapper)
return idx, wrapper.PreparedQuery, wrapper.ct, nil
return prep(wrapped)
}
}
return idx, nil, nil, nil
return idx, nil, nil
}
// PreparedQueryList returns all the prepared queries.

View File

@ -318,7 +318,7 @@ func TestStateStore_PreparedQueryDelete(t *testing.T) {
}
}
func TestStateStore_PreparedQueryLookup(t *testing.T) {
func TestStateStore_PreparedQueryResolve(t *testing.T) {
s := testStateStore(t)
// Set up our test environment.
@ -336,7 +336,7 @@ func TestStateStore_PreparedQueryLookup(t *testing.T) {
// Try to lookup a query that's not there using something that looks
// like a real ID.
idx, actual, _, err := s.PreparedQueryLookup(query.ID)
idx, actual, err := s.PreparedQueryResolve(query.ID)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -349,7 +349,7 @@ func TestStateStore_PreparedQueryLookup(t *testing.T) {
// Try to lookup a query that's not there using something that looks
// like a name
idx, actual, _, err = s.PreparedQueryLookup(query.Name)
idx, actual, err = s.PreparedQueryResolve(query.Name)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -382,7 +382,7 @@ func TestStateStore_PreparedQueryLookup(t *testing.T) {
ModifyIndex: 3,
},
}
idx, actual, _, err = s.PreparedQueryLookup(query.ID)
idx, actual, err = s.PreparedQueryResolve(query.ID)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -394,7 +394,7 @@ func TestStateStore_PreparedQueryLookup(t *testing.T) {
}
// Read it back using the name and verify it again.
idx, actual, _, err = s.PreparedQueryLookup(query.Name)
idx, actual, err = s.PreparedQueryResolve(query.Name)
if err != nil {
t.Fatalf("err: %s", err)
}
@ -407,7 +407,7 @@ func TestStateStore_PreparedQueryLookup(t *testing.T) {
// Make sure an empty lookup is well-behaved if there are actual queries
// in the state store.
idx, actual, _, err = s.PreparedQueryLookup("")
idx, actual, err = s.PreparedQueryResolve("")
if err != ErrMissingQueryID {
t.Fatalf("bad: %v ", err)
}

View File

@ -413,7 +413,7 @@ func (s *StateStore) getWatchTables(method string) []string {
return []string{"acls"}
case "Coordinates":
return []string{"coordinates"}
case "PreparedQueryGet", "PreparedQueryLookup", "PreparedQueryList":
case "PreparedQueryGet", "PreparedQueryResolve", "PreparedQueryList":
return []string{"prepared-queries"}
}