From 2ab94e11a2a8499088a72ab27fd09e897d8c810a Mon Sep 17 00:00:00 2001 From: Madhu Venugopal Date: Fri, 25 Sep 2015 03:19:17 -0700 Subject: [PATCH] Network remote APIs using new router, --net= changes * Moving Network Remote APIs out of experimental * --net can now accept user created networks using network drivers/plugins * Removed the experimental services concept and --default-network option * Neccessary backend changes to accomodate multiple networks per container * Integration Tests Signed-off-by: David Calavera Signed-off-by: Madhu Venugopal --- api/server/router/local/local.go | 15 +- api/server/router/network/network.go | 37 +- .../router/network/network_experimental.go | 51 - api/server/router/network/network_routes.go | 216 ++++ api/server/router/network/network_stable.go | 20 - api/server/router/router.go | 17 +- api/server/server.go | 13 +- api/types/types.go | 41 + daemon/config_experimental.go | 9 - daemon/config_stub.go | 8 - daemon/config_unix.go | 2 - daemon/container_unix.go | 280 ++++-- daemon/container_windows.go | 11 + daemon/daemon.go | 5 - daemon/network.go | 106 ++ daemon/network/settings.go | 2 +- integration-cli/docker_api_network_test.go | 219 +++- .../docker_cli_daemon_experimental_test.go | 40 - integration-cli/docker_cli_netmode_test.go | 2 +- integration-cli/docker_cli_run_test.go | 150 ++- runconfig/config.go | 1 - runconfig/hostconfig_test.go | 2 +- runconfig/hostconfig_unix.go | 15 + runconfig/parse.go | 12 +- runconfig/parse_experimental.go | 19 - runconfig/parse_stub.go | 14 - runconfig/parse_unix.go | 10 +- .../github.com/docker/libnetwork/api/api.go | 949 ------------------ .../github.com/docker/libnetwork/api/types.go | 80 -- 29 files changed, 966 insertions(+), 1380 deletions(-) delete mode 100644 api/server/router/network/network_experimental.go create mode 100644 api/server/router/network/network_routes.go delete mode 100644 api/server/router/network/network_stable.go delete mode 100644 daemon/config_experimental.go delete mode 100644 daemon/config_stub.go create mode 100644 daemon/network.go delete mode 100644 integration-cli/docker_cli_daemon_experimental_test.go delete mode 100644 runconfig/parse_experimental.go delete mode 100644 runconfig/parse_stub.go delete mode 100644 vendor/src/github.com/docker/libnetwork/api/api.go delete mode 100644 vendor/src/github.com/docker/libnetwork/api/types.go diff --git a/api/server/router/local/local.go b/api/server/router/local/local.go index 6cabf868b2..53240b2a30 100644 --- a/api/server/router/local/local.go +++ b/api/server/router/local/local.go @@ -5,11 +5,9 @@ import ( "golang.org/x/net/context" - "github.com/Sirupsen/logrus" "github.com/docker/docker/api/server/httputils" dkrouter "github.com/docker/docker/api/server/router" "github.com/docker/docker/daemon" - "github.com/gorilla/mux" ) // router is a docker router that talks with the local docker daemon. @@ -31,11 +29,14 @@ func (l localRoute) Handler() httputils.APIFunc { return l.handler } -// Register adds the filtered handler to the mux. -func (l localRoute) Register(m *mux.Router, handler http.Handler) { - logrus.Debugf("Registering %s, %s", l.method, l.path) - m.Path(dkrouter.VersionMatcher + l.path).Methods(l.method).Handler(handler) - m.Path(l.path).Methods(l.method).Handler(handler) +// Method returns the http method that the route responds to. +func (l localRoute) Method() string { + return l.method +} + +// Path returns the subpath where the route responds to. +func (l localRoute) Path() string { + return l.path } // NewRoute initialies a new local route for the reouter diff --git a/api/server/router/network/network.go b/api/server/router/network/network.go index 190198564b..7645249b45 100644 --- a/api/server/router/network/network.go +++ b/api/server/router/network/network.go @@ -1,26 +1,41 @@ package network import ( - "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/server/router" + "github.com/docker/docker/api/server/router/local" + "github.com/docker/docker/daemon" ) // networkRouter is a router to talk with the network controller type networkRouter struct { + daemon *daemon.Daemon routes []router.Route } +// NewRouter initializes a new network router +func NewRouter(d *daemon.Daemon) router.Router { + r := &networkRouter{ + daemon: d, + } + r.initRoutes() + return r +} + // Routes returns the available routes to the network controller -func (n networkRouter) Routes() []router.Route { - return n.routes +func (r *networkRouter) Routes() []router.Route { + return r.routes } -type networkRoute struct { - path string - handler httputils.APIFunc -} - -// Handler returns the APIFunc to let the server wrap it in middlewares -func (l networkRoute) Handler() httputils.APIFunc { - return l.handler +func (r *networkRouter) initRoutes() { + r.routes = []router.Route{ + // GET + local.NewGetRoute("/networks", r.getNetworksList), + local.NewGetRoute("/networks/{id:.*}", r.getNetwork), + // POST + local.NewPostRoute("/networks/create", r.postNetworkCreate), + local.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect), + local.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect), + // DELETE + local.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork), + } } diff --git a/api/server/router/network/network_experimental.go b/api/server/router/network/network_experimental.go deleted file mode 100644 index e5decfde10..0000000000 --- a/api/server/router/network/network_experimental.go +++ /dev/null @@ -1,51 +0,0 @@ -// +build experimental - -package network - -import ( - "net/http" - - "golang.org/x/net/context" - - "github.com/Sirupsen/logrus" - "github.com/docker/docker/api/server/router" - "github.com/docker/docker/daemon" - "github.com/docker/libnetwork/api" - "github.com/gorilla/mux" -) - -var httpMethods = []string{"GET", "POST", "PUT", "DELETE"} - -// NewRouter initializes a new network router -func NewRouter(d *daemon.Daemon) router.Router { - c := d.NetworkController() - if c == nil { - return networkRouter{} - } - - var routes []router.Route - netHandler := api.NewHTTPHandler(c) - - // TODO: libnetwork should stop hijacking request/response. - // It should define API functions to add normally to the router. - handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - netHandler(w, r) - return nil - } - - for _, path := range []string{"/networks", "/services", "/sandboxes"} { - routes = append(routes, networkRoute{path, handler}) - } - - return networkRouter{routes} -} - -// Register adds the filtered handler to the mux. -func (n networkRoute) Register(m *mux.Router, handler http.Handler) { - logrus.Debugf("Registering %s, %v", n.path, httpMethods) - subrouter := m.PathPrefix(router.VersionMatcher + n.path).Subrouter() - subrouter.Methods(httpMethods...).Handler(handler) - - subrouter = m.PathPrefix(n.path).Subrouter() - subrouter.Methods(httpMethods...).Handler(handler) -} diff --git a/api/server/router/network/network_routes.go b/api/server/router/network/network_routes.go new file mode 100644 index 0000000000..14cc8f1503 --- /dev/null +++ b/api/server/router/network/network_routes.go @@ -0,0 +1,216 @@ +package network + +import ( + "encoding/json" + "fmt" + "net/http" + + "golang.org/x/net/context" + + "github.com/Sirupsen/logrus" + "github.com/docker/docker/api/server/httputils" + "github.com/docker/docker/api/types" + "github.com/docker/docker/daemon" + "github.com/docker/docker/pkg/parsers/filters" + "github.com/docker/libnetwork" +) + +func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + if err := httputils.ParseForm(r); err != nil { + return err + } + + filter := r.Form.Get("filters") + netFilters, err := filters.FromParam(filter) + if err != nil { + return err + } + + list := []*types.NetworkResource{} + var nameFilter, idFilter bool + var names, ids []string + if names, nameFilter = netFilters["name"]; nameFilter { + for _, name := range names { + if nw, err := n.daemon.GetNetwork(name, daemon.NetworkByName); err == nil { + list = append(list, buildNetworkResource(nw)) + } else { + logrus.Errorf("failed to get network for filter=%s : %v", name, err) + } + } + } + + if ids, idFilter = netFilters["id"]; idFilter { + for _, id := range ids { + for _, nw := range n.daemon.GetNetworksByID(id) { + list = append(list, buildNetworkResource(nw)) + } + } + } + + if !nameFilter && !idFilter { + nwList := n.daemon.GetNetworksByID("") + for _, nw := range nwList { + list = append(list, buildNetworkResource(nw)) + } + } + return httputils.WriteJSON(w, http.StatusOK, list) +} + +func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + if err := httputils.ParseForm(r); err != nil { + return err + } + + nw, err := n.daemon.FindNetwork(vars["id"]) + if err != nil { + return err + } + return httputils.WriteJSON(w, http.StatusOK, buildNetworkResource(nw)) +} + +func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + var create types.NetworkCreate + var warning string + + if err := httputils.ParseForm(r); err != nil { + return err + } + + if err := httputils.CheckForJSON(r); err != nil { + return err + } + + if err := json.NewDecoder(r.Body).Decode(&create); err != nil { + return err + } + + nw, err := n.daemon.GetNetwork(create.Name, daemon.NetworkByName) + if _, ok := err.(libnetwork.ErrNoSuchNetwork); err != nil && !ok { + return err + } + if nw != nil { + if create.CheckDuplicate { + return libnetwork.NetworkNameError(create.Name) + } + warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID()) + } + + nw, err = n.daemon.CreateNetwork(create.Name, create.Driver, create.Options) + if err != nil { + return err + } + + return httputils.WriteJSON(w, http.StatusCreated, &types.NetworkCreateResponse{ + ID: nw.ID(), + Warning: warning, + }) +} + +func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + var connect types.NetworkConnect + if err := httputils.ParseForm(r); err != nil { + return err + } + + if err := httputils.CheckForJSON(r); err != nil { + return err + } + + if err := json.NewDecoder(r.Body).Decode(&connect); err != nil { + return err + } + + nw, err := n.daemon.FindNetwork(vars["id"]) + if err != nil { + return err + } + + container, err := n.daemon.Get(connect.Container) + if err != nil { + return fmt.Errorf("invalid container %s : %v", container, err) + } + return container.ConnectToNetwork(nw.Name()) +} + +func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + var disconnect types.NetworkDisconnect + if err := httputils.ParseForm(r); err != nil { + return err + } + + if err := httputils.CheckForJSON(r); err != nil { + return err + } + + if err := json.NewDecoder(r.Body).Decode(&disconnect); err != nil { + return err + } + + nw, err := n.daemon.FindNetwork(vars["id"]) + if err != nil { + return err + } + + container, err := n.daemon.Get(disconnect.Container) + if err != nil { + return fmt.Errorf("invalid container %s : %v", container, err) + } + return container.DisconnectFromNetwork(nw) +} + +func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + if err := httputils.ParseForm(r); err != nil { + return err + } + + nw, err := n.daemon.FindNetwork(vars["id"]) + if err != nil { + return err + } + + return nw.Delete() +} + +func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource { + r := &types.NetworkResource{} + if nw == nil { + return r + } + + r.Name = nw.Name() + r.ID = nw.ID() + r.Driver = nw.Type() + r.Containers = make(map[string]types.EndpointResource) + epl := nw.Endpoints() + for _, e := range epl { + sb := e.Info().Sandbox() + if sb == nil { + continue + } + + r.Containers[sb.ContainerID()] = buildEndpointResource(e) + } + return r +} + +func buildEndpointResource(e libnetwork.Endpoint) types.EndpointResource { + er := types.EndpointResource{} + if e == nil { + return er + } + + er.EndpointID = e.ID() + if iface := e.Info().Iface(); iface != nil { + if mac := iface.MacAddress(); mac != nil { + er.MacAddress = mac.String() + } + if ip := iface.Address(); len(ip.IP) > 0 { + er.IPv4Address = (&ip).String() + } + + if ipv6 := iface.AddressIPv6(); len(ipv6.IP) > 0 { + er.IPv6Address = (&ipv6).String() + } + } + return er +} diff --git a/api/server/router/network/network_stable.go b/api/server/router/network/network_stable.go deleted file mode 100644 index 9a9b38b4a4..0000000000 --- a/api/server/router/network/network_stable.go +++ /dev/null @@ -1,20 +0,0 @@ -// +build !experimental - -package network - -import ( - "net/http" - - "github.com/docker/docker/api/server/router" - "github.com/docker/docker/daemon" - "github.com/gorilla/mux" -) - -// NewRouter initializes a new network router -func NewRouter(d *daemon.Daemon) router.Router { - return networkRouter{} -} - -// Register adds the filtered handler to the mux. -func (n networkRoute) Register(m *mux.Router, handler http.Handler) { -} diff --git a/api/server/router/router.go b/api/server/router/router.go index 85e81d2d62..f3efa82fa6 100644 --- a/api/server/router/router.go +++ b/api/server/router/router.go @@ -1,15 +1,6 @@ package router -import ( - "net/http" - - "github.com/docker/docker/api/server/httputils" - "github.com/gorilla/mux" -) - -// VersionMatcher defines a variable matcher to be parsed by the router -// when a request is about to be served. -const VersionMatcher = "/v{version:[0-9.]+}" +import "github.com/docker/docker/api/server/httputils" // Router defines an interface to specify a group of routes to add the the docker server. type Router interface { @@ -18,8 +9,10 @@ type Router interface { // Route defines an individual API route in the docker server. type Route interface { - // Register adds the handler route to the docker mux. - Register(*mux.Router, http.Handler) // Handler returns the raw function to create the http handler. Handler() httputils.APIFunc + // Method returns the http method that the route responds to. + Method() string + // Path returns the subpath where the route responds to. + Path() string } diff --git a/api/server/server.go b/api/server/server.go index fd4b88d30c..0dbb81fe99 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -19,6 +19,10 @@ import ( "golang.org/x/net/context" ) +// versionMatcher defines a variable matcher to be parsed by the router +// when a request is about to be served. +const versionMatcher = "/v{version:[0-9.]+}" + // Config provides the configuration for the API server type Config struct { Logging bool @@ -177,10 +181,13 @@ func (s *Server) CreateMux() *mux.Router { } logrus.Debugf("Registering routers") - for _, router := range s.routers { - for _, r := range router.Routes() { + for _, apiRouter := range s.routers { + for _, r := range apiRouter.Routes() { f := s.makeHTTPHandler(r.Handler()) - r.Register(m, f) + + logrus.Debugf("Registering %s, %s", r.Method(), r.Path()) + m.Path(versionMatcher + r.Path()).Methods(r.Method()).Handler(f) + m.Path(r.Path()).Methods(r.Method()).Handler(f) } } diff --git a/api/types/types.go b/api/types/types.go index c2c7852217..764878665c 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -308,3 +308,44 @@ type VolumeCreateRequest struct { Driver string // Driver is the name of the driver that should be used to create the volume DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume. } + +// NetworkResource is the body of the "get network" http response message +type NetworkResource struct { + Name string `json:"name"` + ID string `json:"id"` + Driver string `json:"driver"` + Containers map[string]EndpointResource `json:"containers"` + Options map[string]interface{} `json:"options,omitempty"` +} + +//EndpointResource contains network resources allocated and usd for a container in a network +type EndpointResource struct { + EndpointID string `json:"endpoint"` + MacAddress string `json:"mac_address"` + IPv4Address string `json:"ipv4_address"` + IPv6Address string `json:"ipv6_address"` +} + +// NetworkCreate is the expected body of the "create network" http request message +type NetworkCreate struct { + Name string `json:"name"` + CheckDuplicate bool `json:"check_duplicate"` + Driver string `json:"driver"` + Options map[string]interface{} `json:"options"` +} + +// NetworkCreateResponse is the response message sent by the server for network create call +type NetworkCreateResponse struct { + ID string `json:"id"` + Warning string `json:"warning"` +} + +// NetworkConnect represents the data to be used to connect a container to the network +type NetworkConnect struct { + Container string `json:"container"` +} + +// NetworkDisconnect represents the data to be used to disconnect a container from the network +type NetworkDisconnect struct { + Container string `json:"container"` +} diff --git a/daemon/config_experimental.go b/daemon/config_experimental.go deleted file mode 100644 index 9dd917ab8a..0000000000 --- a/daemon/config_experimental.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build experimental - -package daemon - -import flag "github.com/docker/docker/pkg/mflag" - -func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) { - cmd.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", usageFn("Set default network")) -} diff --git a/daemon/config_stub.go b/daemon/config_stub.go deleted file mode 100644 index 796e6b6e4e..0000000000 --- a/daemon/config_stub.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build !experimental - -package daemon - -import flag "github.com/docker/docker/pkg/mflag" - -func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) { -} diff --git a/daemon/config_unix.go b/daemon/config_unix.go index d4f127c29c..e79df338f8 100644 --- a/daemon/config_unix.go +++ b/daemon/config_unix.go @@ -77,6 +77,4 @@ func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) strin cmd.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, usageFn("Use userland proxy for loopback traffic")) cmd.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, usageFn("Enable CORS headers in the remote API, this is deprecated by --api-cors-header")) cmd.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API")) - - config.attachExperimentalFlags(cmd, usageFn) } diff --git a/daemon/container_unix.go b/daemon/container_unix.go index 2dddcb20dd..4879cec7f5 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -412,7 +412,7 @@ func (container *Container) buildHostnameFile() error { return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644) } -func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, error) { +func (container *Container) buildSandboxOptions(n libnetwork.Network) ([]libnetwork.SandboxOption, error) { var ( sboxOptions []libnetwork.SandboxOption err error @@ -487,6 +487,23 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e } } + for _, extraHost := range container.hostConfig.ExtraHosts { + // allow IPv6 addresses in extra hosts; only split on first ":" + parts := strings.SplitN(extraHost, ":", 2) + sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1])) + } + + // Link feature is supported only for the default bridge network. + // return if this call to build join options is not for default bridge network + if n.Name() != "bridge" { + return sboxOptions, nil + } + + ep, _ := container.getEndpointInNetwork(n) + if ep == nil { + return sboxOptions, nil + } + var childEndpoints, parentEndpoints []string children, err := container.daemon.children(container.Name) @@ -503,17 +520,12 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e aliasList = aliasList + " " + child.Name[1:] } sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.IPAddress)) - if child.NetworkSettings.EndpointID != "" { - childEndpoints = append(childEndpoints, child.NetworkSettings.EndpointID) + cEndpoint, _ := child.getEndpointInNetwork(n) + if cEndpoint != nil && cEndpoint.ID() != "" { + childEndpoints = append(childEndpoints, cEndpoint.ID()) } } - for _, extraHost := range container.hostConfig.ExtraHosts { - // allow IPv6 addresses in extra hosts; only split on first ":" - parts := strings.SplitN(extraHost, ":", 2) - sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1])) - } - refs := container.daemon.containerGraph().RefPaths(container.ID) for _, ref := range refs { if ref.ParentID == "0" { @@ -528,8 +540,8 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e if c != nil && !container.daemon.configStore.DisableBridge && container.hostConfig.NetworkMode.IsPrivate() { logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, container.NetworkSettings.IPAddress) sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate(c.ID, ref.Name, container.NetworkSettings.IPAddress)) - if c.NetworkSettings.EndpointID != "" { - parentEndpoints = append(parentEndpoints, c.NetworkSettings.EndpointID) + if ep.ID() != "" { + parentEndpoints = append(parentEndpoints, ep.ID()) } } } @@ -546,6 +558,11 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e return sboxOptions, nil } +func (container *Container) getEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) { + endpointName := strings.TrimPrefix(container.Name, "/") + return n.EndpointByName(endpointName) +} + func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint, networkSettings *network.Settings) (*network.Settings, error) { if ep == nil { return nil, derr.ErrorCodeEmptyEndpoint @@ -650,10 +667,38 @@ func (container *Container) updateJoinInfo(ep libnetwork.Endpoint) error { return nil } -func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error { - networkSettings := &network.Settings{NetworkID: n.ID(), EndpointID: ep.ID()} +func (container *Container) updateNetworkSettings(n libnetwork.Network) error { + if container.NetworkSettings == nil { + container.NetworkSettings = &network.Settings{Networks: []string{}} + } + settings := container.NetworkSettings - networkSettings, err := container.buildPortMapInfo(ep, networkSettings) + for _, s := range settings.Networks { + sn, err := container.daemon.FindNetwork(s) + if err != nil { + continue + } + + if sn.Name() == n.Name() { + // Avoid duplicate config + return nil + } + if !runconfig.NetworkMode(sn.Type()).IsPrivate() || + !runconfig.NetworkMode(n.Type()).IsPrivate() { + return runconfig.ErrConflictSharedNetwork + } + if runconfig.NetworkMode(sn.Name()).IsNone() || + runconfig.NetworkMode(n.Name()).IsNone() { + return runconfig.ErrConflictNoNetwork + } + } + settings.Networks = append(settings.Networks, n.Name()) + + return nil +} + +func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, ep libnetwork.Endpoint) error { + networkSettings, err := container.buildPortMapInfo(ep, container.NetworkSettings) if err != nil { return err } @@ -667,7 +712,6 @@ func (container *Container) updateEndpointNetworkSettings(n libnetwork.Network, networkSettings.Bridge = container.daemon.configStore.Bridge.Iface } - container.NetworkSettings = networkSettings return nil } @@ -688,7 +732,25 @@ func (container *Container) updateNetwork() error { return derr.ErrorCodeNoSandbox.WithArgs(sid, err) } - options, err := container.buildSandboxOptions() + // Find if container is connected to the default bridge network + var n libnetwork.Network + for _, name := range container.NetworkSettings.Networks { + sn, err := container.daemon.FindNetwork(name) + if err != nil { + continue + } + if sn.Name() == "bridge" { + n = sn + break + } + } + + if n == nil { + // Not connected to the default bridge network; Nothing to do + return nil + } + + options, err := container.buildSandboxOptions(n) if err != nil { return derr.ErrorCodeNetworkUpdate.WithArgs(err) } @@ -781,20 +843,6 @@ func (container *Container) buildCreateEndpointOptions() ([]libnetwork.EndpointO return createOptions, nil } -func parseService(controller libnetwork.NetworkController, service string) (string, string, string) { - dn := controller.Config().Daemon.DefaultNetwork - dd := controller.Config().Daemon.DefaultDriver - - snd := strings.Split(service, ".") - if len(snd) > 2 { - return strings.Join(snd[:len(snd)-2], "."), snd[len(snd)-2], snd[len(snd)-1] - } - if len(snd) > 1 { - return snd[0], snd[1], dd - } - return snd[0], dn, dd -} - func createNetwork(controller libnetwork.NetworkController, dnet string, driver string) (libnetwork.Network, error) { createOptions := []libnetwork.NetworkOption{} genericOption := options.Generic{} @@ -813,61 +861,69 @@ func createNetwork(controller libnetwork.NetworkController, dnet string, driver } func (container *Container) allocateNetwork() error { - mode := container.hostConfig.NetworkMode - controller := container.daemon.netController - if container.Config.NetworkDisabled || mode.IsContainer() { - return nil - } - - networkDriver := string(mode) - service := container.Config.PublishService - networkName := mode.NetworkName() - if mode.IsDefault() { - if service != "" { - service, networkName, networkDriver = parseService(controller, service) - } else { - networkName = controller.Config().Daemon.DefaultNetwork - networkDriver = controller.Config().Daemon.DefaultDriver + settings := container.NetworkSettings.Networks + updateSettings := false + if settings == nil { + mode := container.hostConfig.NetworkMode + controller := container.daemon.netController + if container.Config.NetworkDisabled || mode.IsContainer() { + return nil } - } else if service != "" { - return derr.ErrorCodeNetworkConflict + + networkName := mode.NetworkName() + if mode.IsDefault() { + networkName = controller.Config().Daemon.DefaultNetwork + } + settings = []string{networkName} + updateSettings = true } - if runconfig.NetworkMode(networkDriver).IsBridge() && container.daemon.configStore.DisableBridge { - container.Config.NetworkDisabled = true - return nil - } - - if service == "" { - // dot character "." has a special meaning to support SERVICE[.NETWORK] format. - // For backward compatibility, replacing "." with "-", instead of failing - service = strings.Replace(container.Name, ".", "-", -1) - // Service names dont like "/" in them. removing it instead of failing for backward compatibility - service = strings.Replace(service, "/", "", -1) - } - - if err := container.configureNetwork(networkName, service, networkDriver, mode.IsDefault()); err != nil { - return err + for _, n := range settings { + if err := container.connectToNetwork(n, updateSettings); err != nil { + if updateSettings { + return err + } + // dont fail a container restart case if the user removed the network + logrus.Warnf("Could not connect container %s : %v", container.ID, err) + } } return container.writeHostConfig() } -func (container *Container) configureNetwork(networkName, service, networkDriver string, canCreateNetwork bool) error { +// ConnectToNetwork connects a container to a netork +func (container *Container) ConnectToNetwork(idOrName string) error { + if !container.Running { + return derr.ErrorCodeNotRunning.WithArgs(container.ID) + } + return container.connectToNetwork(idOrName, true) +} + +func (container *Container) connectToNetwork(idOrName string, updateSettings bool) error { + if container.hostConfig.NetworkMode.IsContainer() { + return runconfig.ErrConflictSharedNetwork + } + + if runconfig.NetworkMode(idOrName).IsBridge() && + container.daemon.configStore.DisableBridge { + container.Config.NetworkDisabled = true + return nil + } + controller := container.daemon.netController - n, err := controller.NetworkByName(networkName) + n, err := container.daemon.FindNetwork(idOrName) if err != nil { - if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok || !canCreateNetwork { - return err - } + return err + } - if n, err = createNetwork(controller, networkName, networkDriver); err != nil { + if updateSettings { + if err := container.updateNetworkSettings(n); err != nil { return err } } - ep, err := n.EndpointByName(service) + ep, err := container.getEndpointInNetwork(n) if err != nil { if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok { return err @@ -878,7 +934,8 @@ func (container *Container) configureNetwork(networkName, service, networkDriver return err } - ep, err = n.CreateEndpoint(service, createOptions...) + endpointName := strings.TrimPrefix(container.Name, "/") + ep, err = n.CreateEndpoint(endpointName, createOptions...) if err != nil { return err } @@ -897,7 +954,7 @@ func (container *Container) configureNetwork(networkName, service, networkDriver return false }) if sb == nil { - options, err := container.buildSandboxOptions() + options, err := container.buildSandboxOptions(n) if err != nil { return err } @@ -1039,12 +1096,11 @@ func (container *Container) releaseNetwork() { } sid := container.NetworkSettings.SandboxID - eid := container.NetworkSettings.EndpointID - nid := container.NetworkSettings.NetworkID + networks := container.NetworkSettings.Networks - container.NetworkSettings = &network.Settings{} + container.NetworkSettings = &network.Settings{Networks: networks} - if sid == "" || nid == "" || eid == "" { + if sid == "" || len(networks) == 0 { return } @@ -1054,29 +1110,73 @@ func (container *Container) releaseNetwork() { return } - n, err := container.daemon.netController.NetworkByID(nid) - if err != nil { - logrus.Errorf("error locating network id %s: %v", nid, err) - return - } - - ep, err := n.EndpointByID(eid) - if err != nil { - logrus.Errorf("error locating endpoint id %s: %v", eid, err) - return + for _, ns := range networks { + n, err := container.daemon.FindNetwork(ns) + if err != nil { + continue + } + container.disconnectFromNetwork(n, false) } if err := sb.Delete(); err != nil { logrus.Errorf("Error deleting sandbox id %s for container %s: %v", sid, container.ID, err) - return + } +} + +// DisconnectFromNetwork disconnects a container from a network +func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error { + if !container.Running { + return derr.ErrorCodeNotRunning.WithArgs(container.ID) } - // In addition to leaving all endpoints, delete implicitly created endpoint - if container.Config.PublishService == "" { - if err := ep.Delete(); err != nil { - logrus.Errorf("deleting endpoint failed: %v", err) + return container.disconnectFromNetwork(n, true) +} + +func (container *Container) disconnectFromNetwork(n libnetwork.Network, updateSettings bool) error { + var ( + ep libnetwork.Endpoint + sbox libnetwork.Sandbox + ) + + s := func(current libnetwork.Endpoint) bool { + if sb := current.Info().Sandbox(); sb != nil { + if sb.ContainerID() == container.ID { + ep = current + sbox = sb + return true + } + } + return false + } + n.WalkEndpoints(s) + + if ep == nil { + return fmt.Errorf("could not locate network endpoint for container %s", container.ID) + } + + if err := ep.Leave(sbox); err != nil { + return fmt.Errorf("container %s failed to leave network %s: %v", container.ID, n.Name(), err) + } + + if err := ep.Delete(); err != nil { + return fmt.Errorf("endpoint delete failed for container %s on network %s: %v", container.ID, n.Name(), err) + } + + if updateSettings { + networks := container.NetworkSettings.Networks + for i, s := range networks { + sn, err := container.daemon.FindNetwork(s) + if err != nil { + continue + } + if sn.Name() == n.Name() { + networks = append(networks[:i], networks[i+1:]...) + container.NetworkSettings.Networks = networks + break + } } } + return nil } func (container *Container) unmountVolumes(forceSyscall bool) error { diff --git a/daemon/container_windows.go b/daemon/container_windows.go index 892418252f..ea62f72f61 100644 --- a/daemon/container_windows.go +++ b/daemon/container_windows.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/daemon/execdriver" derr "github.com/docker/docker/errors" + "github.com/docker/libnetwork" ) // DefaultPathEnv is deliberately empty on Windows as the default path will be set by @@ -38,6 +39,16 @@ func (container *Container) initializeNetworking() error { return nil } +// ConnectToNetwork connects a container to the network +func (container *Container) ConnectToNetwork(idOrName string) error { + return nil +} + +// DisconnectFromNetwork disconnects a container from, the network +func (container *Container) DisconnectFromNetwork(n libnetwork.Network) error { + return nil +} + func (container *Container) setupWorkingDirectory() error { return nil } diff --git a/daemon/daemon.go b/daemon/daemon.go index d4eca3de41..f0e9792800 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -1162,11 +1162,6 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig, return verifyPlatformContainerSettings(daemon, hostConfig, config) } -// NetworkController exposes the libnetwork interface to manage networks. -func (daemon *Daemon) NetworkController() libnetwork.NetworkController { - return daemon.netController -} - func configureVolumes(config *Config) (*store.VolumeStore, error) { volumesDriver, err := local.New(config.Root) if err != nil { diff --git a/daemon/network.go b/daemon/network.go new file mode 100644 index 0000000000..546828a4c3 --- /dev/null +++ b/daemon/network.go @@ -0,0 +1,106 @@ +package daemon + +import ( + "errors" + "strings" + + "github.com/docker/libnetwork" + "github.com/docker/libnetwork/netlabel" +) + +const ( + // NetworkByID represents a constant to find a network by its ID + NetworkByID = iota + 1 + // NetworkByName represents a constant to find a network by its Name + NetworkByName +) + +// FindNetwork function finds a network for a given string that can represent network name or id +func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) { + // Find by Name + n, err := daemon.GetNetwork(idName, NetworkByName) + if _, ok := err.(libnetwork.ErrNoSuchNetwork); err != nil && !ok { + return nil, err + } + + if n != nil { + return n, nil + } + + // Find by id + n, err = daemon.GetNetwork(idName, NetworkByID) + if err != nil { + return nil, err + } + + return n, nil +} + +// GetNetwork function returns a network for a given string that represents the network and +// a hint to indicate if the string is an Id or Name of the network +func (daemon *Daemon) GetNetwork(idName string, by int) (libnetwork.Network, error) { + c := daemon.netController + switch by { + case NetworkByID: + list := daemon.GetNetworksByID(idName) + + if len(list) == 0 { + return nil, libnetwork.ErrNoSuchNetwork(idName) + } + + if len(list) > 1 { + return nil, libnetwork.ErrInvalidID(idName) + } + + return list[0], nil + case NetworkByName: + if idName == "" { + idName = c.Config().Daemon.DefaultNetwork + } + return c.NetworkByName(idName) + } + return nil, errors.New("unexpected selector for GetNetwork") +} + +// GetNetworksByID returns a list of networks whose ID partially matches zero or more networks +func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network { + c := daemon.netController + list := []libnetwork.Network{} + l := func(nw libnetwork.Network) bool { + if strings.HasPrefix(nw.ID(), partialID) { + list = append(list, nw) + } + return false + } + c.WalkNetworks(l) + + return list +} + +// CreateNetwork creates a network with the given name, driver and other optional parameters +func (daemon *Daemon) CreateNetwork(name, driver string, options map[string]interface{}) (libnetwork.Network, error) { + c := daemon.netController + if driver == "" { + driver = c.Config().Daemon.DefaultDriver + } + + if options == nil { + options = make(map[string]interface{}) + } + _, ok := options[netlabel.GenericData] + if !ok { + options[netlabel.GenericData] = make(map[string]interface{}) + } + + return c.NewNetwork(driver, name, parseOptions(options)...) +} + +func parseOptions(options map[string]interface{}) []libnetwork.NetworkOption { + var setFctList []libnetwork.NetworkOption + + if options != nil { + setFctList = append(setFctList, libnetwork.NetworkOptionGeneric(options)) + } + + return setFctList +} diff --git a/daemon/network/settings.go b/daemon/network/settings.go index 725b399c31..c88a06c0a7 100644 --- a/daemon/network/settings.go +++ b/daemon/network/settings.go @@ -24,7 +24,7 @@ type Settings struct { LinkLocalIPv6Address string LinkLocalIPv6PrefixLen int MacAddress string - NetworkID string + Networks []string Ports nat.PortMap SandboxKey string SecondaryIPAddresses []Address diff --git a/integration-cli/docker_api_network_test.go b/integration-cli/docker_api_network_test.go index 44d2b31bb7..587cb363ad 100644 --- a/integration-cli/docker_api_network_test.go +++ b/integration-cli/docker_api_network_test.go @@ -1,72 +1,201 @@ -// +build experimental - package main import ( "encoding/json" - "fmt" + "net" "net/http" + "net/url" + "strings" + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/parsers/filters" "github.com/go-check/check" ) +func (s *DockerSuite) TestApiNetworkGetDefaults(c *check.C) { + // By default docker daemon creates 3 networks. check if they are present + defaults := []string{"bridge", "host", "none"} + for _, nn := range defaults { + c.Assert(isNetworkAvailable(c, nn), check.Equals, true) + } +} + +func (s *DockerSuite) TestApiNetworkCreateDelete(c *check.C) { + // Create a network + name := "testnetwork" + id := createNetwork(c, name, true) + c.Assert(isNetworkAvailable(c, name), check.Equals, true) + + // POST another network with same name and CheckDuplicate must fail + createNetwork(c, name, false) + + // delete the network and make sure it is deleted + deleteNetwork(c, id, true) + c.Assert(isNetworkAvailable(c, name), check.Equals, false) +} + +func (s *DockerSuite) TestApiNetworkFilter(c *check.C) { + nr := getNetworkResource(c, getNetworkIDByName(c, "bridge")) + c.Assert(nr.Name, check.Equals, "bridge") +} + +func (s *DockerSuite) TestApiNetworkInspect(c *check.C) { + // Inspect default bridge network + nr := getNetworkResource(c, "bridge") + c.Assert(nr.Name, check.Equals, "bridge") + + // run a container and attach it to the default bridge network + out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top") + containerID := strings.TrimSpace(out) + containerIP := findContainerIP(c, "test") + + // inspect default bridge network again and make sure the container is connected + nr = getNetworkResource(c, nr.ID) + c.Assert(len(nr.Containers), check.Equals, 1) + c.Assert(nr.Containers[containerID], check.NotNil) + + ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) + c.Assert(err, check.IsNil) + c.Assert(ip.String(), check.Equals, containerIP) +} + +func (s *DockerSuite) TestApiNetworkConnectDisconnect(c *check.C) { + // Create test network + name := "testnetwork" + id := createNetwork(c, name, true) + nr := getNetworkResource(c, id) + c.Assert(nr.Name, check.Equals, name) + c.Assert(nr.ID, check.Equals, id) + c.Assert(len(nr.Containers), check.Equals, 0) + + // run a container + out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top") + containerID := strings.TrimSpace(out) + + // connect the container to the test network + connectNetwork(c, nr.ID, containerID) + + // inspect the network to make sure container is connected + nr = getNetworkResource(c, nr.ID) + c.Assert(len(nr.Containers), check.Equals, 1) + c.Assert(nr.Containers[containerID], check.NotNil) + + // check if container IP matches network inspect + ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) + c.Assert(err, check.IsNil) + containerIP := findContainerIP(c, "test") + c.Assert(ip.String(), check.Equals, containerIP) + + // disconnect container from the network + disconnectNetwork(c, nr.ID, containerID) + nr = getNetworkResource(c, nr.ID) + c.Assert(nr.Name, check.Equals, name) + c.Assert(len(nr.Containers), check.Equals, 0) + + // delete the network + deleteNetwork(c, nr.ID, true) +} + 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 { + nJSON := []types.NetworkResource{} + err = json.Unmarshal(body, &nJSON) + c.Assert(err, check.IsNil) + + for _, n := range nJSON { 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) +func getNetworkIDByName(c *check.C, name string) string { + var ( + v = url.Values{} + filterArgs = filters.Args{} + ) + filterArgs["name"] = []string{name} + filterJSON, err := filters.ToParam(filterArgs) c.Assert(err, check.IsNil) + v.Set("filters", filterJSON) - 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) + status, body, err := sockRequest("GET", "/networks?"+v.Encode(), nil) c.Assert(status, check.Equals, http.StatusOK) c.Assert(err, check.IsNil) - if isNetworkAvailable(c, name) { - c.Fatalf("Network %s not deleted", name) - } + nJSON := []types.NetworkResource{} + err = json.Unmarshal(body, &nJSON) + c.Assert(err, check.IsNil) + c.Assert(len(nJSON), check.Equals, 1) + + return nJSON[0].ID +} + +func getNetworkResource(c *check.C, id string) *types.NetworkResource { + _, obj, err := sockRequest("GET", "/networks/"+id, nil) + c.Assert(err, check.IsNil) + + nr := types.NetworkResource{} + err = json.Unmarshal(obj, &nr) + c.Assert(err, check.IsNil) + + return &nr +} + +func createNetwork(c *check.C, name string, shouldSucceed bool) string { + config := types.NetworkCreate{ + Name: name, + Driver: "bridge", + CheckDuplicate: true, + } + + status, resp, err := sockRequest("POST", "/networks/create", config) + if !shouldSucceed { + c.Assert(status, check.Not(check.Equals), http.StatusCreated) + return "" + } + + c.Assert(status, check.Equals, http.StatusCreated) + c.Assert(err, check.IsNil) + + var nr types.NetworkCreateResponse + err = json.Unmarshal(resp, &nr) + c.Assert(err, check.IsNil) + + return nr.ID +} + +func connectNetwork(c *check.C, nid, cid string) { + config := types.NetworkConnect{ + Container: cid, + } + + status, _, err := sockRequest("POST", "/networks/"+nid+"/connect", config) + c.Assert(status, check.Equals, http.StatusOK) + c.Assert(err, check.IsNil) +} + +func disconnectNetwork(c *check.C, nid, cid string) { + config := types.NetworkConnect{ + Container: cid, + } + + status, _, err := sockRequest("POST", "/networks/"+nid+"/disconnect", config) + c.Assert(status, check.Equals, http.StatusOK) + c.Assert(err, check.IsNil) +} + +func deleteNetwork(c *check.C, id string, shouldSucceed bool) { + status, _, err := sockRequest("DELETE", "/networks/"+id, nil) + if !shouldSucceed { + c.Assert(status, check.Not(check.Equals), http.StatusOK) + c.Assert(err, check.NotNil) + return + } + c.Assert(status, check.Equals, http.StatusOK) + c.Assert(err, check.IsNil) } diff --git a/integration-cli/docker_cli_daemon_experimental_test.go b/integration-cli/docker_cli_daemon_experimental_test.go deleted file mode 100644 index 1fca8eaf3e..0000000000 --- a/integration-cli/docker_cli_daemon_experimental_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build daemon,experimental,!windows - -package main - -import ( - "os/exec" - "strings" - - "github.com/go-check/check" -) - -func assertNetwork(c *check.C, d *Daemon, name string) { - out, err := d.Cmd("network", "ls") - c.Assert(err, check.IsNil) - lines := strings.Split(out, "\n") - for i := 1; i < len(lines)-1; i++ { - if strings.Contains(lines[i], name) { - return - } - } - c.Fatalf("Network %s not found in network ls o/p", name) -} - -func (s *DockerDaemonSuite) TestDaemonDefaultNetwork(c *check.C) { - testRequires(c, SameHostDaemon) - d := s.d - - networkName := "testdefault" - err := d.StartWithBusybox("--default-network", "bridge:"+networkName) - c.Assert(err, check.IsNil) - - _, err = d.Cmd("run", "busybox", "true") - c.Assert(err, check.IsNil) - - assertNetwork(c, d, networkName) - - ifconfigCmd := exec.Command("ifconfig", networkName) - _, _, _, err = runCommandWithStdoutStderr(ifconfigCmd) - c.Assert(err, check.IsNil) -} diff --git a/integration-cli/docker_cli_netmode_test.go b/integration-cli/docker_cli_netmode_test.go index 7af359b1c0..faab34e062 100644 --- a/integration-cli/docker_cli_netmode_test.go +++ b/integration-cli/docker_cli_netmode_test.go @@ -77,7 +77,7 @@ func (s *DockerSuite) TestNetHostname(c *check.C) { if out, _, err = runCommandWithOutput(runCmd); err == nil { c.Fatalf(out, err) } - checkContains("invalid --net: weird", out, c) + checkContains("network weird not found", out, c) } func (s *DockerSuite) TestConflictContainerNetworkAndLinks(c *check.C) { diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index a53777c63d..c8d93ad975 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -3168,7 +3168,7 @@ func (s *DockerSuite) TestRunCreateContainerFailedCleanUp(c *check.C) { testRequires(c, DaemonIsLinux) name := "unique_name" _, _, err := dockerCmdWithError("run", "--name", name, "--link", "nothing:nothing", "busybox") - c.Assert(err, check.Not(check.IsNil), check.Commentf("Expected docker run to fail!")) + c.Assert(err, check.NotNil, check.Commentf("Expected docker run to fail!")) containerID, err := inspectField(name, "Id") c.Assert(containerID, check.Equals, "", check.Commentf("Expected not to have this container: %s!", containerID)) @@ -3404,6 +3404,154 @@ func (s *DockerSuite) TestTwoContainersInNetHost(c *check.C) { dockerCmd(c, "stop", "second") } +func (s *DockerSuite) TestContainersInUserDefinedNetwork(c *check.C) { + testRequires(c, DaemonIsLinux) + dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork") + dockerCmd(c, "run", "-d", "--net=testnetwork", "--name=first", "busybox", "top") + dockerCmd(c, "run", "-t", "--net=testnetwork", "--name=second", "busybox", "ping", "-c", "1", "first") + dockerCmd(c, "stop", "first") + dockerCmd(c, "stop", "second") + dockerCmd(c, "network", "rm", "testnetwork") +} + +func (s *DockerSuite) TestContainersInMultipleNetworks(c *check.C) { + testRequires(c, DaemonIsLinux) + // Create 2 networks using bridge driver + dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1") + dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2") + // Run and connect containers to testnetwork1 + dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top") + dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top") + // Check connectivity between containers in testnetwork2 + dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1") + // Connect containers to testnetwork2 + dockerCmd(c, "network", "connect", "testnetwork2", "first") + dockerCmd(c, "network", "connect", "testnetwork2", "second") + // Check connectivity between containers + dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2") + dockerCmd(c, "stop", "first") + dockerCmd(c, "stop", "second") + dockerCmd(c, "network", "rm", "testnetwork1") + dockerCmd(c, "network", "rm", "testnetwork2") +} + +func (s *DockerSuite) TestContainersNetworkIsolation(c *check.C) { + testRequires(c, DaemonIsLinux) + // Create 2 networks using bridge driver + dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1") + dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2") + // Run 1 containers in testnetwork1 and another in testnetwork2 + dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top") + dockerCmd(c, "run", "-d", "--net=testnetwork2", "--name=second", "busybox", "top") + + // Check Isolation between containers : ping must fail + _, _, err := dockerCmdWithError("exec", "first", "ping", "-c", "1", "second") + c.Assert(err, check.NotNil) + // Connect first container to testnetwork2 + dockerCmd(c, "network", "connect", "testnetwork2", "first") + // ping must succeed now + _, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second") + c.Assert(err, check.IsNil) + + // Disconnect first container from testnetwork2 + dockerCmd(c, "network", "disconnect", "testnetwork2", "first") + // ping must fail again + _, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second") + c.Assert(err, check.NotNil) + + dockerCmd(c, "stop", "first") + dockerCmd(c, "stop", "second") + dockerCmd(c, "network", "rm", "testnetwork1") + dockerCmd(c, "network", "rm", "testnetwork2") +} + +func (s *DockerSuite) TestNetworkRmWithActiveContainers(c *check.C) { + testRequires(c, DaemonIsLinux) + // Create 2 networks using bridge driver + dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1") + // Run and connect containers to testnetwork1 + dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top") + dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top") + // Network delete with active containers must fail + _, _, err := dockerCmdWithError("network", "rm", "testnetwork1") + c.Assert(err, check.NotNil) + + dockerCmd(c, "stop", "first") + _, _, err = dockerCmdWithError("network", "rm", "testnetwork1") + c.Assert(err, check.NotNil) + + dockerCmd(c, "stop", "second") + // Network delete must succeed after all the connected containers are inactive + dockerCmd(c, "network", "rm", "testnetwork1") +} + +func (s *DockerSuite) TestContainerRestartInMultipleNetworks(c *check.C) { + testRequires(c, DaemonIsLinux) + // Create 2 networks using bridge driver + dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1") + dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2") + // Run and connect containers to testnetwork1 + dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top") + dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top") + // Check connectivity between containers in testnetwork2 + dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1") + // Connect containers to testnetwork2 + dockerCmd(c, "network", "connect", "testnetwork2", "first") + dockerCmd(c, "network", "connect", "testnetwork2", "second") + // Check connectivity between containers + dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2") + + // Stop second container and test ping failures on both networks + dockerCmd(c, "stop", "second") + _, _, err := dockerCmdWithError("exec", "first", "ping", "-c", "1", "second.testnetwork1") + c.Assert(err, check.NotNil) + _, _, err = dockerCmdWithError("exec", "first", "ping", "-c", "1", "second.testnetwork2") + c.Assert(err, check.NotNil) + + // Start second container and connectivity must be restored on both networks + dockerCmd(c, "start", "second") + dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1") + dockerCmd(c, "exec", "second", "ping", "-c", "1", "first.testnetwork2") + + dockerCmd(c, "stop", "first") + dockerCmd(c, "stop", "second") + dockerCmd(c, "network", "rm", "testnetwork1") + dockerCmd(c, "network", "rm", "testnetwork2") +} + +func (s *DockerSuite) TestContainerWithConflictingHostNetworks(c *check.C) { + testRequires(c, DaemonIsLinux) + // Run a container with --net=host + dockerCmd(c, "run", "-d", "--net=host", "--name=first", "busybox", "top") + + // Create a network using bridge driver + dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1") + + // Connecting to the user defined network must fail + _, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "first") + c.Assert(err, check.NotNil) + dockerCmd(c, "stop", "first") + dockerCmd(c, "network", "rm", "testnetwork1") +} + +func (s *DockerSuite) TestContainerWithConflictingSharedNetwork(c *check.C) { + testRequires(c, DaemonIsLinux) + dockerCmd(c, "run", "-d", "--name=first", "busybox", "top") + // Run second container in first container's network namespace + dockerCmd(c, "run", "-d", "--net=container:first", "--name=second", "busybox", "top") + + // Create a network using bridge driver + dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1") + + // Connecting to the user defined network must fail + _, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "second") + c.Assert(err, check.NotNil) + + dockerCmd(c, "stop", "first") + dockerCmd(c, "stop", "second") + dockerCmd(c, "network", "rm", "testnetwork1") +} + // #11957 - stdin with no tty does not exit if stdin is not closed even though container exited func (s *DockerSuite) TestRunStdinBlockedAfterContainerExit(c *check.C) { cmd := exec.Command(dockerBinary, "run", "-i", "--name=test", "busybox", "true") diff --git a/runconfig/config.go b/runconfig/config.go index 16a2f95b26..48646c1ec4 100644 --- a/runconfig/config.go +++ b/runconfig/config.go @@ -20,7 +20,6 @@ type Config struct { AttachStdout bool // Attach the standard output AttachStderr bool // Attach the standard error ExposedPorts map[nat.Port]struct{} // List of exposed ports - PublishService string // Name of the network service exposed by the container Tty bool // Attach standard streams to a tty, including stdin if it is not closed. OpenStdin bool // Open stdin StdinOnce bool // If true, close stdin after the 1 attached client disconnects. diff --git a/runconfig/hostconfig_test.go b/runconfig/hostconfig_test.go index cf3452ac8a..806f4a8a54 100644 --- a/runconfig/hostconfig_test.go +++ b/runconfig/hostconfig_test.go @@ -24,7 +24,7 @@ func TestNetworkModeTest(t *testing.T) { } networkModeNames := map[NetworkMode]string{ "": "", - "something:weird": "", + "something:weird": "something:weird", "bridge": "bridge", DefaultDaemonNetworkMode(): "bridge", "host": "host", diff --git a/runconfig/hostconfig_unix.go b/runconfig/hostconfig_unix.go index 0ba9e32973..b952b172c2 100644 --- a/runconfig/hostconfig_unix.go +++ b/runconfig/hostconfig_unix.go @@ -34,6 +34,8 @@ func (n NetworkMode) NetworkName() string { return "none" } else if n.IsDefault() { return "default" + } else if n.IsUserDefined() { + return n.UserDefined() } return "" } @@ -59,6 +61,19 @@ func (n NetworkMode) IsNone() bool { return n == "none" } +// IsUserDefined indicates user-created network +func (n NetworkMode) IsUserDefined() bool { + return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() +} + +//UserDefined indicates user-created network +func (n NetworkMode) UserDefined() string { + if n.IsUserDefined() { + return string(n) + } + return "" +} + // MergeConfigs merges the specified container Config and HostConfig. // It creates a ContainerConfigWrapper. func MergeConfigs(config *Config, hostConfig *HostConfig) *ContainerConfigWrapper { diff --git a/runconfig/parse.go b/runconfig/parse.go index ac44ba7345..2a513944a9 100644 --- a/runconfig/parse.go +++ b/runconfig/parse.go @@ -17,6 +17,12 @@ import ( var ( // ErrConflictContainerNetworkAndLinks conflict between --net=container and links ErrConflictContainerNetworkAndLinks = fmt.Errorf("Conflicting options: --net=container can't be used with links. This would result in undefined behavior") + // ErrConflictUserDefinedNetworkAndLinks conflict between --net= and links + ErrConflictUserDefinedNetworkAndLinks = fmt.Errorf("Conflicting options: --net= can't be used with links. This would result in undefined behavior") + // ErrConflictSharedNetwork conflict between private and other networks + ErrConflictSharedNetwork = fmt.Errorf("Container sharing network namespace with another container or host cannot be connected to any other network") + // ErrConflictNoNetwork conflict between private and other networks + ErrConflictNoNetwork = fmt.Errorf("Container cannot be connected to multiple networks with one of the networks in --none mode") // ErrConflictNetworkAndDNS conflict between --dns and the network mode ErrConflictNetworkAndDNS = fmt.Errorf("Conflicting options: --dns and the network mode (--net)") // ErrConflictNetworkHostname conflict between the hostname and the network mode @@ -88,7 +94,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)") flBlkioWeight = cmd.Int64([]string{"-blkio-weight"}, 0, "Block IO (relative weight), between 10 and 1000") flSwappiness = cmd.Int64([]string{"-memory-swappiness"}, -1, "Tuning container memory swappiness (0 to 100)") - flNetMode = cmd.String([]string{"-net"}, "default", "Set the Network mode for the container") + flNetMode = cmd.String([]string{"-net"}, "default", "Set the Network 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") @@ -122,8 +128,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options") cmd.Var(&flLoggingOpts, []string{"-log-opt"}, "Log driver options") - expFlags := attachExperimentalFlags(cmd) - cmd.Require(flag.Min, 1) if err := cmd.ParseFlags(args, true); err != nil { @@ -379,8 +383,6 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe VolumeDriver: *flVolumeDriver, } - applyExperimentalFlags(expFlags, config, hostConfig) - // When allocating stdin in attached mode, close stdin at client disconnect if config.OpenStdin && config.AttachStdin { config.StdinOnce = true diff --git a/runconfig/parse_experimental.go b/runconfig/parse_experimental.go deleted file mode 100644 index 8f8612ba55..0000000000 --- a/runconfig/parse_experimental.go +++ /dev/null @@ -1,19 +0,0 @@ -// +build experimental - -package runconfig - -import flag "github.com/docker/docker/pkg/mflag" - -type experimentalFlags struct { - flags map[string]interface{} -} - -func attachExperimentalFlags(cmd *flag.FlagSet) *experimentalFlags { - flags := make(map[string]interface{}) - flags["publish-service"] = cmd.String([]string{"-publish-service"}, "", "Publish this container as a service") - return &experimentalFlags{flags: flags} -} - -func applyExperimentalFlags(exp *experimentalFlags, config *Config, hostConfig *HostConfig) { - config.PublishService = *(exp.flags["publish-service"]).(*string) -} diff --git a/runconfig/parse_stub.go b/runconfig/parse_stub.go deleted file mode 100644 index 391b6ed43b..0000000000 --- a/runconfig/parse_stub.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build !experimental - -package runconfig - -import flag "github.com/docker/docker/pkg/mflag" - -type experimentalFlags struct{} - -func attachExperimentalFlags(cmd *flag.FlagSet) *experimentalFlags { - return nil -} - -func applyExperimentalFlags(flags *experimentalFlags, config *Config, hostConfig *HostConfig) { -} diff --git a/runconfig/parse_unix.go b/runconfig/parse_unix.go index 1381685d85..fc1aa16d04 100644 --- a/runconfig/parse_unix.go +++ b/runconfig/parse_unix.go @@ -15,14 +15,10 @@ func ValidateNetMode(c *Config, hc *HostConfig) error { return nil } parts := strings.Split(string(hc.NetworkMode), ":") - switch mode := parts[0]; mode { - case "default", "bridge", "none", "host": - case "container": + if parts[0] == "container" { if len(parts) < 2 || parts[1] == "" { return fmt.Errorf("--net: invalid net mode: invalid container format container:") } - default: - return fmt.Errorf("invalid --net: %s", hc.NetworkMode) } if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && c.Hostname != "" { @@ -37,6 +33,10 @@ func ValidateNetMode(c *Config, hc *HostConfig) error { return ErrConflictContainerNetworkAndLinks } + if hc.NetworkMode.IsUserDefined() && len(hc.Links) > 0 { + return ErrConflictUserDefinedNetworkAndLinks + } + if (hc.NetworkMode.IsHost() || hc.NetworkMode.IsContainer()) && len(hc.DNS) > 0 { return ErrConflictNetworkAndDNS } diff --git a/vendor/src/github.com/docker/libnetwork/api/api.go b/vendor/src/github.com/docker/libnetwork/api/api.go deleted file mode 100644 index 8bd88d9a58..0000000000 --- a/vendor/src/github.com/docker/libnetwork/api/api.go +++ /dev/null @@ -1,949 +0,0 @@ -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 - // Gorilla mux encloses the passed pattern with '^' and '$'. So we need to do some tricks - // to have mux eventually build a query regex which matches empty or word string (`^$|[\w]+`) - regex = "[a-zA-Z_0-9-]+" - qregx = "$|" + regex - // Router URL variable definition - nwName = "{" + urlNwName + ":" + regex + "}" - nwNameQr = "{" + urlNwName + ":" + qregx + "}" - nwID = "{" + urlNwID + ":" + regex + "}" - nwPIDQr = "{" + urlNwPID + ":" + qregx + "}" - epName = "{" + urlEpName + ":" + regex + "}" - epNameQr = "{" + urlEpName + ":" + qregx + "}" - epID = "{" + urlEpID + ":" + regex + "}" - epPIDQr = "{" + urlEpPID + ":" + qregx + "}" - 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. - urlNwName = "network-name" - urlNwID = "network-id" - urlNwPID = "network-partial-id" - 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" -) - -// 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", nwNameQr}, procGetNetworks}, - {"/networks", []string{"partial-id", nwPIDQr}, procGetNetworks}, - {"/networks", nil, procGetNetworks}, - {"/networks/" + nwID, nil, procGetNetwork}, - {"/networks/" + nwID + "/endpoints", []string{"name", epNameQr}, procGetEndpoints}, - {"/networks/" + nwID + "/endpoints", []string{"partial-id", epPIDQr}, procGetEndpoints}, - {"/networks/" + nwID + "/endpoints", nil, procGetEndpoints}, - {"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint}, - {"/services", []string{"network", nwNameQr}, procGetServices}, - {"/services", []string{"name", epNameQr}, procGetServices}, - {"/services", []string{"partial-id", epPIDQr}, procGetServices}, - {"/services", nil, procGetServices}, - {"/services/" + epID, nil, procGetService}, - {"/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 + "/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 + "/sandboxes/" + sbID, nil, procLeaveEndpoint}, - {"/services/" + epID, nil, procUnpublishService}, - {"/services/" + epID + "/backend/" + sbID, nil, procDetachBackend}, - {"/sandboxes/" + sbID, nil, procDeleteSandbox}, - }, - } - - 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 - } - } - - res, rsp := fct(ctrl, mux.Vars(req), 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 buildSandboxResource(sb libnetwork.Sandbox) *sandboxResource { - r := &sandboxResource{} - if sb != nil { - r.ID = sb.ID() - r.Key = sb.Key() - r.ContainerID = sb.ContainerID() - } - 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 (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption { - var setFctList []libnetwork.SandboxOption - if sc.HostName != "" { - setFctList = append(setFctList, libnetwork.OptionHostname(sc.HostName)) - } - if sc.DomainName != "" { - setFctList = append(setFctList, libnetwork.OptionDomainname(sc.DomainName)) - } - if sc.HostsPath != "" { - setFctList = append(setFctList, libnetwork.OptionHostsPath(sc.HostsPath)) - } - if sc.ResolvConfPath != "" { - setFctList = append(setFctList, libnetwork.OptionResolvConfPath(sc.ResolvConfPath)) - } - if sc.UseDefaultSandbox { - setFctList = append(setFctList, libnetwork.OptionUseDefaultSandbox()) - } - if sc.UseExternalKey { - setFctList = append(setFctList, libnetwork.OptionUseExternalKey()) - } - if sc.DNS != nil { - for _, d := range sc.DNS { - setFctList = append(setFctList, libnetwork.OptionDNS(d)) - } - } - 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 -*******************/ - -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 - } - 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 -} - -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 -*******************/ -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 - } - - 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 sb.Key(), &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 - } - - sb, errRsp := findSandbox(c, vars[urlSbID], byID) - if !errRsp.isOK() { - return nil, errRsp - } - - err := ep.Leave(sb) - 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 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 - } - - 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 sb.Key(), &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 - } - - 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) - } - - 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 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 - } - 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 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() { - 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 deleted file mode 100644 index 6218932d00..0000000000 --- a/vendor/src/github.com/docker/libnetwork/api/types.go +++ /dev/null @@ -1,80 +0,0 @@ -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"` -} - -// 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 -} - -/*********** - 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"` -} - -// 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"` - UseExternalKey bool `json:"use_external_key"` -} - -// endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages -type endpointJoin struct { - SandboxID string `json:"sandbox_id"` -} - -// 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"` -} - -// extraHost represents the extra host object -type extraHost struct { - Name string `json:"name"` - Address string `json:"address"` -}