github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/commit.go (about)

     1  package daemon // import "github.com/docker/docker/daemon"
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/docker/docker/api/types/backend"
    10  	containertypes "github.com/docker/docker/api/types/container"
    11  	"github.com/docker/docker/builder/dockerfile"
    12  	"github.com/docker/docker/errdefs"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  // merge merges two Config, the image container configuration (defaults values),
    17  // and the user container configuration, either passed by the API or generated
    18  // by the cli.
    19  // It will mutate the specified user configuration (userConf) with the image
    20  // configuration where the user configuration is incomplete.
    21  func merge(userConf, imageConf *containertypes.Config) error {
    22  	if userConf.User == "" {
    23  		userConf.User = imageConf.User
    24  	}
    25  	if len(userConf.ExposedPorts) == 0 {
    26  		userConf.ExposedPorts = imageConf.ExposedPorts
    27  	} else if imageConf.ExposedPorts != nil {
    28  		for port := range imageConf.ExposedPorts {
    29  			if _, exists := userConf.ExposedPorts[port]; !exists {
    30  				userConf.ExposedPorts[port] = struct{}{}
    31  			}
    32  		}
    33  	}
    34  
    35  	if len(userConf.Env) == 0 {
    36  		userConf.Env = imageConf.Env
    37  	} else {
    38  		for _, imageEnv := range imageConf.Env {
    39  			found := false
    40  			imageEnvKey := strings.Split(imageEnv, "=")[0]
    41  			for _, userEnv := range userConf.Env {
    42  				userEnvKey := strings.Split(userEnv, "=")[0]
    43  				if isWindows {
    44  					// Case insensitive environment variables on Windows
    45  					imageEnvKey = strings.ToUpper(imageEnvKey)
    46  					userEnvKey = strings.ToUpper(userEnvKey)
    47  				}
    48  				if imageEnvKey == userEnvKey {
    49  					found = true
    50  					break
    51  				}
    52  			}
    53  			if !found {
    54  				userConf.Env = append(userConf.Env, imageEnv)
    55  			}
    56  		}
    57  	}
    58  
    59  	if userConf.Labels == nil {
    60  		userConf.Labels = map[string]string{}
    61  	}
    62  	for l, v := range imageConf.Labels {
    63  		if _, ok := userConf.Labels[l]; !ok {
    64  			userConf.Labels[l] = v
    65  		}
    66  	}
    67  
    68  	if len(userConf.Entrypoint) == 0 {
    69  		if len(userConf.Cmd) == 0 {
    70  			userConf.Cmd = imageConf.Cmd
    71  		}
    72  
    73  		if userConf.Entrypoint == nil {
    74  			userConf.Entrypoint = imageConf.Entrypoint
    75  		}
    76  	}
    77  	if imageConf.Healthcheck != nil {
    78  		if userConf.Healthcheck == nil {
    79  			userConf.Healthcheck = imageConf.Healthcheck
    80  		} else {
    81  			if len(userConf.Healthcheck.Test) == 0 {
    82  				userConf.Healthcheck.Test = imageConf.Healthcheck.Test
    83  			}
    84  			if userConf.Healthcheck.Interval == 0 {
    85  				userConf.Healthcheck.Interval = imageConf.Healthcheck.Interval
    86  			}
    87  			if userConf.Healthcheck.Timeout == 0 {
    88  				userConf.Healthcheck.Timeout = imageConf.Healthcheck.Timeout
    89  			}
    90  			if userConf.Healthcheck.StartPeriod == 0 {
    91  				userConf.Healthcheck.StartPeriod = imageConf.Healthcheck.StartPeriod
    92  			}
    93  			if userConf.Healthcheck.Retries == 0 {
    94  				userConf.Healthcheck.Retries = imageConf.Healthcheck.Retries
    95  			}
    96  		}
    97  	}
    98  
    99  	if userConf.WorkingDir == "" {
   100  		userConf.WorkingDir = imageConf.WorkingDir
   101  	}
   102  	if len(userConf.Volumes) == 0 {
   103  		userConf.Volumes = imageConf.Volumes
   104  	} else {
   105  		for k, v := range imageConf.Volumes {
   106  			userConf.Volumes[k] = v
   107  		}
   108  	}
   109  
   110  	if userConf.StopSignal == "" {
   111  		userConf.StopSignal = imageConf.StopSignal
   112  	}
   113  	return nil
   114  }
   115  
   116  // CreateImageFromContainer creates a new image from a container. The container
   117  // config will be updated by applying the change set to the custom config, then
   118  // applying that config over the existing container config.
   119  func (daemon *Daemon) CreateImageFromContainer(name string, c *backend.CreateImageConfig) (string, error) {
   120  	start := time.Now()
   121  	container, err := daemon.GetContainer(name)
   122  	if err != nil {
   123  		return "", err
   124  	}
   125  
   126  	// It is not possible to commit a running container on Windows
   127  	if isWindows && container.IsRunning() {
   128  		return "", errors.Errorf("%+v does not support commit of a running container", runtime.GOOS)
   129  	}
   130  
   131  	if container.IsDead() {
   132  		err := fmt.Errorf("You cannot commit container %s which is Dead", container.ID)
   133  		return "", errdefs.Conflict(err)
   134  	}
   135  
   136  	if container.IsRemovalInProgress() {
   137  		err := fmt.Errorf("You cannot commit container %s which is being removed", container.ID)
   138  		return "", errdefs.Conflict(err)
   139  	}
   140  
   141  	if c.Pause && !container.IsPaused() {
   142  		daemon.containerPause(container)
   143  		defer daemon.containerUnpause(container)
   144  	}
   145  
   146  	if c.Config == nil {
   147  		c.Config = container.Config
   148  	}
   149  	newConfig, err := dockerfile.BuildFromConfig(c.Config, c.Changes, container.OS)
   150  	if err != nil {
   151  		return "", err
   152  	}
   153  	if err := merge(newConfig, container.Config); err != nil {
   154  		return "", err
   155  	}
   156  
   157  	id, err := daemon.imageService.CommitImage(backend.CommitConfig{
   158  		Author:              c.Author,
   159  		Comment:             c.Comment,
   160  		Config:              newConfig,
   161  		ContainerConfig:     container.Config,
   162  		ContainerID:         container.ID,
   163  		ContainerMountLabel: container.MountLabel,
   164  		ContainerOS:         container.OS,
   165  		ParentImageID:       string(container.ImageID),
   166  	})
   167  	if err != nil {
   168  		return "", err
   169  	}
   170  
   171  	var imageRef string
   172  	if c.Repo != "" {
   173  		imageRef, err = daemon.imageService.TagImage(string(id), c.Repo, c.Tag)
   174  		if err != nil {
   175  			return "", err
   176  		}
   177  	}
   178  	daemon.LogContainerEventWithAttributes(container, "commit", map[string]string{
   179  		"comment":  c.Comment,
   180  		"imageID":  id.String(),
   181  		"imageRef": imageRef,
   182  	})
   183  	containerActions.WithValues("commit").UpdateSince(start)
   184  	return id.String(), nil
   185  }