1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

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>
This commit is contained in:
Brian Goff 2018-05-23 10:03:02 -04:00 committed by Brian Goff
parent 68e25cf526
commit c0bc14e8dd
11 changed files with 442 additions and 371 deletions

View file

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

View file

@ -1,93 +1 @@
package network // import "github.com/docker/docker/api/server/router/network" 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
}

View file

@ -15,70 +15,54 @@ import (
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/libnetwork" "github.com/docker/libnetwork"
netconst "github.com/docker/libnetwork/datastore" netconst "github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/networkdb"
"github.com/pkg/errors" "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 { 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 { if err := httputils.ParseForm(r); err != nil {
return err return err
} }
filter := r.Form.Get("filters") filter, err := filters.FromJSON(r.Form.Get("filters"))
netFilters, err := filters.FromJSON(filter)
if err != nil { if err != nil {
return err return err
} }
if err := netFilters.Validate(acceptedNetworkFilters); err != nil { if err := network.ValidateFilters(filter); err != nil {
return err return err
} }
list := []types.NetworkResource{} var list []types.NetworkResource
nr, err := n.cluster.GetNetworks(filter)
if nr, err := n.cluster.GetNetworks(); err == nil { if err == nil {
list = append(list, nr...) list = nr
} }
// Combine the network list returned by Docker daemon if it is not already // Combine the network list returned by Docker daemon if it is not already
// returned by the cluster manager // returned by the cluster manager
SKIP: localNetworks, err := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: versions.LessThan(httputils.VersionFromContext(ctx), "1.28")})
for _, nw := range n.backend.GetNetworks() {
for _, nl := range list {
if nl.ID == nw.ID() {
continue SKIP
}
}
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)
}
list = append(list, *nr)
}
list, err = filterNetworks(list, netFilters)
if err != nil { if err != nil {
return err return err
} }
var idx map[string]bool
if len(list) > 0 {
idx = make(map[string]bool, len(list))
for _, n := range list {
idx[n.ID] = true
}
}
for _, n := range localNetworks {
if idx[n.ID] {
continue
}
list = append(list, n)
}
if list == nil {
list = []types.NetworkResource{}
}
return httputils.WriteJSON(w, http.StatusOK, list) 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") 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?
@ -139,20 +116,26 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
listByFullName := map[string]types.NetworkResource{} listByFullName := map[string]types.NetworkResource{}
listByPartialID := 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 { for _, network := range nw {
if network.ID() == term && isMatchingScope(network.Info().Scope(), scope) { if network.ID == term {
return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network, verbose)) 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 // 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] = 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 // 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] = 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 { for _, network := range nr {
if network.ID == term && isMatchingScope(network.Scope, scope) { if network.ID == term {
return httputils.WriteJSON(w, http.StatusOK, network) 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 // 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)
@ -187,7 +170,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) && isMatchingScope(network.Scope, scope) { if strings.HasPrefix(network.ID, term) {
// 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)
@ -327,182 +310,6 @@ func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter
return nil 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 { 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 { if err := httputils.ParseForm(r); err != nil {
return err return err
@ -532,25 +339,25 @@ func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, e
listByFullName := map[string]types.NetworkResource{} listByFullName := map[string]types.NetworkResource{}
listByPartialID := 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 { for _, network := range nw {
if network.ID() == term { if network.ID == term {
return *n.buildDetailedNetworkResources(network, false), nil 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 // 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, 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 // 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, false) listByPartialID[network.ID] = network
} }
} }
nr, _ := n.cluster.GetNetworks() nr, _ := n.cluster.GetNetworks(filter)
for _, network := range nr { for _, network := range nr {
if network.ID == term { if network.ID == term {
return network, nil return network, nil

View file

@ -55,3 +55,10 @@ type PluginEnableConfig struct {
type PluginDisableConfig struct { type PluginDisableConfig struct {
ForceDisable bool 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
}

View file

@ -1,4 +1,8 @@
package network // import "github.com/docker/docker/api/types/network" 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 // Address represents an IP address
type Address struct { type Address struct {
@ -106,3 +110,17 @@ type NetworkingConfig struct {
type ConfigReference struct { type ConfigReference struct {
Network string 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))
}

View file

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

View file

@ -5,9 +5,11 @@ import (
"fmt" "fmt"
apitypes "github.com/docker/docker/api/types" apitypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
types "github.com/docker/docker/api/types/swarm" types "github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/daemon/cluster/convert" "github.com/docker/docker/daemon/cluster/convert"
internalnetwork "github.com/docker/docker/daemon/network"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/runconfig" "github.com/docker/docker/runconfig"
swarmapi "github.com/docker/swarmkit/api" swarmapi "github.com/docker/swarmkit/api"
@ -16,16 +18,32 @@ import (
) )
// GetNetworks returns all current cluster managed networks. // GetNetworks returns all current cluster managed networks.
func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) { func (c *Cluster) GetNetworks(filter filters.Args) ([]apitypes.NetworkResource, error) {
list, err := c.getNetworks(nil) 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 { if err != nil {
return nil, err return nil, err
} }
removePredefinedNetworks(&list) filterPredefinedNetworks(&list)
return list, nil
return internalnetwork.FilterNetworks(list, filter)
} }
func removePredefinedNetworks(networks *[]apitypes.NetworkResource) { func filterPredefinedNetworks(networks *[]apitypes.NetworkResource) {
if networks == nil { if networks == nil {
return return
} }

View file

@ -12,6 +12,7 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container" 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/api/types/network"
"github.com/docker/docker/container" "github.com/docker/docker/container"
clustertypes "github.com/docker/docker/daemon/cluster/provider" clustertypes "github.com/docker/docker/daemon/cluster/provider"
@ -26,6 +27,7 @@ import (
"github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/ipamapi" "github.com/docker/libnetwork/ipamapi"
"github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/networkdb"
"github.com/docker/libnetwork/options" "github.com/docker/libnetwork/options"
networktypes "github.com/docker/libnetwork/types" networktypes "github.com/docker/libnetwork/types"
"github.com/pkg/errors" "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) { func (daemon *Daemon) GetNetworkByID(id string) (libnetwork.Network, error) {
c := daemon.netController c := daemon.netController
if c == nil { if c == nil {
return nil, libnetwork.ErrNoSuchNetwork(id) return nil, errors.Wrap(libnetwork.ErrNoSuchNetwork(id), "netcontroller is nil")
} }
return c.NetworkByID(id) return c.NetworkByID(id)
} }
@ -507,7 +509,7 @@ func (daemon *Daemon) DeleteManagedNetwork(networkID string) error {
func (daemon *Daemon) DeleteNetwork(networkID string) error { func (daemon *Daemon) DeleteNetwork(networkID string) error {
n, err := daemon.GetNetworkByID(networkID) n, err := daemon.GetNetworkByID(networkID)
if err != nil { if err != nil {
return err return errors.Wrap(err, "could not find network by ID")
} }
return daemon.deleteNetwork(n, false) 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 { 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 // 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 // GetNetworks returns a list of all networks
func (daemon *Daemon) GetNetworks() []libnetwork.Network { func (daemon *Daemon) GetNetworks(filter filters.Args, config types.NetworkListConfig) ([]types.NetworkResource, error) {
return daemon.getAllNetworks() 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 // clearAttachableNetworks removes the attachable networks

92
daemon/network/filter.go Normal file
View file

@ -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
}

View file

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

View file

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