178 lines
4.2 KiB
Go
178 lines
4.2 KiB
Go
// Copyright 2017 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"
|
|
|
|
"github.com/deckarep/golang-set"
|
|
|
|
"github.com/coreos/clair/database"
|
|
"github.com/coreos/clair/database/pgsql/detector"
|
|
"github.com/coreos/clair/database/pgsql/util"
|
|
"github.com/coreos/clair/pkg/commonerr"
|
|
)
|
|
|
|
const (
|
|
soiLayer = `
|
|
WITH new_layer AS (
|
|
INSERT INTO layer (hash)
|
|
SELECT CAST ($1 AS VARCHAR)
|
|
WHERE NOT EXISTS (SELECT id FROM layer WHERE hash = $1)
|
|
RETURNING id
|
|
)
|
|
SELECT id FROM new_Layer
|
|
UNION
|
|
SELECT id FROM layer WHERE hash = $1`
|
|
|
|
findLayerID = `SELECT id FROM layer WHERE hash = $1`
|
|
)
|
|
|
|
func FindLayer(tx *sql.Tx, hash string) (database.Layer, bool, error) {
|
|
layer := database.Layer{Hash: hash}
|
|
if hash == "" {
|
|
return layer, false, commonerr.NewBadRequestError("non empty layer hash is expected.")
|
|
}
|
|
|
|
layerID, ok, err := FindLayerID(tx, hash)
|
|
if err != nil || !ok {
|
|
return layer, ok, err
|
|
}
|
|
|
|
detectorMap, err := detector.FindAllDetectors(tx)
|
|
if err != nil {
|
|
return layer, false, err
|
|
}
|
|
|
|
if layer.By, err = FindLayerDetectors(tx, layerID); err != nil {
|
|
return layer, false, err
|
|
}
|
|
|
|
if layer.Features, err = FindLayerFeatures(tx, layerID, detectorMap); err != nil {
|
|
return layer, false, err
|
|
}
|
|
|
|
if layer.Namespaces, err = FindLayerNamespaces(tx, layerID, detectorMap); err != nil {
|
|
return layer, false, err
|
|
}
|
|
|
|
return layer, true, nil
|
|
}
|
|
|
|
func sanitizePersistLayerInput(hash string, features []database.LayerFeature, namespaces []database.LayerNamespace, detectedBy []database.Detector) error {
|
|
if hash == "" {
|
|
return commonerr.NewBadRequestError("expected non-empty layer hash")
|
|
}
|
|
|
|
detectedBySet := mapset.NewSet()
|
|
for _, d := range detectedBy {
|
|
detectedBySet.Add(d)
|
|
}
|
|
|
|
for _, f := range features {
|
|
if !detectedBySet.Contains(f.By) {
|
|
return database.ErrInvalidParameters
|
|
}
|
|
}
|
|
|
|
for _, n := range namespaces {
|
|
if !detectedBySet.Contains(n.By) {
|
|
return database.ErrInvalidParameters
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PersistLayer saves the content of a layer to the database.
|
|
func PersistLayer(tx *sql.Tx, hash string, features []database.LayerFeature, namespaces []database.LayerNamespace, detectedBy []database.Detector) error {
|
|
var (
|
|
err error
|
|
id int64
|
|
detectorIDs []int64
|
|
)
|
|
|
|
if err = sanitizePersistLayerInput(hash, features, namespaces, detectedBy); err != nil {
|
|
return err
|
|
}
|
|
|
|
if id, err = SoiLayer(tx, hash); err != nil {
|
|
return err
|
|
}
|
|
|
|
if detectorIDs, err = detector.FindDetectorIDs(tx, detectedBy); err != nil {
|
|
if err == commonerr.ErrNotFound {
|
|
return database.ErrMissingEntities
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
if err = PersistLayerDetectors(tx, id, detectorIDs); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = PersistAllLayerFeatures(tx, id, features); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = PersistAllLayerNamespaces(tx, id, namespaces); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func FindLayerID(tx *sql.Tx, hash string) (int64, bool, error) {
|
|
var layerID int64
|
|
err := tx.QueryRow(findLayerID, hash).Scan(&layerID)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return layerID, false, nil
|
|
}
|
|
|
|
return layerID, false, util.HandleError("findLayerID", err)
|
|
}
|
|
|
|
return layerID, true, nil
|
|
}
|
|
|
|
func FindLayerIDs(tx *sql.Tx, hashes []string) ([]int64, bool, error) {
|
|
layerIDs := make([]int64, 0, len(hashes))
|
|
for _, hash := range hashes {
|
|
id, ok, err := FindLayerID(tx, hash)
|
|
if !ok {
|
|
return nil, false, nil
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
layerIDs = append(layerIDs, id)
|
|
}
|
|
|
|
return layerIDs, true, nil
|
|
}
|
|
|
|
func SoiLayer(tx *sql.Tx, hash string) (int64, error) {
|
|
var id int64
|
|
if err := tx.QueryRow(soiLayer, hash).Scan(&id); err != nil {
|
|
return 0, util.HandleError("soiLayer", err)
|
|
}
|
|
|
|
return id, nil
|
|
}
|