| 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) |
| } |