github.com/cookieai-jar/moby@v17.12.1-ce-rc2+incompatible/api/server/router/network/network_routes.go (about) 1 package network 2 3 import ( 4 "encoding/json" 5 "net/http" 6 "strconv" 7 "strings" 8 9 "golang.org/x/net/context" 10 11 "github.com/docker/docker/api/server/httputils" 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/filters" 14 "github.com/docker/docker/api/types/network" 15 "github.com/docker/docker/api/types/versions" 16 "github.com/docker/libnetwork" 17 netconst "github.com/docker/libnetwork/datastore" 18 "github.com/docker/libnetwork/networkdb" 19 "github.com/pkg/errors" 20 ) 21 22 var ( 23 // acceptedNetworkFilters is a list of acceptable filters 24 acceptedNetworkFilters = map[string]bool{ 25 "driver": true, 26 "type": true, 27 "name": true, 28 "id": true, 29 "label": true, 30 "scope": true, 31 } 32 ) 33 34 func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 35 if err := httputils.ParseForm(r); err != nil { 36 return err 37 } 38 39 filter := r.Form.Get("filters") 40 netFilters, err := filters.FromJSON(filter) 41 if err != nil { 42 return err 43 } 44 45 if err := netFilters.Validate(acceptedNetworkFilters); err != nil { 46 return err 47 } 48 49 list := []types.NetworkResource{} 50 51 if nr, err := n.cluster.GetNetworks(); err == nil { 52 list = append(list, nr...) 53 } 54 55 // Combine the network list returned by Docker daemon if it is not already 56 // returned by the cluster manager 57 SKIP: 58 for _, nw := range n.backend.GetNetworks() { 59 for _, nl := range list { 60 if nl.ID == nw.ID() { 61 continue SKIP 62 } 63 } 64 65 var nr *types.NetworkResource 66 // Versions < 1.28 fetches all the containers attached to a network 67 // in a network list api call. It is a heavy weight operation when 68 // run across all the networks. Starting API version 1.28, this detailed 69 // info is available for network specific GET API (equivalent to inspect) 70 if versions.LessThan(httputils.VersionFromContext(ctx), "1.28") { 71 nr = n.buildDetailedNetworkResources(nw, false) 72 } else { 73 nr = n.buildNetworkResource(nw) 74 } 75 list = append(list, *nr) 76 } 77 78 list, err = filterNetworks(list, netFilters) 79 if err != nil { 80 return err 81 } 82 return httputils.WriteJSON(w, http.StatusOK, list) 83 } 84 85 type invalidRequestError struct { 86 cause error 87 } 88 89 func (e invalidRequestError) Error() string { 90 return e.cause.Error() 91 } 92 93 func (e invalidRequestError) InvalidParameter() {} 94 95 type ambigousResultsError string 96 97 func (e ambigousResultsError) Error() string { 98 return "network " + string(e) + " is ambiguous" 99 } 100 101 func (ambigousResultsError) InvalidParameter() {} 102 103 type conflictError struct { 104 cause error 105 } 106 107 func (e conflictError) Error() string { 108 return e.cause.Error() 109 } 110 111 func (e conflictError) Cause() error { 112 return e.cause 113 } 114 115 func (e conflictError) Conflict() {} 116 117 func nameConflict(name string) error { 118 return conflictError{libnetwork.NetworkNameError(name)} 119 } 120 121 func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 122 if err := httputils.ParseForm(r); err != nil { 123 return err 124 } 125 126 term := vars["id"] 127 var ( 128 verbose bool 129 err error 130 ) 131 if v := r.URL.Query().Get("verbose"); v != "" { 132 if verbose, err = strconv.ParseBool(v); err != nil { 133 return errors.Wrapf(invalidRequestError{err}, "invalid value for verbose: %s", v) 134 } 135 } 136 scope := r.URL.Query().Get("scope") 137 138 isMatchingScope := func(scope, term string) bool { 139 if term != "" { 140 return scope == term 141 } 142 return true 143 } 144 145 // In case multiple networks have duplicate names, return error. 146 // TODO (yongtang): should we wrap with version here for backward compatibility? 147 148 // First find based on full ID, return immediately once one is found. 149 // If a network appears both in swarm and local, assume it is in local first 150 151 // For full name and partial ID, save the result first, and process later 152 // in case multiple records was found based on the same term 153 listByFullName := map[string]types.NetworkResource{} 154 listByPartialID := map[string]types.NetworkResource{} 155 156 nw := n.backend.GetNetworks() 157 for _, network := range nw { 158 if network.ID() == term && isMatchingScope(network.Info().Scope(), scope) { 159 return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network, verbose)) 160 } 161 if network.Name() == term && isMatchingScope(network.Info().Scope(), scope) { 162 // No need to check the ID collision here as we are still in 163 // local scope and the network ID is unique in this scope. 164 listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, verbose) 165 } 166 if strings.HasPrefix(network.ID(), term) && isMatchingScope(network.Info().Scope(), scope) { 167 // No need to check the ID collision here as we are still in 168 // local scope and the network ID is unique in this scope. 169 listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, verbose) 170 } 171 } 172 173 nwk, err := n.cluster.GetNetwork(term) 174 if err == nil { 175 // If the get network is passed with a specific network ID / partial network ID 176 // or if the get network was passed with a network name and scope as swarm 177 // return the network. Skipped using isMatchingScope because it is true if the scope 178 // is not set which would be case if the client API v1.30 179 if strings.HasPrefix(nwk.ID, term) || (netconst.SwarmScope == scope) { 180 // If we have a previous match "backend", return it, we need verbose when enabled 181 // ex: overlay/partial_ID or name/swarm_scope 182 if nwv, ok := listByPartialID[nwk.ID]; ok { 183 nwk = nwv 184 } else if nwv, ok := listByFullName[nwk.ID]; ok { 185 nwk = nwv 186 } 187 return httputils.WriteJSON(w, http.StatusOK, nwk) 188 } 189 } 190 191 nr, _ := n.cluster.GetNetworks() 192 for _, network := range nr { 193 if network.ID == term && isMatchingScope(network.Scope, scope) { 194 return httputils.WriteJSON(w, http.StatusOK, network) 195 } 196 if network.Name == term && isMatchingScope(network.Scope, scope) { 197 // Check the ID collision as we are in swarm scope here, and 198 // the map (of the listByFullName) may have already had a 199 // network with the same ID (from local scope previously) 200 if _, ok := listByFullName[network.ID]; !ok { 201 listByFullName[network.ID] = network 202 } 203 } 204 if strings.HasPrefix(network.ID, term) && isMatchingScope(network.Scope, scope) { 205 // Check the ID collision as we are in swarm scope here, and 206 // the map (of the listByPartialID) may have already had a 207 // network with the same ID (from local scope previously) 208 if _, ok := listByPartialID[network.ID]; !ok { 209 listByPartialID[network.ID] = network 210 } 211 } 212 } 213 214 // Find based on full name, returns true only if no duplicates 215 if len(listByFullName) == 1 { 216 for _, v := range listByFullName { 217 return httputils.WriteJSON(w, http.StatusOK, v) 218 } 219 } 220 if len(listByFullName) > 1 { 221 return errors.Wrapf(ambigousResultsError(term), "%d matches found based on name", len(listByFullName)) 222 } 223 224 // Find based on partial ID, returns true only if no duplicates 225 if len(listByPartialID) == 1 { 226 for _, v := range listByPartialID { 227 return httputils.WriteJSON(w, http.StatusOK, v) 228 } 229 } 230 if len(listByPartialID) > 1 { 231 return errors.Wrapf(ambigousResultsError(term), "%d matches found based on ID prefix", len(listByPartialID)) 232 } 233 234 return libnetwork.ErrNoSuchNetwork(term) 235 } 236 237 func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 238 var create types.NetworkCreateRequest 239 240 if err := httputils.ParseForm(r); err != nil { 241 return err 242 } 243 244 if err := httputils.CheckForJSON(r); err != nil { 245 return err 246 } 247 248 if err := json.NewDecoder(r.Body).Decode(&create); err != nil { 249 return err 250 } 251 252 if nws, err := n.cluster.GetNetworksByName(create.Name); err == nil && len(nws) > 0 { 253 return nameConflict(create.Name) 254 } 255 256 nw, err := n.backend.CreateNetwork(create) 257 if err != nil { 258 var warning string 259 if _, ok := err.(libnetwork.NetworkNameError); ok { 260 // check if user defined CheckDuplicate, if set true, return err 261 // otherwise prepare a warning message 262 if create.CheckDuplicate { 263 return nameConflict(create.Name) 264 } 265 warning = libnetwork.NetworkNameError(create.Name).Error() 266 } 267 268 if _, ok := err.(libnetwork.ManagerRedirectError); !ok { 269 return err 270 } 271 id, err := n.cluster.CreateNetwork(create) 272 if err != nil { 273 return err 274 } 275 nw = &types.NetworkCreateResponse{ 276 ID: id, 277 Warning: warning, 278 } 279 } 280 281 return httputils.WriteJSON(w, http.StatusCreated, nw) 282 } 283 284 func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 285 var connect types.NetworkConnect 286 if err := httputils.ParseForm(r); err != nil { 287 return err 288 } 289 290 if err := httputils.CheckForJSON(r); err != nil { 291 return err 292 } 293 294 if err := json.NewDecoder(r.Body).Decode(&connect); err != nil { 295 return err 296 } 297 298 return n.backend.ConnectContainerToNetwork(connect.Container, vars["id"], connect.EndpointConfig) 299 } 300 301 func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 302 var disconnect types.NetworkDisconnect 303 if err := httputils.ParseForm(r); err != nil { 304 return err 305 } 306 307 if err := httputils.CheckForJSON(r); err != nil { 308 return err 309 } 310 311 if err := json.NewDecoder(r.Body).Decode(&disconnect); err != nil { 312 return err 313 } 314 315 return n.backend.DisconnectContainerFromNetwork(disconnect.Container, vars["id"], disconnect.Force) 316 } 317 318 func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 319 if err := httputils.ParseForm(r); err != nil { 320 return err 321 } 322 if _, err := n.cluster.GetNetwork(vars["id"]); err == nil { 323 if err = n.cluster.RemoveNetwork(vars["id"]); err != nil { 324 return err 325 } 326 w.WriteHeader(http.StatusNoContent) 327 return nil 328 } 329 if err := n.backend.DeleteNetwork(vars["id"]); err != nil { 330 return err 331 } 332 w.WriteHeader(http.StatusNoContent) 333 return nil 334 } 335 336 func (n *networkRouter) buildNetworkResource(nw libnetwork.Network) *types.NetworkResource { 337 r := &types.NetworkResource{} 338 if nw == nil { 339 return r 340 } 341 342 info := nw.Info() 343 r.Name = nw.Name() 344 r.ID = nw.ID() 345 r.Created = info.Created() 346 r.Scope = info.Scope() 347 r.Driver = nw.Type() 348 r.EnableIPv6 = info.IPv6Enabled() 349 r.Internal = info.Internal() 350 r.Attachable = info.Attachable() 351 r.Ingress = info.Ingress() 352 r.Options = info.DriverOptions() 353 r.Containers = make(map[string]types.EndpointResource) 354 buildIpamResources(r, info) 355 r.Labels = info.Labels() 356 r.ConfigOnly = info.ConfigOnly() 357 358 if cn := info.ConfigFrom(); cn != "" { 359 r.ConfigFrom = network.ConfigReference{Network: cn} 360 } 361 362 peers := info.Peers() 363 if len(peers) != 0 { 364 r.Peers = buildPeerInfoResources(peers) 365 } 366 367 return r 368 } 369 370 func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network, verbose bool) *types.NetworkResource { 371 if nw == nil { 372 return &types.NetworkResource{} 373 } 374 375 r := n.buildNetworkResource(nw) 376 epl := nw.Endpoints() 377 for _, e := range epl { 378 ei := e.Info() 379 if ei == nil { 380 continue 381 } 382 sb := ei.Sandbox() 383 tmpID := e.ID() 384 key := "ep-" + tmpID 385 if sb != nil { 386 key = sb.ContainerID() 387 } 388 389 r.Containers[key] = buildEndpointResource(tmpID, e.Name(), ei) 390 } 391 if !verbose { 392 return r 393 } 394 services := nw.Info().Services() 395 r.Services = make(map[string]network.ServiceInfo) 396 for name, service := range services { 397 tasks := []network.Task{} 398 for _, t := range service.Tasks { 399 tasks = append(tasks, network.Task{ 400 Name: t.Name, 401 EndpointID: t.EndpointID, 402 EndpointIP: t.EndpointIP, 403 Info: t.Info, 404 }) 405 } 406 r.Services[name] = network.ServiceInfo{ 407 VIP: service.VIP, 408 Ports: service.Ports, 409 Tasks: tasks, 410 LocalLBIndex: service.LocalLBIndex, 411 } 412 } 413 return r 414 } 415 416 func buildPeerInfoResources(peers []networkdb.PeerInfo) []network.PeerInfo { 417 peerInfo := make([]network.PeerInfo, 0, len(peers)) 418 for _, peer := range peers { 419 peerInfo = append(peerInfo, network.PeerInfo{ 420 Name: peer.Name, 421 IP: peer.IP, 422 }) 423 } 424 return peerInfo 425 } 426 427 func buildIpamResources(r *types.NetworkResource, nwInfo libnetwork.NetworkInfo) { 428 id, opts, ipv4conf, ipv6conf := nwInfo.IpamConfig() 429 430 ipv4Info, ipv6Info := nwInfo.IpamInfo() 431 432 r.IPAM.Driver = id 433 434 r.IPAM.Options = opts 435 436 r.IPAM.Config = []network.IPAMConfig{} 437 for _, ip4 := range ipv4conf { 438 if ip4.PreferredPool == "" { 439 continue 440 } 441 iData := network.IPAMConfig{} 442 iData.Subnet = ip4.PreferredPool 443 iData.IPRange = ip4.SubPool 444 iData.Gateway = ip4.Gateway 445 iData.AuxAddress = ip4.AuxAddresses 446 r.IPAM.Config = append(r.IPAM.Config, iData) 447 } 448 449 if len(r.IPAM.Config) == 0 { 450 for _, ip4Info := range ipv4Info { 451 iData := network.IPAMConfig{} 452 iData.Subnet = ip4Info.IPAMData.Pool.String() 453 if ip4Info.IPAMData.Gateway != nil { 454 iData.Gateway = ip4Info.IPAMData.Gateway.IP.String() 455 } 456 r.IPAM.Config = append(r.IPAM.Config, iData) 457 } 458 } 459 460 hasIpv6Conf := false 461 for _, ip6 := range ipv6conf { 462 if ip6.PreferredPool == "" { 463 continue 464 } 465 hasIpv6Conf = true 466 iData := network.IPAMConfig{} 467 iData.Subnet = ip6.PreferredPool 468 iData.IPRange = ip6.SubPool 469 iData.Gateway = ip6.Gateway 470 iData.AuxAddress = ip6.AuxAddresses 471 r.IPAM.Config = append(r.IPAM.Config, iData) 472 } 473 474 if !hasIpv6Conf { 475 for _, ip6Info := range ipv6Info { 476 if ip6Info.IPAMData.Pool == nil { 477 continue 478 } 479 iData := network.IPAMConfig{} 480 iData.Subnet = ip6Info.IPAMData.Pool.String() 481 iData.Gateway = ip6Info.IPAMData.Gateway.String() 482 r.IPAM.Config = append(r.IPAM.Config, iData) 483 } 484 } 485 } 486 487 func buildEndpointResource(id string, name string, info libnetwork.EndpointInfo) types.EndpointResource { 488 er := types.EndpointResource{} 489 490 er.EndpointID = id 491 er.Name = name 492 ei := info 493 if ei == nil { 494 return er 495 } 496 497 if iface := ei.Iface(); iface != nil { 498 if mac := iface.MacAddress(); mac != nil { 499 er.MacAddress = mac.String() 500 } 501 if ip := iface.Address(); ip != nil && len(ip.IP) > 0 { 502 er.IPv4Address = ip.String() 503 } 504 505 if ipv6 := iface.AddressIPv6(); ipv6 != nil && len(ipv6.IP) > 0 { 506 er.IPv6Address = ipv6.String() 507 } 508 } 509 return er 510 } 511 512 func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 513 if err := httputils.ParseForm(r); err != nil { 514 return err 515 } 516 517 pruneFilters, err := filters.FromJSON(r.Form.Get("filters")) 518 if err != nil { 519 return err 520 } 521 522 pruneReport, err := n.backend.NetworksPrune(ctx, pruneFilters) 523 if err != nil { 524 return err 525 } 526 return httputils.WriteJSON(w, http.StatusOK, pruneReport) 527 }