Files
nmon/internal/backup/backup_vm.go

76 lines
2.4 KiB
Go

package backup
import (
"context"
"fmt"
"os"
"path/filepath"
"time"
"github.com/v10networks/nmon/internal/config"
"github.com/v10networks/nmon/internal/vmsup"
)
// addMetricStores snapshots each store through its own API, archives the snapshot directory under
// "<name>/" and deletes the snapshot. A store without a local DataDir is skipped with a manifest note.
func addMetricStores(ctx context.Context, o *Options, aw *archiveWriter, m *Manifest) error {
for _, vs := range o.Stores {
if vs.DataDir == "" || vs.URL == "" {
m.Notes = append(m.Notes, fmt.Sprintf("store %s is external and was not backed up", vs.Name))
continue
}
sm, err := backupOneStore(ctx, o, aw, vs, m.Boundary)
if err != nil {
return fmt.Errorf("backup: %s store: %w", vs.Name, err)
}
m.Stores = append(m.Stores, sm)
logf(o.Log, "backup: %s store snapshot archived (%d series fingerprinted)\n", vs.Name, len(sm.Series))
}
return nil
}
func backupOneStore(ctx context.Context, o *Options, aw *archiveWriter, vs VMStore, boundary time.Time) (sm StoreManifest, err error) {
api := vmAPI{hc: o.HTTP, base: vs.URL}
name, err := api.createSnapshot(ctx)
if err != nil {
return sm, err
}
defer func() {
// The caller's context may be the reason we are failing; deleting the snapshot must still happen.
dctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), time.Minute)
defer cancel()
if derr := api.deleteSnapshot(dctx, name); derr != nil && err == nil {
err = derr
}
}()
dir := filepath.Join(vs.DataDir, "snapshots", name)
if _, err := os.Stat(dir); err != nil {
return sm, fmt.Errorf("snapshot directory: %w", err)
}
if err := aw.addTree(vs.Name, dir); err != nil {
return sm, err
}
series, err := api.sampleChecks(ctx, boundary)
if err != nil {
return sm, fmt.Errorf("fingerprint series: %w", err)
}
return StoreManifest{Name: vs.Name, Series: series}, nil
}
// StoresFromStatus turns the supervisor's Status() into the stores to back up. An external store has no
// local data directory, so it is listed without one and skipped.
func StoresFromStatus(dataDir string, status []vmsup.ChildStatus) []VMStore {
out := make([]VMStore, 0, len(status))
for _, c := range status {
vs := VMStore{Name: c.Store, URL: c.URL}
if !c.External {
vs.DataDir = config.HotDir(dataDir)
if c.Store == vmsup.StoreLong {
vs.DataDir = config.LongDir(dataDir)
}
}
out = append(out, vs)
}
return out
}