263 lines
6.6 KiB
Go
263 lines
6.6 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
sq "github.com/Masterminds/squirrel"
|
|
"github.com/concourse/concourse/atc"
|
|
"github.com/concourse/concourse/atc/db/lock"
|
|
)
|
|
|
|
type ResourceTypeNotFoundError struct {
|
|
ID int
|
|
}
|
|
|
|
func (e ResourceTypeNotFoundError) Error() string {
|
|
return fmt.Sprintf("resource type not found: %d", e.ID)
|
|
}
|
|
|
|
//go:generate counterfeiter . ResourceType
|
|
|
|
type ResourceType interface {
|
|
ID() int
|
|
Name() string
|
|
Type() string
|
|
Privileged() bool
|
|
Source() atc.Source
|
|
Params() atc.Params
|
|
Tags() atc.Tags
|
|
CheckEvery() string
|
|
CheckSetupError() error
|
|
CheckError() error
|
|
UniqueVersionHistory() bool
|
|
|
|
SetResourceConfig(atc.Source, atc.VersionedResourceTypes) (ResourceConfigScope, error)
|
|
SetCheckSetupError(error) error
|
|
|
|
Version() atc.Version
|
|
|
|
Reload() (bool, error)
|
|
}
|
|
|
|
type ResourceTypes []ResourceType
|
|
|
|
func (resourceTypes ResourceTypes) Deserialize() atc.VersionedResourceTypes {
|
|
var versionedResourceTypes atc.VersionedResourceTypes
|
|
|
|
for _, t := range resourceTypes {
|
|
versionedResourceTypes = append(versionedResourceTypes, atc.VersionedResourceType{
|
|
ResourceType: atc.ResourceType{
|
|
Name: t.Name(),
|
|
Type: t.Type(),
|
|
Source: t.Source(),
|
|
Privileged: t.Privileged(),
|
|
CheckEvery: t.CheckEvery(),
|
|
Tags: t.Tags(),
|
|
Params: t.Params(),
|
|
UniqueVersionHistory: t.UniqueVersionHistory(),
|
|
},
|
|
Version: t.Version(),
|
|
})
|
|
}
|
|
|
|
return versionedResourceTypes
|
|
}
|
|
|
|
func (resourceTypes ResourceTypes) Configs() atc.ResourceTypes {
|
|
var configs atc.ResourceTypes
|
|
|
|
for _, r := range resourceTypes {
|
|
configs = append(configs, atc.ResourceType{
|
|
Name: r.Name(),
|
|
Type: r.Type(),
|
|
Source: r.Source(),
|
|
Privileged: r.Privileged(),
|
|
CheckEvery: r.CheckEvery(),
|
|
Tags: r.Tags(),
|
|
Params: r.Params(),
|
|
UniqueVersionHistory: r.UniqueVersionHistory(),
|
|
})
|
|
}
|
|
|
|
return configs
|
|
}
|
|
|
|
var resourceTypesQuery = psql.Select("r.id, r.name, r.type, r.config, rcv.version, r.nonce, r.check_error, ro.check_error").
|
|
From("resource_types r").
|
|
LeftJoin("resource_configs c ON c.id = r.resource_config_id").
|
|
LeftJoin("resource_config_scopes ro ON ro.resource_config_id = c.id").
|
|
LeftJoin(`LATERAL (
|
|
SELECT rcv.*
|
|
FROM resource_config_versions rcv
|
|
WHERE rcv.resource_config_scope_id = ro.id AND rcv.check_order != 0
|
|
ORDER BY rcv.check_order DESC
|
|
LIMIT 1
|
|
) AS rcv ON true`).
|
|
Where(sq.Eq{"r.active": true})
|
|
|
|
type resourceType struct {
|
|
id int
|
|
name string
|
|
type_ string
|
|
privileged bool
|
|
source atc.Source
|
|
params atc.Params
|
|
tags atc.Tags
|
|
version atc.Version
|
|
checkEvery string
|
|
checkSetupError error
|
|
checkError error
|
|
uniqueVersionHistory bool
|
|
|
|
conn Conn
|
|
lockFactory lock.LockFactory
|
|
}
|
|
|
|
func (t *resourceType) ID() int { return t.id }
|
|
func (t *resourceType) Name() string { return t.name }
|
|
func (t *resourceType) Type() string { return t.type_ }
|
|
func (t *resourceType) Privileged() bool { return t.privileged }
|
|
func (t *resourceType) CheckEvery() string { return t.checkEvery }
|
|
func (t *resourceType) Source() atc.Source { return t.source }
|
|
func (t *resourceType) Params() atc.Params { return t.params }
|
|
func (t *resourceType) Tags() atc.Tags { return t.tags }
|
|
func (t *resourceType) CheckSetupError() error { return t.checkSetupError }
|
|
func (t *resourceType) CheckError() error { return t.checkError }
|
|
func (t *resourceType) UniqueVersionHistory() bool { return t.uniqueVersionHistory }
|
|
|
|
func (t *resourceType) Version() atc.Version { return t.version }
|
|
|
|
func (t *resourceType) Reload() (bool, error) {
|
|
row := resourceTypesQuery.Where(sq.Eq{"r.id": t.id}).RunWith(t.conn).QueryRow()
|
|
|
|
err := scanResourceType(t, row)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (t *resourceType) SetResourceConfig(source atc.Source, resourceTypes atc.VersionedResourceTypes) (ResourceConfigScope, error) {
|
|
resourceConfigDescriptor, err := constructResourceConfigDescriptor(t.type_, source, resourceTypes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tx, err := t.conn.Begin()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer Rollback(tx)
|
|
|
|
resourceConfig, err := resourceConfigDescriptor.findOrCreate(tx, t.lockFactory, t.conn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, err = psql.Update("resource_types").
|
|
Set("resource_config_id", resourceConfig.ID()).
|
|
Where(sq.Eq{
|
|
"id": t.id,
|
|
}).
|
|
RunWith(tx).
|
|
Exec()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// A nil value is passed into the Resource object parameter because we always want resource type versions to be shared
|
|
resourceConfigScope, err := findOrCreateResourceConfigScope(tx, t.conn, t.lockFactory, resourceConfig, nil, resourceTypes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = tx.Commit()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return resourceConfigScope, nil
|
|
}
|
|
|
|
func (t *resourceType) SetCheckSetupError(cause error) error {
|
|
var err error
|
|
|
|
if cause == nil {
|
|
_, err = psql.Update("resource_types").
|
|
Set("check_error", nil).
|
|
Where(sq.Eq{"id": t.id}).
|
|
RunWith(t.conn).
|
|
Exec()
|
|
} else {
|
|
_, err = psql.Update("resource_types").
|
|
Set("check_error", cause.Error()).
|
|
Where(sq.Eq{"id": t.id}).
|
|
RunWith(t.conn).
|
|
Exec()
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func scanResourceType(t *resourceType, row scannable) error {
|
|
var (
|
|
configJSON []byte
|
|
checkErr, rcsCheckErr, version, nonce sql.NullString
|
|
)
|
|
|
|
err := row.Scan(&t.id, &t.name, &t.type_, &configJSON, &version, &nonce, &checkErr, &rcsCheckErr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if version.Valid {
|
|
err = json.Unmarshal([]byte(version.String), &t.version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
es := t.conn.EncryptionStrategy()
|
|
|
|
var noncense *string
|
|
if nonce.Valid {
|
|
noncense = &nonce.String
|
|
}
|
|
|
|
decryptedConfig, err := es.Decrypt(string(configJSON), noncense)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var config atc.ResourceType
|
|
err = json.Unmarshal(decryptedConfig, &config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
t.source = config.Source
|
|
t.params = config.Params
|
|
t.privileged = config.Privileged
|
|
t.tags = config.Tags
|
|
t.checkEvery = config.CheckEvery
|
|
t.uniqueVersionHistory = config.UniqueVersionHistory
|
|
|
|
if checkErr.Valid {
|
|
t.checkSetupError = errors.New(checkErr.String)
|
|
}
|
|
|
|
if rcsCheckErr.Valid {
|
|
t.checkError = errors.New(rcsCheckErr.String)
|
|
}
|
|
|
|
return nil
|
|
}
|