Merge branch 'feat/47-G-fix11-c3d' into feat/47-G-join11
# Conflicts: # modules/distribution/go.mod # modules/distribution/internal/holder/holder_test.go
This commit is contained in:
@@ -1,135 +0,0 @@
|
||||
// Package holder is distribution's holding of the two image seats, `container-image-registry` and
|
||||
// `oci-artifact-registry`, and of the old seat `mesh-artifact-store` until its window closes (novox/hq design 51 *The
|
||||
// four registry seats*, ADR 0605, ADR 0611, ADR 0617 §2). A build module publishes into a seat through `publish` and
|
||||
// `publish-part`: the parts land in distribution's own object store `incoming`, are hashed, checked against the
|
||||
// builder's reservation (`reserved`) and stored in the artifact store, which listens on its node's loopback; the store's port
|
||||
// is the program's, reads open, writes from its own process alone (front.go).
|
||||
package holder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
)
|
||||
|
||||
// The seats distribution holds, and the old one it holds until its window closes.
|
||||
const (
|
||||
ImageSeat = "container-image-registry"
|
||||
ArtifactSeat = "oci-artifact-registry"
|
||||
OldSeat = "mesh-artifact-store"
|
||||
)
|
||||
|
||||
// The verbs of the seats (design 26 *A seat's definition*; design 51 *One contract*).
|
||||
const (
|
||||
VerbPublish = "publish"
|
||||
VerbPublishPart = "publish-part"
|
||||
VerbVersions = "versions"
|
||||
VerbRemove = "remove"
|
||||
VerbMirror = "mirror"
|
||||
VerbTags = "tags"
|
||||
VerbHold = "hold"
|
||||
VerbRelease = "release"
|
||||
)
|
||||
|
||||
// Seats are the seats distribution answers verbs on.
|
||||
var Seats = []string{ImageSeat, ArtifactSeat}
|
||||
|
||||
// VerbsOf are a seat's verbs: mirror and tags on the image seat alone; hold, release and list on both of distribution's.
|
||||
func VerbsOf(seat string) []string {
|
||||
v := []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove, VerbHold, VerbRelease, VerbList}
|
||||
if seat == ImageSeat {
|
||||
v = append(v, VerbMirror, VerbTags)
|
||||
}
|
||||
sort.Strings(v)
|
||||
return v
|
||||
}
|
||||
|
||||
// Callers is the holder's trusted config value `callers`: per verb, the callers that may call it, read as mesh-sdk's
|
||||
// seat-neutral list per verb reads it (ADR 0627 §2). The holder checks the caller the tool runner proved and stamped on
|
||||
// the request (design 25 § 12.4), never a name in the request's body.
|
||||
type Callers = stdio.VerbCallers
|
||||
|
||||
// DefaultCallers is the list design 51 *Who may call which verb* names: publish and publish-part the build modules;
|
||||
// mirror mesh-build-image; remove the builder (a tagged version) and the artifact sweep's holder (retention); hold the
|
||||
// artifact sweep's holder alone; release that holder, and the operator's session only through registry_holds (Operator
|
||||
// below). versions, tags and list are read by anyone on the mesh, as the store's reads are.
|
||||
func DefaultCallers() Callers {
|
||||
builds := []string{"mesh-build-go", "mesh-build-image", "mesh-build-node"}
|
||||
return Callers{
|
||||
VerbPublish: builds,
|
||||
VerbPublishPart: builds,
|
||||
VerbMirror: {"mesh-build-image"},
|
||||
VerbRemove: {"mesh-delivery-builder", SweepHolder},
|
||||
VerbHold: {SweepHolder},
|
||||
VerbRelease: {SweepHolder, Operator},
|
||||
VerbVersions: {Anyone},
|
||||
VerbTags: {Anyone},
|
||||
VerbList: {Anyone},
|
||||
}
|
||||
}
|
||||
|
||||
// SweepHolder is the module holding the seat `mesh-artifact-sweep`, the artifact sweep.
|
||||
const SweepHolder = "artifact-store-tools"
|
||||
|
||||
// The entries of a list that stand for a class, and the caller the runtime names for a call it could not prove.
|
||||
const (
|
||||
Anyone = stdio.AnyCaller
|
||||
Operator = stdio.AnyPerson
|
||||
Unproven = stdio.Unproven
|
||||
)
|
||||
|
||||
// Self is distribution's own account: never on release's list, so registry_holds cannot release in its own name.
|
||||
const Self = "distribution"
|
||||
|
||||
// ParseCallers reads the config value; an empty value is DefaultCallers. A verb no seat has is refused, and Self on
|
||||
// release's list is refused (design 51).
|
||||
func ParseCallers(raw string) (Callers, error) {
|
||||
c, err := stdio.ParseVerbCallers(raw, VerbsOf(ImageSeat), DefaultCallers())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return selfNotReleasing(c)
|
||||
}
|
||||
|
||||
// configured holds the config value `callers`: asked of the runtime once, by a watch, and replaced on each change of
|
||||
// the value, so a call never asks (issue 1966: an ask that timed out refused every verb).
|
||||
var configured = stdio.HoldVerbCallers(CallersConfig, VerbsOf(ImageSeat), DefaultCallers())
|
||||
|
||||
// ConfiguredCallers is the config value `callers` as mesh-config resolved it for this machine, held from the tools
|
||||
// runtime's watch of it (never an environment variable: nothing renders a config value into a program's environment);
|
||||
// unset is DefaultCallers, and Self on release's list is refused as ParseCallers refuses it.
|
||||
func ConfiguredCallers() (Callers, error) {
|
||||
c, err := configured()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return selfNotReleasing(c)
|
||||
}
|
||||
|
||||
// CallersConfig is the config value's key, and CallersTool the tools bundle's tool answering it as resolved: the
|
||||
// program, which has no tools runtime, asks that tool over the bus (manifest invokes).
|
||||
const (
|
||||
CallersConfig = "callers"
|
||||
CallersTool = "registry_callers"
|
||||
)
|
||||
|
||||
// WithCallers is v with the caller list c: a copy for one call, so a list read on that call never changes another's.
|
||||
func (v *Verbs) WithCallers(c Callers) *Verbs {
|
||||
h := *v.Holder
|
||||
h.Callers = c
|
||||
out := *v
|
||||
out.Holder = &h
|
||||
return &out
|
||||
}
|
||||
|
||||
func selfNotReleasing(c Callers) (Callers, error) {
|
||||
if slices.Contains(c[VerbRelease], Self) {
|
||||
return nil, fmt.Errorf("callers puts %s on release's list: registry_holds would release in its own name", Self)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// CallerModule is the module of a proved module caller, which the runtime stamps `<node>.<module>`.
|
||||
func CallerModule(proved string) string { return stdio.ModuleOf(proved) }
|
||||
@@ -65,8 +65,9 @@ type WindowState interface {
|
||||
type Driver struct {
|
||||
Delivery Delivery
|
||||
State WindowState
|
||||
// Callers is the caller list as it holds on this wake: publish's callers are the build modules counted.
|
||||
Callers func() (Callers, error)
|
||||
// BuildModules are the modules claiming a build seat (`node-build-*`) in the catalog half as it holds on this wake:
|
||||
// the build modules counted (design 47 G §610, A-156).
|
||||
BuildModules func() ([]string, error)
|
||||
// Closed says a window's closing on distribution.events.
|
||||
Closed func(c Closed) error
|
||||
Finding func(kind, subject, words string, raised bool)
|
||||
@@ -87,11 +88,10 @@ func (d *Driver) Read() ([]NodeRead, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the publishing builds cannot be read, so no window moves: %w", err)
|
||||
}
|
||||
callers, err := d.Callers()
|
||||
builds, err := d.BuildModules()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the caller list cannot be read, so no window moves: %w", err)
|
||||
return nil, fmt.Errorf("the build modules cannot be read from the catalog half, so no window moves: %w", err)
|
||||
}
|
||||
builds := callers[VerbPublish]
|
||||
byNode := map[string]*NodeRead{}
|
||||
var order []string
|
||||
for _, h := range haves {
|
||||
|
||||
@@ -194,14 +194,15 @@ func (m memHolds) List() ([]Hold, error) {
|
||||
|
||||
func newBed(builderOK bool) *bed {
|
||||
b := &bed{store: &fakeStore{has: map[string]bool{}}, findings: map[string]bool{}, now: time.Unix(1000, 0), cat: &fakeCatalog{tags: map[string]string{}}}
|
||||
b.h = &Holder{Callers: DefaultCallers(), Objects: memObjects{}, Transfers: memTransfers{}, Builder: fakeBuilder{ok: builderOK},
|
||||
b.h = &Holder{Objects: memObjects{}, Transfers: memTransfers{}, Builder: fakeBuilder{ok: builderOK},
|
||||
Store: b.store, Now: func() time.Time { return b.now },
|
||||
Say: func(seat, event string, body Event) error {
|
||||
b.said = append(b.said, said{seat, event, body})
|
||||
return nil
|
||||
},
|
||||
Finding: func(kind, subject, _ string, raised bool) { b.findings[kind+" "+subject] = raised }}
|
||||
b.v = &Verbs{Holder: b.h, Catalog: b.cat, Holds: memHolds{}, Mirrors: memMirrors{}}
|
||||
b.v = &Verbs{Holder: b.h, Catalog: b.cat, Holds: memHolds{}, Mirrors: memMirrors{},
|
||||
SweepHolder: func() (string, error) { return "artifact-store-tools", nil }}
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -221,7 +222,7 @@ func digestOf(b []byte) string { s := sha256.Sum256(b); return "sha256:" + hex.E
|
||||
const builder = "ace.mesh-build-go"
|
||||
|
||||
// TestTheHolderOfTheTwoImageSeats holds design 51's holder cases at the unit level (CAT-D1, D2, D3, D3b, D4, D4b, D5):
|
||||
// one table or case per rule, the caller list and the store front's.
|
||||
// one table or case per rule, the store front's.
|
||||
func TestTheHolderOfTheTwoImageSeats(t *testing.T) {
|
||||
blob := []byte(strings.Repeat("a", 3000))
|
||||
part := Part{Digest: digestOf(blob), Size: int64(len(blob))}
|
||||
@@ -269,8 +270,6 @@ func TestTheHolderOfTheTwoImageSeats(t *testing.T) {
|
||||
do func(b *bed) error
|
||||
want string
|
||||
}{
|
||||
{"a caller not on the list", func(b *bed) error { _, err := b.h.Publish(ArtifactSeat, "ace.mesh-provisioner", req("t")); return err }, "not on the holder's caller list"},
|
||||
{"an unproven caller", func(b *bed) error { _, err := b.h.Publish(ArtifactSeat, Unproven, req("t")); return err }, "no caller the tool runner proved"},
|
||||
{"a part from another caller than the opener", func(b *bed) error {
|
||||
_, _ = b.h.Publish(ArtifactSeat, builder, req("t"))
|
||||
_, err := b.h.PublishPart(ArtifactSeat, "ace.mesh-build-node", PartRequest{Transfer: "t", Digest: part.Digest, Last: true, Data: blob})
|
||||
@@ -319,13 +318,16 @@ func TestTheHolderOfTheTwoImageSeats(t *testing.T) {
|
||||
{"a hold of an image", func(b *bed) error {
|
||||
return b.v.HoldOne(ImageSeat, "novox.artifact-store-tools", HoldRequest{Name: "m/i", Kind: KindImage, Digest: part.Digest})
|
||||
}, "never held"},
|
||||
{"a hold from another caller", func(b *bed) error {
|
||||
return b.v.HoldOne(ArtifactSeat, "novox.mesh-build-go", HoldRequest{Name: "m/a", Kind: KindArchive, Digest: part.Digest})
|
||||
}, "not on the holder's caller list"},
|
||||
{"a release from a caller not on the list", func(b *bed) error {
|
||||
_, err := b.v.Release(ArtifactSeat, "novox.mesh-build-go", HoldRequest{Name: "m/a", Digest: part.Digest})
|
||||
{"a retention removal by the module that held the sweep seat before", func(b *bed) error {
|
||||
b.v.SweepHolder = func() (string, error) { return "artifact-sweeper", nil } // the seat's holder as the seat says it now
|
||||
_, err := b.v.Remove(ArtifactSeat, "novox.artifact-store-tools", RemoveRequest{Name: "m/program", Digest: part.Digest, Reason: ReasonRetention})
|
||||
return err
|
||||
}, "not on the holder's caller list"},
|
||||
}, "artifact sweep's alone"},
|
||||
{"a retention removal while the sweep's holder cannot be read", func(b *bed) error {
|
||||
b.v.SweepHolder = func() (string, error) { return "", errors.New("the seat's holders are not answered") }
|
||||
_, err := b.v.Remove(ArtifactSeat, "novox.artifact-store-tools", RemoveRequest{Name: "m/program", Digest: part.Digest, Reason: ReasonRetention})
|
||||
return err
|
||||
}, "cannot be read"},
|
||||
{"a release in distribution's own name", func(b *bed) error {
|
||||
_, err := b.v.Release(ArtifactSeat, "novox.distribution", HoldRequest{Name: "m/a", Digest: part.Digest})
|
||||
return err
|
||||
@@ -340,9 +342,6 @@ func TestTheHolderOfTheTwoImageSeats(t *testing.T) {
|
||||
t.Errorf("%s: err = %v, want one saying %q", c.name, err, c.want)
|
||||
}
|
||||
}
|
||||
if _, err := ParseCallers(`{"release":["distribution"]}`); err == nil {
|
||||
t.Error("a caller list putting distribution on release's list is taken")
|
||||
}
|
||||
})
|
||||
t.Run("only the missing layers are sent, and an idle transfer is dropped", func(t *testing.T) {
|
||||
b := newBed(true)
|
||||
@@ -776,7 +775,7 @@ func TestTheHolderOfTheTwoImageSeats(t *testing.T) {
|
||||
var closed []string
|
||||
found := map[string]bool{}
|
||||
now := time.Unix(0, 0)
|
||||
d := &Driver{Delivery: del, State: st, Callers: func() (Callers, error) { return DefaultCallers(), nil }, Now: func() time.Time { return now },
|
||||
d := &Driver{Delivery: del, State: st, BuildModules: func() ([]string, error) { return []string{"mesh-build-go", "mesh-build-image", "mesh-build-node"}, nil }, Now: func() time.Time { return now },
|
||||
Closed: func(c Closed) error { closed = append(closed, c.Window+" "+c.Seat); return nil },
|
||||
Finding: func(kind, subject, _ string, raised bool) { found[kind+" "+subject] = raised }}
|
||||
if _, err := d.Wake("start"); err != nil {
|
||||
@@ -869,8 +868,9 @@ func TestTheHolderOfTheTwoImageSeats(t *testing.T) {
|
||||
if _, ok := claims[OldSeat]; !ok {
|
||||
t.Error("the old seat mesh-artifact-store is no longer claimed while its window is open")
|
||||
}
|
||||
if !reflect.DeepEqual(m.Consumes, []string{"mesh-delivery-view.delivered", "node-engine.manifest-reads-changed"}) {
|
||||
t.Errorf("consumes = %v, want delivered and manifest-reads-changed (the windows' wakes)", m.Consumes)
|
||||
if !reflect.DeepEqual(m.Consumes, []string{"mesh-delivery-view.delivered", "node-engine.manifest-reads-changed", "mesh-conditions.findings-asked",
|
||||
"mesh-catalog.catalog-revision", "mesh-catalog.registration-changed", "mesh-delivery.source-moved"}) {
|
||||
t.Errorf("consumes = %v, want delivered and manifest-reads-changed (the windows' wakes) and the catalog half's events", m.Consumes)
|
||||
}
|
||||
// E14: the registry container, on the loopback, is listed before the program, so the port is free when it starts.
|
||||
store, program := -1, -1
|
||||
|
||||
@@ -29,9 +29,6 @@ type StoreFiles interface {
|
||||
// and name not held is an empty answer. A manifest whose content cannot be read fails the answer, by name: a listing
|
||||
// missing one would read as the store not holding it.
|
||||
func (v *Verbs) List(ctx context.Context, seat, caller, name string) ([]reg.Repository, error) {
|
||||
if err := v.Callers.Allowed(VerbList, caller); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if seat != ImageSeat && seat != ArtifactSeat {
|
||||
return nil, fmt.Errorf("list: %s is no seat of distribution's", seat)
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ func TestListAnswersTheStoreThroughTheSeat(t *testing.T) {
|
||||
f := filesWorld()
|
||||
b := newBed(true)
|
||||
b.v.Files = &Files{Container: "mesh-registry", Run: f.run(t)}
|
||||
if err := b.v.HoldOne(ArtifactSeat, SweepHolder, HoldRequest{Name: "app/tools", Digest: dg("a-kept"), Why: "kept"}); err != nil {
|
||||
if err := b.v.HoldOne(ArtifactSeat, "novox.artifact-store-tools", HoldRequest{Name: "app/tools", Digest: dg("a-kept"), Why: "kept"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Package holder is distribution's holding of the two image seats, `container-image-registry` and
|
||||
// `oci-artifact-registry`, and of the old seat `mesh-artifact-store` until its window closes (novox/hq design 51 *The
|
||||
// four registry seats*, ADR 0605, ADR 0611, ADR 0617 §2). A build module publishes into a seat through `publish` and
|
||||
// `publish-part`: the parts land in distribution's own object store `incoming`, are hashed, checked against the
|
||||
// builder's reservation (`reserved`) and stored in the artifact store, which listens on its node's loopback; the store's port
|
||||
// is the program's, reads open, writes from its own process alone (front.go).
|
||||
package holder
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
)
|
||||
|
||||
// The seats distribution holds, and the old one it holds until its window closes.
|
||||
const (
|
||||
ImageSeat = "container-image-registry"
|
||||
ArtifactSeat = "oci-artifact-registry"
|
||||
OldSeat = "mesh-artifact-store"
|
||||
)
|
||||
|
||||
// SweepSeat is the seat of the artifact sweep, whose holder alone removes an untagged version for retention.
|
||||
const SweepSeat = "mesh-artifact-sweep"
|
||||
|
||||
// The verbs of the seats (design 26 *A seat's definition*; design 51 *One contract*).
|
||||
const (
|
||||
VerbPublish = "publish"
|
||||
VerbPublishPart = "publish-part"
|
||||
VerbVersions = "versions"
|
||||
VerbRemove = "remove"
|
||||
VerbMirror = "mirror"
|
||||
VerbTags = "tags"
|
||||
VerbHold = "hold"
|
||||
VerbRelease = "release"
|
||||
)
|
||||
|
||||
// Seats are the seats distribution answers verbs on.
|
||||
var Seats = []string{ImageSeat, ArtifactSeat}
|
||||
|
||||
// VerbsOf are a seat's verbs: mirror and tags on the image seat alone; hold, release and list on both of distribution's.
|
||||
func VerbsOf(seat string) []string {
|
||||
v := []string{VerbPublish, VerbPublishPart, VerbVersions, VerbRemove, VerbHold, VerbRelease, VerbList}
|
||||
if seat == ImageSeat {
|
||||
v = append(v, VerbMirror, VerbTags)
|
||||
}
|
||||
sort.Strings(v)
|
||||
return v
|
||||
}
|
||||
|
||||
// Self is distribution's own account: never the caller of a release, so registry_holds cannot release in its own name.
|
||||
const Self = "distribution"
|
||||
|
||||
// CallerModule is the module of a proved module caller, which the runtime stamps `<node>.<module>`.
|
||||
func CallerModule(proved string) string { return stdio.ModuleOf(proved) }
|
||||
@@ -158,7 +158,6 @@ type Finding func(kind, subject, words string, raised bool)
|
||||
|
||||
// Holder answers the verbs.
|
||||
type Holder struct {
|
||||
Callers Callers
|
||||
Objects Objects
|
||||
Transfers TransferState
|
||||
Builder Builder
|
||||
@@ -245,9 +244,6 @@ func answerOf(r parts.Received) PublishAnswer {
|
||||
|
||||
// Publish is `publish` on seat for the proved caller.
|
||||
func (h *Holder) Publish(seat, caller string, r PublishRequest) (PublishAnswer, error) {
|
||||
if err := h.Callers.Allowed(VerbPublish, caller); err != nil {
|
||||
return PublishAnswer{}, err
|
||||
}
|
||||
if err := checkPublish(seat, r); err != nil {
|
||||
return PublishAnswer{}, err
|
||||
}
|
||||
@@ -272,9 +268,6 @@ func (h *Holder) Publish(seat, caller string, r PublishRequest) (PublishAnswer,
|
||||
|
||||
// PublishPart is `publish-part`: one chunk, from the caller that opened the transfer alone.
|
||||
func (h *Holder) PublishPart(seat, caller string, r PartRequest) (PublishAnswer, error) {
|
||||
if err := h.Callers.Allowed(VerbPublishPart, caller); err != nil {
|
||||
return PublishAnswer{}, err
|
||||
}
|
||||
got, err := h.receiver().Take(r.Transfer, caller, r.Digest, r.Sequence, r.Last, r.Data, func(t Transfer) error {
|
||||
if t.Shape.Seat != seat {
|
||||
return fmt.Errorf("transfer %s is %s's, not %s's", r.Transfer, t.Shape.Seat, seat)
|
||||
|
||||
@@ -85,6 +85,9 @@ type Verbs struct {
|
||||
Mirrors Mirrors
|
||||
// Files are the store's own files, which list reads (ADR 0632 §3).
|
||||
Files StoreFiles
|
||||
// SweepHolder is the module holding the seat `mesh-artifact-sweep`, as the seat's holders say it now (design 47 G §7, §428);
|
||||
// a removal for retention is that module's alone.
|
||||
SweepHolder func() (string, error)
|
||||
}
|
||||
|
||||
// VersionsAnswer is `versions`: the versions the seat holds of name.
|
||||
@@ -96,9 +99,6 @@ type VersionsAnswer struct {
|
||||
|
||||
// Versions is `versions`.
|
||||
func (v *Verbs) Versions(seat, caller, name string) (VersionsAnswer, error) {
|
||||
if err := v.Callers.Allowed(VerbVersions, caller); err != nil {
|
||||
return VersionsAnswer{}, err
|
||||
}
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return VersionsAnswer{}, errors.New("versions: no name")
|
||||
}
|
||||
@@ -139,16 +139,17 @@ const ReasonSharedDigest = reg.RemoveAnswerReasonSharedDigest
|
||||
// Remove is `remove`: the builder removes a tagged version; the artifact sweep's holder an untagged bundle, archive or
|
||||
// copied base with the reason retention, which a hold keeps. Every removal says `removed` with its reason.
|
||||
func (v *Verbs) Remove(seat, caller string, r RemoveRequest) (Removal, error) {
|
||||
if err := v.Callers.Allowed(VerbRemove, caller); err != nil {
|
||||
return Removal{}, err
|
||||
}
|
||||
if strings.TrimSpace(r.Name) == "" || strings.TrimSpace(r.Reason) == "" {
|
||||
return Removal{}, errors.New("remove: a name and a reason are required")
|
||||
}
|
||||
ev := Event{Name: r.Name, Version: r.Version, Kind: r.Kind, Digest: r.Digest, Holder: Self, Reason: r.Reason, By: caller}
|
||||
switch {
|
||||
case r.Reason == ReasonRetention:
|
||||
if CallerModule(caller) != SweepHolder {
|
||||
sweep, err := v.SweepHolder()
|
||||
if err != nil {
|
||||
return Removal{}, fmt.Errorf("remove: the holder of %s cannot be read, so nothing is removed for retention: %w", SweepSeat, err)
|
||||
}
|
||||
if CallerModule(caller) != sweep {
|
||||
return Removal{}, fmt.Errorf("remove: refused for %s: a removal for retention is the artifact sweep's alone", caller)
|
||||
}
|
||||
if r.Digest == "" {
|
||||
@@ -231,9 +232,6 @@ type HoldRequest struct {
|
||||
|
||||
// HoldOne is `hold`: the artifact sweep's holder alone; an image is never held (its own rules keep images).
|
||||
func (v *Verbs) HoldOne(seat, caller string, r HoldRequest) error {
|
||||
if err := v.Callers.Allowed(VerbHold, caller); err != nil {
|
||||
return err
|
||||
}
|
||||
// The seat's contract names no kind: on the image seat a hold is of a copied base (upstream/…), never an image of
|
||||
// its own; on the artifact seat of a bundle or an archive.
|
||||
if r.Kind == "" && seat == ImageSeat && strings.HasPrefix(r.Name, "upstream/") {
|
||||
@@ -261,9 +259,6 @@ func (v *Verbs) Release(seat, caller string, r HoldRequest) (bool, error) {
|
||||
if CallerModule(caller) == Self && !strings.HasPrefix(caller, "person.") {
|
||||
return false, fmt.Errorf("release: refused for %s: distribution cannot release in its own name", caller)
|
||||
}
|
||||
if err := v.Callers.Allowed(VerbRelease, caller); err != nil {
|
||||
return false, err
|
||||
}
|
||||
key := Hold{Seat: seat, Name: r.Name, Digest: r.Digest}.Key()
|
||||
h, held, err := v.Holds.Get(key)
|
||||
if err != nil {
|
||||
@@ -283,9 +278,6 @@ func (v *Verbs) Tags(seat, caller, name string) (VersionsAnswer, error) {
|
||||
if seat != ImageSeat {
|
||||
return VersionsAnswer{}, fmt.Errorf("tags: %s serves no tags", seat)
|
||||
}
|
||||
if err := v.Callers.Allowed(VerbTags, caller); err != nil {
|
||||
return VersionsAnswer{}, err
|
||||
}
|
||||
got, err := v.Catalog.Versions(name)
|
||||
if err != nil {
|
||||
return VersionsAnswer{}, err
|
||||
@@ -317,9 +309,6 @@ func (v *Verbs) Mirror(seat, caller string, r MirrorRequest) (string, error) {
|
||||
if seat != ImageSeat {
|
||||
return "", fmt.Errorf("mirror: %s serves no mirror", seat)
|
||||
}
|
||||
if err := v.Callers.Allowed(VerbMirror, caller); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if r.Source == "" || !strings.HasPrefix(r.Digest, "sha256:") {
|
||||
return "", errors.New("mirror: refused: an upstream image is copied only by its pinned digest")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user