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