[lxc-devel] [lxd/master] Database logic cleanup part 2
freeekanayaka on Github
lxc-bot at linuxcontainers.org
Tue May 19 12:10:40 UTC 2020
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/20200519/241c363b/attachment.bin>
-------------- next part --------------
From 72b248177ce376d598037f99a485b0bfab919fd8 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Mon, 11 May 2020 11:19:21 +0100
Subject: [PATCH 1/8] lxd/db: Change UpdateCertificate to RenameCertificate
(only renaming supported)
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
lxd/certificates.go | 2 +-
lxd/db/certificates.go | 9 +++++----
lxd/db/certificates.mapper.go | 22 ++++++++++++++++++++++
3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/lxd/certificates.go b/lxd/certificates.go
index fa817c1ee2..917f5d1178 100644
--- a/lxd/certificates.go
+++ b/lxd/certificates.go
@@ -322,7 +322,7 @@ func doCertificateUpdate(d *Daemon, fingerprint string, req api.CertificatePut)
return response.BadRequest(fmt.Errorf("Unknown request type %s", req.Type))
}
- err := d.cluster.UpdateCertificate(fingerprint, req.Name, 1)
+ err := d.cluster.RenameCertificate(fingerprint, req.Name)
if err != nil {
return response.SmartError(err)
}
diff --git a/lxd/db/certificates.go b/lxd/db/certificates.go
index cfefaa773e..5176ceb29c 100644
--- a/lxd/db/certificates.go
+++ b/lxd/db/certificates.go
@@ -12,6 +12,7 @@ package db
//go:generate mapper stmt -p db -e certificate id
//go:generate mapper stmt -p db -e certificate create struct=Certificate
//go:generate mapper stmt -p db -e certificate delete
+//go:generate mapper stmt -p db -e certificate rename
//
//go:generate mapper method -p db -e certificate List
//go:generate mapper method -p db -e certificate Get
@@ -19,6 +20,7 @@ package db
//go:generate mapper method -p db -e certificate Exists struct=Certificate
//go:generate mapper method -p db -e certificate Create struct=Certificate
//go:generate mapper method -p db -e certificate Delete
+//go:generate mapper method -p db -e certificate Rename
// Certificate is here to pass the certificates content
// from the database around
@@ -66,11 +68,10 @@ func (c *Cluster) DeleteCertificate(fingerprint string) error {
return err
}
-// UpdateCertificate updates the certificate with the given fingerprint.
-func (c *Cluster) UpdateCertificate(fingerprint string, certName string, certType int) error {
+// RenameCertificate updates a certificate's name.
+func (c *Cluster) RenameCertificate(fingerprint string, name string) error {
err := c.Transaction(func(tx *ClusterTx) error {
- _, err := tx.tx.Exec("UPDATE certificates SET name=?, type=? WHERE fingerprint=?", certName, certType, fingerprint)
- return err
+ return c.RenameCertificate(fingerprint, name)
})
return err
}
diff --git a/lxd/db/certificates.mapper.go b/lxd/db/certificates.mapper.go
index 4f6e718358..c682094754 100644
--- a/lxd/db/certificates.mapper.go
+++ b/lxd/db/certificates.mapper.go
@@ -41,6 +41,10 @@ var certificateDelete = cluster.RegisterStmt(`
DELETE FROM certificates WHERE fingerprint = ?
`)
+var certificateRename = cluster.RegisterStmt(`
+UPDATE certificates SET name = ? WHERE fingerprint = ?
+`)
+
// GetCertificates returns all available certificates.
func (c *ClusterTx) GetCertificates(filter CertificateFilter) ([]Certificate, error) {
// Result slice.
@@ -203,3 +207,21 @@ func (c *ClusterTx) DeleteCertificate(fingerprint string) error {
return nil
}
+
+// RenameCertificate renames the certificate matching the given key parameters.
+func (c *ClusterTx) RenameCertificate(fingerprint string, to string) error {
+ stmt := c.stmt(certificateRename)
+ result, err := stmt.Exec(to, fingerprint)
+ if err != nil {
+ return errors.Wrap(err, "Rename certificate")
+ }
+
+ n, err := result.RowsAffected()
+ if err != nil {
+ return errors.Wrap(err, "Fetch affected rows")
+ }
+ if n != 1 {
+ return fmt.Errorf("Query affected %d rows instead of 1", n)
+ }
+ return nil
+}
From 1bb5dde03ee8174197bdea33dfe6446cb66e0f7e Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Mon, 11 May 2020 11:23:28 +0100
Subject: [PATCH 2/8] lxd/db: Rename containers.go to instances.go
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
lxd/db/{containers.go => instances.go} | 0
lxd/db/{containers_export_test.go => instances_export_test.go} | 0
lxd/db/{containers_test.go => instances_test.go} | 0
3 files changed, 0 insertions(+), 0 deletions(-)
rename lxd/db/{containers.go => instances.go} (100%)
rename lxd/db/{containers_export_test.go => instances_export_test.go} (100%)
rename lxd/db/{containers_test.go => instances_test.go} (100%)
diff --git a/lxd/db/containers.go b/lxd/db/instances.go
similarity index 100%
rename from lxd/db/containers.go
rename to lxd/db/instances.go
diff --git a/lxd/db/containers_export_test.go b/lxd/db/instances_export_test.go
similarity index 100%
rename from lxd/db/containers_export_test.go
rename to lxd/db/instances_export_test.go
diff --git a/lxd/db/containers_test.go b/lxd/db/instances_test.go
similarity index 100%
rename from lxd/db/containers_test.go
rename to lxd/db/instances_test.go
From 3ebe673c97af596794cc0a3cfa206902576da160 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Mon, 11 May 2020 14:21:32 +0100
Subject: [PATCH 3/8] shared/generate/db: Statement for deleting references
(config and devices)
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
shared/generate/db/stmt.go | 102 ++++++++++++++++++++++++++-----------
1 file changed, 72 insertions(+), 30 deletions(-)
diff --git a/shared/generate/db/stmt.go b/shared/generate/db/stmt.go
index 08f2d39adb..227733a655 100644
--- a/shared/generate/db/stmt.go
+++ b/shared/generate/db/stmt.go
@@ -50,6 +50,10 @@ func (s *Stmt) Generate(buf *file.Buffer) error {
return s.createRef(buf)
}
+ if strings.HasPrefix(s.kind, "delete") && strings.HasSuffix(s.kind, "-ref") {
+ return s.deleteRef(buf)
+ }
+
if strings.HasSuffix(s.kind, "-ref") || strings.Contains(s.kind, "-ref-by-") {
return s.ref(buf)
}
@@ -417,7 +421,6 @@ func (s *Stmt) createRef(buf *file.Buffer) error {
sql := fmt.Sprintf(stmts["create"], table, columns, params)
s.register(buf, sql)
-
} else if field.Type.Name == "map[string]map[string]string" {
// Assume this is a devices table
columns := fmt.Sprintf("%s_id, name, type", s.entity)
@@ -443,35 +446,7 @@ func (s *Stmt) id(buf *file.Buffer) error {
if err != nil {
return errors.Wrap(err, "Parse entity struct")
}
- nk := mapping.NaturalKey()
- criteria := ""
- for i, field := range nk {
- if i > 0 {
- criteria += " AND "
- }
-
- var column string
- if field.IsScalar() {
- column = field.Config.Get("join")
- } else {
- column = mapping.FieldColumnName(field.Name)
- }
-
- criteria += fmt.Sprintf("%s = ?", column)
- }
-
- table := entityTable(s.entity)
- for _, field := range mapping.ScalarFields() {
- join := field.Config.Get("join")
- right := strings.Split(join, ".")[0]
- via := entityTable(s.entity)
- if field.Config.Get("via") != "" {
- via = entityTable(field.Config.Get("via"))
- }
- table += fmt.Sprintf(" JOIN %s ON %s.%s_id = %s.id", right, via, lex.Singular(right), right)
- }
-
- sql := fmt.Sprintf(stmts[s.kind], entityTable(s.entity), table, criteria)
+ sql := naturalKeySelect(s.entity, mapping)
s.register(buf, sql)
return nil
@@ -558,6 +533,38 @@ func (s *Stmt) delete(buf *file.Buffer) error {
return nil
}
+func (s *Stmt) deleteRef(buf *file.Buffer) error {
+ // Base snake-case name of the references (e.g. "used-by-ref" -> "used_by")
+ name := strings.Replace(s.kind[len("create-"):strings.Index(s.kind, "-ref")], "-", "_", -1)
+
+ // Field name of the reference
+ fieldName := lex.Camel(name)
+
+ // Table name where reference objects can be fetched.
+ table := fmt.Sprintf("%s_%s", entityTable(s.entity), name)
+
+ mapping, err := Parse(s.packages[s.pkg], lex.Camel(s.entity))
+ if err != nil {
+ return err
+ }
+
+ field := mapping.FieldByName(fieldName)
+ if field == nil {
+ return fmt.Errorf("Entity %s has no field named %s", s.entity, fieldName)
+ }
+
+ where := fmt.Sprintf("%s_id = ?", s.entity)
+
+ if field.Type.Name == "map[string]string" || field.Type.Name == "map[string]map[string]string" {
+ // Assume this is a config or devices table
+ sql := fmt.Sprintf(stmts["delete"], table, where)
+ s.register(buf, sql)
+
+ }
+
+ return nil
+}
+
// Return a where clause that filters an entity by natural key.
func naturalKeyWhere(mapping *Mapping) string {
nk := mapping.NaturalKey()
@@ -605,6 +612,41 @@ func naturalKeyWhere(mapping *Mapping) string {
return strings.Join(where, " AND ")
}
+// Return a select statement that returns the ID of an entity given its natural key.
+func naturalKeySelect(entity string, mapping *Mapping) string {
+ nk := mapping.NaturalKey()
+ criteria := ""
+ for i, field := range nk {
+ if i > 0 {
+ criteria += " AND "
+ }
+
+ var column string
+ if field.IsScalar() {
+ column = field.Config.Get("join")
+ } else {
+ column = mapping.FieldColumnName(field.Name)
+ }
+
+ criteria += fmt.Sprintf("%s = ?", column)
+ }
+
+ table := entityTable(entity)
+ for _, field := range mapping.ScalarFields() {
+ join := field.Config.Get("join")
+ right := strings.Split(join, ".")[0]
+ via := entityTable(entity)
+ if field.Config.Get("via") != "" {
+ via = entityTable(field.Config.Get("via"))
+ }
+ table += fmt.Sprintf(" JOIN %s ON %s.%s_id = %s.id", right, via, lex.Singular(right), right)
+ }
+
+ sql := fmt.Sprintf(stmts["id"], entityTable(entity), table, criteria)
+
+ return sql
+}
+
// Output a line of code that registers the given statement and declares the
// associated statement code global variable.
func (s *Stmt) register(buf *file.Buffer, sql string, filters ...string) {
From 6451a292bd71d0739a565e95638b488e90c42d4e Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Tue, 19 May 2020 12:04:28 +0100
Subject: [PATCH 4/8] lxd/db: Generate delete stements for profile config and
devices
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
lxd/db/profiles.go | 2 ++
lxd/db/profiles.mapper.go | 8 ++++++++
2 files changed, 10 insertions(+)
diff --git a/lxd/db/profiles.go b/lxd/db/profiles.go
index 19e97787c2..8bd37c96b5 100644
--- a/lxd/db/profiles.go
+++ b/lxd/db/profiles.go
@@ -37,6 +37,8 @@ import (
//go:generate mapper stmt -p db -e profile create-devices-ref
//go:generate mapper stmt -p db -e profile rename
//go:generate mapper stmt -p db -e profile delete
+//go:generate mapper stmt -p db -e profile delete-config-ref
+//go:generate mapper stmt -p db -e profile delete-devices-ref
//
//go:generate mapper method -p db -e profile URIs
//go:generate mapper method -p db -e profile List
diff --git a/lxd/db/profiles.mapper.go b/lxd/db/profiles.mapper.go
index f71d742c42..df5c752a63 100644
--- a/lxd/db/profiles.mapper.go
+++ b/lxd/db/profiles.mapper.go
@@ -119,6 +119,14 @@ var profileDelete = cluster.RegisterStmt(`
DELETE FROM profiles WHERE project_id = (SELECT projects.id FROM projects WHERE projects.name = ?) AND name = ?
`)
+var profileDeleteConfigRef = cluster.RegisterStmt(`
+DELETE FROM profiles_config WHERE profile_id = ?
+`)
+
+var profileDeleteDevicesRef = cluster.RegisterStmt(`
+DELETE FROM profiles_devices WHERE profile_id = ?
+`)
+
// GetProfileURIs returns all available profile URIs.
func (c *ClusterTx) GetProfileURIs(filter ProfileFilter) ([]string, error) {
// Check which filter criteria are active.
From b5ee104c37eb5c692f30d3ab8a28cc321bdc7859 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Tue, 19 May 2020 12:24:33 +0100
Subject: [PATCH 5/8] shared/generate/db: update statement: take ID instead of
natural key
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
lxd/db/projects.go | 12 ++++++------
lxd/db/projects.mapper.go | 2 +-
shared/generate/db/stmt.go | 21 +--------------------
3 files changed, 8 insertions(+), 27 deletions(-)
diff --git a/lxd/db/projects.go b/lxd/db/projects.go
index 49457c1533..295ec8fde1 100644
--- a/lxd/db/projects.go
+++ b/lxd/db/projects.go
@@ -132,8 +132,13 @@ func (c *ClusterTx) ProjectHasImages(name string) (bool, error) {
// UpdateProject updates the project matching the given key parameters.
func (c *ClusterTx) UpdateProject(name string, object api.ProjectPut) error {
+ id, err := c.GetProjectID(name)
+ if err != nil {
+ return errors.Wrap(err, "Fetch project ID")
+ }
+
stmt := c.stmt(projectUpdate)
- result, err := stmt.Exec(object.Description, name)
+ result, err := stmt.Exec(object.Description, id)
if err != nil {
return errors.Wrap(err, "Update project")
}
@@ -146,11 +151,6 @@ func (c *ClusterTx) UpdateProject(name string, object api.ProjectPut) error {
return fmt.Errorf("Query updated %d rows instead of 1", n)
}
- id, err := c.GetProjectID(name)
- if err != nil {
- return errors.Wrap(err, "Fetch project ID")
- }
-
// Clear config.
_, err = c.tx.Exec(`
DELETE FROM projects_config WHERE projects_config.project_id = ?
diff --git a/lxd/db/projects.mapper.go b/lxd/db/projects.mapper.go
index 2e8fd787e6..3b763721f3 100644
--- a/lxd/db/projects.mapper.go
+++ b/lxd/db/projects.mapper.go
@@ -77,7 +77,7 @@ UPDATE projects SET name = ? WHERE name = ?
var projectUpdate = cluster.RegisterStmt(`
UPDATE projects
SET description = ?
- WHERE name = ?
+ WHERE id = ?
`)
var projectDelete = cluster.RegisterStmt(`
diff --git a/shared/generate/db/stmt.go b/shared/generate/db/stmt.go
index 227733a655..db28faad47 100644
--- a/shared/generate/db/stmt.go
+++ b/shared/generate/db/stmt.go
@@ -492,28 +492,9 @@ func (s *Stmt) update(buf *file.Buffer) error {
}
}
- mapping, err = Parse(s.packages[s.pkg], lex.Capital(s.entity))
- if err != nil {
- return errors.Wrap(err, "Parse entity struct")
- }
-
- nk := mapping.NaturalKey()
- where := make([]string, len(nk))
-
- for i, field := range nk {
- if field.IsScalar() {
- ref := lex.Snake(field.Name)
- where[i] = fmt.Sprintf("%s_id = (SELECT id FROM %s WHERE name = ?)", ref, lex.Plural(ref))
- } else {
-
- where[i] = fmt.Sprintf("%s = ?", field.Column())
- }
-
- }
-
sql := fmt.Sprintf(
stmts[s.kind], entityTable(s.entity),
- strings.Join(updates, ", "), strings.Join(where, ", "))
+ strings.Join(updates, ", "), "id = ?")
s.register(buf, sql)
return nil
From 4f4e9c8773706d045a85496fa2425b07ee112f67 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Tue, 19 May 2020 12:49:12 +0100
Subject: [PATCH 6/8] shared/generate/db: Handle config and devices in Update
method
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
shared/generate/db/method.go | 69 +++++++++++++++++++++++++++++++++++-
1 file changed, 68 insertions(+), 1 deletion(-)
diff --git a/shared/generate/db/method.go b/shared/generate/db/method.go
index 0ddc903cd9..c0f6859aa0 100644
--- a/shared/generate/db/method.go
+++ b/shared/generate/db/method.go
@@ -811,8 +811,14 @@ func (m *Method) update(buf *file.Buffer) error {
params[i] = fmt.Sprintf("object.%s", field.Name)
}
+ //buf.L("id, err := c.Get%s(%s)", lex.Camel(m.entity), FieldArgs(nk))
+ buf.L("id, err := c.Get%sID(%s)", lex.Camel(m.entity), FieldParams(nk))
+ buf.L("if err != nil {")
+ buf.L(" return errors.Wrap(err, \"Get %s\")", m.entity)
+ buf.L("}")
+ buf.N()
buf.L("stmt := c.stmt(%s)", stmtCodeVar(m.entity, "update"))
- buf.L("result, err := stmt.Exec(%s)", strings.Join(params, ", ")+", "+FieldParams(nk))
+ buf.L("result, err := stmt.Exec(%s)", strings.Join(params, ", ")+", id")
buf.L("if err != nil {")
buf.L(" return errors.Wrap(err, \"Update %s\")", m.entity)
buf.L("}")
@@ -825,6 +831,67 @@ func (m *Method) update(buf *file.Buffer) error {
buf.L(" return fmt.Errorf(\"Query updated %%d rows instead of 1\", n)")
buf.L("}")
buf.N()
+
+ fields = mapping.RefFields()
+ for _, field := range fields {
+ switch field.Type.Name {
+ case "map[string]string":
+ buf.L("// Delete current config. ")
+ buf.L("stmt = c.stmt(%s)", stmtCodeVar(m.entity, "deleteConfigRef"))
+ buf.L("_, err = stmt.Exec(id)")
+ buf.L("if err != nil {")
+ buf.L(" return errors.Wrap(err, \"Delete current config\")")
+ buf.L("}")
+ buf.N()
+ buf.L("// Insert config reference. ")
+ buf.L("stmt = c.stmt(%s)", stmtCodeVar(m.entity, "createConfigRef"))
+ buf.L("for key, value := range object.%s {", field.Name)
+ buf.L(" _, err := stmt.Exec(id, key, value)")
+ buf.L(" if err != nil {")
+ buf.L(" return errors.Wrap(err, \"Insert config for %s\")", m.entity)
+ buf.L(" }")
+ buf.L("}")
+ buf.N()
+ case "map[string]map[string]string":
+ buf.L("// Delete current devices. ")
+ buf.L("stmt = c.stmt(%s)", stmtCodeVar(m.entity, "deleteDevicesRef"))
+ buf.L("_, err = stmt.Exec(id)")
+ buf.L("if err != nil {")
+ buf.L(" return errors.Wrap(err, \"Delete current devices\")")
+ buf.L("}")
+ buf.N()
+ buf.N()
+ buf.L("// Insert devices reference. ")
+ buf.L("for name, config := range object.%s {", field.Name)
+ buf.L(" typ, ok := config[\"type\"]")
+ buf.L(" if !ok {")
+ buf.L(" return fmt.Errorf(\"No type for device %%s\", name)")
+ buf.L(" }")
+ buf.L(" typCode, err := dbDeviceTypeToInt(typ)")
+ buf.L(" if err != nil {")
+ buf.L(" return errors.Wrapf(err, \"Device type code for %%s\", typ)")
+ buf.L(" }")
+ buf.L(" stmt = c.stmt(%s)", stmtCodeVar(m.entity, "createDevicesRef"))
+ buf.L(" result, err := stmt.Exec(id, name, typCode)")
+ buf.L(" if err != nil {")
+ buf.L(" return errors.Wrapf(err, \"Insert device %%s\", name)")
+ buf.L(" }")
+ buf.L(" deviceID, err := result.LastInsertId()")
+ buf.L(" if err != nil {")
+ buf.L(" return errors.Wrap(err, \"Failed to fetch device ID\")")
+ buf.L(" }")
+ buf.L(" stmt = c.stmt(%s)", stmtCodeVar(m.entity, "createDevicesConfigRef"))
+ buf.L(" for key, value := range config {")
+ buf.L(" _, err := stmt.Exec(deviceID, key, value)")
+ buf.L(" if err != nil {")
+ buf.L(" return errors.Wrap(err, \"Insert config for %s\")", m.entity)
+ buf.L(" }")
+ buf.L(" }")
+ buf.L("}")
+ buf.N()
+ }
+ }
+
buf.L("return nil")
return nil
From 3215afdffe7d8ada49cf5057d8ef2ca6615a466f Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Tue, 19 May 2020 12:49:16 +0100
Subject: [PATCH 7/8] lxd/db: Generate Update method for profiles
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
lxd/db/profiles.go | 2 +
lxd/db/profiles.mapper.go | 81 +++++++++++++++++++++++++++++++++++++++
2 files changed, 83 insertions(+)
diff --git a/lxd/db/profiles.go b/lxd/db/profiles.go
index 8bd37c96b5..b83ad849f8 100644
--- a/lxd/db/profiles.go
+++ b/lxd/db/profiles.go
@@ -39,6 +39,7 @@ import (
//go:generate mapper stmt -p db -e profile delete
//go:generate mapper stmt -p db -e profile delete-config-ref
//go:generate mapper stmt -p db -e profile delete-devices-ref
+//go:generate mapper stmt -p db -e profile update struct=Profile
//
//go:generate mapper method -p db -e profile URIs
//go:generate mapper method -p db -e profile List
@@ -51,6 +52,7 @@ import (
//go:generate mapper method -p db -e profile Create struct=Profile
//go:generate mapper method -p db -e profile Rename
//go:generate mapper method -p db -e profile Delete
+//go:generate mapper method -p db -e profile Update struct=Profile
// Profile is a value object holding db-related details about a profile.
type Profile struct {
diff --git a/lxd/db/profiles.mapper.go b/lxd/db/profiles.mapper.go
index df5c752a63..c9970eb9b4 100644
--- a/lxd/db/profiles.mapper.go
+++ b/lxd/db/profiles.mapper.go
@@ -127,6 +127,12 @@ var profileDeleteDevicesRef = cluster.RegisterStmt(`
DELETE FROM profiles_devices WHERE profile_id = ?
`)
+var profileUpdate = cluster.RegisterStmt(`
+UPDATE profiles
+ SET project_id = (SELECT id FROM projects WHERE name = ?), name = ?, description = ?
+ WHERE id = ?
+`)
+
// GetProfileURIs returns all available profile URIs.
func (c *ClusterTx) GetProfileURIs(filter ProfileFilter) ([]string, error) {
// Check which filter criteria are active.
@@ -715,3 +721,78 @@ func (c *ClusterTx) DeleteProfile(project string, name string) error {
return nil
}
+
+// UpdateProfile updates the profile matching the given key parameters.
+func (c *ClusterTx) UpdateProfile(project string, name string, object Profile) error {
+ id, err := c.GetProfileID(project, name)
+ if err != nil {
+ return errors.Wrap(err, "Get profile")
+ }
+
+ stmt := c.stmt(profileUpdate)
+ result, err := stmt.Exec(object.Project, object.Name, object.Description, id)
+ if err != nil {
+ return errors.Wrap(err, "Update profile")
+ }
+
+ n, err := result.RowsAffected()
+ if err != nil {
+ return errors.Wrap(err, "Fetch affected rows")
+ }
+ if n != 1 {
+ return fmt.Errorf("Query updated %d rows instead of 1", n)
+ }
+
+ // Delete current config.
+ stmt = c.stmt(profileDeleteConfigRef)
+ _, err = stmt.Exec(id)
+ if err != nil {
+ return errors.Wrap(err, "Delete current config")
+ }
+
+ // Insert config reference.
+ stmt = c.stmt(profileCreateConfigRef)
+ for key, value := range object.Config {
+ _, err := stmt.Exec(id, key, value)
+ if err != nil {
+ return errors.Wrap(err, "Insert config for profile")
+ }
+ }
+
+ // Delete current devices.
+ stmt = c.stmt(profileDeleteDevicesRef)
+ _, err = stmt.Exec(id)
+ if err != nil {
+ return errors.Wrap(err, "Delete current devices")
+ }
+
+ // Insert devices reference.
+ for name, config := range object.Devices {
+ typ, ok := config["type"]
+ if !ok {
+ return fmt.Errorf("No type for device %s", name)
+ }
+ typCode, err := dbDeviceTypeToInt(typ)
+ if err != nil {
+ return errors.Wrapf(err, "Device type code for %s", typ)
+ }
+ stmt = c.stmt(profileCreateDevicesRef)
+ result, err := stmt.Exec(id, name, typCode)
+ if err != nil {
+ return errors.Wrapf(err, "Insert device %s", name)
+ }
+ deviceID, err := result.LastInsertId()
+ if err != nil {
+ return errors.Wrap(err, "Failed to fetch device ID")
+ }
+ stmt = c.stmt(profileCreateDevicesConfigRef)
+ for key, value := range config {
+ _, err := stmt.Exec(deviceID, key, value)
+ if err != nil {
+ return errors.Wrap(err, "Insert config for profile")
+ }
+ }
+ }
+
+ return nil
+}
From 94b74aa66d2010f5657621b5611566d071f159a4 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Tue, 19 May 2020 13:04:08 +0100
Subject: [PATCH 8/8] lxd: Plug new UpdateProfile() db method into
doProfileUpdate
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
lxd/profiles_utils.go | 57 ++++++-------------------------------------
1 file changed, 8 insertions(+), 49 deletions(-)
diff --git a/lxd/profiles_utils.go b/lxd/profiles_utils.go
index 4728c55601..672d584d10 100644
--- a/lxd/profiles_utils.go
+++ b/lxd/profiles_utils.go
@@ -2,10 +2,8 @@ package main
import (
"fmt"
- "reflect"
"github.com/lxc/lxd/lxd/db"
- "github.com/lxc/lxd/lxd/db/query"
deviceConfig "github.com/lxc/lxd/lxd/device/config"
"github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/lxd/instance/instancetype"
@@ -78,53 +76,14 @@ func doProfileUpdate(d *Daemon, project, name string, id int64, profile *api.Pro
}
// Update the database
- err = query.Retry(func() error {
- tx, err := d.cluster.Begin()
- if err != nil {
- return err
- }
-
- if profile.Description != req.Description {
- err = db.UpdateProfileDescription(tx, id, req.Description)
- if err != nil {
- tx.Rollback()
- return err
- }
- }
-
- // Optimize for description-only changes
- if reflect.DeepEqual(profile.Config, req.Config) && reflect.DeepEqual(profile.Devices, req.Devices) {
- err = db.TxCommit(tx)
- if err != nil {
- return err
- }
-
- return nil
- }
-
- err = db.ClearProfileConfig(tx, id)
- if err != nil {
- tx.Rollback()
- return err
- }
-
- err = db.CreateProfileConfig(tx, id, req.Config)
- if err != nil {
- tx.Rollback()
- return err
- }
-
- err = db.AddDevicesToEntity(tx, "profile", id, deviceConfig.NewDevices(req.Devices))
- if err != nil {
- tx.Rollback()
- return err
- }
-
- err = db.TxCommit(tx)
- if err != nil {
- return err
- }
- return nil
+ err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
+ return tx.UpdateProfile(project, name, db.Profile{
+ Project: project,
+ Name: name,
+ Description: req.Description,
+ Config: req.Config,
+ Devices: req.Devices,
+ })
})
if err != nil {
return err
More information about the lxc-devel
mailing list