[lxc-devel] [lxd/master] Images: Handle downloading images from other cluster nodes across projects

tomponline on Github lxc-bot at linuxcontainers.org
Thu Sep 17 15:42:30 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 669 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200917/411fcfb5/attachment.bin>
-------------- next part --------------
From 1fbd3a7e1be6ec91ed720ddbb61bfbb751d08c39 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 17 Sep 2020 16:37:19 +0100
Subject: [PATCH 1/4] lxd/instance: Removes image download from cluster node
 from instanceCreateFromImage

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance.go | 28 ----------------------------
 1 file changed, 28 deletions(-)

diff --git a/lxd/instance.go b/lxd/instance.go
index e865de0068..e1457534d8 100644
--- a/lxd/instance.go
+++ b/lxd/instance.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
-	"path/filepath"
 	"strconv"
 	"strings"
 	"time"
@@ -15,7 +14,6 @@ import (
 	cron "gopkg.in/robfig/cron.v2"
 
 	"github.com/flosch/pongo2"
-	"github.com/lxc/lxd/lxd/cluster"
 	"github.com/lxc/lxd/lxd/db"
 	deviceConfig "github.com/lxc/lxd/lxd/device/config"
 	"github.com/lxc/lxd/lxd/instance"
@@ -96,32 +94,6 @@ func instanceCreateFromImage(d *Daemon, args db.InstanceArgs, hash string, op *o
 		return nil, fmt.Errorf("Requested image's type '%s' doesn't match instance type '%s'", imgType, args.Type)
 	}
 
-	// Check if the image is available locally or it's on another node.
-	nodeAddress, err := s.Cluster.LocateImage(hash)
-	if err != nil {
-		return nil, errors.Wrapf(err, "Locate image %s in the cluster", hash)
-	}
-	if nodeAddress != "" {
-		// The image is available from another node, let's try to import it.
-		logger.Debugf("Transferring image %s from node %s", hash, nodeAddress)
-		client, err := cluster.Connect(nodeAddress, d.endpoints.NetworkCert(), false)
-		if err != nil {
-			return nil, err
-		}
-
-		client = client.UseProject(args.Project)
-
-		err = imageImportFromNode(filepath.Join(d.os.VarDir, "images"), client, hash)
-		if err != nil {
-			return nil, err
-		}
-
-		err = d.cluster.AddImageToLocalNode(args.Project, hash)
-		if err != nil {
-			return nil, err
-		}
-	}
-
 	// Set the "image.*" keys.
 	if img.Properties != nil {
 		for k, v := range img.Properties {

From fb8a2e6980646e01182f9dd032a5e2238e0cd01a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 17 Sep 2020 16:37:35 +0100
Subject: [PATCH 2/4] lxd/db/images: Error message uppercase first letter

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/db/images.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/db/images.go b/lxd/db/images.go
index 37f676d151..96346a26ae 100644
--- a/lxd/db/images.go
+++ b/lxd/db/images.go
@@ -610,7 +610,7 @@ WHERE images.fingerprint = ?
 		return "", err
 	}
 	if len(addresses) == 0 {
-		return "", fmt.Errorf("image not available on any online node")
+		return "", fmt.Errorf("Image not available on any online node")
 	}
 
 	for _, address := range addresses {

From a3345b5d651cb7f9a7abab9e4a959242f4996a80 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 17 Sep 2020 16:38:08 +0100
Subject: [PATCH 3/4] lxd/daemon/images: Error quoting

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/daemon_images.go | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lxd/daemon_images.go b/lxd/daemon_images.go
index 5d71a690d8..a7ead3c82c 100644
--- a/lxd/daemon_images.go
+++ b/lxd/daemon_images.go
@@ -155,20 +155,20 @@ func (d *Daemon) ImageDownload(op *operations.Operation, server string, protocol
 		}
 
 		if shared.Int64InSlice(poolID, poolIDs) {
-			logger.Debugf("Image already exists on storage pool \"%s\"", storagePool)
+			logger.Debugf("Image already exists on storage pool %q", storagePool)
 			return info, nil
 		}
 
 		// Import the image in the pool
-		logger.Debugf("Image does not exist on storage pool \"%s\"", storagePool)
+		logger.Debugf("Image does not exist on storage pool %q", storagePool)
 
 		err = imageCreateInPool(d, info, storagePool)
 		if err != nil {
-			logger.Debugf("Failed to create image on storage pool \"%s\": %s", storagePool, err)
+			logger.Debugf("Failed to create image on storage pool %q: %v", storagePool, err)
 			return nil, err
 		}
 
-		logger.Debugf("Created image on storage pool \"%s\"", storagePool)
+		logger.Debugf("Created image on storage pool %q", storagePool)
 		return info, nil
 	}
 
@@ -368,7 +368,7 @@ func (d *Daemon) ImageDownload(op *operations.Operation, server string, protocol
 		}
 
 		if raw.StatusCode != http.StatusOK {
-			return nil, fmt.Errorf("Unable to fetch %s: %s", server, raw.Status)
+			return nil, fmt.Errorf("Unable to fetch %q: %s", server, raw.Status)
 		}
 
 		// Progress handler
@@ -402,7 +402,7 @@ func (d *Daemon) ImageDownload(op *operations.Operation, server string, protocol
 		// Validate hash
 		result := fmt.Sprintf("%x", sha256.Sum(nil))
 		if result != fp {
-			return nil, fmt.Errorf("Hash mismatch for %s: %s != %s", server, result, fp)
+			return nil, fmt.Errorf("Hash mismatch for %q: %s != %s", server, result, fp)
 		}
 
 		// Parse the image

From c003d0140e9d2501b9e8f666afb8394dde139c2b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 17 Sep 2020 16:38:47 +0100
Subject: [PATCH 4/4] lxd/daemon/image: Moves logic to download image from
 another cluster node into ImageDownload

It seems more appropriate to be here, and also allows us to check earlier whether the image is available on another node, before we create the DB entry for our own node (which skews the subsequent locate results).

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/daemon_images.go | 45 ++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 41 insertions(+), 4 deletions(-)

diff --git a/lxd/daemon_images.go b/lxd/daemon_images.go
index a7ead3c82c..6cc97faf68 100644
--- a/lxd/daemon_images.go
+++ b/lxd/daemon_images.go
@@ -10,6 +10,8 @@ import (
 	"sync"
 	"time"
 
+	"github.com/pkg/errors"
+
 	"github.com/lxc/lxd/client"
 	"github.com/lxc/lxd/lxd/cluster"
 	"github.com/lxc/lxd/lxd/db"
@@ -106,12 +108,25 @@ func (d *Daemon) ImageDownload(op *operations.Operation, server string, protocol
 		}
 	}
 
-	// Check if the image already exists in this project (partial hash match)
+	// Check if the image already exists in this project (partial hash match).
+	nodeAddress := ""
 	_, imgInfo, err := d.cluster.GetImage(project, fp, false)
-	if err == db.ErrNoSuchObject {
+	if err == nil {
+		// Check if the image is available locally or it's on another node.
+		nodeAddress, err = d.State().Cluster.LocateImage(imgInfo.Fingerprint)
+		if err != nil {
+			return nil, errors.Wrapf(err, "Locate image %q in the cluster", imgInfo.Fingerprint)
+		}
+	} else if err == db.ErrNoSuchObject {
 		// Check if the image already exists in some other project.
 		_, imgInfo, err = d.cluster.GetImageFromAnyProject(fp)
 		if err == nil {
+			// Check if the image is available locally or it's on another node.
+			nodeAddress, err = d.State().Cluster.LocateImage(imgInfo.Fingerprint)
+			if err != nil {
+				return nil, errors.Wrapf(err, "Locate image %q in the cluster", imgInfo.Fingerprint)
+			}
+
 			// We just need to insert the database data, no actual download necessary.
 			err = d.cluster.CreateImage(
 				project, imgInfo.Fingerprint, imgInfo.Filename, imgInfo.Size, false,
@@ -126,6 +141,7 @@ func (d *Daemon) ImageDownload(op *operations.Operation, server string, protocol
 			if err != nil {
 				return nil, err
 			}
+
 			err = d.cluster.CreateImageSource(id, server, protocol, certificate, alias)
 			if err != nil {
 				return nil, err
@@ -133,10 +149,31 @@ func (d *Daemon) ImageDownload(op *operations.Operation, server string, protocol
 		}
 	}
 
-	if err == nil {
-		logger.Debug("Image already exists in the db", log.Ctx{"image": fp})
+	if imgInfo != nil {
+		logger.Debugf("Image %q already exists in the DB", fp)
 		info = imgInfo
 
+		if nodeAddress != "" {
+			// The image is available from another node, let's try to import it.
+			logger.Debugf("Transferring image %q from node %q", info.Fingerprint, nodeAddress)
+			client, err := cluster.Connect(nodeAddress, d.endpoints.NetworkCert(), false)
+			if err != nil {
+				return nil, err
+			}
+
+			client = client.UseProject(project)
+
+			err = imageImportFromNode(filepath.Join(d.os.VarDir, "images"), client, info.Fingerprint)
+			if err != nil {
+				return nil, errors.Wrapf(err, "Failed importing from node %q", nodeAddress)
+			}
+
+			err = d.cluster.AddImageToLocalNode(project, info.Fingerprint)
+			if err != nil {
+				return nil, errors.Wrapf(err, "Failed adding image to local node")
+			}
+		}
+
 		// If not requested in a particular pool, we're done.
 		if storagePool == "" {
 			return info, nil


More information about the lxc-devel mailing list