mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	Network UX and integration tests
* Exiting experimental network UX * removed experimental service UX * integrated with the new network remote API Signed-off-by: Madhu Venugopal <madhu@docker.com>
This commit is contained in:
		
							parent
							
								
									2ab94e11a2
								
							
						
					
					
						commit
						22a9ba090e
					
				
					 10 changed files with 296 additions and 1027 deletions
				
			
		| 
						 | 
				
			
			@ -1,14 +1,207 @@
 | 
			
		|||
// +build experimental
 | 
			
		||||
 | 
			
		||||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	nwclient "github.com/docker/libnetwork/client"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/api/types"
 | 
			
		||||
	Cli "github.com/docker/docker/cli"
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/pkg/stringid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CmdNetwork is used to create, display and configure network endpoints.
 | 
			
		||||
// CmdNetwork is the parent subcommand for all network commands
 | 
			
		||||
//
 | 
			
		||||
// Usage: docker network <COMMAND> [OPTIONS]
 | 
			
		||||
func (cli *DockerCli) CmdNetwork(args ...string) error {
 | 
			
		||||
	nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper))
 | 
			
		||||
	args = append([]string{"network"}, args...)
 | 
			
		||||
	return nCli.Cmd("docker", args...)
 | 
			
		||||
	cmd := Cli.Subcmd("network", []string{"COMMAND [OPTIONS]"}, networkUsage(), false)
 | 
			
		||||
	cmd.Require(flag.Min, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	cmd.Usage()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdNetworkCreate creates a new network with a given name
 | 
			
		||||
//
 | 
			
		||||
// Usage: docker network create [OPTIONS] <NETWORK-NAME>
 | 
			
		||||
func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
 | 
			
		||||
	cmd := Cli.Subcmd("network create", []string{"NETWORK-NAME"}, "Creates a new network with a name specified by the user", false)
 | 
			
		||||
	flDriver := cmd.String([]string{"d", "-driver"}, "", "Driver to manage the Network")
 | 
			
		||||
	cmd.Require(flag.Exact, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Construct network create request body
 | 
			
		||||
	nc := types.NetworkCreate{Name: cmd.Arg(0), Driver: *flDriver, CheckDuplicate: true}
 | 
			
		||||
	obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	var resp types.NetworkCreateResponse
 | 
			
		||||
	err = json.Unmarshal(obj, &resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Fprintf(cli.out, "%s\n", resp.ID)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdNetworkRm deletes a network
 | 
			
		||||
//
 | 
			
		||||
// Usage: docker network rm <NETWORK-NAME | NETWORK-ID>
 | 
			
		||||
func (cli *DockerCli) CmdNetworkRm(args ...string) error {
 | 
			
		||||
	cmd := Cli.Subcmd("network rm", []string{"NETWORK"}, "Deletes a network", false)
 | 
			
		||||
	cmd.Require(flag.Exact, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = readBody(cli.call("DELETE", "/networks/"+cmd.Arg(0), nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdNetworkConnect connects a container to a network
 | 
			
		||||
//
 | 
			
		||||
// Usage: docker network connect <NETWORK> <CONTAINER>
 | 
			
		||||
func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
 | 
			
		||||
	cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
 | 
			
		||||
	cmd.Require(flag.Exact, 2)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nc := types.NetworkConnect{Container: cmd.Arg(1)}
 | 
			
		||||
	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/connect", nc, nil))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdNetworkDisconnect disconnects a container from a network
 | 
			
		||||
//
 | 
			
		||||
// Usage: docker network disconnect <NETWORK> <CONTAINER>
 | 
			
		||||
func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
 | 
			
		||||
	cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
 | 
			
		||||
	cmd.Require(flag.Exact, 2)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nc := types.NetworkConnect{Container: cmd.Arg(1)}
 | 
			
		||||
	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/disconnect", nc, nil))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdNetworkLs lists all the netorks managed by docker daemon
 | 
			
		||||
//
 | 
			
		||||
// Usage: docker network ls [OPTIONS]
 | 
			
		||||
func (cli *DockerCli) CmdNetworkLs(args ...string) error {
 | 
			
		||||
	cmd := Cli.Subcmd("network ls", []string{""}, "Lists all the networks created by the user", false)
 | 
			
		||||
	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
 | 
			
		||||
	noTrunc := cmd.Bool([]string{"", "-no-trunc"}, false, "Do not truncate the output")
 | 
			
		||||
	nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created")
 | 
			
		||||
	last := cmd.Int([]string{"n"}, -1, "Show n last created networks")
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if *last == -1 && *nLatest {
 | 
			
		||||
		*last = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var networkResources []types.NetworkResource
 | 
			
		||||
	err = json.Unmarshal(obj, &networkResources)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 | 
			
		||||
 | 
			
		||||
	// unless quiet (-q) is specified, print field titles
 | 
			
		||||
	if !*quiet {
 | 
			
		||||
		fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, networkResource := range networkResources {
 | 
			
		||||
		ID := networkResource.ID
 | 
			
		||||
		netName := networkResource.Name
 | 
			
		||||
		if !*noTrunc {
 | 
			
		||||
			ID = stringid.TruncateID(ID)
 | 
			
		||||
		}
 | 
			
		||||
		if *quiet {
 | 
			
		||||
			fmt.Fprintln(wr, ID)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		driver := networkResource.Driver
 | 
			
		||||
		fmt.Fprintf(wr, "%s\t%s\t%s\t",
 | 
			
		||||
			ID,
 | 
			
		||||
			netName,
 | 
			
		||||
			driver)
 | 
			
		||||
		fmt.Fprint(wr, "\n")
 | 
			
		||||
	}
 | 
			
		||||
	wr.Flush()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdNetworkInspect inspects the network object for more details
 | 
			
		||||
//
 | 
			
		||||
// Usage: docker network inspect <NETWORK>
 | 
			
		||||
// CmdNetworkInspect handles Network inspect UI
 | 
			
		||||
func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
 | 
			
		||||
	cmd := Cli.Subcmd("network inspect", []string{"NETWORK"}, "Displays detailed information on a network", false)
 | 
			
		||||
	cmd.Require(flag.Exact, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	obj, _, err := readBody(cli.call("GET", "/networks/"+cmd.Arg(0), nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	networkResource := &types.NetworkResource{}
 | 
			
		||||
	if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	indented := new(bytes.Buffer)
 | 
			
		||||
	if err := json.Indent(indented, obj, "", "    "); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := io.Copy(cli.out, indented); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func networkUsage() string {
 | 
			
		||||
	networkCommands := map[string]string{
 | 
			
		||||
		"create":     "Create a network",
 | 
			
		||||
		"connect":    "Connect container to a network",
 | 
			
		||||
		"disconnect": "Disconnect container from a network",
 | 
			
		||||
		"inspect":    "Display detailed network information",
 | 
			
		||||
		"ls":         "List all networks",
 | 
			
		||||
		"rm":         "Remove a network",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	help := "Commands:\n"
 | 
			
		||||
 | 
			
		||||
	for cmd, description := range networkCommands {
 | 
			
		||||
		help += fmt.Sprintf("  %-25.25s%s\n", cmd, description)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.")
 | 
			
		||||
	return help
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +0,0 @@
 | 
			
		|||
// +build experimental
 | 
			
		||||
 | 
			
		||||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	nwclient "github.com/docker/libnetwork/client"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CmdService is used to manage network services.
 | 
			
		||||
// service command is user to publish, attach and list a service from a container.
 | 
			
		||||
func (cli *DockerCli) CmdService(args ...string) error {
 | 
			
		||||
	nCli := nwclient.NewNetworkCli(cli.out, cli.err, nwclient.CallFunc(cli.callWrapper))
 | 
			
		||||
	args = append([]string{"service"}, args...)
 | 
			
		||||
	return nCli.Cmd(os.Args[0], args...)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,113 +0,0 @@
 | 
			
		|||
// +build experimental
 | 
			
		||||
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-check/check"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func isServiceAvailable(c *check.C, name string, network string) bool {
 | 
			
		||||
	status, body, err := sockRequest("GET", "/services", nil)
 | 
			
		||||
	c.Assert(status, check.Equals, http.StatusOK)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	var inspectJSON []struct {
 | 
			
		||||
		Name    string
 | 
			
		||||
		ID      string
 | 
			
		||||
		Network string
 | 
			
		||||
	}
 | 
			
		||||
	if err = json.Unmarshal(body, &inspectJSON); err != nil {
 | 
			
		||||
		c.Fatalf("unable to unmarshal response body: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, s := range inspectJSON {
 | 
			
		||||
		if s.Name == name && s.Network == network {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isServiceNetworkAvailable(c *check.C, name string) bool {
 | 
			
		||||
	status, body, err := sockRequest("GET", "/networks", nil)
 | 
			
		||||
	c.Assert(status, check.Equals, http.StatusOK)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	var inspectJSON []struct {
 | 
			
		||||
		Name string
 | 
			
		||||
		ID   string
 | 
			
		||||
		Type string
 | 
			
		||||
	}
 | 
			
		||||
	if err = json.Unmarshal(body, &inspectJSON); err != nil {
 | 
			
		||||
		c.Fatalf("unable to unmarshal response body: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	for _, n := range inspectJSON {
 | 
			
		||||
		if n.Name == name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DockerSuite) TestServiceApiCreateDelete(c *check.C) {
 | 
			
		||||
	name := "testnetwork"
 | 
			
		||||
	config := map[string]interface{}{
 | 
			
		||||
		"name":         name,
 | 
			
		||||
		"network_type": "bridge",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	status, resp, err := sockRequest("POST", "/networks", config)
 | 
			
		||||
	c.Assert(status, check.Equals, http.StatusCreated)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	if !isServiceNetworkAvailable(c, name) {
 | 
			
		||||
		c.Fatalf("Network %s not found", name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var nid string
 | 
			
		||||
	err = json.Unmarshal(resp, &nid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sname := "service1"
 | 
			
		||||
	sconfig := map[string]interface{}{
 | 
			
		||||
		"name":         sname,
 | 
			
		||||
		"network_name": name,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	status, resp, err = sockRequest("POST", "/services", sconfig)
 | 
			
		||||
	c.Assert(status, check.Equals, http.StatusCreated)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	if !isServiceAvailable(c, sname, name) {
 | 
			
		||||
		c.Fatalf("Service %s.%s not found", sname, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var id string
 | 
			
		||||
	err = json.Unmarshal(resp, &id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	status, _, err = sockRequest("DELETE", fmt.Sprintf("/services/%s", id), nil)
 | 
			
		||||
	c.Assert(status, check.Equals, http.StatusOK)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	if isServiceAvailable(c, sname, name) {
 | 
			
		||||
		c.Fatalf("Service %s.%s not deleted", sname, name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	status, _, err = sockRequest("DELETE", fmt.Sprintf("/networks/%s", nid), nil)
 | 
			
		||||
	c.Assert(status, check.Equals, http.StatusOK)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
 | 
			
		||||
	if isNetworkAvailable(c, name) {
 | 
			
		||||
		c.Fatalf("Network %s not deleted", name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,10 +1,11 @@
 | 
			
		|||
// +build experimental
 | 
			
		||||
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/api/types"
 | 
			
		||||
	"github.com/go-check/check"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +32,14 @@ func isNwPresent(c *check.C, name string) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getNwResource(c *check.C, name string) *types.NetworkResource {
 | 
			
		||||
	out, _ := dockerCmd(c, "network", "inspect", name)
 | 
			
		||||
	nr := types.NetworkResource{}
 | 
			
		||||
	err := json.Unmarshal([]byte(out), &nr)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
	return &nr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DockerSuite) TestDockerNetworkLsDefault(c *check.C) {
 | 
			
		||||
	defaults := []string{"bridge", "host", "none"}
 | 
			
		||||
	for _, nn := range defaults {
 | 
			
		||||
| 
						 | 
				
			
			@ -45,3 +54,45 @@ func (s *DockerSuite) TestDockerNetworkCreateDelete(c *check.C) {
 | 
			
		|||
	dockerCmd(c, "network", "rm", "test")
 | 
			
		||||
	assertNwNotAvailable(c, "test")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DockerSuite) TestDockerNetworkConnectDisconnect(c *check.C) {
 | 
			
		||||
	dockerCmd(c, "network", "create", "test")
 | 
			
		||||
	assertNwIsAvailable(c, "test")
 | 
			
		||||
	nr := getNwResource(c, "test")
 | 
			
		||||
 | 
			
		||||
	c.Assert(nr.Name, check.Equals, "test")
 | 
			
		||||
	c.Assert(len(nr.Containers), check.Equals, 0)
 | 
			
		||||
 | 
			
		||||
	// run a container
 | 
			
		||||
	out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("test"), check.IsNil)
 | 
			
		||||
	containerID := strings.TrimSpace(out)
 | 
			
		||||
 | 
			
		||||
	// connect the container to the test network
 | 
			
		||||
	dockerCmd(c, "network", "connect", "test", containerID)
 | 
			
		||||
 | 
			
		||||
	// inspect the network to make sure container is connected
 | 
			
		||||
	nr = getNetworkResource(c, nr.ID)
 | 
			
		||||
	c.Assert(len(nr.Containers), check.Equals, 1)
 | 
			
		||||
	c.Assert(nr.Containers[containerID], check.NotNil)
 | 
			
		||||
 | 
			
		||||
	// check if container IP matches network inspect
 | 
			
		||||
	ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address)
 | 
			
		||||
	c.Assert(err, check.IsNil)
 | 
			
		||||
	containerIP := findContainerIP(c, "test")
 | 
			
		||||
	c.Assert(ip.String(), check.Equals, containerIP)
 | 
			
		||||
 | 
			
		||||
	// disconnect container from the network
 | 
			
		||||
	dockerCmd(c, "network", "disconnect", "test", containerID)
 | 
			
		||||
	nr = getNwResource(c, "test")
 | 
			
		||||
	c.Assert(nr.Name, check.Equals, "test")
 | 
			
		||||
	c.Assert(len(nr.Containers), check.Equals, 0)
 | 
			
		||||
 | 
			
		||||
	// check if network connect fails for inactive containers
 | 
			
		||||
	dockerCmd(c, "stop", containerID)
 | 
			
		||||
	_, _, err = dockerCmdWithError("network", "connect", "test", containerID)
 | 
			
		||||
	c.Assert(err, check.NotNil)
 | 
			
		||||
 | 
			
		||||
	dockerCmd(c, "network", "rm", "test")
 | 
			
		||||
	assertNwNotAvailable(c, "test")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,9 @@ import (
 | 
			
		|||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/docker/docker/pkg/integration/checker"
 | 
			
		||||
	"github.com/docker/docker/pkg/nat"
 | 
			
		||||
	"github.com/docker/docker/runconfig"
 | 
			
		||||
	"github.com/docker/libnetwork/resolvconf"
 | 
			
		||||
	"github.com/go-check/check"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -3408,6 +3410,7 @@ func (s *DockerSuite) TestContainersInUserDefinedNetwork(c *check.C) {
 | 
			
		|||
	testRequires(c, DaemonIsLinux)
 | 
			
		||||
	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork")
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=testnetwork", "--name=first", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("first"), check.IsNil)
 | 
			
		||||
	dockerCmd(c, "run", "-t", "--net=testnetwork", "--name=second", "busybox", "ping", "-c", "1", "first")
 | 
			
		||||
	dockerCmd(c, "stop", "first")
 | 
			
		||||
	dockerCmd(c, "stop", "second")
 | 
			
		||||
| 
						 | 
				
			
			@ -3421,7 +3424,9 @@ func (s *DockerSuite) TestContainersInMultipleNetworks(c *check.C) {
 | 
			
		|||
	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
 | 
			
		||||
	// Run and connect containers to testnetwork1
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("first"), check.IsNil)
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("second"), check.IsNil)
 | 
			
		||||
	// Check connectivity between containers in testnetwork2
 | 
			
		||||
	dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1")
 | 
			
		||||
	// Connect containers to testnetwork2
 | 
			
		||||
| 
						 | 
				
			
			@ -3440,9 +3445,11 @@ func (s *DockerSuite) TestContainersNetworkIsolation(c *check.C) {
 | 
			
		|||
	// Create 2 networks using bridge driver
 | 
			
		||||
	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 | 
			
		||||
	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
 | 
			
		||||
	// Run 1 containers in testnetwork1 and another in testnetwork2
 | 
			
		||||
	// Run 1 container in testnetwork1 and another in testnetwork2
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("first"), check.IsNil)
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=testnetwork2", "--name=second", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("second"), check.IsNil)
 | 
			
		||||
 | 
			
		||||
	// Check Isolation between containers : ping must fail
 | 
			
		||||
	_, _, err := dockerCmdWithError("exec", "first", "ping", "-c", "1", "second")
 | 
			
		||||
| 
						 | 
				
			
			@ -3471,7 +3478,9 @@ func (s *DockerSuite) TestNetworkRmWithActiveContainers(c *check.C) {
 | 
			
		|||
	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 | 
			
		||||
	// Run and connect containers to testnetwork1
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("first"), check.IsNil)
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("second"), check.IsNil)
 | 
			
		||||
	// Network delete with active containers must fail
 | 
			
		||||
	_, _, err := dockerCmdWithError("network", "rm", "testnetwork1")
 | 
			
		||||
	c.Assert(err, check.NotNil)
 | 
			
		||||
| 
						 | 
				
			
			@ -3492,7 +3501,9 @@ func (s *DockerSuite) TestContainerRestartInMultipleNetworks(c *check.C) {
 | 
			
		|||
	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork2")
 | 
			
		||||
	// Run and connect containers to testnetwork1
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=first", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("first"), check.IsNil)
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("second"), check.IsNil)
 | 
			
		||||
	// Check connectivity between containers in testnetwork2
 | 
			
		||||
	dockerCmd(c, "exec", "first", "ping", "-c", "1", "second.testnetwork1")
 | 
			
		||||
	// Connect containers to testnetwork2
 | 
			
		||||
| 
						 | 
				
			
			@ -3523,6 +3534,7 @@ func (s *DockerSuite) TestContainerWithConflictingHostNetworks(c *check.C) {
 | 
			
		|||
	testRequires(c, DaemonIsLinux)
 | 
			
		||||
	// Run a container with --net=host
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=host", "--name=first", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("first"), check.IsNil)
 | 
			
		||||
 | 
			
		||||
	// Create a network using bridge driver
 | 
			
		||||
	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 | 
			
		||||
| 
						 | 
				
			
			@ -3537,14 +3549,43 @@ func (s *DockerSuite) TestContainerWithConflictingHostNetworks(c *check.C) {
 | 
			
		|||
func (s *DockerSuite) TestContainerWithConflictingSharedNetwork(c *check.C) {
 | 
			
		||||
	testRequires(c, DaemonIsLinux)
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--name=first", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("first"), check.IsNil)
 | 
			
		||||
	// Run second container in first container's network namespace
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=container:first", "--name=second", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("second"), check.IsNil)
 | 
			
		||||
 | 
			
		||||
	// Create a network using bridge driver
 | 
			
		||||
	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 | 
			
		||||
 | 
			
		||||
	// Connecting to the user defined network must fail
 | 
			
		||||
	_, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "second")
 | 
			
		||||
	out, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "second")
 | 
			
		||||
	c.Assert(err, check.NotNil)
 | 
			
		||||
	c.Assert(out, checker.Contains, runconfig.ErrConflictSharedNetwork.Error())
 | 
			
		||||
 | 
			
		||||
	dockerCmd(c, "stop", "first")
 | 
			
		||||
	dockerCmd(c, "stop", "second")
 | 
			
		||||
	dockerCmd(c, "network", "rm", "testnetwork1")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DockerSuite) TestContainerWithConflictingNoneNetwork(c *check.C) {
 | 
			
		||||
	testRequires(c, DaemonIsLinux)
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=none", "--name=first", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("first"), check.IsNil)
 | 
			
		||||
 | 
			
		||||
	// Create a network using bridge driver
 | 
			
		||||
	dockerCmd(c, "network", "create", "-d", "bridge", "testnetwork1")
 | 
			
		||||
 | 
			
		||||
	// Connecting to the user defined network must fail
 | 
			
		||||
	out, _, err := dockerCmdWithError("network", "connect", "testnetwork1", "first")
 | 
			
		||||
	c.Assert(err, check.NotNil)
 | 
			
		||||
	c.Assert(out, checker.Contains, runconfig.ErrConflictNoNetwork.Error())
 | 
			
		||||
 | 
			
		||||
	// create a container connected to testnetwork1
 | 
			
		||||
	dockerCmd(c, "run", "-d", "--net=testnetwork1", "--name=second", "busybox", "top")
 | 
			
		||||
	c.Assert(waitRun("second"), check.IsNil)
 | 
			
		||||
 | 
			
		||||
	// Connect second container to none network. it must fail as well
 | 
			
		||||
	_, _, err = dockerCmdWithError("network", "connect", "none", "second")
 | 
			
		||||
	c.Assert(err, check.NotNil)
 | 
			
		||||
 | 
			
		||||
	dockerCmd(c, "stop", "first")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,69 +0,0 @@
 | 
			
		|||
// +build experimental
 | 
			
		||||
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-check/check"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func assertSrvIsAvailable(c *check.C, sname, name string) {
 | 
			
		||||
	if !isSrvPresent(c, sname, name) {
 | 
			
		||||
		c.Fatalf("Service %s on network %s not found in service ls o/p", sname, name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func assertSrvNotAvailable(c *check.C, sname, name string) {
 | 
			
		||||
	if isSrvPresent(c, sname, name) {
 | 
			
		||||
		c.Fatalf("Found service %s on network %s in service ls o/p", sname, name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isSrvPresent(c *check.C, sname, name string) bool {
 | 
			
		||||
	out, _, _ := dockerCmdWithStdoutStderr(c, "service", "ls")
 | 
			
		||||
	lines := strings.Split(out, "\n")
 | 
			
		||||
	for i := 1; i < len(lines)-1; i++ {
 | 
			
		||||
		if strings.Contains(lines[i], sname) && strings.Contains(lines[i], name) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isCntPresent(c *check.C, cname, sname, name string) bool {
 | 
			
		||||
	out, _, _ := dockerCmdWithStdoutStderr(c, "service", "ls", "--no-trunc")
 | 
			
		||||
	lines := strings.Split(out, "\n")
 | 
			
		||||
	for i := 1; i < len(lines)-1; i++ {
 | 
			
		||||
		fmt.Println(lines)
 | 
			
		||||
		if strings.Contains(lines[i], name) && strings.Contains(lines[i], sname) && strings.Contains(lines[i], cname) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DockerSuite) TestDockerServiceCreateDelete(c *check.C) {
 | 
			
		||||
	dockerCmdWithStdoutStderr(c, "network", "create", "test")
 | 
			
		||||
	assertNwIsAvailable(c, "test")
 | 
			
		||||
 | 
			
		||||
	dockerCmdWithStdoutStderr(c, "service", "publish", "s1.test")
 | 
			
		||||
	assertSrvIsAvailable(c, "s1", "test")
 | 
			
		||||
 | 
			
		||||
	dockerCmdWithStdoutStderr(c, "service", "unpublish", "s1.test")
 | 
			
		||||
	assertSrvNotAvailable(c, "s1", "test")
 | 
			
		||||
 | 
			
		||||
	dockerCmdWithStdoutStderr(c, "network", "rm", "test")
 | 
			
		||||
	assertNwNotAvailable(c, "test")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DockerSuite) TestDockerPublishServiceFlag(c *check.C) {
 | 
			
		||||
	// Run saying the container is the backend for the specified service on the specified network
 | 
			
		||||
	out, _ := dockerCmd(c, "run", "-d", "--expose=23", "--publish-service", "telnet.production", "busybox", "top")
 | 
			
		||||
	cid := strings.TrimSpace(out)
 | 
			
		||||
 | 
			
		||||
	// Verify container is attached in service ps o/p
 | 
			
		||||
	assertSrvIsAvailable(c, "telnet", "production")
 | 
			
		||||
	dockerCmd(c, "rm", "-f", cid)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,115 +0,0 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CallFunc provides environment specific call utility to invoke backend functions from UI
 | 
			
		||||
type CallFunc func(string, string, interface{}, map[string][]string) (io.ReadCloser, http.Header, int, error)
 | 
			
		||||
 | 
			
		||||
// NetworkCli is the UI object for network subcmds
 | 
			
		||||
type NetworkCli struct {
 | 
			
		||||
	out  io.Writer
 | 
			
		||||
	err  io.Writer
 | 
			
		||||
	call CallFunc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewNetworkCli is a convenient function to create a NetworkCli object
 | 
			
		||||
func NewNetworkCli(out, err io.Writer, call CallFunc) *NetworkCli {
 | 
			
		||||
	return &NetworkCli{
 | 
			
		||||
		out:  out,
 | 
			
		||||
		err:  err,
 | 
			
		||||
		call: call,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getMethod is Borrowed from Docker UI which uses reflection to identify the UI Handler
 | 
			
		||||
func (cli *NetworkCli) getMethod(args ...string) (func(string, ...string) error, bool) {
 | 
			
		||||
	camelArgs := make([]string, len(args))
 | 
			
		||||
	for i, s := range args {
 | 
			
		||||
		if len(s) == 0 {
 | 
			
		||||
			return nil, false
 | 
			
		||||
		}
 | 
			
		||||
		camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
 | 
			
		||||
	}
 | 
			
		||||
	methodName := "Cmd" + strings.Join(camelArgs, "")
 | 
			
		||||
	method := reflect.ValueOf(cli).MethodByName(methodName)
 | 
			
		||||
	if !method.IsValid() {
 | 
			
		||||
		return nil, false
 | 
			
		||||
	}
 | 
			
		||||
	return method.Interface().(func(string, ...string) error), true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cmd is borrowed from Docker UI and acts as the entry point for network UI commands.
 | 
			
		||||
// network UI commands are designed to be invoked from multiple parent chains
 | 
			
		||||
func (cli *NetworkCli) Cmd(chain string, args ...string) error {
 | 
			
		||||
	if len(args) > 2 {
 | 
			
		||||
		method, exists := cli.getMethod(args[:3]...)
 | 
			
		||||
		if exists {
 | 
			
		||||
			return method(chain+" "+args[0]+" "+args[1], args[3:]...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(args) > 1 {
 | 
			
		||||
		method, exists := cli.getMethod(args[:2]...)
 | 
			
		||||
		if exists {
 | 
			
		||||
			return method(chain+" "+args[0], args[2:]...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(args) > 0 {
 | 
			
		||||
		method, exists := cli.getMethod(args[0])
 | 
			
		||||
		if !exists {
 | 
			
		||||
			return fmt.Errorf("%s: '%s' is not a %s command. See '%s --help'.\n", chain, args[0], chain, chain)
 | 
			
		||||
		}
 | 
			
		||||
		return method(chain, args[1:]...)
 | 
			
		||||
	}
 | 
			
		||||
	flag.Usage()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Subcmd is borrowed from Docker UI and performs the same function of configuring the subCmds
 | 
			
		||||
func (cli *NetworkCli) Subcmd(chain, name, signature, description string, exitOnError bool) *flag.FlagSet {
 | 
			
		||||
	var errorHandling flag.ErrorHandling
 | 
			
		||||
	if exitOnError {
 | 
			
		||||
		errorHandling = flag.ExitOnError
 | 
			
		||||
	} else {
 | 
			
		||||
		errorHandling = flag.ContinueOnError
 | 
			
		||||
	}
 | 
			
		||||
	flags := flag.NewFlagSet(name, errorHandling)
 | 
			
		||||
	flags.Usage = func() {
 | 
			
		||||
		flags.ShortUsage()
 | 
			
		||||
		flags.PrintDefaults()
 | 
			
		||||
	}
 | 
			
		||||
	flags.ShortUsage = func() {
 | 
			
		||||
		options := ""
 | 
			
		||||
		if signature != "" {
 | 
			
		||||
			signature = " " + signature
 | 
			
		||||
		}
 | 
			
		||||
		if flags.FlagCountUndeprecated() > 0 {
 | 
			
		||||
			options = " [OPTIONS]"
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintf(cli.out, "\nUsage: %s %s%s%s\n\n%s\n\n", chain, name, options, signature, description)
 | 
			
		||||
		flags.SetOutput(cli.out)
 | 
			
		||||
	}
 | 
			
		||||
	return flags
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readBody(stream io.ReadCloser, hdr http.Header, statusCode int, err error) ([]byte, int, error) {
 | 
			
		||||
	if stream != nil {
 | 
			
		||||
		defer stream.Close()
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, statusCode, err
 | 
			
		||||
	}
 | 
			
		||||
	body, err := ioutil.ReadAll(stream)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, -1, err
 | 
			
		||||
	}
 | 
			
		||||
	return body, statusCode, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,231 +0,0 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/pkg/stringid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type command struct {
 | 
			
		||||
	name        string
 | 
			
		||||
	description string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	networkCommands = []command{
 | 
			
		||||
		{"create", "Create a network"},
 | 
			
		||||
		{"rm", "Remove a network"},
 | 
			
		||||
		{"ls", "List all networks"},
 | 
			
		||||
		{"info", "Display information of a network"},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CmdNetwork handles the root Network UI
 | 
			
		||||
func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "network", "COMMAND [OPTIONS] [arg...]", networkUsage(chain), false)
 | 
			
		||||
	cmd.Require(flag.Min, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		cmd.Usage()
 | 
			
		||||
		return fmt.Errorf("invalid command : %v", args)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdNetworkCreate handles Network Create UI
 | 
			
		||||
func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "create", "NETWORK-NAME", "Creates a new network with a name specified by the user", false)
 | 
			
		||||
	flDriver := cmd.String([]string{"d", "-driver"}, "", "Driver to manage the Network")
 | 
			
		||||
	cmd.Require(flag.Exact, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Construct network create request body
 | 
			
		||||
	ops := make(map[string]interface{})
 | 
			
		||||
	nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, Options: ops}
 | 
			
		||||
	obj, _, err := readBody(cli.call("POST", "/networks", nc, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	var replyID string
 | 
			
		||||
	err = json.Unmarshal(obj, &replyID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Fprintf(cli.out, "%s\n", replyID)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdNetworkRm handles Network Delete UI
 | 
			
		||||
func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "rm", "NETWORK", "Deletes a network", false)
 | 
			
		||||
	cmd.Require(flag.Exact, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	id, err := lookupNetworkID(cli, cmd.Arg(0))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = readBody(cli.call("DELETE", "/networks/"+id, nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdNetworkLs handles Network List UI
 | 
			
		||||
func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "ls", "", "Lists all the networks created by the user", false)
 | 
			
		||||
	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
 | 
			
		||||
	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output")
 | 
			
		||||
	nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created")
 | 
			
		||||
	last := cmd.Int([]string{"n"}, -1, "Show n last created networks")
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if *last == -1 && *nLatest {
 | 
			
		||||
		*last = 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var networkResources []networkResource
 | 
			
		||||
	err = json.Unmarshal(obj, &networkResources)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 | 
			
		||||
 | 
			
		||||
	// unless quiet (-q) is specified, print field titles
 | 
			
		||||
	if !*quiet {
 | 
			
		||||
		fmt.Fprintln(wr, "NETWORK ID\tNAME\tTYPE")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, networkResource := range networkResources {
 | 
			
		||||
		ID := networkResource.ID
 | 
			
		||||
		netName := networkResource.Name
 | 
			
		||||
		if !*noTrunc {
 | 
			
		||||
			ID = stringid.TruncateID(ID)
 | 
			
		||||
		}
 | 
			
		||||
		if *quiet {
 | 
			
		||||
			fmt.Fprintln(wr, ID)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		netType := networkResource.Type
 | 
			
		||||
		fmt.Fprintf(wr, "%s\t%s\t%s\t",
 | 
			
		||||
			ID,
 | 
			
		||||
			netName,
 | 
			
		||||
			netType)
 | 
			
		||||
		fmt.Fprint(wr, "\n")
 | 
			
		||||
	}
 | 
			
		||||
	wr.Flush()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdNetworkInfo handles Network Info UI
 | 
			
		||||
func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "info", "NETWORK", "Displays detailed information on a network", false)
 | 
			
		||||
	cmd.Require(flag.Exact, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	id, err := lookupNetworkID(cli, cmd.Arg(0))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	obj, _, err := readBody(cli.call("GET", "/networks/"+id, nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	networkResource := &networkResource{}
 | 
			
		||||
	if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Fprintf(cli.out, "Network Id: %s\n", networkResource.ID)
 | 
			
		||||
	fmt.Fprintf(cli.out, "Name: %s\n", networkResource.Name)
 | 
			
		||||
	fmt.Fprintf(cli.out, "Type: %s\n", networkResource.Type)
 | 
			
		||||
	if networkResource.Services != nil {
 | 
			
		||||
		for _, serviceResource := range networkResource.Services {
 | 
			
		||||
			fmt.Fprintf(cli.out, "  Service Id: %s\n", serviceResource.ID)
 | 
			
		||||
			fmt.Fprintf(cli.out, "\tName: %s\n", serviceResource.Name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper function to predict if a string is a name or id or partial-id
 | 
			
		||||
// This provides a best-effort mechanism to identify a id with the help of GET Filter APIs
 | 
			
		||||
// Being a UI, its most likely that name will be used by the user, which is used to lookup
 | 
			
		||||
// the corresponding ID. If ID is not found, this function will assume that the passed string
 | 
			
		||||
// is an ID by itself.
 | 
			
		||||
 | 
			
		||||
func lookupNetworkID(cli *NetworkCli, nameID string) (string, error) {
 | 
			
		||||
	obj, statusCode, err := readBody(cli.call("GET", "/networks?name="+nameID, nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if statusCode != http.StatusOK {
 | 
			
		||||
		return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var list []*networkResource
 | 
			
		||||
	err = json.Unmarshal(obj, &list)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	if len(list) > 0 {
 | 
			
		||||
		// name query filter will always return a single-element collection
 | 
			
		||||
		return list[0].ID, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check for Partial-id
 | 
			
		||||
	obj, statusCode, err = readBody(cli.call("GET", "/networks?partial-id="+nameID, nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if statusCode != http.StatusOK {
 | 
			
		||||
		return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = json.Unmarshal(obj, &list)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	if len(list) == 0 {
 | 
			
		||||
		return "", fmt.Errorf("resource not found %s", nameID)
 | 
			
		||||
	}
 | 
			
		||||
	if len(list) > 1 {
 | 
			
		||||
		return "", fmt.Errorf("multiple Networks matching the partial identifier (%s). Please use full identifier", nameID)
 | 
			
		||||
	}
 | 
			
		||||
	return list[0].ID, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func networkUsage(chain string) string {
 | 
			
		||||
	help := "Commands:\n"
 | 
			
		||||
 | 
			
		||||
	for _, cmd := range networkCommands {
 | 
			
		||||
		help += fmt.Sprintf("  %-25.25s%s\n", cmd.name, cmd.description)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	help += fmt.Sprintf("\nRun '%s network COMMAND --help' for more information on a command.", chain)
 | 
			
		||||
	return help
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,392 +0,0 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	flag "github.com/docker/docker/pkg/mflag"
 | 
			
		||||
	"github.com/docker/docker/pkg/stringid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	serviceCommands = []command{
 | 
			
		||||
		{"publish", "Publish a service"},
 | 
			
		||||
		{"unpublish", "Remove a service"},
 | 
			
		||||
		{"attach", "Attach a backend (container) to the service"},
 | 
			
		||||
		{"detach", "Detach the backend from the service"},
 | 
			
		||||
		{"ls", "Lists all services"},
 | 
			
		||||
		{"info", "Display information about a service"},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func lookupServiceID(cli *NetworkCli, nwName, svNameID string) (string, error) {
 | 
			
		||||
	// Sanity Check
 | 
			
		||||
	obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/networks?name=%s", nwName), nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	var nwList []networkResource
 | 
			
		||||
	if err = json.Unmarshal(obj, &nwList); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	if len(nwList) == 0 {
 | 
			
		||||
		return "", fmt.Errorf("Network %s does not exist", nwName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if nwName == "" {
 | 
			
		||||
		obj, _, err := readBody(cli.call("GET", "/networks/"+nwList[0].ID, nil, nil))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		networkResource := &networkResource{}
 | 
			
		||||
		if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		nwName = networkResource.Name
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Query service by name
 | 
			
		||||
	obj, statusCode, err := readBody(cli.call("GET", fmt.Sprintf("/services?name=%s", svNameID), nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if statusCode != http.StatusOK {
 | 
			
		||||
		return "", fmt.Errorf("name query failed for %s due to: (%d) %s", svNameID, statusCode, string(obj))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var list []*serviceResource
 | 
			
		||||
	if err = json.Unmarshal(obj, &list); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	for _, sr := range list {
 | 
			
		||||
		if sr.Network == nwName {
 | 
			
		||||
			return sr.ID, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Query service by Partial-id (this covers full id as well)
 | 
			
		||||
	obj, statusCode, err = readBody(cli.call("GET", fmt.Sprintf("/services?partial-id=%s", svNameID), nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if statusCode != http.StatusOK {
 | 
			
		||||
		return "", fmt.Errorf("partial-id match query failed for %s due to: (%d) %s", svNameID, statusCode, string(obj))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = json.Unmarshal(obj, &list); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	for _, sr := range list {
 | 
			
		||||
		if sr.Network == nwName {
 | 
			
		||||
			return sr.ID, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", fmt.Errorf("Service %s not found on network %s", svNameID, nwName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func lookupContainerID(cli *NetworkCli, cnNameID string) (string, error) {
 | 
			
		||||
	// Container is a Docker resource, ask docker about it.
 | 
			
		||||
	// In case of connecton error, we assume we are running in dnet and return whatever was passed to us
 | 
			
		||||
	obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/containers/%s/json", cnNameID), nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// We are probably running outside of docker
 | 
			
		||||
		return cnNameID, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var x map[string]interface{}
 | 
			
		||||
	err = json.Unmarshal(obj, &x)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	if iid, ok := x["Id"]; ok {
 | 
			
		||||
		if id, ok := iid.(string); ok {
 | 
			
		||||
			return id, nil
 | 
			
		||||
		}
 | 
			
		||||
		return "", fmt.Errorf("Unexpected data type for container ID in json response")
 | 
			
		||||
	}
 | 
			
		||||
	return "", fmt.Errorf("Cannot find container ID in json response")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func lookupSandboxID(cli *NetworkCli, containerID string) (string, error) {
 | 
			
		||||
	obj, _, err := readBody(cli.call("GET", fmt.Sprintf("/sandboxes?partial-container-id=%s", containerID), nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var sandboxList []SandboxResource
 | 
			
		||||
	err = json.Unmarshal(obj, &sandboxList)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(sandboxList) == 0 {
 | 
			
		||||
		return "", fmt.Errorf("cannot find sandbox for container: %s", containerID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sandboxList[0].ID, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdService handles the service UI
 | 
			
		||||
func (cli *NetworkCli) CmdService(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "service", "COMMAND [OPTIONS] [arg...]", serviceUsage(chain), false)
 | 
			
		||||
	cmd.Require(flag.Min, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		cmd.Usage()
 | 
			
		||||
		return fmt.Errorf("Invalid command : %v", args)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse service name for "SERVICE[.NETWORK]" format
 | 
			
		||||
func parseServiceName(name string) (string, string) {
 | 
			
		||||
	s := strings.Split(name, ".")
 | 
			
		||||
	var sName, nName string
 | 
			
		||||
	if len(s) > 1 {
 | 
			
		||||
		nName = s[len(s)-1]
 | 
			
		||||
		sName = strings.Join(s[:len(s)-1], ".")
 | 
			
		||||
	} else {
 | 
			
		||||
		sName = s[0]
 | 
			
		||||
	}
 | 
			
		||||
	return sName, nName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdServicePublish handles service create UI
 | 
			
		||||
func (cli *NetworkCli) CmdServicePublish(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "publish", "SERVICE[.NETWORK]", "Publish a new service on a network", false)
 | 
			
		||||
	cmd.Require(flag.Exact, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sn, nn := parseServiceName(cmd.Arg(0))
 | 
			
		||||
	sc := serviceCreate{Name: sn, Network: nn}
 | 
			
		||||
	obj, _, err := readBody(cli.call("POST", "/services", sc, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var replyID string
 | 
			
		||||
	err = json.Unmarshal(obj, &replyID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(cli.out, "%s\n", replyID)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdServiceUnpublish handles service delete UI
 | 
			
		||||
func (cli *NetworkCli) CmdServiceUnpublish(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "unpublish", "SERVICE[.NETWORK]", "Removes a service", false)
 | 
			
		||||
	cmd.Require(flag.Exact, 1)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sn, nn := parseServiceName(cmd.Arg(0))
 | 
			
		||||
	serviceID, err := lookupServiceID(cli, nn, sn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, _, err = readBody(cli.call("DELETE", "/services/"+serviceID, nil, nil))
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdServiceLs handles service list UI
 | 
			
		||||
func (cli *NetworkCli) CmdServiceLs(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "ls", "SERVICE", "Lists all the services on a network", false)
 | 
			
		||||
	flNetwork := cmd.String([]string{"net", "-network"}, "", "Only show the services that are published on the specified network")
 | 
			
		||||
	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
 | 
			
		||||
	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output")
 | 
			
		||||
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var obj []byte
 | 
			
		||||
	if *flNetwork == "" {
 | 
			
		||||
		obj, _, err = readBody(cli.call("GET", "/services", nil, nil))
 | 
			
		||||
	} else {
 | 
			
		||||
		obj, _, err = readBody(cli.call("GET", "/services?network="+*flNetwork, nil, nil))
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var serviceResources []serviceResource
 | 
			
		||||
	err = json.Unmarshal(obj, &serviceResources)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 | 
			
		||||
	// unless quiet (-q) is specified, print field titles
 | 
			
		||||
	if !*quiet {
 | 
			
		||||
		fmt.Fprintln(wr, "SERVICE ID\tNAME\tNETWORK\tCONTAINER\tSANDBOX")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, sr := range serviceResources {
 | 
			
		||||
		ID := sr.ID
 | 
			
		||||
		bkID, sbID, err := getBackendID(cli, ID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if !*noTrunc {
 | 
			
		||||
			ID = stringid.TruncateID(ID)
 | 
			
		||||
			bkID = stringid.TruncateID(bkID)
 | 
			
		||||
			sbID = stringid.TruncateID(sbID)
 | 
			
		||||
		}
 | 
			
		||||
		if !*quiet {
 | 
			
		||||
			fmt.Fprintf(wr, "%s\t%s\t%s\t%s\t%s\n", ID, sr.Name, sr.Network, bkID, sbID)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintln(wr, ID)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	wr.Flush()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getBackendID(cli *NetworkCli, servID string) (string, string, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		obj []byte
 | 
			
		||||
		err error
 | 
			
		||||
		bk  string
 | 
			
		||||
		sb  string
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if obj, _, err = readBody(cli.call("GET", "/services/"+servID+"/backend", nil, nil)); err == nil {
 | 
			
		||||
		var sr SandboxResource
 | 
			
		||||
		if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&sr); err == nil {
 | 
			
		||||
			bk = sr.ContainerID
 | 
			
		||||
			sb = sr.ID
 | 
			
		||||
		} else {
 | 
			
		||||
			// Only print a message, don't make the caller cli fail for this
 | 
			
		||||
			fmt.Fprintf(cli.out, "Failed to retrieve backend list for service %s (%v)\n", servID, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return bk, sb, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdServiceInfo handles service info UI
 | 
			
		||||
func (cli *NetworkCli) CmdServiceInfo(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "info", "SERVICE[.NETWORK]", "Displays detailed information about a service", false)
 | 
			
		||||
	cmd.Require(flag.Min, 1)
 | 
			
		||||
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sn, nn := parseServiceName(cmd.Arg(0))
 | 
			
		||||
	serviceID, err := lookupServiceID(cli, nn, sn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	obj, _, err := readBody(cli.call("GET", "/services/"+serviceID, nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sr := &serviceResource{}
 | 
			
		||||
	if err := json.NewDecoder(bytes.NewReader(obj)).Decode(sr); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Fprintf(cli.out, "Service Id: %s\n", sr.ID)
 | 
			
		||||
	fmt.Fprintf(cli.out, "\tName: %s\n", sr.Name)
 | 
			
		||||
	fmt.Fprintf(cli.out, "\tNetwork: %s\n", sr.Network)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdServiceAttach handles service attach UI
 | 
			
		||||
func (cli *NetworkCli) CmdServiceAttach(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "attach", "CONTAINER SERVICE[.NETWORK]", "Sets a container as a service backend", false)
 | 
			
		||||
	cmd.Require(flag.Min, 2)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerID, err := lookupContainerID(cli, cmd.Arg(0))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sandboxID, err := lookupSandboxID(cli, containerID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sn, nn := parseServiceName(cmd.Arg(1))
 | 
			
		||||
	serviceID, err := lookupServiceID(cli, nn, sn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nc := serviceAttach{SandboxID: sandboxID}
 | 
			
		||||
 | 
			
		||||
	_, _, err = readBody(cli.call("POST", "/services/"+serviceID+"/backend", nc, nil))
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CmdServiceDetach handles service detach UI
 | 
			
		||||
func (cli *NetworkCli) CmdServiceDetach(chain string, args ...string) error {
 | 
			
		||||
	cmd := cli.Subcmd(chain, "detach", "CONTAINER SERVICE", "Removes a container from service backend", false)
 | 
			
		||||
	cmd.Require(flag.Min, 2)
 | 
			
		||||
	err := cmd.ParseFlags(args, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sn, nn := parseServiceName(cmd.Arg(1))
 | 
			
		||||
	containerID, err := lookupContainerID(cli, cmd.Arg(0))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sandboxID, err := lookupSandboxID(cli, containerID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	serviceID, err := lookupServiceID(cli, nn, sn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, _, err = readBody(cli.call("DELETE", "/services/"+serviceID+"/backend/"+sandboxID, nil, nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func serviceUsage(chain string) string {
 | 
			
		||||
	help := "Commands:\n"
 | 
			
		||||
 | 
			
		||||
	for _, cmd := range serviceCommands {
 | 
			
		||||
		help += fmt.Sprintf("    %-10.10s%s\n", cmd.name, cmd.description)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	help += fmt.Sprintf("\nRun '%s service COMMAND --help' for more information on a command.", chain)
 | 
			
		||||
	return help
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,79 +0,0 @@
 | 
			
		|||
package client
 | 
			
		||||
 | 
			
		||||
import "github.com/docker/libnetwork/types"
 | 
			
		||||
 | 
			
		||||
/***********
 | 
			
		||||
 Resources
 | 
			
		||||
************/
 | 
			
		||||
 | 
			
		||||
// networkResource is the body of the "get network" http response message
 | 
			
		||||
type networkResource struct {
 | 
			
		||||
	Name     string             `json:"name"`
 | 
			
		||||
	ID       string             `json:"id"`
 | 
			
		||||
	Type     string             `json:"type"`
 | 
			
		||||
	Services []*serviceResource `json:"services"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// serviceResource is the body of the "get service" http response message
 | 
			
		||||
type serviceResource struct {
 | 
			
		||||
	Name    string `json:"name"`
 | 
			
		||||
	ID      string `json:"id"`
 | 
			
		||||
	Network string `json:"network"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SandboxResource is the body of "get service backend" response message
 | 
			
		||||
type SandboxResource struct {
 | 
			
		||||
	ID          string `json:"id"`
 | 
			
		||||
	Key         string `json:"key"`
 | 
			
		||||
	ContainerID string `json:"container_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********
 | 
			
		||||
  Body types
 | 
			
		||||
  ************/
 | 
			
		||||
 | 
			
		||||
// networkCreate is the expected body of the "create network" http request message
 | 
			
		||||
type networkCreate struct {
 | 
			
		||||
	Name        string                 `json:"name"`
 | 
			
		||||
	NetworkType string                 `json:"network_type"`
 | 
			
		||||
	Options     map[string]interface{} `json:"options"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// serviceCreate represents the body of the "publish service" http request message
 | 
			
		||||
type serviceCreate struct {
 | 
			
		||||
	Name         string                `json:"name"`
 | 
			
		||||
	Network      string                `json:"network_name"`
 | 
			
		||||
	ExposedPorts []types.TransportPort `json:"exposed_ports"`
 | 
			
		||||
	PortMapping  []types.PortBinding   `json:"port_mapping"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// serviceAttach represents the expected body of the "attach/detach sandbox to/from service" http request messages
 | 
			
		||||
type serviceAttach struct {
 | 
			
		||||
	SandboxID string `json:"sandbox_id"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SandboxCreate is the body of the "post /sandboxes" http request message
 | 
			
		||||
type SandboxCreate struct {
 | 
			
		||||
	ContainerID       string      `json:"container_id"`
 | 
			
		||||
	HostName          string      `json:"host_name"`
 | 
			
		||||
	DomainName        string      `json:"domain_name"`
 | 
			
		||||
	HostsPath         string      `json:"hosts_path"`
 | 
			
		||||
	ResolvConfPath    string      `json:"resolv_conf_path"`
 | 
			
		||||
	DNS               []string    `json:"dns"`
 | 
			
		||||
	ExtraHosts        []extraHost `json:"extra_hosts"`
 | 
			
		||||
	UseDefaultSandbox bool        `json:"use_default_sandbox"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// extraHost represents the extra host object
 | 
			
		||||
type extraHost struct {
 | 
			
		||||
	Name    string `json:"name"`
 | 
			
		||||
	Address string `json:"address"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sandboxParentUpdate is the object carrying the information about the
 | 
			
		||||
// sanbox parent that needs to be updated
 | 
			
		||||
type sandboxParentUpdate struct {
 | 
			
		||||
	ContainerID string `json:"container_id"`
 | 
			
		||||
	Name        string `json:"name"`
 | 
			
		||||
	Address     string `json:"address"`
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue