github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+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 }