mirror of
https://github.com/lxc/incus.git
synced 2026-02-05 09:46:19 +01:00
Automatically generate database schema from database updates
This change integrates the new Schema.Dump() method in the LXD development process. From now one there's a single source of truth of database schema, the database updates, and there's no need to keep updates and CURRENT_SCHEMA declarations in sync, since CURRENT_SCHEMA is generated automatically. Signed-off-by: Free Ekanayaka <free.ekanayaka@canonical.com>
This commit is contained in:
5
Makefile
5
Makefile
@@ -28,6 +28,11 @@ update:
|
||||
go get -t -v -d -u ./...
|
||||
@echo "Dependencies updated"
|
||||
|
||||
.PHONY: update
|
||||
update-schema:
|
||||
go run -v $(TAGS) ./lxd/schema.go
|
||||
@echo "Schema source code updated"
|
||||
|
||||
.PHONY: debug
|
||||
debug:
|
||||
go get -t -v -d ./...
|
||||
|
||||
@@ -1002,17 +1002,23 @@ func initializeDbObject(d *Daemon, path string) error {
|
||||
|
||||
// Apply any database update.
|
||||
//
|
||||
// NOTE: we use the postApply parameter to run a couple of
|
||||
// legacy non-db updates that were introduced before the
|
||||
// NOTE: we use the legacyPatches parameter to run a few
|
||||
// legacy non-db updates that were in place before the
|
||||
// patches mechanism was introduced in lxd/patches.go. The
|
||||
// rest of non-db patches will be applied separately via
|
||||
// patchesApplyAll. See PR #3322 for more details.
|
||||
err = db.UpdatesApplyAll(d.db, true, func(version int) error {
|
||||
if legacyPatch, ok := legacyPatches[version]; ok {
|
||||
return legacyPatch(d)
|
||||
legacy := map[int]*db.LegacyPatch{}
|
||||
for i, patch := range legacyPatches {
|
||||
legacy[i] = &db.LegacyPatch{
|
||||
Hook: func() error {
|
||||
return patch(d)
|
||||
},
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
for _, i := range legacyPatchesNeedingDB {
|
||||
legacy[i].NeedsDB = true
|
||||
}
|
||||
err = db.UpdatesApplyAll(d.db, true, legacy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
197
lxd/db/db.go
197
lxd/db/db.go
@@ -25,196 +25,6 @@ var (
|
||||
NoSuchObjectError = fmt.Errorf("No such object")
|
||||
)
|
||||
|
||||
// CURRENT_SCHEMA contains the current SQLite SQL Schema.
|
||||
const CURRENT_SCHEMA string = `
|
||||
CREATE TABLE IF NOT EXISTS certificates (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
fingerprint VARCHAR(255) NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
certificate TEXT NOT NULL,
|
||||
UNIQUE (fingerprint)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
UNIQUE (key)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS containers (
|
||||
id INTEGER primary key AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
architecture INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
ephemeral INTEGER NOT NULL DEFAULT 0,
|
||||
stateful INTEGER NOT NULL DEFAULT 0,
|
||||
creation_date DATETIME,
|
||||
last_use_date DATETIME,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS containers_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
container_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
FOREIGN KEY (container_id) REFERENCES containers (id) ON DELETE CASCADE,
|
||||
UNIQUE (container_id, key)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS containers_devices (
|
||||
id INTEGER primary key AUTOINCREMENT NOT NULL,
|
||||
container_id INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type INTEGER NOT NULL default 0,
|
||||
FOREIGN KEY (container_id) REFERENCES containers (id) ON DELETE CASCADE,
|
||||
UNIQUE (container_id, name)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS containers_devices_config (
|
||||
id INTEGER primary key AUTOINCREMENT NOT NULL,
|
||||
container_device_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
FOREIGN KEY (container_device_id) REFERENCES containers_devices (id) ON DELETE CASCADE,
|
||||
UNIQUE (container_device_id, key)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS containers_profiles (
|
||||
id INTEGER primary key AUTOINCREMENT NOT NULL,
|
||||
container_id INTEGER NOT NULL,
|
||||
profile_id INTEGER NOT NULL,
|
||||
apply_order INTEGER NOT NULL default 0,
|
||||
UNIQUE (container_id, profile_id),
|
||||
FOREIGN KEY (container_id) REFERENCES containers(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS images (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
cached INTEGER NOT NULL DEFAULT 0,
|
||||
fingerprint VARCHAR(255) NOT NULL,
|
||||
filename VARCHAR(255) NOT NULL,
|
||||
size INTEGER NOT NULL,
|
||||
public INTEGER NOT NULL DEFAULT 0,
|
||||
auto_update INTEGER NOT NULL DEFAULT 0,
|
||||
architecture INTEGER NOT NULL,
|
||||
creation_date DATETIME,
|
||||
expiry_date DATETIME,
|
||||
upload_date DATETIME NOT NULL,
|
||||
last_use_date DATETIME,
|
||||
UNIQUE (fingerprint)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS images_aliases (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
image_id INTEGER NOT NULL,
|
||||
description TEXT,
|
||||
FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS images_properties (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
image_id INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS images_source (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
image_id INTEGER NOT NULL,
|
||||
server TEXT NOT NULL,
|
||||
protocol INTEGER NOT NULL,
|
||||
certificate TEXT NOT NULL,
|
||||
alias VARCHAR(255) NOT NULL,
|
||||
FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS networks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS networks_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
network_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
UNIQUE (network_id, key),
|
||||
FOREIGN KEY (network_id) REFERENCES networks (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS patches (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
applied_at DATETIME NOT NULL,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS profiles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS profiles_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
profile_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value VARCHAR(255),
|
||||
UNIQUE (profile_id, key),
|
||||
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS profiles_devices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
profile_id INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type INTEGER NOT NULL default 0,
|
||||
UNIQUE (profile_id, name),
|
||||
FOREIGN KEY (profile_id) REFERENCES profiles (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS profiles_devices_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
profile_device_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
UNIQUE (profile_device_id, key),
|
||||
FOREIGN KEY (profile_device_id) REFERENCES profiles_devices (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS schema (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
version INTEGER NOT NULL,
|
||||
updated_at DATETIME NOT NULL,
|
||||
UNIQUE (version)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS storage_pools (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
driver VARCHAR(255) NOT NULL,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS storage_pools_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
storage_pool_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
UNIQUE (storage_pool_id, key),
|
||||
FOREIGN KEY (storage_pool_id) REFERENCES storage_pools (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS storage_volumes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
storage_pool_id INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
UNIQUE (storage_pool_id, name, type),
|
||||
FOREIGN KEY (storage_pool_id) REFERENCES storage_pools (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS storage_volumes_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
storage_volume_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
UNIQUE (storage_volume_id, key),
|
||||
FOREIGN KEY (storage_volume_id) REFERENCES storage_volumes (id) ON DELETE CASCADE
|
||||
);`
|
||||
|
||||
func enableForeignKeys(conn *sqlite3.SQLiteConn) error {
|
||||
_, err := conn.Exec("PRAGMA foreign_keys=ON;", nil)
|
||||
return err
|
||||
@@ -249,13 +59,6 @@ func CreateDb(db *sql.DB, patchNames []string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// There isn't an entry for schema version, let's put it in.
|
||||
insertStmt := `INSERT INTO schema (version, updated_at) values (?, strftime("%s"));`
|
||||
_, err = db.Exec(insertStmt, GetLatestSchema())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Mark all existing patches as applied
|
||||
for _, patchName := range patchNames {
|
||||
PatchesMarkApplied(db, patchName)
|
||||
|
||||
198
lxd/db/schema.go
Normal file
198
lxd/db/schema.go
Normal file
@@ -0,0 +1,198 @@
|
||||
package db
|
||||
|
||||
// DO NOT EDIT BY HAND
|
||||
//
|
||||
// This code was generated by the UpdateSchemaDotGo function. If you need to
|
||||
// modify the database schema, please add a new schema update to update.go
|
||||
// and the run 'make update-schema'.
|
||||
const CURRENT_SCHEMA = `
|
||||
CREATE TABLE certificates (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
fingerprint VARCHAR(255) NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
certificate TEXT NOT NULL,
|
||||
UNIQUE (fingerprint)
|
||||
);
|
||||
CREATE TABLE config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
UNIQUE (key)
|
||||
);
|
||||
CREATE TABLE "containers" (
|
||||
id INTEGER primary key AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
architecture INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
ephemeral INTEGER NOT NULL DEFAULT 0,
|
||||
creation_date DATETIME NOT NULL DEFAULT 0,
|
||||
stateful INTEGER NOT NULL DEFAULT 0,
|
||||
last_use_date DATETIME,
|
||||
description TEXT,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE containers_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
container_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
FOREIGN KEY (container_id) REFERENCES containers (id) ON DELETE CASCADE,
|
||||
UNIQUE (container_id, key)
|
||||
);
|
||||
CREATE TABLE containers_devices (
|
||||
id INTEGER primary key AUTOINCREMENT NOT NULL,
|
||||
container_id INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type INTEGER NOT NULL default 0,
|
||||
FOREIGN KEY (container_id) REFERENCES containers (id) ON DELETE CASCADE,
|
||||
UNIQUE (container_id, name)
|
||||
);
|
||||
CREATE TABLE containers_devices_config (
|
||||
id INTEGER primary key AUTOINCREMENT NOT NULL,
|
||||
container_device_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
FOREIGN KEY (container_device_id) REFERENCES containers_devices (id) ON DELETE CASCADE,
|
||||
UNIQUE (container_device_id, key)
|
||||
);
|
||||
CREATE TABLE containers_profiles (
|
||||
id INTEGER primary key AUTOINCREMENT NOT NULL,
|
||||
container_id INTEGER NOT NULL,
|
||||
profile_id INTEGER NOT NULL,
|
||||
apply_order INTEGER NOT NULL default 0,
|
||||
UNIQUE (container_id, profile_id),
|
||||
FOREIGN KEY (container_id) REFERENCES containers(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE images (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
fingerprint VARCHAR(255) NOT NULL,
|
||||
filename VARCHAR(255) NOT NULL,
|
||||
size INTEGER NOT NULL,
|
||||
public INTEGER NOT NULL DEFAULT 0,
|
||||
architecture INTEGER NOT NULL,
|
||||
creation_date DATETIME,
|
||||
expiry_date DATETIME,
|
||||
upload_date DATETIME NOT NULL,
|
||||
cached INTEGER NOT NULL DEFAULT 0,
|
||||
last_use_date DATETIME,
|
||||
auto_update INTEGER NOT NULL DEFAULT 0,
|
||||
UNIQUE (fingerprint)
|
||||
);
|
||||
CREATE TABLE "images_aliases" (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
image_id INTEGER NOT NULL,
|
||||
description TEXT,
|
||||
FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE images_properties (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
image_id INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE images_source (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
image_id INTEGER NOT NULL,
|
||||
server TEXT NOT NULL,
|
||||
protocol INTEGER NOT NULL,
|
||||
certificate TEXT NOT NULL,
|
||||
alias VARCHAR(255) NOT NULL,
|
||||
FOREIGN KEY (image_id) REFERENCES images (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE networks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE networks_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
network_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
UNIQUE (network_id, key),
|
||||
FOREIGN KEY (network_id) REFERENCES networks (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE patches (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
applied_at DATETIME NOT NULL,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE profiles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE profiles_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
profile_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value VARCHAR(255),
|
||||
UNIQUE (profile_id, key),
|
||||
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE profiles_devices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
profile_id INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
type INTEGER NOT NULL default 0,
|
||||
UNIQUE (profile_id, name),
|
||||
FOREIGN KEY (profile_id) REFERENCES profiles (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE profiles_devices_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
profile_device_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
UNIQUE (profile_device_id, key),
|
||||
FOREIGN KEY (profile_device_id) REFERENCES profiles_devices (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE schema (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
version INTEGER NOT NULL,
|
||||
updated_at DATETIME NOT NULL,
|
||||
UNIQUE (version)
|
||||
);
|
||||
CREATE TABLE storage_pools (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
driver VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE storage_pools_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
storage_pool_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
UNIQUE (storage_pool_id, key),
|
||||
FOREIGN KEY (storage_pool_id) REFERENCES storage_pools (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE storage_volumes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
storage_pool_id INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
description TEXT,
|
||||
UNIQUE (storage_pool_id, name, type),
|
||||
FOREIGN KEY (storage_pool_id) REFERENCES storage_pools (id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE storage_volumes_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
storage_volume_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
UNIQUE (storage_volume_id, key),
|
||||
FOREIGN KEY (storage_volume_id) REFERENCES storage_volumes (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
INSERT INTO schema (version, updated_at) VALUES (36, strftime("%s"))
|
||||
`
|
||||
158
lxd/db/update.go
158
lxd/db/update.go
@@ -5,6 +5,8 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -27,6 +29,10 @@ import (
|
||||
DO NOT USE this mechanism for one-time actions which do not involve
|
||||
changes to the database schema. Use patches instead (see lxd/patches.go).
|
||||
|
||||
REMEMBER to run "make update-schema" after you add a new update function to
|
||||
this slice. That will refresh the schema declaration in lxd/db/schema.go and
|
||||
include the effect of applying your patch as well.
|
||||
|
||||
Only append to the updates list, never remove entries and never re-order them.
|
||||
*/
|
||||
|
||||
@@ -69,18 +75,25 @@ var updates = map[int]schema.Update{
|
||||
36: updateFromV35,
|
||||
}
|
||||
|
||||
// Apply all possible database patches. If "doBackup" is true, the
|
||||
// sqlite file will be backed up before any update is applied. If
|
||||
// "postApply" it's passed, it will be called after each database
|
||||
// update gets successfully applied, and be passed the its version (as
|
||||
// of now "postApply" is only used by the daemon as a mean to apply
|
||||
// the legacy V10 and V15 non-db updates during the database upgrade
|
||||
// sequence to, avoid changing semantics see PR #3322).
|
||||
func UpdatesApplyAll(db *sql.DB, doBackup bool, postApply func(int) error) error {
|
||||
// LegacyPatch is a "database" update that performs non-database work. They
|
||||
// are needed for historical reasons, since there was a time were db updates
|
||||
// could do non-db work and depend on functionality external to the db
|
||||
// package. See UpdatesApplyAll below.
|
||||
type LegacyPatch struct {
|
||||
NeedsDB bool // Whether the patch does any DB-related work
|
||||
Hook func() error // The actual patch logic
|
||||
}
|
||||
|
||||
// UpdatesApplyAll applies all possible database patches. If "doBackup" is
|
||||
// true, the sqlite file will be backed up before any update is applied. The
|
||||
// legacyPatches parameter is used by the Daemon as a mean to apply the legacy
|
||||
// V10, V11, V15, V29 and V30 non-db updates during the database upgrade
|
||||
// sequence, to avoid any change in semantics wrt the old logic (see PR #3322).
|
||||
func UpdatesApplyAll(db *sql.DB, doBackup bool, legacyPatches map[int]*LegacyPatch) error {
|
||||
backup := false
|
||||
|
||||
schema := schema.NewFromMap(updates)
|
||||
schema.Hook(func(version int) error {
|
||||
schema.Hook(func(version int, tx *sql.Tx) error {
|
||||
if doBackup && !backup {
|
||||
logger.Infof("Updating the LXD database schema. Backup made as \"lxd.db.bak\"")
|
||||
err := shared.FileCopy(shared.VarPath("lxd.db"), shared.VarPath("lxd.db.bak"))
|
||||
@@ -91,14 +104,87 @@ func UpdatesApplyAll(db *sql.DB, doBackup bool, postApply func(int) error) error
|
||||
backup = true
|
||||
}
|
||||
logger.Debugf("Updating DB schema from %d to %d", version, version+1)
|
||||
if postApply != nil {
|
||||
return postApply(version + 1)
|
||||
|
||||
legacyPatch, ok := legacyPatches[version]
|
||||
if ok {
|
||||
// FIXME We need to commit the transaction before the
|
||||
// hook and then open it again afterwards because this
|
||||
// legacy patch pokes with the database and would fail
|
||||
// with a lock error otherwise.
|
||||
if legacyPatch.NeedsDB {
|
||||
_, err := tx.Exec("COMMIT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := legacyPatch.Hook()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if legacyPatch.NeedsDB {
|
||||
_, err = tx.Exec("BEGIN")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return schema.Ensure(db)
|
||||
}
|
||||
|
||||
// UpdateSchemaDotGo rewrites the 'schema.go' source file in this package to
|
||||
// match the current schema updates.
|
||||
//
|
||||
// The schema.go file contains a "flattened" render of all schema updates
|
||||
// defined in this file, and it's used to initialize brand new databases.
|
||||
func UpdateSchemaDotGo() error {
|
||||
// Apply all the updates that we have on a pristine database and dump
|
||||
// the resulting schema.
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open schema.go for writing: %v", err)
|
||||
}
|
||||
|
||||
schema := schema.NewFromMap(updates)
|
||||
|
||||
err = schema.Ensure(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dump, err := schema.Dump(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Passing 1 to runtime.Caller identifies the caller of runtime.Caller,
|
||||
// that means us.
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
|
||||
file, err := os.Create(path.Join(path.Dir(filename), "schema.go"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open schema.go for writing: %v", err)
|
||||
}
|
||||
|
||||
_, err = file.Write([]byte(fmt.Sprintf(schemaDotGo, dump)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write to schema.go: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Template for schema.go (can't use backticks since we need to use backticks
|
||||
// inside the template itself).
|
||||
const schemaDotGo = "package db\n\n" +
|
||||
"// DO NOT EDIT BY HAND\n" +
|
||||
"//\n" +
|
||||
"// This code was generated by the UpdateSchemaDotGo function. If you need to\n" +
|
||||
"// modify the database schema, please add a new schema update to update.go\n" +
|
||||
"// and the run 'make update-schema'.\n" +
|
||||
"const CURRENT_SCHEMA = `\n" +
|
||||
"%s`\n"
|
||||
|
||||
// Schema updates begin here
|
||||
func updateFromV35(tx *sql.Tx) error {
|
||||
stmts := `
|
||||
@@ -818,7 +904,51 @@ CREATE TABLE IF NOT EXISTS images_aliases (
|
||||
}
|
||||
|
||||
func updateFromV0(tx *sql.Tx) error {
|
||||
// v0..v1 is a noop. It used to add the schema table, but that's now
|
||||
// done by Schema.Ensure().
|
||||
return nil
|
||||
// v0..v1 the dawn of containers
|
||||
stmt := `
|
||||
CREATE TABLE certificates (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
fingerprint VARCHAR(255) NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
certificate TEXT NOT NULL,
|
||||
UNIQUE (fingerprint)
|
||||
);
|
||||
CREATE TABLE containers (
|
||||
id INTEGER primary key AUTOINCREMENT NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
architecture INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
UNIQUE (name)
|
||||
);
|
||||
CREATE TABLE containers_config (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
container_id INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
FOREIGN KEY (container_id) REFERENCES containers (id),
|
||||
UNIQUE (container_id, key)
|
||||
);
|
||||
CREATE TABLE images (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
fingerprint VARCHAR(255) NOT NULL,
|
||||
filename VARCHAR(255) NOT NULL,
|
||||
size INTEGER NOT NULL,
|
||||
public INTEGER NOT NULL DEFAULT 0,
|
||||
architecture INTEGER NOT NULL,
|
||||
creation_date DATETIME,
|
||||
expiry_date DATETIME,
|
||||
upload_date DATETIME NOT NULL,
|
||||
UNIQUE (fingerprint)
|
||||
);
|
||||
CREATE TABLE images_properties (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
image_id INTEGER NOT NULL,
|
||||
type INTEGER NOT NULL,
|
||||
key VARCHAR(255) NOT NULL,
|
||||
value TEXT,
|
||||
FOREIGN KEY (image_id) REFERENCES images (id)
|
||||
);`
|
||||
_, err := tx.Exec(stmt)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2405,9 +2405,10 @@ var legacyPatches = map[int](func(d *Daemon) error){
|
||||
11: patchUpdateFromV10,
|
||||
12: patchUpdateFromV11,
|
||||
16: patchUpdateFromV15,
|
||||
31: patchUpdateFromV30,
|
||||
30: patchUpdateFromV29,
|
||||
31: patchUpdateFromV30,
|
||||
}
|
||||
var legacyPatchesNeedingDB = []int{11, 12, 16} // Legacy patches doing DB work
|
||||
|
||||
func patchUpdateFromV10(d *Daemon) error {
|
||||
if shared.PathExists(shared.VarPath("lxc")) {
|
||||
|
||||
22
lxd/schema.go
Normal file
22
lxd/schema.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// +build never
|
||||
//
|
||||
// We use the tag 'never' because this utility shouldn't normally be built,
|
||||
// unless you're running the 'update-schema' target of the Makefile.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/lxc/lxd/lxd/db"
|
||||
)
|
||||
|
||||
// Entry point for the "schema" development utility, which updates the content
|
||||
// of lxd/db/schema.go according to the current schema updates declared in
|
||||
// updates.go in the same package.
|
||||
func main() {
|
||||
err := db.UpdateSchemaDotGo()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user