64 lines
3.0 KiB
Go
64 lines
3.0 KiB
Go
// Package containerimageregistryclient is the container-image-registry seat's client (novox/hq ADR 0627 §1, §2; ADR 0611, design 51
|
|
// *Publishing*): it publishes a file in parts to the seat's holder. `publish` opens the transfer naming every part,
|
|
// the holder answers the parts it lacks, and only those travel by `publish-part`, in chunks a bus message carries; a
|
|
// cut transfer goes on from what the holder answers when the same transfer is opened again. The sending in parts is
|
|
// mesh-sdk's seat-neutral runtime (parts); this client binds it to this seat's verbs and types alone. The call is the
|
|
// caller's own (a bundle's stdio.Ask, a process's call of a seat verb), so it holds no transport.
|
|
package containerimageregistryclient
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
|
|
"git.novox.be/novox/mesh-sdk/go/parts"
|
|
)
|
|
|
|
// Call asks one verb of the seat, `seat:<seat>.<verb>`, with a body, and answers its JSON.
|
|
type Call func(key string, body any) (json.RawMessage, error)
|
|
|
|
// Sent is what a Publish did: the transfer, whether the holder held its digest already, the parts it lacked and were
|
|
// sent, and the chunks they took.
|
|
type Sent struct {
|
|
Transfer string
|
|
parts.Sent
|
|
}
|
|
|
|
// Publish opens (or opens again) the transfer in on the seat and sends the parts the holder answers it lacks, each in
|
|
// chunks of at most parts.MaxChunk, in sequence, the last marked. in.Parts is written from parts. A part whose bytes
|
|
// do not hash to its digest is refused before anything of it is sent; a part the holder does not name is never read.
|
|
// Its outcome, published or refused, is the holder's event, which the caller waits for.
|
|
func Publish(ctx context.Context, call Call, in seat.PublishInput, ps []parts.Part) (Sent, error) {
|
|
open := func(_ context.Context, ps []parts.Part) (parts.Opened, error) {
|
|
in.Parts = make([]seat.Part, 0, len(ps))
|
|
for _, p := range ps {
|
|
in.Parts = append(in.Parts, seat.Part{Digest: p.Digest, Size: p.Size})
|
|
}
|
|
raw, err := call("seat:"+seat.Seat+"."+seat.VerbPublish, in)
|
|
if err != nil {
|
|
return parts.Opened{}, fmt.Errorf("%s's publish of %s: %w", seat.Seat, in.Name, err)
|
|
}
|
|
var a seat.PublishAnswer
|
|
if err := json.Unmarshal(raw, &a); err != nil {
|
|
return parts.Opened{}, fmt.Errorf("%s's answer to publish: %w", seat.Seat, err)
|
|
}
|
|
return parts.Opened{Held: a.Held, Lacks: a.Lacks}, nil
|
|
}
|
|
chunk := func(_ context.Context, part string, sequence int, last bool, data []byte) error {
|
|
raw, err := call("seat:"+seat.Seat+"."+seat.VerbPublishPart, seat.PublishPartInput{Transfer: in.Transfer, Part: part,
|
|
Sequence: int64(sequence), Last: last, Data: base64.StdEncoding.EncodeToString(data)})
|
|
if err != nil {
|
|
return fmt.Errorf("%s's publish-part: %w", seat.Seat, err)
|
|
}
|
|
var a seat.PublishPartAnswer
|
|
if err := json.Unmarshal(raw, &a); err != nil {
|
|
return fmt.Errorf("%s's answer to publish-part: %w", seat.Seat, err)
|
|
}
|
|
return nil
|
|
}
|
|
sent, err := parts.Send(ctx, open, chunk, ps)
|
|
return Sent{Transfer: in.Transfer, Sent: sent}, err
|
|
}
|