diff --git a/api/types/network/network.go b/api/types/network/network.go index 4d9272d8c5..b8694add23 100644 --- a/api/types/network/network.go +++ b/api/types/network/network.go @@ -38,32 +38,36 @@ type CreateRequest struct { CheckDuplicate *bool `json:",omitempty"` } +type Network struct { + Name string // Name is the name of the network + ID string `json:"Id"` // ID uniquely identifies a network on a single machine + Created time.Time // Created is the time the network created + Scope string // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level) + Driver string // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`) + EnableIPv4 bool // EnableIPv4 represents whether IPv4 is enabled + EnableIPv6 bool // EnableIPv6 represents whether IPv6 is enabled + IPAM IPAM // IPAM is the network's IP Address Management + Internal bool // Internal represents if the network is used internal only + Attachable bool // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode. + Ingress bool // Ingress indicates the network is providing the routing-mesh for the swarm cluster. + ConfigFrom ConfigReference // ConfigFrom specifies the source which will provide the configuration for this network. + ConfigOnly bool // ConfigOnly networks are place-holder networks for network configurations to be used by other networks. ConfigOnly networks cannot be used directly to run containers or services. + Options map[string]string // Options holds the network specific options to use for when creating the network + Labels map[string]string // Labels holds metadata specific to the network being created + Peers []PeerInfo `json:",omitempty"` // List of peer nodes for an overlay network +} + // Inspect is the body of the "get network" http response message. type Inspect struct { - Name string // Name is the name of the network - ID string `json:"Id"` // ID uniquely identifies a network on a single machine - Created time.Time // Created is the time the network created - Scope string // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level) - Driver string // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`) - EnableIPv4 bool // EnableIPv4 represents whether IPv4 is enabled - EnableIPv6 bool // EnableIPv6 represents whether IPv6 is enabled - IPAM IPAM // IPAM is the network's IP Address Management - Internal bool // Internal represents if the network is used internal only - Attachable bool // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode. - Ingress bool // Ingress indicates the network is providing the routing-mesh for the swarm cluster. - ConfigFrom ConfigReference // ConfigFrom specifies the source which will provide the configuration for this network. - ConfigOnly bool // ConfigOnly networks are place-holder networks for network configurations to be used by other networks. ConfigOnly networks cannot be used directly to run containers or services. + Network Containers map[string]EndpointResource // Containers contains endpoints belonging to the network - Options map[string]string // Options holds the network specific options to use for when creating the network - Labels map[string]string // Labels holds metadata specific to the network being created - Peers []PeerInfo `json:",omitempty"` // List of peer nodes for an overlay network Services map[string]ServiceInfo `json:",omitempty"` } -// Summary is used as response when listing networks. It currently is an alias -// for [Inspect], but may diverge in the future, as not all information may -// be included when listing networks. -type Summary = Inspect +// Summary is used as response when listing networks. +type Summary struct { + Network +} // Address represents an IP address type Address struct { diff --git a/client/network_inspect_test.go b/client/network_inspect_test.go index 464d70a871..6894091371 100644 --- a/client/network_inspect_test.go +++ b/client/network_inspect_test.go @@ -46,12 +46,12 @@ func TestNetworkInspect(t *testing.T) { "web": {}, } content, err = json.Marshal(network.Inspect{ - Name: "mynetwork", + Network: network.Network{Name: "mynetwork"}, Services: s, }) } else { content, err = json.Marshal(network.Inspect{ - Name: "mynetwork", + Network: network.Network{Name: "mynetwork"}, }) } if err != nil { diff --git a/client/network_list_test.go b/client/network_list_test.go index 96e10047a2..5d99edbfd9 100644 --- a/client/network_list_test.go +++ b/client/network_list_test.go @@ -74,8 +74,10 @@ func TestNetworkList(t *testing.T) { } content, err := json.Marshal([]network.Summary{ { - Name: "network", - Driver: "bridge", + Network: network.Network{ + Name: "network", + Driver: "bridge", + }, }, }) if err != nil { diff --git a/daemon/cluster/convert/network.go b/daemon/cluster/convert/network.go index b3edf1f696..4b041cee6e 100644 --- a/daemon/cluster/convert/network.go +++ b/daemon/cluster/convert/network.go @@ -138,7 +138,7 @@ func swarmPortConfigToAPIPortConfig(portConfig *swarmapi.PortConfig) types.PortC } // BasicNetworkFromGRPC converts a grpc Network to a NetworkResource. -func BasicNetworkFromGRPC(n swarmapi.Network) network.Inspect { +func BasicNetworkFromGRPC(n swarmapi.Network) network.Network { spec := n.Spec var ipam network.IPAM if n.IPAM != nil { @@ -157,7 +157,7 @@ func BasicNetworkFromGRPC(n swarmapi.Network) network.Inspect { } } - nr := network.Inspect{ + nr := network.Network{ ID: n.ID, Name: n.Spec.Annotations.Name, Scope: scope.Swarm, diff --git a/daemon/cluster/networks.go b/daemon/cluster/networks.go index f8c9e01fbb..d3f6ebe3a9 100644 --- a/daemon/cluster/networks.go +++ b/daemon/cluster/networks.go @@ -36,7 +36,28 @@ func (c *Cluster) GetNetworks(filter networkSettings.Filter) ([]network.Inspect, continue } if filter.Matches(convert.FilterNetwork{N: n}) { - filtered = append(filtered, convert.BasicNetworkFromGRPC(*n)) + filtered = append(filtered, network.Inspect{ + Network: convert.BasicNetworkFromGRPC(*n), + Containers: map[string]network.EndpointResource{}, + }) + } + } + + return filtered, nil +} + +func (c *Cluster) GetNetworkSummaries(filter networkSettings.Filter) ([]network.Summary, error) { + list, err := c.listNetworks(context.TODO(), nil) + if err != nil { + return nil, err + } + var filtered []network.Summary + for _, n := range list { + if n.Spec.Annotations.Labels["com.docker.swarm.predefined"] == "true" { + continue + } + if filter.Matches(convert.FilterNetwork{N: n}) { + filtered = append(filtered, network.Summary{Network: convert.BasicNetworkFromGRPC(*n)}) } } @@ -70,12 +91,15 @@ func (c *Cluster) GetNetwork(input string) (network.Inspect, error) { }); err != nil { return network.Inspect{}, err } - return convert.BasicNetworkFromGRPC(*nw), nil + return network.Inspect{ + Network: convert.BasicNetworkFromGRPC(*nw), + Containers: map[string]network.EndpointResource{}, + }, nil } // GetNetworksByName returns cluster managed networks by name. // It is ok to have multiple networks here. #18864 -func (c *Cluster) GetNetworksByName(name string) ([]network.Inspect, error) { +func (c *Cluster) GetNetworksByName(name string) ([]network.Network, error) { // Note that swarmapi.GetNetworkRequest.Name is not functional. // So we cannot just use that with c.GetNetwork. list, err := c.listNetworks(context.TODO(), &swarmapi.ListNetworksRequest_Filters{ @@ -84,7 +108,7 @@ func (c *Cluster) GetNetworksByName(name string) ([]network.Inspect, error) { if err != nil { return nil, err } - nr := make([]network.Inspect, len(list)) + nr := make([]network.Network, len(list)) for i, n := range list { nr[i] = convert.BasicNetworkFromGRPC(*n) } diff --git a/daemon/network.go b/daemon/network.go index 2a6b82dfae..a3c4d3c6f8 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -595,13 +595,26 @@ func (daemon *Daemon) GetNetworks(filter network.Filter, config backend.NetworkL networks := make([]networktypes.Inspect, 0, len(allNetworks)) for _, n := range allNetworks { if filter.Matches(n) { - nr := buildNetworkResource(n) - if config.Detailed { - nr.Containers = buildContainerAttachments(n) - if config.Verbose { - nr.Services = buildServiceAttachments(n) - } + nr := networktypes.Inspect{ + Network: buildNetworkResource(n), + Containers: buildContainerAttachments(n), } + if config.WithServices { + nr.Services = buildServiceAttachments(n) + } + networks = append(networks, nr) + } + } + + return networks, nil +} + +func (daemon *Daemon) GetNetworkSummaries(filter network.Filter) ([]networktypes.Summary, error) { + allNetworks := daemon.getAllNetworks() + networks := make([]networktypes.Summary, 0, len(allNetworks)) + for _, n := range allNetworks { + if filter.Matches(n) { + nr := networktypes.Summary{Network: buildNetworkResource(n)} networks = append(networks, nr) } } @@ -611,12 +624,12 @@ func (daemon *Daemon) GetNetworks(filter network.Filter, config backend.NetworkL // buildNetworkResource builds a [types.NetworkResource] from the given // [libnetwork.Network], to be returned by the API. -func buildNetworkResource(nw *libnetwork.Network) networktypes.Inspect { +func buildNetworkResource(nw *libnetwork.Network) networktypes.Network { if nw == nil { - return networktypes.Inspect{} + return networktypes.Network{} } - return networktypes.Inspect{ + return networktypes.Network{ Name: nw.Name(), ID: nw.ID(), Created: nw.Created(), @@ -630,7 +643,6 @@ func buildNetworkResource(nw *libnetwork.Network) networktypes.Inspect { Ingress: nw.Ingress(), ConfigFrom: networktypes.ConfigReference{Network: nw.ConfigFrom()}, ConfigOnly: nw.ConfigOnly(), - Containers: map[string]networktypes.EndpointResource{}, Options: nw.DriverOptions(), Labels: nw.Labels(), Peers: buildPeerInfoResources(nw.Peers()), diff --git a/daemon/server/backend/backend.go b/daemon/server/backend/backend.go index 4fae874acf..06b88de6fd 100644 --- a/daemon/server/backend/backend.go +++ b/daemon/server/backend/backend.go @@ -172,7 +172,5 @@ type PluginDisableConfig struct { // 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 + WithServices bool } diff --git a/daemon/server/router/network/backend.go b/daemon/server/router/network/backend.go index 8c47442523..4911cba1f2 100644 --- a/daemon/server/router/network/backend.go +++ b/daemon/server/router/network/backend.go @@ -13,6 +13,7 @@ import ( // to provide network specific functionality. type Backend interface { GetNetworks(dnetwork.Filter, backend.NetworkListConfig) ([]network.Inspect, error) + GetNetworkSummaries(dnetwork.Filter) ([]network.Summary, error) CreateNetwork(ctx context.Context, nc network.CreateRequest) (*network.CreateResponse, error) ConnectContainerToNetwork(ctx context.Context, containerName, networkName string, endpointConfig *network.EndpointSettings) error DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error @@ -24,8 +25,9 @@ type Backend interface { // to provide cluster network specific functionality. type ClusterBackend interface { GetNetworks(dnetwork.Filter) ([]network.Inspect, error) + GetNetworkSummaries(dnetwork.Filter) ([]network.Summary, error) GetNetwork(name string) (network.Inspect, error) - GetNetworksByName(name string) ([]network.Inspect, error) + GetNetworksByName(name string) ([]network.Network, error) CreateNetwork(nc network.CreateRequest) (string, error) RemoveNetwork(name string) error } diff --git a/daemon/server/router/network/network_routes.go b/daemon/server/router/network/network_routes.go index 14e029df78..68ce014801 100644 --- a/daemon/server/router/network/network_routes.go +++ b/daemon/server/router/network/network_routes.go @@ -34,19 +34,32 @@ func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWrit return err } - var list []network.Summary - 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 - localNetworks, err := n.backend.GetNetworks(filter, backend.NetworkListConfig{Detailed: versions.LessThan(httputils.VersionFromContext(ctx), "1.28")}) - if err != nil { - return err + if versions.LessThan(httputils.VersionFromContext(ctx), "1.28") { + list, _ := n.cluster.GetNetworks(filter) + var idx map[string]bool + if len(list) > 0 { + idx = make(map[string]bool, len(list)) + for _, n := range list { + idx[n.ID] = true + } + } + + localNetworks, err := n.backend.GetNetworks(filter, backend.NetworkListConfig{WithServices: false}) + if err != nil { + return err + } + for _, n := range localNetworks { + if !idx[n.ID] { + list = append(list, n) + } + } + if list == nil { + list = []network.Inspect{} + } + return httputils.WriteJSON(w, http.StatusOK, list) } + list, _ := n.cluster.GetNetworkSummaries(filter) var idx map[string]bool if len(list) > 0 { idx = make(map[string]bool, len(list)) @@ -54,11 +67,18 @@ func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWrit idx[n.ID] = true } } + + // Combine the network list returned by Docker daemon if it is not already + // returned by the cluster manager + localNetworks, err := n.backend.GetNetworkSummaries(filter) + if err != nil { + return err + } + for _, n := range localNetworks { - if idx[n.ID] { - continue + if !idx[n.ID] { + list = append(list, n) } - list = append(list, n) } if list == nil { @@ -126,7 +146,7 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r } filter.IDAlsoMatchesName = true - networks, _ := n.backend.GetNetworks(filter, backend.NetworkListConfig{Detailed: true, Verbose: verbose}) + networks, _ := n.backend.GetNetworks(filter, backend.NetworkListConfig{WithServices: verbose}) for _, nw := range networks { if nw.ID == term { return httputils.WriteJSON(w, http.StatusOK, nw) @@ -150,7 +170,7 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r // return the network. Skipped using isMatchingScope because it is true if the scope // is not set which would be case if the client API v1.30 if strings.HasPrefix(nwk.ID, term) || networkScope == scope.Swarm { - // If we have a previous match "backend", return it, we need verbose when enabled + // If we have a previous match "backend", return it // ex: overlay/partial_ID or name/swarm_scope if nwv, ok := listByPartialID[nwk.ID]; ok { nwk = nwv @@ -336,7 +356,7 @@ func (n *networkRouter) findUniqueNetwork(term string) (network.Inspect, error) } filter.IDAlsoMatchesName = true - networks, _ := n.backend.GetNetworks(filter, backend.NetworkListConfig{Detailed: true}) + networks, _ := n.backend.GetNetworks(filter, backend.NetworkListConfig{}) for _, nw := range networks { if nw.ID == term { return nw, nil diff --git a/integration/network/delete_test.go b/integration/network/delete_test.go index d3403da1cf..fc95ca67ac 100644 --- a/integration/network/delete_test.go +++ b/integration/network/delete_test.go @@ -12,7 +12,7 @@ import ( "gotest.tools/v3/skip" ) -func containsNetwork(nws []networktypes.Inspect, networkID string) bool { +func containsNetwork(nws []networktypes.Summary, networkID string) bool { for _, n := range nws { if n.ID == networkID { return true diff --git a/vendor/github.com/moby/moby/api/types/network/network.go b/vendor/github.com/moby/moby/api/types/network/network.go index 4d9272d8c5..b8694add23 100644 --- a/vendor/github.com/moby/moby/api/types/network/network.go +++ b/vendor/github.com/moby/moby/api/types/network/network.go @@ -38,32 +38,36 @@ type CreateRequest struct { CheckDuplicate *bool `json:",omitempty"` } +type Network struct { + Name string // Name is the name of the network + ID string `json:"Id"` // ID uniquely identifies a network on a single machine + Created time.Time // Created is the time the network created + Scope string // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level) + Driver string // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`) + EnableIPv4 bool // EnableIPv4 represents whether IPv4 is enabled + EnableIPv6 bool // EnableIPv6 represents whether IPv6 is enabled + IPAM IPAM // IPAM is the network's IP Address Management + Internal bool // Internal represents if the network is used internal only + Attachable bool // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode. + Ingress bool // Ingress indicates the network is providing the routing-mesh for the swarm cluster. + ConfigFrom ConfigReference // ConfigFrom specifies the source which will provide the configuration for this network. + ConfigOnly bool // ConfigOnly networks are place-holder networks for network configurations to be used by other networks. ConfigOnly networks cannot be used directly to run containers or services. + Options map[string]string // Options holds the network specific options to use for when creating the network + Labels map[string]string // Labels holds metadata specific to the network being created + Peers []PeerInfo `json:",omitempty"` // List of peer nodes for an overlay network +} + // Inspect is the body of the "get network" http response message. type Inspect struct { - Name string // Name is the name of the network - ID string `json:"Id"` // ID uniquely identifies a network on a single machine - Created time.Time // Created is the time the network created - Scope string // Scope describes the level at which the network exists (e.g. `swarm` for cluster-wide or `local` for machine level) - Driver string // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`) - EnableIPv4 bool // EnableIPv4 represents whether IPv4 is enabled - EnableIPv6 bool // EnableIPv6 represents whether IPv6 is enabled - IPAM IPAM // IPAM is the network's IP Address Management - Internal bool // Internal represents if the network is used internal only - Attachable bool // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode. - Ingress bool // Ingress indicates the network is providing the routing-mesh for the swarm cluster. - ConfigFrom ConfigReference // ConfigFrom specifies the source which will provide the configuration for this network. - ConfigOnly bool // ConfigOnly networks are place-holder networks for network configurations to be used by other networks. ConfigOnly networks cannot be used directly to run containers or services. + Network Containers map[string]EndpointResource // Containers contains endpoints belonging to the network - Options map[string]string // Options holds the network specific options to use for when creating the network - Labels map[string]string // Labels holds metadata specific to the network being created - Peers []PeerInfo `json:",omitempty"` // List of peer nodes for an overlay network Services map[string]ServiceInfo `json:",omitempty"` } -// Summary is used as response when listing networks. It currently is an alias -// for [Inspect], but may diverge in the future, as not all information may -// be included when listing networks. -type Summary = Inspect +// Summary is used as response when listing networks. +type Summary struct { + Network +} // Address represents an IP address type Address struct {