Merge pull request #50878 from corhere/network-inspect-concrete-type

api/types/network: separate Summary from Inspect
This commit is contained in:
Sebastiaan van Stijn
2025-09-04 21:36:00 +02:00
committed by GitHub
11 changed files with 148 additions and 82 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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()),

View File

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

View File

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

View File

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

View File

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

View File

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