add 'version' to namespace
Signed-off-by: liang chenye <liangchenye@huawei.com>
This commit is contained in:
parent
b2e6fd114b
commit
53958f6ea5
@ -64,7 +64,7 @@ type Datastore interface {
|
||||
// The Limit and page parameters are used to paginate the return list.
|
||||
// The first given page should be 0. The function will then return the next available page.
|
||||
// If there is no more page, -1 has to be returned.
|
||||
ListVulnerabilities(namespaceName string, limit int, page int) ([]Vulnerability, int, error)
|
||||
ListVulnerabilities(namespace Namespace, limit int, page int) ([]Vulnerability, int, error)
|
||||
|
||||
// InsertVulnerabilities stores the given Vulnerabilities in the database, updating them if
|
||||
// necessary. A vulnerability is uniquely identified by its Namespace and its Name.
|
||||
@ -81,22 +81,22 @@ type Datastore interface {
|
||||
InsertVulnerabilities(vulnerabilities []Vulnerability, createNotification bool) error
|
||||
|
||||
// FindVulnerability retrieves a Vulnerability from the database, including the FixedIn list.
|
||||
FindVulnerability(namespaceName, name string) (Vulnerability, error)
|
||||
FindVulnerability(namespace Namespace, name string) (Vulnerability, error)
|
||||
|
||||
// DeleteVulnerability removes a Vulnerability from the database.
|
||||
// It has to create a Notification that will contain the old Vulnerability.
|
||||
DeleteVulnerability(namespaceName, name string) error
|
||||
DeleteVulnerability(namespace Namespace, name string) error
|
||||
|
||||
// InsertVulnerabilityFixes adds new FixedIn Feature or update the Versions of existing ones to
|
||||
// the specified Vulnerability in the database.
|
||||
// It has has to create a Notification that will contain the old and the updated Vulnerability.
|
||||
InsertVulnerabilityFixes(vulnerabilityNamespace, vulnerabilityName string, fixes []FeatureVersion) error
|
||||
InsertVulnerabilityFixes(vulnerabilityNamespace Namespace, vulnerabilityName string, fixes []FeatureVersion) error
|
||||
|
||||
// DeleteVulnerabilityFix removes a FixedIn Feature from the specified Vulnerability in the
|
||||
// database. It can be used to store the fact that a Vulnerability no longer affects the given
|
||||
// Feature in any Version.
|
||||
// It has has to create a Notification that will contain the old and the updated Vulnerability.
|
||||
DeleteVulnerabilityFix(vulnerabilityNamespace, vulnerabilityName, featureName string) error
|
||||
DeleteVulnerabilityFix(vulnerabilityNamespace Namespace, vulnerabilityName, featureName string) error
|
||||
|
||||
// # Notification
|
||||
// GetAvailableNotification returns the Name, Created, Notified and Deleted fields of a
|
||||
|
@ -40,7 +40,24 @@ type Layer struct {
|
||||
type Namespace struct {
|
||||
Model
|
||||
|
||||
Name string
|
||||
Name string
|
||||
Version types.Version
|
||||
}
|
||||
|
||||
func (ns *Namespace) IsEmpty() bool {
|
||||
if ns.Name == "" && ns.Version.String() == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (ns *Namespace) Equal(namespace Namespace) bool {
|
||||
if ns.Name == namespace.Name && ns.Version.Compare(namespace.Version) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type Feature struct {
|
||||
|
@ -45,8 +45,11 @@ func TestRaceAffects(t *testing.T) {
|
||||
|
||||
// Insert the Feature on which we'll work.
|
||||
feature := database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestRaceAffectsFeatureNamespace1"},
|
||||
Name: "TestRaceAffecturesFeature1",
|
||||
Namespace: database.Namespace{
|
||||
Name: "TestRaceAffectsFeatureNamespace",
|
||||
Version: types.NewVersionUnsafe("1.0"),
|
||||
},
|
||||
Name: "TestRaceAffecturesFeature1",
|
||||
}
|
||||
_, err = datastore.insertFeature(feature)
|
||||
if err != nil {
|
||||
|
@ -31,7 +31,7 @@ func (pgSQL *pgSQL) insertFeature(feature database.Feature) (int, error) {
|
||||
// Do cache lookup.
|
||||
if pgSQL.cache != nil {
|
||||
promCacheQueriesTotal.WithLabelValues("feature").Inc()
|
||||
id, found := pgSQL.cache.Get("feature:" + feature.Namespace.Name + ":" + feature.Name)
|
||||
id, found := pgSQL.cache.Get("feature:" + feature.Namespace.Name + ":" + feature.Namespace.Version.String() + ":" + feature.Name)
|
||||
if found {
|
||||
promCacheHitsTotal.WithLabelValues("feature").Inc()
|
||||
return id.(int), nil
|
||||
@ -55,7 +55,7 @@ func (pgSQL *pgSQL) insertFeature(feature database.Feature) (int, error) {
|
||||
}
|
||||
|
||||
if pgSQL.cache != nil {
|
||||
pgSQL.cache.Add("feature:"+feature.Namespace.Name+":"+feature.Name, id)
|
||||
pgSQL.cache.Add("feature:"+feature.Namespace.Name+":"+feature.Namespace.Version.String()+":"+feature.Name, id)
|
||||
}
|
||||
|
||||
return id, nil
|
||||
@ -67,7 +67,7 @@ func (pgSQL *pgSQL) insertFeatureVersion(featureVersion database.FeatureVersion)
|
||||
}
|
||||
|
||||
// Do cache lookup.
|
||||
cacheIndex := "featureversion:" + featureVersion.Feature.Namespace.Name + ":" + featureVersion.Feature.Name + ":" + featureVersion.Version.String()
|
||||
cacheIndex := "featureversion:" + featureVersion.Feature.Namespace.Name + ":" + featureVersion.Feature.Namespace.Version.String() + ":" + featureVersion.Feature.Name + ":" + featureVersion.Version.String()
|
||||
if pgSQL.cache != nil {
|
||||
promCacheQueriesTotal.WithLabelValues("featureversion").Inc()
|
||||
id, found := pgSQL.cache.Get(cacheIndex)
|
||||
|
@ -44,8 +44,11 @@ func TestInsertFeature(t *testing.T) {
|
||||
|
||||
// Insert Feature and ensure we can find it.
|
||||
feature := database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertFeatureNamespace1"},
|
||||
Name: "TestInsertFeature1",
|
||||
Namespace: database.Namespace{
|
||||
Name: "TestInsertFeatureNamespace",
|
||||
Version: types.NewVersionUnsafe("1.0"),
|
||||
},
|
||||
Name: "TestInsertFeature1",
|
||||
}
|
||||
id1, err := datastore.insertFeature(feature)
|
||||
assert.Nil(t, err)
|
||||
@ -68,15 +71,21 @@ func TestInsertFeature(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Feature: database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertFeatureNamespace2"},
|
||||
Name: "TestInsertFeature2",
|
||||
Namespace: database.Namespace{
|
||||
Name: "TestInsertFeatureNamespace",
|
||||
Version: types.NewVersionUnsafe("2.0"),
|
||||
},
|
||||
Name: "TestInsertFeature2",
|
||||
},
|
||||
Version: types.NewVersionUnsafe(""),
|
||||
},
|
||||
{
|
||||
Feature: database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertFeatureNamespace2"},
|
||||
Name: "TestInsertFeature2",
|
||||
Namespace: database.Namespace{
|
||||
Name: "TestInsertFeatureNamespace",
|
||||
Version: types.NewVersionUnsafe("2.0"),
|
||||
},
|
||||
Name: "TestInsertFeature2",
|
||||
},
|
||||
Version: types.NewVersionUnsafe("bad version"),
|
||||
},
|
||||
@ -89,8 +98,11 @@ func TestInsertFeature(t *testing.T) {
|
||||
// Insert FeatureVersion and ensure we can find it.
|
||||
featureVersion := database.FeatureVersion{
|
||||
Feature: database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertFeatureNamespace1"},
|
||||
Name: "TestInsertFeature1",
|
||||
Namespace: database.Namespace{
|
||||
Name: "TestInsertFeatureNamespace",
|
||||
Version: types.NewVersionUnsafe("1.0"),
|
||||
},
|
||||
Name: "TestInsertFeature1",
|
||||
},
|
||||
Version: types.NewVersionUnsafe("2:3.0-imba"),
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ package pgsql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
@ -66,7 +65,7 @@ func (pgSQL *pgSQL) FindLayer(name string, withFeatures, withVulnerabilities boo
|
||||
for rows.Next() {
|
||||
var pn database.Namespace
|
||||
|
||||
err = rows.Scan(&pn.ID, &pn.Name)
|
||||
err = rows.Scan(&pn.ID, &pn.Name, &pn.Version)
|
||||
if err != nil {
|
||||
return layer, handleError("searchLayerNamespace.Scan()", err)
|
||||
}
|
||||
@ -89,7 +88,7 @@ func (pgSQL *pgSQL) FindLayer(name string, withFeatures, withVulnerabilities boo
|
||||
for rows.Next() {
|
||||
var namespace database.Namespace
|
||||
|
||||
err = rows.Scan(&namespace.ID, &namespace.Name)
|
||||
err = rows.Scan(&namespace.ID, &namespace.Name, &namespace.Version)
|
||||
if err != nil {
|
||||
return layer, handleError("searchLayerNamespace.Scan()", err)
|
||||
}
|
||||
@ -166,8 +165,9 @@ func getLayerFeatureVersions(tx *sql.Tx, layerID int) ([]database.FeatureVersion
|
||||
var featureVersion database.FeatureVersion
|
||||
|
||||
err = rows.Scan(&featureVersion.ID, &modification, &featureVersion.Feature.Namespace.ID,
|
||||
&featureVersion.Feature.Namespace.Name, &featureVersion.Feature.ID,
|
||||
&featureVersion.Feature.Name, &featureVersion.ID, &featureVersion.Version,
|
||||
&featureVersion.Feature.Namespace.Name, &featureVersion.Feature.Namespace.Version,
|
||||
&featureVersion.Feature.ID, &featureVersion.Feature.Name,
|
||||
&featureVersion.ID, &featureVersion.Version,
|
||||
&featureVersion.AddedBy.ID, &featureVersion.AddedBy.Name)
|
||||
if err != nil {
|
||||
return featureVersions, handleError("searchLayerFeatureVersion.Scan()", err)
|
||||
@ -222,7 +222,8 @@ func loadAffectedBy(tx *sql.Tx, featureVersions []database.FeatureVersion) error
|
||||
var vulnerability database.Vulnerability
|
||||
err := rows.Scan(&featureversionID, &vulnerability.ID, &vulnerability.Name,
|
||||
&vulnerability.Description, &vulnerability.Link, &vulnerability.Severity,
|
||||
&vulnerability.Metadata, &vulnerability.Namespace.Name, &vulnerability.FixedBy)
|
||||
&vulnerability.Metadata, &vulnerability.Namespace.Name, &vulnerability.Namespace.Version,
|
||||
&vulnerability.FixedBy)
|
||||
if err != nil {
|
||||
return handleError("searchFeatureVersionVulnerability.Scan()", err)
|
||||
}
|
||||
@ -286,9 +287,7 @@ func (pgSQL *pgSQL) InsertLayer(layer database.Layer) error {
|
||||
// Layer's namespaces has high priority than its parent.
|
||||
// Once a layer has a 'same' namespace with its parent,
|
||||
// it will only keep its namespace.
|
||||
//TODO: add 'Version' to Namespace and use 'Name' directly
|
||||
name := strings.Split(layer.Namespaces[i].Name, ":")
|
||||
mapNamespaceIDs[name[0]] = id
|
||||
mapNamespaceIDs[layer.Namespaces[i].Name] = id
|
||||
}
|
||||
|
||||
// Get parent ID.
|
||||
@ -302,11 +301,9 @@ func (pgSQL *pgSQL) InsertLayer(layer database.Layer) error {
|
||||
parentID = zero.IntFrom(int64(layer.Parent.ID))
|
||||
|
||||
for _, pn := range layer.Parent.Namespaces {
|
||||
//TODO: add 'Version' to Namespace and use 'Name' directly
|
||||
name := strings.Split(pn.Name, ":")
|
||||
if _, ok := mapNamespaceIDs[name[0]]; !ok {
|
||||
if _, ok := mapNamespaceIDs[pn.Name]; !ok {
|
||||
// Layer will inherit its parent's namespace
|
||||
mapNamespaceIDs[name[0]] = pn.ID
|
||||
mapNamespaceIDs[pn.Name] = pn.ID
|
||||
layer.Namespaces = append(layer.Namespaces, pn)
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,10 @@ func TestFindLayer(t *testing.T) {
|
||||
}
|
||||
defer datastore.Close()
|
||||
|
||||
testDebian7 := database.Namespace{
|
||||
Name: "debian",
|
||||
Version: types.NewVersionUnsafe("7"),
|
||||
}
|
||||
// Layer-0: no parent, no namespace, no feature, no vulnerability
|
||||
layer, err := datastore.FindLayer("layer-0", false, false)
|
||||
if assert.Nil(t, err) && assert.NotNil(t, layer) {
|
||||
@ -56,14 +60,14 @@ func TestFindLayer(t *testing.T) {
|
||||
}
|
||||
assert.Equal(t, 1, layer.EngineVersion)
|
||||
assert.Len(t, layer.Namespaces, 1)
|
||||
assert.Equal(t, "debian:7", layer.Namespaces[0].Name)
|
||||
assert.True(t, testDebian7.Equal(layer.Namespaces[0]))
|
||||
assert.Len(t, layer.Features, 0)
|
||||
}
|
||||
|
||||
layer, err = datastore.FindLayer("layer-1", true, false)
|
||||
if assert.Nil(t, err) && assert.NotNil(t, layer) && assert.Len(t, layer.Features, 2) {
|
||||
for _, featureVersion := range layer.Features {
|
||||
assert.Equal(t, "debian:7", featureVersion.Feature.Namespace.Name)
|
||||
assert.True(t, testDebian7.Equal(featureVersion.Feature.Namespace))
|
||||
|
||||
switch featureVersion.Feature.Name {
|
||||
case "wechat":
|
||||
@ -79,7 +83,7 @@ func TestFindLayer(t *testing.T) {
|
||||
layer, err = datastore.FindLayer("layer-1", true, true)
|
||||
if assert.Nil(t, err) && assert.NotNil(t, layer) && assert.Len(t, layer.Features, 2) {
|
||||
for _, featureVersion := range layer.Features {
|
||||
assert.Equal(t, "debian:7", featureVersion.Feature.Namespace.Name)
|
||||
assert.True(t, testDebian7.Equal(featureVersion.Feature.Namespace))
|
||||
|
||||
switch featureVersion.Feature.Name {
|
||||
case "wechat":
|
||||
@ -88,7 +92,7 @@ func TestFindLayer(t *testing.T) {
|
||||
assert.Equal(t, types.NewVersionUnsafe("1.0"), featureVersion.Version)
|
||||
|
||||
if assert.Len(t, featureVersion.AffectedBy, 1) {
|
||||
assert.Equal(t, "debian:7", featureVersion.AffectedBy[0].Namespace.Name)
|
||||
assert.True(t, testDebian7.Equal(featureVersion.AffectedBy[0].Namespace))
|
||||
assert.Equal(t, "CVE-OPENSSL-1-DEB7", featureVersion.AffectedBy[0].Name)
|
||||
assert.Equal(t, types.High, featureVersion.AffectedBy[0].Severity)
|
||||
assert.Equal(t, "A vulnerability affecting OpenSSL < 2.0 on Debian 7.0", featureVersion.AffectedBy[0].Description)
|
||||
@ -137,44 +141,56 @@ func testInsertLayerInvalid(t *testing.T, datastore database.Datastore) {
|
||||
}
|
||||
|
||||
func testInsertLayerTree(t *testing.T, datastore database.Datastore) {
|
||||
testInsertLayerNamespace1 := database.Namespace{
|
||||
Name: "TestInsertLayerNamespace",
|
||||
Version: types.NewVersionUnsafe("1"),
|
||||
}
|
||||
testInsertLayerNamespace2 := database.Namespace{
|
||||
Name: "TestInsertLayerNamespace",
|
||||
Version: types.NewVersionUnsafe("2"),
|
||||
}
|
||||
testInsertLayerNamespace3 := database.Namespace{
|
||||
Name: "TestInsertLayerNamespace",
|
||||
Version: types.NewVersionUnsafe("3"),
|
||||
}
|
||||
f1 := database.FeatureVersion{
|
||||
Feature: database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertLayerNamespace:2"},
|
||||
Namespace: testInsertLayerNamespace2,
|
||||
Name: "TestInsertLayerFeature1",
|
||||
},
|
||||
Version: types.NewVersionUnsafe("1.0"),
|
||||
}
|
||||
f2 := database.FeatureVersion{
|
||||
Feature: database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertLayerNamespace:2"},
|
||||
Namespace: testInsertLayerNamespace2,
|
||||
Name: "TestInsertLayerFeature2",
|
||||
},
|
||||
Version: types.NewVersionUnsafe("0.34"),
|
||||
}
|
||||
f3 := database.FeatureVersion{
|
||||
Feature: database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertLayerNamespace:2"},
|
||||
Namespace: testInsertLayerNamespace2,
|
||||
Name: "TestInsertLayerFeature3",
|
||||
},
|
||||
Version: types.NewVersionUnsafe("0.56"),
|
||||
}
|
||||
f4 := database.FeatureVersion{
|
||||
Feature: database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertLayerNamespace:3"},
|
||||
Namespace: testInsertLayerNamespace3,
|
||||
Name: "TestInsertLayerFeature2",
|
||||
},
|
||||
Version: types.NewVersionUnsafe("0.34"),
|
||||
}
|
||||
f5 := database.FeatureVersion{
|
||||
Feature: database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertLayerNamespace:3"},
|
||||
Namespace: testInsertLayerNamespace3,
|
||||
Name: "TestInsertLayerFeature3",
|
||||
},
|
||||
Version: types.NewVersionUnsafe("0.57"),
|
||||
}
|
||||
f6 := database.FeatureVersion{
|
||||
Feature: database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertLayerNamespace:3"},
|
||||
Namespace: testInsertLayerNamespace3,
|
||||
Name: "TestInsertLayerFeature4",
|
||||
},
|
||||
Version: types.NewVersionUnsafe("0.666"),
|
||||
@ -187,13 +203,13 @@ func testInsertLayerTree(t *testing.T, datastore database.Datastore) {
|
||||
{
|
||||
Name: "TestInsertLayer2",
|
||||
Parent: &database.Layer{Name: "TestInsertLayer1"},
|
||||
Namespaces: []database.Namespace{database.Namespace{Name: "TestInsertLayerNamespace:1"}},
|
||||
Namespaces: []database.Namespace{testInsertLayerNamespace1},
|
||||
},
|
||||
// This layer changes the namespace and adds Features.
|
||||
{
|
||||
Name: "TestInsertLayer3",
|
||||
Parent: &database.Layer{Name: "TestInsertLayer2"},
|
||||
Namespaces: []database.Namespace{database.Namespace{Name: "TestInsertLayerNamespace:2"}},
|
||||
Namespaces: []database.Namespace{testInsertLayerNamespace2},
|
||||
Features: []database.FeatureVersion{f1, f2, f3},
|
||||
},
|
||||
// This layer covers the case where the last layer doesn't provide any new Feature.
|
||||
@ -208,7 +224,7 @@ func testInsertLayerTree(t *testing.T, datastore database.Datastore) {
|
||||
{
|
||||
Name: "TestInsertLayer4b",
|
||||
Parent: &database.Layer{Name: "TestInsertLayer3"},
|
||||
Namespaces: []database.Namespace{database.Namespace{Name: "TestInsertLayerNamespace:3"}},
|
||||
Namespaces: []database.Namespace{testInsertLayerNamespace3},
|
||||
Features: []database.FeatureVersion{
|
||||
// Deletes TestInsertLayerFeature1.
|
||||
// Keep TestInsertLayerFeature2 (old Namespace should be kept):
|
||||
@ -239,7 +255,7 @@ func testInsertLayerTree(t *testing.T, datastore database.Datastore) {
|
||||
|
||||
l4a := retrievedLayers["TestInsertLayer4a"]
|
||||
if assert.Len(t, l4a.Namespaces, 1) {
|
||||
assert.Equal(t, "TestInsertLayerNamespace:2", l4a.Namespaces[0].Name)
|
||||
assert.True(t, testInsertLayerNamespace2.Equal(l4a.Namespaces[0]))
|
||||
}
|
||||
assert.Len(t, l4a.Features, 3)
|
||||
for _, featureVersion := range l4a.Features {
|
||||
@ -250,7 +266,7 @@ func testInsertLayerTree(t *testing.T, datastore database.Datastore) {
|
||||
|
||||
l4b := retrievedLayers["TestInsertLayer4b"]
|
||||
if assert.Len(t, l4b.Namespaces, 1) {
|
||||
assert.Equal(t, "TestInsertLayerNamespace:3", l4b.Namespaces[0].Name)
|
||||
assert.True(t, testInsertLayerNamespace3.Equal(l4b.Namespaces[0]))
|
||||
}
|
||||
assert.Len(t, l4b.Features, 3)
|
||||
for _, featureVersion := range l4b.Features {
|
||||
@ -261,9 +277,17 @@ func testInsertLayerTree(t *testing.T, datastore database.Datastore) {
|
||||
}
|
||||
|
||||
func testInsertLayerUpdate(t *testing.T, datastore database.Datastore) {
|
||||
testInsertLayerNamespace3 := database.Namespace{
|
||||
Name: "TestInsertLayerNamespace",
|
||||
Version: types.NewVersionUnsafe("3"),
|
||||
}
|
||||
testInsertLayerNamespaceUpdated1 := database.Namespace{
|
||||
Name: "TestInsertLayerNamespaceUpdated",
|
||||
Version: types.NewVersionUnsafe("1"),
|
||||
}
|
||||
f7 := database.FeatureVersion{
|
||||
Feature: database.Feature{
|
||||
Namespace: database.Namespace{Name: "TestInsertLayerNamespace:3"},
|
||||
Namespace: testInsertLayerNamespace3,
|
||||
Name: "TestInsertLayerFeature7",
|
||||
},
|
||||
Version: types.NewVersionUnsafe("0.01"),
|
||||
@ -273,7 +297,7 @@ func testInsertLayerUpdate(t *testing.T, datastore database.Datastore) {
|
||||
l3u := database.Layer{
|
||||
Name: l3.Name,
|
||||
Parent: l3.Parent,
|
||||
Namespaces: []database.Namespace{database.Namespace{Name: "TestInsertLayerNamespaceUpdated:1"}},
|
||||
Namespaces: []database.Namespace{testInsertLayerNamespaceUpdated1},
|
||||
Features: []database.FeatureVersion{f7},
|
||||
}
|
||||
|
||||
@ -291,7 +315,7 @@ func testInsertLayerUpdate(t *testing.T, datastore database.Datastore) {
|
||||
l3uf, err := datastore.FindLayer(l3u.Name, true, false)
|
||||
if assert.Nil(t, err) {
|
||||
if assert.Len(t, l3.Namespaces, 1) && assert.Len(t, l3uf.Namespaces, 1) {
|
||||
assert.Equal(t, l3.Namespaces[0].Name, l3uf.Namespaces[0].Name)
|
||||
assert.True(t, l3.Namespaces[0].Equal(l3uf.Namespaces[0]))
|
||||
}
|
||||
assert.Equal(t, l3.EngineVersion, l3uf.EngineVersion)
|
||||
assert.Len(t, l3uf.Features, len(l3.Features))
|
||||
@ -304,7 +328,6 @@ func testInsertLayerUpdate(t *testing.T, datastore database.Datastore) {
|
||||
assert.Nil(t, err)
|
||||
|
||||
l3uf, err = datastore.FindLayer(l3u.Name, true, false)
|
||||
fmt.Println(l3u.Namespaces, l3uf.Namespaces)
|
||||
if assert.Nil(t, err) {
|
||||
if assert.Len(t, l3u.Namespaces, 1) && assert.Len(t, l3uf.Namespaces, 2) {
|
||||
assert.True(t, containNS(l3uf.Namespaces, l3u.Namespaces[0]), "Updated layer should have %#v", l3u.Namespaces[0])
|
||||
@ -359,7 +382,7 @@ func cmpFV(a, b database.FeatureVersion) bool {
|
||||
|
||||
func containNS(namespaces []database.Namespace, namespace database.Namespace) bool {
|
||||
for _, n := range namespaces {
|
||||
if n.Name == namespace.Name {
|
||||
if n.Equal(namespace) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS Namespace (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(128) NULL);
|
||||
name VARCHAR(128) NULL,
|
||||
version VARCHAR(128) NULL);
|
||||
|
||||
|
||||
-- -----------------------------------------------------
|
||||
|
@ -22,13 +22,13 @@ import (
|
||||
)
|
||||
|
||||
func (pgSQL *pgSQL) insertNamespace(namespace database.Namespace) (int, error) {
|
||||
if namespace.Name == "" {
|
||||
if namespace.IsEmpty() {
|
||||
return 0, cerrors.NewBadRequestError("could not find/insert invalid Namespace")
|
||||
}
|
||||
|
||||
if pgSQL.cache != nil {
|
||||
promCacheQueriesTotal.WithLabelValues("namespace").Inc()
|
||||
if id, found := pgSQL.cache.Get("namespace:" + namespace.Name); found {
|
||||
if id, found := pgSQL.cache.Get("namespace:" + namespace.Name + ":" + namespace.Version.String()); found {
|
||||
promCacheHitsTotal.WithLabelValues("namespace").Inc()
|
||||
return id.(int), nil
|
||||
}
|
||||
@ -38,13 +38,13 @@ func (pgSQL *pgSQL) insertNamespace(namespace database.Namespace) (int, error) {
|
||||
defer observeQueryTime("insertNamespace", "all", time.Now())
|
||||
|
||||
var id int
|
||||
err := pgSQL.QueryRow(soiNamespace, namespace.Name).Scan(&id)
|
||||
err := pgSQL.QueryRow(soiNamespace, namespace.Name, &namespace.Version).Scan(&id)
|
||||
if err != nil {
|
||||
return 0, handleError("soiNamespace", err)
|
||||
}
|
||||
|
||||
if pgSQL.cache != nil {
|
||||
pgSQL.cache.Add("namespace:"+namespace.Name, id)
|
||||
pgSQL.cache.Add("namespace:"+namespace.Name+":"+namespace.Version.String(), id)
|
||||
}
|
||||
|
||||
return id, nil
|
||||
@ -60,7 +60,7 @@ func (pgSQL *pgSQL) ListNamespaces() (namespaces []database.Namespace, err error
|
||||
for rows.Next() {
|
||||
var namespace database.Namespace
|
||||
|
||||
err = rows.Scan(&namespace.ID, &namespace.Name)
|
||||
err = rows.Scan(&namespace.ID, &namespace.Name, &namespace.Version)
|
||||
if err != nil {
|
||||
return namespaces, handleError("listNamespace.Scan()", err)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -36,9 +37,9 @@ func TestInsertNamespace(t *testing.T) {
|
||||
assert.Zero(t, id0)
|
||||
|
||||
// Insert Namespace and ensure we can find it.
|
||||
id1, err := datastore.insertNamespace(database.Namespace{Name: "TestInsertNamespace1"})
|
||||
id1, err := datastore.insertNamespace(database.Namespace{Name: "TestInsertNamespace", Version: types.NewVersionUnsafe("1")})
|
||||
assert.Nil(t, err)
|
||||
id2, err := datastore.insertNamespace(database.Namespace{Name: "TestInsertNamespace1"})
|
||||
id2, err := datastore.insertNamespace(database.Namespace{Name: "TestInsertNamespace", Version: types.NewVersionUnsafe("1")})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, id1, id2)
|
||||
}
|
||||
@ -54,12 +55,13 @@ func TestListNamespace(t *testing.T) {
|
||||
namespaces, err := datastore.ListNamespaces()
|
||||
assert.Nil(t, err)
|
||||
if assert.Len(t, namespaces, 2) {
|
||||
testDebian7 := database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("7")}
|
||||
testDebian8 := database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("8")}
|
||||
for _, namespace := range namespaces {
|
||||
switch namespace.Name {
|
||||
case "debian:7", "debian:8":
|
||||
if namespace.Equal(testDebian7) || namespace.Equal(testDebian8) {
|
||||
continue
|
||||
default:
|
||||
assert.Error(t, fmt.Errorf("ListNamespaces should not have returned '%s'", namespace.Name))
|
||||
} else {
|
||||
assert.Error(t, fmt.Errorf("ListNamespaces should not have returned '%s:%s'", namespace.Name, namespace.Version.String()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,19 @@ func TestNotification(t *testing.T) {
|
||||
|
||||
// Create some data.
|
||||
f1 := database.Feature{
|
||||
Name: "TestNotificationFeature1",
|
||||
Namespace: database.Namespace{Name: "TestNotificationNamespace1"},
|
||||
Name: "TestNotificationFeature1",
|
||||
Namespace: database.Namespace{
|
||||
Name: "TestNotificationNamespace",
|
||||
Version: types.NewVersionUnsafe("1.0"),
|
||||
},
|
||||
}
|
||||
|
||||
f2 := database.Feature{
|
||||
Name: "TestNotificationFeature2",
|
||||
Namespace: database.Namespace{Name: "TestNotificationNamespace1"},
|
||||
Name: "TestNotificationFeature2",
|
||||
Namespace: database.Namespace{
|
||||
Name: "TestNotificationNamespace",
|
||||
Version: types.NewVersionUnsafe("1.0"),
|
||||
},
|
||||
}
|
||||
|
||||
l1 := database.Layer{
|
||||
@ -186,7 +192,7 @@ func TestNotification(t *testing.T) {
|
||||
}
|
||||
|
||||
// Delete a vulnerability and verify the notification.
|
||||
if assert.Nil(t, datastore.DeleteVulnerability(v1b.Namespace.Name, v1b.Name)) {
|
||||
if assert.Nil(t, datastore.DeleteVulnerability(v1b.Namespace, v1b.Name)) {
|
||||
notification, err = datastore.GetAvailableNotification(time.Second)
|
||||
assert.Nil(t, err)
|
||||
assert.NotEmpty(t, notification.Name)
|
||||
|
@ -29,17 +29,17 @@ const (
|
||||
// namespace.go
|
||||
soiNamespace = `
|
||||
WITH new_namespace AS (
|
||||
INSERT INTO Namespace(name)
|
||||
SELECT CAST($1 AS VARCHAR)
|
||||
WHERE NOT EXISTS (SELECT name FROM Namespace WHERE name = $1)
|
||||
INSERT INTO Namespace(name, version)
|
||||
SELECT CAST($1 AS VARCHAR), CAST($2 AS VARCHAR)
|
||||
WHERE NOT EXISTS (SELECT name FROM Namespace WHERE name = $1 AND version = $2)
|
||||
RETURNING id
|
||||
)
|
||||
SELECT id FROM Namespace WHERE name = $1
|
||||
SELECT id FROM Namespace WHERE name = $1 AND version = $2
|
||||
UNION
|
||||
SELECT id FROM new_namespace`
|
||||
|
||||
searchNamespace = `SELECT id FROM Namespace WHERE name = $1`
|
||||
listNamespace = `SELECT id, name FROM Namespace`
|
||||
searchNamespace = `SELECT id FROM Namespace WHERE name = $1 AND version = $2`
|
||||
listNamespace = `SELECT id, name, version FROM Namespace`
|
||||
|
||||
// feature.go
|
||||
soiFeature = `
|
||||
@ -92,7 +92,7 @@ const (
|
||||
FROM Layer l, layer_tree lt
|
||||
WHERE l.id = lt.parent_id
|
||||
)
|
||||
SELECT ldf.featureversion_id, ldf.modification, fn.id, fn.name, f.id, f.name, fv.id, fv.version, ltree.id, ltree.name
|
||||
SELECT ldf.featureversion_id, ldf.modification, fn.id, fn.name, fn.version, f.id, f.name, fv.id, fv.version, ltree.id, ltree.name
|
||||
FROM Layer_diff_FeatureVersion ldf
|
||||
JOIN (
|
||||
SELECT row_number() over (ORDER BY depth DESC), id, name FROM layer_tree
|
||||
@ -102,7 +102,7 @@ const (
|
||||
|
||||
searchFeatureVersionVulnerability = `
|
||||
SELECT vafv.featureversion_id, v.id, v.name, v.description, v.link, v.severity, v.metadata,
|
||||
vn.name, vfif.version
|
||||
vn.name, vn.version, vfif.version
|
||||
FROM Vulnerability_Affects_FeatureVersion vafv, Vulnerability v,
|
||||
Namespace vn, Vulnerability_FixedIn_Feature vfif
|
||||
WHERE vafv.featureversion_id = ANY($1::integer[])
|
||||
@ -112,7 +112,7 @@ const (
|
||||
AND v.deleted_at IS NULL`
|
||||
|
||||
searchLayerNamespace = `
|
||||
SELECT n.id, n.name
|
||||
SELECT n.id, n.name, n.version
|
||||
FROM LayerNamespace ln, Namespace n
|
||||
WHERE ln.layer_id = $1 AND ln.namespace_id = n.id`
|
||||
|
||||
@ -159,12 +159,12 @@ const (
|
||||
|
||||
// vulnerability.go
|
||||
searchVulnerabilityBase = `
|
||||
SELECT v.id, v.name, n.id, n.name, v.description, v.link, v.severity, v.metadata
|
||||
SELECT v.id, v.name, n.id, n.name, n.version, v.description, v.link, v.severity, v.metadata
|
||||
FROM Vulnerability v JOIN Namespace n ON v.namespace_id = n.id`
|
||||
searchVulnerabilityForUpdate = ` FOR UPDATE OF v`
|
||||
searchVulnerabilityByNamespaceAndName = ` WHERE n.name = $1 AND v.name = $2 AND v.deleted_at IS NULL`
|
||||
searchVulnerabilityByNamespaceAndName = ` WHERE n.name = $1 AND n.version = $2 AND v.name = $3 AND v.deleted_at IS NULL`
|
||||
searchVulnerabilityByID = ` WHERE v.id = $1`
|
||||
searchVulnerabilityByNamespace = ` WHERE n.name = $1 AND v.deleted_at IS NULL
|
||||
searchVulnerabilityByNamespaceID = ` WHERE n.id = $1 AND v.deleted_at IS NULL
|
||||
AND v.id >= $2
|
||||
ORDER BY v.id
|
||||
LIMIT $3`
|
||||
@ -189,8 +189,8 @@ const (
|
||||
removeVulnerability = `
|
||||
UPDATE Vulnerability
|
||||
SET deleted_at = CURRENT_TIMESTAMP
|
||||
WHERE namespace_id = (SELECT id FROM Namespace WHERE name = $1)
|
||||
AND name = $2
|
||||
WHERE namespace_id = (SELECT id FROM Namespace WHERE name = $1 AND version = $2)
|
||||
AND name = $3
|
||||
AND deleted_at IS NULL
|
||||
RETURNING id`
|
||||
|
||||
|
6
database/pgsql/testdata/data.sql
vendored
6
database/pgsql/testdata/data.sql
vendored
@ -12,9 +12,9 @@
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
INSERT INTO namespace (id, name) VALUES
|
||||
(1, 'debian:7'),
|
||||
(2, 'debian:8');
|
||||
INSERT INTO namespace (id, name, version) VALUES
|
||||
(1, 'debian', '7'),
|
||||
(2, 'debian', '8');
|
||||
|
||||
INSERT INTO feature (id, namespace_id, name) VALUES
|
||||
(1, 1, 'wechat'),
|
||||
|
@ -28,12 +28,12 @@ import (
|
||||
"github.com/guregu/null/zero"
|
||||
)
|
||||
|
||||
func (pgSQL *pgSQL) ListVulnerabilities(namespaceName string, limit int, startID int) ([]database.Vulnerability, int, error) {
|
||||
func (pgSQL *pgSQL) ListVulnerabilities(namespace database.Namespace, limit int, startID int) ([]database.Vulnerability, int, error) {
|
||||
defer observeQueryTime("listVulnerabilities", "all", time.Now())
|
||||
|
||||
// Query Namespace.
|
||||
var id int
|
||||
err := pgSQL.QueryRow(searchNamespace, namespaceName).Scan(&id)
|
||||
err := pgSQL.QueryRow(searchNamespace, namespace.Name, &namespace.Version).Scan(&id)
|
||||
if err != nil {
|
||||
return nil, -1, handleError("searchNamespace", err)
|
||||
} else if id == 0 {
|
||||
@ -41,10 +41,10 @@ func (pgSQL *pgSQL) ListVulnerabilities(namespaceName string, limit int, startID
|
||||
}
|
||||
|
||||
// Query.
|
||||
query := searchVulnerabilityBase + searchVulnerabilityByNamespace
|
||||
rows, err := pgSQL.Query(query, namespaceName, startID, limit+1)
|
||||
query := searchVulnerabilityBase + searchVulnerabilityByNamespaceID
|
||||
rows, err := pgSQL.Query(query, id, startID, limit+1)
|
||||
if err != nil {
|
||||
return nil, -1, handleError("searchVulnerabilityByNamespace", err)
|
||||
return nil, -1, handleError("searchVulnerabilityByNamespaceID", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
@ -60,6 +60,7 @@ func (pgSQL *pgSQL) ListVulnerabilities(namespaceName string, limit int, startID
|
||||
&vulnerability.Name,
|
||||
&vulnerability.Namespace.ID,
|
||||
&vulnerability.Namespace.Name,
|
||||
&vulnerability.Namespace.Version,
|
||||
&vulnerability.Description,
|
||||
&vulnerability.Link,
|
||||
&vulnerability.Severity,
|
||||
@ -83,11 +84,11 @@ func (pgSQL *pgSQL) ListVulnerabilities(namespaceName string, limit int, startID
|
||||
return vulns, nextID, nil
|
||||
}
|
||||
|
||||
func (pgSQL *pgSQL) FindVulnerability(namespaceName, name string) (database.Vulnerability, error) {
|
||||
return findVulnerability(pgSQL, namespaceName, name, false)
|
||||
func (pgSQL *pgSQL) FindVulnerability(namespace database.Namespace, name string) (database.Vulnerability, error) {
|
||||
return findVulnerability(pgSQL, namespace, name, false)
|
||||
}
|
||||
|
||||
func findVulnerability(queryer Queryer, namespaceName, name string, forUpdate bool) (database.Vulnerability, error) {
|
||||
func findVulnerability(queryer Queryer, namespace database.Namespace, name string, forUpdate bool) (database.Vulnerability, error) {
|
||||
defer observeQueryTime("findVulnerability", "all", time.Now())
|
||||
|
||||
queryName := "searchVulnerabilityBase+searchVulnerabilityByNamespaceAndName"
|
||||
@ -97,7 +98,7 @@ func findVulnerability(queryer Queryer, namespaceName, name string, forUpdate bo
|
||||
query = query + searchVulnerabilityForUpdate
|
||||
}
|
||||
|
||||
return scanVulnerability(queryer, queryName, queryer.QueryRow(query, namespaceName, name))
|
||||
return scanVulnerability(queryer, queryName, queryer.QueryRow(query, namespace.Name, &namespace.Version, name))
|
||||
}
|
||||
|
||||
func (pgSQL *pgSQL) findVulnerabilityByIDWithDeleted(id int) (database.Vulnerability, error) {
|
||||
@ -117,6 +118,7 @@ func scanVulnerability(queryer Queryer, queryName string, vulnerabilityRow *sql.
|
||||
&vulnerability.Name,
|
||||
&vulnerability.Namespace.ID,
|
||||
&vulnerability.Namespace.Name,
|
||||
&vulnerability.Namespace.Version,
|
||||
&vulnerability.Description,
|
||||
&vulnerability.Link,
|
||||
&vulnerability.Severity,
|
||||
@ -193,7 +195,7 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
|
||||
tf := time.Now()
|
||||
|
||||
// Verify parameters
|
||||
if vulnerability.Name == "" || vulnerability.Namespace.Name == "" {
|
||||
if vulnerability.Name == "" || vulnerability.Namespace.IsEmpty() {
|
||||
return cerrors.NewBadRequestError("insertVulnerability needs at least the Name and the Namespace")
|
||||
}
|
||||
if !onlyFixedIn && !vulnerability.Severity.IsValid() {
|
||||
@ -204,11 +206,12 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
|
||||
for i := 0; i < len(vulnerability.FixedIn); i++ {
|
||||
fifv := &vulnerability.FixedIn[i]
|
||||
|
||||
if fifv.Feature.Namespace.Name == "" {
|
||||
if fifv.Feature.Namespace.IsEmpty() {
|
||||
// As there is no Namespace on that FixedIn FeatureVersion, set it to the Vulnerability's
|
||||
// Namespace.
|
||||
fifv.Feature.Namespace.Name = vulnerability.Namespace.Name
|
||||
} else if fifv.Feature.Namespace.Name != vulnerability.Namespace.Name {
|
||||
fifv.Feature.Namespace.Version = vulnerability.Namespace.Version
|
||||
} else if !fifv.Feature.Namespace.Equal(vulnerability.Namespace) {
|
||||
msg := "could not insert an invalid vulnerability that contains FixedIn FeatureVersion that are not in the same namespace as the Vulnerability"
|
||||
log.Warning(msg)
|
||||
return cerrors.NewBadRequestError(msg)
|
||||
@ -226,7 +229,7 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
|
||||
}
|
||||
|
||||
// Find existing vulnerability and its Vulnerability_FixedIn_Features (for update).
|
||||
existingVulnerability, err := findVulnerability(tx, vulnerability.Namespace.Name, vulnerability.Name, true)
|
||||
existingVulnerability, err := findVulnerability(tx, vulnerability.Namespace, vulnerability.Name, true)
|
||||
if err != nil && err != cerrors.ErrNotFound {
|
||||
tx.Rollback()
|
||||
return err
|
||||
@ -264,7 +267,7 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
|
||||
}
|
||||
|
||||
// Mark the old vulnerability as non latest.
|
||||
_, err = tx.Exec(removeVulnerability, vulnerability.Namespace.Name, vulnerability.Name)
|
||||
_, err = tx.Exec(removeVulnerability, vulnerability.Namespace.Name, &vulnerability.Namespace.Version, vulnerability.Name)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return handleError("removeVulnerability", err)
|
||||
@ -497,13 +500,14 @@ func linkVulnerabilityToFeatureVersions(tx *sql.Tx, fixedInID, vulnerabilityID,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pgSQL *pgSQL) InsertVulnerabilityFixes(vulnerabilityNamespace, vulnerabilityName string, fixes []database.FeatureVersion) error {
|
||||
func (pgSQL *pgSQL) InsertVulnerabilityFixes(vulnerabilityNamespace database.Namespace, vulnerabilityName string, fixes []database.FeatureVersion) error {
|
||||
defer observeQueryTime("InsertVulnerabilityFixes", "all", time.Now())
|
||||
|
||||
v := database.Vulnerability{
|
||||
Name: vulnerabilityName,
|
||||
Namespace: database.Namespace{
|
||||
Name: vulnerabilityNamespace,
|
||||
Name: vulnerabilityNamespace.Name,
|
||||
Version: vulnerabilityNamespace.Version,
|
||||
},
|
||||
FixedIn: fixes,
|
||||
}
|
||||
@ -511,20 +515,22 @@ func (pgSQL *pgSQL) InsertVulnerabilityFixes(vulnerabilityNamespace, vulnerabili
|
||||
return pgSQL.insertVulnerability(v, true, true)
|
||||
}
|
||||
|
||||
func (pgSQL *pgSQL) DeleteVulnerabilityFix(vulnerabilityNamespace, vulnerabilityName, featureName string) error {
|
||||
func (pgSQL *pgSQL) DeleteVulnerabilityFix(vulnerabilityNamespace database.Namespace, vulnerabilityName, featureName string) error {
|
||||
defer observeQueryTime("DeleteVulnerabilityFix", "all", time.Now())
|
||||
|
||||
v := database.Vulnerability{
|
||||
Name: vulnerabilityName,
|
||||
Namespace: database.Namespace{
|
||||
Name: vulnerabilityNamespace,
|
||||
Name: vulnerabilityNamespace.Name,
|
||||
Version: vulnerabilityNamespace.Version,
|
||||
},
|
||||
FixedIn: []database.FeatureVersion{
|
||||
{
|
||||
Feature: database.Feature{
|
||||
Name: featureName,
|
||||
Namespace: database.Namespace{
|
||||
Name: vulnerabilityNamespace,
|
||||
Name: vulnerabilityNamespace.Name,
|
||||
Version: vulnerabilityNamespace.Version,
|
||||
},
|
||||
},
|
||||
Version: types.MinVersion,
|
||||
@ -535,7 +541,7 @@ func (pgSQL *pgSQL) DeleteVulnerabilityFix(vulnerabilityNamespace, vulnerability
|
||||
return pgSQL.insertVulnerability(v, true, true)
|
||||
}
|
||||
|
||||
func (pgSQL *pgSQL) DeleteVulnerability(namespaceName, name string) error {
|
||||
func (pgSQL *pgSQL) DeleteVulnerability(namespace database.Namespace, name string) error {
|
||||
defer observeQueryTime("DeleteVulnerability", "all", time.Now())
|
||||
|
||||
// Begin transaction.
|
||||
@ -546,7 +552,7 @@ func (pgSQL *pgSQL) DeleteVulnerability(namespaceName, name string) error {
|
||||
}
|
||||
|
||||
var vulnerabilityID int
|
||||
err = tx.QueryRow(removeVulnerability, namespaceName, name).Scan(&vulnerabilityID)
|
||||
err = tx.QueryRow(removeVulnerability, namespace.Name, &namespace.Version, name).Scan(&vulnerabilityID)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return handleError("removeVulnerability", err)
|
||||
|
@ -32,8 +32,12 @@ func TestFindVulnerability(t *testing.T) {
|
||||
}
|
||||
defer datastore.Close()
|
||||
|
||||
testExistNamespace := database.Namespace{
|
||||
Name: "debian",
|
||||
Version: types.NewVersionUnsafe("7"),
|
||||
}
|
||||
// Find a vulnerability that does not exist.
|
||||
_, err = datastore.FindVulnerability("", "")
|
||||
_, err = datastore.FindVulnerability(database.Namespace{}, "")
|
||||
assert.Equal(t, cerrors.ErrNotFound, err)
|
||||
|
||||
// Find a normal vulnerability.
|
||||
@ -42,7 +46,7 @@ func TestFindVulnerability(t *testing.T) {
|
||||
Description: "A vulnerability affecting OpenSSL < 2.0 on Debian 7.0",
|
||||
Link: "http://google.com/#q=CVE-OPENSSL-1-DEB7",
|
||||
Severity: types.High,
|
||||
Namespace: database.Namespace{Name: "debian:7"},
|
||||
Namespace: testExistNamespace,
|
||||
FixedIn: []database.FeatureVersion{
|
||||
{
|
||||
Feature: database.Feature{Name: "openssl"},
|
||||
@ -55,7 +59,7 @@ func TestFindVulnerability(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
v1f, err := datastore.FindVulnerability("debian:7", "CVE-OPENSSL-1-DEB7")
|
||||
v1f, err := datastore.FindVulnerability(testExistNamespace, "CVE-OPENSSL-1-DEB7")
|
||||
if assert.Nil(t, err) {
|
||||
equalsVuln(t, &v1, &v1f)
|
||||
}
|
||||
@ -64,11 +68,11 @@ func TestFindVulnerability(t *testing.T) {
|
||||
v2 := database.Vulnerability{
|
||||
Name: "CVE-NOPE",
|
||||
Description: "A vulnerability affecting nothing",
|
||||
Namespace: database.Namespace{Name: "debian:7"},
|
||||
Namespace: testExistNamespace,
|
||||
Severity: types.Unknown,
|
||||
}
|
||||
|
||||
v2f, err := datastore.FindVulnerability("debian:7", "CVE-NOPE")
|
||||
v2f, err := datastore.FindVulnerability(testExistNamespace, "CVE-NOPE")
|
||||
if assert.Nil(t, err) {
|
||||
equalsVuln(t, &v2, &v2f)
|
||||
}
|
||||
@ -82,16 +86,24 @@ func TestDeleteVulnerability(t *testing.T) {
|
||||
}
|
||||
defer datastore.Close()
|
||||
|
||||
testExistNamespace := database.Namespace{
|
||||
Name: "debian",
|
||||
Version: types.NewVersionUnsafe("7"),
|
||||
}
|
||||
testNonExistNamespace := database.Namespace{
|
||||
Name: "TestDeleteVulnerabilityNamespace",
|
||||
Version: types.NewVersionUnsafe("1.0"),
|
||||
}
|
||||
// Delete non-existing Vulnerability.
|
||||
err = datastore.DeleteVulnerability("TestDeleteVulnerabilityNamespace1", "CVE-OPENSSL-1-DEB7")
|
||||
err = datastore.DeleteVulnerability(testNonExistNamespace, "CVE-OPENSSL-1-DEB7")
|
||||
assert.Equal(t, cerrors.ErrNotFound, err)
|
||||
err = datastore.DeleteVulnerability("debian:7", "TestDeleteVulnerabilityVulnerability1")
|
||||
err = datastore.DeleteVulnerability(testExistNamespace, "TestDeleteVulnerabilityVulnerability1")
|
||||
assert.Equal(t, cerrors.ErrNotFound, err)
|
||||
|
||||
// Delete Vulnerability.
|
||||
err = datastore.DeleteVulnerability("debian:7", "CVE-OPENSSL-1-DEB7")
|
||||
err = datastore.DeleteVulnerability(testExistNamespace, "CVE-OPENSSL-1-DEB7")
|
||||
if assert.Nil(t, err) {
|
||||
_, err := datastore.FindVulnerability("debian:7", "CVE-OPENSSL-1-DEB7")
|
||||
_, err := datastore.FindVulnerability(testExistNamespace, "CVE-OPENSSL-1-DEB7")
|
||||
assert.Equal(t, cerrors.ErrNotFound, err)
|
||||
}
|
||||
}
|
||||
@ -105,8 +117,8 @@ func TestInsertVulnerability(t *testing.T) {
|
||||
defer datastore.Close()
|
||||
|
||||
// Create some data.
|
||||
n1 := database.Namespace{Name: "TestInsertVulnerabilityNamespace1"}
|
||||
n2 := database.Namespace{Name: "TestInsertVulnerabilityNamespace2"}
|
||||
n1 := database.Namespace{Name: "TestInsertVulnerabilityNamespace", Version: types.NewVersionUnsafe("1.0")}
|
||||
n2 := database.Namespace{Name: "TestInsertVulnerabilityNamespace", Version: types.NewVersionUnsafe("2.0")}
|
||||
|
||||
f1 := database.FeatureVersion{
|
||||
Feature: database.Feature{
|
||||
@ -215,7 +227,7 @@ func TestInsertVulnerability(t *testing.T) {
|
||||
}
|
||||
err = datastore.InsertVulnerabilities([]database.Vulnerability{v1}, true)
|
||||
if assert.Nil(t, err) {
|
||||
v1f, err := datastore.FindVulnerability(n1.Name, v1.Name)
|
||||
v1f, err := datastore.FindVulnerability(n1, v1.Name)
|
||||
if assert.Nil(t, err) {
|
||||
equalsVuln(t, &v1, &v1f)
|
||||
}
|
||||
@ -231,7 +243,7 @@ func TestInsertVulnerability(t *testing.T) {
|
||||
|
||||
err = datastore.InsertVulnerabilities([]database.Vulnerability{v1}, true)
|
||||
if assert.Nil(t, err) {
|
||||
v1f, err := datastore.FindVulnerability(n1.Name, v1.Name)
|
||||
v1f, err := datastore.FindVulnerability(n1, v1.Name)
|
||||
if assert.Nil(t, err) {
|
||||
// We already had f1 before the update.
|
||||
// Add it to the struct for comparison.
|
||||
@ -251,7 +263,7 @@ func TestInsertVulnerability(t *testing.T) {
|
||||
|
||||
func equalsVuln(t *testing.T, expected, actual *database.Vulnerability) {
|
||||
assert.Equal(t, expected.Name, actual.Name)
|
||||
assert.Equal(t, expected.Namespace.Name, actual.Namespace.Name)
|
||||
assert.True(t, expected.Namespace.Equal(actual.Namespace))
|
||||
assert.Equal(t, expected.Description, actual.Description)
|
||||
assert.Equal(t, expected.Link, actual.Link)
|
||||
assert.Equal(t, expected.Severity, actual.Severity)
|
||||
@ -264,7 +276,7 @@ func equalsVuln(t *testing.T, expected, actual *database.Vulnerability) {
|
||||
if expectedFeatureVersion.Feature.Name == actualFeatureVersion.Feature.Name {
|
||||
found = true
|
||||
|
||||
assert.Equal(t, expected.Namespace.Name, actualFeatureVersion.Feature.Namespace.Name)
|
||||
assert.True(t, expected.Namespace.Equal(actualFeatureVersion.Feature.Namespace))
|
||||
assert.Equal(t, expectedFeatureVersion.Version, actualFeatureVersion.Version)
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ func DetectFeatures(data map[string][]byte, namespaces []database.Namespace) ([]
|
||||
// Ensure that every feature has a Namespace associated
|
||||
for i := 0; i < len(pkgs); i++ {
|
||||
pkgs[i].Feature.Namespace.Name = namespace.Name
|
||||
pkgs[i].Feature.Namespace.Version = namespace.Version
|
||||
}
|
||||
packages = append(packages, pkgs...)
|
||||
break
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils/types"
|
||||
"github.com/coreos/clair/worker/detectors"
|
||||
)
|
||||
|
||||
@ -75,7 +76,7 @@ func (detector *AptSourcesNamespaceDetector) Detect(data map[string][]byte) *dat
|
||||
}
|
||||
|
||||
if OS != "" && version != "" {
|
||||
return &database.Namespace{Name: OS + ":" + version}
|
||||
return &database.Namespace{Name: OS, Version: types.NewVersionUnsafe(version)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -18,12 +18,13 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils/types"
|
||||
"github.com/coreos/clair/worker/detectors/namespace"
|
||||
)
|
||||
|
||||
var aptSourcesOSTests = []namespace.NamespaceTest{
|
||||
{
|
||||
ExpectedNamespace: database.Namespace{Name: "debian:unstable"},
|
||||
ExpectedNamespace: database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("unstable")},
|
||||
Data: map[string][]byte{
|
||||
"etc/os-release": []byte(
|
||||
`PRETTY_NAME="Debian GNU/Linux stretch/sid"
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils/types"
|
||||
"github.com/coreos/clair/worker/detectors"
|
||||
)
|
||||
|
||||
@ -70,7 +71,7 @@ func (detector *LsbReleaseNamespaceDetector) Detect(data map[string][]byte) *dat
|
||||
}
|
||||
|
||||
if OS != "" && version != "" {
|
||||
return &database.Namespace{Name: OS + ":" + version}
|
||||
return &database.Namespace{Name: OS, Version: types.NewVersionUnsafe(version)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -18,12 +18,13 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils/types"
|
||||
"github.com/coreos/clair/worker/detectors/namespace"
|
||||
)
|
||||
|
||||
var lsbReleaseOSTests = []namespace.NamespaceTest{
|
||||
{
|
||||
ExpectedNamespace: database.Namespace{Name: "ubuntu:12.04"},
|
||||
ExpectedNamespace: database.Namespace{Name: "ubuntu", Version: types.NewVersionUnsafe("12.04")},
|
||||
Data: map[string][]byte{
|
||||
"etc/lsb-release": []byte(
|
||||
`DISTRIB_ID=Ubuntu
|
||||
@ -33,7 +34,7 @@ DISTRIB_DESCRIPTION="Ubuntu 12.04 LTS"`),
|
||||
},
|
||||
},
|
||||
{ // We don't care about the minor version of Debian
|
||||
ExpectedNamespace: database.Namespace{Name: "debian:7"},
|
||||
ExpectedNamespace: database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("7")},
|
||||
Data: map[string][]byte{
|
||||
"etc/lsb-release": []byte(
|
||||
`DISTRIB_ID=Debian
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils/types"
|
||||
"github.com/coreos/clair/worker/detectors"
|
||||
)
|
||||
|
||||
@ -65,7 +66,7 @@ func (detector *OsReleaseNamespaceDetector) Detect(data map[string][]byte) *data
|
||||
}
|
||||
|
||||
if OS != "" && version != "" {
|
||||
return &database.Namespace{Name: OS + ":" + version}
|
||||
return &database.Namespace{Name: OS, Version: types.NewVersionUnsafe(version)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -18,12 +18,13 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils/types"
|
||||
"github.com/coreos/clair/worker/detectors/namespace"
|
||||
)
|
||||
|
||||
var osReleaseOSTests = []namespace.NamespaceTest{
|
||||
{
|
||||
ExpectedNamespace: database.Namespace{Name: "debian:8"},
|
||||
ExpectedNamespace: database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("8")},
|
||||
Data: map[string][]byte{
|
||||
"etc/os-release": []byte(
|
||||
`PRETTY_NAME="Debian GNU/Linux 8 (jessie)"
|
||||
@ -37,7 +38,7 @@ BUG_REPORT_URL="https://bugs.debian.org/"`),
|
||||
},
|
||||
},
|
||||
{
|
||||
ExpectedNamespace: database.Namespace{Name: "ubuntu:15.10"},
|
||||
ExpectedNamespace: database.Namespace{Name: "ubuntu", Version: types.NewVersionUnsafe("15.10")},
|
||||
Data: map[string][]byte{
|
||||
"etc/os-release": []byte(
|
||||
`NAME="Ubuntu"
|
||||
@ -52,7 +53,7 @@ BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`),
|
||||
},
|
||||
},
|
||||
{ // Doesn't have quotes around VERSION_ID
|
||||
ExpectedNamespace: database.Namespace{Name: "fedora:20"},
|
||||
ExpectedNamespace: database.Namespace{Name: "fedora", Version: types.NewVersionUnsafe("20")},
|
||||
Data: map[string][]byte{
|
||||
"etc/os-release": []byte(
|
||||
`NAME=Fedora
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils/types"
|
||||
"github.com/coreos/clair/worker/detectors"
|
||||
)
|
||||
|
||||
@ -46,7 +47,7 @@ func (detector *RedhatReleaseNamespaceDetector) Detect(data map[string][]byte) *
|
||||
|
||||
r := redhatReleaseRegexp.FindStringSubmatch(string(f))
|
||||
if len(r) == 4 {
|
||||
return &database.Namespace{Name: strings.ToLower(r[1]) + ":" + r[3]}
|
||||
return &database.Namespace{Name: strings.ToLower(r[1]), Version: types.NewVersionUnsafe(r[3])}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,18 +18,19 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/clair/database"
|
||||
"github.com/coreos/clair/utils/types"
|
||||
"github.com/coreos/clair/worker/detectors/namespace"
|
||||
)
|
||||
|
||||
var redhatReleaseTests = []namespace.NamespaceTest{
|
||||
{
|
||||
ExpectedNamespace: database.Namespace{Name: "centos:6"},
|
||||
ExpectedNamespace: database.Namespace{Name: "centos", Version: types.NewVersionUnsafe("6")},
|
||||
Data: map[string][]byte{
|
||||
"etc/centos-release": []byte(`CentOS release 6.6 (Final)`),
|
||||
},
|
||||
},
|
||||
{
|
||||
ExpectedNamespace: database.Namespace{Name: "centos:7"},
|
||||
ExpectedNamespace: database.Namespace{Name: "centos", Version: types.NewVersionUnsafe("7")},
|
||||
Data: map[string][]byte{
|
||||
"etc/system-release": []byte(`CentOS Linux release 7.1.1503 (Core)`),
|
||||
},
|
||||
|
@ -29,6 +29,6 @@ type NamespaceTest struct {
|
||||
|
||||
func TestNamespaceDetector(t *testing.T, detector detectors.NamespaceDetector, tests []NamespaceTest) {
|
||||
for _, test := range tests {
|
||||
assert.Equal(t, test.ExpectedNamespace, *detector.Detect(test.Data))
|
||||
assert.True(t, test.ExpectedNamespace.Equal(*detector.Detect(test.Data)))
|
||||
}
|
||||
}
|
||||
|
@ -53,12 +53,20 @@ func TestProcessWithDistUpgrade(t *testing.T) {
|
||||
|
||||
wheezy, err := datastore.FindLayer("wheezy", true, false)
|
||||
if assert.Nil(t, err) {
|
||||
assert.Equal(t, "debian:7", wheezy.Namespaces[0].Name)
|
||||
testDebian7 := database.Namespace{
|
||||
Name: "debian",
|
||||
Version: types.NewVersionUnsafe("7"),
|
||||
}
|
||||
assert.True(t, testDebian7.Equal(wheezy.Namespaces[0]))
|
||||
assert.Len(t, wheezy.Features, 52)
|
||||
|
||||
jessie, err := datastore.FindLayer("jessie", true, false)
|
||||
if assert.Nil(t, err) {
|
||||
assert.Equal(t, "debian:8", jessie.Namespaces[0].Name)
|
||||
testDebian8 := database.Namespace{
|
||||
Name: "debian",
|
||||
Version: types.NewVersionUnsafe("8"),
|
||||
}
|
||||
assert.True(t, testDebian8.Equal(jessie.Namespaces[0]))
|
||||
assert.Len(t, jessie.Features, 74)
|
||||
|
||||
// These FeatureVersions haven't been upgraded.
|
||||
@ -98,13 +106,13 @@ func TestProcessWithDistUpgrade(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, nufv := range nonUpgradedFeatureVersions {
|
||||
nufv.Feature.Namespace.Name = "debian:7"
|
||||
nufv.Feature.Namespace.Name = "debian"
|
||||
nufv.Feature.Namespace.Version = types.NewVersionUnsafe("7")
|
||||
|
||||
found := false
|
||||
for _, fv := range jessie.Features {
|
||||
if fv.Feature.Name == nufv.Feature.Name &&
|
||||
fv.Feature.Namespace.Name == nufv.Feature.Namespace.Name &&
|
||||
fv.Version == nufv.Version {
|
||||
fv.Feature.Namespace.Equal(nufv.Feature.Namespace) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
@ -113,13 +121,13 @@ func TestProcessWithDistUpgrade(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, nufv := range nonUpgradedFeatureVersions {
|
||||
nufv.Feature.Namespace.Name = "debian:8"
|
||||
nufv.Feature.Namespace.Name = "debian"
|
||||
nufv.Feature.Namespace.Version = types.NewVersionUnsafe("8")
|
||||
|
||||
found := false
|
||||
for _, fv := range jessie.Features {
|
||||
if fv.Feature.Name == nufv.Feature.Name &&
|
||||
fv.Feature.Namespace.Name == nufv.Feature.Namespace.Name &&
|
||||
fv.Version == nufv.Version {
|
||||
fv.Feature.Namespace.Equal(nufv.Feature.Namespace) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user