Skip to content

Commit

Permalink
Add boilerplate for docker net {create, list, rm, plug, unplug}
Browse files Browse the repository at this point in the history
- New interfaces are passed to libcontainer on container start.
- docker net create accepts labels, which are passed to the network driver
- docker {run, create} accept --network to connect to said network
  • Loading branch information
Tom Wilkie committed Apr 14, 2015
1 parent 00eddf5 commit 2018905
Show file tree
Hide file tree
Showing 12 changed files with 745 additions and 23 deletions.
146 changes: 146 additions & 0 deletions api/client/network.go
@@ -0,0 +1,146 @@
package client

import (
"encoding/json"
"fmt"
"text/tabwriter"

"github.com/docker/docker/api/types"
"github.com/docker/docker/opts"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/runconfig"
)

// CmdNetCreate creates a new network.
//
// Usage: docker net create [OPTIONS] CONTAINER
func (cli *DockerCli) CmdNetCreate(args ...string) error {
var (
cmd = cli.Subcmd("net create", "", "Create a new network", true)
driver = cmd.String([]string{"-driver"}, "", "Use driver for network")
name = cmd.String([]string{"-name"}, "", "Assign a name to the network")
labels = opts.NewListOpts(opts.ValidateEnv)
)
cmd.Var(&labels, []string{"l", "-label"}, "Set meta data on a container")
cmd.Require(flag.Exact, 0)
cmd.ParseFlags(args, true)

values := make(map[string]interface{})
values["Labels"] = runconfig.ConvertKVStringsToMap(labels.GetAll())
if *driver != "" {
values["Driver"] = *driver
}

stream, _, err := cli.call("POST", "/networks/"+*name, values, nil)
if err != nil {
return err
}

var response types.NetworkResponse
if err := json.NewDecoder(stream).Decode(&response); err != nil {
return err
}

fmt.Fprintf(cli.out, "%s\n", response.ID)
return nil
}

// CmdNetList outputs a list of networks
//
// Usage: docker net list [OPTIONS]
func (cli *DockerCli) CmdNetList(args ...string) error {
var (
err error
cmd = cli.Subcmd("net list", "", "List networks", true)
)
cmd.Require(flag.Exact, 0)
cmd.ParseFlags(args, true)

rdr, _, err := cli.call("GET", "/networks/json", nil, nil)
if err != nil {
return err
}

networks := []types.NetworkResponse{}
if err := json.NewDecoder(rdr).Decode(&networks); err != nil {
return err
}

w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
fmt.Fprint(w, "NETWORK ID\tNAME\tDRIVER\tLABELS\n")

for _, net := range networks {
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", net.ID, net.Name, net.Driver, net.Labels)
}

w.Flush()
return nil
}

// CmdNetRm removes network with given name
//
// Usage: docker net rm [OPTIONS] name [IMAGE...]
func (cli *DockerCli) CmdNetRm(args ...string) error {
var (
cmd = cli.Subcmd("net rm", "NAME", "Remove one or more images", true)
)
cmd.Require(flag.Min, 1)
cmd.ParseFlags(args, true)

name := cmd.Arg(0)
_, _, err := cli.call("DELETE", "/networks/"+name, nil, nil)
if err != nil {
return err
}

return nil
}

// CmdNetPlug creates a new endpoint for container on network
//
// Usage: docker net plug [OPTIONS] container network
func (cli *DockerCli) CmdNetPlug(args ...string) error {
var (
cmd = cli.Subcmd("net plug", "CONTAINER NETWORK", "Attach a container to a network", true)
labels = opts.NewListOpts(opts.ValidateEnv)
)
cmd.Var(&labels, []string{"l", "-label"}, "Set meta data on a container")
cmd.Require(flag.Min, 2)
cmd.ParseFlags(args, true)

container := cmd.Arg(0)
network := cmd.Arg(1)
values := make(map[string]interface{})
values["Labels"] = runconfig.ConvertKVStringsToMap(labels.GetAll())

stream, _, err := cli.call("POST", fmt.Sprintf("/container/%s/plug/%s", container, network), values, nil)
if err != nil {
return err
}

var response types.NetworkPlugResponse
if err := json.NewDecoder(stream).Decode(&response); err != nil {
return err
}
fmt.Fprintf(cli.out, "%s\n", response.ID)
return nil
}

// CmdNetUnplug destries said endpoint
//
// Usage: docker net attach [OPTIONS] container network
func (cli *DockerCli) CmdNetUnplug(args ...string) error {
cmd := cli.Subcmd("net unplug", "CONTAINER ENDPOINT", "Detach endpoint on container", true)
cmd.Require(flag.Min, 2)
cmd.ParseFlags(args, true)

container := cmd.Arg(0)
endpoint := cmd.Arg(1)

_, _, err := cli.call("POST", fmt.Sprintf("/container/%s/unplug/%s", container, endpoint), nil, nil)
if err != nil {
return err
}

return nil
}
112 changes: 112 additions & 0 deletions api/server/server.go
Expand Up @@ -578,6 +578,16 @@ func getContainersLogs(eng *engine.Engine, version version.Version, w http.Respo
return nil
}

func getNetworksJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}

d := getDaemon(eng)
networks := d.NetworkList()
return writeJSON(w, http.StatusOK, networks)
}

func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
Expand Down Expand Up @@ -924,6 +934,14 @@ func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWr
return writeJSON(w, http.StatusOK, list)
}

func deleteNetworks(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}
d := getDaemon(eng)
return d.NetworkDestroy(vars["name"])
}

func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
Expand Down Expand Up @@ -1088,6 +1106,94 @@ func postContainersAttach(eng *engine.Engine, version version.Version, w http.Re
return nil
}

func getString(name string, body map[string]interface{}) (string, error) {
inf, found := body[name]
if !found {
return "", fmt.Errorf("Must specify %s", name)
}

value, ok := inf.(string)
if !ok {
return "", fmt.Errorf("%s must be a string", name)
}

return value, nil
}

func getLabels(body map[string]interface{}) (map[string]string, error) {
inf, found := body["Labels"]
if !found {
return nil, fmt.Errorf("Must specify Labels")
}

value, ok := inf.(map[string]string)
if !ok {
return nil, fmt.Errorf("Labels must be a map[string]string")
}

return value, nil
}

func postNetworkCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}
if err := checkForJson(r); err != nil {
return err
}

body := make(map[string]interface{})
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&body); err != nil {
return err
}

driver, err := getString("Driver", body)
if err != nil {
return err
}

labels, err := getLabels(body)
if err != nil {
return err
}

d := getDaemon(eng)
id, err := d.NetworkCreate(vars["name"], driver, labels)
if err != nil {
return err
}

return writeJSON(w, http.StatusCreated, &types.NetworkResponse{
ID: id,
})
}

func postNetworkPlug(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}

d := getDaemon(eng)
id, err := d.NetworkPlug(vars["name"], vars["network"])
if err != nil {
return err
}

return writeJSON(w, http.StatusCreated, &types.NetworkPlugResponse{
ID: id,
})
}

func postNetworkUnplug(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}

d := getDaemon(eng)
return d.NetworkUnplug(vars["name"], vars["endpoint"])
}

func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
Expand Down Expand Up @@ -1483,6 +1589,7 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, corsHeaders stri
"/containers/{name:.*}/stats": getContainersStats,
"/containers/{name:.*}/attach/ws": wsContainersAttach,
"/exec/{id:.*}/json": getExecByID,
"/networks/json": getNetworksJSON,
},
"POST": {
"/auth": postAuth,
Expand All @@ -1507,10 +1614,15 @@ func createRouter(eng *engine.Engine, logging, enableCors bool, corsHeaders stri
"/exec/{name:.*}/start": postContainerExecStart,
"/exec/{name:.*}/resize": postContainerExecResize,
"/containers/{name:.*}/rename": postContainerRename,

"/container/{name:.*}/plug/{network:.*}": postNetworkPlug,
"/container/{name:.*}/unplug/{endpoint:.*}": postNetworkUnplug,
"/networks/{name:.*}": postNetworkCreate,
},
"DELETE": {
"/containers/{name:.*}": deleteContainers,
"/images/{name:.*}": deleteImages,
"/networks/{name:.*}": deleteNetworks,
},
"OPTIONS": {
"": optionsHandler,
Expand Down
17 changes: 17 additions & 0 deletions api/types/types.go
Expand Up @@ -10,6 +10,23 @@ type ContainerCreateResponse struct {
Warnings []string `json:"Warnings"`
}

// NetworkResponse contains the information returned to a client on the
// creation of a new network.
type NetworkResponse struct {
// ID is the ID of the created container.
ID string `json:"Id"`
Name string
Driver string
Labels map[string]string
}

// NetworkCreateResponse contains the information returned to a client on the
// creation of a new network.
type NetworkPlugResponse struct {
// ID is the ID of the created container.
ID string `json:"Id"`
}

// POST /containers/{name:.*}/exec
type ContainerExecCreateResponse struct {
// ID is the exec ID.
Expand Down
26 changes: 26 additions & 0 deletions daemon/container.go
Expand Up @@ -110,6 +110,8 @@ type Container struct {
logDriver logger.Logger
logCopier *logger.Copier
AppliedVolumesFrom map[string]struct{}

Endpoints []*Endpoint
}

func (container *Container) FromDisk() error {
Expand Down Expand Up @@ -294,6 +296,15 @@ func populateCommand(c *Container, env []string) error {
return fmt.Errorf("invalid network mode: %s", c.hostConfig.NetworkMode)
}

// Plug enpoints for new network model
for _, endpoint := range c.Endpoints {
inf, err := endpoint.Plug(c.daemon)
if err != nil {
return err
}
en.Interfaces = append(en.Interfaces, inf)
}

ipc := &execdriver.Ipc{}

if c.hostConfig.IpcMode.IsContainer() {
Expand Down Expand Up @@ -690,6 +701,12 @@ func (container *Container) cleanup() {
logrus.Errorf("%v: Failed to umount filesystem: %v", container.ID, err)
}

for _, endpoint := range container.Endpoints {
if err := endpoint.Unplug(container.daemon); err != nil {
logrus.Errorf("%v: Failed to unplug enpoint %s - %s", container.ID, endpoint.ID, err)
}
}

for _, eConfig := range container.execCommands.s {
container.daemon.unregisterExecCommand(eConfig)
}
Expand Down Expand Up @@ -1534,3 +1551,12 @@ func (c *Container) LogDriverType() string {
}
return c.hostConfig.LogConfig.Type
}

func (c *Container) GetEndpoint(id string) (int, *Endpoint) {
for i, e := range c.Endpoints {
if e.ID == id {
return i, e
}
}
return -1, nil
}

0 comments on commit 2018905

Please sign in to comment.