chore: publish pxmon v0.2.0
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CapacityDiskPoint struct {
|
||||
Mount string `json:"mount"`
|
||||
UsedBytes uint64 `json:"used_bytes"`
|
||||
TotalBytes uint64 `json:"total_bytes"`
|
||||
}
|
||||
|
||||
type CapacitySnapshot struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Disks []CapacityDiskPoint `json:"disks,omitempty"`
|
||||
}
|
||||
|
||||
type CapacityStore struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
func NewCapacityStore(baseDir string) *CapacityStore {
|
||||
if strings.TrimSpace(baseDir) == "" {
|
||||
baseDir = "."
|
||||
}
|
||||
return &CapacityStore{dir: filepath.Join(baseDir, "history", "capacity")}
|
||||
}
|
||||
|
||||
func (s *CapacityStore) Path(clusterID string) string {
|
||||
return filepath.Join(s.dir, sanitizeClusterID(clusterID)+".jsonl")
|
||||
}
|
||||
|
||||
func (s *CapacityStore) Append(clusterID string, snap CapacitySnapshot) error {
|
||||
if strings.TrimSpace(clusterID) == "" {
|
||||
return errors.New("empty cluster id")
|
||||
}
|
||||
if snap.Timestamp.IsZero() {
|
||||
snap.Timestamp = time.Now().UTC()
|
||||
}
|
||||
if len(snap.Disks) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := os.MkdirAll(s.dir, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.OpenFile(s.Path(clusterID), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
b, err := json.Marshal(snap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(append(b, '\n'))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *CapacityStore) Load(clusterID string, since time.Time) ([]CapacitySnapshot, error) {
|
||||
if strings.TrimSpace(clusterID) == "" {
|
||||
return nil, errors.New("empty cluster id")
|
||||
}
|
||||
f, err := os.Open(s.Path(clusterID))
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return []CapacitySnapshot{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
sc := bufio.NewScanner(f)
|
||||
sc.Buffer(make([]byte, 0, 64*1024), 8*1024*1024)
|
||||
out := make([]CapacitySnapshot, 0, 512)
|
||||
for sc.Scan() {
|
||||
line := strings.TrimSpace(sc.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
var snap CapacitySnapshot
|
||||
if err := json.Unmarshal([]byte(line), &snap); err != nil {
|
||||
continue
|
||||
}
|
||||
if !since.IsZero() && snap.Timestamp.Before(since) {
|
||||
continue
|
||||
}
|
||||
if len(snap.Disks) == 0 {
|
||||
continue
|
||||
}
|
||||
out = append(out, snap)
|
||||
}
|
||||
if err := sc.Err(); err != nil {
|
||||
return nil, fmt.Errorf("scan capacity: %w", err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
Reference in New Issue
Block a user