diff --git a/internal/broker/buildbacklog.go b/internal/broker/buildbacklog.go index abd880a8..49f9b538 100644 --- a/internal/broker/buildbacklog.go +++ b/internal/broker/buildbacklog.go @@ -9,8 +9,23 @@ import "git.novox.be/novox/mesh-sdk/go/delivery" const ( // ViewModule is the module of the delivery view (mesh-delivery-view). ViewModule = "mesh-delivery-view" + // BuilderModule is the module of the builder (mesh-delivery-builder). + BuilderModule = "mesh-delivery-builder" ) +// BuildAskReads are the API subjects the builder requests when it takes over, at its start, a key naming an ask (issue +// 1910): each build seat's stream's information and its messages by sequence, to tell whether the ask is on its stream. +// Reads only: no consumer, no pull, no acknowledgement; the builder puts its asks by its seat use, as before. +func BuildAskReads() []string { + var out []string + for _, lang := range delivery.AskLanguages { + if stream, err := delivery.AskStream(lang); err == nil { + out = append(out, "$JS.API.STREAM.INFO."+stream, "$JS.API.STREAM.MSG.GET."+stream) + } + } + return out +} + // BuildBacklogReads are the API subjects the view requests: the information of each build seat's stream and of each of its // shared workers (delivery.AskStream, delivery.AskWorker), and the message of the stream by sequence. Reads only: no // message is pulled, none acknowledged, none published. diff --git a/internal/broker/buildbacklog_test.go b/internal/broker/buildbacklog_test.go index 606d8126..3aa99531 100644 --- a/internal/broker/buildbacklog_test.go +++ b/internal/broker/buildbacklog_test.go @@ -67,3 +67,22 @@ func TestTheViewReadsTheBuildSeatsWorkersByNameAndPublishesNoAsk(t *testing.T) { t.Errorf("another module reads the build seats: %v", other) } } + +// 1910: the builder may read each build seat's stream's information and its messages by sequence, by name, and may not +// pull, acknowledge or make a consumer there, nor list every stream. +func TestTheBuilderReadsTheBuildSeatsStreamsByName(t *testing.T) { + pub, _ := composedFor(t, BuilderModule) + for _, s := range []string{"$JS.API.STREAM.INFO.SEAT_NODE_BUILD_GO", "$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_GO", + "$JS.API.STREAM.INFO.SEAT_NODE_BUILD_NODE", "$JS.API.STREAM.MSG.GET.SEAT_NODE_BUILD_IMAGE"} { + if !hasSubject(pub, s) { + t.Errorf("the builder cannot request %s", s) + } + } + perms, _ := PermissionsFor(Principal{Kind: KindModule, Node: "one", Module: BuilderModule}) + for _, s := range []string{"$JS.API.STREAM.NAMES", "$JS.API.STREAM.INFO.SEAT_OPERATOR_CHANNEL", + "$JS.API.CONSUMER.MSG.NEXT.SEAT_NODE_BUILD_GO.SEAT_NODE_BUILD_GO_CHECK_worker", "$JS.API.CONSUMER.CREATE.SEAT_NODE_BUILD_GO"} { + if MayPublish(perms, s) { + t.Errorf("the builder may publish %s", s) + } + } +} diff --git a/internal/broker/nats.go b/internal/broker/nats.go index df3c4401..01cf591d 100644 --- a/internal/broker/nats.go +++ b/internal/broker/nats.go @@ -860,6 +860,11 @@ func PermissionsFor(p Principal) (Permissions, error) { if p.Module == ViewModule { pub = append(pub, BuildBacklogReads()...) } + // The builder alone reads the build seats' streams by sequence, to put again at its start an ask a key names that no + // stream holds (issue 1910). + if p.Module == BuilderModule { + pub = append(pub, BuildAskReads()...) + } // 1. Its own namespace: it publishes its events there and serves its tools there. Nothing // else may publish into it, so an event's source is a fact the server enforces rather // than a claim in the body (design 29 ยง2).