After the build ended the module pushes the packed bundle with mesh-sdk's PushBlob under <module>/<artifact> by its digest, a candidate's as a trunk build's, and the step answers artifact-store://<module>/<artifact>/blobs/<digest> instead of the archive's path. A push that fails fails the step not stored: <artifact>: <the store's words>; the packed bytes are never written to the node. The manifest requires artifact-store and places its address.
100 lines
3.2 KiB
Go
100 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// pushed are the blobs the fake artifact store took, by digest, for the tests that read an archive back.
|
|
var pushed sync.Map
|
|
|
|
// fakeStore is a store of the test's own: its address in RegistryFile, each blob kept by repository and digest; fail,
|
|
// when set, is the store's refusal.
|
|
type fakeStore struct {
|
|
mu sync.Mutex
|
|
blobs map[string][]byte // "<repository>@<digest>"
|
|
fail error
|
|
}
|
|
|
|
func withStore(t *testing.T, m *Module) *fakeStore {
|
|
t.Helper()
|
|
s := &fakeStore{blobs: map[string][]byte{}}
|
|
m.RegistryFile = filepath.Join(t.TempDir(), "registry")
|
|
if err := os.WriteFile(m.RegistryFile, []byte("store.test:5000\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m.Push = func(ctx context.Context, registry, repository, digest string, body []byte) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if s.fail != nil {
|
|
return s.fail
|
|
}
|
|
if sum := sha256.Sum256(body); "sha256:"+hex.EncodeToString(sum[:]) != digest || registry != "store.test:5000" {
|
|
t.Errorf("pushed %s to %s with bytes of another digest", digest, registry)
|
|
}
|
|
s.blobs[repository+"@"+digest] = append([]byte(nil), body...)
|
|
pushed.Store(digest, s.blobs[repository+"@"+digest])
|
|
return nil
|
|
}
|
|
return s
|
|
}
|
|
|
|
// archiveOf is the archive a build pushed, by its digest.
|
|
func archiveOf(t *testing.T, r BuildResult) []byte {
|
|
t.Helper()
|
|
raw, ok := pushed.Load(r.Digest)
|
|
if !ok {
|
|
t.Fatalf("nothing was pushed under %s", r.Digest)
|
|
}
|
|
return raw.([]byte)
|
|
}
|
|
|
|
// 1587-S6 (ADR 0498 §1): a done bundle answers the reference of the archive it pushed, under the manifest's module.
|
|
func TestADoneBundleAnswersTheReferenceOfTheArchiveItPushed(t *testing.T) {
|
|
m, _, _ := builtMachine(t, ``)
|
|
store := withStore(t, m)
|
|
out, err := m.build(context.Background(), buildArgs())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := out.(BuildResult)
|
|
if want := "artifact-store://m/tools/blobs/" + r.Digest; r.Reference != want {
|
|
t.Errorf("reference %q, want %q", r.Reference, want)
|
|
}
|
|
if store.blobs["m/tools@"+r.Digest] == nil {
|
|
t.Errorf("the store holds %d blobs, not m/tools@%s", len(store.blobs), r.Digest)
|
|
}
|
|
}
|
|
|
|
// 1587-S7 and S8 (ADR 0498 §2, the lead's decision L9): a push the store refuses fails the step `not stored:
|
|
// <artifact>: <the store's words>`, and whatever the push answered, no packed file is left on the node.
|
|
func TestARefusedPushFailsNotStoredAndNoPackedFileStays(t *testing.T) {
|
|
for _, fail := range []error{nil, errors.New("store.test:5000 refused the blob: 507 Insufficient Storage")} {
|
|
m, _, work := builtMachine(t, ``)
|
|
withStore(t, m).fail = fail
|
|
_, err := m.build(context.Background(), buildArgs())
|
|
if fail != nil && (err == nil || !strings.HasPrefix(err.Error(), delivery.NotStored+"tools: ") || !strings.Contains(err.Error(), "507")) {
|
|
t.Errorf("a refused push answered %v", err)
|
|
}
|
|
var archives []string
|
|
filepath.WalkDir(work, func(p string, d os.DirEntry, err error) error {
|
|
if err == nil && strings.HasSuffix(p, ".tar.gz") {
|
|
archives = append(archives, p)
|
|
}
|
|
return nil
|
|
})
|
|
if len(archives) != 0 {
|
|
t.Errorf("push answered %v: packed files left on the node: %v", fail, archives)
|
|
}
|
|
}
|
|
}
|