Merge pull request #16645 from mavenugo/ux

Docker Network UX & remote API changes
This commit is contained in:
Arnaud Porterie 2015-10-07 10:44:58 -07:00
Родитель a1c373197f dd28ded711
Коммит 8e31036816
59 изменённых файлов: 2254 добавлений и 3306 удалений

Просмотреть файл

@ -1,14 +1,207 @@
// +build experimental
package client
import (
nwclient "github.com/docker/libnetwork/client"
"bytes"
"encoding/json"
"fmt"
"io"
"text/tabwriter"
"github.com/docker/docker/api/types"
Cli "github.com/docker/docker/cli"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/stringid"
)
// CmdNetwork is used to create, display and configure network endpoints.
// CmdNetwork is the parent subcommand for all network commands
//
// Usage: docker network <COMMAND> [OPTIONS]
func (cli *DockerCli) CmdNetwork(args ...string) error {
nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper))
args = append([]string{"network"}, args...)
return nCli.Cmd("docker", args...)
cmd := Cli.Subcmd("network", []string{"COMMAND [OPTIONS]"}, networkUsage(), false)
cmd.Require(flag.Min, 1)
err := cmd.ParseFlags(args, true)
cmd.Usage()
return err
}
// CmdNetworkCreate creates a new network with a given name
//
// Usage: docker network create [OPTIONS] <NETWORK-NAME>
func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
cmd := Cli.Subcmd("network create", []string{"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
nc := types.NetworkCreate{Name: cmd.Arg(0), Driver: *flDriver, CheckDuplicate: true}
obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil))
if err != nil {
return err
}
var resp types.NetworkCreateResponse
err = json.Unmarshal(obj, &resp)
if err != nil {
return err
}
fmt.Fprintf(cli.out, "%s\n", resp.ID)
return nil
}
// CmdNetworkRm deletes a network
//
// Usage: docker network rm <NETWORK-NAME | NETWORK-ID>
func (cli *DockerCli) CmdNetworkRm(args ...string) error {
cmd := Cli.Subcmd("network rm", []string{"NETWORK"}, "Deletes a network", false)
cmd.Require(flag.Exact, 1)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
_, _, err = readBody(cli.call("DELETE", "/networks/"+cmd.Arg(0), nil, nil))
if err != nil {
return err
}
return nil
}
// CmdNetworkConnect connects a container to a network
//
// Usage: docker network connect <NETWORK> <CONTAINER>
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
cmd.Require(flag.Exact, 2)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
nc := types.NetworkConnect{Container: cmd.Arg(1)}
_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/connect", nc, nil))
return err
}
// CmdNetworkDisconnect disconnects a container from a network
//
// Usage: docker network disconnect <NETWORK> <CONTAINER>
func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
cmd.Require(flag.Exact, 2)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
nc := types.NetworkConnect{Container: cmd.Arg(1)}
_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/disconnect", nc, nil))
return err
}
// CmdNetworkLs lists all the netorks managed by docker daemon
//
// Usage: docker network ls [OPTIONS]
func (cli *DockerCli) CmdNetworkLs(args ...string) error {
cmd := Cli.Subcmd("network ls", []string{""}, "Lists all the networks created by the user", false)
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
noTrunc := cmd.Bool([]string{"", "-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 []types.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\tDRIVER")
}
for _, networkResource := range networkResources {
ID := networkResource.ID
netName := networkResource.Name
if !*noTrunc {
ID = stringid.TruncateID(ID)
}
if *quiet {
fmt.Fprintln(wr, ID)
continue
}
driver := networkResource.Driver
fmt.Fprintf(wr, "%s\t%s\t%s\t",
ID,
netName,
driver)
fmt.Fprint(wr, "\n")
}
wr.Flush()
return nil
}
// CmdNetworkInspect inspects the network object for more details
//
// Usage: docker network inspect <NETWORK>
// CmdNetworkInspect handles Network inspect UI
func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
cmd := Cli.Subcmd("network inspect", []string{"NETWORK"}, "Displays detailed information on a network", false)
cmd.Require(flag.Exact, 1)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
obj, _, err := readBody(cli.call("GET", "/networks/"+cmd.Arg(0), nil, nil))
if err != nil {
return err
}
networkResource := &types.NetworkResource{}
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
return err
}
indented := new(bytes.Buffer)
if err := json.Indent(indented, obj, "", " "); err != nil {
return err
}
if _, err := io.Copy(cli.out, indented); err != nil {
return err
}
return nil
}
func networkUsage() string {
networkCommands := map[string]string{
"create": "Create a network",
"connect": "Connect container to a network",
"disconnect": "Disconnect container from a network",
"inspect": "Display detailed network information",
"ls": "List all networks",
"rm": "Remove a network",
}
help := "Commands:\n"
for cmd, description := range networkCommands {
help += fmt.Sprintf(" %-25.25s%s\n", cmd, description)
}
help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.")
return help
}

Просмотреть файл

@ -1,17 +0,0 @@
// +build experimental
package client
import (
"os"
nwclient "github.com/docker/libnetwork/client"
)
// CmdService is used to manage network services.
// service command is user to publish, attach and list a service from a container.
func (cli *DockerCli) CmdService(args ...string) error {
nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper))
args = append([]string{"service"}, args...)
return nCli.Cmd(os.Args[0], args...)
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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),
}
}

Просмотреть файл

@ -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)
}

Просмотреть файл

@ -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
}

Просмотреть файл

@ -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) {
}

Просмотреть файл

@ -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
}

Просмотреть файл

@ -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)
}
}

Просмотреть файл

@ -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"`
}

Просмотреть файл

@ -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"))
}

Просмотреть файл

@ -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) {
}

Просмотреть файл

@ -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)
}

Просмотреть файл

@ -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 {

Просмотреть файл

@ -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
}

Просмотреть файл

@ -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 {

106
daemon/network.go Normal file
Просмотреть файл

@ -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
}

Просмотреть файл

@ -24,7 +24,7 @@ type Settings struct {
LinkLocalIPv6Address string
LinkLocalIPv6PrefixLen int
MacAddress string
NetworkID string
Networks []string
Ports nat.PortMap
SandboxKey string
SecondaryIPAddresses []Address

Просмотреть файл

@ -2,7 +2,7 @@
+++
title = "Network configuration"
description = "Docker networking"
keywords = ["network, networking, bridge, docker, documentation"]
keywords = ["network, networking, bridge, overlay, cluster, multihost, docker, documentation"]
[menu.main]
parent= "smn_administrate"
+++
@ -10,6 +10,9 @@ parent= "smn_administrate"
# Network configuration
> **Note:**
> This document is outdated and needs a major overhaul.
## Summary
When Docker starts, it creates a virtual interface named `docker0` on

Просмотреть файл

@ -15,6 +15,7 @@ weight = 6
Currently, you can extend Docker by adding a plugin. This section contains the following topics:
* [Understand Docker plugins](/extend/plugins)
* [Write a volume plugin](/extend/plugins_volume)
* [Docker plugin API](/extend/plugin_api)
* [Understand Docker plugins](/extend/plugins.md)
* [Write a volume plugin](/extend/plugins_volume.md)
* [Write a network plugin](/extend/plugins_network.md)
* [Docker plugin API](/extend/plugin_api.md)

Просмотреть файл

@ -17,8 +17,10 @@ plugins.
## Types of plugins
Plugins extend Docker's functionality. They come in specific types. For
example, a [volume plugin](/extend/plugins_volume) might enable Docker
volumes to persist across multiple Docker hosts.
example, a [volume plugin](/extend/plugins_volume.md) might enable Docker
volumes to persist across multiple Docker hosts and a
[network plugin](/extend/plugins_network.md) might provide network plumbing
using a favorite networking technology, such as vxlan overlay, ipvlan, EVPN, etc.
Currently Docker supports volume and network driver plugins. In the future it
will support additional plugin types.

Просмотреть файл

@ -1,4 +1,4 @@
# Experimental: Docker network driver plugins
# Docker network driver plugins
Docker supports network driver plugins via
[LibNetwork](https://github.com/docker/libnetwork). Network driver plugins are
@ -21,7 +21,9 @@ commands. For example,
Some network driver plugins are listed in [plugins.md](/docs/extend/plugins.md)
The network thus created is owned by the plugin, so subsequent commands
referring to that network will also be run through the plugin.
referring to that network will also be run through the plugin such as,
docker run --net=mynet busybox top
## Network driver plugin protocol
@ -36,10 +38,3 @@ Google Groups, or the IRC channel #docker-network.
- [#14083](https://github.com/docker/docker/issues/14083) Feedback on
experimental networking features
Other pertinent issues:
- [#13977](https://github.com/docker/docker/issues/13977) UI for using networks
- [#14023](https://github.com/docker/docker/pull/14023) --default-network option
- [#14051](https://github.com/docker/docker/pull/14051) --publish-service option
- [#13441](https://github.com/docker/docker/pull/13441) (Deprecated) Networks API & UI

Просмотреть файл

@ -2484,6 +2484,218 @@ Status Codes
- **409** - volume is in use and cannot be removed
- **500** - server error
## 2.5 Networks
### List networks
`GET /networks`
**Example request**:
GET /networks HTTP/1.1
**Example response**:
HTTP/1.1 200 OK
Content-Type: application/json
```
[
{
"name": "bridge",
"id": "f995e41e471c833266786a64df584fbe4dc654ac99f63a4ee7495842aa093fc4",
"driver": "bridge"
},
{
"name": "none",
"id": "21e34df9b29c74ae45ba312f8e9f83c02433c9a877cfebebcf57be78f69b77c8",
"driver": "null"
},
{
"name": "host",
"id": "3f43a0873f00310a71cd6a71e2e60c113cf17d1812be2ec22fd519fbac68ec91",
"driver": "host"
}
]
```
Query Parameters:
- **filter** - JSON encoded value of the filters (a `map[string][]string`) to process on the volumes list. Available filters: `name=[network-names]` , `id=[network-ids]`
Status Codes:
- **200** - no error
- **500** - server error
### Inspect network
`GET /networks/<network-id>`
**Example request**:
GET /networks/f995e41e471c833266786a64df584fbe4dc654ac99f63a4ee7495842aa093fc4 HTTP/1.1
**Example response**:
HTTP/1.1 200 OK
Content-Type: application/json
```
{
"name": "bridge",
"id": "f995e41e471c833266786a64df584fbe4dc654ac99f63a4ee7495842aa093fc4",
"driver": "bridge",
"containers": {
"931d29e96e63022a3691f55ca18b28600239acf53878451975f77054b05ba559": {
"endpoint": "aa79321e2899e6d72fcd46e6a4ad7f81ab9a19c3b06e384ef4ce51fea35827f9",
"mac_address": "02:42:ac:11:00:04",
"ipv4_address": "172.17.0.4/16"
},
"961249b4ae6c764b11eed923e8463c102689111fffd933627b2e7e359c7d0f7c": {
"endpoint": "4f62c5aea6b9a70512210be7db976bd4ec2cdba47125e4fe514d18c81b1624b1",
"mac_address": "02:42:ac:11:00:02",
"ipv4_address": "172.17.0.2/16"
},
"9f6e0fec4449f42a173ed85be96dc2253b6719edd850d8169bc31bdc45db675c": {
"endpoint": "352b512a5bccdfc77d16c2c04d04408e718f879a16f9ce3913a4733139e4f98d",
"mac_address": "02:42:ac:11:00:03",
"ipv4_address": "172.17.0.3/16"
}
}
}
```
Status Codes:
- **200** - no error
- **404** - network not found
### Create a network
`POST /networks/create`
Create a network
**Example request**:
POST /networks/create HTTP/1.1
Content-Type: application/json
```
{
"name":"isolated_nw",
"driver":"bridge"
}
```
**Example response**:
HTTP/1.1 201 Created
Content-Type: application/json
```
{
"id": "22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30",
"warning": ""
}
```
Status Codes:
- **201** - no error
- **404** - driver not found
- **500** - server error
JSON Parameters:
- **name** - The new network's name. this is a mandatory field
- **driver** - Name of the network driver to use. Defaults to `bridge` driver
- **options** - Network specific options to be used by the drivers
- **check_duplicate** - Requests daemon to check for networks with same name
### Connect a container to a network
`POST /networks/(id)/connect`
Connects a container to a network
**Example request**:
POST /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30/connect HTTP/1.1
Content-Type: application/json
```
{
"container":"3613f73ba0e4"
}
```
**Example response**:
HTTP/1.1 200 OK
Status Codes:
- **201** - no error
- **404** - network or container is not found
JSON Parameters:
- **container** - container-id/name to be connected to the network
### Disconnect a container from a network
`POST /networks/(id)/disconnect`
Disconnects a container from a network
**Example request**:
POST /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30/disconnect HTTP/1.1
Content-Type: application/json
```
{
"container":"3613f73ba0e4"
}
```
**Example response**:
HTTP/1.1 200 OK
Status Codes:
- **201** - no error
- **404** - network or container not found
JSON Parameters:
- **container** - container-id/name to be disconnected from a network
### Remove a network
`DELETE /networks/(id)`
Instruct the driver to remove the network (`id`).
**Example request**:
DELETE /networks/22be93d5babb089c5aab8dbc369042fad48ff791584ca2da2100db837a1c7c30 HTTP/1.1
**Example response**:
HTTP/1.1 204 No Content
Status Codes
- **204** - no error
- **404** - no such network
- **500** - server error
# 3. Going further
## 3.1 Inside `docker run`

Просмотреть файл

@ -0,0 +1,30 @@
<!--[metadata]>
+++
title = "network connect"
description = "The network connect command description and usage"
keywords = ["network, connect"]
[menu.main]
parent = "smn_cli"
+++
<![end-metadata]-->
# network connect
Usage: docker network connect [OPTIONS] NETWORK CONTAINER
Connects a container to a network
--help=false Print usage
Connects a running container to a network. This enables instant communication with other containers belonging to the same network.
```
$ docker network create -d overlay multi-host-network
$ docker run -d --name=container1 busybox top
$ docker network connect multi-host-network container1
```
the container will be connected to the network that is created and managed by the driver (multi-host overlay driver in the above example) or external network plugins.
Multiple containers can be connected to the same network and the containers in the same network will start to communicate with each other. If the driver/plugin supports multi-host connectivity, then the containers connected to the same multi-host network will be able to communicate seamlessly.

Просмотреть файл

@ -0,0 +1,32 @@
<!--[metadata]>
+++
title = "network create"
description = "The network create command description and usage"
keywords = ["network, create"]
[menu.main]
parent = "smn_cli"
+++
<![end-metadata]-->
# network create
Usage: docker network create [OPTIONS] NETWORK-NAME
Creates a new network with a name specified by the user
-d, --driver= Driver to manage the Network
--help=false Print usage
Creates a new network that containers can connect to. If the driver supports multi-host networking, the created network will be made available across all the hosts in the cluster. Daemon will do its best to identify network name conflicts. But its the users responsibility to make sure network name is unique across the cluster. You create a network and then configure the container to use it, for example:
```
$ docker network create -d overlay multi-host-network
$ docker run -itd --net=multi-host-network busybox
```
the container will be connected to the network that is created and managed by the driver (multi-host overlay driver in the above example) or external network plugins.
Multiple containers can be connected to the same network and the containers in the same network will start to communicate with each other. If the driver/plugin supports multi-host connectivity, then the containers connected to the same multi-host network will be able to communicate seamlessly.
*Note*: UX needs enhancement to accept network options to be passed to the drivers

Просмотреть файл

@ -0,0 +1,27 @@
<!--[metadata]>
+++
title = "network disconnect"
description = "The network disconnect command description and usage"
keywords = ["network, disconnect"]
[menu.main]
parent = "smn_cli"
+++
<![end-metadata]-->
# network disconnect
Usage: docker network disconnect [OPTIONS] NETWORK CONTAINER
Disconnects a container from a network
--help=false Print usage
Disconnects a running container from a network.
```
$ docker network create -d overlay multi-host-network
$ docker run -d --net=multi-host-network --name=container1 busybox top
$ docker network disconnect multi-host-network container1
```
the container will be disconnected from the network.

Просмотреть файл

@ -0,0 +1,49 @@
<!--[metadata]>
+++
title = "network inspect"
description = "The network inspect command description and usage"
keywords = ["network, inspect"]
[menu.main]
parent = "smn_cli"
+++
<![end-metadata]-->
# network inspect
Usage: docker network inspect [OPTIONS] NETWORK
Displays detailed information on a network
--help=false Print usage
Returns information about a network. By default, this command renders all results
in a JSON object.
Example output:
```
$ sudo docker run -itd --name=container1 busybox
f2870c98fd504370fb86e59f32cd0753b1ac9b69b7d80566ffc7192a82b3ed27
$ sudo docker run -itd --name=container2 busybox
bda12f8922785d1f160be70736f26c1e331ab8aaf8ed8d56728508f2e2fd4727
$ sudo docker network inspect bridge
{
"name": "bridge",
"id": "7fca4eb8c647e57e9d46c32714271e0c3f8bf8d17d346629e2820547b2d90039",
"driver": "bridge",
"containers": {
"bda12f8922785d1f160be70736f26c1e331ab8aaf8ed8d56728508f2e2fd4727": {
"endpoint": "e0ac95934f803d7e36384a2029b8d1eeb56cb88727aa2e8b7edfeebaa6dfd758",
"mac_address": "02:42:ac:11:00:03",
"ipv4_address": "172.17.0.3/16"
},
"f2870c98fd504370fb86e59f32cd0753b1ac9b69b7d80566ffc7192a82b3ed27": {
"endpoint": "31de280881d2a774345bbfb1594159ade4ae4024ebfb1320cb74a30225f6a8ae",
"mac_address": "02:42:ac:11:00:02",
"ipv4_address": "172.17.0.2/16"
}
}
}
```

Просмотреть файл

@ -0,0 +1,32 @@
<!--[metadata]>
+++
title = "network ls"
description = "The network ls command description and usage"
keywords = ["network, list"]
[menu.main]
parent = "smn_cli"
+++
<![end-metadata]-->
# docker network ls
Usage: docker network ls [OPTIONS]
Lists all the networks created by the user
--help=false Print usage
-l, --latest=false Show the latest network created
-n=-1 Show n last created networks
--no-trunc=false Do not truncate the output
-q, --quiet=false Only display numeric IDs
Lists all the networks Docker knows about. This include the networks that spans across multiple hosts in a cluster.
Example output:
```
$ sudo docker network ls
NETWORK ID NAME DRIVER
7fca4eb8c647 bridge bridge
9f904ee27bf5 none null
cf03ee007fb4 host host
```

Просмотреть файл

@ -0,0 +1,23 @@
<!--[metadata]>
+++
title = "network rm"
description = "the network rm command description and usage"
keywords = ["network, rm"]
[menu.main]
parent = "smn_cli"
+++
<![end-metadata]-->
# network rm
Usage: docker network rm [OPTIONS] NETWORK
Deletes a network
--help=false Print usage
Removes a network. You cannot remove a network that is in use by 1 or more containers.
```
$ docker network rm my-network
```

Просмотреть файл

@ -132,6 +132,12 @@ namespaces, cgroups, capabilities, and filesystem access controls. It allows
you to manage the lifecycle of the container performing additional operations
after the container is created.
## libnetwork
libnetwork provides a native Go implementation for creating and managing container
network namespaces and other network resources. It manage the networking lifecycle
of the container performing additional operations after the container is created.
## link
links provide an interface to connect Docker containers running on the same host
@ -149,7 +155,12 @@ installs Docker on them, then configures the Docker client to talk to them.
*Also known as : docker-machine*
## overlay
## overlay network driver
Overlay network driver provides out of the box multi-host network connectivity
for docker containers in a cluster.
## overlay storage driver
OverlayFS is a [filesystem](#filesystem) service for Linux which implements a
[union mount](http://en.wikipedia.org/wiki/Union_mount) for other file systems.

Просмотреть файл

@ -245,11 +245,12 @@ of the containers.
## Network settings
--dns=[] : Set custom dns servers for the container
--net="bridge" : Set the Network mode for the container
--net="bridge" : Connects a container to a network
'bridge': creates a new network stack for the container on the docker bridge
'none': no networking for this container
'container:<name|id>': reuses another container network stack
'host': use the host network stack inside the container
'NETWORK': connects the container to user-created network using `docker network create` command
--add-host="" : Add a line to /etc/hosts (host:IP)
--mac-address="" : Sets the container's Ethernet device's MAC address
@ -269,12 +270,12 @@ By default, the MAC address is generated using the IP address allocated to the
container. You can set the container's MAC address explicitly by providing a
MAC address via the `--mac-address` parameter (format:`12:34:56:78:9a:bc`).
Supported networking modes are:
Supported networks :
<table>
<thead>
<tr>
<th class="no-wrap">Mode</th>
<th class="no-wrap">Network</th>
<th>Description</th>
</tr>
</thead>
@ -304,19 +305,25 @@ Supported networking modes are:
its *name* or *id*.
</td>
</tr>
<tr>
<td class="no-wrap"><strong>NETWORK</strong></td>
<td>
Connects the container to a user created network (using `docker network create` command)
</td>
</tr>
</tbody>
</table>
#### Mode: none
#### Network: none
With the networking mode set to `none` a container will not have a
With the network is `none` a container will not have
access to any external routes. The container will still have a
`loopback` interface enabled in the container but it does not have any
routes to external traffic.
#### Mode: bridge
#### Network: bridge
With the networking mode set to `bridge` a container will use docker's
With the network set to `bridge` a container will use docker's
default networking setup. A bridge is setup on the host, commonly named
`docker0`, and a pair of `veth` interfaces will be created for the
container. One side of the `veth` pair will remain on the host attached
@ -325,9 +332,9 @@ container's namespaces in addition to the `loopback` interface. An IP
address will be allocated for containers on the bridge's network and
traffic will be routed though this bridge to the container.
#### Mode: host
#### Network: host
With the networking mode set to `host` a container will share the host's
With the network set to `host` a container will share the host's
network stack and all interfaces from the host will be available to the
container. The container's hostname will match the hostname on the host
system. Note that `--add-host` `--hostname` `--dns` `--dns-search`
@ -343,9 +350,9 @@ or a High Performance Web Server.
> **Note**: `--net="host"` gives the container full access to local system
> services such as D-bus and is therefore considered insecure.
#### Mode: container
#### Network: container
With the networking mode set to `container` a container will share the
With the network set to `container` a container will share the
network stack of another container. The other container's name must be
provided in the format of `--net container:<name|id>`. Note that `--add-host`
`--hostname` `--dns` `--dns-search` `--dns-opt` and `--mac-address` are
@ -360,6 +367,21 @@ running the `redis-cli` command and connecting to the Redis server over the
$ # use the redis container's network stack to access localhost
$ docker run --rm -it --net container:redis example/redis-cli -h 127.0.0.1
#### Network: User-Created NETWORK
In addition to all the above special networks, user can create a network using
their favorite network driver or external plugin. The driver used to create the
network takes care of all the network plumbing requirements for the container
connected to that network.
Example creating a network using the inbuilt overlay network driver and running
a container in the created network
```
$ docker network create -d overlay multi-host-network
$ docker run --net=multi-host-network -itd --name=container3 busybox
```
### Managing /etc/hosts
Your container will have lines in `/etc/hosts` which define the hostname of the

Просмотреть файл

@ -347,7 +347,7 @@ allowing linked communication to continue.
# Next step
Now that you know how to link Docker containers together, the next step is
learning how to manage data, volumes and mounts inside your containers.
learning how to take complete control over docker networking.
Go to [Managing Data in Containers](/userguide/dockervolumes).
Go to [Docker Networking](/userguide/dockernetworks.md).

Просмотреть файл

@ -0,0 +1,510 @@
<!--[metadata]>
+++
title = "Docker container networking"
description = "How do we connect docker containers within and across hosts ?"
keywords = ["Examples, Usage, network, docker, documentation, user guide, multihost, cluster"]
[menu.main]
parent = "smn_containers"
weight = 3
+++
<![end-metadata]-->
# Docker container networking
So far we've been introduced to some [basic Docker
concepts](/userguide/usingdocker/), seen how to work with [Docker
images](/userguide/dockerimages/) as well as learned about basic [networking
and links between containers](/userguide/dockerlinks/). In this section
we're going to discuss how you can take control over more advanced
container networking.
This section makes use of `docker network` commands and outputs to explain the
advanced networking functionality supported by Docker.
# Default Networks
By default, docker creates 3 networks using 3 different network drivers :
```
$ sudo docker network ls
NETWORK ID NAME DRIVER
7fca4eb8c647 bridge bridge
9f904ee27bf5 none null
cf03ee007fb4 host host
```
`docker network inspect` gives more information about a network
```
$ sudo docker network inspect bridge
{
"name": "bridge",
"id": "7fca4eb8c647e57e9d46c32714271e0c3f8bf8d17d346629e2820547b2d90039",
"driver": "bridge"
}
```
By default containers are launched on Bridge network
```
$ sudo docker run -itd --name=container1 busybox
f2870c98fd504370fb86e59f32cd0753b1ac9b69b7d80566ffc7192a82b3ed27
$ sudo docker run -itd --name=container2 busybox
bda12f8922785d1f160be70736f26c1e331ab8aaf8ed8d56728508f2e2fd4727
```
```
$ sudo docker network inspect bridge
{
"name": "bridge",
"id": "7fca4eb8c647e57e9d46c32714271e0c3f8bf8d17d346629e2820547b2d90039",
"driver": "bridge",
"containers": {
"bda12f8922785d1f160be70736f26c1e331ab8aaf8ed8d56728508f2e2fd4727": {
"endpoint": "e0ac95934f803d7e36384a2029b8d1eeb56cb88727aa2e8b7edfeebaa6dfd758",
"mac_address": "02:42:ac:11:00:03",
"ipv4_address": "172.17.0.3/16"
},
"f2870c98fd504370fb86e59f32cd0753b1ac9b69b7d80566ffc7192a82b3ed27": {
"endpoint": "31de280881d2a774345bbfb1594159ade4ae4024ebfb1320cb74a30225f6a8ae",
"mac_address": "02:42:ac:11:00:02",
"ipv4_address": "172.17.0.2/16"
}
}
}
```
`docker network inspect` command above shows all the connected containers and its network resources on a given network
Containers in a network should be able to communicate with each other using container names
```
$ sudo docker attach container1
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:17 errors:0 dropped:0 overruns:0 frame:0
TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1382 (1.3 KiB) TX bytes:258 (258.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ # ping container2
PING container2 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.125 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.130 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.172 ms
^C
--- container2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.125/0.142/0.172 ms
/ # cat /etc/hosts
172.17.0.2 f2870c98fd50
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 container1
172.17.0.2 container1.bridge
172.17.0.3 container2
172.17.0.3 container2.bridge
```
```
$ sudo docker attach container2
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03
inet addr:172.17.0.3 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:3/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ # ping container1
PING container1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.277 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.179 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.130 ms
64 bytes from 172.17.0.2: seq=3 ttl=64 time=0.113 ms
^C
--- container1 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.113/0.174/0.277 ms
/ # cat /etc/hosts
172.17.0.3 bda12f892278
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 container1
172.17.0.2 container1.bridge
172.17.0.3 container2
172.17.0.3 container2.bridge
/ #
```
# User defined Networks
In addition to the inbuilt networks, user can create networks using inbuilt drivers
(such as bridge or overlay driver) or external plugins supplied by the community.
Networks by definition should provides complete isolation for the containers.
```
$ docker network create -d bridge isolated_nw
8b05faa32aeb43215f67678084a9c51afbdffe64cd91e3f5bb8267475f8bf1a7
$ docker network inspect isolated_nw
{
"name": "isolated_nw",
"id": "8b05faa32aeb43215f67678084a9c51afbdffe64cd91e3f5bb8267475f8bf1a7",
"driver": "bridge"
}
$ docker network ls
NETWORK ID NAME DRIVER
9f904ee27bf5 none null
cf03ee007fb4 host host
7fca4eb8c647 bridge bridge
8b05faa32aeb isolated_nw bridge
```
Container can be launched on a user-defined network using the --net=<NETWORK> option
in `docker run` command
```
$ docker run --net=isolated_nw -itd --name=container3 busybox
777344ef4943d34827a3504a802bf15db69327d7abe4af28a05084ca7406f843
$ docker network inspect isolated_nw
{
"name": "isolated_nw",
"id": "8b05faa32aeb43215f67678084a9c51afbdffe64cd91e3f5bb8267475f8bf1a7",
"driver": "bridge",
"containers": {
"777344ef4943d34827a3504a802bf15db69327d7abe4af28a05084ca7406f843": {
"endpoint": "c7f22f8da07fb8ecc687d08377cfcdb80b4dd8624c2a8208b1a4268985e38683",
"mac_address": "02:42:ac:14:00:01",
"ipv4_address": "172.20.0.1/16"
}
}
}
```
# Connecting to Multiple networks
Docker containers can dynamically connect to 1 or more networks with each network backed
by same or different network driver / plugin.
```
$ docker network connect isolated_nw container2
$ docker network inspect isolated_nw
{
"name": "isolated_nw",
"id": "8b05faa32aeb43215f67678084a9c51afbdffe64cd91e3f5bb8267475f8bf1a7",
"driver": "bridge",
"containers": {
"777344ef4943d34827a3504a802bf15db69327d7abe4af28a05084ca7406f843": {
"endpoint": "c7f22f8da07fb8ecc687d08377cfcdb80b4dd8624c2a8208b1a4268985e38683",
"mac_address": "02:42:ac:14:00:01",
"ipv4_address": "172.20.0.1/16"
},
"bda12f8922785d1f160be70736f26c1e331ab8aaf8ed8d56728508f2e2fd4727": {
"endpoint": "2ac11345af68b0750341beeda47cc4cce93bb818d8eb25e61638df7a4997cb1b",
"mac_address": "02:42:ac:14:00:02",
"ipv4_address": "172.20.0.2/16"
}
}
}
```
Lets check the network resources used by container2.
```
$ docker inspect --format='{{.NetworkSettings.Networks}}' container2
[bridge isolated_nw]
$ sudo docker attach container2
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03
inet addr:172.17.0.3 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:3/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:21 errors:0 dropped:0 overruns:0 frame:0
TX packets:18 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1586 (1.5 KiB) TX bytes:1460 (1.4 KiB)
eth1 Link encap:Ethernet HWaddr 02:42:AC:14:00:02
inet addr:172.20.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe14:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
```
In the example discussed in this section thus far, container3 and container2 are
connected to isolated_nw and can talk to each other.
But container3 and container1 are not in the same network and hence they cannot communicate.
```
$ docker attach container3
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:14:00:01
inet addr:172.20.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe14:1/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:24 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1944 (1.8 KiB) TX bytes:648 (648.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ # ping container2.isolated_nw
PING container2.isolated_nw (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.217 ms
64 bytes from 172.20.0.2: seq=1 ttl=64 time=0.150 ms
64 bytes from 172.20.0.2: seq=2 ttl=64 time=0.188 ms
64 bytes from 172.20.0.2: seq=3 ttl=64 time=0.176 ms
^C
--- container2.isolated_nw ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.150/0.182/0.217 ms
/ # ping container2
PING container2 (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.120 ms
64 bytes from 172.20.0.2: seq=1 ttl=64 time=0.109 ms
^C
--- container2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.109/0.114/0.120 ms
/ # ping container1
ping: bad address 'container1'
/ # ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
^C
--- 172.17.0.2 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
/ # ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
^C
--- 172.17.0.3 ping statistics ---
4 packets transmitted, 0 packets received, 100% packet loss
```
While container2 is attached to both the networks (bridge and isolated_nw) and hence it
can talk to both container1 and container3
```
$ docker attach container2
/ # cat /etc/hosts
172.17.0.3 bda12f892278
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 container1
172.17.0.2 container1.bridge
172.17.0.3 container2
172.17.0.3 container2.bridge
172.20.0.1 container3
172.20.0.1 container3.isolated_nw
172.20.0.2 container2
172.20.0.2 container2.isolated_nw
/ # ping container3
PING container3 (172.20.0.1): 56 data bytes
64 bytes from 172.20.0.1: seq=0 ttl=64 time=0.138 ms
64 bytes from 172.20.0.1: seq=1 ttl=64 time=0.133 ms
64 bytes from 172.20.0.1: seq=2 ttl=64 time=0.133 ms
^C
--- container3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.133/0.134/0.138 ms
/ # ping container1
PING container1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.121 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.250 ms
64 bytes from 172.17.0.2: seq=2 ttl=64 time=0.133 ms
^C
--- container1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.121/0.168/0.250 ms
/ #
```
Just like it is easy to connect a container to multiple networks, one can
disconnect a container from a network using the `docker network disconnect` command.
```
root@Ubuntu-vm ~$ docker network disconnect isolated_nw container2
$ docker inspect --format='{{.NetworkSettings.Networks}}' container2
[bridge]
root@Ubuntu-vm ~$ docker network inspect isolated_nw
{
"name": "isolated_nw",
"id": "8b05faa32aeb43215f67678084a9c51afbdffe64cd91e3f5bb8267475f8bf1a7",
"driver": "bridge",
"containers": {
"777344ef4943d34827a3504a802bf15db69327d7abe4af28a05084ca7406f843": {
"endpoint": "c7f22f8da07fb8ecc687d08377cfcdb80b4dd8624c2a8208b1a4268985e38683",
"mac_address": "02:42:ac:14:00:01",
"ipv4_address": "172.20.0.1/16"
}
}
}
```
Once a container is disconnected from a network, it cannot communicate with other containers
connected to that network. In this example, container2 cannot talk to container3 any more
in isolated_nw
```
$ sudo docker attach container2
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03
inet addr:172.17.0.3 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:3/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:26 errors:0 dropped:0 overruns:0 frame:0
TX packets:23 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1964 (1.9 KiB) TX bytes:1838 (1.7 KiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
/ # ping container3
PING container3 (172.20.0.1): 56 data bytes
^C
--- container3 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss
But container2 still has full connectivity to the bridge network
/ # ping container1
PING container1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.119 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.174 ms
^C
--- container1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.119/0.146/0.174 ms
/ #
```
When all the containers in a network stops or disconnected the network can be removed
```
$ docker network inspect isolated_nw
{
"name": "isolated_nw",
"id": "8b05faa32aeb43215f67678084a9c51afbdffe64cd91e3f5bb8267475f8bf1a7",
"driver": "bridge"
}
$ docker network rm isolated_nw
$ docker network ls
NETWORK ID NAME DRIVER
9f904ee27bf5 none null
cf03ee007fb4 host host
7fca4eb8c647 bridge bridge
```
# Native Multi-host networking
With the help of libnetwork and the inbuilt `VXLAN based overlay network driver` docker supports multi-host networking natively out of the box. Technical details are documented under https://github.com/docker/libnetwork/blob/master/docs/overlay.md.
Using the exact same above `docker network` UI, the user can exercise the power of multi-host networking.
In order to create a network using the inbuilt overlay driver,
```
$ docker network create -d overlay multi-host-network
```
Since `network` object is globally significant, this feature requires distributed states provided by `libkv`. Using `libkv`, the user can plug any of the supported Key-Value store (such as consul, etcd or zookeeper).
User can specify the Key-Value store of choice using the `--cluster-store` daemon flag, which takes configuration value of format `PROVIDER://URL`, where
`PROVIDER` is the name of the Key-Value store (such as consul, etcd or zookeeper) and
`URL` is the url to reach the Key-Value store.
Example : `docker daemon --cluster-store=consul://localhost:8500`
# Next step
Now that you know how to link Docker containers together, the next step is
learning how to manage data, volumes and mounts inside your containers.
Go to [Managing Data in Containers](/userguide/dockervolumes.md).

Просмотреть файл

@ -42,7 +42,7 @@ Go to [Using Docker Hub](/docker-hub).
Docker offers a *container-based* virtualization platform to power your
applications. To learn how to Dockerize applications and run them:
Go to [Dockerizing Applications](/userguide/dockerizing).
Go to [Dockerizing Applications](/docs/userguide/dockerizing.md).
## Working with containers
@ -52,7 +52,7 @@ Once you get a grip on running your applications in Docker containers
we're going to show you how to manage those containers. To find out
about how to inspect, monitor and manage containers:
Go to [Working With Containers](/userguide/usingdocker).
Go to [Working With Containers](/docs/userguide/usingdocker.md).
## Working with Docker images
@ -61,7 +61,7 @@ Go to [Working With Containers](/userguide/usingdocker).
Once you've learnt how to use Docker it's time to take the next step and
learn how to build your own application images with Docker.
Go to [Working with Docker Images](/userguide/dockerimages).
Go to [Working with Docker Images](/docs/userguide/dockerimages.md).
## Linking containers together
@ -69,14 +69,24 @@ Until now we've seen how to build individual applications inside Docker
containers. Now learn how to build whole application stacks with Docker
by linking together multiple Docker containers.
Go to [Linking Containers Together](/userguide/dockerlinks).
Go to [Linking Containers Together](/docs/userguide/dockerlinks.md).
## Docker container networking
Links provides a very easy and convenient way to connect the containers.
But, it is very opinionated and doesnt provide a lot of flexibility or
choice to the end-users. Now, lets learn about a flexible way to connect
containers together within a host or across multiple hosts in a cluster
using various networking technologies, with the help of extensible plugins.
Go to [Docker Networking](/docs/userguide/dockernetworks.md).
## Managing data in containers
Now we know how to link Docker containers together the next step is
learning how to manage data, volumes and mounts inside our containers.
Go to [Managing Data in Containers](/userguide/dockervolumes).
Go to [Managing Data in Containers](/docs/userguide/dockervolumes.md).
## Working with Docker Hub
@ -84,7 +94,7 @@ Now we've learned a bit more about how to use Docker we're going to see
how to combine Docker with the services available on Docker Hub including
Trusted Builds and private repositories.
Go to [Working with Docker Hub](/userguide/dockerrepos).
Go to [Working with Docker Hub](/docs/userguide/dockerrepos.md).
## Docker Compose

Просмотреть файл

@ -71,11 +71,6 @@ to build a Docker binary with the experimental features enabled:
## Current experimental features
* [Network plugins](plugins_network.md)
* [Networking and Services UI](networking.md)
* [Native multi-host networking](network_overlay.md)
* [Compose, Swarm and networking integration](compose_swarm_networking.md)
## How to comment on an experimental feature
Each feature's documentation includes a list of proposal pull requests or PRs associated with the feature. If you want to comment on or suggest a change to a feature, please add it to the existing feature PR.

Просмотреть файл

@ -1,238 +0,0 @@
# Experimental: Compose, Swarm and Multi-Host Networking
The [experimental build of Docker](https://github.com/docker/docker/tree/master/experimental) has an entirely new networking system, which enables secure communication between containers on multiple hosts. In combination with Docker Swarm and Docker Compose, you can now run multi-container apps on multi-host clusters with the same tooling and configuration format you use to develop them locally.
> Note: This functionality is in the experimental stage, and contains some hacks and workarounds which will be removed as it matures.
## Prerequisites
Before you start, youll need to install the experimental build of Docker, and the latest versions of Machine and Compose.
- To install the experimental Docker build on a Linux machine, follow the instructions [here](https://github.com/docker/docker/tree/master/experimental#install-docker-experimental).
- To install the experimental Docker build on a Mac, run these commands:
$ curl -L https://experimental.docker.com/builds/Darwin/x86_64/docker-latest > /usr/local/bin/docker
$ chmod +x /usr/local/bin/docker
- To install Machine, follow the instructions [here](http://docs.docker.com/machine/).
- To install Compose, follow the instructions [here](http://docs.docker.com/compose/install/).
Youll also need a [Docker Hub](https://hub.docker.com/account/signup/) account and a [Digital Ocean](https://www.digitalocean.com/) account.
It works with the amazonec2 driver as well (by adapting the commands accordingly), except you'll need to manually open the ports 8500 (consul) and 7946 (serf) by editing the inbound rules of the corresponding security group.
## Set up a swarm with multi-host networking
Set the `DIGITALOCEAN_ACCESS_TOKEN` environment variable to a valid Digital Ocean API token, which you can generate in the [API panel](https://cloud.digitalocean.com/settings/applications).
export DIGITALOCEAN_ACCESS_TOKEN=abc12345
Start a consul server:
docker-machine --debug create \
-d digitalocean \
--engine-install-url="https://experimental.docker.com" \
consul
docker $(docker-machine config consul) run -d \
-p "8500:8500" \
-h "consul" \
progrium/consul -server -bootstrap
(In a real world setting youd set up a distributed consul, but thats beyond the scope of this guide!)
Create a Swarm token:
export SWARM_TOKEN=$(docker run swarm create)
Next, you create a Swarm master with Machine:
docker-machine --debug create \
-d digitalocean \
--digitalocean-image="ubuntu-14-04-x64" \
--engine-install-url="https://experimental.docker.com" \
--engine-opt="default-network=overlay:multihost" \
--engine-opt="kv-store=consul:$(docker-machine ip consul):8500" \
--engine-label="com.docker.network.driver.overlay.bind_interface=eth0" \
swarm-0
Usually Machine can create Swarms for you, but it doesn't yet fully support multi-host networks yet, so you'll have to start up the Swarm manually:
docker $(docker-machine config swarm-0) run -d \
--restart="always" \
--net="bridge" \
swarm:latest join \
--addr "$(docker-machine ip swarm-0):2376" \
"token://$SWARM_TOKEN"
docker $(docker-machine config swarm-0) run -d \
--restart="always" \
--net="bridge" \
-p "3376:3376" \
-v "/etc/docker:/etc/docker" \
swarm:latest manage \
--tlsverify \
--tlscacert="/etc/docker/ca.pem" \
--tlscert="/etc/docker/server.pem" \
--tlskey="/etc/docker/server-key.pem" \
-H "tcp://0.0.0.0:3376" \
--strategy spread \
"token://$SWARM_TOKEN"
Create a Swarm node:
docker-machine --debug create \
-d digitalocean \
--digitalocean-image="ubuntu-14-10-x64" \
--engine-install-url="https://experimental.docker.com" \
--engine-opt="default-network=overlay:multihost" \
--engine-opt="kv-store=consul:$(docker-machine ip consul):8500" \
--engine-label="com.docker.network.driver.overlay.bind_interface=eth0" \
--engine-label="com.docker.network.driver.overlay.neighbor_ip=$(docker-machine ip swarm-0)" \
swarm-1
docker $(docker-machine config swarm-1) run -d \
--restart="always" \
--net="bridge" \
swarm:latest join \
--addr "$(docker-machine ip swarm-1):2376" \
"token://$SWARM_TOKEN"
You can create more Swarm nodes if you want - its best to give them sensible names (swarm-2, swarm-3, etc).
Finally, point Docker at your swarm:
export DOCKER_HOST=tcp://"$(docker-machine ip swarm-0):3376"
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH="$HOME/.docker/machine/machines/swarm-0"
## Run containers and get them communicating
Now that youve got a swarm up and running, you can create containers on it just like a single Docker instance:
$ docker run busybox echo hello world
hello world
If you run `docker ps -a`, you can see what node that container was started on by looking at its name (here its swarm-3):
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41f59749737b busybox "echo hello world" 15 seconds ago Exited (0) 13 seconds ago swarm-3/trusting_leakey
As you start more containers, theyll be placed on different nodes across the cluster, thanks to Swarms default “spread” scheduling strategy.
Every container started on this swarm will use the “overlay:multihost” network by default, meaning they can all intercommunicate. Each container gets an IP address on that network, and an `/etc/hosts` file which will be updated on-the-fly with every other containers IP address and name. That means that if you have a running container named foo, other containers can access it at the hostname foo.
Lets verify that multi-host networking is functioning. Start a long-running container:
$ docker run -d --name long-running busybox top
<container id>
If you start a new container and inspect its /etc/hosts file, youll see the long-running container in there:
$ docker run busybox cat /etc/hosts
...
172.21.0.6 long-running
Verify that connectivity works between containers:
$ docker run busybox ping long-running
PING long-running (172.21.0.6): 56 data bytes
64 bytes from 172.21.0.6: seq=0 ttl=64 time=7.975 ms
64 bytes from 172.21.0.6: seq=1 ttl=64 time=1.378 ms
64 bytes from 172.21.0.6: seq=2 ttl=64 time=1.348 ms
^C
--- long-running ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 1.140/2.099/7.975 ms
## Run a Compose application
Heres an example of a simple Python + Redis app using multi-host networking on a swarm.
Create a directory for the app:
$ mkdir composetest
$ cd composetest
Inside this directory, create 2 files.
First, create `app.py` - a simple web app that uses the Flask framework and increments a value in Redis:
from flask import Flask
from redis import Redis
import os
app = Flask(__name__)
redis = Redis(host='composetest_redis_1', port=6379)
@app.route('/')
def hello():
redis.incr('hits')
return 'Hello World! I have been seen %s times.' % redis.get('hits')
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
Note that were connecting to a host called `composetest_redis_1` - this is the name of the Redis container that Compose will start.
Second, create a Dockerfile for the app container:
FROM python:2.7
RUN pip install flask redis
ADD . /code
WORKDIR /code
CMD ["python", "app.py"]
Build the Docker image and push it to the Hub (youll need a Hub account). Replace `<username>` with your Docker Hub username:
$ docker build -t <username>/counter .
$ docker push <username>/counter
Next, create a `docker-compose.yml`, which defines the configuration for the web and redis containers. Once again, replace `<username>` with your Hub username:
web:
image: <username>/counter
ports:
- "80:5000"
redis:
image: redis
Now start the app:
$ docker-compose up -d
Pulling web (username/counter:latest)...
swarm-0: Pulling username/counter:latest... : downloaded
swarm-2: Pulling username/counter:latest... : downloaded
swarm-1: Pulling username/counter:latest... : downloaded
swarm-3: Pulling username/counter:latest... : downloaded
swarm-4: Pulling username/counter:latest... : downloaded
Creating composetest_web_1...
Pulling redis (redis:latest)...
swarm-2: Pulling redis:latest... : downloaded
swarm-1: Pulling redis:latest... : downloaded
swarm-3: Pulling redis:latest... : downloaded
swarm-4: Pulling redis:latest... : downloaded
swarm-0: Pulling redis:latest... : downloaded
Creating composetest_redis_1...
Swarm has created containers for both web and redis, and placed them on different nodes, which you can check with `docker ps`:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
92faad2135c9 redis "/entrypoint.sh redi 43 seconds ago Up 42 seconds swarm-2/composetest_redis_1
adb809e5cdac username/counter "/bin/sh -c 'python 55 seconds ago Up 54 seconds 45.67.8.9:80->5000/tcp swarm-1/composetest_web_1
You can also see that the web container has exposed port 80 on its swarm node. If you curl that IP, youll get a response from the container:
$ curl http://45.67.8.9
Hello World! I have been seen 1 times.
If you hit it repeatedly, the counter will increment, demonstrating that the web and redis container are communicating:
$ curl http://45.67.8.9
Hello World! I have been seen 2 times.
$ curl http://45.67.8.9
Hello World! I have been seen 3 times.
$ curl http://45.67.8.9
Hello World! I have been seen 4 times.

Просмотреть файл

@ -1,14 +0,0 @@
# Native Multi-host networking
There is a lot to talk about the native multi-host networking and the `overlay` driver that makes it happen. The technical details are documented under https://github.com/docker/libnetwork/blob/master/docs/overlay.md.
Using the above experimental UI `docker network`, `docker service` and `--publish-service`, the user can exercise the power of multi-host networking.
Since `network` and `service` objects are globally significant, this feature requires distributed states provided by the `libkv` project.
Using `libkv`, the user can plug any of the supported Key-Value store (such as consul, etcd or zookeeper).
User can specify the Key-Value store of choice using the `--cluster-store` daemon flag, which takes configuration value of format `PROVIDER:URL`, where
`PROVIDER` is the name of the Key-Value store (such as consul, etcd or zookeeper) and
`URL` is the url to reach the Key-Value store.
Example : `docker daemon --cluster-store=consul://localhost:8500`
Send us feedback and comments on [#14083](https://github.com/docker/docker/issues/14083)
or on the usual Google Groups (docker-user, docker-dev) and IRC channels.

Просмотреть файл

@ -1,120 +0,0 @@
# Experimental: Networking and Services
In this feature:
- `network` and `service` become first class objects in the Docker UI
- one can now create networks, publish services on that network and attach containers to the services
- Native multi-host networking
- `network` and `service` objects are globally significant and provides multi-host container connectivity natively
- Inbuilt simple Service Discovery
- With multi-host networking and top-level `service` object, Docker now provides out of the box simple Service Discovery for containers running in a network
- Batteries included but removable
- Docker provides inbuilt native multi-host networking by default & can be swapped by any remote driver provided by external plugins.
This is an experimental feature. For information on installing and using experimental features, see [the experimental feature overview](README.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 <plugin_name> 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
## User-Defined default network
Docker daemon supports a configuration flag `--default-network` which takes configuration value of format `DRIVER:NETWORK`, where,
`DRIVER` represents the in-built drivers such as bridge, overlay, container, host and none. or Remote drivers via Network Plugins.
`NETWORK` is the name of the network created using the `docker network create` command
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.
Example : `docker daemon --default-network=overlay:multihost`
## Using Services
Usage: docker service COMMAND [OPTIONS] [arg...]
Commands:
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
Run 'docker service COMMAND --help' for more information on a command.
--help=false Print usage
Assuming we want to publish a service from container `a0ebc12d3e48` on network `foo` as `my-service` we would use the following command:
$ docker service publish my-service.foo
ec56fd74717d00f968c26675c9a77707e49ae64b8e54832ebf78888eb116e428
$ docker service attach a0ebc12d3e48 my-service.foo
This would make the container `a0ebc12d3e48` accessible as `my-service` on network `foo`. Any other container in network `foo` can use DNS to resolve the address of `my-service`
This can also be achieved by using the `--publish-service` flag for `docker run`:
docker run -itd --publish-service db.foo postgres
`db.foo` in this instance means "place the container on network `foo`, and allow other hosts on `foo` to discover it under the name `db`"
We can see the current services using the `docker service ls` command
$ docker service ls
SERVICE ID NAME NETWORK PROVIDER
ec56fd74717d my-service foo a0ebc12d3e48
To remove the a service:
$ docker service detach a0ebc12d3e48 my-service.foo
$ docker service unpublish my-service.foo
Send us feedback and comments on [#14083](https://github.com/docker/docker/issues/14083)
or on the usual Google Groups (docker-user, docker-dev) and IRC channels.

Просмотреть файл

@ -1,489 +0,0 @@
# 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/<network_id>`
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/<network_id>/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/<network_id>/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/<network_id>/endpoints/<endpoint_id>`
**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/<network_id>/endpoints/<endpoint_id>/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/<network_id>/endpoints/<endpoint_id>/containers/<container_id>`
**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/<network_id>/endpoints/<endpoint_id>`
**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/<network_id>`
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
# Services API
### Publish a Service
`POST /services`
Publish a service
**Example Request**
POST /services HTTP/1.1
Content-Type: application/json
{
"name": "bar",
"network_name": "foo",
"exposed_ports": null,
"port_mapping": null
}
**Example Response**
HTTP/1.1 200 OK
Content-Type: application/json
"0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff"
Status Codes:
- **200** – no error
- **400** – bad parameter
- **500** – server error
### Get a Service
`GET /services/0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff`
Get a service
**Example Request**:
GET /services/0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff HTTP/1.1
**Example Response**:
HTTP/1.1 200 OK
Content-Type: application/json
{
"name": "bar",
"id": "0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff",
"network": "foo"
}
Status Codes:
- **200** – no error
- **400** – bad parameter
- **404** - not found
- **500** – server error
### Attach a backend to a service
`POST /services/0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff/backend`
Attach a backend to a service
**Example Request**:
POST /services/0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff/backend HTTP/1.1
Content-Type: application/json
{
"container_id": "98c5241f9475e9efc17e7198e931fb48166010b80f96d48df204e251378ca547",
"host_name": "",
"domain_name": "",
"hosts_path": "",
"resolv_conf_path": "",
"dns": null,
"extra_hosts": null,
"parent_updates": null,
"use_default_sandbox": false
}
**Example Response**:
HTTP/1.1 200 OK
Content-Type: application/json
"/var/run/docker/netns/98c5241f9475"
Status Codes:
- **200** – no error
- **400** – bad parameter
- **500** – server error
### Get Backends for a Service
Get all backends for a given service
**Example Request**
GET /services/0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff/backend HTTP/1.1
**Example Response**
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": "98c5241f9475e9efc17e7198e931fb48166010b80f96d48df204e251378ca547"
}
]
Status Codes:
- **200** – no error
- **400** – bad parameter
- **500** – server error
### List Services
`GET /services`
List services
**Example request**:
GET /services HTTP/1.1
**Example response**:
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"name": "/stupefied_stallman",
"id": "c826b26bf736fb4a77db33f83562e59f9a770724e259ab9c3d50d948f8233ae4",
"network": "bridge"
},
{
"name": "bar",
"id": "0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff",
"network": "foo"
}
]
Query Parameters:
- **name** – Filter results with the given name
- **partial-id** – Filter results using the partial network ID
- **network** - Filter results by the given network
Status Codes:
- **200** – no error
- **400** – bad parameter
- **500** – server error
### Detach a Backend from a Service
`DELETE /services/0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff/backend/98c5241f9475e9efc17e7198e931fb48166010b80f96d48df204e251378ca547`
Detach a backend from a service
**Example Request**
DELETE /services/0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff/backend/98c5241f9475e9efc17e7198e931fb48166010b80f96d48df204e251378ca547 HTTP/1.1
**Example Response**
HTTP/1.1 200 OK
Status Codes:
- **200** – no error
- **400** – bad parameter
- **500** – server error
### Un-Publish a Service
`DELETE /services/0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff`
Unpublish a service
**Example Request**
DELETE /services/0aee0899e6c5e903cf3ef2bdc28a1c9aaf639c8c8c331fa4ae26344d9e32c1ff HTTP/1.1
**Example Response**
HTTP/1.1 200 OK
Status Codes:
- **200** – no error
- **400** – bad parameter
- **500** – server error

Просмотреть файл

@ -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)
}

Просмотреть файл

@ -1,113 +0,0 @@
// +build experimental
package main
import (
"encoding/json"
"fmt"
"net/http"
"github.com/go-check/check"
)
func isServiceAvailable(c *check.C, name string, network string) bool {
status, body, err := sockRequest("GET", "/services", nil)
c.Assert(status, check.Equals, http.StatusOK)
c.Assert(err, check.IsNil)
var inspectJSON []struct {
Name string
ID string
Network string
}
if err = json.Unmarshal(body, &inspectJSON); err != nil {
c.Fatalf("unable to unmarshal response body: %v", err)
}
for _, s := range inspectJSON {
if s.Name == name && s.Network == network {
return true
}
}
return false
}
func isServiceNetworkAvailable(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) TestServiceApiCreateDelete(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 !isServiceNetworkAvailable(c, name) {
c.Fatalf("Network %s not found", name)
}
var nid string
err = json.Unmarshal(resp, &nid)
if err != nil {
c.Fatal(err)
}
sname := "service1"
sconfig := map[string]interface{}{
"name": sname,
"network_name": name,
}
status, resp, err = sockRequest("POST", "/services", sconfig)
c.Assert(status, check.Equals, http.StatusCreated)
c.Assert(err, check.IsNil)
if !isServiceAvailable(c, sname, name) {
c.Fatalf("Service %s.%s not found", sname, name)
}
var id string
err = json.Unmarshal(resp, &id)
if err != nil {
c.Fatal(err)
}
status, _, err = sockRequest("DELETE", fmt.Sprintf("/services/%s", id), nil)
c.Assert(status, check.Equals, http.StatusOK)
c.Assert(err, check.IsNil)
if isServiceAvailable(c, sname, name) {
c.Fatalf("Service %s.%s not deleted", sname, name)
}
status, _, err = sockRequest("DELETE", fmt.Sprintf("/networks/%s", nid), nil)
c.Assert(status, check.Equals, http.StatusOK)
c.Assert(err, check.IsNil)
if isNetworkAvailable(c, name) {
c.Fatalf("Network %s not deleted", name)
}
}

Просмотреть файл

@ -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)
}

Просмотреть файл

@ -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) {

Просмотреть файл

@ -1,10 +1,11 @@
// +build experimental
package main
import (
"encoding/json"
"net"
"strings"
"github.com/docker/docker/api/types"
"github.com/go-check/check"
)
@ -31,6 +32,14 @@ func isNwPresent(c *check.C, name string) bool {
return false
}
func getNwResource(c *check.C, name string) *types.NetworkResource {
out, _ := dockerCmd(c, "network", "inspect", name)
nr := types.NetworkResource{}
err := json.Unmarshal([]byte(out), &nr)
c.Assert(err, check.IsNil)
return &nr
}
func (s *DockerSuite) TestDockerNetworkLsDefault(c *check.C) {
defaults := []string{"bridge", "host", "none"}
for _, nn := range defaults {
@ -45,3 +54,45 @@ func (s *DockerSuite) TestDockerNetworkCreateDelete(c *check.C) {
dockerCmd(c, "network", "rm", "test")
assertNwNotAvailable(c, "test")
}
func (s *DockerSuite) TestDockerNetworkConnectDisconnect(c *check.C) {
dockerCmd(c, "network", "create", "test")
assertNwIsAvailable(c, "test")
nr := getNwResource(c, "test")
c.Assert(nr.Name, check.Equals, "test")
c.Assert(len(nr.Containers), check.Equals, 0)
// run a container
out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
c.Assert(waitRun("test"), check.IsNil)
containerID := strings.TrimSpace(out)
// connect the container to the test network
dockerCmd(c, "network", "connect", "test", 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
dockerCmd(c, "network", "disconnect", "test", containerID)
nr = getNwResource(c, "test")
c.Assert(nr.Name, check.Equals, "test")
c.Assert(len(nr.Containers), check.Equals, 0)
// check if network connect fails for inactive containers
dockerCmd(c, "stop", containerID)
_, _, err = dockerCmdWithError("network", "connect", "test", containerID)
c.Assert(err, check.NotNil)
dockerCmd(c, "network", "rm", "test")
assertNwNotAvailable(c, "test")
}

Просмотреть файл

@ -18,7 +18,9 @@ import (
"sync"
"time"
"github.com/docker/docker/pkg/integration/checker"
"github.com/docker/docker/pkg/nat"
"github.com/docker/docker/runconfig"
"github.com/docker/libnetwork/resolvconf"
"github.com/go-check/check"
)
@ -3168,7 +3170,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 +3406,193 @@ 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")
c.Assert(waitRun("first"), check.IsNil)
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")
c.Assert(waitRun("first"), check.IsNil)
dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// 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 container in testnetwork1 and another in testnetwork2
dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
c.Assert(waitRun("first"), check.IsNil)
dockerCmd(c, "run", "-d", "--net=testnetwork2", "--name=second", "busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// 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")
c.Assert(waitRun("first"), check.IsNil)
dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// 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")
c.Assert(waitRun("first"), check.IsNil)
dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// 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")
c.Assert(waitRun("first"), check.IsNil)
// 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")
c.Assert(waitRun("first"), check.IsNil)
// Run second container in first container's network namespace
dockerCmd(c, "run", "-d", "--net=container:first", "--name=second", "busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// Create a network using bridge driver
dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
// Connecting to the user defined network must fail
out, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "second")
c.Assert(err, check.NotNil)
c.Assert(out, checker.Contains, runconfig.ErrConflictSharedNetwork.Error())
dockerCmd(c, "stop", "first")
dockerCmd(c, "stop", "second")
dockerCmd(c, "network", "rm", "testnetwork1")
}
func (s *DockerSuite) TestContainerWithConflictingNoneNetwork(c *check.C) {
testRequires(c, DaemonIsLinux)
dockerCmd(c, "run", "-d", "--net=none", "--name=first", "busybox", "top")
c.Assert(waitRun("first"), check.IsNil)
// Create a network using bridge driver
dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
// Connecting to the user defined network must fail
out, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "first")
c.Assert(err, check.NotNil)
c.Assert(out, checker.Contains, runconfig.ErrConflictNoNetwork.Error())
// create a container connected to testnetwork1
dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
c.Assert(waitRun("second"), check.IsNil)
// Connect second container to none network. it must fail as well
_, _, err = dockerCmdWithError("network", "connect", "none", "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")

Просмотреть файл

@ -1,69 +0,0 @@
// +build experimental
package main
import (
"fmt"
"strings"
"github.com/go-check/check"
)
func assertSrvIsAvailable(c *check.C, sname, name string) {
if !isSrvPresent(c, sname, name) {
c.Fatalf("Service %s on network %s not found in service ls o/p", sname, name)
}
}
func assertSrvNotAvailable(c *check.C, sname, name string) {
if isSrvPresent(c, sname, name) {
c.Fatalf("Found service %s on network %s in service ls o/p", sname, name)
}
}
func isSrvPresent(c *check.C, sname, name string) bool {
out, _, _ := dockerCmdWithStdoutStderr(c, "service", "ls")
lines := strings.Split(out, "\n")
for i := 1; i < len(lines)-1; i++ {
if strings.Contains(lines[i], sname) && strings.Contains(lines[i], name) {
return true
}
}
return false
}
func isCntPresent(c *check.C, cname, sname, name string) bool {
out, _, _ := dockerCmdWithStdoutStderr(c, "service", "ls", "--no-trunc")
lines := strings.Split(out, "\n")
for i := 1; i < len(lines)-1; i++ {
fmt.Println(lines)
if strings.Contains(lines[i], name) && strings.Contains(lines[i], sname) && strings.Contains(lines[i], cname) {
return true
}
}
return false
}
func (s *DockerSuite) TestDockerServiceCreateDelete(c *check.C) {
dockerCmdWithStdoutStderr(c, "network", "create", "test")
assertNwIsAvailable(c, "test")
dockerCmdWithStdoutStderr(c, "service", "publish", "s1.test")
assertSrvIsAvailable(c, "s1", "test")
dockerCmdWithStdoutStderr(c, "service", "unpublish", "s1.test")
assertSrvNotAvailable(c, "s1", "test")
dockerCmdWithStdoutStderr(c, "network", "rm", "test")
assertNwNotAvailable(c, "test")
}
func (s *DockerSuite) TestDockerPublishServiceFlag(c *check.C) {
// Run saying the container is the backend for the specified service on the specified network
out, _ := dockerCmd(c, "run", "-d", "--expose=23", "--publish-service", "telnet.production", "busybox", "top")
cid := strings.TrimSpace(out)
// Verify container is attached in service ps o/p
assertSrvIsAvailable(c, "telnet", "production")
dockerCmd(c, "rm", "-f", cid)
}

Просмотреть файл

@ -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.

Просмотреть файл

@ -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",

Просмотреть файл

@ -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 {

Просмотреть файл

@ -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=<NETWORK> and links
ErrConflictUserDefinedNetworkAndLinks = fmt.Errorf("Conflicting options: --net=<NETWORK> 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.Uint16([]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

Просмотреть файл

@ -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)
}

Просмотреть файл

@ -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) {
}

Просмотреть файл

@ -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:<name|id>")
}
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
}

Просмотреть файл

@ -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)
}

Просмотреть файл

@ -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"`
}

Просмотреть файл

@ -1,115 +0,0 @@
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
}

Просмотреть файл

@ -1,231 +0,0 @@
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
}

Просмотреть файл

@ -1,392 +0,0 @@
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")
}
func lookupSandboxID(cli *NetworkCli, containerID string) (string, error) {
obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/sandboxes?partial-container-id=%s", containerID), nil, nil))
if err != nil {
return "", err
}
var sandboxList []SandboxResource
err = json.Unmarshal(obj, &sandboxList)
if err != nil {
return "", err
}
if len(sandboxList) == 0 {
return "", fmt.Errorf("cannot find sandbox for container: %s", containerID)
}
return sandboxList[0].ID, nil
}
// CmdService handles the service UI
func (cli *NetworkCli) CmdService(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "service", "COMMAND [OPTIONS] [arg...]", serviceUsage(chain), false)
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\tSANDBOX")
}
for _, sr := range serviceResources {
ID := sr.ID
bkID, sbID, err := getBackendID(cli, ID)
if err != nil {
return err
}
if !*noTrunc {
ID = stringid.TruncateID(ID)
bkID = stringid.TruncateID(bkID)
sbID = stringid.TruncateID(sbID)
}
if !*quiet {
fmt.Fprintf(wr, "%s\t%s\t%s\t%s\t%s\n", ID, sr.Name, sr.Network, bkID, sbID)
} else {
fmt.Fprintln(wr, ID)
}
}
wr.Flush()
return nil
}
func getBackendID(cli *NetworkCli, servID string) (string, string, error) {
var (
obj []byte
err error
bk string
sb string
)
if obj, _, err = readBody(cli.call("GET", "/services/"+servID+"/backend", nil, nil)); err == nil {
var sr SandboxResource
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&sr); err == nil {
bk = sr.ContainerID
sb = sr.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)\n", servID, err)
}
}
return bk, sb, 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
}
sandboxID, err := lookupSandboxID(cli, containerID)
if err != nil {
return err
}
sn, nn := parseServiceName(cmd.Arg(1))
serviceID, err := lookupServiceID(cli, nn, sn)
if err != nil {
return err
}
nc := serviceAttach{SandboxID: sandboxID}
_, _, 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
}
sandboxID, err := lookupSandboxID(cli, containerID)
if err != nil {
return err
}
serviceID, err := lookupServiceID(cli, nn, sn)
if err != nil {
return err
}
_, _, err = readBody(cli.call("DELETE", "/services/"+serviceID+"/backend/"+sandboxID, 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
}

Просмотреть файл

@ -1,79 +0,0 @@
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"`
}
// 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"`
}
/***********
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 sandbox to/from service" http request messages
type serviceAttach struct {
SandboxID string `json:"sandbox_id"`
}
// SandboxCreate is the body of the "post /sandboxes" 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"`
}
// extraHost represents the extra host object
type extraHost struct {
Name string `json:"name"`
Address string `json:"address"`
}
// sandboxParentUpdate is the object carrying the information about the
// sanbox parent that needs to be updated
type sandboxParentUpdate struct {
ContainerID string `json:"container_id"`
Name string `json:"name"`
Address string `json:"address"`
}