mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Plugged 4 more commands into the real backend: 'diff', 'reset', 'run', 'stop'. 'run' actually runs the process, but doesn't capture outptu properly, and encounters mount issues
This commit is contained in:
parent
dbc7fb7575
commit
5d6dd22fb2
5 changed files with 143 additions and 117 deletions
26
container.go
26
container.go
|
@ -11,6 +11,8 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
"strings"
|
||||||
|
"bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Container struct {
|
type Container struct {
|
||||||
|
@ -30,6 +32,9 @@ type Container struct {
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
stdout *writeBroadcaster
|
stdout *writeBroadcaster
|
||||||
stderr *writeBroadcaster
|
stderr *writeBroadcaster
|
||||||
|
|
||||||
|
stdoutLog *bytes.Buffer
|
||||||
|
stderrLog *bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -51,8 +56,13 @@ func createContainer(id string, root string, command string, args []string, laye
|
||||||
lxcConfigPath: path.Join(root, "config.lxc"),
|
lxcConfigPath: path.Join(root, "config.lxc"),
|
||||||
stdout: newWriteBroadcaster(),
|
stdout: newWriteBroadcaster(),
|
||||||
stderr: newWriteBroadcaster(),
|
stderr: newWriteBroadcaster(),
|
||||||
|
stdoutLog: new(bytes.Buffer),
|
||||||
|
stderrLog: new(bytes.Buffer),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
container.stdout.AddWriter(NopWriteCloser(container.stdoutLog))
|
||||||
|
container.stderr.AddWriter(NopWriteCloser(container.stderrLog))
|
||||||
|
|
||||||
if err := os.Mkdir(root, 0700); err != nil {
|
if err := os.Mkdir(root, 0700); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -73,6 +83,8 @@ func loadContainer(containerPath string) (*Container, error) {
|
||||||
container := &Container{
|
container := &Container{
|
||||||
stdout: newWriteBroadcaster(),
|
stdout: newWriteBroadcaster(),
|
||||||
stderr: newWriteBroadcaster(),
|
stderr: newWriteBroadcaster(),
|
||||||
|
stdoutLog: new(bytes.Buffer),
|
||||||
|
stderrLog: new(bytes.Buffer),
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal(data, container); err != nil {
|
if err := json.Unmarshal(data, container); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -81,6 +93,11 @@ func loadContainer(containerPath string) (*Container, error) {
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (container *Container) Cmd() *exec.Cmd {
|
||||||
|
return container.cmd
|
||||||
|
}
|
||||||
|
|
||||||
func (container *Container) loadUserData() (map[string]string, error) {
|
func (container *Container) loadUserData() (map[string]string, error) {
|
||||||
jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json"))
|
jsonData, err := ioutil.ReadFile(path.Join(container.Root, "userdata.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -200,12 +217,21 @@ func (container *Container) StdoutPipe() (io.ReadCloser, error) {
|
||||||
return newBufReader(reader), nil
|
return newBufReader(reader), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (container *Container) StdoutLog() io.Reader {
|
||||||
|
return strings.NewReader(container.stdoutLog.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
func (container *Container) StderrPipe() (io.ReadCloser, error) {
|
||||||
reader, writer := io.Pipe()
|
reader, writer := io.Pipe()
|
||||||
container.stderr.AddWriter(writer)
|
container.stderr.AddWriter(writer)
|
||||||
return newBufReader(reader), nil
|
return newBufReader(reader), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (container *Container) StderrLog() io.Reader {
|
||||||
|
return strings.NewReader(container.stderrLog.String())
|
||||||
|
}
|
||||||
|
|
||||||
func (container *Container) monitor() {
|
func (container *Container) monitor() {
|
||||||
// Wait for the program to exit
|
// Wait for the program to exit
|
||||||
container.cmd.Wait()
|
container.cmd.Wait()
|
||||||
|
|
|
@ -9,11 +9,9 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"bytes"
|
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"sort"
|
"sort"
|
||||||
"os"
|
"os"
|
||||||
|
@ -49,6 +47,29 @@ func (srv *Server) Help() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (srv *Server) CmdStop(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||||
|
cmd := rcli.Subcmd(stdout, "stop", "[OPTIONS] NAME", "Stop a running container")
|
||||||
|
if err := cmd.Parse(args); err != nil {
|
||||||
|
cmd.Usage()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if cmd.NArg() < 1 {
|
||||||
|
cmd.Usage()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, name := range cmd.Args() {
|
||||||
|
if container := srv.docker.Get(name); container != nil {
|
||||||
|
if err := container.Stop(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintln(stdout, container.Id)
|
||||||
|
} else {
|
||||||
|
return errors.New("No such container: " + container.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (srv *Server) CmdList(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdList(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||||
flags := rcli.Subcmd(stdout, "list", "[OPTIONS] [NAME]", "List containers")
|
flags := rcli.Subcmd(stdout, "list", "[OPTIONS] [NAME]", "List containers")
|
||||||
limit := flags.Int("l", 0, "Only show the N most recent versions of each name")
|
limit := flags.Int("l", 0, "Only show the N most recent versions of each name")
|
||||||
|
@ -87,7 +108,7 @@ func (srv *Server) CmdList(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
/* SOURCE */ container.GetUserData("source"),
|
/* SOURCE */ container.GetUserData("source"),
|
||||||
/* SIZE */ fmt.Sprintf("%.1fM", float32(fake.RandomContainerSize()) / 1024 / 1024),
|
/* SIZE */ fmt.Sprintf("%.1fM", float32(fake.RandomContainerSize()) / 1024 / 1024),
|
||||||
/* CHANGES */ fmt.Sprintf("%.1fM", float32(fake.RandomBytesChanged() / 1024 / 1024)),
|
/* CHANGES */ fmt.Sprintf("%.1fM", float32(fake.RandomBytesChanged() / 1024 / 1024)),
|
||||||
/* RUNNING */ fmt.Sprintf("%v", fake.ContainerRunning()),
|
/* RUNNING */ fmt.Sprintf("%v", container.State.Running),
|
||||||
/* COMMAND */ fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " ")),
|
/* COMMAND */ fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " ")),
|
||||||
} {
|
} {
|
||||||
if idx == 0 {
|
if idx == 0 {
|
||||||
|
@ -193,11 +214,14 @@ func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...stri
|
||||||
if dstName == "" {
|
if dstName == "" {
|
||||||
dstName = srcName
|
dstName = srcName
|
||||||
}
|
}
|
||||||
if _, exists := srv.findContainer(srcName); exists {
|
/*
|
||||||
|
if src, exists := srv.findContainer(srcName); exists {
|
||||||
|
baseLayer := src.Filesystem.Layers[0]
|
||||||
//dst := srv.addContainer(dstName, "snapshot:" + src.Id, src.Size)
|
//dst := srv.addContainer(dstName, "snapshot:" + src.Id, src.Size)
|
||||||
//fmt.Fprintln(stdout, dst.Id)
|
//fmt.Fprintln(stdout, dst.Id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return errors.New("No such container: " + srcName)
|
return errors.New("No such container: " + srcName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,9 +244,6 @@ func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
flags := rcli.Subcmd(stdout,
|
flags := rcli.Subcmd(stdout,
|
||||||
"diff", "CONTAINER [OPTIONS]",
|
"diff", "CONTAINER [OPTIONS]",
|
||||||
"Inspect changes on a container's filesystem")
|
"Inspect changes on a container's filesystem")
|
||||||
fl_diff := flags.Bool("d", true, "Show changes in diff format")
|
|
||||||
fl_bytes := flags.Bool("b", false, "Show how many bytes have been changed")
|
|
||||||
fl_list := flags.Bool("l", false, "Show a list of changed files")
|
|
||||||
if err := flags.Parse(args); err != nil {
|
if err := flags.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -231,44 +252,37 @@ func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
}
|
}
|
||||||
if container, exists := srv.findContainer(flags.Arg(0)); !exists {
|
if container, exists := srv.findContainer(flags.Arg(0)); !exists {
|
||||||
return errors.New("No such container")
|
return errors.New("No such container")
|
||||||
} else if *fl_bytes {
|
|
||||||
fmt.Fprintf(stdout, "%d\n", container.BytesChanged)
|
|
||||||
} else if *fl_list {
|
|
||||||
// FAKE
|
|
||||||
fmt.Fprintf(stdout, strings.Join([]string{
|
|
||||||
"/etc/postgres/pg.conf",
|
|
||||||
"/etc/passwd",
|
|
||||||
"/var/lib/postgres",
|
|
||||||
"/usr/bin/postgres",
|
|
||||||
"/usr/bin/psql",
|
|
||||||
"/var/log/postgres",
|
|
||||||
"/var/log/postgres/postgres.log",
|
|
||||||
"/var/log/postgres/postgres.log.0",
|
|
||||||
"/var/log/postgres/postgres.log.1.gz"}, "\n"))
|
|
||||||
} else if *fl_diff {
|
|
||||||
// Achievement unlocked: embed a diff of your code as a string in your code
|
|
||||||
fmt.Fprintf(stdout, `
|
|
||||||
diff --git a/dockerd/dockerd.go b/dockerd/dockerd.go
|
|
||||||
index 2dae694..e43caca 100644
|
|
||||||
--- a/dockerd/dockerd.go
|
|
||||||
+++ b/dockerd/dockerd.go
|
|
||||||
@@ -158,6 +158,7 @@ func (srv *Server) CmdDiff(stdin io.ReadCloser, stdout io.Writer, args ...str
|
|
||||||
flags := rcli.Subcmd(stdout,
|
|
||||||
"diff", "CONTAINER [OPTIONS]",
|
|
||||||
"Inspect changes on a container's filesystem")
|
|
||||||
+ fl_diff := flags.Bool("d", true, "Show changes in diff format")
|
|
||||||
fl_bytes := flags.Bool("b", false, "Show how many bytes have been changes")
|
|
||||||
fl_list := flags.Bool("l", false, "Show a list of changed files")
|
|
||||||
fl_download := flags.Bool("d", false, "Download the changes as gzipped tar stream")
|
|
||||||
`)
|
|
||||||
return nil
|
|
||||||
} else {
|
} else {
|
||||||
flags.Usage()
|
changes, err := container.Filesystem.Changes()
|
||||||
return nil
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, change := range changes {
|
||||||
|
fmt.Fprintln(stdout, change.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (srv *Server) CmdReset(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||||
|
flags := rcli.Subcmd(stdout,
|
||||||
|
"reset", "CONTAINER [OPTIONS]",
|
||||||
|
"Reset changes to a container's filesystem")
|
||||||
|
if err := flags.Parse(args); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if flags.NArg() < 1 {
|
||||||
|
return errors.New("Not enough arguments")
|
||||||
|
}
|
||||||
|
for _, name := range flags.Args() {
|
||||||
|
if container, exists := srv.findContainer(name); exists {
|
||||||
|
if err := container.Filesystem.Reset(); err != nil {
|
||||||
|
return errors.New("Reset " + container.Id + ": " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ByDate wraps an array of layers so they can be sorted by date (most recent first)
|
// ByDate wraps an array of layers so they can be sorted by date (most recent first)
|
||||||
|
|
||||||
|
@ -349,15 +363,19 @@ func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
|
if _, err := io.Copy(stdout, container.StdoutLog()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if _, err := io.Copy(stdout, container.StderrLog()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New("No such container: " + flags.Arg(0))
|
return errors.New("No such container: " + flags.Arg(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||||
flags := rcli.Subcmd(stdout, "run", "[OPTIONS] CONTAINER COMMAND [ARG...]", "Run a command in a container")
|
flags := rcli.Subcmd(stdout, "run", "[OPTIONS] CONTAINER COMMAND [ARG...]", "Run a command in a container")
|
||||||
fl_attach := flags.Bool("a", false, "Attach stdin and stdout")
|
fl_attach := flags.Bool("a", false, "Attach stdin and stdout")
|
||||||
fl_tty := flags.Bool("t", false, "Allocate a pseudo-tty")
|
//fl_tty := flags.Bool("t", false, "Allocate a pseudo-tty")
|
||||||
if err := flags.Parse(args); err != nil {
|
if err := flags.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -367,16 +385,37 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
|
||||||
}
|
}
|
||||||
name, cmd := flags.Arg(0), flags.Args()[1:]
|
name, cmd := flags.Arg(0), flags.Args()[1:]
|
||||||
if container, exists := srv.findContainer(name); exists {
|
if container, exists := srv.findContainer(name); exists {
|
||||||
if container.Running {
|
log.Printf("Running container %#v\n", container)
|
||||||
return errors.New("Already running: " + name)
|
container.Path = cmd[0]
|
||||||
}
|
container.Args = cmd[1:]
|
||||||
if *fl_attach {
|
if *fl_attach {
|
||||||
return container.Run(cmd[0], cmd[1:], stdin, stdout, *fl_tty)
|
cmd_stdout, err := container.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd_stderr, err := container.StderrPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := container.Start(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sending_stdout := future.Go(func() error { _, err := io.Copy(stdout, cmd_stdout); return err })
|
||||||
|
sending_stderr := future.Go(func() error { _, err := io.Copy(stdout, cmd_stderr); return err })
|
||||||
|
err_sending_stdout := <-sending_stdout
|
||||||
|
err_sending_stderr := <-sending_stderr
|
||||||
|
if err_sending_stdout != nil {
|
||||||
|
return err_sending_stdout
|
||||||
|
}
|
||||||
|
return err_sending_stderr
|
||||||
} else {
|
} else {
|
||||||
go container.Run(cmd[0], cmd[1:], ioutil.NopCloser(new(bytes.Buffer)), ioutil.Discard, *fl_tty)
|
if output, err := container.Output(); err != nil {
|
||||||
fmt.Fprintln(stdout, container.Id)
|
return err
|
||||||
return nil
|
} else {
|
||||||
|
fmt.Printf("-->|%s|\n", output)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New("No such container: " + name)
|
return errors.New("No such container: " + name)
|
||||||
}
|
}
|
||||||
|
@ -415,10 +454,7 @@ func New() (*Server, error) {
|
||||||
layers: store,
|
layers: store,
|
||||||
docker: d,
|
docker: d,
|
||||||
}
|
}
|
||||||
// Update name index
|
|
||||||
log.Printf("Building name index from %s...\n")
|
|
||||||
for _, container := range srv.docker.List() {
|
for _, container := range srv.docker.List() {
|
||||||
log.Printf("Indexing %s to %s\n", container.Id, container.GetUserData("name"))
|
|
||||||
name := container.GetUserData("name")
|
name := container.GetUserData("name")
|
||||||
if _, exists := srv.containersByName[name]; !exists {
|
if _, exists := srv.containersByName[name]; !exists {
|
||||||
srv.containersByName[name] = new(ByDate)
|
srv.containersByName[name] = new(ByDate)
|
||||||
|
|
66
fake/fake.go
66
fake/fake.go
|
@ -6,11 +6,8 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"github.com/dotcloud/docker"
|
"github.com/dotcloud/docker"
|
||||||
"github.com/dotcloud/docker/future"
|
|
||||||
"errors"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"fmt"
|
|
||||||
"github.com/kr/pty"
|
"github.com/kr/pty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -66,77 +63,14 @@ type Container struct {
|
||||||
Size uint
|
Size uint
|
||||||
FilesChanged uint
|
FilesChanged uint
|
||||||
BytesChanged uint
|
BytesChanged uint
|
||||||
Running bool
|
|
||||||
stdoutLog *bytes.Buffer
|
|
||||||
stdinLog *bytes.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContainer(c *docker.Container) *Container {
|
func NewContainer(c *docker.Container) *Container {
|
||||||
return &Container{
|
return &Container{
|
||||||
Container: c,
|
Container: c,
|
||||||
Name: c.GetUserData("name"),
|
Name: c.GetUserData("name"),
|
||||||
stdoutLog: new(bytes.Buffer),
|
|
||||||
stdinLog: new(bytes.Buffer),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) Run(command string, args []string, stdin io.ReadCloser, stdout io.Writer, tty bool) error {
|
|
||||||
// Not thread-safe
|
|
||||||
if c.Running {
|
|
||||||
return errors.New("Already running")
|
|
||||||
}
|
|
||||||
c.Path = command
|
|
||||||
c.Args = args
|
|
||||||
// Reset logs
|
|
||||||
c.stdoutLog.Reset()
|
|
||||||
c.stdinLog.Reset()
|
|
||||||
cmd := exec.Command(c.Path, c.Args...)
|
|
||||||
cmd_stdin, cmd_stdout, err := startCommand(cmd, tty)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Running = true
|
|
||||||
// ADD FAKE RANDOM CHANGES
|
|
||||||
c.FilesChanged = RandomFilesChanged()
|
|
||||||
c.BytesChanged = RandomBytesChanged()
|
|
||||||
copy_out := future.Go(func() error {
|
|
||||||
_, err := io.Copy(io.MultiWriter(stdout, c.stdoutLog), cmd_stdout)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
future.Go(func() error {
|
|
||||||
_, err := io.Copy(io.MultiWriter(cmd_stdin, c.stdinLog), stdin)
|
|
||||||
cmd_stdin.Close()
|
|
||||||
stdin.Close()
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
wait := future.Go(func() error {
|
|
||||||
err := cmd.Wait()
|
|
||||||
c.Running = false
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
if err := <-copy_out; err != nil {
|
|
||||||
if c.Running {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := <-wait; err != nil {
|
|
||||||
if status, ok := err.(*exec.ExitError); ok {
|
|
||||||
fmt.Fprintln(stdout, status)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) StdoutLog() io.Reader {
|
|
||||||
return strings.NewReader(c.stdoutLog.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) StdinLog() io.Reader {
|
|
||||||
return strings.NewReader(c.stdinLog.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) CmdString() string {
|
func (c *Container) CmdString() string {
|
||||||
return strings.Join(append([]string{c.Path}, c.Args...), " ")
|
return strings.Join(append([]string{c.Path}, c.Args...), " ")
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,16 @@ type Change struct {
|
||||||
Kind ChangeType
|
Kind ChangeType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (change *Change) String() string {
|
||||||
|
var kind string
|
||||||
|
switch change.Kind {
|
||||||
|
case ChangeModify: kind = "C"
|
||||||
|
case ChangeAdd: kind = "A"
|
||||||
|
case ChangeDelete: kind = "D"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s %s", kind, change.Path)
|
||||||
|
}
|
||||||
|
|
||||||
func (fs *Filesystem) Changes() ([]Change, error) {
|
func (fs *Filesystem) Changes() ([]Change, error) {
|
||||||
var changes []Change
|
var changes []Change
|
||||||
err := filepath.Walk(fs.RWPath, func(path string, f os.FileInfo, err error) error {
|
err := filepath.Walk(fs.RWPath, func(path string, f os.FileInfo, err error) error {
|
||||||
|
@ -152,6 +162,16 @@ func (fs *Filesystem) Changes() ([]Change, error) {
|
||||||
return changes, nil
|
return changes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *Filesystem) Reset() error {
|
||||||
|
if err := os.RemoveAll(fs.RWPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := fs.createMountPoints(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func newFilesystem(rootfs string, rwpath string, layers []string) *Filesystem {
|
func newFilesystem(rootfs string, rwpath string, layers []string) *Filesystem {
|
||||||
return &Filesystem{
|
return &Filesystem{
|
||||||
RootFS: rootfs,
|
RootFS: rootfs,
|
||||||
|
|
10
utils.go
10
utils.go
|
@ -7,6 +7,16 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type nopWriteCloser struct {
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *nopWriteCloser) Close() error { return nil }
|
||||||
|
|
||||||
|
func NopWriteCloser(w io.Writer) io.WriteCloser {
|
||||||
|
return &nopWriteCloser{w}
|
||||||
|
}
|
||||||
|
|
||||||
type bufReader struct {
|
type bufReader struct {
|
||||||
buf *bytes.Buffer
|
buf *bytes.Buffer
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue