mirror of
https://github.com/anchore/syft.git
synced 2026-11-02 07:26:34 +01:00
chore: revert custom index in favor of key_Split
Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
This commit is contained in:
+82
-24
@@ -2,6 +2,7 @@ package bus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/wagoodman/go-partybus"
|
||||
"github.com/wagoodman/go-progress"
|
||||
@@ -38,39 +39,96 @@ func Notify(message string) {
|
||||
})
|
||||
}
|
||||
|
||||
type catalogerTaskProgressKey struct{}
|
||||
type catalogerTaskRegistryKey struct{}
|
||||
|
||||
// WithCatalogerTaskProgress returns a context whose cataloger tasks report into prog rather than
|
||||
// starting a row of their own.
|
||||
// catalogerTaskRegistry remembers the row each cataloger publishes, keyed by the publishing task's
|
||||
// identity.
|
||||
//
|
||||
// Nested cataloging runs under such a context: re-running the cataloger pipeline against the
|
||||
// contents of an archive is work that belongs to the progress of the task driving the recursion, not
|
||||
// a second start of every cataloger. Starting rows again per archive floods a consumer's display,
|
||||
// and where the new row carries an ID the consumer has already seen (every package cataloger does -
|
||||
// the ID is the cataloger name) it replaces the live row for that ID. The replaced row is then never
|
||||
// rendered again, its completion is never observed, and a UI that waits on every row it started - as
|
||||
// the syft CLI does - blocks forever on teardown.
|
||||
func WithCatalogerTaskProgress(ctx context.Context, prog *monitor.TaskProgress) context.Context {
|
||||
return context.WithValue(ctx, catalogerTaskProgressKey{}, prog)
|
||||
// A cataloger runs more than once per scan: once over the scan root, then again inside every archive
|
||||
// the archive cataloger walks. A row per run floods the display, and reusing a published ID replaces
|
||||
// the live row - which is then never completed, hanging a UI (like the syft CLI) that waits on every
|
||||
// row it started. So a re-run reports into the row it already owns, and its count covers the whole
|
||||
// scan rather than the root alone.
|
||||
type catalogerTaskRegistry struct {
|
||||
mu sync.Mutex
|
||||
rows map[string]*monitor.TaskProgress
|
||||
}
|
||||
|
||||
// catalogerTaskProgressFromContext returns the progress that cataloger tasks started under this
|
||||
// context report into, or nil when they should start one of their own.
|
||||
func catalogerTaskProgressFromContext(ctx context.Context) *monitor.TaskProgress {
|
||||
prog, _ := ctx.Value(catalogerTaskProgressKey{}).(*monitor.TaskProgress)
|
||||
return prog
|
||||
// WithCatalogerTaskRegistry returns a context under which each cataloger publishes one row no matter
|
||||
// how many times it runs; without it every run publishes, which suits a single-pass caller.
|
||||
//
|
||||
// Install once per scan before any task runs, so a re-run finds the first run's row. Idempotent: a
|
||||
// second registry would hold none of the existing rows, so every re-run would publish again.
|
||||
func WithCatalogerTaskRegistry(ctx context.Context) context.Context {
|
||||
if catalogerTaskRegistryFromContext(ctx) != nil {
|
||||
return ctx
|
||||
}
|
||||
return context.WithValue(ctx, catalogerTaskRegistryKey{}, &catalogerTaskRegistry{
|
||||
rows: make(map[string]*monitor.TaskProgress),
|
||||
})
|
||||
}
|
||||
|
||||
func catalogerTaskRegistryFromContext(ctx context.Context) *catalogerTaskRegistry {
|
||||
reg, _ := ctx.Value(catalogerTaskRegistryKey{}).(*catalogerTaskRegistry)
|
||||
return reg
|
||||
}
|
||||
|
||||
// catalogerTaskKey identifies the row a task owns: its ID where it has one (a package cataloger's ID
|
||||
// is its name), otherwise its parent and title (as every file cataloger, which publishes no ID).
|
||||
func catalogerTaskKey(info monitor.GenericTask) string {
|
||||
if info.ID != "" {
|
||||
return "id:" + info.ID
|
||||
}
|
||||
return "title:" + info.ParentID + "/" + info.Title.Default
|
||||
}
|
||||
|
||||
func StartCatalogerTask(ctx context.Context, info monitor.GenericTask, size int64, initialStage string) *monitor.TaskProgress {
|
||||
if parent := catalogerTaskProgressFromContext(ctx); parent != nil {
|
||||
// the stage is shared, so what this cataloger is working on shows up on the row already on
|
||||
// screen; the count is not, so this task completing does not complete the row it reports into
|
||||
return &monitor.TaskProgress{
|
||||
AtomicStage: parent.AtomicStage,
|
||||
Manual: progress.NewManual(size),
|
||||
}
|
||||
reg := catalogerTaskRegistryFromContext(ctx)
|
||||
if reg == nil {
|
||||
return publishCatalogerTask(info, size, initialStage)
|
||||
}
|
||||
|
||||
reg.mu.Lock()
|
||||
defer reg.mu.Unlock()
|
||||
|
||||
key := catalogerTaskKey(info)
|
||||
if row, ok := reg.rows[key]; ok {
|
||||
resumeCatalogerTask(row, size, initialStage)
|
||||
return row
|
||||
}
|
||||
|
||||
t := publishCatalogerTask(info, size, initialStage)
|
||||
reg.rows[key] = t
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// resumeCatalogerTask reopens a row an earlier run completed and grows its total by what this run
|
||||
// adds.
|
||||
//
|
||||
// Growing the total matters: a row completes once its count reaches its total, so incrementing a row
|
||||
// whose total covers only the first run makes it read as finished mid-walk. An indeterminate total on
|
||||
// either side stays indeterminate.
|
||||
//
|
||||
// Only a completed row is reopened; one carrying a real failure keeps it, since a later clean run
|
||||
// does not mean the earlier one succeeded.
|
||||
func resumeCatalogerTask(row *monitor.TaskProgress, size int64, initialStage string) {
|
||||
if size < 0 || row.Size() < 0 {
|
||||
row.SetTotal(-1)
|
||||
} else {
|
||||
row.SetTotal(row.Size() + size)
|
||||
}
|
||||
|
||||
if progress.IsErrCompleted(row.Error()) {
|
||||
row.SetError(nil)
|
||||
}
|
||||
|
||||
if initialStage != "" {
|
||||
row.AtomicStage.Set(initialStage)
|
||||
}
|
||||
}
|
||||
|
||||
func publishCatalogerTask(info monitor.GenericTask, size int64, initialStage string) *monitor.TaskProgress {
|
||||
t := &monitor.TaskProgress{
|
||||
AtomicStage: progress.NewAtomicStage(initialStage),
|
||||
Manual: progress.NewManual(size),
|
||||
|
||||
Reference in New Issue
Block a user