github.com/sams1990/dockerrepo@v17.12.1-ce-rc2+incompatible/daemon/commit.go (about)

     1  package daemon
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"runtime"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/docker/distribution/reference"
    12  	"github.com/docker/docker/api/types/backend"
    13  	containertypes "github.com/docker/docker/api/types/container"
    14  	"github.com/docker/docker/builder/dockerfile"
    15  	"github.com/docker/docker/container"
    16  	"github.com/docker/docker/image"
    17  	"github.com/docker/docker/layer"
    18  	"github.com/docker/docker/pkg/ioutils"
    19  	"github.com/pkg/errors"
    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  		for port := range imageConf.ExposedPorts {
    35  			if _, exists := userConf.ExposedPorts[port]; !exists {
    36  				userConf.ExposedPorts[port] = struct{}{}
    37  			}
    38  		}
    39  	}
    40  
    41  	if len(userConf.Env) == 0 {
    42  		userConf.Env = imageConf.Env
    43  	} else {
    44  		for _, imageEnv := range imageConf.Env {
    45  			found := false
    46  			imageEnvKey := strings.Split(imageEnv, "=")[0]
    47  			for _, userEnv := range userConf.Env {
    48  				userEnvKey := strings.Split(userEnv, "=")[0]
    49  				if runtime.GOOS == "windows" {
    50  					// Case insensitive environment variables on Windows
    51  					imageEnvKey = strings.ToUpper(imageEnvKey)
    52  					userEnvKey = strings.ToUpper(userEnvKey)
    53  				}
    54  				if imageEnvKey == userEnvKey {
    55  					found = true
    56  					break
    57  				}
    58  			}
    59  			if !found {
    60  				userConf.Env = append(userConf.Env, imageEnv)
    61  			}
    62  		}
    63  	}
    64  
    65  	if userConf.Labels == nil {
    66  		userConf.Labels = map[string]string{}
    67  	}
    68  	for l, v := range imageConf.Labels {
    69  		if _, ok := userConf.Labels[l]; !ok {
    70  			userConf.Labels[l] = v
    71  		}
    72  	}
    73  
    74  	if len(userConf.Entrypoint) == 0 {
    75  		if len(userConf.Cmd) == 0 {
    76  			userConf.Cmd = imageConf.Cmd
    77  			userConf.ArgsEscaped = imageConf.ArgsEscaped
    78  		}
    79  
    80  		if userConf.Entrypoint == nil {
    81  			userConf.Entrypoint = imageConf.Entrypoint
    82  		}
    83  	}
    84  	if imageConf.Healthcheck != nil {
    85  		if userConf.Healthcheck == nil {
    86  			userConf.Healthcheck = imageConf.Healthcheck
    87  		} else {
    88  			if len(userConf.Healthcheck.Test) == 0 {
    89  				userConf.Healthcheck.Test = imageConf.Healthcheck.Test
    90  			}
    91  			if userConf.Healthcheck.Interval == 0 {
    92  				userConf.Healthcheck.Interval = imageConf.Healthcheck.Interval
    93  			}
    94  			if userConf.Healthcheck.Timeout == 0 {
    95  				userConf.Healthcheck.Timeout = imageConf.Healthcheck.Timeout
    96  			}
    97  			if userConf.Healthcheck.StartPeriod == 0 {
    98  				userConf.Healthcheck.StartPeriod = imageConf.Healthcheck.StartPeriod
    99  			}
   100  			if userConf.Healthcheck.Retries == 0 {
   101  				userConf.Healthcheck.Retries = imageConf.Healthcheck.Retries
   102  			}
   103  		}
   104  	}
   105  
   106  	if userConf.WorkingDir == "" {
   107  		userConf.WorkingDir = imageConf.WorkingDir
   108  	}
   109  	if len(userConf.Volumes) == 0 {
   110  		userConf.Volumes = imageConf.Volumes
   111  	} else {
   112  		for k, v := range imageConf.Volumes {
   113  			userConf.Volumes[k] = v
   114  		}
   115  	}
   116  
   117  	if userConf.StopSignal == "" {
   118  		userConf.StopSignal = imageConf.StopSignal
   119  	}
   120  	return nil
   121  }
   122  
   123  // Commit creates a new filesystem image from the current state of a container.
   124  // The image can optionally be tagged into a repository.
   125  func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (string, error) {
   126  	start := time.Now()
   127  	container, err := daemon.GetContainer(name)
   128  	if err != nil {
   129  		return "", err
   130  	}
   131  
   132  	// It is not possible to commit a running container on Windows
   133  	if (runtime.GOOS == "windows") && container.IsRunning() {
   134  		return "", errors.Errorf("%+v does not support commit of a running container", runtime.GOOS)
   135  	}
   136  
   137  	if container.IsDead() {
   138  		err := fmt.Errorf("You cannot commit container %s which is Dead", container.ID)
   139  		return "", stateConflictError{err}
   140  	}
   141  
   142  	if container.IsRemovalInProgress() {
   143  		err := fmt.Errorf("You cannot commit container %s which is being removed", container.ID)
   144  		return "", stateConflictError{err}
   145  	}
   146  
   147  	if c.Pause && !container.IsPaused() {
   148  		daemon.containerPause(container)
   149  		defer daemon.containerUnpause(container)
   150  	}
   151  
   152  	newConfig, err := dockerfile.BuildFromConfig(c.Config, c.Changes)
   153  	if err != nil {
   154  		return "", err
   155  	}
   156  
   157  	if c.MergeConfigs {
   158  		if err := merge(newConfig, container.Config); err != nil {
   159  			return "", err
   160  		}
   161  	}
   162  
   163  	rwTar, err := daemon.exportContainerRw(container)
   164  	if err != nil {
   165  		return "", err
   166  	}
   167  	defer func() {
   168  		if rwTar != nil {
   169  			rwTar.Close()
   170  		}
   171  	}()
   172  
   173  	var parent *image.Image
   174  	if container.ImageID == "" {
   175  		parent = new(image.Image)
   176  		parent.RootFS = image.NewRootFS()
   177  	} else {
   178  		parent, err = daemon.stores[container.OS].imageStore.Get(container.ImageID)
   179  		if err != nil {
   180  			return "", err
   181  		}
   182  	}
   183  
   184  	l, err := daemon.stores[container.OS].layerStore.Register(rwTar, parent.RootFS.ChainID(), layer.OS(container.OS))
   185  	if err != nil {
   186  		return "", err
   187  	}
   188  	defer layer.ReleaseAndLog(daemon.stores[container.OS].layerStore, l)
   189  
   190  	containerConfig := c.ContainerConfig
   191  	if containerConfig == nil {
   192  		containerConfig = container.Config
   193  	}
   194  	cc := image.ChildConfig{
   195  		ContainerID:     container.ID,
   196  		Author:          c.Author,
   197  		Comment:         c.Comment,
   198  		ContainerConfig: containerConfig,
   199  		Config:          newConfig,
   200  		DiffID:          l.DiffID(),
   201  	}
   202  	config, err := json.Marshal(image.NewChildImage(parent, cc, container.OS))
   203  	if err != nil {
   204  		return "", err
   205  	}
   206  
   207  	id, err := daemon.stores[container.OS].imageStore.Create(config)
   208  	if err != nil {
   209  		return "", err
   210  	}
   211  
   212  	if container.ImageID != "" {
   213  		if err := daemon.stores[container.OS].imageStore.SetParent(id, container.ImageID); err != nil {
   214  			return "", err
   215  		}
   216  	}
   217  
   218  	imageRef := ""
   219  	if c.Repo != "" {
   220  		newTag, err := reference.ParseNormalizedNamed(c.Repo) // todo: should move this to API layer
   221  		if err != nil {
   222  			return "", err
   223  		}
   224  		if !reference.IsNameOnly(newTag) {
   225  			return "", errors.Errorf("unexpected repository name: %s", c.Repo)
   226  		}
   227  		if c.Tag != "" {
   228  			if newTag, err = reference.WithTag(newTag, c.Tag); err != nil {
   229  				return "", err
   230  			}
   231  		}
   232  		if err := daemon.TagImageWithReference(id, container.OS, newTag); err != nil {
   233  			return "", err
   234  		}
   235  		imageRef = reference.FamiliarString(newTag)
   236  	}
   237  
   238  	attributes := map[string]string{
   239  		"comment":  c.Comment,
   240  		"imageID":  id.String(),
   241  		"imageRef": imageRef,
   242  	}
   243  	daemon.LogContainerEventWithAttributes(container, "commit", attributes)
   244  	containerActions.WithValues("commit").UpdateSince(start)
   245  	return id.String(), nil
   246  }
   247  
   248  func (daemon *Daemon) exportContainerRw(container *container.Container) (arch io.ReadCloser, err error) {
   249  	rwlayer, err := daemon.stores[container.OS].layerStore.GetRWLayer(container.ID)
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  	defer func() {
   254  		if err != nil {
   255  			daemon.stores[container.OS].layerStore.ReleaseRWLayer(rwlayer)
   256  		}
   257  	}()
   258  
   259  	// TODO: this mount call is not necessary as we assume that TarStream() should
   260  	// mount the layer if needed. But the Diff() function for windows requests that
   261  	// the layer should be mounted when calling it. So we reserve this mount call
   262  	// until windows driver can implement Diff() interface correctly.
   263  	_, err = rwlayer.Mount(container.GetMountLabel())
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	archive, err := rwlayer.TarStream()
   269  	if err != nil {
   270  		rwlayer.Unmount()
   271  		return nil, err
   272  	}
   273  	return ioutils.NewReadCloserWrapper(archive, func() error {
   274  			archive.Close()
   275  			err = rwlayer.Unmount()
   276  			daemon.stores[container.OS].layerStore.ReleaseRWLayer(rwlayer)
   277  			return err
   278  		}),
   279  		nil
   280  }