From f0816d2c4dd92e10fe8e0713fb7890da1e306c0f Mon Sep 17 00:00:00 2001 From: Quentin Machu Date: Fri, 12 Feb 2016 16:24:37 -0500 Subject: [PATCH] database: add docs about the interface --- database/database.go | 96 ++++++++++++++++++++++++++++++++++++----- database/pgsql/layer.go | 13 ------ 2 files changed, 86 insertions(+), 23 deletions(-) diff --git a/database/database.go b/database/database.go index a571f9cad5..d192c376f5 100644 --- a/database/database.go +++ b/database/database.go @@ -21,9 +21,6 @@ import ( ) var ( - // ErrTransaction is an error that occurs when a database transaction fails. - // ErrTransaction = errors.New("database: transaction failed (concurrent modification?)") - // 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") @@ -36,38 +33,117 @@ var ( ErrCantOpen = errors.New("database: could not open database") ) +// Datastore is the interface that describes a database backend implementation. type Datastore interface { - // Namespace + // # Namespace + // ListNamespaces returns the entire list of known Namespaces. ListNamespaces() ([]Namespace, error) - // Layer + // # Layer + // 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 shouln'd return an + // error. InsertLayer(Layer) error + + // FindLayer retrieves a Layer from the database. + // withFeatures specifies whether 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 - // Vulnerability + // # Vulnerability + // 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 + // responsability of the implementation to update the list properly. A version equals to + // types.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 - // Notifications - GetAvailableNotification(renotifyInterval time.Duration) (VulnerabilityNotification, error) // Does not fill old/new Vulnerabilities. + // # Notification + // 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 + // availage 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 - // Key/Value + // # Key/Value + // 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 + // # Lock + // 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 experation time if it + // exists. FindLock(name string) (string, time.Time, error) + // # Miscellaneous + // Ping returns the health status of the database. Ping() bool + + // Close closes the database and free any allocated resource. Close() } diff --git a/database/pgsql/layer.go b/database/pgsql/layer.go index b5bd734201..4aa298df2e 100644 --- a/database/pgsql/layer.go +++ b/database/pgsql/layer.go @@ -181,18 +181,6 @@ func (pgSQL *pgSQL) loadAffectedBy(featureVersions []database.FeatureVersion) er return nil } -// InsertLayer insert a single layer in the database -// -// The Name and EngineVersion fields are required. -// The Parent, Namespace, Features are optional. -// However, please note that the Parent field, if provided, is expected to have been retrieved -// using FindLayer with its Features. -// -// The Name must be unique for two different layers. -// -// If the Layer already exists and the EngineVersion value of the inserted layer is higher than the -// stored value, the EngineVersion, the Namespace and the Feature list will be updated. -// // Internally, only Feature additions/removals are stored for each layer. If a layer has a parent, // the Feature list will be compared to the parent's Feature list and the difference will be stored. // Note that when the Namespace of a layer differs from its parent, it is expected that several @@ -200,7 +188,6 @@ func (pgSQL *pgSQL) loadAffectedBy(featureVersions []database.FeatureVersion) er // (happens when Feature detectors relies on the detected layer Namespace). However, if the listed // Feature has the same Name/Version as its parent, InsertLayer considers that the Feature hasn't // been modified. -// TODO(Quentin-M): This behavior should be implemented at the Feature detectors level. func (pgSQL *pgSQL) InsertLayer(layer database.Layer) error { tf := time.Now()