mesh-vault: a store file that is not a record is listed by name with its error (issue 1409, V10)
mesh/delivery merged: source.main moved to novox/mesh-catalog@792a79720b30
mesh/check checking 45b0779b6801 on 792a79720b30
mesh/delivery merged: source.main moved to novox/mesh-catalog@792a79720b30
mesh/check checking 45b0779b6801 on 792a79720b30
Store.ListReadable lists every record and, beside them, each file it cannot read by name with its error; RecordsHeld lists those as rows with an error and goes on. recordsError stays for a store that cannot be listed at all. Store.List and ListPrefix keep their behaviour for their other callers. Builds: design 39 bus-and-switch §20; design 39 parts §12. Lead answers (issue 1409): V9 (keep recordsCount and recordsError), V10.
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -102,7 +103,7 @@ type RecordHeld struct {
|
||||
// RecordsHeld lists every record the store keeps, by name, ordered as the store lists them. The fingerprint is the
|
||||
// vault's own of the whole value, as the ledger gives it.
|
||||
func (v *Vault) RecordsHeld() ([]RecordHeld, error) {
|
||||
records, err := v.Store.List()
|
||||
records, bad, err := v.Store.ListReadable()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -122,5 +123,9 @@ func (v *Vault) RecordsHeld() ([]RecordHeld, error) {
|
||||
}
|
||||
out = append(out, row)
|
||||
}
|
||||
for _, b := range bad {
|
||||
out = append(out, RecordHeld{Name: b.Name, Error: "the vault could not read the record's file: " + b.Err.Error()})
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -138,6 +139,10 @@ func TestSecretHoldersListsEveryStoreRecordByNameAndFingerprintNeverTheValue(t *
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(v.Store.pathOf("pair/notjson/one"), []byte("{not json"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got, err := toolNamed(t, definitions(&Env{Vault: v.Vault}), "secret_holders").Run(map[string]any{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -151,8 +156,8 @@ func TestSecretHoldersListsEveryStoreRecordByNameAndFingerprintNeverTheValue(t *
|
||||
for _, r := range m["records"].([]RecordHeld) {
|
||||
rows[r.Name] = r
|
||||
}
|
||||
if m["recordsCount"] != 4 || len(rows) != 4 {
|
||||
t.Fatalf("records %v, want 4 (two licence secrets, a pair, a broken one)", rows)
|
||||
if m["recordsCount"] != 5 || len(rows) != 5 {
|
||||
t.Fatalf("records %v, want 5 (two licence secrets, a pair, a sealed-broken one, a file that is not JSON)", rows)
|
||||
}
|
||||
for part, value := range map[string]string{LicenceGrant: grant, LicenceAccess: access} {
|
||||
r := rows["licence.jane_at_example.org."+part]
|
||||
@@ -167,4 +172,10 @@ func TestSecretHoldersListsEveryStoreRecordByNameAndFingerprintNeverTheValue(t *
|
||||
if r := rows["pair/broken/one"]; r.Error == "" || r.Fingerprint != "" {
|
||||
t.Errorf("a record the vault cannot open must stay listed with an error: %+v", r)
|
||||
}
|
||||
if r := rows["pair/notjson/one"]; r.Error == "" || r.Fingerprint != "" {
|
||||
t.Errorf("a file that is not JSON must be listed by name with its error, the listing going on (V10): %+v", r)
|
||||
}
|
||||
if _, bad := m["recordsError"]; bad {
|
||||
t.Errorf("recordsError is for a store that cannot be listed at all (V10): %v", m["recordsError"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,6 +327,42 @@ func (s *Store) ListPrefix(prefix string) ([]Record, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Unreadable is a record file the store could not read, by the name its file gives.
|
||||
type Unreadable struct {
|
||||
Name string
|
||||
Err error
|
||||
}
|
||||
|
||||
// ListReadable is every record by name, and beside them each file that is not a record (not valid JSON, say) by name
|
||||
// with its error: the listing goes on past it (issue 1409, V10). Only an unreadable directory is an error.
|
||||
func (s *Store) ListReadable() ([]Record, []Unreadable, error) {
|
||||
entries, err := os.ReadDir(filepath.Join(s.dir, "secrets"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var out []Record
|
||||
var bad []Unreadable
|
||||
for _, e := range entries {
|
||||
if !strings.HasSuffix(e.Name(), ".json") {
|
||||
continue
|
||||
}
|
||||
name, err := url.PathUnescape(strings.TrimSuffix(e.Name(), ".json"))
|
||||
if err != nil {
|
||||
bad = append(bad, Unreadable{Name: strings.TrimSuffix(e.Name(), ".json"), Err: err})
|
||||
continue
|
||||
}
|
||||
r, ok, err := s.Read(name)
|
||||
if err != nil {
|
||||
bad = append(bad, Unreadable{Name: name, Err: err})
|
||||
} else if ok {
|
||||
out = append(out, r)
|
||||
}
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
|
||||
sort.Slice(bad, func(i, j int) bool { return bad[i].Name < bad[j].Name })
|
||||
return out, bad, nil
|
||||
}
|
||||
|
||||
type operatorFile struct {
|
||||
Key string `json:"key"`
|
||||
Since time.Time `json:"since"`
|
||||
|
||||
Reference in New Issue
Block a user