github.com/boynux/docker@v1.11.0-rc4/daemon/commit.go (about)

     1  package daemon
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"runtime"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/docker/docker/container"
    11  	"github.com/docker/docker/dockerversion"
    12  	"github.com/docker/docker/image"
    13  	"github.com/docker/docker/layer"
    14  	"github.com/docker/docker/pkg/archive"
    15  	"github.com/docker/docker/pkg/ioutils"
    16  	"github.com/docker/docker/reference"
    17  	"github.com/docker/engine-api/types"
    18  	containertypes "github.com/docker/engine-api/types/container"
    19  	"github.com/docker/go-connections/nat"
    20  )
    21  
    22  // merge merges two Config, the image container configuration (defaults values),
    23  // and the user container configuration, either passed by the API or generated
    24  // by the cli.
    25  // It will mutate the specified user configuration (userConf) with the image
    26  // configuration where the user configuration is incomplete.
    27  func merge(userConf, imageConf *containertypes.Config) error {
    28  	if userConf.User == "" {
    29  		userConf.User = imageConf.User
    30  	}
    31  	if len(userConf.ExposedPorts) == 0 {
    32  		userConf.ExposedPorts = imageConf.ExposedPorts
    33  	} else if imageConf.ExposedPorts != nil {
    34  		if userConf.ExposedPorts == nil {
    35  			userConf.ExposedPorts = make(nat.PortSet)
    36  		}
    37  		for port := range imageConf.ExposedPorts {
    38  			if _, exists := userConf.ExposedPorts[port]; !exists {
    39  				userConf.ExposedPorts[port] = struct{}{}
    40  			}
    41  		}
    42  	}
    43  
    44  	if len(userConf.Env) == 0 {
    45  		userConf.Env = imageConf.Env
    46  	} else {
    47  		for _, imageEnv := range imageConf.Env {
    48  			found := false
    49  			imageEnvKey := strings.Split(imageEnv, "=")[0]
    50  			for _, userEnv := range userConf.Env {
    51  				userEnvKey := strings.Split(userEnv, "=")[0]
    52  				if imageEnvKey == userEnvKey {
    53  					found = true
    54  					break
    55  				}
    56  			}
    57  			if !found {
    58  				userConf.Env = append(userConf.Env, imageEnv)
    59  			}
    60  		}
    61  	}
    62  
    63  	if userConf.Labels == nil {
    64  		userConf.Labels = map[string]string{}
    65  	}
    66  	if imageConf.Labels != nil {
    67  		for l := range userConf.Labels {
    68  			imageConf.Labels[l] = userConf.Labels[l]
    69  		}
    70  		userConf.Labels = imageConf.Labels
    71  	}
    72  
    73  	if len(userConf.Entrypoint) == 0 {
    74  		if len(userConf.Cmd) == 0 {
    75  			userConf.Cmd = imageConf.Cmd
    76  		}
    77  
    78  		if userConf.Entrypoint == nil {
    79  			userConf.Entrypoint = imageConf.Entrypoint
    80  		}
    81  	}
    82  	if userConf.WorkingDir == "" {
    83  		userConf.WorkingDir = imageConf.WorkingDir
    84  	}
    85  	if len(userConf.Volumes) == 0 {
    86  		userConf.Volumes = imageConf.Volumes
    87  	} else {
    88  		for k, v := range imageConf.Volumes {
    89  			userConf.Volumes[k] = v
    90  		}
    91  	}
    92  
    93  	if userConf.StopSignal == "" {
    94  		userConf.StopSignal = imageConf.StopSignal
    95  	}
    96  	return nil
    97  }
    98  
    99  // Commit creates a new filesystem image from the current state of a container.
   100  // The image can optionally be tagged into a repository.
   101  func (daemon *Daemon) Commit(name string, c *types.ContainerCommitConfig) (string, error) {
   102  	container, err := daemon.GetContainer(name)
   103  	if err != nil {
   104  		return "", err
   105  	}
   106  
   107  	// It is not possible to commit a running container on Windows
   108  	if runtime.GOOS == "windows" && container.IsRunning() {
   109  		return "", fmt.Errorf("Windows does not support commit of a running container")
   110  	}
   111  
   112  	if c.Pause && !container.IsPaused() {
   113  		daemon.containerPause(container)
   114  		defer daemon.containerUnpause(container)
   115  	}
   116  
   117  	if c.MergeConfigs {
   118  		if err := merge(c.Config, container.Config); err != nil {
   119  			return "", err
   120  		}
   121  	}
   122  
   123  	rwTar, err := daemon.exportContainerRw(container)
   124  	if err != nil {
   125  		return "", err
   126  	}
   127  	defer func() {
   128  		if rwTar != nil {
   129  			rwTar.Close()
   130  		}
   131  	}()
   132  
   133  	var history []image.History
   134  	rootFS := image.NewRootFS()
   135  
   136  	if container.ImageID != "" {
   137  		img, err := daemon.imageStore.Get(container.ImageID)
   138  		if err != nil {
   139  			return "", err
   140  		}
   141  		history = img.History
   142  		rootFS = img.RootFS
   143  	}
   144  
   145  	l, err := daemon.layerStore.Register(rwTar, rootFS.ChainID())
   146  	if err != nil {
   147  		return "", err
   148  	}
   149  	defer layer.ReleaseAndLog(daemon.layerStore, l)
   150  
   151  	h := image.History{
   152  		Author:     c.Author,
   153  		Created:    time.Now().UTC(),
   154  		CreatedBy:  strings.Join(container.Config.Cmd, " "),
   155  		Comment:    c.Comment,
   156  		EmptyLayer: true,
   157  	}
   158  
   159  	if diffID := l.DiffID(); layer.DigestSHA256EmptyTar != diffID {
   160  		h.EmptyLayer = false
   161  		rootFS.Append(diffID)
   162  	}
   163  
   164  	history = append(history, h)
   165  
   166  	config, err := json.Marshal(&image.Image{
   167  		V1Image: image.V1Image{
   168  			DockerVersion:   dockerversion.Version,
   169  			Config:          c.Config,
   170  			Architecture:    runtime.GOARCH,
   171  			OS:              runtime.GOOS,
   172  			Container:       container.ID,
   173  			ContainerConfig: *container.Config,
   174  			Author:          c.Author,
   175  			Created:         h.Created,
   176  		},
   177  		RootFS:  rootFS,
   178  		History: history,
   179  	})
   180  
   181  	if err != nil {
   182  		return "", err
   183  	}
   184  
   185  	id, err := daemon.imageStore.Create(config)
   186  	if err != nil {
   187  		return "", err
   188  	}
   189  
   190  	if container.ImageID != "" {
   191  		if err := daemon.imageStore.SetParent(id, container.ImageID); err != nil {
   192  			return "", err
   193  		}
   194  	}
   195  
   196  	if c.Repo != "" {
   197  		newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer
   198  		if err != nil {
   199  			return "", err
   200  		}
   201  		if c.Tag != "" {
   202  			if newTag, err = reference.WithTag(newTag, c.Tag); err != nil {
   203  				return "", err
   204  			}
   205  		}
   206  		if err := daemon.TagImage(newTag, id.String()); err != nil {
   207  			return "", err
   208  		}
   209  	}
   210  
   211  	attributes := map[string]string{
   212  		"comment": c.Comment,
   213  	}
   214  	daemon.LogContainerEventWithAttributes(container, "commit", attributes)
   215  	return id.String(), nil
   216  }
   217  
   218  func (daemon *Daemon) exportContainerRw(container *container.Container) (archive.Archive, error) {
   219  	if err := daemon.Mount(container); err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	archive, err := container.RWLayer.TarStream()
   224  	if err != nil {
   225  		daemon.Unmount(container) // logging is already handled in the `Unmount` function
   226  		return nil, err
   227  	}
   228  	return ioutils.NewReadCloserWrapper(archive, func() error {
   229  			archive.Close()
   230  			return container.RWLayer.Unmount()
   231  		}),
   232  		nil
   233  }