zfs: read an unwritten ledger instance as state null and sequence 0, pass every error up (review finding 2, A-40)
Builds: design 42 zfs-owns-zfs A-40
This commit is contained in:
@@ -162,15 +162,22 @@ func (busLedger) Apply(instance, operation string, body any, call string) error
|
||||
return err
|
||||
}
|
||||
|
||||
// absentInstance says whether a ledger get said the instance is not there, which an instance never written is.
|
||||
func absentInstance(err error) bool {
|
||||
s := strings.ToLower(err.Error())
|
||||
for _, w := range []string{"not found", "no instance", "no such", "does not exist", "unknown instance", "not kept"} {
|
||||
if strings.Contains(s, w) {
|
||||
return true
|
||||
}
|
||||
// takenOf reads a ledger get's answer: an instance never written answers state null and sequence 0, which is empty (A-40).
|
||||
func takenOf(raw []byte) (struct {
|
||||
Taken map[string]LedgerEntry `json:"taken"`
|
||||
}, error) {
|
||||
var body struct {
|
||||
Taken map[string]LedgerEntry `json:"taken"`
|
||||
}
|
||||
return false
|
||||
var st buscalls.InstanceState
|
||||
if err := json.Unmarshal(raw, &st); err != nil {
|
||||
return body, err
|
||||
}
|
||||
if st.Sequence == 0 && (len(st.State) == 0 || string(st.State) == "null") {
|
||||
return body, nil
|
||||
}
|
||||
err := json.Unmarshal(st.State, &body)
|
||||
return body, err
|
||||
}
|
||||
|
||||
func (busLedger) Entries(datasets []string) ([]LedgerEntry, error) {
|
||||
@@ -179,19 +186,10 @@ func (busLedger) Entries(datasets []string) ([]LedgerEntry, error) {
|
||||
for _, kind := range keepKinds {
|
||||
raw, err := stdio.BusState(buscalls.LedgerGet, buscalls.InstanceRequest{Ledger: LedgerName, Instance: LedgerInstance(kind, ds)})
|
||||
if err != nil {
|
||||
if absentInstance(err) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var st buscalls.InstanceState
|
||||
if err := json.Unmarshal(raw, &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var body struct {
|
||||
Taken map[string]LedgerEntry `json:"taken"`
|
||||
}
|
||||
if err := json.Unmarshal(st.State, &body); err != nil {
|
||||
body, err := takenOf(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, name := range sortedKeys(body.Taken) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestTakenOfLedgerAnswers(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
name, raw string
|
||||
want int
|
||||
err bool
|
||||
}{
|
||||
{"never written", `{"instance":"x","state":null,"sequence":0}`, 0, false},
|
||||
{"written", `{"instance":"x","state":{"taken":{"a":{}}},"sequence":3}`, 1, false},
|
||||
{"bad state", `{"instance":"x","state":"oops","sequence":3}`, 0, true},
|
||||
{"bad answer", `nope`, 0, true},
|
||||
} {
|
||||
b, err := takenOf([]byte(c.raw))
|
||||
if (err != nil) != c.err || len(b.Taken) != c.want {
|
||||
t.Errorf("%s: %v %d", c.name, err, len(b.Taken))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user