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