2017-07-26 23:23:54 +00:00
|
|
|
// Copyright 2016 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 migrations
|
|
|
|
|
2018-09-13 21:06:46 +00:00
|
|
|
var (
|
|
|
|
// entities are the basic building blocks to relate the vulnerabilities with
|
|
|
|
// the ancestry.
|
|
|
|
entities = MigrationQuery{
|
|
|
|
Up: []string{
|
2019-02-19 21:32:24 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS feature_type (
|
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
name TEXT NOT NULL UNIQUE);`,
|
|
|
|
|
|
|
|
`INSERT INTO feature_type(name) VALUES ('source'), ('binary')`,
|
|
|
|
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS namespace (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
name TEXT NULL,
|
|
|
|
version_format TEXT,
|
|
|
|
UNIQUE (name, version_format));`,
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE INDEX ON namespace(name);`,
|
|
|
|
|
|
|
|
`CREATE TABLE IF NOT EXISTS feature (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
version TEXT NOT NULL,
|
|
|
|
version_format TEXT NOT NULL,
|
2019-02-19 21:32:24 +00:00
|
|
|
type INT REFERENCES feature_type ON DELETE CASCADE,
|
|
|
|
UNIQUE (name, version, version_format, type));`,
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE INDEX ON feature(name);`,
|
|
|
|
|
|
|
|
`CREATE TABLE IF NOT EXISTS namespaced_feature (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
2018-10-08 15:11:30 +00:00
|
|
|
namespace_id INT REFERENCES namespace ON DELETE CASCADE,
|
|
|
|
feature_id INT REFERENCES feature ON DELETE CASCADE,
|
2018-09-12 18:09:17 +00:00
|
|
|
UNIQUE (namespace_id, feature_id));`,
|
2018-09-13 21:06:46 +00:00
|
|
|
},
|
|
|
|
Down: []string{
|
2019-02-19 21:32:24 +00:00
|
|
|
`DROP TABLE IF EXISTS namespace, feature, namespaced_feature, feature_type CASCADE;`,
|
2018-09-13 21:06:46 +00:00
|
|
|
},
|
|
|
|
}
|
2017-07-26 23:23:54 +00:00
|
|
|
|
2018-09-13 21:06:46 +00:00
|
|
|
// detector is analysis extensions used by the worker.
|
|
|
|
detector = MigrationQuery{
|
|
|
|
Up: []string{
|
|
|
|
`CREATE TYPE detector_type AS ENUM ('namespace', 'feature');`,
|
|
|
|
|
|
|
|
`CREATE TABLE IF NOT EXISTS detector (
|
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
version TEXT NOT NULL,
|
|
|
|
dtype detector_type NOT NULL,
|
|
|
|
UNIQUE (name, version, dtype));`,
|
|
|
|
},
|
|
|
|
Down: []string{
|
|
|
|
`DROP TABLE IF EXISTS detector CASCADE;`,
|
|
|
|
`DROP TYPE IF EXISTS detector_type;`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// layer contains all metadata and scanned features and namespaces.
|
|
|
|
layer = MigrationQuery{
|
|
|
|
Up: []string{
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS layer(
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
hash TEXT NOT NULL UNIQUE);`,
|
2017-07-26 23:23:54 +00:00
|
|
|
|
2018-09-13 21:06:46 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS layer_detector(
|
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
layer_id INT REFERENCES layer ON DELETE CASCADE,
|
|
|
|
detector_id INT REFERENCES detector ON DELETE CASCADE,
|
|
|
|
UNIQUE(layer_id, detector_id));`,
|
|
|
|
`CREATE INDEX ON layer_detector(layer_id);`,
|
|
|
|
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS layer_feature (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
layer_id INT REFERENCES layer ON DELETE CASCADE,
|
|
|
|
feature_id INT REFERENCES feature ON DELETE CASCADE,
|
2018-09-13 21:06:46 +00:00
|
|
|
detector_id INT REFERENCES detector ON DELETE CASCADE,
|
2019-03-07 12:53:58 +00:00
|
|
|
namespace_id INT NULL REFERENCES namespace ON DELETE CASCADE,
|
2019-03-06 12:57:11 +00:00
|
|
|
UNIQUE (layer_id, feature_id, namespace_id));`,
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE INDEX ON layer_feature(layer_id);`,
|
|
|
|
|
|
|
|
`CREATE TABLE IF NOT EXISTS layer_namespace (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
layer_id INT REFERENCES layer ON DELETE CASCADE,
|
|
|
|
namespace_id INT REFERENCES namespace ON DELETE CASCADE,
|
2018-09-13 21:06:46 +00:00
|
|
|
detector_id INT REFERENCES detector ON DELETE CASCADE,
|
2018-09-12 18:09:17 +00:00
|
|
|
UNIQUE (layer_id, namespace_id));`,
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE INDEX ON layer_namespace(layer_id);`,
|
2018-09-13 21:06:46 +00:00
|
|
|
},
|
|
|
|
Down: []string{
|
|
|
|
`DROP TABLE IF EXISTS layer, layer_detector, layer_feature, layer_namespace CASCADE;`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// ancestry contains all meta information around scanned manifest and its
|
|
|
|
// layers.
|
|
|
|
ancestry = MigrationQuery{
|
|
|
|
Up: []string{
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS ancestry (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
name TEXT NOT NULL UNIQUE);`,
|
2017-07-26 23:23:54 +00:00
|
|
|
|
|
|
|
`CREATE TABLE IF NOT EXISTS ancestry_layer (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
2019-02-25 22:50:40 +00:00
|
|
|
ancestry_id INT NOT NULL REFERENCES ancestry ON DELETE CASCADE,
|
2018-09-12 18:09:17 +00:00
|
|
|
ancestry_index INT NOT NULL,
|
2018-10-08 15:11:30 +00:00
|
|
|
layer_id INT NOT NULL REFERENCES layer ON DELETE RESTRICT,
|
2018-09-12 18:09:17 +00:00
|
|
|
UNIQUE (ancestry_id, ancestry_index));`,
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE INDEX ON ancestry_layer(ancestry_id);`,
|
|
|
|
|
2018-09-05 15:34:49 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS ancestry_feature(
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
2019-02-25 22:50:40 +00:00
|
|
|
ancestry_layer_id INT NOT NULL REFERENCES ancestry_layer ON DELETE CASCADE,
|
|
|
|
namespaced_feature_id INT NOT NULL REFERENCES namespaced_feature ON DELETE CASCADE,
|
|
|
|
feature_detector_id INT NOT NULL REFERENCES detector ON DELETE CASCADE,
|
2019-03-14 16:27:30 +00:00
|
|
|
namespace_detector_id INT REFERENCES detector ON DELETE CASCADE,
|
2018-09-12 18:09:17 +00:00
|
|
|
UNIQUE (ancestry_layer_id, namespaced_feature_id));`,
|
2017-07-26 23:23:54 +00:00
|
|
|
|
2018-09-13 21:06:46 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS ancestry_detector(
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
2019-02-25 22:50:40 +00:00
|
|
|
ancestry_id INT NOT NULL REFERENCES ancestry ON DELETE CASCADE,
|
|
|
|
detector_id INT NOT NULL REFERENCES detector ON DELETE CASCADE,
|
2018-09-13 21:06:46 +00:00
|
|
|
UNIQUE(ancestry_id, detector_id));`,
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE INDEX ON ancestry_detector(ancestry_id);`,
|
2018-09-13 21:06:46 +00:00
|
|
|
},
|
|
|
|
Down: []string{
|
|
|
|
`DROP TABLE IF EXISTS ancestry, ancestry_layer, ancestry_feature, ancestry_detector CASCADE;`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// vulnerability contains the metadata and vulnerability affecting relation.
|
|
|
|
vulnerability = MigrationQuery{
|
|
|
|
Up: []string{
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE TYPE severity AS ENUM ('Unknown', 'Negligible', 'Low', 'Medium', 'High', 'Critical', 'Defcon1');`,
|
|
|
|
|
|
|
|
`CREATE TABLE IF NOT EXISTS vulnerability (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
2018-09-13 21:06:46 +00:00
|
|
|
namespace_id INT REFERENCES Namespace,
|
2018-09-12 18:09:17 +00:00
|
|
|
name TEXT NOT NULL,
|
|
|
|
description TEXT NULL,
|
|
|
|
link TEXT NULL,
|
|
|
|
severity severity NOT NULL,
|
|
|
|
metadata TEXT NULL,
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
deleted_at TIMESTAMP WITH TIME ZONE NULL);`,
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE INDEX ON vulnerability(namespace_id, name);`,
|
|
|
|
`CREATE INDEX ON vulnerability(namespace_id);`,
|
|
|
|
|
2019-02-19 21:32:24 +00:00
|
|
|
// vulnerability_affected_feature is a de-normalized table to store
|
|
|
|
// the affected features in a independent place other than the
|
|
|
|
// feature table to reduce table lock issue, and makes it easier for
|
|
|
|
// decoupling updater and the Clair main logic.
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS vulnerability_affected_feature (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
2019-02-25 22:50:40 +00:00
|
|
|
vulnerability_id INT NOT NULL REFERENCES vulnerability ON DELETE CASCADE,
|
2018-09-12 18:09:17 +00:00
|
|
|
feature_name TEXT NOT NULL,
|
2019-02-19 21:32:24 +00:00
|
|
|
feature_type INT NOT NULL REFERENCES feature_type ON DELETE CASCADE,
|
2018-09-12 18:09:17 +00:00
|
|
|
affected_version TEXT,
|
|
|
|
fixedin TEXT);`,
|
2019-02-19 21:32:24 +00:00
|
|
|
`CREATE INDEX ON vulnerability_affected_feature(vulnerability_id, feature_name, feature_type);`,
|
2017-07-26 23:23:54 +00:00
|
|
|
|
|
|
|
`CREATE TABLE IF NOT EXISTS vulnerability_affected_namespaced_feature(
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
2019-02-25 22:50:40 +00:00
|
|
|
vulnerability_id INT NOT NULL REFERENCES vulnerability ON DELETE CASCADE,
|
|
|
|
namespaced_feature_id INT NOT NULL REFERENCES namespaced_feature ON DELETE CASCADE,
|
|
|
|
added_by INT NOT NULL REFERENCES vulnerability_affected_feature ON DELETE CASCADE,
|
2018-09-12 18:09:17 +00:00
|
|
|
UNIQUE (vulnerability_id, namespaced_feature_id));`,
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE INDEX ON vulnerability_affected_namespaced_feature(namespaced_feature_id);`,
|
2018-09-13 21:06:46 +00:00
|
|
|
},
|
|
|
|
Down: []string{
|
|
|
|
`DROP TABLE IF EXISTS vulnerability, vulnerability_affected_feature, vulnerability_affected_namespaced_feature CASCADE;`,
|
2019-02-19 21:32:24 +00:00
|
|
|
`DROP TYPE IF EXISTS severity;`,
|
2018-09-13 21:06:46 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// updaterLock is the lock to be used by updater to prevent multiple
|
|
|
|
// updaters running on the same vulnerability source.
|
|
|
|
updaterLock = MigrationQuery{
|
|
|
|
Up: []string{
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS KeyValue (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
key TEXT NOT NULL UNIQUE,
|
|
|
|
value TEXT);`,
|
2017-07-26 23:23:54 +00:00
|
|
|
|
|
|
|
`CREATE TABLE IF NOT EXISTS Lock (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
name VARCHAR(64) NOT NULL UNIQUE,
|
|
|
|
owner VARCHAR(64) NOT NULL,
|
|
|
|
until TIMESTAMP WITH TIME ZONE);`,
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE INDEX ON Lock (owner);`,
|
2018-09-13 21:06:46 +00:00
|
|
|
},
|
|
|
|
Down: []string{
|
|
|
|
`DROP TABLE IF EXISTS KeyValue, Lock CASCADE;`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// notification is the vulnerability notification spawned by the
|
|
|
|
// vulnerability changes.
|
|
|
|
notification = MigrationQuery{
|
|
|
|
Up: []string{
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE TABLE IF NOT EXISTS Vulnerability_Notification (
|
2018-09-12 18:09:17 +00:00
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
name VARCHAR(64) NOT NULL UNIQUE,
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
notified_at TIMESTAMP WITH TIME ZONE NULL,
|
|
|
|
deleted_at TIMESTAMP WITH TIME ZONE NULL,
|
|
|
|
old_vulnerability_id INT NULL REFERENCES Vulnerability ON DELETE CASCADE,
|
|
|
|
new_vulnerability_id INT NULL REFERENCES Vulnerability ON DELETE CASCADE);`,
|
2017-07-26 23:23:54 +00:00
|
|
|
`CREATE INDEX ON Vulnerability_Notification (notified_at);`,
|
2018-09-13 21:06:46 +00:00
|
|
|
},
|
|
|
|
Down: []string{
|
|
|
|
`DROP TABLE IF EXISTS Vulnerability_Notification CASCADE;`,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
RegisterMigration(NewSimpleMigration(1,
|
|
|
|
[]MigrationQuery{
|
|
|
|
entities,
|
|
|
|
detector,
|
|
|
|
layer,
|
|
|
|
ancestry,
|
|
|
|
vulnerability,
|
|
|
|
updaterLock,
|
|
|
|
notification,
|
|
|
|
}))
|
2017-07-26 23:23:54 +00:00
|
|
|
}
|