134 lines
3.7 KiB
Go
134 lines
3.7 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
var ErrInstanceLimit = errors.New("instance limit reached")
|
|
var ErrIntentState = errors.New("intent is no longer waiting")
|
|
|
|
type Intent struct {
|
|
ID string `json:"id"`
|
|
UID string `json:"uid"`
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
BuildID string `json:"build_id"`
|
|
InstanceID string `json:"instance_id"`
|
|
Status string `json:"status"`
|
|
Error string `json:"error"`
|
|
Host string `json:"-"`
|
|
Created int64 `json:"created_at"`
|
|
}
|
|
|
|
func (s *Store) EnsureIntents() error {
|
|
_, err := s.DB.Exec(`CREATE TABLE IF NOT EXISTS creation_intents(id TEXT PRIMARY KEY,uid TEXT NOT NULL,name TEXT NOT NULL,version TEXT NOT NULL,build_id TEXT NOT NULL DEFAULT '',instance_id TEXT NOT NULL DEFAULT '',status TEXT NOT NULL,error TEXT NOT NULL DEFAULT '',host TEXT NOT NULL,created INTEGER NOT NULL); CREATE INDEX IF NOT EXISTS intents_owner ON creation_intents(uid,status);`)
|
|
return err
|
|
}
|
|
|
|
func reserveSlot(tx *sql.Tx, uid string) error {
|
|
var count int
|
|
if err := tx.QueryRow(`SELECT (SELECT count(*) FROM instances WHERE uid=?) + (SELECT count(*) FROM creation_intents WHERE uid=? AND status='waiting')`, uid, uid).Scan(&count); err != nil {
|
|
return err
|
|
}
|
|
if count >= 2 {
|
|
return ErrInstanceLimit
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Store) ReserveIntent(i Intent) error {
|
|
tx, err := s.DB.Begin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback()
|
|
if err = reserveSlot(tx, i.UID); err != nil {
|
|
return err
|
|
}
|
|
_, err = tx.Exec(`INSERT INTO creation_intents(id,uid,name,version,status,host,created) VALUES(?,?,?,?,'waiting',?,?)`, i.ID, i.UID, i.Name, i.Version, i.Host, time.Now().Unix())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
|
|
func (s *Store) ReserveInstance(i Instance) error {
|
|
tx, err := s.DB.Begin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback()
|
|
if err = reserveSlot(tx, i.UID); err != nil {
|
|
return err
|
|
}
|
|
if err = insertInstance(tx, i); err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
func insertInstance(tx *sql.Tx, i Instance) error {
|
|
_, err := tx.Exec(`INSERT INTO instances VALUES(?,?,?,?,?,?,?,?)`, i.ID, i.UID, i.Name, i.Version, i.ImageID, i.Status, i.ContainerID, time.Now().Unix())
|
|
return err
|
|
}
|
|
func (s *Store) ClaimIntent(id string, i Instance) error {
|
|
tx, err := s.DB.Begin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback()
|
|
result, err := tx.Exec(`UPDATE creation_intents SET status='creating',instance_id=? WHERE id=? AND uid=? AND version=? AND status='waiting'`, i.ID, id, i.UID, i.Version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n, err := result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n != 1 {
|
|
return ErrIntentState
|
|
}
|
|
if err = insertInstance(tx, i); err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
func (s *Store) CancelIntent(id, uid string) error {
|
|
r, err := s.DB.Exec(`UPDATE creation_intents SET status='cancelled' WHERE id=? AND uid=? AND status='waiting'`, id, uid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n, err := r.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n != 1 {
|
|
return ErrIntentState
|
|
}
|
|
return nil
|
|
}
|
|
func (s *Store) Intents(uid string) ([]Intent, error) {
|
|
query := `SELECT id,uid,name,version,build_id,instance_id,status,error,host,created FROM creation_intents`
|
|
var args []any
|
|
if uid != "" {
|
|
query += ` WHERE uid=?`
|
|
args = append(args, uid)
|
|
}
|
|
query += ` ORDER BY created,id`
|
|
rows, err := s.DB.Query(query, args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
out := []Intent{}
|
|
for rows.Next() {
|
|
var i Intent
|
|
if err = rows.Scan(&i.ID, &i.UID, &i.Name, &i.Version, &i.BuildID, &i.InstanceID, &i.Status, &i.Error, &i.Host, &i.Created); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, i)
|
|
}
|
|
return out, rows.Err()
|
|
}
|