| ... | ... |
@@ -607,15 +607,18 @@ func (daemon *Daemon) Commit(container *Container, repository, tag, comment, aut |
| 607 | 607 |
containerID, containerImage string |
| 608 | 608 |
containerConfig *runconfig.Config |
| 609 | 609 |
) |
| 610 |
+ |
|
| 610 | 611 |
if container != nil {
|
| 611 | 612 |
containerID = container.ID |
| 612 | 613 |
containerImage = container.Image |
| 613 | 614 |
containerConfig = container.Config |
| 614 | 615 |
} |
| 616 |
+ |
|
| 615 | 617 |
img, err := daemon.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config) |
| 616 | 618 |
if err != nil {
|
| 617 | 619 |
return nil, err |
| 618 | 620 |
} |
| 621 |
+ |
|
| 619 | 622 |
// Register the image if needed |
| 620 | 623 |
if repository != "" {
|
| 621 | 624 |
if err := daemon.repositories.Set(repository, tag, img.ID, true); err != nil {
|
| ... | ... |
@@ -162,135 +162,147 @@ func createVolumes(container *Container) error {
|
| 162 | 162 |
return err |
| 163 | 163 |
} |
| 164 | 164 |
|
| 165 |
- volumesDriver := container.daemon.volumes.Driver() |
|
| 166 | 165 |
// Create the requested volumes if they don't exist |
| 167 | 166 |
for volPath := range container.Config.Volumes {
|
| 168 |
- volPath = filepath.Clean(volPath) |
|
| 169 |
- volIsDir := true |
|
| 170 |
- // Skip existing volumes |
|
| 171 |
- if _, exists := container.Volumes[volPath]; exists {
|
|
| 172 |
- continue |
|
| 167 |
+ if err := initializeVolume(container, volPath, binds); err != nil {
|
|
| 168 |
+ return err |
|
| 173 | 169 |
} |
| 174 |
- var srcPath string |
|
| 175 |
- var isBindMount bool |
|
| 176 |
- srcRW := false |
|
| 177 |
- // If an external bind is defined for this volume, use that as a source |
|
| 178 |
- if bindMap, exists := binds[volPath]; exists {
|
|
| 179 |
- isBindMount = true |
|
| 180 |
- srcPath = bindMap.SrcPath |
|
| 181 |
- if !filepath.IsAbs(srcPath) {
|
|
| 182 |
- return fmt.Errorf("%s must be an absolute path", srcPath)
|
|
| 183 |
- } |
|
| 184 |
- if strings.ToLower(bindMap.Mode) == "rw" {
|
|
| 185 |
- srcRW = true |
|
| 186 |
- } |
|
| 187 |
- if stat, err := os.Stat(bindMap.SrcPath); err != nil {
|
|
| 188 |
- return err |
|
| 189 |
- } else {
|
|
| 190 |
- volIsDir = stat.IsDir() |
|
| 191 |
- } |
|
| 192 |
- // Otherwise create an directory in $ROOT/volumes/ and use that |
|
| 193 |
- } else {
|
|
| 170 |
+ } |
|
| 171 |
+ return nil |
|
| 172 |
+} |
|
| 194 | 173 |
|
| 195 |
- // Do not pass a container as the parameter for the volume creation. |
|
| 196 |
- // The graph driver using the container's information ( Image ) to |
|
| 197 |
- // create the parent. |
|
| 198 |
- c, err := container.daemon.volumes.Create(nil, "", "", "", "", nil, nil) |
|
| 199 |
- if err != nil {
|
|
| 200 |
- return err |
|
| 201 |
- } |
|
| 202 |
- srcPath, err = volumesDriver.Get(c.ID, "") |
|
| 203 |
- if err != nil {
|
|
| 204 |
- return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
|
|
| 174 |
+func createIfNotExists(path string, isDir bool) error {
|
|
| 175 |
+ if _, err := os.Stat(path); err != nil {
|
|
| 176 |
+ if os.IsNotExist(err) {
|
|
| 177 |
+ if isDir {
|
|
| 178 |
+ if err := os.MkdirAll(path, 0755); err != nil {
|
|
| 179 |
+ return err |
|
| 180 |
+ } |
|
| 181 |
+ } else {
|
|
| 182 |
+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
|
| 183 |
+ return err |
|
| 184 |
+ } |
|
| 185 |
+ f, err := os.OpenFile(path, os.O_CREATE, 0755) |
|
| 186 |
+ if err != nil {
|
|
| 187 |
+ return err |
|
| 188 |
+ } |
|
| 189 |
+ defer f.Close() |
|
| 205 | 190 |
} |
| 206 |
- srcRW = true // RW by default |
|
| 207 | 191 |
} |
| 192 |
+ } |
|
| 193 |
+ return nil |
|
| 194 |
+} |
|
| 195 |
+ |
|
| 196 |
+func initializeVolume(container *Container, volPath string, binds map[string]BindMap) error {
|
|
| 197 |
+ volumesDriver := container.daemon.volumes.Driver() |
|
| 198 |
+ volPath = filepath.Clean(volPath) |
|
| 199 |
+ // Skip existing volumes |
|
| 200 |
+ if _, exists := container.Volumes[volPath]; exists {
|
|
| 201 |
+ return nil |
|
| 202 |
+ } |
|
| 208 | 203 |
|
| 209 |
- if p, err := filepath.EvalSymlinks(srcPath); err != nil {
|
|
| 204 |
+ var ( |
|
| 205 |
+ srcPath string |
|
| 206 |
+ isBindMount bool |
|
| 207 |
+ volIsDir = true |
|
| 208 |
+ |
|
| 209 |
+ srcRW = false |
|
| 210 |
+ ) |
|
| 211 |
+ |
|
| 212 |
+ // If an external bind is defined for this volume, use that as a source |
|
| 213 |
+ if bindMap, exists := binds[volPath]; exists {
|
|
| 214 |
+ isBindMount = true |
|
| 215 |
+ srcPath = bindMap.SrcPath |
|
| 216 |
+ if !filepath.IsAbs(srcPath) {
|
|
| 217 |
+ return fmt.Errorf("%s must be an absolute path", srcPath)
|
|
| 218 |
+ } |
|
| 219 |
+ if strings.ToLower(bindMap.Mode) == "rw" {
|
|
| 220 |
+ srcRW = true |
|
| 221 |
+ } |
|
| 222 |
+ if stat, err := os.Stat(bindMap.SrcPath); err != nil {
|
|
| 210 | 223 |
return err |
| 211 | 224 |
} else {
|
| 212 |
- srcPath = p |
|
| 225 |
+ volIsDir = stat.IsDir() |
|
| 213 | 226 |
} |
| 227 |
+ // Otherwise create an directory in $ROOT/volumes/ and use that |
|
| 228 |
+ } else {
|
|
| 214 | 229 |
|
| 215 |
- // Create the mountpoint |
|
| 216 |
- rootVolPath, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, volPath), container.basefs) |
|
| 230 |
+ // Do not pass a container as the parameter for the volume creation. |
|
| 231 |
+ // The graph driver using the container's information ( Image ) to |
|
| 232 |
+ // create the parent. |
|
| 233 |
+ c, err := container.daemon.volumes.Create(nil, "", "", "", "", nil, nil) |
|
| 217 | 234 |
if err != nil {
|
| 218 | 235 |
return err |
| 219 | 236 |
} |
| 220 |
- |
|
| 221 |
- newVolPath, err := filepath.Rel(container.basefs, rootVolPath) |
|
| 237 |
+ srcPath, err = volumesDriver.Get(c.ID, "") |
|
| 222 | 238 |
if err != nil {
|
| 223 |
- return err |
|
| 239 |
+ return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
|
|
| 224 | 240 |
} |
| 225 |
- newVolPath = "/" + newVolPath |
|
| 241 |
+ srcRW = true // RW by default |
|
| 242 |
+ } |
|
| 226 | 243 |
|
| 227 |
- if volPath != newVolPath {
|
|
| 228 |
- delete(container.Volumes, volPath) |
|
| 229 |
- delete(container.VolumesRW, volPath) |
|
| 230 |
- } |
|
| 244 |
+ if p, err := filepath.EvalSymlinks(srcPath); err != nil {
|
|
| 245 |
+ return err |
|
| 246 |
+ } else {
|
|
| 247 |
+ srcPath = p |
|
| 248 |
+ } |
|
| 231 | 249 |
|
| 232 |
- container.Volumes[newVolPath] = srcPath |
|
| 233 |
- container.VolumesRW[newVolPath] = srcRW |
|
| 250 |
+ // Create the mountpoint |
|
| 251 |
+ rootVolPath, err := symlink.FollowSymlinkInScope(filepath.Join(container.basefs, volPath), container.basefs) |
|
| 252 |
+ if err != nil {
|
|
| 253 |
+ return err |
|
| 254 |
+ } |
|
| 255 |
+ |
|
| 256 |
+ newVolPath, err := filepath.Rel(container.basefs, rootVolPath) |
|
| 257 |
+ if err != nil {
|
|
| 258 |
+ return err |
|
| 259 |
+ } |
|
| 260 |
+ newVolPath = "/" + newVolPath |
|
| 261 |
+ |
|
| 262 |
+ if volPath != newVolPath {
|
|
| 263 |
+ delete(container.Volumes, volPath) |
|
| 264 |
+ delete(container.VolumesRW, volPath) |
|
| 265 |
+ } |
|
| 266 |
+ |
|
| 267 |
+ container.Volumes[newVolPath] = srcPath |
|
| 268 |
+ container.VolumesRW[newVolPath] = srcRW |
|
| 269 |
+ |
|
| 270 |
+ if err := createIfNotExists(rootVolPath, volIsDir); err != nil {
|
|
| 271 |
+ return err |
|
| 272 |
+ } |
|
| 234 | 273 |
|
| 235 |
- if err := createIfNotExists(rootVolPath, volIsDir); err != nil {
|
|
| 274 |
+ // Do not copy or change permissions if we are mounting from the host |
|
| 275 |
+ if srcRW && !isBindMount {
|
|
| 276 |
+ volList, err := ioutil.ReadDir(rootVolPath) |
|
| 277 |
+ if err != nil {
|
|
| 236 | 278 |
return err |
| 237 | 279 |
} |
| 238 |
- |
|
| 239 |
- // Do not copy or change permissions if we are mounting from the host |
|
| 240 |
- if srcRW && !isBindMount {
|
|
| 241 |
- volList, err := ioutil.ReadDir(rootVolPath) |
|
| 280 |
+ if len(volList) > 0 {
|
|
| 281 |
+ srcList, err := ioutil.ReadDir(srcPath) |
|
| 242 | 282 |
if err != nil {
|
| 243 | 283 |
return err |
| 244 | 284 |
} |
| 245 |
- if len(volList) > 0 {
|
|
| 246 |
- srcList, err := ioutil.ReadDir(srcPath) |
|
| 247 |
- if err != nil {
|
|
| 248 |
- return err |
|
| 249 |
- } |
|
| 250 |
- if len(srcList) == 0 {
|
|
| 251 |
- // If the source volume is empty copy files from the root into the volume |
|
| 252 |
- if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
|
|
| 253 |
- return err |
|
| 254 |
- } |
|
| 255 |
- } |
|
| 256 |
- } |
|
| 257 |
- |
|
| 258 |
- var stat syscall.Stat_t |
|
| 259 |
- if err := syscall.Stat(rootVolPath, &stat); err != nil {
|
|
| 260 |
- return err |
|
| 261 |
- } |
|
| 262 |
- var srcStat syscall.Stat_t |
|
| 263 |
- if err := syscall.Stat(srcPath, &srcStat); err != nil {
|
|
| 264 |
- return err |
|
| 265 |
- } |
|
| 266 |
- // Change the source volume's ownership if it differs from the root |
|
| 267 |
- // files that were just copied |
|
| 268 |
- if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
|
|
| 269 |
- if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
|
|
| 285 |
+ if len(srcList) == 0 {
|
|
| 286 |
+ // If the source volume is empty copy files from the root into the volume |
|
| 287 |
+ if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
|
|
| 270 | 288 |
return err |
| 271 | 289 |
} |
| 272 | 290 |
} |
| 273 | 291 |
} |
| 274 |
- } |
|
| 275 |
- return nil |
|
| 276 |
-} |
|
| 277 | 292 |
|
| 278 |
-func createIfNotExists(path string, isDir bool) error {
|
|
| 279 |
- if _, err := os.Stat(path); err != nil {
|
|
| 280 |
- if os.IsNotExist(err) {
|
|
| 281 |
- if isDir {
|
|
| 282 |
- if err := os.MkdirAll(path, 0755); err != nil {
|
|
| 283 |
- return err |
|
| 284 |
- } |
|
| 285 |
- } else {
|
|
| 286 |
- if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
|
| 287 |
- return err |
|
| 288 |
- } |
|
| 289 |
- f, err := os.OpenFile(path, os.O_CREATE, 0755) |
|
| 290 |
- if err != nil {
|
|
| 291 |
- return err |
|
| 292 |
- } |
|
| 293 |
- defer f.Close() |
|
| 293 |
+ var stat syscall.Stat_t |
|
| 294 |
+ if err := syscall.Stat(rootVolPath, &stat); err != nil {
|
|
| 295 |
+ return err |
|
| 296 |
+ } |
|
| 297 |
+ var srcStat syscall.Stat_t |
|
| 298 |
+ if err := syscall.Stat(srcPath, &srcStat); err != nil {
|
|
| 299 |
+ return err |
|
| 300 |
+ } |
|
| 301 |
+ // Change the source volume's ownership if it differs from the root |
|
| 302 |
+ // files that were just copied |
|
| 303 |
+ if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
|
|
| 304 |
+ if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
|
|
| 305 |
+ return err |
|
| 294 | 306 |
} |
| 295 | 307 |
} |
| 296 | 308 |
} |
| ... | ... |
@@ -2,12 +2,6 @@ package graph |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
- "github.com/dotcloud/docker/archive" |
|
| 6 |
- "github.com/dotcloud/docker/daemon/graphdriver" |
|
| 7 |
- "github.com/dotcloud/docker/dockerversion" |
|
| 8 |
- "github.com/dotcloud/docker/image" |
|
| 9 |
- "github.com/dotcloud/docker/runconfig" |
|
| 10 |
- "github.com/dotcloud/docker/utils" |
|
| 11 | 5 |
"io" |
| 12 | 6 |
"io/ioutil" |
| 13 | 7 |
"os" |
| ... | ... |
@@ -17,6 +11,13 @@ import ( |
| 17 | 17 |
"strings" |
| 18 | 18 |
"syscall" |
| 19 | 19 |
"time" |
| 20 |
+ |
|
| 21 |
+ "github.com/dotcloud/docker/archive" |
|
| 22 |
+ "github.com/dotcloud/docker/daemon/graphdriver" |
|
| 23 |
+ "github.com/dotcloud/docker/dockerversion" |
|
| 24 |
+ "github.com/dotcloud/docker/image" |
|
| 25 |
+ "github.com/dotcloud/docker/runconfig" |
|
| 26 |
+ "github.com/dotcloud/docker/utils" |
|
| 20 | 27 |
) |
| 21 | 28 |
|
| 22 | 29 |
// A Graph is a store for versioned filesystem images and the relationship between them. |
| ... | ... |
@@ -141,11 +142,13 @@ func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, contain |
| 141 | 141 |
Architecture: runtime.GOARCH, |
| 142 | 142 |
OS: runtime.GOOS, |
| 143 | 143 |
} |
| 144 |
+ |
|
| 144 | 145 |
if containerID != "" {
|
| 145 | 146 |
img.Parent = containerImage |
| 146 | 147 |
img.Container = containerID |
| 147 | 148 |
img.ContainerConfig = *containerConfig |
| 148 | 149 |
} |
| 150 |
+ |
|
| 149 | 151 |
if err := graph.Register(nil, layerData, img); err != nil {
|
| 150 | 152 |
return nil, err |
| 151 | 153 |
} |
| ... | ... |
@@ -1,9 +1,10 @@ |
| 1 | 1 |
package runconfig |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "strings" |
|
| 5 |
+ |
|
| 4 | 6 |
"github.com/dotcloud/docker/nat" |
| 5 | 7 |
"github.com/dotcloud/docker/utils" |
| 6 |
- "strings" |
|
| 7 | 8 |
) |
| 8 | 9 |
|
| 9 | 10 |
func Merge(userConf, imageConf *Config) error {
|
| ... | ... |
@@ -82,6 +83,7 @@ func Merge(userConf, imageConf *Config) error {
|
| 82 | 82 |
} |
| 83 | 83 |
} |
| 84 | 84 |
} |
| 85 |
+ |
|
| 85 | 86 |
if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
|
| 86 | 87 |
userConf.Cmd = imageConf.Cmd |
| 87 | 88 |
} |
| ... | ... |
@@ -1050,8 +1050,12 @@ func (srv *Server) ContainerCommit(job *engine.Job) engine.Status {
|
| 1050 | 1050 |
if container == nil {
|
| 1051 | 1051 |
return job.Errorf("No such container: %s", name)
|
| 1052 | 1052 |
} |
| 1053 |
- var config = container.Config |
|
| 1054 |
- var newConfig runconfig.Config |
|
| 1053 |
+ |
|
| 1054 |
+ var ( |
|
| 1055 |
+ config = container.Config |
|
| 1056 |
+ newConfig runconfig.Config |
|
| 1057 |
+ ) |
|
| 1058 |
+ |
|
| 1055 | 1059 |
if err := job.GetenvJson("config", &newConfig); err != nil {
|
| 1056 | 1060 |
return job.Error(err) |
| 1057 | 1061 |
} |