github.com/containerd/nerdctl@v1.7.7/pkg/composer/down.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 "fmt" 22 23 "github.com/containerd/log" 24 "github.com/containerd/nerdctl/pkg/strutil" 25 ) 26 27 type DownOptions struct { 28 RemoveVolumes bool 29 RemoveOrphans bool 30 } 31 32 func (c *Composer) Down(ctx context.Context, downOptions DownOptions) error { 33 serviceNames, err := c.ServiceNames() 34 if err != nil { 35 return err 36 } 37 // reverse dependency order 38 for _, svc := range strutil.ReverseStrSlice(serviceNames) { 39 containers, err := c.Containers(ctx, svc) 40 if err != nil { 41 return err 42 } 43 if err := c.removeContainers(ctx, containers, RemoveOptions{Stop: true, Volumes: downOptions.RemoveVolumes}); err != nil { 44 return err 45 } 46 } 47 48 // remove orphan containers 49 parsedServices, err := c.Services(ctx) 50 if err != nil { 51 return err 52 } 53 orphans, err := c.getOrphanContainers(ctx, parsedServices) 54 if err != nil && downOptions.RemoveOrphans { 55 return fmt.Errorf("error getting orphaned containers: %s", err) 56 } 57 if len(orphans) > 0 { 58 if downOptions.RemoveOrphans { 59 if err := c.removeContainers(ctx, orphans, RemoveOptions{Stop: true, Volumes: downOptions.RemoveVolumes}); err != nil { 60 return fmt.Errorf("error removeing orphaned containers: %s", err) 61 } 62 } else { 63 log.G(ctx).Warnf("found %d orphaned containers: %v, you can run this command with the --remove-orphans flag to clean it up", len(orphans), orphans) 64 } 65 } 66 67 for shortName := range c.project.Networks { 68 if err := c.downNetwork(ctx, shortName); err != nil { 69 return err 70 } 71 } 72 73 if downOptions.RemoveVolumes { 74 for shortName := range c.project.Volumes { 75 if err := c.downVolume(ctx, shortName); err != nil { 76 return err 77 } 78 } 79 } 80 81 return nil 82 } 83 84 func (c *Composer) downNetwork(ctx context.Context, shortName string) error { 85 net, ok := c.project.Networks[shortName] 86 if !ok { 87 return fmt.Errorf("invalid network name %q", shortName) 88 } 89 if net.External.External { 90 // NOP 91 return nil 92 } 93 // shortName is like "default", fullName is like "compose-wordpress_default" 94 fullName := net.Name 95 netExists, err := c.NetworkExists(fullName) 96 if err != nil { 97 return err 98 } else if netExists { 99 netUsed, err := c.NetworkInUse(ctx, fullName) 100 if err != nil { 101 return err 102 } 103 if netUsed { 104 return fmt.Errorf("network %s is in use", fullName) 105 } 106 107 log.G(ctx).Infof("Removing network %s", fullName) 108 if err := c.runNerdctlCmd(ctx, "network", "rm", fullName); err != nil { 109 log.G(ctx).Warn(err) 110 } 111 } 112 return nil 113 } 114 115 func (c *Composer) downVolume(ctx context.Context, shortName string) error { 116 vol, ok := c.project.Volumes[shortName] 117 if !ok { 118 return fmt.Errorf("invalid volume name %q", shortName) 119 } 120 if vol.External.External { 121 // NOP 122 return nil 123 } 124 // shortName is like "db_data", fullName is like "compose-wordpress_db_data" 125 fullName := vol.Name 126 volExists, err := c.VolumeExists(fullName) 127 if err != nil { 128 return err 129 } else if volExists { 130 log.G(ctx).Infof("Removing volume %s", fullName) 131 if err := c.runNerdctlCmd(ctx, "volume", "rm", "-f", fullName); err != nil { 132 log.G(ctx).Warn(err) 133 } 134 } 135 return nil 136 }