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  }