[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