[lxc-devel] [lxd/master] Bugfixes and minor features
stgraber on Github
lxc-bot at linuxcontainers.org
Sat Feb 27 21:03:45 UTC 2016
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160227/3f076825/attachment.bin>
-------------- next part --------------
From 7b184c16f12bec7933109a89f2d97e968da82e23 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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
More information about the lxc-devel
mailing list