package buscompose import ( "fmt" "github.com/nats-io/nats.go/jetstream" "git.novox.be/novox/mesh-sdk/go/catalogue" ) // A module's own object bucket (novox/hq ADR 0592 §5, §9). The grants part creates it once, create-only, kept with no // age limit and refusing a new object when full; only its module writes it; another module reads it only through a // `reads` entry naming it and the owner's trusted config value of the bucket's grant naming that module, read-only, as a // restricted stream is heard (ADR 0363 §4, §5). The condition when it passes 75 percent of its cap is the grants part's // (grants.FullCondition). // ObjectBucket is one object bucket a module owns, as the bus holds it. type ObjectBucket struct { Module string Name string // MaxMiB is its cap, catalogue.ObjectsLeastMiB to catalogue.ObjectsMostMiB. MaxMiB int // Restricted says it names a grant, so a module the owner's trusted config value names reads it. Restricted bool } // Bucket is its name on the bus, `_` (catalogue.ObjectsBucket). func (o ObjectBucket) Bucket() string { return catalogue.ObjectsBucket(o.Module, o.Name) } // Stream is the stream the bus keeps it in. func (o ObjectBucket) Stream() string { return "OBJ_" + o.Bucket() } // ObjectsStreamConfig is a module's object bucket as the stream the client library's object store makes of it, with // the mesh's rule: no age limit, its own cap, and a new object refused when full rather than an old one dropped (ADR // 0592 §5); file storage and one replica, as a state's. func ObjectsStreamConfig(o ObjectBucket) jetstream.StreamConfig { b := o.Bucket() return jetstream.StreamConfig{ Name: o.Stream(), Description: fmt.Sprintf("%s's object bucket %q (novox/hq ADR 0592 §5): written by %s alone, read by the modules its "+ "grant names, kept with no age limit and at most %d MiB, a new object refused when full", o.Module, o.Name, o.Module, o.MaxMiB), Subjects: []string{"$O." + b + ".C.>", "$O." + b + ".M.>"}, MaxBytes: int64(o.MaxMiB) * 1024 * 1024, Storage: jetstream.FileStorage, Replicas: 1, Discard: jetstream.DiscardNew, AllowRollup: true, AllowDirect: true, MaxMsgs: -1, MaxConsumers: -1, } } // objectReadGrants is what reading one object bucket needs and nothing more: binding it, a direct get of an object's // metadata, an ordered consumer on its chunks or its names (a list or a watch), that consumer's deletion and flow // control. No publish under `$O.`, so no write; no purge. func objectReadGrants(bucket string) []string { stream := "OBJ_" + bucket return []string{ "$JS.API.STREAM.INFO." + stream, "$JS.API.DIRECT.GET." + stream + ".>", "$JS.API.CONSUMER.CREATE." + stream + ".>", "$JS.API.CONSUMER.DELETE." + stream + ".>", "$JS.FC." + stream + ".>", } } // objectGrants is what a principal publishes for its module's own object buckets (written, and a replaced or deleted // object's chunks purged) and for those it reads (read-only), never their creation, which is the grants part's. func objectGrants(own, reads []string) []string { var out []string for _, b := range own { if !safeSubject.MatchString(b) { continue } out = append(out, "$O."+b+".>", "$JS.API.STREAM.PURGE.OBJ_"+b) out = append(out, objectReadGrants(b)...) } for _, b := range reads { if safeSubject.MatchString(b) { out = append(out, objectReadGrants(b)...) } } return out } // objectNames is the bus names of a module's own object buckets. func objectNames(objects []ObjectBucket) []string { out := make([]string, 0, len(objects)) for _, o := range objects { out = append(out, o.Bucket()) } return out } // writesOwnObjects is a module writing one of its own object buckets (ADR 0592 §5), itself or through the runtime that // carries it, and nothing of another's. func writesOwnObjects(p Principal, pattern string) bool { switch p.Kind { case KindModule: for _, b := range p.Objects { if pattern == "$O."+b+".>" { return true } } case KindNodeTools: for _, d := range p.Carries { for _, b := range objectNames(d.Objects) { if pattern == "$O."+b+".>" { return true } } } } return false }