92 lines
4.0 KiB
Go
92 lines
4.0 KiB
Go
package buildstep
|
|
|
|
// **The late publish** (novox/hq ADR 0617 §1, design 51 *The versions*, *Publishing*; CAT-BG2, CAT-BI1): a trunk build
|
|
// whose number the builder did not know yet publishes its digest unnumbered; the builder says version.numbered once it
|
|
// numbers that fingerprint, and the build module publishes the held digest again under that version. Nothing travels:
|
|
// the publish names the one part by its digest, which the holder holds, so it answers held and tags the version (the
|
|
// holder asks builder_versions first, ADR 0605 §4). The bytes never stayed on the node, so a holder that no longer holds
|
|
// the digest is said and the event offered again. Every instance may hear the same event; the publish is the same call
|
|
// under the same transfer name, so a second one is answered held. One place for every build module: mesh-build-go's
|
|
// bundles and archives on oci-artifact-registry, mesh-build-image's images on container-image-registry.
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"slices"
|
|
"time"
|
|
|
|
reg "git.novox.be/novox/mesh-delivery/contracts/go/seats/containerimageregistry"
|
|
builds "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
|
|
"git.novox.be/novox/mesh-delivery/sdk/parts"
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
// NumberedPattern is the event type a build module consumes for the late publish, as its manifest's `consumes` names it.
|
|
const NumberedPattern = builds.BuilderModule + "." + builds.VersionNumberedEvent
|
|
|
|
// errNotHere is a held part's Open: its bytes did not stay on the node.
|
|
var errNotHere = errors.New("the built bytes did not stay on the node; the holder must hold the digest")
|
|
|
|
// LatePublish publishes a numbered build's held digest under its version on seat, from the builder's version.numbered
|
|
// body, when its kind is one of kinds. It answers false, nil for an event that is not the module's to publish: another
|
|
// kind, another seat (an event naming none is the artifact seat's, as the builder wrote it before images), or a version
|
|
// numbered with no digest.
|
|
func LatePublish(ctx context.Context, call SeatCall, body []byte, seat string, kinds ...string) (bool, error) {
|
|
n, err := builds.DecodeVersionNumbered(body)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
on := n.Seat
|
|
if on == "" {
|
|
on = ArtifactSeat
|
|
}
|
|
if n.Digest == "" || on != seat || !slices.Contains(kinds, n.Kind) {
|
|
return false, nil
|
|
}
|
|
in := reg.PublishInput{Name: n.Name, Kind: n.Kind, Version: n.Version, Tag: n.Tag, Digest: n.Digest,
|
|
Fingerprint: n.Fingerprint, Commit: n.Commit, Transfer: TransferName(n.Name, "v"+n.Version, n.Digest)}
|
|
sent, err := OnSeat(ctx, seat, "late publish of "+n.Name+" "+n.Version+" at "+n.Digest, func() (parts.Sent, error) {
|
|
return PublishParts(ctx, call, seat, in, []parts.Part{{Digest: n.Digest,
|
|
Open: func() (io.ReadCloser, error) { return nil, errNotHere }}})
|
|
}, nil)
|
|
if err != nil {
|
|
return false, fmt.Errorf("the late publish of %s@%s (%s): %w", n.Name, n.Version, n.Digest, err)
|
|
}
|
|
if len(sent.Parts) > 0 {
|
|
return false, fmt.Errorf("the late publish of %s@%s sent parts it should not have", n.Name, n.Version)
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// HearNumbered subscribes late to the builder's version.numbered, asking again until the runtime takes it, and says
|
|
// each publish and each failure; an error is the event's, which is offered again.
|
|
func HearNumbered(ctx context.Context, late func(ctx context.Context, body []byte) (bool, error)) {
|
|
handle := func(e stdio.Envelope) error {
|
|
did, err := late(ctx, e.Body)
|
|
if err != nil {
|
|
log.Printf("%v", err)
|
|
return err
|
|
}
|
|
if did {
|
|
log.Printf("published a numbered build's held digest under its version (%s)", e.Headers["x-event-id"])
|
|
}
|
|
return nil
|
|
}
|
|
for wait := 2 * time.Second; ; wait = min(wait*2, time.Minute) {
|
|
err := stdio.Subscribe(NumberedPattern, handle)
|
|
if err == nil {
|
|
log.Printf("listening for the builder's %s", NumberedPattern)
|
|
return
|
|
}
|
|
log.Printf("not hearing the builder's %s yet (%v); asking again in %s", NumberedPattern, err, wait)
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(wait):
|
|
}
|
|
}
|
|
}
|