From fd43ee13238c05266a2626a5281b699f78043a26 Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Wed, 1 Jul 2015 22:00:48 -0700 Subject: [PATCH] Introduce Sandbox entity - Maps 1 to 1 with container's networking stack - It holds container's specific nw options which before were incorrectly owned by Endpoint. - Sandbox creation no longer coupled with Endpoint Join, sandbox and endpoint have now separate lifecycle. - LeaveAll naturally replaced by Sandbox.Delete - some pkg and file renaming in order to have clear mapping between structure name and entity ("sandbox") - Revisited hosts and resolv.conf handling - Removed from JoinInfo interface capability of setting hosts and resolv.conf paths - Changed etchosts.Build() to first write the search domains and then the nameservers Signed-off-by: Alessandro Boch --- libnetwork/README.md | 18 +- libnetwork/api/api.go | 251 +++++-- libnetwork/api/api_test.go | 230 +++++- libnetwork/api/types.go | 42 +- libnetwork/client/client_test.go | 13 +- libnetwork/client/service.go | 28 +- libnetwork/client/types.go | 47 +- libnetwork/cmd/ovrouter/ovrouter.go | 17 +- libnetwork/cmd/readme_test/readme.go | 16 +- libnetwork/controller.go | 150 +++- libnetwork/driverapi/driverapi.go | 26 +- libnetwork/drivers/bridge/bridge.go | 46 +- libnetwork/drivers/bridge/bridge_test.go | 12 +- libnetwork/drivers/host/host.go | 22 +- libnetwork/drivers/null/null.go | 16 +- libnetwork/drivers/overlay/joinleave.go | 5 +- libnetwork/drivers/overlay/ov_endpoint.go | 15 +- libnetwork/drivers/overlay/ov_network.go | 29 +- libnetwork/drivers/overlay/ov_serf.go | 13 +- libnetwork/drivers/overlay/ov_utils.go | 3 +- libnetwork/drivers/overlay/overlay.go | 3 +- libnetwork/drivers/overlay/peerdb.go | 20 +- libnetwork/drivers/remote/api/api.go | 2 - libnetwork/drivers/remote/driver.go | 44 +- libnetwork/drivers/remote/driver_test.go | 24 +- libnetwork/drivers/windows/windows.go | 19 +- libnetwork/endpoint.go | 674 +++--------------- libnetwork/endpoint_info.go | 63 +- libnetwork/libnetwork_test.go | 566 +++++++++------ libnetwork/network.go | 41 +- .../{sandbox => osl}/interface_freebsd.go | 2 +- .../{sandbox => osl}/interface_linux.go | 2 +- .../{sandbox => osl}/interface_windows.go | 2 +- .../{sandbox => osl}/namespace_linux.go | 2 +- .../{sandbox => osl}/namespace_unsupported.go | 2 +- .../{sandbox => osl}/namespace_windows.go | 2 +- libnetwork/{sandbox => osl}/neigh_freebsd.go | 2 +- libnetwork/{sandbox => osl}/neigh_linux.go | 2 +- libnetwork/{sandbox => osl}/neigh_windows.go | 2 +- libnetwork/{sandbox => osl}/options_linux.go | 2 +- libnetwork/{sandbox => osl}/route_linux.go | 2 +- libnetwork/{sandbox => osl}/sandbox.go | 3 +- .../{sandbox => osl}/sandbox_freebsd.go | 2 +- .../{sandbox => osl}/sandbox_linux_test.go | 2 +- libnetwork/{sandbox => osl}/sandbox_test.go | 2 +- .../{sandbox => osl}/sandbox_unsupported.go | 2 +- .../sandbox_unsupported_test.go | 2 +- libnetwork/resolvconf/resolvconf.go | 21 +- libnetwork/resolvconf/resolvconf_test.go | 6 +- libnetwork/sandbox.go | 662 +++++++++++++++++ libnetwork/sandbox_test.go | 219 ++++++ libnetwork/sandboxdata.go | 259 ------- libnetwork/sandboxdata_test.go | 141 ---- libnetwork/store.go | 17 +- 54 files changed, 2166 insertions(+), 1649 deletions(-) rename libnetwork/{sandbox => osl}/interface_freebsd.go (85%) rename libnetwork/{sandbox => osl}/interface_linux.go (99%) rename libnetwork/{sandbox => osl}/interface_windows.go (85%) rename libnetwork/{sandbox => osl}/namespace_linux.go (99%) rename libnetwork/{sandbox => osl}/namespace_unsupported.go (89%) rename libnetwork/{sandbox => osl}/namespace_windows.go (97%) rename libnetwork/{sandbox => osl}/neigh_freebsd.go (84%) rename libnetwork/{sandbox => osl}/neigh_linux.go (99%) rename libnetwork/{sandbox => osl}/neigh_windows.go (84%) rename libnetwork/{sandbox => osl}/options_linux.go (98%) rename libnetwork/{sandbox => osl}/route_linux.go (99%) rename libnetwork/{sandbox => osl}/sandbox.go (98%) rename libnetwork/{sandbox => osl}/sandbox_freebsd.go (97%) rename libnetwork/{sandbox => osl}/sandbox_linux_test.go (99%) rename libnetwork/{sandbox => osl}/sandbox_test.go (99%) rename libnetwork/{sandbox => osl}/sandbox_unsupported.go (97%) rename libnetwork/{sandbox => osl}/sandbox_unsupported_test.go (93%) create mode 100644 libnetwork/sandbox.go create mode 100644 libnetwork/sandbox_test.go delete mode 100644 libnetwork/sandboxdata.go delete mode 100644 libnetwork/sandboxdata_test.go diff --git a/libnetwork/README.md b/libnetwork/README.md index d71c5b31e4..62a0205fc7 100644 --- a/libnetwork/README.md +++ b/libnetwork/README.md @@ -29,13 +29,13 @@ There are many networking solutions available to suit a broad range of use-cases driverOptions := options.Generic{} genericOption := make(map[string]interface{}) genericOption[netlabel.GenericData] = driverOptions - err := controller.ConfigureNetworkDriver(networkType, genericOption) + err = controller.ConfigureNetworkDriver(networkType, genericOption) if err != nil { return } // Create a network for containers to join. - // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can make of + // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can use. network, err := controller.NewNetwork(networkType, "network1") if err != nil { return @@ -50,12 +50,14 @@ There are many networking solutions available to suit a broad range of use-cases return } - // A container can join the endpoint by providing the container ID to the join - // api. - // Join accepts Variadic arguments which will be made use of by libnetwork and Drivers - err = ep.Join("container1", - libnetwork.JoinOptionHostname("test"), - libnetwork.JoinOptionDomainname("docker.io")) + // Create the sandbox for the containr. + sbx, err := controller.NewSandbox("container1", + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io")) + + // A sandbox can join the endpoint via the join api. + // Join accepts Variadic arguments which libnetwork and Drivers can use. + err = ep.Join(sbx) if err != nil { return } diff --git a/libnetwork/api/api.go b/libnetwork/api/api.go index 2bfc81b078..12cefe138b 100644 --- a/libnetwork/api/api.go +++ b/libnetwork/api/api.go @@ -35,7 +35,10 @@ const ( epNameQr = "{" + urlEpName + ":" + qregx + "}" epID = "{" + urlEpID + ":" + regex + "}" epPIDQr = "{" + urlEpPID + ":" + qregx + "}" - cnID = "{" + urlCnID + ":" + regex + "}" + sbID = "{" + urlSbID + ":" + regex + "}" + sbPIDQr = "{" + urlSbPID + ":" + qregx + "}" + cnIDQr = "{" + urlCnID + ":" + qregx + "}" + cnPIDQr = "{" + urlCnPID + ":" + qregx + "}" // Internal URL variable name.They can be anything as // long as they do not collide with query fields. @@ -45,7 +48,10 @@ const ( urlEpName = "endpoint-name" urlEpID = "endpoint-id" urlEpPID = "endpoint-partial-id" + urlSbID = "sandbox-id" + urlSbPID = "sandbox-partial-id" urlCnID = "container-id" + urlCnPID = "container-partial-id" // BridgeNetworkDriver is the built-in default for Network Driver BridgeNetworkDriver = "bridge" @@ -106,21 +112,28 @@ func (h *httpHandler) initRouter() { {"/services", []string{"partial-id", epPIDQr}, procGetServices}, {"/services", nil, procGetServices}, {"/services/" + epID, nil, procGetService}, - {"/services/" + epID + "/backend", nil, procGetContainers}, + {"/services/" + epID + "/backend", nil, procGetSandbox}, + {"/sandboxes", []string{"partial-container-id", cnPIDQr}, procGetSandboxes}, + {"/sandboxes", []string{"container-id", cnIDQr}, procGetSandboxes}, + {"/sandboxes", []string{"partial-id", sbPIDQr}, procGetSandboxes}, + {"/sandboxes", nil, procGetSandboxes}, + {"/sandboxes/" + sbID, nil, procGetSandbox}, }, "POST": { {"/networks", nil, procCreateNetwork}, {"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint}, - {"/networks/" + nwID + "/endpoints/" + epID + "/containers", nil, procJoinEndpoint}, + {"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes", nil, procJoinEndpoint}, {"/services", nil, procPublishService}, {"/services/" + epID + "/backend", nil, procAttachBackend}, + {"/sandboxes", nil, procCreateSandbox}, }, "DELETE": { {"/networks/" + nwID, nil, procDeleteNetwork}, {"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint}, - {"/networks/" + nwID + "/endpoints/" + epID + "/containers/" + cnID, nil, procLeaveEndpoint}, + {"/networks/" + nwID + "/endpoints/" + epID + "/sandboxes/" + sbID, nil, procLeaveEndpoint}, {"/services/" + epID, nil, procUnpublishService}, - {"/services/" + epID + "/backend/" + cnID, nil, procDetachBackend}, + {"/services/" + epID + "/backend/" + sbID, nil, procDetachBackend}, + {"/sandboxes/" + sbID, nil, procDeleteSandbox}, }, } @@ -191,10 +204,12 @@ func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource { return r } -func buildContainerResource(ci libnetwork.ContainerInfo) *containerResource { - r := &containerResource{} - if ci != nil { - r.ID = ci.ID() +func buildSandboxResource(sb libnetwork.Sandbox) *sandboxResource { + r := &sandboxResource{} + if sb != nil { + r.ID = sb.ID() + r.Key = sb.Key() + r.ContainerID = sb.ContainerID() } return r } @@ -213,41 +228,41 @@ func (nc *networkCreate) parseOptions() []libnetwork.NetworkOption { return setFctList } -func (ej *endpointJoin) parseOptions() []libnetwork.EndpointOption { - var setFctList []libnetwork.EndpointOption - if ej.HostName != "" { - setFctList = append(setFctList, libnetwork.JoinOptionHostname(ej.HostName)) +func (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption { + var setFctList []libnetwork.SandboxOption + if sc.HostName != "" { + setFctList = append(setFctList, libnetwork.OptionHostname(sc.HostName)) } - if ej.DomainName != "" { - setFctList = append(setFctList, libnetwork.JoinOptionDomainname(ej.DomainName)) + if sc.DomainName != "" { + setFctList = append(setFctList, libnetwork.OptionDomainname(sc.DomainName)) } - if ej.HostsPath != "" { - setFctList = append(setFctList, libnetwork.JoinOptionHostsPath(ej.HostsPath)) + if sc.HostsPath != "" { + setFctList = append(setFctList, libnetwork.OptionHostsPath(sc.HostsPath)) } - if ej.ResolvConfPath != "" { - setFctList = append(setFctList, libnetwork.JoinOptionResolvConfPath(ej.ResolvConfPath)) + if sc.ResolvConfPath != "" { + setFctList = append(setFctList, libnetwork.OptionResolvConfPath(sc.ResolvConfPath)) } - if ej.UseDefaultSandbox { - setFctList = append(setFctList, libnetwork.JoinOptionUseDefaultSandbox()) + if sc.UseDefaultSandbox { + setFctList = append(setFctList, libnetwork.OptionUseDefaultSandbox()) } - if ej.DNS != nil { - for _, d := range ej.DNS { - setFctList = append(setFctList, libnetwork.JoinOptionDNS(d)) + if sc.DNS != nil { + for _, d := range sc.DNS { + setFctList = append(setFctList, libnetwork.OptionDNS(d)) } } - if ej.ExtraHosts != nil { - for _, e := range ej.ExtraHosts { - setFctList = append(setFctList, libnetwork.JoinOptionExtraHost(e.Name, e.Address)) - } - } - if ej.ParentUpdates != nil { - for _, p := range ej.ParentUpdates { - setFctList = append(setFctList, libnetwork.JoinOptionParentUpdate(p.EndpointID, p.Name, p.Address)) + if sc.ExtraHosts != nil { + for _, e := range sc.ExtraHosts { + setFctList = append(setFctList, libnetwork.OptionExtraHost(e.Name, e.Address)) } } return setFctList } +func (ej *endpointJoin) parseOptions() []libnetwork.EndpointOption { + // priority will go here + return []libnetwork.EndpointOption{} +} + /****************** Process functions *******************/ @@ -337,6 +352,22 @@ func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, bod return list, &successResponse } +func procCreateSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var create sandboxCreate + + err := json.Unmarshal(body, &create) + if err != nil { + return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} + } + + sb, err := c.NewSandbox(create.ContainerID, create.parseOptions()...) + if err != nil { + return "", convertNetworkError(err) + } + + return sb.ID(), &createdResponse +} + /****************** Network interface *******************/ @@ -456,11 +487,16 @@ func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, bo return nil, errRsp } - err = ep.Join(ej.ContainerID, ej.parseOptions()...) + sb, errRsp := findSandbox(c, ej.SandboxID, byID) + if !errRsp.isOK() { + return nil, errRsp + } + + err = ep.Join(sb) if err != nil { return nil, convertNetworkError(err) } - return ep.Info().SandboxKey(), &successResponse + return sb.Key(), &successResponse } func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { @@ -472,7 +508,12 @@ func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, b return nil, errRsp } - err := ep.Leave(vars[urlCnID]) + sb, errRsp := findSandbox(c, vars[urlSbID], byID) + if !errRsp.isOK() { + return nil, errRsp + } + + err := ep.Leave(sb) if err != nil { return nil, convertNetworkError(err) } @@ -567,19 +608,6 @@ func procGetService(c libnetwork.NetworkController, vars map[string]string, body return buildEndpointResource(sv), &successResponse } -func procGetContainers(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { - epT, epBy := detectEndpointTarget(vars) - sv, errRsp := findService(c, epT, epBy) - if !errRsp.isOK() { - return nil, endpointToService(errRsp) - } - var list []*containerResource - if sv.ContainerInfo() != nil { - list = append(list, buildContainerResource(sv.ContainerInfo())) - } - return list, &successResponse -} - func procPublishService(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { var sp servicePublish @@ -635,11 +663,16 @@ func procAttachBackend(c libnetwork.NetworkController, vars map[string]string, b return nil, errRsp } - err = sv.Join(bk.ContainerID, bk.parseOptions()...) + sb, errRsp := findSandbox(c, bk.SandboxID, byID) + if !errRsp.isOK() { + return nil, errRsp + } + + err = sv.Join(sb) if err != nil { return nil, convertNetworkError(err) } - return sv.Info().SandboxKey(), &successResponse + return sb.Key(), &successResponse } func procDetachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { @@ -649,7 +682,94 @@ func procDetachBackend(c libnetwork.NetworkController, vars map[string]string, b return nil, errRsp } - err := sv.Leave(vars[urlCnID]) + sb, errRsp := findSandbox(c, vars[urlSbID], byID) + if !errRsp.isOK() { + return nil, errRsp + } + + err := sv.Leave(sb) + if err != nil { + return nil, convertNetworkError(err) + } + + return nil, &successResponse +} + +/****************** + Sandbox interface +*******************/ +func procGetSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + if epT, ok := vars[urlEpID]; ok { + sv, errRsp := findService(c, epT, byID) + if !errRsp.isOK() { + return nil, endpointToService(errRsp) + } + return buildSandboxResource(sv.Info().Sandbox()), &successResponse + } + + sbT, by := detectSandboxTarget(vars) + sb, errRsp := findSandbox(c, sbT, by) + if !errRsp.isOK() { + return nil, errRsp + } + return buildSandboxResource(sb), &successResponse +} + +type cndFnMkr func(string) cndFn +type cndFn func(libnetwork.Sandbox) bool + +// list of (query type, condition function makers) couples +var cndMkrList = []struct { + identifier string + maker cndFnMkr +}{ + {urlSbPID, func(id string) cndFn { + return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ID(), id) } + }}, + {urlCnID, func(id string) cndFn { + return func(sb libnetwork.Sandbox) bool { return sb.ContainerID() == id } + }}, + {urlCnPID, func(id string) cndFn { + return func(sb libnetwork.Sandbox) bool { return strings.HasPrefix(sb.ContainerID(), id) } + }}, +} + +func getQueryCondition(vars map[string]string) func(libnetwork.Sandbox) bool { + for _, im := range cndMkrList { + if val, ok := vars[im.identifier]; ok { + return im.maker(val) + } + } + return func(sb libnetwork.Sandbox) bool { return true } +} + +func sandboxWalker(condition cndFn, list *[]*sandboxResource) libnetwork.SandboxWalker { + return func(sb libnetwork.Sandbox) bool { + if condition(sb) { + *list = append(*list, buildSandboxResource(sb)) + } + return false + } +} + +func procGetSandboxes(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var list []*sandboxResource + + cnd := getQueryCondition(vars) + c.WalkSandboxes(sandboxWalker(cnd, &list)) + + return list, &successResponse +} + +func procDeleteSandbox(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + sbT, by := detectSandboxTarget(vars) + + sb, errRsp := findSandbox(c, sbT, by) + if !errRsp.isOK() { + return nil, errRsp + } + + err := sb.Delete() if err != nil { return nil, convertNetworkError(err) } @@ -676,6 +796,14 @@ func detectNetworkTarget(vars map[string]string) (string, int) { panic("Missing URL variable parameter for network") } +func detectSandboxTarget(vars map[string]string) (string, int) { + if target, ok := vars[urlSbID]; ok { + return target, byID + } + // vars are populated from the URL, following cannot happen + panic("Missing URL variable parameter for sandbox") +} + func detectEndpointTarget(vars map[string]string) (string, int) { if target, ok := vars[urlEpName]; ok { return target, byName @@ -712,6 +840,27 @@ func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.N return nw, &successResponse } +func findSandbox(c libnetwork.NetworkController, s string, by int) (libnetwork.Sandbox, *responseStatus) { + var ( + sb libnetwork.Sandbox + err error + ) + + switch by { + case byID: + sb, err = c.SandboxByID(s) + default: + panic(fmt.Sprintf("unexpected selector for sandbox search: %d", by)) + } + if err != nil { + if _, ok := err.(types.NotFoundError); ok { + return nil, &responseStatus{Status: "Resource not found: Sandbox", StatusCode: http.StatusNotFound} + } + return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} + } + return sb, &successResponse +} + func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) { nw, errRsp := findNetwork(c, ns, nwBy) if !errRsp.isOK() { diff --git a/libnetwork/api/api_test.go b/libnetwork/api/api_test.go index 8f1c48f99e..3dd29664ae 100644 --- a/libnetwork/api/api_test.go +++ b/libnetwork/api/api_test.go @@ -71,10 +71,18 @@ func i2nL(i interface{}) []*networkResource { return s } -func i2cL(i interface{}) []*containerResource { - s, ok := i.([]*containerResource) +func i2sb(i interface{}) *sandboxResource { + s, ok := i.(*sandboxResource) if !ok { - panic(fmt.Sprintf("Failed i2cL for %v", i)) + panic(fmt.Sprintf("Failed i2sb for %v", i)) + } + return s +} + +func i2sbL(i interface{}) []*sandboxResource { + s, ok := i.([]*sandboxResource) + if !ok { + panic(fmt.Sprintf("Failed i2sbL for %v", i)) } return s } @@ -112,30 +120,27 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestJoinOptionParser(t *testing.T) { +func TestSandboxOptionParser(t *testing.T) { hn := "host1" dn := "docker.com" hp := "/etc/hosts" rc := "/etc/resolv.conf" dnss := []string{"8.8.8.8", "172.28.34.5"} - ehs := []endpointExtraHost{endpointExtraHost{Name: "extra1", Address: "172.28.9.1"}, endpointExtraHost{Name: "extra2", Address: "172.28.9.2"}} - pus := []endpointParentUpdate{endpointParentUpdate{EndpointID: "abc123def456", Name: "serv1", Address: "172.28.30.123"}} + ehs := []extraHost{extraHost{Name: "extra1", Address: "172.28.9.1"}, extraHost{Name: "extra2", Address: "172.28.9.2"}} - ej := endpointJoin{ + sb := sandboxCreate{ HostName: hn, DomainName: dn, HostsPath: hp, ResolvConfPath: rc, DNS: dnss, ExtraHosts: ehs, - ParentUpdates: pus, UseDefaultSandbox: true, } - if len(ej.parseOptions()) != 10 { - t.Fatalf("Failed to generate all libnetwork.EndpointJoinOption methods libnetwork.EndpointJoinOption method") + if len(sb.parseOptions()) != 9 { + t.Fatalf("Failed to generate all libnetwork.SandboxOption methods") } - } func TestJson(t *testing.T) { @@ -155,7 +160,7 @@ func TestJson(t *testing.T) { t.Fatalf("Incorrect networkCreate after json encoding/deconding: %v", ncp) } - jl := endpointJoin{ContainerID: "abcdef456789"} + jl := endpointJoin{SandboxID: "abcdef456789"} b, err = json.Marshal(jl) if err != nil { t.Fatal(err) @@ -167,7 +172,7 @@ func TestJson(t *testing.T) { t.Fatal(err) } - if jl.ContainerID != jld.ContainerID { + if jl.SandboxID != jld.SandboxID { t.Fatalf("Incorrect endpointJoin after json encoding/deconding: %v", jld) } } @@ -913,7 +918,8 @@ func TestAttachDetachBackend(t *testing.T) { t.Fatalf("Expected %d. Got: %v", http.StatusNotFound, errRsp) } - _, errRsp = procGetContainers(c, vars, nil) + vars[urlEpID] = "db" + _, errRsp = procGetSandbox(c, vars, nil) if errRsp.isOK() { t.Fatalf("Expected failure. Got %v", errRsp) } @@ -931,7 +937,11 @@ func TestAttachDetachBackend(t *testing.T) { } cid := "abcdefghi" - jl := endpointJoin{ContainerID: cid} + sbox, err := c.NewSandbox(cid) + sid := sbox.ID() + defer sbox.Delete() + + jl := endpointJoin{SandboxID: sid} jlb, err := json.Marshal(jl) if err != nil { t.Fatal(err) @@ -942,16 +952,16 @@ func TestAttachDetachBackend(t *testing.T) { t.Fatalf("Unexpected failure, got: %v", errRsp) } - cli, errRsp := procGetContainers(c, vars, nil) + sli, errRsp := procGetSandboxes(c, vars, nil) if errRsp != &successResponse { t.Fatalf("Unexpected failure, got: %v", errRsp) } - cl := i2cL(cli) - if len(cl) != 1 { - t.Fatalf("Did not find expected number of containers attached to the service: %d", len(cl)) + sl := i2sbL(sli) + if len(sl) != 1 { + t.Fatalf("Did not find expected number of sandboxes attached to the service: %d", len(sl)) } - if cl[0].ID != cid { - t.Fatalf("Did not find expected container attached to the service: %v", cl[0]) + if sl[0].ContainerID != cid { + t.Fatalf("Did not find expected sandbox attached to the service: %v", sl[0]) } _, errRsp = procUnpublishService(c, vars, nil) @@ -980,19 +990,20 @@ func TestAttachDetachBackend(t *testing.T) { t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp) } - vars[urlCnID] = cid + vars[urlSbID] = sid _, errRsp = procDetachBackend(c, vars, nil) if errRsp != &successResponse { t.Fatalf("Unexpected failure, got: %v", errRsp) } - cli, errRsp = procGetContainers(c, vars, nil) + delete(vars, urlEpID) + si, errRsp := procGetSandbox(c, vars, nil) if errRsp != &successResponse { t.Fatalf("Unexpected failure, got: %v", errRsp) } - cl = i2cL(cli) - if len(cl) != 0 { - t.Fatalf("Did not find expected number of containers attached to the service: %d", len(cl)) + sb := i2sb(si) + if sb.ContainerID != cid { + t.Fatalf("Did not find expected sandbox. Got %v", sb) } err = ep1.Delete() @@ -1284,7 +1295,10 @@ func TestJoinLeave(t *testing.T) { } cid := "abcdefghi" - jl := endpointJoin{ContainerID: cid} + sb, err := c.NewSandbox(cid) + defer sb.Delete() + + jl := endpointJoin{SandboxID: sb.ID()} jlb, err := json.Marshal(jl) if err != nil { t.Fatal(err) @@ -1314,7 +1328,7 @@ func TestJoinLeave(t *testing.T) { vars[urlEpName] = "endpoint" key, errRsp := procJoinEndpoint(c, vars, jlb) if errRsp != &successResponse { - t.Fatalf("Expected failure, got: %v", errRsp) + t.Fatalf("Unexepected failure, got: %v", errRsp) } keyStr := i2s(key) @@ -1371,7 +1385,7 @@ func TestJoinLeave(t *testing.T) { t.Fatalf("Expected failure, got: %v", errRsp) } - vars[urlCnID] = cid + vars[urlSbID] = sb.ID() _, errRsp = procLeaveEndpoint(c, vars, jlb) if errRsp != &successResponse { t.Fatalf("Unexepected failure: %v", errRsp) @@ -2065,6 +2079,164 @@ func TestEndToEnd(t *testing.T) { if epr.Name != "ep-TwentyTwo" || epr.ID != eid { t.Fatalf("Incongruent resource found: %v", epr) } + + // Store two container ids and one partial ids + cid1 := "container10010000000" + cid2 := "container20010000000" + chars = []byte(cid1) + cpid1 := string(chars[0 : len(chars)/2]) + + // Create sandboxes + sb1, err := json.Marshal(sandboxCreate{ContainerID: cid1}) + if err != nil { + t.Fatal(err) + } + + lr = newLocalReader(sb1) + req, err = http.NewRequest("POST", "/v5.22/sandboxes", lr) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusCreated { + t.Fatalf("Unexpectded status code. Expected (%d). Got (%d): %s.", http.StatusCreated, rsp.statusCode, string(rsp.body)) + } + if len(rsp.body) == 0 { + t.Fatalf("Empty response body") + } + // Get sandbox id and partial id + var sid1 string + err = json.Unmarshal(rsp.body, &sid1) + if err != nil { + t.Fatal(err) + } + + sb2, err := json.Marshal(sandboxCreate{ContainerID: cid2}) + if err != nil { + t.Fatal(err) + } + + lr = newLocalReader(sb2) + req, err = http.NewRequest("POST", "/v5.22/sandboxes", lr) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusCreated { + t.Fatalf("Unexpectded status code. Expected (%d). Got (%d): %s.", http.StatusCreated, rsp.statusCode, string(rsp.body)) + } + if len(rsp.body) == 0 { + t.Fatalf("Empty response body") + } + // Get sandbox id and partial id + var sid2 string + err = json.Unmarshal(rsp.body, &sid2) + if err != nil { + t.Fatal(err) + } + chars = []byte(sid2) + spid2 := string(chars[0 : len(chars)/2]) + + // Query sandboxes + req, err = http.NewRequest("GET", "/sandboxes", nil) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusOK { + t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body) + } + + var sbList []*sandboxResource + err = json.Unmarshal(rsp.body, &sbList) + if err != nil { + t.Fatal(err) + } + if len(sbList) != 2 { + t.Fatalf("Expected 2 elements in list. Got %v", sbList) + } + + // Get sandbox by id + req, err = http.NewRequest("GET", "/sandboxes/"+sid1, nil) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusOK { + t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) + } + + var sbr sandboxResource + err = json.Unmarshal(rsp.body, &sbr) + if err != nil { + t.Fatal(err) + } + if sbr.ContainerID != cid1 { + t.Fatalf("Incongruent resource found: %v", sbr) + } + + // Query sandbox by partial sandbox id + req, err = http.NewRequest("GET", "/sandboxes?partial-id="+spid2, nil) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusOK { + t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) + } + + err = json.Unmarshal(rsp.body, &sbList) + if err != nil { + t.Fatal(err) + } + if len(sbList) == 0 { + t.Fatalf("Empty response body") + } + if sbList[0].ID != sid2 { + t.Fatalf("Incongruent resource found: %v", sbList[0]) + } + + // Query sandbox by container id + req, err = http.NewRequest("GET", "/sandboxes?container-id="+cid2, nil) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusOK { + t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) + } + + err = json.Unmarshal(rsp.body, &sbList) + if err != nil { + t.Fatal(err) + } + if len(sbList) == 0 { + t.Fatalf("Empty response body") + } + if sbList[0].ContainerID != cid2 { + t.Fatalf("Incongruent resource found: %v", sbList[0]) + } + + // Query sandbox by partial container id + req, err = http.NewRequest("GET", "/sandboxes?partial-container-id="+cpid1, nil) + if err != nil { + t.Fatal(err) + } + handleRequest(rsp, req) + if rsp.statusCode != http.StatusOK { + t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body) + } + + err = json.Unmarshal(rsp.body, &sbList) + if err != nil { + t.Fatal(err) + } + if len(sbList) == 0 { + t.Fatalf("Empty response body") + } + if sbList[0].ContainerID != cid1 { + t.Fatalf("Incongruent resource found: %v", sbList[0]) + } } type bre struct{} diff --git a/libnetwork/api/types.go b/libnetwork/api/types.go index 72f20db24f..d734a97c0e 100644 --- a/libnetwork/api/types.go +++ b/libnetwork/api/types.go @@ -21,9 +21,11 @@ type endpointResource struct { Network string `json:"network"` } -// containerResource is the body of "get service backend" response message -type containerResource struct { - ID string `json:"id"` +// sandboxResource is the body of "get service backend" response message +type sandboxResource struct { + ID string `json:"id"` + Key string `json:"key"` + ContainerID string `json:"container_id"` // will add more fields once labels change is in } @@ -45,17 +47,21 @@ type endpointCreate struct { PortMapping []types.PortBinding `json:"port_mapping"` } +// sandboxCreate is the expected body of the "create sandbox" http request message +type sandboxCreate struct { + ContainerID string `json:"container_id"` + HostName string `json:"host_name"` + DomainName string `json:"domain_name"` + HostsPath string `json:"hosts_path"` + ResolvConfPath string `json:"resolv_conf_path"` + DNS []string `json:"dns"` + ExtraHosts []extraHost `json:"extra_hosts"` + UseDefaultSandbox bool `json:"use_default_sandbox"` +} + // endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages type endpointJoin struct { - ContainerID string `json:"container_id"` - HostName string `json:"host_name"` - DomainName string `json:"domain_name"` - HostsPath string `json:"hosts_path"` - ResolvConfPath string `json:"resolv_conf_path"` - DNS []string `json:"dns"` - ExtraHosts []endpointExtraHost `json:"extra_hosts"` - ParentUpdates []endpointParentUpdate `json:"parent_updates"` - UseDefaultSandbox bool `json:"use_default_sandbox"` + SandboxID string `json:"sandbox_id"` } // servicePublish represents the body of the "publish service" http request message @@ -66,16 +72,8 @@ type servicePublish struct { PortMapping []types.PortBinding `json:"port_mapping"` } -// EndpointExtraHost represents the extra host object -type endpointExtraHost struct { +// extraHost represents the extra host object +type extraHost struct { Name string `json:"name"` Address string `json:"address"` } - -// EndpointParentUpdate is the object carrying the information about the -// endpoint parent that needs to be updated -type endpointParentUpdate struct { - EndpointID string `json:"endpoint_id"` - Name string `json:"name"` - Address string `json:"address"` -} diff --git a/libnetwork/client/client_test.go b/libnetwork/client/client_test.go index 06b8b2a6dc..886444a12d 100644 --- a/libnetwork/client/client_test.go +++ b/libnetwork/client/client_test.go @@ -26,12 +26,13 @@ func TestMain(m *testing.M) { } var callbackFunc func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) -var mockNwJSON, mockNwListJSON, mockServiceJSON, mockServiceListJSON []byte +var mockNwJSON, mockNwListJSON, mockServiceJSON, mockServiceListJSON, mockSbJSON, mockSbListJSON []byte var mockNwName = "test" var mockNwID = "2a3456789" var mockServiceName = "testSrv" var mockServiceID = "2a3456789" var mockContainerID = "2a3456789" +var mockSandboxID = "2b3456789" func setupMockHTTPCallback() { var list []networkResource @@ -46,6 +47,12 @@ func setupMockHTTPCallback() { srvList = append(srvList, ep) mockServiceListJSON, _ = json.Marshal(srvList) + var sbxList []sandboxResource + sb := sandboxResource{ID: mockSandboxID, ContainerID: mockContainerID} + mockSbJSON, _ = json.Marshal(sb) + sbxList = append(sbxList, sb) + mockSbListJSON, _ = json.Marshal(sbxList) + dummyHTTPHdr := http.Header{} callbackFunc = func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) { @@ -78,6 +85,8 @@ func setupMockHTTPCallback() { rsp = string(mockServiceJSON) } else if strings.Contains(path, "containers") { return nopCloser{bytes.NewBufferString("")}, dummyHTTPHdr, 400, fmt.Errorf("Bad Request") + } else if strings.Contains(path, fmt.Sprintf("sandboxes?container-id=%s", mockContainerID)) { + rsp = string(mockSbListJSON) } case "POST": var data []byte @@ -86,7 +95,7 @@ func setupMockHTTPCallback() { } else if strings.HasSuffix(path, "services") { data, _ = json.Marshal(mockServiceID) } else if strings.HasSuffix(path, "backend") { - data, _ = json.Marshal(mockContainerID) + data, _ = json.Marshal(mockSandboxID) } rsp = string(data) case "PUT": diff --git a/libnetwork/client/service.go b/libnetwork/client/service.go index 35e040f805..b2f4101629 100644 --- a/libnetwork/client/service.go +++ b/libnetwork/client/service.go @@ -114,6 +114,25 @@ func lookupContainerID(cli *NetworkCli, cnNameID string) (string, error) { return "", fmt.Errorf("Cannot find container ID in json response") } +func lookupSandboxID(cli *NetworkCli, containerID string) (string, error) { + obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/sandboxes?container-id=%s", containerID), nil, nil)) + if err != nil { + return "", err + } + + var sandboxList []sandboxResource + err = json.Unmarshal(obj, &sandboxList) + if err != nil { + return "", err + } + + if len(sandboxList) == 0 { + return "", fmt.Errorf("cannot find sandbox for container: %s", containerID) + } + + return sandboxList[0].ID, nil +} + // CmdService handles the service UI func (cli *NetworkCli) CmdService(chain string, args ...string) error { cmd := cli.Subcmd(chain, "service", "COMMAND [OPTIONS] [arg...]", serviceUsage(chain), false) @@ -249,7 +268,7 @@ func getBackendID(cli *NetworkCli, servID string) (string, error) { ) if obj, _, err = readBody(cli.call("GET", "/services/"+servID+"/backend", nil, nil)); err == nil { - var bkl []backendResource + var bkl []sandboxResource if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&bkl); err == nil { if len(bkl) > 0 { bk = bkl[0].ID @@ -310,13 +329,18 @@ func (cli *NetworkCli) CmdServiceAttach(chain string, args ...string) error { return err } + sandboxID, err := lookupSandboxID(cli, containerID) + if err != nil { + return err + } + sn, nn := parseServiceName(cmd.Arg(1)) serviceID, err := lookupServiceID(cli, nn, sn) if err != nil { return err } - nc := serviceAttach{ContainerID: containerID} + nc := serviceAttach{SandboxID: sandboxID} _, _, err = readBody(cli.call("POST", "/services/"+serviceID+"/backend", nc, nil)) diff --git a/libnetwork/client/types.go b/libnetwork/client/types.go index e14460aba7..f215a4efed 100644 --- a/libnetwork/client/types.go +++ b/libnetwork/client/types.go @@ -21,9 +21,11 @@ type serviceResource struct { Network string `json:"network"` } -// backendResource is the body of "get service backend" response message -type backendResource struct { - ID string `json:"id"` +// sandboxResource is the body of "get service backend" response message +type sandboxResource struct { + ID string `json:"id"` + Key string `json:"key"` + ContainerID string `json:"container_id"` } /*********** @@ -45,29 +47,32 @@ type serviceCreate struct { PortMapping []types.PortBinding `json:"port_mapping"` } -// serviceAttach represents the expected body of the "attach/detach backend to/from service" http request messages +// serviceAttach represents the expected body of the "attach/detach sandbox to/from service" http request messages type serviceAttach struct { - ContainerID string `json:"container_id"` - HostName string `json:"host_name"` - DomainName string `json:"domain_name"` - HostsPath string `json:"hosts_path"` - ResolvConfPath string `json:"resolv_conf_path"` - DNS []string `json:"dns"` - ExtraHosts []serviceExtraHost `json:"extra_hosts"` - ParentUpdates []serviceParentUpdate `json:"parent_updates"` - UseDefaultSandbox bool `json:"use_default_sandbox"` + SandboxID string `json:"sandbox_id"` } -// serviceExtraHost represents the extra host object -type serviceExtraHost struct { +type sandboxCreate struct { + ContainerID string `json:"container_id"` + HostName string `json:"host_name"` + DomainName string `json:"domain_name"` + HostsPath string `json:"hosts_path"` + ResolvConfPath string `json:"resolv_conf_path"` + DNS []string `json:"dns"` + ExtraHosts []extraHost `json:"extra_hosts"` + UseDefaultSandbox bool `json:"use_default_sandbox"` +} + +// extraHost represents the extra host object +type extraHost struct { Name string `json:"name"` Address string `json:"address"` } -// EndpointParentUpdate is the object carrying the information about the -// endpoint parent that needs to be updated -type serviceParentUpdate struct { - EndpointID string `json:"service_id"` - Name string `json:"name"` - Address string `json:"address"` +// sandboxParentUpdate is the object carrying the information about the +// sanbox parent that needs to be updated +type sandboxParentUpdate struct { + ContainerID string `json:"container_id"` + Name string `json:"name"` + Address string `json:"address"` } diff --git a/libnetwork/cmd/ovrouter/ovrouter.go b/libnetwork/cmd/ovrouter/ovrouter.go index a75b55067c..1dc60c8271 100644 --- a/libnetwork/cmd/ovrouter/ovrouter.go +++ b/libnetwork/cmd/ovrouter/ovrouter.go @@ -10,7 +10,6 @@ import ( "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/drivers/overlay" "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" "github.com/vishvananda/netlink" ) @@ -68,14 +67,6 @@ func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int, return nil } -func (ep *endpoint) SetHostsPath(string) error { - return nil -} - -func (ep *endpoint) SetResolvConfPath(string) error { - return nil -} - func main() { if reexec.Init() { return @@ -103,20 +94,20 @@ func main() { r.d.Config(opt) - if err := r.d.CreateNetwork(types.UUID("testnetwork"), + if err := r.d.CreateNetwork("testnetwork", map[string]interface{}{}); err != nil { fmt.Printf("Failed to create network in the driver: %v\n", err) os.Exit(1) } ep := &endpoint{} - if err := r.d.CreateEndpoint(types.UUID("testnetwork"), types.UUID("testep"), + if err := r.d.CreateEndpoint("testnetwork", "testep", ep, map[string]interface{}{}); err != nil { fmt.Printf("Failed to create endpoint in the driver: %v\n", err) os.Exit(1) } - if err := r.d.Join(types.UUID("testnetwork"), types.UUID("testep"), + if err := r.d.Join("testnetwork", "testep", "", ep, map[string]interface{}{}); err != nil { fmt.Printf("Failed to join an endpoint in the driver: %v\n", err) os.Exit(1) @@ -141,7 +132,7 @@ func main() { for { select { case <-sigCh: - r.d.Leave(types.UUID("testnetwork"), types.UUID("testep")) + r.d.Leave("testnetwork", "testep") overlay.Fini(r.d) os.Exit(0) } diff --git a/libnetwork/cmd/readme_test/readme.go b/libnetwork/cmd/readme_test/readme.go index 669b517f43..9591aefa5c 100644 --- a/libnetwork/cmd/readme_test/readme.go +++ b/libnetwork/cmd/readme_test/readme.go @@ -28,7 +28,7 @@ func main() { } // Create a network for containers to join. - // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can make of + // NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can use. network, err := controller.NewNetwork(networkType, "network1") if err != nil { return @@ -43,12 +43,14 @@ func main() { return } - // A container can join the endpoint by providing the container ID to the join - // api. - // Join accepts Variadic arguments which will be made use of by libnetwork and Drivers - err = ep.Join("container1", - libnetwork.JoinOptionHostname("test"), - libnetwork.JoinOptionDomainname("docker.io")) + // Create the sandbox for the containr. + sbx, err := controller.NewSandbox("container1", + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io")) + + // A sandbox can join the endpoint via the join api. + // Join accepts Variadic arguments which libnetwork and Drivers can use. + err = ep.Join(sbx) if err != nil { return } diff --git a/libnetwork/controller.go b/libnetwork/controller.go index 02a9f7eb2a..4e4578ffb3 100644 --- a/libnetwork/controller.go +++ b/libnetwork/controller.go @@ -45,6 +45,7 @@ create network namespaces and allocate interfaces for containers to use. package libnetwork import ( + "container/heap" "fmt" "net" "strings" @@ -58,7 +59,7 @@ import ( "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/hostdiscovery" "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/sandbox" + "github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/types" ) @@ -87,8 +88,17 @@ type NetworkController interface { // NetworkByID returns the Network which has the passed id. If not found, the error ErrNoSuchNetwork is returned. NetworkByID(id string) (Network, error) - // LeaveAll accepts a container id and attempts to leave all endpoints that the container has joined - LeaveAll(id string) error + // NewSandbox cretes a new network sandbox for the passed container id + NewSandbox(containerID string, options ...SandboxOption) (Sandbox, error) + + // Sandboxes returns the list of Sandbox(s) managed by this controller. + Sandboxes() []Sandbox + + // WlakSandboxes uses the provided function to walk the Sandbox(s) managed by this controller. + WalkSandboxes(walker SandboxWalker) + + // SandboxByID returns the Sandbox which has the passed id. If not found, a types.NotFoundError is returned. + SandboxByID(id string) (Sandbox, error) // GC triggers immediate garbage collection of resources which are garbage collected. GC() @@ -98,15 +108,19 @@ type NetworkController interface { // When the function returns true, the walk will stop. type NetworkWalker func(nw Network) bool +// SandboxWalker is a client provided function which will be used to walk the Sandboxes. +// When the function returns true, the walk will stop. +type SandboxWalker func(sb Sandbox) bool + type driverData struct { driver driverapi.Driver capability driverapi.Capability } type driverTable map[string]*driverData -type networkTable map[types.UUID]*network -type endpointTable map[types.UUID]*endpoint -type sandboxTable map[string]*sandboxData +type networkTable map[string]*network +type endpointTable map[string]*endpoint +type sandboxTable map[string]*sandbox type controller struct { networks networkTable @@ -250,7 +264,7 @@ func (c *controller) NewNetwork(networkType, name string, options ...NetworkOpti network := &network{ name: name, networkType: networkType, - id: types.UUID(stringid.GenerateRandomID()), + id: stringid.GenerateRandomID(), ctrlr: c, endpoints: endpointTable{}, } @@ -356,12 +370,130 @@ func (c *controller) NetworkByID(id string) (Network, error) { } c.Lock() defer c.Unlock() - if n, ok := c.networks[types.UUID(id)]; ok { + if n, ok := c.networks[id]; ok { return n, nil } return nil, ErrNoSuchNetwork(id) } +// NewSandbox creates a new sandbox for the passed container id +func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (Sandbox, error) { + var err error + + if containerID == "" { + return nil, types.BadRequestErrorf("invalid container ID") + } + + var existing Sandbox + look := SandboxContainerWalker(&existing, containerID) + c.WalkSandboxes(look) + if existing != nil { + return nil, types.BadRequestErrorf("container %s is already present: %v", containerID, existing) + } + + // Create sandbox and process options first. Key generation depends on an option + sb := &sandbox{ + id: stringid.GenerateRandomID(), + containerID: containerID, + endpoints: epHeap{}, + epPriority: map[string]int{}, + config: containerConfig{}, + controller: c, + } + // This sandbox may be using an existing osl sandbox, sharing it with another sandbox + var peerSb Sandbox + c.WalkSandboxes(SandboxKeyWalker(&peerSb, sb.Key())) + if peerSb != nil { + sb.osSbox = peerSb.(*sandbox).osSbox + } + + heap.Init(&sb.endpoints) + + sb.processOptions(options...) + + err = sb.buildHostsFile() + if err != nil { + return nil, err + } + + err = sb.updateParentHosts() + if err != nil { + return nil, err + } + + err = sb.setupDNS() + if err != nil { + return nil, err + } + + if sb.osSbox == nil { + if sb.osSbox, err = osl.NewSandbox(sb.Key(), !sb.config.useDefaultSandBox); err != nil { + return nil, fmt.Errorf("failed to create new osl sandbox: %v", err) + } + } + + c.Lock() + c.sandboxes[sb.id] = sb + c.Unlock() + + return sb, nil +} + +func (c *controller) Sandboxes() []Sandbox { + c.Lock() + defer c.Unlock() + + list := make([]Sandbox, 0, len(c.sandboxes)) + for _, s := range c.sandboxes { + list = append(list, s) + } + + return list +} + +func (c *controller) WalkSandboxes(walker SandboxWalker) { + for _, sb := range c.Sandboxes() { + if walker(sb) { + return + } + } +} + +func (c *controller) SandboxByID(id string) (Sandbox, error) { + if id == "" { + return nil, ErrInvalidID(id) + } + c.Lock() + s, ok := c.sandboxes[id] + c.Unlock() + if !ok { + return nil, types.NotFoundErrorf("sandbox %s not found", id) + } + return s, nil +} + +// SandboxContainerWalker returns a Sandbox Walker function which looks for an existing Sandbox with the passed containerID +func SandboxContainerWalker(out *Sandbox, containerID string) SandboxWalker { + return func(sb Sandbox) bool { + if sb.ContainerID() == containerID { + *out = sb + return true + } + return false + } +} + +// SandboxKeyWalker returns a Sandbox Walker function which looks for an existing Sandbox with the passed key +func SandboxKeyWalker(out *Sandbox, key string) SandboxWalker { + return func(sb Sandbox) bool { + if sb.Key() == key { + *out = sb + return true + } + return false + } +} + func (c *controller) loadDriver(networkType string) (*driverData, error) { // Plugins pkg performs lazy loading of plugins that acts as remote drivers. // As per the design, this Get call will result in remote driver discovery if there is a corresponding plugin available. @@ -395,5 +527,5 @@ func (c *controller) isDriverGlobalScoped(networkType string) (bool, error) { } func (c *controller) GC() { - sandbox.GC() + osl.GC() } diff --git a/libnetwork/driverapi/driverapi.go b/libnetwork/driverapi/driverapi.go index e53947d82e..97813a9824 100644 --- a/libnetwork/driverapi/driverapi.go +++ b/libnetwork/driverapi/driverapi.go @@ -1,10 +1,6 @@ package driverapi -import ( - "net" - - "github.com/docker/libnetwork/types" -) +import "net" // NetworkPluginEndpointType represents the Endpoint Type used by Plugin system const NetworkPluginEndpointType = "NetworkDriver" @@ -17,31 +13,31 @@ type Driver interface { // CreateNetwork invokes the driver method to create a network passing // the network id and network specific config. The config mechanism will // eventually be replaced with labels which are yet to be introduced. - CreateNetwork(nid types.UUID, options map[string]interface{}) error + CreateNetwork(nid string, options map[string]interface{}) error // DeleteNetwork invokes the driver method to delete network passing // the network id. - DeleteNetwork(nid types.UUID) error + DeleteNetwork(nid string) error // CreateEndpoint invokes the driver method to create an endpoint // passing the network id, endpoint id endpoint information and driver // specific config. The endpoint information can be either consumed by // the driver or populated by the driver. The config mechanism will // eventually be replaced with labels which are yet to be introduced. - CreateEndpoint(nid, eid types.UUID, epInfo EndpointInfo, options map[string]interface{}) error + CreateEndpoint(nid, eid string, epInfo EndpointInfo, options map[string]interface{}) error // DeleteEndpoint invokes the driver method to delete an endpoint // passing the network id and endpoint id. - DeleteEndpoint(nid, eid types.UUID) error + DeleteEndpoint(nid, eid string) error // EndpointOperInfo retrieves from the driver the operational data related to the specified endpoint - EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) + EndpointOperInfo(nid, eid string) (map[string]interface{}, error) // Join method is invoked when a Sandbox is attached to an endpoint. - Join(nid, eid types.UUID, sboxKey string, jinfo JoinInfo, options map[string]interface{}) error + Join(nid, eid string, sboxKey string, jinfo JoinInfo, options map[string]interface{}) error // Leave method is invoked when a Sandbox detaches from an endpoint. - Leave(nid, eid types.UUID) error + Leave(nid, eid string) error // Type returns the the type of this driver, the network type this driver manages Type() string @@ -107,12 +103,6 @@ type JoinInfo interface { // AddStaticRoute adds a routes to the sandbox. // It may be used in addtion to or instead of a default gateway (as above). AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP, interfaceID int) error - - // SetHostsPath sets the overriding /etc/hosts path to use for the container. - SetHostsPath(string) error - - // SetResolvConfPath sets the overriding /etc/resolv.conf path to use for the container. - SetResolvConfPath(string) error } // DriverCallback provides a Callback interface for Drivers into LibNetwork diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index a6a88b5cb8..faf9eb60d7 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -75,7 +75,7 @@ type containerConfiguration struct { } type bridgeEndpoint struct { - id types.UUID + id string srcName string addr *net.IPNet addrv6 *net.IPNet @@ -86,10 +86,10 @@ type bridgeEndpoint struct { } type bridgeNetwork struct { - id types.UUID + id string bridge *bridgeInterface // The bridge's L3 interface config *networkConfiguration - endpoints map[types.UUID]*bridgeEndpoint // key: endpoint id + endpoints map[string]*bridgeEndpoint // key: endpoint id portMapper *portmapper.PortMapper driver *driver // The network's driver sync.Mutex @@ -100,7 +100,7 @@ type driver struct { network *bridgeNetwork natChain *iptables.ChainInfo filterChain *iptables.ChainInfo - networks map[types.UUID]*bridgeNetwork + networks map[string]*bridgeNetwork sync.Mutex } @@ -110,7 +110,7 @@ func init() { // New constructs a new bridge driver func newDriver() driverapi.Driver { - return &driver{networks: map[types.UUID]*bridgeNetwork{}} + return &driver{networks: map[string]*bridgeNetwork{}} } // Init registers a new instance of bridge driver @@ -346,7 +346,7 @@ func (n *bridgeNetwork) getNetworkBridgeName() string { return config.BridgeName } -func (n *bridgeNetwork) getEndpoint(eid types.UUID) (*bridgeEndpoint, error) { +func (n *bridgeNetwork) getEndpoint(eid string) (*bridgeEndpoint, error) { n.Lock() defer n.Unlock() @@ -394,7 +394,7 @@ func (n *bridgeNetwork) isolateNetwork(others []*bridgeNetwork, enable bool) err } // Checks whether this network's configuration for the network with this id conflicts with any of the passed networks -func (c *networkConfiguration) conflictsWithNetworks(id types.UUID, others []*bridgeNetwork) error { +func (c *networkConfiguration) conflictsWithNetworks(id string, others []*bridgeNetwork) error { for _, nw := range others { nw.Lock() @@ -475,7 +475,7 @@ func (d *driver) Config(option map[string]interface{}) error { return nil } -func (d *driver) getNetwork(id types.UUID) (*bridgeNetwork, error) { +func (d *driver) getNetwork(id string) (*bridgeNetwork, error) { d.Lock() defer d.Unlock() @@ -567,7 +567,7 @@ func (d *driver) getNetworks() []*bridgeNetwork { } // Create a new network using bridge plugin -func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { var err error // Sanity checks @@ -596,7 +596,7 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err // Create and set network handler in driver network := &bridgeNetwork{ id: id, - endpoints: make(map[types.UUID]*bridgeEndpoint), + endpoints: make(map[string]*bridgeEndpoint), config: config, portMapper: portmapper.New(), driver: d, @@ -719,7 +719,7 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err return nil } -func (d *driver) DeleteNetwork(nid types.UUID) error { +func (d *driver) DeleteNetwork(nid string) error { var err error // Get network handler and remove it from driver @@ -843,7 +843,7 @@ func setHairpinMode(link netlink.Link, enable bool) error { return nil } -func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { +func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { var ( ipv6Addr *net.IPNet err error @@ -927,13 +927,13 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn LinkAttrs: netlink.LinkAttrs{Name: hostIfName, TxQLen: 0}, PeerName: containerIfName} if err = netlink.LinkAdd(veth); err != nil { - return err + return types.InternalErrorf("failed to add the host (%s) <=> sandbox (%s) pair interfaces: %v", hostIfName, containerIfName, err) } // Get the host side pipe interface handler host, err := netlink.LinkByName(hostIfName) if err != nil { - return err + return types.InternalErrorf("failed to find host side interface %s: %v", hostIfName, err) } defer func() { if err != nil { @@ -944,7 +944,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn // Get the sandbox side pipe interface handler sbox, err := netlink.LinkByName(containerIfName) if err != nil { - return err + return types.InternalErrorf("failed to find sandbox side interface %s: %v", containerIfName, err) } defer func() { if err != nil { @@ -960,11 +960,11 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn if config.Mtu != 0 { err = netlink.LinkSetMTU(host, config.Mtu) if err != nil { - return err + return types.InternalErrorf("failed to set MTU on host interface %s: %v", hostIfName, err) } err = netlink.LinkSetMTU(sbox, config.Mtu) if err != nil { - return err + return types.InternalErrorf("failed to set MTU on sandbox interface %s: %v", containerIfName, err) } } @@ -1054,7 +1054,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn return nil } -func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { +func (d *driver) DeleteEndpoint(nid, eid string) error { var err error // Get the network handler and make sure it exists @@ -1138,7 +1138,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { return nil } -func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { // Get the network handler and make sure it exists d.Lock() n, ok := d.networks[nid] @@ -1195,7 +1195,7 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, } // Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { network, err := d.getNetwork(nid) if err != nil { return err @@ -1238,7 +1238,7 @@ func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinI } // Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid types.UUID) error { +func (d *driver) Leave(nid, eid string) error { network, err := d.getNetwork(nid) if err != nil { return err @@ -1282,7 +1282,7 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options if endpoint.config != nil && endpoint.config.ExposedPorts != nil { for _, p := range cc.ParentEndpoints { var parentEndpoint *bridgeEndpoint - parentEndpoint, err = network.getEndpoint(types.UUID(p)) + parentEndpoint, err = network.getEndpoint(p) if err != nil { return err } @@ -1312,7 +1312,7 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options for _, c := range cc.ChildEndpoints { var childEndpoint *bridgeEndpoint - childEndpoint, err = network.getEndpoint(types.UUID(c)) + childEndpoint, err = network.getEndpoint(c) if err != nil { return err } diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index 78eed8291c..60f025700d 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -176,7 +176,7 @@ func TestCreateMultipleNetworks(t *testing.T) { verifyV4INCEntries(dd.networks, 0, t) } -func verifyV4INCEntries(networks map[types.UUID]*bridgeNetwork, numEntries int, t *testing.T) { +func verifyV4INCEntries(networks map[string]*bridgeNetwork, numEntries int, t *testing.T) { out, err := iptables.Raw("-L", "FORWARD") if err != nil { t.Fatal(err) @@ -268,16 +268,6 @@ func (te *testEndpoint) SetGatewayIPv6(gw6 net.IP) error { return nil } -func (te *testEndpoint) SetHostsPath(path string) error { - te.hostsPath = path - return nil -} - -func (te *testEndpoint) SetResolvConfPath(path string) error { - te.resolvConfPath = path - return nil -} - func (te *testEndpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP, interfaceID int) error { te.routes = append(te.routes, types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop, InterfaceID: interfaceID}) return nil diff --git a/libnetwork/drivers/host/host.go b/libnetwork/drivers/host/host.go index 5dbc4ef2fb..2c9d172d9f 100644 --- a/libnetwork/drivers/host/host.go +++ b/libnetwork/drivers/host/host.go @@ -10,7 +10,7 @@ import ( const networkType = "host" type driver struct { - network types.UUID + network string sync.Mutex } @@ -26,7 +26,7 @@ func (d *driver) Config(option map[string]interface{}) error { return nil } -func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { d.Lock() defer d.Unlock() @@ -39,33 +39,29 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err return nil } -func (d *driver) DeleteNetwork(nid types.UUID) error { +func (d *driver) DeleteNetwork(nid string) error { return types.ForbiddenErrorf("network of type \"%s\" cannot be deleted", networkType) } -func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { +func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { return nil } -func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { +func (d *driver) DeleteEndpoint(nid, eid string) error { return nil } -func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { return make(map[string]interface{}, 0), nil } // Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { - if err := jinfo.SetHostsPath("/etc/hosts"); err != nil { - return err - } - - return jinfo.SetResolvConfPath("/etc/resolv.conf") +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { + return nil } // Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid types.UUID) error { +func (d *driver) Leave(nid, eid string) error { return nil } diff --git a/libnetwork/drivers/null/null.go b/libnetwork/drivers/null/null.go index d1f2797e5d..e0bf6b15ca 100644 --- a/libnetwork/drivers/null/null.go +++ b/libnetwork/drivers/null/null.go @@ -10,7 +10,7 @@ import ( const networkType = "null" type driver struct { - network types.UUID + network string sync.Mutex } @@ -26,7 +26,7 @@ func (d *driver) Config(option map[string]interface{}) error { return nil } -func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { d.Lock() defer d.Unlock() @@ -39,29 +39,29 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err return nil } -func (d *driver) DeleteNetwork(nid types.UUID) error { +func (d *driver) DeleteNetwork(nid string) error { return types.ForbiddenErrorf("network of type \"%s\" cannot be deleted", networkType) } -func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { +func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { return nil } -func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { +func (d *driver) DeleteEndpoint(nid, eid string) error { return nil } -func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { return make(map[string]interface{}, 0), nil } // Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { return nil } // Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid types.UUID) error { +func (d *driver) Leave(nid, eid string) error { return nil } diff --git a/libnetwork/drivers/overlay/joinleave.go b/libnetwork/drivers/overlay/joinleave.go index 474970bcf5..0d428b2b04 100644 --- a/libnetwork/drivers/overlay/joinleave.go +++ b/libnetwork/drivers/overlay/joinleave.go @@ -4,12 +4,11 @@ import ( "fmt" "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/types" "github.com/vishvananda/netlink" ) // Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { if err := validateID(nid, eid); err != nil { return err } @@ -77,7 +76,7 @@ func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinI } // Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid types.UUID) error { +func (d *driver) Leave(nid, eid string) error { if err := validateID(nid, eid); err != nil { return err } diff --git a/libnetwork/drivers/overlay/ov_endpoint.go b/libnetwork/drivers/overlay/ov_endpoint.go index 7256b66e2f..ed9658e3c8 100644 --- a/libnetwork/drivers/overlay/ov_endpoint.go +++ b/libnetwork/drivers/overlay/ov_endpoint.go @@ -7,18 +7,17 @@ import ( "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/types" ) -type endpointTable map[types.UUID]*endpoint +type endpointTable map[string]*endpoint type endpoint struct { - id types.UUID + id string mac net.HardwareAddr addr *net.IPNet } -func (n *network) endpoint(eid types.UUID) *endpoint { +func (n *network) endpoint(eid string) *endpoint { n.Lock() defer n.Unlock() @@ -31,13 +30,13 @@ func (n *network) addEndpoint(ep *endpoint) { n.Unlock() } -func (n *network) deleteEndpoint(eid types.UUID) { +func (n *network) deleteEndpoint(eid string) { n.Lock() delete(n.endpoints, eid) n.Unlock() } -func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, +func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { if err := validateID(nid, eid); err != nil { return err @@ -85,7 +84,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn return nil } -func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { +func (d *driver) DeleteEndpoint(nid, eid string) error { if err := validateID(nid, eid); err != nil { return err } @@ -105,6 +104,6 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { return nil } -func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { return make(map[string]interface{}, 0), nil } diff --git a/libnetwork/drivers/overlay/ov_network.go b/libnetwork/drivers/overlay/ov_network.go index 21009e1685..e55cc2e8ef 100644 --- a/libnetwork/drivers/overlay/ov_network.go +++ b/libnetwork/drivers/overlay/ov_network.go @@ -10,20 +10,19 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/ipallocator" - "github.com/docker/libnetwork/sandbox" - "github.com/docker/libnetwork/types" + "github.com/docker/libnetwork/osl" "github.com/vishvananda/netlink" "github.com/vishvananda/netlink/nl" ) -type networkTable map[types.UUID]*network +type networkTable map[string]*network type network struct { - id types.UUID + id string vni uint32 dbIndex uint64 dbExists bool - sbox sandbox.Sandbox + sbox osl.Sandbox endpoints endpointTable ipAllocator *ipallocator.IPAllocator gw net.IP @@ -36,7 +35,7 @@ type network struct { sync.Mutex } -func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { if id == "" { return fmt.Errorf("invalid network id") } @@ -59,7 +58,7 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err return nil } -func (d *driver) DeleteNetwork(nid types.UUID) error { +func (d *driver) DeleteNetwork(nid string) error { if nid == "" { return fmt.Errorf("invalid network id") } @@ -140,8 +139,8 @@ func (n *network) initSandbox() error { n.initEpoch++ n.Unlock() - sbox, err := sandbox.NewSandbox( - sandbox.GenerateKey(fmt.Sprintf("%d-", n.initEpoch)+string(n.id)), true) + sbox, err := osl.NewSandbox( + osl.GenerateKey(fmt.Sprintf("%d-", n.initEpoch)+n.id), true) if err != nil { return fmt.Errorf("could not create network sandbox: %v", err) } @@ -216,7 +215,7 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket) { continue } - if err := n.driver.peerAdd(n.id, types.UUID("dummy"), neigh.IP, mac, vtep, true); err != nil { + if err := n.driver.peerAdd(n.id, "dummy", neigh.IP, mac, vtep, true); err != nil { logrus.Errorf("could not add neighbor entry for missed peer: %v", err) } } @@ -229,27 +228,27 @@ func (d *driver) addNetwork(n *network) { d.Unlock() } -func (d *driver) deleteNetwork(nid types.UUID) { +func (d *driver) deleteNetwork(nid string) { d.Lock() delete(d.networks, nid) d.Unlock() } -func (d *driver) network(nid types.UUID) *network { +func (d *driver) network(nid string) *network { d.Lock() defer d.Unlock() return d.networks[nid] } -func (n *network) sandbox() sandbox.Sandbox { +func (n *network) sandbox() osl.Sandbox { n.Lock() defer n.Unlock() return n.sbox } -func (n *network) setSandbox(sbox sandbox.Sandbox) { +func (n *network) setSandbox(sbox osl.Sandbox) { n.Lock() n.sbox = sbox n.Unlock() @@ -269,7 +268,7 @@ func (n *network) setVxlanID(vni uint32) { } func (n *network) Key() []string { - return []string{"overlay", "network", string(n.id)} + return []string{"overlay", "network", n.id} } func (n *network) KeyPrefix() []string { diff --git a/libnetwork/drivers/overlay/ov_serf.go b/libnetwork/drivers/overlay/ov_serf.go index 5f467d19fa..453ef2c912 100644 --- a/libnetwork/drivers/overlay/ov_serf.go +++ b/libnetwork/drivers/overlay/ov_serf.go @@ -7,14 +7,13 @@ import ( "time" "github.com/Sirupsen/logrus" - "github.com/docker/libnetwork/types" "github.com/hashicorp/serf/serf" ) type ovNotify struct { action string - eid types.UUID - nid types.UUID + eid string + nid string } type logWriter struct{} @@ -150,12 +149,12 @@ func (d *driver) processEvent(u serf.UserEvent) { switch action { case "join": - if err := d.peerAdd(types.UUID(nid), types.UUID(eid), net.ParseIP(ipStr), mac, + if err := d.peerAdd(nid, eid, net.ParseIP(ipStr), mac, net.ParseIP(vtepStr), true); err != nil { fmt.Printf("Peer add failed in the driver: %v\n", err) } case "leave": - if err := d.peerDelete(types.UUID(nid), types.UUID(eid), net.ParseIP(ipStr), mac, + if err := d.peerDelete(nid, eid, net.ParseIP(ipStr), mac, net.ParseIP(vtepStr), true); err != nil { fmt.Printf("Peer delete failed in the driver: %v\n", err) } @@ -171,7 +170,7 @@ func (d *driver) processQuery(q *serf.Query) { fmt.Printf("Failed to scan query payload string: %v\n", err) } - peerMac, vtep, err := d.peerDbSearch(types.UUID(nid), net.ParseIP(ipStr)) + peerMac, vtep, err := d.peerDbSearch(nid, net.ParseIP(ipStr)) if err != nil { return } @@ -179,7 +178,7 @@ func (d *driver) processQuery(q *serf.Query) { q.Respond([]byte(fmt.Sprintf("%s %s", peerMac.String(), vtep.String()))) } -func (d *driver) resolvePeer(nid types.UUID, peerIP net.IP) (net.HardwareAddr, net.IP, error) { +func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.IP, error) { qPayload := fmt.Sprintf("%s %s", string(nid), peerIP.String()) resp, err := d.serfInstance.Query("peerlookup", []byte(qPayload), nil) if err != nil { diff --git a/libnetwork/drivers/overlay/ov_utils.go b/libnetwork/drivers/overlay/ov_utils.go index 384ec468e1..d5a1a0b981 100644 --- a/libnetwork/drivers/overlay/ov_utils.go +++ b/libnetwork/drivers/overlay/ov_utils.go @@ -4,12 +4,11 @@ import ( "fmt" "github.com/docker/libnetwork/netutils" - "github.com/docker/libnetwork/types" "github.com/vishvananda/netlink" "github.com/vishvananda/netlink/nl" ) -func validateID(nid, eid types.UUID) error { +func validateID(nid, eid string) error { if nid == "" { return fmt.Errorf("invalid network id") } diff --git a/libnetwork/drivers/overlay/overlay.go b/libnetwork/drivers/overlay/overlay.go index c4fcaa3797..9bd63f558f 100644 --- a/libnetwork/drivers/overlay/overlay.go +++ b/libnetwork/drivers/overlay/overlay.go @@ -11,7 +11,6 @@ import ( "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/idm" "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/types" "github.com/hashicorp/serf/serf" ) @@ -77,7 +76,7 @@ func Init(dc driverapi.DriverCallback) error { return dc.RegisterDriver(networkType, &driver{ networks: networkTable{}, peerDb: peerNetworkMap{ - mp: map[types.UUID]peerMap{}, + mp: map[string]peerMap{}, }, }, c) } diff --git a/libnetwork/drivers/overlay/peerdb.go b/libnetwork/drivers/overlay/peerdb.go index 9e4f7f7e12..acb99dc688 100644 --- a/libnetwork/drivers/overlay/peerdb.go +++ b/libnetwork/drivers/overlay/peerdb.go @@ -5,8 +5,6 @@ import ( "net" "sync" "syscall" - - "github.com/docker/libnetwork/types" ) type peerKey struct { @@ -15,7 +13,7 @@ type peerKey struct { } type peerEntry struct { - eid types.UUID + eid string vtep net.IP inSandbox bool isLocal bool @@ -27,7 +25,7 @@ type peerMap struct { } type peerNetworkMap struct { - mp map[types.UUID]peerMap + mp map[string]peerMap sync.Mutex } @@ -58,7 +56,7 @@ func (pKey *peerKey) Scan(state fmt.ScanState, verb rune) error { var peerDbWg sync.WaitGroup -func (d *driver) peerDbWalk(nid types.UUID, f func(*peerKey, *peerEntry) bool) error { +func (d *driver) peerDbWalk(nid string, f func(*peerKey, *peerEntry) bool) error { d.peerDb.Lock() pMap, ok := d.peerDb.mp[nid] if !ok { @@ -84,7 +82,7 @@ func (d *driver) peerDbWalk(nid types.UUID, f func(*peerKey, *peerEntry) bool) e return nil } -func (d *driver) peerDbSearch(nid types.UUID, peerIP net.IP) (net.HardwareAddr, net.IP, error) { +func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IP, error) { var ( peerMac net.HardwareAddr vtep net.IP @@ -113,7 +111,7 @@ func (d *driver) peerDbSearch(nid types.UUID, peerIP net.IP) (net.HardwareAddr, return peerMac, vtep, nil } -func (d *driver) peerDbAdd(nid, eid types.UUID, peerIP net.IP, +func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerMac net.HardwareAddr, vtep net.IP, isLocal bool) { peerDbWg.Wait() @@ -145,7 +143,7 @@ func (d *driver) peerDbAdd(nid, eid types.UUID, peerIP net.IP, pMap.Unlock() } -func (d *driver) peerDbDelete(nid, eid types.UUID, peerIP net.IP, +func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerMac net.HardwareAddr, vtep net.IP) { peerDbWg.Wait() @@ -167,7 +165,7 @@ func (d *driver) peerDbDelete(nid, eid types.UUID, peerIP net.IP, pMap.Unlock() } -func (d *driver) peerDbUpdateSandbox(nid types.UUID) { +func (d *driver) peerDbUpdateSandbox(nid string) { d.peerDb.Lock() pMap, ok := d.peerDb.mp[nid] if !ok { @@ -214,7 +212,7 @@ func (d *driver) peerDbUpdateSandbox(nid types.UUID) { peerDbWg.Done() } -func (d *driver) peerAdd(nid, eid types.UUID, peerIP net.IP, +func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error { if err := validateID(nid, eid); err != nil { @@ -249,7 +247,7 @@ func (d *driver) peerAdd(nid, eid types.UUID, peerIP net.IP, return nil } -func (d *driver) peerDelete(nid, eid types.UUID, peerIP net.IP, +func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error { if err := validateID(nid, eid); err != nil { diff --git a/libnetwork/drivers/remote/api/api.go b/libnetwork/drivers/remote/api/api.go index ec703235c6..35ad2bfe15 100644 --- a/libnetwork/drivers/remote/api/api.go +++ b/libnetwork/drivers/remote/api/api.go @@ -127,8 +127,6 @@ type JoinResponse struct { InterfaceNames []*InterfaceName Gateway string GatewayIPv6 string - HostsPath string - ResolvConfPath string StaticRoutes []StaticRoute } diff --git a/libnetwork/drivers/remote/driver.go b/libnetwork/drivers/remote/driver.go index 8cfbe51aa2..88827e5d75 100644 --- a/libnetwork/drivers/remote/driver.go +++ b/libnetwork/drivers/remote/driver.go @@ -57,20 +57,20 @@ func (d *driver) call(methodName string, arg interface{}, retVal maybeError) err return nil } -func (d *driver) CreateNetwork(id types.UUID, options map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, options map[string]interface{}) error { create := &api.CreateNetworkRequest{ - NetworkID: string(id), + NetworkID: id, Options: options, } return d.call("CreateNetwork", create, &api.CreateNetworkResponse{}) } -func (d *driver) DeleteNetwork(nid types.UUID) error { - delete := &api.DeleteNetworkRequest{NetworkID: string(nid)} +func (d *driver) DeleteNetwork(nid string) error { + delete := &api.DeleteNetworkRequest{NetworkID: nid} return d.call("DeleteNetwork", delete, &api.DeleteNetworkResponse{}) } -func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { +func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { if epInfo == nil { return fmt.Errorf("must not be called with nil EndpointInfo") } @@ -87,8 +87,8 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn } } create := &api.CreateEndpointRequest{ - NetworkID: string(nid), - EndpointID: string(eid), + NetworkID: nid, + EndpointID: eid, Interfaces: reqIfaces, Options: epOptions, } @@ -129,18 +129,18 @@ func errorWithRollback(msg string, err error) error { return fmt.Errorf("%s; %s", msg, rollback) } -func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { +func (d *driver) DeleteEndpoint(nid, eid string) error { delete := &api.DeleteEndpointRequest{ - NetworkID: string(nid), - EndpointID: string(eid), + NetworkID: nid, + EndpointID: eid, } return d.call("DeleteEndpoint", delete, &api.DeleteEndpointResponse{}) } -func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { info := &api.EndpointInfoRequest{ - NetworkID: string(nid), - EndpointID: string(eid), + NetworkID: nid, + EndpointID: eid, } var res api.EndpointInfoResponse if err := d.call("EndpointOperInfo", info, &res); err != nil { @@ -150,10 +150,10 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, } // Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { join := &api.JoinRequest{ - NetworkID: string(nid), - EndpointID: string(eid), + NetworkID: nid, + EndpointID: eid, SandboxKey: sboxKey, Options: options, } @@ -209,20 +209,14 @@ func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinI } } } - if jinfo.SetHostsPath(res.HostsPath) != nil { - return errorWithRollback(fmt.Sprintf("failed to set hosts path: %s", res.HostsPath), d.Leave(nid, eid)) - } - if jinfo.SetResolvConfPath(res.ResolvConfPath) != nil { - return errorWithRollback(fmt.Sprintf("failed to set resolv.conf path: %s", res.ResolvConfPath), d.Leave(nid, eid)) - } return nil } // Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid types.UUID) error { +func (d *driver) Leave(nid, eid string) error { leave := &api.LeaveRequest{ - NetworkID: string(nid), - EndpointID: string(eid), + NetworkID: nid, + EndpointID: eid, } return d.call("Leave", leave, &api.LeaveResponse{}) } diff --git a/libnetwork/drivers/remote/driver_test.go b/libnetwork/drivers/remote/driver_test.go index e562e1a035..cc3e14622a 100644 --- a/libnetwork/drivers/remote/driver_test.go +++ b/libnetwork/drivers/remote/driver_test.go @@ -138,20 +138,6 @@ func (test *testEndpoint) SetGatewayIPv6(ipv6 net.IP) error { return nil } -func (test *testEndpoint) SetHostsPath(p string) error { - if p != test.hostsPath { - test.t.Fatalf(`Wrong HostsPath; expected "%s", got "%s"`, test.hostsPath, p) - } - return nil -} - -func (test *testEndpoint) SetResolvConfPath(p string) error { - if p != test.resolvConfPath { - test.t.Fatalf(`Wrong ResolvConfPath; expected "%s", got "%s"`, test.resolvConfPath, p) - } - return nil -} - func (test *testEndpoint) SetNames(src string, dst string) error { if test.src != src { test.t.Fatalf(`Wrong SrcName; expected "%s", got "%s"`, test.src, src) @@ -282,13 +268,13 @@ func TestRemoteDriver(t *testing.T) { t.Fatal("Driver type does not match that given") } - netID := types.UUID("dummy-network") + netID := "dummy-network" err = driver.CreateNetwork(netID, map[string]interface{}{}) if err != nil { t.Fatal(err) } - endID := types.UUID("dummy-endpoint") + endID := "dummy-endpoint" err = driver.CreateEndpoint(netID, endID, ep, map[string]interface{}{}) if err != nil { t.Fatal(err) @@ -345,7 +331,7 @@ func TestDriverError(t *testing.T) { driver := newDriver(plugin, p.Client) - if err := driver.CreateEndpoint(types.UUID("dummy"), types.UUID("dummy"), &testEndpoint{t: t}, map[string]interface{}{}); err == nil { + if err := driver.CreateEndpoint("dummy", "dummy", &testEndpoint{t: t}, map[string]interface{}{}); err == nil { t.Fatalf("Expected error from driver") } } @@ -379,7 +365,7 @@ func TestMissingValues(t *testing.T) { } driver := newDriver(plugin, p.Client) - if err := driver.CreateEndpoint(types.UUID("dummy"), types.UUID("dummy"), ep, map[string]interface{}{}); err != nil { + if err := driver.CreateEndpoint("dummy", "dummy", ep, map[string]interface{}{}); err != nil { t.Fatal(err) } } @@ -427,7 +413,7 @@ func TestRollback(t *testing.T) { ep := &rollbackEndpoint{} - if err := driver.CreateEndpoint(types.UUID("dummy"), types.UUID("dummy"), ep, map[string]interface{}{}); err == nil { + if err := driver.CreateEndpoint("dummy", "dummy", ep, map[string]interface{}{}); err == nil { t.Fatalf("Expected error from driver") } if !rolledback { diff --git a/libnetwork/drivers/windows/windows.go b/libnetwork/drivers/windows/windows.go index 925e402bb0..80b5c467c7 100644 --- a/libnetwork/drivers/windows/windows.go +++ b/libnetwork/drivers/windows/windows.go @@ -1,9 +1,6 @@ package windows -import ( - "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/types" -) +import "github.com/docker/libnetwork/driverapi" const networkType = "windows" @@ -23,33 +20,33 @@ func (d *driver) Config(option map[string]interface{}) error { return nil } -func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { return nil } -func (d *driver) DeleteNetwork(nid types.UUID) error { +func (d *driver) DeleteNetwork(nid string) error { return nil } -func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { +func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { return nil } -func (d *driver) DeleteEndpoint(nid, eid types.UUID) error { +func (d *driver) DeleteEndpoint(nid, eid string) error { return nil } -func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) { +func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) { return make(map[string]interface{}, 0), nil } // Join method is invoked when a Sandbox is attached to an endpoint. -func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { +func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error { return nil } // Leave method is invoked when a Sandbox detaches from an endpoint. -func (d *driver) Leave(nid, eid types.UUID) error { +func (d *driver) Leave(nid, eid string) error { return nil } diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 3475e9eafd..0ec9f1d798 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -1,22 +1,14 @@ package libnetwork import ( - "bytes" "encoding/json" "fmt" - "io/ioutil" - "os" - "path" - "path/filepath" + "net" "sync" log "github.com/Sirupsen/logrus" - "github.com/docker/docker/pkg/ioutils" "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/etchosts" "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/resolvconf" - "github.com/docker/libnetwork/sandbox" "github.com/docker/libnetwork/types" ) @@ -31,14 +23,12 @@ type Endpoint interface { // Network returns the name of the network to which this endpoint is attached. Network() string - // Join creates a new sandbox for the given container ID and populates the - // network resources allocated for the endpoint and joins the sandbox to - // the endpoint. - Join(containerID string, options ...EndpointOption) error + // Join joins the sandbox to the endpoint and populates into the sandbox + // the network resources allocated for the endpoint. + Join(sandbox Sandbox, options ...EndpointOption) error - // Leave removes the sandbox associated with container ID and detaches - // the network resources populated in the sandbox - Leave(containerID string, options ...EndpointOption) error + // Leave detaches the network resources populated in the sandbox. + Leave(sandbox Sandbox, options ...EndpointOption) error // Return certain operational data belonging to this endpoint Info() EndpointInfo @@ -46,14 +36,8 @@ type Endpoint interface { // DriverInfo returns a collection of driver operational data related to this endpoint retrieved from the driver DriverInfo() (map[string]interface{}, error) - // ContainerInfo returns the info available at the endpoint about the attached container - ContainerInfo() ContainerInfo - // Delete and detaches this endpoint from the network. Delete() error - - // Retrieve the interfaces' statistics from the sandbox - Statistics() (map[string]*sandbox.InterfaceStatistics, error) } // EndpointOption is a option setter function type used to pass varios options to Network @@ -61,68 +45,13 @@ type Endpoint interface { // provided by libnetwork, they look like Option[...](...) type EndpointOption func(ep *endpoint) -// ContainerData is a set of data returned when a container joins an endpoint. -type ContainerData struct { - SandboxKey string -} - -// These are the container configs used to customize container /etc/hosts file. -type hostsPathConfig struct { - hostName string - domainName string - hostsPath string - extraHosts []extraHost - parentUpdates []parentUpdate -} - -// These are the container configs used to customize container /etc/resolv.conf file. -type resolvConfPathConfig struct { - resolvConfPath string - dnsList []string - dnsSearchList []string -} - -type containerConfig struct { - hostsPathConfig - resolvConfPathConfig - generic map[string]interface{} - useDefaultSandBox bool - prio int // higher the value, more the priority -} - -type extraHost struct { - name string - IP string -} - -type parentUpdate struct { - eid string - name string - ip string -} - -type containerInfo struct { - id string - config containerConfig - data ContainerData - sync.Mutex -} - -func (ci *containerInfo) ID() string { - return ci.id -} - -func (ci *containerInfo) Labels() map[string]interface{} { - return ci.config.generic -} - type endpoint struct { name string - id types.UUID + id string network *network iFaces []*endpointInterface joinInfo *endpointJoinInfo - container *containerInfo + sandboxID string exposedPorts []types.TransportPort generic map[string]interface{} joinLeaveDone chan struct{} @@ -131,39 +60,17 @@ type endpoint struct { sync.Mutex } -func (ci *containerInfo) MarshalJSON() ([]byte, error) { - ci.Lock() - defer ci.Unlock() - - // We are just interested in the container ID. This can be expanded to include all of containerInfo if there is a need - return json.Marshal(ci.id) -} - -func (ci *containerInfo) UnmarshalJSON(b []byte) (err error) { - ci.Lock() - defer ci.Unlock() - - var id string - if err := json.Unmarshal(b, &id); err != nil { - return err - } - ci.id = id - return nil -} - func (ep *endpoint) MarshalJSON() ([]byte, error) { ep.Lock() defer ep.Unlock() epMap := make(map[string]interface{}) epMap["name"] = ep.name - epMap["id"] = string(ep.id) + epMap["id"] = ep.id epMap["ep_iface"] = ep.iFaces epMap["exposed_ports"] = ep.exposedPorts epMap["generic"] = ep.generic - if ep.container != nil { - epMap["container"] = ep.container - } + epMap["sandbox"] = ep.sandboxID return json.Marshal(epMap) } @@ -176,7 +83,7 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) { return err } ep.name = epMap["name"].(string) - ep.id = types.UUID(epMap["id"].(string)) + ep.id = epMap["id"].(string) ib, _ := json.Marshal(epMap["ep_iface"]) var ifaces []endpointInterface @@ -191,13 +98,8 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) { json.Unmarshal(tb, &tPorts) ep.exposedPorts = tPorts - epc, ok := epMap["container"] - if ok { - cb, _ := json.Marshal(epc) - var cInfo containerInfo - json.Unmarshal(cb, &cInfo) - ep.container = &cInfo - } + cb, _ := json.Marshal(epMap["sandbox"]) + json.Unmarshal(cb, &ep.sandboxID) if epMap["generic"] != nil { ep.generic = epMap["generic"].(map[string]interface{}) @@ -205,13 +107,11 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) { return nil } -const defaultPrefix = "/var/lib/docker/network/files" - func (ep *endpoint) ID() string { ep.Lock() defer ep.Unlock() - return string(ep.id) + return ep.id } func (ep *endpoint) Name() string { @@ -222,36 +122,27 @@ func (ep *endpoint) Name() string { } func (ep *endpoint) Network() string { - ep.Lock() - defer ep.Unlock() - - return ep.network.name + return ep.getNetwork().name } // endpoint Key structure : endpoint/network-id/endpoint-id func (ep *endpoint) Key() []string { - ep.Lock() - n := ep.network - defer ep.Unlock() - return []string{datastore.EndpointKeyPrefix, string(n.id), string(ep.id)} + return []string{datastore.EndpointKeyPrefix, ep.getNetwork().id, ep.id} } func (ep *endpoint) KeyPrefix() []string { - ep.Lock() - n := ep.network - defer ep.Unlock() - return []string{datastore.EndpointKeyPrefix, string(n.id)} + return []string{datastore.EndpointKeyPrefix, ep.getNetwork().id} } -func (ep *endpoint) networkIDFromKey(key []string) (types.UUID, error) { +func (ep *endpoint) networkIDFromKey(key []string) (string, error) { // endpoint Key structure : endpoint/network-id/endpoint-id // it's an invalid key if the key doesn't have all the 3 key elements above if key == nil || len(key) < 3 || key[0] != datastore.EndpointKeyPrefix { - return types.UUID(""), fmt.Errorf("invalid endpoint key : %v", key) + return "", fmt.Errorf("invalid endpoint key : %v", key) } // network-id is placed at index=1. pls refer to endpoint.Key() method - return types.UUID(key[1]), nil + return key[1], nil } func (ep *endpoint) Value() []byte { @@ -296,27 +187,6 @@ func (ep *endpoint) processOptions(options ...EndpointOption) { } } -func createBasePath(dir string) error { - return os.MkdirAll(dir, 0644) -} - -func createFile(path string) error { - var f *os.File - - dir, _ := filepath.Split(path) - err := createBasePath(dir) - if err != nil { - return err - } - - f, err = os.Create(path) - if err == nil { - f.Close() - } - - return err -} - // joinLeaveStart waits to ensure there are no joins or leaves in progress and // marks this join/leave in progress without race func (ep *endpoint) joinLeaveStart() { @@ -349,44 +219,36 @@ func (ep *endpoint) joinLeaveEnd() { } } -func (ep *endpoint) Join(containerID string, options ...EndpointOption) error { +func (ep *endpoint) Join(sbox Sandbox, options ...EndpointOption) error { var err error - if containerID == "" { - return InvalidContainerIDError(containerID) + if sbox == nil { + return types.BadRequestErrorf("endpoint cannot be joined by nil container") + } + + sb, ok := sbox.(*sandbox) + if !ok { + return types.BadRequestErrorf("not a valid Sandbox interface") } ep.joinLeaveStart() - defer func() { - ep.joinLeaveEnd() - }() + defer ep.joinLeaveEnd() ep.Lock() - if ep.container != nil { + if ep.sandboxID != "" { ep.Unlock() - return ErrInvalidJoin{} + return types.ForbiddenErrorf("a sandbox has already joined the endpoint") } - ep.container = &containerInfo{ - id: containerID, - config: containerConfig{ - hostsPathConfig: hostsPathConfig{ - extraHosts: []extraHost{}, - parentUpdates: []parentUpdate{}, - }, - }} - + ep.sandboxID = sbox.ID() ep.joinInfo = &endpointJoinInfo{} - - container := ep.container network := ep.network epid := ep.id - ep.Unlock() defer func() { if err != nil { ep.Lock() - ep.container = nil + ep.sandboxID = "" ep.Unlock() } }() @@ -394,17 +256,11 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) error { network.Lock() driver := network.driver nid := network.id - ctrlr := network.ctrlr network.Unlock() ep.processOptions(options...) - sboxKey := sandbox.GenerateKey(containerID) - if container.config.useDefaultSandBox { - sboxKey = sandbox.GenerateKey("default") - } - - err = driver.Join(nid, epid, sboxKey, ep, container.config.generic) + err = driver.Join(nid, epid, sbox.Key(), ep, sbox.Labels()) if err != nil { return err } @@ -417,36 +273,26 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) error { } }() - err = ep.buildHostsFiles() - if err != nil { + address := "" + if ip := ep.getFirstInterfaceAddress(); ip != nil { + address = ip.String() + } + if err = sb.updateHostsFile(address, network.getSvcRecords()); err != nil { return err } - err = ep.updateParentHosts() - if err != nil { + if err = sb.updateDNS(ep.getNetwork().enableIPv6); err != nil { return err } - err = ep.setupDNS() - if err != nil { + if err = network.ctrlr.updateEndpointToStore(ep); err != nil { return err } - sb, err := ctrlr.sandboxAdd(sboxKey, !container.config.useDefaultSandBox, ep) - if err != nil { - return fmt.Errorf("failed sandbox add: %v", err) - } - defer func() { - if err != nil { - ctrlr.sandboxRm(sboxKey, ep) - } - }() - - if err := network.ctrlr.updateEndpointToStore(ep); err != nil { + if err = sb.populateNetworkResources(ep); err != nil { return err } - container.data.SandboxKey = sb.Key() return nil } @@ -463,49 +309,54 @@ func (ep *endpoint) hasInterface(iName string) bool { return false } -func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error { - var err error - +func (ep *endpoint) Leave(sbox Sandbox, options ...EndpointOption) error { ep.joinLeaveStart() defer ep.joinLeaveEnd() + if sbox == nil || sbox.ID() == "" || sbox.Key() == "" { + return types.BadRequestErrorf("invalid Sandbox passed to enpoint leave: %v", sbox) + } + + sb, ok := sbox.(*sandbox) + if !ok { + return types.BadRequestErrorf("not a valid Sandbox interface") + } + + ep.Lock() + sid := ep.sandboxID + ep.Unlock() + + if sid == "" { + return types.ForbiddenErrorf("cannot leave endpoint with no attached sandbox") + } + if sid != sbox.ID() { + return types.ForbiddenErrorf("unexpected sandbox ID in leave request. Expected %s. Got %s", ep.sandboxID, sbox.ID()) + } + ep.processOptions(options...) ep.Lock() - container := ep.container + ep.sandboxID = "" n := ep.network - - if container == nil || container.id == "" || container.data.SandboxKey == "" || - containerID == "" || container.id != containerID { - if container == nil { - err = ErrNoContainer{} - } else { - err = InvalidContainerIDError(containerID) - } - - ep.Unlock() - return err - } - ep.container = nil ep.Unlock() n.Lock() - driver := n.driver - ctrlr := n.ctrlr + c := n.ctrlr + d := n.driver n.Unlock() - if err := ctrlr.updateEndpointToStore(ep); err != nil { + if err := c.updateEndpointToStore(ep); err != nil { ep.Lock() - ep.container = container + ep.sandboxID = sid ep.Unlock() return err } - err = driver.Leave(n.id, ep.id) + if err := d.Leave(n.id, ep.id); err != nil { + return err + } - ctrlr.sandboxRm(container.data.SandboxKey, ep) - - return err + return sb.clearNetworkResources(ep) } func (ep *endpoint) Delete() error { @@ -514,9 +365,9 @@ func (ep *endpoint) Delete() error { epid := ep.id name := ep.name n := ep.network - if ep.container != nil { + if ep.sandboxID != "" { ep.Unlock() - return &ActiveContainerError{name: name, id: string(epid)} + return &ActiveContainerError{name: name, id: epid} } n.Lock() ctrlr := n.ctrlr @@ -556,33 +407,6 @@ func (ep *endpoint) Delete() error { return nil } -func (ep *endpoint) Statistics() (map[string]*sandbox.InterfaceStatistics, error) { - m := make(map[string]*sandbox.InterfaceStatistics) - - ep.Lock() - n := ep.network - skey := ep.container.data.SandboxKey - ep.Unlock() - - n.Lock() - c := n.ctrlr - n.Unlock() - - sbox := c.sandboxGet(skey) - if sbox == nil { - return m, nil - } - - var err error - for _, i := range sbox.Info().Interfaces() { - if m[i.DstName()], err = i.Statistics(); err != nil { - return m, err - } - } - - return m, nil -} - func (ep *endpoint) deleteEndpoint() error { ep.Lock() n := ep.network @@ -616,260 +440,36 @@ func (ep *endpoint) deleteEndpoint() error { return nil } -func (ep *endpoint) addHostEntries(recs []etchosts.Record) { +func (ep *endpoint) getNetwork() *network { ep.Lock() - container := ep.container - ep.Unlock() - - if container == nil { - return - } - - if err := etchosts.Add(container.config.hostsPath, recs); err != nil { - log.Warnf("Failed adding service host entries to the running container: %v", err) - } + defer ep.Unlock() + return ep.network } -func (ep *endpoint) deleteHostEntries(recs []etchosts.Record) { +func (ep *endpoint) getSandbox() (*sandbox, bool) { ep.Lock() - container := ep.container + c := ep.network.getController() + sid := ep.sandboxID ep.Unlock() - if container == nil { - return - } + c.Lock() + ps, ok := c.sandboxes[sid] + c.Unlock() - if err := etchosts.Delete(container.config.hostsPath, recs); err != nil { - log.Warnf("Failed deleting service host entries to the running container: %v", err) - } + return ps, ok } -func (ep *endpoint) buildHostsFiles() error { - var extraContent []etchosts.Record - +func (ep *endpoint) getFirstInterfaceAddress() net.IP { ep.Lock() - container := ep.container - joinInfo := ep.joinInfo - ifaces := ep.iFaces - n := ep.network - ep.Unlock() + defer ep.Unlock() - if container == nil { - return ErrNoContainer{} - } - - if container.config.hostsPath == "" { - container.config.hostsPath = defaultPrefix + "/" + container.id + "/hosts" - } - - dir, _ := filepath.Split(container.config.hostsPath) - err := createBasePath(dir) - if err != nil { - return err - } - - if joinInfo != nil && joinInfo.hostsPath != "" { - content, err := ioutil.ReadFile(joinInfo.hostsPath) - if err != nil && !os.IsNotExist(err) { - return err - } - - if err == nil { - return ioutil.WriteFile(container.config.hostsPath, content, 0644) - } - } - - for _, extraHost := range container.config.extraHosts { - extraContent = append(extraContent, - etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP}) - } - - extraContent = append(extraContent, n.getSvcRecords()...) - - IP := "" - if len(ifaces) != 0 && ifaces[0] != nil { - IP = ifaces[0].addr.IP.String() - } - - return etchosts.Build(container.config.hostsPath, IP, container.config.hostName, - container.config.domainName, extraContent) -} - -func (ep *endpoint) updateParentHosts() error { - ep.Lock() - container := ep.container - network := ep.network - ep.Unlock() - - if container == nil { - return ErrNoContainer{} - } - - for _, update := range container.config.parentUpdates { - network.Lock() - pep, ok := network.endpoints[types.UUID(update.eid)] - if !ok { - network.Unlock() - continue - } - network.Unlock() - - pep.Lock() - pContainer := pep.container - pep.Unlock() - - if pContainer != nil { - if err := etchosts.Update(pContainer.config.hostsPath, update.ip, update.name); err != nil { - return err - } - } + if len(ep.iFaces) != 0 && ep.iFaces[0] != nil { + return ep.iFaces[0].addr.IP } return nil } -func (ep *endpoint) updateDNS(resolvConf []byte) error { - ep.Lock() - container := ep.container - network := ep.network - ep.Unlock() - - if container == nil { - return ErrNoContainer{} - } - - oldHash := []byte{} - hashFile := container.config.resolvConfPath + ".hash" - - resolvBytes, err := ioutil.ReadFile(container.config.resolvConfPath) - if err != nil { - if !os.IsNotExist(err) { - return err - } - } else { - oldHash, err = ioutil.ReadFile(hashFile) - if err != nil { - if !os.IsNotExist(err) { - return err - } - - oldHash = []byte{} - } - } - - curHash, err := ioutils.HashData(bytes.NewReader(resolvBytes)) - if err != nil { - return err - } - - if string(oldHash) != "" && curHash != string(oldHash) { - // Seems the user has changed the container resolv.conf since the last time - // we checked so return without doing anything. - return nil - } - - // replace any localhost/127.* and remove IPv6 nameservers if IPv6 disabled. - resolvConf, _ = resolvconf.FilterResolvDNS(resolvConf, network.enableIPv6) - - newHash, err := ioutils.HashData(bytes.NewReader(resolvConf)) - if err != nil { - return err - } - - // for atomic updates to these files, use temporary files with os.Rename: - dir := path.Dir(container.config.resolvConfPath) - tmpHashFile, err := ioutil.TempFile(dir, "hash") - if err != nil { - return err - } - tmpResolvFile, err := ioutil.TempFile(dir, "resolv") - if err != nil { - return err - } - - // Change the perms to 0644 since ioutil.TempFile creates it by default as 0600 - if err := os.Chmod(tmpResolvFile.Name(), 0644); err != nil { - return err - } - - // write the updates to the temp files - if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newHash), 0644); err != nil { - return err - } - if err = ioutil.WriteFile(tmpResolvFile.Name(), resolvConf, 0644); err != nil { - return err - } - - // rename the temp files for atomic replace - if err = os.Rename(tmpHashFile.Name(), hashFile); err != nil { - return err - } - return os.Rename(tmpResolvFile.Name(), container.config.resolvConfPath) -} - -func copyFile(src, dst string) error { - sBytes, err := ioutil.ReadFile(src) - if err != nil { - return err - } - - return ioutil.WriteFile(dst, sBytes, 0644) -} - -func (ep *endpoint) setupDNS() error { - ep.Lock() - container := ep.container - joinInfo := ep.joinInfo - ep.Unlock() - - if container == nil { - return ErrNoContainer{} - } - - if container.config.resolvConfPath == "" { - container.config.resolvConfPath = defaultPrefix + "/" + container.id + "/resolv.conf" - } - - dir, _ := filepath.Split(container.config.resolvConfPath) - err := createBasePath(dir) - if err != nil { - return err - } - - if joinInfo.resolvConfPath != "" { - if err := copyFile(joinInfo.resolvConfPath, container.config.resolvConfPath); err != nil { - return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", joinInfo.resolvConfPath, container.config.resolvConfPath, err) - } - - return nil - } - - resolvConf, err := resolvconf.Get() - if err != nil { - return err - } - - if len(container.config.dnsList) > 0 || - len(container.config.dnsSearchList) > 0 { - var ( - dnsList = resolvconf.GetNameservers(resolvConf) - dnsSearchList = resolvconf.GetSearchDomains(resolvConf) - ) - - if len(container.config.dnsList) > 0 { - dnsList = container.config.dnsList - } - - if len(container.config.dnsSearchList) > 0 { - dnsSearchList = container.config.dnsSearchList - } - - return resolvconf.Build(container.config.resolvConfPath, dnsList, dnsSearchList) - } - - return ep.updateDNS(resolvConf) -} - // EndpointOptionGeneric function returns an option setter for a Generic option defined // in a Dictionary of Key-Value pair func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption { @@ -880,86 +480,6 @@ func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption { } } -// JoinOptionPriority function returns an option setter for priority option to -// be passed to endpoint Join method. -func JoinOptionPriority(prio int) EndpointOption { - return func(ep *endpoint) { - ep.container.config.prio = prio - } -} - -// JoinOptionHostname function returns an option setter for hostname option to -// be passed to endpoint Join method. -func JoinOptionHostname(name string) EndpointOption { - return func(ep *endpoint) { - ep.container.config.hostName = name - } -} - -// JoinOptionDomainname function returns an option setter for domainname option to -// be passed to endpoint Join method. -func JoinOptionDomainname(name string) EndpointOption { - return func(ep *endpoint) { - ep.container.config.domainName = name - } -} - -// JoinOptionHostsPath function returns an option setter for hostspath option to -// be passed to endpoint Join method. -func JoinOptionHostsPath(path string) EndpointOption { - return func(ep *endpoint) { - ep.container.config.hostsPath = path - } -} - -// JoinOptionExtraHost function returns an option setter for extra /etc/hosts options -// which is a name and IP as strings. -func JoinOptionExtraHost(name string, IP string) EndpointOption { - return func(ep *endpoint) { - ep.container.config.extraHosts = append(ep.container.config.extraHosts, extraHost{name: name, IP: IP}) - } -} - -// JoinOptionParentUpdate function returns an option setter for parent container -// which needs to update the IP address for the linked container. -func JoinOptionParentUpdate(eid string, name, ip string) EndpointOption { - return func(ep *endpoint) { - ep.container.config.parentUpdates = append(ep.container.config.parentUpdates, parentUpdate{eid: eid, name: name, ip: ip}) - } -} - -// JoinOptionResolvConfPath function returns an option setter for resolvconfpath option to -// be passed to endpoint Join method. -func JoinOptionResolvConfPath(path string) EndpointOption { - return func(ep *endpoint) { - ep.container.config.resolvConfPath = path - } -} - -// JoinOptionDNS function returns an option setter for dns entry option to -// be passed to endpoint Join method. -func JoinOptionDNS(dns string) EndpointOption { - return func(ep *endpoint) { - ep.container.config.dnsList = append(ep.container.config.dnsList, dns) - } -} - -// JoinOptionDNSSearch function returns an option setter for dns search entry option to -// be passed to endpoint Join method. -func JoinOptionDNSSearch(search string) EndpointOption { - return func(ep *endpoint) { - ep.container.config.dnsSearchList = append(ep.container.config.dnsSearchList, search) - } -} - -// JoinOptionUseDefaultSandbox function returns an option setter for using default sandbox to -// be passed to endpoint Join method. -func JoinOptionUseDefaultSandbox() EndpointOption { - return func(ep *endpoint) { - ep.container.config.useDefaultSandBox = true - } -} - // CreateOptionExposedPorts function returns an option setter for the container exposed // ports option to be passed to network.CreateEndpoint() method. func CreateOptionExposedPorts(exposedPorts []types.TransportPort) EndpointOption { @@ -984,11 +504,19 @@ func CreateOptionPortMapping(portBindings []types.PortBinding) EndpointOption { } } -// JoinOptionGeneric function returns an option setter for Generic configuration -// that is not managed by libNetwork but can be used by the Drivers during the call to -// endpoint join method. Container Labels are a good example. -func JoinOptionGeneric(generic map[string]interface{}) EndpointOption { +// JoinOptionPriority function returns an option setter for priority option to +// be passed to the endpoint.Join() method. +func JoinOptionPriority(ep Endpoint, prio int) EndpointOption { return func(ep *endpoint) { - ep.container.config.generic = generic + // ep lock already acquired + c := ep.network.getController() + c.Lock() + sb, ok := c.sandboxes[ep.sandboxID] + c.Unlock() + if !ok { + log.Errorf("Could not set endpoint priority value during Join to endpoint %s: No sandbox id present in endpoint", ep.id) + return + } + sb.epPriority[ep.id] = prio } } diff --git a/libnetwork/endpoint_info.go b/libnetwork/endpoint_info.go index 6c0e117132..d6b528b533 100644 --- a/libnetwork/endpoint_info.go +++ b/libnetwork/endpoint_info.go @@ -22,10 +22,8 @@ type EndpointInfo interface { // This will only return a valid value if a container has joined the endpoint. GatewayIPv6() net.IP - // SandboxKey returns the sanbox key for the container which has joined - // the endpoint. If there is no container joined then this will return an - // empty string. - SandboxKey() string + // Sandbox returns the attached sandbox if there, nil otherwise. + Sandbox() Sandbox } // InterfaceInfo provides an interface to retrieve interface addresses bound to the endpoint. @@ -40,14 +38,6 @@ type InterfaceInfo interface { AddressIPv6() net.IPNet } -// ContainerInfo provides an interface to retrieve the info about the container attached to the endpoint -type ContainerInfo interface { - // ID returns the ID of the container - ID() string - // Labels returns the container's labels - Labels() map[string]interface{} -} - type endpointInterface struct { id int mac net.HardwareAddr @@ -115,23 +105,9 @@ func (epi *endpointInterface) UnmarshalJSON(b []byte) (err error) { } type endpointJoinInfo struct { - gw net.IP - gw6 net.IP - hostsPath string - resolvConfPath string - StaticRoutes []*types.StaticRoute -} - -func (ep *endpoint) ContainerInfo() ContainerInfo { - ep.Lock() - ci := ep.container - defer ep.Unlock() - - // Need this since we return the interface - if ci == nil { - return nil - } - return ci + gw net.IP + gw6 net.IP + StaticRoutes []*types.StaticRoute } func (ep *endpoint) Info() EndpointInfo { @@ -257,15 +233,12 @@ func (ep *endpoint) addInterfaceRoute(route *types.StaticRoute) error { route.InterfaceID) } -func (ep *endpoint) SandboxKey() string { - ep.Lock() - defer ep.Unlock() - - if ep.container == nil { - return "" +func (ep *endpoint) Sandbox() Sandbox { + cnt, ok := ep.getSandbox() + if !ok { + return nil } - - return ep.container.data.SandboxKey + return cnt } func (ep *endpoint) Gateway() net.IP { @@ -305,19 +278,3 @@ func (ep *endpoint) SetGatewayIPv6(gw6 net.IP) error { ep.joinInfo.gw6 = types.GetIPCopy(gw6) return nil } - -func (ep *endpoint) SetHostsPath(path string) error { - ep.Lock() - defer ep.Unlock() - - ep.joinInfo.hostsPath = path - return nil -} - -func (ep *endpoint) SetResolvConfPath(path string) error { - ep.Lock() - defer ep.Unlock() - - ep.joinInfo.resolvConfPath = path - return nil -} diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index f2f9407cf9..8fba632523 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -23,6 +23,7 @@ import ( "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/options" + "github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/types" "github.com/vishvananda/netlink" "github.com/vishvananda/netns" @@ -98,6 +99,14 @@ func getPortMapping() []types.PortBinding { } func TestNull(t *testing.T) { + cnt, err := controller.NewSandbox("null_container", + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + if err != nil { + t.Fatal(err) + } + network, err := createTestNetwork("null", "testnull", options.Generic{}) if err != nil { t.Fatal(err) @@ -108,15 +117,12 @@ func TestNull(t *testing.T) { t.Fatal(err) } - err = ep.Join("null_container", - libnetwork.JoinOptionHostname("test"), - libnetwork.JoinOptionDomainname("docker.io"), - libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) + err = ep.Join(cnt) if err != nil { t.Fatal(err) } - err = ep.Leave("null_container") + err = ep.Leave(cnt) if err != nil { t.Fatal(err) } @@ -125,6 +131,10 @@ func TestNull(t *testing.T) { t.Fatal(err) } + if err := cnt.Delete(); err != nil { + t.Fatal(err) + } + // host type is special network. Cannot be removed. err = network.Delete() if err == nil { @@ -136,6 +146,34 @@ func TestNull(t *testing.T) { } func TestHost(t *testing.T) { + sbx1, err := controller.NewSandbox("host_c1", + libnetwork.OptionHostname("test1"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1"), + libnetwork.OptionUseDefaultSandbox()) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sbx1.Delete(); err != nil { + t.Fatal(err) + } + }() + + sbx2, err := controller.NewSandbox("host_c2", + libnetwork.OptionHostname("test2"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1"), + libnetwork.OptionUseDefaultSandbox()) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sbx2.Delete(); err != nil { + t.Fatal(err) + } + }() + network, err := createTestNetwork("host", "testhost", options.Generic{}) if err != nil { t.Fatal(err) @@ -146,12 +184,7 @@ func TestHost(t *testing.T) { t.Fatal(err) } - err = ep1.Join("host_container1", - libnetwork.JoinOptionHostname("test1"), - libnetwork.JoinOptionDomainname("docker.io"), - libnetwork.JoinOptionExtraHost("web", "192.168.0.1"), - libnetwork.JoinOptionUseDefaultSandbox()) - if err != nil { + if err := ep1.Join(sbx1); err != nil { t.Fatal(err) } @@ -160,22 +193,15 @@ func TestHost(t *testing.T) { t.Fatal(err) } - err = ep2.Join("host_container2", - libnetwork.JoinOptionHostname("test2"), - libnetwork.JoinOptionDomainname("docker.io"), - libnetwork.JoinOptionExtraHost("web", "192.168.0.1"), - libnetwork.JoinOptionUseDefaultSandbox()) - if err != nil { + if err := ep2.Join(sbx2); err != nil { t.Fatal(err) } - err = ep1.Leave("host_container1") - if err != nil { + if err := ep1.Leave(sbx1); err != nil { t.Fatal(err) } - err = ep2.Leave("host_container2") - if err != nil { + if err := ep2.Leave(sbx2); err != nil { t.Fatal(err) } @@ -188,22 +214,30 @@ func TestHost(t *testing.T) { } // Try to create another host endpoint and join/leave that. + cnt3, err := controller.NewSandbox("host_c3", + libnetwork.OptionHostname("test3"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1"), + libnetwork.OptionUseDefaultSandbox()) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := cnt3.Delete(); err != nil { + t.Fatal(err) + } + }() + ep3, err := network.CreateEndpoint("testep3") if err != nil { t.Fatal(err) } - err = ep3.Join("host_container3", - libnetwork.JoinOptionHostname("test3"), - libnetwork.JoinOptionDomainname("docker.io"), - libnetwork.JoinOptionExtraHost("web", "192.168.0.1"), - libnetwork.JoinOptionUseDefaultSandbox()) - if err != nil { + if err := ep3.Join(sbx2); err != nil { t.Fatal(err) } - err = ep3.Leave("host_container3") - if err != nil { + if err := ep3.Leave(sbx2); err != nil { t.Fatal(err) } @@ -926,7 +960,7 @@ func TestNetworkQuery(t *testing.T) { } } -const containerID = "valid_container" +const containerID = "valid_c" func checkSandbox(t *testing.T, info libnetwork.EndpointInfo) { origns, err := netns.Get() @@ -935,7 +969,7 @@ func checkSandbox(t *testing.T, info libnetwork.EndpointInfo) { } defer origns.Close() - key := info.SandboxKey() + key := info.Sandbox().Key() f, err := os.OpenFile(key, os.O_RDONLY, 0) if err != nil { t.Fatalf("Failed to open network namespace path %q: %v", key, err) @@ -995,7 +1029,6 @@ func TestEndpointJoin(t *testing.T) { // Validate if ep.Info() only gives me IP address info and not names and gateway during CreateEndpoint() info := ep1.Info() - for _, iface := range info.InterfaceList() { if iface.Address().IP.To4() == nil { t.Fatalf("Invalid IP address returned: %v", iface.Address()) @@ -1006,22 +1039,48 @@ func TestEndpointJoin(t *testing.T) { t.Fatalf("Expected empty gateway for an empty endpoint. Instead found a gateway: %v", info.Gateway()) } - if info.SandboxKey() != "" { - t.Fatalf("Expected an empty sandbox key for an empty endpoint. Instead found a non-empty sandbox key: %s", info.SandboxKey()) + if info.Sandbox() != nil { + t.Fatalf("Expected an empty sandbox key for an empty endpoint. Instead found a non-empty sandbox key: %s", info.Sandbox().Key()) } - defer controller.LeaveAll(containerID) + // test invalid joins + err = ep1.Join(nil) + if err == nil { + t.Fatalf("Expected to fail join with nil Sandbox") + } + if _, ok := err.(types.BadRequestError); !ok { + t.Fatalf("Unexpected error type returned: %T", err) + } - err = ep1.Join(containerID, - libnetwork.JoinOptionHostname("test"), - libnetwork.JoinOptionDomainname("docker.io"), - libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) + fsbx := &fakeSandbox{} + if err = ep1.Join(fsbx); err == nil { + t.Fatalf("Expected to fail join with invalid Sandbox") + } + if _, ok := err.(types.BadRequestError); !ok { + t.Fatalf("Unexpected error type returned: %T", err) + } + + sb, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + if err != nil { + t.Fatal(err) + } + + defer func() { + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep1.Join(sb) runtime.LockOSThread() if err != nil { t.Fatal(err) } defer func() { - err = ep1.Leave(containerID) + err = ep1.Leave(sb) runtime.LockOSThread() if err != nil { t.Fatal(err) @@ -1034,17 +1093,17 @@ func TestEndpointJoin(t *testing.T) { t.Fatalf("Expected a valid gateway for a joined endpoint. Instead found an invalid gateway: %v", info.Gateway()) } - if info.SandboxKey() == "" { + if info.Sandbox() == nil { t.Fatalf("Expected an non-empty sandbox key for a joined endpoint. Instead found a empty sandbox key") } // Check endpoint provided container information - if ep1.ContainerInfo().ID() != containerID { - t.Fatalf("Endpoint ContainerInfo returned unexpected id: %s", ep1.ContainerInfo().ID()) + if ep1.Info().Sandbox().Key() != sb.Key() { + t.Fatalf("Endpoint Info returned unexpected sandbox key: %s", sb.Key()) } // Attempt retrieval of endpoint interfaces statistics - stats, err := ep1.Statistics() + stats, err := sb.Statistics() if err != nil { t.Fatal(err) } @@ -1079,65 +1138,50 @@ func TestEndpointJoin(t *testing.T) { } }() - err = ep2.Join(containerID) + err = ep2.Join(sb) if err != nil { t.Fatal(err) } runtime.LockOSThread() defer func() { - err = ep2.Leave(containerID) + err = ep2.Leave(sb) runtime.LockOSThread() if err != nil { t.Fatal(err) } }() - if ep1.ContainerInfo().ID() != ep2.ContainerInfo().ID() { - t.Fatalf("ep1 and ep2 returned different container info") + if ep1.Info().Sandbox().Key() != ep2.Info().Sandbox().Key() { + t.Fatalf("ep1 and ep2 returned different container sandbox key") } checkSandbox(t, info) - } -func TestEndpointJoinInvalidContainerId(t *testing.T) { - if !netutils.IsRunningInContainer() { - defer netutils.SetupTestNetNS(t)() - } +type fakeSandbox struct{} - n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{ - netlabel.GenericData: options.Generic{ - "BridgeName": "testnetwork", - "AllowNonDefaultBridge": true, - }, - }) - if err != nil { - t.Fatal(err) - } - defer func() { - if err := n.Delete(); err != nil { - t.Fatal(err) - } - }() +func (f *fakeSandbox) ID() string { + return "fake sandbox" +} - ep, err := n.CreateEndpoint("ep1") - if err != nil { - t.Fatal(err) - } - defer func() { - if err := ep.Delete(); err != nil { - t.Fatal(err) - } - }() +func (f *fakeSandbox) ContainerID() string { + return "" +} - err = ep.Join("") - if err == nil { - t.Fatal("Expected to fail join with empty container id string") - } +func (f *fakeSandbox) Key() string { + return "fake key" +} - if _, ok := err.(libnetwork.InvalidContainerIDError); !ok { - t.Fatalf("Failed for unexpected reason: %v", err) - } +func (f *fakeSandbox) Labels() map[string]interface{} { + return nil +} + +func (f *fakeSandbox) Statistics() (map[string]*osl.InterfaceStatistics, error) { + return nil, nil +} + +func (f *fakeSandbox) Delete() error { + return nil } func TestEndpointDeleteWithActiveContainer(t *testing.T) { @@ -1171,18 +1215,23 @@ func TestEndpointDeleteWithActiveContainer(t *testing.T) { } }() - defer controller.LeaveAll(containerID) + cnt, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + defer func() { + if err := cnt.Delete(); err != nil { + t.Fatal(err) + } + }() - err = ep.Join(containerID, - libnetwork.JoinOptionHostname("test"), - libnetwork.JoinOptionDomainname("docker.io"), - libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) + err = ep.Join(cnt) runtime.LockOSThread() if err != nil { t.Fatal(err) } defer func() { - err = ep.Leave(containerID) + err = ep.Leave(cnt) runtime.LockOSThread() if err != nil { t.Fatal(err) @@ -1204,9 +1253,9 @@ func TestEndpointMultipleJoins(t *testing.T) { defer netutils.SetupTestNetNS(t)() } - n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{ + n, err := createTestNetwork(bridgeNetType, "testmultiple", options.Generic{ netlabel.GenericData: options.Generic{ - "BridgeName": "testnetwork", + "BridgeName": "testmultiple", "AllowNonDefaultBridge": true, }, }) @@ -1229,32 +1278,46 @@ func TestEndpointMultipleJoins(t *testing.T) { } }() - defer controller.LeaveAll(containerID) + sbx1, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + defer func() { + if err := sbx1.Delete(); err != nil { + t.Fatal(err) + } + }() - err = ep.Join(containerID, - libnetwork.JoinOptionHostname("test"), - libnetwork.JoinOptionDomainname("docker.io"), - libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) + sbx2, err := controller.NewSandbox("c2") + defer func() { + if err := sbx2.Delete(); err != nil { + t.Fatal(err) + } + runtime.LockOSThread() + }() + + err = ep.Join(sbx1) runtime.LockOSThread() if err != nil { t.Fatal(err) } defer func() { - err = ep.Leave(containerID) + err = ep.Leave(sbx1) runtime.LockOSThread() if err != nil { t.Fatal(err) } }() - err = ep.Join("container2") + err = ep.Join(sbx2) if err == nil { t.Fatal("Expected to fail multiple joins for the same endpoint") } - if _, ok := err.(libnetwork.ErrInvalidJoin); !ok { - t.Fatalf("Failed for unexpected reason: %v", err) + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Failed with unexpected error type: %T. Desc: %s", err, err.Error()) } + } func TestLeaveAll(t *testing.T) { @@ -1272,6 +1335,7 @@ func TestLeaveAll(t *testing.T) { t.Fatal(err) } defer func() { + // If this goes through, it means cnt.Delete() effectively detached from all the endpoints if err := n.Delete(); err != nil { t.Fatal(err) } @@ -1297,32 +1361,30 @@ func TestLeaveAll(t *testing.T) { } }() - err = ep1.Join("leaveall") + cnt, err := controller.NewSandbox("leaveall") + if err != nil { + t.Fatal(err) + } + + err = ep1.Join(cnt) if err != nil { t.Fatalf("Failed to join ep1: %v", err) } runtime.LockOSThread() - err = ep2.Join("leaveall") + err = ep2.Join(cnt) if err != nil { t.Fatalf("Failed to join ep2: %v", err) } runtime.LockOSThread() - err = ep1.Leave("leaveall") - if err != nil { - t.Fatalf("Failed to leave ep1: %v", err) - } - runtime.LockOSThread() - - err = controller.LeaveAll("leaveall") + err = cnt.Delete() if err != nil { t.Fatal(err) } - runtime.LockOSThread() } -func TestEndpointInvalidLeave(t *testing.T) { +func TestontainerInvalidLeave(t *testing.T) { if !netutils.IsRunningInContainer() { defer netutils.SetupTestNetNS(t)() } @@ -1352,51 +1414,40 @@ func TestEndpointInvalidLeave(t *testing.T) { } }() - err = ep.Leave(containerID) - if err == nil { - t.Fatal("Expected to fail leave from an endpoint which has no active join") - } - - if _, ok := err.(libnetwork.InvalidContainerIDError); !ok { - if _, ok := err.(libnetwork.ErrNoContainer); !ok { - t.Fatalf("Failed for unexpected reason: %v", err) - } - } - - defer controller.LeaveAll(containerID) - - err = ep.Join(containerID, - libnetwork.JoinOptionHostname("test"), - libnetwork.JoinOptionDomainname("docker.io"), - libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) - runtime.LockOSThread() + cnt, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) if err != nil { t.Fatal(err) } defer func() { - err = ep.Leave(containerID) - runtime.LockOSThread() - if err != nil { + if err := cnt.Delete(); err != nil { t.Fatal(err) } }() - err = ep.Leave("") + err = ep.Leave(cnt) if err == nil { - t.Fatal("Expected to fail leave with empty container id") + t.Fatal("Expected to fail leave from an endpoint which has no active join") + } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("Failed with unexpected error type: %T. Desc: %s", err, err.Error()) } - if _, ok := err.(libnetwork.InvalidContainerIDError); !ok { - t.Fatalf("Failed for unexpected reason: %v", err) + if err := ep.Leave(nil); err == nil { + t.Fatalf("Expected to fail leave nil Sandbox") + } + if _, ok := err.(types.BadRequestError); !ok { + t.Fatalf("Unexpected error type returned: %T", err) } - err = ep.Leave("container2") - if err == nil { - t.Fatal("Expected to fail leave with wrong container id") + fsbx := &fakeSandbox{} + if err = ep.Leave(fsbx); err == nil { + t.Fatalf("Expected to fail leave with invalid Sandbox") } - - if _, ok := err.(libnetwork.InvalidContainerIDError); !ok { - t.Fatalf("Failed for unexpected reason: %v", err) + if _, ok := err.(types.BadRequestError); !ok { + t.Fatalf("Unexpected error type returned: %T", err) } } @@ -1430,23 +1481,6 @@ func TestEndpointUpdateParent(t *testing.T) { } }() - defer controller.LeaveAll(containerID) - err = ep1.Join(containerID, - libnetwork.JoinOptionHostname("test1"), - libnetwork.JoinOptionDomainname("docker.io"), - libnetwork.JoinOptionExtraHost("web", "192.168.0.1")) - runtime.LockOSThread() - if err != nil { - t.Fatal(err) - } - defer func() { - err = ep1.Leave(containerID) - runtime.LockOSThread() - if err != nil { - t.Fatal(err) - } - }() - ep2, err := n.CreateEndpoint("ep2") if err != nil { t.Fatal(err) @@ -1457,23 +1491,50 @@ func TestEndpointUpdateParent(t *testing.T) { } }() - defer controller.LeaveAll("container2") - err = ep2.Join("container2", - libnetwork.JoinOptionHostname("test2"), - libnetwork.JoinOptionDomainname("docker.io"), - libnetwork.JoinOptionHostsPath("/var/lib/docker/test_network/container2/hosts"), - libnetwork.JoinOptionParentUpdate(ep1.ID(), "web", "192.168.0.2")) + sbx1, err := controller.NewSandbox(containerID, + libnetwork.OptionHostname("test"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionExtraHost("web", "192.168.0.1")) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sbx1.Delete(); err != nil { + t.Fatal(err) + } + }() + + sbx2, err := controller.NewSandbox("c2", + libnetwork.OptionHostname("test2"), + libnetwork.OptionDomainname("docker.io"), + libnetwork.OptionHostsPath("/var/lib/docker/test_network/container2/hosts"), + libnetwork.OptionExtraHost("web", "192.168.0.2")) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sbx2.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep1.Join(sbx1) runtime.LockOSThread() if err != nil { t.Fatal(err) } - err = ep2.Leave("container2") + err = ep2.Join(sbx2) runtime.LockOSThread() if err != nil { t.Fatal(err) } + err = ep2.Leave(sbx2) + runtime.LockOSThread() + if err != nil { + t.Fatal(err) + } } func TestEnableIPv6(t *testing.T) { @@ -1481,7 +1542,7 @@ func TestEnableIPv6(t *testing.T) { defer netutils.SetupTestNetNS(t)() } - tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888") + tmpResolvConf := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n") //take a copy of resolv.conf for restoring after test completes resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf") if err != nil { @@ -1536,15 +1597,22 @@ func TestEnableIPv6(t *testing.T) { resolvConfPath := "/tmp/libnetwork_test/resolv.conf" defer os.Remove(resolvConfPath) - defer controller.LeaveAll(containerID) - err = ep1.Join(containerID, - libnetwork.JoinOptionResolvConfPath(resolvConfPath)) - runtime.LockOSThread() + sb, err := controller.NewSandbox(containerID, libnetwork.OptionResolvConfPath(resolvConfPath)) if err != nil { t.Fatal(err) } defer func() { - err = ep1.Leave(containerID) + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep1.Join(sb) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep1.Leave(sb) runtime.LockOSThread() if err != nil { t.Fatal(err) @@ -1557,7 +1625,7 @@ func TestEnableIPv6(t *testing.T) { } if !bytes.Equal(content, tmpResolvConf) { - t.Fatalf("Expected %s, Got %s", string(tmpResolvConf), string(content)) + t.Fatalf("Expected:\n%s\nGot:\n%s", string(tmpResolvConf), string(content)) } if err != nil { @@ -1570,7 +1638,7 @@ func TestResolvConfHost(t *testing.T) { defer netutils.SetupTestNetNS(t)() } - tmpResolvConf := []byte("search localhost.net\nnameserver 127.0.0.1\nnameserver 2001:4860:4860::8888") + tmpResolvConf := []byte("search localhost.net\nnameserver 127.0.0.1\nnameserver 2001:4860:4860::8888\n") //take a copy of resolv.conf for restoring after test completes resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf") @@ -1601,13 +1669,24 @@ func TestResolvConfHost(t *testing.T) { resolvConfPath := "/tmp/libnetwork_test/resolv.conf" defer os.Remove(resolvConfPath) - err = ep1.Join(containerID, - libnetwork.JoinOptionResolvConfPath(resolvConfPath)) + sb, err := controller.NewSandbox(containerID, + libnetwork.OptionResolvConfPath(resolvConfPath), + libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) if err != nil { t.Fatal(err) } defer func() { - err = ep1.Leave(containerID) + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep1.Join(sb) + if err != nil { + t.Fatal(err) + } + defer func() { + err = ep1.Leave(sb) if err != nil { t.Fatal(err) } @@ -1629,7 +1708,7 @@ func TestResolvConfHost(t *testing.T) { } if !bytes.Equal(content, tmpResolvConf) { - t.Fatalf("Expected %s, Got %s", string(tmpResolvConf), string(content)) + t.Fatalf("Expected:\n%s\nGot:\n%s", string(tmpResolvConf), string(content)) } } @@ -1638,9 +1717,9 @@ func TestResolvConf(t *testing.T) { defer netutils.SetupTestNetNS(t)() } - tmpResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888") + tmpResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\nnameserver 2001:4860:4860::8888\n") expectedResolvConf1 := []byte("search pommesfrites.fr\nnameserver 12.34.56.78\n") - tmpResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\nnameserver 2001:4860:4860::8888") + tmpResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\nnameserver 2001:4860:4860::8888\n") expectedResolvConf2 := []byte("search pommesfrites.fr\nnameserver 112.34.56.78\n") tmpResolvConf3 := []byte("search pommesfrites.fr\nnameserver 113.34.56.78\n") @@ -1672,12 +1751,12 @@ func TestResolvConf(t *testing.T) { } }() - ep1, err := n.CreateEndpoint("ep1") + ep, err := n.CreateEndpoint("ep") if err != nil { t.Fatal(err) } defer func() { - if err := ep1.Delete(); err != nil { + if err := ep.Delete(); err != nil { t.Fatal(err) } }() @@ -1689,21 +1768,22 @@ func TestResolvConf(t *testing.T) { resolvConfPath := "/tmp/libnetwork_test/resolv.conf" defer os.Remove(resolvConfPath) - defer controller.LeaveAll(containerID) - err = ep1.Join(containerID, - libnetwork.JoinOptionResolvConfPath(resolvConfPath)) - runtime.LockOSThread() + sb1, err := controller.NewSandbox(containerID, libnetwork.OptionResolvConfPath(resolvConfPath)) if err != nil { t.Fatal(err) } defer func() { - err = ep1.Leave(containerID) - runtime.LockOSThread() - if err != nil { + if err := sb1.Delete(); err != nil { t.Fatal(err) } }() + err = ep.Join(sb1) + runtime.LockOSThread() + if err != nil { + t.Fatal(err) + } + finfo, err := os.Stat(resolvConfPath) if err != nil { t.Fatal(err) @@ -1720,10 +1800,11 @@ func TestResolvConf(t *testing.T) { } if !bytes.Equal(content, expectedResolvConf1) { - t.Fatalf("Expected %s, Got %s", string(expectedResolvConf1), string(content)) + fmt.Printf("\n%v\n%v\n", expectedResolvConf1, content) + t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf1), string(content)) } - err = ep1.Leave(containerID) + err = ep.Leave(sb1) runtime.LockOSThread() if err != nil { t.Fatal(err) @@ -1733,8 +1814,17 @@ func TestResolvConf(t *testing.T) { t.Fatal(err) } - err = ep1.Join(containerID, - libnetwork.JoinOptionResolvConfPath(resolvConfPath)) + sb2, err := controller.NewSandbox(containerID+"_2", libnetwork.OptionResolvConfPath(resolvConfPath)) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sb2.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep.Join(sb2) runtime.LockOSThread() if err != nil { t.Fatal(err) @@ -1746,21 +1836,20 @@ func TestResolvConf(t *testing.T) { } if !bytes.Equal(content, expectedResolvConf2) { - t.Fatalf("Expected %s, Got %s", string(expectedResolvConf2), string(content)) + t.Fatalf("Expected:\n%s\nGot:\n%s", string(expectedResolvConf2), string(content)) } if err := ioutil.WriteFile(resolvConfPath, tmpResolvConf3, 0644); err != nil { t.Fatal(err) } - err = ep1.Leave(containerID) + err = ep.Leave(sb2) runtime.LockOSThread() if err != nil { t.Fatal(err) } - err = ep1.Join(containerID, - libnetwork.JoinOptionResolvConfPath(resolvConfPath)) + err = ep.Join(sb2) runtime.LockOSThread() if err != nil { t.Fatal(err) @@ -1772,7 +1861,7 @@ func TestResolvConf(t *testing.T) { } if !bytes.Equal(content, tmpResolvConf3) { - t.Fatalf("Expected %s, Got %s", string(tmpResolvConf3), string(content)) + t.Fatalf("Expected:\n%s\nGot:\n%s", string(tmpResolvConf3), string(content)) } } @@ -1886,6 +1975,7 @@ var ( done = make(chan chan struct{}, numThreads-1) origns = netns.None() testns = netns.None() + sboxes = make([]libnetwork.Sandbox, numThreads) ) const ( @@ -1945,6 +2035,15 @@ func createGlobalInstance(t *testing.T) { if err != nil { t.Fatal(err) } + + if sboxes[first-1], err = controller.NewSandbox(fmt.Sprintf("%drace", first), libnetwork.OptionUseDefaultSandbox()); err != nil { + t.Fatal(err) + } + for thd := first + 1; thd <= last; thd++ { + if sboxes[thd-1], err = controller.NewSandbox(fmt.Sprintf("%drace", thd)); err != nil { + t.Fatal(err) + } + } } func debugf(format string, a ...interface{}) (int, error) { @@ -1955,42 +2054,49 @@ func debugf(format string, a ...interface{}) (int, error) { return 0, nil } -func parallelJoin(t *testing.T, ep libnetwork.Endpoint, thrNumber int) { +func parallelJoin(t *testing.T, rc libnetwork.Sandbox, ep libnetwork.Endpoint, thrNumber int) { debugf("J%d.", thrNumber) var err error - if thrNumber == first { - err = ep.Join(fmt.Sprintf("%drace", thrNumber), libnetwork.JoinOptionUseDefaultSandbox()) - } else { - err = ep.Join(fmt.Sprintf("%drace", thrNumber)) - } + + sb := sboxes[thrNumber-1] + err = ep.Join(sb) runtime.LockOSThread() if err != nil { - if _, ok := err.(libnetwork.ErrNoContainer); !ok { - if _, ok := err.(libnetwork.ErrInvalidJoin); !ok { - t.Fatalf("thread %d: %v", thrNumber, err) - } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("thread %d: %v", thrNumber, err) } debugf("JE%d(%v).", thrNumber, err) } debugf("JD%d.", thrNumber) } -func parallelLeave(t *testing.T, ep libnetwork.Endpoint, thrNumber int) { +func parallelLeave(t *testing.T, rc libnetwork.Sandbox, ep libnetwork.Endpoint, thrNumber int) { debugf("L%d.", thrNumber) var err error + + cid := fmt.Sprintf("%drace", thrNumber) + sb := sboxes[thrNumber-1] + if thrNumber == first { - err = ep.Leave(fmt.Sprintf("%drace", thrNumber)) + err = ep.Leave(sb) } else { - err = controller.LeaveAll(fmt.Sprintf("%drace", thrNumber)) + err = sb.Delete() + // re add sandbox + defer func() { + if err == nil { + var e error + if sboxes[thrNumber-1], e = controller.NewSandbox(cid); e != nil { + t.Fatalf("Failed to recreate sandbox %s: %v", cid, e) + } + } + }() } runtime.LockOSThread() if err != nil { - if _, ok := err.(libnetwork.ErrNoContainer); !ok { - if _, ok := err.(libnetwork.ErrInvalidJoin); !ok { - t.Fatalf("thread %d: %v", thrNumber, err) - } + if _, ok := err.(types.ForbiddenError); !ok { + t.Fatalf("thread %d: %v", thrNumber, err) } debugf("LE%d(%v).", thrNumber, err) } @@ -1998,7 +2104,11 @@ func parallelLeave(t *testing.T, ep libnetwork.Endpoint, thrNumber int) { } func runParallelTests(t *testing.T, thrNumber int) { - var err error + var ( + ep libnetwork.Endpoint + sb libnetwork.Sandbox + err error + ) t.Parallel() @@ -2046,7 +2156,7 @@ func runParallelTests(t *testing.T, thrNumber int) { t.Fatal(err) } if net1 == nil { - t.Fatal("Could not find network1") + t.Fatal("Could not find testhost") } net2, err := controller.NetworkByName("network2") @@ -2059,9 +2169,6 @@ func runParallelTests(t *testing.T, thrNumber int) { epName := fmt.Sprintf("pep%d", thrNumber) - //var err error - var ep libnetwork.Endpoint - if thrNumber == first { ep, err = net1.EndpointByName(epName) } else { @@ -2075,9 +2182,15 @@ func runParallelTests(t *testing.T, thrNumber int) { t.Fatal("Got nil ep with no error") } + cid := fmt.Sprintf("%drace", thrNumber) + controller.WalkSandboxes(libnetwork.SandboxContainerWalker(&sb, cid)) + if sb == nil { + t.Fatalf("Got nil sandbox for container: %s", cid) + } + for i := 0; i < iterCnt; i++ { - parallelJoin(t, ep, thrNumber) - parallelLeave(t, ep, thrNumber) + parallelJoin(t, sb, ep, thrNumber) + parallelLeave(t, sb, ep, thrNumber) } debugf("\n") @@ -2095,6 +2208,15 @@ func runParallelTests(t *testing.T, thrNumber int) { } testns.Close() + err = sb.Delete() + if err != nil { + t.Fatal(err) + } + + ep.Delete() + if err != nil { + t.Fatal(err) + } if err := net2.Delete(); err != nil { t.Fatal(err) diff --git a/libnetwork/network.go b/libnetwork/network.go index 9ad4381114..ab95292cea 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -59,7 +59,7 @@ type network struct { ctrlr *controller name string networkType string - id types.UUID + id string driver driverapi.Driver enableIPv6 bool endpointCnt uint64 @@ -83,7 +83,7 @@ func (n *network) ID() string { n.Lock() defer n.Unlock() - return string(n.id) + return n.id } func (n *network) Type() string { @@ -100,7 +100,7 @@ func (n *network) Type() string { func (n *network) Key() []string { n.Lock() defer n.Unlock() - return []string{datastore.NetworkKeyPrefix, string(n.id)} + return []string{datastore.NetworkKeyPrefix, n.id} } func (n *network) KeyPrefix() []string { @@ -162,7 +162,7 @@ func (n *network) DecEndpointCnt() { func (n *network) MarshalJSON() ([]byte, error) { netMap := make(map[string]interface{}) netMap["name"] = n.name - netMap["id"] = string(n.id) + netMap["id"] = n.id netMap["networkType"] = n.networkType netMap["endpointCnt"] = n.endpointCnt netMap["enableIPv6"] = n.enableIPv6 @@ -177,7 +177,7 @@ func (n *network) UnmarshalJSON(b []byte) (err error) { return err } n.name = netMap["name"].(string) - n.id = types.UUID(netMap["id"].(string)) + n.id = netMap["id"].(string) n.networkType = netMap["networkType"].(string) n.endpointCnt = uint64(netMap["endpointCnt"].(float64)) n.enableIPv6 = netMap["enableIPv6"].(bool) @@ -223,12 +223,12 @@ func (n *network) Delete() error { ctrlr.Unlock() if !ok { - return &UnknownNetworkError{name: n.name, id: string(n.id)} + return &UnknownNetworkError{name: n.name, id: n.id} } numEps := n.EndpointCnt() if numEps != 0 { - return &ActiveEndpointsError{name: n.name, id: string(n.id)} + return &ActiveEndpointsError{name: n.name, id: n.id} } // deleteNetworkFromStore performs an atomic delete operation and the network.endpointCnt field will help @@ -287,7 +287,7 @@ func (n *network) addEndpoint(ep *endpoint) error { err = d.CreateEndpoint(n.id, ep.id, ep, ep.generic) if err != nil { - return err + return types.InternalErrorf("failed to create endpoint %s on network %s: %v", ep.Name(), n.Name(), err) } n.updateSvcRecord(ep, true) @@ -307,7 +307,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi ep := &endpoint{name: name, iFaces: []*endpointInterface{}, generic: make(map[string]interface{})} - ep.id = types.UUID(stringid.GenerateRandomID()) + ep.id = stringid.GenerateRandomID() ep.network = n ep.processOptions(options...) @@ -393,7 +393,7 @@ func (n *network) EndpointByID(id string) (Endpoint, error) { } n.Lock() defer n.Unlock() - if e, ok := n.endpoints[types.UUID(id)]; ok { + if e, ok := n.endpoints[id]; ok { return e, nil } return nil, ErrNoSuchEndpoint(id) @@ -435,22 +435,19 @@ func (n *network) updateSvcRecord(ep *endpoint, isAdd bool) { return } - var epList []*endpoint + var sbList []*sandbox n.WalkEndpoints(func(e Endpoint) bool { - cEp := e.(*endpoint) - cEp.Lock() - if cEp.container != nil { - epList = append(epList, cEp) + if sb, hasSandbox := e.(*endpoint).getSandbox(); hasSandbox { + sbList = append(sbList, sb) } - cEp.Unlock() return false }) - for _, cEp := range epList { + for _, sb := range sbList { if isAdd { - cEp.addHostEntries(recs) + sb.addHostsEntries(recs) } else { - cEp.deleteHostEntries(recs) + sb.deleteHostsEntries(recs) } } } @@ -469,3 +466,9 @@ func (n *network) getSvcRecords() []etchosts.Record { return recs } + +func (n *network) getController() *controller { + n.Lock() + defer n.Unlock() + return n.ctrlr +} diff --git a/libnetwork/sandbox/interface_freebsd.go b/libnetwork/osl/interface_freebsd.go similarity index 85% rename from libnetwork/sandbox/interface_freebsd.go rename to libnetwork/osl/interface_freebsd.go index 115290d82b..9c0141fd9b 100644 --- a/libnetwork/sandbox/interface_freebsd.go +++ b/libnetwork/osl/interface_freebsd.go @@ -1,4 +1,4 @@ -package sandbox +package osl // IfaceOption is a function option type to set interface options type IfaceOption func() diff --git a/libnetwork/sandbox/interface_linux.go b/libnetwork/osl/interface_linux.go similarity index 99% rename from libnetwork/sandbox/interface_linux.go rename to libnetwork/osl/interface_linux.go index 7fc8c70681..c314b011dc 100644 --- a/libnetwork/sandbox/interface_linux.go +++ b/libnetwork/osl/interface_linux.go @@ -1,4 +1,4 @@ -package sandbox +package osl import ( "fmt" diff --git a/libnetwork/sandbox/interface_windows.go b/libnetwork/osl/interface_windows.go similarity index 85% rename from libnetwork/sandbox/interface_windows.go rename to libnetwork/osl/interface_windows.go index 115290d82b..9c0141fd9b 100644 --- a/libnetwork/sandbox/interface_windows.go +++ b/libnetwork/osl/interface_windows.go @@ -1,4 +1,4 @@ -package sandbox +package osl // IfaceOption is a function option type to set interface options type IfaceOption func() diff --git a/libnetwork/sandbox/namespace_linux.go b/libnetwork/osl/namespace_linux.go similarity index 99% rename from libnetwork/sandbox/namespace_linux.go rename to libnetwork/osl/namespace_linux.go index 214da312d0..843ef973ca 100644 --- a/libnetwork/sandbox/namespace_linux.go +++ b/libnetwork/osl/namespace_linux.go @@ -1,4 +1,4 @@ -package sandbox +package osl import ( "fmt" diff --git a/libnetwork/sandbox/namespace_unsupported.go b/libnetwork/osl/namespace_unsupported.go similarity index 89% rename from libnetwork/sandbox/namespace_unsupported.go rename to libnetwork/osl/namespace_unsupported.go index 9d38206bd7..77f286e5ea 100644 --- a/libnetwork/sandbox/namespace_unsupported.go +++ b/libnetwork/osl/namespace_unsupported.go @@ -1,6 +1,6 @@ // +build !linux,!windows,!freebsd -package sandbox +package osl // GC triggers garbage collection of namespace path right away // and waits for it. diff --git a/libnetwork/sandbox/namespace_windows.go b/libnetwork/osl/namespace_windows.go similarity index 97% rename from libnetwork/sandbox/namespace_windows.go rename to libnetwork/osl/namespace_windows.go index 4aa7787113..6a79787a3f 100644 --- a/libnetwork/sandbox/namespace_windows.go +++ b/libnetwork/osl/namespace_windows.go @@ -1,4 +1,4 @@ -package sandbox +package osl // GenerateKey generates a sandbox key based on the passed // container id. diff --git a/libnetwork/sandbox/neigh_freebsd.go b/libnetwork/osl/neigh_freebsd.go similarity index 84% rename from libnetwork/sandbox/neigh_freebsd.go rename to libnetwork/osl/neigh_freebsd.go index 58b30587e2..280f006396 100644 --- a/libnetwork/sandbox/neigh_freebsd.go +++ b/libnetwork/osl/neigh_freebsd.go @@ -1,4 +1,4 @@ -package sandbox +package osl // NeighOption is a function option type to set neighbor options type NeighOption func() diff --git a/libnetwork/sandbox/neigh_linux.go b/libnetwork/osl/neigh_linux.go similarity index 99% rename from libnetwork/sandbox/neigh_linux.go rename to libnetwork/osl/neigh_linux.go index 873f14f193..a221e712da 100644 --- a/libnetwork/sandbox/neigh_linux.go +++ b/libnetwork/osl/neigh_linux.go @@ -1,4 +1,4 @@ -package sandbox +package osl import ( "bytes" diff --git a/libnetwork/sandbox/neigh_windows.go b/libnetwork/osl/neigh_windows.go similarity index 84% rename from libnetwork/sandbox/neigh_windows.go rename to libnetwork/osl/neigh_windows.go index 58b30587e2..280f006396 100644 --- a/libnetwork/sandbox/neigh_windows.go +++ b/libnetwork/osl/neigh_windows.go @@ -1,4 +1,4 @@ -package sandbox +package osl // NeighOption is a function option type to set neighbor options type NeighOption func() diff --git a/libnetwork/sandbox/options_linux.go b/libnetwork/osl/options_linux.go similarity index 98% rename from libnetwork/sandbox/options_linux.go rename to libnetwork/osl/options_linux.go index e34699c790..5295eb85c5 100644 --- a/libnetwork/sandbox/options_linux.go +++ b/libnetwork/osl/options_linux.go @@ -1,4 +1,4 @@ -package sandbox +package osl import "net" diff --git a/libnetwork/sandbox/route_linux.go b/libnetwork/osl/route_linux.go similarity index 99% rename from libnetwork/sandbox/route_linux.go rename to libnetwork/osl/route_linux.go index 946e364860..3ebaaec77c 100644 --- a/libnetwork/sandbox/route_linux.go +++ b/libnetwork/osl/route_linux.go @@ -1,4 +1,4 @@ -package sandbox +package osl import ( "fmt" diff --git a/libnetwork/sandbox/sandbox.go b/libnetwork/osl/sandbox.go similarity index 98% rename from libnetwork/sandbox/sandbox.go rename to libnetwork/osl/sandbox.go index f32bcfe30a..9e87f472b8 100644 --- a/libnetwork/sandbox/sandbox.go +++ b/libnetwork/osl/sandbox.go @@ -1,4 +1,5 @@ -package sandbox +// Package osl describes structures and interfaces which abstract os entities +package osl import ( "fmt" diff --git a/libnetwork/sandbox/sandbox_freebsd.go b/libnetwork/osl/sandbox_freebsd.go similarity index 97% rename from libnetwork/sandbox/sandbox_freebsd.go rename to libnetwork/osl/sandbox_freebsd.go index 4aa7787113..6a79787a3f 100644 --- a/libnetwork/sandbox/sandbox_freebsd.go +++ b/libnetwork/osl/sandbox_freebsd.go @@ -1,4 +1,4 @@ -package sandbox +package osl // GenerateKey generates a sandbox key based on the passed // container id. diff --git a/libnetwork/sandbox/sandbox_linux_test.go b/libnetwork/osl/sandbox_linux_test.go similarity index 99% rename from libnetwork/sandbox/sandbox_linux_test.go rename to libnetwork/osl/sandbox_linux_test.go index e5ad326cf5..63eb2156c0 100644 --- a/libnetwork/sandbox/sandbox_linux_test.go +++ b/libnetwork/osl/sandbox_linux_test.go @@ -1,4 +1,4 @@ -package sandbox +package osl import ( "net" diff --git a/libnetwork/sandbox/sandbox_test.go b/libnetwork/osl/sandbox_test.go similarity index 99% rename from libnetwork/sandbox/sandbox_test.go rename to libnetwork/osl/sandbox_test.go index 32f89a76ed..585db31853 100644 --- a/libnetwork/sandbox/sandbox_test.go +++ b/libnetwork/osl/sandbox_test.go @@ -1,4 +1,4 @@ -package sandbox +package osl import ( "os" diff --git a/libnetwork/sandbox/sandbox_unsupported.go b/libnetwork/osl/sandbox_unsupported.go similarity index 97% rename from libnetwork/sandbox/sandbox_unsupported.go rename to libnetwork/osl/sandbox_unsupported.go index 4ed3c9f58b..3bc6c38500 100644 --- a/libnetwork/sandbox/sandbox_unsupported.go +++ b/libnetwork/osl/sandbox_unsupported.go @@ -1,6 +1,6 @@ // +build !linux,!windows,!freebsd -package sandbox +package osl import "errors" diff --git a/libnetwork/sandbox/sandbox_unsupported_test.go b/libnetwork/osl/sandbox_unsupported_test.go similarity index 93% rename from libnetwork/sandbox/sandbox_unsupported_test.go rename to libnetwork/osl/sandbox_unsupported_test.go index 48dc2aa726..ad7e89ed9f 100644 --- a/libnetwork/sandbox/sandbox_unsupported_test.go +++ b/libnetwork/osl/sandbox_unsupported_test.go @@ -1,6 +1,6 @@ // +build !linux -package sandbox +package osl import ( "errors" diff --git a/libnetwork/resolvconf/resolvconf.go b/libnetwork/resolvconf/resolvconf.go index ebe3b71aa4..b6fd51c06f 100644 --- a/libnetwork/resolvconf/resolvconf.go +++ b/libnetwork/resolvconf/resolvconf.go @@ -168,20 +168,25 @@ func GetSearchDomains(resolvConf []byte) []string { // Build writes a configuration file to path containing a "nameserver" entry // for every element in dns, and a "search" entry for every element in // dnsSearch. -func Build(path string, dns, dnsSearch []string) error { +func Build(path string, dns, dnsSearch []string) (string, error) { content := bytes.NewBuffer(nil) - for _, dns := range dns { - if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil { - return err - } - } if len(dnsSearch) > 0 { if searchString := strings.Join(dnsSearch, " "); strings.Trim(searchString, " ") != "." { if _, err := content.WriteString("search " + searchString + "\n"); err != nil { - return err + return "", err } } } + for _, dns := range dns { + if _, err := content.WriteString("nameserver " + dns + "\n"); err != nil { + return "", err + } + } - return ioutil.WriteFile(path, content.Bytes(), 0644) + hash, err := ioutils.HashData(bytes.NewReader(content.Bytes())) + if err != nil { + return "", err + } + + return hash, ioutil.WriteFile(path, content.Bytes(), 0644) } diff --git a/libnetwork/resolvconf/resolvconf_test.go b/libnetwork/resolvconf/resolvconf_test.go index a21c7afb3e..44881ab963 100644 --- a/libnetwork/resolvconf/resolvconf_test.go +++ b/libnetwork/resolvconf/resolvconf_test.go @@ -119,7 +119,7 @@ func TestBuild(t *testing.T) { } defer os.Remove(file.Name()) - err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}) + _, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}) if err != nil { t.Fatal(err) } @@ -129,7 +129,7 @@ func TestBuild(t *testing.T) { t.Fatal(err) } - if expected := "nameserver ns1\nnameserver ns2\nnameserver ns3\nsearch search1\n"; !bytes.Contains(content, []byte(expected)) { + if expected := "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\n"; !bytes.Contains(content, []byte(expected)) { t.Fatalf("Expected to find '%s' got '%s'", expected, content) } } @@ -141,7 +141,7 @@ func TestBuildWithZeroLengthDomainSearch(t *testing.T) { } defer os.Remove(file.Name()) - err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."}) + _, err = Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."}) if err != nil { t.Fatal(err) } diff --git a/libnetwork/sandbox.go b/libnetwork/sandbox.go new file mode 100644 index 0000000000..0d1351393b --- /dev/null +++ b/libnetwork/sandbox.go @@ -0,0 +1,662 @@ +package libnetwork + +import ( + "bytes" + "container/heap" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path" + "path/filepath" + "sync" + + log "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/ioutils" + "github.com/docker/libnetwork/etchosts" + "github.com/docker/libnetwork/osl" + "github.com/docker/libnetwork/resolvconf" + "github.com/docker/libnetwork/types" +) + +// Sandbox provides the control over the network container entity. It is a one to one mapping with the container. +type Sandbox interface { + // ID returns the ID of the sandbox + ID() string + // Key returns the sandbox's key + Key() string + // ContainerID returns the container id associated to this sandbox + ContainerID() string + // Labels returns the sandbox's labels + Labels() map[string]interface{} + // Statistics retrieves the interfaces' statistics for the sandbox + Statistics() (map[string]*osl.InterfaceStatistics, error) + // Delete destroys this container after detaching it from all connected endpoints. + Delete() error +} + +// SandboxOption is a option setter function type used to pass varios options to +// NewNetContainer method. The various setter functions of type SandboxOption are +// provided by libnetwork, they look like ContainerOptionXXXX(...) +type SandboxOption func(sb *sandbox) + +func (sb *sandbox) processOptions(options ...SandboxOption) { + for _, opt := range options { + if opt != nil { + opt(sb) + } + } +} + +type epHeap []*endpoint + +type sandbox struct { + id string + containerID string + config containerConfig + osSbox osl.Sandbox + controller *controller + refCnt int + endpoints epHeap + epPriority map[string]int + //hostsPath string + //resolvConfPath string + joinLeaveDone chan struct{} + sync.Mutex +} + +// These are the container configs used to customize container /etc/hosts file. +type hostsPathConfig struct { + hostName string + domainName string + hostsPath string + originHostsPath string + extraHosts []extraHost + parentUpdates []parentUpdate +} + +type parentUpdate struct { + cid string + name string + ip string +} + +type extraHost struct { + name string + IP string +} + +// These are the container configs used to customize container /etc/resolv.conf file. +type resolvConfPathConfig struct { + resolvConfPath string + originResolvConfPath string + resolvConfHashFile string + dnsList []string + dnsSearchList []string +} + +type containerConfig struct { + hostsPathConfig + resolvConfPathConfig + generic map[string]interface{} + useDefaultSandBox bool + prio int // higher the value, more the priority +} + +func (sb *sandbox) ID() string { + return sb.id +} + +func (sb *sandbox) ContainerID() string { + return sb.containerID +} + +func (sb *sandbox) Key() string { + if sb.config.useDefaultSandBox { + return osl.GenerateKey("default") + } + return osl.GenerateKey(sb.id) +} + +func (sb *sandbox) Labels() map[string]interface{} { + return sb.config.generic +} + +func (sb *sandbox) Statistics() (map[string]*osl.InterfaceStatistics, error) { + m := make(map[string]*osl.InterfaceStatistics) + + if sb.osSbox == nil { + return m, nil + } + + var err error + for _, i := range sb.osSbox.Info().Interfaces() { + if m[i.DstName()], err = i.Statistics(); err != nil { + return m, err + } + } + + return m, nil +} + +func (sb *sandbox) Delete() error { + sb.Lock() + c := sb.controller + eps := make([]*endpoint, len(sb.endpoints)) + for i, ep := range sb.endpoints { + eps[i] = ep + } + sb.Unlock() + + // Detach from all containers + for _, ep := range eps { + if err := ep.Leave(sb); err != nil { + log.Warnf("Failed detaching sandbox %s from endpoint %s: %v\n", sb.ID(), ep.ID(), err) + } + } + + if sb.osSbox != nil { + sb.osSbox.Destroy() + } + + c.Lock() + delete(c.sandboxes, sb.ID()) + c.Unlock() + + return nil +} + +func (sb *sandbox) MarshalJSON() ([]byte, error) { + sb.Lock() + defer sb.Unlock() + + // We are just interested in the container ID. This can be expanded to include all of containerInfo if there is a need + return json.Marshal(sb.id) +} + +func (sb *sandbox) UnmarshalJSON(b []byte) (err error) { + sb.Lock() + defer sb.Unlock() + + var id string + if err := json.Unmarshal(b, &id); err != nil { + return err + } + sb.id = id + return nil +} + +func (sb *sandbox) updateGateway(ep *endpoint) error { + sb.osSbox.UnsetGateway() + sb.osSbox.UnsetGatewayIPv6() + + if ep == nil { + return nil + } + + ep.Lock() + joinInfo := ep.joinInfo + ep.Unlock() + + if err := sb.osSbox.SetGateway(joinInfo.gw); err != nil { + return fmt.Errorf("failed to set gateway while updating gateway: %v", err) + } + + if err := sb.osSbox.SetGatewayIPv6(joinInfo.gw6); err != nil { + return fmt.Errorf("failed to set IPv6 gateway while updating gateway: %v", err) + } + + return nil +} + +func (sb *sandbox) populateNetworkResources(ep *endpoint) error { + ep.Lock() + joinInfo := ep.joinInfo + ifaces := ep.iFaces + ep.Unlock() + + for _, i := range ifaces { + var ifaceOptions []osl.IfaceOption + + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(&i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes)) + if i.addrv6.IP.To16() != nil { + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(&i.addrv6)) + } + + if err := sb.osSbox.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil { + return fmt.Errorf("failed to add interface %s to sandbox: %v", i.srcName, err) + } + } + + if joinInfo != nil { + // Set up non-interface routes. + for _, r := range joinInfo.StaticRoutes { + if err := sb.osSbox.AddStaticRoute(r); err != nil { + return fmt.Errorf("failed to add static route %s: %v", r.Destination.String(), err) + } + } + } + + sb.Lock() + heap.Push(&sb.endpoints, ep) + highEp := sb.endpoints[0] + sb.Unlock() + if ep == highEp { + if err := sb.updateGateway(ep); err != nil { + return err + } + } + + return nil +} + +func (sb *sandbox) clearNetworkResources(ep *endpoint) error { + + for _, i := range sb.osSbox.Info().Interfaces() { + // Only remove the interfaces owned by this endpoint from the sandbox. + if ep.hasInterface(i.SrcName()) { + if err := i.Remove(); err != nil { + log.Debugf("Remove interface failed: %v", err) + } + } + } + + ep.Lock() + joinInfo := ep.joinInfo + ep.Unlock() + + // Remove non-interface routes. + for _, r := range joinInfo.StaticRoutes { + if err := sb.osSbox.RemoveStaticRoute(r); err != nil { + log.Debugf("Remove route failed: %v", err) + } + } + + sb.Lock() + if len(sb.endpoints) == 0 { + // sb.endpoints should never be empty and this is unexpected error condition + // We log an error message to note this down for debugging purposes. + log.Errorf("No endpoints in sandbox while trying to remove endpoint %s", ep.Name()) + sb.Unlock() + return nil + } + + highEpBefore := sb.endpoints[0] + var ( + i int + e *endpoint + ) + for i, e = range sb.endpoints { + if e == ep { + break + } + } + heap.Remove(&sb.endpoints, i) + var highEpAfter *endpoint + if len(sb.endpoints) > 0 { + highEpAfter = sb.endpoints[0] + } + delete(sb.epPriority, ep.ID()) + sb.Unlock() + + if highEpBefore != highEpAfter { + sb.updateGateway(highEpAfter) + } + + return nil +} + +const ( + defaultPrefix = "/var/lib/docker/network/files" + filePerm = 0644 +) + +func (sb *sandbox) buildHostsFile() error { + if sb.config.hostsPath == "" { + sb.config.hostsPath = defaultPrefix + "/" + sb.id + "/hosts" + } + + dir, _ := filepath.Split(sb.config.hostsPath) + if err := createBasePath(dir); err != nil { + return err + } + + // This is for the host mode networking + if sb.config.originHostsPath != "" { + if err := copyFile(sb.config.originHostsPath, sb.config.hostsPath); err != nil && !os.IsNotExist(err) { + return types.InternalErrorf("could not copy source hosts file %s to %s: %v", sb.config.originHostsPath, sb.config.hostsPath, err) + } + return nil + } + + extraContent := make([]etchosts.Record, 0, len(sb.config.extraHosts)) + for _, extraHost := range sb.config.extraHosts { + extraContent = append(extraContent, etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP}) + } + + return etchosts.Build(sb.config.hostsPath, "", sb.config.hostName, sb.config.domainName, extraContent) +} + +func (sb *sandbox) updateHostsFile(ifaceIP string, svcRecords []etchosts.Record) error { + // Rebuild the hosts file accounting for the passed interface IP and service records + extraContent := make([]etchosts.Record, 0, len(sb.config.extraHosts)+len(svcRecords)) + + for _, extraHost := range sb.config.extraHosts { + extraContent = append(extraContent, etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP}) + } + + for _, svc := range svcRecords { + extraContent = append(extraContent, svc) + } + + return etchosts.Build(sb.config.hostsPath, ifaceIP, sb.config.hostName, sb.config.domainName, extraContent) +} + +func (sb *sandbox) addHostsEntries(recs []etchosts.Record) { + if err := etchosts.Add(sb.config.hostsPath, recs); err != nil { + log.Warnf("Failed adding service host entries to the running container: %v", err) + } +} + +func (sb *sandbox) deleteHostsEntries(recs []etchosts.Record) { + if err := etchosts.Delete(sb.config.hostsPath, recs); err != nil { + log.Warnf("Failed deleting service host entries to the running container: %v", err) + } +} + +func (sb *sandbox) updateParentHosts() error { + var pSb Sandbox + + for _, update := range sb.config.parentUpdates { + sb.controller.WalkSandboxes(SandboxContainerWalker(&pSb, update.cid)) + if pSb == nil { + continue + } + if err := etchosts.Update(pSb.(*sandbox).config.hostsPath, update.ip, update.name); err != nil { + return err + } + } + + return nil +} + +func (sb *sandbox) setupDNS() error { + if sb.config.resolvConfPath == "" { + sb.config.resolvConfPath = defaultPrefix + "/" + sb.id + "/resolv.conf" + } + + sb.config.resolvConfHashFile = sb.config.resolvConfPath + ".hash" + + dir, _ := filepath.Split(sb.config.resolvConfPath) + if err := createBasePath(dir); err != nil { + return err + } + + // This is for the host mode networking + if sb.config.originResolvConfPath != "" { + if err := copyFile(sb.config.originResolvConfPath, sb.config.resolvConfPath); err != nil { + return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", sb.config.originResolvConfPath, sb.config.resolvConfPath, err) + } + return nil + } + + resolvConf, err := resolvconf.Get() + if err != nil { + return err + } + dnsList := resolvconf.GetNameservers(resolvConf) + dnsSearchList := resolvconf.GetSearchDomains(resolvConf) + + if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 { + if len(sb.config.dnsList) > 0 { + dnsList = sb.config.dnsList + } + if len(sb.config.dnsSearchList) > 0 { + dnsSearchList = sb.config.dnsSearchList + } + } + + hash, err := resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList) + if err != nil { + return err + } + + // write hash + if err := ioutil.WriteFile(sb.config.resolvConfHashFile, []byte(hash), filePerm); err != nil { + return types.InternalErrorf("failed to write resol.conf hash file when setting up dns for sandbox %s: %v", sb.ID(), err) + } + + return nil +} + +func (sb *sandbox) updateDNS(ipv6Enabled bool) error { + var oldHash []byte + hashFile := sb.config.resolvConfHashFile + + resolvConf, err := ioutil.ReadFile(sb.config.resolvConfPath) + if err != nil { + if !os.IsNotExist(err) { + return err + } + } else { + oldHash, err = ioutil.ReadFile(hashFile) + if err != nil { + if !os.IsNotExist(err) { + return err + } + + oldHash = []byte{} + } + } + + curHash, err := ioutils.HashData(bytes.NewReader(resolvConf)) + if err != nil { + return err + } + + if string(oldHash) != "" && curHash != string(oldHash) { + // Seems the user has changed the container resolv.conf since the last time + // we checked so return without doing anything. + log.Infof("Skipping update of resolv.conf file with ipv6Enabled: %t because file was touched by user", ipv6Enabled) + return nil + } + + // replace any localhost/127.* and remove IPv6 nameservers if IPv6 disabled. + resolvConf, _ = resolvconf.FilterResolvDNS(resolvConf, ipv6Enabled) + + newHash, err := ioutils.HashData(bytes.NewReader(resolvConf)) + if err != nil { + return err + } + + // for atomic updates to these files, use temporary files with os.Rename: + dir := path.Dir(sb.config.resolvConfPath) + tmpHashFile, err := ioutil.TempFile(dir, "hash") + if err != nil { + return err + } + tmpResolvFile, err := ioutil.TempFile(dir, "resolv") + if err != nil { + return err + } + + // Change the perms to filePerm (0644) since ioutil.TempFile creates it by default as 0600 + if err := os.Chmod(tmpResolvFile.Name(), filePerm); err != nil { + return err + } + + // write the updates to the temp files + if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newHash), filePerm); err != nil { + return err + } + if err = ioutil.WriteFile(tmpResolvFile.Name(), resolvConf, filePerm); err != nil { + return err + } + + // rename the temp files for atomic replace + if err = os.Rename(tmpHashFile.Name(), hashFile); err != nil { + return err + } + return os.Rename(tmpResolvFile.Name(), sb.config.resolvConfPath) +} + +// OptionHostname function returns an option setter for hostname option to +// be passed to NewSandbox method. +func OptionHostname(name string) SandboxOption { + return func(sb *sandbox) { + sb.config.hostName = name + } +} + +// OptionDomainname function returns an option setter for domainname option to +// be passed to NewSandbox method. +func OptionDomainname(name string) SandboxOption { + return func(sb *sandbox) { + sb.config.domainName = name + } +} + +// OptionHostsPath function returns an option setter for hostspath option to +// be passed to NewSandbox method. +func OptionHostsPath(path string) SandboxOption { + return func(sb *sandbox) { + sb.config.hostsPath = path + } +} + +// OptionOriginHostsPath function returns an option setter for origin hosts file path +// tbeo passed to NewSandbox method. +func OptionOriginHostsPath(path string) SandboxOption { + return func(sb *sandbox) { + sb.config.originHostsPath = path + } +} + +// OptionExtraHost function returns an option setter for extra /etc/hosts options +// which is a name and IP as strings. +func OptionExtraHost(name string, IP string) SandboxOption { + return func(sb *sandbox) { + sb.config.extraHosts = append(sb.config.extraHosts, extraHost{name: name, IP: IP}) + } +} + +// OptionParentUpdate function returns an option setter for parent container +// which needs to update the IP address for the linked container. +func OptionParentUpdate(cid string, name, ip string) SandboxOption { + return func(sb *sandbox) { + sb.config.parentUpdates = append(sb.config.parentUpdates, parentUpdate{cid: cid, name: name, ip: ip}) + } +} + +// OptionResolvConfPath function returns an option setter for resolvconfpath option to +// be passed to net container methods. +func OptionResolvConfPath(path string) SandboxOption { + return func(sb *sandbox) { + sb.config.resolvConfPath = path + } +} + +// OptionOriginResolvConfPath function returns an option setter to set the path to the +// origin resolv.conf file to be passed to net container methods. +func OptionOriginResolvConfPath(path string) SandboxOption { + return func(sb *sandbox) { + sb.config.originResolvConfPath = path + } +} + +// OptionDNS function returns an option setter for dns entry option to +// be passed to container Create method. +func OptionDNS(dns string) SandboxOption { + return func(sb *sandbox) { + sb.config.dnsList = append(sb.config.dnsList, dns) + } +} + +// OptionDNSSearch function returns an option setter for dns search entry option to +// be passed to container Create method. +func OptionDNSSearch(search string) SandboxOption { + return func(sb *sandbox) { + sb.config.dnsSearchList = append(sb.config.dnsSearchList, search) + } +} + +// OptionUseDefaultSandbox function returns an option setter for using default sandbox to +// be passed to container Create method. +func OptionUseDefaultSandbox() SandboxOption { + return func(sb *sandbox) { + sb.config.useDefaultSandBox = true + } +} + +// OptionGeneric function returns an option setter for Generic configuration +// that is not managed by libNetwork but can be used by the Drivers during the call to +// net container creation method. Container Labels are a good example. +func OptionGeneric(generic map[string]interface{}) SandboxOption { + return func(sb *sandbox) { + sb.config.generic = generic + } +} + +func (eh epHeap) Len() int { return len(eh) } + +func (eh epHeap) Less(i, j int) bool { + ci, _ := eh[i].getSandbox() + cj, _ := eh[j].getSandbox() + + cip, ok := ci.epPriority[eh[i].ID()] + if !ok { + cip = 0 + } + cjp, ok := cj.epPriority[eh[j].ID()] + if !ok { + cjp = 0 + } + if cip == cjp { + return eh[i].getNetwork().Name() < eh[j].getNetwork().Name() + } + + return cip > cjp +} + +func (eh epHeap) Swap(i, j int) { eh[i], eh[j] = eh[j], eh[i] } + +func (eh *epHeap) Push(x interface{}) { + *eh = append(*eh, x.(*endpoint)) +} + +func (eh *epHeap) Pop() interface{} { + old := *eh + n := len(old) + x := old[n-1] + *eh = old[0 : n-1] + return x +} + +func createBasePath(dir string) error { + return os.MkdirAll(dir, filePerm) +} + +func createFile(path string) error { + var f *os.File + + dir, _ := filepath.Split(path) + err := createBasePath(dir) + if err != nil { + return err + } + + f, err = os.Create(path) + if err == nil { + f.Close() + } + + return err +} + +func copyFile(src, dst string) error { + sBytes, err := ioutil.ReadFile(src) + if err != nil { + return err + } + return ioutil.WriteFile(dst, sBytes, filePerm) +} diff --git a/libnetwork/sandbox_test.go b/libnetwork/sandbox_test.go new file mode 100644 index 0000000000..1434dbc661 --- /dev/null +++ b/libnetwork/sandbox_test.go @@ -0,0 +1,219 @@ +package libnetwork + +import ( + "testing" + + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/options" + "github.com/docker/libnetwork/osl" +) + +func createEmptyCtrlr() *controller { + return &controller{sandboxes: sandboxTable{}} +} + +func createEmptyEndpoint() *endpoint { + return &endpoint{ + joinInfo: &endpointJoinInfo{}, + iFaces: []*endpointInterface{}, + } +} + +func getTestEnv(t *testing.T) (NetworkController, Network, Network) { + c, err := New() + if err != nil { + t.Fatal(err) + } + + option := options.Generic{ + "EnableIPForwarding": true, + } + genericOption := make(map[string]interface{}) + genericOption[netlabel.GenericData] = option + if err := c.ConfigureNetworkDriver("bridge", genericOption); err != nil { + t.Fatal(err) + } + + netType := "bridge" + name1 := "test_nw_1" + netOption1 := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": name1, + "AllowNonDefaultBridge": true, + }, + } + n1, err := c.NewNetwork(netType, name1, NetworkOptionGeneric(netOption1)) + if err != nil { + t.Fatal(err) + } + + name2 := "test_nw_2" + netOption2 := options.Generic{ + netlabel.GenericData: options.Generic{ + "BridgeName": name2, + "AllowNonDefaultBridge": true, + }, + } + n2, err := c.NewNetwork(netType, name2, NetworkOptionGeneric(netOption2)) + if err != nil { + t.Fatal(err) + } + + return c, n1, n2 +} + +func TestSandboxAddEmpty(t *testing.T) { + ctrlr := createEmptyCtrlr() + + sbx, err := ctrlr.NewSandbox("sandbox0") + if err != nil { + t.Fatal(err) + } + + if err := sbx.Delete(); err != nil { + t.Fatal(err) + } + + if len(ctrlr.sandboxes) != 0 { + t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes)) + } + + osl.GC() +} + +func TestSandboxAddMultiPrio(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + c, nw, _ := getTestEnv(t) + ctrlr := c.(*controller) + + sbx, err := ctrlr.NewSandbox("sandbox1") + if err != nil { + t.Fatal(err) + } + sid := sbx.ID() + + ep1, err := nw.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + ep2, err := nw.CreateEndpoint("ep2") + if err != nil { + t.Fatal(err) + } + ep3, err := nw.CreateEndpoint("ep3") + if err != nil { + t.Fatal(err) + } + + if err := ep1.Join(sbx, JoinOptionPriority(ep1, 1)); err != nil { + t.Fatal(err) + } + + if err := ep2.Join(sbx, JoinOptionPriority(ep2, 2)); err != nil { + t.Fatal(err) + } + + if err := ep3.Join(sbx, JoinOptionPriority(ep3, 3)); err != nil { + t.Fatal(err) + } + + if ctrlr.sandboxes[sid].endpoints[0] != ep3 { + t.Fatal("Expected ep3 to be at the top of the heap. But did not find ep3 at the top of the heap") + } + + if err := ep3.Leave(sbx); err != nil { + t.Fatal(err) + } + if ctrlr.sandboxes[sid].endpoints[0] != ep2 { + t.Fatal("Expected ep2 to be at the top of the heap after removing ep3. But did not find ep2 at the top of the heap") + } + + if err := ep2.Leave(sbx); err != nil { + t.Fatal(err) + } + if ctrlr.sandboxes[sid].endpoints[0] != ep1 { + t.Fatal("Expected ep1 to be at the top of the heap after removing ep2. But did not find ep1 at the top of the heap") + } + + // Re-add ep3 back + if err := ep3.Join(sbx, JoinOptionPriority(ep3, 3)); err != nil { + t.Fatal(err) + } + + if ctrlr.sandboxes[sid].endpoints[0] != ep3 { + t.Fatal("Expected ep3 to be at the top of the heap after adding ep3 back. But did not find ep3 at the top of the heap") + } + + if err := sbx.Delete(); err != nil { + t.Fatal(err) + } + + if len(ctrlr.sandboxes) != 0 { + t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes)) + } + + osl.GC() +} + +func TestSandboxAddSamePrio(t *testing.T) { + if !netutils.IsRunningInContainer() { + defer netutils.SetupTestNetNS(t)() + } + + c, nw1, nw2 := getTestEnv(t) + + ctrlr := c.(*controller) + + sbx, err := ctrlr.NewSandbox("sandbox1") + if err != nil { + t.Fatal(err) + } + sid := sbx.ID() + + ep1, err := nw1.CreateEndpoint("ep1") + if err != nil { + t.Fatal(err) + } + ep2, err := nw2.CreateEndpoint("ep2") + if err != nil { + t.Fatal(err) + } + + if err := ep1.Join(sbx); err != nil { + t.Fatal(err) + } + + if err := ep2.Join(sbx); err != nil { + t.Fatal(err) + } + + if ctrlr.sandboxes[sid].endpoints[0] != ep1 { + t.Fatal("Expected ep1 to be at the top of the heap. But did not find ep1 at the top of the heap") + } + + if err := ep1.Leave(sbx); err != nil { + t.Fatal(err) + } + + if ctrlr.sandboxes[sid].endpoints[0] != ep2 { + t.Fatal("Expected ep2 to be at the top of the heap after removing ep3. But did not find ep2 at the top of the heap") + } + + if err := ep2.Leave(sbx); err != nil { + t.Fatal(err) + } + + if err := sbx.Delete(); err != nil { + t.Fatal(err) + } + + if len(ctrlr.sandboxes) != 0 { + t.Fatalf("controller containers is not empty. len = %d", len(ctrlr.sandboxes)) + } + + osl.GC() +} diff --git a/libnetwork/sandboxdata.go b/libnetwork/sandboxdata.go deleted file mode 100644 index 9b0d8ea1bf..0000000000 --- a/libnetwork/sandboxdata.go +++ /dev/null @@ -1,259 +0,0 @@ -package libnetwork - -import ( - "container/heap" - "fmt" - "sync" - - "github.com/Sirupsen/logrus" - "github.com/docker/libnetwork/sandbox" -) - -type epHeap []*endpoint - -type sandboxData struct { - sbox sandbox.Sandbox - refCnt int - endpoints epHeap - sync.Mutex -} - -func (eh epHeap) Len() int { return len(eh) } - -func (eh epHeap) Less(i, j int) bool { - eh[i].Lock() - eh[j].Lock() - defer eh[j].Unlock() - defer eh[i].Unlock() - - if eh[i].container.config.prio == eh[j].container.config.prio { - return eh[i].network.Name() < eh[j].network.Name() - } - - return eh[i].container.config.prio > eh[j].container.config.prio -} - -func (eh epHeap) Swap(i, j int) { eh[i], eh[j] = eh[j], eh[i] } - -func (eh *epHeap) Push(x interface{}) { - *eh = append(*eh, x.(*endpoint)) -} - -func (eh *epHeap) Pop() interface{} { - old := *eh - n := len(old) - x := old[n-1] - *eh = old[0 : n-1] - return x -} - -func (s *sandboxData) updateGateway(ep *endpoint) error { - sb := s.sandbox() - - sb.UnsetGateway() - sb.UnsetGatewayIPv6() - - if ep == nil { - return nil - } - - ep.Lock() - joinInfo := ep.joinInfo - ep.Unlock() - - if err := sb.SetGateway(joinInfo.gw); err != nil { - return fmt.Errorf("failed to set gateway while updating gateway: %v", err) - } - - if err := sb.SetGatewayIPv6(joinInfo.gw6); err != nil { - return fmt.Errorf("failed to set IPv6 gateway while updating gateway: %v", err) - } - - return nil -} - -func (s *sandboxData) addEndpoint(ep *endpoint) error { - ep.Lock() - joinInfo := ep.joinInfo - ifaces := ep.iFaces - ep.Unlock() - - sb := s.sandbox() - for _, i := range ifaces { - var ifaceOptions []sandbox.IfaceOption - - ifaceOptions = append(ifaceOptions, sb.InterfaceOptions().Address(&i.addr), - sb.InterfaceOptions().Routes(i.routes)) - if i.addrv6.IP.To16() != nil { - ifaceOptions = append(ifaceOptions, - sb.InterfaceOptions().AddressIPv6(&i.addrv6)) - } - - if err := sb.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil { - return fmt.Errorf("failed to add interface %s to sandbox: %v", i.srcName, err) - } - } - - if joinInfo != nil { - // Set up non-interface routes. - for _, r := range ep.joinInfo.StaticRoutes { - if err := sb.AddStaticRoute(r); err != nil { - return fmt.Errorf("failed to add static route %s: %v", r.Destination.String(), err) - } - } - } - - s.Lock() - heap.Push(&s.endpoints, ep) - highEp := s.endpoints[0] - s.Unlock() - - if ep == highEp { - if err := s.updateGateway(ep); err != nil { - return err - } - } - - return nil -} - -func (s *sandboxData) rmEndpoint(ep *endpoint) { - ep.Lock() - joinInfo := ep.joinInfo - ep.Unlock() - - sb := s.sandbox() - for _, i := range sb.Info().Interfaces() { - // Only remove the interfaces owned by this endpoint from the sandbox. - if ep.hasInterface(i.SrcName()) { - if err := i.Remove(); err != nil { - logrus.Debugf("Remove interface failed: %v", err) - } - } - } - - // Remove non-interface routes. - for _, r := range joinInfo.StaticRoutes { - if err := sb.RemoveStaticRoute(r); err != nil { - logrus.Debugf("Remove route failed: %v", err) - } - } - - s.Lock() - if len(s.endpoints) == 0 { - // s.endpoints should never be empty and this is unexpected error condition - // We log an error message to note this down for debugging purposes. - logrus.Errorf("No endpoints in sandbox while trying to remove endpoint %s", ep.Name()) - s.Unlock() - return - } - - highEpBefore := s.endpoints[0] - var ( - i int - e *endpoint - ) - for i, e = range s.endpoints { - if e == ep { - break - } - } - heap.Remove(&s.endpoints, i) - var highEpAfter *endpoint - if len(s.endpoints) > 0 { - highEpAfter = s.endpoints[0] - } - - s.Unlock() - - if highEpBefore != highEpAfter { - s.updateGateway(highEpAfter) - } -} - -func (s *sandboxData) sandbox() sandbox.Sandbox { - s.Lock() - defer s.Unlock() - - return s.sbox -} - -func (c *controller) sandboxAdd(key string, create bool, ep *endpoint) (sandbox.Sandbox, error) { - c.Lock() - sData, ok := c.sandboxes[key] - c.Unlock() - - if !ok { - sb, err := sandbox.NewSandbox(key, create) - if err != nil { - return nil, fmt.Errorf("failed to create new sandbox: %v", err) - } - - sData = &sandboxData{ - sbox: sb, - endpoints: epHeap{}, - } - - heap.Init(&sData.endpoints) - c.Lock() - c.sandboxes[key] = sData - c.Unlock() - } - - if err := sData.addEndpoint(ep); err != nil { - return nil, err - } - - return sData.sandbox(), nil -} - -func (c *controller) sandboxRm(key string, ep *endpoint) { - c.Lock() - sData := c.sandboxes[key] - c.Unlock() - - sData.rmEndpoint(ep) -} - -func (c *controller) sandboxGet(key string) sandbox.Sandbox { - c.Lock() - sData, ok := c.sandboxes[key] - c.Unlock() - - if !ok { - return nil - } - - return sData.sandbox() -} - -func (c *controller) LeaveAll(id string) error { - c.Lock() - sData, ok := c.sandboxes[sandbox.GenerateKey(id)] - c.Unlock() - - if !ok { - return fmt.Errorf("could not find sandbox for container id %s", id) - } - - sData.Lock() - eps := make([]*endpoint, len(sData.endpoints)) - for i, ep := range sData.endpoints { - eps[i] = ep - } - sData.Unlock() - - for _, ep := range eps { - if err := ep.Leave(id); err != nil { - logrus.Warnf("Failed leaving endpoint id %s: %v\n", ep.ID(), err) - } - } - - sData.sandbox().Destroy() - - c.Lock() - delete(c.sandboxes, sandbox.GenerateKey(id)) - c.Unlock() - - return nil -} diff --git a/libnetwork/sandboxdata_test.go b/libnetwork/sandboxdata_test.go deleted file mode 100644 index 8084320967..0000000000 --- a/libnetwork/sandboxdata_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package libnetwork - -import ( - "testing" - - "github.com/docker/libnetwork/sandbox" -) - -func createEmptyCtrlr() *controller { - return &controller{sandboxes: sandboxTable{}} -} - -func createEmptyEndpoint() *endpoint { - return &endpoint{ - container: &containerInfo{}, - joinInfo: &endpointJoinInfo{}, - iFaces: []*endpointInterface{}, - } -} - -func TestSandboxAddEmpty(t *testing.T) { - ctrlr := createEmptyCtrlr() - ep := createEmptyEndpoint() - - if _, err := ctrlr.sandboxAdd(sandbox.GenerateKey("sandbox1"), true, ep); err != nil { - t.Fatal(err) - } - - ctrlr.sandboxRm(sandbox.GenerateKey("sandbox1"), ep) - - ctrlr.LeaveAll("sandbox1") - if len(ctrlr.sandboxes) != 0 { - t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes)) - } - - sandbox.GC() -} - -func TestSandboxAddMultiPrio(t *testing.T) { - ctrlr := createEmptyCtrlr() - ep1 := createEmptyEndpoint() - ep2 := createEmptyEndpoint() - ep3 := createEmptyEndpoint() - - ep1.container.config.prio = 1 - ep2.container.config.prio = 2 - ep3.container.config.prio = 3 - - sKey := sandbox.GenerateKey("sandbox1") - - if _, err := ctrlr.sandboxAdd(sKey, true, ep1); err != nil { - t.Fatal(err) - } - - if _, err := ctrlr.sandboxAdd(sKey, true, ep2); err != nil { - t.Fatal(err) - } - - if _, err := ctrlr.sandboxAdd(sKey, true, ep3); err != nil { - t.Fatal(err) - } - - if ctrlr.sandboxes[sKey].endpoints[0] != ep3 { - t.Fatal("Expected ep3 to be at the top of the heap. But did not find ep3 at the top of the heap") - } - - ctrlr.sandboxRm(sKey, ep3) - - if ctrlr.sandboxes[sKey].endpoints[0] != ep2 { - t.Fatal("Expected ep2 to be at the top of the heap after removing ep3. But did not find ep2 at the top of the heap") - } - - ctrlr.sandboxRm(sKey, ep2) - - if ctrlr.sandboxes[sKey].endpoints[0] != ep1 { - t.Fatal("Expected ep1 to be at the top of the heap after removing ep2. But did not find ep1 at the top of the heap") - } - - // Re-add ep3 back - if _, err := ctrlr.sandboxAdd(sKey, true, ep3); err != nil { - t.Fatal(err) - } - - if ctrlr.sandboxes[sKey].endpoints[0] != ep3 { - t.Fatal("Expected ep3 to be at the top of the heap after adding ep3 back. But did not find ep3 at the top of the heap") - } - - ctrlr.sandboxRm(sKey, ep3) - ctrlr.sandboxRm(sKey, ep1) - - if err := ctrlr.LeaveAll("sandbox1"); err != nil { - t.Fatal(err) - } - - if len(ctrlr.sandboxes) != 0 { - t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes)) - } - - sandbox.GC() -} - -func TestSandboxAddSamePrio(t *testing.T) { - ctrlr := createEmptyCtrlr() - ep1 := createEmptyEndpoint() - ep2 := createEmptyEndpoint() - - ep1.network = &network{name: "aaa"} - ep2.network = &network{name: "bbb"} - - sKey := sandbox.GenerateKey("sandbox1") - - if _, err := ctrlr.sandboxAdd(sKey, true, ep1); err != nil { - t.Fatal(err) - } - - if _, err := ctrlr.sandboxAdd(sKey, true, ep2); err != nil { - t.Fatal(err) - } - - if ctrlr.sandboxes[sKey].endpoints[0] != ep1 { - t.Fatal("Expected ep1 to be at the top of the heap. But did not find ep1 at the top of the heap") - } - - ctrlr.sandboxRm(sKey, ep1) - - if ctrlr.sandboxes[sKey].endpoints[0] != ep2 { - t.Fatal("Expected ep2 to be at the top of the heap after removing ep3. But did not find ep2 at the top of the heap") - } - - ctrlr.sandboxRm(sKey, ep2) - - if err := ctrlr.LeaveAll("sandbox1"); err != nil { - t.Fatal(err) - } - - if len(ctrlr.sandboxes) != 0 { - t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes)) - } - - sandbox.GC() -} diff --git a/libnetwork/store.go b/libnetwork/store.go index a832adf01b..e14aa72b01 100644 --- a/libnetwork/store.go +++ b/libnetwork/store.go @@ -7,7 +7,6 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/libkv/store" "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/types" ) func (c *controller) validateDatastoreConfig() bool { @@ -91,7 +90,7 @@ func (c *controller) deleteNetworkFromStore(n *network) error { return nil } -func (c *controller) getNetworkFromStore(nid types.UUID) (*network, error) { +func (c *controller) getNetworkFromStore(nid string) (*network, error) { n := network{id: nid} if err := c.store.GetObject(datastore.Key(n.Key()...), &n); err != nil { return nil, err @@ -105,7 +104,7 @@ func (c *controller) newEndpointFromStore(key string, ep *endpoint) error { id := ep.id ep.Unlock() - _, err := n.EndpointByID(string(id)) + _, err := n.EndpointByID(id) if err != nil { if _, ok := err.(ErrNoSuchEndpoint); ok { return n.addEndpoint(ep) @@ -134,7 +133,7 @@ func (c *controller) updateEndpointToStore(ep *endpoint) error { return cs.PutObjectAtomic(ep) } -func (c *controller) getEndpointFromStore(eid types.UUID) (*endpoint, error) { +func (c *controller) getEndpointFromStore(eid string) (*endpoint, error) { ep := endpoint{id: eid} if err := c.store.GetObject(datastore.Key(ep.Key()...), &ep); err != nil { return nil, err @@ -346,7 +345,7 @@ func (c *controller) processEndpointUpdate(ep *endpoint) bool { if !ok { return true } - existing, _ := n.EndpointByID(string(ep.id)) + existing, _ := n.EndpointByID(ep.id) if existing == nil { return true } @@ -357,13 +356,7 @@ func (c *controller) processEndpointUpdate(ep *endpoint) bool { // Can't use SetIndex() because ee is locked. ee.dbIndex = ep.Index() ee.dbExists = true - if ee.container != nil && ep.container != nil { - // we care only about the container id - ee.container.id = ep.container.id - } else { - // we still care only about the container id, but this is a short-cut to communicate join or leave operation - ee.container = ep.container - } + ee.sandboxID = ep.sandboxID } ee.Unlock()