github.com/brahmaroutu/docker@v1.2.1-0.20160809185609-eb28dde01f16/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 userConf.ArgsEscaped = imageConf.ArgsEscaped 78 } 79 80 if userConf.Entrypoint == nil { 81 userConf.Entrypoint = imageConf.Entrypoint 82 } 83 } 84 if imageConf.Healthcheck != nil { 85 if userConf.Healthcheck == nil { 86 userConf.Healthcheck = imageConf.Healthcheck 87 } else { 88 if len(userConf.Healthcheck.Test) == 0 { 89 userConf.Healthcheck.Test = imageConf.Healthcheck.Test 90 } 91 if userConf.Healthcheck.Interval == 0 { 92 userConf.Healthcheck.Interval = imageConf.Healthcheck.Interval 93 } 94 if userConf.Healthcheck.Timeout == 0 { 95 userConf.Healthcheck.Timeout = imageConf.Healthcheck.Timeout 96 } 97 if userConf.Healthcheck.Retries == 0 { 98 userConf.Healthcheck.Retries = imageConf.Healthcheck.Retries 99 } 100 } 101 } 102 103 if userConf.WorkingDir == "" { 104 userConf.WorkingDir = imageConf.WorkingDir 105 } 106 if len(userConf.Volumes) == 0 { 107 userConf.Volumes = imageConf.Volumes 108 } else { 109 for k, v := range imageConf.Volumes { 110 userConf.Volumes[k] = v 111 } 112 } 113 114 if userConf.StopSignal == "" { 115 userConf.StopSignal = imageConf.StopSignal 116 } 117 return nil 118 } 119 120 // Commit creates a new filesystem image from the current state of a container. 121 // The image can optionally be tagged into a repository. 122 func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (string, error) { 123 container, err := daemon.GetContainer(name) 124 if err != nil { 125 return "", err 126 } 127 128 // It is not possible to commit a running container on Windows 129 if runtime.GOOS == "windows" && container.IsRunning() { 130 return "", fmt.Errorf("Windows does not support commit of a running container") 131 } 132 133 if c.Pause && !container.IsPaused() { 134 daemon.containerPause(container) 135 defer daemon.containerUnpause(container) 136 } 137 138 newConfig, err := dockerfile.BuildFromConfig(c.Config, c.Changes) 139 if err != nil { 140 return "", err 141 } 142 143 if c.MergeConfigs { 144 if err := merge(newConfig, container.Config); err != nil { 145 return "", err 146 } 147 } 148 149 rwTar, err := daemon.exportContainerRw(container) 150 if err != nil { 151 return "", err 152 } 153 defer func() { 154 if rwTar != nil { 155 rwTar.Close() 156 } 157 }() 158 159 var history []image.History 160 rootFS := image.NewRootFS() 161 osVersion := "" 162 var osFeatures []string 163 164 if container.ImageID != "" { 165 img, err := daemon.imageStore.Get(container.ImageID) 166 if err != nil { 167 return "", err 168 } 169 history = img.History 170 rootFS = img.RootFS 171 osVersion = img.OSVersion 172 osFeatures = img.OSFeatures 173 } 174 175 l, err := daemon.layerStore.Register(rwTar, rootFS.ChainID()) 176 if err != nil { 177 return "", err 178 } 179 defer layer.ReleaseAndLog(daemon.layerStore, l) 180 181 h := image.History{ 182 Author: c.Author, 183 Created: time.Now().UTC(), 184 CreatedBy: strings.Join(container.Config.Cmd, " "), 185 Comment: c.Comment, 186 EmptyLayer: true, 187 } 188 189 if diffID := l.DiffID(); layer.DigestSHA256EmptyTar != diffID { 190 h.EmptyLayer = false 191 rootFS.Append(diffID) 192 } 193 194 history = append(history, h) 195 196 config, err := json.Marshal(&image.Image{ 197 V1Image: image.V1Image{ 198 DockerVersion: dockerversion.Version, 199 Config: newConfig, 200 Architecture: runtime.GOARCH, 201 OS: runtime.GOOS, 202 Container: container.ID, 203 ContainerConfig: *container.Config, 204 Author: c.Author, 205 Created: h.Created, 206 }, 207 RootFS: rootFS, 208 History: history, 209 OSFeatures: osFeatures, 210 OSVersion: osVersion, 211 }) 212 213 if err != nil { 214 return "", err 215 } 216 217 id, err := daemon.imageStore.Create(config) 218 if err != nil { 219 return "", err 220 } 221 222 if container.ImageID != "" { 223 if err := daemon.imageStore.SetParent(id, container.ImageID); err != nil { 224 return "", err 225 } 226 } 227 228 if c.Repo != "" { 229 newTag, err := reference.WithName(c.Repo) // todo: should move this to API layer 230 if err != nil { 231 return "", err 232 } 233 if c.Tag != "" { 234 if newTag, err = reference.WithTag(newTag, c.Tag); err != nil { 235 return "", err 236 } 237 } 238 if err := daemon.TagImageWithReference(id, newTag); err != nil { 239 return "", err 240 } 241 } 242 243 attributes := map[string]string{ 244 "comment": c.Comment, 245 } 246 daemon.LogContainerEventWithAttributes(container, "commit", attributes) 247 return id.String(), nil 248 } 249 250 func (daemon *Daemon) exportContainerRw(container *container.Container) (archive.Archive, error) { 251 if err := daemon.Mount(container); err != nil { 252 return nil, err 253 } 254 255 archive, err := container.RWLayer.TarStream() 256 if err != nil { 257 daemon.Unmount(container) // logging is already handled in the `Unmount` function 258 return nil, err 259 } 260 return ioutils.NewReadCloserWrapper(archive, func() error { 261 archive.Close() 262 return container.RWLayer.Unmount() 263 }), 264 nil 265 }