diff --git a/go/seatdef/seatdef.go b/go/seatdef/seatdef.go index fa1dc6e6..34d75038 100644 --- a/go/seatdef/seatdef.go +++ b/go/seatdef/seatdef.go @@ -224,6 +224,9 @@ type Verb struct { // ByCaller says the holder's process answers the verb itself and tells callers apart by the account the bus proved // (design 47 G §554, §621, §656). ByCaller bool `json:"by-caller,omitempty"` + // Chunks says the holder's process sends the answer's file in chunks, each acknowledged by the caller, on the + // subjects mesh.seat..chunk... and mesh.seat..ack... (design 47 G §681). + Chunks bool `json:"chunks,omitempty"` } // The streams an accepted kind may ride (design 47 G §415). diff --git a/go/seatdef/seatdef_test.go b/go/seatdef/seatdef_test.go index 1f159a40..740f958b 100644 --- a/go/seatdef/seatdef_test.go +++ b/go/seatdef/seatdef_test.go @@ -89,6 +89,7 @@ func TestADefinitionIsReadStrictlyNamingWhatIsWrong(t *testing.T) { {"a state offered as a view (design 47 G 699)", withState(whole, `{"name":"asks","template":"state","view":true, "key-forms":[{"key":"","value":{"type":"object"}}]}`), ""}, {"a verb answered by the holder's process by caller (design 47 G 656)", strings.Replace(whole, `"name":"list",`, `"name":"list","by-caller":true,`, 1), ""}, + {"a verb sending its answer's file in chunks (design 47 G 681)", strings.Replace(whole, `"name":"list",`, `"name":"list","by-caller":true,"chunks":true,`, 1), ""}, {"a stream state with an age", withState(whole, `{"name":"placement","template":"events","max-age-days":7, "key-forms":[{"key":"changed.","value":{"type":"object"}}]}`), ""}, {"a state of an unknown template", withState(whole, `{"name":"asks","template":"bucket", diff --git a/go/seatlist/bootstrap/mesh-broker.json b/go/seatlist/bootstrap/mesh-broker.json index fc42bdfc..4fab3163 100644 --- a/go/seatlist/bootstrap/mesh-broker.json +++ b/go/seatlist/bootstrap/mesh-broker.json @@ -209,6 +209,214 @@ }, "optional": true, "class": "warrant" + }, + { + "name": "snapshot-get", + "description": "Send the stream snapshot the bus module took before a drop or recreate: its fields, then the file in chunks of at most 512 KiB on the chunk subject of the call, each acknowledged by the caller on the ack subject within 30 seconds. A caller granted no chunk subjects is answered the fields alone, with chunks 0. Refuses snapshot damaged, snapshot missing, snapshot protected and snapshot taken before milestone G (design 47 G 681).", + "by-caller": true, + "chunks": true, + "class": "read", + "input": { + "type": "object", + "properties": { + "request": { + "type": "string", + "description": "the snapshot's request: the id of the request the keeper took it for, in its state requests", + "minLength": 1 + }, + "call": { + "type": "string", + "description": "128 random bits the caller chooses: the subject token of this send's chunks and acknowledgements", + "minLength": 1 + } + }, + "required": [ + "request", + "call" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "properties": { + "request": { + "type": "string" + }, + "module": { + "type": "string" + }, + "local": { + "type": "string", + "description": "the entry's local name" + }, + "stream": { + "type": "string", + "description": "the stream's name on the bus" + }, + "size": { + "type": "integer", + "minimum": 0 + }, + "digest": { + "type": "string", + "description": "sha256: of the file's bytes" + }, + "at": { + "type": "string", + "format": "date-time", + "description": "when the snapshot was taken, RFC 3339" + }, + "chunks": { + "type": "integer", + "minimum": 0, + "description": "how many chunks are sent; 0 when the caller is granted no chunk subjects" + }, + "error": { + "type": "string", + "description": "why not: snapshot damaged, snapshot missing, snapshot protected or snapshot taken before milestone G; set alone" + } + } + }, + "optional": true + }, + { + "name": "snapshot-restore", + "description": "Restore in place the snapshot of a protected stream the bus module took before a drop or recreate, under the stream's own name: with verify true it only checks the file against its recorded digest and restores nothing; it refuses names live while a live object of the stream's name exists. Answers accepted, busy or error at once; the outcome comes on the event snapshot-restored (design 47 G 681).", + "by-caller": true, + "class": "act", + "input": { + "type": "object", + "properties": { + "request": { + "type": "string", + "description": "the snapshot's request: the id of the request the keeper took it for, in its state requests", + "minLength": 1 + }, + "verify": { + "type": "boolean", + "description": "check the file against its digest and restore nothing" + }, + "why": { + "type": "string", + "description": "why, which the hand-act log keeps" + } + }, + "required": [ + "request", + "why" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "description": "the act accepted, or busy with the act that runs, or the error; exactly one of the three is set (design 47 G 681, 691, A-44)", + "properties": { + "accepted": { + "type": "string", + "description": "accepted: the act's id" + }, + "busy": { + "type": "string", + "description": "busy: the id of the act that runs; it keeps no queue" + }, + "error": { + "type": "string", + "description": "why not; set alone" + } + } + }, + "optional": true + }, + { + "name": "snapshot-restore-cancel", + "description": "Cancel a running restore: it removes the keeper's own partial stream, says snapshot-restored with outcome failed under that act and answers accepted, or answers error when that act has ended (design 47 G 681).", + "by-caller": true, + "class": "act", + "input": { + "type": "object", + "properties": { + "act": { + "type": "string", + "description": "the restore's act id", + "minLength": 1 + }, + "why": { + "type": "string", + "description": "why, which the hand-act log keeps" + } + }, + "required": [ + "act", + "why" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "description": "the act cancelled, or busy with the act that runs, or the error; exactly one of the three is set (design 47 G 681, 691, A-44)", + "properties": { + "accepted": { + "type": "string", + "description": "accepted: the act's id" + }, + "busy": { + "type": "string", + "description": "busy: the id of the act that runs; it keeps no queue" + }, + "error": { + "type": "string", + "description": "why not; set alone" + } + } + }, + "optional": true + }, + { + "name": "snapshot-remove", + "description": "Remove one snapshot and its record now, or every expired one, written in the hand-act log first; a snapshot of before milestone G only by request. Answers accepted, busy or error (design 47 G 681), A-44.", + "by-caller": true, + "class": "act", + "input": { + "type": "object", + "properties": { + "request": { + "type": "string", + "description": "the snapshot's request; or expired true, not both", + "minLength": 1 + }, + "expired": { + "type": "boolean", + "description": "true: every snapshot past snapshot-keep-days" + }, + "why": { + "type": "string", + "description": "why, which the hand-act log keeps" + } + }, + "required": [ + "why" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "description": "the act accepted, or busy with the act that runs, or the error; exactly one of the three is set (design 47 G 681, 691, A-44)", + "properties": { + "accepted": { + "type": "string", + "description": "accepted: the act's id" + }, + "busy": { + "type": "string", + "description": "busy: the id of the act that runs; it keeps no queue" + }, + "error": { + "type": "string", + "description": "why not; set alone" + } + } + }, + "optional": true } ], "events": [ @@ -265,6 +473,42 @@ "body": { "$ref": "#/defs/Said" } + }, + { + "name": "snapshot-restored", + "subject": "snapshot-restored..", + "keep": { + "max-age-days": 90, + "last-per-subject": true + }, + "body": { + "type": "object", + "description": "how one restore in place of a protected snapshot ended, under the act it was answered accepted with, so a retried restore is told apart from an earlier one (design 47 G 681)", + "properties": { + "request": { + "type": "string" + }, + "act": { + "type": "string" + }, + "outcome": { + "type": "string", + "enum": [ + "done", + "failed" + ] + }, + "error": { + "type": "string", + "description": "when failed" + } + }, + "required": [ + "request", + "act", + "outcome" + ] + } } ], "family": { diff --git a/go/seatlist/seatlisttest/seats/mesh-broker.json b/go/seatlist/seatlisttest/seats/mesh-broker.json index fc42bdfc..4fab3163 100644 --- a/go/seatlist/seatlisttest/seats/mesh-broker.json +++ b/go/seatlist/seatlisttest/seats/mesh-broker.json @@ -209,6 +209,214 @@ }, "optional": true, "class": "warrant" + }, + { + "name": "snapshot-get", + "description": "Send the stream snapshot the bus module took before a drop or recreate: its fields, then the file in chunks of at most 512 KiB on the chunk subject of the call, each acknowledged by the caller on the ack subject within 30 seconds. A caller granted no chunk subjects is answered the fields alone, with chunks 0. Refuses snapshot damaged, snapshot missing, snapshot protected and snapshot taken before milestone G (design 47 G 681).", + "by-caller": true, + "chunks": true, + "class": "read", + "input": { + "type": "object", + "properties": { + "request": { + "type": "string", + "description": "the snapshot's request: the id of the request the keeper took it for, in its state requests", + "minLength": 1 + }, + "call": { + "type": "string", + "description": "128 random bits the caller chooses: the subject token of this send's chunks and acknowledgements", + "minLength": 1 + } + }, + "required": [ + "request", + "call" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "properties": { + "request": { + "type": "string" + }, + "module": { + "type": "string" + }, + "local": { + "type": "string", + "description": "the entry's local name" + }, + "stream": { + "type": "string", + "description": "the stream's name on the bus" + }, + "size": { + "type": "integer", + "minimum": 0 + }, + "digest": { + "type": "string", + "description": "sha256: of the file's bytes" + }, + "at": { + "type": "string", + "format": "date-time", + "description": "when the snapshot was taken, RFC 3339" + }, + "chunks": { + "type": "integer", + "minimum": 0, + "description": "how many chunks are sent; 0 when the caller is granted no chunk subjects" + }, + "error": { + "type": "string", + "description": "why not: snapshot damaged, snapshot missing, snapshot protected or snapshot taken before milestone G; set alone" + } + } + }, + "optional": true + }, + { + "name": "snapshot-restore", + "description": "Restore in place the snapshot of a protected stream the bus module took before a drop or recreate, under the stream's own name: with verify true it only checks the file against its recorded digest and restores nothing; it refuses names live while a live object of the stream's name exists. Answers accepted, busy or error at once; the outcome comes on the event snapshot-restored (design 47 G 681).", + "by-caller": true, + "class": "act", + "input": { + "type": "object", + "properties": { + "request": { + "type": "string", + "description": "the snapshot's request: the id of the request the keeper took it for, in its state requests", + "minLength": 1 + }, + "verify": { + "type": "boolean", + "description": "check the file against its digest and restore nothing" + }, + "why": { + "type": "string", + "description": "why, which the hand-act log keeps" + } + }, + "required": [ + "request", + "why" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "description": "the act accepted, or busy with the act that runs, or the error; exactly one of the three is set (design 47 G 681, 691, A-44)", + "properties": { + "accepted": { + "type": "string", + "description": "accepted: the act's id" + }, + "busy": { + "type": "string", + "description": "busy: the id of the act that runs; it keeps no queue" + }, + "error": { + "type": "string", + "description": "why not; set alone" + } + } + }, + "optional": true + }, + { + "name": "snapshot-restore-cancel", + "description": "Cancel a running restore: it removes the keeper's own partial stream, says snapshot-restored with outcome failed under that act and answers accepted, or answers error when that act has ended (design 47 G 681).", + "by-caller": true, + "class": "act", + "input": { + "type": "object", + "properties": { + "act": { + "type": "string", + "description": "the restore's act id", + "minLength": 1 + }, + "why": { + "type": "string", + "description": "why, which the hand-act log keeps" + } + }, + "required": [ + "act", + "why" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "description": "the act cancelled, or busy with the act that runs, or the error; exactly one of the three is set (design 47 G 681, 691, A-44)", + "properties": { + "accepted": { + "type": "string", + "description": "accepted: the act's id" + }, + "busy": { + "type": "string", + "description": "busy: the id of the act that runs; it keeps no queue" + }, + "error": { + "type": "string", + "description": "why not; set alone" + } + } + }, + "optional": true + }, + { + "name": "snapshot-remove", + "description": "Remove one snapshot and its record now, or every expired one, written in the hand-act log first; a snapshot of before milestone G only by request. Answers accepted, busy or error (design 47 G 681), A-44.", + "by-caller": true, + "class": "act", + "input": { + "type": "object", + "properties": { + "request": { + "type": "string", + "description": "the snapshot's request; or expired true, not both", + "minLength": 1 + }, + "expired": { + "type": "boolean", + "description": "true: every snapshot past snapshot-keep-days" + }, + "why": { + "type": "string", + "description": "why, which the hand-act log keeps" + } + }, + "required": [ + "why" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "description": "the act accepted, or busy with the act that runs, or the error; exactly one of the three is set (design 47 G 681, 691, A-44)", + "properties": { + "accepted": { + "type": "string", + "description": "accepted: the act's id" + }, + "busy": { + "type": "string", + "description": "busy: the id of the act that runs; it keeps no queue" + }, + "error": { + "type": "string", + "description": "why not; set alone" + } + } + }, + "optional": true } ], "events": [ @@ -265,6 +473,42 @@ "body": { "$ref": "#/defs/Said" } + }, + { + "name": "snapshot-restored", + "subject": "snapshot-restored..", + "keep": { + "max-age-days": 90, + "last-per-subject": true + }, + "body": { + "type": "object", + "description": "how one restore in place of a protected snapshot ended, under the act it was answered accepted with, so a retried restore is told apart from an earlier one (design 47 G 681)", + "properties": { + "request": { + "type": "string" + }, + "act": { + "type": "string" + }, + "outcome": { + "type": "string", + "enum": [ + "done", + "failed" + ] + }, + "error": { + "type": "string", + "description": "when failed" + } + }, + "required": [ + "request", + "act", + "outcome" + ] + } } ], "family": { diff --git a/go/testdata/beside/CAPTURED b/go/testdata/beside/CAPTURED index 5df55f8a..ab0768f8 100644 --- a/go/testdata/beside/CAPTURED +++ b/go/testdata/beside/CAPTURED @@ -11,7 +11,7 @@ Only what go/catalogue's tests read is captured (R4b, issue 1379). Refreshed 202 repositories; before that it was mesh-controller's copy, which still held two modules that had left the catalogue. mesh-catalog b5dbfbb784507e211372a7c9640d91f7d6dadd84 modules/*/module.json, modules/*/files/**, modules/*/templates/**, predefined.json -mesh-contracts 3658ceb1b68c21e6a3b4a8eca2e35a6e7d82b53b the seat files of the eight bootstrapped seats held there (seats/, health-and-repair/seats/, operator-and-conversation/seats/), as seat.json.captured (milestone 47/G) +mesh-contracts cf5590c7599d3ee3b61c3aa66c88a939ecb41130 the seat files of the eight bootstrapped seats held there (seats/, health-and-repair/seats/, operator-and-conversation/seats/), as seat.json.captured (milestone 47/G) mesh-delivery 5bf848144283055a1ef37441eeda0e439fb6ad7e contracts/seats/mesh-catalog and mesh-delivery-checker, as seat.json.captured (go/seatlist's bootstrap test; milestone 47/G) mesh-host cdcba9ca73755854d21e4e657d7e46c32b9b5265 internal/apply/placement_guard.go diff --git a/go/testdata/beside/mesh-contracts/seats/mesh-broker/seat.json.captured b/go/testdata/beside/mesh-contracts/seats/mesh-broker/seat.json.captured index fc42bdfc..4fab3163 100644 --- a/go/testdata/beside/mesh-contracts/seats/mesh-broker/seat.json.captured +++ b/go/testdata/beside/mesh-contracts/seats/mesh-broker/seat.json.captured @@ -209,6 +209,214 @@ }, "optional": true, "class": "warrant" + }, + { + "name": "snapshot-get", + "description": "Send the stream snapshot the bus module took before a drop or recreate: its fields, then the file in chunks of at most 512 KiB on the chunk subject of the call, each acknowledged by the caller on the ack subject within 30 seconds. A caller granted no chunk subjects is answered the fields alone, with chunks 0. Refuses snapshot damaged, snapshot missing, snapshot protected and snapshot taken before milestone G (design 47 G 681).", + "by-caller": true, + "chunks": true, + "class": "read", + "input": { + "type": "object", + "properties": { + "request": { + "type": "string", + "description": "the snapshot's request: the id of the request the keeper took it for, in its state requests", + "minLength": 1 + }, + "call": { + "type": "string", + "description": "128 random bits the caller chooses: the subject token of this send's chunks and acknowledgements", + "minLength": 1 + } + }, + "required": [ + "request", + "call" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "properties": { + "request": { + "type": "string" + }, + "module": { + "type": "string" + }, + "local": { + "type": "string", + "description": "the entry's local name" + }, + "stream": { + "type": "string", + "description": "the stream's name on the bus" + }, + "size": { + "type": "integer", + "minimum": 0 + }, + "digest": { + "type": "string", + "description": "sha256: of the file's bytes" + }, + "at": { + "type": "string", + "format": "date-time", + "description": "when the snapshot was taken, RFC 3339" + }, + "chunks": { + "type": "integer", + "minimum": 0, + "description": "how many chunks are sent; 0 when the caller is granted no chunk subjects" + }, + "error": { + "type": "string", + "description": "why not: snapshot damaged, snapshot missing, snapshot protected or snapshot taken before milestone G; set alone" + } + } + }, + "optional": true + }, + { + "name": "snapshot-restore", + "description": "Restore in place the snapshot of a protected stream the bus module took before a drop or recreate, under the stream's own name: with verify true it only checks the file against its recorded digest and restores nothing; it refuses names live while a live object of the stream's name exists. Answers accepted, busy or error at once; the outcome comes on the event snapshot-restored (design 47 G 681).", + "by-caller": true, + "class": "act", + "input": { + "type": "object", + "properties": { + "request": { + "type": "string", + "description": "the snapshot's request: the id of the request the keeper took it for, in its state requests", + "minLength": 1 + }, + "verify": { + "type": "boolean", + "description": "check the file against its digest and restore nothing" + }, + "why": { + "type": "string", + "description": "why, which the hand-act log keeps" + } + }, + "required": [ + "request", + "why" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "description": "the act accepted, or busy with the act that runs, or the error; exactly one of the three is set (design 47 G 681, 691, A-44)", + "properties": { + "accepted": { + "type": "string", + "description": "accepted: the act's id" + }, + "busy": { + "type": "string", + "description": "busy: the id of the act that runs; it keeps no queue" + }, + "error": { + "type": "string", + "description": "why not; set alone" + } + } + }, + "optional": true + }, + { + "name": "snapshot-restore-cancel", + "description": "Cancel a running restore: it removes the keeper's own partial stream, says snapshot-restored with outcome failed under that act and answers accepted, or answers error when that act has ended (design 47 G 681).", + "by-caller": true, + "class": "act", + "input": { + "type": "object", + "properties": { + "act": { + "type": "string", + "description": "the restore's act id", + "minLength": 1 + }, + "why": { + "type": "string", + "description": "why, which the hand-act log keeps" + } + }, + "required": [ + "act", + "why" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "description": "the act cancelled, or busy with the act that runs, or the error; exactly one of the three is set (design 47 G 681, 691, A-44)", + "properties": { + "accepted": { + "type": "string", + "description": "accepted: the act's id" + }, + "busy": { + "type": "string", + "description": "busy: the id of the act that runs; it keeps no queue" + }, + "error": { + "type": "string", + "description": "why not; set alone" + } + } + }, + "optional": true + }, + { + "name": "snapshot-remove", + "description": "Remove one snapshot and its record now, or every expired one, written in the hand-act log first; a snapshot of before milestone G only by request. Answers accepted, busy or error (design 47 G 681), A-44.", + "by-caller": true, + "class": "act", + "input": { + "type": "object", + "properties": { + "request": { + "type": "string", + "description": "the snapshot's request; or expired true, not both", + "minLength": 1 + }, + "expired": { + "type": "boolean", + "description": "true: every snapshot past snapshot-keep-days" + }, + "why": { + "type": "string", + "description": "why, which the hand-act log keeps" + } + }, + "required": [ + "why" + ], + "additionalProperties": false + }, + "answer": { + "type": "object", + "description": "the act accepted, or busy with the act that runs, or the error; exactly one of the three is set (design 47 G 681, 691, A-44)", + "properties": { + "accepted": { + "type": "string", + "description": "accepted: the act's id" + }, + "busy": { + "type": "string", + "description": "busy: the id of the act that runs; it keeps no queue" + }, + "error": { + "type": "string", + "description": "why not; set alone" + } + } + }, + "optional": true } ], "events": [ @@ -265,6 +473,42 @@ "body": { "$ref": "#/defs/Said" } + }, + { + "name": "snapshot-restored", + "subject": "snapshot-restored..", + "keep": { + "max-age-days": 90, + "last-per-subject": true + }, + "body": { + "type": "object", + "description": "how one restore in place of a protected snapshot ended, under the act it was answered accepted with, so a retried restore is told apart from an earlier one (design 47 G 681)", + "properties": { + "request": { + "type": "string" + }, + "act": { + "type": "string" + }, + "outcome": { + "type": "string", + "enum": [ + "done", + "failed" + ] + }, + "error": { + "type": "string", + "description": "when failed" + } + }, + "required": [ + "request", + "act", + "outcome" + ] + } } ], "family": {