-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
orm.go
122 lines (105 loc) · 3.92 KB
/
orm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package versioning
import (
"context"
"database/sql"
"time"
"github.com/Masterminds/semver/v3"
"github.com/jackc/pgconn"
"github.com/pkg/errors"
"github.com/smartcontractkit/sqlx"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/pg"
)
// Version ORM manages the node_versions table
// NOTE: If you just need the current application version, consider using static.Version instead
// The database version is ONLY useful for managing versioning specific to the database e.g. for backups or migrations
type ORM interface {
FindLatestNodeVersion() (*NodeVersion, error)
UpsertNodeVersion(version NodeVersion) error
}
type orm struct {
db *sqlx.DB
lggr logger.Logger
timeout time.Duration
}
func NewORM(db *sqlx.DB, lggr logger.Logger, timeout time.Duration) *orm {
return &orm{
db: db,
lggr: lggr.Named("VersioningORM"),
timeout: timeout,
}
}
// UpsertNodeVersion inserts a new NodeVersion, returning error if the DB
// version is newer than the current one
// NOTE: If you just need the current application version, consider using static.Version instead
// The database version is ONLY useful for managing versioning specific to the database e.g. for backups or migrations
func (o *orm) UpsertNodeVersion(version NodeVersion) error {
now := time.Now()
if _, err := semver.NewVersion(version.Version); err != nil {
return errors.Wrapf(err, "%q is not valid semver", version.Version)
}
ctx, cancel := context.WithTimeout(context.Background(), o.timeout)
defer cancel()
return pg.SqlxTransaction(ctx, o.db, o.lggr, func(tx pg.Queryer) error {
if _, _, err := CheckVersion(tx, logger.NullLogger, version.Version); err != nil {
return err
}
stmt := `
INSERT INTO node_versions (version, created_at)
VALUES ($1, $2)
ON CONFLICT ((version IS NOT NULL)) DO UPDATE SET
version = EXCLUDED.version,
created_at = EXCLUDED.created_at
`
_, err := tx.Exec(stmt, version.Version, now)
return err
})
}
// CheckVersion returns an error if there is a valid semver version in the
// node_versions table that is lower than the current app version
func CheckVersion(q pg.Queryer, lggr logger.Logger, appVersion string) (appv, dbv *semver.Version, err error) {
lggr = lggr.Named("Version")
var dbVersion string
err = q.Get(&dbVersion, `SELECT version FROM node_versions ORDER BY created_at DESC LIMIT 1 FOR UPDATE`)
if errors.Is(err, sql.ErrNoRows) {
lggr.Debugw("No previous version set", "appVersion", appVersion)
return nil, nil, nil
} else if err != nil {
var pqErr *pgconn.PgError
ok := errors.As(err, &pqErr)
if ok && pqErr.Code == "42P01" && pqErr.Message == `relation "node_versions" does not exist` {
lggr.Debugw("Previous version not set; node_versions table does not exist", "appVersion", appVersion)
return nil, nil, nil
}
return nil, nil, err
}
dbv, dberr := semver.NewVersion(dbVersion)
appv, apperr := semver.NewVersion(appVersion)
if dberr != nil {
lggr.Warnf("Database version %q is not valid semver; skipping version check", dbVersion)
return nil, nil, nil
}
if apperr != nil {
return nil, nil, errors.Errorf("Application version %q is not valid semver", appVersion)
}
if dbv.GreaterThan(appv) {
return nil, nil, errors.Errorf("Application version (%s) is older than database version (%s). Only Chainlink %s or later can be run on this database", appv, dbv, dbv)
}
return appv, dbv, nil
}
// FindLatestNodeVersion looks up the latest node version
// NOTE: If you just need the current application version, consider using static.Version instead
// The database version is ONLY useful for managing versioning specific to the database e.g. for backups or migrations
func (o *orm) FindLatestNodeVersion() (*NodeVersion, error) {
stmt := `
SELECT version, created_at
FROM node_versions
ORDER BY created_at DESC
`
var nodeVersion NodeVersion
err := o.db.Get(&nodeVersion, stmt)
if err != nil {
return nil, err
}
return &nodeVersion, err
}