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 }