github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/commit.go (about)

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