Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/04-Upgrading.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Some Icinga DB upgrades require manual intervention, others do not. If you need
point you to the specific upgrade section on this page.

Please note that version upgrades are incremental. If you are upgrading across multiple versions, make sure to follow
the steps for each of them.
the steps for each of them. For example, when upgrading from version 1.1.0 to 1.2.0, follow all instructions for
upgrading to 1.1.1, then all for 1.2.0, including schema upgrades.

## Database Schema Upgrades

Expand Down
29 changes: 23 additions & 6 deletions pkg/icingadb/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const (
expectedPostgresSchemaVersion = 5
)

// ErrSchemaNotExists implies that no Icinga DB schema has been imported.
// ErrSchemaNotExists implies that no Icinga DB schema has been imported. May result in a later [ImportSchema] call.
var ErrSchemaNotExists = stderrors.New("no database schema exists")

// ErrSchemaMismatch implies an unexpected schema version, most likely after Icinga DB was updated but the database
Expand Down Expand Up @@ -48,12 +48,13 @@ func CheckSchema(ctx context.Context, db *database.DB) error {
return ErrSchemaNotExists
}

var version uint16
var versions []uint16

err := retry.WithBackoff(
ctx,
func(ctx context.Context) error {
query := "SELECT version FROM icingadb_schema ORDER BY id DESC LIMIT 1"
if err := db.QueryRowxContext(ctx, query).Scan(&version); err != nil {
query := "SELECT version FROM icingadb_schema ORDER BY version ASC"
if err := db.SelectContext(ctx, &versions, query); err != nil {
return database.CantPerformQuery(err, query)
}
return nil
Expand All @@ -65,12 +66,28 @@ func CheckSchema(ctx context.Context, db *database.DB) error {
return errors.Wrap(err, "can't check database schema version")
}

if version != expectedDbSchemaVersion {
if len(versions) == 0 {
return fmt.Errorf("%w: no database schema version is stored in the database", ErrSchemaMismatch)
}

// Check if each schema update between the initial import and the latest version was applied or, in other words,
// that no schema update was left out. The loop goes over the ascending sorted array of schema versions, verifying
// that each element's successor is the increment of this version, ensuring no gaps in between.
for i := 0; i < len(versions)-1; i++ {
if versions[i] != versions[i+1]-1 {
return fmt.Errorf(
"%w: incomplete database schema upgrade: intermediate version v%d is missing,"+
" please make sure you have applied all database migrations after upgrading Icinga DB",
ErrSchemaMismatch, versions[i]+1)
}
}

if latestVersion := versions[len(versions)-1]; latestVersion != expectedDbSchemaVersion {
// Since these error messages are trivial and mostly caused by users, we don't need
// to print a stack trace here. However, since errors.Errorf() does this automatically,
// we need to use fmt instead.
return fmt.Errorf("%w: v%d (expected v%d), please make sure you have applied all database"+
" migrations after upgrading Icinga DB", ErrSchemaMismatch, version, expectedDbSchemaVersion,
" migrations after upgrading Icinga DB", ErrSchemaMismatch, latestVersion, expectedDbSchemaVersion,
)
}

Expand Down
Loading