[lxc-devel] [lxd/master] Database changes for multi arch cluster

freeekanayaka on Github lxc-bot at linuxcontainers.org
Wed Nov 27 11:08:41 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 427 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191127/b4fc66e6/attachment.bin>
-------------- next part --------------
From b7a1d3f1d7c50e591adac4a81be1be2121533d5a Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Wed, 27 Nov 2019 11:02:00 +0000
Subject: [PATCH 1/2] Add arch column to nodes table

Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
 lxd/db/cluster/open.go         |  9 +++++++--
 lxd/db/cluster/open_test.go    |  5 +++--
 lxd/db/cluster/schema.go       |  3 ++-
 lxd/db/cluster/update.go       | 27 +++++++++++++++++++++++++
 lxd/db/cluster/update_test.go  | 37 ++++++++++++++++++++++++++++++++++
 shared/osarch/architectures.go | 13 ++++++++++++
 6 files changed, 89 insertions(+), 5 deletions(-)

diff --git a/lxd/db/cluster/open.go b/lxd/db/cluster/open.go
index cffb2b50c6..0ce55f6a0b 100644
--- a/lxd/db/cluster/open.go
+++ b/lxd/db/cluster/open.go
@@ -12,6 +12,7 @@ import (
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/logger"
+	"github.com/lxc/lxd/shared/osarch"
 	"github.com/lxc/lxd/shared/version"
 	"github.com/pkg/errors"
 )
@@ -165,11 +166,15 @@ func EnsureSchema(db *sql.DB, address string, dir string) (bool, error) {
 	// 1. This is needed for referential integrity with other tables. Also,
 	// create a default profile.
 	if initial == 0 {
+		arch, err := osarch.ArchitectureGetLocalID()
+		if err != nil {
+			return false, err
+		}
 		err = query.Transaction(db, func(tx *sql.Tx) error {
 			stmt := `
-INSERT INTO nodes(id, name, address, schema, api_extensions) VALUES(1, 'none', '0.0.0.0', ?, ?)
+INSERT INTO nodes(id, name, address, schema, api_extensions, arch) VALUES(1, 'none', '0.0.0.0', ?, ?, ?)
 `
-			_, err = tx.Exec(stmt, SchemaVersion, apiExtensions)
+			_, err = tx.Exec(stmt, SchemaVersion, apiExtensions, arch)
 			if err != nil {
 				return err
 			}
diff --git a/lxd/db/cluster/open_test.go b/lxd/db/cluster/open_test.go
index bdf5505bc4..19c0b98ae5 100644
--- a/lxd/db/cluster/open_test.go
+++ b/lxd/db/cluster/open_test.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/lxc/lxd/lxd/db/cluster"
 	"github.com/lxc/lxd/lxd/db/query"
+	"github.com/lxc/lxd/shared/osarch"
 	"github.com/lxc/lxd/shared/version"
 	"github.com/mpvl/subtest"
 	"github.com/stretchr/testify/assert"
@@ -165,10 +166,10 @@ CREATE TABLE schema (
 func addNode(t *testing.T, db *sql.DB, address string, schema int, apiExtensions int) {
 	err := query.Transaction(db, func(tx *sql.Tx) error {
 		stmt := `
-INSERT INTO nodes(name, address, schema, api_extensions) VALUES (?, ?, ?, ?)
+INSERT INTO nodes(name, address, schema, api_extensions, arch) VALUES (?, ?, ?, ?, ?)
 `
 		name := fmt.Sprintf("node at %s", address)
-		_, err := tx.Exec(stmt, name, address, schema, apiExtensions)
+		_, err := tx.Exec(stmt, name, address, schema, apiExtensions, osarch.ARCH_64BIT_INTEL_X86)
 		return err
 	})
 	require.NoError(t, err)
diff --git a/lxd/db/cluster/schema.go b/lxd/db/cluster/schema.go
index 03306ab770..fbe9bcabfd 100644
--- a/lxd/db/cluster/schema.go
+++ b/lxd/db/cluster/schema.go
@@ -303,6 +303,7 @@ CREATE TABLE nodes (
     api_extensions INTEGER NOT NULL,
     heartbeat DATETIME DEFAULT CURRENT_TIMESTAMP,
     pending INTEGER NOT NULL DEFAULT 0,
+    arch INTEGER NOT NULL DEFAULT 0 CHECK (arch >= 1 AND arch <= 8),
     UNIQUE (name),
     UNIQUE (address)
 );
@@ -487,5 +488,5 @@ CREATE TABLE storage_volumes_config (
     FOREIGN KEY (storage_volume_id) REFERENCES storage_volumes (id) ON DELETE CASCADE
 );
 
-INSERT INTO schema (version, updated_at) VALUES (19, strftime("%s"))
+INSERT INTO schema (version, updated_at) VALUES (20, strftime("%s"))
 `
diff --git a/lxd/db/cluster/update.go b/lxd/db/cluster/update.go
index c2f285cb2f..62e3e6b7b8 100644
--- a/lxd/db/cluster/update.go
+++ b/lxd/db/cluster/update.go
@@ -10,6 +10,7 @@ import (
 	"github.com/lxc/lxd/lxd/db/query"
 	"github.com/lxc/lxd/lxd/db/schema"
 	"github.com/lxc/lxd/shared"
+	"github.com/lxc/lxd/shared/osarch"
 	"github.com/pkg/errors"
 )
 
@@ -54,6 +55,32 @@ var updates = map[int]schema.Update{
 	17: updateFromV16,
 	18: updateFromV17,
 	19: updateFromV18,
+	20: updateFromV19,
+}
+
+// Add a new "arch" column to the "nodes" table.
+func updateFromV19(tx *sql.Tx) error {
+	// The column has a not-null constraint and a default value of
+	// 0. However, leaving the 0 default won't effectively be accepted when
+	// creating a new, due to the check constraint, so we are sure to end
+	// up with a valid value.
+	stmt := fmt.Sprintf(`
+ALTER TABLE nodes ADD COLUMN arch INTEGER NOT NULL DEFAULT 0 CHECK (arch >= %d AND arch <= %d)
+`,
+		osarch.ARCH_32BIT_INTEL_X86, osarch.ARCH_64BIT_S390_BIG_ENDIAN)
+	_, err := tx.Exec(stmt)
+	if err != nil {
+		return err
+	}
+	arch, err := osarch.ArchitectureGetLocalID()
+	if err != nil {
+		return err
+	}
+	_, err = tx.Exec("UPDATE nodes SET arch = ?", arch)
+	if err != nil {
+		return err
+	}
+	return nil
 }
 
 // Rename 'containers' to 'instances' in *_used_by_ref views.
diff --git a/lxd/db/cluster/update_test.go b/lxd/db/cluster/update_test.go
index c620f2c56a..abc3bfac58 100644
--- a/lxd/db/cluster/update_test.go
+++ b/lxd/db/cluster/update_test.go
@@ -8,6 +8,8 @@ import (
 
 	"github.com/lxc/lxd/lxd/db/cluster"
 	"github.com/lxc/lxd/lxd/db/query"
+	"github.com/lxc/lxd/shared/osarch"
+	"github.com/mattn/go-sqlite3"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
@@ -583,3 +585,38 @@ INSERT INTO instances VALUES (2, 1, 'eoan/snap', 2, 1, 0, ?, 0, ?, 'Eoan Ermine
 	require.NoError(t, err)
 	assert.Equal(t, config, map[string]string{"k": "v"})
 }
+
+func TestUpdateFromV19(t *testing.T) {
+	schema := cluster.Schema()
+	db, err := schema.ExerciseUpdate(20, func(db *sql.DB) {
+		// Insert a node.
+		_, err := db.Exec(
+			"INSERT INTO nodes VALUES (1, 'n1', '', '1.2.3.4:666', 1, 32, ?, 0)",
+			time.Now())
+		require.NoError(t, err)
+	})
+	require.NoError(t, err)
+	defer db.Close()
+
+	expectedArch, err := osarch.ArchitectureGetLocalID()
+	require.NoError(t, err)
+
+	row := db.QueryRow("SELECT arch FROM nodes")
+	arch := 0
+	err = row.Scan(&arch)
+	require.NoError(t, err)
+
+	assert.Equal(t, expectedArch, arch)
+
+	// Trying to create a row without specififying the architecture results
+	// in an error.
+	_, err = db.Exec(`
+INSERT INTO nodes(id, name, description, address, schema, api_extensions, heartbeat, pending)
+VALUES (2, 'n2', '', '2.2.3.4:666', 1, 32, ?, 0)`, time.Now())
+	if err == nil {
+		t.Fatal("expected insertion to fail")
+	}
+	sqliteErr, ok := err.(sqlite3.Error)
+	require.True(t, ok)
+	assert.Equal(t, sqliteErr.Code, sqlite3.ErrConstraint)
+}
diff --git a/shared/osarch/architectures.go b/shared/osarch/architectures.go
index 2956acfb0a..84d9f68568 100644
--- a/shared/osarch/architectures.go
+++ b/shared/osarch/architectures.go
@@ -105,3 +105,16 @@ func ArchitecturePersonalities(arch int) ([]int, error) {
 
 	return []int{}, fmt.Errorf("Architecture isn't supported: %d", arch)
 }
+
+// ArchitectureGetLocalID returns the local hardware architecture ID
+func ArchitectureGetLocalID() (int, error) {
+	name, err := ArchitectureGetLocal()
+	if err != nil {
+		return -1, err
+	}
+	id, err := ArchitectureId(name)
+	if err != nil {
+		return -1, err
+	}
+	return id, nil
+}

From 94b9c5f5623a22f446d72e78cc3ad0eb2d6eeec6 Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Wed, 27 Nov 2019 11:03:18 +0000
Subject: [PATCH 2/2] Add NodeAddWithArch() method to add a node with a
 specific arch

Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
 lxd/cluster/membership.go | 10 +++++++++-
 lxd/db/node.go            | 18 +++++++++++++++---
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/lxd/cluster/membership.go b/lxd/cluster/membership.go
index fe582040db..b572400553 100644
--- a/lxd/cluster/membership.go
+++ b/lxd/cluster/membership.go
@@ -18,6 +18,7 @@ import (
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/log15"
 	"github.com/lxc/lxd/shared/logger"
+	"github.com/lxc/lxd/shared/osarch"
 	"github.com/lxc/lxd/shared/version"
 	"github.com/pkg/errors"
 )
@@ -172,8 +173,15 @@ func Accept(state *state.State, gateway *Gateway, name, address string, schema,
 			return err
 		}
 
+		// TODO: when fixing #6380 this should be replaced with the
+		// actual architecture of the foreign node.
+		arch, err := osarch.ArchitectureGetLocalID()
+		if err != nil {
+			return err
+		}
+
 		// Add the new node
-		id, err := tx.NodeAdd(name, address)
+		id, err := tx.NodeAddWithArch(name, address, arch)
 		if err != nil {
 			return errors.Wrap(err, "Failed to insert new node into the database")
 		}
diff --git a/lxd/db/node.go b/lxd/db/node.go
index 7da9782955..b38d48d920 100644
--- a/lxd/db/node.go
+++ b/lxd/db/node.go
@@ -11,6 +11,7 @@ import (
 	"github.com/lxc/lxd/lxd/db/cluster"
 	"github.com/lxc/lxd/lxd/db/query"
 	"github.com/lxc/lxd/lxd/util"
+	"github.com/lxc/lxd/shared/osarch"
 	"github.com/lxc/lxd/shared/version"
 	"github.com/pkg/errors"
 )
@@ -307,10 +308,21 @@ func (c *ClusterTx) nodes(pending bool, where string, args ...interface{}) ([]No
 }
 
 // NodeAdd adds a node to the current list of LXD nodes that are part of the
-// cluster. It returns the ID of the newly inserted row.
+// cluster. The node's architecture will be the architecture of the machine the
+// method is being run on. It returns the ID of the newly inserted row.
 func (c *ClusterTx) NodeAdd(name string, address string) (int64, error) {
-	columns := []string{"name", "address", "schema", "api_extensions"}
-	values := []interface{}{name, address, cluster.SchemaVersion, version.APIExtensionsCount()}
+	arch, err := osarch.ArchitectureGetLocalID()
+	if err != nil {
+		return -1, err
+	}
+	return c.NodeAddWithArch(name, address, arch)
+}
+
+// NodeAddWithArch is the same as NodeAdd, but lets setting the node
+// architecture explicitely.
+func (c *ClusterTx) NodeAddWithArch(name string, address string, arch int) (int64, error) {
+	columns := []string{"name", "address", "schema", "api_extensions", "arch"}
+	values := []interface{}{name, address, cluster.SchemaVersion, version.APIExtensionsCount(), arch}
 	return query.UpsertObject(c.tx, "nodes", columns, values)
 }
 


More information about the lxc-devel mailing list