| ... | ... |
@@ -1,6 +1,13 @@ |
| 1 | 1 |
# Changelog |
| 2 | 2 |
|
| 3 |
-## 0.2.1 (2012-05-01) |
|
| 3 |
+## 0.2.2 (2013-05-03) |
|
| 4 |
+ + Support for data volumes ('docker run -v=PATH')
|
|
| 5 |
+ + Share data volumes between containers ('docker run -volumes-from')
|
|
| 6 |
+ + Improved documentation |
|
| 7 |
+ * Upgrade to Go 1.0.3 |
|
| 8 |
+ * Various upgrades to the dev environment for contributors |
|
| 9 |
+ |
|
| 10 |
+## 0.2.1 (2013-05-01) |
|
| 4 | 11 |
+ 'docker commit -run' bundles a layer with default runtime options: command, ports etc. |
| 5 | 12 |
* Improve install process on Vagrant |
| 6 | 13 |
+ New Dockerfile operation: "maintainer" |
| ... | ... |
@@ -10,7 +17,7 @@ |
| 10 | 10 |
+ 'docker -d -r': restart crashed containers at daemon startup |
| 11 | 11 |
* Runtime: improve test coverage |
| 12 | 12 |
|
| 13 |
-## 0.2.0 (2012-04-23) |
|
| 13 |
+## 0.2.0 (2013-04-23) |
|
| 14 | 14 |
- Runtime: ghost containers can be killed and waited for |
| 15 | 15 |
* Documentation: update install intructions |
| 16 | 16 |
- Packaging: fix Vagrantfile |
| ... | ... |
@@ -1,8 +1,8 @@ |
| 1 | 1 |
# -*- mode: ruby -*- |
| 2 | 2 |
# vi: set ft=ruby : |
| 3 | 3 |
|
| 4 |
-BOX_NAME = "ubuntu" |
|
| 5 |
-BOX_URI = "http://files.vagrantup.com/precise64.box" |
|
| 4 |
+BOX_NAME = ENV['BOX_NAME'] || "ubuntu" |
|
| 5 |
+BOX_URI = ENV['BOX_URI'] || "http://files.vagrantup.com/precise64.box" |
|
| 6 | 6 |
PPA_KEY = "E61D797F63561DC6" |
| 7 | 7 |
|
| 8 | 8 |
Vagrant::Config.run do |config| |
| ... | ... |
@@ -11,7 +11,7 @@ Vagrant::Config.run do |config| |
| 11 | 11 |
config.vm.box_url = BOX_URI |
| 12 | 12 |
# Add docker PPA key to the local repository and install docker |
| 13 | 13 |
pkg_cmd = "apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys #{PPA_KEY}; "
|
| 14 |
- pkg_cmd << "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >>/etc/apt/sources.list; " |
|
| 14 |
+ pkg_cmd << "echo 'deb http://ppa.launchpad.net/dotcloud/lxc-docker/ubuntu precise main' >/etc/apt/sources.list.d/lxc-docker.list; " |
|
| 15 | 15 |
pkg_cmd << "apt-get update -qq; apt-get install -q -y lxc-docker" |
| 16 | 16 |
if ARGV.include?("--provider=aws".downcase)
|
| 17 | 17 |
# Add AUFS dependency to amazon's VM |
| ... | ... |
@@ -320,9 +320,16 @@ func ListenAndServe(addr string, srv *Server) error {
|
| 320 | 320 |
|
| 321 | 321 |
r.Path("/containers/{name:.*}").Methods("DELETE").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
| 322 | 322 |
log.Println(r.Method, r.RequestURI) |
| 323 |
+ if err := r.ParseForm(); err != nil {
|
|
| 324 |
+ http.Error(w, err.Error(), http.StatusInternalServerError) |
|
| 325 |
+ } |
|
| 323 | 326 |
vars := mux.Vars(r) |
| 324 | 327 |
name := vars["name"] |
| 325 |
- if err := srv.ContainerDestroy(name); err != nil {
|
|
| 328 |
+ var v bool |
|
| 329 |
+ if r.Form.Get("v") == "1" {
|
|
| 330 |
+ v = true |
|
| 331 |
+ } |
|
| 332 |
+ if err := srv.ContainerDestroy(name, v); err != nil {
|
|
| 326 | 333 |
httpError(w, err) |
| 327 | 334 |
} else {
|
| 328 | 335 |
w.WriteHeader(http.StatusOK) |
| ... | ... |
@@ -13,12 +13,13 @@ import ( |
| 13 | 13 |
"net/http/httputil" |
| 14 | 14 |
"net/url" |
| 15 | 15 |
"os" |
| 16 |
+ "path/filepath" |
|
| 16 | 17 |
"strconv" |
| 17 | 18 |
"text/tabwriter" |
| 18 | 19 |
"time" |
| 19 | 20 |
) |
| 20 | 21 |
|
| 21 |
-const VERSION = "0.2.1" |
|
| 22 |
+const VERSION = "0.2.2" |
|
| 22 | 23 |
|
| 23 | 24 |
var ( |
| 24 | 25 |
GIT_COMMIT string |
| ... | ... |
@@ -434,7 +435,7 @@ func CmdRmi(args ...string) error {
|
| 434 | 434 |
return nil |
| 435 | 435 |
} |
| 436 | 436 |
|
| 437 |
- for _, name := range args {
|
|
| 437 |
+ for _, name := range cmd.Args() {
|
|
| 438 | 438 |
_, _, err := call("DELETE", "/images/"+name, nil)
|
| 439 | 439 |
if err != nil {
|
| 440 | 440 |
fmt.Printf("%s", err)
|
| ... | ... |
@@ -476,7 +477,8 @@ func CmdHistory(args ...string) error {
|
| 476 | 476 |
} |
| 477 | 477 |
|
| 478 | 478 |
func CmdRm(args ...string) error {
|
| 479 |
- cmd := Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove a container")
|
|
| 479 |
+ cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove a container")
|
|
| 480 |
+ v := cmd.Bool("v", false, "Remove the volumes associated to the container")
|
|
| 480 | 481 |
if err := cmd.Parse(args); err != nil {
|
| 481 | 482 |
return nil |
| 482 | 483 |
} |
| ... | ... |
@@ -484,9 +486,12 @@ func CmdRm(args ...string) error {
|
| 484 | 484 |
cmd.Usage() |
| 485 | 485 |
return nil |
| 486 | 486 |
} |
| 487 |
- |
|
| 488 |
- for _, name := range args {
|
|
| 489 |
- _, _, err := call("DELETE", "/containers/"+name, nil)
|
|
| 487 |
+ val := url.Values{}
|
|
| 488 |
+ if *v {
|
|
| 489 |
+ val.Set("v", "1")
|
|
| 490 |
+ } |
|
| 491 |
+ for _, name := range cmd.Args() {
|
|
| 492 |
+ _, _, err := call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
|
|
| 490 | 493 |
if err != nil {
|
| 491 | 494 |
fmt.Printf("%s", err)
|
| 492 | 495 |
} else {
|
| ... | ... |
@@ -930,6 +935,25 @@ func (opts AttachOpts) Get(val string) bool {
|
| 930 | 930 |
return false |
| 931 | 931 |
} |
| 932 | 932 |
|
| 933 |
+// PathOpts stores a unique set of absolute paths |
|
| 934 |
+type PathOpts map[string]struct{}
|
|
| 935 |
+ |
|
| 936 |
+func NewPathOpts() PathOpts {
|
|
| 937 |
+ return make(PathOpts) |
|
| 938 |
+} |
|
| 939 |
+ |
|
| 940 |
+func (opts PathOpts) String() string {
|
|
| 941 |
+ return fmt.Sprintf("%v", map[string]struct{}(opts))
|
|
| 942 |
+} |
|
| 943 |
+ |
|
| 944 |
+func (opts PathOpts) Set(val string) error {
|
|
| 945 |
+ if !filepath.IsAbs(val) {
|
|
| 946 |
+ return fmt.Errorf("%s is not an absolute path", val)
|
|
| 947 |
+ } |
|
| 948 |
+ opts[filepath.Clean(val)] = struct{}{}
|
|
| 949 |
+ return nil |
|
| 950 |
+} |
|
| 951 |
+ |
|
| 933 | 952 |
func CmdTag(args ...string) error {
|
| 934 | 953 |
cmd := Subcmd("tag", "[OPTIONS] IMAGE REPOSITORY [TAG]", "Tag an image into a repository")
|
| 935 | 954 |
force := cmd.Bool("f", false, "Force")
|
| ... | ... |
@@ -48,6 +48,7 @@ type Container struct {
|
| 48 | 48 |
runtime *Runtime |
| 49 | 49 |
|
| 50 | 50 |
waitLock chan struct{}
|
| 51 |
+ Volumes map[string]string |
|
| 51 | 52 |
} |
| 52 | 53 |
|
| 53 | 54 |
type Config struct {
|
| ... | ... |
@@ -66,6 +67,8 @@ type Config struct {
|
| 66 | 66 |
Cmd []string |
| 67 | 67 |
Dns []string |
| 68 | 68 |
Image string // Name of the image as it was passed by the operator (eg. could be symbolic) |
| 69 |
+ Volumes map[string]struct{}
|
|
| 70 |
+ VolumesFrom string |
|
| 69 | 71 |
} |
| 70 | 72 |
|
| 71 | 73 |
func ParseRun(args []string) (*Config, *flag.FlagSet, error) {
|
| ... | ... |
@@ -92,6 +95,11 @@ func ParseRun(args []string) (*Config, *flag.FlagSet, error) {
|
| 92 | 92 |
var flDns ListOpts |
| 93 | 93 |
cmd.Var(&flDns, "dns", "Set custom dns servers") |
| 94 | 94 |
|
| 95 |
+ flVolumes := NewPathOpts() |
|
| 96 |
+ cmd.Var(flVolumes, "v", "Attach a data volume") |
|
| 97 |
+ |
|
| 98 |
+ flVolumesFrom := cmd.String("volumes-from", "", "Mount volumes from the specified container")
|
|
| 99 |
+ |
|
| 95 | 100 |
if err := cmd.Parse(args); err != nil {
|
| 96 | 101 |
return nil, cmd, err |
| 97 | 102 |
} |
| ... | ... |
@@ -131,6 +139,8 @@ func ParseRun(args []string) (*Config, *flag.FlagSet, error) {
|
| 131 | 131 |
Cmd: runCmd, |
| 132 | 132 |
Dns: flDns, |
| 133 | 133 |
Image: image, |
| 134 |
+ Volumes: flVolumes, |
|
| 135 |
+ VolumesFrom: *flVolumesFrom, |
|
| 134 | 136 |
} |
| 135 | 137 |
|
| 136 | 138 |
// When allocating stdin in attached mode, close stdin at client disconnect |
| ... | ... |
@@ -384,10 +394,40 @@ func (container *Container) Start() error {
|
| 384 | 384 |
log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
|
| 385 | 385 |
container.Config.MemorySwap = -1 |
| 386 | 386 |
} |
| 387 |
+ container.Volumes = make(map[string]string) |
|
| 388 |
+ |
|
| 389 |
+ // Create the requested volumes volumes |
|
| 390 |
+ for volPath := range container.Config.Volumes {
|
|
| 391 |
+ if c, err := container.runtime.volumes.Create(nil, container, "", "", nil); err != nil {
|
|
| 392 |
+ return err |
|
| 393 |
+ } else {
|
|
| 394 |
+ if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
|
|
| 395 |
+ return nil |
|
| 396 |
+ } |
|
| 397 |
+ container.Volumes[volPath] = c.Id |
|
| 398 |
+ } |
|
| 399 |
+ } |
|
| 400 |
+ |
|
| 401 |
+ if container.Config.VolumesFrom != "" {
|
|
| 402 |
+ c := container.runtime.Get(container.Config.VolumesFrom) |
|
| 403 |
+ if c == nil {
|
|
| 404 |
+ return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.Id)
|
|
| 405 |
+ } |
|
| 406 |
+ for volPath, id := range c.Volumes {
|
|
| 407 |
+ if _, exists := container.Volumes[volPath]; exists {
|
|
| 408 |
+ return fmt.Errorf("The requested volume %s overlap one of the volume of the container %s", volPath, c.Id)
|
|
| 409 |
+ } |
|
| 410 |
+ if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
|
|
| 411 |
+ return nil |
|
| 412 |
+ } |
|
| 413 |
+ container.Volumes[volPath] = id |
|
| 414 |
+ } |
|
| 415 |
+ } |
|
| 387 | 416 |
|
| 388 | 417 |
if err := container.generateLXCConfig(); err != nil {
|
| 389 | 418 |
return err |
| 390 | 419 |
} |
| 420 |
+ |
|
| 391 | 421 |
params := []string{
|
| 392 | 422 |
"-n", container.Id, |
| 393 | 423 |
"-f", container.lxcConfigPath(), |
| ... | ... |
@@ -446,6 +486,7 @@ func (container *Container) Start() error {
|
| 446 | 446 |
|
| 447 | 447 |
// Init the lock |
| 448 | 448 |
container.waitLock = make(chan struct{})
|
| 449 |
+ |
|
| 449 | 450 |
container.ToDisk() |
| 450 | 451 |
go container.monitor() |
| 451 | 452 |
return nil |
| ... | ... |
@@ -777,6 +818,22 @@ func (container *Container) RootfsPath() string {
|
| 777 | 777 |
return path.Join(container.root, "rootfs") |
| 778 | 778 |
} |
| 779 | 779 |
|
| 780 |
+func (container *Container) GetVolumes() (map[string]string, error) {
|
|
| 781 |
+ ret := make(map[string]string) |
|
| 782 |
+ for volPath, id := range container.Volumes {
|
|
| 783 |
+ volume, err := container.runtime.volumes.Get(id) |
|
| 784 |
+ if err != nil {
|
|
| 785 |
+ return nil, err |
|
| 786 |
+ } |
|
| 787 |
+ root, err := volume.root() |
|
| 788 |
+ if err != nil {
|
|
| 789 |
+ return nil, err |
|
| 790 |
+ } |
|
| 791 |
+ ret[volPath] = path.Join(root, "layer") |
|
| 792 |
+ } |
|
| 793 |
+ return ret, nil |
|
| 794 |
+} |
|
| 795 |
+ |
|
| 780 | 796 |
func (container *Container) rwPath() string {
|
| 781 | 797 |
return path.Join(container.root, "rw") |
| 782 | 798 |
} |
| ... | ... |
@@ -17,3 +17,6 @@ |
| 17 | 17 |
-p=[]: Map a network port to the container |
| 18 | 18 |
-t=false: Allocate a pseudo-tty |
| 19 | 19 |
-u="": Username or UID |
| 20 |
+ -d=[]: Set custom dns servers for the container |
|
| 21 |
+ -v=[]: Creates a new volumes and mount it at the specified path. |
|
| 22 |
+ -volumes-from="": Mount all volumes from the given container. |
| ... | ... |
@@ -12,7 +12,7 @@ Images |
| 12 | 12 |
------ |
| 13 | 13 |
An original container image. These are stored on disk and are comparable with what you normally expect from a stopped virtual machine image. Images are stored (and retrieved from) repository |
| 14 | 14 |
|
| 15 |
-Images are stored on your local file system under /var/lib/docker/images |
|
| 15 |
+Images are stored on your local file system under /var/lib/docker/graph |
|
| 16 | 16 |
|
| 17 | 17 |
|
| 18 | 18 |
.. _containers: |
| 19 | 19 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,53 @@ |
| 0 |
+:title: Sharing data between 2 couchdb databases |
|
| 1 |
+:description: Sharing data between 2 couchdb databases |
|
| 2 |
+:keywords: docker, example, package installation, networking, couchdb, data volumes |
|
| 3 |
+ |
|
| 4 |
+.. _running_redis_service: |
|
| 5 |
+ |
|
| 6 |
+Create a redis service |
|
| 7 |
+====================== |
|
| 8 |
+ |
|
| 9 |
+.. include:: example_header.inc |
|
| 10 |
+ |
|
| 11 |
+Here's an example of using data volumes to share the same data between 2 couchdb containers. |
|
| 12 |
+This could be used for hot upgrades, testing different versions of couchdb on the same data, etc. |
|
| 13 |
+ |
|
| 14 |
+Create first database |
|
| 15 |
+--------------------- |
|
| 16 |
+ |
|
| 17 |
+Note that we're marking /var/lib/couchdb as a data volume. |
|
| 18 |
+ |
|
| 19 |
+.. code-block:: bash |
|
| 20 |
+ |
|
| 21 |
+ COUCH1=$(docker run -d -v /var/lib/couchdb shykes/couchdb:2013-05-03) |
|
| 22 |
+ |
|
| 23 |
+Add data to the first database |
|
| 24 |
+------------------------------ |
|
| 25 |
+ |
|
| 26 |
+We're assuming your docker host is reachable at `localhost`. If not, replace `localhost` with the public IP of your docker host. |
|
| 27 |
+ |
|
| 28 |
+.. code-block:: bash |
|
| 29 |
+ |
|
| 30 |
+ HOST=localhost |
|
| 31 |
+ URL="http://$HOST:$(docker port $COUCH1 5984)/_utils/" |
|
| 32 |
+ echo "Navigate to $URL in your browser, and use the couch interface to add data" |
|
| 33 |
+ |
|
| 34 |
+Create second database |
|
| 35 |
+---------------------- |
|
| 36 |
+ |
|
| 37 |
+This time, we're requesting shared access to $COUCH1's volumes. |
|
| 38 |
+ |
|
| 39 |
+.. code-block:: bash |
|
| 40 |
+ |
|
| 41 |
+ COUCH2=$(docker run -d -volumes-from $COUCH1) shykes/couchdb:2013-05-03) |
|
| 42 |
+ |
|
| 43 |
+Browse data on the second database |
|
| 44 |
+---------------------------------- |
|
| 45 |
+ |
|
| 46 |
+.. code-block:: bash |
|
| 47 |
+ |
|
| 48 |
+ HOST=localhost |
|
| 49 |
+ URL="http://$HOST:$(docker port $COUCH2 5984)/_utils/" |
|
| 50 |
+ echo "Navigate to $URL in your browser. You should see the same data as in the first database!" |
|
| 51 |
+ |
|
| 52 |
+Congratulations, you are running 2 Couchdb containers, completely isolated from each other *except* for their data. |
| ... | ... |
@@ -79,7 +79,11 @@ lxc.mount.entry = {{.SysInitPath}} {{$ROOTFS}}/sbin/init none bind,ro 0 0
|
| 79 | 79 |
|
| 80 | 80 |
# In order to get a working DNS environment, mount bind (ro) the host's /etc/resolv.conf into the container |
| 81 | 81 |
lxc.mount.entry = {{.ResolvConfPath}} {{$ROOTFS}}/etc/resolv.conf none bind,ro 0 0
|
| 82 |
- |
|
| 82 |
+{{if .Volumes}}
|
|
| 83 |
+{{range $virtualPath, $realPath := .GetVolumes}}
|
|
| 84 |
+lxc.mount.entry = {{$realPath}} {{$ROOTFS}}/{{$virtualPath}} none bind,rw 0 0
|
|
| 85 |
+{{end}}
|
|
| 86 |
+{{end}}
|
|
| 83 | 87 |
|
| 84 | 88 |
# drop linux capabilities (apply mainly to the user root in the container) |
| 85 | 89 |
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 |
| ... | ... |
@@ -1,3 +1,12 @@ |
| 1 |
+lxc-docker (0.2.2-1) precise; urgency=low |
|
| 2 |
+ - Support for data volumes ('docker run -v=PATH')
|
|
| 3 |
+ - Share data volumes between containers ('docker run -volumes-from')
|
|
| 4 |
+ - Improved documentation |
|
| 5 |
+ - Upgrade to Go 1.0.3 |
|
| 6 |
+ - Various upgrades to the dev environment for contributors |
|
| 7 |
+ |
|
| 8 |
+ -- dotCloud <ops@dotcloud.com> Fri, 3 May 2013 00:00:00 -0700 |
|
| 9 |
+ |
|
| 1 | 10 |
|
| 2 | 11 |
lxc-docker (0.2.1-1) precise; urgency=low |
| 3 | 12 |
|
| ... | ... |
@@ -32,6 +32,7 @@ type Runtime struct {
|
| 32 | 32 |
capabilities *Capabilities |
| 33 | 33 |
kernelVersion *KernelVersionInfo |
| 34 | 34 |
autoRestart bool |
| 35 |
+ volumes *Graph |
|
| 35 | 36 |
} |
| 36 | 37 |
|
| 37 | 38 |
var sysInitPath string |
| ... | ... |
@@ -79,10 +80,10 @@ func (runtime *Runtime) containerRoot(id string) string {
|
| 79 | 79 |
} |
| 80 | 80 |
|
| 81 | 81 |
func (runtime *Runtime) mergeConfig(userConf, imageConf *Config) {
|
| 82 |
- if userConf.Hostname != "" {
|
|
| 82 |
+ if userConf.Hostname == "" {
|
|
| 83 | 83 |
userConf.Hostname = imageConf.Hostname |
| 84 | 84 |
} |
| 85 |
- if userConf.User != "" {
|
|
| 85 |
+ if userConf.User == "" {
|
|
| 86 | 86 |
userConf.User = imageConf.User |
| 87 | 87 |
} |
| 88 | 88 |
if userConf.Memory == 0 {
|
| ... | ... |
@@ -126,7 +127,7 @@ func (runtime *Runtime) Create(config *Config) (*Container, error) {
|
| 126 | 126 |
runtime.mergeConfig(config, img.Config) |
| 127 | 127 |
} |
| 128 | 128 |
|
| 129 |
- if config.Cmd == nil {
|
|
| 129 |
+ if config.Cmd == nil || len(config.Cmd) == 0 {
|
|
| 130 | 130 |
return nil, fmt.Errorf("No command specified")
|
| 131 | 131 |
} |
| 132 | 132 |
|
| ... | ... |
@@ -405,6 +406,10 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
|
| 405 | 405 |
if err != nil {
|
| 406 | 406 |
return nil, err |
| 407 | 407 |
} |
| 408 |
+ volumes, err := NewGraph(path.Join(root, "volumes")) |
|
| 409 |
+ if err != nil {
|
|
| 410 |
+ return nil, err |
|
| 411 |
+ } |
|
| 408 | 412 |
repositories, err := NewTagStore(path.Join(root, "repositories"), g) |
| 409 | 413 |
if err != nil {
|
| 410 | 414 |
return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
|
| ... | ... |
@@ -432,6 +437,7 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
|
| 432 | 432 |
idIndex: NewTruncIndex(), |
| 433 | 433 |
capabilities: &Capabilities{},
|
| 434 | 434 |
autoRestart: autoRestart, |
| 435 |
+ volumes: volumes, |
|
| 435 | 436 |
} |
| 436 | 437 |
|
| 437 | 438 |
if err := runtime.restore(); err != nil {
|
| ... | ... |
@@ -294,11 +294,38 @@ func (srv *Server) ContainerRestart(name string, t int) error {
|
| 294 | 294 |
return nil |
| 295 | 295 |
} |
| 296 | 296 |
|
| 297 |
-func (srv *Server) ContainerDestroy(name string) error {
|
|
| 297 |
+func (srv *Server) ContainerDestroy(name string, v bool) error {
|
|
| 298 |
+ |
|
| 298 | 299 |
if container := srv.runtime.Get(name); container != nil {
|
| 300 |
+ volumes := make(map[string]struct{})
|
|
| 301 |
+ // Store all the deleted containers volumes |
|
| 302 |
+ for _, volumeId := range container.Volumes {
|
|
| 303 |
+ volumes[volumeId] = struct{}{}
|
|
| 304 |
+ } |
|
| 299 | 305 |
if err := srv.runtime.Destroy(container); err != nil {
|
| 300 | 306 |
return fmt.Errorf("Error destroying container %s: %s", name, err.Error())
|
| 301 | 307 |
} |
| 308 |
+ |
|
| 309 |
+ if v {
|
|
| 310 |
+ // Retrieve all volumes from all remaining containers |
|
| 311 |
+ usedVolumes := make(map[string]*Container) |
|
| 312 |
+ for _, container := range srv.runtime.List() {
|
|
| 313 |
+ for _, containerVolumeId := range container.Volumes {
|
|
| 314 |
+ usedVolumes[containerVolumeId] = container |
|
| 315 |
+ } |
|
| 316 |
+ } |
|
| 317 |
+ |
|
| 318 |
+ for volumeId := range volumes {
|
|
| 319 |
+ // If the requested volu |
|
| 320 |
+ if c, exists := usedVolumes[volumeId]; exists {
|
|
| 321 |
+ log.Printf("The volume %s is used by the container %s. Impossible to remove it. Skipping.\n", volumeId, c.Id)
|
|
| 322 |
+ continue |
|
| 323 |
+ } |
|
| 324 |
+ if err := srv.runtime.volumes.Delete(volumeId); err != nil {
|
|
| 325 |
+ return err |
|
| 326 |
+ } |
|
| 327 |
+ } |
|
| 328 |
+ } |
|
| 302 | 329 |
} else {
|
| 303 | 330 |
return fmt.Errorf("No such container: %s", name)
|
| 304 | 331 |
} |
| ... | ... |
@@ -466,7 +466,7 @@ func FindCgroupMountpoint(cgroupType string) (string, error) {
|
| 466 | 466 |
// cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0 |
| 467 | 467 |
for _, line := range strings.Split(string(output), "\n") {
|
| 468 | 468 |
parts := strings.Split(line, " ") |
| 469 |
- if parts[2] == "cgroup" {
|
|
| 469 |
+ if len(parts) == 6 && parts[2] == "cgroup" {
|
|
| 470 | 470 |
for _, opt := range strings.Split(parts[3], ",") {
|
| 471 | 471 |
if opt == cgroupType {
|
| 472 | 472 |
return parts[1], nil |