Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit 0e83b2f

Browse files
committed
Merge pull request #130 from vdemeester/129-fix-rm-command
Fixing rm command and API…
2 parents 90f0f98 + 26edba0 commit 0e83b2f

File tree

5 files changed

+115
-21
lines changed

5 files changed

+115
-21
lines changed

cli/app/app.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,26 @@ func ProjectPull(p *project.Project, c *cli.Context) {
163163

164164
// ProjectDelete delete services.
165165
func ProjectDelete(p *project.Project, c *cli.Context) {
166-
if !c.Bool("force") && len(c.Args()) == 0 {
167-
logrus.Fatal("Will not remove all services without --force")
166+
stoppedContainers, err := p.ListStoppedContainers(c.Args()...)
167+
if err != nil {
168+
logrus.Fatal(err)
169+
}
170+
if len(stoppedContainers) == 0 {
171+
fmt.Println("No stopped containers")
172+
return
173+
}
174+
if !c.Bool("force") {
175+
fmt.Printf("Going to remove %v\nAre you sure? [yN]\n", strings.Join(stoppedContainers, ", "))
176+
var answer string
177+
_, err := fmt.Scanln(&answer)
178+
if err != nil {
179+
logrus.Fatal(err)
180+
}
181+
if answer != "y" && answer != "Y" {
182+
return
183+
}
168184
}
169-
err := p.Delete(c.Args()...)
185+
err = p.Delete(c.Args()...)
170186
if err != nil {
171187
logrus.Fatal(err)
172188
}

docker/container.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,26 @@ func (c *Container) Delete() error {
210210
return err
211211
}
212212

213-
if info.State.Running {
214-
err := c.client.StopContainer(container.ID, c.service.context.Timeout)
215-
if err != nil {
216-
return err
217-
}
213+
if !info.State.Running {
214+
return c.client.RemoveContainer(dockerclient.RemoveContainerOptions{ID: container.ID, RemoveVolumes: c.service.context.Volume})
215+
}
216+
217+
return nil
218+
}
219+
220+
// IsRunning returns the running state of the container.
221+
func (c *Container) IsRunning() (bool, error) {
222+
container, err := c.findExisting()
223+
if err != nil || container == nil {
224+
return false, err
225+
}
226+
227+
info, err := c.client.InspectContainer(container.ID)
228+
if err != nil {
229+
return false, err
218230
}
219231

220-
return c.client.RemoveContainer(dockerclient.RemoveContainerOptions{ID: container.ID, Force: true, RemoveVolumes: c.service.context.Volume})
232+
return info.State.Running, nil
221233
}
222234

223235
// Up creates and start the container based on the image name and send an event

integration/rm_test.go

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,53 @@ func (s *RunSuite) TestDelete(c *C) {
1515
c.Assert(cn, NotNil)
1616
c.Assert(cn.State.Running, Equals, true)
1717

18-
s.FromText(c, p, "rm", "--force", `
19-
hello:
20-
image: busybox
21-
stdin_open: true
22-
tty: true
23-
`)
18+
s.FromText(c, p, "stop", SimpleTemplate)
19+
s.FromText(c, p, "rm", "--force", SimpleTemplate)
2420

2521
cn = s.GetContainerByName(c, name)
2622
c.Assert(cn, IsNil)
2723
}
2824

25+
func (s *RunSuite) TestDeleteOnlyRemovesStopped(c *C) {
26+
projectTemplate := `
27+
hello:
28+
image: busybox
29+
stdin_open: true
30+
tty: true
31+
bye:
32+
image: busybox
33+
stdin_open: true
34+
tty: true
35+
`
36+
37+
p := s.ProjectFromText(c, "up", projectTemplate)
38+
39+
helloName := fmt.Sprintf("%s_%s_1", p, "hello")
40+
byeName := fmt.Sprintf("%s_%s_1", p, "bye")
41+
42+
helloContainer := s.GetContainerByName(c, helloName)
43+
c.Assert(helloContainer, NotNil)
44+
c.Assert(helloContainer.State.Running, Equals, true)
45+
46+
byeContainer := s.GetContainerByName(c, byeName)
47+
c.Assert(byeContainer, NotNil)
48+
c.Assert(byeContainer.State.Running, Equals, true)
49+
50+
s.FromText(c, p, "stop", "bye", projectTemplate)
51+
52+
byeContainer = s.GetContainerByName(c, byeName)
53+
c.Assert(byeContainer, NotNil)
54+
c.Assert(byeContainer.State.Running, Equals, false)
55+
56+
s.FromText(c, p, "rm", "--force", projectTemplate)
57+
58+
byeContainer = s.GetContainerByName(c, byeName)
59+
c.Assert(byeContainer, IsNil)
60+
61+
helloContainer = s.GetContainerByName(c, helloName)
62+
c.Assert(helloContainer, NotNil)
63+
}
64+
2965
func (s *RunSuite) TestDeleteWithVol(c *C) {
3066
p := s.ProjectFromText(c, "up", SimpleTemplate)
3167

@@ -35,12 +71,8 @@ func (s *RunSuite) TestDeleteWithVol(c *C) {
3571
c.Assert(cn, NotNil)
3672
c.Assert(cn.State.Running, Equals, true)
3773

38-
s.FromText(c, p, "rm", "--force", "-v", `
39-
hello:
40-
image: busybox
41-
stdin_open: true
42-
tty: true
43-
`)
74+
s.FromText(c, p, "stop", SimpleTemplate)
75+
s.FromText(c, p, "rm", "--force", "-v", SimpleTemplate)
4476

4577
cn = s.GetContainerByName(c, name)
4678
c.Assert(cn, IsNil)

project/project.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,39 @@ func (p *Project) Pull(services ...string) error {
226226
}), nil)
227227
}
228228

229+
// ListStoppedContainers lists the stopped containers for the specified services.
230+
func (p *Project) ListStoppedContainers(services ...string) ([]string, error) {
231+
stoppedContainers := []string{}
232+
err := p.forEach(services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {
233+
wrapper.Do(nil, EventServiceDeleteStart, EventServiceDelete, func(service Service) error {
234+
containers, innerErr := service.Containers()
235+
if innerErr != nil {
236+
return innerErr
237+
}
238+
239+
for _, container := range containers {
240+
running, innerErr := container.IsRunning()
241+
if innerErr != nil {
242+
log.Error(innerErr)
243+
}
244+
if !running {
245+
containerID, innerErr := container.ID()
246+
if innerErr != nil {
247+
log.Error(innerErr)
248+
}
249+
stoppedContainers = append(stoppedContainers, containerID)
250+
}
251+
}
252+
253+
return nil
254+
})
255+
}), nil)
256+
if err != nil {
257+
return nil, err
258+
}
259+
return stoppedContainers, nil
260+
}
261+
229262
// Delete removes the specified services (like docker rm).
230263
func (p *Project) Delete(services ...string) error {
231264
return p.perform(EventProjectDeleteStart, EventProjectDeleteDone, services, wrapperAction(func(wrapper *serviceWrapper, wrappers map[string]*serviceWrapper) {

project/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ type Container interface {
264264
ID() (string, error)
265265
Name() string
266266
Port(port string) (string, error)
267+
IsRunning() (bool, error)
267268
}
268269

269270
// ServiceFactory is an interface factory to create Service object for the specified

0 commit comments

Comments
 (0)