Monorepo for Tangled tangled.org

spindle: db: setup pipelines table & helpers

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

anirudh.fi baeaadbf 55ef369a

verified
Changed files
+141 -38
spindle
+17 -4
spindle/db/db.go
··· 1 package db 2 3 - import "database/sql" 4 5 type DB struct { 6 *sql.DB ··· 27 ); 28 29 create table if not exists pipelines ( 30 - rkey text not null, 31 - pipeline text not null, -- json 32 - primary key rkey 33 ); 34 `) 35 if err != nil {
··· 1 package db 2 3 + import ( 4 + "database/sql" 5 + 6 + _ "github.com/mattn/go-sqlite3" 7 + ) 8 9 type DB struct { 10 *sql.DB ··· 31 ); 32 33 create table if not exists pipelines ( 34 + at_uri text not null, 35 + status text not null, 36 + 37 + -- only set if status is 'failed' 38 + error text, 39 + exit_code integer, 40 + 41 + started_at timestamp not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 42 + updated_at timestamp not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 43 + finished_at timestamp, 44 + 45 + primary key (at_uri) 46 ); 47 `) 48 if err != nil {
+124 -34
spindle/db/pipelines.go
··· 2 3 import ( 4 "fmt" 5 ) 6 7 type Pipeline struct { 8 - Rkey string 9 - PipelineJson string 10 } 11 12 - func (d *DB) InsertPipeline(pipeline Pipeline) error { 13 - _, err := d.Exec( 14 - `insert into pipelines (rkey, nsid, event) values (?, ?, ?)`, 15 - pipeline.Rkey, 16 - pipeline.PipelineJson, 17 - ) 18 19 - return err 20 } 21 22 - func (d *DB) GetPipeline(rkey, cursor string) (Pipeline, error) { 23 - whereClause := "where rkey = ?" 24 - args := []any{rkey} 25 26 - if cursor != "" { 27 - whereClause += " and rkey > ?" 28 - args = append(args, cursor) 29 } 30 31 - query := fmt.Sprintf(` 32 - select rkey, pipeline 33 - from pipelines 34 - %s 35 - limit 1 36 - `, whereClause) 37 38 - row := d.QueryRow(query, args...) 39 40 - var p Pipeline 41 - err := row.Scan(&p.Rkey, &p.PipelineJson) 42 if err != nil { 43 - return Pipeline{}, err 44 } 45 46 - return p, nil 47 } 48 49 - func (d *DB) GetPipelines(cursor string) ([]Pipeline, error) { 50 whereClause := "" 51 args := []any{} 52 if cursor != "" { ··· 55 } 56 57 query := fmt.Sprintf(` 58 - select rkey, nsid, pipeline 59 from pipelines 60 %s 61 order by rkey asc 62 limit 100 63 `, whereClause) 64 65 - rows, err := d.Query(query, args...) 66 if err != nil { 67 return nil, err 68 } 69 defer rows.Close() 70 71 - var evts []Pipeline 72 for rows.Next() { 73 - var ev Pipeline 74 - rows.Scan(&ev.Rkey, &ev.PipelineJson) 75 - evts = append(evts, ev) 76 } 77 78 if err := rows.Err(); err != nil { 79 return nil, err 80 } 81 82 - return evts, nil 83 }
··· 2 3 import ( 4 "fmt" 5 + "time" 6 + 7 + "tangled.sh/tangled.sh/core/api/tangled" 8 + "tangled.sh/tangled.sh/core/knotserver/notifier" 9 + ) 10 + 11 + type PipelineStatus string 12 + 13 + var ( 14 + PipelinePending PipelineStatus = "pending" 15 + PipelineRunning PipelineStatus = "running" 16 + PipelineFailed PipelineStatus = "failed" 17 + PipelineTimeout PipelineStatus = "timeout" 18 + PipelineCancelled PipelineStatus = "cancelled" 19 + PipelineSuccess PipelineStatus = "success" 20 ) 21 22 type Pipeline struct { 23 + Rkey string `json:"rkey"` 24 + Knot string `json:"knot"` 25 + Status PipelineStatus `json:"status"` 26 + 27 + // only if Failed 28 + Error string `json:"error"` 29 + ExitCode int `json:"exit_code"` 30 + 31 + StartedAt time.Time `json:"started_at"` 32 + UpdatedAt time.Time `json:"updated_at"` 33 + FinishedAt time.Time `json:"finished_at"` 34 } 35 36 + func (p Pipeline) AsRecord() *tangled.PipelineStatus { 37 + exitCode64 := int64(p.ExitCode) 38 + finishedAt := p.FinishedAt.String() 39 + 40 + return &tangled.PipelineStatus{ 41 + Pipeline: fmt.Sprintf("at://%s/%s", p.Knot, p.Rkey), 42 + Status: string(p.Status), 43 + 44 + ExitCode: &exitCode64, 45 + Error: &p.Error, 46 + 47 + StartedAt: p.StartedAt.String(), 48 + UpdatedAt: p.UpdatedAt.String(), 49 + FinishedAt: &finishedAt, 50 + } 51 + } 52 53 + func pipelineAtUri(rkey, knot string) string { 54 + return fmt.Sprintf("at://%s/did:web:%s/%s", tangled.PipelineStatusNSID, knot, rkey) 55 } 56 57 + func (db *DB) CreatePipeline(rkey, knot string, n *notifier.Notifier) error { 58 + _, err := db.Exec(` 59 + insert into pipelines (at_uri, status) 60 + values (?, ?) 61 + `, pipelineAtUri(rkey, knot), PipelinePending) 62 63 + if err != nil { 64 + return err 65 } 66 + n.NotifyAll() 67 + return nil 68 + } 69 70 + func (db *DB) MarkPipelineRunning(rkey, knot string, n *notifier.Notifier) error { 71 + _, err := db.Exec(` 72 + update pipelines 73 + set status = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') 74 + where at_uri = ? 75 + `, PipelineRunning, pipelineAtUri(rkey, knot)) 76 77 + if err != nil { 78 + return err 79 + } 80 + n.NotifyAll() 81 + return nil 82 + } 83 + 84 + func (db *DB) MarkPipelineFailed(rkey, knot string, exitCode int, errorMsg string, n *notifier.Notifier) error { 85 + _, err := db.Exec(` 86 + update pipelines 87 + set status = ?, 88 + exit_code = ?, 89 + error = ?, 90 + updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), 91 + finished_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') 92 + where at_uri = ? 93 + `, PipelineFailed, exitCode, errorMsg, pipelineAtUri(rkey, knot)) 94 + if err != nil { 95 + return err 96 + } 97 + n.NotifyAll() 98 + return nil 99 + } 100 + 101 + func (db *DB) MarkPipelineTimeout(rkey, knot string, n *notifier.Notifier) error { 102 + _, err := db.Exec(` 103 + update pipelines 104 + set status = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') 105 + where at_uri = ? 106 + `, PipelineTimeout, pipelineAtUri(rkey, knot)) 107 + if err != nil { 108 + return err 109 + } 110 + n.NotifyAll() 111 + return nil 112 + } 113 + 114 + func (db *DB) MarkPipelineSuccess(rkey, knot string, n *notifier.Notifier) error { 115 + _, err := db.Exec(` 116 + update pipelines 117 + set status = ?, updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now'), 118 + finished_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') 119 + where at_uri = ? 120 + `, PipelineSuccess, pipelineAtUri(rkey, knot)) 121 122 if err != nil { 123 + return err 124 } 125 + n.NotifyAll() 126 + return nil 127 + } 128 129 + func (db *DB) GetPipeline(rkey, knot string) (Pipeline, error) { 130 + var p Pipeline 131 + err := db.QueryRow(` 132 + select rkey, status, error, exit_code, started_at, updated_at, finished_at 133 + from pipelines 134 + where at_uri = ? 135 + `, pipelineAtUri(rkey, knot)).Scan(&p.Rkey, &p.Status, &p.Error, &p.ExitCode, &p.StartedAt, &p.UpdatedAt, &p.FinishedAt) 136 + return p, err 137 } 138 139 + func (db *DB) GetPipelines(cursor string) ([]Pipeline, error) { 140 whereClause := "" 141 args := []any{} 142 if cursor != "" { ··· 145 } 146 147 query := fmt.Sprintf(` 148 + select rkey, status, error, exit_code, started_at, updated_at, finished_at 149 from pipelines 150 %s 151 order by rkey asc 152 limit 100 153 `, whereClause) 154 155 + rows, err := db.Query(query, args...) 156 if err != nil { 157 return nil, err 158 } 159 defer rows.Close() 160 161 + var pipelines []Pipeline 162 for rows.Next() { 163 + var p Pipeline 164 + rows.Scan(&p.Rkey, &p.Status, &p.Error, &p.ExitCode, &p.StartedAt, &p.UpdatedAt, &p.FinishedAt) 165 + pipelines = append(pipelines, p) 166 } 167 168 if err := rows.Err(); err != nil { 169 return nil, err 170 } 171 172 + return pipelines, nil 173 }