| ... | ... |
@@ -39,6 +39,7 @@ func (srv *Server) Help() string {
|
| 39 | 39 |
{"commit", "Save the state of a container"},
|
| 40 | 40 |
{"attach", "Attach to the standard inputs and outputs of a running container"},
|
| 41 | 41 |
{"info", "Display system-wide information"},
|
| 42 |
+ {"tar", "Stream the contents of a container as a tar archive"},
|
|
| 42 | 43 |
{"web", "Generate a web UI"},
|
| 43 | 44 |
} {
|
| 44 | 45 |
help += fmt.Sprintf(" %-10.10s%s\n", cmd...)
|
| ... | ... |
@@ -233,9 +234,16 @@ func (srv *Server) CmdTar(stdin io.ReadCloser, stdout io.Writer, args ...string) |
| 233 | 233 |
return nil |
| 234 | 234 |
} |
| 235 | 235 |
name := flags.Arg(0) |
| 236 |
- if _, exists := srv.findContainer(name); exists {
|
|
| 236 |
+ if container, exists := srv.findContainer(name); exists {
|
|
| 237 |
+ data, err := container.Filesystem.Tar() |
|
| 238 |
+ if err != nil {
|
|
| 239 |
+ return err |
|
| 240 |
+ } |
|
| 237 | 241 |
// Stream the entire contents of the container (basically a volatile snapshot) |
| 238 |
- return fake.WriteFakeTar(stdout) |
|
| 242 |
+ if _, err := io.Copy(stdout, data); err != nil {
|
|
| 243 |
+ return err |
|
| 244 |
+ } |
|
| 245 |
+ return nil |
|
| 239 | 246 |
} |
| 240 | 247 |
return errors.New("No such container: " + name)
|
| 241 | 248 |
} |
| ... | ... |
@@ -67,6 +67,24 @@ func (fs *Filesystem) IsMounted() bool {
|
| 67 | 67 |
return false |
| 68 | 68 |
} |
| 69 | 69 |
|
| 70 |
+// Tar returns the contents of the filesystem as an uncompressed tar stream |
|
| 71 |
+func (fs *Filesystem) Tar() (io.Reader, error) {
|
|
| 72 |
+ if err := fs.EnsureMounted(); err != nil {
|
|
| 73 |
+ return nil, err |
|
| 74 |
+ } |
|
| 75 |
+ return Tar(fs.RootFS) |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+ |
|
| 79 |
+func (fs *Filesystem) EnsureMounted() error {
|
|
| 80 |
+ if !fs.IsMounted() {
|
|
| 81 |
+ if err := fs.Mount(); err != nil {
|
|
| 82 |
+ return err |
|
| 83 |
+ } |
|
| 84 |
+ } |
|
| 85 |
+ return nil |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 70 | 88 |
type ChangeType int |
| 71 | 89 |
|
| 72 | 90 |
const ( |
| ... | ... |
@@ -5,8 +5,28 @@ import ( |
| 5 | 5 |
"container/list" |
| 6 | 6 |
"io" |
| 7 | 7 |
"sync" |
| 8 |
+ "os/exec" |
|
| 8 | 9 |
) |
| 9 | 10 |
|
| 11 |
+// Tar generates a tar archive from a filesystem path, and returns it as a stream. |
|
| 12 |
+// Path must point to a directory. |
|
| 13 |
+ |
|
| 14 |
+func Tar(path string) (io.Reader, error) {
|
|
| 15 |
+ cmd := exec.Command("tar", "-C", path, "-c", ".")
|
|
| 16 |
+ output, err := cmd.StdoutPipe() |
|
| 17 |
+ if err != nil {
|
|
| 18 |
+ return nil, err |
|
| 19 |
+ } |
|
| 20 |
+ if err := cmd.Start(); err != nil {
|
|
| 21 |
+ return nil, err |
|
| 22 |
+ } |
|
| 23 |
+ // FIXME: errors will not be passed because we don't wait for the command. |
|
| 24 |
+ // Instead, consumers will hit EOF right away. |
|
| 25 |
+ // This can be fixed by waiting for the process to exit, or for the first write |
|
| 26 |
+ // on stdout, whichever comes first. |
|
| 27 |
+ return output, nil |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 10 | 30 |
type nopWriteCloser struct {
|
| 11 | 31 |
io.Writer |
| 12 | 32 |
} |