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-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"
|
2016-01-04 19:05:26 -05:00
|
|
|
"github.com/docker/engine-api/types"
|
|
|
|
containertypes "github.com/docker/engine-api/types/container"
|
2016-01-05 11:48:09 -05:00
|
|
|
"github.com/docker/go-connections/nat"
|
2014-07-31 16:57:21 -04:00
|
|
|
)
|
|
|
|
|
2016-01-05 11:48:09 -05:00
|
|
|
// merge merges two Config, the image container configuration (defaults values),
|
|
|
|
// and the user container configuration, either passed by the API or generated
|
|
|
|
// by the cli.
|
|
|
|
// It will mutate the specified user configuration (userConf) with the image
|
|
|
|
// configuration where the user configuration is incomplete.
|
|
|
|
func merge(userConf, imageConf *containertypes.Config) error {
|
|
|
|
if userConf.User == "" {
|
|
|
|
userConf.User = imageConf.User
|
|
|
|
}
|
|
|
|
if len(userConf.ExposedPorts) == 0 {
|
|
|
|
userConf.ExposedPorts = imageConf.ExposedPorts
|
|
|
|
} else if imageConf.ExposedPorts != nil {
|
|
|
|
if userConf.ExposedPorts == nil {
|
|
|
|
userConf.ExposedPorts = make(nat.PortSet)
|
|
|
|
}
|
|
|
|
for port := range imageConf.ExposedPorts {
|
|
|
|
if _, exists := userConf.ExposedPorts[port]; !exists {
|
|
|
|
userConf.ExposedPorts[port] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(userConf.Env) == 0 {
|
|
|
|
userConf.Env = imageConf.Env
|
|
|
|
} else {
|
|
|
|
for _, imageEnv := range imageConf.Env {
|
|
|
|
found := false
|
|
|
|
imageEnvKey := strings.Split(imageEnv, "=")[0]
|
|
|
|
for _, userEnv := range userConf.Env {
|
|
|
|
userEnvKey := strings.Split(userEnv, "=")[0]
|
|
|
|
if imageEnvKey == userEnvKey {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
userConf.Env = append(userConf.Env, imageEnv)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if userConf.Labels == nil {
|
|
|
|
userConf.Labels = map[string]string{}
|
|
|
|
}
|
|
|
|
if imageConf.Labels != nil {
|
|
|
|
for l := range userConf.Labels {
|
|
|
|
imageConf.Labels[l] = userConf.Labels[l]
|
|
|
|
}
|
|
|
|
userConf.Labels = imageConf.Labels
|
|
|
|
}
|
|
|
|
|
|
|
|
if userConf.Entrypoint.Len() == 0 {
|
|
|
|
if userConf.Cmd.Len() == 0 {
|
|
|
|
userConf.Cmd = imageConf.Cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
if userConf.Entrypoint == nil {
|
|
|
|
userConf.Entrypoint = imageConf.Entrypoint
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if userConf.WorkingDir == "" {
|
|
|
|
userConf.WorkingDir = imageConf.WorkingDir
|
|
|
|
}
|
|
|
|
if len(userConf.Volumes) == 0 {
|
|
|
|
userConf.Volumes = imageConf.Volumes
|
|
|
|
} else {
|
|
|
|
for k, v := range imageConf.Volumes {
|
|
|
|
userConf.Volumes[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-31 16:57:21 -04:00
|
|
|
// 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 {
|
2016-01-05 11:48:09 -05:00
|
|
|
if err := 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
|
|
|
|
2016-01-07 17:14:05 -05:00
|
|
|
attributes := map[string]string{
|
|
|
|
"comment": c.Comment,
|
|
|
|
}
|
|
|
|
daemon.LogContainerEventWithAttributes(container, "commit", attributes)
|
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-12-16 17:13:50 -05:00
|
|
|
return container.RWLayer.Unmount()
|
2015-11-02 17:37:45 -05:00
|
|
|
}),
|
|
|
|
nil
|
|
|
|
}
|