Skip to content

Commit

Permalink
database: Allow specifying datastore driver by config
Browse files Browse the repository at this point in the history
Fixes #145
  • Loading branch information
Quentin-M committed May 11, 2016
1 parent 53e6257 commit e7b960c
Show file tree
Hide file tree
Showing 17 changed files with 264 additions and 229 deletions.
4 changes: 2 additions & 2 deletions clair.go
Expand Up @@ -26,7 +26,7 @@ import (
"github.com/coreos/clair/api"
"github.com/coreos/clair/api/context"
"github.com/coreos/clair/config"
"github.com/coreos/clair/database/pgsql"
"github.com/coreos/clair/database"
"github.com/coreos/clair/notifier"
"github.com/coreos/clair/updater"
"github.com/coreos/clair/utils"
Expand All @@ -42,7 +42,7 @@ func Boot(config *config.Config) {
st := utils.NewStopper()

// Open database
db, err := pgsql.Open(config.Database)
db, err := database.Open(config.Database)
if err != nil {
log.Fatal(err)
}
Expand Down
6 changes: 4 additions & 2 deletions cmd/clair/main.go
Expand Up @@ -20,11 +20,11 @@ import (
"runtime/pprof"
"strings"

"github.com/coreos/pkg/capnslog"

"github.com/coreos/clair"
"github.com/coreos/clair/config"

"github.com/coreos/pkg/capnslog"

// Register components
_ "github.com/coreos/clair/notifier/notifiers"

Expand All @@ -43,6 +43,8 @@ import (
_ "github.com/coreos/clair/worker/detectors/namespace/lsbrelease"
_ "github.com/coreos/clair/worker/detectors/namespace/osrelease"
_ "github.com/coreos/clair/worker/detectors/namespace/redhatrelease"

_ "github.com/coreos/clair/database/pgsql"
)

var log = capnslog.NewPackageLogger("github.com/coreos/clair/cmd/clair", "main")
Expand Down
15 changes: 9 additions & 6 deletions config.example.yaml
Expand Up @@ -15,13 +15,16 @@
# The values specified here are the default values that Clair uses if no configuration file is specified or if the keys are not defined.
clair:
database:
# PostgreSQL Connection string
# http://www.postgresql.org/docs/9.4/static/libpq-connect.html
source:
# Database driver
type: pgsql
options:
# PostgreSQL Connection string
# http://www.postgresql.org/docs/9.4/static/libpq-connect.html
source:

# Number of elements kept in the cache
# Values unlikely to change (e.g. namespaces) are cached in order to save prevent needless roundtrips to the database.
cacheSize: 16384
# Number of elements kept in the cache
# Values unlikely to change (e.g. namespaces) are cached in order to save prevent needless roundtrips to the database.
cachesize: 16384

api:
# API server port
Expand Down
27 changes: 12 additions & 15 deletions config/config.go
Expand Up @@ -27,6 +27,14 @@ import (
// ErrDatasourceNotLoaded is returned when the datasource variable in the configuration file is not loaded properly
var ErrDatasourceNotLoaded = errors.New("could not load configuration: no database source specified")

// RegistrableComponentConfig is a configuration block that can be used to
// determine which registrable component should be initialized and pass
// custom configuration to it.
type RegistrableComponentConfig struct {
Type string
Options map[string]interface{}
}

// File represents a YAML configuration file that namespaces all Clair
// configuration under the top-level "clair" key.
type File struct {
Expand All @@ -35,19 +43,12 @@ type File struct {

// Config is the global configuration for an instance of Clair.
type Config struct {
Database *DatabaseConfig
Database RegistrableComponentConfig
Updater *UpdaterConfig
Notifier *NotifierConfig
API *APIConfig
}

// DatabaseConfig is the configuration used to specify how Clair connects
// to a database.
type DatabaseConfig struct {
Source string
CacheSize int
}

// UpdaterConfig is the configuration for the Updater service.
type UpdaterConfig struct {
Interval time.Duration
Expand All @@ -72,8 +73,8 @@ type APIConfig struct {
// DefaultConfig is a configuration that can be used as a fallback value.
func DefaultConfig() Config {
return Config{
Database: &DatabaseConfig{
CacheSize: 16384,
Database: RegistrableComponentConfig{
Type: "pgsql",
},
Updater: &UpdaterConfig{
Interval: 1 * time.Hour,
Expand Down Expand Up @@ -116,12 +117,8 @@ func Load(path string) (config *Config, err error) {
}
config = &cfgFile.Clair

if config.Database.Source == "" {
err = ErrDatasourceNotLoaded
return
}

// Generate a pagination key if none is provided.
// TODO(Quentin-M): Move to the API code.
if config.API.PaginationKey == "" {
var key fernet.Key
if err = key.Generate(); err != nil {
Expand Down
81 changes: 0 additions & 81 deletions config/config_test.go

This file was deleted.

35 changes: 32 additions & 3 deletions database/database.go
Expand Up @@ -17,7 +17,10 @@ package database

import (
"errors"
"fmt"
"time"

"github.com/coreos/clair/config"
)

var (
Expand All @@ -28,11 +31,37 @@ var (
// ErrInconsistent is an error that occurs when a database consistency check
// fails (ie. when an entity which is supposed to be unique is detected twice)
ErrInconsistent = errors.New("database: inconsistent database")

// ErrCantOpen is an error that occurs when the database could not be opened
ErrCantOpen = errors.New("database: could not open 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 is the interface that describes a database backend implementation.
type Datastore interface {
// # Namespace
Expand Down
7 changes: 4 additions & 3 deletions database/pgsql/complex_test.go
Expand Up @@ -23,11 +23,12 @@ import (
"testing"
"time"

"github.com/pborman/uuid"
"github.com/stretchr/testify/assert"

"github.com/coreos/clair/database"
"github.com/coreos/clair/utils"
"github.com/coreos/clair/utils/types"
"github.com/pborman/uuid"
"github.com/stretchr/testify/assert"
)

const (
Expand All @@ -36,7 +37,7 @@ const (
)

func TestRaceAffects(t *testing.T) {
datastore, err := OpenForTest("RaceAffects", false)
datastore, err := openDatabaseForTest("RaceAffects", false)
if err != nil {
t.Error(err)
return
Expand Down
5 changes: 3 additions & 2 deletions database/pgsql/feature_test.go
Expand Up @@ -17,13 +17,14 @@ package pgsql
import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/coreos/clair/database"
"github.com/coreos/clair/utils/types"
"github.com/stretchr/testify/assert"
)

func TestInsertFeature(t *testing.T) {
datastore, err := OpenForTest("InsertFeature", false)
datastore, err := openDatabaseForTest("InsertFeature", false)
if err != nil {
t.Error(err)
return
Expand Down
2 changes: 1 addition & 1 deletion database/pgsql/keyvalue_test.go
Expand Up @@ -21,7 +21,7 @@ import (
)

func TestKeyValue(t *testing.T) {
datastore, err := OpenForTest("KeyValue", false)
datastore, err := openDatabaseForTest("KeyValue", false)
if err != nil {
t.Error(err)
return
Expand Down
4 changes: 1 addition & 3 deletions database/pgsql/layer.go
Expand Up @@ -41,9 +41,7 @@ func (pgSQL *pgSQL) FindLayer(name string, withFeatures, withVulnerabilities boo
var namespaceName sql.NullString

t := time.Now()
err := pgSQL.QueryRow(searchLayer, name).
Scan(&layer.ID, &layer.Name, &layer.EngineVersion, &parentID, &parentName, &namespaceID,
&namespaceName)
err := pgSQL.QueryRow(searchLayer, name).Scan(&layer.ID, &layer.Name, &layer.EngineVersion, &parentID, &parentName, &namespaceID, &namespaceName)
observeQueryTime("FindLayer", "searchLayer", t)

if err != nil {
Expand Down
7 changes: 4 additions & 3 deletions database/pgsql/layer_test.go
Expand Up @@ -18,14 +18,15 @@ import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"

"github.com/coreos/clair/database"
cerrors "github.com/coreos/clair/utils/errors"
"github.com/coreos/clair/utils/types"
"github.com/stretchr/testify/assert"
)

func TestFindLayer(t *testing.T) {
datastore, err := OpenForTest("FindLayer", true)
datastore, err := openDatabaseForTest("FindLayer", true)
if err != nil {
t.Error(err)
return
Expand Down Expand Up @@ -102,7 +103,7 @@ func TestFindLayer(t *testing.T) {
}

func TestInsertLayer(t *testing.T) {
datastore, err := OpenForTest("InsertLayer", false)
datastore, err := openDatabaseForTest("InsertLayer", false)
if err != nil {
t.Error(err)
return
Expand Down
2 changes: 1 addition & 1 deletion database/pgsql/lock_test.go
Expand Up @@ -22,7 +22,7 @@ import (
)

func TestLock(t *testing.T) {
datastore, err := OpenForTest("InsertNamespace", false)
datastore, err := openDatabaseForTest("InsertNamespace", false)
if err != nil {
t.Error(err)
return
Expand Down
7 changes: 4 additions & 3 deletions database/pgsql/namespace_test.go
Expand Up @@ -18,12 +18,13 @@ import (
"fmt"
"testing"

"github.com/coreos/clair/database"
"github.com/stretchr/testify/assert"

"github.com/coreos/clair/database"
)

func TestInsertNamespace(t *testing.T) {
datastore, err := OpenForTest("InsertNamespace", false)
datastore, err := openDatabaseForTest("InsertNamespace", false)
if err != nil {
t.Error(err)
return
Expand All @@ -44,7 +45,7 @@ func TestInsertNamespace(t *testing.T) {
}

func TestListNamespace(t *testing.T) {
datastore, err := OpenForTest("ListNamespaces", true)
datastore, err := openDatabaseForTest("ListNamespaces", true)
if err != nil {
t.Error(err)
return
Expand Down
5 changes: 3 additions & 2 deletions database/pgsql/notification_test.go
Expand Up @@ -4,14 +4,15 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"

"github.com/coreos/clair/database"
cerrors "github.com/coreos/clair/utils/errors"
"github.com/coreos/clair/utils/types"
"github.com/stretchr/testify/assert"
)

func TestNotification(t *testing.T) {
datastore, err := OpenForTest("Notification", false)
datastore, err := openDatabaseForTest("Notification", false)
if err != nil {
t.Error(err)
return
Expand Down

0 comments on commit e7b960c

Please sign in to comment.