github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/libnetwork/api/api.go (about) 1 package api 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "strconv" 9 "strings" 10 11 "github.com/docker/docker/libnetwork" 12 "github.com/docker/docker/libnetwork/netlabel" 13 "github.com/docker/docker/libnetwork/netutils" 14 "github.com/docker/docker/libnetwork/types" 15 "github.com/gorilla/mux" 16 ) 17 18 var ( 19 successResponse = responseStatus{Status: "Success", StatusCode: http.StatusOK} 20 createdResponse = responseStatus{Status: "Created", StatusCode: http.StatusCreated} 21 badQueryResponse = responseStatus{Status: "Unsupported query", StatusCode: http.StatusBadRequest} 22 ) 23 24 const ( 25 // Resource name regex 26 // Gorilla mux encloses the passed pattern with '^' and '$'. So we need to do some tricks 27 // to have mux eventually build a query regex which matches empty or word string (`^$|[\w]+`) 28 regex = "[a-zA-Z_0-9-]+" 29 qregx = "$|" + regex 30 // Router URL variable definition 31 nwNameQr = "{" + urlNwName + ":" + qregx + "}" 32 nwID = "{" + urlNwID + ":" + regex + "}" 33 nwPIDQr = "{" + urlNwPID + ":" + qregx + "}" 34 epNameQr = "{" + urlEpName + ":" + qregx + "}" 35 epID = "{" + urlEpID + ":" + regex + "}" 36 epPIDQr = "{" + urlEpPID + ":" + qregx + "}" 37 sbID = "{" + urlSbID + ":" + regex + "}" 38 sbPIDQr = "{" + urlSbPID + ":" + qregx + "}" 39 cnIDQr = "{" + urlCnID + ":" + qregx + "}" 40 cnPIDQr = "{" + urlCnPID + ":" + qregx + "}" 41 42 // Internal URL variable name.They can be anything as 43 // long as they do not collide with query fields. 44 urlNwName = "network-name" 45 urlNwID = "network-id" 46 urlNwPID = "network-partial-id" 47 urlEpName = "endpoint-name" 48 urlEpID = "endpoint-id" 49 urlEpPID = "endpoint-partial-id" 50 urlSbID = "sandbox-id" 51 urlSbPID = "sandbox-partial-id" 52 urlCnID = "container-id" 53 urlCnPID = "container-partial-id" 54 ) 55 56 // NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork 57 func NewHTTPHandler(c libnetwork.NetworkController) func(w http.ResponseWriter, req *http.Request) { 58 h := &httpHandler{c: c} 59 h.initRouter() 60 return h.handleRequest 61 } 62 63 type responseStatus struct { 64 Status string 65 StatusCode int 66 } 67 68 func (r *responseStatus) isOK() bool { 69 return r.StatusCode == http.StatusOK || r.StatusCode == http.StatusCreated 70 } 71 72 type processor func(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) 73 74 type httpHandler struct { 75 c libnetwork.NetworkController 76 r *mux.Router 77 } 78 79 func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) { 80 // Make sure the service is there 81 if h.c == nil { 82 http.Error(w, "NetworkController is not available", http.StatusServiceUnavailable) 83 return 84 } 85 86 // Get handler from router and execute it 87 h.r.ServeHTTP(w, req) 88 } 89 90 func (h *httpHandler) initRouter() { 91 m := map[string][]struct { 92 url string 93 qrs []string 94 fct processor 95 }{ 96 "GET": { 97 // Order matters 98 {"/networks", []string{"name", nwNameQr}, procGetNetworks}, 99 {"/networks", []string{"partial-id", nwPIDQr}, procGetNetworks}, 100 {"/networks", nil, procGetNetworks}, 101 {"/networks/" + nwID, nil, procGetNetwork}, 102 {"/networks/" + nwID + "/endpoints", []string{"name", epNameQr}, procGetEndpoints}, 103 {"/networks/" + nwID + "/endpoints", []string{"partial-id", epPIDQr}, procGetEndpoints}, 104 {"/networks/" + nwID + "/endpoints", nil, procGetEndpoints}, 105 {"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint}, 106 {"/services", []string{"network", nwNameQr}, procGetServices}, 107 {"/services", []string{"name", epNameQr}, procGetServices}, 108 {"/services", []string{"partial-id", epPIDQr}, procGetServices}, 109 {"/services", nil, procGetServices}, 110 {"/services/" + epID, nil, procGetService}, 111 {"/services/" + epID + "/backend", nil, procGetSandbox}, 112 {"/sandboxes", []string{"partial-container-id", cnPIDQr}, procGetSandboxes}, 113 {"/sandboxes", []string{"container-id", cnIDQr}, procGetSandboxes}, 114 {"/sandboxes", []string{"partial-id", sbPIDQr}, procGetSandboxes}, 115 {"/sandboxes", nil, procGetSandboxes}, 116 {"/sandboxes/" + sbID, nil, procGetSandbox}, 117 }, 118 "POST": { 119 {"/networks", nil, procCreateNetwork}, 120 {"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint}, 121 {"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes", nil, procJoinEndpoint}, 122 {"/services", nil, procPublishService}, 123 {"/services/" + epID + "/backend", nil, procAttachBackend}, 124 {"/sandboxes", nil, procCreateSandbox}, 125 }, 126 "DELETE": { 127 {"/networks/" + nwID, nil, procDeleteNetwork}, 128 {"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint}, 129 {"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes/" + sbID, nil, procLeaveEndpoint}, 130 {"/services/" + epID, nil, procUnpublishService}, 131 {"/services/" + epID + "/backend/" + sbID, nil, procDetachBackend}, 132 {"/sandboxes/" + sbID, nil, procDeleteSandbox}, 133 }, 134 } 135 136 h.r = mux.NewRouter() 137 for method, routes := range m { 138 for _, route := range routes { 139 r := h.r.Path("/{.*}" + route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct)) 140 if route.qrs != nil { 141 r.Queries(route.qrs...) 142 } 143 144 r = h.r.Path(route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct)) 145 if route.qrs != nil { 146 r.Queries(route.qrs...) 147 } 148 } 149 } 150 } 151 152 func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerFunc { 153 return func(w http.ResponseWriter, req *http.Request) { 154 var ( 155 body []byte 156 err error 157 ) 158 if req.Body != nil { 159 body, err = ioutil.ReadAll(req.Body) 160 if err != nil { 161 http.Error(w, "Invalid body: "+err.Error(), http.StatusBadRequest) 162 return 163 } 164 } 165 166 res, rsp := fct(ctrl, mux.Vars(req), body) 167 if !rsp.isOK() { 168 http.Error(w, rsp.Status, rsp.StatusCode) 169 return 170 } 171 if res != nil { 172 writeJSON(w, rsp.StatusCode, res) 173 } 174 } 175 } 176 177 /***************** 178 Resource Builders 179 ******************/ 180 181 func buildNetworkResource(nw libnetwork.Network) *networkResource { 182 r := &networkResource{} 183 if nw != nil { 184 r.Name = nw.Name() 185 r.ID = nw.ID() 186 r.Type = nw.Type() 187 epl := nw.Endpoints() 188 r.Endpoints = make([]*endpointResource, 0, len(epl)) 189 for _, e := range epl { 190 epr := buildEndpointResource(e) 191 r.Endpoints = append(r.Endpoints, epr) 192 } 193 } 194 return r 195 } 196 197 func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource { 198 r := &endpointResource{} 199 if ep != nil { 200 r.Name = ep.Name() 201 r.ID = ep.ID() 202 r.Network = ep.Network() 203 } 204 return r 205 } 206 207 func buildSandboxResource(sb libnetwork.Sandbox) *sandboxResource { 208 r := &sandboxResource{} 209 if sb != nil { 210 r.ID = sb.ID() 211 r.Key = sb.Key() 212 r.ContainerID = sb.ContainerID() 213 } 214 return r 215 } 216 217 /**************** 218 Options Parsers 219 *****************/ 220 221 func (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption { 222 var setFctList []libnetwork.SandboxOption 223 if sc.HostName != "" { 224 setFctList = append(setFctList, libnetwork.OptionHostname(sc.HostName)) 225 } 226 if sc.DomainName != "" { 227 setFctList = append(setFctList, libnetwork.OptionDomainname(sc.DomainName)) 228 } 229 if sc.HostsPath != "" { 230 setFctList = append(setFctList, libnetwork.OptionHostsPath(sc.HostsPath)) 231 } 232 if sc.ResolvConfPath != "" { 233 setFctList = append(setFctList, libnetwork.OptionResolvConfPath(sc.ResolvConfPath)) 234 } 235 if sc.UseDefaultSandbox { 236 setFctList = append(setFctList, libnetwork.OptionUseDefaultSandbox()) 237 } 238 if sc.UseExternalKey { 239 setFctList = append(setFctList, libnetwork.OptionUseExternalKey()) 240 } 241 if sc.DNS != nil { 242 for _, d := range sc.DNS { 243 setFctList = append(setFctList, libnetwork.OptionDNS(d)) 244 } 245 } 246 if sc.ExtraHosts != nil { 247 for _, e := range sc.ExtraHosts { 248 setFctList = append(setFctList, libnetwork.OptionExtraHost(e.Name, e.Address)) 249 } 250 } 251 if sc.ExposedPorts != nil { 252 setFctList = append(setFctList, libnetwork.OptionExposedPorts(sc.ExposedPorts)) 253 } 254 if sc.PortMapping != nil { 255 setFctList = append(setFctList, libnetwork.OptionPortMapping(sc.PortMapping)) 256 } 257 return setFctList 258 } 259 260 /****************** 261 Process functions 262 *******************/ 263 264 func processCreateDefaults(c libnetwork.NetworkController, nc *networkCreate) { 265 if nc.NetworkType == "" { 266 nc.NetworkType = c.Config().Daemon.DefaultDriver 267 } 268 } 269 270 /*************************** 271 NetworkController interface 272 ****************************/ 273 func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 274 var create networkCreate 275 276 err := json.Unmarshal(body, &create) 277 if err != nil { 278 return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} 279 } 280 processCreateDefaults(c, &create) 281 282 options := []libnetwork.NetworkOption{} 283 if val, ok := create.NetworkOpts[netlabel.Internal]; ok { 284 internal, err := strconv.ParseBool(val) 285 if err != nil { 286 return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} 287 } 288 if internal { 289 options = append(options, libnetwork.NetworkOptionInternalNetwork()) 290 } 291 } 292 if val, ok := create.NetworkOpts[netlabel.EnableIPv6]; ok { 293 enableIPv6, err := strconv.ParseBool(val) 294 if err != nil { 295 return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} 296 } 297 options = append(options, libnetwork.NetworkOptionEnableIPv6(enableIPv6)) 298 } 299 if len(create.DriverOpts) > 0 { 300 options = append(options, libnetwork.NetworkOptionDriverOpts(create.DriverOpts)) 301 } 302 303 if len(create.IPv4Conf) > 0 { 304 ipamV4Conf := &libnetwork.IpamConf{ 305 PreferredPool: create.IPv4Conf[0].PreferredPool, 306 SubPool: create.IPv4Conf[0].SubPool, 307 } 308 309 options = append(options, libnetwork.NetworkOptionIpam("default", "", []*libnetwork.IpamConf{ipamV4Conf}, nil, nil)) 310 } 311 312 nw, err := c.NewNetwork(create.NetworkType, create.Name, create.ID, options...) 313 if err != nil { 314 return nil, convertNetworkError(err) 315 } 316 317 return nw.ID(), &createdResponse 318 } 319 320 func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 321 t, by := detectNetworkTarget(vars) 322 nw, errRsp := findNetwork(c, t, by) 323 if !errRsp.isOK() { 324 return nil, errRsp 325 } 326 return buildNetworkResource(nw), &successResponse 327 } 328 329 func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 330 var list []*networkResource 331 332 // Look for query filters and validate 333 name, queryByName := vars[urlNwName] 334 shortID, queryByPid := vars[urlNwPID] 335 if queryByName && queryByPid { 336 return nil, &badQueryResponse 337 } 338 339 if queryByName { 340 if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() { 341 list = append(list, buildNetworkResource(nw)) 342 } 343 } else if queryByPid { 344 // Return all the prefix-matching networks 345 l := func(nw libnetwork.Network) bool { 346 if strings.HasPrefix(nw.ID(), shortID) { 347 list = append(list, buildNetworkResource(nw)) 348 } 349 return false 350 } 351 c.WalkNetworks(l) 352 } else { 353 for _, nw := range c.Networks() { 354 list = append(list, buildNetworkResource(nw)) 355 } 356 } 357 358 return list, &successResponse 359 } 360 361 func procCreateSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 362 var create sandboxCreate 363 364 err := json.Unmarshal(body, &create) 365 if err != nil { 366 return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} 367 } 368 369 sb, err := c.NewSandbox(create.ContainerID, create.parseOptions()...) 370 if err != nil { 371 return "", convertNetworkError(err) 372 } 373 374 return sb.ID(), &createdResponse 375 } 376 377 /****************** 378 Network interface 379 *******************/ 380 func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 381 var ec endpointCreate 382 383 err := json.Unmarshal(body, &ec) 384 if err != nil { 385 return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} 386 } 387 388 nwT, nwBy := detectNetworkTarget(vars) 389 n, errRsp := findNetwork(c, nwT, nwBy) 390 if !errRsp.isOK() { 391 return "", errRsp 392 } 393 394 var setFctList []libnetwork.EndpointOption 395 for _, str := range ec.MyAliases { 396 setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str)) 397 } 398 399 ep, err := n.CreateEndpoint(ec.Name, setFctList...) 400 if err != nil { 401 return "", convertNetworkError(err) 402 } 403 404 return ep.ID(), &createdResponse 405 } 406 407 func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 408 nwT, nwBy := detectNetworkTarget(vars) 409 epT, epBy := detectEndpointTarget(vars) 410 411 ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) 412 if !errRsp.isOK() { 413 return nil, errRsp 414 } 415 416 return buildEndpointResource(ep), &successResponse 417 } 418 419 func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 420 // Look for query filters and validate 421 name, queryByName := vars[urlEpName] 422 shortID, queryByPid := vars[urlEpPID] 423 if queryByName && queryByPid { 424 return nil, &badQueryResponse 425 } 426 427 nwT, nwBy := detectNetworkTarget(vars) 428 nw, errRsp := findNetwork(c, nwT, nwBy) 429 if !errRsp.isOK() { 430 return nil, errRsp 431 } 432 433 var list []*endpointResource 434 435 // If query parameter is specified, return a filtered collection 436 if queryByName { 437 if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() { 438 list = append(list, buildEndpointResource(ep)) 439 } 440 } else if queryByPid { 441 // Return all the prefix-matching endpoints 442 l := func(ep libnetwork.Endpoint) bool { 443 if strings.HasPrefix(ep.ID(), shortID) { 444 list = append(list, buildEndpointResource(ep)) 445 } 446 return false 447 } 448 nw.WalkEndpoints(l) 449 } else { 450 for _, ep := range nw.Endpoints() { 451 epr := buildEndpointResource(ep) 452 list = append(list, epr) 453 } 454 } 455 456 return list, &successResponse 457 } 458 459 func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 460 target, by := detectNetworkTarget(vars) 461 462 nw, errRsp := findNetwork(c, target, by) 463 if !errRsp.isOK() { 464 return nil, errRsp 465 } 466 467 err := nw.Delete() 468 if err != nil { 469 return nil, convertNetworkError(err) 470 } 471 472 return nil, &successResponse 473 } 474 475 /****************** 476 Endpoint interface 477 *******************/ 478 func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 479 var ej endpointJoin 480 var setFctList []libnetwork.EndpointOption 481 err := json.Unmarshal(body, &ej) 482 if err != nil { 483 return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} 484 } 485 486 nwT, nwBy := detectNetworkTarget(vars) 487 epT, epBy := detectEndpointTarget(vars) 488 489 ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) 490 if !errRsp.isOK() { 491 return nil, errRsp 492 } 493 494 sb, errRsp := findSandbox(c, ej.SandboxID, byID) 495 if !errRsp.isOK() { 496 return nil, errRsp 497 } 498 499 for _, str := range ej.Aliases { 500 name, alias, err := netutils.ParseAlias(str) 501 if err != nil { 502 return "", convertNetworkError(err) 503 } 504 setFctList = append(setFctList, libnetwork.CreateOptionAlias(name, alias)) 505 } 506 507 err = ep.Join(sb, setFctList...) 508 if err != nil { 509 return nil, convertNetworkError(err) 510 } 511 return sb.Key(), &successResponse 512 } 513 514 func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 515 nwT, nwBy := detectNetworkTarget(vars) 516 epT, epBy := detectEndpointTarget(vars) 517 518 ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) 519 if !errRsp.isOK() { 520 return nil, errRsp 521 } 522 523 sb, errRsp := findSandbox(c, vars[urlSbID], byID) 524 if !errRsp.isOK() { 525 return nil, errRsp 526 } 527 528 err := ep.Leave(sb) 529 if err != nil { 530 return nil, convertNetworkError(err) 531 } 532 533 return nil, &successResponse 534 } 535 536 func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 537 nwT, nwBy := detectNetworkTarget(vars) 538 epT, epBy := detectEndpointTarget(vars) 539 540 ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) 541 if !errRsp.isOK() { 542 return nil, errRsp 543 } 544 545 err := ep.Delete(false) 546 if err != nil { 547 return nil, convertNetworkError(err) 548 } 549 550 return nil, &successResponse 551 } 552 553 /****************** 554 Service interface 555 *******************/ 556 func procGetServices(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 557 // Look for query filters and validate 558 nwName, filterByNwName := vars[urlNwName] 559 svName, queryBySvName := vars[urlEpName] 560 shortID, queryBySvPID := vars[urlEpPID] 561 562 if filterByNwName && queryBySvName || filterByNwName && queryBySvPID || queryBySvName && queryBySvPID { 563 return nil, &badQueryResponse 564 } 565 566 var list []*endpointResource 567 568 switch { 569 case filterByNwName: 570 // return all service present on the specified network 571 nw, errRsp := findNetwork(c, nwName, byName) 572 if !errRsp.isOK() { 573 return list, &successResponse 574 } 575 for _, ep := range nw.Endpoints() { 576 epr := buildEndpointResource(ep) 577 list = append(list, epr) 578 } 579 case queryBySvName: 580 // Look in each network for the service with the specified name 581 l := func(ep libnetwork.Endpoint) bool { 582 if ep.Name() == svName { 583 list = append(list, buildEndpointResource(ep)) 584 return true 585 } 586 return false 587 } 588 for _, nw := range c.Networks() { 589 nw.WalkEndpoints(l) 590 } 591 case queryBySvPID: 592 // Return all the prefix-matching services 593 l := func(ep libnetwork.Endpoint) bool { 594 if strings.HasPrefix(ep.ID(), shortID) { 595 list = append(list, buildEndpointResource(ep)) 596 } 597 return false 598 } 599 for _, nw := range c.Networks() { 600 nw.WalkEndpoints(l) 601 } 602 default: 603 for _, nw := range c.Networks() { 604 for _, ep := range nw.Endpoints() { 605 epr := buildEndpointResource(ep) 606 list = append(list, epr) 607 } 608 } 609 } 610 611 return list, &successResponse 612 } 613 614 func procGetService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 615 epT, epBy := detectEndpointTarget(vars) 616 sv, errRsp := findService(c, epT, epBy) 617 if !errRsp.isOK() { 618 return nil, endpointToService(errRsp) 619 } 620 return buildEndpointResource(sv), &successResponse 621 } 622 623 func procPublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 624 var sp servicePublish 625 626 err := json.Unmarshal(body, &sp) 627 if err != nil { 628 return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} 629 } 630 631 n, errRsp := findNetwork(c, sp.Network, byName) 632 if !errRsp.isOK() { 633 return "", errRsp 634 } 635 636 var setFctList []libnetwork.EndpointOption 637 for _, str := range sp.MyAliases { 638 setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str)) 639 } 640 641 ep, err := n.CreateEndpoint(sp.Name, setFctList...) 642 if err != nil { 643 return "", endpointToService(convertNetworkError(err)) 644 } 645 646 return ep.ID(), &createdResponse 647 } 648 649 func procUnpublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 650 var sd serviceDelete 651 652 if body != nil { 653 err := json.Unmarshal(body, &sd) 654 if err != nil { 655 return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} 656 } 657 } 658 659 epT, epBy := detectEndpointTarget(vars) 660 sv, errRsp := findService(c, epT, epBy) 661 if !errRsp.isOK() { 662 return nil, errRsp 663 } 664 665 if err := sv.Delete(sd.Force); err != nil { 666 return nil, endpointToService(convertNetworkError(err)) 667 } 668 return nil, &successResponse 669 } 670 671 func procAttachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 672 var bk endpointJoin 673 var setFctList []libnetwork.EndpointOption 674 err := json.Unmarshal(body, &bk) 675 if err != nil { 676 return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} 677 } 678 679 epT, epBy := detectEndpointTarget(vars) 680 sv, errRsp := findService(c, epT, epBy) 681 if !errRsp.isOK() { 682 return nil, errRsp 683 } 684 685 sb, errRsp := findSandbox(c, bk.SandboxID, byID) 686 if !errRsp.isOK() { 687 return nil, errRsp 688 } 689 690 for _, str := range bk.Aliases { 691 name, alias, err := netutils.ParseAlias(str) 692 if err != nil { 693 return "", convertNetworkError(err) 694 } 695 setFctList = append(setFctList, libnetwork.CreateOptionAlias(name, alias)) 696 } 697 698 err = sv.Join(sb, setFctList...) 699 if err != nil { 700 return nil, convertNetworkError(err) 701 } 702 703 return sb.Key(), &successResponse 704 } 705 706 func procDetachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 707 epT, epBy := detectEndpointTarget(vars) 708 sv, errRsp := findService(c, epT, epBy) 709 if !errRsp.isOK() { 710 return nil, errRsp 711 } 712 713 sb, errRsp := findSandbox(c, vars[urlSbID], byID) 714 if !errRsp.isOK() { 715 return nil, errRsp 716 } 717 718 err := sv.Leave(sb) 719 if err != nil { 720 return nil, convertNetworkError(err) 721 } 722 723 return nil, &successResponse 724 } 725 726 /****************** 727 Sandbox interface 728 *******************/ 729 func procGetSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 730 if epT, ok := vars[urlEpID]; ok { 731 sv, errRsp := findService(c, epT, byID) 732 if !errRsp.isOK() { 733 return nil, endpointToService(errRsp) 734 } 735 return buildSandboxResource(sv.Info().Sandbox()), &successResponse 736 } 737 738 sbT, by := detectSandboxTarget(vars) 739 sb, errRsp := findSandbox(c, sbT, by) 740 if !errRsp.isOK() { 741 return nil, errRsp 742 } 743 return buildSandboxResource(sb), &successResponse 744 } 745 746 type cndFnMkr func(string) cndFn 747 type cndFn func(libnetwork.Sandbox) bool 748 749 // list of (query type, condition function makers) couples 750 var cndMkrList = []struct { 751 identifier string 752 maker cndFnMkr 753 }{ 754 {urlSbPID, func(id string) cndFn { 755 return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ID(), id) } 756 }}, 757 {urlCnID, func(id string) cndFn { 758 return func(sb libnetwork.Sandbox) bool { return sb.ContainerID() == id } 759 }}, 760 {urlCnPID, func(id string) cndFn { 761 return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ContainerID(), id) } 762 }}, 763 } 764 765 func getQueryCondition(vars map[string]string) func(libnetwork.Sandbox) bool { 766 for _, im := range cndMkrList { 767 if val, ok := vars[im.identifier]; ok { 768 return im.maker(val) 769 } 770 } 771 return func(sb libnetwork.Sandbox) bool { return true } 772 } 773 774 func sandboxWalker(condition cndFn, list *[]*sandboxResource) libnetwork.SandboxWalker { 775 return func(sb libnetwork.Sandbox) bool { 776 if condition(sb) { 777 *list = append(*list, buildSandboxResource(sb)) 778 } 779 return false 780 } 781 } 782 783 func procGetSandboxes(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 784 var list []*sandboxResource 785 786 cnd := getQueryCondition(vars) 787 c.WalkSandboxes(sandboxWalker(cnd, &list)) 788 789 return list, &successResponse 790 } 791 792 func procDeleteSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { 793 sbT, by := detectSandboxTarget(vars) 794 795 sb, errRsp := findSandbox(c, sbT, by) 796 if !errRsp.isOK() { 797 return nil, errRsp 798 } 799 800 err := sb.Delete() 801 if err != nil { 802 return nil, convertNetworkError(err) 803 } 804 805 return nil, &successResponse 806 } 807 808 /*********** 809 Utilities 810 ************/ 811 const ( 812 byID = iota 813 byName 814 ) 815 816 func detectNetworkTarget(vars map[string]string) (string, int) { 817 if target, ok := vars[urlNwName]; ok { 818 return target, byName 819 } 820 if target, ok := vars[urlNwID]; ok { 821 return target, byID 822 } 823 // vars are populated from the URL, following cannot happen 824 panic("Missing URL variable parameter for network") 825 } 826 827 func detectSandboxTarget(vars map[string]string) (string, int) { 828 if target, ok := vars[urlSbID]; ok { 829 return target, byID 830 } 831 // vars are populated from the URL, following cannot happen 832 panic("Missing URL variable parameter for sandbox") 833 } 834 835 func detectEndpointTarget(vars map[string]string) (string, int) { 836 if target, ok := vars[urlEpName]; ok { 837 return target, byName 838 } 839 if target, ok := vars[urlEpID]; ok { 840 return target, byID 841 } 842 // vars are populated from the URL, following cannot happen 843 panic("Missing URL variable parameter for endpoint") 844 } 845 846 func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) { 847 var ( 848 nw libnetwork.Network 849 err error 850 ) 851 switch by { 852 case byID: 853 nw, err = c.NetworkByID(s) 854 case byName: 855 if s == "" { 856 s = c.Config().Daemon.DefaultNetwork 857 } 858 nw, err = c.NetworkByName(s) 859 default: 860 panic(fmt.Sprintf("unexpected selector for network search: %d", by)) 861 } 862 if err != nil { 863 if _, ok := err.(types.NotFoundError); ok { 864 return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound} 865 } 866 return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} 867 } 868 return nw, &successResponse 869 } 870 871 func findSandbox(c libnetwork.NetworkController, s string, by int) (libnetwork.Sandbox, *responseStatus) { 872 var ( 873 sb libnetwork.Sandbox 874 err error 875 ) 876 877 switch by { 878 case byID: 879 sb, err = c.SandboxByID(s) 880 default: 881 panic(fmt.Sprintf("unexpected selector for sandbox search: %d", by)) 882 } 883 if err != nil { 884 if _, ok := err.(types.NotFoundError); ok { 885 return nil, &responseStatus{Status: "Resource not found: Sandbox", StatusCode: http.StatusNotFound} 886 } 887 return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} 888 } 889 return sb, &successResponse 890 } 891 892 func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) { 893 nw, errRsp := findNetwork(c, ns, nwBy) 894 if !errRsp.isOK() { 895 return nil, errRsp 896 } 897 var ( 898 err error 899 ep libnetwork.Endpoint 900 ) 901 switch epBy { 902 case byID: 903 ep, err = nw.EndpointByID(es) 904 case byName: 905 ep, err = nw.EndpointByName(es) 906 default: 907 panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy)) 908 } 909 if err != nil { 910 if _, ok := err.(types.NotFoundError); ok { 911 return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound} 912 } 913 return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} 914 } 915 return ep, &successResponse 916 } 917 918 func findService(c libnetwork.NetworkController, svs string, svBy int) (libnetwork.Endpoint, *responseStatus) { 919 for _, nw := range c.Networks() { 920 var ( 921 ep libnetwork.Endpoint 922 err error 923 ) 924 switch svBy { 925 case byID: 926 ep, err = nw.EndpointByID(svs) 927 case byName: 928 ep, err = nw.EndpointByName(svs) 929 default: 930 panic(fmt.Sprintf("unexpected selector for service search: %d", svBy)) 931 } 932 if err == nil { 933 return ep, &successResponse 934 } else if _, ok := err.(types.NotFoundError); !ok { 935 return nil, convertNetworkError(err) 936 } 937 } 938 return nil, &responseStatus{Status: "Service not found", StatusCode: http.StatusNotFound} 939 } 940 941 func endpointToService(rsp *responseStatus) *responseStatus { 942 rsp.Status = strings.Replace(rsp.Status, "endpoint", "service", -1) 943 return rsp 944 } 945 946 func convertNetworkError(err error) *responseStatus { 947 var code int 948 switch err.(type) { 949 case types.BadRequestError: 950 code = http.StatusBadRequest 951 case types.ForbiddenError: 952 code = http.StatusForbidden 953 case types.NotFoundError: 954 code = http.StatusNotFound 955 case types.TimeoutError: 956 code = http.StatusRequestTimeout 957 case types.NotImplementedError: 958 code = http.StatusNotImplemented 959 case types.NoServiceError: 960 code = http.StatusServiceUnavailable 961 case types.InternalError: 962 code = http.StatusInternalServerError 963 default: 964 code = http.StatusInternalServerError 965 } 966 return &responseStatus{Status: err.Error(), StatusCode: code} 967 } 968 969 func writeJSON(w http.ResponseWriter, code int, v interface{}) error { 970 w.Header().Set("Content-Type", "application/json") 971 w.WriteHeader(code) 972 return json.NewEncoder(w).Encode(v) 973 }