[lxc-devel] [lxd/master] Storage migration source cleanup

tomponline on Github lxc-bot at linuxcontainers.org
Tue Dec 3 12:38:40 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 380 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191203/e8af0a09/attachment.bin>
-------------- next part --------------
From e0cfd645988c734a18d022ba3a83734b0c57d9ae Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 3 Dec 2019 10:29:10 +0000
Subject: [PATCH 1/6] lxd/container/post: Returns instances resources from
 containerPost

Removes duplicated instance type container check (this is checked in migrationSourceWs.Do).

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

diff --git a/lxd/container_post.go b/lxd/container_post.go
index b8b6ba8592..9139571dd2 100644
--- a/lxd/container_post.go
+++ b/lxd/container_post.go
@@ -206,18 +206,14 @@ func containerPost(d *Daemon, r *http.Request) response.Response {
 		}
 
 		instanceOnly := req.InstanceOnly || req.ContainerOnly
-
-		if inst.Type() != instancetype.Container {
-			return response.SmartError(fmt.Errorf("Instance is not container type"))
-		}
-
 		ws, err := NewMigrationSource(inst, stateful, instanceOnly)
 		if err != nil {
 			return response.InternalError(err)
 		}
 
 		resources := map[string][]string{}
-		resources["containers"] = []string{name}
+		resources["instances"] = []string{name}
+		resources["containers"] = resources["instances"]
 
 		if req.Target != nil {
 			// Push mode

From e3460a0aafb15f4c8822a8a30fa60150bb0cb90a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 3 Dec 2019 10:33:37 +0000
Subject: [PATCH 2/6] lxd/migrate/container: Removes duplicated instance type
 checks from migrationSourceWs.Do

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

diff --git a/lxd/migrate_container.go b/lxd/migrate_container.go
index 05e021453b..0f019db2ac 100644
--- a/lxd/migrate_container.go
+++ b/lxd/migrate_container.go
@@ -334,6 +334,11 @@ func (s *migrationSourceWs) preDumpLoop(args *preDumpLoopArgs) (bool, error) {
 
 func (s *migrationSourceWs) Do(migrateOp *operations.Operation) error {
 	<-s.allConnected
+	if s.instance.Type() != instancetype.Container {
+		return fmt.Errorf("Instance is not container type")
+	}
+
+	ct := s.instance.(*containerLXC)
 
 	criuType := migration.CRIUType_CRIU_RSYNC.Enum()
 	if !s.live {
@@ -343,12 +348,6 @@ func (s *migrationSourceWs) Do(migrateOp *operations.Operation) error {
 		}
 	}
 
-	if s.instance.Type() != instancetype.Container {
-		return fmt.Errorf("Instance is not container type")
-	}
-
-	c := s.instance.(*containerLXC)
-
 	// Storage needs to start unconditionally now, since we need to
 	// initialize a new storage interface.
 	ourStart, err := s.instance.StorageStart()
@@ -360,8 +359,7 @@ func (s *migrationSourceWs) Do(migrateOp *operations.Operation) error {
 	}
 
 	idmaps := make([]*migration.IDMapType, 0)
-
-	idmapset, err := c.DiskIdmap()
+	idmapset, err := ct.DiskIdmap()
 	if err != nil {
 		return err
 	}
@@ -402,11 +400,6 @@ func (s *migrationSourceWs) Do(migrateOp *operations.Operation) error {
 
 	// The protocol says we have to send a header no matter what, so let's
 	// do that, but then immediately send an error.
-	if s.instance.Type() != instancetype.Container {
-		return fmt.Errorf("Instance type must be container")
-	}
-
-	ct := s.instance.(*containerLXC)
 
 	myType := ct.Storage().MigrationType()
 	hasFeature := true
@@ -650,7 +643,7 @@ func (s *migrationSourceWs) Do(migrateOp *operations.Operation) error {
 
 				// Do the final CRIU dump. This is needs no special
 				// handling if pre-dumps are used or not
-				dumpSuccess <- c.Migrate(&criuMigrationArgs)
+				dumpSuccess <- ct.Migrate(&criuMigrationArgs)
 				os.RemoveAll(checkpointDir)
 			}()
 
@@ -675,7 +668,7 @@ func (s *migrationSourceWs) Do(migrateOp *operations.Operation) error {
 				preDumpDir:   "",
 			}
 
-			err = c.Migrate(&criuMigrationArgs)
+			err = ct.Migrate(&criuMigrationArgs)
 			if err != nil {
 				return abort(err)
 			}

From 4ef6ac03cd7a28989b228769efff686d5ace014b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 3 Dec 2019 10:47:14 +0000
Subject: [PATCH 3/6] lxd: Removes dependency on instance.DaemonState() from
 migrationSourceWs

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/container_post.go     | 8 ++++++--
 lxd/container_snapshot.go | 8 ++++++--
 lxd/migrate_container.go  | 3 +--
 3 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/lxd/container_post.go b/lxd/container_post.go
index 9139571dd2..a1b82db3a1 100644
--- a/lxd/container_post.go
+++ b/lxd/container_post.go
@@ -215,6 +215,10 @@ func containerPost(d *Daemon, r *http.Request) response.Response {
 		resources["instances"] = []string{name}
 		resources["containers"] = resources["instances"]
 
+		run := func(op *operations.Operation) error {
+			return ws.Do(d.State(), op)
+		}
+
 		if req.Target != nil {
 			// Push mode
 			err := ws.ConnectContainerTarget(*req.Target)
@@ -222,7 +226,7 @@ func containerPost(d *Daemon, r *http.Request) response.Response {
 				return response.InternalError(err)
 			}
 
-			op, err := operations.OperationCreate(d.State(), project, operations.OperationClassTask, db.OperationContainerMigrate, resources, nil, ws.Do, nil, nil)
+			op, err := operations.OperationCreate(d.State(), project, operations.OperationClassTask, db.OperationContainerMigrate, resources, nil, run, nil, nil)
 			if err != nil {
 				return response.InternalError(err)
 			}
@@ -231,7 +235,7 @@ func containerPost(d *Daemon, r *http.Request) response.Response {
 		}
 
 		// Pull mode
-		op, err := operations.OperationCreate(d.State(), project, operations.OperationClassWebsocket, db.OperationContainerMigrate, resources, ws.Metadata(), ws.Do, nil, ws.Connect)
+		op, err := operations.OperationCreate(d.State(), project, operations.OperationClassWebsocket, db.OperationContainerMigrate, resources, ws.Metadata(), run, nil, ws.Connect)
 		if err != nil {
 			return response.InternalError(err)
 		}
diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go
index 338750d2d9..44daff38aa 100644
--- a/lxd/container_snapshot.go
+++ b/lxd/container_snapshot.go
@@ -369,6 +369,10 @@ func snapshotPost(d *Daemon, r *http.Request, sc instance.Instance, containerNam
 		resources := map[string][]string{}
 		resources["containers"] = []string{containerName}
 
+		run := func(op *operations.Operation) error {
+			return ws.Do(d.State(), op)
+		}
+
 		if req.Target != nil {
 			// Push mode
 			err := ws.ConnectContainerTarget(*req.Target)
@@ -376,7 +380,7 @@ func snapshotPost(d *Daemon, r *http.Request, sc instance.Instance, containerNam
 				return response.InternalError(err)
 			}
 
-			op, err := operations.OperationCreate(d.State(), sc.Project(), operations.OperationClassTask, db.OperationSnapshotTransfer, resources, nil, ws.Do, nil, nil)
+			op, err := operations.OperationCreate(d.State(), sc.Project(), operations.OperationClassTask, db.OperationSnapshotTransfer, resources, nil, run, nil, nil)
 			if err != nil {
 				return response.InternalError(err)
 			}
@@ -385,7 +389,7 @@ func snapshotPost(d *Daemon, r *http.Request, sc instance.Instance, containerNam
 		}
 
 		// Pull mode
-		op, err := operations.OperationCreate(d.State(), sc.Project(), operations.OperationClassWebsocket, db.OperationSnapshotTransfer, resources, ws.Metadata(), ws.Do, nil, ws.Connect)
+		op, err := operations.OperationCreate(d.State(), sc.Project(), operations.OperationClassWebsocket, db.OperationSnapshotTransfer, resources, ws.Metadata(), run, nil, ws.Connect)
 		if err != nil {
 			return response.InternalError(err)
 		}
diff --git a/lxd/migrate_container.go b/lxd/migrate_container.go
index 0f019db2ac..6bff998ff4 100644
--- a/lxd/migrate_container.go
+++ b/lxd/migrate_container.go
@@ -332,7 +332,7 @@ func (s *migrationSourceWs) preDumpLoop(args *preDumpLoopArgs) (bool, error) {
 	return final, nil
 }
 
-func (s *migrationSourceWs) Do(migrateOp *operations.Operation) error {
+func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operation) error {
 	<-s.allConnected
 	if s.instance.Type() != instancetype.Container {
 		return fmt.Errorf("Instance is not container type")
@@ -546,7 +546,6 @@ func (s *migrationSourceWs) Do(migrateOp *operations.Operation) error {
 				return abort(err)
 			}
 
-			state := s.instance.DaemonState()
 			actionScriptOp, err := operations.OperationCreate(
 				state,
 				s.instance.Project(),

From df217102772f1aeb41db5d4a6a6d369d19ad999c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 3 Dec 2019 10:49:27 +0000
Subject: [PATCH 4/6] lxd/storage: Removes DaemonState() from pool interface

This same function is deprecated in containerLXC with a comment:

"This function should go away, since the abstract container interface should not be coupled with internal state details."

Which seems a valid sentiment for the storage interface too.

At the very least it shouldnt be an exported function, but as it happens its not used anyway so removing it entirely.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/backend_lxd.go    | 4 ----
 lxd/storage/backend_mock.go   | 4 ----
 lxd/storage/pool_interface.go | 4 ----
 3 files changed, 12 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 511f0d9a96..0a548b9142 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -34,10 +34,6 @@ type lxdBackend struct {
 	logger logger.Logger
 }
 
-func (b *lxdBackend) DaemonState() *state.State {
-	return b.state
-}
-
 // ID returns the storage pool ID.
 func (b *lxdBackend) ID() int64 {
 	return b.id
diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index 089ff98676..b40cf40f20 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -19,10 +19,6 @@ type mockBackend struct {
 	logger logger.Logger
 }
 
-func (b *mockBackend) DaemonState() *state.State {
-	return b.state
-}
-
 func (b *mockBackend) ID() int64 {
 	return -1
 }
diff --git a/lxd/storage/pool_interface.go b/lxd/storage/pool_interface.go
index bd7462345f..802e4858bc 100644
--- a/lxd/storage/pool_interface.go
+++ b/lxd/storage/pool_interface.go
@@ -7,16 +7,12 @@ import (
 	"github.com/lxc/lxd/lxd/instance"
 	"github.com/lxc/lxd/lxd/migration"
 	"github.com/lxc/lxd/lxd/operations"
-	"github.com/lxc/lxd/lxd/state"
 	"github.com/lxc/lxd/lxd/storage/drivers"
 	"github.com/lxc/lxd/shared/api"
 )
 
 // Pool represents a LXD storage pool.
 type Pool interface {
-	// Internal.
-	DaemonState() *state.State
-
 	// Pool.
 	ID() int64
 	Name() string

From 039d0c2c8df6425f9975a60844b5e205f3d4e27c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 3 Dec 2019 12:25:28 +0000
Subject: [PATCH 5/6] lxd/migrate/storage/volumes: Removes unrelated comment

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

diff --git a/lxd/migrate_storage_volumes.go b/lxd/migrate_storage_volumes.go
index f4a073fd92..5b2918bef0 100644
--- a/lxd/migrate_storage_volumes.go
+++ b/lxd/migrate_storage_volumes.go
@@ -122,8 +122,7 @@ func (s *migrationSourceWs) DoStorage(state *state.State, poolName string, volNa
 	offerHeader.SnapshotNames = snapshotNames
 	offerHeader.Snapshots = snapshots
 
-	// The protocol says we have to send a header no matter what, so let's
-	// do that, but then immediately send an error.
+	// Send offer to target.
 	err = s.send(&offerHeader)
 	if err != nil {
 		logger.Errorf("Failed to send storage volume migration header")

From 9a4de2d214c16012e749d0de276f180ab0faf99f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 3 Dec 2019 12:33:29 +0000
Subject: [PATCH 6/6] lxd/migrate/container: Restructures
 migrationSourceWs.Do() ready for new storage layer.

- Adds placeholder variable for new storage pool so that logic can be grouped into old/new sections by detecting if it is nil.
- Cleans up comment format.
- Replaces shared header variable with offerHeader and respHeader to clearly differentiate between each side during negotiation.
- Make golint happy.

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

diff --git a/lxd/migrate_container.go b/lxd/migrate_container.go
index 6bff998ff4..9c8f5787bf 100644
--- a/lxd/migrate_container.go
+++ b/lxd/migrate_container.go
@@ -340,6 +340,42 @@ func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operati
 
 	ct := s.instance.(*containerLXC)
 
+	var offerHeader migration.MigrationHeader
+
+	var pool storagePools.Pool // Placeholder for new storage pool.
+	if s.instance.Type() == instancetype.Container {
+		// Storage needs to start unconditionally now, since we need to initialize a new
+		// storage interface.
+		ourStart, err := s.instance.StorageStart()
+		if err != nil {
+			return err
+		}
+		if ourStart {
+			defer s.instance.StorageStop()
+		}
+
+		myType := ct.Storage().MigrationType()
+		hasFeature := true
+		offerHeader = migration.MigrationHeader{
+			Fs: &myType,
+			RsyncFeatures: &migration.RsyncFeatures{
+				Xattrs:        &hasFeature,
+				Delete:        &hasFeature,
+				Compress:      &hasFeature,
+				Bidirectional: &hasFeature,
+			},
+		}
+
+		if len(zfsVersion) >= 3 && zfsVersion[0:3] != "0.6" {
+			offerHeader.ZfsFeatures = &migration.ZfsFeatures{
+				Compress: &hasFeature,
+			}
+		}
+	} else {
+		return fmt.Errorf("Instance type not supported")
+	}
+
+	// Add CRIO info to source header.
 	criuType := migration.CRIUType_CRIU_RSYNC.Enum()
 	if !s.live {
 		criuType = nil
@@ -347,24 +383,14 @@ func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operati
 			criuType = migration.CRIUType_NONE.Enum()
 		}
 	}
+	offerHeader.Criu = criuType
 
-	// Storage needs to start unconditionally now, since we need to
-	// initialize a new storage interface.
-	ourStart, err := s.instance.StorageStart()
-	if err != nil {
-		return err
-	}
-	if ourStart {
-		defer s.instance.StorageStop()
-	}
-
+	// Add idmap info to source header.
 	idmaps := make([]*migration.IDMapType, 0)
 	idmapset, err := ct.DiskIdmap()
 	if err != nil {
 		return err
-	}
-
-	if idmapset != nil {
+	} else if idmapset != nil {
 		for _, ctnIdmap := range idmapset.Idmap {
 			idmap := migration.IDMapType{
 				Isuid:    proto.Bool(ctnIdmap.Isuid),
@@ -378,9 +404,11 @@ func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operati
 		}
 	}
 
+	offerHeader.Idmap = idmaps
+
+	// Add snapshot info to source header if needed.
 	snapshots := []*migration.Snapshot{}
 	snapshotNames := []string{}
-	// Only send snapshots when requested.
 	if !s.instanceOnly {
 		fullSnaps, err := s.instance.Snapshots()
 		if err == nil {
@@ -392,128 +420,117 @@ func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operati
 		}
 	}
 
-	use_pre_dumps := false
-	max_iterations := 0
+	offerHeader.SnapshotNames = snapshotNames
+	offerHeader.Snapshots = snapshots
+
+	// Add predump info to source header.
+	usePreDumps := false
+	maxDumpIterations := 0
 	if s.live {
-		use_pre_dumps, max_iterations = s.checkForPreDumpSupport()
-	}
-
-	// The protocol says we have to send a header no matter what, so let's
-	// do that, but then immediately send an error.
-
-	myType := ct.Storage().MigrationType()
-	hasFeature := true
-	header := migration.MigrationHeader{
-		Fs:            &myType,
-		Criu:          criuType,
-		Idmap:         idmaps,
-		SnapshotNames: snapshotNames,
-		Snapshots:     snapshots,
-		Predump:       proto.Bool(use_pre_dumps),
-		RsyncFeatures: &migration.RsyncFeatures{
-			Xattrs:        &hasFeature,
-			Delete:        &hasFeature,
-			Compress:      &hasFeature,
-			Bidirectional: &hasFeature,
-		},
-	}
-
-	if len(zfsVersion) >= 3 && zfsVersion[0:3] != "0.6" {
-		header.ZfsFeatures = &migration.ZfsFeatures{
-			Compress: &hasFeature,
-		}
+		usePreDumps, maxDumpIterations = s.checkForPreDumpSupport()
 	}
 
-	err = s.send(&header)
+	offerHeader.Predump = proto.Bool(usePreDumps)
+
+	// Send offer to target.
+	err = s.send(&offerHeader)
 	if err != nil {
 		s.sendControl(err)
 		return err
 	}
 
-	err = s.recv(&header)
+	// Receive response from target.
+	var respHeader migration.MigrationHeader
+	err = s.recv(&respHeader)
 	if err != nil {
 		s.sendControl(err)
 		return err
 	}
 
-	// Handle rsync options
-	rsyncFeatures := header.GetRsyncFeaturesSlice()
+	var legacyDriver MigrationStorageSourceDriver
+	var abort func(err error) error
+	var bwlimit string
+
+	// Handle rsync options.
+	rsyncFeatures := respHeader.GetRsyncFeaturesSlice()
 	if !shared.StringInSlice("bidirectional", rsyncFeatures) {
-		// If no bi-directional support, assume LXD 3.7 level
-		// NOTE: Do NOT extend this list of arguments
+		// If no bi-directional support, assume LXD 3.7 level.
+		// NOTE: Do NOT extend this list of arguments.
 		rsyncFeatures = []string{"xattrs", "delete", "compress"}
 	}
 
-	// Handle zfs options
-	zfsFeatures := header.GetZfsFeaturesSlice()
+	if pool == nil {
+		// Handle zfs options.
+		zfsFeatures := respHeader.GetZfsFeaturesSlice()
 
-	// Set source args
-	sourceArgs := MigrationSourceArgs{
-		Instance:      s.instance,
-		InstanceOnly:  s.instanceOnly,
-		RsyncFeatures: rsyncFeatures,
-		ZfsFeatures:   zfsFeatures,
-	}
+		// Set source args.
+		sourceArgs := MigrationSourceArgs{
+			Instance:      s.instance,
+			InstanceOnly:  s.instanceOnly,
+			RsyncFeatures: rsyncFeatures,
+			ZfsFeatures:   zfsFeatures,
+		}
 
-	// Initialize storage driver
-	driver, fsErr := ct.Storage().MigrationSource(sourceArgs)
-	if fsErr != nil {
-		s.sendControl(fsErr)
-		return fsErr
-	}
+		// Initialize storage driver.
+		var fsErr error
+		legacyDriver, fsErr = ct.Storage().MigrationSource(sourceArgs)
+		if fsErr != nil {
+			s.sendControl(fsErr)
+			return fsErr
+		}
 
-	bwlimit := ""
-	if header.GetRefresh() || *header.Fs != myType {
-		myType = migration.MigrationFSType_RSYNC
-		header.Fs = &myType
+		if respHeader.GetRefresh() || *offerHeader.Fs != *respHeader.Fs {
+			myType := migration.MigrationFSType_RSYNC
+			respHeader.Fs = &myType
 
-		if header.GetRefresh() {
-			driver, _ = rsyncRefreshSource(header.GetSnapshotNames(), sourceArgs)
-		} else {
-			driver, _ = rsyncMigrationSource(sourceArgs)
+			if respHeader.GetRefresh() {
+				legacyDriver, _ = rsyncRefreshSource(respHeader.GetSnapshotNames(), sourceArgs)
+			} else {
+				legacyDriver, _ = rsyncMigrationSource(sourceArgs)
+			}
+
+			// Check if this storage pool has a rate limit set for rsync.
+			poolwritable := ct.Storage().GetStoragePoolWritable()
+			if poolwritable.Config != nil {
+				bwlimit = poolwritable.Config["rsync.bwlimit"]
+			}
 		}
 
-		// Check if this storage pool has a rate limit set for rsync.
-		poolwritable := ct.Storage().GetStoragePoolWritable()
-		if poolwritable.Config != nil {
-			bwlimit = poolwritable.Config["rsync.bwlimit"]
+		// All failure paths need to do a few things to correctly handle errors before
+		// returning. Unfortunately, handling errors is not well-suited to defer as the code
+		// depends on the status of driver and the error value. The error value is
+		// especially tricky due to the common case of creating a new err variable
+		// (intentional or not) due to scoping and use of ":=".  Capturing err in a closure
+		// for use in defer would be fragile, which defeats the purpose of using defer.
+		// An abort function reduces the odds of mishandling errors without introducing the
+		// fragility of closing on err.
+		abort = func(err error) error {
+			legacyDriver.Cleanup()
+			go s.sendControl(err)
+			return err
+		}
+
+		err = legacyDriver.SendWhileRunning(s.fsConn, migrateOp, bwlimit, s.instanceOnly)
+		if err != nil {
+			return abort(err)
 		}
 	}
 
-	// Check if the other side knows about pre-dumping and
-	// the associated rsync protocol
-	use_pre_dumps = header.GetPredump()
-	if use_pre_dumps {
+	// Check if the other side knows about pre-dumping and the associated rsync protocol.
+	usePreDumps = respHeader.GetPredump()
+	if usePreDumps {
 		logger.Debugf("The other side does support pre-copy")
 	} else {
 		logger.Debugf("The other side does not support pre-copy")
 	}
 
-	// All failure paths need to do a few things to correctly handle errors before returning.
-	// Unfortunately, handling errors is not well-suited to defer as the code depends on the
-	// status of driver and the error value.  The error value is especially tricky due to the
-	// common case of creating a new err variable (intentional or not) due to scoping and use
-	// of ":=".  Capturing err in a closure for use in defer would be fragile, which defeats
-	// the purpose of using defer.  An abort function reduces the odds of mishandling errors
-	// without introducing the fragility of closing on err.
-	abort := func(err error) error {
-		driver.Cleanup()
-		go s.sendControl(err)
-		return err
-	}
-
-	err = driver.SendWhileRunning(s.fsConn, migrateOp, bwlimit, s.instanceOnly)
-	if err != nil {
-		return abort(err)
-	}
-
 	restoreSuccess := make(chan bool, 1)
 	dumpSuccess := make(chan error, 1)
 
 	if s.live {
-		if header.Criu == nil {
+		if respHeader.Criu == nil {
 			return abort(fmt.Errorf("Got no CRIU socket type for live migration"))
-		} else if *header.Criu != migration.CRIUType_CRIU_RSYNC {
+		} else if *respHeader.Criu != migration.CRIUType_CRIU_RSYNC {
 			return abort(fmt.Errorf("Formats other than criu rsync not understood"))
 		}
 
@@ -523,22 +540,16 @@ func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operati
 		}
 
 		if util.RuntimeLiblxcVersionAtLeast(2, 0, 4) {
-			/* What happens below is slightly convoluted. Due to various
-			 * complications with networking, there's no easy way for criu
-			 * to exit and leave the container in a frozen state for us to
-			 * somehow resume later.
-			 *
-			 * Instead, we use what criu calls an "action-script", which is
-			 * basically a callback that lets us know when the dump is
-			 * done. (Unfortunately, we can't pass arguments, just an
-			 * executable path, so we write a custom action script with the
-			 * real command we want to run.)
-			 *
-			 * This script then hangs until the migration operation either
-			 * finishes successfully or fails, and exits 1 or 0, which
-			 * causes criu to either leave the container running or kill it
-			 * as we asked.
-			 */
+			// What happens below is slightly convoluted. Due to various complications
+			// with networking, there's no easy way for criu to exit and leave the
+			// container in a frozen state for us to somehow resume later.
+			// Instead, we use what criu calls an "action-script", which is basically a
+			// callback that lets us know when the dump is done. (Unfortunately, we
+			// can't pass arguments, just an executable path, so we write a custom
+			// action script with the real command we want to run.)
+			// This script then hangs until the migration operation either finishes
+			// successfully or fails, and exits 1 or 0, which causes criu to either
+			// leave the container running or kill it as we asked.
 			dumpDone := make(chan bool, 1)
 			actionScriptOpSecret, err := shared.RandomCryptoString()
 			if err != nil {
@@ -595,17 +606,17 @@ func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operati
 
 			preDumpCounter := 0
 			preDumpDir := ""
-			if use_pre_dumps {
+			if usePreDumps {
 				final := false
 				for !final {
 					preDumpCounter++
-					if preDumpCounter < max_iterations {
+					if preDumpCounter < maxDumpIterations {
 						final = false
 					} else {
 						final = true
 					}
 					dumpDir := fmt.Sprintf("%03d", preDumpCounter)
-					loop_args := preDumpLoopArgs{
+					loopArgs := preDumpLoopArgs{
 						checkpointDir: checkpointDir,
 						bwlimit:       bwlimit,
 						preDumpDir:    preDumpDir,
@@ -613,7 +624,7 @@ func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operati
 						final:         final,
 						rsyncFeatures: rsyncFeatures,
 					}
-					final, err = s.preDumpLoop(&loop_args)
+					final, err = s.preDumpLoop(&loopArgs)
 					if err != nil {
 						os.RemoveAll(checkpointDir)
 						return abort(err)
@@ -640,17 +651,17 @@ func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operati
 					function:     "migration",
 				}
 
-				// Do the final CRIU dump. This is needs no special
-				// handling if pre-dumps are used or not
+				// Do the final CRIU dump. This is needs no special handling if
+				// pre-dumps are used or not.
 				dumpSuccess <- ct.Migrate(&criuMigrationArgs)
 				os.RemoveAll(checkpointDir)
 			}()
 
 			select {
-			/* the checkpoint failed, let's just abort */
+			// The checkpoint failed, let's just abort.
 			case err = <-dumpSuccess:
 				return abort(err)
-			/* the dump finished, let's continue on to the restore */
+			// The dump finished, let's continue on to the restore.
 			case <-dumpDone:
 				logger.Debugf("Dump finished, continuing with restore...")
 			}
@@ -673,13 +684,11 @@ func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operati
 			}
 		}
 
-		/*
-		 * We do the serially right now, but there's really no reason for us
-		 * to; since we have separate websockets, we can do it in parallel if
-		 * we wanted to. However, assuming we're network bound, there's really
-		 * no reason to do these in parallel. In the future when we're using
-		 * p.haul's protocol, it will make sense to do these in parallel.
-		 */
+		// We do the transger serially right now, but there's really no reason for us to;
+		// since we have separate websockets, we can do it in parallel if we wanted to.
+		// However assuming we're network bound, there's really no reason to do these in.
+		// parallel. In the future when we're using p.haul's protocol, it will make sense
+		// to do these in parallel.
 		ctName, _, _ := shared.InstanceGetParentAndSnapshotName(s.instance.Name())
 		state := s.instance.DaemonState()
 		err = rsync.Send(ctName, shared.AddSlash(checkpointDir), &shared.WebsocketIO{Conn: s.criuConn}, nil, rsyncFeatures, bwlimit, state.OS.ExecPath)
@@ -688,14 +697,16 @@ func (s *migrationSourceWs) Do(state *state.State, migrateOp *operations.Operati
 		}
 	}
 
-	if s.live || (header.Criu != nil && *header.Criu == migration.CRIUType_NONE) {
-		err = driver.SendAfterCheckpoint(s.fsConn, bwlimit)
-		if err != nil {
-			return abort(err)
+	if pool == nil {
+		if s.live || (respHeader.Criu != nil && *respHeader.Criu == migration.CRIUType_NONE) {
+			err = legacyDriver.SendAfterCheckpoint(s.fsConn, bwlimit)
+			if err != nil {
+				return abort(err)
+			}
 		}
-	}
 
-	driver.Cleanup()
+		legacyDriver.Cleanup()
+	}
 
 	msg := migration.MigrationControl{}
 	err = s.recv(&msg)


More information about the lxc-devel mailing list