2015-11-07 00:59:32 +00:00
|
|
|
package state
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-11-10 05:48:35 +00:00
|
|
|
"regexp"
|
2015-11-07 00:59:32 +00:00
|
|
|
|
2016-02-26 08:25:44 +00:00
|
|
|
"github.com/hashicorp/consul/consul/prepared_query"
|
2015-11-07 00:59:32 +00:00
|
|
|
"github.com/hashicorp/consul/consul/structs"
|
|
|
|
"github.com/hashicorp/go-memdb"
|
|
|
|
)
|
|
|
|
|
2015-11-10 05:48:35 +00:00
|
|
|
// validUUID is used to check if a given string looks like a UUID
|
2015-11-17 06:57:47 +00:00
|
|
|
var validUUID = regexp.MustCompile(`(?i)^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$`)
|
2015-11-10 05:48:35 +00:00
|
|
|
|
|
|
|
// isUUID returns true if the given string is a valid UUID.
|
|
|
|
func isUUID(str string) bool {
|
|
|
|
return validUUID.MatchString(str)
|
|
|
|
}
|
|
|
|
|
2016-02-26 08:25:44 +00:00
|
|
|
// queryWrapper is an internal structure that is used to store a query alongside
|
|
|
|
// its compiled template, which can be nil.
|
|
|
|
type queryWrapper struct {
|
|
|
|
*structs.PreparedQuery
|
|
|
|
ct *prepared_query.CompiledTemplate
|
|
|
|
}
|
|
|
|
|
|
|
|
// toPreparedQuery unwraps the internal form of a prepared query and returns
|
|
|
|
// the regular struct.
|
|
|
|
func toPreparedQuery(wrapped interface{}) *structs.PreparedQuery {
|
|
|
|
if wrapped == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return wrapped.(*queryWrapper).PreparedQuery
|
|
|
|
}
|
|
|
|
|
2016-02-26 09:27:51 +00:00
|
|
|
// isQueryWild returns the wild-ness of a query. See isWild for details.
|
|
|
|
func isQueryWild(query *structs.PreparedQuery) bool {
|
|
|
|
return query != nil && prepared_query.IsTemplate(query) && query.Name == ""
|
|
|
|
}
|
|
|
|
|
|
|
|
// isWrappedWild is used to determine if the given wrapped query is a wild one,
|
|
|
|
// which means it has an empty Name and it's a template. See the comments for
|
|
|
|
// "wild" in schema.go for more details and to see where this is used.
|
|
|
|
func isWrappedWild(obj interface{}) (bool, error) {
|
|
|
|
return isQueryWild(toPreparedQuery(obj)), nil
|
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
// PreparedQueries is used to pull all the prepared queries from the snapshot.
|
2016-02-26 08:25:44 +00:00
|
|
|
func (s *StateSnapshot) PreparedQueries() (structs.PreparedQueries, error) {
|
|
|
|
queries, err := s.tx.Get("prepared-queries", "id")
|
2015-11-07 00:59:32 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-02-26 08:25:44 +00:00
|
|
|
|
|
|
|
var ret structs.PreparedQueries
|
|
|
|
for wrapped := queries.Next(); wrapped != nil; wrapped = queries.Next() {
|
|
|
|
ret = append(ret, toPreparedQuery(wrapped))
|
|
|
|
}
|
|
|
|
return ret, nil
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
// PrepparedQuery is used when restoring from a snapshot. For general inserts,
|
|
|
|
// use PreparedQuerySet.
|
|
|
|
func (s *StateRestore) PreparedQuery(query *structs.PreparedQuery) error {
|
2016-02-26 08:25:44 +00:00
|
|
|
// If this is a template, compile it, otherwise leave the compiled
|
|
|
|
// template field nil.
|
|
|
|
var ct *prepared_query.CompiledTemplate
|
|
|
|
if prepared_query.IsTemplate(query) {
|
|
|
|
var err error
|
|
|
|
ct, err = prepared_query.Compile(query)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed compiling template: %s", err)
|
|
|
|
}
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
|
2016-02-26 08:25:44 +00:00
|
|
|
// Insert the wrapped query.
|
|
|
|
if err := s.tx.Insert("prepared-queries", &queryWrapper{query, ct}); err != nil {
|
|
|
|
return fmt.Errorf("failed restoring prepared query: %s", err)
|
|
|
|
}
|
2015-11-10 04:37:41 +00:00
|
|
|
if err := indexUpdateMaxTxn(s.tx, query.ModifyIndex, "prepared-queries"); err != nil {
|
2015-11-07 00:59:32 +00:00
|
|
|
return fmt.Errorf("failed updating index: %s", err)
|
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
s.watches.Arm("prepared-queries")
|
2015-11-07 00:59:32 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
// PreparedQuerySet is used to create or update a prepared query.
|
|
|
|
func (s *StateStore) PreparedQuerySet(idx uint64, query *structs.PreparedQuery) error {
|
2015-11-07 00:59:32 +00:00
|
|
|
tx := s.db.Txn(true)
|
|
|
|
defer tx.Abort()
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
if err := s.preparedQuerySetTxn(tx, idx, query); err != nil {
|
2015-11-07 00:59:32 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
tx.Commit()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
// preparedQuerySetTxn is the inner method used to insert a prepared query with
|
|
|
|
// the proper indexes into the state store.
|
|
|
|
func (s *StateStore) preparedQuerySetTxn(tx *memdb.Txn, idx uint64, query *structs.PreparedQuery) error {
|
2015-11-07 00:59:32 +00:00
|
|
|
// Check that the ID is set.
|
|
|
|
if query.ID == "" {
|
|
|
|
return ErrMissingQueryID
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for an existing query.
|
2016-02-26 08:25:44 +00:00
|
|
|
wrapped, err := tx.First("prepared-queries", "id", query.ID)
|
2015-11-07 00:59:32 +00:00
|
|
|
if err != nil {
|
2015-11-10 04:37:41 +00:00
|
|
|
return fmt.Errorf("failed prepared query lookup: %s", err)
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
2016-02-26 08:25:44 +00:00
|
|
|
existing := toPreparedQuery(wrapped)
|
2015-11-07 00:59:32 +00:00
|
|
|
|
|
|
|
// Set the indexes.
|
|
|
|
if existing != nil {
|
2016-02-26 08:25:44 +00:00
|
|
|
query.CreateIndex = existing.CreateIndex
|
2015-11-07 00:59:32 +00:00
|
|
|
query.ModifyIndex = idx
|
|
|
|
} else {
|
|
|
|
query.CreateIndex = idx
|
|
|
|
query.ModifyIndex = idx
|
|
|
|
}
|
|
|
|
|
2015-12-02 17:04:51 +00:00
|
|
|
// Verify that the query name doesn't already exist, or that we are
|
|
|
|
// updating the same instance that has this name.
|
|
|
|
if query.Name != "" {
|
2016-02-26 08:25:44 +00:00
|
|
|
wrapped, err := tx.First("prepared-queries", "name", query.Name)
|
2015-12-02 17:04:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed prepared query lookup: %s", err)
|
|
|
|
}
|
2016-02-26 08:25:44 +00:00
|
|
|
other := toPreparedQuery(wrapped)
|
|
|
|
if other != nil && (existing == nil || existing.ID != other.ID) {
|
2015-12-02 17:04:51 +00:00
|
|
|
return fmt.Errorf("name '%s' aliases an existing query name", query.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-26 09:27:51 +00:00
|
|
|
// Similarly, if this is the wild query make sure there isn't another
|
|
|
|
// one, or that we are updating the same one.
|
|
|
|
if isQueryWild(query) {
|
|
|
|
wrapped, err := tx.First("prepared-queries", "wild", true)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed prepared query lookup: %s", err)
|
|
|
|
}
|
|
|
|
other := toPreparedQuery(wrapped)
|
|
|
|
if other != nil && (existing == nil || existing.ID != other.ID) {
|
|
|
|
return fmt.Errorf("a prepared query template already exists with an empty name")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-10 05:15:55 +00:00
|
|
|
// Verify that the name doesn't alias any existing ID. We allow queries
|
|
|
|
// to be looked up by ID *or* name so we don't want anyone to try to
|
|
|
|
// register a query with a name equal to some other query's ID in an
|
|
|
|
// attempt to hijack it. We also look up by ID *then* name in order to
|
|
|
|
// prevent this, but it seems prudent to prevent these types of rogue
|
2015-11-10 05:48:35 +00:00
|
|
|
// queries from ever making it into the state store. Note that we have
|
|
|
|
// to see if the name looks like a UUID before checking since the UUID
|
|
|
|
// index will complain if we look up something that's not formatted
|
|
|
|
// like one.
|
|
|
|
if isUUID(query.Name) {
|
2016-02-26 08:25:44 +00:00
|
|
|
wrapped, err := tx.First("prepared-queries", "id", query.Name)
|
2015-11-10 05:48:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed prepared query lookup: %s", err)
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
2016-02-26 08:25:44 +00:00
|
|
|
if wrapped != nil {
|
2015-12-02 17:04:51 +00:00
|
|
|
return fmt.Errorf("name '%s' aliases an existing query ID", query.Name)
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that the session exists.
|
|
|
|
if query.Session != "" {
|
|
|
|
sess, err := tx.First("sessions", "id", query.Session)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed session lookup: %s", err)
|
|
|
|
}
|
|
|
|
if sess == nil {
|
|
|
|
return fmt.Errorf("invalid session %#v", query.Session)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// We do not verify the service here, nor the token, if any. These are
|
|
|
|
// checked at execute time and not doing integrity checking on them
|
|
|
|
// helps avoid bootstrapping chicken and egg problems.
|
2015-11-07 00:59:32 +00:00
|
|
|
|
2016-02-26 08:25:44 +00:00
|
|
|
// If this is a template, compile it, otherwise leave the compiled
|
|
|
|
// template field nil.
|
|
|
|
var ct *prepared_query.CompiledTemplate
|
|
|
|
if prepared_query.IsTemplate(query) {
|
|
|
|
var err error
|
|
|
|
ct, err = prepared_query.Compile(query)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed compiling template: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert the wrapped query.
|
|
|
|
if err := tx.Insert("prepared-queries", &queryWrapper{query, ct}); err != nil {
|
2015-11-10 04:37:41 +00:00
|
|
|
return fmt.Errorf("failed inserting prepared query: %s", err)
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
2015-11-10 04:37:41 +00:00
|
|
|
if err := tx.Insert("index", &IndexEntry{"prepared-queries", idx}); err != nil {
|
2015-11-07 00:59:32 +00:00
|
|
|
return fmt.Errorf("failed updating index: %s", err)
|
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
tx.Defer(func() { s.tableWatches["prepared-queries"].Notify() })
|
2015-11-07 00:59:32 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
// PreparedQueryDelete deletes the given query by ID.
|
|
|
|
func (s *StateStore) PreparedQueryDelete(idx uint64, queryID string) error {
|
2015-11-07 00:59:32 +00:00
|
|
|
tx := s.db.Txn(true)
|
|
|
|
defer tx.Abort()
|
|
|
|
|
|
|
|
watches := NewDumbWatchManager(s.tableWatches)
|
2015-11-10 04:37:41 +00:00
|
|
|
if err := s.preparedQueryDeleteTxn(tx, idx, watches, queryID); err != nil {
|
|
|
|
return fmt.Errorf("failed prepared query delete: %s", err)
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tx.Defer(func() { watches.Notify() })
|
|
|
|
tx.Commit()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
// preparedQueryDeleteTxn is the inner method used to delete a prepared query
|
|
|
|
// with the proper indexes into the state store.
|
|
|
|
func (s *StateStore) preparedQueryDeleteTxn(tx *memdb.Txn, idx uint64, watches *DumbWatchManager,
|
2015-11-07 00:59:32 +00:00
|
|
|
queryID string) error {
|
|
|
|
// Pull the query.
|
2016-02-26 08:25:44 +00:00
|
|
|
wrapped, err := tx.First("prepared-queries", "id", queryID)
|
2015-11-07 00:59:32 +00:00
|
|
|
if err != nil {
|
2015-11-10 04:37:41 +00:00
|
|
|
return fmt.Errorf("failed prepared query lookup: %s", err)
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
2016-02-26 08:25:44 +00:00
|
|
|
if wrapped == nil {
|
2015-11-07 00:59:32 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete the query and update the index.
|
2016-02-26 08:25:44 +00:00
|
|
|
if err := tx.Delete("prepared-queries", wrapped); err != nil {
|
2015-11-10 04:37:41 +00:00
|
|
|
return fmt.Errorf("failed prepared query delete: %s", err)
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
2015-11-10 04:37:41 +00:00
|
|
|
if err := tx.Insert("index", &IndexEntry{"prepared-queries", idx}); err != nil {
|
2015-11-07 00:59:32 +00:00
|
|
|
return fmt.Errorf("failed updating index: %s", err)
|
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
watches.Arm("prepared-queries")
|
2015-11-07 00:59:32 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
// PreparedQueryGet returns the given prepared query by ID.
|
|
|
|
func (s *StateStore) PreparedQueryGet(queryID string) (uint64, *structs.PreparedQuery, error) {
|
2015-11-07 00:59:32 +00:00
|
|
|
tx := s.db.Txn(false)
|
|
|
|
defer tx.Abort()
|
|
|
|
|
|
|
|
// Get the table index.
|
2015-11-10 04:37:41 +00:00
|
|
|
idx := maxIndexTxn(tx, s.getWatchTables("PreparedQueryGet")...)
|
2015-11-07 00:59:32 +00:00
|
|
|
|
|
|
|
// Look up the query by its ID.
|
2016-02-26 08:25:44 +00:00
|
|
|
wrapped, err := tx.First("prepared-queries", "id", queryID)
|
2015-11-07 00:59:32 +00:00
|
|
|
if err != nil {
|
2015-11-10 04:37:41 +00:00
|
|
|
return 0, nil, fmt.Errorf("failed prepared query lookup: %s", err)
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
2016-02-26 08:25:44 +00:00
|
|
|
return idx, toPreparedQuery(wrapped), nil
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
|
2016-02-26 20:07:43 +00:00
|
|
|
// 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) {
|
2015-11-07 00:59:32 +00:00
|
|
|
tx := s.db.Txn(false)
|
|
|
|
defer tx.Abort()
|
|
|
|
|
|
|
|
// Get the table index.
|
2016-02-26 20:07:43 +00:00
|
|
|
idx := maxIndexTxn(tx, s.getWatchTables("PreparedQueryResolve")...)
|
2015-11-07 00:59:32 +00:00
|
|
|
|
|
|
|
// 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 == "" {
|
2016-02-26 20:07:43 +00:00
|
|
|
return 0, nil, ErrMissingQueryID
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
|
2015-11-10 05:48:35 +00:00
|
|
|
// Try first by ID if it looks like they gave us an ID. We check the
|
|
|
|
// format before trying this because the UUID index will complain if
|
|
|
|
// we look up something that's not formatted like one.
|
|
|
|
if isUUID(queryIDOrName) {
|
2016-02-26 08:25:44 +00:00
|
|
|
wrapped, err := tx.First("prepared-queries", "id", queryIDOrName)
|
2015-11-10 05:48:35 +00:00
|
|
|
if err != nil {
|
2016-02-26 20:07:43 +00:00
|
|
|
return 0, nil, fmt.Errorf("failed prepared query lookup: %s", err)
|
2015-11-10 05:48:35 +00:00
|
|
|
}
|
2016-02-26 08:25:44 +00:00
|
|
|
if wrapped != nil {
|
2016-02-26 20:07:43 +00:00
|
|
|
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
|
2015-11-10 05:48:35 +00:00
|
|
|
}
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
|
2016-02-26 09:27:51 +00:00
|
|
|
// Then try by name. We use a prefix match but check to make sure that
|
|
|
|
// the query's name matches the whole prefix for a non-template query.
|
|
|
|
// Templates are allowed to use the partial match. It's more efficient
|
|
|
|
// to combine the two lookups here, even though the logic is a little
|
|
|
|
// less clear.
|
|
|
|
{
|
|
|
|
wrapped, err := tx.First("prepared-queries", "name_prefix", queryIDOrName)
|
|
|
|
if err != nil {
|
2016-02-26 20:07:43 +00:00
|
|
|
return 0, nil, fmt.Errorf("failed prepared query lookup: %s", err)
|
2016-02-26 09:27:51 +00:00
|
|
|
}
|
|
|
|
if wrapped != nil {
|
2016-02-26 20:07:43 +00:00
|
|
|
query := toPreparedQuery(wrapped)
|
2016-02-26 09:27:51 +00:00
|
|
|
if query.Name == queryIDOrName || prepared_query.IsTemplate(query) {
|
2016-02-26 20:07:43 +00:00
|
|
|
return prep(wrapped)
|
2016-02-26 09:27:51 +00:00
|
|
|
}
|
|
|
|
}
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
2016-02-26 09:27:51 +00:00
|
|
|
|
|
|
|
// Finally, see if there's a wild template we can use.
|
|
|
|
{
|
|
|
|
wrapped, err := tx.First("prepared-queries", "wild", true)
|
|
|
|
if err != nil {
|
2016-02-26 20:07:43 +00:00
|
|
|
return 0, nil, fmt.Errorf("failed prepared query lookup: %s", err)
|
2016-02-26 09:27:51 +00:00
|
|
|
}
|
|
|
|
if wrapped != nil {
|
2016-02-26 20:07:43 +00:00
|
|
|
return prep(wrapped)
|
2016-02-26 09:27:51 +00:00
|
|
|
}
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
|
2016-02-26 20:07:43 +00:00
|
|
|
return idx, nil, nil
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
// PreparedQueryList returns all the prepared queries.
|
|
|
|
func (s *StateStore) PreparedQueryList() (uint64, structs.PreparedQueries, error) {
|
2015-11-07 00:59:32 +00:00
|
|
|
tx := s.db.Txn(false)
|
|
|
|
defer tx.Abort()
|
|
|
|
|
|
|
|
// Get the table index.
|
2015-11-10 04:37:41 +00:00
|
|
|
idx := maxIndexTxn(tx, s.getWatchTables("PreparedQueryList")...)
|
2015-11-07 00:59:32 +00:00
|
|
|
|
|
|
|
// Query all of the prepared queries in the state store.
|
2015-11-10 04:37:41 +00:00
|
|
|
queries, err := tx.Get("prepared-queries", "id")
|
2015-11-07 00:59:32 +00:00
|
|
|
if err != nil {
|
2015-11-10 04:37:41 +00:00
|
|
|
return 0, nil, fmt.Errorf("failed prepared query lookup: %s", err)
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Go over all of the queries and build the response.
|
2015-11-15 05:59:23 +00:00
|
|
|
var result structs.PreparedQueries
|
2016-02-26 08:25:44 +00:00
|
|
|
for wrapped := queries.Next(); wrapped != nil; wrapped = queries.Next() {
|
|
|
|
result = append(result, toPreparedQuery(wrapped))
|
2015-11-07 00:59:32 +00:00
|
|
|
}
|
|
|
|
return idx, result, nil
|
|
|
|
}
|