diff --git a/commands.go b/commands.go index 531828d122..5ad92428e6 100644 --- a/commands.go +++ b/commands.go @@ -387,7 +387,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout io.Writer, args ...stri } archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout) } - img, err := srv.runtime.graph.Create(archive, "", "Imported from "+src) + img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src) if err != nil { return err } @@ -737,33 +737,22 @@ func (srv *Server) CmdPs(stdin io.ReadCloser, stdout io.Writer, args ...string) func (srv *Server) CmdCommit(stdin io.ReadCloser, stdout io.Writer, args ...string) error { cmd := rcli.Subcmd(stdout, - "commit", "[OPTIONS] CONTAINER [DEST]", + "commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes") if err := cmd.Parse(args); err != nil { return nil } - containerName, imgName := cmd.Arg(0), cmd.Arg(1) - if containerName == "" || imgName == "" { + containerName, repository, tag := cmd.Arg(0), cmd.Arg(1), cmd.Arg(2) + if containerName == "" { cmd.Usage() return nil } - if container := srv.runtime.Get(containerName); container != nil { - // FIXME: freeze the container before copying it to avoid data corruption? - // FIXME: this shouldn't be in commands. - rwTar, err := container.ExportRw() - if err != nil { - return err - } - // Create a new image from the container's base layers + a new layer from container changes - img, err := srv.runtime.graph.Create(rwTar, container.Image, "") - if err != nil { - return err - } - - fmt.Fprintln(stdout, img.Id) - return nil + img, err := srv.runtime.Commit(containerName, repository, tag) + if err != nil { + return err } - return errors.New("No such container: " + containerName) + fmt.Fprintln(stdout, img.Id) + return nil } func (srv *Server) CmdExport(stdin io.ReadCloser, stdout io.Writer, args ...string) error { diff --git a/container_test.go b/container_test.go index 64558871ac..94db11408d 100644 --- a/container_test.go +++ b/container_test.go @@ -46,7 +46,7 @@ func TestCommitRun(t *testing.T) { if err != nil { t.Error(err) } - img, err := runtime.graph.Create(rwTar, container1.Image, "unit test commited image") + img, err := runtime.graph.Create(rwTar, container1, "unit test commited image") if err != nil { t.Error(err) } diff --git a/graph.go b/graph.go index b0440518ad..c6bb30353a 100644 --- a/graph.go +++ b/graph.go @@ -47,13 +47,17 @@ func (graph *Graph) Get(id string) (*Image, error) { return img, nil } -func (graph *Graph) Create(layerData Archive, parent, comment string) (*Image, error) { +func (graph *Graph) Create(layerData Archive, container *Container, comment string) (*Image, error) { img := &Image{ Id: GenerateId(), - Parent: parent, Comment: comment, Created: time.Now(), } + if container != nil { + img.Parent = container.Image + img.ParentContainer = container.Id + img.ParentCommand = append([]string{container.Path}, container.Args...) + } if err := graph.Register(layerData, img); err != nil { return nil, err } diff --git a/graph_test.go b/graph_test.go index 20e879e52c..b9bdc5140f 100644 --- a/graph_test.go +++ b/graph_test.go @@ -35,7 +35,7 @@ func TestGraphCreate(t *testing.T) { if err != nil { t.Fatal(err) } - image, err := graph.Create(archive, "", "Testing") + image, err := graph.Create(archive, nil, "Testing") if err != nil { t.Fatal(err) } @@ -92,7 +92,7 @@ func TestMount(t *testing.T) { if err != nil { t.Fatal(err) } - image, err := graph.Create(archive, "", "Testing") + image, err := graph.Create(archive, nil, "Testing") if err != nil { t.Fatal(err) } @@ -128,7 +128,7 @@ func TestDelete(t *testing.T) { t.Fatal(err) } assertNImages(graph, t, 0) - img, err := graph.Create(archive, "", "Bla bla") + img, err := graph.Create(archive, nil, "Bla bla") if err != nil { t.Fatal(err) } @@ -139,11 +139,11 @@ func TestDelete(t *testing.T) { assertNImages(graph, t, 0) // Test 2 create (same name) / 1 delete - img1, err := graph.Create(archive, "foo", "Testing") + img1, err := graph.Create(archive, nil, "Testing") if err != nil { t.Fatal(err) } - if _, err = graph.Create(archive, "foo", "Testing"); err != nil { + if _, err = graph.Create(archive, nil, "Testing"); err != nil { t.Fatal(err) } assertNImages(graph, t, 2) diff --git a/image.go b/image.go index 44964a743d..546237c983 100644 --- a/image.go +++ b/image.go @@ -15,11 +15,13 @@ import ( ) type Image struct { - Id string `json:"id"` - Parent string `json:"parent,omitempty"` - Comment string `json:"comment,omitempty"` - Created time.Time `json:"created"` - graph *Graph + Id string `json:"id"` + Parent string `json:"parent,omitempty"` + Comment string `json:"comment,omitempty"` + Created time.Time `json:"created"` + ParentContainer string `json:-` + ParentCommand []string `json:-` + graph *Graph } func LoadImage(root string) (*Image, error) { diff --git a/runtime.go b/runtime.go index 9aa213f7e2..dcaaec6366 100644 --- a/runtime.go +++ b/runtime.go @@ -200,6 +200,33 @@ func (runtime *Runtime) Destroy(container *Container) error { return nil } +// Commit creates a new filesystem image from the current state of a container. +// The image can optionally be tagged into a repository +func (runtime *Runtime) Commit(id, repository, tag string) (*Image, error) { + container := runtime.Get(id) + if container == nil { + return nil, fmt.Errorf("No such container: %s", id) + } + // FIXME: freeze the container before copying it to avoid data corruption? + // FIXME: this shouldn't be in commands. + rwTar, err := container.ExportRw() + if err != nil { + return nil, err + } + // Create a new image from the container's base layers + a new layer from container changes + img, err := runtime.graph.Create(rwTar, container, "") + if err != nil { + return nil, err + } + // Register the image if needed + if repository != "" { + if err := runtime.repositories.Set(repository, tag, img.Id); err != nil { + return img, err + } + } + return img, nil +} + func (runtime *Runtime) restore() error { dir, err := ioutil.ReadDir(runtime.repository) if err != nil { diff --git a/tags.go b/tags.go index 55a5135860..a26a2f196d 100644 --- a/tags.go +++ b/tags.go @@ -2,9 +2,11 @@ package docker import ( "encoding/json" + "fmt" "io/ioutil" "os" "path/filepath" + "strings" ) type TagStore struct { @@ -60,6 +62,12 @@ func (store *TagStore) Reload() error { } func (store *TagStore) Set(repoName, tag, revision string) error { + if strings.Contains(repoName, ":") { + return fmt.Errorf("Illegal repository name: %s", repoName) + } + if strings.Contains(repoName, ":") { + return fmt.Errorf("Illegal tag name: %s", tag) + } if err := store.Reload(); err != nil { return err }