CAT-PB: buildstep.PushBlob is removed, its last users write through the seats

No build module writes into a store any more: mesh-build-go's store test
plays the holder with its own two-request upload, as mesh-build-node's does.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-04 15:51:58 +01:00
parent d47d231ec3
commit 6da45bc090
3 changed files with 34 additions and 324 deletions
@@ -3,13 +3,16 @@
package main
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"os/exec"
"path/filepath"
@@ -23,8 +26,6 @@ import (
registry "git.novox.be/novox/mesh-contracts/go/seats/containerimageregistry"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
)
// realStore starts the artifact store's own program (the distribution registry, modules/distribution) inside this
@@ -53,7 +54,7 @@ func realStore(t *testing.T, m *Module) string {
defer s.mu.Unlock()
for k, b := range s.blobs {
name, digest, _ := strings.Cut(k, "@")
if err := buildstep.PushBlob(context.Background(), http.DefaultClient, addr, name, digest, b); err != nil {
if err := uploadBlob(srv.URL, name, digest, b); err != nil {
return nil, err
}
delete(s.blobs, k)
@@ -63,6 +64,36 @@ func realStore(t *testing.T, m *Module) string {
return addr
}
// uploadBlob is the holder's own write into its store: an upload opened, then its bytes put by their digest (the
// distribution protocol's two requests), as distribution stores a part it took whole.
func uploadBlob(base, name, digest string, body []byte) error {
resp, err := http.Post(base+"/v2/"+name+"/blobs/uploads/", "", nil)
if err != nil {
return err
}
resp.Body.Close()
loc, err := url.Parse(resp.Header.Get("Location"))
if err != nil || resp.StatusCode != http.StatusAccepted {
return fmt.Errorf("the store opened no upload of %s: %s %v", digest, resp.Status, err)
}
b, _ := url.Parse(base)
u := b.ResolveReference(loc)
q := u.Query()
q.Set("digest", digest)
u.RawQuery = q.Encode()
req, _ := http.NewRequest(http.MethodPut, u.String(), bytes.NewReader(body))
req.Header.Set("Content-Type", "application/octet-stream")
put, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
put.Body.Close()
if put.StatusCode != http.StatusCreated {
return fmt.Errorf("the store did not take %s: %s", digest, put.Status)
}
return nil
}
// resolve reads a done artifact's reference from the store as a node-engine does (catalogue.Routed): the seat's form
// oci-artifact-registry/<module>/<artifact>@<digest> is <store>/v2/<module>/<artifact>/blobs/<digest> (ADR 0605 §9).
func resolve(t *testing.T, addr string, a delivery.BuiltArtifact) []byte {