VAULT-31075 CE changes (#28845)

This commit is contained in:
Violet Hynes
2024-11-06 12:50:46 -05:00
committed by GitHub
parent 2e4a30f914
commit a7ffab97d0
2 changed files with 119 additions and 3 deletions

View File

@@ -6,6 +6,7 @@ package vault
import (
"context"
"errors"
"fmt"
"os"
"strings"
"time"
@@ -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{
@@ -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
}
@@ -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()

View File

@@ -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) {
@@ -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()