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 // example, if the user looks up foobar.query.consul via DNS then we will call
// this function with "foobar" on the compiled template. // this function with "foobar" on the compiled template.
func (ct *CompiledTemplate) Render(name string) (*structs.PreparedQuery, error) { 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 // Start with a fresh, detached copy of the original so we don't disturb
// the prototype. // the prototype.
dup, err := copystructure.Copy(ct.query) dup, err := copystructure.Copy(ct.query)

View File

@ -8,7 +8,6 @@ import (
"time" "time"
"github.com/armon/go-metrics" "github.com/armon/go-metrics"
"github.com/hashicorp/consul/consul/prepared_query"
"github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/go-uuid" "github.com/hashicorp/go-uuid"
) )
@ -290,7 +289,7 @@ func (p *PreparedQuery) Execute(args *structs.PreparedQueryExecuteRequest,
// Try to locate the query. // Try to locate the query.
state := p.srv.fsm.State() state := p.srv.fsm.State()
_, query, ct, err := state.PreparedQueryLookup(args.QueryIDOrName) _, query, err := state.PreparedQueryResolve(args.QueryIDOrName)
if err != nil { if err != nil {
return err return err
} }
@ -298,18 +297,6 @@ func (p *PreparedQuery) Execute(args *structs.PreparedQueryExecuteRequest,
return ErrQueryNotFound 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. // Execute the query for the local DC.
if err := p.execute(query, reply); err != nil { if err := p.execute(query, reply); err != nil {
return err return err

View File

@ -261,21 +261,22 @@ func (s *StateStore) PreparedQueryGet(queryID string) (uint64, *structs.Prepared
return idx, toPreparedQuery(wrapped), nil return idx, toPreparedQuery(wrapped), nil
} }
// PreparedQueryLookup returns the given prepared query by looking up an ID or // PreparedQueryResolve returns the given prepared query by looking up an ID or
// Name. // Name. If the query was looked up by name and it's a template, then the
func (s *StateStore) PreparedQueryLookup(queryIDOrName string) (uint64, *structs.PreparedQuery, *prepared_query.CompiledTemplate, error) { // template will be rendered before it is returned.
func (s *StateStore) PreparedQueryResolve(queryIDOrName string) (uint64, *structs.PreparedQuery, error) {
tx := s.db.Txn(false) tx := s.db.Txn(false)
defer tx.Abort() defer tx.Abort()
// Get the table index. // 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 // 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, // 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 // 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). // return the results from the first query w/o a name).
if queryIDOrName == "" { 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 // 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) { if isUUID(queryIDOrName) {
wrapped, err := tx.First("prepared-queries", "id", queryIDOrName) wrapped, err := tx.First("prepared-queries", "id", queryIDOrName)
if err != nil { 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 { if wrapped != nil {
wrapper := wrapped.(*queryWrapper) query := toPreparedQuery(wrapped)
return idx, wrapper.PreparedQuery, wrapper.ct, nil 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) wrapped, err := tx.First("prepared-queries", "name_prefix", queryIDOrName)
if err != nil { 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 { if wrapped != nil {
wrapper := wrapped.(*queryWrapper) query := toPreparedQuery(wrapped)
query, ct := wrapper.PreparedQuery, wrapper.ct
if query.Name == queryIDOrName || prepared_query.IsTemplate(query) { 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) wrapped, err := tx.First("prepared-queries", "wild", true)
if err != nil { 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 { if wrapped != nil {
wrapper := wrapped.(*queryWrapper) return prep(wrapped)
return idx, wrapper.PreparedQuery, wrapper.ct, nil
} }
} }
return idx, nil, nil, nil return idx, nil, nil
} }
// PreparedQueryList returns all the prepared queries. // 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) s := testStateStore(t)
// Set up our test environment. // 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 // Try to lookup a query that's not there using something that looks
// like a real ID. // like a real ID.
idx, actual, _, err := s.PreparedQueryLookup(query.ID) idx, actual, err := s.PreparedQueryResolve(query.ID)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) 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 // Try to lookup a query that's not there using something that looks
// like a name // like a name
idx, actual, _, err = s.PreparedQueryLookup(query.Name) idx, actual, err = s.PreparedQueryResolve(query.Name)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -382,7 +382,7 @@ func TestStateStore_PreparedQueryLookup(t *testing.T) {
ModifyIndex: 3, ModifyIndex: 3,
}, },
} }
idx, actual, _, err = s.PreparedQueryLookup(query.ID) idx, actual, err = s.PreparedQueryResolve(query.ID)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) 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. // 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 { if err != nil {
t.Fatalf("err: %s", err) 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 // Make sure an empty lookup is well-behaved if there are actual queries
// in the state store. // in the state store.
idx, actual, _, err = s.PreparedQueryLookup("") idx, actual, err = s.PreparedQueryResolve("")
if err != ErrMissingQueryID { if err != ErrMissingQueryID {
t.Fatalf("bad: %v ", err) t.Fatalf("bad: %v ", err)
} }

View File

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