From d86ac0bd5dfe4ad6c2809f097a95ff28fe65397c Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Wed, 23 May 2018 10:03:02 -0400 Subject: [PATCH] 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 Upstream-commit: c0bc14e8dd5a31f5edc804e9a1347bb3eb44483e Component: engine --- .../api/server/router/network/backend.go | 4 +- .../api/server/router/network/filter.go | 92 ------ .../server/router/network/network_routes.go | 297 +++--------------- components/engine/api/types/configs.go | 7 + .../engine/api/types/network/network.go | 18 ++ components/engine/daemon/cluster.go | 3 +- components/engine/daemon/cluster/networks.go | 28 +- components/engine/daemon/network.go | 216 ++++++++++++- components/engine/daemon/network/filter.go | 92 ++++++ .../router => daemon}/network/filter_test.go | 54 ++-- components/engine/daemon/prune.go | 2 +- 11 files changed, 442 insertions(+), 371 deletions(-) create mode 100644 components/engine/daemon/network/filter.go rename components/engine/{api/server/router => daemon}/network/filter_test.go (64%) diff --git a/components/engine/api/server/router/network/backend.go b/components/engine/api/server/router/network/backend.go index 1bab353a5a..42eb950ed8 100644 --- a/components/engine/api/server/router/network/backend.go +++ b/components/engine/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) diff --git a/components/engine/api/server/router/network/filter.go b/components/engine/api/server/router/network/filter.go index 02683e8005..804be8024a 100644 --- a/components/engine/api/server/router/network/filter.go +++ b/components/engine/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 -} diff --git a/components/engine/api/server/router/network/network_routes.go b/components/engine/api/server/router/network/network_routes.go index 0248662a49..8b9382cb45 100644 --- a/components/engine/api/server/router/network/network_routes.go +++ b/components/engine/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 - } - } - - 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) + localNetworks, err := n.backend.GetNetworks(filter, types.NetworkListConfig{Detailed: versions.LessThan(httputils.VersionFromContext(ctx), "1.28")}) if err != nil { 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) } @@ -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 diff --git a/components/engine/api/types/configs.go b/components/engine/api/types/configs.go index f6537a27f2..178e911a7a 100644 --- a/components/engine/api/types/configs.go +++ b/components/engine/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 +} diff --git a/components/engine/api/types/network/network.go b/components/engine/api/types/network/network.go index 761d0b34f2..ccb448f23a 100644 --- a/components/engine/api/types/network/network.go +++ b/components/engine/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)) +} diff --git a/components/engine/daemon/cluster.go b/components/engine/daemon/cluster.go index b5ac6c4856..62eb2a2e59 100644 --- a/components/engine/daemon/cluster.go +++ b/components/engine/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 } diff --git a/components/engine/daemon/cluster/networks.go b/components/engine/daemon/cluster/networks.go index b8e31baa11..bcda316ca2 100644 --- a/components/engine/daemon/cluster/networks.go +++ b/components/engine/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 } diff --git a/components/engine/daemon/network.go b/components/engine/daemon/network.go index 4263409be8..b0c20e1c8c 100644 --- a/components/engine/daemon/network.go +++ b/components/engine/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 diff --git a/components/engine/daemon/network/filter.go b/components/engine/daemon/network/filter.go new file mode 100644 index 0000000000..01a7d2dd81 --- /dev/null +++ b/components/engine/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 +} diff --git a/components/engine/api/server/router/network/filter_test.go b/components/engine/daemon/network/filter_test.go similarity index 64% rename from components/engine/api/server/router/network/filter_test.go rename to components/engine/daemon/network/filter_test.go index 8a84fd16fe..d3180a05a9 100644 --- a/components/engine/api/server/router/network/filter_test.go +++ b/components/engine/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) + 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") - } + } 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)) + if len(result) != testCase.resultCount { + t.Fatalf("expect '%d' networks, got '%d' networks", testCase.resultCount, len(result)) + } } - } + }) } } diff --git a/components/engine/daemon/prune.go b/components/engine/daemon/prune.go index b690f2e552..b1bcd5b79c 100644 --- a/components/engine/daemon/prune.go +++ b/components/engine/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 }