store: P2R-10 schema for source-group + host-default hooks (migration 0010)

Adds pre_hook/post_hook BLOB columns to source_groups and
pre_hook_default/post_hook_default to hosts. Bytes stored verbatim
(AEAD encrypt/decrypt happens at the HTTP layer where the AEAD key
lives). Round-trip tests cover set/clear semantics on both tables.
This commit is contained in:
2026-05-04 10:52:16 +01:00
parent c9b49637d1
commit 18b0bf976d
5 changed files with 190 additions and 11 deletions
+24 -4
View File
@@ -42,7 +42,8 @@ func (s *Store) LookupHostByAgentToken(ctx context.Context, tokenHash string) (*
enrolled_at, last_seen_at, status, repo_id, tags,
current_job_id, last_backup_at, last_backup_status,
repo_size_bytes, snapshot_count, open_alert_count,
applied_schedule_version, bandwidth_up_kbps, bandwidth_down_kbps
applied_schedule_version, bandwidth_up_kbps, bandwidth_down_kbps,
pre_hook_default, post_hook_default
FROM hosts WHERE agent_token_hash = ?`,
tokenHash)
return scanHost(row)
@@ -55,7 +56,8 @@ func (s *Store) GetHost(ctx context.Context, id string) (*Host, error) {
enrolled_at, last_seen_at, status, repo_id, tags,
current_job_id, last_backup_at, last_backup_status,
repo_size_bytes, snapshot_count, open_alert_count,
applied_schedule_version, bandwidth_up_kbps, bandwidth_down_kbps
applied_schedule_version, bandwidth_up_kbps, bandwidth_down_kbps,
pre_hook_default, post_hook_default
FROM hosts WHERE id = ?`, id)
return scanHost(row)
}
@@ -116,7 +118,8 @@ func (s *Store) ListHosts(ctx context.Context) ([]Host, error) {
enrolled_at, last_seen_at, status, repo_id, tags,
current_job_id, last_backup_at, last_backup_status,
repo_size_bytes, snapshot_count, open_alert_count,
applied_schedule_version, bandwidth_up_kbps, bandwidth_down_kbps
applied_schedule_version, bandwidth_up_kbps, bandwidth_down_kbps,
pre_hook_default, post_hook_default
FROM hosts ORDER BY name`)
if err != nil {
return nil, fmt.Errorf("store: list hosts: %w", err)
@@ -155,13 +158,15 @@ func scanHostRow(s hostScanner) (*Host, error) {
enrolled string
tags string
bwUp, bwDown sql.NullInt64
preHook, postHook []byte
)
err := s.Scan(&h.ID, &h.Name, &h.OS, &h.Arch,
&h.AgentVersion, &h.ResticVersion, &h.ProtocolVersion,
&enrolled, &lastSeen, &h.Status, &repoID, &tags,
&currentJob, &lastBackupAt, &lastBkSt,
&h.RepoSizeBytes, &h.SnapshotCount, &h.OpenAlertCount,
&h.AppliedScheduleVersion, &bwUp, &bwDown)
&h.AppliedScheduleVersion, &bwUp, &bwDown,
&preHook, &postHook)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNotFound
@@ -210,9 +215,24 @@ func scanHostRow(s hostScanner) (*Host, error) {
v := int(bwDown.Int64)
h.BandwidthDownKBps = &v
}
h.PreHookDefault = preHook
h.PostHookDefault = postHook
return &h, nil
}
// SetHostHooks replaces the host-wide pre/post hook defaults. Pass
// nil/empty to clear that hook. Stored verbatim — caller is expected
// to encrypt the bytes before they reach this layer.
func (s *Store) SetHostHooks(ctx context.Context, hostID string, pre, post []byte) error {
_, err := s.db.ExecContext(ctx,
`UPDATE hosts SET pre_hook_default = ?, post_hook_default = ? WHERE id = ?`,
nullableBytes(pre), nullableBytes(post), hostID)
if err != nil {
return fmt.Errorf("store: set host hooks: %w", err)
}
return nil
}
// SetHostBandwidth replaces the host's upload/download caps. Pass nil
// to clear a cap. Caller decides validation; non-positive caps are
// treated as "no cap" by the agent regardless.