Repository: netobserv/flowlogs-pipeline
Pull request #628 (2024-03-11 13:31:00)

Bump google.golang.org/grpc from 1.62.0 to 1.62.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.62.0 to 1.62.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/grpc/grpc-go/releases">google.golang.org/grpc's releases</a>.</em></p>
<blockquote>
<h2>Release 1.62.1</h2>
<h1>Bug Fixes</h1>
<ul>
<li>xds: fix a bug that results in <code>no matching virtual host found</code> RPC errors due to a difference between the target and LDS resource names (<a href="https://redirect.github.com/grpc/grpc-go/issues/6997">#6997</a>)</li>
<li>server: fixed stats handler data <code>InPayload.Length</code> for unary RPC calls (<a href="https://redirect.github.com/grpc/grpc-go/issues/6766">#6766</a>)
<ul>
<li>Special Thanks: <a href="https://github.com/hueypark"><code>@​hueypark</code></a></li>
</ul>
</li>
<li>grpc: the experimental <code>RecvBufferPool</code> <code>DialOption</code> and <code>ServerOption</code> are now active during unary RPCs with compression (<a href="https://redirect.github.com/grpc/grpc-go/issues/6766">#6766</a>)
<ul>
<li>Special Thanks: <a href="https://github.com/hueypark"><code>@​hueypark</code></a></li>
</ul>
</li>
<li>grpc: trim whitespaces in <code>accept-encoding</code> header before determining compressors
<ul>
<li>Special Thanks: <a href="https://github.com/sercand"><code>@​sercand</code></a></li>
</ul>
</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/grpc/grpc-go/commit/9952aa83979822b5915c3fcb2bb0f60afe55aa7d"><code>9952aa8</code></a> Change version to 1.62.1 (<a href="https://redirect.github.com/grpc/grpc-go/issues/7020">#7020</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/d7334c477d1ea670fa8d5fa12f06a2bfe4f41d4f"><code>d7334c4</code></a> fix enabling compression by trimming whitespaces in accept encoding header (#...</li>
<li><a href="https://github.com/grpc/grpc-go/commit/d076e14b4849f4262f6f50042a9370ec5ce0116d"><code>d076e14</code></a> rpc_util: Fix RecvBufferPool deactivation issues (<a href="https://redirect.github.com/grpc/grpc-go/issues/6766">#6766</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/9d981b0eb01a1ccd61f16593461277e95e83a34b"><code>9d981b0</code></a> cherry-pick <a href="https://redirect.github.com/grpc/grpc-go/issues/6997">#6997</a> to 1.62.x release branch (<a href="https://redirect.github.com/grpc/grpc-go/issues/6979">#6979</a>) (<a href="https://redirect.github.com/grpc/grpc-go/issues/7018">#7018</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/7c4b5533d07d5d7d01aa71145c32af27ac6e1a4d"><code>7c4b553</code></a> Switch version to 1.62.1-dev (<a href="https://redirect.github.com/grpc/grpc-go/issues/6995">#6995</a>)</li>
<li>See full diff in <a href="https://github.com/grpc/grpc-go/compare/v1.62.0...v1.62.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/grpc&package-manager=go_modules&previous-version=1.62.0&new-version=1.62.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

openshift-ci[bot]: Hi @dependabot[bot]. Thanks for your PR.

I'm waiting for a [netobserv](https://github.com/orgs/netobserv/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://github.com/orgs/netobserv/people) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=netobserv%2Fflowlogs-pipeline).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

openshift-ci[bot]: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please assign [oliviercazade](https://github.com/oliviercazade) for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=netobserv%2Fflowlogs-pipeline).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/netobserv/flowlogs-pipeline/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["oliviercazade"]} -->
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/pull/628?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 67.22%. Comparing base [(`8c770b7`)](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/commit/8c770b77257df9c17b65296176fd784bb431e79d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv) to head [(`ebd02ca`)](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/pull/628?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv).


<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #628   +/-   ##
=======================================
  Coverage   67.22%   67.22%           
=======================================
  Files         104      104           
  Lines        7459     7459           
=======================================
  Hits         5014     5014           
  Misses       2143     2143           
  Partials      302      302           
```

| [Flag](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/pull/628/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv) | Coverage Δ | |
|---|---|---|
| [unittests](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/pull/628/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv) | `67.22% <ø> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/netobserv/flowlogs-pipeline/pull/628?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=netobserv).


Patch:
diff --git a/go.mod b/go.mod
index cc62b592..6befc45c 100644
--- a/go.mod
+++ b/go.mod
@@ -12,11 +12,11 @@ require (
 	github.com/ip2location/ip2location-go/v9 v9.7.0
 	github.com/json-iterator/go v1.1.12
 	github.com/mariomac/guara v0.0.0-20220523124851-5fc279816f1f
-	github.com/minio/minio-go/v7 v7.0.68
+	github.com/minio/minio-go/v7 v7.0.69
 	github.com/mitchellh/mapstructure v1.5.0
 	github.com/netobserv/gopipes v0.3.0
 	github.com/netobserv/loki-client-go v0.0.0-20220927092034-f37122a54500
-	github.com/netobserv/netobserv-ebpf-agent v0.3.3
+	github.com/netobserv/netobserv-ebpf-agent v0.3.4-0.20240312085757-e9bf0d0d5a09
 	github.com/netsampler/goflow2 v1.3.7
 	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.19.0
@@ -26,7 +26,7 @@ require (
 	github.com/spf13/cobra v1.8.0
 	github.com/spf13/pflag v1.0.5
 	github.com/spf13/viper v1.18.2
-	github.com/stretchr/testify v1.8.4
+	github.com/stretchr/testify v1.9.0
 	github.com/vladimirvivien/gexe v0.2.0
 	github.com/vmware/go-ipfix v0.9.0
 	go.opentelemetry.io/otel v1.24.0
@@ -39,7 +39,7 @@ require (
 	go.opentelemetry.io/otel/sdk v1.24.0
 	go.opentelemetry.io/otel/sdk/metric v1.24.0
 	golang.org/x/net v0.22.0
-	google.golang.org/grpc v1.62.0
+	google.golang.org/grpc v1.62.1
 	google.golang.org/protobuf v1.32.0
 	gopkg.in/yaml.v2 v2.4.0
 	k8s.io/api v0.29.2
@@ -106,7 +106,7 @@ require (
 	github.com/sourcegraph/conc v0.3.0 // indirect
 	github.com/spf13/afero v1.11.0 // indirect
 	github.com/spf13/cast v1.6.0 // indirect
-	github.com/stretchr/objx v0.5.0 // indirect
+	github.com/stretchr/objx v0.5.2 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
 	github.com/xdg-go/scram v1.1.2 // indirect
diff --git a/go.sum b/go.sum
index 8fd08b0b..3bc97caf 100644
--- a/go.sum
+++ b/go.sum
@@ -583,8 +583,8 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju
 github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
 github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
-github.com/minio/minio-go/v7 v7.0.68 h1:hTqSIfLlpXaKuNy4baAp4Jjy2sqZEN9hRxD0M4aOfrQ=
-github.com/minio/minio-go/v7 v7.0.68/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ=
+github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0=
+github.com/minio/minio-go/v7 v7.0.69/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ=
 github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
 github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
 github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@@ -631,8 +631,8 @@ github.com/netobserv/gopipes v0.3.0 h1:IYmPnnAVCdSK7VmHmpFhrVBOEm45qpgbZmJz1sSW+
 github.com/netobserv/gopipes v0.3.0/go.mod h1:N7/Gz05EOF0CQQSKWsv3eof22Cj2PB08Pbttw98YFYU=
 github.com/netobserv/loki-client-go v0.0.0-20220927092034-f37122a54500 h1:RmnoJe/ci5q+QdM7upFdxiU+D8F3L3qTd5wXCwwHefw=
 github.com/netobserv/loki-client-go v0.0.0-20220927092034-f37122a54500/go.mod h1:LHXpc5tjKvsfZn0pwLKrvlgEhZcCaw3Di9mUEZGAI4E=
-github.com/netobserv/netobserv-ebpf-agent v0.3.3 h1:4e/mDVhSTd8JzaqKapaQ78BFLJP/GYpYWQ/LHPArJeI=
-github.com/netobserv/netobserv-ebpf-agent v0.3.3/go.mod h1:R3zRIhu5faj91s9pCU735oULLC+oBDPzA/xVwiUJ9PY=
+github.com/netobserv/netobserv-ebpf-agent v0.3.4-0.20240312085757-e9bf0d0d5a09 h1:9Y7nPXhMc8PToNX4aQltGNcpSDBehWoTHlg/LWToils=
+github.com/netobserv/netobserv-ebpf-agent v0.3.4-0.20240312085757-e9bf0d0d5a09/go.mod h1:W26Mf03LriOdylI/3T0yKgmU3bR4gy/ihzRd0SzJb3w=
 github.com/netsampler/goflow2 v1.3.7 h1:XZaTy8kkMnGXpJ9hS3KbO1McyrFTpVNhVFEx9rNhMmc=
 github.com/netsampler/goflow2 v1.3.7/go.mod h1:4UZsVGVAs//iMCptUHn3WNScztJeUhZH7kDW2+/vDdQ=
 github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
@@ -813,8 +813,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -825,8 +826,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
 github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@@ -1319,8 +1321,8 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
 google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
-google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
+google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
+google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/pkg/api/write_grpc.go b/pkg/api/write_grpc.go
new file mode 100644
index 00000000..a628a899
--- /dev/null
+++ b/pkg/api/write_grpc.go
@@ -0,0 +1,21 @@
+package api
+
+import "errors"
+
+type WriteGRPC struct {
+	TargetHost string `yaml:"targetHost,omitempty" json:"targetHost,omitempty" doc:"the host name or IP of the target Flow collector"`
+	TargetPort int    `yaml:"targetPort,omitempty" json:"targetPort,omitempty" doc:"the port of the target Flow collector"`
+}
+
+func (w *WriteGRPC) Validate() error {
+	if w == nil {
+		return errors.New("you must provide a configuration")
+	}
+	if w.TargetHost == "" {
+		return errors.New("targetHost can't be empty")
+	}
+	if w.TargetPort == 0 {
+		return errors.New("targetPort can't be empty")
+	}
+	return nil
+}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index ea10e204..25c08c26 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -129,6 +129,7 @@ type Write struct {
 	Loki   *api.WriteLoki   `yaml:"loki,omitempty" json:"loki,omitempty"`
 	Stdout *api.WriteStdout `yaml:"stdout,omitempty" json:"stdout,omitempty"`
 	Ipfix  *api.WriteIpfix  `yaml:"ipfix,omitempty" json:"ipfix,omitempty"`
+	GRPC   *api.WriteGRPC   `yaml:"grpc,omitempty" json:"grpc,omitempty"`
 }
 
 // ParseConfig creates the internal unmarshalled representation from the Pipeline and Parameters json
diff --git a/pkg/pipeline/pipeline_builder.go b/pkg/pipeline/pipeline_builder.go
index 769a2a39..6577b90d 100644
--- a/pkg/pipeline/pipeline_builder.go
+++ b/pkg/pipeline/pipeline_builder.go
@@ -368,6 +368,8 @@ func getWriter(opMetrics *operational.Metrics, params config.StageParam) (write.
 	var writer write.Writer
 	var err error
 	switch params.Write.Type {
+	case api.GRPCType:
+		writer, err = write.NewWriteGRPC(params)
 	case api.StdoutType:
 		writer, err = write.NewWriteStdout(params)
 	case api.NoneType:
diff --git a/pkg/pipeline/write/grpc/client.go b/pkg/pipeline/write/grpc/client.go
new file mode 100644
index 00000000..082b46ce
--- /dev/null
+++ b/pkg/pipeline/write/grpc/client.go
@@ -0,0 +1,40 @@
+package grpc
+
+import (
+	"flag"
+	"log"
+
+	pb "github.com/netobserv/flowlogs-pipeline/pkg/pipeline/write/grpc/genericmap"
+	"github.com/netobserv/netobserv-ebpf-agent/pkg/utils"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials/insecure"
+)
+
+// ClientConnection wraps a gRPC+protobuf connection
+type ClientConnection struct {
+	client pb.CollectorClient
+	conn   *grpc.ClientConn
+}
+
+func ConnectClient(hostIP string, hostPort int) (*ClientConnection, error) {
+	flag.Parse()
+	// Set up a connection to the server.
+	socket := utils.GetSocket(hostIP, hostPort)
+	conn, err := grpc.Dial(socket, grpc.WithTransportCredentials(insecure.NewCredentials()))
+	if err != nil {
+		log.Fatalf("did not connect: %v", err)
+	}
+
+	return &ClientConnection{
+		client: pb.NewCollectorClient(conn),
+		conn:   conn,
+	}, nil
+}
+
+func (cp *ClientConnection) Client() pb.CollectorClient {
+	return cp.client
+}
+
+func (cp *ClientConnection) Close() error {
+	return cp.conn.Close()
+}
diff --git a/pkg/pipeline/write/grpc/genericmap/genericmap.pb.go b/pkg/pipeline/write/grpc/genericmap/genericmap.pb.go
new file mode 100644
index 00000000..a3ba1db8
--- /dev/null
+++ b/pkg/pipeline/write/grpc/genericmap/genericmap.pb.go
@@ -0,0 +1,209 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.28.0
+// 	protoc        v3.19.6
+// source: proto/genericmap.proto
+
+package genericmap
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	anypb "google.golang.org/protobuf/types/known/anypb"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// The request message containing the GenericMap
+type Flow struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	GenericMap *anypb.Any `protobuf:"bytes,1,opt,name=genericMap,proto3" json:"genericMap,omitempty"`
+}
+
+func (x *Flow) Reset() {
+	*x = Flow{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_genericmap_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Flow) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Flow) ProtoMessage() {}
+
+func (x *Flow) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_genericmap_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Flow.ProtoReflect.Descriptor instead.
+func (*Flow) Descriptor() ([]byte, []int) {
+	return file_proto_genericmap_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Flow) GetGenericMap() *anypb.Any {
+	if x != nil {
+		return x.GenericMap
+	}
+	return nil
+}
+
+// intentionally empty
+type CollectorReply struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *CollectorReply) Reset() {
+	*x = CollectorReply{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_genericmap_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CollectorReply) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CollectorReply) ProtoMessage() {}
+
+func (x *CollectorReply) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_genericmap_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CollectorReply.ProtoReflect.Descriptor instead.
+func (*CollectorReply) Descriptor() ([]byte, []int) {
+	return file_proto_genericmap_proto_rawDescGZIP(), []int{1}
+}
+
+var File_proto_genericmap_proto protoreflect.FileDescriptor
+
+var file_proto_genericmap_proto_rawDesc = []byte{
+	0x0a, 0x16, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x6d,
+	0x61, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69,
+	0x63, 0x6d, 0x61, 0x70, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+	0x3c, 0x0a, 0x04, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x34, 0x0a, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72,
+	0x69, 0x63, 0x4d, 0x61, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f,
+	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e,
+	0x79, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x4d, 0x61, 0x70, 0x22, 0x10, 0x0a,
+	0x0e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32,
+	0x43, 0x0a, 0x09, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x36, 0x0a, 0x04,
+	0x53, 0x65, 0x6e, 0x64, 0x12, 0x10, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x6d, 0x61,
+	0x70, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x1a, 0x1a, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63,
+	0x6d, 0x61, 0x70, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70,
+	0x6c, 0x79, 0x22, 0x00, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69,
+	0x63, 0x6d, 0x61, 0x70, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_proto_genericmap_proto_rawDescOnce sync.Once
+	file_proto_genericmap_proto_rawDescData = file_proto_genericmap_proto_rawDesc
+)
+
+func file_proto_genericmap_proto_rawDescGZIP() []byte {
+	file_proto_genericmap_proto_rawDescOnce.Do(func() {
+		file_proto_genericmap_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_genericmap_proto_rawDescData)
+	})
+	return file_proto_genericmap_proto_rawDescData
+}
+
+var file_proto_genericmap_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_proto_genericmap_proto_goTypes = []interface{}{
+	(*Flow)(nil),           // 0: genericmap.Flow
+	(*CollectorReply)(nil), // 1: genericmap.CollectorReply
+	(*anypb.Any)(nil),      // 2: google.protobuf.Any
+}
+var file_proto_genericmap_proto_depIdxs = []int32{
+	2, // 0: genericmap.Flow.genericMap:type_name -> google.protobuf.Any
+	0, // 1: genericmap.Collector.Send:input_type -> genericmap.Flow
+	1, // 2: genericmap.Collector.Send:output_type -> genericmap.CollectorReply
+	2, // [2:3] is the sub-list for method output_type
+	1, // [1:2] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_proto_genericmap_proto_init() }
+func file_proto_genericmap_proto_init() {
+	if File_proto_genericmap_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_proto_genericmap_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Flow); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_proto_genericmap_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CollectorReply); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_proto_genericmap_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_proto_genericmap_proto_goTypes,
+		DependencyIndexes: file_proto_genericmap_proto_depIdxs,
+		MessageInfos:      file_proto_genericmap_proto_msgTypes,
+	}.Build()
+	File_proto_genericmap_proto = out.File
+	file_proto_genericmap_proto_rawDesc = nil
+	file_proto_genericmap_proto_goTypes = nil
+	file_proto_genericmap_proto_depIdxs = nil
+}
diff --git a/pkg/pipeline/write/grpc/genericmap/genericmap_grpc.pb.go b/pkg/pipeline/write/grpc/genericmap/genericmap_grpc.pb.go
new file mode 100644
index 00000000..12a8d8aa
--- /dev/null
+++ b/pkg/pipeline/write/grpc/genericmap/genericmap_grpc.pb.go
@@ -0,0 +1,105 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.2.0
+// - protoc             v3.19.6
+// source: proto/genericmap.proto
+
+package genericmap
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+// CollectorClient is the client API for Collector service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type CollectorClient interface {
+	Send(ctx context.Context, in *Flow, opts ...grpc.CallOption) (*CollectorReply, error)
+}
+
+type collectorClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewCollectorClient(cc grpc.ClientConnInterface) CollectorClient {
+	return &collectorClient{cc}
+}
+
+func (c *collectorClient) Send(ctx context.Context, in *Flow, opts ...grpc.CallOption) (*CollectorReply, error) {
+	out := new(CollectorReply)
+	err := c.cc.Invoke(ctx, "/genericmap.Collector/Send", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// CollectorServer is the server API for Collector service.
+// All implementations must embed UnimplementedCollectorServer
+// for forward compatibility
+type CollectorServer interface {
+	Send(context.Context, *Flow) (*CollectorReply, error)
+	mustEmbedUnimplementedCollectorServer()
+}
+
+// UnimplementedCollectorServer must be embedded to have forward compatible implementations.
+type UnimplementedCollectorServer struct {
+}
+
+func (UnimplementedCollectorServer) Send(context.Context, *Flow) (*CollectorReply, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Send not implemented")
+}
+func (UnimplementedCollectorServer) mustEmbedUnimplementedCollectorServer() {}
+
+// UnsafeCollectorServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to CollectorServer will
+// result in compilation errors.
+type UnsafeCollectorServer interface {
+	mustEmbedUnimplementedCollectorServer()
+}
+
+func RegisterCollectorServer(s grpc.ServiceRegistrar, srv CollectorServer) {
+	s.RegisterService(&Collector_ServiceDesc, srv)
+}
+
+func _Collector_Send_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(Flow)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(CollectorServer).Send(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/genericmap.Collector/Send",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(CollectorServer).Send(ctx, req.(*Flow))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// Collector_ServiceDesc is the grpc.ServiceDesc for Collector service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Collector_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "genericmap.Collector",
+	HandlerType: (*CollectorServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Send",
+			Handler:    _Collector_Send_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "proto/genericmap.proto",
+}
diff --git a/pkg/pipeline/write/grpc/server.go b/pkg/pipeline/write/grpc/server.go
new file mode 100644
index 00000000..0a085e92
--- /dev/null
+++ b/pkg/pipeline/write/grpc/server.go
@@ -0,0 +1,77 @@
+package grpc
+
+import (
+	"context"
+	"fmt"
+	"net"
+
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/reflection"
+
+	"github.com/netobserv/flowlogs-pipeline/pkg/pipeline/write/grpc/genericmap"
+)
+
+// CollectorServer wraps a Flow Collector connection & session
+type CollectorServer struct {
+	grpcServer *grpc.Server
+}
+
+type collectorOptions struct {
+	grpcServerOptions []grpc.ServerOption
+}
+
+// CollectorOption allows overriding the default configuration of the CollectorServer instance.
+// Use them in the StartCollector function.
+type CollectorOption func(options *collectorOptions)
+
+func WithGRPCServerOptions(options ...grpc.ServerOption) CollectorOption {
+	return func(copt *collectorOptions) {
+		copt.grpcServerOptions = options
+	}
+}
+
+// StartCollector listens in background for gRPC+Protobuf flows in the given port, and forwards each
+// set of *genericmap.Flow by the provided channel.
+func StartCollector(
+	port int, recordForwarder chan<- *genericmap.Flow, options ...CollectorOption,
+) (*CollectorServer, error) {
+	copts := collectorOptions{}
+	for _, opt := range options {
+		opt(&copts)
+	}
+
+	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
+	if err != nil {
+		return nil, err
+	}
+	grpcServer := grpc.NewServer(copts.grpcServerOptions...)
+	genericmap.RegisterCollectorServer(grpcServer, &collectorAPI{
+		recordForwarder: recordForwarder,
+	})
+	reflection.Register(grpcServer)
+	go func() {
+		if err := grpcServer.Serve(lis); err != nil {
+			panic("error connecting to server: " + err.Error())
+		}
+	}()
+	return &CollectorServer{
+		grpcServer: grpcServer,
+	}, nil
+}
+
+func (c *CollectorServer) Close() error {
+	c.grpcServer.Stop()
+	return nil
+}
+
+type collectorAPI struct {
+	genericmap.UnimplementedCollectorServer
+	recordForwarder chan<- *genericmap.Flow
+}
+
+var okReply = &genericmap.CollectorReply{}
+
+func (c *collectorAPI) Send(_ context.Context, records *genericmap.Flow) (*genericmap.CollectorReply, error) {
+	c.recordForwarder <- records
+	return okReply, nil
+}
diff --git a/pkg/pipeline/write/write_grpc.go b/pkg/pipeline/write/write_grpc.go
new file mode 100644
index 00000000..c89d5f36
--- /dev/null
+++ b/pkg/pipeline/write/write_grpc.go
@@ -0,0 +1,55 @@
+package write
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+
+	"github.com/netobserv/flowlogs-pipeline/pkg/config"
+	"github.com/netobserv/flowlogs-pipeline/pkg/pipeline/write/grpc"
+	"github.com/netobserv/flowlogs-pipeline/pkg/pipeline/write/grpc/genericmap"
+	"github.com/sirupsen/logrus"
+	"google.golang.org/protobuf/types/known/anypb"
+)
+
+type writeGRPC struct {
+	hostIP     string
+	hostPort   int
+	clientConn *grpc.ClientConnection
+}
+
+// Write writes a flow before being stored
+func (t *writeGRPC) Write(v config.GenericMap) {
+	logrus.Tracef("entering writeGRPC Write %s", v)
+	value, _ := json.Marshal(v)
+	if _, err := t.clientConn.Client().Send(context.TODO(), &genericmap.Flow{
+		GenericMap: &anypb.Any{
+			Value: value,
+		},
+	}); err != nil {
+		logrus.Errorf("writeGRPC send error: %v", err)
+	}
+}
+
+// NewWriteGRPC create a new write
+func NewWriteGRPC(params config.StageParam) (Writer, error) {
+	logrus.Debugf("entering NewWriteGRPC")
+
+	writeGRPC := &writeGRPC{}
+	if params.Write != nil && params.Write.GRPC != nil {
+		if err := params.Write.GRPC.Validate(); err != nil {
+			return nil, fmt.Errorf("the provided config is not valid: %w", err)
+		}
+		writeGRPC.hostIP = params.Write.GRPC.TargetHost
+		writeGRPC.hostPort = params.Write.GRPC.TargetPort
+	} else {
+		return nil, fmt.Errorf("write.grpc param is mandatory: %v", params.Write)
+	}
+	logrus.Debugf("NewWriteGRPC ConnectClient %s:%d...", writeGRPC.hostIP, writeGRPC.hostPort)
+	clientConn, err := grpc.ConnectClient(writeGRPC.hostIP, writeGRPC.hostPort)
+	if err != nil {
+		return nil, err
+	}
+	writeGRPC.clientConn = clientConn
+	return writeGRPC, nil
+}
diff --git a/pkg/pipeline/write/write_ipfix.go b/pkg/pipeline/write/write_ipfix.go
index e76bb9e1..cf984a26 100644
--- a/pkg/pipeline/write/write_ipfix.go
+++ b/pkg/pipeline/write/write_ipfix.go
@@ -246,8 +246,9 @@ func setStandardIEValue(record config.GenericMap, ieValPtr *entities.InfoElement
 			return fmt.Errorf("unable to find ethernet type (Etype) in record")
 		}
 	case "flowDirection":
-		if record["FlowDirection"] != nil {
-			ieVal.SetUnsigned8Value(uint8(record["FlowDirection"].(int)))
+		dirs := record["IfDirections"].([]int)
+		if len(dirs) > 0 {
+			ieVal.SetUnsigned8Value(uint8(dirs[0]))
 		} else {
 			return fmt.Errorf("unable to find flow direction (flowDirection) in record")
 		}
@@ -336,8 +337,9 @@ func setStandardIEValue(record config.GenericMap, ieValPtr *entities.InfoElement
 			return fmt.Errorf("unable to find packets in record")
 		}
 	case "interfaceName":
-		if record["Interface"] != nil {
-			ieVal.SetStringValue(record["Interface"].(string))
+		interfaces := record["Interfaces"].([]string)
+		if len(interfaces) > 0 {
+			ieVal.SetStringValue(interfaces[0])
 		} else {
 			return fmt.Errorf("unable to find interface in record")
 		}
diff --git a/proto/README.md b/proto/README.md
new file mode 100644
index 00000000..78fbc764
--- /dev/null
+++ b/proto/README.md
@@ -0,0 +1,8 @@
+# Update genericmap gRPC
+
+Run the following commands to update `genericmap.pb.go` and `genericmap_grpc.pb.go`:
+
+```bash
+$ protoc --go_out=./pkg/pipeline/write/grpc ./proto/genericmap.proto
+$ protoc --go-grpc_out=./pkg/pipeline/write/grpc ./proto/genericmap.proto
+```
\ No newline at end of file
diff --git a/proto/genericmap.proto b/proto/genericmap.proto
new file mode 100644
index 00000000..54513d17
--- /dev/null
+++ b/proto/genericmap.proto
@@ -0,0 +1,19 @@
+syntax = "proto3";
+
+option go_package = "./genericmap";
+
+package genericmap;
+
+import "google/protobuf/any.proto";
+
+service Collector {
+  rpc Send (Flow) returns (CollectorReply) {}
+}
+
+// The request message containing the GenericMap
+message Flow {
+  google.protobuf.Any genericMap = 1;
+}
+
+// intentionally empty
+message CollectorReply {}
\ No newline at end of file
diff --git a/vendor/github.com/minio/minio-go/v7/CREDITS b/vendor/github.com/minio/minio-go/v7/CREDITS
index d2092318..dce3d4c9 100644
--- a/vendor/github.com/minio/minio-go/v7/CREDITS
+++ b/vendor/github.com/minio/minio-go/v7/CREDITS
@@ -1365,60 +1365,6 @@ THE SOFTWARE.
 
 ================================================================
 
-github.com/sirupsen/logrus
-https://github.com/sirupsen/logrus
-----------------------------------------------------------------
-The MIT License (MIT)
-
-Copyright (c) 2014 Simon Eskildsen
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-================================================================
-
-github.com/stretchr/testify
-https://github.com/stretchr/testify
-----------------------------------------------------------------
-MIT License
-
-Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-================================================================
-
 golang.org/x/crypto
 https://golang.org/x/crypto
 ----------------------------------------------------------------
@@ -1748,59 +1694,3 @@ third-party archives.
 
 ================================================================
 
-gopkg.in/yaml.v3
-https://gopkg.in/yaml.v3
-----------------------------------------------------------------
-
-This project is covered by two different licenses: MIT and Apache.
-
-#### MIT License ####
-
-The following files were ported to Go from C files of libyaml, and thus
-are still covered by their original MIT license, with the additional
-copyright staring in 2011 when the project was ported over:
-
-    apic.go emitterc.go parserc.go readerc.go scannerc.go
-    writerc.go yamlh.go yamlprivateh.go
-
-Copyright (c) 2006-2010 Kirill Simonov
-Copyright (c) 2006-2011 Kirill Simonov
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
-### Apache License ###
-
-All the remaining project files are covered by the Apache license:
-
-Copyright (c) 2011-2019 Canonical Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
-================================================================
-
diff --git a/vendor/github.com/minio/minio-go/v7/api.go b/vendor/github.com/minio/minio-go/v7/api.go
index 78708705..930e082a 100644
--- a/vendor/github.com/minio/minio-go/v7/api.go
+++ b/vendor/github.com/minio/minio-go/v7/api.go
@@ -1,6 +1,6 @@
 /*
  * MinIO Go Library for Amazon S3 Compatible Cloud Storage
- * Copyright 2015-2023 MinIO, Inc.
+ * Copyright 2015-2024 MinIO, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -80,6 +80,8 @@ type Client struct {
 
 	// S3 specific accelerated endpoint.
 	s3AccelerateEndpoint string
+	// S3 dual-stack endpoints are enabled by default.
+	s3DualstackEnabled bool
 
 	// Region endpoint
 	region string
@@ -127,7 +129,7 @@ type Options struct {
 // Global constants.
 const (
 	libraryName    = "minio-go"
-	libraryVersion = "v7.0.68"
+	libraryVersion = "v7.0.69"
 )
 
 // User Agent should always following the below style.
@@ -158,9 +160,12 @@ func New(endpoint string, opts *Options) (*Client, error) {
 	if err != nil {
 		return nil, err
 	}
-	// If Amazon S3 set to signature v4.
 	if s3utils.IsAmazonEndpoint(*clnt.endpointURL) {
+		// If Amazon S3 set to signature v4.
 		clnt.overrideSignerType = credentials.SignatureV4
+		// Amazon S3 endpoints are resolved into dual-stack endpoints by default
+		// for backwards compatibility.
+		clnt.s3DualstackEnabled = true
 	}
 
 	return clnt, nil
@@ -330,6 +335,16 @@ func (c *Client) SetS3TransferAccelerate(accelerateEndpoint string) {
 	}
 }
 
+// SetS3EnableDualstack turns s3 dual-stack endpoints on or off for all requests.
+// The feature is only specific to S3 and is on by default. To read more about
+// Amazon S3 dual-stack endpoints visit -
+// https://docs.aws.amazon.com/AmazonS3/latest/userguide/dual-stack-endpoints.html
+func (c *Client) SetS3EnableDualstack(enabled bool) {
+	if s3utils.IsAmazonEndpoint(*c.endpointURL) {
+		c.s3DualstackEnabled = enabled
+	}
+}
+
 // Hash materials provides relevant initialized hash algo writers
 // based on the expected signature type.
 //
@@ -926,7 +941,7 @@ func (c *Client) makeTargetURL(bucketName, objectName, bucketLocation string, is
 			// Do not change the host if the endpoint URL is a FIPS S3 endpoint or a S3 PrivateLink interface endpoint
 			if !s3utils.IsAmazonFIPSEndpoint(*c.endpointURL) && !s3utils.IsAmazonPrivateLinkEndpoint(*c.endpointURL) {
 				// Fetch new host based on the bucket location.
-				host = getS3Endpoint(bucketLocation)
+				host = getS3Endpoint(bucketLocation, c.s3DualstackEnabled)
 			}
 		}
 	}
diff --git a/vendor/github.com/minio/minio-go/v7/pkg/credentials/iam_aws.go b/vendor/github.com/minio/minio-go/v7/pkg/credentials/iam_aws.go
index 90d075ab..7322948e 100644
--- a/vendor/github.com/minio/minio-go/v7/pkg/credentials/iam_aws.go
+++ b/vendor/github.com/minio/minio-go/v7/pkg/credentials/iam_aws.go
@@ -61,6 +61,7 @@ type IAM struct {
 	// Support for container authorization token https://docs.aws.amazon.com/sdkref/latest/guide/feature-container-credentials.html
 	Container struct {
 		AuthorizationToken     string
+		AuthorizationTokenFile string
 		CredentialsFullURI     string
 		CredentialsRelativeURI string
 	}
@@ -105,6 +106,11 @@ func (m *IAM) Retrieve() (Value, error) {
 		token = m.Container.AuthorizationToken
 	}
 
+	tokenFile := os.Getenv("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE")
+	if tokenFile == "" {
+		tokenFile = m.Container.AuthorizationToken
+	}
+
 	relativeURI := os.Getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
 	if relativeURI == "" {
 		relativeURI = m.Container.CredentialsRelativeURI
@@ -181,6 +187,10 @@ func (m *IAM) Retrieve() (Value, error) {
 
 		roleCreds, err = getEcsTaskCredentials(m.Client, endpoint, token)
 
+	case tokenFile != "" && fullURI != "":
+		endpoint = fullURI
+		roleCreds, err = getEKSPodIdentityCredentials(m.Client, endpoint, tokenFile)
+
 	case fullURI != "":
 		if len(endpoint) == 0 {
 			endpoint = fullURI
@@ -305,6 +315,18 @@ func getEcsTaskCredentials(client *http.Client, endpoint, token string) (ec2Role
 	return respCreds, nil
 }
 
+func getEKSPodIdentityCredentials(client *http.Client, endpoint string, tokenFile string) (ec2RoleCredRespBody, error) {
+	if tokenFile != "" {
+		bytes, err := os.ReadFile(tokenFile)
+		if err != nil {
+			return ec2RoleCredRespBody{}, fmt.Errorf("getEKSPodIdentityCredentials: failed to read token file:%s", err)
+		}
+		token := string(bytes)
+		return getEcsTaskCredentials(client, endpoint, token)
+	}
+	return ec2RoleCredRespBody{}, fmt.Errorf("getEKSPodIdentityCredentials: no tokenFile found")
+}
+
 func fetchIMDSToken(client *http.Client, endpoint string) (string, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
 	defer cancel()
diff --git a/vendor/github.com/minio/minio-go/v7/s3-endpoints.go b/vendor/github.com/minio/minio-go/v7/s3-endpoints.go
index b1de7b62..068a6bfa 100644
--- a/vendor/github.com/minio/minio-go/v7/s3-endpoints.go
+++ b/vendor/github.com/minio/minio-go/v7/s3-endpoints.go
@@ -1,6 +1,6 @@
 /*
  * MinIO Go Library for Amazon S3 Compatible Cloud Storage
- * Copyright 2015-2017 MinIO, Inc.
+ * Copyright 2015-2024 MinIO, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,48 +17,155 @@
 
 package minio
 
+type awsS3Endpoint struct {
+	endpoint          string
+	dualstackEndpoint string
+}
+
 // awsS3EndpointMap Amazon S3 endpoint map.
-var awsS3EndpointMap = map[string]string{
-	"us-east-1":      "s3.dualstack.us-east-1.amazonaws.com",
-	"us-east-2":      "s3.dualstack.us-east-2.amazonaws.com",
-	"us-west-2":      "s3.dualstack.us-west-2.amazonaws.com",
-	"us-west-1":      "s3.dualstack.us-west-1.amazonaws.com",
-	"ca-central-1":   "s3.dualstack.ca-central-1.amazonaws.com",
-	"eu-west-1":      "s3.dualstack.eu-west-1.amazonaws.com",
-	"eu-west-2":      "s3.dualstack.eu-west-2.amazonaws.com",
-	"eu-west-3":      "s3.dualstack.eu-west-3.amazonaws.com",
-	"eu-central-1":   "s3.dualstack.eu-central-1.amazonaws.com",
-	"eu-central-2":   "s3.dualstack.eu-central-2.amazonaws.com",
-	"eu-north-1":     "s3.dualstack.eu-north-1.amazonaws.com",
-	"eu-south-1":     "s3.dualstack.eu-south-1.amazonaws.com",
-	"eu-south-2":     "s3.dualstack.eu-south-2.amazonaws.com",
-	"ap-east-1":      "s3.dualstack.ap-east-1.amazonaws.com",
-	"ap-south-1":     "s3.dualstack.ap-south-1.amazonaws.com",
-	"ap-south-2":     "s3.dualstack.ap-south-2.amazonaws.com",
-	"ap-southeast-1": "s3.dualstack.ap-southeast-1.amazonaws.com",
-	"ap-southeast-2": "s3.dualstack.ap-southeast-2.amazonaws.com",
-	"ap-northeast-1": "s3.dualstack.ap-northeast-1.amazonaws.com",
-	"ap-northeast-2": "s3.dualstack.ap-northeast-2.amazonaws.com",
-	"ap-northeast-3": "s3.dualstack.ap-northeast-3.amazonaws.com",
-	"af-south-1":     "s3.dualstack.af-south-1.amazonaws.com",
-	"me-central-1":   "s3.dualstack.me-central-1.amazonaws.com",
-	"me-south-1":     "s3.dualstack.me-south-1.amazonaws.com",
-	"sa-east-1":      "s3.dualstack.sa-east-1.amazonaws.com",
-	"us-gov-west-1":  "s3.dualstack.us-gov-west-1.amazonaws.com",
-	"us-gov-east-1":  "s3.dualstack.us-gov-east-1.amazonaws.com",
-	"cn-north-1":     "s3.dualstack.cn-north-1.amazonaws.com.cn",
-	"cn-northwest-1": "s3.dualstack.cn-northwest-1.amazonaws.com.cn",
-	"ap-southeast-3": "s3.dualstack.ap-southeast-3.amazonaws.com",
-	"ap-southeast-4": "s3.dualstack.ap-southeast-4.amazonaws.com",
-	"il-central-1":   "s3.dualstack.il-central-1.amazonaws.com",
+var awsS3EndpointMap = map[string]awsS3Endpoint{
+	"us-east-1": {
+		"s3.us-east-1.amazonaws.com",
+		"s3.dualstack.us-east-1.amazonaws.com",
+	},
+	"us-east-2": {
+		"s3.us-east-2.amazonaws.com",
+		"s3.dualstack.us-east-2.amazonaws.com",
+	},
+	"us-west-2": {
+		"s3.us-west-2.amazonaws.com",
+		"s3.dualstack.us-west-2.amazonaws.com",
+	},
+	"us-west-1": {
+		"s3.us-west-1.amazonaws.com",
+		"s3.dualstack.us-west-1.amazonaws.com",
+	},
+	"ca-central-1": {
+		"s3.ca-central-1.amazonaws.com",
+		"s3.dualstack.ca-central-1.amazonaws.com",
+	},
+	"eu-west-1": {
+		"s3.eu-west-1.amazonaws.com",
+		"s3.dualstack.eu-west-1.amazonaws.com",
+	},
+	"eu-west-2": {
+		"s3.eu-west-2.amazonaws.com",
+		"s3.dualstack.eu-west-2.amazonaws.com",
+	},
+	"eu-west-3": {
+		"s3.eu-west-3.amazonaws.com",
+		"s3.dualstack.eu-west-3.amazonaws.com",
+	},
+	"eu-central-1": {
+		"s3.eu-central-1.amazonaws.com",
+		"s3.dualstack.eu-central-1.amazonaws.com",
+	},
+	"eu-central-2": {
+		"s3.eu-central-2.amazonaws.com",
+		"s3.dualstack.eu-central-2.amazonaws.com",
+	},
+	"eu-north-1": {
+		"s3.eu-north-1.amazonaws.com",
+		"s3.dualstack.eu-north-1.amazonaws.com",
+	},
+	"eu-south-1": {
+		"s3.eu-south-1.amazonaws.com",
+		"s3.dualstack.eu-south-1.amazonaws.com",
+	},
+	"eu-south-2": {
+		"s3.eu-south-2.amazonaws.com",
+		"s3.dualstack.eu-south-2.amazonaws.com",
+	},
+	"ap-east-1": {
+		"s3.ap-east-1.amazonaws.com",
+		"s3.dualstack.ap-east-1.amazonaws.com",
+	},
+	"ap-south-1": {
+		"s3.ap-south-1.amazonaws.com",
+		"s3.dualstack.ap-south-1.amazonaws.com",
+	},
+	"ap-south-2": {
+		"s3.ap-south-2.amazonaws.com",
+		"s3.dualstack.ap-south-2.amazonaws.com",
+	},
+	"ap-southeast-1": {
+		"s3.ap-southeast-1.amazonaws.com",
+		"s3.dualstack.ap-southeast-1.amazonaws.com",
+	},
+	"ap-southeast-2": {
+		"s3.ap-southeast-2.amazonaws.com",
+		"s3.dualstack.ap-southeast-2.amazonaws.com",
+	},
+	"ap-southeast-3": {
+		"s3.ap-southeast-3.amazonaws.com",
+		"s3.dualstack.ap-southeast-3.amazonaws.com",
+	},
+	"ap-southeast-4": {
+		"s3.ap-southeast-4.amazonaws.com",
+		"s3.dualstack.ap-southeast-4.amazonaws.com",
+	},
+	"ap-northeast-1": {
+		"s3.ap-northeast-1.amazonaws.com",
+		"s3.dualstack.ap-northeast-1.amazonaws.com",
+	},
+	"ap-northeast-2": {
+		"s3.ap-northeast-2.amazonaws.com",
+		"s3.dualstack.ap-northeast-2.amazonaws.com",
+	},
+	"ap-northeast-3": {
+		"s3.ap-northeast-3.amazonaws.com",
+		"s3.dualstack.ap-northeast-3.amazonaws.com",
+	},
+	"af-south-1": {
+		"s3.af-south-1.amazonaws.com",
+		"s3.dualstack.af-south-1.amazonaws.com",
+	},
+	"me-central-1": {
+		"s3.me-central-1.amazonaws.com",
+		"s3.dualstack.me-central-1.amazonaws.com",
+	},
+	"me-south-1": {
+		"s3.me-south-1.amazonaws.com",
+		"s3.dualstack.me-south-1.amazonaws.com",
+	},
+	"sa-east-1": {
+		"s3.sa-east-1.amazonaws.com",
+		"s3.dualstack.sa-east-1.amazonaws.com",
+	},
+	"us-gov-west-1": {
+		"s3.us-gov-west-1.amazonaws.com",
+		"s3.dualstack.us-gov-west-1.amazonaws.com",
+	},
+	"us-gov-east-1": {
+		"s3.us-gov-east-1.amazonaws.com",
+		"s3.dualstack.us-gov-east-1.amazonaws.com",
+	},
+	"cn-north-1": {
+		"s3.cn-north-1.amazonaws.com.cn",
+		"s3.dualstack.cn-north-1.amazonaws.com.cn",
+	},
+	"cn-northwest-1": {
+		"s3.cn-northwest-1.amazonaws.com.cn",
+		"s3.dualstack.cn-northwest-1.amazonaws.com.cn",
+	},
+	"il-central-1": {
+		"s3.il-central-1.amazonaws.com",
+		"s3.dualstack.il-central-1.amazonaws.com",
+	},
 }
 
 // getS3Endpoint get Amazon S3 endpoint based on the bucket location.
-func getS3Endpoint(bucketLocation string) (s3Endpoint string) {
+func getS3Endpoint(bucketLocation string, useDualstack bool) (endpoint string) {
 	s3Endpoint, ok := awsS3EndpointMap[bucketLocation]
 	if !ok {
-		// Default to 's3.dualstack.us-east-1.amazonaws.com' endpoint.
-		s3Endpoint = "s3.dualstack.us-east-1.amazonaws.com"
+		// Default to 's3.us-east-1.amazonaws.com' endpoint.
+		if useDualstack {
+			return "s3.dualstack.us-east-1.amazonaws.com"
+		}
+		return "s3.us-east-1.amazonaws.com"
+	}
+	if useDualstack {
+		return s3Endpoint.dualstackEndpoint
 	}
-	return s3Endpoint
+	return s3Endpoint.endpoint
 }
diff --git a/vendor/github.com/minio/minio-go/v7/utils.go b/vendor/github.com/minio/minio-go/v7/utils.go
index 46123ea7..d68f1484 100644
--- a/vendor/github.com/minio/minio-go/v7/utils.go
+++ b/vendor/github.com/minio/minio-go/v7/utils.go
@@ -517,6 +517,7 @@ var supportedReplicationEncryptionHeaders = map[string]bool{
 	"x-minio-replication-server-side-encryption-seal-algorithm": true,
 	"x-minio-replication-server-side-encryption-iv":             true,
 	"x-minio-replication-encrypted-multipart":                   true,
+	"x-minio-replication-actual-object-size":                    true,
 	// Add more supported headers here.
 	// Must be lower case.
 }
diff --git a/vendor/github.com/netobserv/netobserv-ebpf-agent/pkg/decode/decode_protobuf.go b/vendor/github.com/netobserv/netobserv-ebpf-agent/pkg/decode/decode_protobuf.go
index 4d0ea2bb..0bd286c6 100644
--- a/vendor/github.com/netobserv/netobserv-ebpf-agent/pkg/decode/decode_protobuf.go
+++ b/vendor/github.com/netobserv/netobserv-ebpf-agent/pkg/decode/decode_protobuf.go
@@ -46,18 +46,19 @@ func PBFlowToMap(flow *pbflow.Record) config.GenericMap {
 		return config.GenericMap{}
 	}
 	out := config.GenericMap{
-		"FlowDirection":   int(flow.Direction.Number()),
 		"SrcMac":          macToStr(flow.DataLink.GetSrcMac()),
 		"DstMac":          macToStr(flow.DataLink.GetDstMac()),
 		"Etype":           flow.EthProtocol,
-		"Duplicate":       flow.Duplicate,
 		"TimeFlowStartMs": flow.TimeFlowStart.AsTime().UnixMilli(),
 		"TimeFlowEndMs":   flow.TimeFlowEnd.AsTime().UnixMilli(),
 		"TimeReceived":    time.Now().Unix(),
-		"Interface":       flow.Interface,
 		"AgentIP":         ipToStr(flow.AgentIp),
 	}
 
+	if flow.Duplicate {
+		out["Duplicate"] = true
+	}
+
 	if flow.Bytes != 0 {
 		out["Bytes"] = flow.Bytes
 	}
@@ -65,17 +66,20 @@ func PBFlowToMap(flow *pbflow.Record) config.GenericMap {
 	if flow.Packets != 0 {
 		out["Packets"] = flow.Packets
 	}
-	var interfaces []interface{}
-	var flowDirections []interface{}
 
+	var interfaces []string
+	var directions []int
 	if len(flow.GetDupList()) != 0 {
 		for _, entry := range flow.GetDupList() {
 			interfaces = append(interfaces, entry.Interface)
-			flowDirections = append(flowDirections, entry.Direction)
+			directions = append(directions, int(entry.Direction))
 		}
-		out["Interfaces"] = interfaces
-		out["FlowDirections"] = flowDirections
+	} else {
+		interfaces = append(interfaces, flow.Interface)
+		directions = append(directions, int(flow.Direction))
 	}
+	out["Interfaces"] = interfaces
+	out["IfDirections"] = directions
 
 	ethType := ethernet.EtherType(flow.EthProtocol)
 	if ethType == ethernet.EtherTypeIPv4 || ethType == ethernet.EtherTypeIPv6 {
diff --git a/vendor/github.com/stretchr/objx/README.md b/vendor/github.com/stretchr/objx/README.md
index 246660b2..78dc1f8b 100644
--- a/vendor/github.com/stretchr/objx/README.md
+++ b/vendor/github.com/stretchr/objx/README.md
@@ -4,20 +4,20 @@
 [![Maintainability](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/maintainability)](https://codeclimate.com/github/stretchr/objx/maintainability)
 [![Test Coverage](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/test_coverage)](https://codeclimate.com/github/stretchr/objx/test_coverage)
 [![Sourcegraph](https://sourcegraph.com/github.com/stretchr/objx/-/badge.svg)](https://sourcegraph.com/github.com/stretchr/objx)
-[![GoDoc](https://godoc.org/github.com/stretchr/objx?status.svg)](https://godoc.org/github.com/stretchr/objx)
+[![GoDoc](https://pkg.go.dev/badge/github.com/stretchr/objx?utm_source=godoc)](https://pkg.go.dev/github.com/stretchr/objx)
 
 Objx - Go package for dealing with maps, slices, JSON and other data.
 
 Get started:
 
 - Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date)
-- Check out the API Documentation http://godoc.org/github.com/stretchr/objx
+- Check out the API Documentation http://pkg.go.dev/github.com/stretchr/objx
 
 ## Overview
 Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc.
 
 ### Pattern
-Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going:
+Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going:
 
     m, err := objx.FromJSON(json)
 
@@ -74,7 +74,7 @@ To update Objx to the latest version, run:
     go get -u github.com/stretchr/objx
 
 ### Supported go versions
-We support the lastest three major Go versions, which are 1.10, 1.11 and 1.12 at the moment.
+We currently support the three recent major Go versions.
 
 ## Contributing
 Please feel free to submit issues, fork the repository and send pull requests!
diff --git a/vendor/github.com/stretchr/objx/Taskfile.yml b/vendor/github.com/stretchr/objx/Taskfile.yml
index 7746f516..8a79e8d6 100644
--- a/vendor/github.com/stretchr/objx/Taskfile.yml
+++ b/vendor/github.com/stretchr/objx/Taskfile.yml
@@ -1,7 +1,4 @@
-version: '2'
-
-env:
-  GOFLAGS: -mod=vendor
+version: '3'
 
 tasks:
   default:
diff --git a/vendor/github.com/stretchr/objx/accessors.go b/vendor/github.com/stretchr/objx/accessors.go
index 4c604558..72f1d1c1 100644
--- a/vendor/github.com/stretchr/objx/accessors.go
+++ b/vendor/github.com/stretchr/objx/accessors.go
@@ -14,17 +14,17 @@ const (
 	// For example, `location.address.city`
 	PathSeparator string = "."
 
-	// arrayAccesRegexString is the regex used to extract the array number
+	// arrayAccessRegexString is the regex used to extract the array number
 	// from the access path
-	arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`
+	arrayAccessRegexString = `^(.+)\[([0-9]+)\]$`
 
 	// mapAccessRegexString is the regex used to extract the map key
 	// from the access path
 	mapAccessRegexString = `^([^\[]*)\[([^\]]+)\](.*)$`
 )
 
-// arrayAccesRegex is the compiled arrayAccesRegexString
-var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)
+// arrayAccessRegex is the compiled arrayAccessRegexString
+var arrayAccessRegex = regexp.MustCompile(arrayAccessRegexString)
 
 // mapAccessRegex is the compiled mapAccessRegexString
 var mapAccessRegex = regexp.MustCompile(mapAccessRegexString)
@@ -37,11 +37,11 @@ var mapAccessRegex = regexp.MustCompile(mapAccessRegexString)
 //
 // Get can only operate directly on map[string]interface{} and []interface.
 //
-// Example
+// # Example
 //
 // To access the title of the third chapter of the second book, do:
 //
-//    o.Get("books[1].chapters[2].title")
+//	o.Get("books[1].chapters[2].title")
 func (m Map) Get(selector string) *Value {
 	rawObj := access(m, selector, nil, false)
 	return &Value{data: rawObj}
@@ -52,26 +52,26 @@ func (m Map) Get(selector string) *Value {
 //
 // Set can only operate directly on map[string]interface{} and []interface
 //
-// Example
+// # Example
 //
 // To set the title of the third chapter of the second book, do:
 //
-//    o.Set("books[1].chapters[2].title","Time to Go")
+//	o.Set("books[1].chapters[2].title","Time to Go")
 func (m Map) Set(selector string, value interface{}) Map {
 	access(m, selector, value, true)
 	return m
 }
 
-// getIndex returns the index, which is hold in s by two braches.
-// It also returns s withour the index part, e.g. name[1] will return (1, name).
+// getIndex returns the index, which is hold in s by two branches.
+// It also returns s without the index part, e.g. name[1] will return (1, name).
 // If no index is found, -1 is returned
 func getIndex(s string) (int, string) {
-	arrayMatches := arrayAccesRegex.FindStringSubmatch(s)
+	arrayMatches := arrayAccessRegex.FindStringSubmatch(s)
 	if len(arrayMatches) > 0 {
 		// Get the key into the map
 		selector := arrayMatches[1]
 		// Get the index into the array at the key
-		// We know this cannt fail because arrayMatches[2] is an int for sure
+		// We know this can't fail because arrayMatches[2] is an int for sure
 		index, _ := strconv.Atoi(arrayMatches[2])
 		return index, selector
 	}
diff --git a/vendor/github.com/stretchr/objx/conversions.go b/vendor/github.com/stretchr/objx/conversions.go
index 080aa46e..01c63d7d 100644
--- a/vendor/github.com/stretchr/objx/conversions.go
+++ b/vendor/github.com/stretchr/objx/conversions.go
@@ -15,7 +15,7 @@ import (
 const SignatureSeparator = "_"
 
 // URLValuesSliceKeySuffix is the character that is used to
-// specify a suffic for slices parsed by URLValues.
+// specify a suffix for slices parsed by URLValues.
 // If the suffix is set to "[i]", then the index of the slice
 // is used in place of i
 // Ex: Suffix "[]" would have the form a[]=b&a[]=c
@@ -30,7 +30,7 @@ const (
 )
 
 // SetURLValuesSliceKeySuffix sets the character that is used to
-// specify a suffic for slices parsed by URLValues.
+// specify a suffix for slices parsed by URLValues.
 // If the suffix is set to "[i]", then the index of the slice
 // is used in place of i
 // Ex: Suffix "[]" would have the form a[]=b&a[]=c
diff --git a/vendor/github.com/stretchr/objx/doc.go b/vendor/github.com/stretchr/objx/doc.go
index 6d6af1a8..b170af74 100644
--- a/vendor/github.com/stretchr/objx/doc.go
+++ b/vendor/github.com/stretchr/objx/doc.go
@@ -1,19 +1,19 @@
 /*
-Objx - Go package for dealing with maps, slices, JSON and other data.
+Package objx provides utilities for dealing with maps, slices, JSON and other data.
 
-Overview
+# Overview
 
 Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes
 a powerful `Get` method (among others) that allows you to easily and quickly get
 access to data within the map, without having to worry too much about type assertions,
 missing data, default values etc.
 
-Pattern
+# Pattern
 
-Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy.
+Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy.
 Call one of the `objx.` functions to create your `objx.Map` to get going:
 
-    m, err := objx.FromJSON(json)
+	m, err := objx.FromJSON(json)
 
 NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong,
 the rest will be optimistic and try to figure things out without panicking.
@@ -21,46 +21,46 @@ the rest will be optimistic and try to figure things out without panicking.
 Use `Get` to access the value you're interested in.  You can use dot and array
 notation too:
 
-     m.Get("places[0].latlng")
+	m.Get("places[0].latlng")
 
 Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type.
 
-     if m.Get("code").IsStr() { // Your code... }
+	if m.Get("code").IsStr() { // Your code... }
 
 Or you can just assume the type, and use one of the strong type methods to extract the real value:
 
-   m.Get("code").Int()
+	m.Get("code").Int()
 
 If there's no value there (or if it's the wrong type) then a default value will be returned,
 or you can be explicit about the default value.
 
-     Get("code").Int(-1)
+	Get("code").Int(-1)
 
 If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating,
 manipulating and selecting that data.  You can find out more by exploring the index below.
 
-Reading data
+# Reading data
 
 A simple example of how to use Objx:
 
-   // Use MustFromJSON to make an objx.Map from some JSON
-   m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
+	// Use MustFromJSON to make an objx.Map from some JSON
+	m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
 
-   // Get the details
-   name := m.Get("name").Str()
-   age := m.Get("age").Int()
+	// Get the details
+	name := m.Get("name").Str()
+	age := m.Get("age").Int()
 
-   // Get their nickname (or use their name if they don't have one)
-   nickname := m.Get("nickname").Str(name)
+	// Get their nickname (or use their name if they don't have one)
+	nickname := m.Get("nickname").Str(name)
 
-Ranging
+# Ranging
 
 Since `objx.Map` is a `map[string]interface{}` you can treat it as such.
 For example, to `range` the data, do what you would expect:
 
-    m := objx.MustFromJSON(json)
-    for key, value := range m {
-      // Your code...
-    }
+	m := objx.MustFromJSON(json)
+	for key, value := range m {
+	  // Your code...
+	}
 */
 package objx
diff --git a/vendor/github.com/stretchr/objx/map.go b/vendor/github.com/stretchr/objx/map.go
index a64712a0..ab9f9ae6 100644
--- a/vendor/github.com/stretchr/objx/map.go
+++ b/vendor/github.com/stretchr/objx/map.go
@@ -47,17 +47,16 @@ func New(data interface{}) Map {
 //
 // The arguments follow a key, value pattern.
 //
-//
 // Returns nil if any key argument is non-string or if there are an odd number of arguments.
 //
-// Example
+// # Example
 //
 // To easily create Maps:
 //
-//     m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
+//	m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
 //
-//     // creates an Map equivalent to
-//     m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}}
+//	// creates an Map equivalent to
+//	m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}}
 func MSI(keyAndValuePairs ...interface{}) Map {
 	newMap := Map{}
 	keyAndValuePairsLen := len(keyAndValuePairs)
diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go
index f6e815e6..f0b7f320 100644
--- a/vendor/google.golang.org/grpc/clientconn.go
+++ b/vendor/google.golang.org/grpc/clientconn.go
@@ -1772,6 +1772,8 @@ func parseTarget(target string) (resolver.Target, error) {
 	return resolver.Target{URL: *u}, nil
 }
 
+// encodeAuthority escapes the authority string based on valid chars defined in
+// https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.
 func encodeAuthority(authority string) string {
 	const upperhex = "0123456789ABCDEF"
 
diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go
index b7b8fec1..d3796c25 100644
--- a/vendor/google.golang.org/grpc/internal/transport/transport.go
+++ b/vendor/google.golang.org/grpc/internal/transport/transport.go
@@ -28,6 +28,7 @@ import (
 	"fmt"
 	"io"
 	"net"
+	"strings"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -362,8 +363,12 @@ func (s *Stream) SendCompress() string {
 
 // ClientAdvertisedCompressors returns the compressor names advertised by the
 // client via grpc-accept-encoding header.
-func (s *Stream) ClientAdvertisedCompressors() string {
-	return s.clientAdvertisedCompressors
+func (s *Stream) ClientAdvertisedCompressors() []string {
+	values := strings.Split(s.clientAdvertisedCompressors, ",")
+	for i, v := range values {
+		values[i] = strings.TrimSpace(v)
+	}
+	return values
 }
 
 // Done returns a channel which is closed when it receives the final status
diff --git a/vendor/google.golang.org/grpc/resolver/resolver.go b/vendor/google.golang.org/grpc/resolver/resolver.go
index adf89dd9..d72f21c1 100644
--- a/vendor/google.golang.org/grpc/resolver/resolver.go
+++ b/vendor/google.golang.org/grpc/resolver/resolver.go
@@ -168,6 +168,9 @@ type BuildOptions struct {
 	// field. In most cases though, it is not appropriate, and this field may
 	// be ignored.
 	Dialer func(context.Context, string) (net.Conn, error)
+	// Authority is the effective authority of the clientconn for which the
+	// resolver is built.
+	Authority string
 }
 
 // An Endpoint is one network endpoint, or server, which may have multiple
diff --git a/vendor/google.golang.org/grpc/resolver_wrapper.go b/vendor/google.golang.org/grpc/resolver_wrapper.go
index c79bab12..f845ac95 100644
--- a/vendor/google.golang.org/grpc/resolver_wrapper.go
+++ b/vendor/google.golang.org/grpc/resolver_wrapper.go
@@ -75,6 +75,7 @@ func (ccr *ccResolverWrapper) start() error {
 			DialCreds:            ccr.cc.dopts.copts.TransportCredentials,
 			CredsBundle:          ccr.cc.dopts.copts.CredsBundle,
 			Dialer:               ccr.cc.dopts.copts.Dialer,
+			Authority:            ccr.cc.authority,
 		}
 		var err error
 		ccr.resolver, err = ccr.cc.resolverBuilder.Build(ccr.cc.parsedTarget, ccr, opts)
diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go
index d17ede0f..82493d23 100644
--- a/vendor/google.golang.org/grpc/rpc_util.go
+++ b/vendor/google.golang.org/grpc/rpc_util.go
@@ -744,17 +744,19 @@ type payloadInfo struct {
 	uncompressedBytes []byte
 }
 
-func recvAndDecompress(p *parser, s *transport.Stream, dc Decompressor, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor) ([]byte, error) {
-	pf, buf, err := p.recvMsg(maxReceiveMessageSize)
+// recvAndDecompress reads a message from the stream, decompressing it if necessary.
+//
+// Cancelling the returned cancel function releases the buffer back to the pool. So the caller should cancel as soon as
+// the buffer is no longer needed.
+func recvAndDecompress(p *parser, s *transport.Stream, dc Decompressor, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor,
+) (uncompressedBuf []byte, cancel func(), err error) {
+	pf, compressedBuf, err := p.recvMsg(maxReceiveMessageSize)
 	if err != nil {
-		return nil, err
-	}
-	if payInfo != nil {
-		payInfo.compressedLength = len(buf)
+		return nil, nil, err
 	}
 
 	if st := checkRecvPayload(pf, s.RecvCompress(), compressor != nil || dc != nil); st != nil {
-		return nil, st.Err()
+		return nil, nil, st.Err()
 	}
 
 	var size int
@@ -762,21 +764,35 @@ func recvAndDecompress(p *parser, s *transport.Stream, dc Decompressor, maxRecei
 		// To match legacy behavior, if the decompressor is set by WithDecompressor or RPCDecompressor,
 		// use this decompressor as the default.
 		if dc != nil {
-			buf, err = dc.Do(bytes.NewReader(buf))
-			size = len(buf)
+			uncompressedBuf, err = dc.Do(bytes.NewReader(compressedBuf))
+			size = len(uncompressedBuf)
 		} else {
-			buf, size, err = decompress(compressor, buf, maxReceiveMessageSize)
+			uncompressedBuf, size, err = decompress(compressor, compressedBuf, maxReceiveMessageSize)
 		}
 		if err != nil {
-			return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err)
+			return nil, nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err)
 		}
 		if size > maxReceiveMessageSize {
 			// TODO: Revisit the error code. Currently keep it consistent with java
 			// implementation.
-			return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max (%d vs. %d)", size, maxReceiveMessageSize)
+			return nil, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max (%d vs. %d)", size, maxReceiveMessageSize)
 		}
+	} else {
+		uncompressedBuf = compressedBuf
 	}
-	return buf, nil
+
+	if payInfo != nil {
+		payInfo.compressedLength = len(compressedBuf)
+		payInfo.uncompressedBytes = uncompressedBuf
+
+		cancel = func() {}
+	} else {
+		cancel = func() {
+			p.recvBufferPool.Put(&compressedBuf)
+		}
+	}
+
+	return uncompressedBuf, cancel, nil
 }
 
 // Using compressor, decompress d, returning data and size.
@@ -796,6 +812,9 @@ func decompress(compressor encoding.Compressor, d []byte, maxReceiveMessageSize
 			// size is used as an estimate to size the buffer, but we
 			// will read more data if available.
 			// +MinRead so ReadFrom will not reallocate if size is correct.
+			//
+			// TODO: If we ensure that the buffer size is the same as the DecompressedSize,
+			// we can also utilize the recv buffer pool here.
 			buf := bytes.NewBuffer(make([]byte, 0, size+bytes.MinRead))
 			bytesRead, err := buf.ReadFrom(io.LimitReader(dcReader, int64(maxReceiveMessageSize)+1))
 			return buf.Bytes(), int(bytesRead), err
@@ -811,18 +830,15 @@ func decompress(compressor encoding.Compressor, d []byte, maxReceiveMessageSize
 // dc takes precedence over compressor.
 // TODO(dfawley): wrap the old compressor/decompressor using the new API?
 func recv(p *parser, c baseCodec, s *transport.Stream, dc Decompressor, m any, maxReceiveMessageSize int, payInfo *payloadInfo, compressor encoding.Compressor) error {
-	buf, err := recvAndDecompress(p, s, dc, maxReceiveMessageSize, payInfo, compressor)
+	buf, cancel, err := recvAndDecompress(p, s, dc, maxReceiveMessageSize, payInfo, compressor)
 	if err != nil {
 		return err
 	}
+	defer cancel()
+
 	if err := c.Unmarshal(buf, m); err != nil {
 		return status.Errorf(codes.Internal, "grpc: failed to unmarshal the received message: %v", err)
 	}
-	if payInfo != nil {
-		payInfo.uncompressedBytes = buf
-	} else {
-		p.recvBufferPool.Put(&buf)
-	}
 	return nil
 }
 
diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go
index 0bf5c78b..a6a11704 100644
--- a/vendor/google.golang.org/grpc/server.go
+++ b/vendor/google.golang.org/grpc/server.go
@@ -1342,7 +1342,8 @@ func (s *Server) processUnaryRPC(ctx context.Context, t transport.ServerTranspor
 	if len(shs) != 0 || len(binlogs) != 0 {
 		payInfo = &payloadInfo{}
 	}
-	d, err := recvAndDecompress(&parser{r: stream, recvBufferPool: s.opts.recvBufferPool}, stream, dc, s.opts.maxReceiveMessageSize, payInfo, decomp)
+
+	d, cancel, err := recvAndDecompress(&parser{r: stream, recvBufferPool: s.opts.recvBufferPool}, stream, dc, s.opts.maxReceiveMessageSize, payInfo, decomp)
 	if err != nil {
 		if e := t.WriteStatus(stream, status.Convert(err)); e != nil {
 			channelz.Warningf(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e)
@@ -1353,6 +1354,8 @@ func (s *Server) processUnaryRPC(ctx context.Context, t transport.ServerTranspor
 		t.IncrMsgRecv()
 	}
 	df := func(v any) error {
+		defer cancel()
+
 		if err := s.getCodec(stream.ContentSubtype()).Unmarshal(d, v); err != nil {
 			return status.Errorf(codes.Internal, "grpc: error unmarshalling request: %v", err)
 		}
@@ -2117,7 +2120,7 @@ func ClientSupportedCompressors(ctx context.Context) ([]string, error) {
 		return nil, fmt.Errorf("failed to fetch the stream from the given context %v", ctx)
 	}
 
-	return strings.Split(stream.ClientAdvertisedCompressors(), ","), nil
+	return stream.ClientAdvertisedCompressors(), nil
 }
 
 // SetTrailer sets the trailer metadata that will be sent when an RPC returns.
@@ -2157,7 +2160,7 @@ func (c *channelzServer) ChannelzMetric() *channelz.ServerInternalMetric {
 
 // validateSendCompressor returns an error when given compressor name cannot be
 // handled by the server or the client based on the advertised compressors.
-func validateSendCompressor(name, clientCompressors string) error {
+func validateSendCompressor(name string, clientCompressors []string) error {
 	if name == encoding.Identity {
 		return nil
 	}
@@ -2166,7 +2169,7 @@ func validateSendCompressor(name, clientCompressors string) error {
 		return fmt.Errorf("compressor not registered %q", name)
 	}
 
-	for _, c := range strings.Split(clientCompressors, ",") {
+	for _, c := range clientCompressors {
 		if c == name {
 			return nil // found match
 		}
diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go
index df85a021..46ad8113 100644
--- a/vendor/google.golang.org/grpc/version.go
+++ b/vendor/google.golang.org/grpc/version.go
@@ -19,4 +19,4 @@
 package grpc
 
 // Version is the current grpc version.
-const Version = "1.62.0"
+const Version = "1.62.1"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 33e35128..63801340 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -199,7 +199,7 @@ github.com/mdlayher/ethernet
 # github.com/minio/md5-simd v1.1.2
 ## explicit; go 1.14
 github.com/minio/md5-simd
-# github.com/minio/minio-go/v7 v7.0.68
+# github.com/minio/minio-go/v7 v7.0.69
 ## explicit; go 1.21
 github.com/minio/minio-go/v7
 github.com/minio/minio-go/v7/pkg/credentials
@@ -250,7 +250,7 @@ github.com/netobserv/loki-client-go/pkg/labelutil
 github.com/netobserv/loki-client-go/pkg/logproto
 github.com/netobserv/loki-client-go/pkg/metric
 github.com/netobserv/loki-client-go/pkg/urlutil
-# github.com/netobserv/netobserv-ebpf-agent v0.3.3
+# github.com/netobserv/netobserv-ebpf-agent v0.3.4-0.20240312085757-e9bf0d0d5a09
 ## explicit; go 1.20
 github.com/netobserv/netobserv-ebpf-agent/pkg/decode
 github.com/netobserv/netobserv-ebpf-agent/pkg/grpc
@@ -454,11 +454,11 @@ github.com/spf13/viper/internal/encoding/json
 github.com/spf13/viper/internal/encoding/toml
 github.com/spf13/viper/internal/encoding/yaml
 github.com/spf13/viper/internal/features
-# github.com/stretchr/objx v0.5.0
-## explicit; go 1.12
-github.com/stretchr/objx
-# github.com/stretchr/testify v1.8.4
+# github.com/stretchr/objx v0.5.2
 ## explicit; go 1.20
+github.com/stretchr/objx
+# github.com/stretchr/testify v1.9.0
+## explicit; go 1.17
 github.com/stretchr/testify/assert
 github.com/stretchr/testify/mock
 github.com/stretchr/testify/require
@@ -654,7 +654,7 @@ google.golang.org/genproto/googleapis/api/httpbody
 ## explicit; go 1.19
 google.golang.org/genproto/googleapis/rpc/errdetails
 google.golang.org/genproto/googleapis/rpc/status
-# google.golang.org/grpc v1.62.0
+# google.golang.org/grpc v1.62.1
 ## explicit; go 1.19
 google.golang.org/grpc
 google.golang.org/grpc/attributes


Test patch:
diff --git a/pkg/pipeline/pipeline_test.go b/pkg/pipeline/pipeline_test.go
index d9742f2d..fa8476fc 100644
--- a/pkg/pipeline/pipeline_test.go
+++ b/pkg/pipeline/pipeline_test.go
@@ -228,7 +228,7 @@ parameters:
 	assert.NotZero(t, capturedRecord["TimeReceived"])
 	delete(capturedRecord, "TimeReceived")
 	assert.EqualValues(t, map[string]interface{}{
-		"FlowDirection":          float64(1),
+		"IfDirections":           []interface{}{float64(1)},
 		"Bytes":                  float64(456),
 		"SrcAddr":                "1.2.3.4",
 		"DstAddr":                "5.6.7.8",
@@ -237,13 +237,12 @@ parameters:
 		"SrcMac":                 "01:02:03:04:05:06",
 		"SrcPort":                float64(23000),
 		"DstPort":                float64(443),
-		"Duplicate":              false,
 		"Etype":                  float64(2048),
 		"Packets":                float64(123),
 		"Proto":                  float64(17),
 		"TimeFlowStartMs":        float64(startTime.UnixMilli()),
 		"TimeFlowEndMs":          float64(endTime.UnixMilli()),
-		"Interface":              "eth0",
+		"Interfaces":             []interface{}{"eth0"},
 		"AgentIP":                "10.11.12.13",
 		"PktDropBytes":           float64(100),
 		"PktDropPackets":         float64(10),
diff --git a/pkg/pipeline/write/grpc/grpc_test.go b/pkg/pipeline/write/grpc/grpc_test.go
new file mode 100644
index 00000000..226c0b73
--- /dev/null
+++ b/pkg/pipeline/write/grpc/grpc_test.go
@@ -0,0 +1,94 @@
+package grpc
+
+import (
+	"context"
+	"encoding/json"
+	"testing"
+	"time"
+
+	"github.com/mariomac/guara/pkg/test"
+	"github.com/netobserv/flowlogs-pipeline/pkg/config"
+	"github.com/netobserv/flowlogs-pipeline/pkg/pipeline/write/grpc/genericmap"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"google.golang.org/grpc"
+	"google.golang.org/protobuf/types/known/anypb"
+)
+
+const timeout = 5 * time.Second
+
+func TestGRPCCommunication(t *testing.T) {
+	port, err := test.FreeTCPPort()
+	require.NoError(t, err)
+	serverOut := make(chan *genericmap.Flow)
+	_, err = StartCollector(port, serverOut)
+	require.NoError(t, err)
+	cc, err := ConnectClient("127.0.0.1", port)
+	require.NoError(t, err)
+	client := cc.Client()
+
+	gm := config.GenericMap{
+		"test": "test",
+	}
+	value, err := json.Marshal(gm)
+	require.NoError(t, err)
+
+	go func() {
+		_, err = client.Send(context.Background(),
+			&genericmap.Flow{
+				GenericMap: &anypb.Any{
+					Value: value,
+				},
+			})
+		require.NoError(t, err)
+	}()
+
+	var rs *genericmap.Flow
+	select {
+	case rs = <-serverOut:
+	case <-time.After(timeout):
+		require.Fail(t, "timeout waiting for flows")
+	}
+	assert.NotNil(t, rs.GenericMap)
+	assert.EqualValues(t, value, rs.GenericMap.Value)
+	select {
+	case rs = <-serverOut:
+		assert.Failf(t, "shouldn't have received any flow", "Got: %#v", rs)
+	default:
+		//ok!
+	}
+}
+
+func TestConstructorOptions(t *testing.T) {
+	port, err := test.FreeTCPPort()
+	require.NoError(t, err)
+	intercepted := make(chan struct{})
+	// Override the default GRPC collector to verify that StartCollector is applying the
+	// passed options
+	_, err = StartCollector(port, make(chan *genericmap.Flow),
+		WithGRPCServerOptions(grpc.UnaryInterceptor(func(
+			ctx context.Context,
+			req interface{},
+			info *grpc.UnaryServerInfo,
+			handler grpc.UnaryHandler,
+		) (resp interface{}, err error) {
+			close(intercepted)
+			return handler(ctx, req)
+		})))
+	require.NoError(t, err)
+	cc, err := ConnectClient("127.0.0.1", port)
+	require.NoError(t, err)
+	client := cc.Client()
+
+	go func() {
+		_, err = client.Send(context.Background(),
+			&genericmap.Flow{GenericMap: &anypb.Any{}})
+		require.NoError(t, err)
+	}()
+
+	select {
+	case <-intercepted:
+	case <-time.After(timeout):
+		require.Fail(t, "timeout waiting for unary interceptor to work")
+	}
+}
diff --git a/pkg/pipeline/write/testnorace/write_ipfix_test.go b/pkg/pipeline/write/testnorace/write_ipfix_test.go
index 075764d4..13d5aa94 100644
--- a/pkg/pipeline/write/testnorace/write_ipfix_test.go
+++ b/pkg/pipeline/write/testnorace/write_ipfix_test.go
@@ -147,7 +147,7 @@ func matchElement(t *testing.T, element entities.InfoElementWithValue, flow conf
 	case "ethernetType":
 		assert.Equal(t, flow["Etype"], uint32(element.GetUnsigned16Value()))
 	case "flowDirection":
-		assert.Equal(t, flow["FlowDirection"], int(element.GetUnsigned8Value()))
+		assert.Equal(t, flow["IfDirections"], []int{int(element.GetUnsigned8Value())})
 	case "protocolIdentifier":
 		assert.Equal(t, flow["Proto"], uint32(element.GetUnsigned8Value()))
 	case "sourceTransportPort":
@@ -163,7 +163,7 @@ func matchElement(t *testing.T, element entities.InfoElementWithValue, flow conf
 	case "packetDeltaCount":
 		assert.Equal(t, flow["Packets"], element.GetUnsigned64Value())
 	case "interfaceName":
-		assert.Equal(t, flow["Interface"], element.GetStringValue())
+		assert.Equal(t, flow["Interfaces"], []string{element.GetStringValue()})
 	case "sourcePodNamespace":
 		assert.Equal(t, flow["SrcK8S_Namespace"], element.GetStringValue())
 	case "sourcePodName":
diff --git a/pkg/pipeline/write/write_grpc_test.go b/pkg/pipeline/write/write_grpc_test.go
new file mode 100644
index 00000000..fddd07f7
--- /dev/null
+++ b/pkg/pipeline/write/write_grpc_test.go
@@ -0,0 +1,42 @@
+package write
+
+import (
+	"testing"
+
+	"github.com/mariomac/guara/pkg/test"
+	"github.com/netobserv/flowlogs-pipeline/pkg/api"
+	"github.com/netobserv/flowlogs-pipeline/pkg/config"
+	"github.com/netobserv/flowlogs-pipeline/pkg/pipeline/write/grpc"
+	"github.com/stretchr/testify/require"
+)
+
+func Test_WriteGRPC(t *testing.T) {
+	port, err := test.FreeTCPPort()
+	require.NoError(t, err)
+	cc, err := grpc.ConnectClient("127.0.0.1", port)
+	require.NoError(t, err)
+	ws := writeGRPC{
+		hostIP:     "127.0.0.1",
+		hostPort:   port,
+		clientConn: cc,
+	}
+	ws.Write(config.GenericMap{"key": "test"})
+}
+
+func Test_NewWriteGRPC(t *testing.T) {
+	writer, err := NewWriteGRPC(config.StageParam{})
+	require.Nil(t, writer)
+	require.Error(t, err)
+
+	writeParams := api.WriteGRPC{
+		TargetHost: "target",
+		TargetPort: 1234,
+	}
+	writer, err = NewWriteGRPC(config.StageParam{
+		Write: &config.Write{
+			GRPC: &writeParams,
+		},
+	})
+	require.Nil(t, err)
+	require.NotNil(t, writer)
+}
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go
index b774da88..4d4b4aad 100644
--- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go
+++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go
@@ -28,6 +28,8 @@ var (
 	uint32Type = reflect.TypeOf(uint32(1))
 	uint64Type = reflect.TypeOf(uint64(1))
 
+	uintptrType = reflect.TypeOf(uintptr(1))
+
 	float32Type = reflect.TypeOf(float32(1))
 	float64Type = reflect.TypeOf(float64(1))
 
@@ -308,11 +310,11 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
 	case reflect.Struct:
 		{
 			// All structs enter here. We're not interested in most types.
-			if !canConvert(obj1Value, timeType) {
+			if !obj1Value.CanConvert(timeType) {
 				break
 			}
 
-			// time.Time can compared!
+			// time.Time can be compared!
 			timeObj1, ok := obj1.(time.Time)
 			if !ok {
 				timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time)
@@ -328,7 +330,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
 	case reflect.Slice:
 		{
 			// We only care about the []byte type.
-			if !canConvert(obj1Value, bytesType) {
+			if !obj1Value.CanConvert(bytesType) {
 				break
 			}
 
@@ -345,6 +347,26 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
 
 			return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true
 		}
+	case reflect.Uintptr:
+		{
+			uintptrObj1, ok := obj1.(uintptr)
+			if !ok {
+				uintptrObj1 = obj1Value.Convert(uintptrType).Interface().(uintptr)
+			}
+			uintptrObj2, ok := obj2.(uintptr)
+			if !ok {
+				uintptrObj2 = obj2Value.Convert(uintptrType).Interface().(uintptr)
+			}
+			if uintptrObj1 > uintptrObj2 {
+				return compareGreater, true
+			}
+			if uintptrObj1 == uintptrObj2 {
+				return compareEqual, true
+			}
+			if uintptrObj1 < uintptrObj2 {
+				return compareLess, true
+			}
+		}
 	}
 
 	return compareEqual, false
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go
deleted file mode 100644
index da867903..00000000
--- a/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go
+++ /dev/null
@@ -1,16 +0,0 @@
-//go:build go1.17
-// +build go1.17
-
-// TODO: once support for Go 1.16 is dropped, this file can be
-//       merged/removed with assertion_compare_go1.17_test.go and
-//       assertion_compare_legacy.go
-
-package assert
-
-import "reflect"
-
-// Wrapper around reflect.Value.CanConvert, for compatibility
-// reasons.
-func canConvert(value reflect.Value, to reflect.Type) bool {
-	return value.CanConvert(to)
-}
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go
deleted file mode 100644
index 1701af2a..00000000
--- a/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go
+++ /dev/null
@@ -1,16 +0,0 @@
-//go:build !go1.17
-// +build !go1.17
-
-// TODO: once support for Go 1.16 is dropped, this file can be
-//       merged/removed with assertion_compare_go1.17_test.go and
-//       assertion_compare_can_convert.go
-
-package assert
-
-import "reflect"
-
-// Older versions of Go does not have the reflect.Value.CanConvert
-// method.
-func canConvert(value reflect.Value, to reflect.Type) bool {
-	return false
-}
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go
index 84dbd6c7..3ddab109 100644
--- a/vendor/github.com/stretchr/testify/assert/assertion_format.go
+++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go
@@ -1,7 +1,4 @@
-/*
-* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
-* THIS FILE MUST NOT BE EDITED BY HAND
- */
+// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT.
 
 package assert
 
@@ -107,7 +104,7 @@ func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{},
 	return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...)
 }
 
-// EqualValuesf asserts that two objects are equal or convertable to the same types
+// EqualValuesf asserts that two objects are equal or convertible to the same types
 // and equal.
 //
 //	assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
@@ -616,6 +613,16 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf
 	return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...)
 }
 
+// NotImplementsf asserts that an object does not implement the specified interface.
+//
+//	assert.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
+func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	return NotImplements(t, interfaceObject, object, append([]interface{}{msg}, args...)...)
+}
+
 // NotNilf asserts that the specified object is not nil.
 //
 //	assert.NotNilf(t, err, "error message %s", "formatted")
@@ -660,10 +667,12 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string,
 	return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...)
 }
 
-// NotSubsetf asserts that the specified list(array, slice...) contains not all
-// elements given in the specified subset(array, slice...).
+// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
+// contain all elements given in the specified subset list(array, slice...) or
+// map.
 //
-//	assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
+//	assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted")
+//	assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
 func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -747,10 +756,11 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg
 	return Same(t, expected, actual, append([]interface{}{msg}, args...)...)
 }
 
-// Subsetf asserts that the specified list(array, slice...) contains all
-// elements given in the specified subset(array, slice...).
+// Subsetf asserts that the specified list(array, slice...) or map contains all
+// elements given in the specified subset list(array, slice...) or map.
 //
-//	assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
+//	assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted")
+//	assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
 func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go
index b1d94aec..a84e09bd 100644
--- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go
+++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go
@@ -1,7 +1,4 @@
-/*
-* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
-* THIS FILE MUST NOT BE EDITED BY HAND
- */
+// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT.
 
 package assert
 
@@ -189,7 +186,7 @@ func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface
 	return EqualExportedValuesf(a.t, expected, actual, msg, args...)
 }
 
-// EqualValues asserts that two objects are equal or convertable to the same types
+// EqualValues asserts that two objects are equal or convertible to the same types
 // and equal.
 //
 //	a.EqualValues(uint32(123), int32(123))
@@ -200,7 +197,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
 	return EqualValues(a.t, expected, actual, msgAndArgs...)
 }
 
-// EqualValuesf asserts that two objects are equal or convertable to the same types
+// EqualValuesf asserts that two objects are equal or convertible to the same types
 // and equal.
 //
 //	a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted")
@@ -1221,6 +1218,26 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in
 	return NotErrorIsf(a.t, err, target, msg, args...)
 }
 
+// NotImplements asserts that an object does not implement the specified interface.
+//
+//	a.NotImplements((*MyInterface)(nil), new(MyObject))
+func (a *Assertions) NotImplements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	return NotImplements(a.t, interfaceObject, object, msgAndArgs...)
+}
+
+// NotImplementsf asserts that an object does not implement the specified interface.
+//
+//	a.NotImplementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
+func (a *Assertions) NotImplementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	return NotImplementsf(a.t, interfaceObject, object, msg, args...)
+}
+
 // NotNil asserts that the specified object is not nil.
 //
 //	a.NotNil(err)
@@ -1309,10 +1326,12 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri
 	return NotSamef(a.t, expected, actual, msg, args...)
 }
 
-// NotSubset asserts that the specified list(array, slice...) contains not all
-// elements given in the specified subset(array, slice...).
+// NotSubset asserts that the specified list(array, slice...) or map does NOT
+// contain all elements given in the specified subset list(array, slice...) or
+// map.
 //
-//	a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
+//	a.NotSubset([1, 3, 4], [1, 2])
+//	a.NotSubset({"x": 1, "y": 2}, {"z": 3})
 func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1320,10 +1339,12 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs
 	return NotSubset(a.t, list, subset, msgAndArgs...)
 }
 
-// NotSubsetf asserts that the specified list(array, slice...) contains not all
-// elements given in the specified subset(array, slice...).
+// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
+// contain all elements given in the specified subset list(array, slice...) or
+// map.
 //
-//	a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
+//	a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted")
+//	a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
 func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1483,10 +1504,11 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string,
 	return Samef(a.t, expected, actual, msg, args...)
 }
 
-// Subset asserts that the specified list(array, slice...) contains all
-// elements given in the specified subset(array, slice...).
+// Subset asserts that the specified list(array, slice...) or map contains all
+// elements given in the specified subset list(array, slice...) or map.
 //
-//	a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
+//	a.Subset([1, 2, 3], [1, 2])
+//	a.Subset({"x": 1, "y": 2}, {"x": 1})
 func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1494,10 +1516,11 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...
 	return Subset(a.t, list, subset, msgAndArgs...)
 }
 
-// Subsetf asserts that the specified list(array, slice...) contains all
-// elements given in the specified subset(array, slice...).
+// Subsetf asserts that the specified list(array, slice...) or map contains all
+// elements given in the specified subset list(array, slice...) or map.
 //
-//	a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
+//	a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted")
+//	a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
 func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go
index a55d1bba..0b7570f2 100644
--- a/vendor/github.com/stretchr/testify/assert/assertions.go
+++ b/vendor/github.com/stretchr/testify/assert/assertions.go
@@ -19,7 +19,7 @@ import (
 
 	"github.com/davecgh/go-spew/spew"
 	"github.com/pmezard/go-difflib/difflib"
-	yaml "gopkg.in/yaml.v3"
+	"gopkg.in/yaml.v3"
 )
 
 //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
@@ -110,7 +110,12 @@ func copyExportedFields(expected interface{}) interface{} {
 		return result.Interface()
 
 	case reflect.Array, reflect.Slice:
-		result := reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len())
+		var result reflect.Value
+		if expectedKind == reflect.Array {
+			result = reflect.New(reflect.ArrayOf(expectedValue.Len(), expectedType.Elem())).Elem()
+		} else {
+			result = reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len())
+		}
 		for i := 0; i < expectedValue.Len(); i++ {
 			index := expectedValue.Index(i)
 			if isNil(index) {
@@ -140,6 +145,8 @@ func copyExportedFields(expected interface{}) interface{} {
 // structures.
 //
 // This function does no assertion of any kind.
+//
+// Deprecated: Use [EqualExportedValues] instead.
 func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool {
 	expectedCleaned := copyExportedFields(expected)
 	actualCleaned := copyExportedFields(actual)
@@ -153,17 +160,40 @@ func ObjectsAreEqualValues(expected, actual interface{}) bool {
 		return true
 	}
 
-	actualType := reflect.TypeOf(actual)
-	if actualType == nil {
+	expectedValue := reflect.ValueOf(expected)
+	actualValue := reflect.ValueOf(actual)
+	if !expectedValue.IsValid() || !actualValue.IsValid() {
 		return false
 	}
-	expectedValue := reflect.ValueOf(expected)
-	if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) {
+
+	expectedType := expectedValue.Type()
+	actualType := actualValue.Type()
+	if !expectedType.ConvertibleTo(actualType) {
+		return false
+	}
+
+	if !isNumericType(expectedType) || !isNumericType(actualType) {
 		// Attempt comparison after type conversion
-		return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual)
+		return reflect.DeepEqual(
+			expectedValue.Convert(actualType).Interface(), actual,
+		)
 	}
 
-	return false
+	// If BOTH values are numeric, there are chances of false positives due
+	// to overflow or underflow. So, we need to make sure to always convert
+	// the smaller type to a larger type before comparing.
+	if expectedType.Size() >= actualType.Size() {
+		return actualValue.Convert(expectedType).Interface() == expected
+	}
+
+	return expectedValue.Convert(actualType).Interface() == actual
+}
+
+// isNumericType returns true if the type is one of:
+// int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64,
+// float32, float64, complex64, complex128
+func isNumericType(t reflect.Type) bool {
+	return t.Kind() >= reflect.Int && t.Kind() <= reflect.Complex128
 }
 
 /* CallerInfo is necessary because the assert functions use the testing object
@@ -266,7 +296,7 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
 
 // Aligns the provided message so that all lines after the first line start at the same location as the first line.
 // Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab).
-// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the
+// The longestLabelLen parameter specifies the length of the longest label in the output (required because this is the
 // basis on which the alignment occurs).
 func indentMessageLines(message string, longestLabelLen int) string {
 	outBuf := new(bytes.Buffer)
@@ -382,6 +412,25 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg
 	return true
 }
 
+// NotImplements asserts that an object does not implement the specified interface.
+//
+//	assert.NotImplements(t, (*MyInterface)(nil), new(MyObject))
+func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	interfaceType := reflect.TypeOf(interfaceObject).Elem()
+
+	if object == nil {
+		return Fail(t, fmt.Sprintf("Cannot check if nil does not implement %v", interfaceType), msgAndArgs...)
+	}
+	if reflect.TypeOf(object).Implements(interfaceType) {
+		return Fail(t, fmt.Sprintf("%T implements %v", object, interfaceType), msgAndArgs...)
+	}
+
+	return true
+}
+
 // IsType asserts that the specified objects are of the same type.
 func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
 	if h, ok := t.(tHelper); ok {
@@ -496,7 +545,7 @@ func samePointers(first, second interface{}) bool {
 // representations appropriate to be presented to the user.
 //
 // If the values are not of like type, the returned strings will be prefixed
-// with the type name, and the value will be enclosed in parenthesis similar
+// with the type name, and the value will be enclosed in parentheses similar
 // to a type conversion in the Go grammar.
 func formatUnequalValues(expected, actual interface{}) (e string, a string) {
 	if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
@@ -523,7 +572,7 @@ func truncatingFormat(data interface{}) string {
 	return value
 }
 
-// EqualValues asserts that two objects are equal or convertable to the same types
+// EqualValues asserts that two objects are equal or convertible to the same types
 // and equal.
 //
 //	assert.EqualValues(t, uint32(123), int32(123))
@@ -566,12 +615,19 @@ func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ..
 		return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
 	}
 
+	if aType.Kind() == reflect.Ptr {
+		aType = aType.Elem()
+	}
+	if bType.Kind() == reflect.Ptr {
+		bType = bType.Elem()
+	}
+
 	if aType.Kind() != reflect.Struct {
-		return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...)
+		return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...)
 	}
 
 	if bType.Kind() != reflect.Struct {
-		return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...)
+		return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...)
 	}
 
 	expected = copyExportedFields(expected)
@@ -620,17 +676,6 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
 	return Fail(t, "Expected value not to be nil.", msgAndArgs...)
 }
 
-// containsKind checks if a specified kind in the slice of kinds.
-func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool {
-	for i := 0; i < len(kinds); i++ {
-		if kind == kinds[i] {
-			return true
-		}
-	}
-
-	return false
-}
-
 // isNil checks if a specified object is nil or not, without Failing.
 func isNil(object interface{}) bool {
 	if object == nil {
@@ -638,16 +683,13 @@ func isNil(object interface{}) bool {
 	}
 
 	value := reflect.ValueOf(object)
-	kind := value.Kind()
-	isNilableKind := containsKind(
-		[]reflect.Kind{
-			reflect.Chan, reflect.Func,
-			reflect.Interface, reflect.Map,
-			reflect.Ptr, reflect.Slice, reflect.UnsafePointer},
-		kind)
-
-	if isNilableKind && value.IsNil() {
-		return true
+	switch value.Kind() {
+	case
+		reflect.Chan, reflect.Func,
+		reflect.Interface, reflect.Map,
+		reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
+
+		return value.IsNil()
 	}
 
 	return false
@@ -731,16 +773,14 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
 
 }
 
-// getLen try to get length of object.
-// return (false, 0) if impossible.
-func getLen(x interface{}) (ok bool, length int) {
+// getLen tries to get the length of an object.
+// It returns (0, false) if impossible.
+func getLen(x interface{}) (length int, ok bool) {
 	v := reflect.ValueOf(x)
 	defer func() {
-		if e := recover(); e != nil {
-			ok = false
-		}
+		ok = recover() == nil
 	}()
-	return true, v.Len()
+	return v.Len(), true
 }
 
 // Len asserts that the specified object has specific length.
@@ -751,13 +791,13 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{})
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
 	}
-	ok, l := getLen(object)
+	l, ok := getLen(object)
 	if !ok {
-		return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...)
+		return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...)
 	}
 
 	if l != length {
-		return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
+		return Fail(t, fmt.Sprintf("\"%v\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
 	}
 	return true
 }
@@ -919,10 +959,11 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
 
 }
 
-// Subset asserts that the specified list(array, slice...) contains all
-// elements given in the specified subset(array, slice...).
+// Subset asserts that the specified list(array, slice...) or map contains all
+// elements given in the specified subset list(array, slice...) or map.
 //
-//	assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
+//	assert.Subset(t, [1, 2, 3], [1, 2])
+//	assert.Subset(t, {"x": 1, "y": 2}, {"x": 1})
 func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -975,10 +1016,12 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
 	return true
 }
 
-// NotSubset asserts that the specified list(array, slice...) contains not all
-// elements given in the specified subset(array, slice...).
+// NotSubset asserts that the specified list(array, slice...) or map does NOT
+// contain all elements given in the specified subset list(array, slice...) or
+// map.
 //
-//	assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
+//	assert.NotSubset(t, [1, 3, 4], [1, 2])
+//	assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3})
 func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1439,7 +1482,7 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd
 		h.Helper()
 	}
 	if math.IsNaN(epsilon) {
-		return Fail(t, "epsilon must not be NaN")
+		return Fail(t, "epsilon must not be NaN", msgAndArgs...)
 	}
 	actualEpsilon, err := calcRelativeError(expected, actual)
 	if err != nil {
@@ -1458,19 +1501,26 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
 	}
-	if expected == nil || actual == nil ||
-		reflect.TypeOf(actual).Kind() != reflect.Slice ||
-		reflect.TypeOf(expected).Kind() != reflect.Slice {
+
+	if expected == nil || actual == nil {
 		return Fail(t, "Parameters must be slice", msgAndArgs...)
 	}
 
-	actualSlice := reflect.ValueOf(actual)
 	expectedSlice := reflect.ValueOf(expected)
+	actualSlice := reflect.ValueOf(actual)
 
-	for i := 0; i < actualSlice.Len(); i++ {
-		result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon)
-		if !result {
-			return result
+	if expectedSlice.Type().Kind() != reflect.Slice {
+		return Fail(t, "Expected value must be slice", msgAndArgs...)
+	}
+
+	expectedLen := expectedSlice.Len()
+	if !IsType(t, expected, actual) || !Len(t, actual, expectedLen) {
+		return false
+	}
+
+	for i := 0; i < expectedLen; i++ {
+		if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon, "at index %d", i) {
+			return false
 		}
 	}
 
@@ -1870,23 +1920,18 @@ func (c *CollectT) Errorf(format string, args ...interface{}) {
 }
 
 // FailNow panics.
-func (c *CollectT) FailNow() {
+func (*CollectT) FailNow() {
 	panic("Assertion failed")
 }
 
-// Reset clears the collected errors.
-func (c *CollectT) Reset() {
-	c.errors = nil
+// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
+func (*CollectT) Reset() {
+	panic("Reset() is deprecated")
 }
 
-// Copy copies the collected errors to the supplied t.
-func (c *CollectT) Copy(t TestingT) {
-	if tt, ok := t.(tHelper); ok {
-		tt.Helper()
-	}
-	for _, err := range c.errors {
-		t.Errorf("%v", err)
-	}
+// Deprecated: That was a method for internal usage that should not have been published. Now just panics.
+func (*CollectT) Copy(TestingT) {
+	panic("Copy() is deprecated")
 }
 
 // EventuallyWithT asserts that given condition will be met in waitFor time,
@@ -1912,8 +1957,8 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
 		h.Helper()
 	}
 
-	collect := new(CollectT)
-	ch := make(chan bool, 1)
+	var lastFinishedTickErrs []error
+	ch := make(chan []error, 1)
 
 	timer := time.NewTimer(waitFor)
 	defer timer.Stop()
@@ -1924,19 +1969,25 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
 	for tick := ticker.C; ; {
 		select {
 		case <-timer.C:
-			collect.Copy(t)
+			for _, err := range lastFinishedTickErrs {
+				t.Errorf("%v", err)
+			}
 			return Fail(t, "Condition never satisfied", msgAndArgs...)
 		case <-tick:
 			tick = nil
-			collect.Reset()
 			go func() {
+				collect := new(CollectT)
+				defer func() {
+					ch <- collect.errors
+				}()
 				condition(collect)
-				ch <- len(collect.errors) == 0
 			}()
-		case v := <-ch:
-			if v {
+		case errs := <-ch:
+			if len(errs) == 0 {
 				return true
 			}
+			// Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached.
+			lastFinishedTickErrs = errs
 			tick = ticker.C
 		}
 	}
diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go
index d8038c28..861ed4b7 100644
--- a/vendor/github.com/stretchr/testify/assert/http_assertions.go
+++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go
@@ -12,7 +12,7 @@ import (
 // an error if building a new request fails.
 func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) {
 	w := httptest.NewRecorder()
-	req, err := http.NewRequest(method, url, nil)
+	req, err := http.NewRequest(method, url, http.NoBody)
 	if err != nil {
 		return -1, err
 	}
@@ -32,12 +32,12 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
 	}
 	code, err := httpCode(handler, method, url, values)
 	if err != nil {
-		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
+		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...)
 	}
 
 	isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent
 	if !isSuccessCode {
-		Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code))
+		Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...)
 	}
 
 	return isSuccessCode
@@ -54,12 +54,12 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
 	}
 	code, err := httpCode(handler, method, url, values)
 	if err != nil {
-		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
+		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...)
 	}
 
 	isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
 	if !isRedirectCode {
-		Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code))
+		Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...)
 	}
 
 	return isRedirectCode
@@ -76,12 +76,12 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
 	}
 	code, err := httpCode(handler, method, url, values)
 	if err != nil {
-		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
+		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...)
 	}
 
 	isErrorCode := code >= http.StatusBadRequest
 	if !isErrorCode {
-		Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code))
+		Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...)
 	}
 
 	return isErrorCode
@@ -98,12 +98,12 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, va
 	}
 	code, err := httpCode(handler, method, url, values)
 	if err != nil {
-		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
+		Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...)
 	}
 
 	successful := code == statuscode
 	if !successful {
-		Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code))
+		Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code), msgAndArgs...)
 	}
 
 	return successful
@@ -113,7 +113,10 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, va
 // empty string if building a new request fails.
 func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
 	w := httptest.NewRecorder()
-	req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
+	if len(values) > 0 {
+		url += "?" + values.Encode()
+	}
+	req, err := http.NewRequest(method, url, http.NoBody)
 	if err != nil {
 		return ""
 	}
@@ -135,7 +138,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string,
 
 	contains := strings.Contains(body, fmt.Sprint(str))
 	if !contains {
-		Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
+		Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...)
 	}
 
 	return contains
@@ -155,7 +158,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url strin
 
 	contains := strings.Contains(body, fmt.Sprint(str))
 	if contains {
-		Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
+		Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...)
 	}
 
 	return !contains
diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go
index f4b42e44..213bde2e 100644
--- a/vendor/github.com/stretchr/testify/mock/mock.go
+++ b/vendor/github.com/stretchr/testify/mock/mock.go
@@ -18,6 +18,9 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+// regex for GCCGO functions
+var gccgoRE = regexp.MustCompile(`\.pN\d+_`)
+
 // TestingT is an interface wrapper around *testing.T
 type TestingT interface {
 	Logf(format string, args ...interface{})
@@ -111,7 +114,7 @@ func (c *Call) Return(returnArguments ...interface{}) *Call {
 	return c
 }
 
-// Panic specifies if the functon call should fail and the panic message
+// Panic specifies if the function call should fail and the panic message
 //
 //	Mock.On("DoSomething").Panic("test panic")
 func (c *Call) Panic(msg string) *Call {
@@ -123,21 +126,21 @@ func (c *Call) Panic(msg string) *Call {
 	return c
 }
 
-// Once indicates that that the mock should only return the value once.
+// Once indicates that the mock should only return the value once.
 //
 //	Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
 func (c *Call) Once() *Call {
 	return c.Times(1)
 }
 
-// Twice indicates that that the mock should only return the value twice.
+// Twice indicates that the mock should only return the value twice.
 //
 //	Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
 func (c *Call) Twice() *Call {
 	return c.Times(2)
 }
 
-// Times indicates that that the mock should only return the indicated number
+// Times indicates that the mock should only return the indicated number
 // of times.
 //
 //	Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
@@ -455,9 +458,8 @@ func (m *Mock) Called(arguments ...interface{}) Arguments {
 	// For Ex:  github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock
 	// uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree
 	// With GCCGO we need to remove interface information starting from pN<dd>.
-	re := regexp.MustCompile("\\.pN\\d+_")
-	if re.MatchString(functionPath) {
-		functionPath = re.Split(functionPath, -1)[0]
+	if gccgoRE.MatchString(functionPath) {
+		functionPath = gccgoRE.Split(functionPath, -1)[0]
 	}
 	parts := strings.Split(functionPath, ".")
 	functionName := parts[len(parts)-1]
@@ -474,7 +476,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
 	found, call := m.findExpectedCall(methodName, arguments...)
 
 	if found < 0 {
-		// expected call found but it has already been called with repeatable times
+		// expected call found, but it has already been called with repeatable times
 		if call != nil {
 			m.mutex.Unlock()
 			m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo())
@@ -563,7 +565,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen
 	Assertions
 */
 
-type assertExpectationser interface {
+type assertExpectationiser interface {
 	AssertExpectations(TestingT) bool
 }
 
@@ -580,7 +582,7 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
 			t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)")
 			obj = m
 		}
-		m := obj.(assertExpectationser)
+		m := obj.(assertExpectationiser)
 		if !m.AssertExpectations(t) {
 			t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m))
 			return false
@@ -592,6 +594,9 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
 // AssertExpectations asserts that everything specified with On and Return was
 // in fact called as expected.  Calls may have occurred in any order.
 func (m *Mock) AssertExpectations(t TestingT) bool {
+	if s, ok := t.(interface{ Skipped() bool }); ok && s.Skipped() {
+		return true
+	}
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
 	}
@@ -606,8 +611,8 @@ func (m *Mock) AssertExpectations(t TestingT) bool {
 		satisfied, reason := m.checkExpectation(expectedCall)
 		if !satisfied {
 			failedExpectations++
+			t.Logf(reason)
 		}
-		t.Logf(reason)
 	}
 
 	if failedExpectations != 0 {
@@ -758,25 +763,33 @@ const (
 	Anything = "mock.Anything"
 )
 
-// AnythingOfTypeArgument is a string that contains the type of an argument
+// AnythingOfTypeArgument contains the type of an argument
+// for use when type checking.  Used in Diff and Assert.
+//
+// Deprecated: this is an implementation detail that must not be used. Use [AnythingOfType] instead.
+type AnythingOfTypeArgument = anythingOfTypeArgument
+
+// anythingOfTypeArgument is a string that contains the type of an argument
 // for use when type checking.  Used in Diff and Assert.
-type AnythingOfTypeArgument string
+type anythingOfTypeArgument string
 
-// AnythingOfType returns an AnythingOfTypeArgument object containing the
-// name of the type to check for.  Used in Diff and Assert.
+// AnythingOfType returns a special value containing the
+// name of the type to check for. The type name will be matched against the type name returned by [reflect.Type.String].
+//
+// Used in Diff and Assert.
 //
 // For example:
 //
 //	Assert(t, AnythingOfType("string"), AnythingOfType("int"))
 func AnythingOfType(t string) AnythingOfTypeArgument {
-	return AnythingOfTypeArgument(t)
+	return anythingOfTypeArgument(t)
 }
 
 // IsTypeArgument is a struct that contains the type of an argument
 // for use when type checking.  This is an alternative to AnythingOfType.
 // Used in Diff and Assert.
 type IsTypeArgument struct {
-	t interface{}
+	t reflect.Type
 }
 
 // IsType returns an IsTypeArgument object containing the type to check for.
@@ -786,7 +799,7 @@ type IsTypeArgument struct {
 // For example:
 // Assert(t, IsType(""), IsType(0))
 func IsType(t interface{}) *IsTypeArgument {
-	return &IsTypeArgument{t: t}
+	return &IsTypeArgument{t: reflect.TypeOf(t)}
 }
 
 // FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument
@@ -950,53 +963,55 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
 				differences++
 				output = fmt.Sprintf("%s\t%d: FAIL:  %s not matched by %s\n", output, i, actualFmt, matcher)
 			}
-		} else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() {
-			// type checking
-			if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) {
-				// not match
-				differences++
-				output = fmt.Sprintf("%s\t%d: FAIL:  type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt)
-			}
-		} else if reflect.TypeOf(expected) == reflect.TypeOf((*IsTypeArgument)(nil)) {
-			t := expected.(*IsTypeArgument).t
-			if reflect.TypeOf(t) != reflect.TypeOf(actual) {
-				differences++
-				output = fmt.Sprintf("%s\t%d: FAIL:  type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt)
-			}
-		} else if reflect.TypeOf(expected) == reflect.TypeOf((*FunctionalOptionsArgument)(nil)) {
-			t := expected.(*FunctionalOptionsArgument).value
+		} else {
+			switch expected := expected.(type) {
+			case anythingOfTypeArgument:
+				// type checking
+				if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) {
+					// not match
+					differences++
+					output = fmt.Sprintf("%s\t%d: FAIL:  type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt)
+				}
+			case *IsTypeArgument:
+				actualT := reflect.TypeOf(actual)
+				if actualT != expected.t {
+					differences++
+					output = fmt.Sprintf("%s\t%d: FAIL:  type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt)
+				}
+			case *FunctionalOptionsArgument:
+				t := expected.value
 
-			var name string
-			tValue := reflect.ValueOf(t)
-			if tValue.Len() > 0 {
-				name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String()
-			}
+				var name string
+				tValue := reflect.ValueOf(t)
+				if tValue.Len() > 0 {
+					name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String()
+				}
 
-			tName := reflect.TypeOf(t).Name()
-			if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 {
-				differences++
-				output = fmt.Sprintf("%s\t%d: FAIL:  type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt)
-			} else {
-				if ef, af := assertOpts(t, actual); ef == "" && af == "" {
+				tName := reflect.TypeOf(t).Name()
+				if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 {
+					differences++
+					output = fmt.Sprintf("%s\t%d: FAIL:  type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt)
+				} else {
+					if ef, af := assertOpts(t, actual); ef == "" && af == "" {
+						// match
+						output = fmt.Sprintf("%s\t%d: PASS:  %s == %s\n", output, i, tName, tName)
+					} else {
+						// not match
+						differences++
+						output = fmt.Sprintf("%s\t%d: FAIL:  %s != %s\n", output, i, af, ef)
+					}
+				}
+
+			default:
+				if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) {
 					// match
-					output = fmt.Sprintf("%s\t%d: PASS:  %s == %s\n", output, i, tName, tName)
+					output = fmt.Sprintf("%s\t%d: PASS:  %s == %s\n", output, i, actualFmt, expectedFmt)
 				} else {
 					// not match
 					differences++
-					output = fmt.Sprintf("%s\t%d: FAIL:  %s != %s\n", output, i, af, ef)
+					output = fmt.Sprintf("%s\t%d: FAIL:  %s != %s\n", output, i, actualFmt, expectedFmt)
 				}
 			}
-		} else {
-			// normal checking
-
-			if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) {
-				// match
-				output = fmt.Sprintf("%s\t%d: PASS:  %s == %s\n", output, i, actualFmt, expectedFmt)
-			} else {
-				// not match
-				differences++
-				output = fmt.Sprintf("%s\t%d: FAIL:  %s != %s\n", output, i, actualFmt, expectedFmt)
-			}
 		}
 
 	}
diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go
index 63f85214..506a82f8 100644
--- a/vendor/github.com/stretchr/testify/require/require.go
+++ b/vendor/github.com/stretchr/testify/require/require.go
@@ -1,7 +1,4 @@
-/*
-* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
-* THIS FILE MUST NOT BE EDITED BY HAND
- */
+// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT.
 
 package require
 
@@ -235,7 +232,7 @@ func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{},
 	t.FailNow()
 }
 
-// EqualValues asserts that two objects are equal or convertable to the same types
+// EqualValues asserts that two objects are equal or convertible to the same types
 // and equal.
 //
 //	assert.EqualValues(t, uint32(123), int32(123))
@@ -249,7 +246,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg
 	t.FailNow()
 }
 
-// EqualValuesf asserts that two objects are equal or convertable to the same types
+// EqualValuesf asserts that two objects are equal or convertible to the same types
 // and equal.
 //
 //	assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
@@ -1546,6 +1543,32 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf
 	t.FailNow()
 }
 
+// NotImplements asserts that an object does not implement the specified interface.
+//
+//	assert.NotImplements(t, (*MyInterface)(nil), new(MyObject))
+func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	if assert.NotImplements(t, interfaceObject, object, msgAndArgs...) {
+		return
+	}
+	t.FailNow()
+}
+
+// NotImplementsf asserts that an object does not implement the specified interface.
+//
+//	assert.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
+func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) {
+	if h, ok := t.(tHelper); ok {
+		h.Helper()
+	}
+	if assert.NotImplementsf(t, interfaceObject, object, msg, args...) {
+		return
+	}
+	t.FailNow()
+}
+
 // NotNil asserts that the specified object is not nil.
 //
 //	assert.NotNil(t, err)
@@ -1658,10 +1681,12 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string,
 	t.FailNow()
 }
 
-// NotSubset asserts that the specified list(array, slice...) contains not all
-// elements given in the specified subset(array, slice...).
+// NotSubset asserts that the specified list(array, slice...) or map does NOT
+// contain all elements given in the specified subset list(array, slice...) or
+// map.
 //
-//	assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
+//	assert.NotSubset(t, [1, 3, 4], [1, 2])
+//	assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3})
 func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1672,10 +1697,12 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i
 	t.FailNow()
 }
 
-// NotSubsetf asserts that the specified list(array, slice...) contains not all
-// elements given in the specified subset(array, slice...).
+// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
+// contain all elements given in the specified subset list(array, slice...) or
+// map.
 //
-//	assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
+//	assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted")
+//	assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
 func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1880,10 +1907,11 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg
 	t.FailNow()
 }
 
-// Subset asserts that the specified list(array, slice...) contains all
-// elements given in the specified subset(array, slice...).
+// Subset asserts that the specified list(array, slice...) or map contains all
+// elements given in the specified subset list(array, slice...) or map.
 //
-//	assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
+//	assert.Subset(t, [1, 2, 3], [1, 2])
+//	assert.Subset(t, {"x": 1, "y": 2}, {"x": 1})
 func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1894,10 +1922,11 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte
 	t.FailNow()
 }
 
-// Subsetf asserts that the specified list(array, slice...) contains all
-// elements given in the specified subset(array, slice...).
+// Subsetf asserts that the specified list(array, slice...) or map contains all
+// elements given in the specified subset list(array, slice...) or map.
 //
-//	assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
+//	assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted")
+//	assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
 func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go
index 3b5b0933..eee8310a 100644
--- a/vendor/github.com/stretchr/testify/require/require_forward.go
+++ b/vendor/github.com/stretchr/testify/require/require_forward.go
@@ -1,7 +1,4 @@
-/*
-* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
-* THIS FILE MUST NOT BE EDITED BY HAND
- */
+// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT.
 
 package require
 
@@ -190,7 +187,7 @@ func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface
 	EqualExportedValuesf(a.t, expected, actual, msg, args...)
 }
 
-// EqualValues asserts that two objects are equal or convertable to the same types
+// EqualValues asserts that two objects are equal or convertible to the same types
 // and equal.
 //
 //	a.EqualValues(uint32(123), int32(123))
@@ -201,7 +198,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
 	EqualValues(a.t, expected, actual, msgAndArgs...)
 }
 
-// EqualValuesf asserts that two objects are equal or convertable to the same types
+// EqualValuesf asserts that two objects are equal or convertible to the same types
 // and equal.
 //
 //	a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted")
@@ -1222,6 +1219,26 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in
 	NotErrorIsf(a.t, err, target, msg, args...)
 }
 
+// NotImplements asserts that an object does not implement the specified interface.
+//
+//	a.NotImplements((*MyInterface)(nil), new(MyObject))
+func (a *Assertions) NotImplements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	NotImplements(a.t, interfaceObject, object, msgAndArgs...)
+}
+
+// NotImplementsf asserts that an object does not implement the specified interface.
+//
+//	a.NotImplementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
+func (a *Assertions) NotImplementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) {
+	if h, ok := a.t.(tHelper); ok {
+		h.Helper()
+	}
+	NotImplementsf(a.t, interfaceObject, object, msg, args...)
+}
+
 // NotNil asserts that the specified object is not nil.
 //
 //	a.NotNil(err)
@@ -1310,10 +1327,12 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri
 	NotSamef(a.t, expected, actual, msg, args...)
 }
 
-// NotSubset asserts that the specified list(array, slice...) contains not all
-// elements given in the specified subset(array, slice...).
+// NotSubset asserts that the specified list(array, slice...) or map does NOT
+// contain all elements given in the specified subset list(array, slice...) or
+// map.
 //
-//	a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]")
+//	a.NotSubset([1, 3, 4], [1, 2])
+//	a.NotSubset({"x": 1, "y": 2}, {"z": 3})
 func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1321,10 +1340,12 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs
 	NotSubset(a.t, list, subset, msgAndArgs...)
 }
 
-// NotSubsetf asserts that the specified list(array, slice...) contains not all
-// elements given in the specified subset(array, slice...).
+// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
+// contain all elements given in the specified subset list(array, slice...) or
+// map.
 //
-//	a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted")
+//	a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted")
+//	a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
 func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1484,10 +1505,11 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string,
 	Samef(a.t, expected, actual, msg, args...)
 }
 
-// Subset asserts that the specified list(array, slice...) contains all
-// elements given in the specified subset(array, slice...).
+// Subset asserts that the specified list(array, slice...) or map contains all
+// elements given in the specified subset list(array, slice...) or map.
 //
-//	a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]")
+//	a.Subset([1, 2, 3], [1, 2])
+//	a.Subset({"x": 1, "y": 2}, {"x": 1})
 func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1495,10 +1517,11 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...
 	Subset(a.t, list, subset, msgAndArgs...)
 }
 
-// Subsetf asserts that the specified list(array, slice...) contains all
-// elements given in the specified subset(array, slice...).
+// Subsetf asserts that the specified list(array, slice...) or map contains all
+// elements given in the specified subset list(array, slice...) or map.
 //
-//	a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted")
+//	a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted")
+//	a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
 func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()