140 lines
4.2 KiB
Go
140 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 ancestry
|
||
|
|
||
|
import (
|
||
|
"database/sql"
|
||
|
|
||
|
"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"
|
||
|
"github.com/coreos/clair/pkg/commonerr"
|
||
|
)
|
||
|
|
||
|
const findAncestryFeatures = `
|
||
|
SELECT namespace.name, namespace.version_format, feature.name,
|
||
|
feature.version, feature.version_format, feature_type.name, ancestry_layer.ancestry_index,
|
||
|
ancestry_feature.feature_detector_id, ancestry_feature.namespace_detector_id
|
||
|
FROM namespace, feature, feature_type, namespaced_feature, ancestry_layer, ancestry_feature
|
||
|
WHERE ancestry_layer.ancestry_id = $1
|
||
|
AND feature_type.id = feature.type
|
||
|
AND ancestry_feature.ancestry_layer_id = ancestry_layer.id
|
||
|
AND ancestry_feature.namespaced_feature_id = namespaced_feature.id
|
||
|
AND namespaced_feature.feature_id = feature.id
|
||
|
AND namespaced_feature.namespace_id = namespace.id`
|
||
|
|
||
|
func FindAncestryFeatures(tx *sql.Tx, ancestryID int64, detectors detector.DetectorMap) (map[int64][]database.AncestryFeature, error) {
|
||
|
// ancestry_index -> ancestry features
|
||
|
featureMap := make(map[int64][]database.AncestryFeature)
|
||
|
// retrieve ancestry layer's namespaced features
|
||
|
rows, err := tx.Query(findAncestryFeatures, ancestryID)
|
||
|
if err != nil {
|
||
|
return nil, util.HandleError("findAncestryFeatures", err)
|
||
|
}
|
||
|
|
||
|
defer rows.Close()
|
||
|
|
||
|
for rows.Next() {
|
||
|
var (
|
||
|
featureDetectorID int64
|
||
|
namespaceDetectorID int64
|
||
|
feature database.NamespacedFeature
|
||
|
// index is used to determine which layer the feature belongs to.
|
||
|
index sql.NullInt64
|
||
|
)
|
||
|
|
||
|
if err := rows.Scan(
|
||
|
&feature.Namespace.Name,
|
||
|
&feature.Namespace.VersionFormat,
|
||
|
&feature.Feature.Name,
|
||
|
&feature.Feature.Version,
|
||
|
&feature.Feature.VersionFormat,
|
||
|
&feature.Feature.Type,
|
||
|
&index,
|
||
|
&featureDetectorID,
|
||
|
&namespaceDetectorID,
|
||
|
); err != nil {
|
||
|
return nil, util.HandleError("findAncestryFeatures", err)
|
||
|
}
|
||
|
|
||
|
if feature.Feature.VersionFormat != feature.Namespace.VersionFormat {
|
||
|
// Feature must have the same version format as the associated
|
||
|
// namespace version format.
|
||
|
return nil, database.ErrInconsistent
|
||
|
}
|
||
|
|
||
|
fDetector, ok := detectors.ByID[featureDetectorID]
|
||
|
if !ok {
|
||
|
return nil, database.ErrInconsistent
|
||
|
}
|
||
|
|
||
|
nsDetector, ok := detectors.ByID[namespaceDetectorID]
|
||
|
if !ok {
|
||
|
return nil, database.ErrInconsistent
|
||
|
}
|
||
|
|
||
|
featureMap[index.Int64] = append(featureMap[index.Int64], database.AncestryFeature{
|
||
|
NamespacedFeature: feature,
|
||
|
FeatureBy: fDetector,
|
||
|
NamespaceBy: nsDetector,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return featureMap, nil
|
||
|
}
|
||
|
|
||
|
func InsertAncestryFeatures(tx *sql.Tx, ancestryLayerID int64, layer database.AncestryLayer) error {
|
||
|
detectors, err := detector.FindAllDetectors(tx)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
nsFeatureIDs, err := feature.FindNamespacedFeatureIDs(tx, layer.GetFeatures())
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// find the detectors for each feature
|
||
|
stmt, err := tx.Prepare(insertAncestryFeatures)
|
||
|
if err != nil {
|
||
|
return util.HandleError("insertAncestryFeatures", err)
|
||
|
}
|
||
|
|
||
|
defer stmt.Close()
|
||
|
|
||
|
for index, id := range nsFeatureIDs {
|
||
|
if !id.Valid {
|
||
|
return database.ErrMissingEntities
|
||
|
}
|
||
|
|
||
|
namespaceDetectorID, ok := detectors.ByValue[layer.Features[index].NamespaceBy]
|
||
|
if !ok {
|
||
|
return database.ErrMissingEntities
|
||
|
}
|
||
|
|
||
|
featureDetectorID, ok := detectors.ByValue[layer.Features[index].FeatureBy]
|
||
|
if !ok {
|
||
|
return database.ErrMissingEntities
|
||
|
}
|
||
|
|
||
|
if _, err := stmt.Exec(ancestryLayerID, id, featureDetectorID, namespaceDetectorID); err != nil {
|
||
|
return util.HandleError("insertAncestryFeatures", commonerr.CombineErrors(err, stmt.Close()))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|