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