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 }