Files
secret/internal/cli/info_helper.go
T
sneak 1bb8134756
check / check (push) Waiting to run
Count every file of each version in secret info's Total Size (closes #147)
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
2026-10-14 23:41:49 +00:00

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
}