Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package falcosecurity-container-plugin for 
openSUSE:Factory checked in at 2026-09-24 22:58:01
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/falcosecurity-container-plugin (Old)
 and      /work/SRC/openSUSE:Factory/.falcosecurity-container-plugin.new.383539 
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "falcosecurity-container-plugin"

Thu Sep 24 22:58:01 2026 rev:4 rq:1380077 version:0.7.5

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/falcosecurity-container-plugin/falcosecurity-container-plugin.changes
    2026-09-11 18:05:39.510686986 +0200
+++ 
/work/SRC/openSUSE:Factory/.falcosecurity-container-plugin.new.383539/falcosecurity-container-plugin.changes
        2026-09-24 23:00:19.744428031 +0200
@@ -1,0 +2,11 @@
+Wed Sep 23 19:25:20 UTC 2026 - Martin Pluskal <[email protected]>
+
+- Update to version 0.7.5:
+  * chore(plugins/container): bump version to 0.7.5
+  * fix(plugins/container): unblock listener shutdown
+  * build(deps): bump 
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
+  * build(deps): bump go.opentelemetry.io/otel/sdk
+- Export GOROOT for the go-worker build: the trimmed
+  distribution go binary cannot locate it alone
+
+-------------------------------------------------------------------

Old:
----
  container-plugin-0.7.4.tar

New:
----
  container-plugin-0.7.5.tar

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ falcosecurity-container-plugin.spec ++++++
--- /var/tmp/diff_new_pack.imQnhz/_old  2026-09-24 23:00:20.805472400 +0200
+++ /var/tmp/diff_new_pack.imQnhz/_new  2026-09-24 23:00:20.807472484 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           falcosecurity-container-plugin
-Version:        0.7.4
+Version:        0.7.5
 Release:        0
 Summary:        Falcosecurity plugin providing container metadata
 # Legal-Review-Notice: MPL-2.0 from vendored Go modules statically
@@ -63,6 +63,8 @@
 export GOPROXY=off
 export GOTOOLCHAIN=local
 export GOCACHE=%{_builddir}/.gocache
+# The distribution go binary is trimmed and cannot locate its GOROOT alone.
+export GOROOT=$(go env GOROOT)
 %define __builder ninja
 # ENABLE_TESTS pulls in a from-source libsinsp (sinsp_test_support); keep it 
OFF
 # so the plugin builds against the system stack only.

++++++ _service ++++++
--- /var/tmp/diff_new_pack.imQnhz/_old  2026-09-24 23:00:20.866474951 +0200
+++ /var/tmp/diff_new_pack.imQnhz/_new  2026-09-24 23:00:20.870475119 +0200
@@ -6,10 +6,10 @@
   <service name="obs_scm" mode="manual">
     <param name="url">https://github.com/falcosecurity/plugins.git</param>
     <param name="scm">git</param>
-    <param name="revision">refs/tags/plugins/container/v0.7.4</param>
+    <param name="revision">refs/tags/plugins/container/v0.7.5</param>
     <param name="subdir">plugins/container</param>
     <param name="filename">container-plugin</param>
-    <param name="versionformat">0.7.4</param>
+    <param name="versionformat">0.7.5</param>
     <param name="changesgenerate">enable</param>
   </service>
   <service name="tar" mode="manual"/>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.imQnhz/_old  2026-09-24 23:00:20.898476290 +0200
+++ /var/tmp/diff_new_pack.imQnhz/_new  2026-09-24 23:00:20.908476708 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/falcosecurity/plugins.git</param>
-              <param 
name="changesrevision">efa4416dd148f0127499b23bb4ffa49bd417c385</param></service></servicedata>
+              <param 
name="changesrevision">a246c88790db1180f8b15411a5fb20ce853f67a4</param></service></servicedata>
 (No newline at EOF)
 

++++++ container-plugin-0.7.4.tar -> container-plugin-0.7.5.tar ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/container-plugin-0.7.4/CMakeLists.txt 
new/container-plugin-0.7.5/CMakeLists.txt
--- old/container-plugin-0.7.4/CMakeLists.txt   2026-09-10 17:06:26.000000000 
+0200
+++ new/container-plugin-0.7.5/CMakeLists.txt   2026-09-22 18:51:59.000000000 
+0200
@@ -9,7 +9,7 @@
 # project metadata
 project(
         container
-        VERSION 0.7.4
+        VERSION 0.7.5
         DESCRIPTION "Falco container metadata enrichment Plugin"
         LANGUAGES CXX)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/container-plugin-0.7.4/go-worker/go.mod 
new/container-plugin-0.7.5/go-worker/go.mod
--- old/container-plugin-0.7.4/go-worker/go.mod 2026-09-10 17:06:26.000000000 
+0200
+++ new/container-plugin-0.7.5/go-worker/go.mod 2026-09-22 18:51:59.000000000 
+0200
@@ -75,7 +75,7 @@
        github.com/felixge/httpsnoop v1.0.4 // indirect
        github.com/fsnotify/fsnotify v1.10.1 // indirect
        github.com/go-jose/go-jose/v4 v4.1.4 // indirect
-       github.com/go-logr/logr v1.4.3 // indirect
+       github.com/go-logr/logr v1.4.4 // indirect
        github.com/go-logr/stdr v1.2.2 // indirect
        github.com/godbus/dbus/v5 v5.2.2 // indirect
        github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // 
indirect
@@ -142,13 +142,13 @@
        go.opencensus.io v0.24.0 // indirect
        go.opentelemetry.io/auto/sdk v1.2.1 // indirect
        go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 
// indirect
-       go.opentelemetry.io/otel v1.44.0 // indirect
-       go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
-       go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 
// indirect
-       go.opentelemetry.io/otel/metric v1.44.0 // indirect
-       go.opentelemetry.io/otel/sdk v1.44.0 // indirect
-       go.opentelemetry.io/otel/trace v1.44.0 // indirect
-       go.opentelemetry.io/proto/otlp v1.10.0 // indirect
+       go.opentelemetry.io/otel v1.45.0 // indirect
+       go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 // indirect
+       go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0 
// indirect
+       go.opentelemetry.io/otel/metric v1.45.0 // indirect
+       go.opentelemetry.io/otel/sdk v1.45.0 // indirect
+       go.opentelemetry.io/otel/trace v1.45.0 // indirect
+       go.opentelemetry.io/proto/otlp v1.11.0 // indirect
        go.podman.io/image/v5 v5.40.0 // indirect
        go.podman.io/podman/v6 v6.0.2
        go.podman.io/storage v1.63.0 // indirect
@@ -161,8 +161,8 @@
        golang.org/x/term v0.45.0 // indirect
        golang.org/x/text v0.41.0 // indirect
        golang.org/x/time v0.15.0 // indirect
-       google.golang.org/genproto/googleapis/api 
v0.0.0-20260526163538-3dc84a4a5aaa // indirect
-       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260526163538-3dc84a4a5aaa // indirect
+       google.golang.org/genproto/googleapis/api 
v0.0.0-20260803160001-6ac0973c030d // indirect
+       google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260803160001-6ac0973c030d // indirect
        google.golang.org/grpc v1.83.2
        google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af
        gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/container-plugin-0.7.4/go-worker/go.sum 
new/container-plugin-0.7.5/go-worker/go.sum
--- old/container-plugin-0.7.4/go-worker/go.sum 2026-09-10 17:06:26.000000000 
+0200
+++ new/container-plugin-0.7.5/go-worker/go.sum 2026-09-22 18:51:59.000000000 
+0200
@@ -119,8 +119,8 @@
 github.com/go-jose/go-jose/v4 v4.1.4 
h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
 github.com/go-jose/go-jose/v4 v4.1.4/go.mod 
h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
 github.com/go-logr/logr v1.2.2/go.mod 
h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
-github.com/go-logr/logr v1.4.3/go.mod 
h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
+github.com/go-logr/logr v1.4.4/go.mod 
h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod 
h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-task/slim-sprig/v3 v3.0.0 
h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
@@ -352,24 +352,24 @@
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc 
v0.69.0/go.mod h1:D7J12YRapIekYyPWgGPlA/23pRmpSEZC5xJC/TTLI9U=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 
h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod 
h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI=
-go.opentelemetry.io/otel v1.44.0 
h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
-go.opentelemetry.io/otel v1.44.0/go.mod 
h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 
h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod 
h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 
h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod 
h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo=
+go.opentelemetry.io/otel v1.45.0 
h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
+go.opentelemetry.io/otel v1.45.0/go.mod 
h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 
h1:QRefszxJmfPdjXUUm3j6iDzY03mTPXMjqErFqQ67vUg=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0/go.mod 
h1:Tiz03lTBVBrm7eWZBOidzEaYaJa8tjwGUGv6d8mlTyk=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0 
h1:fG5MCxGz8+2VtrN/WgqSpJFctVz24gpxj8CxkKmc8Ww=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0/go.mod 
h1:BmAYTn+3ysbRe+IU2msxmf5Rx3g6DHvex+tWI3LdhYI=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 
h1:lgh3PiVrRUWMLOVSkQicxzZll5NjF1r+AtsX1XRIHw0=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0/go.mod 
h1:5Cnhth3m/AgOeTgE3ex12pPmiu/gGtZit03kSzx9X7s=
-go.opentelemetry.io/otel/metric v1.44.0 
h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
-go.opentelemetry.io/otel/metric v1.44.0/go.mod 
h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
-go.opentelemetry.io/otel/sdk v1.44.0 
h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
-go.opentelemetry.io/otel/sdk v1.44.0/go.mod 
h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
-go.opentelemetry.io/otel/sdk/metric v1.44.0 
h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
-go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod 
h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
-go.opentelemetry.io/otel/trace v1.44.0 
h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
-go.opentelemetry.io/otel/trace v1.44.0/go.mod 
h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
-go.opentelemetry.io/proto/otlp v1.10.0 
h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
-go.opentelemetry.io/proto/otlp v1.10.0/go.mod 
h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
+go.opentelemetry.io/otel/metric v1.45.0 
h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
+go.opentelemetry.io/otel/metric v1.45.0/go.mod 
h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
+go.opentelemetry.io/otel/sdk v1.45.0 
h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
+go.opentelemetry.io/otel/sdk v1.45.0/go.mod 
h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
+go.opentelemetry.io/otel/sdk/metric v1.45.0 
h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
+go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod 
h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
+go.opentelemetry.io/otel/trace v1.45.0 
h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
+go.opentelemetry.io/otel/trace v1.45.0/go.mod 
h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
+go.opentelemetry.io/proto/otlp v1.11.0 
h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk=
+go.opentelemetry.io/proto/otlp v1.11.0/go.mod 
h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E=
 go.podman.io/buildah v1.44.1 h1:WQhr/LK3do8ENLHYTls+wUIx6XWZPghZ4VH232WbL0U=
 go.podman.io/buildah v1.44.1/go.mod 
h1:hi+WSERjv19PU2CJ+AKbgwCO3IKuvtDXEUA8eXNkpqk=
 go.podman.io/common v0.68.1 h1:y8NoHLidghpgHtWtwB9+pN7cHhIDe33g1sB4RFCRvKI=
@@ -501,10 +501,10 @@
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod 
h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod 
h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod 
h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa 
h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
-google.golang.org/genproto/googleapis/api 
v0.0.0-20260526163538-3dc84a4a5aaa/go.mod 
h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa 
h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
-google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260526163538-3dc84a4a5aaa/go.mod 
h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d 
h1:FarXi840EJWSHYTN3ERkADbPWjl307+FGrA22KAVjjc=
+google.golang.org/genproto/googleapis/api 
v0.0.0-20260803160001-6ac0973c030d/go.mod 
h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d 
h1:IL4hdHzcUv2l/gcg98/Rj3FbtE6axwqslOW8SW0C+S0=
+google.golang.org/genproto/googleapis/rpc 
v0.0.0-20260803160001-6ac0973c030d/go.mod 
h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
 google.golang.org/grpc v1.19.0/go.mod 
h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod 
h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod 
h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/container-plugin-0.7.4/go-worker/pkg/container/containerd.go 
new/container-plugin-0.7.5/go-worker/pkg/container/containerd.go
--- old/container-plugin-0.7.4/go-worker/pkg/container/containerd.go    
2026-09-10 17:06:26.000000000 +0200
+++ new/container-plugin-0.7.5/go-worker/pkg/container/containerd.go    
2026-09-22 18:51:59.000000000 +0200
@@ -427,9 +427,13 @@
                                } else {
                                        info, _ = 
c.ctrToInfo(namespacedContext, container)
                                }
-                               outCh <- event.Event{
+                               select {
+                               case outCh <- event.Event{
                                        Info:     info,
                                        IsCreate: isCreate,
+                               }:
+                               case <-ctx.Done():
+                                       return
                                }
                        }
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/container-plugin-0.7.4/go-worker/pkg/container/containerd_listener_test.go 
new/container-plugin-0.7.5/go-worker/pkg/container/containerd_listener_test.go
--- 
old/container-plugin-0.7.4/go-worker/pkg/container/containerd_listener_test.go  
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/container-plugin-0.7.5/go-worker/pkg/container/containerd_listener_test.go  
    2026-09-22 18:51:59.000000000 +0200
@@ -0,0 +1,109 @@
+package container
+
+import (
+       "context"
+       "log/slog"
+       "net"
+       "path/filepath"
+       "strings"
+       "sync"
+       "testing"
+       "time"
+
+       ctEvents "github.com/containerd/containerd/api/events"
+       containersapi 
"github.com/containerd/containerd/api/services/containers/v1"
+       eventsapi "github.com/containerd/containerd/api/services/events/v1"
+       typesapi "github.com/containerd/containerd/api/types"
+       "github.com/containerd/typeurl/v2"
+       "github.com/stretchr/testify/require"
+       "google.golang.org/grpc"
+       "google.golang.org/grpc/codes"
+       "google.golang.org/grpc/status"
+)
+
+type shutdownEventService struct {
+       eventsapi.UnimplementedEventsServer
+}
+
+func (*shutdownEventService) Subscribe(_ *eventsapi.SubscribeRequest, stream 
eventsapi.Events_SubscribeServer) error {
+       evt, err := typeurl.MarshalAnyToProto(&ctEvents.ContainerCreate{ID: 
strings.Repeat("a", 64), Image: "alpine:latest"})
+       if err != nil {
+               return err
+       }
+       if err := stream.Send(&typesapi.Envelope{Namespace: "k8s.io", Topic: 
"/containers/create", Event: evt}); err != nil {
+               return err
+       }
+       <-stream.Context().Done()
+       return stream.Context().Err()
+}
+
+type shutdownContainerService struct {
+       containersapi.UnimplementedContainersServer
+       inspecting chan struct{}
+       release    chan struct{}
+}
+
+func (s *shutdownContainerService) Get(ctx context.Context, _ 
*containersapi.GetContainerRequest) (*containersapi.GetContainerResponse, 
error) {
+       close(s.inspecting)
+       select {
+       case <-ctx.Done():
+               return nil, status.FromContextError(ctx.Err()).Err()
+       case <-s.release:
+               return nil, status.Error(codes.NotFound, "container already 
removed")
+       }
+}
+
+func TestContainerdListenShutdown(t *testing.T) {
+       for _, receive := range []bool{true, false} {
+               name := "cancel_during_inspection"
+               if receive {
+                       name = "delivered_fallback"
+               }
+               t.Run(name, func(t *testing.T) {
+                       s := &shutdownContainerService{inspecting: make(chan 
struct{}), release: make(chan struct{})}
+                       socket := filepath.Join(t.TempDir(), "runtime.sock")
+                       listener, err := net.Listen("unix", socket)
+                       require.NoError(t, err)
+                       server := grpc.NewServer()
+                       containersapi.RegisterContainersServer(server, s)
+                       eventsapi.RegisterEventsServer(server, 
&shutdownEventService{})
+                       go func() { _ = server.Serve(listener) }()
+                       t.Cleanup(server.Stop)
+                       engine, err := 
newContainerdEngine(context.Background(), slog.Default(), socket)
+                       require.NoError(t, err)
+                       t.Cleanup(func() { _ = 
engine.(*containerdEngine).client.Close() })
+                       ctx, cancel := context.WithCancel(context.Background())
+                       defer cancel()
+                       var wg sync.WaitGroup
+                       out, err := engine.Listen(ctx, &wg)
+                       require.NoError(t, err)
+                       t.Cleanup(func() {
+                               cancel()
+                               go func() {
+                                       for range out {
+                                       }
+                               }()
+                       })
+                       select {
+                       case <-s.inspecting:
+                       case <-time.After(5 * time.Second):
+                               t.Fatal("metadata request not started")
+                       }
+                       if receive {
+                               close(s.release)
+                               evt := waitOnChannelOrTimeout(t, out)
+                               require.True(t, evt.IsCreate)
+                               require.Equal(t, strings.Repeat("a", 64), 
evt.FullID)
+                               require.Equal(t, "alpine:latest", evt.Image)
+                       }
+                       cancel()
+                       done := make(chan struct{})
+                       go func() { wg.Wait(); close(done) }()
+                       select {
+                       case <-done:
+                       case <-time.After(5 * time.Second):
+                               t.Fatal("listener did not stop without an event 
receiver")
+                       }
+               })
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/container-plugin-0.7.4/go-worker/pkg/container/cri.go 
new/container-plugin-0.7.5/go-worker/pkg/container/cri.go
--- old/container-plugin-0.7.4/go-worker/pkg/container/cri.go   2026-09-10 
17:06:26.000000000 +0200
+++ new/container-plugin-0.7.5/go-worker/pkg/container/cri.go   2026-09-22 
18:51:59.000000000 +0200
@@ -489,7 +489,15 @@
 // In case events have been disabled in the criEngine,
 // an error will be captured and passed to the caller.
 func (c *criEngine) Listen(ctx context.Context, wg *sync.WaitGroup) (<-chan 
event.Event, error) {
+       ctx, cancel := context.WithCancel(ctx)
        containerEventsCh := make(chan *v1.ContainerEventResponse)
+       // The CRI client can be blocked sending an event when its receiver 
exits.
+       // Cancel the RPC, then receive until the producer closes its channel.
+       stop := func() {
+               cancel()
+               for range containerEventsCh {
+               }
+       }
        // Buffered to prevent blocking; only the first subscription reports 
through it.
        containerEventsErrorCh := make(chan error, 1)
        wg.Add(1)
@@ -532,6 +540,7 @@
        select {
        case err := <-containerEventsErrorCh:
                if err != nil {
+                       stop()
                        return nil, err
                }
        case <-time.After(containerEventsErrorTimeout):
@@ -543,6 +552,7 @@
        go func() {
                defer close(outCh)
                defer wg.Done()
+               defer stop()
                for {
                        select {
                        case <-ctx.Done():
@@ -611,8 +621,12 @@
                }
                info = c.ctrToInfo(ctx, ctr.GetStatus(), cPodSandbox, 
ctr.GetInfo(), podSandboxStatus.GetInfo())
        }
-       outCh <- event.Event{
+       select {
+       case outCh <- event.Event{
                Info:     info,
                IsCreate: evt.ContainerEventType != 
v1.ContainerEventType_CONTAINER_DELETED_EVENT,
+       }:
+       case <-ctx.Done():
+               return
        }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/container-plugin-0.7.4/go-worker/pkg/container/cri_test.go 
new/container-plugin-0.7.5/go-worker/pkg/container/cri_test.go
--- old/container-plugin-0.7.4/go-worker/pkg/container/cri_test.go      
2026-09-10 17:06:26.000000000 +0200
+++ new/container-plugin-0.7.5/go-worker/pkg/container/cri_test.go      
2026-09-22 18:51:59.000000000 +0200
@@ -6,9 +6,11 @@
        "errors"
        "fmt"
        "log/slog"
+       "strings"
        "sync"
        "sync/atomic"
        "testing"
+       "testing/synctest"
        "time"
 
        "github.com/google/uuid"
@@ -656,6 +658,133 @@
        }
 }
 
+type shutdownRuntimeService struct {
+       internalapi.RuntimeService
+       inspecting   chan struct{}
+       release      chan struct{}
+       queued       chan struct{}
+       finished     chan struct{}
+       finishedOnce sync.Once
+       sourceError  error
+       queueNext    bool
+}
+
+func (s *shutdownRuntimeService) GetContainerEvents(ctx context.Context, out 
chan *v1.ContainerEventResponse, _ 
func(v1.RuntimeService_GetContainerEventsClient)) error {
+       defer s.finishedOnce.Do(func() { close(s.finished) })
+       if s.sourceError != nil {
+               return s.sourceError
+       }
+       evt := &v1.ContainerEventResponse{ContainerId: strings.Repeat("b", 64), 
ContainerEventType: v1.ContainerEventType_CONTAINER_CREATED_EVENT}
+       // Match cri-client: once Recv returns an event, its channel send does 
not
+       // select on ctx.Done. Cancellation alone cannot release the producer.
+       out <- evt
+       if s.queueNext {
+               close(s.queued)
+               out <- evt
+       }
+       <-ctx.Done()
+       return ctx.Err()
+}
+
+func (s *shutdownRuntimeService) ContainerStatus(ctx context.Context, _ 
string, _ bool) (*v1.ContainerStatusResponse, error) {
+       select {
+       case <-s.inspecting:
+       default:
+               close(s.inspecting)
+       }
+       select {
+       case <-ctx.Done():
+               return nil, ctx.Err()
+       case <-s.release:
+               return nil, errors.New("container already removed")
+       }
+}
+
+func TestCRIListenShutdown(t *testing.T) {
+       for _, tc := range []struct {
+               name               string
+               receive, queueNext bool
+       }{
+               {name: "delivered_fallback", receive: true},
+               {name: "cancel_during_inspection"},
+               {name: "cancel_with_pending_source_send", queueNext: true},
+       } {
+               t.Run(tc.name, func(t *testing.T) {
+                       config.Load(`{"hooks":7}`)
+                       s := &shutdownRuntimeService{inspecting: make(chan 
struct{}), release: make(chan struct{}), queued: make(chan struct{}), finished: 
make(chan struct{}), queueNext: tc.queueNext}
+                       engine := &criEngine{client: s, logger: slog.Default(), 
runtime: typeCri.ToCTValue()}
+                       ctx, cancel := context.WithCancel(context.Background())
+                       defer cancel()
+                       var wg sync.WaitGroup
+                       out, err := engine.Listen(ctx, &wg)
+                       require.NoError(t, err)
+                       // Rescue a broken implementation after the assertion, 
so a failing
+                       // regression does not leave a producer blocked for the 
rest of the suite.
+                       t.Cleanup(func() {
+                               cancel()
+                               go func() {
+                                       for range out {
+                                       }
+                               }()
+                       })
+                       select {
+                       case <-s.inspecting:
+                       case <-time.After(5 * time.Second):
+                               t.Fatal("metadata request not started")
+                       }
+                       if tc.queueNext {
+                               select {
+                               case <-s.queued:
+                               case <-time.After(5 * time.Second):
+                                       t.Fatal("second source event not 
queued")
+                               }
+                       }
+                       if tc.receive {
+                               close(s.release)
+                               evt := waitOnChannelOrTimeout(t, out)
+                               require.True(t, evt.IsCreate)
+                               require.Equal(t, strings.Repeat("b", 64), 
evt.FullID)
+                               require.True(t, evt.IsPodSandbox)
+                       }
+                       cancel()
+                       done := make(chan struct{})
+                       go func() { wg.Wait(); close(done) }()
+                       select {
+                       case <-done:
+                       case <-time.After(5 * time.Second):
+                               t.Fatal("listener did not stop without an event 
receiver")
+                       }
+                       select {
+                       case <-s.finished:
+                       default:
+                               t.Fatal("upstream producer still running")
+                       }
+               })
+       }
+}
+
+func TestCRIListenInitialErrorStopsProducer(t *testing.T) {
+       synctest.Test(t, func(t *testing.T) {
+               errUnavailable := errors.New("event stream unavailable")
+               s := &shutdownRuntimeService{sourceError: errUnavailable, 
finished: make(chan struct{})}
+               engine := &criEngine{client: s, logger: slog.Default()}
+               ctx, cancel := context.WithCancel(context.Background())
+               defer cancel()
+               var wg sync.WaitGroup
+               out, err := engine.Listen(ctx, &wg)
+               require.ErrorIs(t, err, errUnavailable)
+               require.Nil(t, out)
+               require.NoError(t, ctx.Err(), "failure cleanup must not cancel 
the caller")
+               done := make(chan struct{})
+               go func() { wg.Wait(); close(done) }()
+               select {
+               case <-done:
+               case <-time.After(time.Second):
+                       t.Fatal("failed listener left its producer retrying")
+               }
+       })
+}
+
 // restartingRuntimeService serves one batch of events per call to
 // GetContainerEvents, then ends the stream the way a runtime restart does.
 type restartingRuntimeService struct {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/container-plugin-0.7.4/go-worker/pkg/container/docker.go 
new/container-plugin-0.7.5/go-worker/pkg/container/docker.go
--- old/container-plugin-0.7.4/go-worker/pkg/container/docker.go        
2026-09-10 17:06:26.000000000 +0200
+++ new/container-plugin-0.7.5/go-worker/pkg/container/docker.go        
2026-09-22 18:51:59.000000000 +0200
@@ -336,9 +336,13 @@
                                        dc.logger.LogAttrs(ctx, 
config.LevelTrace, "container create or start event", 
slog.String("container_id", msg.Actor.ID))
                                        ctrJson, _, err = 
dc.ContainerInspectWithRaw(ctx, msg.Actor.ID, config.GetWithSize())
                                        if err == nil {
-                                               outCh <- event.Event{
+                                               select {
+                                               case outCh <- event.Event{
                                                        Info:     
dc.ctrToInfo(ctx, ctrJson),
                                                        IsCreate: true,
+                                               }:
+                                               case <-ctx.Done():
+                                                       return
                                                }
                                        }
                                case events.ActionDestroy:
@@ -350,7 +354,8 @@
                                // AND as a fallback whenever 
ContainerInspectWithRaw fails.
                                if err != nil {
                                        // At least send an event with the 
minimum set of data
-                                       outCh <- event.Event{
+                                       select {
+                                       case outCh <- event.Event{
                                                Info: event.Info{
                                                        Container: 
event.Container{
                                                                Type:   
typeDocker.ToCTValue(),
@@ -360,6 +365,9 @@
                                                        },
                                                },
                                                IsCreate: msg.Action != 
events.ActionDestroy,
+                                       }:
+                                       case <-ctx.Done():
+                                               return
                                        }
                                }
                        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/container-plugin-0.7.4/go-worker/pkg/container/docker_listener_test.go 
new/container-plugin-0.7.5/go-worker/pkg/container/docker_listener_test.go
--- old/container-plugin-0.7.4/go-worker/pkg/container/docker_listener_test.go  
1970-01-01 01:00:00.000000000 +0100
+++ new/container-plugin-0.7.5/go-worker/pkg/container/docker_listener_test.go  
2026-09-22 18:51:59.000000000 +0200
@@ -0,0 +1,185 @@
+package container
+
+import (
+       "context"
+       "encoding/json"
+       "io"
+       "log/slog"
+       "net"
+       "net/http"
+       "path/filepath"
+       "strings"
+       "sync"
+       "testing"
+       "time"
+
+       "github.com/docker/docker/client"
+       "github.com/stretchr/testify/require"
+
+       
"github.com/falcosecurity/plugins/plugins/container/go-worker/pkg/config"
+)
+
+// Docker and Podman expose the same event stream format. Keep the inspection
+// blocked until the test either allows a response or cancels the listener.
+func newListenerAPI(t *testing.T, missing bool) (string, <-chan struct{}, 
chan<- struct{}, <-chan struct{}) {
+       t.Helper()
+       socket := filepath.Join(t.TempDir(), "runtime.sock")
+       listener, err := net.Listen("unix", socket)
+       require.NoError(t, err)
+       inspecting, release, disconnected := make(chan struct{}), make(chan 
struct{}), make(chan struct{})
+       var inspectOnce sync.Once
+       server := &http.Server{Handler: http.HandlerFunc(func(w 
http.ResponseWriter, r *http.Request) {
+               w.Header().Set("Content-Type", "application/json")
+               switch {
+               case strings.HasSuffix(r.URL.Path, "/_ping"):
+                       w.Header().Set("API-Version", "1.47")
+                       w.Header().Set("Libpod-API-Version", "6.0.0")
+                       w.Header().Set("OSType", "linux")
+               case strings.HasSuffix(r.URL.Path, "/events"):
+                       // More than one event exercises a producer delivering 
the next
+                       // event while the listener is still inspecting the 
first one.
+                       for range 3 {
+                               _ = 
json.NewEncoder(w).Encode(map[string]any{"Type": "container", "Action": 
"create", "Actor": map[string]any{"ID": fakeDockerID(1)}})
+                       }
+                       w.(http.Flusher).Flush()
+                       <-r.Context().Done()
+                       close(disconnected)
+               case strings.Contains(r.URL.Path, "/containers/") && 
strings.HasSuffix(r.URL.Path, "/json"):
+                       inspectOnce.Do(func() { close(inspecting) })
+                       select {
+                       case <-r.Context().Done():
+                               return
+                       case <-release:
+                       }
+                       if missing {
+                               w.WriteHeader(http.StatusNotFound)
+                               _ = 
json.NewEncoder(w).Encode(map[string]string{"message": "container already 
removed"})
+                               return
+                       }
+                       _ = json.NewEncoder(w).Encode(map[string]any{
+                               "Id": fakeDockerID(1), "Name": 
"/listener-test", "Created": "2026-09-09T00:00:00Z",
+                               "Image": "sha256:" + strings.Repeat("a", 64), 
"ImageName": "alpine:latest",
+                               "Config": map[string]any{"Image": 
"alpine:latest", "Labels": map[string]string{"app": "test"}},
+                       })
+               default:
+                       w.WriteHeader(http.StatusNotFound)
+               }
+       })}
+       go func() { _ = server.Serve(listener) }()
+       t.Cleanup(func() { _ = server.Close() })
+       return socket, inspecting, release, disconnected
+}
+
+func testHTTPListenerShutdown(t *testing.T, generate engineGenerator, observe 
func(Engine, chan struct{})) {
+       t.Helper()
+       for _, tc := range []struct {
+               name                                 string
+               receive, missing, cancelAfterInspect bool
+       }{
+               {name: "delivered_metadata", receive: true},
+               {name: "delivered_fallback", receive: true, missing: true},
+               {name: "cancel_during_inspection"},
+               {name: "cancel_after_successful_inspection", 
cancelAfterInspect: true},
+       } {
+               t.Run(tc.name, func(t *testing.T) {
+                       config.Load(`{"hooks":7}`)
+                       socket, inspecting, release, disconnected := 
newListenerAPI(t, tc.missing)
+                       // The engine context deliberately outlives the 
listener: a listener
+                       // must not depend on cancellation of the entire 
connection.
+                       engineCtx, stopEngine := 
context.WithCancel(context.Background())
+                       defer stopEngine()
+                       engine, err := generate(engineCtx, slog.Default(), 
socket)
+                       require.NoError(t, err)
+                       inspected := make(chan struct{})
+                       observe(engine, inspected)
+                       ctx, cancel := context.WithCancel(context.Background())
+                       defer cancel()
+                       var wg sync.WaitGroup
+                       out, err := engine.Listen(ctx, &wg)
+                       require.NoError(t, err)
+                       t.Cleanup(func() {
+                               cancel()
+                               go func() {
+                                       for range out {
+                                       }
+                               }()
+                       })
+                       select {
+                       case <-inspecting:
+                       case <-time.After(5 * time.Second):
+                               t.Fatal("metadata request not started")
+                       }
+                       if tc.receive {
+                               close(release)
+                               for range 3 {
+                                       evt := waitOnChannelOrTimeout(t, out)
+                                       require.True(t, evt.IsCreate)
+                                       require.Equal(t, fakeDockerID(1), 
evt.FullID)
+                                       if !tc.missing {
+                                               require.Equal(t, 
"listener-test", evt.Name)
+                                               require.Equal(t, "test", 
evt.Labels["app"])
+                                       }
+                               }
+                       }
+                       if tc.cancelAfterInspect {
+                               close(release)
+                               select {
+                               case <-inspected:
+                               case <-time.After(5 * time.Second):
+                                       t.Fatal("successful inspection not 
completed")
+                               }
+                       }
+                       cancel()
+                       done := make(chan struct{})
+                       go func() { wg.Wait(); close(done) }()
+                       select {
+                       case <-done:
+                       case <-time.After(5 * time.Second):
+                               t.Fatal("listener did not stop without an event 
receiver")
+                       }
+                       select {
+                       case <-disconnected:
+                       case <-time.After(5 * time.Second):
+                               t.Fatal("event stream still connected after 
listener stopped")
+                       }
+                       require.NoError(t, engineCtx.Err())
+               })
+       }
+}
+
+func TestDockerListenShutdown(t *testing.T) {
+       testHTTPListenerShutdown(t, newDockerEngine, func(engine Engine, 
inspected chan struct{}) {
+               dc := engine.(*dockerEngine)
+               httpClient := dc.HTTPClient()
+               httpClient.Transport = &inspectionTransport{RoundTripper: 
httpClient.Transport, inspected: inspected}
+               require.NoError(t, client.WithHTTPClient(httpClient)(dc.Client))
+       })
+}
+
+// The SDK closes an inspection response after parsing it, before returning
+// successfully. This lets a test cancel at the successful event-delivery path.
+type inspectionTransport struct {
+       http.RoundTripper
+       inspected chan struct{}
+       once      sync.Once
+}
+
+func (tr *inspectionTransport) RoundTrip(req *http.Request) (*http.Response, 
error) {
+       resp, err := tr.RoundTripper.RoundTrip(req)
+       if err == nil && strings.Contains(req.URL.Path, "/containers/") && 
strings.HasSuffix(req.URL.Path, "/json") {
+               tr.once.Do(func() { resp.Body = 
&observedResponseBody{ReadCloser: resp.Body, closed: tr.inspected} })
+       }
+       return resp, err
+}
+
+type observedResponseBody struct {
+       io.ReadCloser
+       closed chan struct{}
+       once   sync.Once
+}
+
+func (b *observedResponseBody) Close() error {
+       err := b.ReadCloser.Close()
+       b.once.Do(func() { close(b.closed) })
+       return err
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/container-plugin-0.7.4/go-worker/pkg/container/podman.go 
new/container-plugin-0.7.5/go-worker/pkg/container/podman.go
--- old/container-plugin-0.7.4/go-worker/pkg/container/podman.go        
2026-09-10 17:06:26.000000000 +0200
+++ new/container-plugin-0.7.5/go-worker/pkg/container/podman.go        
2026-09-22 18:51:59.000000000 +0200
@@ -290,22 +290,26 @@
        }
 
        evChn := make(chan types.Event)
-       cancelChan := make(chan bool)
-       err := system.Events(pc.pCtx, evChn, cancelChan, &system.EventsOptions{
+       reqCtx, cancel := pc.requestCtx(ctx)
+       err := system.Events(reqCtx, evChn, nil, &system.EventsOptions{
                Filters: filters,
                Stream:  &stream,
        })
        if err != nil {
+               cancel()
                return nil, err
        }
 
        outCh := make(chan event.Event)
        wg.Add(1)
        go func() {
+               defer wg.Done()
+               defer close(outCh)
                defer func() {
-                       wg.Done()
-                       close(cancelChan)
-                       close(outCh)
+                       cancel()
+                       // The bindings own evChn and can be blocked sending 
the next event.
+                       for range evChn {
+                       }
                }()
                size := config.GetWithSize()
                // Blocking: convert all events from podman to json strings
@@ -313,12 +317,11 @@
                for {
                        select {
                        case <-ctx.Done():
-                               cancelChan <- true
                                return
                        case ev, ok := <-evChn:
                                if !ok {
                                        // evChn has been closed - kill the 
goroutine
-                                       // NOTE this should never happen since 
we are the ones closing the channel.
+                                       // The bindings close the channel when 
the stream ends.
                                        return
                                }
                                var (
@@ -327,11 +330,15 @@
                                )
                                switch ev.Action {
                                case events.ActionCreate, events.ActionStart:
-                                       ctr, err = containers.Inspect(pc.pCtx, 
ev.Actor.ID, &containers.InspectOptions{Size: &size})
+                                       ctr, err = containers.Inspect(reqCtx, 
ev.Actor.ID, &containers.InspectOptions{Size: &size})
                                        if err == nil {
-                                               outCh <- event.Event{
+                                               select {
+                                               case outCh <- event.Event{
                                                        Info:     
pc.ctrToInfo(ctr),
                                                        IsCreate: true,
+                                               }:
+                                               case <-ctx.Done():
+                                                       return
                                                }
                                        }
                                case events.ActionRemove:
@@ -342,7 +349,8 @@
                                // AND as a fallback whenever Inspect fails.
                                if err != nil {
                                        // At least send an event with the 
minimal set of data
-                                       outCh <- event.Event{
+                                       select {
+                                       case outCh <- event.Event{
                                                Info: event.Info{
                                                        Container: 
event.Container{
                                                                Type:   
typePodman.ToCTValue(),
@@ -352,6 +360,9 @@
                                                        },
                                                },
                                                IsCreate: ev.Action != 
events.ActionRemove,
+                                       }:
+                                       case <-ctx.Done():
+                                               return
                                        }
                                }
                        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/container-plugin-0.7.4/go-worker/pkg/container/podman_listener_test.go 
new/container-plugin-0.7.5/go-worker/pkg/container/podman_listener_test.go
--- old/container-plugin-0.7.4/go-worker/pkg/container/podman_listener_test.go  
1970-01-01 01:00:00.000000000 +0100
+++ new/container-plugin-0.7.5/go-worker/pkg/container/podman_listener_test.go  
2026-09-22 18:51:59.000000000 +0200
@@ -0,0 +1,104 @@
+//go:build linux
+
+package container
+
+import (
+       "context"
+       "fmt"
+       "io"
+       "log/slog"
+       "net/http"
+       "strings"
+       "sync"
+       "testing"
+       "time"
+
+       "github.com/stretchr/testify/require"
+       "go.podman.io/podman/v6/pkg/bindings"
+
+       
"github.com/falcosecurity/plugins/plugins/container/go-worker/pkg/config"
+)
+
+func TestPodmanListenShutdown(t *testing.T) {
+       testHTTPListenerShutdown(t, newPodmanEngine, func(engine Engine, 
inspected chan struct{}) {
+               connection, err := 
bindings.GetClient(engine.(*podmanEngine).pCtx)
+               require.NoError(t, err)
+               connection.Client.Transport = 
&inspectionTransport{RoundTripper: connection.Client.Transport, inspected: 
inspected}
+       })
+}
+
+type eventTransport struct {
+       http.RoundTripper
+       body io.ReadCloser
+}
+
+func (tr eventTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+       if strings.HasSuffix(req.URL.Path, "/events") {
+               return &http.Response{StatusCode: http.StatusOK, Header: 
make(http.Header), Body: tr.body, Request: req}, nil
+       }
+       return tr.RoundTripper.RoundTrip(req)
+}
+
+func TestPodmanListenDrainsPendingSourceEvent(t *testing.T) {
+       config.Load(`{"hooks":7}`)
+       socket, inspecting, _, _ := newListenerAPI(t, false)
+       engineCtx, stopEngine := context.WithCancel(context.Background())
+       defer stopEngine()
+       engine, err := newPodmanEngine(engineCtx, slog.Default(), socket)
+       require.NoError(t, err)
+       connection, err := bindings.GetClient(engine.(*podmanEngine).pCtx)
+       require.NoError(t, err)
+       reader, writer := io.Pipe()
+       t.Cleanup(func() { _ = reader.Close(); _ = writer.Close() })
+       body := &observedResponseBody{ReadCloser: reader, closed: make(chan 
struct{})}
+       connection.Client.Transport = eventTransport{RoundTripper: 
connection.Client.Transport, body: body}
+       secondRead := make(chan struct{})
+       go func() {
+               defer writer.Close()
+               for range 2 {
+                       if _, err := fmt.Fprintf(writer, 
`{"Type":"container","Action":"create","Actor":{"ID":%q}}`+"\n", 
fakeDockerID(1)); err != nil {
+                               return
+                       }
+               }
+               // io.Pipe.Write returns only after the decoder read the bytes. 
The
+               // SDK now has an event to send even if its context is 
cancelled.
+               close(secondRead)
+       }()
+       ctx, cancel := context.WithCancel(context.Background())
+       defer cancel()
+       var wg sync.WaitGroup
+       out, err := engine.Listen(ctx, &wg)
+       require.NoError(t, err)
+       t.Cleanup(func() {
+               cancel()
+               go func() {
+                       for range out {
+                       }
+               }()
+       })
+       select {
+       case <-inspecting:
+       case <-time.After(5 * time.Second):
+               t.Fatal("metadata request not started")
+       }
+       select {
+       case <-secondRead:
+       case <-time.After(5 * time.Second):
+               t.Fatal("second source event not read")
+       }
+       cancel()
+       done := make(chan struct{})
+       go func() { wg.Wait(); close(done) }()
+       select {
+       case <-done:
+       case <-time.After(5 * time.Second):
+               t.Fatal("listener did not stop")
+       }
+       // Only Events' deferred cleanup calls this Close. Merely cancelling an
+       // HTTP request would not prove its channel sender has returned.
+       select {
+       case <-body.closed:
+       case <-time.After(5 * time.Second):
+               t.Fatal("SDK producer stranded after listener stopped")
+       }
+}

++++++ container-plugin.obsinfo ++++++
--- /var/tmp/diff_new_pack.imQnhz/_old  2026-09-24 23:00:21.066483315 +0200
+++ /var/tmp/diff_new_pack.imQnhz/_new  2026-09-24 23:00:21.069483441 +0200
@@ -1,5 +1,5 @@
 name: container-plugin
-version: 0.7.4
-mtime: 1789052786
-commit: efa4416dd148f0127499b23bb4ffa49bd417c385
+version: 0.7.5
+mtime: 1790095919
+commit: a246c88790db1180f8b15411a5fb20ce853f67a4
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/falcosecurity-container-plugin/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.falcosecurity-container-plugin.new.383539/vendor.tar.gz
 differ: char 5, line 1

Reply via email to