mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
feat: add catalog delete (#1377)
This commit is contained in:
parent
17aa8287e6
commit
23a3173c9f
@ -26,6 +26,15 @@ loopNewIDs:
|
||||
}
|
||||
}
|
||||
|
||||
func (s *orderedIDSet) delete(id artifact.ID) {
|
||||
for i, existingID := range s.slice {
|
||||
if existingID == id {
|
||||
s.slice = append(s.slice[:i], s.slice[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Catalog represents a collection of Packages.
|
||||
type Catalog struct {
|
||||
byID map[artifact.ID]Package
|
||||
@ -92,30 +101,32 @@ func (c *Catalog) Packages(ids []artifact.ID) (result []Package) {
|
||||
return result
|
||||
}
|
||||
|
||||
// Add a package to the Catalog.
|
||||
func (c *Catalog) Add(p Package) {
|
||||
// Add n packages to the catalog.
|
||||
func (c *Catalog) Add(pkgs ...Package) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
id := p.ID()
|
||||
if id == "" {
|
||||
log.Warnf("found package with empty ID while adding to the catalog: %+v", p)
|
||||
p.SetID()
|
||||
id = p.ID()
|
||||
}
|
||||
|
||||
if existing, exists := c.byID[id]; exists {
|
||||
// there is already a package with this fingerprint merge the existing record with the new one
|
||||
if err := existing.merge(p); err != nil {
|
||||
log.Warnf("failed to merge packages: %+v", err)
|
||||
} else {
|
||||
c.byID[id] = existing
|
||||
c.addPathsToIndex(p)
|
||||
for _, p := range pkgs {
|
||||
id := p.ID()
|
||||
if id == "" {
|
||||
log.Warnf("found package with empty ID while adding to the catalog: %+v", p)
|
||||
p.SetID()
|
||||
id = p.ID()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.addToIndex(p)
|
||||
if existing, exists := c.byID[id]; exists {
|
||||
// there is already a package with this fingerprint merge the existing record with the new one
|
||||
if err := existing.merge(p); err != nil {
|
||||
log.Warnf("failed to merge packages: %+v", err)
|
||||
} else {
|
||||
c.byID[id] = existing
|
||||
c.addPathsToIndex(p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.addToIndex(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Catalog) addToIndex(p Package) {
|
||||
@ -157,6 +168,59 @@ func (c *Catalog) addPathToIndex(id artifact.ID, path string) {
|
||||
c.idsByPath[path] = pathIndex
|
||||
}
|
||||
|
||||
func (c *Catalog) Delete(ids ...artifact.ID) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
for _, id := range ids {
|
||||
p, exists := c.byID[id]
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
|
||||
delete(c.byID, id)
|
||||
c.deleteNameFromIndex(p)
|
||||
c.deleteTypeFromIndex(p)
|
||||
c.deletePathsFromIndex(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Catalog) deleteNameFromIndex(p Package) {
|
||||
nameIndex := c.idsByName[p.Name]
|
||||
nameIndex.delete(p.id)
|
||||
c.idsByName[p.Name] = nameIndex
|
||||
}
|
||||
|
||||
func (c *Catalog) deleteTypeFromIndex(p Package) {
|
||||
typeIndex := c.idsByType[p.Type]
|
||||
typeIndex.delete(p.id)
|
||||
c.idsByType[p.Type] = typeIndex
|
||||
}
|
||||
|
||||
func (c *Catalog) deletePathsFromIndex(p Package) {
|
||||
observedPaths := internal.NewStringSet()
|
||||
for _, l := range p.Locations.ToSlice() {
|
||||
if l.RealPath != "" && !observedPaths.Contains(l.RealPath) {
|
||||
c.deletePathFromIndex(p.id, l.RealPath)
|
||||
observedPaths.Add(l.RealPath)
|
||||
}
|
||||
if l.VirtualPath != "" && l.RealPath != l.VirtualPath && !observedPaths.Contains(l.VirtualPath) {
|
||||
c.deletePathFromIndex(p.id, l.VirtualPath)
|
||||
observedPaths.Add(l.VirtualPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Catalog) deletePathFromIndex(id artifact.ID, path string) {
|
||||
pathIndex := c.idsByPath[path]
|
||||
pathIndex.delete(id)
|
||||
if len(pathIndex.slice) == 0 {
|
||||
delete(c.idsByPath, path)
|
||||
} else {
|
||||
c.idsByPath[path] = pathIndex
|
||||
}
|
||||
}
|
||||
|
||||
// Enumerate all packages for the given type(s), enumerating all packages if no type is specified.
|
||||
func (c *Catalog) Enumerate(types ...Type) <-chan Package {
|
||||
channel := make(chan Package)
|
||||
|
||||
@ -16,6 +16,148 @@ type expectedIndexes struct {
|
||||
byPath map[string]*strset.Set
|
||||
}
|
||||
|
||||
func TestCatalogDeleteRemovesPackages(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pkgs []Package
|
||||
deleteIDs []artifact.ID
|
||||
expectedIndexes expectedIndexes
|
||||
}{
|
||||
{
|
||||
name: "delete one package",
|
||||
pkgs: []Package{
|
||||
{
|
||||
id: "pkg:deb/debian/1",
|
||||
Name: "debian",
|
||||
Version: "1",
|
||||
Type: DebPkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.NewVirtualLocation("/c/path", "/another/path1"),
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "pkg:deb/debian/2",
|
||||
Name: "debian",
|
||||
Version: "2",
|
||||
Type: DebPkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.NewVirtualLocation("/d/path", "/another/path2"),
|
||||
),
|
||||
},
|
||||
},
|
||||
deleteIDs: []artifact.ID{
|
||||
artifact.ID("pkg:deb/debian/1"),
|
||||
},
|
||||
expectedIndexes: expectedIndexes{
|
||||
byType: map[Type]*strset.Set{
|
||||
DebPkg: strset.New("pkg:deb/debian/2"),
|
||||
},
|
||||
byPath: map[string]*strset.Set{
|
||||
"/d/path": strset.New("pkg:deb/debian/2"),
|
||||
"/another/path2": strset.New("pkg:deb/debian/2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete multiple packages",
|
||||
pkgs: []Package{
|
||||
{
|
||||
id: "pkg:deb/debian/1",
|
||||
Name: "debian",
|
||||
Version: "1",
|
||||
Type: DebPkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.NewVirtualLocation("/c/path", "/another/path1"),
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "pkg:deb/debian/2",
|
||||
Name: "debian",
|
||||
Version: "2",
|
||||
Type: DebPkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.NewVirtualLocation("/d/path", "/another/path2"),
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "pkg:deb/debian/3",
|
||||
Name: "debian",
|
||||
Version: "3",
|
||||
Type: DebPkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.NewVirtualLocation("/e/path", "/another/path3"),
|
||||
),
|
||||
},
|
||||
},
|
||||
deleteIDs: []artifact.ID{
|
||||
artifact.ID("pkg:deb/debian/1"),
|
||||
artifact.ID("pkg:deb/debian/3"),
|
||||
},
|
||||
expectedIndexes: expectedIndexes{
|
||||
byType: map[Type]*strset.Set{
|
||||
DebPkg: strset.New("pkg:deb/debian/2"),
|
||||
},
|
||||
byPath: map[string]*strset.Set{
|
||||
"/d/path": strset.New("pkg:deb/debian/2"),
|
||||
"/another/path2": strset.New("pkg:deb/debian/2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "delete non-existent package",
|
||||
pkgs: []Package{
|
||||
{
|
||||
id: artifact.ID("pkg:deb/debian/1"),
|
||||
Name: "debian",
|
||||
Version: "1",
|
||||
Type: DebPkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.NewVirtualLocation("/c/path", "/another/path1"),
|
||||
),
|
||||
},
|
||||
{
|
||||
id: artifact.ID("pkg:deb/debian/2"),
|
||||
Name: "debian",
|
||||
Version: "2",
|
||||
Type: DebPkg,
|
||||
Locations: source.NewLocationSet(
|
||||
source.NewVirtualLocation("/d/path", "/another/path2"),
|
||||
),
|
||||
},
|
||||
},
|
||||
deleteIDs: []artifact.ID{
|
||||
artifact.ID("pkg:deb/debian/3"),
|
||||
},
|
||||
expectedIndexes: expectedIndexes{
|
||||
byType: map[Type]*strset.Set{
|
||||
DebPkg: strset.New("pkg:deb/debian/1", "pkg:deb/debian/2"),
|
||||
},
|
||||
byPath: map[string]*strset.Set{
|
||||
"/c/path": strset.New("pkg:deb/debian/1"),
|
||||
"/another/path1": strset.New("pkg:deb/debian/1"),
|
||||
"/d/path": strset.New("pkg:deb/debian/2"),
|
||||
"/another/path2": strset.New("pkg:deb/debian/2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := NewCatalog()
|
||||
for _, p := range test.pkgs {
|
||||
c.Add(p)
|
||||
}
|
||||
|
||||
for _, id := range test.deleteIDs {
|
||||
c.Delete(id)
|
||||
}
|
||||
|
||||
assertIndexes(t, c, test.expectedIndexes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCatalogAddPopulatesIndex(t *testing.T) {
|
||||
|
||||
var pkgs = []Package{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user