Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/lxd-to-incus: Uncompress snapshot files in target database #69

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions cmd/lxd-to-incus/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package main

import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"

"github.com/pierrec/lz4/v4"

"github.com/lxc/incus/shared"
)

// Uncompress the raft snapshot files in the given database directory.
//
// A backup will be created and kept around in case of errors.
func migrateDatabase(dir string) error {
global := filepath.Join(dir, "global")

err := shared.DirCopy(global, global+".bak")
if err != nil {
return fmt.Errorf("Failed to backup database directory %q: %w", global, err)
}

files, err := ioutil.ReadDir(global)
if err != nil {
return fmt.Errorf("Failed to list database directory %q: %w", global, err)
}

for _, file := range files {
var timestamp uint64
var first uint64
var last uint64

if !file.Mode().IsRegular() {
continue
}

n, err := fmt.Sscanf(file.Name(), "snapshot-%d-%d-%d\n", &timestamp, &first, &last)
if err != nil || n != 3 {
continue
}

filename := filepath.Join(global, file.Name())
err = lz4Uncompress(filename)
if err != nil {
return fmt.Errorf("Failed to uncompress snapshot %q: %w", filename, err)
}
}

return nil
}

// Uncompress the given file, preserving its mode and ownership.
func lz4Uncompress(zfilename string) error {
zr := lz4.NewReader(nil)

zfile, err := os.Open(zfilename)
if err != nil {
return fmt.Errorf("Failed to open file %q: %w", zfilename, err)
}

zinfo, err := zfile.Stat()
if err != nil {
return fmt.Errorf("Fialed to get file info for %q: %w", zfilename, err)
}

// use the same mode for the output file
mode := zinfo.Mode()

_, uid, gid := shared.GetOwnerMode(zinfo)

filename := zfilename + ".uncompressed"
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
if err != nil {
return fmt.Errorf("Failed to open file %q: %w", filename, err)
}

zr.Reset(zfile)

_, err = io.Copy(file, zr)
if err != nil {
return fmt.Errorf("Failed to uncompress %q into %q: %w", zfilename, filename, err)
}

for _, c := range []io.Closer{zfile, file} {
err := c.Close()
if err != nil {
return fmt.Errorf("Failed to close file: %w", err)
}
}

err = os.Chown(filename, uid, gid)
if err != nil {
return fmt.Errorf("Failed to set ownership of %q: %w", filename, err)
}

err = os.Rename(filename, zfilename)
if err != nil {
return fmt.Errorf("Failed to rename %q to %q: %w", filename, zfilename, err)
}

return nil
}
1 change: 1 addition & 0 deletions cmd/lxd-to-incus/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require (
github.com/muhlemmer/gu v0.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect
github.com/pkg/sftp v1.13.6 // indirect
github.com/pkg/xattr v0.4.9 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
Expand Down
2 changes: 2 additions & 0 deletions cmd/lxd-to-incus/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
Expand Down
9 changes: 8 additions & 1 deletion cmd/lxd-to-incus/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func main() {
}
}

type cmdMigrate struct{
type cmdMigrate struct {
flagYes bool
}

Expand Down Expand Up @@ -395,6 +395,13 @@ Instances will come back online once the migration is complete.
return fmt.Errorf("Failed to move %q to %q: %w", sourcePaths.Daemon, targetPaths.Daemon, err)
}

// Migrate database format.
fmt.Println("=> Migrating database")
err = migrateDatabase(filepath.Join(targetPaths.Daemon, "database"))
freeekanayaka marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return fmt.Errorf("Failed to migrate database in %q: %w", filepath.Join(targetPaths.Daemon, "database"), err)
}

// Cleanup paths.
fmt.Println("=> Cleaning up target paths")

Expand Down