Merge pull request 'mesh-build-image names its copy wait's ownership mark nonce, so the state takes it and nats images are stored again (issue 1966)' (#645) from fix/1966-mirror-waits-token into main
This commit was merged in pull request #645.
This commit is contained in:
@@ -99,7 +99,7 @@ func (m *Module) copy(ctx context.Context, args map[string]any) (any, error) {
|
|||||||
now = m.Now
|
now = m.Now
|
||||||
}
|
}
|
||||||
asked := CopyWait{Digest: digest, Module: module, Artifact: name, From: from, Node: m.Node,
|
asked := CopyWait{Digest: digest, Module: module, Artifact: name, From: from, Node: m.Node,
|
||||||
Asked: now().UTC(), Token: strconv.FormatInt(now().UnixNano(), 36) + "-" + m.Node}
|
Asked: now().UTC(), Nonce: strconv.FormatInt(now().UnixNano(), 36) + "-" + m.Node}
|
||||||
// Waited for before the ask, in memory and in the state mirror-waits, so an outcome said at once, or heard by another
|
// Waited for before the ask, in memory and in the state mirror-waits, so an outcome said at once, or heard by another
|
||||||
// instance of the module (the seat's events reach one of them), is not missed.
|
// instance of the module (the seat's events reach one of them), is not missed.
|
||||||
heard, stop := m.Outcomes.Expect(digest)
|
heard, stop := m.Outcomes.Expect(digest)
|
||||||
@@ -107,7 +107,7 @@ func (m *Module) copy(ctx context.Context, args map[string]any) (any, error) {
|
|||||||
if _, err := m.Outcomes.Waits.Put(key, asked); err != nil {
|
if _, err := m.Outcomes.Waits.Put(key, asked); err != nil {
|
||||||
return nil, fmt.Errorf("%s%s: keeping the copy's wait in %s: %v", builds.NotStored, name, MirrorWaitsState, err)
|
return nil, fmt.Errorf("%s%s: keeping the copy's wait in %s: %v", builds.NotStored, name, MirrorWaitsState, err)
|
||||||
}
|
}
|
||||||
defer m.Outcomes.drop(key, asked.Token)
|
defer m.Outcomes.drop(key, asked.Nonce)
|
||||||
said := buildstep.NarrateSeat(ctx, buildstep.ImageSeat, "mirror of "+from)
|
said := buildstep.NarrateSeat(ctx, buildstep.ImageSeat, "mirror of "+from)
|
||||||
raw, err := m.call()("seat:"+buildstep.ImageSeat+"."+reg.VerbMirror, reg.MirrorInput{Name: module + "/" + name, Source: from, Digest: digest})
|
raw, err := m.call()("seat:"+buildstep.ImageSeat+"."+reg.VerbMirror, reg.MirrorInput{Name: module + "/" + name, Source: from, Digest: digest})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -170,7 +170,8 @@ const MirrorWaitsState = "mirror-waits"
|
|||||||
// MirrorWaitKey is a copy's key in mirror-waits: its digest, with the colon a key cannot hold as a dash.
|
// MirrorWaitKey is a copy's key in mirror-waits: its digest, with the colon a key cannot hold as a dash.
|
||||||
func MirrorWaitKey(digest string) string { return strings.Replace(digest, ":", "-", 1) }
|
func MirrorWaitKey(digest string) string { return strings.Replace(digest, ":", "-", 1) }
|
||||||
|
|
||||||
// CopyWait is one value of mirror-waits: the copy asked, and its outcome once heard.
|
// CopyWait is one value of mirror-waits: the copy asked, and its outcome once heard. Nonce tells this copy's wait from
|
||||||
|
// a later copy's of the same digest; it is no credential, and no field here may be named as one (the state refuses it).
|
||||||
type CopyWait struct {
|
type CopyWait struct {
|
||||||
Digest string `json:"digest"`
|
Digest string `json:"digest"`
|
||||||
Module string `json:"module"`
|
Module string `json:"module"`
|
||||||
@@ -178,7 +179,7 @@ type CopyWait struct {
|
|||||||
From string `json:"from"`
|
From string `json:"from"`
|
||||||
Node string `json:"node"`
|
Node string `json:"node"`
|
||||||
Asked time.Time `json:"asked"`
|
Asked time.Time `json:"asked"`
|
||||||
Token string `json:"token"`
|
Nonce string `json:"nonce"`
|
||||||
Outcome *MirrorOutcome `json:"outcome,omitempty"`
|
Outcome *MirrorOutcome `json:"outcome,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,9 +282,9 @@ func (o *Outcomes) kept(key string) (MirrorOutcome, bool) {
|
|||||||
return *w.Outcome, true
|
return *w.Outcome, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// drop removes key's wait when it is still the copy's own (token): a later copy of the same digest keeps its own.
|
// drop removes key's wait when it is still the copy's own (nonce): a later copy of the same digest keeps its own.
|
||||||
func (o *Outcomes) drop(key, token string) {
|
func (o *Outcomes) drop(key, nonce string) {
|
||||||
if w, err := o.read(key); err == nil && w != nil && w.Token == token {
|
if w, err := o.read(key); err == nil && w != nil && w.Nonce == nonce {
|
||||||
_ = o.Waits.Delete(key)
|
_ = o.Waits.Delete(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,6 +115,9 @@ func (w *memWaits) Put(key string, value any) (uint64, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
if f := credentialShaped(b); f != "" {
|
||||||
|
return 0, fmt.Errorf("the state refuses a field %q, which names a credential", f)
|
||||||
|
}
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
if w.m == nil {
|
if w.m == nil {
|
||||||
w.m = map[string]json.RawMessage{}
|
w.m = map[string]json.RawMessage{}
|
||||||
@@ -339,3 +342,38 @@ func tool(t *testing.T, m *Module, name string) func(map[string]any) (any, error
|
|||||||
t.Fatalf("no tool %s", name)
|
t.Fatalf("no tool %s", name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// credentialShaped is the first field name in a JSON value that ends as the state's guard refuses (the bus's
|
||||||
|
// credentialEndings), or "": the in-memory state refuses it as the bus does, so every copy test meets the guard.
|
||||||
|
func credentialShaped(value []byte) string {
|
||||||
|
var v any
|
||||||
|
if json.Unmarshal(value, &v) != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var walk func(any) string
|
||||||
|
walk = func(v any) string {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case map[string]any:
|
||||||
|
for k, x := range t {
|
||||||
|
norm := strings.NewReplacer("-", "", "_", "", " ", "").Replace(strings.ToLower(k))
|
||||||
|
for _, end := range []string{"password", "passwd", "secret", "token", "credential", "credentials",
|
||||||
|
"authorization", "apikey", "privatekey", "accesskey", "cookie"} {
|
||||||
|
if strings.HasSuffix(norm, end) {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f := walk(x); f != "" {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case []any:
|
||||||
|
for _, x := range t {
|
||||||
|
if f := walk(x); f != "" {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return walk(v)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user