mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-10-31 10:17:43 +00:00
events: add missing NETWORK filter to generateEventFilter
podman events --filter network=<name> was unconditionally returning 'NETWORK is an invalid filter' because the NETWORK case was absent from the generateEventFilter switch statement in filters.go. All other first-class event types (container, image, pod, volume) had corresponding filter cases, but the Network type - despite being fully defined in config.go along with NetworkConnect/NetworkDisconnect statuses and the Event.Network field - had no handler. Add the NETWORK case to filter by network name (e.Network), consistent with Docker's --filter network= behaviour. ID-prefix matching is intentionally omitted: for network connect/disconnect events e.ID holds the container ID, not the network ID, so prefix matching would only work for create/remove events and silently miss join/leave events. Also add unit tests (filters_test.go), integration tests (test/e2e/events_test.go, test/system/090-events.bats), shell completion support for --filter network= (completion.go), and document the new filter key in the man page. Fixes: https://github.com/podman-container-tools/podman/issues/29387 Signed-off-by: Aftab Ali <aftab123215@gmail.com>
This commit is contained in:
parent
67aa9604b5
commit
ff12dabebb
6 changed files with 171 additions and 1 deletions
|
|
@ -1580,7 +1580,7 @@ func getMethodNames(f reflect.Value, prefix string) []formatSuggestion {
|
|||
}
|
||||
|
||||
// AutocompleteEventFilter - Autocomplete event filter flag options.
|
||||
// -> "container=", "event=", "image=", "pod=", "volume=", "type=", "artifact="
|
||||
// -> "container=", "event=", "image=", "network=", "pod=", "volume=", "type=", "artifact="
|
||||
func AutocompleteEventFilter(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
event := func(_ string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{
|
||||
|
|
@ -1606,6 +1606,7 @@ func AutocompleteEventFilter(cmd *cobra.Command, _ []string, toComplete string)
|
|||
"container=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeDefault) },
|
||||
"image=": func(s string) ([]string, cobra.ShellCompDirective) { return getImages(cmd, s) },
|
||||
"artifact=": func(s string) ([]string, cobra.ShellCompDirective) { return getArtifacts(cmd, s) },
|
||||
"network=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeDefault) },
|
||||
"pod=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeDefault) },
|
||||
"volume=": func(s string) ([]string, cobra.ShellCompDirective) { return getVolumes(cmd, s) },
|
||||
"event=": event,
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ filters are supported:
|
|||
| event | event_status (described above) |
|
||||
| image | [Name or ID] Image name or ID |
|
||||
| label | [key] or [key=value] label |
|
||||
| network | [Name] Network name |
|
||||
| pod | [Name or ID] Pod name or ID |
|
||||
| volume | [Name or ID] Volume name or ID |
|
||||
| type | Event_type (described above) |
|
||||
|
|
|
|||
|
|
@ -59,6 +59,23 @@ func generateEventFilter(filter, filterValue string) (func(e *Event) bool, error
|
|||
}
|
||||
return strings.HasPrefix(e.ID, filterValue)
|
||||
}, nil
|
||||
case "NETWORK":
|
||||
return func(e *Event) bool {
|
||||
if e.Type != Network {
|
||||
return false
|
||||
}
|
||||
if e.Network == filterValue {
|
||||
return true
|
||||
}
|
||||
// For connect/disconnect events e.ID is the container ID, not
|
||||
// the network ID, so ID-prefix matching would produce false
|
||||
// positives. Only fall back to HasPrefix for create/remove
|
||||
// events where e.ID genuinely holds the network ID.
|
||||
if e.Status == NetworkConnect || e.Status == NetworkDisconnect {
|
||||
return false
|
||||
}
|
||||
return strings.HasPrefix(e.ID, filterValue)
|
||||
}, nil
|
||||
case "VOLUME":
|
||||
return func(e *Event) bool {
|
||||
if e.Type != Volume {
|
||||
|
|
|
|||
110
libpod/events/filters_test.go
Normal file
110
libpod/events/filters_test.go
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
//go:build linux || freebsd
|
||||
|
||||
package events
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestGenerateEventFilter_Network is a regression test for the missing NETWORK
|
||||
// filter support. Before this fix, calling generateEventFilter("network", ...)
|
||||
// returned an error ("NETWORK is an invalid filter") instead of a working
|
||||
// filter function.
|
||||
func TestGenerateEventFilter_Network(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
filterValue string
|
||||
event Event
|
||||
wantMatch bool
|
||||
}{
|
||||
{
|
||||
name: "match by network name",
|
||||
filterValue: "mynet",
|
||||
event: Event{Type: Network, Network: "mynet"},
|
||||
wantMatch: true,
|
||||
},
|
||||
{
|
||||
name: "no match when network name differs",
|
||||
filterValue: "mynet",
|
||||
event: Event{Type: Network, Network: "othernet"},
|
||||
wantMatch: false,
|
||||
},
|
||||
{
|
||||
// For create/remove events e.ID is the network ID, so an
|
||||
// ID-prefix filter should match.
|
||||
name: "match by network ID prefix on create event",
|
||||
filterValue: "abc123",
|
||||
event: Event{Type: Network, Status: Create, ID: "abc123def456", Network: "mynet"},
|
||||
wantMatch: true,
|
||||
},
|
||||
{
|
||||
// For connect/disconnect events e.ID is the container ID, not
|
||||
// the network ID. A filter value that is a prefix of the
|
||||
// container ID must NOT produce a false positive.
|
||||
name: "no false-positive on connect event with colliding container ID",
|
||||
filterValue: "abc123",
|
||||
event: Event{Type: Network, Status: NetworkConnect, ID: "abc123def456", Network: "mynet"},
|
||||
wantMatch: false,
|
||||
},
|
||||
{
|
||||
// Same guarantee for disconnect events.
|
||||
name: "no false-positive on disconnect event with colliding container ID",
|
||||
filterValue: "abc123",
|
||||
event: Event{Type: Network, Status: NetworkDisconnect, ID: "abc123def456", Network: "mynet"},
|
||||
wantMatch: false,
|
||||
},
|
||||
{
|
||||
name: "no match when event type is not Network",
|
||||
filterValue: "mynet",
|
||||
event: Event{Type: Container, Network: "mynet"},
|
||||
wantMatch: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
filter, err := generateEventFilter("network", tc.filterValue)
|
||||
require.NoError(t, err, "generateEventFilter must not return an error for network filter")
|
||||
require.NotNil(t, filter)
|
||||
require.Equal(t, tc.wantMatch, filter(&tc.event))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestGenerateEventFilter_InvalidFilter verifies that an unrecognised filter
|
||||
// key produces the expected error.
|
||||
func TestGenerateEventFilter_InvalidFilter(t *testing.T) {
|
||||
_, err := generateEventFilter("doesnotexist", "value")
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "invalid filter")
|
||||
}
|
||||
|
||||
// TestGenerateEventFilter_KnownFilters is a smoke-test that all documented
|
||||
// filter keys are accepted without error.
|
||||
func TestGenerateEventFilter_KnownFilters(t *testing.T) {
|
||||
knownFilters := []struct {
|
||||
key string
|
||||
value string
|
||||
}{
|
||||
{"container", "mycontainer"},
|
||||
{"event", "start"},
|
||||
{"status", "stop"},
|
||||
{"image", "alpine"},
|
||||
{"artifact", "myartifact"},
|
||||
{"pod", "mypod"},
|
||||
{"volume", "myvol"},
|
||||
{"network", "mynet"},
|
||||
{"type", "container"},
|
||||
{"label", "mykey=myvalue"},
|
||||
}
|
||||
|
||||
for _, kf := range knownFilters {
|
||||
t.Run(kf.key, func(t *testing.T) {
|
||||
filter, err := generateEventFilter(kf.key, kf.value)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, filter)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -350,4 +350,24 @@ var _ = Describe("Podman events", func() {
|
|||
Expect(events[2]).To(And(ContainSubstring("artifact remove"), ContainSubstring(artifactName)), "event log includes artifact remove")
|
||||
Expect(events[3]).To(And(ContainSubstring("artifact pull"), ContainSubstring(artifactName)), "event log includes artifact pull")
|
||||
})
|
||||
It("podman events with a network filter", func() {
|
||||
netName := "net-" + stringid.GenerateRandomID()[:10]
|
||||
session := podmanTest.Podman([]string{"network", "create", netName})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitCleanly())
|
||||
|
||||
rm := podmanTest.Podman([]string{"network", "rm", netName})
|
||||
rm.WaitWithDefaultTimeout()
|
||||
Expect(rm).Should(ExitCleanly())
|
||||
|
||||
result := podmanTest.Podman([]string{"events", "--stream=false", "--since", "1m", "--filter", "network=" + netName})
|
||||
result.WaitWithDefaultTimeout()
|
||||
Expect(result).Should(ExitCleanly())
|
||||
events := result.OutputToStringArray()
|
||||
Expect(events).To(HaveLen(2), "number of network events")
|
||||
Expect(events[0]).To(ContainSubstring("network create"), "first event is network create")
|
||||
Expect(events[0]).To(ContainSubstring(netName), "create event includes network name")
|
||||
Expect(events[1]).To(ContainSubstring("network remove"), "second event is network remove")
|
||||
Expect(events[1]).To(ContainSubstring(netName), "remove event includes network name")
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -481,3 +481,24 @@ EOF
|
|||
|
||||
run_podman pod rm -f $pname
|
||||
}
|
||||
|
||||
# bats test_tags=ci:parallel
|
||||
@test "events - network filter" {
|
||||
local netname=net-$(safename)
|
||||
|
||||
# Create and immediately remove a network so we have events to filter.
|
||||
run_podman network create $netname
|
||||
run_podman network rm $netname
|
||||
|
||||
# --filter network=<name> must return both create and remove events.
|
||||
run_podman events --since=1m --stream=false --filter network=$netname
|
||||
assert "${lines[0]}" =~ ".* network create .*name=$netname," \
|
||||
"network create event returned by --filter network="
|
||||
assert "${lines[1]}" =~ ".* network remove .*name=$netname," \
|
||||
"network remove event returned by --filter network="
|
||||
assert "${#lines[@]}" = "2" "exactly two network events for $netname"
|
||||
|
||||
# A different name must not match.
|
||||
run_podman events --since=1m --stream=false --filter network=nosuchnetwork-$(safename)
|
||||
assert "$output" = "" "no events for a non-existent network name"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue