瀏覽代碼

Move network conversions out of API router

This stuff doesn't belong here and is causing imports of libnetwork into
the router, which is not what we want.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
Brian Goff 7 年之前
父節點
當前提交
c0bc14e8dd

+ 2 - 2
api/server/router/network/backend.go

@@ -13,7 +13,7 @@ import (
 // to provide network specific functionality.
 type Backend interface {
 	FindNetwork(idName string) (libnetwork.Network, error)
-	GetNetworks() []libnetwork.Network
+	GetNetworks(filters.Args, types.NetworkListConfig) ([]types.NetworkResource, error)
 	CreateNetwork(nc types.NetworkCreateRequest) (*types.NetworkCreateResponse, error)
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
 	DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error
@@ -24,7 +24,7 @@ type Backend interface {
 // ClusterBackend is all the methods that need to be implemented
 // to provide cluster network specific functionality.
 type ClusterBackend interface {
-	GetNetworks() ([]types.NetworkResource, error)
+	GetNetworks(filters.Args) ([]types.NetworkResource, error)
 	GetNetwork(name string) (types.NetworkResource, error)
 	GetNetworksByName(name string) ([]types.NetworkResource, error)
 	CreateNetwork(nc types.NetworkCreateRequest) (string, error)

+ 0 - 92
api/server/router/network/filter.go

@@ -1,93 +1 @@
 package network // import "github.com/docker/docker/api/server/router/network"
-
-import (
-	"github.com/docker/docker/api/types"
-	"github.com/docker/docker/api/types/filters"
-	"github.com/docker/docker/runconfig"
-)
-
-func filterNetworkByType(nws []types.NetworkResource, netType string) ([]types.NetworkResource, error) {
-	retNws := []types.NetworkResource{}
-	switch netType {
-	case "builtin":
-		for _, nw := range nws {
-			if runconfig.IsPreDefinedNetwork(nw.Name) {
-				retNws = append(retNws, nw)
-			}
-		}
-	case "custom":
-		for _, nw := range nws {
-			if !runconfig.IsPreDefinedNetwork(nw.Name) {
-				retNws = append(retNws, nw)
-			}
-		}
-	default:
-		return nil, invalidFilter(netType)
-	}
-	return retNws, nil
-}
-
-type invalidFilter string
-
-func (e invalidFilter) Error() string {
-	return "Invalid filter: 'type'='" + string(e) + "'"
-}
-
-func (e invalidFilter) InvalidParameter() {}
-
-// filterNetworks filters network list according to user specified filter
-// and returns user chosen networks
-func filterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.NetworkResource, error) {
-	// if filter is empty, return original network list
-	if filter.Len() == 0 {
-		return nws, nil
-	}
-
-	displayNet := []types.NetworkResource{}
-	for _, nw := range nws {
-		if filter.Contains("driver") {
-			if !filter.ExactMatch("driver", nw.Driver) {
-				continue
-			}
-		}
-		if filter.Contains("name") {
-			if !filter.Match("name", nw.Name) {
-				continue
-			}
-		}
-		if filter.Contains("id") {
-			if !filter.Match("id", nw.ID) {
-				continue
-			}
-		}
-		if filter.Contains("label") {
-			if !filter.MatchKVList("label", nw.Labels) {
-				continue
-			}
-		}
-		if filter.Contains("scope") {
-			if !filter.ExactMatch("scope", nw.Scope) {
-				continue
-			}
-		}
-		displayNet = append(displayNet, nw)
-	}
-
-	if filter.Contains("type") {
-		typeNet := []types.NetworkResource{}
-		errFilter := filter.WalkValues("type", func(fval string) error {
-			passList, err := filterNetworkByType(displayNet, fval)
-			if err != nil {
-				return err
-			}
-			typeNet = append(typeNet, passList...)
-			return nil
-		})
-		if errFilter != nil {
-			return nil, errFilter
-		}
-		displayNet = typeNet
-	}
-
-	return displayNet, nil
-}

+ 50 - 243
api/server/router/network/network_routes.go

@@ -15,70 +15,54 @@ import (
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/libnetwork"
 	netconst "github.com/docker/libnetwork/datastore"
-	"github.com/docker/libnetwork/networkdb"
 	"github.com/pkg/errors"
 )
 
-var (
-	// acceptedNetworkFilters is a list of acceptable filters
-	acceptedNetworkFilters = map[string]bool{
-		"driver": true,
-		"type":   true,
-		"name":   true,
-		"id":     true,
-		"label":  true,
-		"scope":  true,
-	}
-)
-
 func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := httputils.ParseForm(r); err != nil {
 		return err
 	}
 
-	filter := r.Form.Get("filters")
-	netFilters, err := filters.FromJSON(filter)
+	filter, err := filters.FromJSON(r.Form.Get("filters"))
 	if err != nil {
 		return err
 	}
 
-	if err := netFilters.Validate(acceptedNetworkFilters); err != nil {
+	if err := network.ValidateFilters(filter); err != nil {
 		return err
 	}
 
-	list := []types.NetworkResource{}
-
-	if nr, err := n.cluster.GetNetworks(); err == nil {
-		list = append(list, nr...)
+	var list []types.NetworkResource
+	nr, err := n.cluster.GetNetworks(filter)
+	if err == nil {
+		list = nr
 	}
 
 	// Combine the network list returned by Docker daemon if it is not already
 	// returned by the cluster manager
-SKIP:
-	for _, nw := range n.backend.GetNetworks() {
-		for _, nl := range list {
-			if nl.ID == nw.ID() {
-				continue SKIP
-			}
-		}
+	localNetworks, err := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: versions.LessThan(httputils.VersionFromContext(ctx), "1.28")})
+	if err != nil {
+		return err
+	}
 
-		var nr *types.NetworkResource
-		// Versions < 1.28 fetches all the containers attached to a network
-		// in a network list api call. It is a heavy weight operation when
-		// run across all the networks. Starting API version 1.28, this detailed
-		// info is available for network specific GET API (equivalent to inspect)
-		if versions.LessThan(httputils.VersionFromContext(ctx), "1.28") {
-			nr = n.buildDetailedNetworkResources(nw, false)
-		} else {
-			nr = n.buildNetworkResource(nw)
+	var idx map[string]bool
+	if len(list) > 0 {
+		idx = make(map[string]bool, len(list))
+		for _, n := range list {
+			idx[n.ID] = true
 		}
-		list = append(list, *nr)
+	}
+	for _, n := range localNetworks {
+		if idx[n.ID] {
+			continue
+		}
+		list = append(list, n)
 	}
 
-	list, err = filterNetworks(list, netFilters)
-	if err != nil {
-		return err
+	if list == nil {
+		list = []types.NetworkResource{}
 	}
+
 	return httputils.WriteJSON(w, http.StatusOK, list)
 }
 
@@ -121,13 +105,6 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 	}
 	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.
 	// TODO (yongtang): should we wrap with version here for backward compatibility?
 
@@ -139,20 +116,26 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 	listByFullName := map[string]types.NetworkResource{}
 	listByPartialID := map[string]types.NetworkResource{}
 
-	nw := n.backend.GetNetworks()
+	// TODO(@cpuguy83): All this logic for figuring out which network to return does not belong here
+	// Instead there should be a backend function to just get one network.
+	filter := filters.NewArgs(filters.Arg("idOrName", term))
+	if scope != "" {
+		filter.Add("scope", scope)
+	}
+	nw, _ := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: true, Verbose: verbose})
 	for _, network := range nw {
-		if network.ID() == term && isMatchingScope(network.Info().Scope(), scope) {
-			return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network, verbose))
+		if network.ID == term {
+			return httputils.WriteJSON(w, http.StatusOK, network)
 		}
-		if network.Name() == term && isMatchingScope(network.Info().Scope(), scope) {
+		if network.Name == term {
 			// No need to check the ID collision here as we are still in
 			// local scope and the network ID is unique in this scope.
-			listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
+			listByFullName[network.ID] = network
 		}
-		if strings.HasPrefix(network.ID(), term) && isMatchingScope(network.Info().Scope(), scope) {
+		if strings.HasPrefix(network.ID, term) {
 			// No need to check the ID collision here as we are still in
 			// local scope and the network ID is unique in this scope.
-			listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
+			listByPartialID[network.ID] = network
 		}
 	}
 
@@ -174,12 +157,12 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 		}
 	}
 
-	nr, _ := n.cluster.GetNetworks()
+	nr, _ := n.cluster.GetNetworks(filter)
 	for _, network := range nr {
-		if network.ID == term && isMatchingScope(network.Scope, scope) {
+		if network.ID == term {
 			return httputils.WriteJSON(w, http.StatusOK, network)
 		}
-		if network.Name == term && isMatchingScope(network.Scope, scope) {
+		if network.Name == term {
 			// Check the ID collision as we are in swarm scope here, and
 			// the map (of the listByFullName) may have already had a
 			// network with the same ID (from local scope previously)
@@ -187,7 +170,7 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 				listByFullName[network.ID] = network
 			}
 		}
-		if strings.HasPrefix(network.ID, term) && isMatchingScope(network.Scope, scope) {
+		if strings.HasPrefix(network.ID, term) {
 			// Check the ID collision as we are in swarm scope here, and
 			// the map (of the listByPartialID) may have already had a
 			// network with the same ID (from local scope previously)
@@ -327,182 +310,6 @@ func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter
 	return nil
 }
 
-func (n *networkRouter) buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
-	r := &types.NetworkResource{}
-	if nw == nil {
-		return r
-	}
-
-	info := nw.Info()
-	r.Name = nw.Name()
-	r.ID = nw.ID()
-	r.Created = info.Created()
-	r.Scope = info.Scope()
-	r.Driver = nw.Type()
-	r.EnableIPv6 = info.IPv6Enabled()
-	r.Internal = info.Internal()
-	r.Attachable = info.Attachable()
-	r.Ingress = info.Ingress()
-	r.Options = info.DriverOptions()
-	r.Containers = make(map[string]types.EndpointResource)
-	buildIpamResources(r, info)
-	r.Labels = info.Labels()
-	r.ConfigOnly = info.ConfigOnly()
-
-	if cn := info.ConfigFrom(); cn != "" {
-		r.ConfigFrom = network.ConfigReference{Network: cn}
-	}
-
-	peers := info.Peers()
-	if len(peers) != 0 {
-		r.Peers = buildPeerInfoResources(peers)
-	}
-
-	return r
-}
-
-func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network, verbose bool) *types.NetworkResource {
-	if nw == nil {
-		return &types.NetworkResource{}
-	}
-
-	r := n.buildNetworkResource(nw)
-	epl := nw.Endpoints()
-	for _, e := range epl {
-		ei := e.Info()
-		if ei == nil {
-			continue
-		}
-		sb := ei.Sandbox()
-		tmpID := e.ID()
-		key := "ep-" + tmpID
-		if sb != nil {
-			key = sb.ContainerID()
-		}
-
-		r.Containers[key] = buildEndpointResource(tmpID, e.Name(), ei)
-	}
-	if !verbose {
-		return r
-	}
-	services := nw.Info().Services()
-	r.Services = make(map[string]network.ServiceInfo)
-	for name, service := range services {
-		tasks := []network.Task{}
-		for _, t := range service.Tasks {
-			tasks = append(tasks, network.Task{
-				Name:       t.Name,
-				EndpointID: t.EndpointID,
-				EndpointIP: t.EndpointIP,
-				Info:       t.Info,
-			})
-		}
-		r.Services[name] = network.ServiceInfo{
-			VIP:          service.VIP,
-			Ports:        service.Ports,
-			Tasks:        tasks,
-			LocalLBIndex: service.LocalLBIndex,
-		}
-	}
-	return r
-}
-
-func buildPeerInfoResources(peers []networkdb.PeerInfo) []network.PeerInfo {
-	peerInfo := make([]network.PeerInfo, 0, len(peers))
-	for _, peer := range peers {
-		peerInfo = append(peerInfo, network.PeerInfo{
-			Name: peer.Name,
-			IP:   peer.IP,
-		})
-	}
-	return peerInfo
-}
-
-func buildIpamResources(r *types.NetworkResource, nwInfo libnetwork.NetworkInfo) {
-	id, opts, ipv4conf, ipv6conf := nwInfo.IpamConfig()
-
-	ipv4Info, ipv6Info := nwInfo.IpamInfo()
-
-	r.IPAM.Driver = id
-
-	r.IPAM.Options = opts
-
-	r.IPAM.Config = []network.IPAMConfig{}
-	for _, ip4 := range ipv4conf {
-		if ip4.PreferredPool == "" {
-			continue
-		}
-		iData := network.IPAMConfig{}
-		iData.Subnet = ip4.PreferredPool
-		iData.IPRange = ip4.SubPool
-		iData.Gateway = ip4.Gateway
-		iData.AuxAddress = ip4.AuxAddresses
-		r.IPAM.Config = append(r.IPAM.Config, iData)
-	}
-
-	if len(r.IPAM.Config) == 0 {
-		for _, ip4Info := range ipv4Info {
-			iData := network.IPAMConfig{}
-			iData.Subnet = ip4Info.IPAMData.Pool.String()
-			if ip4Info.IPAMData.Gateway != nil {
-				iData.Gateway = ip4Info.IPAMData.Gateway.IP.String()
-			}
-			r.IPAM.Config = append(r.IPAM.Config, iData)
-		}
-	}
-
-	hasIpv6Conf := false
-	for _, ip6 := range ipv6conf {
-		if ip6.PreferredPool == "" {
-			continue
-		}
-		hasIpv6Conf = true
-		iData := network.IPAMConfig{}
-		iData.Subnet = ip6.PreferredPool
-		iData.IPRange = ip6.SubPool
-		iData.Gateway = ip6.Gateway
-		iData.AuxAddress = ip6.AuxAddresses
-		r.IPAM.Config = append(r.IPAM.Config, iData)
-	}
-
-	if !hasIpv6Conf {
-		for _, ip6Info := range ipv6Info {
-			if ip6Info.IPAMData.Pool == nil {
-				continue
-			}
-			iData := network.IPAMConfig{}
-			iData.Subnet = ip6Info.IPAMData.Pool.String()
-			iData.Gateway = ip6Info.IPAMData.Gateway.String()
-			r.IPAM.Config = append(r.IPAM.Config, iData)
-		}
-	}
-}
-
-func buildEndpointResource(id string, name string, info libnetwork.EndpointInfo) types.EndpointResource {
-	er := types.EndpointResource{}
-
-	er.EndpointID = id
-	er.Name = name
-	ei := info
-	if ei == nil {
-		return er
-	}
-
-	if iface := ei.Iface(); iface != nil {
-		if mac := iface.MacAddress(); mac != nil {
-			er.MacAddress = mac.String()
-		}
-		if ip := iface.Address(); ip != nil && len(ip.IP) > 0 {
-			er.IPv4Address = ip.String()
-		}
-
-		if ipv6 := iface.AddressIPv6(); ipv6 != nil && len(ipv6.IP) > 0 {
-			er.IPv6Address = ipv6.String()
-		}
-	}
-	return er
-}
-
 func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := httputils.ParseForm(r); err != nil {
 		return err
@@ -532,25 +339,25 @@ func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, e
 	listByFullName := map[string]types.NetworkResource{}
 	listByPartialID := map[string]types.NetworkResource{}
 
-	nw := n.backend.GetNetworks()
+	filter := filters.NewArgs(filters.Arg("idOrName", term))
+	nw, _ := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: true})
 	for _, network := range nw {
-		if network.ID() == term {
-			return *n.buildDetailedNetworkResources(network, false), nil
-
+		if network.ID == term {
+			return network, nil
 		}
-		if network.Name() == term && !network.Info().Ingress() {
+		if network.Name == term && !network.Ingress {
 			// No need to check the ID collision here as we are still in
 			// local scope and the network ID is unique in this scope.
-			listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, false)
+			listByFullName[network.ID] = network
 		}
-		if strings.HasPrefix(network.ID(), term) {
+		if strings.HasPrefix(network.ID, term) {
 			// No need to check the ID collision here as we are still in
 			// local scope and the network ID is unique in this scope.
-			listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, false)
+			listByPartialID[network.ID] = network
 		}
 	}
 
-	nr, _ := n.cluster.GetNetworks()
+	nr, _ := n.cluster.GetNetworks(filter)
 	for _, network := range nr {
 		if network.ID == term {
 			return network, nil

+ 7 - 0
api/types/configs.go

@@ -55,3 +55,10 @@ type PluginEnableConfig struct {
 type PluginDisableConfig struct {
 	ForceDisable bool
 }
+
+// NetworkListConfig stores the options available for listing networks
+type NetworkListConfig struct {
+	// TODO(@cpuguy83): naming is hard, this is pulled from what was being used in the router before moving here
+	Detailed bool
+	Verbose  bool
+}

+ 18 - 0
api/types/network/network.go

@@ -1,4 +1,8 @@
 package network // import "github.com/docker/docker/api/types/network"
+import (
+	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/errdefs"
+)
 
 // Address represents an IP address
 type Address struct {
@@ -106,3 +110,17 @@ type NetworkingConfig struct {
 type ConfigReference struct {
 	Network string
 }
+
+var acceptedFilters = map[string]bool{
+	"driver": true,
+	"type":   true,
+	"name":   true,
+	"id":     true,
+	"label":  true,
+	"scope":  true,
+}
+
+// ValidateFilters validates the list of filter args with the available filters.
+func ValidateFilters(filter filters.Args) error {
+	return errdefs.InvalidParameter(filter.Validate(acceptedFilters))
+}

+ 2 - 1
daemon/cluster.go

@@ -2,6 +2,7 @@ package daemon // import "github.com/docker/docker/daemon"
 
 import (
 	apitypes "github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
 	lncluster "github.com/docker/libnetwork/cluster"
 )
 
@@ -21,6 +22,6 @@ type ClusterStatus interface {
 // NetworkManager provides methods to manage networks
 type NetworkManager interface {
 	GetNetwork(input string) (apitypes.NetworkResource, error)
-	GetNetworks() ([]apitypes.NetworkResource, error)
+	GetNetworks(filters.Args) ([]apitypes.NetworkResource, error)
 	RemoveNetwork(input string) error
 }

+ 23 - 5
daemon/cluster/networks.go

@@ -5,9 +5,11 @@ import (
 	"fmt"
 
 	apitypes "github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/network"
 	types "github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/daemon/cluster/convert"
+	internalnetwork "github.com/docker/docker/daemon/network"
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/runconfig"
 	swarmapi "github.com/docker/swarmkit/api"
@@ -16,16 +18,32 @@ import (
 )
 
 // GetNetworks returns all current cluster managed networks.
-func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) {
-	list, err := c.getNetworks(nil)
+func (c *Cluster) GetNetworks(filter filters.Args) ([]apitypes.NetworkResource, error) {
+	var f *swarmapi.ListNetworksRequest_Filters
+
+	if filter.Len() > 0 {
+		f = &swarmapi.ListNetworksRequest_Filters{}
+
+		if filter.Contains("name") {
+			f.Names = filter.Get("name")
+			f.NamePrefixes = filter.Get("name")
+		}
+
+		if filter.Contains("id") {
+			f.IDPrefixes = filter.Get("id")
+		}
+	}
+
+	list, err := c.getNetworks(f)
 	if err != nil {
 		return nil, err
 	}
-	removePredefinedNetworks(&list)
-	return list, nil
+	filterPredefinedNetworks(&list)
+
+	return internalnetwork.FilterNetworks(list, filter)
 }
 
-func removePredefinedNetworks(networks *[]apitypes.NetworkResource) {
+func filterPredefinedNetworks(networks *[]apitypes.NetworkResource) {
 	if networks == nil {
 		return
 	}

+ 211 - 5
daemon/network.go

@@ -12,6 +12,7 @@ import (
 
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/container"
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
@@ -26,6 +27,7 @@ import (
 	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/ipamapi"
 	"github.com/docker/libnetwork/netlabel"
+	"github.com/docker/libnetwork/networkdb"
 	"github.com/docker/libnetwork/options"
 	networktypes "github.com/docker/libnetwork/types"
 	"github.com/pkg/errors"
@@ -89,7 +91,7 @@ func (daemon *Daemon) FindNetwork(term string) (libnetwork.Network, error) {
 func (daemon *Daemon) GetNetworkByID(id string) (libnetwork.Network, error) {
 	c := daemon.netController
 	if c == nil {
-		return nil, libnetwork.ErrNoSuchNetwork(id)
+		return nil, errors.Wrap(libnetwork.ErrNoSuchNetwork(id), "netcontroller is nil")
 	}
 	return c.NetworkByID(id)
 }
@@ -507,7 +509,7 @@ func (daemon *Daemon) DeleteManagedNetwork(networkID string) error {
 func (daemon *Daemon) DeleteNetwork(networkID string) error {
 	n, err := daemon.GetNetworkByID(networkID)
 	if err != nil {
-		return err
+		return errors.Wrap(err, "could not find network by ID")
 	}
 	return daemon.deleteNetwork(n, false)
 }
@@ -560,7 +562,7 @@ func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error {
 	}
 
 	if err := nw.Delete(); err != nil {
-		return err
+		return errors.Wrap(err, "error while removing network")
 	}
 
 	// If this is not a configuration only network, we need to
@@ -576,8 +578,212 @@ func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error {
 }
 
 // GetNetworks returns a list of all networks
-func (daemon *Daemon) GetNetworks() []libnetwork.Network {
-	return daemon.getAllNetworks()
+func (daemon *Daemon) GetNetworks(filter filters.Args, config types.NetworkListConfig) ([]types.NetworkResource, error) {
+	networks := daemon.getAllNetworks()
+
+	list := make([]types.NetworkResource, 0, len(networks))
+	var idx map[string]libnetwork.Network
+	if config.Detailed {
+		idx = make(map[string]libnetwork.Network)
+	}
+
+	for _, n := range networks {
+		nr := buildNetworkResource(n)
+		list = append(list, nr)
+		if config.Detailed {
+			idx[nr.ID] = n
+		}
+	}
+
+	var err error
+	list, err = internalnetwork.FilterNetworks(list, filter)
+	if err != nil {
+		return nil, err
+	}
+
+	if config.Detailed {
+		for i, n := range list {
+			np := &n
+			buildDetailedNetworkResources(np, idx[n.ID], config.Verbose)
+			list[i] = *np
+		}
+	}
+
+	return list, nil
+}
+
+func buildNetworkResource(nw libnetwork.Network) types.NetworkResource {
+	r := types.NetworkResource{}
+	if nw == nil {
+		return r
+	}
+
+	info := nw.Info()
+	r.Name = nw.Name()
+	r.ID = nw.ID()
+	r.Created = info.Created()
+	r.Scope = info.Scope()
+	r.Driver = nw.Type()
+	r.EnableIPv6 = info.IPv6Enabled()
+	r.Internal = info.Internal()
+	r.Attachable = info.Attachable()
+	r.Ingress = info.Ingress()
+	r.Options = info.DriverOptions()
+	r.Containers = make(map[string]types.EndpointResource)
+	buildIpamResources(&r, info)
+	r.Labels = info.Labels()
+	r.ConfigOnly = info.ConfigOnly()
+
+	if cn := info.ConfigFrom(); cn != "" {
+		r.ConfigFrom = network.ConfigReference{Network: cn}
+	}
+
+	peers := info.Peers()
+	if len(peers) != 0 {
+		r.Peers = buildPeerInfoResources(peers)
+	}
+
+	return r
+}
+
+func buildDetailedNetworkResources(r *types.NetworkResource, nw libnetwork.Network, verbose bool) {
+	if nw == nil {
+		return
+	}
+
+	epl := nw.Endpoints()
+	for _, e := range epl {
+		ei := e.Info()
+		if ei == nil {
+			continue
+		}
+		sb := ei.Sandbox()
+		tmpID := e.ID()
+		key := "ep-" + tmpID
+		if sb != nil {
+			key = sb.ContainerID()
+		}
+
+		r.Containers[key] = buildEndpointResource(tmpID, e.Name(), ei)
+	}
+	if !verbose {
+		return
+	}
+	services := nw.Info().Services()
+	r.Services = make(map[string]network.ServiceInfo)
+	for name, service := range services {
+		tasks := []network.Task{}
+		for _, t := range service.Tasks {
+			tasks = append(tasks, network.Task{
+				Name:       t.Name,
+				EndpointID: t.EndpointID,
+				EndpointIP: t.EndpointIP,
+				Info:       t.Info,
+			})
+		}
+		r.Services[name] = network.ServiceInfo{
+			VIP:          service.VIP,
+			Ports:        service.Ports,
+			Tasks:        tasks,
+			LocalLBIndex: service.LocalLBIndex,
+		}
+	}
+}
+
+func buildPeerInfoResources(peers []networkdb.PeerInfo) []network.PeerInfo {
+	peerInfo := make([]network.PeerInfo, 0, len(peers))
+	for _, peer := range peers {
+		peerInfo = append(peerInfo, network.PeerInfo{
+			Name: peer.Name,
+			IP:   peer.IP,
+		})
+	}
+	return peerInfo
+}
+
+func buildIpamResources(r *types.NetworkResource, nwInfo libnetwork.NetworkInfo) {
+	id, opts, ipv4conf, ipv6conf := nwInfo.IpamConfig()
+
+	ipv4Info, ipv6Info := nwInfo.IpamInfo()
+
+	r.IPAM.Driver = id
+
+	r.IPAM.Options = opts
+
+	r.IPAM.Config = []network.IPAMConfig{}
+	for _, ip4 := range ipv4conf {
+		if ip4.PreferredPool == "" {
+			continue
+		}
+		iData := network.IPAMConfig{}
+		iData.Subnet = ip4.PreferredPool
+		iData.IPRange = ip4.SubPool
+		iData.Gateway = ip4.Gateway
+		iData.AuxAddress = ip4.AuxAddresses
+		r.IPAM.Config = append(r.IPAM.Config, iData)
+	}
+
+	if len(r.IPAM.Config) == 0 {
+		for _, ip4Info := range ipv4Info {
+			iData := network.IPAMConfig{}
+			iData.Subnet = ip4Info.IPAMData.Pool.String()
+			if ip4Info.IPAMData.Gateway != nil {
+				iData.Gateway = ip4Info.IPAMData.Gateway.IP.String()
+			}
+			r.IPAM.Config = append(r.IPAM.Config, iData)
+		}
+	}
+
+	hasIpv6Conf := false
+	for _, ip6 := range ipv6conf {
+		if ip6.PreferredPool == "" {
+			continue
+		}
+		hasIpv6Conf = true
+		iData := network.IPAMConfig{}
+		iData.Subnet = ip6.PreferredPool
+		iData.IPRange = ip6.SubPool
+		iData.Gateway = ip6.Gateway
+		iData.AuxAddress = ip6.AuxAddresses
+		r.IPAM.Config = append(r.IPAM.Config, iData)
+	}
+
+	if !hasIpv6Conf {
+		for _, ip6Info := range ipv6Info {
+			if ip6Info.IPAMData.Pool == nil {
+				continue
+			}
+			iData := network.IPAMConfig{}
+			iData.Subnet = ip6Info.IPAMData.Pool.String()
+			iData.Gateway = ip6Info.IPAMData.Gateway.String()
+			r.IPAM.Config = append(r.IPAM.Config, iData)
+		}
+	}
+}
+
+func buildEndpointResource(id string, name string, info libnetwork.EndpointInfo) types.EndpointResource {
+	er := types.EndpointResource{}
+
+	er.EndpointID = id
+	er.Name = name
+	ei := info
+	if ei == nil {
+		return er
+	}
+
+	if iface := ei.Iface(); iface != nil {
+		if mac := iface.MacAddress(); mac != nil {
+			er.MacAddress = mac.String()
+		}
+		if ip := iface.Address(); ip != nil && len(ip.IP) > 0 {
+			er.IPv4Address = ip.String()
+		}
+
+		if ipv6 := iface.AddressIPv6(); ipv6 != nil && len(ipv6.IP) > 0 {
+			er.IPv6Address = ipv6.String()
+		}
+	}
+	return er
 }
 
 // clearAttachableNetworks removes the attachable networks

+ 92 - 0
daemon/network/filter.go

@@ -0,0 +1,92 @@
+package network // import "github.com/docker/docker/daemon/network"
+
+import (
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/runconfig"
+	"github.com/pkg/errors"
+)
+
+// FilterNetworks filters network list according to user specified filter
+// and returns user chosen networks
+func FilterNetworks(nws []types.NetworkResource, filter filters.Args) ([]types.NetworkResource, error) {
+	// if filter is empty, return original network list
+	if filter.Len() == 0 {
+		return nws, nil
+	}
+
+	displayNet := nws[:0]
+	for _, nw := range nws {
+		if filter.Contains("driver") {
+			if !filter.ExactMatch("driver", nw.Driver) {
+				continue
+			}
+		}
+		if filter.Contains("name") {
+			if !filter.Match("name", nw.Name) {
+				continue
+			}
+		}
+		if filter.Contains("id") {
+			if !filter.Match("id", nw.ID) {
+				continue
+			}
+		}
+		if filter.Contains("label") {
+			if !filter.MatchKVList("label", nw.Labels) {
+				continue
+			}
+		}
+		if filter.Contains("scope") {
+			if !filter.ExactMatch("scope", nw.Scope) {
+				continue
+			}
+		}
+
+		if filter.Contains("idOrName") {
+			if !filter.Match("name", nw.Name) && !filter.Match("id", nw.Name) {
+				continue
+			}
+		}
+		displayNet = append(displayNet, nw)
+	}
+
+	if filter.Contains("type") {
+		typeNet := []types.NetworkResource{}
+		errFilter := filter.WalkValues("type", func(fval string) error {
+			passList, err := filterNetworkByType(displayNet, fval)
+			if err != nil {
+				return err
+			}
+			typeNet = append(typeNet, passList...)
+			return nil
+		})
+		if errFilter != nil {
+			return nil, errFilter
+		}
+		displayNet = typeNet
+	}
+
+	return displayNet, nil
+}
+
+func filterNetworkByType(nws []types.NetworkResource, netType string) ([]types.NetworkResource, error) {
+	retNws := []types.NetworkResource{}
+	switch netType {
+	case "builtin":
+		for _, nw := range nws {
+			if runconfig.IsPreDefinedNetwork(nw.Name) {
+				retNws = append(retNws, nw)
+			}
+		}
+	case "custom":
+		for _, nw := range nws {
+			if !runconfig.IsPreDefinedNetwork(nw.Name) {
+				retNws = append(retNws, nw)
+			}
+		}
+	default:
+		return nil, errors.Errorf("invalid filter: 'type'='%s'", netType)
+	}
+	return retNws, nil
+}

+ 36 - 22
api/server/router/network/filter_test.go → daemon/network/filter_test.go

@@ -1,6 +1,6 @@
 // +build !windows
 
-package network // import "github.com/docker/docker/api/server/router/network"
+package network // import "github.com/docker/docker/daemon/network"
 
 import (
 	"strings"
@@ -75,75 +75,89 @@ func TestFilterNetworks(t *testing.T) {
 		filter      filters.Args
 		resultCount int
 		err         string
+		name        string
 	}{
 		{
 			filter:      bridgeDriverFilters,
 			resultCount: 1,
 			err:         "",
+			name:        "bridge driver filters",
 		},
 		{
 			filter:      overlayDriverFilters,
 			resultCount: 1,
 			err:         "",
+			name:        "overlay driver filters",
 		},
 		{
 			filter:      nonameDriverFilters,
 			resultCount: 0,
 			err:         "",
+			name:        "no name driver filters",
 		},
 		{
 			filter:      customDriverFilters,
 			resultCount: 3,
 			err:         "",
+			name:        "custom driver filters",
 		},
 		{
 			filter:      builtinDriverFilters,
 			resultCount: 3,
 			err:         "",
+			name:        "builtin driver filters",
 		},
 		{
 			filter:      invalidDriverFilters,
 			resultCount: 0,
-			err:         "Invalid filter: 'type'='invalid'",
+			err:         "invalid filter: 'type'='invalid'",
+			name:        "invalid driver filters",
 		},
 		{
 			filter:      localScopeFilters,
 			resultCount: 4,
 			err:         "",
+			name:        "local scope filters",
 		},
 		{
 			filter:      swarmScopeFilters,
 			resultCount: 1,
 			err:         "",
+			name:        "swarm scope filters",
 		},
 		{
 			filter:      globalScopeFilters,
 			resultCount: 1,
 			err:         "",
+			name:        "global scope filters",
 		},
 	}
 
 	for _, testCase := range testCases {
-		result, err := filterNetworks(networks, testCase.filter)
-		if testCase.err != "" {
-			if err == nil {
-				t.Fatalf("expect error '%s', got no error", testCase.err)
-
-			} else if !strings.Contains(err.Error(), testCase.err) {
-				t.Fatalf("expect error '%s', got '%s'", testCase.err, err)
-			}
-		} else {
-			if err != nil {
-				t.Fatalf("expect no error, got error '%s'", err)
-			}
-			// Make sure result is not nil
-			if result == nil {
-				t.Fatal("filterNetworks should not return nil")
-			}
-
-			if len(result) != testCase.resultCount {
-				t.Fatalf("expect '%d' networks, got '%d' networks", testCase.resultCount, len(result))
+		t.Run(testCase.name, func(t *testing.T) {
+			ls := make([]types.NetworkResource, 0, len(networks))
+			ls = append(ls, networks...)
+			result, err := FilterNetworks(ls, testCase.filter)
+			if testCase.err != "" {
+				if err == nil {
+					t.Fatalf("expect error '%s', got no error", testCase.err)
+
+				} else if !strings.Contains(err.Error(), testCase.err) {
+					t.Fatalf("expect error '%s', got '%s'", testCase.err, err)
+				}
+			} else {
+				if err != nil {
+					t.Fatalf("expect no error, got error '%s'", err)
+				}
+				// Make sure result is not nil
+				if result == nil {
+					t.Fatal("filterNetworks should not return nil")
+				}
+
+				if len(result) != testCase.resultCount {
+					t.Fatalf("expect '%d' networks, got '%d' networks", testCase.resultCount, len(result))
+				}
 			}
-		}
+		})
 	}
 }

+ 1 - 1
daemon/prune.go

@@ -141,7 +141,7 @@ func (daemon *Daemon) clusterNetworksPrune(ctx context.Context, pruneFilters fil
 		return rep, nil
 	}
 
-	networks, err := cluster.GetNetworks()
+	networks, err := cluster.GetNetworks(pruneFilters)
 	if err != nil {
 		return rep, err
 	}