All checks were successful
check / check (push) Successful in 1m8s
- Replace .golangci.yml with the canonical strict config (all linters enabled except the standard disable list; lll 88, funlen 80/50, cyclop 15, dupl 100; test files now linted) - Pin the Dockerfile lint stage to golangci/golangci-lint:v2.12.2 by tag and digest (Debian-based) - Fix all ~1550 findings surfaced by the new config: line wrapping, wsl_v5/nlreturn blank lines, noinlineerr splits, err113 sentinel errors, perfsprint/modernize rewrites, goconst constants, thelper, testifylint, noctx CommandContext, testpackage conversions, t.Parallel() where safe, and complexity/dupl helper extraction - Record the change and follow-up items in TODO.md
123 lines
2.9 KiB
Go
123 lines
2.9 KiB
Go
package cli
|
|
|
|
import (
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"git.eeqj.de/sneak/secret/internal/secret"
|
|
"github.com/spf13/afero"
|
|
)
|
|
|
|
// 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 size of encrypted data
|
|
dataPath := filepath.Join(versionPath, "data.age")
|
|
|
|
stat, err := fs.Stat(dataPath)
|
|
if err == nil {
|
|
s.totalSize += stat.Size()
|
|
}
|
|
|
|
// Add size of metadata
|
|
metaPath := filepath.Join(versionPath, "metadata.age")
|
|
|
|
stat, err = fs.Stat(metaPath)
|
|
if err == nil {
|
|
s.totalSize += stat.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
|
|
}
|