package main import ( "archive/tar" "encoding/json" "fmt" "io" "io/ioutil" "os" "strings" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/spf13/cobra" ) var ( optStart = false restoreCmd = &cobra.Command{ Use: "restore ", Short: "restores a backup of a container", RunE: func(cmd *cobra.Command, args []string) error { if len(args) < 1 { return fmt.Errorf("restore requires a .json or .tar backup") } if strings.HasSuffix(args[0], ".json") { return restore(args[0]) } else if strings.HasSuffix(args[0], ".tar") { return restoreTar(args[0]) } return fmt.Errorf("Unknown file type, please provide a .tar or .json file") }, } ) func restoreTar(filename string) error { tarfile, err := os.Open(filename) if err != nil { return err } defer tarfile.Close() tr := tar.NewReader(tarfile) var b []byte for { th, err := tr.Next() if err == io.EOF { break } if err != nil { return err } switch th.Name { case "container.json": var err error b, err = ioutil.ReadAll(tr) if err != nil { return err } } } var backup Backup err = json.Unmarshal(b, &backup) if err != nil { return err } id, err := createContainer(backup) if err != nil { return err } conf, err := cli.ContainerInspect(ctx, id) if err != nil { return err } tt := map[string]string{} for _, oldPath := range backup.Mounts { for _, hostPath := range conf.Mounts { if oldPath.Destination == hostPath.Destination { tt[oldPath.Source] = hostPath.Source break } } } if _, err := tarfile.Seek(0, 0); err != nil { return err } tr = tar.NewReader(tarfile) for { th, err := tr.Next() if err == io.EOF { break } if err != nil { return err } if th.Name == "container.json" { continue } path := th.Name fmt.Println("Restoring:", path) for k, v := range tt { if strings.HasPrefix(path, k) { path = v + path[len(k):] } } if th.Typeflag == tar.TypeDir { if err := os.MkdirAll(path, os.FileMode(th.Mode)); err != nil { return err } } else { file, err := os.Create(path) if err != nil { return err } if _, err := io.Copy(file, tr); err != nil { return err } file.Close() } if err := os.Chmod(path, os.FileMode(th.Mode)); err != nil { return err } if err := os.Chown(path, th.Uid, th.Gid); err != nil { return err } fmt.Println("Created as:", path) } if optStart { return startContainer(id) } return nil } func restore(filename string) error { var backup Backup b, err := ioutil.ReadFile(filename) if err != nil { return err } err = json.Unmarshal(b, &backup) if err != nil { return err } id, err := createContainer(backup) if err != nil { return err } if optStart { return startContainer(id) } return nil } func createContainer(backup Backup) (string, error) { nameparts := strings.Split(backup.Name, "/") name := nameparts[len(nameparts)-1] fmt.Println("Restoring Container:", name) _, _, err := cli.ImageInspectWithRaw(ctx, backup.Config.Image) if err != nil { fmt.Println("Pulling Image:", backup.Config.Image) _, err := cli.ImagePull(ctx, backup.Config.Image, types.ImagePullOptions{}) if err != nil { return "", err } } // io.Copy(os.Stdout, reader) resp, err := cli.ContainerCreate(ctx, backup.Config, &container.HostConfig{ PortBindings: backup.PortMap, }, nil, name) if err != nil { return "", err } fmt.Println("Created Container with ID:", resp.ID) for _, m := range backup.Mounts { fmt.Printf("Old Mount (type %s) %s -> %s\n", m.Type, m.Source, m.Destination) } conf, err := cli.ContainerInspect(ctx, resp.ID) if err != nil { return "", err } for _, m := range conf.Mounts { fmt.Printf("New Mount (type %s) %s -> %s\n", m.Type, m.Source, m.Destination) } return resp.ID, nil } func startContainer(id string) error { fmt.Println("Starting container:", id[:12]) err := cli.ContainerStart(ctx, id, types.ContainerStartOptions{}) if err != nil { return err } /* statusCh, errCh := cli.ContainerWait(ctx, id, container.WaitConditionNotRunning) select { case err := <-errCh: if err != nil { return err } case <-statusCh: } out, err := cli.ContainerLogs(ctx, id, types.ContainerLogsOptions{ShowStdout: true}) if err != nil { return err } io.Copy(os.Stdout, out) */ return nil } func init() { restoreCmd.Flags().BoolVarP(&optStart, "start", "s", false, "start restored container") RootCmd.AddCommand(restoreCmd) }