github.com/uppal0016/docker_new@v0.0.0-20240123060250-1c98be13ac2c/daemon/commit.go (about) 1 package daemon 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "runtime" 7 "strings" 8 "time" 9 10 "github.com/docker/docker/api/types/backend" 11 "github.com/docker/docker/builder/dockerfile" 12 "github.com/docker/docker/container" 13 "github.com/docker/docker/dockerversion" 14 "github.com/docker/docker/image" 15 "github.com/docker/docker/layer" 16 "github.com/docker/docker/pkg/archive" 17 "github.com/docker/docker/pkg/ioutils" 18 "github.com/docker/docker/reference" 19 containertypes "github.com/docker/engine-api/types/container" 20 "github.com/docker/go-connections/nat" 21 ) 22 23 // merge merges two Config, the image container configuration (defaults values), 24 // and the user container configuration, either passed by the API or generated 25 // by the cli. 26 // It will mutate the specified user configuration (userConf) with the image 27 // configuration where the user configuration is incomplete. 28 func merge(userConf, imageConf *containertypes.Config) error { 29 if userConf.User == "" { 30 userConf.User = imageConf.User 31 } 32 if len(userConf.ExposedPorts) == 0 { 33 userConf.ExposedPorts = imageConf.ExposedPorts 34 } else if imageConf.ExposedPorts != nil { 35 if userConf.ExposedPorts == nil { 36 userConf.ExposedPorts = make(nat.PortSet) 37 } 38 for port := range imageConf.ExposedPorts { 39 if _, exists := userConf.ExposedPorts[port]; !exists { 40 userConf.ExposedPorts[port] = struct{}{} 41 } 42 } 43 } 44 45 if len(userConf.Env) == 0 { 46 userConf.Env = imageConf.Env 47 } else { 48 for _, imageEnv := range imageConf.Env { 49 found := false 50 imageEnvKey := strings.Split(imageEnv, "=")[0] 51 for _, userEnv := range userConf.Env { 52 userEnvKey := strings.Split(userEnv, "=")[0] 53 if imageEnvKey == userEnvKey { 54 found = true 55 break 56 } 57 } 58 if !found { 59 userConf.Env = append(userConf.Env, imageEnv) 60 } 61 } 62 } 63 64 if userConf.Labels == nil { 65 userConf.Labels = map[string]string{} 66 } 67 if imageConf.Labels != nil { 68 for l := range userConf.Labels { 69 imageConf.Labels[l] = userConf.Labels[l] 70 } 71 userConf.Labels = imageConf.Labels 72 } 73 74 if len(userConf.Entrypoint) == 0 { 75 if len(userConf.Cmd) == 0 { 76 userConf.Cmd = imageConf.Cmd 77 } 78 79 if userConf.Entrypoint == nil { 80 userConf.Entrypoint = imageConf.Entrypoint 81 } 82 } 83 if userConf.WorkingDir == "" { 84 userConf.WorkingDir = imageConf.WorkingDir 85 } 86 if len(userConf.Volumes) == 0 { 87 userConf.Volumes = imageConf.Volumes 88 } else { 89 for k, v := range imageConf.Volumes { 90 userConf.Volumes[k] = v 91 } 92 } 93 94 if userConf.StopSignal == "" { 95 userConf.StopSignal = imageConf.StopSignal 96 } 97 return nil 98 } 99 100 // Commit creates a new filesystem image from the current state of a container. 101 // The image can optionally be tagged into a repository. 102 func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (string, error) { 103 container, err := daemon.GetContainer(name) 104 if err != nil { 105 return "", err 106 } 107 108 // It is not possible to commit a running container on Windows 109 if runtime.GOOS == "windows" && container.IsRunning() { 110 return "", fmt.Errorf("Windows does not support commit of a running container") 111 } 112 113 if c.Pause && !container.IsPaused() { 114 daemon.containerPause(container) 115 defer daemon.containerUnpause(container) 116 } 117 118 newConfig, err := dockerfile.BuildFromConfig(c.Config, c.Changes) 119 if err != nil { 120 return "", err 121 } 122 123 if c.MergeConfigs { 124 if err := merge(newConfig, container.Config); err != nil { 125 return "", err 126 } 127 } 128 129 rwTar, err := daemon.exportContainerRw(container) 130 if err != nil { 131 return "", err 132 } 133 defer func() { 134 if rwTar != nil { 135 rwTar.Close() 136 } 137 }() 138 139 var history []image.History 140 rootFS := image.NewRootFS() 141 osVersion := "" 142 var osFeatures []string 143 144 if container.ImageID != "" { 145 img, err := daemon.imageStore.Get(container.ImageID) 146 if err != nil { 147 return "", err 148 } 149 history = img.History 150 rootFS = img.RootFS 151 osVersion = img.OSVersion 152 osFeatures = img.OSFeatures 153 } 154 155 l, err := daemon.layerStore.Register(rwTar, rootFS.ChainID()) 156 if err != nil { 157 return "", err 158 } 159 defer layer.ReleaseAndLog(daemon.layerStore, l) 160 161 h := image.History{ 162 Author: c.Author, 163 Created: time.Now().UTC(), 164 CreatedBy: strings.Join(container.Config.Cmd, " "), 165 Comment: c.Comment, 166 EmptyLayer: true, 167 } 168 169 if diffID := l.DiffID(); layer.DigestSHA256EmptyTar != diffID { 170 h.EmptyLayer = false 171 rootFS.Append(diffID) 172 } 173 174 history = append(history, h) 175 176 config, err := json.Marshal(&image.Image{ 177 V1Image: image.V1Image{ 178 DockerVersion: dockerversion.Version, 179 Config: newConfig, 180 Architecture: runtime.GOARCH, 181 OS: runtime.GOOS, 182 Container: container.ID, 183 ContainerConfig: *container.Config, 184 Author: c.Author, 185 Created: h.Created, 186 }, 187 RootFS: rootFS, 188 History: history, 189 OSFeatures: osFeatures, 190 OSVersion: osVersion, 191 }) 192 193 if err != nil { 194 return "", err 195 } 196 197 id, err := daemon.imageStore.Create(config) 198 if err != nil { 199 return "", err 200 } 201 202 if container.ImageID != "" { 203 if err := daemon.imageStore.SetParent(id, container.ImageID); err != nil { 204 return "", err 205 } 206 } 207 208 if c.Repo != "" { 209 newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer 210 if err != nil { 211 return "", err 212 } 213 if c.Tag != "" { 214 if newTag, err = reference.WithTag(newTag, c.Tag); err != nil { 215 return "", err 216 } 217 } 218 if err := daemon.TagImageWithReference(id, newTag); err != nil { 219 return "", err 220 } 221 } 222 223 attributes := map[string]string{ 224 "comment": c.Comment, 225 } 226 daemon.LogContainerEventWithAttributes(container, "commit", attributes) 227 return id.String(), nil 228 } 229 230 func (daemon *Daemon) exportContainerRw(container *container.Container) (archive.Archive, error) { 231 if err := daemon.Mount(container); err != nil { 232 return nil, err 233 } 234 235 archive, err := container.RWLayer.TarStream() 236 if err != nil { 237 daemon.Unmount(container) // logging is already handled in the `Unmount` function 238 return nil, err 239 } 240 return ioutils.NewReadCloserWrapper(archive, func() error { 241 archive.Close() 242 return container.RWLayer.Unmount() 243 }), 244 nil 245 }