mirror of https://github.com/docker/docs.git
Merge pull request #5918 from crosbymichael/volumes-commit
Do not commit host bind mounts into image
This commit is contained in:
commit
70d35b9d39
|
@ -607,15 +607,18 @@ func (daemon *Daemon) Commit(container *Container, repository, tag, comment, aut
|
||||||
containerID, containerImage string
|
containerID, containerImage string
|
||||||
containerConfig *runconfig.Config
|
containerConfig *runconfig.Config
|
||||||
)
|
)
|
||||||
|
|
||||||
if container != nil {
|
if container != nil {
|
||||||
containerID = container.ID
|
containerID = container.ID
|
||||||
containerImage = container.Image
|
containerImage = container.Image
|
||||||
containerConfig = container.Config
|
containerConfig = container.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
img, err := daemon.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
|
img, err := daemon.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the image if needed
|
// Register the image if needed
|
||||||
if repository != "" {
|
if repository != "" {
|
||||||
if err := daemon.repositories.Set(repository, tag, img.ID, true); err != nil {
|
if err := daemon.repositories.Set(repository, tag, img.ID, true); err != nil {
|
||||||
|
|
|
@ -162,18 +162,59 @@ func createVolumes(container *Container) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
volumesDriver := container.daemon.volumes.Driver()
|
|
||||||
// Create the requested volumes if they don't exist
|
// Create the requested volumes if they don't exist
|
||||||
for volPath := range container.Config.Volumes {
|
for volPath := range container.Config.Volumes {
|
||||||
|
if err := initializeVolume(container, volPath, binds); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for volPath := range binds {
|
||||||
|
if err := initializeVolume(container, volPath, binds); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIfNotExists(path string, isDir bool) error {
|
||||||
|
if _, err := os.Stat(path); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
if isDir {
|
||||||
|
if err := os.MkdirAll(path, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(path, os.O_CREATE, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeVolume(container *Container, volPath string, binds map[string]BindMap) error {
|
||||||
|
volumesDriver := container.daemon.volumes.Driver()
|
||||||
volPath = filepath.Clean(volPath)
|
volPath = filepath.Clean(volPath)
|
||||||
volIsDir := true
|
|
||||||
// Skip existing volumes
|
// Skip existing volumes
|
||||||
if _, exists := container.Volumes[volPath]; exists {
|
if _, exists := container.Volumes[volPath]; exists {
|
||||||
continue
|
return nil
|
||||||
}
|
}
|
||||||
var srcPath string
|
|
||||||
var isBindMount bool
|
var (
|
||||||
srcRW := false
|
srcPath string
|
||||||
|
isBindMount bool
|
||||||
|
volIsDir = true
|
||||||
|
|
||||||
|
srcRW = false
|
||||||
|
)
|
||||||
|
|
||||||
// If an external bind is defined for this volume, use that as a source
|
// If an external bind is defined for this volume, use that as a source
|
||||||
if bindMap, exists := binds[volPath]; exists {
|
if bindMap, exists := binds[volPath]; exists {
|
||||||
isBindMount = true
|
isBindMount = true
|
||||||
|
@ -191,7 +232,6 @@ func createVolumes(container *Container) error {
|
||||||
}
|
}
|
||||||
// Otherwise create an directory in $ROOT/volumes/ and use that
|
// Otherwise create an directory in $ROOT/volumes/ and use that
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Do not pass a container as the parameter for the volume creation.
|
// Do not pass a container as the parameter for the volume creation.
|
||||||
// The graph driver using the container's information ( Image ) to
|
// The graph driver using the container's information ( Image ) to
|
||||||
// create the parent.
|
// create the parent.
|
||||||
|
@ -238,15 +278,25 @@ func createVolumes(container *Container) error {
|
||||||
|
|
||||||
// Do not copy or change permissions if we are mounting from the host
|
// Do not copy or change permissions if we are mounting from the host
|
||||||
if srcRW && !isBindMount {
|
if srcRW && !isBindMount {
|
||||||
|
if err := copyExistingContents(rootVolPath, srcPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyExistingContents(rootVolPath, srcPath string) error {
|
||||||
volList, err := ioutil.ReadDir(rootVolPath)
|
volList, err := ioutil.ReadDir(rootVolPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(volList) > 0 {
|
if len(volList) > 0 {
|
||||||
srcList, err := ioutil.ReadDir(srcPath)
|
srcList, err := ioutil.ReadDir(srcPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(srcList) == 0 {
|
if len(srcList) == 0 {
|
||||||
// If the source volume is empty copy files from the root into the volume
|
// If the source volume is empty copy files from the root into the volume
|
||||||
if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
|
if err := archive.CopyWithTar(rootVolPath, srcPath); err != nil {
|
||||||
|
@ -255,11 +305,14 @@ func createVolumes(container *Container) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var stat syscall.Stat_t
|
var (
|
||||||
|
stat syscall.Stat_t
|
||||||
|
srcStat syscall.Stat_t
|
||||||
|
)
|
||||||
|
|
||||||
if err := syscall.Stat(rootVolPath, &stat); err != nil {
|
if err := syscall.Stat(rootVolPath, &stat); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var srcStat syscall.Stat_t
|
|
||||||
if err := syscall.Stat(srcPath, &srcStat); err != nil {
|
if err := syscall.Stat(srcPath, &srcStat); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -270,29 +323,5 @@ func createVolumes(container *Container) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createIfNotExists(path string, isDir bool) error {
|
|
||||||
if _, err := os.Stat(path); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
if isDir {
|
|
||||||
if err := os.MkdirAll(path, 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
f, err := os.OpenFile(path, os.O_CREATE, 0755)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,6 @@ package graph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dotcloud/docker/archive"
|
|
||||||
"github.com/dotcloud/docker/daemon/graphdriver"
|
|
||||||
"github.com/dotcloud/docker/dockerversion"
|
|
||||||
"github.com/dotcloud/docker/image"
|
|
||||||
"github.com/dotcloud/docker/runconfig"
|
|
||||||
"github.com/dotcloud/docker/utils"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -17,6 +11,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/archive"
|
||||||
|
"github.com/dotcloud/docker/daemon/graphdriver"
|
||||||
|
"github.com/dotcloud/docker/dockerversion"
|
||||||
|
"github.com/dotcloud/docker/image"
|
||||||
|
"github.com/dotcloud/docker/runconfig"
|
||||||
|
"github.com/dotcloud/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Graph is a store for versioned filesystem images and the relationship between them.
|
// 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
|
||||||
Architecture: runtime.GOARCH,
|
Architecture: runtime.GOARCH,
|
||||||
OS: runtime.GOOS,
|
OS: runtime.GOOS,
|
||||||
}
|
}
|
||||||
|
|
||||||
if containerID != "" {
|
if containerID != "" {
|
||||||
img.Parent = containerImage
|
img.Parent = containerImage
|
||||||
img.Container = containerID
|
img.Container = containerID
|
||||||
img.ContainerConfig = *containerConfig
|
img.ContainerConfig = *containerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := graph.Register(nil, layerData, img); err != nil {
|
if err := graph.Register(nil, layerData, img); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,3 +83,28 @@ func TestCommitTTY(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommitWithHostBindMount(t *testing.T) {
|
||||||
|
cmd := exec.Command(dockerBinary, "run", "--name", "bind-commit", "-v", "/dev/null:/winning", "busybox", "true")
|
||||||
|
if _, err := runCommand(cmd); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command(dockerBinary, "commit", "bind-commit", "bindtest")
|
||||||
|
imageId, _, err := runCommandWithOutput(cmd)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
imageId = strings.Trim(imageId, "\r\n")
|
||||||
|
|
||||||
|
cmd = exec.Command(dockerBinary, "run", "bindtest", "true")
|
||||||
|
|
||||||
|
if _, err := runCommand(cmd); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAllContainers()
|
||||||
|
deleteImages(imageId)
|
||||||
|
|
||||||
|
logDone("commit - commit bind mounted file")
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package runconfig
|
package runconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/dotcloud/docker/nat"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/dotcloud/docker/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parse(t *testing.T, args string) (*Config, *HostConfig, error) {
|
func parse(t *testing.T, args string) (*Config, *HostConfig, error) {
|
||||||
|
@ -93,32 +94,20 @@ func TestParseRunVolumes(t *testing.T) {
|
||||||
t.Fatalf("Error parsing volume flags, `-v /var` is missing from volumes. Received %v", config.Volumes)
|
t.Fatalf("Error parsing volume flags, `-v /var` is missing from volumes. Received %v", config.Volumes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
|
if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
|
||||||
t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp` should mount-bind /hostTmp into /containeTmp. Received %v", hostConfig.Binds)
|
t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp` should mount-bind /hostTmp into /containeTmp. Received %v", hostConfig.Binds)
|
||||||
} else if _, exists := config.Volumes["/containerTmp"]; !exists {
|
|
||||||
t.Fatalf("Error parsing volume flags, `-v /tmp` is missing from volumes. Received %v", config.Volumes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /hostVar:/containerVar"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp" || hostConfig.Binds[1] != "/hostVar:/containerVar" {
|
if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /hostVar:/containerVar"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp" || hostConfig.Binds[1] != "/hostVar:/containerVar" {
|
||||||
t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /hostVar:/containerVar` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
|
t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /hostVar:/containerVar` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
|
||||||
} else if _, exists := config.Volumes["/containerTmp"]; !exists {
|
|
||||||
t.Fatalf("Error parsing volume flags, `-v /containerTmp` is missing from volumes. Received %v", config.Volumes)
|
|
||||||
} else if _, exists := config.Volumes["/containerVar"]; !exists {
|
|
||||||
t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp:ro" || hostConfig.Binds[1] != "/hostVar:/containerVar:rw" {
|
if _, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw"); hostConfig.Binds == nil || hostConfig.Binds[0] != "/hostTmp:/containerTmp:ro" || hostConfig.Binds[1] != "/hostVar:/containerVar:rw" {
|
||||||
t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
|
t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp:ro -v /hostVar:/containerVar:rw` should mount-bind /hostTmp into /containeTmp and /hostVar into /hostContainer. Received %v", hostConfig.Binds)
|
||||||
} else if _, exists := config.Volumes["/containerTmp"]; !exists {
|
|
||||||
t.Fatalf("Error parsing volume flags, `-v /containerTmp` is missing from volumes. Received %v", config.Volumes)
|
|
||||||
} else if _, exists := config.Volumes["/containerVar"]; !exists {
|
|
||||||
t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /containerVar"); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
|
if config, hostConfig := mustParse(t, "-v /hostTmp:/containerTmp -v /containerVar"); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != "/hostTmp:/containerTmp" {
|
||||||
t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /containerVar` should mount-bind only /hostTmp into /containeTmp. Received %v", hostConfig.Binds)
|
t.Fatalf("Error parsing volume flags, `-v /hostTmp:/containerTmp -v /containerVar` should mount-bind only /hostTmp into /containeTmp. Received %v", hostConfig.Binds)
|
||||||
} else if _, exists := config.Volumes["/containerTmp"]; !exists {
|
|
||||||
t.Fatalf("Error parsing volume flags, `-v /containerTmp` is missing from volumes. Received %v", config.Volumes)
|
|
||||||
} else if _, exists := config.Volumes["/containerVar"]; !exists {
|
} else if _, exists := config.Volumes["/containerVar"]; !exists {
|
||||||
t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
|
t.Fatalf("Error parsing volume flags, `-v /containerVar` is missing from volumes. Received %v", config.Volumes)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package runconfig
|
package runconfig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/dotcloud/docker/nat"
|
"github.com/dotcloud/docker/nat"
|
||||||
"github.com/dotcloud/docker/utils"
|
"github.com/dotcloud/docker/utils"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Merge(userConf, imageConf *Config) error {
|
func Merge(userConf, imageConf *Config) error {
|
||||||
|
@ -82,6 +83,7 @@ func Merge(userConf, imageConf *Config) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
|
if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
|
||||||
userConf.Cmd = imageConf.Cmd
|
userConf.Cmd = imageConf.Cmd
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,8 +135,8 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
|
||||||
if arr[0] == "/" {
|
if arr[0] == "/" {
|
||||||
return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
|
return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
|
||||||
}
|
}
|
||||||
dstDir := arr[1]
|
// after creating the bind mount we want to delete it from the flVolumes values because
|
||||||
flVolumes.Set(dstDir)
|
// we do not want bind mounts being committed to image configs
|
||||||
binds = append(binds, bind)
|
binds = append(binds, bind)
|
||||||
flVolumes.Delete(bind)
|
flVolumes.Delete(bind)
|
||||||
} else if bind == "/" {
|
} else if bind == "/" {
|
||||||
|
|
|
@ -1050,8 +1050,12 @@ func (srv *Server) ContainerCommit(job *engine.Job) engine.Status {
|
||||||
if container == nil {
|
if container == nil {
|
||||||
return job.Errorf("No such container: %s", name)
|
return job.Errorf("No such container: %s", name)
|
||||||
}
|
}
|
||||||
var config = container.Config
|
|
||||||
var newConfig runconfig.Config
|
var (
|
||||||
|
config = container.Config
|
||||||
|
newConfig runconfig.Config
|
||||||
|
)
|
||||||
|
|
||||||
if err := job.GetenvJson("config", &newConfig); err != nil {
|
if err := job.GetenvJson("config", &newConfig); err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue