Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/gnacho/netgrip
go 1.26.0

require (
github.com/gnacho/netpulse/agent v1.5.5
github.com/gnacho/netpulse/agent v1.5.6
gopkg.in/yaml.v3 v3.0.1
)

Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/gnacho/netpulse/agent v1.5.4 h1:dxQwJY5z81DlUyZUgDlx21TRzaJc8j3CHNB2u/KYS6w=
github.com/gnacho/netpulse/agent v1.5.4/go.mod h1:TEBH43X4N9t/UbxQzTg0WTi1V1eKZYU2zc0v4REbg90=
github.com/gnacho/netpulse/agent v1.5.5 h1:hpRWCFED4dVAjyD3myPcebslNMB/87WiS4jBHh5s/4M=
github.com/gnacho/netpulse/agent v1.5.5/go.mod h1:TEBH43X4N9t/UbxQzTg0WTi1V1eKZYU2zc0v4REbg90=
github.com/gnacho/netpulse/agent v1.5.6 h1:ANjkn+EDgYCihw1XBcyqm+Lg0tST1UEtBfXnte2IKTE=
github.com/gnacho/netpulse/agent v1.5.6/go.mod h1:TEBH43X4N9t/UbxQzTg0WTi1V1eKZYU2zc0v4REbg90=
github.com/gonzalop/mq v0.9.11 h1:cKdoCT3dgiz3kV53QKUDqtcdDHCpMHS8nXrxVhqt3gE=
github.com/gonzalop/mq v0.9.11/go.mod h1:QVPtaf408IuhzPJO4w2Q0sjzM+nP6FWHyfyXUjvY/PQ=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
Expand Down
42 changes: 29 additions & 13 deletions internal/modules/netpulse_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,19 +503,20 @@ func applyNetPulseAgent(p netpulsePaths) {
}

opts := runtime.Options{
Server: cfg.Server,
Token: cfg.Token,
Slug: cfg.Slug,
ServerFP: cfg.ServerFP,
Interval: parseNetPulseInterval(cfg.Interval),
ScanInterval: parseNetPulseScanInterval(cfg.ScanInterval),
WanTarget: cfg.WanTarget,
GwTarget: cfg.GwTarget,
EnvFile: p.env,
Version: version,
Kind: "netgrip",
SelfMQTT: netPulseSelfMQTT,
OnUpgrade: netPulseUpgradeTrigger,
Server: cfg.Server,
Token: cfg.Token,
Slug: cfg.Slug,
ServerFP: cfg.ServerFP,
Interval: parseNetPulseInterval(cfg.Interval),
ScanInterval: parseNetPulseScanInterval(cfg.ScanInterval),
WanTarget: cfg.WanTarget,
GwTarget: cfg.GwTarget,
EnvFile: p.env,
Version: version,
Kind: "netgrip",
SelfMQTT: netPulseSelfMQTT,
OnUpgrade: netPulseUpgradeTrigger,
OnConfigBackup: netPulseConfigBackupTrigger,
}

ctx, cancel := context.WithCancel(base)
Expand Down Expand Up @@ -618,6 +619,21 @@ func statusCallbackGen(gen int) func(runtime.Status) {
}
}

// netPulseConfigBackupTrigger: evento SSE "config_backup" del servidor
// NetPulse (netpulse#1462 / netgrip#553): snapshot standalone de la config,
// independiente del flujo de firmware. Crea un snapshot fresco con el
// módulo propio y lo empuja al endpoint genérico de backups. Best-effort:
// los fallos van a log; NetPulse descubre el resultado al recibir (o no) la
// subida.
func netPulseConfigBackupTrigger(_ string) {
res := CreateAndPushSnapshot()
if !res.Ok {
log.Printf("netpulse: config_backup solicitado por el servidor falló: %s", res.Error)
return
}
log.Printf("netpulse: config_backup solicitado por el servidor → snapshot %s subido", res.SnapshotID)
}

// netPulseUpgradeTrigger: evento SSE "upgrade" del servidor NetPulse (#363):
// NetGrip se actualiza a sí mismo con su propio updater (sus releases de
// GitHub, sha verificado); el servidor solo dispara. Si ya está en la última,
Expand Down
17 changes: 17 additions & 0 deletions internal/modules/snapshot_push.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ func SetPushConfig(cfg PushConfig) error {
return os.WriteFile(pushConfigPath, data, 0600)
}

// CreateAndPushSnapshot (#553): crea un snapshot fresco y lo empuja a
// NetPulse por el camino existente. Es la respuesta al evento SSE
// "config_backup" de netpulse#1462: un backup standalone, independiente del
// flujo de upgrade. Devuelve el resultado del push (o del propio create).
func CreateAndPushSnapshot() PushResult {
if _, err := createSnapshotFn(); err != nil {
return PushResult{Error: fmt.Sprintf("create snapshot: %s", err)}
}
return pushLatestFn()
}

// Inyectables en tests (mismo patrón que netPulseMQTTEnvPath).
var (
createSnapshotFn = CreateSnapshot
pushLatestFn = PushLatestSnapshot
)

func PushLatestSnapshot() PushResult {
cfg := GetPushConfig()
if cfg.ServerURL == "" || cfg.RouterID == "" {
Expand Down
45 changes: 45 additions & 0 deletions internal/modules/snapshot_push_553_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package modules

import (
"errors"
"testing"
)

var errTestSnapshot = errors.New("create failed")

// #553: CreateAndPushSnapshot crea un snapshot fresco y empuja el último.
// Los pasos son inyectables: sin OpenWrt (sin uci) el create real fallaría.
func TestCreateAndPushSnapshot(t *testing.T) {
oldCreate, oldPush := createSnapshotFn, pushLatestFn
t.Cleanup(func() { createSnapshotFn, pushLatestFn = oldCreate, oldPush })

t.Run("create falla y no empuja", func(t *testing.T) {
createSnapshotFn = func() (*ConfigSnapshot, error) { return nil, errTestSnapshot }
pushed := false
pushLatestFn = func() PushResult { pushed = true; return PushResult{Ok: true} }
res := CreateAndPushSnapshot()
if res.Ok || pushed {
t.Fatalf("create falló: got %+v pushed=%v", res, pushed)
}
})

t.Run("create ok y empuja el resultado", func(t *testing.T) {
createSnapshotFn = func() (*ConfigSnapshot, error) {
return &ConfigSnapshot{ID: "20261021-000000"}, nil
}
pushLatestFn = func() PushResult { return PushResult{Ok: true, SnapshotID: "20261021-000000"} }
res := CreateAndPushSnapshot()
if !res.Ok || res.SnapshotID != "20261021-000000" {
t.Fatalf("ok: got %+v", res)
}
})

t.Run("el push fallo se propaga", func(t *testing.T) {
createSnapshotFn = func() (*ConfigSnapshot, error) { return &ConfigSnapshot{ID: "x"}, nil }
pushLatestFn = func() PushResult { return PushResult{Error: "push failed"} }
res := CreateAndPushSnapshot()
if res.Ok || res.Error != "push failed" {
t.Fatalf("push falló: got %+v", res)
}
})
}
Loading