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