The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/1674
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) ===
From 7b184c16f12bec7933109a89f2d97e968da82e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Feb 2016 15:56:09 -0500 Subject: [PATCH 1/7] tests: Fix failure on networked test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- test/suites/remote.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/remote.sh b/test/suites/remote.sh index e5f2107..0c7b37f 100644 --- a/test/suites/remote.sh +++ b/test/suites/remote.sh @@ -73,7 +73,7 @@ test_remote_admin() { # avoid default high port behind some proxies: if [ -z "${LXD_OFFLINE:-}" ]; then - lxc_remote remote add images images.linuxcontainers.org + lxc_remote remote add images1 images.linuxcontainers.org lxc_remote remote add images2 images.linuxcontainers.org:443 fi } From 906e0208bd2d01312482ccd1cb1dccf7dcf940f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Feb 2016 16:02:08 -0500 Subject: [PATCH 2/7] tests: Fix the number of certs check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- test/suites/remote.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/suites/remote.sh b/test/suites/remote.sh index 0c7b37f..ee04ad1 100644 --- a/test/suites/remote.sh +++ b/test/suites/remote.sh @@ -65,8 +65,9 @@ test_remote_admin() { # now re-add under a different alias lxc_remote config trust add "${LXD_CONF}/client2.crt" - if [ "$(lxc_remote config trust list | wc -l)" -ne 6 ]; then + if [ "$(lxc_remote config trust list | wc -l)" -ne 7 ]; then echo "wrong number of certs" + false fi # Check that we can add domains with valid certs without confirmation: From cd8d73c9489cb905ecfd3c6c53b1a8e375b6e76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Fri, 26 Feb 2016 23:18:12 -0500 Subject: [PATCH 3/7] Add support for profile descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- client.go | 2 +- lxd/db.go | 3 ++- lxd/db_profiles.go | 61 ++++++++++++++++++++++++++++++++++------------------- lxd/db_update.go | 14 ++++++++++++ lxd/profiles.go | 47 +++++++++++++++++++++++------------------ shared/container.go | 7 +++--- specs/database.md | 1 + specs/rest-api.md | 3 +++ 8 files changed, 91 insertions(+), 47 deletions(-) diff --git a/client.go b/client.go index c5b1468..4547872 100644 --- a/client.go +++ b/client.go @@ -1826,7 +1826,7 @@ func (c *Client) PutProfile(name string, profile shared.ProfileConfig) error { if profile.Name != name { return fmt.Errorf("Cannot change profile name") } - body := shared.Jmap{"name": name, "config": profile.Config, "devices": profile.Devices} + body := shared.Jmap{"name": name, "description": profile.Description, "config": profile.Config, "devices": profile.Devices} _, err := c.put(fmt.Sprintf("profiles/%s", name), body, Sync) return err } diff --git a/lxd/db.go b/lxd/db.go index c4f6cf5..a365b0e 100644 --- a/lxd/db.go +++ b/lxd/db.go @@ -34,7 +34,7 @@ type Profile struct { // Profiles will contain a list of all Profiles. type Profiles []Profile -const DB_CURRENT_VERSION int = 23 +const DB_CURRENT_VERSION int = 24 // CURRENT_SCHEMA contains the current SQLite SQL Schema. const CURRENT_SCHEMA string = ` @@ -127,6 +127,7 @@ CREATE TABLE IF NOT EXISTS images_properties ( 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 ( diff --git a/lxd/db_profiles.go b/lxd/db_profiles.go index 78cfbad..4ab8ef3 100644 --- a/lxd/db_profiles.go +++ b/lxd/db_profiles.go @@ -9,24 +9,6 @@ import ( "github.com/lxc/lxd/shared" ) -func dbProfileID(db *sql.DB, profile string) (int64, error) { - id := int64(-1) - - rows, err := dbQuery(db, "SELECT id FROM profiles WHERE name=?", profile) - if err != nil { - return id, err - } - defer rows.Close() - - for rows.Next() { - var xID int64 - rows.Scan(&xID) - id = xID - } - - return id, nil -} - // dbProfiles returns a string list of profiles. func dbProfiles(db *sql.DB) ([]string, error) { q := fmt.Sprintf("SELECT name FROM profiles") @@ -46,14 +28,44 @@ func dbProfiles(db *sql.DB) ([]string, error) { return response, nil } -func dbProfileCreate(db *sql.DB, profile string, config map[string]string, +func dbProfileGet(db *sql.DB, profile string) (int64, *shared.ProfileConfig, error) { + id := int64(-1) + description := sql.NullString{} + + q := "SELECT id, description FROM profiles WHERE name=?" + arg1 := []interface{}{profile} + arg2 := []interface{}{&id, &description} + err := dbQueryRowScan(db, q, arg1, arg2) + if err != nil { + return -1, nil, fmt.Errorf("here: %s", err) + } + + config, err := dbProfileConfig(db, profile) + if err != nil { + return -1, nil, err + } + + devices, err := dbDevices(db, profile, true) + if err != nil { + return -1, nil, err + } + + return id, &shared.ProfileConfig{ + Name: profile, + Config: config, + Description: description.String, + Devices: devices, + }, nil +} + +func dbProfileCreate(db *sql.DB, profile string, description string, config map[string]string, devices shared.Devices) (int64, error) { tx, err := dbBegin(db) if err != nil { return -1, err } - result, err := tx.Exec("INSERT INTO profiles (name) VALUES (?)", profile) + result, err := tx.Exec("INSERT INTO profiles (name, description) VALUES (?, ?)", profile, description) if err != nil { tx.Rollback() return -1, err @@ -85,7 +97,7 @@ func dbProfileCreate(db *sql.DB, profile string, config map[string]string, } func dbProfileCreateDefault(db *sql.DB) error { - id, err := dbProfileID(db, "default") + id, _, err := dbProfileGet(db, "default") if err != nil { return err } @@ -102,7 +114,7 @@ func dbProfileCreateDefault(db *sql.DB) error { "type": "nic", "nictype": "bridged", "parent": "lxcbr0"}} - id, err = dbProfileCreate(db, "default", map[string]string{}, devices) + id, err = dbProfileCreate(db, "default", "Default LXD profile", map[string]string{}, devices) if err != nil { return err } @@ -188,6 +200,11 @@ func dbProfileUpdate(db *sql.DB, name string, newName string) error { return err } +func dbProfileDescriptionUpdate(tx *sql.Tx, id int64, description string) error { + _, err := tx.Exec("UPDATE profiles SET description=? WHERE id=?", description, id) + return err +} + func dbProfileConfigClear(tx *sql.Tx, id int64) error { _, err := tx.Exec("DELETE FROM profiles_config WHERE profile_id=?", id) if err != nil { diff --git a/lxd/db_update.go b/lxd/db_update.go index 8f11df8..251e97e 100644 --- a/lxd/db_update.go +++ b/lxd/db_update.go @@ -15,6 +15,14 @@ import ( log "gopkg.in/inconshreveable/log15.v2" ) +func dbUpdateFromV23(db *sql.DB) error { + stmt := ` +ALTER TABLE profiles ADD COLUMN description TEXT; +INSERT INTO schema (version, updated_at) VALUES (?, strftime("%s"));` + _, err := db.Exec(stmt, 24) + return err +} + func dbUpdateFromV22(db *sql.DB) error { stmt := ` DELETE FROM containers_devices_config WHERE key='type'; @@ -889,6 +897,12 @@ func dbUpdate(d *Daemon, prevVersion int) error { return err } } + if prevVersion < 24 { + err = dbUpdateFromV23(db) + if err != nil { + return err + } + } return nil } diff --git a/lxd/profiles.go b/lxd/profiles.go index b37c811..16d563e 100644 --- a/lxd/profiles.go +++ b/lxd/profiles.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "reflect" "github.com/gorilla/mux" _ "github.com/mattn/go-sqlite3" @@ -15,9 +16,10 @@ import ( /* This is used for both profiles post and profile put */ type profilesPostReq struct { - Name string `json:"name"` - Config map[string]string `json:"config"` - Devices shared.Devices `json:"devices"` + Name string `json:"name"` + Config map[string]string `json:"config"` + Description string `json:"description"` + Devices shared.Devices `json:"devices"` } func profilesGet(d *Daemon, r *http.Request) Response { @@ -75,7 +77,7 @@ func profilesPost(d *Daemon, r *http.Request) Response { } // Update DB entry - _, err = dbProfileCreate(d.db, req.Name, req.Config, req.Devices) + _, err = dbProfileCreate(d.db, req.Name, req.Description, req.Config, req.Devices) if err != nil { return InternalError( fmt.Errorf("Error inserting %s into database: %s", req.Name, err)) @@ -90,21 +92,8 @@ var profilesCmd = Command{ post: profilesPost} func doProfileGet(d *Daemon, name string) (*shared.ProfileConfig, error) { - config, err := dbProfileConfig(d.db, name) - if err != nil { - return nil, err - } - - devices, err := dbDevices(d.db, name, true) - if err != nil { - return nil, err - } - - return &shared.ProfileConfig{ - Name: name, - Config: config, - Devices: devices, - }, nil + _, profile, err := dbProfileGet(d.db, name) + return profile, err } func profileGet(d *Daemon, r *http.Request) Response { @@ -168,7 +157,7 @@ func profilePut(d *Daemon, r *http.Request) Response { } // Update the database - id, err := dbProfileID(d.db, name) + id, profile, err := dbProfileGet(d.db, name) if err != nil { return InternalError(fmt.Errorf("Failed to retrieve profile='%s'", name)) } @@ -178,6 +167,24 @@ func profilePut(d *Daemon, r *http.Request) Response { return InternalError(err) } + if profile.Description != req.Description { + err = dbProfileDescriptionUpdate(tx, id, req.Description) + if err != nil { + tx.Rollback() + return InternalError(err) + } + } + + // Optimize for description-only changes + if reflect.DeepEqual(profile.Config, req.Config) && reflect.DeepEqual(profile.Devices, req.Devices) { + err = txCommit(tx) + if err != nil { + return InternalError(err) + } + + return EmptySyncResponse + } + err = dbProfileConfigClear(tx, id) if err != nil { tx.Rollback() diff --git a/shared/container.go b/shared/container.go index 58bcd8d..0283ba5 100644 --- a/shared/container.go +++ b/shared/container.go @@ -115,7 +115,8 @@ const ( ) type ProfileConfig struct { - Name string `json:"name"` - Config map[string]string `json:"config"` - Devices Devices `json:"devices"` + Name string `json:"name"` + Config map[string]string `json:"config"` + Description string `json:"description"` + Devices Devices `json:"devices"` } diff --git a/specs/database.md b/specs/database.md index 850d0d9..23bb3fe 100644 --- a/specs/database.md +++ b/specs/database.md @@ -225,6 +225,7 @@ Column | Type | Default | Constraint | Descriptio :----- | :--- | :------ | :--------- | :---------- id | INTEGER | SERIAL | NOT NULL | SERIAL name | VARCHAR(255) | - | NOT NULL | Profile name +description | TEXT | - | | Description of the profile Index: UNIQUE on id AND name diff --git a/specs/rest-api.md b/specs/rest-api.md index 09fca34..b9ade7d 100644 --- a/specs/rest-api.md +++ b/specs/rest-api.md @@ -1402,6 +1402,7 @@ Input: { "name": "my-profilename", + "description": "Some description string", "config": { "limits.memory": "2GB" }, @@ -1424,6 +1425,7 @@ Output: { "name": "test", + "description": "Some description string", "config": { "limits.memory": "2GB" }, @@ -1447,6 +1449,7 @@ Input: "config": { "limits.memory": "4GB" }, + "description": "Some description string", "devices": { "kvm": { "path": "/dev/kvm", From 72be9ce65a01e5dac7a08718c653ca56c257d0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Fri, 26 Feb 2016 23:29:32 -0500 Subject: [PATCH 4/7] Add some simplestream aliases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- shared/simplestreams.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/shared/simplestreams.go b/shared/simplestreams.go index b82a51d..e9843f6 100644 --- a/shared/simplestreams.go +++ b/shared/simplestreams.go @@ -150,6 +150,7 @@ func (s *SimpleStreamsManifest) ToLXD() ([]ImageInfo, map[string][][]string) { image.Properties = map[string]string{ "os": product.OperatingSystem, "release": product.Release, + "version": product.Version, "architecture": product.Architecture, "label": version.Label, "serial": name, @@ -358,6 +359,26 @@ func (s *SimpleStreams) applyAliases(images []ImageInfo) ([]ImageInfo, map[strin if alias != nil { image.Aliases = append(image.Aliases, *alias) } + + alias = addAlias(fmt.Sprintf("%s/%c", image.Properties["os"], image.Properties["release"][0]), image.Fingerprint) + if alias != nil { + image.Aliases = append(image.Aliases, *alias) + } + + alias = addAlias(fmt.Sprintf("%s/%c/%s", image.Properties["os"], image.Properties["release"][0], image.Properties["serial"]), image.Fingerprint) + if alias != nil { + image.Aliases = append(image.Aliases, *alias) + } + + alias = addAlias(fmt.Sprintf("%s/%s", image.Properties["os"], image.Properties["version"]), image.Fingerprint) + if alias != nil { + image.Aliases = append(image.Aliases, *alias) + } + + alias = addAlias(fmt.Sprintf("%s/%s/%s", image.Properties["os"], image.Properties["version"], image.Properties["serial"]), image.Fingerprint) + if alias != nil { + image.Aliases = append(image.Aliases, *alias) + } } // Medium @@ -366,12 +387,32 @@ func (s *SimpleStreams) applyAliases(images []ImageInfo) ([]ImageInfo, map[strin image.Aliases = append(image.Aliases, *alias) } + alias = addAlias(fmt.Sprintf("%s/%c/%s", image.Properties["os"], image.Properties["release"][0], image.Properties["architecture"]), image.Fingerprint) + if alias != nil { + image.Aliases = append(image.Aliases, *alias) + } + + alias = addAlias(fmt.Sprintf("%s/%s/%s", image.Properties["os"], image.Properties["version"], image.Properties["architecture"]), image.Fingerprint) + if alias != nil { + image.Aliases = append(image.Aliases, *alias) + } + // Medium alias = addAlias(fmt.Sprintf("%s/%s/%s/%s", image.Properties["os"], image.Properties["release"], image.Properties["architecture"], image.Properties["serial"]), image.Fingerprint) if alias != nil { image.Aliases = append(image.Aliases, *alias) } + alias = addAlias(fmt.Sprintf("%s/%c/%s/%s", image.Properties["os"], image.Properties["release"][0], image.Properties["architecture"], image.Properties["serial"]), image.Fingerprint) + if alias != nil { + image.Aliases = append(image.Aliases, *alias) + } + + alias = addAlias(fmt.Sprintf("%s/%s/%s/%s", image.Properties["os"], image.Properties["version"], image.Properties["architecture"], image.Properties["serial"]), image.Fingerprint) + if alias != nil { + image.Aliases = append(image.Aliases, *alias) + } + newImages = append(newImages, image) } From e31b7c1e8517d4497138adf5501a3fea7ea62746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Feb 2016 00:54:27 -0500 Subject: [PATCH 5/7] Fix snapshot configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When snapshotting a container, only its local state should be included. Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/container_snapshot.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go index 0f333e2..d8cbcd9 100644 --- a/lxd/container_snapshot.go +++ b/lxd/container_snapshot.go @@ -125,16 +125,15 @@ func containerSnapshotsPost(d *Daemon, r *http.Request) Response { req.Name snapshot := func(op *operation) error { - config := c.ExpandedConfig() args := containerArgs{ Name: fullName, Ctype: cTypeSnapshot, - Config: config, + Config: c.LocalConfig(), Profiles: c.Profiles(), Ephemeral: c.IsEphemeral(), - BaseImage: config["volatile.base_image"], + BaseImage: c.ExpandedConfig()["volatile.base_image"], Architecture: c.Architecture(), - Devices: c.ExpandedDevices(), + Devices: c.LocalDevices(), } _, err := containerCreateAsSnapshot(d, args, c, req.Stateful) From 4ebe16f929d7c93ffdd0748778d088c63e057d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Feb 2016 01:11:45 -0500 Subject: [PATCH 6/7] Don't rely on the filesystem to check if stateful MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of our backends don't keep snapshots mounted so we can't look for state in there, instead lets use the database for that. Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/container.go | 8 +++++--- lxd/container_lxc.go | 12 ++++++++++-- lxd/container_snapshot.go | 5 +++-- lxd/db.go | 3 ++- lxd/db_containers.go | 18 ++++++++++++++---- lxd/db_update.go | 14 ++++++++++++++ specs/database.md | 1 + 7 files changed, 49 insertions(+), 12 deletions(-) diff --git a/lxd/container.go b/lxd/container.go index 0abfbd6..70869c3 100644 --- a/lxd/container.go +++ b/lxd/container.go @@ -304,6 +304,7 @@ type containerArgs struct { Ephemeral bool Name string Profiles []string + Stateful bool } // The container interface @@ -345,6 +346,7 @@ type container interface { IsFrozen() bool IsEphemeral() bool IsSnapshot() bool + IsStateful() bool IsNesting() bool // Hooks @@ -473,7 +475,7 @@ func containerCreateAsCopy(d *Daemon, args containerArgs, sourceContainer contai return c, nil } -func containerCreateAsSnapshot(d *Daemon, args containerArgs, sourceContainer container, stateful bool) (container, error) { +func containerCreateAsSnapshot(d *Daemon, args containerArgs, sourceContainer container) (container, error) { // Create the snapshot c, err := containerCreateInternal(d, args) if err != nil { @@ -481,7 +483,7 @@ func containerCreateAsSnapshot(d *Daemon, args containerArgs, sourceContainer co } // Deal with state - if stateful { + if args.Stateful { stateDir := sourceContainer.StatePath() err = os.MkdirAll(stateDir, 0700) if err != nil { @@ -519,7 +521,7 @@ func containerCreateAsSnapshot(d *Daemon, args containerArgs, sourceContainer co } // Once we're done, remove the state directory - if stateful { + if args.Stateful { os.RemoveAll(sourceContainer.StatePath()) } diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index b52738c..96c3139 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -97,10 +97,12 @@ func containerLXCCreate(d *Daemon, args containerArgs) (container, error) { ephemeral: args.Ephemeral, architecture: args.Architecture, cType: args.Ctype, + stateful: args.Stateful, creationDate: args.CreationDate, profiles: args.Profiles, localConfig: args.Config, - localDevices: args.Devices} + localDevices: args.Devices, + } // No need to detect storage here, its a new container. c.storage = d.Storage @@ -196,7 +198,8 @@ func containerLXCLoad(d *Daemon, args containerArgs) (container, error) { creationDate: args.CreationDate, profiles: args.Profiles, localConfig: args.Config, - localDevices: args.Devices} + localDevices: args.Devices, + stateful: args.Stateful} // Detect the storage backend s, err := storageForFilename(d, shared.VarPath("containers", strings.Split(c.name, "/")[0])) @@ -223,6 +226,7 @@ type containerLXC struct { ephemeral bool id int name string + stateful bool // Config expandedConfig map[string]string @@ -3980,6 +3984,10 @@ func (c *containerLXC) setNetworkLimits(name string, m shared.Device) error { } // Various state query functions +func (c *containerLXC) IsStateful() bool { + return c.stateful +} + func (c *containerLXC) IsEphemeral() bool { return c.ephemeral } diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go index d8cbcd9..5d9045b 100644 --- a/lxd/container_snapshot.go +++ b/lxd/container_snapshot.go @@ -47,7 +47,7 @@ func containerSnapshotsGet(d *Daemon, r *http.Request) Response { body := shared.Jmap{ "name": snapName, "created_at": snap.CreationDate(), - "stateful": shared.PathExists(snap.StatePath())} + "stateful": snap.IsStateful()} resultMap = append(resultMap, body) } } @@ -134,9 +134,10 @@ func containerSnapshotsPost(d *Daemon, r *http.Request) Response { BaseImage: c.ExpandedConfig()["volatile.base_image"], Architecture: c.Architecture(), Devices: c.LocalDevices(), + Stateful: req.Stateful, } - _, err := containerCreateAsSnapshot(d, args, c, req.Stateful) + _, err := containerCreateAsSnapshot(d, args, c) if err != nil { return err } diff --git a/lxd/db.go b/lxd/db.go index a365b0e..ce6e8b2 100644 --- a/lxd/db.go +++ b/lxd/db.go @@ -34,7 +34,7 @@ type Profile struct { // Profiles will contain a list of all Profiles. type Profiles []Profile -const DB_CURRENT_VERSION int = 24 +const DB_CURRENT_VERSION int = 25 // CURRENT_SCHEMA contains the current SQLite SQL Schema. const CURRENT_SCHEMA string = ` @@ -58,6 +58,7 @@ CREATE TABLE IF NOT EXISTS containers ( architecture INTEGER NOT NULL, type INTEGER NOT NULL, ephemeral INTEGER NOT NULL DEFAULT 0, + stateful INTEGER NOT NULL DEFAULT 0, creation_date DATETIME, UNIQUE (name) ); diff --git a/lxd/db_containers.go b/lxd/db_containers.go index f7067cd..a6d2e84 100644 --- a/lxd/db_containers.go +++ b/lxd/db_containers.go @@ -66,9 +66,10 @@ func dbContainerGet(db *sql.DB, name string) (containerArgs, error) { args.Name = name ephemInt := -1 - q := "SELECT id, architecture, type, ephemeral, creation_date FROM containers WHERE name=?" + statefulInt := -1 + q := "SELECT id, architecture, type, ephemeral, stateful, creation_date FROM containers WHERE name=?" arg1 := []interface{}{name} - arg2 := []interface{}{&args.Id, &args.Architecture, &args.Ctype, &ephemInt, &args.CreationDate} + arg2 := []interface{}{&args.Id, &args.Architecture, &args.Ctype, &ephemInt, &statefulInt, &args.CreationDate} err := dbQueryRowScan(db, q, arg1, arg2) if err != nil { return args, err @@ -82,6 +83,10 @@ func dbContainerGet(db *sql.DB, name string) (containerArgs, error) { args.Ephemeral = true } + if statefulInt == 1 { + args.Stateful = true + } + config, err := dbContainerConfig(db, args.Id) if err != nil { return args, err @@ -124,16 +129,21 @@ func dbContainerCreate(db *sql.DB, args containerArgs) (int, error) { ephemInt = 1 } + statefulInt := 0 + if args.Stateful == true { + statefulInt = 1 + } + args.CreationDate = time.Now().UTC() - str := fmt.Sprintf("INSERT INTO containers (name, architecture, type, ephemeral, creation_date) VALUES (?, ?, ?, ?, ?)") + str := fmt.Sprintf("INSERT INTO containers (name, architecture, type, ephemeral, creation_date, stateful) VALUES (?, ?, ?, ?, ?, ?)") stmt, err := tx.Prepare(str) if err != nil { tx.Rollback() return 0, err } defer stmt.Close() - result, err := stmt.Exec(args.Name, args.Architecture, args.Ctype, ephemInt, args.CreationDate.Unix()) + result, err := stmt.Exec(args.Name, args.Architecture, args.Ctype, ephemInt, args.CreationDate.Unix(), statefulInt) if err != nil { tx.Rollback() return 0, err diff --git a/lxd/db_update.go b/lxd/db_update.go index 251e97e..a3a1579 100644 --- a/lxd/db_update.go +++ b/lxd/db_update.go @@ -15,6 +15,14 @@ import ( log "gopkg.in/inconshreveable/log15.v2" ) +func dbUpdateFromV24(db *sql.DB) error { + stmt := ` +ALTER TABLE containers ADD COLUMN stateful INTEGER NOT NULL DEFAULT 0; +INSERT INTO schema (version, updated_at) VALUES (?, strftime("%s"));` + _, err := db.Exec(stmt, 25) + return err +} + func dbUpdateFromV23(db *sql.DB) error { stmt := ` ALTER TABLE profiles ADD COLUMN description TEXT; @@ -903,6 +911,12 @@ func dbUpdate(d *Daemon, prevVersion int) error { return err } } + if prevVersion < 25 { + err = dbUpdateFromV24(db) + if err != nil { + return err + } + } return nil } diff --git a/specs/database.md b/specs/database.md index 23bb3fe..1c3a703 100644 --- a/specs/database.md +++ b/specs/database.md @@ -110,6 +110,7 @@ name | VARCHAR(255) | - | NOT NULL | Container architecture | INTEGER | - | NOT NULL | Container architecture type | INTEGER | 0 | NOT NULL | Container type (0 = container, 1 = container snapshot) ephemeral | INTEGER | 0 | NOT NULL | Whether the container is ephemeral (0 = persistent, 1 = ephemeral) +stateful | INTEGER | 0 | NOT NULL | Whether the snapshot contains state (snapshot only) creation\_date | DATETIME | - | | Image creation date (user supplied, 0 = unknown) Index: UNIQUE ON id AND name From f2a5f999823f0f0746ae8fe46cdded39d8f2542f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com> Date: Sat, 27 Feb 2016 01:23:44 -0500 Subject: [PATCH 7/7] Catch checkpoint failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- lxd/container.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lxd/container.go b/lxd/container.go index 70869c3..34f45b4 100644 --- a/lxd/container.go +++ b/lxd/container.go @@ -512,6 +512,10 @@ func containerCreateAsSnapshot(d *Daemon, args containerArgs, sourceContainer co if err2 != nil { shared.Log.Warn("failed to collect criu log file", log.Ctx{"error": err2}) } + + if err != nil { + return nil, err + } } // Clone the container
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel