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