github.com/containerd/nerdctl@v1.7.7/pkg/composer/rm.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package composer 18 19 import ( 20 "context" 21 "strings" 22 "sync" 23 24 "github.com/containerd/containerd" 25 "github.com/containerd/log" 26 "github.com/containerd/nerdctl/pkg/composer/serviceparser" 27 "github.com/containerd/nerdctl/pkg/formatter" 28 "github.com/containerd/nerdctl/pkg/labels" 29 "github.com/containerd/nerdctl/pkg/strutil" 30 ) 31 32 // RemoveOptions stores all options when removing compose containers: 33 // Stop: if true, remove using `rm -f`; if false, check and skip running containers. 34 // Volumes: if remove anonymous volumes associated with the container. 35 type RemoveOptions struct { 36 Stop bool 37 Volumes bool 38 } 39 40 // Remove removes stopped containers in `services`. 41 func (c *Composer) Remove(ctx context.Context, opt RemoveOptions, services []string) error { 42 serviceNames, err := c.ServiceNames(services...) 43 if err != nil { 44 return err 45 } 46 // reverse dependency order 47 for _, svc := range strutil.ReverseStrSlice(serviceNames) { 48 containers, err := c.Containers(ctx, svc) 49 if err != nil { 50 return err 51 } 52 if opt.Stop { 53 // use default Options to stop service containers. 54 if err := c.stopContainers(ctx, containers, StopOptions{}); err != nil { 55 return err 56 } 57 } 58 if err := c.removeContainers(ctx, containers, opt); err != nil { 59 return err 60 } 61 } 62 return nil 63 } 64 65 func (c *Composer) removeContainers(ctx context.Context, containers []containerd.Container, opt RemoveOptions) error { 66 args := []string{"rm", "-f"} 67 if opt.Volumes { 68 args = append(args, "-v") 69 } 70 71 var rmWG sync.WaitGroup 72 for _, container := range containers { 73 container := container 74 rmWG.Add(1) 75 go func() { 76 defer rmWG.Done() 77 info, _ := container.Info(ctx, containerd.WithoutRefreshedMetadata) 78 // if not `Stop`, check status and skip running container 79 if !opt.Stop { 80 cStatus := formatter.ContainerStatus(ctx, container) 81 if strings.HasPrefix(cStatus, "Up") { 82 log.G(ctx).Warnf("Removing container %s failed: container still running.", info.Labels[labels.Name]) 83 return 84 } 85 } 86 87 log.G(ctx).Infof("Removing container %s", info.Labels[labels.Name]) 88 if err := c.runNerdctlCmd(ctx, append(args, container.ID())...); err != nil { 89 log.G(ctx).Warn(err) 90 } 91 }() 92 } 93 rmWG.Wait() 94 95 return nil 96 } 97 98 func (c *Composer) removeContainersFromParsedServices(ctx context.Context, containers map[string]serviceparser.Container) { 99 var rmWG sync.WaitGroup 100 for id, container := range containers { 101 id := id 102 container := container 103 rmWG.Add(1) 104 go func() { 105 defer rmWG.Done() 106 log.G(ctx).Infof("Removing container %s", container.Name) 107 if err := c.runNerdctlCmd(ctx, "rm", "-f", id); err != nil { 108 log.G(ctx).Warn(err) 109 } 110 }() 111 } 112 rmWG.Wait() 113 }