2014-07-31 16:57:21 -04:00
|
|
|
package daemon
|
|
|
|
|
|
|
|
import (
|
2015-11-18 17:20:54 -05:00
|
|
|
"encoding/json"
|
2015-11-06 13:05:00 -05:00
|
|
|
"fmt"
|
|
|
|
"runtime"
|
2015-11-18 17:20:54 -05:00
|
|
|
"strings"
|
|
|
|
"time"
|
2015-11-06 13:05:00 -05:00
|
|
|
|
2015-12-04 15:34:43 -05:00
|
|
|
"github.com/docker/docker/api/types"
|
2015-11-12 14:55:17 -05:00
|
|
|
"github.com/docker/docker/container"
|
2015-11-18 17:20:54 -05:00
|
|
|
"github.com/docker/docker/dockerversion"
|
2015-07-20 13:57:15 -04:00
|
|
|
"github.com/docker/docker/image"
|
2015-11-18 17:20:54 -05:00
|
|
|
"github.com/docker/docker/layer"
|
2015-11-02 17:37:45 -05:00
|
|
|
"github.com/docker/docker/pkg/archive"
|
|
|
|
"github.com/docker/docker/pkg/ioutils"
|
2015-12-04 16:55:15 -05:00
|
|
|
"github.com/docker/docker/reference"
|
2014-07-31 16:57:21 -04:00
|
|
|
"github.com/docker/docker/runconfig"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Commit creates a new filesystem image from the current state of a container.
|
2015-07-30 17:01:53 -04:00
|
|
|
// The image can optionally be tagged into a repository.
|
2015-12-04 15:34:43 -05:00
|
|
|
func (daemon *Daemon) Commit(name string, c *types.ContainerCommitConfig) (string, error) {
|
2015-12-11 12:39:28 -05:00
|
|
|
container, err := daemon.GetContainer(name)
|
2015-11-06 13:05:00 -05:00
|
|
|
if err != nil {
|
2015-11-18 17:20:54 -05:00
|
|
|
return "", err
|
2015-11-06 13:05:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// It is not possible to commit a running container on Windows
|
|
|
|
if runtime.GOOS == "windows" && container.IsRunning() {
|
2015-11-18 17:20:54 -05:00
|
|
|
return "", fmt.Errorf("Windows does not support commit of a running container")
|
2015-11-06 13:05:00 -05:00
|
|
|
}
|
|
|
|
|
2015-11-12 14:55:17 -05:00
|
|
|
if c.Pause && !container.IsPaused() {
|
2015-11-02 18:39:39 -05:00
|
|
|
daemon.containerPause(container)
|
|
|
|
defer daemon.containerUnpause(container)
|
2014-07-31 16:57:21 -04:00
|
|
|
}
|
|
|
|
|
2015-11-06 13:05:00 -05:00
|
|
|
if c.MergeConfigs {
|
|
|
|
if err := runconfig.Merge(c.Config, container.Config); err != nil {
|
2015-11-18 17:20:54 -05:00
|
|
|
return "", err
|
2015-11-06 13:05:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-02 17:37:45 -05:00
|
|
|
rwTar, err := daemon.exportContainerRw(container)
|
2014-07-31 16:57:21 -04:00
|
|
|
if err != nil {
|
2015-11-18 17:20:54 -05:00
|
|
|
return "", err
|
2014-07-31 16:57:21 -04:00
|
|
|
}
|
2015-04-24 18:03:53 -04:00
|
|
|
defer func() {
|
|
|
|
if rwTar != nil {
|
|
|
|
rwTar.Close()
|
|
|
|
}
|
|
|
|
}()
|
2014-07-31 16:57:21 -04:00
|
|
|
|
2015-11-18 17:20:54 -05:00
|
|
|
var history []image.History
|
|
|
|
rootFS := image.NewRootFS()
|
|
|
|
|
|
|
|
if container.ImageID != "" {
|
|
|
|
img, err := daemon.imageStore.Get(container.ImageID)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
history = img.History
|
|
|
|
rootFS = img.RootFS
|
|
|
|
}
|
|
|
|
|
|
|
|
l, err := daemon.layerStore.Register(rwTar, rootFS.ChainID())
|
2014-07-31 16:57:21 -04:00
|
|
|
if err != nil {
|
2015-11-18 17:20:54 -05:00
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
defer layer.ReleaseAndLog(daemon.layerStore, l)
|
|
|
|
|
|
|
|
h := image.History{
|
|
|
|
Author: c.Author,
|
|
|
|
Created: time.Now().UTC(),
|
|
|
|
CreatedBy: strings.Join(container.Config.Cmd.Slice(), " "),
|
|
|
|
Comment: c.Comment,
|
|
|
|
EmptyLayer: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
if diffID := l.DiffID(); layer.DigestSHA256EmptyTar != diffID {
|
|
|
|
h.EmptyLayer = false
|
|
|
|
rootFS.Append(diffID)
|
|
|
|
}
|
|
|
|
|
|
|
|
history = append(history, h)
|
|
|
|
|
|
|
|
config, err := json.Marshal(&image.Image{
|
|
|
|
V1Image: image.V1Image{
|
|
|
|
DockerVersion: dockerversion.Version,
|
|
|
|
Config: c.Config,
|
|
|
|
Architecture: runtime.GOARCH,
|
|
|
|
OS: runtime.GOOS,
|
|
|
|
Container: container.ID,
|
|
|
|
ContainerConfig: *container.Config,
|
|
|
|
Author: c.Author,
|
|
|
|
Created: h.Created,
|
|
|
|
},
|
|
|
|
RootFS: rootFS,
|
|
|
|
History: history,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
id, err := daemon.imageStore.Create(config)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
if container.ImageID != "" {
|
|
|
|
if err := daemon.imageStore.SetParent(id, container.ImageID); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
2014-07-31 16:57:21 -04:00
|
|
|
}
|
|
|
|
|
2015-06-20 06:40:37 -04:00
|
|
|
if c.Repo != "" {
|
2015-11-18 17:20:54 -05:00
|
|
|
newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if c.Tag != "" {
|
|
|
|
if newTag, err = reference.WithTag(newTag, c.Tag); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
2015-12-01 17:02:02 -05:00
|
|
|
if err := daemon.TagImage(newTag, id.String()); err != nil {
|
2015-11-18 17:20:54 -05:00
|
|
|
return "", err
|
2014-07-31 16:57:21 -04:00
|
|
|
}
|
|
|
|
}
|
2015-11-03 12:33:13 -05:00
|
|
|
|
|
|
|
daemon.LogContainerEvent(container, "commit")
|
2015-11-18 17:20:54 -05:00
|
|
|
return id.String(), nil
|
2014-07-31 16:57:21 -04:00
|
|
|
}
|
2015-11-02 17:37:45 -05:00
|
|
|
|
2015-11-12 14:55:17 -05:00
|
|
|
func (daemon *Daemon) exportContainerRw(container *container.Container) (archive.Archive, error) {
|
2015-11-18 17:20:54 -05:00
|
|
|
if err := daemon.Mount(container); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-11-12 14:55:17 -05:00
|
|
|
archive, err := container.RWLayer.TarStream()
|
2015-11-02 17:37:45 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return ioutils.NewReadCloserWrapper(archive, func() error {
|
2015-11-25 19:39:54 -05:00
|
|
|
archive.Close()
|
2015-11-18 17:20:54 -05:00
|
|
|
return daemon.layerStore.Unmount(container.ID)
|
2015-11-02 17:37:45 -05:00
|
|
|
}),
|
|
|
|
nil
|
|
|
|
}
|