func ShiftIfNecessary(container container, srcIdmap *shared.IdmapSet) error { dstIdmap := container.IdmapSet() if dstIdmap == nil { dstIdmap = new(shared.IdmapSet) } if !reflect.DeepEqual(srcIdmap, dstIdmap) { if err := srcIdmap.UnshiftRootfs(container.Path()); err != nil { return err } if err := dstIdmap.ShiftRootfs(container.Path()); err != nil { return err } } return nil }
func (c *migrationSink) do() error { var err error c.controlConn, err = c.connectWithSecret(c.controlSecret) if err != nil { return err } defer c.disconnect() c.fsConn, err = c.connectWithSecret(c.fsSecret) if err != nil { c.sendControl(err) return err } if c.live { c.criuConn, err = c.connectWithSecret(c.criuSecret) if err != nil { c.sendControl(err) return err } } // For now, we just ignore whatever the server sends us. We only // support RSYNC, so that's what we respond with. header := MigrationHeader{} if err := c.recv(&header); err != nil { c.sendControl(err) return err } criuType := CRIUType_CRIU_RSYNC.Enum() if !c.live { criuType = nil } resp := MigrationHeader{Fs: MigrationFSType_RSYNC.Enum(), Criu: criuType} if err := c.send(&resp); err != nil { c.sendControl(err) return err } restore := make(chan error) go func(c *migrationSink) { imagesDir := "" srcIdmap := new(shared.IdmapSet) dstIdmap := c.IdmapSet if dstIdmap == nil { dstIdmap = new(shared.IdmapSet) } if c.live { var err error imagesDir, err = ioutil.TempDir("", "lxd_migration_") if err != nil { os.RemoveAll(imagesDir) c.sendControl(err) return } defer func() { err := CollectCRIULogFile(c.container, imagesDir, "migration", "restore") /* * If the checkpoint fails, we won't have any log to collect, * so don't warn about that. */ if err != nil && !os.IsNotExist(err) { shared.Debugf("Error collectiong migration log file %s", err) } os.RemoveAll(imagesDir) }() if err := RsyncRecv(shared.AddSlash(imagesDir), c.criuConn); err != nil { restore <- err os.RemoveAll(imagesDir) c.sendControl(err) return } /* * For unprivileged containers we need to shift the * perms on the images images so that they can be * opened by the process after it is in its user * namespace. */ if dstIdmap != nil { if err := dstIdmap.ShiftRootfs(imagesDir); err != nil { restore <- err os.RemoveAll(imagesDir) c.sendControl(err) return } } } fsDir := c.container.ConfigItem("lxc.rootfs")[0] if err := RsyncRecv(shared.AddSlash(fsDir), c.fsConn); err != nil { restore <- err c.sendControl(err) return } for _, idmap := range header.Idmap { e := shared.IdmapEntry{ Isuid: *idmap.Isuid, Isgid: *idmap.Isgid, Nsid: int(*idmap.Nsid), Hostid: int(*idmap.Hostid), Maprange: int(*idmap.Maprange)} srcIdmap.Idmap = shared.Extend(srcIdmap.Idmap, e) } if !reflect.DeepEqual(srcIdmap, dstIdmap) { if err := srcIdmap.UnshiftRootfs(shared.VarPath("containers", c.container.Name())); err != nil { restore <- err c.sendControl(err) return } if err := dstIdmap.ShiftRootfs(shared.VarPath("containers", c.container.Name())); err != nil { restore <- err c.sendControl(err) return } } if c.live { f, err := ioutil.TempFile("", "lxd_lxc_migrateconfig_") if err != nil { restore <- err return } if err = f.Chmod(0600); err != nil { f.Close() os.Remove(f.Name()) return } f.Close() if err := c.container.SaveConfigFile(f.Name()); err != nil { restore <- err return } cmd := exec.Command( os.Args[0], "forkmigrate", c.container.Name(), c.container.ConfigPath(), f.Name(), imagesDir, ) err = cmd.Run() if err != nil { log := GetCRIULogErrors(imagesDir, "restore") err = fmt.Errorf("restore failed:\n%s", log) } restore <- err } else { restore <- nil } }(c) source := c.controlChannel() for { select { case err = <-restore: c.sendControl(err) return err case msg, ok := <-source: if !ok { c.disconnect() return fmt.Errorf("Got error reading source") } if !*msg.Success { c.disconnect() return fmt.Errorf(*msg.Message) } else { // The source can only tell us it failed (e.g. if // checkpointing failed). We have to tell the source // whether or not the restore was successful. shared.Debugf("Unknown message %v from source", msg) } } } }