blob: 17d7598af2841e8806dd9bac1c78962eb551ca2e [file] [log] [blame]
package bmdb
import (
"context"
"database/sql"
"fmt"
"github.com/google/uuid"
"k8s.io/klog/v2"
"source.monogon.dev/cloud/bmaas/bmdb/model"
"source.monogon.dev/cloud/bmaas/bmdb/reflection"
)
// Open creates a new Connection to the BMDB for the calling component. Multiple
// connections can be opened (although there is no advantage to doing so, as
// Connections manage an underlying CockroachDB connection pool, which performs
// required reconnects and connection pooling automatically).
func (b *BMDB) Open(migrate bool) (*Connection, error) {
if b.Config.Database.Migrations == nil {
klog.Infof("Using default migrations source.")
m, err := model.MigrationsSource()
if err != nil {
klog.Exitf("failed to prepare migrations source: %w", err)
}
b.Config.Database.Migrations = m
}
if migrate {
if err := b.Database.MigrateUp(); err != nil {
return nil, fmt.Errorf("migration failed: %w", err)
}
}
db, err := b.Database.Connect()
if err != nil {
return nil, err
}
return &Connection{
bmdb: b,
db: db,
DatabaseName: b.Config.Database.DatabaseName,
Address: b.Config.Database.EndpointHost,
InMemory: b.Config.Database.InMemory,
}, nil
}
// Connection to the BMDB. Internally, this contains a sql.DB connection pool,
// so components can (and likely should) reuse Connections as much as possible
// internally.
type Connection struct {
bmdb *BMDB
db *sql.DB
// The database name that we're connected to.
DatabaseName string
// The address of the CockroachDB endpoint we've connected to.
Address string
// Whether this connection is to an in-memory database. Note: this only works if
// this Connection came directly from calling Open on a BMDB that was defined to
// be in-memory. If you just connect to an in-memory CRDB manually, this will
// still be false.
InMemory bool
}
// Reflect returns a reflection.Schema as detected by inspecting the table
// information of this connection to the BMDB. The Schema can then be used to
// retrieve arbitrary tag/machine information without going through the
// concurrency/ordering mechanism of the BMDB.
//
// This should only be used to implement debugging tooling and should absolutely
// not be in the path of any user requests.
//
// This Connection will be used not only to query the Schema information, but
// also for all subsequent data retrieval operations on it. Please ensure that
// the Schema is rebuilt in the event of a database connection failure. Ideally,
// you should be rebuilding the schema often, to follow what is currently
// available on the production database - but not for every request. Use a cache
// or something.
func (c *Connection) Reflect(ctx context.Context) (*reflection.Schema, error) {
return reflection.Reflect(ctx, c.db)
}
// ListHistoryOf retrieves a full audit history of a machine, sorted
// chronologically. It can be read without a session / transaction for debugging
// purposes.
func (c *Connection) ListHistoryOf(ctx context.Context, machine uuid.UUID) ([]model.WorkHistory, error) {
return model.New(c.db).ListHistoryOf(ctx, machine)
}
// GetSession retrieves all information about a session. It can be read without a
// session/transaction for debugging purposes.
func (c *Connection) GetSession(ctx context.Context, session uuid.UUID) ([]model.Session, error) {
return model.New(c.db).GetSession(ctx, session)
}