Skip to content

Commit

Permalink
admin: add a check for compatible migration version
Browse files Browse the repository at this point in the history
Add the ability to check migration version of the database before
running an admin command so you know you are operating on a compatible
database schema.

Backports: #1915
Signed-off-by: crozzy <joseph.crosland@gmail.com>
(cherry picked from commit 1f2b34f)
  • Loading branch information
crozzy authored and hdonnay committed Feb 26, 2024
1 parent 5d689ef commit 9517c7b
Showing 1 changed file with 40 additions and 3 deletions.
43 changes: 40 additions & 3 deletions cmd/clairctl/admin.go
Expand Up @@ -297,9 +297,12 @@ func adminPre473(c *cli.Context) error {

func updateGoPackages(c *cli.Context) error {
const (
getPackageNames = "SELECT DISTINCT package_name FROM vuln WHERE updater = 'osv/go'"
getPackages = "SELECT id, version FROM package WHERE name = $1 and norm_version IS NULL"
updatePackages = "UPDATE package SET norm_version=$1::int[], norm_kind=$2 WHERE id = $3 and norm_version IS NULL"
// TODO (crozzy): This could describe something more interesting like >6 or 6-10 but
// at the moment that seems like overkill.
compatibleMigrationVersion = 7
getPackageNames = "SELECT DISTINCT package_name FROM vuln WHERE updater = 'osv/go'"
getPackages = "SELECT id, version FROM package WHERE name = $1 and norm_version IS NULL"
updatePackages = "UPDATE package SET norm_version=$1::int[], norm_kind=$2 WHERE id = $3 and norm_version IS NULL"
)

ctx := c.Context
Expand Down Expand Up @@ -344,6 +347,10 @@ func updateGoPackages(c *cli.Context) error {
return fmt.Errorf("error creating indexer pool: %w", err)
}
defer indexerPool.Close()
err = checkMigrationVersion(ctx, indexerPool, "libindex_migrations", []int{compatibleMigrationVersion})
if err != nil {
return fmt.Errorf("error checking migration version: %w", err)
}

for _, p := range packageNames {
err := indexerPool.AcquireFunc(ctx, func(conn *pgxpool.Conn) error {
Expand Down Expand Up @@ -403,6 +410,36 @@ func updateGoPackages(c *cli.Context) error {
return nil
}

type ErrNonCompatibleMigrationVersion struct {
version int
acceptableVersions []int
}

func NewErrNonCompatibleMigrationVersion(version int, acceptableVersions []int) ErrNonCompatibleMigrationVersion {
return ErrNonCompatibleMigrationVersion{version: version, acceptableVersions: acceptableVersions}
}

func (e ErrNonCompatibleMigrationVersion) Error() string {
return fmt.Sprintf("non-compatible migration version %d (acceptable versions: %v)", e.version, e.acceptableVersions)
}

func checkMigrationVersion(ctx context.Context, pool *pgxpool.Pool, migrationTable string, acceptableVersions []int) error {
checkMigrationVersionQuery := fmt.Sprintf("SELECT MAX(version) FROM %s", migrationTable)
return pool.AcquireFunc(ctx, func(conn *pgxpool.Conn) error {
var version int
err := conn.QueryRow(ctx, checkMigrationVersionQuery).Scan(&version)
if err != nil {
return err
}
for _, v := range acceptableVersions {
if v == version {
return nil
}
}
return NewErrNonCompatibleMigrationVersion(version, acceptableVersions)
})
}

func createConnPool(ctx context.Context, dsn string, maxConns int32) (*pgxpool.Pool, error) {
pgcfg, err := pgxpool.ParseConfig(dsn)
if err != nil {
Expand Down

0 comments on commit 9517c7b

Please sign in to comment.