github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/cmd/network/list.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 network
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"errors"
    23  	"fmt"
    24  	"strings"
    25  	"text/tabwriter"
    26  	"text/template"
    27  
    28  	"github.com/containerd/nerdctl/v2/pkg/api/types"
    29  	"github.com/containerd/nerdctl/v2/pkg/formatter"
    30  	"github.com/containerd/nerdctl/v2/pkg/netutil"
    31  )
    32  
    33  type networkPrintable struct {
    34  	ID     string // empty for non-nerdctl networks
    35  	Name   string
    36  	Labels string
    37  	// TODO: "CreatedAt", "Driver", "IPv6", "Internal", "Scope"
    38  	file string `json:"-"`
    39  }
    40  
    41  func List(ctx context.Context, options types.NetworkListOptions) error {
    42  	globalOptions := options.GOptions
    43  	quiet := options.Quiet
    44  	format := options.Format
    45  	w := options.Stdout
    46  	filters := options.Filters
    47  	var tmpl *template.Template
    48  
    49  	switch format {
    50  	case "", "table", "wide":
    51  		w = tabwriter.NewWriter(w, 4, 8, 4, ' ', 0)
    52  		if !quiet {
    53  			fmt.Fprintln(w, "NETWORK ID\tNAME\tFILE")
    54  		}
    55  	case "raw":
    56  		return errors.New("unsupported format: \"raw\"")
    57  	default:
    58  		if quiet {
    59  			return errors.New("format and quiet must not be specified together")
    60  		}
    61  		var err error
    62  		tmpl, err = formatter.ParseTemplate(format)
    63  		if err != nil {
    64  			return err
    65  		}
    66  	}
    67  
    68  	e, err := netutil.NewCNIEnv(globalOptions.CNIPath, globalOptions.CNINetConfPath)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	netConfigs, err := e.NetworkList()
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	labelFilterFuncs, nameFilterFuncs, err := getNetworkFilterFuncs(filters)
    78  	if err != nil {
    79  		return err
    80  	}
    81  	if len(filters) > 0 {
    82  		filtered := make([]*netutil.NetworkConfig, 0)
    83  		for _, net := range netConfigs {
    84  			if networkMatchesFilter(net, labelFilterFuncs, nameFilterFuncs) {
    85  				filtered = append(filtered, net)
    86  			}
    87  		}
    88  		netConfigs = filtered
    89  	}
    90  
    91  	pp := make([]networkPrintable, len(netConfigs))
    92  	for i, n := range netConfigs {
    93  		p := networkPrintable{
    94  			Name: n.Name,
    95  			file: n.File,
    96  		}
    97  		if n.NerdctlID != nil {
    98  			p.ID = *n.NerdctlID
    99  			if len(p.ID) > 12 {
   100  				p.ID = p.ID[:12]
   101  			}
   102  		}
   103  		if n.NerdctlLabels != nil {
   104  			p.Labels = formatter.FormatLabels(*n.NerdctlLabels)
   105  		}
   106  		pp[i] = p
   107  	}
   108  
   109  	// append pseudo networks
   110  	if len(filters) == 0 { // filter a pseudo networks is meanless
   111  		pp = append(pp, []networkPrintable{
   112  			{
   113  				Name: "host",
   114  			},
   115  			{
   116  				Name: "none",
   117  			},
   118  		}...)
   119  	}
   120  
   121  	for _, p := range pp {
   122  		if tmpl != nil {
   123  			var b bytes.Buffer
   124  			if err := tmpl.Execute(&b, p); err != nil {
   125  				return err
   126  			}
   127  			if _, err = fmt.Fprintln(w, b.String()); err != nil {
   128  				return err
   129  			}
   130  		} else if quiet {
   131  			if p.ID != "" {
   132  				fmt.Fprintln(w, p.ID)
   133  			}
   134  		} else {
   135  			fmt.Fprintf(w, "%s\t%s\t%s\n", p.ID, p.Name, p.file)
   136  		}
   137  	}
   138  	if f, ok := w.(formatter.Flusher); ok {
   139  		return f.Flush()
   140  	}
   141  	return nil
   142  }
   143  
   144  func getNetworkFilterFuncs(filters []string) ([]func(*map[string]string) bool, []func(string) bool, error) {
   145  	labelFilterFuncs := make([]func(*map[string]string) bool, 0)
   146  	nameFilterFuncs := make([]func(string) bool, 0)
   147  
   148  	for _, filter := range filters {
   149  		if strings.HasPrefix(filter, "name") || strings.HasPrefix(filter, "label") {
   150  			subs := strings.SplitN(filter, "=", 2)
   151  			if len(subs) < 2 {
   152  				continue
   153  			}
   154  			switch subs[0] {
   155  			case "name":
   156  				nameFilterFuncs = append(nameFilterFuncs, func(name string) bool {
   157  					return strings.Contains(name, subs[1])
   158  				})
   159  			case "label":
   160  				v, k, hasValue := "", subs[1], false
   161  				if subs := strings.SplitN(subs[1], "=", 2); len(subs) == 2 {
   162  					hasValue = true
   163  					k, v = subs[0], subs[1]
   164  				}
   165  				labelFilterFuncs = append(labelFilterFuncs, func(labels *map[string]string) bool {
   166  					if labels == nil {
   167  						return false
   168  					}
   169  					val, ok := (*labels)[k]
   170  					if !ok || (hasValue && val != v) {
   171  						return false
   172  					}
   173  					return true
   174  				})
   175  			}
   176  			continue
   177  		}
   178  	}
   179  	return labelFilterFuncs, nameFilterFuncs, nil
   180  }
   181  
   182  func networkMatchesFilter(net *netutil.NetworkConfig, labelFilterFuncs []func(*map[string]string) bool, nameFilterFuncs []func(string) bool) bool {
   183  	for _, labelFilterFunc := range labelFilterFuncs {
   184  		if !labelFilterFunc(net.NerdctlLabels) {
   185  			return false
   186  		}
   187  	}
   188  	for _, nameFilterFunc := range nameFilterFuncs {
   189  		if !nameFilterFunc(net.Name) {
   190  			return false
   191  		}
   192  	}
   193  
   194  	return true
   195  }