1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

+ Support for data volumes

This commit is contained in:
Solomon Hykes 2013-05-03 13:03:47 -07:00
commit 18b8eeb484
5 changed files with 117 additions and 2 deletions

View file

@ -10,6 +10,7 @@ import (
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
"path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
@ -400,7 +401,8 @@ func (srv *Server) CmdHistory(stdin io.ReadCloser, stdout io.Writer, args ...str
} }
func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error { func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "rm", "CONTAINER [CONTAINER...]", "Remove a container") cmd := rcli.Subcmd(stdout, "rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove a container")
v := cmd.Bool("v", false, "Remove the volumes associated to the container")
if err := cmd.Parse(args); err != nil { if err := cmd.Parse(args); err != nil {
return nil return nil
} }
@ -408,15 +410,40 @@ func (srv *Server) CmdRm(stdin io.ReadCloser, stdout io.Writer, args ...string)
cmd.Usage() cmd.Usage()
return nil return nil
} }
volumes := make(map[string]struct{})
for _, name := range cmd.Args() { for _, name := range cmd.Args() {
container := srv.runtime.Get(name) container := srv.runtime.Get(name)
if container == nil { if container == nil {
return fmt.Errorf("No such container: %s", name) return fmt.Errorf("No such container: %s", name)
} }
// Store all the deleted containers volumes
for _, volumeId := range container.Volumes {
volumes[volumeId] = struct{}{}
}
if err := srv.runtime.Destroy(container); err != nil { if err := srv.runtime.Destroy(container); err != nil {
fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error()) fmt.Fprintln(stdout, "Error destroying container "+name+": "+err.Error())
} }
} }
if *v {
// Retrieve all volumes from all remaining containers
usedVolumes := make(map[string]*Container)
for _, container := range srv.runtime.List() {
for _, containerVolumeId := range container.Volumes {
usedVolumes[containerVolumeId] = container
}
}
for volumeId := range volumes {
// If the requested volu
if c, exists := usedVolumes[volumeId]; exists {
fmt.Fprintf(stdout, "The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.Id)
continue
}
if err := srv.runtime.volumes.Delete(volumeId); err != nil {
return err
}
}
}
return nil return nil
} }
@ -913,6 +940,25 @@ func (opts AttachOpts) Get(val string) bool {
return false return false
} }
// PathOpts stores a unique set of absolute paths
type PathOpts map[string]struct{}
func NewPathOpts() PathOpts {
return make(PathOpts)
}
func (opts PathOpts) String() string {
return fmt.Sprintf("%v", map[string]struct{}(opts))
}
func (opts PathOpts) Set(val string) error {
if !filepath.IsAbs(val) {
return fmt.Errorf("%s is not an absolute path", val)
}
opts[filepath.Clean(val)] = struct{}{}
return nil
}
func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string) error { func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
cmd := rcli.Subcmd(stdout, "tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository") cmd := rcli.Subcmd(stdout, "tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
force := cmd.Bool("f", false, "Force") force := cmd.Bool("f", false, "Force")

View file

@ -48,6 +48,7 @@ type Container struct {
runtime *Runtime runtime *Runtime
waitLock chan struct{} waitLock chan struct{}
Volumes map[string]string
} }
type Config struct { type Config struct {
@ -66,6 +67,8 @@ type Config struct {
Cmd []string Cmd []string
Dns []string Dns []string
Image string // Name of the image as it was passed by the operator (eg. could be symbolic) Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
Volumes map[string]struct{}
VolumesFrom string
} }
func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Config, error) { func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Config, error) {
@ -97,6 +100,11 @@ func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Con
var flDns ListOpts var flDns ListOpts
cmd.Var(&flDns, "dns", "Set custom dns servers") cmd.Var(&flDns, "dns", "Set custom dns servers")
flVolumes := NewPathOpts()
cmd.Var(flVolumes, "v", "Attach a data volume")
flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
if err := cmd.Parse(args); err != nil { if err := cmd.Parse(args); err != nil {
return nil, err return nil, err
} }
@ -136,6 +144,8 @@ func ParseRun(args []string, stdout io.Writer, capabilities *Capabilities) (*Con
Cmd: runCmd, Cmd: runCmd,
Dns: flDns, Dns: flDns,
Image: image, Image: image,
Volumes: flVolumes,
VolumesFrom: *flVolumesFrom,
} }
if *flMemory > 0 && !capabilities.SwapLimit { if *flMemory > 0 && !capabilities.SwapLimit {
@ -394,10 +404,40 @@ func (container *Container) Start() error {
log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n") log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
container.Config.MemorySwap = -1 container.Config.MemorySwap = -1
} }
container.Volumes = make(map[string]string)
// Create the requested volumes volumes
for volPath := range container.Config.Volumes {
if c, err := container.runtime.volumes.Create(nil, container, "", "", nil); err != nil {
return err
} else {
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
return nil
}
container.Volumes[volPath] = c.Id
}
}
if container.Config.VolumesFrom != "" {
c := container.runtime.Get(container.Config.VolumesFrom)
if c == nil {
return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.Id)
}
for volPath, id := range c.Volumes {
if _, exists := container.Volumes[volPath]; exists {
return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.Id)
}
if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
return nil
}
container.Volumes[volPath] = id
}
}
if err := container.generateLXCConfig(); err != nil { if err := container.generateLXCConfig(); err != nil {
return err return err
} }
params := []string{ params := []string{
"-n", container.Id, "-n", container.Id,
"-f", container.lxcConfigPath(), "-f", container.lxcConfigPath(),
@ -456,6 +496,7 @@ func (container *Container) Start() error {
// Init the lock // Init the lock
container.waitLock = make(chan struct{}) container.waitLock = make(chan struct{})
container.ToDisk() container.ToDisk()
go container.monitor() go container.monitor()
return nil return nil
@ -787,6 +828,22 @@ func (container *Container) RootfsPath() string {
return path.Join(container.root, "rootfs") return path.Join(container.root, "rootfs")
} }
func (container *Container) GetVolumes() (map[string]string, error) {
ret := make(map[string]string)
for volPath, id := range container.Volumes {
volume, err := container.runtime.volumes.Get(id)
if err != nil {
return nil, err
}
root, err := volume.root()
if err != nil {
return nil, err
}
ret[volPath] = path.Join(root, "layer")
}
return ret, nil
}
func (container *Container) rwPath() string { func (container *Container) rwPath() string {
return path.Join(container.root, "rw") return path.Join(container.root, "rw")
} }

View file

@ -17,3 +17,5 @@
-p=[]: Map a network port to the container -p=[]: Map a network port to the container
-t=false: Allocate a pseudo-tty -t=false: Allocate a pseudo-tty
-u="": Username or UID -u="": Username or UID
-d=[]: Set custom dns servers for the container
-v=[]: Creates a new volumes and mount it at the specified path. A container ID can be passed instead of a path in order to mount all volumes from the given container.

View file

@ -79,7 +79,11 @@ lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/sbin/init none bind,ro 0 0
# In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container # In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container
lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0 lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
{{if .Volumes}}
{{range $virtualPath, $realPath := .GetVolumes}}
lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,rw 0 0
{{end}}
{{end}}
# drop linux capabilities (apply mainly to the user root in the container) # drop linux capabilities (apply mainly to the user root in the container)
lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod setfcap setpcap sys_admin sys_boot sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config

View file

@ -32,6 +32,7 @@ type Runtime struct {
capabilities *Capabilities capabilities *Capabilities
kernelVersion *KernelVersionInfo kernelVersion *KernelVersionInfo
autoRestart bool autoRestart bool
volumes *Graph
} }
var sysInitPath string var sysInitPath string
@ -405,6 +406,10 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
volumes, err := NewGraph(path.Join(root, "volumes"))
if err != nil {
return nil, err
}
repositories, err := NewTagStore(path.Join(root, "repositories"), g) repositories, err := NewTagStore(path.Join(root, "repositories"), g)
if err != nil { if err != nil {
return nil, fmt.Errorf("Couldn't create Tag store: %s", err) return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
@ -432,6 +437,7 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
idIndex: NewTruncIndex(), idIndex: NewTruncIndex(),
capabilities: &Capabilities{}, capabilities: &Capabilities{},
autoRestart: autoRestart, autoRestart: autoRestart,
volumes: volumes,
} }
if err := runtime.restore(); err != nil { if err := runtime.restore(); err != nil {