From da5a3e6dee80f1f5d4059851e4762ffb0484f7e9 Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Wed, 20 May 2015 05:20:19 -0700 Subject: [PATCH 1/2] register libnetwork API and UI with docker parent chain This commit also brings in the ability to specify a default network and its corresponding driver as daemon flags. This helps in existing clients to make use of newer networking features provided by libnetwork. Signed-off-by: Madhu Venugopal --- api/client/network.go | 15 + api/server/server_experimental.go | 12 + api/server/server_linux.go | 1 + api/server/server_stub.go | 6 + api/server/server_windows.go | 1 + daemon/config.go | 2 + daemon/container_linux.go | 37 +- daemon/daemon_unix.go | 36 +- hack/vendor.sh | 2 +- integration-cli/docker_api_containers_test.go | 2 +- integration-cli/docker_api_network_test.go | 72 ++ integration-cli/docker_cli_network_test.go | 54 ++ runconfig/hostconfig.go | 23 + runconfig/parse.go | 4 +- .../github.com/docker/libnetwork/api/api.go | 807 ++++++++++++++++++ .../github.com/docker/libnetwork/api/types.go | 81 ++ .../docker/libnetwork/client/client.go | 115 +++ .../docker/libnetwork/client/network.go | 231 +++++ .../docker/libnetwork/client/service.go | 362 ++++++++ .../docker/libnetwork/client/types.go | 73 ++ .../docker/libnetwork/config/config.go | 21 + .../docker/libnetwork/controller.go | 8 +- .../docker/libnetwork/netlabel/labels.go | 16 +- .../github.com/docker/libnetwork/network.go | 3 +- 24 files changed, 1968 insertions(+), 16 deletions(-) create mode 100644 api/client/network.go create mode 100644 api/server/server_experimental.go create mode 100644 api/server/server_stub.go create mode 100644 integration-cli/docker_api_network_test.go create mode 100644 integration-cli/docker_cli_network_test.go create mode 100644 vendor/src/github.com/docker/libnetwork/api/api.go create mode 100644 vendor/src/github.com/docker/libnetwork/api/types.go create mode 100644 vendor/src/github.com/docker/libnetwork/client/client.go create mode 100644 vendor/src/github.com/docker/libnetwork/client/network.go create mode 100644 vendor/src/github.com/docker/libnetwork/client/service.go create mode 100644 vendor/src/github.com/docker/libnetwork/client/types.go diff --git a/api/client/network.go b/api/client/network.go new file mode 100644 index 0000000000..f7c4e54f90 --- /dev/null +++ b/api/client/network.go @@ -0,0 +1,15 @@ +// +build experimental + +package client + +import ( + "os" + + nwclient "github.com/docker/libnetwork/client" +) + +func (cli *DockerCli) CmdNetwork(args ...string) error { + nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.call)) + args = append([]string{"network"}, args...) + return nCli.Cmd(os.Args[0], args...) +} diff --git a/api/server/server_experimental.go b/api/server/server_experimental.go new file mode 100644 index 0000000000..a93b58af1e --- /dev/null +++ b/api/server/server_experimental.go @@ -0,0 +1,12 @@ +// +build experimental + +package server + +func (s *Server) registerSubRouter() { + httpHandler := s.daemon.NetworkApiRouter() + + subrouter := s.router.PathPrefix("/v{version:[0-9.]+}/networks").Subrouter() + subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler) + subrouter = s.router.PathPrefix("/networks").Subrouter() + subrouter.Methods("GET", "POST", "PUT", "DELETE").HandlerFunc(httpHandler) +} diff --git a/api/server/server_linux.go b/api/server/server_linux.go index 93dd42990a..2ab186e972 100644 --- a/api/server/server_linux.go +++ b/api/server/server_linux.go @@ -70,6 +70,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) { func (s *Server) AcceptConnections(d *daemon.Daemon) { // Tell the init daemon we are accepting requests s.daemon = d + s.registerSubRouter() go systemd.SdNotify("READY=1") // close the lock so the listeners start accepting connections select { diff --git a/api/server/server_stub.go b/api/server/server_stub.go new file mode 100644 index 0000000000..160c292259 --- /dev/null +++ b/api/server/server_stub.go @@ -0,0 +1,6 @@ +// +build !experimental + +package server + +func (s *Server) registerSubRouter() { +} diff --git a/api/server/server_windows.go b/api/server/server_windows.go index 1d32839664..5105b9b82a 100644 --- a/api/server/server_windows.go +++ b/api/server/server_windows.go @@ -45,6 +45,7 @@ func (s *Server) newServer(proto, addr string) ([]serverCloser, error) { func (s *Server) AcceptConnections(d *daemon.Daemon) { s.daemon = d + s.registerSubRouter() // close the lock so the listeners start accepting connections select { case <-s.start: diff --git a/daemon/config.go b/daemon/config.go index d27c111b77..369ac2188a 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -32,6 +32,7 @@ type CommonConfig struct { Pidfile string Root string TrustKeyPath string + DefaultNetwork string } // InstallCommonFlags adds command-line options to the top-level flag parser for @@ -50,6 +51,7 @@ func (config *Config) InstallCommonFlags() { flag.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, "Set the containers network MTU") flag.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, "Enable CORS headers in the remote API, this is deprecated by --api-cors-header") flag.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", "Set CORS headers in the remote API") + flag.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", "Set default network") // FIXME: why the inconsistency between "hosts" and "sockets"? opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use") opts.DnsSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "DNS search domains to use") diff --git a/daemon/container_linux.go b/daemon/container_linux.go index b46e6107cd..9c8945499e 100644 --- a/daemon/container_linux.go +++ b/daemon/container_linux.go @@ -737,17 +737,47 @@ func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointO return createOptions, nil } +func createDefaultNetwork(controller libnetwork.NetworkController) (libnetwork.Network, error) { + createOptions := []libnetwork.NetworkOption{} + genericOption := options.Generic{} + dnet := controller.Config().Daemon.DefaultNetwork + driver := controller.Config().Daemon.DefaultDriver + + // Bridge driver is special due to legacy reasons + if runconfig.NetworkMode(driver).IsBridge() { + genericOption[netlabel.GenericData] = map[string]interface{}{ + "BridgeName": dnet, + "AllowNonDefaultBridge": "true", + } + networkOption := libnetwork.NetworkOptionGeneric(genericOption) + createOptions = append(createOptions, networkOption) + } + + return controller.NewNetwork(driver, dnet, createOptions...) +} + func (container *Container) AllocateNetwork() error { mode := container.hostConfig.NetworkMode + controller := container.daemon.netController if container.Config.NetworkDisabled || mode.IsContainer() { return nil } + networkName := mode.NetworkName() + if mode.IsDefault() { + networkName = controller.Config().Daemon.DefaultNetwork + } + var err error - n, err := container.daemon.netController.NetworkByName(string(mode)) + n, err := controller.NetworkByName(networkName) if err != nil { - return fmt.Errorf("error locating network with name %s: %v", string(mode), err) + if !mode.IsDefault() { + return fmt.Errorf("error locating network with name %s: %v", networkName, err) + } + if n, err = createDefaultNetwork(controller); err != nil { + return err + } } createOptions, err := container.buildCreateEndpointOptions() @@ -790,9 +820,8 @@ func (container *Container) initializeNetworking() error { // Make sure NetworkMode has an acceptable value before // initializing networking. if container.hostConfig.NetworkMode == runconfig.NetworkMode("") { - container.hostConfig.NetworkMode = runconfig.NetworkMode("bridge") + container.hostConfig.NetworkMode = runconfig.NetworkMode("default") } - if container.hostConfig.NetworkMode.IsContainer() { // we need to get the hosts files from the container to join nc, err := container.getNetworkedContainer() diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index 3a2e62106d..0275675c21 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -5,6 +5,7 @@ package daemon import ( "fmt" "net" + "net/http" "os" "path/filepath" "runtime" @@ -24,6 +25,8 @@ import ( "github.com/docker/docker/volume/local" "github.com/docker/libcontainer/label" "github.com/docker/libnetwork" + nwapi "github.com/docker/libnetwork/api" + nwconfig "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/options" ) @@ -264,8 +267,35 @@ func isNetworkDisabled(config *Config) bool { return config.Bridge.Iface == disableNetworkBridge } +func networkOptions(dconfig *Config) ([]nwconfig.Option, error) { + options := []nwconfig.Option{} + if dconfig == nil { + return options, nil + } + if strings.TrimSpace(dconfig.DefaultNetwork) != "" { + dn := strings.Split(dconfig.DefaultNetwork, ":") + if len(dn) < 2 { + return nil, fmt.Errorf("default network daemon config must be of the form NETWORKDRIVER:NETWORKNAME") + } + options = append(options, nwconfig.OptionDefaultDriver(dn[0])) + options = append(options, nwconfig.OptionDefaultNetwork(strings.Join(dn[1:], ":"))) + } else { + dd := runconfig.DefaultDaemonNetworkMode() + dn := runconfig.DefaultDaemonNetworkMode().NetworkName() + options = append(options, nwconfig.OptionDefaultDriver(string(dd))) + options = append(options, nwconfig.OptionDefaultNetwork(dn)) + } + options = append(options, nwconfig.OptionLabels(dconfig.Labels)) + return options, nil +} + func initNetworkController(config *Config) (libnetwork.NetworkController, error) { - controller, err := libnetwork.New() + netOptions, err := networkOptions(config) + if err != nil { + return nil, err + } + + controller, err := libnetwork.New(netOptions...) if err != nil { return nil, fmt.Errorf("error obtaining controller instance: %v", err) } @@ -419,3 +449,7 @@ func setupInitLayer(initLayer string) error { // Layer is ready to use, if it wasn't before. return nil } + +func (daemon *Daemon) NetworkApiRouter() func(w http.ResponseWriter, req *http.Request) { + return nwapi.NewHTTPHandler(daemon.netController) +} diff --git a/hack/vendor.sh b/hack/vendor.sh index faf08a6c41..e422cabe00 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -18,7 +18,7 @@ clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://gith clone hg code.google.com/p/gosqlite 74691fb6f837 #get libnetwork packages -clone git github.com/docker/libnetwork 3be488927db8d719568917203deddd630a194564 +clone git github.com/docker/libnetwork fc7abaa93fd33a77cc37845adbbc4adf03676dd5 clone git github.com/docker/libkv e8cde779d58273d240c1eff065352a6cd67027dd clone git github.com/vishvananda/netns 5478c060110032f972e86a1f844fdb9a2f008f2c clone git github.com/vishvananda/netlink 8eb64238879fed52fd51c5b30ad20b928fb4c36c diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index baee2e57c1..98f8a6c52a 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -869,7 +869,7 @@ func (s *DockerSuite) TestContainerApiCreate(c *check.C) { out, err := exec.Command(dockerBinary, "start", "-a", container.Id).CombinedOutput() if err != nil { - c.Fatal(out, err) + c.Fatal(string(out), err) } if strings.TrimSpace(string(out)) != "/test" { c.Fatalf("expected output `/test`, got %q", out) diff --git a/integration-cli/docker_api_network_test.go b/integration-cli/docker_api_network_test.go new file mode 100644 index 0000000000..44d2b31bb7 --- /dev/null +++ b/integration-cli/docker_api_network_test.go @@ -0,0 +1,72 @@ +// +build experimental + +package main + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/go-check/check" +) + +func isNetworkAvailable(c *check.C, name string) bool { + status, body, err := sockRequest("GET", "/networks", nil) + c.Assert(status, check.Equals, http.StatusOK) + c.Assert(err, check.IsNil) + + var inspectJSON []struct { + Name string + ID string + Type string + } + if err = json.Unmarshal(body, &inspectJSON); err != nil { + c.Fatalf("unable to unmarshal response body: %v", err) + } + for _, n := range inspectJSON { + if n.Name == name { + return true + } + } + return false + +} + +func (s *DockerSuite) TestNetworkApiGetAll(c *check.C) { + defaults := []string{"bridge", "host", "none"} + for _, nn := range defaults { + if !isNetworkAvailable(c, nn) { + c.Fatalf("Missing Default network : %s", nn) + } + } +} + +func (s *DockerSuite) TestNetworkApiCreateDelete(c *check.C) { + name := "testnetwork" + config := map[string]interface{}{ + "name": name, + "network_type": "bridge", + } + + status, resp, err := sockRequest("POST", "/networks", config) + c.Assert(status, check.Equals, http.StatusCreated) + c.Assert(err, check.IsNil) + + if !isNetworkAvailable(c, name) { + c.Fatalf("Network %s not found", name) + } + + var id string + err = json.Unmarshal(resp, &id) + if err != nil { + c.Fatal(err) + } + + status, _, err = sockRequest("DELETE", fmt.Sprintf("/networks/%s", id), nil) + c.Assert(status, check.Equals, http.StatusOK) + c.Assert(err, check.IsNil) + + if isNetworkAvailable(c, name) { + c.Fatalf("Network %s not deleted", name) + } +} diff --git a/integration-cli/docker_cli_network_test.go b/integration-cli/docker_cli_network_test.go new file mode 100644 index 0000000000..820a0ae8a9 --- /dev/null +++ b/integration-cli/docker_cli_network_test.go @@ -0,0 +1,54 @@ +// +build experimental + +package main + +import ( + "os/exec" + "strings" + + "github.com/go-check/check" +) + +func isNetworkPresent(c *check.C, name string) bool { + runCmd := exec.Command(dockerBinary, "network", "ls") + out, _, _, err := runCommandWithStdoutStderr(runCmd) + if err != nil { + c.Fatal(out, err) + } + lines := strings.Split(out, "\n") + for i := 1; i < len(lines)-1; i++ { + if strings.Contains(lines[i], name) { + return true + } + } + return false +} + +func (s *DockerSuite) TestDockerNetworkLsDefault(c *check.C) { + defaults := []string{"bridge", "host", "none"} + for _, nn := range defaults { + if !isNetworkPresent(c, nn) { + c.Fatalf("Missing Default network : %s", nn) + } + } +} + +func (s *DockerSuite) TestDockerNetworkCreateDelete(c *check.C) { + runCmd := exec.Command(dockerBinary, "network", "create", "test") + out, _, _, err := runCommandWithStdoutStderr(runCmd) + if err != nil { + c.Fatal(out, err) + } + if !isNetworkPresent(c, "test") { + c.Fatalf("Network test not found") + } + + runCmd = exec.Command(dockerBinary, "network", "rm", "test") + out, _, _, err = runCommandWithStdoutStderr(runCmd) + if err != nil { + c.Fatal(out, err) + } + if isNetworkPresent(c, "test") { + c.Fatalf("Network test is not removed") + } +} diff --git a/runconfig/hostconfig.go b/runconfig/hostconfig.go index 1418dea4a8..3092336404 100644 --- a/runconfig/hostconfig.go +++ b/runconfig/hostconfig.go @@ -21,6 +21,29 @@ func (n NetworkMode) IsPrivate() bool { return !(n.IsHost() || n.IsContainer()) } +func (n NetworkMode) IsDefault() bool { + return n == "default" +} + +func DefaultDaemonNetworkMode() NetworkMode { + return NetworkMode("bridge") +} + +func (n NetworkMode) NetworkName() string { + if n.IsBridge() { + return "bridge" + } else if n.IsHost() { + return "host" + } else if n.IsContainer() { + return "container" + } else if n.IsNone() { + return "none" + } else if n.IsDefault() { + return "default" + } + return "" +} + func (n NetworkMode) IsBridge() bool { return n == "bridge" } diff --git a/runconfig/parse.go b/runconfig/parse.go index 3ea48a5a3b..b622c5eec8 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -72,7 +72,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe flCpusetMems = cmd.String([]string{"-cpuset-mems"}, "", "MEMs in which to allow execution (0-3, 0,1)") flCpuQuota = cmd.Int64([]string{"-cpu-quota"}, 0, "Limit the CPU CFS quota") flBlkioWeight = cmd.Int64([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000") - flNetMode = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container") + flNetMode = cmd.String([]string{"-net"}, "default", "Set the Network mode for the container") flMacAddress = cmd.String([]string{"-mac-address"}, "", "Container MAC address (e.g. 92:d0:c6:0a:29:33)") flIpcMode = cmd.String([]string{"-ipc"}, "", "IPC namespace to use") flRestartPolicy = cmd.String([]string{"-restart"}, "no", "Restart policy to apply when a container exits") @@ -485,7 +485,7 @@ func parseKeyValueOpts(opts opts.ListOpts) ([]KeyValuePair, error) { func parseNetMode(netMode string) (NetworkMode, error) { parts := strings.Split(netMode, ":") switch mode := parts[0]; mode { - case "bridge", "none", "host": + case "default", "bridge", "none", "host": case "container": if len(parts) < 2 || parts[1] == "" { return "", fmt.Errorf("invalid container format container:") diff --git a/vendor/src/github.com/docker/libnetwork/api/api.go b/vendor/src/github.com/docker/libnetwork/api/api.go new file mode 100644 index 0000000000..2b5b577354 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/api/api.go @@ -0,0 +1,807 @@ +package api + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/types" + "github.com/gorilla/mux" +) + +var ( + successResponse = responseStatus{Status: "Success", StatusCode: http.StatusOK} + createdResponse = responseStatus{Status: "Created", StatusCode: http.StatusCreated} + mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest} + badQueryResponse = responseStatus{Status: "Unsupported query", StatusCode: http.StatusBadRequest} +) + +const ( + // Resource name regex + regex = "[a-zA-Z_0-9-]+" + // Router URL variable definition + nwName = "{" + urlNwName + ":" + regex + "}" + nwID = "{" + urlNwID + ":" + regex + "}" + nwPID = "{" + urlNwPID + ":" + regex + "}" + epName = "{" + urlEpName + ":" + regex + "}" + epID = "{" + urlEpID + ":" + regex + "}" + epPID = "{" + urlEpPID + ":" + regex + "}" + cnID = "{" + urlCnID + ":" + regex + "}" + + // Though this name can be anything, in order to support default network, + // we will keep it as name + urlNwName = "name" + // Internal URL variable name, they can be anything + urlNwID = "network-id" + urlNwPID = "network-partial-id" + urlEpName = "endpoint-name" + urlEpID = "endpoint-id" + urlEpPID = "endpoint-partial-id" + urlCnID = "container-id" + + // BridgeNetworkDriver is the built-in default for Network Driver + BridgeNetworkDriver = "bridge" +) + +// NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork +func NewHTTPHandler(c libnetwork.NetworkController) func(w http.ResponseWriter, req *http.Request) { + h := &httpHandler{c: c} + h.initRouter() + return h.handleRequest +} + +type responseStatus struct { + Status string + StatusCode int +} + +func (r *responseStatus) isOK() bool { + return r.StatusCode == http.StatusOK || r.StatusCode == http.StatusCreated +} + +type processor func(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) + +type httpHandler struct { + c libnetwork.NetworkController + r *mux.Router +} + +func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) { + // Make sure the service is there + if h.c == nil { + http.Error(w, "NetworkController is not available", http.StatusServiceUnavailable) + return + } + + // Get handler from router and execute it + h.r.ServeHTTP(w, req) +} + +func (h *httpHandler) initRouter() { + m := map[string][]struct { + url string + qrs []string + fct processor + }{ + "GET": { + // Order matters + {"/networks", []string{"name", nwName}, procGetNetworks}, + {"/networks", []string{"partial-id", nwPID}, procGetNetworks}, + {"/networks", nil, procGetNetworks}, + {"/networks/" + nwID, nil, procGetNetwork}, + {"/networks/" + nwID + "/endpoints", []string{"name", epName}, procGetEndpoints}, + {"/networks/" + nwID + "/endpoints", []string{"partial-id", epPID}, procGetEndpoints}, + {"/networks/" + nwID + "/endpoints", nil, procGetEndpoints}, + {"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint}, + {"/services", []string{"network", nwName}, procGetServices}, + {"/services", []string{"name", epName}, procGetServices}, + {"/services", []string{"partial-id", epPID}, procGetServices}, + {"/services", nil, procGetServices}, + {"/services/" + epID, nil, procGetService}, + {"/services/" + epID + "/backend", nil, procGetContainers}, + }, + "POST": { + {"/networks", nil, procCreateNetwork}, + {"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint}, + {"/networks/" + nwID + "/endpoints/" + epID + "/containers", nil, procJoinEndpoint}, + {"/services", nil, procPublishService}, + {"/services/" + epID + "/backend", nil, procAttachBackend}, + }, + "DELETE": { + {"/networks/" + nwID, nil, procDeleteNetwork}, + {"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint}, + {"/networks/" + nwID + "/endpoints/" + epID + "/containers/" + cnID, nil, procLeaveEndpoint}, + {"/services/" + epID, nil, procUnpublishService}, + {"/services/" + epID + "/backend/" + cnID, nil, procDetachBackend}, + }, + } + + h.r = mux.NewRouter() + for method, routes := range m { + for _, route := range routes { + r := h.r.Path("/{.*}" + route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct)) + if route.qrs != nil { + r.Queries(route.qrs...) + } + + r = h.r.Path(route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct)) + if route.qrs != nil { + r.Queries(route.qrs...) + } + } + } +} + +func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerFunc { + return func(w http.ResponseWriter, req *http.Request) { + var ( + body []byte + err error + ) + if req.Body != nil { + body, err = ioutil.ReadAll(req.Body) + if err != nil { + http.Error(w, "Invalid body: "+err.Error(), http.StatusBadRequest) + return + } + } + + mvars := mux.Vars(req) + rvars := req.URL.Query() + // workaround a mux issue which filters out valid queries with empty value + for k := range rvars { + if _, ok := mvars[k]; !ok { + if rvars.Get(k) == "" { + mvars[k] = "" + } + } + } + + res, rsp := fct(ctrl, mvars, body) + if !rsp.isOK() { + http.Error(w, rsp.Status, rsp.StatusCode) + return + } + if res != nil { + writeJSON(w, rsp.StatusCode, res) + } + } +} + +/***************** + Resource Builders +******************/ + +func buildNetworkResource(nw libnetwork.Network) *networkResource { + r := &networkResource{} + if nw != nil { + r.Name = nw.Name() + r.ID = nw.ID() + r.Type = nw.Type() + epl := nw.Endpoints() + r.Endpoints = make([]*endpointResource, 0, len(epl)) + for _, e := range epl { + epr := buildEndpointResource(e) + r.Endpoints = append(r.Endpoints, epr) + } + } + return r +} + +func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource { + r := &endpointResource{} + if ep != nil { + r.Name = ep.Name() + r.ID = ep.ID() + r.Network = ep.Network() + } + return r +} + +func buildContainerResource(ci libnetwork.ContainerInfo) *containerResource { + r := &containerResource{} + if ci != nil { + r.ID = ci.ID() + } + return r +} + +/**************** + Options Parsers +*****************/ + +func (nc *networkCreate) parseOptions() []libnetwork.NetworkOption { + var setFctList []libnetwork.NetworkOption + + if nc.Options != nil { + setFctList = append(setFctList, libnetwork.NetworkOptionGeneric(nc.Options)) + } + + return setFctList +} + +func (ej *endpointJoin) parseOptions() []libnetwork.EndpointOption { + var setFctList []libnetwork.EndpointOption + if ej.HostName != "" { + setFctList = append(setFctList, libnetwork.JoinOptionHostname(ej.HostName)) + } + if ej.DomainName != "" { + setFctList = append(setFctList, libnetwork.JoinOptionDomainname(ej.DomainName)) + } + if ej.HostsPath != "" { + setFctList = append(setFctList, libnetwork.JoinOptionHostsPath(ej.HostsPath)) + } + if ej.ResolvConfPath != "" { + setFctList = append(setFctList, libnetwork.JoinOptionResolvConfPath(ej.ResolvConfPath)) + } + if ej.UseDefaultSandbox { + setFctList = append(setFctList, libnetwork.JoinOptionUseDefaultSandbox()) + } + if ej.DNS != nil { + for _, d := range ej.DNS { + setFctList = append(setFctList, libnetwork.JoinOptionDNS(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)) + } + } + return setFctList +} + +/****************** + Process functions +*******************/ + +func processCreateDefaults(c libnetwork.NetworkController, nc *networkCreate) { + if nc.NetworkType == "" { + nc.NetworkType = c.Config().Daemon.DefaultDriver + } + if nc.NetworkType == BridgeNetworkDriver { + if nc.Options == nil { + nc.Options = make(map[string]interface{}) + } + genericData, ok := nc.Options[netlabel.GenericData] + if !ok { + genericData = make(map[string]interface{}) + } + gData := genericData.(map[string]interface{}) + + if _, ok := gData["BridgeName"]; !ok { + gData["BridgeName"] = nc.Name + } + if _, ok := gData["AllowNonDefaultBridge"]; !ok { + gData["AllowNonDefaultBridge"] = "true" + } + nc.Options[netlabel.GenericData] = genericData + } +} + +/*************************** + NetworkController interface +****************************/ +func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var create networkCreate + + err := json.Unmarshal(body, &create) + if err != nil { + return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} + } + processCreateDefaults(c, &create) + + nw, err := c.NewNetwork(create.NetworkType, create.Name, create.parseOptions()...) + if err != nil { + return "", convertNetworkError(err) + } + + return nw.ID(), &createdResponse +} + +func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + t, by := detectNetworkTarget(vars) + nw, errRsp := findNetwork(c, t, by) + if !errRsp.isOK() { + return nil, errRsp + } + return buildNetworkResource(nw), &successResponse +} + +func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var list []*networkResource + + // Look for query filters and validate + name, queryByName := vars[urlNwName] + shortID, queryByPid := vars[urlNwPID] + if queryByName && queryByPid { + return nil, &badQueryResponse + } + + if queryByName { + if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() { + list = append(list, buildNetworkResource(nw)) + } + } else if queryByPid { + // Return all the prefix-matching networks + l := func(nw libnetwork.Network) bool { + if strings.HasPrefix(nw.ID(), shortID) { + list = append(list, buildNetworkResource(nw)) + } + return false + } + c.WalkNetworks(l) + } else { + for _, nw := range c.Networks() { + list = append(list, buildNetworkResource(nw)) + } + } + + return list, &successResponse +} + +/****************** + Network interface +*******************/ +func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var ec endpointCreate + + err := json.Unmarshal(body, &ec) + if err != nil { + return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} + } + + nwT, nwBy := detectNetworkTarget(vars) + n, errRsp := findNetwork(c, nwT, nwBy) + if !errRsp.isOK() { + return "", errRsp + } + + var setFctList []libnetwork.EndpointOption + if ec.ExposedPorts != nil { + setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts)) + } + if ec.PortMapping != nil { + setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping)) + } + + ep, err := n.CreateEndpoint(ec.Name, setFctList...) + if err != nil { + return "", convertNetworkError(err) + } + + return ep.ID(), &createdResponse +} + +func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + nwT, nwBy := detectNetworkTarget(vars) + epT, epBy := detectEndpointTarget(vars) + + ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) + if !errRsp.isOK() { + return nil, errRsp + } + + return buildEndpointResource(ep), &successResponse +} + +func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + // Look for query filters and validate + name, queryByName := vars[urlEpName] + shortID, queryByPid := vars[urlEpPID] + if queryByName && queryByPid { + return nil, &badQueryResponse + } + + nwT, nwBy := detectNetworkTarget(vars) + nw, errRsp := findNetwork(c, nwT, nwBy) + if !errRsp.isOK() { + return nil, errRsp + } + + var list []*endpointResource + + // If query parameter is specified, return a filtered collection + if queryByName { + if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() { + list = append(list, buildEndpointResource(ep)) + } + } else if queryByPid { + // Return all the prefix-matching endpoints + l := func(ep libnetwork.Endpoint) bool { + if strings.HasPrefix(ep.ID(), shortID) { + list = append(list, buildEndpointResource(ep)) + } + return false + } + nw.WalkEndpoints(l) + } else { + for _, ep := range nw.Endpoints() { + epr := buildEndpointResource(ep) + list = append(list, epr) + } + } + + return list, &successResponse +} + +func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + target, by := detectNetworkTarget(vars) + + nw, errRsp := findNetwork(c, target, by) + if !errRsp.isOK() { + return nil, errRsp + } + + err := nw.Delete() + if err != nil { + return nil, convertNetworkError(err) + } + + return nil, &successResponse +} + +/****************** + Endpoint interface +*******************/ +func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var ej endpointJoin + err := json.Unmarshal(body, &ej) + if err != nil { + return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} + } + + nwT, nwBy := detectNetworkTarget(vars) + epT, epBy := detectEndpointTarget(vars) + + ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) + if !errRsp.isOK() { + return nil, errRsp + } + + err = ep.Join(ej.ContainerID, ej.parseOptions()...) + if err != nil { + return nil, convertNetworkError(err) + } + return ep.Info().SandboxKey(), &successResponse +} + +func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + nwT, nwBy := detectNetworkTarget(vars) + epT, epBy := detectEndpointTarget(vars) + + ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) + if !errRsp.isOK() { + return nil, errRsp + } + + err := ep.Leave(vars[urlCnID]) + if err != nil { + return nil, convertNetworkError(err) + } + + return nil, &successResponse +} + +func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + nwT, nwBy := detectNetworkTarget(vars) + epT, epBy := detectEndpointTarget(vars) + + ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy) + if !errRsp.isOK() { + return nil, errRsp + } + + err := ep.Delete() + if err != nil { + return nil, convertNetworkError(err) + } + + return nil, &successResponse +} + +/****************** + Service interface +*******************/ +func procGetServices(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + // Look for query filters and validate + nwName, filterByNwName := vars[urlNwName] + svName, queryBySvName := vars[urlEpName] + shortID, queryBySvPID := vars[urlEpPID] + + if filterByNwName && queryBySvName || filterByNwName && queryBySvPID || queryBySvName && queryBySvPID { + return nil, &badQueryResponse + } + + var list []*endpointResource + + switch { + case filterByNwName: + // return all service present on the specified network + nw, errRsp := findNetwork(c, nwName, byName) + if !errRsp.isOK() { + return list, &successResponse + } + for _, ep := range nw.Endpoints() { + epr := buildEndpointResource(ep) + list = append(list, epr) + } + case queryBySvName: + // Look in each network for the service with the specified name + l := func(ep libnetwork.Endpoint) bool { + if ep.Name() == svName { + list = append(list, buildEndpointResource(ep)) + return true + } + return false + } + for _, nw := range c.Networks() { + nw.WalkEndpoints(l) + } + case queryBySvPID: + // Return all the prefix-matching services + l := func(ep libnetwork.Endpoint) bool { + if strings.HasPrefix(ep.ID(), shortID) { + list = append(list, buildEndpointResource(ep)) + } + return false + } + for _, nw := range c.Networks() { + nw.WalkEndpoints(l) + } + default: + for _, nw := range c.Networks() { + for _, ep := range nw.Endpoints() { + epr := buildEndpointResource(ep) + list = append(list, epr) + } + } + } + + return list, &successResponse +} + +func procGetService(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) + } + 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 + + err := json.Unmarshal(body, &sp) + if err != nil { + return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} + } + + n, errRsp := findNetwork(c, sp.Network, byName) + if !errRsp.isOK() { + return "", errRsp + } + + var setFctList []libnetwork.EndpointOption + if sp.ExposedPorts != nil { + setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(sp.ExposedPorts)) + } + if sp.PortMapping != nil { + setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(sp.PortMapping)) + } + + ep, err := n.CreateEndpoint(sp.Name, setFctList...) + if err != nil { + return "", endpointToService(convertNetworkError(err)) + } + + return ep.ID(), &createdResponse +} + +func procUnpublishService(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, errRsp + } + err := sv.Delete() + if err != nil { + return nil, endpointToService(convertNetworkError(err)) + } + return nil, &successResponse +} + +func procAttachBackend(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) { + var bk endpointJoin + err := json.Unmarshal(body, &bk) + if err != nil { + return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest} + } + + epT, epBy := detectEndpointTarget(vars) + sv, errRsp := findService(c, epT, epBy) + if !errRsp.isOK() { + return nil, errRsp + } + + err = sv.Join(bk.ContainerID, bk.parseOptions()...) + if err != nil { + return nil, convertNetworkError(err) + } + return sv.Info().SandboxKey(), &successResponse +} + +func procDetachBackend(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, errRsp + } + + err := sv.Leave(vars[urlCnID]) + if err != nil { + return nil, convertNetworkError(err) + } + + return nil, &successResponse +} + +/*********** + Utilities +************/ +const ( + byID = iota + byName +) + +func detectNetworkTarget(vars map[string]string) (string, int) { + if target, ok := vars[urlNwName]; ok { + return target, byName + } + if target, ok := vars[urlNwID]; ok { + return target, byID + } + // vars are populated from the URL, following cannot happen + panic("Missing URL variable parameter for network") +} + +func detectEndpointTarget(vars map[string]string) (string, int) { + if target, ok := vars[urlEpName]; ok { + return target, byName + } + if target, ok := vars[urlEpID]; ok { + return target, byID + } + // vars are populated from the URL, following cannot happen + panic("Missing URL variable parameter for endpoint") +} + +func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) { + var ( + nw libnetwork.Network + err error + ) + switch by { + case byID: + nw, err = c.NetworkByID(s) + case byName: + if s == "" { + s = c.Config().Daemon.DefaultNetwork + } + nw, err = c.NetworkByName(s) + default: + panic(fmt.Sprintf("unexpected selector for network search: %d", by)) + } + if err != nil { + if _, ok := err.(types.NotFoundError); ok { + return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound} + } + return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} + } + return nw, &successResponse +} + +func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) { + nw, errRsp := findNetwork(c, ns, nwBy) + if !errRsp.isOK() { + return nil, errRsp + } + var ( + err error + ep libnetwork.Endpoint + ) + switch epBy { + case byID: + ep, err = nw.EndpointByID(es) + case byName: + ep, err = nw.EndpointByName(es) + default: + panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy)) + } + if err != nil { + if _, ok := err.(types.NotFoundError); ok { + return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound} + } + return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest} + } + return ep, &successResponse +} + +func findService(c libnetwork.NetworkController, svs string, svBy int) (libnetwork.Endpoint, *responseStatus) { + for _, nw := range c.Networks() { + var ( + ep libnetwork.Endpoint + err error + ) + switch svBy { + case byID: + ep, err = nw.EndpointByID(svs) + case byName: + ep, err = nw.EndpointByName(svs) + default: + panic(fmt.Sprintf("unexpected selector for service search: %d", svBy)) + } + if err == nil { + return ep, &successResponse + } else if _, ok := err.(types.NotFoundError); !ok { + return nil, convertNetworkError(err) + } + } + return nil, &responseStatus{Status: "Service not found", StatusCode: http.StatusNotFound} +} + +func endpointToService(rsp *responseStatus) *responseStatus { + rsp.Status = strings.Replace(rsp.Status, "endpoint", "service", -1) + return rsp +} + +func convertNetworkError(err error) *responseStatus { + var code int + switch err.(type) { + case types.BadRequestError: + code = http.StatusBadRequest + case types.ForbiddenError: + code = http.StatusForbidden + case types.NotFoundError: + code = http.StatusNotFound + case types.TimeoutError: + code = http.StatusRequestTimeout + case types.NotImplementedError: + code = http.StatusNotImplemented + case types.NoServiceError: + code = http.StatusServiceUnavailable + case types.InternalError: + code = http.StatusInternalServerError + default: + code = http.StatusInternalServerError + } + return &responseStatus{Status: err.Error(), StatusCode: code} +} + +func writeJSON(w http.ResponseWriter, code int, v interface{}) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + return json.NewEncoder(w).Encode(v) +} diff --git a/vendor/src/github.com/docker/libnetwork/api/types.go b/vendor/src/github.com/docker/libnetwork/api/types.go new file mode 100644 index 0000000000..72f20db24f --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/api/types.go @@ -0,0 +1,81 @@ +package api + +import "github.com/docker/libnetwork/types" + +/*********** + Resources +************/ + +// networkResource is the body of the "get network" http response message +type networkResource struct { + Name string `json:"name"` + ID string `json:"id"` + Type string `json:"type"` + Endpoints []*endpointResource `json:"endpoints"` +} + +// endpointResource is the body of the "get endpoint" http response message +type endpointResource struct { + Name string `json:"name"` + ID string `json:"id"` + Network string `json:"network"` +} + +// containerResource is the body of "get service backend" response message +type containerResource struct { + ID string `json:"id"` + // will add more fields once labels change is in +} + +/*********** + Body types + ************/ + +// networkCreate is the expected body of the "create network" http request message +type networkCreate struct { + Name string `json:"name"` + NetworkType string `json:"network_type"` + Options map[string]interface{} `json:"options"` +} + +// endpointCreate represents the body of the "create endpoint" http request message +type endpointCreate struct { + Name string `json:"name"` + ExposedPorts []types.TransportPort `json:"exposed_ports"` + PortMapping []types.PortBinding `json:"port_mapping"` +} + +// 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"` +} + +// servicePublish represents the body of the "publish service" http request message +type servicePublish struct { + Name string `json:"name"` + Network string `json:"network_name"` + ExposedPorts []types.TransportPort `json:"exposed_ports"` + PortMapping []types.PortBinding `json:"port_mapping"` +} + +// EndpointExtraHost represents the extra host object +type endpointExtraHost 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/vendor/src/github.com/docker/libnetwork/client/client.go b/vendor/src/github.com/docker/libnetwork/client/client.go new file mode 100644 index 0000000000..c5713b01b3 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/client/client.go @@ -0,0 +1,115 @@ +package client + +import ( + "fmt" + "io" + "io/ioutil" + "net/http" + "reflect" + "strings" + + flag "github.com/docker/docker/pkg/mflag" +) + +// CallFunc provides environment specific call utility to invoke backend functions from UI +type CallFunc func(string, string, interface{}, map[string][]string) (io.ReadCloser, http.Header, int, error) + +// NetworkCli is the UI object for network subcmds +type NetworkCli struct { + out io.Writer + err io.Writer + call CallFunc +} + +// NewNetworkCli is a convenient function to create a NetworkCli object +func NewNetworkCli(out, err io.Writer, call CallFunc) *NetworkCli { + return &NetworkCli{ + out: out, + err: err, + call: call, + } +} + +// getMethod is Borrowed from Docker UI which uses reflection to identify the UI Handler +func (cli *NetworkCli) getMethod(args ...string) (func(string, ...string) error, bool) { + camelArgs := make([]string, len(args)) + for i, s := range args { + if len(s) == 0 { + return nil, false + } + camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:]) + } + methodName := "Cmd" + strings.Join(camelArgs, "") + method := reflect.ValueOf(cli).MethodByName(methodName) + if !method.IsValid() { + return nil, false + } + return method.Interface().(func(string, ...string) error), true +} + +// Cmd is borrowed from Docker UI and acts as the entry point for network UI commands. +// network UI commands are designed to be invoked from multiple parent chains +func (cli *NetworkCli) Cmd(chain string, args ...string) error { + if len(args) > 2 { + method, exists := cli.getMethod(args[:3]...) + if exists { + return method(chain+" "+args[0]+" "+args[1], args[3:]...) + } + } + if len(args) > 1 { + method, exists := cli.getMethod(args[:2]...) + if exists { + return method(chain+" "+args[0], args[2:]...) + } + } + if len(args) > 0 { + method, exists := cli.getMethod(args[0]) + if !exists { + return fmt.Errorf("%s: '%s' is not a %s command. See '%s --help'.\n", chain, args[0], chain, chain) + } + return method(chain, args[1:]...) + } + flag.Usage() + return nil +} + +// Subcmd is borrowed from Docker UI and performs the same function of configuring the subCmds +func (cli *NetworkCli) Subcmd(chain, name, signature, description string, exitOnError bool) *flag.FlagSet { + var errorHandling flag.ErrorHandling + if exitOnError { + errorHandling = flag.ExitOnError + } else { + errorHandling = flag.ContinueOnError + } + flags := flag.NewFlagSet(name, errorHandling) + flags.Usage = func() { + flags.ShortUsage() + flags.PrintDefaults() + } + flags.ShortUsage = func() { + options := "" + if signature != "" { + signature = " " + signature + } + if flags.FlagCountUndeprecated() > 0 { + options = " [OPTIONS]" + } + fmt.Fprintf(cli.out, "\nUsage: %s %s%s%s\n\n%s\n\n", chain, name, options, signature, description) + flags.SetOutput(cli.out) + } + return flags +} + +func readBody(stream io.ReadCloser, hdr http.Header, statusCode int, err error) ([]byte, int, error) { + if stream != nil { + defer stream.Close() + } + if err != nil { + return nil, statusCode, err + } + body, err := ioutil.ReadAll(stream) + if err != nil { + return nil, -1, err + } + return body, statusCode, nil +} diff --git a/vendor/src/github.com/docker/libnetwork/client/network.go b/vendor/src/github.com/docker/libnetwork/client/network.go new file mode 100644 index 0000000000..9d9ce71c3f --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/client/network.go @@ -0,0 +1,231 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "text/tabwriter" + + flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/pkg/stringid" +) + +type command struct { + name string + description string +} + +var ( + networkCommands = []command{ + {"create", "Create a network"}, + {"rm", "Remove a network"}, + {"ls", "List all networks"}, + {"info", "Display information of a network"}, + } +) + +// CmdNetwork handles the root Network UI +func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "network", "COMMAND [OPTIONS] [arg...]", networkUsage(chain), false) + cmd.Require(flag.Min, 1) + err := cmd.ParseFlags(args, true) + if err == nil { + cmd.Usage() + return fmt.Errorf("invalid command : %v", args) + } + return err +} + +// CmdNetworkCreate handles Network Create UI +func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "create", "NETWORK-NAME", "Creates a new network with a name specified by the user", false) + flDriver := cmd.String([]string{"d", "-driver"}, "", "Driver to manage the Network") + cmd.Require(flag.Exact, 1) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + + // Construct network create request body + ops := make(map[string]interface{}) + nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, Options: ops} + obj, _, err := readBody(cli.call("POST", "/networks", nc, nil)) + if err != nil { + return err + } + var replyID string + err = json.Unmarshal(obj, &replyID) + if err != nil { + return err + } + fmt.Fprintf(cli.out, "%s\n", replyID) + return nil +} + +// CmdNetworkRm handles Network Delete UI +func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "rm", "NETWORK", "Deletes a network", false) + cmd.Require(flag.Exact, 1) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + id, err := lookupNetworkID(cli, cmd.Arg(0)) + if err != nil { + return err + } + _, _, err = readBody(cli.call("DELETE", "/networks/"+id, nil, nil)) + if err != nil { + return err + } + return nil +} + +// CmdNetworkLs handles Network List UI +func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "ls", "", "Lists all the networks created by the user", false) + quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") + noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output") + nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created") + last := cmd.Int([]string{"n"}, -1, "Show n last created networks") + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + obj, _, err := readBody(cli.call("GET", "/networks", nil, nil)) + if err != nil { + return err + } + if *last == -1 && *nLatest { + *last = 1 + } + + var networkResources []networkResource + err = json.Unmarshal(obj, &networkResources) + if err != nil { + return err + } + + wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) + + // unless quiet (-q) is specified, print field titles + if !*quiet { + fmt.Fprintln(wr, "NETWORK ID\tNAME\tTYPE") + } + + for _, networkResource := range networkResources { + ID := networkResource.ID + netName := networkResource.Name + if !*noTrunc { + ID = stringid.TruncateID(ID) + } + if *quiet { + fmt.Fprintln(wr, ID) + continue + } + netType := networkResource.Type + fmt.Fprintf(wr, "%s\t%s\t%s\t", + ID, + netName, + netType) + fmt.Fprint(wr, "\n") + } + wr.Flush() + return nil +} + +// CmdNetworkInfo handles Network Info UI +func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "info", "NETWORK", "Displays detailed information on a network", false) + cmd.Require(flag.Exact, 1) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + + id, err := lookupNetworkID(cli, cmd.Arg(0)) + if err != nil { + return err + } + + obj, _, err := readBody(cli.call("GET", "/networks/"+id, nil, nil)) + if err != nil { + return err + } + networkResource := &networkResource{} + if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil { + return err + } + fmt.Fprintf(cli.out, "Network Id: %s\n", networkResource.ID) + fmt.Fprintf(cli.out, "Name: %s\n", networkResource.Name) + fmt.Fprintf(cli.out, "Type: %s\n", networkResource.Type) + if networkResource.Services != nil { + for _, serviceResource := range networkResource.Services { + fmt.Fprintf(cli.out, " Service Id: %s\n", serviceResource.ID) + fmt.Fprintf(cli.out, "\tName: %s\n", serviceResource.Name) + } + } + + return nil +} + +// Helper function to predict if a string is a name or id or partial-id +// This provides a best-effort mechanism to identify a id with the help of GET Filter APIs +// Being a UI, its most likely that name will be used by the user, which is used to lookup +// the corresponding ID. If ID is not found, this function will assume that the passed string +// is an ID by itself. + +func lookupNetworkID(cli *NetworkCli, nameID string) (string, error) { + obj, statusCode, err := readBody(cli.call("GET", "/networks?name="+nameID, nil, nil)) + if err != nil { + return "", err + } + + if statusCode != http.StatusOK { + return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj)) + } + + var list []*networkResource + err = json.Unmarshal(obj, &list) + if err != nil { + return "", err + } + if len(list) > 0 { + // name query filter will always return a single-element collection + return list[0].ID, nil + } + + // Check for Partial-id + obj, statusCode, err = readBody(cli.call("GET", "/networks?partial-id="+nameID, nil, nil)) + if err != nil { + return "", err + } + + if statusCode != http.StatusOK { + return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj)) + } + + err = json.Unmarshal(obj, &list) + if err != nil { + return "", err + } + if len(list) == 0 { + return "", fmt.Errorf("resource not found %s", nameID) + } + if len(list) > 1 { + return "", fmt.Errorf("multiple Networks matching the partial identifier (%s). Please use full identifier", nameID) + } + return list[0].ID, nil +} + +func networkUsage(chain string) string { + help := "Commands:\n" + + for _, cmd := range networkCommands { + help += fmt.Sprintf(" %-25.25s%s\n", cmd.name, cmd.description) + } + + help += fmt.Sprintf("\nRun '%s network COMMAND --help' for more information on a command.", chain) + return help +} diff --git a/vendor/src/github.com/docker/libnetwork/client/service.go b/vendor/src/github.com/docker/libnetwork/client/service.go new file mode 100644 index 0000000000..35e040f805 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/client/service.go @@ -0,0 +1,362 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "strings" + "text/tabwriter" + + flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/docker/pkg/stringid" +) + +var ( + serviceCommands = []command{ + {"publish", "Publish a service"}, + {"unpublish", "Remove a service"}, + {"attach", "Attach a backend (container) to the service"}, + {"detach", "Detach the backend from the service"}, + {"ls", "Lists all services"}, + {"info", "Display information about a service"}, + } +) + +func lookupServiceID(cli *NetworkCli, nwName, svNameID string) (string, error) { + // Sanity Check + obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/networks?name=%s", nwName), nil, nil)) + if err != nil { + return "", err + } + var nwList []networkResource + if err = json.Unmarshal(obj, &nwList); err != nil { + return "", err + } + if len(nwList) == 0 { + return "", fmt.Errorf("Network %s does not exist", nwName) + } + + if nwName == "" { + obj, _, err := readBody(cli.call("GET", "/networks/"+nwList[0].ID, nil, nil)) + if err != nil { + return "", err + } + networkResource := &networkResource{} + if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil { + return "", err + } + nwName = networkResource.Name + } + + // Query service by name + obj, statusCode, err := readBody(cli.call("GET", fmt.Sprintf("/services?name=%s", svNameID), nil, nil)) + if err != nil { + return "", err + } + + if statusCode != http.StatusOK { + return "", fmt.Errorf("name query failed for %s due to: (%d) %s", svNameID, statusCode, string(obj)) + } + + var list []*serviceResource + if err = json.Unmarshal(obj, &list); err != nil { + return "", err + } + for _, sr := range list { + if sr.Network == nwName { + return sr.ID, nil + } + } + + // Query service by Partial-id (this covers full id as well) + obj, statusCode, err = readBody(cli.call("GET", fmt.Sprintf("/services?partial-id=%s", svNameID), nil, nil)) + if err != nil { + return "", err + } + + if statusCode != http.StatusOK { + return "", fmt.Errorf("partial-id match query failed for %s due to: (%d) %s", svNameID, statusCode, string(obj)) + } + + if err = json.Unmarshal(obj, &list); err != nil { + return "", err + } + for _, sr := range list { + if sr.Network == nwName { + return sr.ID, nil + } + } + + return "", fmt.Errorf("Service %s not found on network %s", svNameID, nwName) +} + +func lookupContainerID(cli *NetworkCli, cnNameID string) (string, error) { + // Container is a Docker resource, ask docker about it. + // In case of connecton error, we assume we are running in dnet and return whatever was passed to us + obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/containers/%s/json", cnNameID), nil, nil)) + if err != nil { + // We are probably running outside of docker + return cnNameID, nil + } + + var x map[string]interface{} + err = json.Unmarshal(obj, &x) + if err != nil { + return "", err + } + if iid, ok := x["Id"]; ok { + if id, ok := iid.(string); ok { + return id, nil + } + return "", fmt.Errorf("Unexpected data type for container ID in json response") + } + return "", fmt.Errorf("Cannot find container ID in json response") +} + +// 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) + cmd.Require(flag.Min, 1) + err := cmd.ParseFlags(args, true) + if err == nil { + cmd.Usage() + return fmt.Errorf("Invalid command : %v", args) + } + return err +} + +// Parse service name for "SERVICE[.NETWORK]" format +func parseServiceName(name string) (string, string) { + s := strings.Split(name, ".") + var sName, nName string + if len(s) > 1 { + nName = s[len(s)-1] + sName = strings.Join(s[:len(s)-1], ".") + } else { + sName = s[0] + } + return sName, nName +} + +// CmdServicePublish handles service create UI +func (cli *NetworkCli) CmdServicePublish(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "publish", "SERVICE[.NETWORK]", "Publish a new service on a network", false) + cmd.Require(flag.Exact, 1) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + + sn, nn := parseServiceName(cmd.Arg(0)) + sc := serviceCreate{Name: sn, Network: nn} + obj, _, err := readBody(cli.call("POST", "/services", sc, nil)) + if err != nil { + return err + } + + var replyID string + err = json.Unmarshal(obj, &replyID) + if err != nil { + return err + } + + fmt.Fprintf(cli.out, "%s\n", replyID) + return nil +} + +// CmdServiceUnpublish handles service delete UI +func (cli *NetworkCli) CmdServiceUnpublish(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "unpublish", "SERVICE[.NETWORK]", "Removes a service", false) + cmd.Require(flag.Exact, 1) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + + sn, nn := parseServiceName(cmd.Arg(0)) + serviceID, err := lookupServiceID(cli, nn, sn) + if err != nil { + return err + } + + _, _, err = readBody(cli.call("DELETE", "/services/"+serviceID, nil, nil)) + + return err +} + +// CmdServiceLs handles service list UI +func (cli *NetworkCli) CmdServiceLs(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "ls", "SERVICE", "Lists all the services on a network", false) + flNetwork := cmd.String([]string{"net", "-network"}, "", "Only show the services that are published on the specified network") + quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") + noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output") + + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + + var obj []byte + if *flNetwork == "" { + obj, _, err = readBody(cli.call("GET", "/services", nil, nil)) + } else { + obj, _, err = readBody(cli.call("GET", "/services?network="+*flNetwork, nil, nil)) + } + if err != nil { + return err + } + + var serviceResources []serviceResource + err = json.Unmarshal(obj, &serviceResources) + if err != nil { + fmt.Println(err) + return err + } + + wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) + // unless quiet (-q) is specified, print field titles + if !*quiet { + fmt.Fprintln(wr, "SERVICE ID\tNAME\tNETWORK\tCONTAINER") + } + + for _, sr := range serviceResources { + ID := sr.ID + bkID, err := getBackendID(cli, ID) + if err != nil { + return err + } + if !*noTrunc { + ID = stringid.TruncateID(ID) + bkID = stringid.TruncateID(bkID) + } + if !*quiet { + fmt.Fprintf(wr, "%s\t%s\t%s\t%s\n", ID, sr.Name, sr.Network, bkID) + } else { + fmt.Fprintln(wr, ID) + } + } + wr.Flush() + + return nil +} + +func getBackendID(cli *NetworkCli, servID string) (string, error) { + var ( + obj []byte + err error + bk string + ) + + if obj, _, err = readBody(cli.call("GET", "/services/"+servID+"/backend", nil, nil)); err == nil { + var bkl []backendResource + if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&bkl); err == nil { + if len(bkl) > 0 { + bk = bkl[0].ID + } + } else { + // Only print a message, don't make the caller cli fail for this + fmt.Fprintf(cli.out, "Failed to retrieve backend list for service %s (%v)", servID, err) + } + } + + return bk, err +} + +// CmdServiceInfo handles service info UI +func (cli *NetworkCli) CmdServiceInfo(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "info", "SERVICE[.NETWORK]", "Displays detailed information about a service", false) + cmd.Require(flag.Min, 1) + + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + + sn, nn := parseServiceName(cmd.Arg(0)) + serviceID, err := lookupServiceID(cli, nn, sn) + if err != nil { + return err + } + + obj, _, err := readBody(cli.call("GET", "/services/"+serviceID, nil, nil)) + if err != nil { + return err + } + + sr := &serviceResource{} + if err := json.NewDecoder(bytes.NewReader(obj)).Decode(sr); err != nil { + return err + } + + fmt.Fprintf(cli.out, "Service Id: %s\n", sr.ID) + fmt.Fprintf(cli.out, "\tName: %s\n", sr.Name) + fmt.Fprintf(cli.out, "\tNetwork: %s\n", sr.Network) + + return nil +} + +// CmdServiceAttach handles service attach UI +func (cli *NetworkCli) CmdServiceAttach(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "attach", "CONTAINER SERVICE[.NETWORK]", "Sets a container as a service backend", false) + cmd.Require(flag.Min, 2) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + + containerID, err := lookupContainerID(cli, cmd.Arg(0)) + 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} + + _, _, err = readBody(cli.call("POST", "/services/"+serviceID+"/backend", nc, nil)) + + return err +} + +// CmdServiceDetach handles service detach UI +func (cli *NetworkCli) CmdServiceDetach(chain string, args ...string) error { + cmd := cli.Subcmd(chain, "detach", "CONTAINER SERVICE", "Removes a container from service backend", false) + cmd.Require(flag.Min, 2) + err := cmd.ParseFlags(args, true) + if err != nil { + return err + } + + sn, nn := parseServiceName(cmd.Arg(1)) + containerID, err := lookupContainerID(cli, cmd.Arg(0)) + if err != nil { + return err + } + + serviceID, err := lookupServiceID(cli, nn, sn) + if err != nil { + return err + } + + _, _, err = readBody(cli.call("DELETE", "/services/"+serviceID+"/backend/"+containerID, nil, nil)) + if err != nil { + return err + } + return nil +} + +func serviceUsage(chain string) string { + help := "Commands:\n" + + for _, cmd := range serviceCommands { + help += fmt.Sprintf(" %-10.10s%s\n", cmd.name, cmd.description) + } + + help += fmt.Sprintf("\nRun '%s service COMMAND --help' for more information on a command.", chain) + return help +} diff --git a/vendor/src/github.com/docker/libnetwork/client/types.go b/vendor/src/github.com/docker/libnetwork/client/types.go new file mode 100644 index 0000000000..e14460aba7 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/client/types.go @@ -0,0 +1,73 @@ +package client + +import "github.com/docker/libnetwork/types" + +/*********** + Resources +************/ + +// networkResource is the body of the "get network" http response message +type networkResource struct { + Name string `json:"name"` + ID string `json:"id"` + Type string `json:"type"` + Services []*serviceResource `json:"services"` +} + +// serviceResource is the body of the "get service" http response message +type serviceResource struct { + Name string `json:"name"` + ID string `json:"id"` + Network string `json:"network"` +} + +// backendResource is the body of "get service backend" response message +type backendResource struct { + ID string `json:"id"` +} + +/*********** + Body types + ************/ + +// networkCreate is the expected body of the "create network" http request message +type networkCreate struct { + Name string `json:"name"` + NetworkType string `json:"network_type"` + Options map[string]interface{} `json:"options"` +} + +// serviceCreate represents the body of the "publish service" http request message +type serviceCreate struct { + Name string `json:"name"` + Network string `json:"network_name"` + ExposedPorts []types.TransportPort `json:"exposed_ports"` + PortMapping []types.PortBinding `json:"port_mapping"` +} + +// serviceAttach represents the expected body of the "attach/detach backend 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"` +} + +// serviceExtraHost represents the extra host object +type serviceExtraHost 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"` +} diff --git a/vendor/src/github.com/docker/libnetwork/config/config.go b/vendor/src/github.com/docker/libnetwork/config/config.go index 6a8a354912..602f5c33e4 100644 --- a/vendor/src/github.com/docker/libnetwork/config/config.go +++ b/vendor/src/github.com/docker/libnetwork/config/config.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/BurntSushi/toml" + "github.com/docker/libnetwork/netlabel" ) // Config encapsulates configurations of various Libnetwork components @@ -18,6 +19,7 @@ type DaemonCfg struct { Debug bool DefaultNetwork string DefaultDriver string + Labels []string } // ClusterCfg represents cluster configuration @@ -66,6 +68,17 @@ func OptionDefaultDriver(dd string) Option { } } +// OptionLabels function returns an option setter for labels +func OptionLabels(labels []string) Option { + return func(c *Config) { + for _, label := range labels { + if strings.HasPrefix(label, netlabel.Prefix) { + c.Daemon.Labels = append(c.Daemon.Labels, label) + } + } + } +} + // OptionKVProvider function returns an option setter for kvstore provider func OptionKVProvider(provider string) Option { return func(c *Config) { @@ -88,3 +101,11 @@ func (c *Config) ProcessOptions(options ...Option) { } } } + +// IsValidName validates configuration objects supported by libnetwork +func IsValidName(name string) bool { + if name == "" || strings.Contains(name, ".") { + return false + } + return true +} diff --git a/vendor/src/github.com/docker/libnetwork/controller.go b/vendor/src/github.com/docker/libnetwork/controller.go index 06db0bf49d..a2ab5046ef 100644 --- a/vendor/src/github.com/docker/libnetwork/controller.go +++ b/vendor/src/github.com/docker/libnetwork/controller.go @@ -169,6 +169,9 @@ func (c *controller) hostLeaveCallback(hosts []net.IP) { func (c *controller) Config() config.Config { c.Lock() defer c.Unlock() + if c.cfg == nil { + return config.Config{} + } return *c.cfg } @@ -185,6 +188,9 @@ func (c *controller) ConfigureNetworkDriver(networkType string, options map[stri func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver, capability driverapi.Capability) error { c.Lock() defer c.Unlock() + if !config.IsValidName(networkType) { + return ErrInvalidName(networkType) + } if _, ok := c.drivers[networkType]; ok { return driverapi.ErrActiveRegistration(networkType) } @@ -195,7 +201,7 @@ func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver, // NewNetwork creates a new network of the specified network type. The options // are network specific and modeled in a generic way. func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) { - if name == "" { + if !config.IsValidName(name) { return nil, ErrInvalidName(name) } // Check if a network already exists with the specified network name diff --git a/vendor/src/github.com/docker/libnetwork/netlabel/labels.go b/vendor/src/github.com/docker/libnetwork/netlabel/labels.go index adbabbc475..799ef5a08a 100644 --- a/vendor/src/github.com/docker/libnetwork/netlabel/labels.go +++ b/vendor/src/github.com/docker/libnetwork/netlabel/labels.go @@ -1,18 +1,24 @@ package netlabel const ( + // Prefix constant marks the reserved label space for libnetwork + Prefix = "com.docker.network" + + // DriverPrefix constant marks the reserved label space for libnetwork drivers + DriverPrefix = Prefix + ".driver" + // GenericData constant that helps to identify an option as a Generic constant - GenericData = "io.docker.network.generic" + GenericData = Prefix + ".generic" // PortMap constant represents Port Mapping - PortMap = "io.docker.network.endpoint.portmap" + PortMap = Prefix + ".portmap" // MacAddress constant represents Mac Address config of a Container - MacAddress = "io.docker.network.endpoint.macaddress" + MacAddress = Prefix + ".endpoint.macaddress" // ExposedPorts constant represents exposedports of a Container - ExposedPorts = "io.docker.network.endpoint.exposedports" + ExposedPorts = Prefix + ".endpoint.exposedports" //EnableIPv6 constant represents enabling IPV6 at network level - EnableIPv6 = "io.docker.network.enable_ipv6" + EnableIPv6 = Prefix + ".enable_ipv6" ) diff --git a/vendor/src/github.com/docker/libnetwork/network.go b/vendor/src/github.com/docker/libnetwork/network.go index 7e6c343b1a..28369656f4 100644 --- a/vendor/src/github.com/docker/libnetwork/network.go +++ b/vendor/src/github.com/docker/libnetwork/network.go @@ -6,6 +6,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/stringid" + "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/netlabel" @@ -274,7 +275,7 @@ func (n *network) addEndpoint(ep *endpoint) error { func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) { var err error - if name == "" { + if !config.IsValidName(name) { return nil, ErrInvalidName(name) } From cca990c67a3132d46e0157878507138aa2c6d2e5 Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Thu, 18 Jun 2015 11:48:08 -0700 Subject: [PATCH 2/2] Network UI / API docs Signed-off-by: Madhu Venugopal --- experimental/networking.md | 72 +++++++++ experimental/networking_api.md | 287 +++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 experimental/networking.md create mode 100644 experimental/networking_api.md diff --git a/experimental/networking.md b/experimental/networking.md new file mode 100644 index 0000000000..f3bd66b615 --- /dev/null +++ b/experimental/networking.md @@ -0,0 +1,72 @@ +# Experimental: Networking and Services + +In this feature: + +- `network` become a first class objects in the Docker UI + +This is an experimental feature. For information on installing and using experimental features, see [the experimental feature overview](experimental.md). + +## Using Networks + + Usage: docker network [OPTIONS] COMMAND [OPTIONS] [arg...] + + Commands: + create Create a network + rm Remove a network + ls List all networks + info Display information of a network + + Run 'docker network COMMAND --help' for more information on a command. + + --help=false Print usage + +The `docker network` command is used to manage Networks. + +To create a network, `docker network create foo`. You can also specify a driver +if you have loaded a networking plugin e.g `docker network create -d foo` + + $ docker network create foo + aae601f43744bc1f57c515a16c8c7c4989a2cad577978a32e6910b799a6bccf6 + $ docker network create -d overlay bar + d9989793e2f5fe400a58ef77f706d03f668219688ee989ea68ea78b990fa2406 + +`docker network ls` is used to display the currently configured networks + + $ docker network ls + NETWORK ID NAME TYPE + d367e613ff7f none null + bd61375b6993 host host + cc455abccfeb bridge bridge + aae601f43744 foo bridge + d9989793e2f5 bar overlay + +To get detailed information on a network, you can use the `docker network info` +command. + + $ docker network info foo + Network Id: aae601f43744bc1f57c515a16c8c7c4989a2cad577978a32e6910b799a6bccf6 + Name: foo + Type: null + +If you no longer have need of a network, you can delete it with `docker network rm` + + $ docker network rm bar + bar + $ docker network ls + NETWORK ID NAME TYPE + aae601f43744 foo bridge + d367e613ff7f none null + bd61375b6993 host host + cc455abccfeb bridge bridge + + +Currently the only way this network can be used to connect container is via default network-mode. +Docker daemon supports a configuration flag `--default-network` which takes configuration value of format `NETWORK:DRIVER`, where, +`NETWORK` is the name of the network created using the `docker network create` command and +`DRIVER` represents the in-built drivers such as bridge, overlay, container, host and none. or Remote drivers via Network Plugins. +When a container is created and if the network mode (`--net`) is not specified, then this default network will be used to connect +the container. If `--default-network` is not specified, the default network will be the `bridge` driver. + +Send us feedback and comments on [#](https://github.com/docker/docker/issues/?), +or on the usual Google Groups (docker-user, docker-dev) and IRC channels. + diff --git a/experimental/networking_api.md b/experimental/networking_api.md new file mode 100644 index 0000000000..7af86b77d0 --- /dev/null +++ b/experimental/networking_api.md @@ -0,0 +1,287 @@ +# Networking API + +### List networks + +`GET /networks` + +List networks + +**Example request**: + + GET /networks HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "name": "none", + "id": "8e4e55c6863ef4241c548c1c6fc77289045e9e5d5b5e4875401a675326981898", + "type": "null", + "endpoints": [] + }, + { + "name": "host", + "id": "062b6d9ea7913fde549e2d186ff0402770658f8c4e769958e1b943ff4e675011", + "type": "host", + "endpoints": [] + }, + { + "name": "bridge", + "id": "a87dd9a9d58f030962df1c15fb3fa142fbd9261339de458bc89be1895cef2c70", + "type": "bridge", + "endpoints": [] + } + ] + +Query Parameters: + +- **name** – Filter results with the given name +- **partial-id** – Filter results using the partial network ID + +Status Codes: + +- **200** – no error +- **400** – bad parameter +- **500** – server error + +### Create a Network + +`POST /networks` + +**Example request** + + POST /networks HTTP/1.1 + Content-Type: application/json + + { + "name": "foo", + "network_type": "", + "options": {} + } + +**Example Response** + + HTTP/1.1 200 OK + "32fbf63200e2897f5de72cb2a4b653e4b1a523b15116e96e3d73f7849e583653", + +Status Codes: + +- **200** – no error +- **400** – bad request +- **500** – server error + +### Get a network + +`GET /networks/` + +Get a network + +**Example request**: + + GET /networks/32fbf63200e2897f5de72cb2a4b653e4b1a523b15116e96e3d73f7849e583653 HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "name": "foo", + "id": "32fbf63200e2897f5de72cb2a4b653e4b1a523b15116e96e3d73f7849e583653", + "type": "bridge", + "endpoints": [] + } + +Status Codes: + +- **200** – no error +- **404** – not found +- **500** – server error + +### List a networks endpoints + +`GET /networks//endpoints` + +**Example request** + + GET /networks/32fbf63200e2897f5de72cb2a4b653e4b1a523b15116e96e3d73f7849e583653/endpoints HTTP/1.1 + +**Example Response** + + HTTP/1.1 200 OK + Content-Type: application/json + + [ + { + "id": "7e0c116b882ee489a8a5345a2638c0129099aa47f4ba114edde34e75c1e4ae0d", + "name": "/lonely_pasteur", + "network": "foo" + } + ] + +Query Parameters: + +- **name** – Filter results with the given name +- **partial-id** – Filter results using the partial network ID + +Status Codes: + +- **200** – no error +- **400** – bad parameter +- **500** – server error + +### Create an endpoint on a network + +`POST /networks//endpoints` + +**Example request** + + POST /networks/32fbf63200e2897f5de72cb2a4b653e4b1a523b15116e96e3d73f7849e583653/endpoints HTTP/1.1 + Content-Type: application/json + + { + "name": "baz", + "exposed_ports": [ + { + "proto": 6, + "port": 8080 + } + ], + "port_mapping": null + } + +**Example Response** + + HTTP/1.1 200 OK + Content-Type: application/json + + "b18b795af8bad85cdd691ff24ffa2b08c02219d51992309dd120322689d2ab5a" + +Status Codes: + +- **200** – no error +- **400** – bad parameter +- **500** – server error + +### Get an endpoint + +`GET /networks//endpoints/` + +**Example request** + + GET /networks/32fbf63200e2897f5de72cb2a4b653e4b1a523b15116e96e3d73f7849e583653/endpoints/b18b795af8bad85cdd691ff24ffa2b08c02219d51992309dd120322689d2ab5a HTTP/1.1 + +**Example Response** + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "id": "b18b795af8bad85cdd691ff24ffa2b08c02219d51992309dd120322689d2ab5a", + "name": "baz", + "network": "foo" + } + +Status Codes: + +- **200** – no error +- **404** - not found +- **500** – server error + +### Join an endpoint to a container + +`POST /networks//endpoints//containers` + +**Example request** + + POST /networks/32fbf63200e2897f5de72cb2a4b653e4b1a523b15116e96e3d73f7849e583653//endpoints/b18b795af8bad85cdd691ff24ffa2b08c02219d51992309dd120322689d2ab5a/containers HTTP/1.1 + Content-Type: application/json + + { + "container_id": "e76f406417031bd24c17aeb9bb2f5968b628b9fb6067da264b234544754bf857", + "host_name": null, + "domain_name": null, + "hosts_path": null, + "resolv_conf_path": null, + "dns": null, + "extra_hosts": null, + "parent_updates": null, + "use_default_sandbox": true + } + +**Example response** + + HTTP/1.1 200 OK + Content-Type: application/json + + "/var/run/docker/netns/e76f40641703" + + +Status Codes: + +- **200** – no error +- **400** – bad parameter +- **404** - not found +- **500** – server error + +### Detach an endpoint from a container + +`DELETE /networks//endpoints//containers/` + +**Example request** + + DELETE /networks/32fbf63200e2897f5de72cb2a4b653e4b1a523b15116e96e3d73f7849e583653/endpoints/b18b795af8bad85cdd691ff24ffa2b08c02219d51992309dd120322689d2ab5a/containers/e76f406417031bd24c17aeb9bb2f5968b628b9fb6067da264b234544754bf857 HTTP/1.1 + Content-Type: application/json + +**Example response** + + HTTP/1.1 200 OK + +Status Codes: + +- **200** – no error +- **400** – bad parameter +- **404** - not found +- **500** – server error + + +### Delete an endpoint + +`DELETE /networks//endpoints/` + +**Example request** + + DELETE /networks/32fbf63200e2897f5de72cb2a4b653e4b1a523b15116e96e3d73f7849e583653/endpoints/b18b795af8bad85cdd691ff24ffa2b08c02219d51992309dd120322689d2ab5a HTTP/1.1 + +**Example Response** + + HTTP/1.1 200 OK + +Status Codes: + +- **200** – no error +- **404** - not found +- **500** – server error + +### Delete a network + +`DELETE /networks/` + +Delete a network + +**Example request**: + + DELETE /networks/0984d158bd8ae108e4d6bc8fcabedf51da9a174b32cc777026d4a29045654951 HTTP/1.1 + +**Example response**: + + HTTP/1.1 200 OK + +Status Codes: + +- **200** – no error +- **404** – not found +- **500** – server error