package delivery import ( "encoding/json" "fmt" "time" ) // A put (novox/hq ADR 0546): one version of a module put on one node by the assigner seat's verb `put`. Each put is the // key `.` of the assigner's state puts, which holds the version asked, the hold, why, and each step the // assigner took or waits on, so a reader watches the key to follow it. The want the put writes carries Put (and Hold), // so the assigner's rules pass over that key while the put holds it. // AssignerPutsBucket is the assigner's bucket of puts, `_puts`. const AssignerPutsBucket = AssignerModule + "_puts" // AssignerPutsHistory is the revisions the puts bucket keeps per key: each step of a put is one. const AssignerPutsHistory = 20 // A put's steps, in the order they are taken (ADR 0546 clauses 3 to 9). A put stands at the last step written. const ( // PutAsked is a put written by the verb, not taken up by a pass yet. PutAsked = "asked" // PutBuild waits for the builder's build at the fingerprint. PutBuild = "build" // PutCompose waits for the controller's composition (and grant) at the fingerprint. PutCompose = "compose" // PutPerson waits for the operator's answer to the ask about the move. PutPerson = "person" // PutSnapshot waits for the bus's streams to be snapshotted after the operator's yes. PutSnapshot = "snapshot" // PutWanted is the want written: the node's have has not reached it yet. PutWanted = "wanted" // PutHolding is a held put whose have runs it healthy: the want stays until released. PutHolding = "holding" // PutDone is a put without hold whose have ran it healthy: the rules write the want again. PutDone = "done" // PutReleased is a put released by the verb `release`. PutReleased = "released" // PutRefused is a put that cannot go on: a build that failed, an answer no, a snapshot not taken. Its words say why. PutRefused = "refused" ) // PutEnded says a put at this step is over: nothing more is done for it and the rules own the want. func PutEnded(step string) bool { return step == PutDone || step == PutReleased || step == PutRefused } // PutStep is one step a put took: when, which, and what was done or waits, in a sentence. type PutStep struct { At time.Time `json:"at"` Step string `json:"step"` Words string `json:"words"` } // PutStepsKept is how many steps a put keeps, newest last. const PutStepsKept = 16 // Put is the value of a key of AssignerPutsBucket. type Put struct { V int `json:"v"` // FP is the fingerprint put, of the new form. FP string `json:"fp"` // Hold keeps the want at FP until released. Hold bool `json:"hold,omitempty"` Why string `json:"why"` // By is who asked, as the bus named the caller; "unproven" when it named none. By string `json:"by,omitempty"` At time.Time `json:"at"` // Step is where the put stands now, one of the Put* steps; Words say it in a sentence. Step string `json:"step"` Words string `json:"words,omitempty"` // Composed is the digest of the composition the want names, once known. Composed string `json:"composed,omitempty"` // Ask is the key, in AssignerAsksBucket, of the ask about the move, once asked. Ask string `json:"ask,omitempty"` // Snapshot says where the bus's streams were snapshotted, once they were. Snapshot string `json:"snapshot,omitempty"` // Steps are the steps taken, oldest first, at most PutStepsKept. Steps []PutStep `json:"steps,omitempty"` } // Said is p at step with words, the step added to its steps when it differs from the last one. func (p Put) Said(at time.Time, step, words string) Put { if p.Step == step && p.Words == words { return p } p.Step, p.Words = step, words p.Steps = append(append([]PutStep(nil), p.Steps...), PutStep{At: at.UTC(), Step: step, Words: words}) if len(p.Steps) > PutStepsKept { p.Steps = p.Steps[len(p.Steps)-PutStepsKept:] } return p } func (p Put) check() error { if err := checkFingerprint(p.FP, "a put's fingerprint"); err != nil { return err } if p.Why == "" { return fmt.Errorf("delivery: a put says why") } switch p.Step { case PutAsked, PutBuild, PutCompose, PutPerson, PutSnapshot, PutWanted, PutHolding, PutDone, PutReleased, PutRefused: default: return fmt.Errorf("delivery: %q is not a step of a put", p.Step) } if p.Composed != "" { return checkDigest(p.Composed, "a put's composition digest") } return nil } // PutKey is the key, in AssignerPutsBucket, of the put of module on node: `.`. func PutKey(node, module string) (string, error) { return HaveKey(node, module) } // ParsePutKey reads a PutKey. func ParsePutKey(key string) (node, module string, err error) { node, module, isNode, err := ParseHaveKey(key) if err == nil && isNode { err = fmt.Errorf("delivery: %q is a node's own key, and no put", key) } return node, module, err } // EncodePut is p as JSON at Version; it refuses a fingerprint not of the new form, a put that says no why and an // unknown step. func EncodePut(p Put) ([]byte, error) { if err := p.check(); err != nil { return nil, err } p.V = Version return json.Marshal(p) } // DecodePut reads a put; it refuses an unknown version (ErrUnknownVersion) and what EncodePut refuses. func DecodePut(data []byte) (Put, error) { var p Put if err := decode(data, &p); err != nil { return Put{}, err } if err := p.check(); err != nil { return Put{}, err } return p, nil }