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