148 lines
4.2 KiB
Go
148 lines
4.2 KiB
Go
|
// Copyright 2019 clair authors
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package layer
|
||
|
|
||
|
import (
|
||
|
"database/sql"
|
||
|
"sort"
|
||
|
|
||
|
"github.com/coreos/clair/database/pgsql/namespace"
|
||
|
|
||
|
"github.com/coreos/clair/database"
|
||
|
"github.com/coreos/clair/database/pgsql/detector"
|
||
|
"github.com/coreos/clair/database/pgsql/feature"
|
||
|
"github.com/coreos/clair/database/pgsql/util"
|
||
|
)
|
||
|
|
||
|
const findLayerFeatures = `
|
||
|
SELECT
|
||
|
f.name, f.version, f.version_format, ft.name, lf.detector_id, ns.name, ns.version_format
|
||
|
FROM
|
||
|
layer_feature AS lf
|
||
|
LEFT JOIN feature f on f.id = lf.feature_id
|
||
|
LEFT JOIN feature_type ft on ft.id = f.type
|
||
|
LEFT JOIN namespace ns ON ns.id = lf.namespace_id
|
||
|
|
||
|
WHERE lf.layer_id = $1`
|
||
|
|
||
|
func queryPersistLayerFeature(count int) string {
|
||
|
return util.QueryPersist(count,
|
||
|
"layer_feature",
|
||
|
"layer_feature_layer_id_feature_id_namespace_id_key",
|
||
|
"layer_id",
|
||
|
"feature_id",
|
||
|
"detector_id",
|
||
|
"namespace_id")
|
||
|
}
|
||
|
|
||
|
// dbLayerFeature represents the layer_feature table
|
||
|
type dbLayerFeature struct {
|
||
|
layerID int64
|
||
|
featureID int64
|
||
|
detectorID int64
|
||
|
namespaceID sql.NullInt64
|
||
|
}
|
||
|
|
||
|
func FindLayerFeatures(tx *sql.Tx, layerID int64, detectors detector.DetectorMap) ([]database.LayerFeature, error) {
|
||
|
rows, err := tx.Query(findLayerFeatures, layerID)
|
||
|
if err != nil {
|
||
|
return nil, util.HandleError("findLayerFeatures", err)
|
||
|
}
|
||
|
defer rows.Close()
|
||
|
|
||
|
features := []database.LayerFeature{}
|
||
|
for rows.Next() {
|
||
|
var (
|
||
|
detectorID int64
|
||
|
feature database.LayerFeature
|
||
|
)
|
||
|
var namespaceName, namespaceVersion sql.NullString
|
||
|
if err := rows.Scan(&feature.Name, &feature.Version, &feature.VersionFormat, &feature.Type, &detectorID, &namespaceName, &namespaceVersion); err != nil {
|
||
|
return nil, util.HandleError("findLayerFeatures", err)
|
||
|
}
|
||
|
feature.PotentialNamespace.Name = namespaceName.String
|
||
|
feature.PotentialNamespace.VersionFormat = namespaceVersion.String
|
||
|
|
||
|
feature.By = detectors.ByID[detectorID]
|
||
|
features = append(features, feature)
|
||
|
}
|
||
|
|
||
|
return features, nil
|
||
|
}
|
||
|
|
||
|
func PersistAllLayerFeatures(tx *sql.Tx, layerID int64, features []database.LayerFeature) error {
|
||
|
detectorMap, err := detector.FindAllDetectors(tx)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
var namespaces []database.Namespace
|
||
|
for _, feature := range features {
|
||
|
namespaces = append(namespaces, feature.PotentialNamespace)
|
||
|
}
|
||
|
nameSpaceIDs, _ := namespace.FindNamespaceIDs(tx, namespaces)
|
||
|
featureNamespaceMap := map[database.Namespace]sql.NullInt64{}
|
||
|
rawFeatures := make([]database.Feature, 0, len(features))
|
||
|
for i, f := range features {
|
||
|
rawFeatures = append(rawFeatures, f.Feature)
|
||
|
if f.PotentialNamespace.Valid() {
|
||
|
featureNamespaceMap[f.PotentialNamespace] = nameSpaceIDs[i]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
featureIDs, err := feature.FindFeatureIDs(tx, rawFeatures)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
var namespaceID sql.NullInt64
|
||
|
dbFeatures := make([]dbLayerFeature, 0, len(features))
|
||
|
for i, f := range features {
|
||
|
detectorID := detectorMap.ByValue[f.By]
|
||
|
featureID := featureIDs[i].Int64
|
||
|
if !featureIDs[i].Valid {
|
||
|
return database.ErrMissingEntities
|
||
|
}
|
||
|
namespaceID = featureNamespaceMap[f.PotentialNamespace]
|
||
|
|
||
|
dbFeatures = append(dbFeatures, dbLayerFeature{layerID, featureID, detectorID, namespaceID})
|
||
|
}
|
||
|
|
||
|
if err := PersistLayerFeatures(tx, dbFeatures); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func PersistLayerFeatures(tx *sql.Tx, features []dbLayerFeature) error {
|
||
|
if len(features) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
sort.Slice(features, func(i, j int) bool {
|
||
|
return features[i].featureID < features[j].featureID
|
||
|
})
|
||
|
keys := make([]interface{}, 0, len(features)*4)
|
||
|
|
||
|
for _, f := range features {
|
||
|
keys = append(keys, f.layerID, f.featureID, f.detectorID, f.namespaceID)
|
||
|
}
|
||
|
|
||
|
_, err := tx.Exec(queryPersistLayerFeature(len(features)), keys...)
|
||
|
if err != nil {
|
||
|
return util.HandleError("queryPersistLayerFeature", err)
|
||
|
}
|
||
|
return nil
|
||
|
}
|