github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/libnetwork/client/network.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "strings" 9 "text/tabwriter" 10 11 "github.com/docker/docker/pkg/stringid" 12 flag "github.com/docker/libnetwork/client/mflag" 13 "github.com/docker/libnetwork/netlabel" 14 ) 15 16 type command struct { 17 name string 18 description string 19 } 20 21 var ( 22 networkCommands = []command{ 23 {"create", "Create a network"}, 24 {"rm", "Remove a network"}, 25 {"ls", "List all networks"}, 26 {"info", "Display information of a network"}, 27 } 28 ) 29 30 // CmdNetwork handles the root Network UI 31 func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error { 32 cmd := cli.Subcmd(chain, "network", "COMMAND [OPTIONS] [arg...]", networkUsage(chain), false) 33 cmd.Require(flag.Min, 1) 34 err := cmd.ParseFlags(args, true) 35 if err == nil { 36 cmd.Usage() 37 return fmt.Errorf("invalid command : %v", args) 38 } 39 return err 40 } 41 42 // CmdNetworkCreate handles Network Create UI 43 func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error { 44 cmd := cli.Subcmd(chain, "create", "NETWORK-NAME", "Creates a new network with a name specified by the user", false) 45 flDriver := cmd.String([]string{"d", "-driver"}, "", "Driver to manage the Network") 46 flID := cmd.String([]string{"-id"}, "", "Network ID string") 47 flOpts := cmd.String([]string{"o", "-opt"}, "", "Network options") 48 flInternal := cmd.Bool([]string{"-internal"}, false, "Config the network to be internal") 49 flIPv6 := cmd.Bool([]string{"-ipv6"}, false, "Enable IPv6 on the network") 50 flSubnet := cmd.String([]string{"-subnet"}, "", "Subnet option") 51 flRange := cmd.String([]string{"-ip-range"}, "", "Range option") 52 53 cmd.Require(flag.Exact, 1) 54 err := cmd.ParseFlags(args, true) 55 if err != nil { 56 return err 57 } 58 networkOpts := make(map[string]string) 59 if *flInternal { 60 networkOpts[netlabel.Internal] = "true" 61 } 62 if *flIPv6 { 63 networkOpts[netlabel.EnableIPv6] = "true" 64 } 65 66 driverOpts := make(map[string]string) 67 if *flOpts != "" { 68 opts := strings.Split(*flOpts, ",") 69 for _, opt := range opts { 70 driverOpts[netlabel.Key(opt)] = netlabel.Value(opt) 71 } 72 } 73 74 var icList []ipamConf 75 if *flSubnet != "" { 76 ic := ipamConf{ 77 PreferredPool: *flSubnet, 78 } 79 80 if *flRange != "" { 81 ic.SubPool = *flRange 82 } 83 84 icList = append(icList, ic) 85 } 86 87 // Construct network create request body 88 nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, ID: *flID, IPv4Conf: icList, DriverOpts: driverOpts, NetworkOpts: networkOpts} 89 obj, _, err := readBody(cli.call("POST", "/networks", nc, nil)) 90 if err != nil { 91 return err 92 } 93 var replyID string 94 err = json.Unmarshal(obj, &replyID) 95 if err != nil { 96 return err 97 } 98 fmt.Fprintf(cli.out, "%s\n", replyID) 99 return nil 100 } 101 102 // CmdNetworkRm handles Network Delete UI 103 func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error { 104 cmd := cli.Subcmd(chain, "rm", "NETWORK", "Deletes a network", false) 105 cmd.Require(flag.Exact, 1) 106 err := cmd.ParseFlags(args, true) 107 if err != nil { 108 return err 109 } 110 id, err := lookupNetworkID(cli, cmd.Arg(0)) 111 if err != nil { 112 return err 113 } 114 _, _, err = readBody(cli.call("DELETE", "/networks/"+id, nil, nil)) 115 if err != nil { 116 return err 117 } 118 return nil 119 } 120 121 // CmdNetworkLs handles Network List UI 122 func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error { 123 cmd := cli.Subcmd(chain, "ls", "", "Lists all the networks created by the user", false) 124 quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") 125 noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output") 126 nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created") 127 last := cmd.Int([]string{"n"}, -1, "Show n last created networks") 128 err := cmd.ParseFlags(args, true) 129 if err != nil { 130 return err 131 } 132 obj, _, err := readBody(cli.call("GET", "/networks", nil, nil)) 133 if err != nil { 134 return err 135 } 136 if *last == -1 && *nLatest { 137 *last = 1 138 } 139 140 var networkResources []networkResource 141 err = json.Unmarshal(obj, &networkResources) 142 if err != nil { 143 return err 144 } 145 146 wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) 147 148 // unless quiet (-q) is specified, print field titles 149 if !*quiet { 150 fmt.Fprintln(wr, "NETWORK ID\tNAME\tTYPE") 151 } 152 153 for _, networkResource := range networkResources { 154 ID := networkResource.ID 155 netName := networkResource.Name 156 if !*noTrunc { 157 ID = stringid.TruncateID(ID) 158 } 159 if *quiet { 160 fmt.Fprintln(wr, ID) 161 continue 162 } 163 netType := networkResource.Type 164 fmt.Fprintf(wr, "%s\t%s\t%s\t", 165 ID, 166 netName, 167 netType) 168 fmt.Fprint(wr, "\n") 169 } 170 wr.Flush() 171 return nil 172 } 173 174 // CmdNetworkInfo handles Network Info UI 175 func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error { 176 cmd := cli.Subcmd(chain, "info", "NETWORK", "Displays detailed information on a network", false) 177 cmd.Require(flag.Exact, 1) 178 err := cmd.ParseFlags(args, true) 179 if err != nil { 180 return err 181 } 182 183 id, err := lookupNetworkID(cli, cmd.Arg(0)) 184 if err != nil { 185 return err 186 } 187 188 obj, _, err := readBody(cli.call("GET", "/networks/"+id, nil, nil)) 189 if err != nil { 190 return err 191 } 192 networkResource := &networkResource{} 193 if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil { 194 return err 195 } 196 fmt.Fprintf(cli.out, "Network Id: %s\n", networkResource.ID) 197 fmt.Fprintf(cli.out, "Name: %s\n", networkResource.Name) 198 fmt.Fprintf(cli.out, "Type: %s\n", networkResource.Type) 199 if networkResource.Services != nil { 200 for _, serviceResource := range networkResource.Services { 201 fmt.Fprintf(cli.out, " Service Id: %s\n", serviceResource.ID) 202 fmt.Fprintf(cli.out, "\tName: %s\n", serviceResource.Name) 203 } 204 } 205 206 return nil 207 } 208 209 // Helper function to predict if a string is a name or id or partial-id 210 // This provides a best-effort mechanism to identify an id with the help of GET Filter APIs 211 // Being a UI, its most likely that name will be used by the user, which is used to lookup 212 // the corresponding ID. If ID is not found, this function will assume that the passed string 213 // is an ID by itself. 214 215 func lookupNetworkID(cli *NetworkCli, nameID string) (string, error) { 216 obj, statusCode, err := readBody(cli.call("GET", "/networks?name="+nameID, nil, nil)) 217 if err != nil { 218 return "", err 219 } 220 221 if statusCode != http.StatusOK { 222 return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj)) 223 } 224 225 var list []*networkResource 226 err = json.Unmarshal(obj, &list) 227 if err != nil { 228 return "", err 229 } 230 if len(list) > 0 { 231 // name query filter will always return a single-element collection 232 return list[0].ID, nil 233 } 234 235 // Check for Partial-id 236 obj, statusCode, err = readBody(cli.call("GET", "/networks?partial-id="+nameID, nil, nil)) 237 if err != nil { 238 return "", err 239 } 240 241 if statusCode != http.StatusOK { 242 return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj)) 243 } 244 245 err = json.Unmarshal(obj, &list) 246 if err != nil { 247 return "", err 248 } 249 if len(list) == 0 { 250 return "", fmt.Errorf("resource not found %s", nameID) 251 } 252 if len(list) > 1 { 253 return "", fmt.Errorf("multiple Networks matching the partial identifier (%s). Please use full identifier", nameID) 254 } 255 return list[0].ID, nil 256 } 257 258 func networkUsage(chain string) string { 259 help := "Commands:\n" 260 261 for _, cmd := range networkCommands { 262 help += fmt.Sprintf(" %-25.25s%s\n", cmd.name, cmd.description) 263 } 264 265 help += fmt.Sprintf("\nRun '%s network COMMAND --help' for more information on a command.", chain) 266 return help 267 }