Created
March 24, 2021 23:28
-
-
Save xkisu/1ad4cb960d34d52fa81976455c0316d4 to your computer and use it in GitHub Desktop.
Restoer Docker Volumes From Folder of Tarfiles
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
// set GOARCH=amd64 | |
// set GOOS=linux | |
// go build -o compose-backup ./main.go | |
import ( | |
"fmt" | |
"io" | |
"io/ioutil" | |
"os" | |
"path/filepath" | |
"strings" | |
"github.com/spf13/cobra" | |
"github.com/docker/docker/api/types" | |
"github.com/docker/docker/api/types/container" | |
"github.com/docker/docker/api/types/mount" | |
"github.com/docker/docker/api/types/volume" | |
"github.com/docker/docker/client" | |
) | |
var targetDir string | |
func init () { | |
rootCmd.PersistentFlags().StringVarP(&targetDir, "target", "D", "./", "specifies the directory to put backups in and restore from") | |
rootCmd.AddCommand(restoreCmd) | |
} | |
var restoreCmd = &cobra.Command{ | |
Use: "restore", | |
Short: "Restore backed up volumes from a directory", | |
RunE: func(cmd *cobra.Command, args []string) error { | |
fmt.Println("connecting to docker socket") | |
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) | |
if err != nil { | |
panic(err) | |
} | |
// Pull the default image | |
fmt.Println("pulling default image") | |
reader, err := cli.ImagePull(cmd.Context(), "docker.io/library/ubuntu", types.ImagePullOptions{}) | |
if err != nil { | |
return fmt.Errorf("failed to pull image: %w", err) | |
} | |
io.Copy(os.Stdout, reader) | |
fmt.Println("reading archives from", targetDir) | |
items, _ := ioutil.ReadDir(targetDir) | |
for _, item := range items { | |
if item.IsDir() { | |
continue | |
} | |
// Check the file extension | |
extension := filepath.Ext(item.Name()) | |
if extension != ".tar" { | |
continue | |
} | |
// Extract the filename as the volume name | |
filename := filepath.Base(item.Name()) | |
volumeName := filename[0:len(filename)-len(extension)] | |
fmt.Println("processing", item.Name()) | |
// Attempt to create the volume | |
fmt.Println("creating volume", volumeName) | |
vol, err := cli.VolumeCreate(cmd.Context(), volume.VolumeCreateBody{ | |
Driver: "local", | |
DriverOpts: map[string]string{}, | |
Labels: map[string]string{}, | |
Name: volumeName, | |
}) | |
if err != nil { | |
return err | |
} | |
// Create the container for unarchiving the backup into the volume | |
// NOTE: strip-components is required in my case because all the data is in a /data folder in the tar | |
containerCmd := []string{"tar", "xvf", "/backup/" + filename, "-C", "/volume", "--strip-components=1"} | |
fmt.Println("creating volume extraction container", strings.Join(containerCmd, " ")) | |
cont, err := cli.ContainerCreate(cmd.Context(), &container.Config{ | |
Image: "ubuntu", | |
Cmd: containerCmd, | |
Tty: false, | |
}, &container.HostConfig{ | |
Mounts: []mount.Mount{ | |
{ // Mount the directory with the backed up files to /backup | |
Type: mount.TypeBind, | |
Source: targetDir, | |
Target: "/backup", | |
}, | |
}, | |
Binds: []string{ | |
// Mount the target volume to /volume | |
vol.Name+":/volume", | |
}, | |
AutoRemove: true, | |
}, nil, nil, "") | |
if err != nil { | |
return fmt.Errorf("failed to create container: %w", err) | |
} | |
// Start the container | |
fmt.Println("starting volume extraction container") | |
if err := cli.ContainerStart(cmd.Context(), cont.ID, types.ContainerStartOptions{}); err != nil { | |
return fmt.Errorf("failed to start container: %w", err) | |
} | |
// Wait for the extraction to finish | |
fmt.Println("waiting for container extraction to finish") | |
statusCh, errCh := cli.ContainerWait(cmd.Context(), cont.ID, container.WaitConditionNotRunning) | |
select { | |
case err := <-errCh: | |
if err != nil { | |
return fmt.Errorf("container exited with error: %w", err) | |
} | |
case <-statusCh: | |
} | |
} | |
fmt.Println("finished restoring data to volumes") | |
return nil | |
}, | |
} | |
var rootCmd = &cobra.Command{ | |
Use: "compose-backup", | |
Short: "Tool to backup and restore docker compose volumes", | |
} | |
func main () { | |
if err := rootCmd.Execute(); err != nil { | |
panic(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment