Skip to content

Commit

Permalink
backport of commit a7ffab9 (#28886)
Browse files Browse the repository at this point in the history
Co-authored-by: Violet Hynes <[email protected]>
  • Loading branch information
1 parent be335c1 commit 6ddf44b
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 3 deletions.
95 changes: 92 additions & 3 deletions vault/core_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package vault
import (
"context"
"errors"
"fmt"
"os"
"strings"
"time"
Expand Down Expand Up @@ -403,7 +404,7 @@ func (c *Core) findKvMounts() []*kvMount {
for _, entry := range c.mounts.Entries {
if entry.Type == "kv" || entry.Type == "generic" {
version, ok := entry.Options["version"]
if !ok {
if !ok || version == "" {
version = "1"
}
mounts = append(mounts, &kvMount{
Expand Down Expand Up @@ -452,9 +453,13 @@ func (c *Core) walkKvMountSecrets(ctx context.Context, m *kvMount) {
resp, err := c.router.Route(ctx, listRequest)
if err != nil {
c.kvCollectionErrorCount()
// ErrUnsupportedPath probably means that the mount is not there any more,
// ErrUnsupportedPath probably means that the mount is not there anymore,
// don't log those cases.
if !strings.Contains(err.Error(), logical.ErrUnsupportedPath.Error()) {
if !strings.Contains(err.Error(), logical.ErrUnsupportedPath.Error()) &&
// ErrSetupReadOnly means the mount's currently being set up.
// Nothing is wrong and there's no cause for alarm, just that we can't get data from it
// yet. We also shouldn't log these cases
!strings.Contains(err.Error(), logical.ErrSetupReadOnly.Error()) {
c.logger.Error("failed to perform internal KV list", "mount_point", m.MountPoint, "error", err)
break
}
Expand Down Expand Up @@ -485,6 +490,90 @@ func (c *Core) walkKvMountSecrets(ctx context.Context, m *kvMount) {
}
}

// getMinNamespaceSecrets is expected to be called on the output
// of GetKvUsageMetrics to get the min number of secrets in a single namespace.
func getMinNamespaceSecrets(mapOfNamespacesToSecrets map[string]int) int {
currentMin := 0
for _, n := range mapOfNamespacesToSecrets {
if n < currentMin || currentMin == 0 {
currentMin = n
}
}
return currentMin
}

// getMaxNamespaceSecrets is expected to be called on the output
// of GetKvUsageMetrics to get the max number of secrets in a single namespace.
func getMaxNamespaceSecrets(mapOfNamespacesToSecrets map[string]int) int {
currentMax := 0
for _, n := range mapOfNamespacesToSecrets {
if n > currentMax {
currentMax = n
}
}
return currentMax
}

// getTotalSecretsAcrossAllNamespaces is expected to be called on the output
// of GetKvUsageMetrics to get the total number of secrets across namespaces.
func getTotalSecretsAcrossAllNamespaces(mapOfNamespacesToSecrets map[string]int) int {
total := 0
for _, n := range mapOfNamespacesToSecrets {
total += n
}
return total
}

// getMeanNamespaceSecrets is expected to be called on the output
// of GetKvUsageMetrics to get the mean number of secrets across namespaces.
func getMeanNamespaceSecrets(mapOfNamespacesToSecrets map[string]int) int {
length := len(mapOfNamespacesToSecrets)
// Avoid divide by zero:
if length == 0 {
return length
}
return getTotalSecretsAcrossAllNamespaces(mapOfNamespacesToSecrets) / length
}

// GetKvUsageMetrics returns a map of namespace paths to KV secret counts within those namespaces.
func (c *Core) GetKvUsageMetrics(ctx context.Context, kvVersion string) (map[string]int, error) {
mounts := c.findKvMounts()
results := make(map[string]int)

if kvVersion == "1" || kvVersion == "2" {
var newMounts []*kvMount
for _, mount := range mounts {
if mount.Version == kvVersion {
newMounts = append(newMounts, mount)
}
}
mounts = newMounts
} else if kvVersion != "0" {
return results, fmt.Errorf("kv version %s not supported, must be 0, 1, or 2", kvVersion)
}

for _, m := range mounts {
select {
case <-ctx.Done():
return nil, fmt.Errorf("context expired")
default:
break
}

c.walkKvMountSecrets(ctx, m)

_, ok := results[m.Namespace.Path]
if ok {
// we need to add, not overwrite
results[m.Namespace.Path] += m.NumSecrets
} else {
results[m.Namespace.Path] = m.NumSecrets
}
}

return results, nil
}

func (c *Core) kvSecretGaugeCollector(ctx context.Context) ([]metricsutil.GaugeLabelValues, error) {
// Find all KV mounts
mounts := c.findKvMounts()
Expand Down
27 changes: 27 additions & 0 deletions vault/core_metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/sdk/logical"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestCoreMetrics_KvSecretGauge(t *testing.T) {
Expand Down Expand Up @@ -246,6 +247,32 @@ func TestCoreMetrics_KvSecretGaugeError(t *testing.T) {
}
}

// TestCoreMetrics_KvUsageMetricsHelperFunctions tests the KV Product Usage
// metrics helper functions designed to be used on the output of GetKvUsageMetrics.
func TestCoreMetrics_KvUsageMetricsHelperFunctions(t *testing.T) {
// This is just "", but it makes it clearer
rootNsPath := namespace.RootNamespace.Path

testMap := map[string]int{
rootNsPath: 10,
"ns1": 20,
"ns3": 30,
}

require.Equal(t, 60, getTotalSecretsAcrossAllNamespaces(testMap))
require.Equal(t, 0, getTotalSecretsAcrossAllNamespaces(map[string]int{}))
require.Equal(t, 10, getTotalSecretsAcrossAllNamespaces(map[string]int{rootNsPath: 10}))
require.Equal(t, 20, getMeanNamespaceSecrets(testMap))
require.Equal(t, 0, getMeanNamespaceSecrets(map[string]int{}))
require.Equal(t, 10, getMeanNamespaceSecrets(map[string]int{rootNsPath: 10}))
require.Equal(t, 30, getMaxNamespaceSecrets(testMap))
require.Equal(t, 0, getMaxNamespaceSecrets(map[string]int{}))
require.Equal(t, 10, getMaxNamespaceSecrets(map[string]int{rootNsPath: 10}))
require.Equal(t, 10, getMinNamespaceSecrets(testMap))
require.Equal(t, 0, getMinNamespaceSecrets(map[string]int{}))
require.Equal(t, 10, getMinNamespaceSecrets(map[string]int{rootNsPath: 10}))
}

func metricLabelsMatch(t *testing.T, actual []metrics.Label, expected map[string]string) {
t.Helper()

Expand Down

0 comments on commit 6ddf44b

Please sign in to comment.