Browse Source

Merge pull request #33630 from yongtang/167-cli-network-inspect-scope

Add `scope` filter in `GET /networks/(id or name)`
Aaron Lehmann 8 years ago
parent
commit
4310f7da7e

+ 14 - 6
api/server/router/network/network_routes.go

@@ -98,6 +98,14 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 			return errors.NewBadRequestError(err)
 			return errors.NewBadRequestError(err)
 		}
 		}
 	}
 	}
+	scope := r.URL.Query().Get("scope")
+
+	isMatchingScope := func(scope, term string) bool {
+		if term != "" {
+			return scope == term
+		}
+		return true
+	}
 
 
 	// In case multiple networks have duplicate names, return error.
 	// In case multiple networks have duplicate names, return error.
 	// TODO (yongtang): should we wrap with version here for backward compatibility?
 	// TODO (yongtang): should we wrap with version here for backward compatibility?
@@ -112,15 +120,15 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 
 
 	nw := n.backend.GetNetworks()
 	nw := n.backend.GetNetworks()
 	for _, network := range nw {
 	for _, network := range nw {
-		if network.ID() == term {
+		if network.ID() == term && isMatchingScope(network.Info().Scope(), scope) {
 			return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network, verbose))
 			return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network, verbose))
 		}
 		}
-		if network.Name() == term {
+		if network.Name() == term && isMatchingScope(network.Info().Scope(), scope) {
 			// No need to check the ID collision here as we are still in
 			// No need to check the ID collision here as we are still in
 			// local scope and the network ID is unique in this scope.
 			// local scope and the network ID is unique in this scope.
 			listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
 			listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
 		}
 		}
-		if strings.HasPrefix(network.ID(), term) {
+		if strings.HasPrefix(network.ID(), term) && isMatchingScope(network.Info().Scope(), scope) {
 			// No need to check the ID collision here as we are still in
 			// No need to check the ID collision here as we are still in
 			// local scope and the network ID is unique in this scope.
 			// local scope and the network ID is unique in this scope.
 			listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
 			listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
@@ -129,10 +137,10 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 
 
 	nr, _ := n.cluster.GetNetworks()
 	nr, _ := n.cluster.GetNetworks()
 	for _, network := range nr {
 	for _, network := range nr {
-		if network.ID == term {
+		if network.ID == term && isMatchingScope(network.Scope, scope) {
 			return httputils.WriteJSON(w, http.StatusOK, network)
 			return httputils.WriteJSON(w, http.StatusOK, network)
 		}
 		}
-		if network.Name == term {
+		if network.Name == term && isMatchingScope(network.Scope, scope) {
 			// Check the ID collision as we are in swarm scope here, and
 			// Check the ID collision as we are in swarm scope here, and
 			// the map (of the listByFullName) may have already had a
 			// the map (of the listByFullName) may have already had a
 			// network with the same ID (from local scope previously)
 			// network with the same ID (from local scope previously)
@@ -140,7 +148,7 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 				listByFullName[network.ID] = network
 				listByFullName[network.ID] = network
 			}
 			}
 		}
 		}
-		if strings.HasPrefix(network.ID, term) {
+		if strings.HasPrefix(network.ID, term) && isMatchingScope(network.Scope, scope) {
 			// Check the ID collision as we are in swarm scope here, and
 			// Check the ID collision as we are in swarm scope here, and
 			// the map (of the listByPartialID) may have already had a
 			// the map (of the listByPartialID) may have already had a
 			// network with the same ID (from local scope previously)
 			// network with the same ID (from local scope previously)

+ 4 - 0
api/swagger.yaml

@@ -6437,6 +6437,10 @@ paths:
           description: "Detailed inspect output for troubleshooting"
           description: "Detailed inspect output for troubleshooting"
           type: "boolean"
           type: "boolean"
           default: false
           default: false
+        - name: "scope"
+          in: "query"
+          description: "Filter the network by scope (swarm, global, or local)"
+          type: "string"
       tags: ["Network"]
       tags: ["Network"]
 
 
     delete:
     delete:

+ 6 - 0
api/types/types.go

@@ -468,6 +468,12 @@ type NetworkDisconnect struct {
 	Force     bool
 	Force     bool
 }
 }
 
 
+// NetworkInspectOptions holds parameters to inspect network
+type NetworkInspectOptions struct {
+	Scope   string
+	Verbose bool
+}
+
 // Checkpoint represents the details of a checkpoint
 // Checkpoint represents the details of a checkpoint
 type Checkpoint struct {
 type Checkpoint struct {
 	Name string // Name is the name of the checkpoint
 	Name string // Name is the name of the checkpoint

+ 2 - 2
client/interface.go

@@ -99,8 +99,8 @@ type NetworkAPIClient interface {
 	NetworkConnect(ctx context.Context, networkID, container string, config *network.EndpointSettings) error
 	NetworkConnect(ctx context.Context, networkID, container string, config *network.EndpointSettings) error
 	NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
 	NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
 	NetworkDisconnect(ctx context.Context, networkID, container string, force bool) error
 	NetworkDisconnect(ctx context.Context, networkID, container string, force bool) error
-	NetworkInspect(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, error)
-	NetworkInspectWithRaw(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, []byte, error)
+	NetworkInspect(ctx context.Context, networkID string, options types.NetworkInspectOptions) (types.NetworkResource, error)
+	NetworkInspectWithRaw(ctx context.Context, networkID string, options types.NetworkInspectOptions) (types.NetworkResource, []byte, error)
 	NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error)
 	NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error)
 	NetworkRemove(ctx context.Context, networkID string) error
 	NetworkRemove(ctx context.Context, networkID string) error
 	NetworksPrune(ctx context.Context, pruneFilter filters.Args) (types.NetworksPruneReport, error)
 	NetworksPrune(ctx context.Context, pruneFilter filters.Args) (types.NetworksPruneReport, error)

+ 7 - 4
client/network_inspect.go

@@ -12,22 +12,25 @@ import (
 )
 )
 
 
 // NetworkInspect returns the information for a specific network configured in the docker host.
 // NetworkInspect returns the information for a specific network configured in the docker host.
-func (cli *Client) NetworkInspect(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, error) {
-	networkResource, _, err := cli.NetworkInspectWithRaw(ctx, networkID, verbose)
+func (cli *Client) NetworkInspect(ctx context.Context, networkID string, options types.NetworkInspectOptions) (types.NetworkResource, error) {
+	networkResource, _, err := cli.NetworkInspectWithRaw(ctx, networkID, options)
 	return networkResource, err
 	return networkResource, err
 }
 }
 
 
 // NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation.
 // NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation.
-func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, []byte, error) {
+func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, options types.NetworkInspectOptions) (types.NetworkResource, []byte, error) {
 	var (
 	var (
 		networkResource types.NetworkResource
 		networkResource types.NetworkResource
 		resp            serverResponse
 		resp            serverResponse
 		err             error
 		err             error
 	)
 	)
 	query := url.Values{}
 	query := url.Values{}
-	if verbose {
+	if options.Verbose {
 		query.Set("verbose", "true")
 		query.Set("verbose", "true")
 	}
 	}
+	if options.Scope != "" {
+		query.Set("scope", options.Scope)
+	}
 	resp, err = cli.get(ctx, "/networks/"+networkID, query, nil)
 	resp, err = cli.get(ctx, "/networks/"+networkID, query, nil)
 	if err != nil {
 	if err != nil {
 		if resp.statusCode == http.StatusNotFound {
 		if resp.statusCode == http.StatusNotFound {

+ 16 - 5
client/network_inspect_test.go

@@ -11,6 +11,7 @@ import (
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/network"
+	"github.com/stretchr/testify/assert"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
@@ -19,7 +20,7 @@ func TestNetworkInspectError(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 	}
 	}
 
 
-	_, err := client.NetworkInspect(context.Background(), "nothing", false)
+	_, err := client.NetworkInspect(context.Background(), "nothing", types.NetworkInspectOptions{})
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
@@ -30,7 +31,7 @@ func TestNetworkInspectContainerNotFound(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusNotFound, "Server error")),
 		client: newMockClient(errorMock(http.StatusNotFound, "Server error")),
 	}
 	}
 
 
-	_, err := client.NetworkInspect(context.Background(), "unknown", false)
+	_, err := client.NetworkInspect(context.Background(), "unknown", types.NetworkInspectOptions{})
 	if err == nil || !IsErrNetworkNotFound(err) {
 	if err == nil || !IsErrNetworkNotFound(err) {
 		t.Fatalf("expected a networkNotFound error, got %v", err)
 		t.Fatalf("expected a networkNotFound error, got %v", err)
 	}
 	}
@@ -51,7 +52,14 @@ func TestNetworkInspect(t *testing.T) {
 				content []byte
 				content []byte
 				err     error
 				err     error
 			)
 			)
-			if strings.HasPrefix(req.URL.RawQuery, "verbose=true") {
+			if strings.Contains(req.URL.RawQuery, "scope=global") {
+				return &http.Response{
+					StatusCode: http.StatusNotFound,
+					Body:       ioutil.NopCloser(bytes.NewReader(content)),
+				}, nil
+			}
+
+			if strings.Contains(req.URL.RawQuery, "verbose=true") {
 				s := map[string]network.ServiceInfo{
 				s := map[string]network.ServiceInfo{
 					"web": {},
 					"web": {},
 				}
 				}
@@ -74,7 +82,7 @@ func TestNetworkInspect(t *testing.T) {
 		}),
 		}),
 	}
 	}
 
 
-	r, err := client.NetworkInspect(context.Background(), "network_id", false)
+	r, err := client.NetworkInspect(context.Background(), "network_id", types.NetworkInspectOptions{})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -82,7 +90,7 @@ func TestNetworkInspect(t *testing.T) {
 		t.Fatalf("expected `mynetwork`, got %s", r.Name)
 		t.Fatalf("expected `mynetwork`, got %s", r.Name)
 	}
 	}
 
 
-	r, err = client.NetworkInspect(context.Background(), "network_id", true)
+	r, err = client.NetworkInspect(context.Background(), "network_id", types.NetworkInspectOptions{Verbose: true})
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -93,4 +101,7 @@ func TestNetworkInspect(t *testing.T) {
 	if !ok {
 	if !ok {
 		t.Fatalf("expected service `web` missing in the verbose output")
 		t.Fatalf("expected service `web` missing in the verbose output")
 	}
 	}
+
+	_, err = client.NetworkInspect(context.Background(), "network_id", types.NetworkInspectOptions{Scope: "global"})
+	assert.EqualError(t, err, "Error: No such network: network_id")
 }
 }

+ 1 - 0
docs/api/version-history.md

@@ -21,6 +21,7 @@ keywords: "API, Docker, rcli, REST, documentation"
 * `POST /secrets/create` now returns status code 409 instead of 500 when creating an already existing secret.
 * `POST /secrets/create` now returns status code 409 instead of 500 when creating an already existing secret.
 * `POST /secrets/(name)/update` now returns status code 400 instead of 500 when updating a secret's content which is not the labels.
 * `POST /secrets/(name)/update` now returns status code 400 instead of 500 when updating a secret's content which is not the labels.
 * `POST /nodes/(name)/update` now returns status code 400 instead of 500 when demoting last node fails.
 * `POST /nodes/(name)/update` now returns status code 400 instead of 500 when demoting last node fails.
+* `GET /networks/(id or name)` now takes an optional query parameter `scope` that will filter the network based on the scope (`local`, `swarm`, or `global`).
 
 
 ## v1.30 API changes
 ## v1.30 API changes
 
 

+ 34 - 0
integration-cli/docker_api_swarm_test.go

@@ -8,6 +8,7 @@ import (
 	"io/ioutil"
 	"io/ioutil"
 	"net"
 	"net"
 	"net/http"
 	"net/http"
+	"net/url"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"strings"
 	"strings"
@@ -1002,3 +1003,36 @@ func (s *DockerSwarmSuite) TestSwarmRepeatedRootRotation(c *check.C) {
 		currentTrustRoot = clusterTLSInfo.TrustRoot
 		currentTrustRoot = clusterTLSInfo.TrustRoot
 	}
 	}
 }
 }
+
+func (s *DockerSwarmSuite) TestAPINetworkInspectWithScope(c *check.C) {
+	d := s.AddDaemon(c, true, true)
+
+	name := "foo"
+	networkCreateRequest := types.NetworkCreateRequest{
+		Name: name,
+	}
+
+	var n types.NetworkCreateResponse
+	networkCreateRequest.NetworkCreate.Driver = "overlay"
+
+	status, out, err := d.SockRequest("POST", "/networks/create", networkCreateRequest)
+	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
+	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(out)))
+	c.Assert(json.Unmarshal(out, &n), checker.IsNil)
+
+	var r types.NetworkResource
+
+	status, body, err := d.SockRequest("GET", "/networks/"+name, nil)
+	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
+	c.Assert(status, checker.Equals, http.StatusOK, check.Commentf(string(out)))
+	c.Assert(json.Unmarshal(body, &r), checker.IsNil)
+	c.Assert(r.Scope, checker.Equals, "swarm")
+	c.Assert(r.ID, checker.Equals, n.ID)
+
+	v := url.Values{}
+	v.Set("scope", "local")
+
+	status, body, err = d.SockRequest("GET", "/networks/"+name+"?"+v.Encode(), nil)
+	c.Assert(err, checker.IsNil, check.Commentf(string(out)))
+	c.Assert(status, checker.Equals, http.StatusNotFound, check.Commentf(string(out)))
+}