vault: Adding metrics profiling

This commit is contained in:
Armon Dadgar 2015-04-08 16:43:17 -07:00
parent 429ad7e5cb
commit 512b3d7afd
8 changed files with 76 additions and 0 deletions

View File

@ -7,7 +7,9 @@ import (
"log"
"strings"
"sync"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/vault/audit"
"github.com/hashicorp/vault/logical"
)
@ -260,6 +262,7 @@ func (a *AuditBroker) IsRegistered(name string) bool {
// LogRequest is used to ensure all the audit backends have an opportunity to
// log the given request and that *at least one* succeeds.
func (a *AuditBroker) LogRequest(auth *logical.Auth, req *logical.Request) error {
defer metrics.MeasureSince([]string{"audit", "log_request"}, time.Now())
a.l.RLock()
defer a.l.RUnlock()
@ -282,6 +285,7 @@ func (a *AuditBroker) LogRequest(auth *logical.Auth, req *logical.Request) error
// log the given response and that *at least one* succeeds.
func (a *AuditBroker) LogResponse(auth *logical.Auth, req *logical.Request,
resp *logical.Response, err error) error {
defer metrics.MeasureSince([]string{"audit", "log_response"}, time.Now())
a.l.RLock()
defer a.l.RUnlock()

View File

@ -8,7 +8,9 @@ import (
"fmt"
"strings"
"sync"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/vault/physical"
)
@ -203,6 +205,7 @@ func (b *AESGCMBarrier) Seal() error {
// Put is used to insert or update an entry
func (b *AESGCMBarrier) Put(entry *Entry) error {
defer metrics.MeasureSince([]string{"barrier", "put"}, time.Now())
b.l.RLock()
defer b.l.RUnlock()
@ -220,6 +223,7 @@ func (b *AESGCMBarrier) Put(entry *Entry) error {
// Get is used to fetch an entry
func (b *AESGCMBarrier) Get(key string) (*Entry, error) {
defer metrics.MeasureSince([]string{"barrier", "get"}, time.Now())
b.l.RLock()
defer b.l.RUnlock()
@ -252,6 +256,7 @@ func (b *AESGCMBarrier) Get(key string) (*Entry, error) {
// Delete is used to permanently delete an entry
func (b *AESGCMBarrier) Delete(key string) error {
defer metrics.MeasureSince([]string{"barrier", "delete"}, time.Now())
b.l.RLock()
defer b.l.RUnlock()
if b.sealed {
@ -264,6 +269,7 @@ func (b *AESGCMBarrier) Delete(key string) error {
// List is used ot list all the keys under a given
// prefix, up to the next prefix.
func (b *AESGCMBarrier) List(prefix string) ([]string, error) {
defer metrics.MeasureSince([]string{"barrier", "list"}, time.Now())
b.l.RLock()
defer b.l.RUnlock()
if b.sealed {

View File

@ -9,7 +9,9 @@ import (
"os"
"strings"
"sync"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/vault/audit"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/physical"
@ -152,6 +154,9 @@ type Core struct {
// token store is used to manage authentication tokens
tokenStore *TokenStore
// metricsCh is used to stop the metrics streaming
metricsCh chan struct{}
logger *log.Logger
}
@ -230,6 +235,7 @@ func (c *Core) HandleRequest(req *logical.Request) (*logical.Response, error) {
}
func (c *Core) handleRequest(req *logical.Request) (*logical.Response, error) {
defer metrics.MeasureSince([]string{"core", "handle_request"}, time.Now())
// Validate the token
auth, err := c.checkToken(req.Operation, req.Path, req.ClientToken)
if err != nil {
@ -321,6 +327,8 @@ func (c *Core) handleRequest(req *logical.Request) (*logical.Response, error) {
// handleLoginRequest is used to handle a login request, which is an
// unauthenticated request to the backend.
func (c *Core) handleLoginRequest(req *logical.Request) (*logical.Response, error) {
defer metrics.MeasureSince([]string{"core", "handle_login_request"}, time.Now())
// Create an audit trail of the request, auth is not available on login requests
if err := c.auditBroker.LogRequest(nil, req); err != nil {
c.logger.Printf("[ERR] core: failed to audit request (%#v): %v",
@ -380,6 +388,8 @@ func (c *Core) handleLoginRequest(req *logical.Request) (*logical.Response, erro
func (c *Core) checkToken(
op logical.Operation, path string, token string) (*logical.Auth, error) {
defer metrics.MeasureSince([]string{"core", "check_token"}, time.Now())
// Ensure there is a client token
if token == "" {
return nil, fmt.Errorf("missing client token")
@ -605,6 +615,8 @@ func (c *Core) SecretProgress() int {
// this method is done with it. If you want to keep the key around, a copy
// should be made.
func (c *Core) Unseal(key []byte) (bool, error) {
defer metrics.MeasureSince([]string{"core", "unseal"}, time.Now())
// Verify the key length
min, max := c.barrier.KeyLength()
max += shamir.ShareOverhead
@ -689,6 +701,7 @@ func (c *Core) Unseal(key []byte) (bool, error) {
// Seal is used to re-seal the Vault. This requires the Vault to
// be unsealed again to perform any further operations.
func (c *Core) Seal(token string) error {
defer metrics.MeasureSince([]string{"core", "seal"}, time.Now())
c.stateLock.Lock()
defer c.stateLock.Unlock()
if c.sealed {
@ -723,6 +736,7 @@ func (c *Core) Seal(token string) error {
// requires the Vault to be unsealed such as mount tables, logical backends,
// credential stores, etc.
func (c *Core) postUnseal() error {
defer metrics.MeasureSince([]string{"core", "post_unseal"}, time.Now())
if err := c.loadMounts(); err != nil {
return err
}
@ -750,12 +764,19 @@ func (c *Core) postUnseal() error {
if err := c.setupAudits(); err != nil {
return err
}
c.metricsCh = make(chan struct{})
go c.emitMetrics(c.metricsCh)
return nil
}
// preSeal is invoked before the barrier is sealed, allowing
// for any state teardown required.
func (c *Core) preSeal() error {
defer metrics.MeasureSince([]string{"core", "pre_seal"}, time.Now())
if c.metricsCh != nil {
close(c.metricsCh)
c.metricsCh = nil
}
if err := c.teardownAudits(); err != nil {
return err
}
@ -776,3 +797,15 @@ func (c *Core) preSeal() error {
}
return nil
}
// emitMetrics is used to periodically expose metrics while runnig
func (c *Core) emitMetrics(stopCh chan struct{}) {
for {
select {
case <-time.After(time.Second):
c.expiration.emitMetrics()
case <-stopCh:
return
}
}
}

View File

@ -10,6 +10,7 @@ import (
"sync"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/vault/logical"
)
@ -156,6 +157,7 @@ func (m *ExpirationManager) Stop() error {
// Revoke is used to revoke a secret named by the given LeaseID
func (m *ExpirationManager) Revoke(leaseID string) error {
defer metrics.MeasureSince([]string{"expire", "revoke"}, time.Now())
// Load the entry
le, err := m.loadEntry(leaseID)
if err != nil {
@ -191,6 +193,7 @@ func (m *ExpirationManager) Revoke(leaseID string) error {
// The prefix maps to that of the mount table to make this simpler
// to reason about.
func (m *ExpirationManager) RevokePrefix(prefix string) error {
defer metrics.MeasureSince([]string{"expire", "revoke-prefix"}, time.Now())
// Ensure there is a trailing slash
if !strings.HasSuffix(prefix, "/") {
prefix = prefix + "/"
@ -217,6 +220,7 @@ func (m *ExpirationManager) RevokePrefix(prefix string) error {
// Renew is used to renew a secret using the given leaseID
// and a renew interval. The increment may be ignored.
func (m *ExpirationManager) Renew(leaseID string, increment time.Duration) (*logical.Response, error) {
defer metrics.MeasureSince([]string{"expire", "renew"}, time.Now())
// Load the entry
le, err := m.loadEntry(leaseID)
if err != nil {
@ -279,6 +283,7 @@ func (m *ExpirationManager) Renew(leaseID string, increment time.Duration) (*log
// RenewToken is used to renew a token which does not need to
// invoke a logical backend.
func (m *ExpirationManager) RenewToken(source string, token string) (*logical.Auth, error) {
defer metrics.MeasureSince([]string{"expire", "renew-token"}, time.Now())
// Compute the Lease ID
leaseID := path.Join(source, m.tokenStore.SaltID(token))
@ -324,6 +329,7 @@ func (m *ExpirationManager) RenewToken(source string, token string) (*logical.Au
// lease. The secret gets assigned a LeaseID and the management of
// of lease is assumed by the expiration manager.
func (m *ExpirationManager) Register(req *logical.Request, resp *logical.Response) (string, error) {
defer metrics.MeasureSince([]string{"expire", "register"}, time.Now())
// Ignore if there is no leased secret
if resp == nil || resp.Secret == nil {
return "", nil
@ -372,6 +378,7 @@ func (m *ExpirationManager) Register(req *logical.Request, resp *logical.Respons
// The token does not get a LeaseID, but the lease management is handled by
// the expiration manager.
func (m *ExpirationManager) RegisterAuth(source string, auth *logical.Auth) error {
defer metrics.MeasureSince([]string{"expire", "register-auth"}, time.Now())
// Create a lease entry
now := time.Now().UTC()
leaseTotal := auth.Lease + auth.LeaseGracePeriod
@ -499,6 +506,14 @@ func (m *ExpirationManager) deleteEntry(leaseID string) error {
return nil
}
// emitMetrics is invoked periodically to emit statistics
func (m *ExpirationManager) emitMetrics() {
m.pendingLock.Lock()
num := len(m.pending)
m.pendingLock.Unlock()
metrics.SetGauge([]string{"expire", "num_leases"}, float32(num))
}
// leaseEntry is used to structure the values the expiration
// manager stores. This is used to handle renew and revocation.
type leaseEntry struct {

View File

@ -2,7 +2,9 @@ package vault
import (
"fmt"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/golang-lru"
"github.com/hashicorp/vault/logical"
)
@ -54,6 +56,7 @@ func (c *Core) teardownPolicyStore() error {
// SetPolicy is used to create or update the given policy
func (ps *PolicyStore) SetPolicy(p *Policy) error {
defer metrics.MeasureSince([]string{"policy", "set_policy"}, time.Now())
if p.Name == "root" {
return fmt.Errorf("cannot update root policy")
}
@ -76,6 +79,7 @@ func (ps *PolicyStore) SetPolicy(p *Policy) error {
// GetPolicy is used to fetch the named policy
func (ps *PolicyStore) GetPolicy(name string) (*Policy, error) {
defer metrics.MeasureSince([]string{"policy", "get_policy"}, time.Now())
// Check for cached policy
if raw, ok := ps.lru.Get(name); ok {
return raw.(*Policy), nil
@ -111,6 +115,7 @@ func (ps *PolicyStore) GetPolicy(name string) (*Policy, error) {
// ListPolicies is used to list the available policies
func (ps *PolicyStore) ListPolicies() ([]string, error) {
defer metrics.MeasureSince([]string{"policy", "list_policies"}, time.Now())
// Scan the view, since the policy names are the same as the
// key names.
return CollectKeys(ps.view)
@ -118,6 +123,7 @@ func (ps *PolicyStore) ListPolicies() ([]string, error) {
// DeletePolicy is used to delete the named policy
func (ps *PolicyStore) DeletePolicy(name string) error {
defer metrics.MeasureSince([]string{"policy", "delete_policy"}, time.Now())
if name == "root" {
return fmt.Errorf("cannot delete root policy")
}

View File

@ -2,9 +2,11 @@ package vault
import (
"log"
"strings"
"sync"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/vault/logical"
)
@ -124,6 +126,7 @@ func (m *RollbackManager) startRollback(path string) *rollbackState {
// attemptRollback invokes a RollbackOperation for the given path
func (m *RollbackManager) attemptRollback(path string, rs *rollbackState) (err error) {
defer metrics.MeasureSince([]string{"rollback", "attempt", strings.Replace(path, "/", "-", -1)}, time.Now())
defer func() {
rs.lastError = err
rs.Done()

View File

@ -6,7 +6,9 @@ import (
"fmt"
"strings"
"sync"
"time"
"github.com/armon/go-metrics"
"github.com/armon/go-radix"
"github.com/hashicorp/vault/logical"
)
@ -157,6 +159,7 @@ func (r *Router) Route(req *logical.Request) (*logical.Response, error) {
if !ok {
return nil, fmt.Errorf("no handler for route '%s'", req.Path)
}
defer metrics.MeasureSince([]string{"route", strings.Replace(mount, "/", "-", -1)}, time.Now())
me := raw.(*mountEntry)
// If the path is tainted, we reject any operation except for

View File

@ -8,6 +8,7 @@ import (
"strings"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
"github.com/mitchellh/mapstructure"
@ -247,6 +248,7 @@ func (ts *TokenStore) RootToken() (*TokenEntry, error) {
// Create is used to create a new token entry. The entry is assigned
// a newly generated ID if not provided.
func (ts *TokenStore) Create(entry *TokenEntry) error {
defer metrics.MeasureSince([]string{"token", "create"}, time.Now())
// Generate an ID if necessary
if entry.ID == "" {
entry.ID = generateUUID()
@ -292,6 +294,7 @@ func (ts *TokenStore) Create(entry *TokenEntry) error {
// Lookup is used to find a token given its ID
func (ts *TokenStore) Lookup(id string) (*TokenEntry, error) {
defer metrics.MeasureSince([]string{"token", "lookup"}, time.Now())
if id == "" {
return nil, fmt.Errorf("cannot lookup blank token")
}
@ -323,6 +326,7 @@ func (ts *TokenStore) lookupSalted(saltedId string) (*TokenEntry, error) {
// Revoke is used to invalidate a given token, any child tokens
// will be orphaned.
func (ts *TokenStore) Revoke(id string) error {
defer metrics.MeasureSince([]string{"token", "revoke"}, time.Now())
if id == "" {
return fmt.Errorf("cannot revoke blank token")
}
@ -357,6 +361,7 @@ func (ts *TokenStore) revokeSalted(saltedId string) error {
// RevokeTree is used to invalide a given token and all
// child tokens.
func (ts *TokenStore) RevokeTree(id string) error {
defer metrics.MeasureSince([]string{"token", "revoke-tree"}, time.Now())
// Verify the token is not blank
if id == "" {
return fmt.Errorf("cannot revoke blank token")
@ -419,6 +424,7 @@ func (ts *TokenStore) revokeTreeSalted(saltedId string) error {
// RevokeAll is used to invalidate all generated tokens.
func (ts *TokenStore) RevokeAll() error {
defer metrics.MeasureSince([]string{"token", "revoke-all"}, time.Now())
// Collect all the tokens
sub := ts.view.SubView(lookupPrefix)
tokens, err := CollectKeys(sub)