From 5952486df8d5a1b45f2136e15c0f3afd5460b57a Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Wed, 28 Feb 2024 14:28:47 +0100 Subject: [PATCH] podman network inspect: include running containers Like docker podman network inspect should output the information of running container with their ip/mac address on this network. However the output format is not docker compatible as this cannot include all the info we have and the previous output was already not compatible so this is not new. New example output: ``` [ { ... "containers": { "7c0d295779cee4a6db7adc07a99e635909413a390eeab9f951edbc4aac406bf1": { "name": "c2", "interfaces": { "eth0": { "subnets": [ { "ipnet": "10.89.0.4/24", "gateway": "10.89.0.1" }, { "ipnet": "fda3:b4da:da1e:7e9d::4/64", "gateway": "fda3:b4da:da1e:7e9d::1" } ], "mac_address": "1a:bd:ca:ea:4b:3a" } } }, "b17c6651ae6d9cc7d5825968e01d6b1e67f44460bb0c140bcc32bd9d436ac11d": { "name": "c1", "interfaces": { "eth0": { "subnets": [ { "ipnet": "10.89.0.3/24", "gateway": "10.89.0.1" }, { "ipnet": "fda3:b4da:da1e:7e9d::3/64", "gateway": "fda3:b4da:da1e:7e9d::1" } ], "mac_address": "f6:50:e6:22:d9:55" } } } } } ] ``` Fixes #14126 Fixes https://issues.redhat.com/browse/RHEL-3153 Signed-off-by: Paul Holzinger --- cmd/podman/common/completion.go | 2 +- cmd/podman/networks/inspect.go | 3 +- .../markdown/podman-network-inspect.1.md | 2 + pkg/api/handlers/compat/networks.go | 43 +++----------- pkg/api/handlers/swagger/responses.go | 2 +- pkg/bindings/network/network.go | 4 +- pkg/domain/entities/engine_container.go | 2 +- pkg/domain/entities/network.go | 3 + pkg/domain/entities/types/network.go | 14 +++++ pkg/domain/infra/abi/network.go | 58 ++++++++++++++++++- pkg/domain/infra/tunnel/network.go | 4 +- test/e2e/network_create_test.go | 19 +++--- test/e2e/network_test.go | 10 ++-- test/e2e/run_networking_test.go | 6 +- test/system/500-networking.bats | 48 +++++++++++++++ 15 files changed, 155 insertions(+), 65 deletions(-) diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index a8e9fc0102..9427e7535b 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -1298,7 +1298,7 @@ func getEntityType(cmd *cobra.Command, args []string, o interface{}) interface{} } // network logic if networks, _ := getNetworks(cmd, args[0], completeDefault); len(networks) > 0 { - return &types.Network{} + return &entities.NetworkInspectReport{} } return o } diff --git a/cmd/podman/networks/inspect.go b/cmd/podman/networks/inspect.go index 6bb10287b5..f2962fdeca 100644 --- a/cmd/podman/networks/inspect.go +++ b/cmd/podman/networks/inspect.go @@ -1,7 +1,6 @@ package network import ( - "github.com/containers/common/libnetwork/types" "github.com/containers/podman/v5/cmd/podman/common" "github.com/containers/podman/v5/cmd/podman/inspect" "github.com/containers/podman/v5/cmd/podman/registry" @@ -33,7 +32,7 @@ func init() { formatFlagName := "format" flags.StringVarP(&inspectOpts.Format, formatFlagName, "f", "", "Pretty-print network to JSON or using a Go template") - _ = networkinspectCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&types.Network{})) + _ = networkinspectCommand.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(&entities.NetworkInspectReport{})) } func networkInspect(_ *cobra.Command, args []string) error { diff --git a/docs/source/markdown/podman-network-inspect.1.md b/docs/source/markdown/podman-network-inspect.1.md index 7a3f0445b9..2350764485 100644 --- a/docs/source/markdown/podman-network-inspect.1.md +++ b/docs/source/markdown/podman-network-inspect.1.md @@ -16,6 +16,7 @@ Pretty-print networks to JSON or using a Go template. | **Placeholder** | **Description** | |--------------------|-------------------------------------------| +| .Containers ... | Running containers on this network. | | .Created ... | Timestamp when the network was created | | .DNSEnabled | Network has dns enabled (boolean) | | .Driver | Network driver | @@ -25,6 +26,7 @@ Pretty-print networks to JSON or using a Go template. | .IPv6Enabled | Network has ipv6 subnet (boolean) | | .Labels ... | Network labels | | .Name | Network name | +| .Network ... | Nested Network type | | .NetworkDNSServers | Array of DNS servers used in this network | | .NetworkInterface | Name of the network interface on the host | | .Options ... | Network options | diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index a727225692..d7ea086201 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -22,36 +22,6 @@ import ( "github.com/sirupsen/logrus" ) -type containerNetStatus struct { - name string - id string - status map[string]nettypes.StatusBlock -} - -func getContainerNetStatuses(rt *libpod.Runtime) ([]containerNetStatus, error) { - cons, err := rt.GetAllContainers() - if err != nil { - return nil, err - } - statuses := make([]containerNetStatus, 0, len(cons)) - for _, con := range cons { - status, err := con.GetNetworkStatus() - if err != nil { - if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) { - continue - } - return nil, err - } - - statuses = append(statuses, containerNetStatus{ - id: con.ID(), - name: con.Name(), - status: status, - }) - } - return statuses, nil -} - func normalizeNetworkName(rt *libpod.Runtime, name string) (string, bool) { if name == nettypes.BridgeNetworkDriver { return rt.Network().DefaultNetworkName(), true @@ -86,7 +56,8 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { utils.NetworkNotFound(w, name, err) return } - statuses, err := getContainerNetStatuses(runtime) + ic := abi.ContainerEngine{Libpod: runtime} + statuses, err := ic.GetContainerNetStatuses() if err != nil { utils.InternalServerError(w, err) return @@ -95,10 +66,10 @@ func InspectNetwork(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, report) } -func convertLibpodNetworktoDockerNetwork(runtime *libpod.Runtime, statuses []containerNetStatus, network *nettypes.Network, changeDefaultName bool) *types.NetworkResource { +func convertLibpodNetworktoDockerNetwork(runtime *libpod.Runtime, statuses []abi.ContainerNetStatus, network *nettypes.Network, changeDefaultName bool) *types.NetworkResource { containerEndpoints := make(map[string]types.EndpointResource, len(statuses)) for _, st := range statuses { - if netData, ok := st.status[network.Name]; ok { + if netData, ok := st.Status[network.Name]; ok { ipv4Address := "" ipv6Address := "" macAddr := "" @@ -116,12 +87,12 @@ func convertLibpodNetworktoDockerNetwork(runtime *libpod.Runtime, statuses []con break } containerEndpoint := types.EndpointResource{ - Name: st.name, + Name: st.Name, MacAddress: macAddr, IPv4Address: ipv4Address, IPv6Address: ipv6Address, } - containerEndpoints[st.id] = containerEndpoint + containerEndpoints[st.ID] = containerEndpoint } } ipamConfigs := make([]dockerNetwork.IPAMConfig, 0, len(network.Subnets)) @@ -192,7 +163,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - statuses, err := getContainerNetStatuses(runtime) + statuses, err := ic.GetContainerNetStatuses() if err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/api/handlers/swagger/responses.go b/pkg/api/handlers/swagger/responses.go index ee8704dea8..6c52a00588 100644 --- a/pkg/api/handlers/swagger/responses.go +++ b/pkg/api/handlers/swagger/responses.go @@ -434,7 +434,7 @@ type networkRmResponse struct { // swagger:response type networkInspectResponse struct { // in:body - Body types.Network + Body entities.NetworkInspectReport } // Network list diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go index 0ff425313c..a0512d5ec4 100644 --- a/pkg/bindings/network/network.go +++ b/pkg/bindings/network/network.go @@ -70,8 +70,8 @@ func Update(ctx context.Context, netNameOrID string, options *UpdateOptions) err } // Inspect returns information about a network configuration -func Inspect(ctx context.Context, nameOrID string, _ *InspectOptions) (types.Network, error) { - var net types.Network +func Inspect(ctx context.Context, nameOrID string, _ *InspectOptions) (entitiesTypes.NetworkInspectReport, error) { + var net entitiesTypes.NetworkInspectReport conn, err := bindings.GetClient(ctx) if err != nil { return net, err diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 5c9c53517f..15cf309bf2 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -70,7 +70,7 @@ type ContainerEngine interface { //nolint:interfacebloat NetworkUpdate(ctx context.Context, networkname string, options NetworkUpdateOptions) error NetworkDisconnect(ctx context.Context, networkname string, options NetworkDisconnectOptions) error NetworkExists(ctx context.Context, networkname string) (*BoolReport, error) - NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]netTypes.Network, []error, error) + NetworkInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]NetworkInspectReport, []error, error) NetworkList(ctx context.Context, options NetworkListOptions) ([]netTypes.Network, error) NetworkPrune(ctx context.Context, options NetworkPruneOptions) ([]*NetworkPruneReport, error) NetworkReload(ctx context.Context, names []string, options NetworkReloadOptions) ([]*NetworkReloadReport, error) diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go index c2e223233c..1edc335c3b 100644 --- a/pkg/domain/entities/network.go +++ b/pkg/domain/entities/network.go @@ -82,3 +82,6 @@ type NetworkPruneReport = entitiesTypes.NetworkPruneReport type NetworkPruneOptions struct { Filters map[string][]string } + +type NetworkInspectReport = entitiesTypes.NetworkInspectReport +type NetworkContainerInfo = entitiesTypes.NetworkContainerInfo diff --git a/pkg/domain/entities/types/network.go b/pkg/domain/entities/types/network.go index 8c547ee94d..ae233b8232 100644 --- a/pkg/domain/entities/types/network.go +++ b/pkg/domain/entities/types/network.go @@ -35,3 +35,17 @@ type NetworkRmReport struct { type NetworkCreateReport struct { Name string } + +type NetworkInspectReport struct { + commonTypes.Network + + Containers map[string]NetworkContainerInfo `json:"containers"` +} + +type NetworkContainerInfo struct { + // Name of the container + Name string `json:"name"` + + // Interfaces configured for this container with their addresses + Interfaces map[string]commonTypes.NetInterface `json:"interfaces,omitempty"` +} diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index c5309ed8d1..3db382448d 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -64,9 +64,13 @@ func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.Net return nets, err } -func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]types.Network, []error, error) { +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { var errs []error - networks := make([]types.Network, 0, len(namesOrIds)) + statuses, err := ic.GetContainerNetStatuses() + if err != nil { + return nil, nil, fmt.Errorf("failed to get network status for containers: %w", err) + } + networks := make([]entities.NetworkInspectReport, 0, len(namesOrIds)) for _, name := range namesOrIds { net, err := ic.Libpod.Network().NetworkInspect(name) if err != nil { @@ -77,7 +81,22 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri return nil, nil, fmt.Errorf("inspecting network %s: %w", name, err) } } - networks = append(networks, net) + containerMap := make(map[string]entities.NetworkContainerInfo) + for _, st := range statuses { + // Make sure to only show the info for the correct network + if sb, ok := st.Status[net.Name]; ok { + containerMap[st.ID] = entities.NetworkContainerInfo{ + Name: st.Name, + Interfaces: sb.Interfaces, + } + } + } + + netReport := entities.NetworkInspectReport{ + Network: net, + Containers: containerMap, + } + networks = append(networks, netReport) } return networks, errs, nil } @@ -243,3 +262,36 @@ func (ic *ContainerEngine) createDanglingFilterFunc(wantDangling bool) (types.Fi return wantDangling }, nil } + +type ContainerNetStatus struct { + // Name of the container + Name string + // ID of the container + ID string + // Status contains the net status, the key is the network name + Status map[string]types.StatusBlock +} + +func (ic *ContainerEngine) GetContainerNetStatuses() ([]ContainerNetStatus, error) { + cons, err := ic.Libpod.GetAllContainers() + if err != nil { + return nil, err + } + statuses := make([]ContainerNetStatus, 0, len(cons)) + for _, con := range cons { + status, err := con.GetNetworkStatus() + if err != nil { + if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) { + continue + } + return nil, err + } + + statuses = append(statuses, ContainerNetStatus{ + ID: con.ID(), + Name: con.Name(), + Status: status, + }) + } + return statuses, nil +} diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index e09e247a3b..bd48f6dcaa 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -22,9 +22,9 @@ func (ic *ContainerEngine) NetworkList(ctx context.Context, opts entities.Networ return network.List(ic.ClientCtx, options) } -func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]types.Network, []error, error) { +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { var ( - reports = make([]types.Network, 0, len(namesOrIds)) + reports = make([]entities.NetworkInspectReport, 0, len(namesOrIds)) errs = []error{} ) options := new(network.InspectOptions) diff --git a/test/e2e/network_create_test.go b/test/e2e/network_create_test.go index 764f97cdbb..28edc41be3 100644 --- a/test/e2e/network_create_test.go +++ b/test/e2e/network_create_test.go @@ -5,6 +5,7 @@ import ( "net" "github.com/containers/common/libnetwork/types" + "github.com/containers/podman/v5/pkg/domain/entities" . "github.com/containers/podman/v5/test/utils" "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo/v2" @@ -32,7 +33,7 @@ var _ = Describe("Podman network create", func() { Expect(inspect).Should(ExitCleanly()) // JSON the network configuration into something usable - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -84,7 +85,7 @@ var _ = Describe("Podman network create", func() { Expect(inspect).Should(ExitCleanly()) // JSON the network configuration into something usable - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -125,7 +126,7 @@ var _ = Describe("Podman network create", func() { Expect(inspect).Should(ExitCleanly()) // JSON the network configuration into something usable - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -168,7 +169,7 @@ var _ = Describe("Podman network create", func() { Expect(inspect).Should(ExitCleanly()) // JSON the network configuration into something usable - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -213,7 +214,7 @@ var _ = Describe("Podman network create", func() { Expect(inspect).Should(ExitCleanly()) // JSON the network configuration into something usable - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -254,7 +255,7 @@ var _ = Describe("Podman network create", func() { Expect(inspect).Should(ExitCleanly()) // JSON the network configuration into something usable - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -284,7 +285,7 @@ var _ = Describe("Podman network create", func() { Expect(inspect).Should(ExitCleanly()) // JSON the network configuration into something usable - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -323,7 +324,7 @@ var _ = Describe("Podman network create", func() { Expect(inspect).Should(ExitCleanly()) // JSON the network configuration into something usable - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -711,7 +712,7 @@ var _ = Describe("Podman network create", func() { Expect(inspect).Should(ExitCleanly()) // JSON the network configuration into something usable - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index 1117eb7685..338d9b4d5b 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -6,7 +6,7 @@ import ( "path/filepath" "time" - "github.com/containers/common/libnetwork/types" + "github.com/containers/podman/v5/pkg/domain/entities" . "github.com/containers/podman/v5/test/utils" "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo/v2" @@ -530,7 +530,7 @@ var _ = Describe("Podman network", func() { Expect(inspect).Should(ExitCleanly()) // JSON the network configuration into something usable - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -556,7 +556,7 @@ var _ = Describe("Podman network", func() { inspect.WaitWithDefaultTimeout() Expect(inspect).Should(ExitCleanly()) - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -584,7 +584,7 @@ var _ = Describe("Podman network", func() { inspect.WaitWithDefaultTimeout() Expect(inspect).Should(ExitCleanly()) - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -627,7 +627,7 @@ var _ = Describe("Podman network", func() { inspect.WaitWithDefaultTimeout() Expect(inspect).Should(ExitCleanly()) - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(inspect.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index b854e76010..6f036295d4 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -10,7 +10,7 @@ import ( "syscall" "github.com/containernetworking/plugins/pkg/ns" - "github.com/containers/common/libnetwork/types" + "github.com/containers/podman/v5/pkg/domain/entities" . "github.com/containers/podman/v5/test/utils" "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo/v2" @@ -36,7 +36,7 @@ var _ = Describe("Podman run networking", func() { session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) Expect(session).Should(ExitCleanly()) - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(session.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) @@ -83,7 +83,7 @@ var _ = Describe("Podman run networking", func() { session.WaitWithDefaultTimeout() defer podmanTest.removeNetwork(net) Expect(session).Should(ExitCleanly()) - var results []types.Network + var results []entities.NetworkInspectReport err := json.Unmarshal([]byte(session.OutputToString()), &results) Expect(err).ToNot(HaveOccurred()) Expect(results).To(HaveLen(1)) diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index fc6c881124..174d2e4d47 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -953,4 +953,52 @@ EOF assert "$output" = $hostname "/etc/hostname with --uts=host --net=host must be equal to 'uname -n'" } +@test "podman network inspect running containers" { + local cname1=c1-$(random_string 10) + local cname2=c2-$(random_string 10) + local cname3=c3-$(random_string 10) + + local netname=net-$(random_string 10) + local subnet=$(random_rfc1918_subnet) + + run_podman network create --subnet "${subnet}.0/24" $netname + + run_podman network inspect --format "{{json .Containers}}" $netname + assert "$output" == "{}" "no containers on the network" + + run_podman create --name $cname1 --network $netname $IMAGE top + cid1="$output" + run_podman create --name $cname2 --network $netname $IMAGE top + cid2="$output" + + # containers should only be part of the output when they are running + run_podman network inspect --format "{{json .Containers}}" $netname + assert "$output" == "{}" "no running containers on the network" + + # start the containers to setup the network info + run_podman start $cname1 $cname2 + + # also run a third container on different network (should not be part of inspect then) + run_podman run -d --name $cname3 --network podman $IMAGE top + cid3="$output" + + # Map ordering is not deterministic so we check each container one by one + local expect="\{\"name\":\"$cname1\",\"interfaces\":\{\"eth0\":\{\"subnets\":\[\{\"ipnet\":\"${subnet}.2/24\"\,\"gateway\":\"${subnet}.1\"\}\],\"mac_address\":\"[0-9a-f]{2}:.*\"\}\}\}" + run_podman network inspect --format "{{json (index .Containers \"$cid1\")}}" $netname + assert "$output" =~ "$expect" "container 1 on the network" + + local expect="\{\"name\":\"$cname2\",\"interfaces\":\{\"eth0\":\{\"subnets\":\[\{\"ipnet\":\"${subnet}.3/24\"\,\"gateway\":\"${subnet}.1\"\}\],\"mac_address\":\"[0-9a-f]{2}:.*\"\}\}\}" + run_podman network inspect --format "{{json (index .Containers \"$cid2\")}}" $netname + assert "$output" =~ "$expect" "container 2 on the network" + + # container 3 should not be part of the inspect, index does not error if the key does not + # exists so just make sure the cid3 and cname3 are not in the json. + run_podman network inspect --format "{{json .Containers}}" $netname + assert "$output" !~ "$cid3" "container 3 on the network (cid)" + assert "$output" !~ "$cname3" "container 3 on the network (name)" + + run_podman rm -f -t0 $cname1 $cname2 $cname3 + run_podman network rm $netname +} + # vim: filetype=sh