Browse code

client: NetworkInspect: wrap result and remove NetworkInspectWithRaw

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Sebastiaan van Stijn authored on 2025/10/22 00:26:24
Showing 16 changed files
... ...
@@ -130,8 +130,7 @@ type NetworkAPIClient interface {
130 130
 	NetworkConnect(ctx context.Context, network, container string, config *network.EndpointSettings) error
131 131
 	NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (network.CreateResponse, error)
132 132
 	NetworkDisconnect(ctx context.Context, network, container string, force bool) error
133
-	NetworkInspect(ctx context.Context, network string, options NetworkInspectOptions) (network.Inspect, error)
134
-	NetworkInspectWithRaw(ctx context.Context, network string, options NetworkInspectOptions) (network.Inspect, []byte, error)
133
+	NetworkInspect(ctx context.Context, network string, options NetworkInspectOptions) (NetworkInspectResult, error)
135 134
 	NetworkList(ctx context.Context, options NetworkListOptions) ([]network.Summary, error)
136 135
 	NetworkRemove(ctx context.Context, network string) error
137 136
 	NetworksPrune(ctx context.Context, opts NetworkPruneOptions) (NetworkPruneResult, error)
... ...
@@ -1,26 +1,23 @@
1 1
 package client
2 2
 
3 3
 import (
4
-	"bytes"
5 4
 	"context"
6
-	"encoding/json"
7
-	"io"
8 5
 	"net/url"
9 6
 
10 7
 	"github.com/moby/moby/api/types/network"
11 8
 )
12 9
 
13
-// NetworkInspect returns the information for a specific network configured in the docker host.
14
-func (cli *Client) NetworkInspect(ctx context.Context, networkID string, options NetworkInspectOptions) (network.Inspect, error) {
15
-	networkResource, _, err := cli.NetworkInspectWithRaw(ctx, networkID, options)
16
-	return networkResource, err
10
+// NetworkInspectResult contains the result of a network inspection.
11
+type NetworkInspectResult struct {
12
+	Network network.Inspect
13
+	Raw     []byte
17 14
 }
18 15
 
19
-// NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation.
20
-func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, options NetworkInspectOptions) (network.Inspect, []byte, error) {
16
+// NetworkInspect returns the information for a specific network configured in the docker host.
17
+func (cli *Client) NetworkInspect(ctx context.Context, networkID string, options NetworkInspectOptions) (NetworkInspectResult, error) {
21 18
 	networkID, err := trimID("network", networkID)
22 19
 	if err != nil {
23
-		return network.Inspect{}, nil, err
20
+		return NetworkInspectResult{}, err
24 21
 	}
25 22
 	query := url.Values{}
26 23
 	if options.Verbose {
... ...
@@ -33,15 +30,10 @@ func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string,
33 33
 	resp, err := cli.get(ctx, "/networks/"+networkID, query, nil)
34 34
 	defer ensureReaderClosed(resp)
35 35
 	if err != nil {
36
-		return network.Inspect{}, nil, err
37
-	}
38
-
39
-	raw, err := io.ReadAll(resp.Body)
40
-	if err != nil {
41
-		return network.Inspect{}, nil, err
36
+		return NetworkInspectResult{}, err
42 37
 	}
43 38
 
44
-	var nw network.Inspect
45
-	err = json.NewDecoder(bytes.NewReader(raw)).Decode(&nw)
46
-	return nw, raw, err
39
+	var out NetworkInspectResult
40
+	out.Raw, err = decodeWithRaw(resp, &out.Network)
41
+	return out, err
47 42
 }
... ...
@@ -77,13 +77,13 @@ func TestNetworkInspect(t *testing.T) {
77 77
 	t.Run("no options", func(t *testing.T) {
78 78
 		r, err := client.NetworkInspect(context.Background(), "network_id", NetworkInspectOptions{})
79 79
 		assert.NilError(t, err)
80
-		assert.Check(t, is.Equal(r.Name, "mynetwork"))
80
+		assert.Check(t, is.Equal(r.Network.Name, "mynetwork"))
81 81
 	})
82 82
 	t.Run("verbose", func(t *testing.T) {
83 83
 		r, err := client.NetworkInspect(context.Background(), "network_id", NetworkInspectOptions{Verbose: true})
84 84
 		assert.NilError(t, err)
85
-		assert.Check(t, is.Equal(r.Name, "mynetwork"))
86
-		_, ok := r.Services["web"]
85
+		assert.Check(t, is.Equal(r.Network.Name, "mynetwork"))
86
+		_, ok := r.Network.Services["web"]
87 87
 		assert.Check(t, ok, "expected service `web` missing in the verbose output")
88 88
 	})
89 89
 	t.Run("global scope", func(t *testing.T) {
... ...
@@ -1025,10 +1025,10 @@ func (s *DockerSwarmSuite) TestAPINetworkInspectWithScope(c *testing.T) {
1025 1025
 	resp, err := apiclient.NetworkCreate(ctx, name, client.NetworkCreateOptions{Driver: "overlay"})
1026 1026
 	assert.NilError(c, err)
1027 1027
 
1028
-	nw, err := apiclient.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
1028
+	res, err := apiclient.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
1029 1029
 	assert.NilError(c, err)
1030
-	assert.Check(c, is.Equal("swarm", nw.Scope))
1031
-	assert.Check(c, is.Equal(resp.ID, nw.ID))
1030
+	assert.Check(c, is.Equal("swarm", res.Network.Scope))
1031
+	assert.Check(c, is.Equal(resp.ID, res.Network.ID))
1032 1032
 
1033 1033
 	_, err = apiclient.NetworkInspect(ctx, name, client.NetworkInspectOptions{Scope: "local"})
1034 1034
 	assert.Check(c, is.ErrorType(err, cerrdefs.IsNotFound))
... ...
@@ -153,9 +153,9 @@ func TestDaemonHostGatewayIP(t *testing.T) {
153 153
 	assert.NilError(t, err)
154 154
 	assert.Assert(t, is.Len(res.Stderr(), 0))
155 155
 	assert.Equal(t, 0, res.ExitCode)
156
-	inspect, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
156
+	resp, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
157 157
 	assert.NilError(t, err)
158
-	assert.Check(t, is.Contains(res.Stdout(), inspect.IPAM.Config[0].Gateway.String()))
158
+	assert.Check(t, is.Contains(res.Stdout(), resp.Network.IPAM.Config[0].Gateway.String()))
159 159
 	c.ContainerRemove(ctx, cID, client.ContainerRemoveOptions{Force: true})
160 160
 	d.Stop(t)
161 161
 
... ...
@@ -420,7 +420,7 @@ func testDefaultBridgeIPAM(ctx context.Context, t *testing.T, tc defaultBridgeIP
420 420
 			c := d.NewClientT(t)
421 421
 			defer c.Close()
422 422
 
423
-			insp, err := c.NetworkInspect(ctx, network.NetworkBridge, client.NetworkInspectOptions{})
423
+			res, err := c.NetworkInspect(ctx, network.NetworkBridge, client.NetworkInspectOptions{})
424 424
 			assert.NilError(t, err)
425 425
 			expIPAMConfig := slices.Clone(tc.expIPAMConfig)
426 426
 			for i := range expIPAMConfig {
... ...
@@ -428,7 +428,7 @@ func testDefaultBridgeIPAM(ctx context.Context, t *testing.T, tc defaultBridgeIP
428 428
 					expIPAMConfig[i].Gateway = llAddr
429 429
 				}
430 430
 			}
431
-			assert.Check(t, is.DeepEqual(insp.IPAM.Config, expIPAMConfig, cmpopts.EquateComparable(netip.Addr{}, netip.Prefix{})))
431
+			assert.Check(t, is.DeepEqual(res.Network.IPAM.Config, expIPAMConfig, cmpopts.EquateComparable(netip.Addr{}, netip.Prefix{})))
432 432
 		})
433 433
 	})
434 434
 }
... ...
@@ -65,10 +65,10 @@ func TestCreateWithIPv6DefaultsToULAPrefix(t *testing.T) {
65 65
 	network.CreateNoError(ctx, t, apiClient, nwName, network.WithIPv6())
66 66
 	defer network.RemoveNoError(ctx, t, apiClient, nwName)
67 67
 
68
-	nw, err := apiClient.NetworkInspect(ctx, "testnetula", client.NetworkInspectOptions{})
68
+	res, err := apiClient.NetworkInspect(ctx, "testnetula", client.NetworkInspectOptions{})
69 69
 	assert.NilError(t, err)
70 70
 
71
-	for _, ipam := range nw.IPAM.Config {
71
+	for _, ipam := range res.Network.IPAM.Config {
72 72
 		if netip.MustParsePrefix("fd00::/8").Overlaps(ipam.Subnet) {
73 73
 			return
74 74
 		}
... ...
@@ -91,10 +91,10 @@ func TestCreateWithIPv6WithoutEnableIPv6Flag(t *testing.T) {
91 91
 	network.CreateNoError(ctx, t, apiClient, nwName)
92 92
 	defer network.RemoveNoError(ctx, t, apiClient, nwName)
93 93
 
94
-	nw, err := apiClient.NetworkInspect(ctx, "testnetula", client.NetworkInspectOptions{})
94
+	res, err := apiClient.NetworkInspect(ctx, "testnetula", client.NetworkInspectOptions{})
95 95
 	assert.NilError(t, err)
96 96
 
97
-	for _, ipam := range nw.IPAM.Config {
97
+	for _, ipam := range res.Network.IPAM.Config {
98 98
 		if netip.MustParsePrefix("fd00::/8").Overlaps(ipam.Subnet) {
99 99
 			return
100 100
 		}
... ...
@@ -135,20 +135,20 @@ func TestDefaultIPvOptOverride(t *testing.T) {
135 135
 					network.CreateNoError(ctx, t, c, netName, nopts...)
136 136
 					defer network.RemoveNoError(ctx, t, c, netName)
137 137
 
138
-					insp, err := c.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
138
+					res, err := c.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
139 139
 					assert.NilError(t, err)
140
-					t.Log("override4", override4, "override6", override6, "->", insp.Options)
140
+					t.Log("override4", override4, "override6", override6, "->", res.Network.Options)
141 141
 
142
-					gotOpt4, have4 := insp.Options[netlabel.EnableIPv4]
142
+					gotOpt4, have4 := res.Network.Options[netlabel.EnableIPv4]
143 143
 					assert.Check(t, is.Equal(have4, !override4))
144
-					assert.Check(t, is.Equal(insp.EnableIPv4, override4))
144
+					assert.Check(t, is.Equal(res.Network.EnableIPv4, override4))
145 145
 					if have4 {
146 146
 						assert.Check(t, is.Equal(gotOpt4, opt4))
147 147
 					}
148 148
 
149
-					gotOpt6, have6 := insp.Options[netlabel.EnableIPv6]
149
+					gotOpt6, have6 := res.Network.Options[netlabel.EnableIPv6]
150 150
 					assert.Check(t, is.Equal(have6, !override6))
151
-					assert.Check(t, is.Equal(insp.EnableIPv6, true))
151
+					assert.Check(t, is.Equal(res.Network.EnableIPv6, true))
152 152
 					if have6 {
153 153
 						assert.Check(t, is.Equal(gotOpt6, opt6))
154 154
 					}
... ...
@@ -1074,9 +1074,9 @@ func TestBridgeIPAMStatus(t *testing.T) {
1074 1074
 		netName string, want networktypes.SubnetStatuses,
1075 1075
 	) bool {
1076 1076
 		t.Helper()
1077
-		nw, err := c.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
1078
-		if assert.Check(t, err) && assert.Check(t, nw.Status != nil) {
1079
-			return assert.Check(t, is.DeepEqual(want, nw.Status.IPAM.Subnets))
1077
+		res, err := c.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
1078
+		if assert.Check(t, err) && assert.Check(t, res.Network.Status != nil) {
1079
+			return assert.Check(t, is.DeepEqual(want, res.Network.Status.IPAM.Subnets))
1080 1080
 		}
1081 1081
 		return false
1082 1082
 	}
... ...
@@ -1188,9 +1188,9 @@ func TestBridgeIPAMStatus(t *testing.T) {
1188 1188
 		})
1189 1189
 
1190 1190
 		oldc := d.NewClientT(t, client.WithVersion("1.51"))
1191
-		nw, err := oldc.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
1191
+		res, err := oldc.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
1192 1192
 		if assert.Check(t, err) {
1193
-			assert.Check(t, nw.Status == nil, "expected nil Status with API version 1.51")
1193
+			assert.Check(t, res.Network.Status == nil, "expected nil Status with API version 1.51")
1194 1194
 		}
1195 1195
 	})
1196 1196
 
... ...
@@ -1260,7 +1260,7 @@ func TestJoinError(t *testing.T) {
1260 1260
 	// extNet should not report any attached containers
1261 1261
 	extNetInsp, err := c.NetworkInspect(ctx, extNet, client.NetworkInspectOptions{})
1262 1262
 	assert.Check(t, err)
1263
-	assert.Check(t, is.Len(extNetInsp.Containers, 0))
1263
+	assert.Check(t, is.Len(extNetInsp.Network.Containers, 0))
1264 1264
 
1265 1265
 	// The container should have an eth0, but no eth1.
1266 1266
 	res = ctr.ExecT(ctx, t, c, cid, []string{"ip", "link", "show", "eth0"})
... ...
@@ -1281,7 +1281,7 @@ func TestJoinError(t *testing.T) {
1281 1281
 	assert.Check(t, is.Contains(ctrInsp.NetworkSettings.Networks, extNet))
1282 1282
 	extNetInsp, err = c.NetworkInspect(ctx, extNet, client.NetworkInspectOptions{})
1283 1283
 	assert.Check(t, err)
1284
-	assert.Check(t, is.Len(extNetInsp.Containers, 1))
1284
+	assert.Check(t, is.Len(extNetInsp.Network.Containers, 1))
1285 1285
 	res = ctr.ExecT(ctx, t, c, cid, []string{"ip", "link", "show", "eth0"})
1286 1286
 	assert.Check(t, is.Equal(res.ExitCode, 0), "container should have an eth0")
1287 1287
 	res = ctr.ExecT(ctx, t, c, cid, []string{"ip", "link", "show", "eth1"})
... ...
@@ -125,34 +125,34 @@ func TestInspectNetwork(t *testing.T) {
125 125
 				for _, d := range append([]*daemon.Daemon{worker1}, mgr[:]...) {
126 126
 					t.Logf("--- Node %s (%s) ---", d.ID(), d.NodeID())
127 127
 					c := d.NewClientT(t)
128
-					nw, err := c.NetworkInspect(ctx, tc.network, tc.opts)
128
+					res, err := c.NetworkInspect(ctx, tc.network, tc.opts)
129 129
 					if !assert.Check(t, err) {
130 130
 						continue
131 131
 					}
132 132
 
133
-					assert.Check(t, nw.IPAM.Config != nil)
134
-					for _, cfg := range nw.IPAM.Config {
133
+					assert.Check(t, res.Network.IPAM.Config != nil)
134
+					for _, cfg := range res.Network.IPAM.Config {
135 135
 						assert.Assert(t, cfg.Gateway.IsValid())
136 136
 						assert.Assert(t, cfg.Subnet.IsValid())
137 137
 					}
138 138
 
139 139
 					if d.CachedInfo.Swarm.ControlAvailable {
140 140
 						// The global view of the network status is only available from manager nodes.
141
-						if assert.Check(t, nw.Status != nil) {
141
+						if assert.Check(t, res.Network.Status != nil) {
142 142
 							wantSubnetStatus := map[netip.Prefix]networktypes.SubnetStatus{
143 143
 								cidrv4: {
144 144
 									IPsInUse:            uint64(1 + instances + len(mgr) + 1),
145 145
 									DynamicIPsAvailable: uint64(128 - (instances + len(mgr) + 1)),
146 146
 								},
147 147
 							}
148
-							assert.Check(t, is.DeepEqual(wantSubnetStatus, nw.Status.IPAM.Subnets))
148
+							assert.Check(t, is.DeepEqual(wantSubnetStatus, res.Network.Status.IPAM.Subnets))
149 149
 						}
150 150
 					} else {
151 151
 						// Services are only inspectable on nodes that have the network instantiated in
152 152
 						// libnetwork, i.e. nodes with tasks attached to the network. In this test, only
153 153
 						// the one worker node has tasks assigned.
154
-						if assert.Check(t, is.Contains(nw.Services, serviceName)) {
155
-							assert.Check(t, is.Len(nw.Services[serviceName].Tasks, instances))
154
+						if assert.Check(t, is.Contains(res.Network.Services, serviceName)) {
155
+							assert.Check(t, is.Len(res.Network.Services[serviceName].Tasks, instances))
156 156
 						}
157 157
 					}
158 158
 					c.Close()
... ...
@@ -552,15 +552,15 @@ func TestIpvlanIPAM(t *testing.T) {
552 552
 			assert.Check(t, is.Equal(strings.TrimSpace(sysctlRes.Combined()), expDisableIPv6))
553 553
 
554 554
 			cc := d.NewClientT(t, dclient.WithVersion("1.52"))
555
-			inspect, err := cc.NetworkInspect(ctx, netName, dclient.NetworkInspectOptions{})
556
-			if assert.Check(t, err) && assert.Check(t, inspect.Status != nil) {
557
-				assert.Check(t, is.DeepEqual(wantSubnetStatus, inspect.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
555
+			res, err := cc.NetworkInspect(ctx, netName, dclient.NetworkInspectOptions{})
556
+			if assert.Check(t, err) && assert.Check(t, res.Network.Status != nil) {
557
+				assert.Check(t, is.DeepEqual(wantSubnetStatus, res.Network.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
558 558
 			}
559 559
 			cc.Close()
560 560
 			cc = d.NewClientT(t, dclient.WithVersion("1.51"))
561
-			inspect, err = cc.NetworkInspect(ctx, netName, dclient.NetworkInspectOptions{})
561
+			res, err = cc.NetworkInspect(ctx, netName, dclient.NetworkInspectOptions{})
562 562
 			assert.Check(t, err)
563
-			assert.Check(t, inspect.Status == nil)
563
+			assert.Check(t, res.Network.Status == nil)
564 564
 			cc.Close()
565 565
 		})
566 566
 	}
... ...
@@ -585,9 +585,9 @@ func TestIpvlanIPAMOverlap(t *testing.T) {
585 585
 
586 586
 	checkNetworkIPAMState := func(networkID string, want map[netip.Prefix]network.SubnetStatus) bool {
587 587
 		t.Helper()
588
-		nw, err := c.NetworkInspect(ctx, networkID, dclient.NetworkInspectOptions{})
589
-		if assert.Check(t, err) && assert.Check(t, nw.Status != nil) {
590
-			return assert.Check(t, is.DeepEqual(want, nw.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
588
+		res, err := c.NetworkInspect(ctx, networkID, dclient.NetworkInspectOptions{})
589
+		if assert.Check(t, err) && assert.Check(t, res.Network.Status != nil) {
590
+			return assert.Check(t, is.DeepEqual(want, res.Network.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
591 591
 		}
592 592
 		return false
593 593
 	}
... ...
@@ -555,16 +555,16 @@ func TestMacvlanIPAM(t *testing.T) {
555 555
 			assert.Check(t, is.Equal(strings.TrimSpace(sysctlRes.Combined()), expDisableIPv6))
556 556
 
557 557
 			cc := d.NewClientT(t, client.WithVersion("1.52"))
558
-			inspect, err := cc.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
559
-			if assert.Check(t, err) && assert.Check(t, inspect.Status != nil) {
560
-				assert.Check(t, is.DeepEqual(wantSubnetStatus, inspect.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
558
+			res, err := cc.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
559
+			if assert.Check(t, err) && assert.Check(t, res.Network.Status != nil) {
560
+				assert.Check(t, is.DeepEqual(wantSubnetStatus, res.Network.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
561 561
 			}
562
-			cc.Close()
562
+			_ = cc.Close()
563 563
 			cc = d.NewClientT(t, client.WithVersion("1.51"))
564
-			inspect, err = cc.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
564
+			res, err = cc.NetworkInspect(ctx, netName, client.NetworkInspectOptions{})
565 565
 			assert.Check(t, err)
566
-			assert.Check(t, inspect.Status == nil)
567
-			cc.Close()
566
+			assert.Check(t, res.Network.Status == nil)
567
+			_ = cc.Close()
568 568
 		})
569 569
 	}
570 570
 }
... ...
@@ -588,9 +588,9 @@ func TestMacvlanIPAMOverlap(t *testing.T) {
588 588
 
589 589
 	checkNetworkIPAMState := func(networkID string, want map[netip.Prefix]network.SubnetStatus) bool {
590 590
 		t.Helper()
591
-		nw, err := c.NetworkInspect(ctx, networkID, client.NetworkInspectOptions{})
592
-		if assert.Check(t, err) && assert.Check(t, nw.Status != nil) {
593
-			return assert.Check(t, is.DeepEqual(want, nw.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
591
+		res, err := c.NetworkInspect(ctx, networkID, client.NetworkInspectOptions{})
592
+		if assert.Check(t, err) && assert.Check(t, res.Network.Status != nil) {
593
+			return assert.Check(t, is.DeepEqual(want, res.Network.Status.IPAM.Subnets, cmpopts.EquateEmpty()))
594 594
 		}
595 595
 		return false
596 596
 	}
... ...
@@ -88,17 +88,17 @@ func TestHostIPv4BridgeLabel(t *testing.T) {
88 88
 		network.WithOption("com.docker.network.bridge.name", bridgeName),
89 89
 	)
90 90
 	defer network.RemoveNoError(ctx, t, c, bridgeName)
91
-	out, err := c.NetworkInspect(ctx, bridgeName, client.NetworkInspectOptions{Verbose: true})
91
+	res, err := c.NetworkInspect(ctx, bridgeName, client.NetworkInspectOptions{Verbose: true})
92 92
 	assert.NilError(t, err)
93
-	assert.Assert(t, len(out.IPAM.Config) > 0)
93
+	assert.Assert(t, len(res.Network.IPAM.Config) > 0)
94 94
 	// Make sure the SNAT rule exists
95 95
 	if strings.HasPrefix(testEnv.FirewallBackendDriver(), "nftables") {
96 96
 		chain := testutil.RunCommand(ctx, "nft", "--stateless", "list", "chain", "ip", "docker-bridges", "nat-postrouting-out__hostIPv4Bridge").Combined()
97 97
 		exp := fmt.Sprintf(`oifname != "hostIPv4Bridge" ip saddr %s counter snat to %s comment "SNAT"`,
98
-			out.IPAM.Config[0].Subnet, ipv4SNATAddr)
98
+			res.Network.IPAM.Config[0].Subnet, ipv4SNATAddr)
99 99
 		assert.Check(t, is.Contains(chain, exp))
100 100
 	} else {
101
-		testutil.RunCommand(ctx, "iptables", "-t", "nat", "-C", "POSTROUTING", "-s", out.IPAM.Config[0].Subnet.String(), "!", "-o", bridgeName, "-j", "SNAT", "--to-source", ipv4SNATAddr).Assert(t, icmd.Success)
101
+		testutil.RunCommand(ctx, "iptables", "-t", "nat", "-C", "POSTROUTING", "-s", res.Network.IPAM.Config[0].Subnet.String(), "!", "-o", bridgeName, "-j", "SNAT", "--to-source", ipv4SNATAddr).Assert(t, icmd.Success)
102 102
 	}
103 103
 }
104 104
 
... ...
@@ -45,9 +45,9 @@ func TestDaemonRestartWithLiveRestore(t *testing.T) {
45 45
 	defer c.Close()
46 46
 
47 47
 	// Verify bridge network's subnet
48
-	out, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
48
+	res, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
49 49
 	assert.NilError(t, err)
50
-	subnet := out.IPAM.Config[0].Subnet
50
+	subnet := res.Network.IPAM.Config[0].Subnet
51 51
 
52 52
 	d.Restart(t,
53 53
 		"--live-restore=true",
... ...
@@ -55,10 +55,10 @@ func TestDaemonRestartWithLiveRestore(t *testing.T) {
55 55
 		"--default-address-pool", "base=175.33.0.0/16,size=24",
56 56
 	)
57 57
 
58
-	out1, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
58
+	res1, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
59 59
 	assert.NilError(t, err)
60 60
 	// Make sure docker0 doesn't get override with new IP in live restore case
61
-	assert.Equal(t, out1.IPAM.Config[0].Subnet, subnet)
61
+	assert.Equal(t, res1.Network.IPAM.Config[0].Subnet, subnet)
62 62
 }
63 63
 
64 64
 func TestDaemonDefaultNetworkPools(t *testing.T) {
... ...
@@ -82,9 +82,9 @@ func TestDaemonDefaultNetworkPools(t *testing.T) {
82 82
 	defer c.Close()
83 83
 
84 84
 	// Verify bridge network's subnet
85
-	out, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
85
+	res, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
86 86
 	assert.NilError(t, err)
87
-	assert.Equal(t, out.IPAM.Config[0].Subnet, netip.MustParsePrefix("175.30.0.0/16"))
87
+	assert.Equal(t, res.Network.IPAM.Config[0].Subnet, netip.MustParsePrefix("175.30.0.0/16"))
88 88
 
89 89
 	// Create a bridge network and verify its subnet is the second default pool
90 90
 	name := "elango" + t.Name()
... ...
@@ -92,9 +92,9 @@ func TestDaemonDefaultNetworkPools(t *testing.T) {
92 92
 		network.WithDriver("bridge"),
93 93
 	)
94 94
 	defer network.RemoveNoError(ctx, t, c, name)
95
-	out, err = c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
95
+	res, err = c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
96 96
 	assert.NilError(t, err)
97
-	assert.Check(t, is.Equal(out.IPAM.Config[0].Subnet, netip.MustParsePrefix("175.33.0.0/24")))
97
+	assert.Check(t, is.Equal(res.Network.IPAM.Config[0].Subnet, netip.MustParsePrefix("175.33.0.0/24")))
98 98
 
99 99
 	// Create a bridge network and verify its subnet is the third default pool
100 100
 	name = "saanvi" + t.Name()
... ...
@@ -102,9 +102,9 @@ func TestDaemonDefaultNetworkPools(t *testing.T) {
102 102
 		network.WithDriver("bridge"),
103 103
 	)
104 104
 	defer network.RemoveNoError(ctx, t, c, name)
105
-	out, err = c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
105
+	res, err = c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
106 106
 	assert.NilError(t, err)
107
-	assert.Check(t, is.Equal(out.IPAM.Config[0].Subnet, netip.MustParsePrefix("175.33.1.0/24")))
107
+	assert.Check(t, is.Equal(res.Network.IPAM.Config[0].Subnet, netip.MustParsePrefix("175.33.1.0/24")))
108 108
 }
109 109
 
110 110
 func TestDaemonRestartWithExistingNetwork(t *testing.T) {
... ...
@@ -127,9 +127,9 @@ func TestDaemonRestartWithExistingNetwork(t *testing.T) {
127 127
 	defer network.RemoveNoError(ctx, t, c, name)
128 128
 
129 129
 	// Verify bridge network's subnet
130
-	out, err := c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
130
+	res, err := c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
131 131
 	assert.NilError(t, err)
132
-	networkip := out.IPAM.Config[0].Subnet
132
+	networkip := res.Network.IPAM.Config[0].Subnet
133 133
 
134 134
 	// Restart daemon with default address pool option
135 135
 	d.Restart(t,
... ...
@@ -137,9 +137,9 @@ func TestDaemonRestartWithExistingNetwork(t *testing.T) {
137 137
 		"--default-address-pool", "base=175.33.0.0/16,size=24")
138 138
 	defer delInterface(ctx, t, "docker0")
139 139
 
140
-	out1, err := c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
140
+	res2, err := c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
141 141
 	assert.NilError(t, err)
142
-	assert.Equal(t, out1.IPAM.Config[0].Subnet, networkip)
142
+	assert.Equal(t, res2.Network.IPAM.Config[0].Subnet, networkip)
143 143
 }
144 144
 
145 145
 func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) {
... ...
@@ -163,9 +163,9 @@ func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) {
163 163
 	defer network.RemoveNoError(ctx, t, c, name)
164 164
 
165 165
 	// Verify bridge network's subnet
166
-	out, err := c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
166
+	res, err := c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
167 167
 	assert.NilError(t, err)
168
-	networkip := out.IPAM.Config[0].Subnet
168
+	networkip := res.Network.IPAM.Config[0].Subnet
169 169
 
170 170
 	// Create a bridge network
171 171
 	name = "sthira" + t.Name()
... ...
@@ -173,9 +173,9 @@ func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) {
173 173
 		network.WithDriver("bridge"),
174 174
 	)
175 175
 	defer network.RemoveNoError(ctx, t, c, name)
176
-	out, err = c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
176
+	res, err = c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
177 177
 	assert.NilError(t, err)
178
-	networkip2 := out.IPAM.Config[0].Subnet
178
+	networkip2 := res.Network.IPAM.Config[0].Subnet
179 179
 
180 180
 	// Restart daemon with default address pool option
181 181
 	d.Restart(t,
... ...
@@ -190,11 +190,11 @@ func TestDaemonRestartWithExistingNetworkWithDefaultPoolRange(t *testing.T) {
190 190
 		network.WithDriver("bridge"),
191 191
 	)
192 192
 	defer network.RemoveNoError(ctx, t, c, name)
193
-	out1, err := c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
193
+	res1, err := c.NetworkInspect(ctx, name, client.NetworkInspectOptions{})
194 194
 	assert.NilError(t, err)
195 195
 
196
-	assert.Check(t, out1.IPAM.Config[0].Subnet != networkip)
197
-	assert.Check(t, out1.IPAM.Config[0].Subnet != networkip2)
196
+	assert.Check(t, res1.Network.IPAM.Config[0].Subnet != networkip)
197
+	assert.Check(t, res1.Network.IPAM.Config[0].Subnet != networkip2)
198 198
 }
199 199
 
200 200
 func TestDaemonWithBipAndDefaultNetworkPool(t *testing.T) {
... ...
@@ -217,10 +217,10 @@ func TestDaemonWithBipAndDefaultNetworkPool(t *testing.T) {
217 217
 	defer c.Close()
218 218
 
219 219
 	// Verify bridge network's subnet
220
-	out, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
220
+	res, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
221 221
 	assert.NilError(t, err)
222 222
 	// Make sure BIP IP doesn't get override with new default address pool .
223
-	assert.Equal(t, out.IPAM.Config[0].Subnet, netip.MustParsePrefix("172.60.0.0/16"))
223
+	assert.Equal(t, res.Network.IPAM.Config[0].Subnet, netip.MustParsePrefix("172.60.0.0/16"))
224 224
 }
225 225
 
226 226
 func TestServiceWithPredefinedNetwork(t *testing.T) {
... ...
@@ -297,33 +297,33 @@ func TestServiceRemoveKeepsIngressNetwork(t *testing.T) {
297 297
 
298 298
 	// Ensure that "ingress" is not removed or corrupted
299 299
 	time.Sleep(10 * time.Second)
300
-	netInfo, err := c.NetworkInspect(ctx, ingressNet, client.NetworkInspectOptions{
300
+	res, err := c.NetworkInspect(ctx, ingressNet, client.NetworkInspectOptions{
301 301
 		Verbose: true,
302 302
 		Scope:   "swarm",
303 303
 	})
304 304
 	assert.NilError(t, err, "Ingress network was removed after removing service!")
305
-	assert.Assert(t, len(netInfo.Containers) != 0, "No load balancing endpoints in ingress network")
306
-	assert.Assert(t, len(netInfo.Peers) != 0, "No peers (including self) in ingress network")
307
-	_, ok := netInfo.Containers["ingress-sbox"]
305
+	assert.Assert(t, len(res.Network.Containers) != 0, "No load balancing endpoints in ingress network")
306
+	assert.Assert(t, len(res.Network.Peers) != 0, "No peers (including self) in ingress network")
307
+	_, ok := res.Network.Containers["ingress-sbox"]
308 308
 	assert.Assert(t, ok, "ingress-sbox not present in ingress network")
309 309
 }
310 310
 
311 311
 //nolint:unused // for some reason, the "unused" linter marks this function as "unused"
312 312
 func swarmIngressReady(ctx context.Context, apiClient client.NetworkAPIClient) func(log poll.LogT) poll.Result {
313 313
 	return func(log poll.LogT) poll.Result {
314
-		netInfo, err := apiClient.NetworkInspect(ctx, ingressNet, client.NetworkInspectOptions{
314
+		res, err := apiClient.NetworkInspect(ctx, ingressNet, client.NetworkInspectOptions{
315 315
 			Verbose: true,
316 316
 			Scope:   "swarm",
317 317
 		})
318 318
 		if err != nil {
319 319
 			return poll.Error(err)
320 320
 		}
321
-		np := len(netInfo.Peers)
322
-		nc := len(netInfo.Containers)
321
+		np := len(res.Network.Peers)
322
+		nc := len(res.Network.Containers)
323 323
 		if np == 0 || nc == 0 {
324 324
 			return poll.Continue("ingress not ready: %d peers and %d containers", nc, np)
325 325
 		}
326
-		_, ok := netInfo.Containers["ingress-sbox"]
326
+		_, ok := res.Network.Containers["ingress-sbox"]
327 327
 		if !ok {
328 328
 			return poll.Continue("ingress not ready: does not contain the ingress-sbox")
329 329
 		}
... ...
@@ -443,21 +443,21 @@ func TestServiceWithDefaultAddressPoolInit(t *testing.T) {
443 443
 	_, _, err := cli.ServiceInspectWithRaw(ctx, serviceID, client.ServiceInspectOptions{})
444 444
 	assert.NilError(t, err)
445 445
 
446
-	out, err := cli.NetworkInspect(ctx, overlayID, client.NetworkInspectOptions{Verbose: true})
446
+	res, err := cli.NetworkInspect(ctx, overlayID, client.NetworkInspectOptions{Verbose: true})
447 447
 	assert.NilError(t, err)
448
-	t.Logf("%s: NetworkInspect: %+v", t.Name(), out)
449
-	assert.Assert(t, len(out.IPAM.Config) > 0)
448
+	t.Logf("%s: NetworkInspect: %+v", t.Name(), res)
449
+	assert.Assert(t, len(res.Network.IPAM.Config) > 0)
450 450
 	// As of docker/swarmkit#2890, the ingress network uses the default address
451 451
 	// pool (whereas before, the subnet for the ingress network was hard-coded.
452 452
 	// This means that the ingress network gets the subnet 20.20.0.0/24, and
453 453
 	// the network we just created gets subnet 20.20.1.0/24.
454
-	assert.Equal(t, out.IPAM.Config[0].Subnet, netip.MustParsePrefix("20.20.1.0/24"))
454
+	assert.Equal(t, res.Network.IPAM.Config[0].Subnet, netip.MustParsePrefix("20.20.1.0/24"))
455 455
 
456 456
 	// Also inspect ingress network and make sure its in the same subnet
457
-	out, err = cli.NetworkInspect(ctx, "ingress", client.NetworkInspectOptions{Verbose: true})
457
+	res, err = cli.NetworkInspect(ctx, "ingress", client.NetworkInspectOptions{Verbose: true})
458 458
 	assert.NilError(t, err)
459
-	assert.Assert(t, len(out.IPAM.Config) > 0)
460
-	assert.Equal(t, out.IPAM.Config[0].Subnet, netip.MustParsePrefix("20.20.0.0/24"))
459
+	assert.Assert(t, len(res.Network.IPAM.Config) > 0)
460
+	assert.Equal(t, res.Network.IPAM.Config[0].Subnet, netip.MustParsePrefix("20.20.0.0/24"))
461 461
 
462 462
 	err = cli.ServiceRemove(ctx, serviceID)
463 463
 	poll.WaitOn(t, noServices(ctx, cli), swarm.ServicePoll)
... ...
@@ -584,9 +584,9 @@ func TestAccessToPublishedPort(t *testing.T) {
584 584
 			// Use the default bridge addresses as host addresses (like "host-gateway", but
585 585
 			// there's no way to tell wget to prefer ipv4/ipv6 transport, so just use the
586 586
 			// addresses directly).
587
-			insp, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
587
+			res, err := c.NetworkInspect(ctx, "bridge", client.NetworkInspectOptions{})
588 588
 			assert.NilError(t, err)
589
-			for _, ipamCfg := range insp.IPAM.Config {
589
+			for _, ipamCfg := range res.Network.IPAM.Config {
590 590
 				ipv := "ipv4"
591 591
 				if ipamCfg.Gateway.Is6() {
592 592
 					ipv = "ipv6"
... ...
@@ -1900,9 +1900,9 @@ func TestNetworkInspectGateway(t *testing.T) {
1900 1900
 	assert.NilError(t, err)
1901 1901
 	defer network.RemoveNoError(ctx, t, c, netName)
1902 1902
 
1903
-	insp, err := c.NetworkInspect(ctx, nid, client.NetworkInspectOptions{})
1903
+	res, err := c.NetworkInspect(ctx, nid, client.NetworkInspectOptions{})
1904 1904
 	assert.NilError(t, err)
1905
-	for _, ipamCfg := range insp.IPAM.Config {
1905
+	for _, ipamCfg := range res.Network.IPAM.Config {
1906 1906
 		assert.Check(t, ipamCfg.Gateway.IsValid())
1907 1907
 	}
1908 1908
 }
... ...
@@ -227,7 +227,7 @@ func TestServiceUpdateNetwork(t *testing.T) {
227 227
 		Scope:   "swarm",
228 228
 	})
229 229
 	assert.NilError(t, err)
230
-	assert.Assert(t, len(netInfo.Containers) == 2, "Expected 2 endpoints, one for container and one for LB Sandbox")
230
+	assert.Assert(t, len(netInfo.Network.Containers) == 2, "Expected 2 endpoints, one for container and one for LB Sandbox")
231 231
 
232 232
 	// Remove network from service
233 233
 	service.Spec.TaskTemplate.Networks = []swarmtypes.NetworkAttachmentConfig{}
... ...
@@ -241,7 +241,7 @@ func TestServiceUpdateNetwork(t *testing.T) {
241 241
 	})
242 242
 
243 243
 	assert.NilError(t, err)
244
-	assert.Assert(t, len(netInfo.Containers) == 0, "Load balancing endpoint still exists in network")
244
+	assert.Assert(t, len(netInfo.Network.Containers) == 0, "Load balancing endpoint still exists in network")
245 245
 
246 246
 	err = apiClient.NetworkRemove(ctx, overlayID)
247 247
 	assert.NilError(t, err)
... ...
@@ -130,8 +130,7 @@ type NetworkAPIClient interface {
130 130
 	NetworkConnect(ctx context.Context, network, container string, config *network.EndpointSettings) error
131 131
 	NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (network.CreateResponse, error)
132 132
 	NetworkDisconnect(ctx context.Context, network, container string, force bool) error
133
-	NetworkInspect(ctx context.Context, network string, options NetworkInspectOptions) (network.Inspect, error)
134
-	NetworkInspectWithRaw(ctx context.Context, network string, options NetworkInspectOptions) (network.Inspect, []byte, error)
133
+	NetworkInspect(ctx context.Context, network string, options NetworkInspectOptions) (NetworkInspectResult, error)
135 134
 	NetworkList(ctx context.Context, options NetworkListOptions) ([]network.Summary, error)
136 135
 	NetworkRemove(ctx context.Context, network string) error
137 136
 	NetworksPrune(ctx context.Context, opts NetworkPruneOptions) (NetworkPruneResult, error)
... ...
@@ -1,26 +1,23 @@
1 1
 package client
2 2
 
3 3
 import (
4
-	"bytes"
5 4
 	"context"
6
-	"encoding/json"
7
-	"io"
8 5
 	"net/url"
9 6
 
10 7
 	"github.com/moby/moby/api/types/network"
11 8
 )
12 9
 
13
-// NetworkInspect returns the information for a specific network configured in the docker host.
14
-func (cli *Client) NetworkInspect(ctx context.Context, networkID string, options NetworkInspectOptions) (network.Inspect, error) {
15
-	networkResource, _, err := cli.NetworkInspectWithRaw(ctx, networkID, options)
16
-	return networkResource, err
10
+// NetworkInspectResult contains the result of a network inspection.
11
+type NetworkInspectResult struct {
12
+	Network network.Inspect
13
+	Raw     []byte
17 14
 }
18 15
 
19
-// NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation.
20
-func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, options NetworkInspectOptions) (network.Inspect, []byte, error) {
16
+// NetworkInspect returns the information for a specific network configured in the docker host.
17
+func (cli *Client) NetworkInspect(ctx context.Context, networkID string, options NetworkInspectOptions) (NetworkInspectResult, error) {
21 18
 	networkID, err := trimID("network", networkID)
22 19
 	if err != nil {
23
-		return network.Inspect{}, nil, err
20
+		return NetworkInspectResult{}, err
24 21
 	}
25 22
 	query := url.Values{}
26 23
 	if options.Verbose {
... ...
@@ -33,15 +30,10 @@ func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string,
33 33
 	resp, err := cli.get(ctx, "/networks/"+networkID, query, nil)
34 34
 	defer ensureReaderClosed(resp)
35 35
 	if err != nil {
36
-		return network.Inspect{}, nil, err
37
-	}
38
-
39
-	raw, err := io.ReadAll(resp.Body)
40
-	if err != nil {
41
-		return network.Inspect{}, nil, err
36
+		return NetworkInspectResult{}, err
42 37
 	}
43 38
 
44
-	var nw network.Inspect
45
-	err = json.NewDecoder(bytes.NewReader(raw)).Decode(&nw)
46
-	return nw, raw, err
39
+	var out NetworkInspectResult
40
+	out.Raw, err = decodeWithRaw(resp, &out.Network)
41
+	return out, err
47 42
 }