Преглед изворни кода

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
 	}