// 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 database defines the Clair's models and a common interface for // database implementations. package database import ( "errors" "fmt" "time" "github.com/coreos/clair/config" ) var ( // ErrBackendException is an error that occurs when the database backend does // not work properly (ie. unreachable). ErrBackendException = errors.New("database: an error occured when querying the backend") // ErrInconsistent is an error that occurs when a database consistency check // fails (i.e. when an entity which is supposed to be unique is detected // twice) ErrInconsistent = errors.New("database: inconsistent database") ) var drivers = make(map[string]Driver) // Driver is a function that opens a Datastore specified by its database driver type and specific // configuration. type Driver func(config.RegistrableComponentConfig) (Datastore, error) // Register makes a Constructor available by the provided name. // // If this function is called twice with the same name or if the Constructor is // nil, it panics. func Register(name string, driver Driver) { if driver == nil { panic("database: could not register nil Driver") } if _, dup := drivers[name]; dup { panic("database: could not register duplicate Driver: " + name) } drivers[name] = driver } // Open opens a Datastore specified by a configuration. func Open(cfg config.RegistrableComponentConfig) (Datastore, error) { driver, ok := drivers[cfg.Type] if !ok { return nil, fmt.Errorf("database: unknown Driver %q (forgotten configuration or import?)", cfg.Type) } return driver(cfg) } // Datastore represents the required operations on a persistent data store for // a Clair deployment. type Datastore interface { // ListNamespaces returns the entire list of known Namespaces. ListNamespaces() ([]Namespace, error) // InsertLayer stores a Layer in the database. // // A Layer is uniquely identified by its Name. // The Name and EngineVersion fields are mandatory. // If a Parent is specified, it is expected that it has been retrieved using // FindLayer. // If a Layer that already exists is inserted and the EngineVersion of the // given Layer is higher than the stored one, the stored Layer should be // updated. // The function has to be idempotent, inserting a layer that already exists // shouldn't return an error. InsertLayer(Layer) error // FindLayer retrieves a Layer from the database. // // When `withFeatures` is true, the Features field should be filled. // When `withVulnerabilities` is true, the Features field should be filled // and their AffectedBy fields should contain every vulnerabilities that // affect them. FindLayer(name string, withFeatures, withVulnerabilities bool) (Layer, error) // DeleteLayer deletes a Layer from the database and every layers that are // based on it, recursively. DeleteLayer(name string) error // ListVulnerabilities returns the list of vulnerabilities of a particular // Namespace. // // The Limit and page parameters are used to paginate the return list. // The first given page should be 0. // The function should return the next available page. If there are no more // pages, -1 has to be returned. ListVulnerabilities(namespaceName string, 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. // The FixedIn field may only contain a partial list of Features that are // affected by the Vulnerability, along with the version in which the // vulnerability is fixed. It is the responsibility of the implementation to // update the list properly. // A version equals to versionfmt.MinVersion means that the given Feature is // not being affected by the Vulnerability at all and thus, should be removed // from the list. // It is important that Features should be unique in the FixedIn list. For // example, it doesn't make sense to have two `openssl` Feature listed as a // Vulnerability can only be fixed in one Version. This is true because // Vulnerabilities and Features are namespaced (i.e. specific to one // operating system). // Each vulnerability insertion or update has to create a Notification that // will contain the old and the updated Vulnerability, unless // createNotification equals to true. InsertVulnerabilities(vulnerabilities []Vulnerability, createNotification bool) error // FindVulnerability retrieves a Vulnerability from the database, including // the FixedIn list. FindVulnerability(namespaceName, 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 // 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 // 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 // GetAvailableNotification returns the Name, Created, Notified and Deleted // fields of a Notification that should be handled. // // The renotify interval defines how much time after being marked as Notified // by SetNotificationNotified, a Notification that hasn't been deleted should // be returned again by this function. // A Notification for which there is a valid Lock with the same Name should // not be returned. GetAvailableNotification(renotifyInterval time.Duration) (VulnerabilityNotification, error) // GetNotification returns a Notification, including its OldVulnerability and // NewVulnerability fields. // // On these Vulnerabilities, LayersIntroducingVulnerability should be filled // with every Layer that introduces the Vulnerability (i.e. adds at least one // affected FeatureVersion). // The Limit and page parameters are used to paginate // LayersIntroducingVulnerability. The first given page should be // VulnerabilityNotificationFirstPage. The function will then return the next // available page. If there is no more page, NoVulnerabilityNotificationPage // has to be returned. GetNotification(name string, limit int, page VulnerabilityNotificationPageNumber) (VulnerabilityNotification, VulnerabilityNotificationPageNumber, error) // SetNotificationNotified marks a Notification as notified and thus, makes // it unavailable for GetAvailableNotification, until the renotify duration // is elapsed. SetNotificationNotified(name string) error // DeleteNotification marks a Notification as deleted, and thus, makes it // unavailable for GetAvailableNotification. DeleteNotification(name string) error // InsertKeyValue stores or updates a simple key/value pair in the database. InsertKeyValue(key, value string) error // GetKeyValue retrieves a value from the database from the given key. // // It returns an empty string if there is no such key. GetKeyValue(key string) (string, error) // Lock creates or renew a Lock in the database with the given name, owner // and duration. // // After the specified duration, the Lock expires by itself if it hasn't been // unlocked, and thus, let other users create a Lock with the same name. // However, the owner can renew its Lock by setting renew to true. // Lock should not block, it should instead returns whether the Lock has been // successfully acquired/renewed. If it's the case, the expiration time of // that Lock is returned as well. Lock(name string, owner string, duration time.Duration, renew bool) (bool, time.Time) // Unlock releases an existing Lock. Unlock(name, owner string) // FindLock returns the owner of a Lock specified by the name, and its // expiration time if it exists. FindLock(name string) (string, time.Time, error) // Ping returns the health status of the database. Ping() bool // Close closes the database and frees any allocated resource. Close() }