docker volume rm

Signed-off-by: Victor Vieux <vieux@docker.com>
This commit is contained in:
Victor Vieux 2015-09-06 00:55:43 -07:00
parent ce7c0a6dfa
commit 440a379860
12 changed files with 138 additions and 12 deletions

2
Godeps/Godeps.json generated
View File

@ -104,7 +104,7 @@
},
{
"ImportPath": "github.com/samalba/dockerclient",
"Rev": "77b723e2c0d07a21cc9483dc6108f95bd403b576"
"Rev": "14849c8f5ab609e834348fccad25e32877952fa7"
},
{
"ImportPath": "github.com/samuel/go-zookeeper/zk",

View File

@ -757,3 +757,9 @@ func (client *DockerClient) ListVolumes() ([]*Volume, error) {
}
return volumesList.Volumes, nil
}
func (client *DockerClient) RemoveVolume(name string) error {
uri := fmt.Sprintf("/%s/volumes/%s", APIVersion, name)
_, err := client.doRequest("DELETE", uri, nil, nil)
return err
}

View File

@ -46,4 +46,5 @@ type Client interface {
ImportImage(source string, repository string, tag string, tar io.Reader) (io.ReadCloser, error)
BuildImage(image *BuildImage) (io.ReadCloser, error)
ListVolumes() ([]*Volume, error)
RemoveVolume(name string) error
}

View File

@ -175,3 +175,8 @@ func (client *MockClient) ListVolumes() ([]*dockerclient.Volume, error) {
args := client.Mock.Called()
return args.Get(0).([]*dockerclient.Volume), args.Error(1)
}
func (client *MockClient) RemoveVolume(name string) error {
args := client.Mock.Called(name)
return args.Error(0)
}

View File

@ -149,3 +149,7 @@ func (client *NopClient) BuildImage(image *dockerclient.BuildImage) (io.ReadClos
func (client *NopClient) ListVolumes() ([]*dockerclient.Volume, error) {
return nil, ErrNoEngine
}
func (client *NopClient) RemoveVolume(name string) error {
return ErrNoEngine
}

View File

@ -401,12 +401,13 @@ func deleteContainers(c *context, w http.ResponseWriter, r *http.Request) {
name := mux.Vars(r)["name"]
force := boolValue(r, "force")
volumes := boolValue(r, "v")
container := c.cluster.Container(name)
if container == nil {
httpError(w, fmt.Sprintf("Container %s not found", name), http.StatusNotFound)
return
}
if err := c.cluster.RemoveContainer(container, force); err != nil {
if err := c.cluster.RemoveContainer(container, force, volumes); err != nil {
httpError(w, err.Error(), http.StatusInternalServerError)
return
}
@ -572,6 +573,27 @@ func deleteImages(c *context, w http.ResponseWriter, r *http.Request) {
json.NewEncoder(NewWriteFlusher(w)).Encode(out)
}
// DELETE /volumes/{names:.*}
func deleteVolumes(c *context, w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
httpError(w, err.Error(), http.StatusInternalServerError)
return
}
var name = mux.Vars(r)["name"]
found, err := c.cluster.RemoveVolumes(name)
if err != nil {
httpError(w, err.Error(), http.StatusInternalServerError)
return
}
if !found {
httpError(w, fmt.Sprintf("No such volume %s", name), http.StatusNotFound)
return
}
w.WriteHeader(http.StatusNoContent)
}
// GET /_ping
func ping(c *context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte{'O', 'K'})

View File

@ -73,6 +73,7 @@ var routes = map[string]map[string]handler{
"/containers/{name:.*}/exec": postContainersExec,
"/exec/{execid:.*}/start": proxyHijack,
"/exec/{execid:.*}/resize": proxyContainer,
// "/volumes": postVolumes,
},
"PUT": {
"/containers/{name:.*}/archive": proxyContainer,
@ -80,6 +81,7 @@ var routes = map[string]map[string]handler{
"DELETE": {
"/containers/{name:.*}": deleteContainers,
"/images/{name:.*}": deleteImages,
"/volumes/{name:.*}": deleteVolumes,
},
"OPTIONS": {
"": optionsHandler,

View File

@ -12,7 +12,7 @@ type Cluster interface {
CreateContainer(config *ContainerConfig, name string) (*Container, error)
// Remove a container
RemoveContainer(container *Container, force bool) error
RemoveContainer(container *Container, force, volumes bool) error
// Return all images
Images(all bool) []*Image
@ -37,6 +37,9 @@ type Cluster interface {
// Return one volume from the cluster
Volume(name string) *Volume
// Remove volumes from the cluster
RemoveVolumes(name string) (bool, error)
// Pull images
// `callback` can be called multiple time
// `where` is where it is being pulled

View File

@ -36,6 +36,7 @@ func NewEngine(addr string, overcommitRatio float64) *Engine {
Labels: make(map[string]string),
stopCh: make(chan struct{}),
containers: make(map[string]*Container),
volumes: make(map[string]*Volume),
healthy: true,
overcommitRatio: int64(overcommitRatio * 100),
}
@ -57,7 +58,7 @@ type Engine struct {
stopCh chan struct{}
containers map[string]*Container
images []*Image
volumes []*Volume
volumes map[string]*Volume
client dockerclient.Client
eventHandler EventHandler
healthy bool
@ -187,6 +188,21 @@ func (e *Engine) RemoveImage(image *Image, name string, force bool) ([]*dockercl
return e.client.RemoveImage(name, force)
}
// RemoveVolume deletes a volume from the engine.
func (e *Engine) RemoveVolume(name string) error {
if err := e.client.RemoveVolume(name); err != nil {
return err
}
// Remove the container from the state. Eventually, the state refresh loop
// will rewrite this.
e.Lock()
defer e.Unlock()
delete(e.volumes, name)
return nil
}
// RefreshImages refreshes the list of images on the engine.
func (e *Engine) RefreshImages() error {
images, err := e.client.ListImages(true)
@ -209,9 +225,9 @@ func (e *Engine) RefreshVolumes() error {
return err
}
e.Lock()
e.volumes = nil
e.volumes = make(map[string]*Volume)
for _, volume := range volumes {
e.volumes = append(e.volumes, &Volume{Volume: *volume, Engine: e})
e.volumes[volume.Name] = &Volume{Volume: *volume, Engine: e}
}
e.Unlock()
return nil
@ -455,8 +471,8 @@ func (e *Engine) Create(config *ContainerConfig, name string, pullImage bool) (*
}
// RemoveContainer a container from the engine.
func (e *Engine) RemoveContainer(container *Container, force bool) error {
if err := e.client.RemoveContainer(container.Id, force, true); err != nil {
func (e *Engine) RemoveContainer(container *Container, force, volumes bool) error {
if err := e.client.RemoveContainer(container.Id, force, volumes); err != nil {
return err
}

View File

@ -179,11 +179,11 @@ func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string)
}
// RemoveContainer to remove containers on mesos cluster
func (c *Cluster) RemoveContainer(container *cluster.Container, force bool) error {
func (c *Cluster) RemoveContainer(container *cluster.Container, force, volumes bool) error {
c.scheduler.Lock()
defer c.scheduler.Unlock()
return container.Engine.RemoveContainer(container, force)
return container.Engine.RemoveContainer(container, force, volumes)
}
// Images returns all the images in the cluster.
@ -222,6 +222,11 @@ func (c *Cluster) RemoveImages(name string, force bool) ([]*dockerclient.ImageDe
return nil, errNotSupported
}
// RemoveVolumes removes volumes from the cluster
func (c *Cluster) RemoveVolumes(name string) (bool, error) {
return false, errNotSupported
}
func formatContainer(container *cluster.Container) *cluster.Container {
if container == nil {
return nil

View File

@ -131,11 +131,11 @@ func (c *Cluster) createContainer(config *cluster.ContainerConfig, name string,
// RemoveContainer aka Remove a container from the cluster. Containers should
// always be destroyed through the scheduler to guarantee atomicity.
func (c *Cluster) RemoveContainer(container *cluster.Container, force bool) error {
func (c *Cluster) RemoveContainer(container *cluster.Container, force, volumes bool) error {
c.scheduler.Lock()
defer c.scheduler.Unlock()
err := container.Engine.RemoveContainer(container, force)
err := container.Engine.RemoveContainer(container, force, volumes)
return err
}
@ -295,6 +295,31 @@ func (c *Cluster) RemoveImages(name string, force bool) ([]*dockerclient.ImageDe
return out, err
}
// RemoveVolumes removes all the volumes that match `name` from the cluster
func (c *Cluster) RemoveVolumes(name string) (bool, error) {
c.Lock()
defer c.Unlock()
found := false
errs := []string{}
var err error
for _, e := range c.engines {
for _, volume := range e.Volumes() {
if volume.Name == name {
if err := volume.Engine.RemoveVolume(name); err != nil {
errs = append(errs, fmt.Sprintf("%s: %s", volume.Engine.Name, err.Error()))
continue
}
found = true
}
}
}
if len(errs) > 0 {
err = errors.New(strings.Join(errs, "\n"))
}
return found, err
}
// Pull is exported
func (c *Cluster) Pull(name string, authConfig *dockerclient.AuthConfig, callback func(where, status string, err error)) {
var wg sync.WaitGroup

View File

@ -40,4 +40,41 @@ function teardown() {
run docker_swarm volume inspect ${output}
[ "${#lines[@]}" -eq 7 ]
[[ "${output}" == *"\"Driver\": \"local\""* ]]
}
@test "docker volume create" {
skip
start_docker 2
swarm_manage
docker_swarm volume create --name=test_volume
run docker_swarm volume
[ "${#lines[@]}" -eq 3 ]
}
@test "docker volume rm" {
start_docker_with_busybox 2
swarm_manage
run docker_swarm volume rm test_volume
[ "$status" -ne 0 ]
docker_swarm run -d --name=test_container -v=/tmp busybox true
run docker_swarm volume ls -q
volume=${output}
[ "${#lines[@]}" -eq 1 ]
run docker_swarm volume rm $volume
[ "$status" -ne 0 ]
docker_swarm rm test_container
run docker_swarm volume rm $volume
[ "$status" -eq 0 ]
[ "${#lines[@]}" -eq 1 ]
run docker_swarm volume
echo $output
[ "${#lines[@]}" -eq 1 ]
}