check / check (push) Waiting to run
Total Size added up each version's data.age and metadata.age, but a version keeps its value in value.age, so no secret's value was counted. It now adds up every file in each version directory: the value, the metadata and the version's two keys, as the plan on the issue decided. A version directory that cannot be read adds nothing and gets a warning naming it. The new tests add a 10000-byte secret and check that the total grows by at least that much, and check the warning for a version directory that cannot be read. Model: opus-5-5
122 lines
3.0 KiB
Go
122 lines
3.0 KiB
Go
package cli
|
|
|
|
import (
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/spf13/afero"
|
|
"sneak.berlin/go/secret/internal/secret"
|
|
)
|
|
|
|
// vaultStats accumulates statistics while walking vault directories
|
|
type vaultStats struct {
|
|
totalSecrets int
|
|
totalSize int64
|
|
oldestTime time.Time
|
|
latestTime time.Time
|
|
}
|
|
|
|
// addVersion accumulates size and timestamp info for one version directory
|
|
func (s *vaultStats) addVersion(fs afero.Fs, versionPath string) {
|
|
// Add the size of every file in the version directory: its value,
|
|
// metadata and keys. A directory that cannot be read adds nothing and
|
|
// gets a warning; its timestamp is still tracked below.
|
|
files, err := afero.ReadDir(fs, versionPath)
|
|
if err != nil {
|
|
secret.Warn("Could not read version directory",
|
|
"directory", versionPath, "error", err)
|
|
}
|
|
|
|
for _, file := range files {
|
|
if !file.IsDir() {
|
|
s.totalSize += file.Size()
|
|
}
|
|
}
|
|
|
|
// Track timestamps
|
|
stat, err := fs.Stat(versionPath)
|
|
if err == nil {
|
|
modTime := stat.ModTime()
|
|
if s.oldestTime.IsZero() || modTime.Before(s.oldestTime) {
|
|
s.oldestTime = modTime
|
|
}
|
|
|
|
if s.latestTime.IsZero() || modTime.After(s.latestTime) {
|
|
s.latestTime = modTime
|
|
}
|
|
}
|
|
}
|
|
|
|
// addSecret accumulates stats for one secret directory
|
|
func (s *vaultStats) addSecret(fs afero.Fs, secretsPath, secretName string) {
|
|
s.totalSecrets++
|
|
secretPath := filepath.Join(secretsPath, secretName)
|
|
|
|
// Get size and timestamps from all versions
|
|
versionsPath := filepath.Join(secretPath, "versions")
|
|
|
|
versionEntries, err := afero.ReadDir(fs, versionsPath)
|
|
if err != nil {
|
|
secret.Warn("Could not read versions directory for secret",
|
|
"secret", secretName, "error", err)
|
|
|
|
return
|
|
}
|
|
|
|
for _, versionEntry := range versionEntries {
|
|
if !versionEntry.IsDir() {
|
|
continue
|
|
}
|
|
|
|
s.addVersion(fs, filepath.Join(versionsPath, versionEntry.Name()))
|
|
}
|
|
}
|
|
|
|
// addVault accumulates stats for one vault directory
|
|
func (s *vaultStats) addVault(fs afero.Fs, vaultsDir, vaultName string) {
|
|
vaultPath := filepath.Join(vaultsDir, vaultName)
|
|
secretsPath := filepath.Join(vaultPath, "secrets.d")
|
|
|
|
// Count secrets in this vault
|
|
secretEntries, err := afero.ReadDir(fs, secretsPath)
|
|
if err != nil {
|
|
secret.Warn("Could not read secrets directory for vault",
|
|
"vault", vaultName, "error", err)
|
|
|
|
return
|
|
}
|
|
|
|
for _, secretEntry := range secretEntries {
|
|
if !secretEntry.IsDir() {
|
|
continue
|
|
}
|
|
|
|
s.addSecret(fs, secretsPath, secretEntry.Name())
|
|
}
|
|
}
|
|
|
|
// gatherVaultStats collects statistics from all vaults, returning the
|
|
// total secret count, total size, and oldest/latest secret timestamps
|
|
func gatherVaultStats(
|
|
fs afero.Fs,
|
|
vaultsDir string,
|
|
) (int, int64, time.Time, time.Time, error) {
|
|
vaultEntries, err := afero.ReadDir(fs, vaultsDir)
|
|
if err != nil {
|
|
return 0, 0, time.Time{}, time.Time{}, err
|
|
}
|
|
|
|
var stats vaultStats
|
|
|
|
for _, vaultEntry := range vaultEntries {
|
|
if !vaultEntry.IsDir() {
|
|
continue
|
|
}
|
|
|
|
stats.addVault(fs, vaultsDir, vaultEntry.Name())
|
|
}
|
|
|
|
return stats.totalSecrets, stats.totalSize,
|
|
stats.oldestTime, stats.latestTime, nil
|
|
}
|